[initial] Pull tcpdump tags/tcpdump-4.99.0
Bug: 55476, 84481
Change-Id: I2dd153f053ec1334ec8542fd41af0cdd8c60a4cd
diff --git a/CREDITS b/CREDITS
new file mode 100644
index 0000000..4496fac
--- /dev/null
+++ b/CREDITS
@@ -0,0 +1,322 @@
+This file lists people who have contributed to tcpdump.
+
+The current maintainers (in alphabetical order):
+ Denis Ovsienko <denis at ovsienko dot info>
+ Francois-Xavier Le Bail <devel dot fx dot lebail at orange dot fr>
+ Guy Harris <gharris at sonic dot net>
+ Michael Richardson <mcr at sandelman dot ottawa dot on dot ca>
+
+Additional people who have contributed patches (in alphabetical order):
+ Aaron Campbell <aaron at arbor dot net>
+ A Costa <agcosta at gis dot net>
+ Adam Sampson <ats at offog dot org>
+ Ahmed Abdelsalam <ahabdels at gmail dot com>
+ Albert Chin <china at thewrittenword dot com>
+ Alexandra Kossovsky <alexandra1975 at sourceforge dot net>
+ Alexandr Nedvedicky <alexandr dot nedvedicky at oracle dot com>
+ Alfredo Andres <aandres at s21sec dot com>
+ Ali Abdulkadir <autostart dot ini at gmail dot com>
+ Ananth Suryanarayana <anantha at juniper dot net>
+ Andrea Bittau <a dot bittau at cs dot ucl dot ac dot uk>
+ Andrea Ieri <andrea dot ieri at canonical dot com>
+ Andreas Jaggi <andreas dot jaggi at waterwave dot ch>
+ Andrew Brown <atatat at atatdot dot net>
+ Andrew Church <andrew at users dot sourceforge dot net>
+ Andrew Darqui <andrew dot darqui at gmail dot com>
+ Andrew Hintz <adhintz at users dot sourceforge dot net>
+ Andrew Lunn <andrew at lunn dot ch>
+ Andrew Nording <andrew at nording dot ru>
+ Andrew Tridgell <tridge at linuxcare dot com>
+ Andy Heffernan <ahh at juniper dot net>
+ Angus Cameron <anguscc at yahoo dot com>
+ Anton Bernal <anton at juniper dot net>
+ Antonin Décimo <antonin dot decimo at gmail dot com>
+ Aravind Prasad S <raja dot avi at gmail dot com>
+ Arkadiusz Miskiewicz <misiek at pld dot org dot pl>
+ Armando L. Caro Jr. <acaro at mail dot eecis dot udel dot edu>
+ Arnaldo Carvalho de Melo <acme at ghostprotocols dot net>
+ Atsushi Onoe <onoe at netbsd dot org>
+ Baptiste Jonglez <baptiste dot jonglez at ens-lyon dot org>
+ Baruch Siach <baruch at tkos dot co dot il>
+ Ben Byer <bushing at sourceforge dot net>
+ Ben Smithurst <ben at scientia dot demon dot co dot uk>
+ Bert Vermeulen <bert at biot dot com>
+ Bill Parker <wp02855 at gmail dot com>
+ Bjoern A. Zeeb <bzeeb at Zabbadoz dot NeT>
+ Bram <tcpdump at mail dot wizbit dot be>
+ Brent L. Bates <blbates at vigyan dot com>
+ Brian Carpenter <brian dot carpenter at gmail dot com>
+ Brian Ginsbach <ginsbach at cray dot com>
+ Brooks Davis <brooks at one-eyed-alien dot net>
+ Bruce M. Simpson <bms at spc dot org>
+ Bryce Wood <woodbr at oregonstate dot edu>
+ bugyo <bugyo at users dot noreply dot github dot com>
+ Carles Kishimoto Bisbe <ckishimo at ac dot upc dot es>
+ Casey Deccio <casey at deccio dot net>
+ Charles M. Hannum <mycroft at netbsd dot org>
+ Charlie Lenahan <clenahan at fortresstech dot com>
+ Chris Cogdon <chris at cogdon dot org>
+ Chris G. Demetriou <cgd at netbsd dot org>
+ Chris Jepeway <jepeway at blasted-heath dot com>
+ Chris Larson <clarson at kergoth dot com>
+ Christian Sievers <c_s at users dot sourceforge dot net>
+ Christophe Rhodes <csr21 at cantab dot net>
+ Cliff Frey <cliff at meraki dot com>
+ Craig Leres <leres at xse dot com>
+ Craig Rodrigues <rodrigc at mediaone dot net>
+ Crist J. Clark <cjclark at alum dot mit dot edu>
+ Daniel Hagerty <hag at ai dot mit dot edu>
+ Daniel Lee <Longinus00 at gmail dot com>
+ Daniel Miller <dmiller at nmap dot org>
+ Dario Lombardo <lomato at gmail dot com>
+ Darren Reed <darrenr at reed dot wattle dot id dot au>
+ David Binderman <d dot binderman at virgin dot net>
+ David Cronin <davidcronin94 at gmail dot com>
+ Davide Caratti <dcaratti at redhat dot com>
+ David Horn <dhorn2000 at gmail dot com>
+ David Smith <dsmith at redhat dot com>
+ David Young <dyoung at ojctech dot com>
+ Dion Bosschieter <dbosschieter at transip dot nl>
+ Dmitrij Tejblum <tejblum at yandex-team dot ru>
+ Dmitry Eremin-Solenikov <dbaryshkov at gmail dot com>
+ Don Ebright <Don dot Ebright at compuware dot com>
+ d simonov <simonov-d at yandex-team dot ru>
+ Duane Wessels <dwessels at verisign dot com>
+ Eamon Doyle <eamonjd at arista dot com>
+ Eddie Kohler <xexd at sourceforge dot net>
+ Eliot Lear <lear at upstairs dot ofcourseimright dot com>
+ Elmar Kirchner <elmar at juniper dot net>
+ Eric S. Raymond <esr at thyrsus dot com>
+ Etienne Marais <etienne at marais dot green>
+ Fang Wang <fangwang at sourceforge dot net>
+ Ferry Huberts <ferry dot huberts at pelagic dot nl>
+ Florent Drouin <Florent dot Drouin at alcatel-lucent dot fr>
+ Florian Fainelli <f dot fainelli at gmail dot com>
+ Florian Forster <octo at verplant dot org>
+ fra <foo at bar dot baz>
+ Francesco Fondelli <francesco dot fondelli at gmail dot com>
+ Francisco Matias Cuenca-Acuna <mcuenca at george dot rutgers dot edu>
+ Francis Dupont <Francis dot Dupont at enst-bretagne dot fr>
+ Frank Volf <volf at oasis dot IAEhv dot nl>
+ Fulvio Risso <risso at polito dot it>
+ George Bakos <gbakos at ists dot dartmouth dot edu>
+ Gerald Combs <gerald at ethereal dot com>
+ Gerard Garcia <ggarcia at deic dot uab dot cat>
+ Gerrit Renker <gerrit at erg dot abdn dot ac dot uk>
+ Gert Doering <gert at greenie dot muc dot de>
+ Gilbert Ramirez Jr. <gram at xiexie dot org>
+ Gisle Vanem <gvanem at yahoo dot no>
+ Gleb Smirnoff <glebius at FreeBSD dot org>
+ Greg Minshall <minshall at acm dot org>
+ Grégoire Henry <henry at pps dot jussieu dot fr>
+ Gregory Detal <gregory dot detal at uclouvain dot be>
+ Greg Stark <gsstark at mit dot edu>
+ Greg Steinbrecher <steinbrecher at alum dot mit dot edu>
+ Guy Lewin <guy at lewin dot co dot il>
+ Hank Leininger <tcpdump-workers at progressive-comp dot com>
+ Hannes Viertel <hviertel at juniper dot net>
+ Hanno Böck <hanno at hboeck dot de>
+ Harry Raaymakers <harryr at connect dot com dot au>
+ Heinz-Ado Arnolds <Ado dot Arnolds at dhm-systems dot de>
+ Hendrik Scholz <hendrik at scholz dot net>
+ Herwin Weststrate <herwin at quarantainenet dot nl>
+ Ian McDonald <imcdnzl at gmail dot com>
+ Ilpo Järvinen <ilpo dot jarvinen at helsinki dot fi>
+ Jacek Tobiasz <Jacek dot Tobiasz at atm dot com dot pl>
+ Jacob Davis <jacobgb24 at yahoo dot com>
+ Jakob Schlyter <jakob at openbsd dot org>
+ Jamal Hadi Salim <hadi at cyberus dot ca>
+ James Ko <jck at exegin dot com>
+ Jamie Bainbridge <jamie dot bainbridge at gmail dot com>
+ Jan Oravec <wsx at wsx6 dot net>
+ Jason L. Wright <jason at thought dot net>
+ Jason R. Thorpe <thorpej at netbsd dot org>
+ Jean-Raphaël Gaglione <jr dot gaglione at yahoo dot fr>
+ Jeff Chan <jchan at arista dot com>
+ Jefferson Ogata <jogata at nodc dot noaa dot gov>
+ Jeffrey Hutzelman <jhutz at cmu dot edu>
+ Jeremy Browne <jer at ifni dot ca>
+ Jesper Peterson <jesper at endace dot com>
+ Jesse Gross <jesse at nicira dot com>
+ Jim Hutchins <jim at ca dot sandia dot gov>
+ João Medeiros <ignotus21 at sourceforge dot net>
+ Job Snijders <job at instituut dot net>
+ Joerg Mayer <jmayer at loplof dot de>
+ Jonathan Heusser <jonny at drugphish dot ch>
+ Jorge Boncompte [DTI2] <jorge at dti2 dot net>
+ Jørgen Thomsen <jth at jth dot net>
+ Julian Cowley <julian at lava dot net>
+ Juliusz Chroboczek <jch at pps dot jussieu dot fr>
+ Kaarthik Sivakumar <kaarthik at torrentnet dot com>
+ Kaladhar Musunuru <kaladharm at sourceforge dot net>
+ Kamil Frankowicz <kontakt at frankowicz dot me>
+ Karl Norby <karl-norby at sourceforge dot net>
+ Kazushi Sugyo <sugyo at pb dot jp dot nec dot com>
+ Kelly Carmichael <kcarmich at ipapp dot com>
+ Ken Hornstein <kenh at cmf dot nrl dot navy dot mil>
+ Kenichi Maehashi <webmaster at kenichimaehashi dot com>
+ Kevin Steves <stevesk at pobox dot com>
+ Klaus Klein <kleink at reziprozitaet dot de>
+ Kris Kennaway <kris at freebsd dot org>
+ Krzysztof Halasa <khc at pm dot waw dot pl>
+ Larry Lile <lile at stdio dot com>
+ Lennert Buytenhek <buytenh at gnu dot org>
+ Loganaden Velvindron <logan at cyberstorm dot mu>
+ Loris Degioanni <loris at netgroup-serv dot polito dot it>
+ Love Hörnquist-Åstrand <lha at stacken dot kth dot se>
+ Lucas C. Villa Real <lucasvr at us dot ibm dot com>
+ Luigi Rizzo <luigi at freebsd dot org>
+ Luis MartinGarcia <luis dot mgarc at gmail dot com>
+ Luiz Otavio O Souza <loos at freebsd dot org>
+ Maciej W. Rozycki <macro at ds2 dot pg dot gda dot pl>
+ Manoharan Sundaramoorthy <manoharan at arista dot com>
+ Manu Pathak <mapathak at cisco dot com>
+ Marc Abramowitz <marc at marc-abramowitz dot com>
+ Marc A. Lehmann <pcg at goof dot com>
+ Marc Binderberger <mbind at sourceforge dot net>
+ Mark Andrews <marka at isc dot org>
+ Mark Ellzey Thomas <mark at ackers dot net>
+ Marko Kiiskila <carnil at cs dot tut dot fi>
+ Markus Schöpflin <schoepflin at sourceforge dot net>
+ Marshall Rose <mrose at dbc dot mtview dot ca dot us>
+ Martin Buck <mb-tmp-tvguho dot pbz at gromit dot dyndns dot org>
+ Martin Husemann <martin at netbsd dot org>
+ Martin Sehnoutka <msehnout at redhat dot com>
+ Matt Eaton <agnosticdev at gmail dot com>
+ Matthieu Boutier <boutier at pps dot univ-paris-diderot dot fr>
+ Max Laier <max at love2party dot net>
+ Michael A. Meffie III <meffie at sourceforge dot net>
+ Michael Haardt <michael at moria dot de>
+ Michael Kirkhart <michael dot kirkhart at att dot net>
+ Michael Madore <mmadore at turbolinux dot com>
+ Michael Riepe <too-tired at sourceforge dot net>
+ Michael Shalayeff <mickey at openbsd dot org>
+ Michael Shields <shields at msrl dot com>
+ Michael T. Stolarchuk <mts at off dot to>
+ Michal Sekletar <msekleta at redhat dot com>
+ Michele "mydecay" Marchetto <smarchetto1 at tin dot it>
+ Mike Frysinger <vapier at gmail dot com>
+ Minto Jeyananth <minto at juniper dot net>
+ Miroslav Lichvar <mlichvar at redhat dot com>
+ Mister X <3520734+Mister-X- at users dot noreply dot github dot com>
+ Mitsunori Komatsu <komamitsu at gmail dot com>
+ Monroe Williams <monroe at pobox dot com>
+ Moses Devadason <mosesdevadason at gmail dot com>
+ Motonori Shindo <mshindo at mshindo dot net>
+ Nan Xiao <nan at chinadtrace dot org>
+ Nathaniel Couper-Noles <Nathaniel at isi1 dot tccisi dot com>
+ Nathan J. Williams <nathanw at MIT dot EDU>
+ Neil T. Spring <bluehal at users dot sourceforge dot net>
+ Nickolai Zeldovich <kolya at MIT dot EDU>
+ Nicolas Ferrero <toorop at babylo dot net>
+ Niels Provos <provos at openbsd dot org>
+ Nikhil AP <nikhilap at arista dot com>
+ Noritoshi Demizu <demizu at users dot sourceforge dot net>
+ Olaf Kirch <okir at caldera dot de>
+ Ola Martin Lykkja <ola dot lykkja at q-free dot com>
+ Oleksij Rempel <linux at rempel-privat dot de>
+ Onno van der Linden <onno at simplex dot nl>
+ Paolo Abeni <paolo dot abeni at email dot it>
+ Partha Ghosh <psg at cumulusnetworks dot com>
+ Pascal Hennequin <pascal dot hennequin at int-evry dot fr>
+ Pasvorn Boonmark <boonmark at juniper dot net>
+ Patrik Lundquist <patrik dot lundquist at gmail dot com>
+ Paul Ferrell <pflarr at sourceforge dot net>
+ Paul Mundt <lethal at linux-sh dot org>
+ Paul S. Traina <pst at freebsd dot org>
+ Pavlin Radoslavov <pavlin at icir dot org>
+ Pawel Worach <pawel dot worach at gmail dot com>
+ Pedro Monreal <pmgdeb at gmail dot com>
+ Pekka Savola <pekkas at netcore dot fi>
+ Petar Alilovic <petar dot alilovic at gmail dot com>
+ Peter Fales <peter at fales-lorenz dot net>
+ Peter Jeremy <peter dot jeremy at alcatel dot com dot au>
+ Peter Krystad <peter dot krystad at linux dot intel dot com>
+ Peter Volkov <pva at gentoo dot org>
+ Petr Vorel <pvorel at suse dot cz>
+ <pfhunt at users dot sourceforge dot net>
+ Phil Wood <cpw at lanl dot gov>
+ Pier Carlo Chiodi <pierky at pierky dot com>
+ Rafal Maszkowski <rzm at icm dot edu dot pl>
+ Randy Sofia <rsofia at users dot sourceforge dot net>
+ Raphael Raimbault <raphael dot raimbault at netasq dot com>
+ Renato Botelho <garga at FreeBSD dot org>
+ Ricardo Nabinger Sanchez <rnsanchez at taghos dot com dot br>
+ Richard Scheffenegger <srichard at netapp dot com>
+ Rick Cheng <rcheng at juniper dot net>
+ Rick Jones <rick dot jones2 at hp dot com>
+ Rick Watson <watsonrick at users dot sourceforge dot net>
+ Ritesh Ranjan <r dot ranjan789 at gmail dot com>
+ Rob Braun <bbraun at synack dot net>
+ Robert Edmonds <stu-42 at sourceforge dot net>
+ Rocco Lucia <rlucia at iscanet dot com>
+ Roderick Schertler <roderick at argon dot org>
+ Romain Francoise <rfrancoise at debian dot org>
+ Romero Malaquias <romero dot malaquias at gmail dot com>
+ Ruben Kerkhof <ruben at rubenkerkhof dot com>
+ Rui Paulo <rpaulo at FreeBSD dot org>
+ Sabrina Dubroca <sd at queasysnail dot net>
+ Sagun Shakya <sagun dot shakya at sun dot com>
+ Sami Farin <safari at iki dot fi>
+ Sawssen Hadded <saw dot hadded at gmail dot com>
+ Scott Mcmillan <scott dot a dot mcmillan at intel dot com>
+ Scott Rose <syberpunk at users dot sourceforge dot net>
+ Sebastian Krahmer <krahmer at cs dot uni-potsdam dot de>
+ Sebastien Raveau <sebastien dot raveau at epita dot fr>
+ Sebastien Vincent <svincent at idems dot fr>
+ Sepherosa Ziehau <sepherosa at gmail dot com>
+ Seth Webster <swebster at sst dot ll dot mit dot edu>
+ Shinsuke Suzuki <suz at kame dot net>
+ Simon Nicolussi <sinic at sinic dot name>
+ Simon Ruderich <simon at ruderich dot org>
+ Slava Shwartsman <slavash at mellanox dot com>
+ Stefan Hajnoczi <stefanha at redhat dot com>
+ Steinar Haug <sthaug at nethelp dot no>
+ Stephane Bortzmeyer <stephane+github at bortzmeyer dot org>
+ Steve Kay <stevekay at gmail dot com>
+ Steven H. Wang <wang dot steven dot h at gmail dot com>
+ Swaathi Vetrivel <swaathiv at juniper dot net>
+ Swaminathan Chandrasekaran <chander at juniper dot net>
+ Takashi Yamamoto <yamt at mwd dot biglobe dot ne dot jp>
+ Tatuya Jinmei <jinmei at kame dot net>
+ Tero Kivinen <kivinen at iki dot fi>
+ Terry Kennedy <terry at tmk dot com>
+ Thomas Jacob <jacob at internet24 dot de>
+ Timo Koskiahde
+ Tom Jones <thj at freebsd dot org>
+ Tommy Beadle <tbeadle at arbor dot net>
+ Tony Li <tli at procket dot com>
+ Tony Samuels <vegizombie at gmail dot com>
+ Tony Xu <hhktony at gmail dot com>
+ Toshihiro Kanda <candy at fct dot kgc dot co dot jp>
+ Udayakumar <udaya011 at gmail dot com>
+ Ulrich Windl <Ulrich dot Windl at RZ dot Uni-Regensburg dot DE>
+ Uns Lider <unslider at miranda dot org>
+ Victor Oppleman <oppleman at users dot sourceforge dot net>
+ Viral Mehta <viral dot mehta at dell dot com>
+ Vitaly Lavrov <vel21ripn at gmail dot com>
+ Vivien Didelot <vivien dot didelot at gmail dot com>
+ Vyacheslav Trushkin <dogonthesun at gmail dot com>
+ Wang Jian <larkwang at gmail dot com>
+ Weesan Lee <weesan at juniper dot net>
+ Wesley Griffin <wgriffin at users dot sourceforge dot net>
+ Wesley Shields <wxs at FreeBSD dot org>
+ Wilbert de Graaf <wilbertdg at hetnet dot nl>
+ Will Drewry <will at alum dot bu dot edu>
+ William J. Hulley <bill dot hulley at gmail dot com>
+ Wim Torfs <wtorfs at gmail dot com>
+ Wolfgang Karall <office at karall-edv dot at>
+ Yen Yen Lim
+ Yoshifumi Nishida
+ zolf <flos at xs4all dot nl>
+
+The original LBL crew:
+ Steve McCanne
+ Craig Leres
+ Van Jacobson
+
+Past maintainers (in alphabetical order):
+ Bill Fenner <fenner at research dot att dot com>
+ Fulvio Risso <risso at polito dot it>
+ Hannes Gredler <hannes at gredler dot at>
+ Jun-ichiro itojun Hagino <itojun at iijlab dot net> Also see: http://www.wide.ad.jp/itojun-award/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a10474d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,19 @@
+License: BSD
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ 3. The names of the authors may not be used to endorse or promote
+ products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7e381e1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,226 @@
+# tcpdump
+
+[![Build Status](https://travis-ci.org/the-tcpdump-group/tcpdump.svg?branch=master)](https://travis-ci.org/the-tcpdump-group/tcpdump)
+
+[![Build Status](https://ci.appveyor.com/api/projects/status/github/the-tcpdump-group/tcpdump?branch=master&svg=true)](https://ci.appveyor.com/project/guyharris/tcpdump)
+
+To report a security issue please send an e-mail to security@tcpdump.org.
+
+To report bugs and other problems, contribute patches, request a
+feature, provide generic feedback etc please see the file
+CONTRIBUTING in the tcpdump source tree root.
+
+TCPDUMP 4.x.y
+Now maintained by "The Tcpdump Group"
+See https://www.tcpdump.org
+
+Anonymous Git is available via:
+
+ git clone git://bpf.tcpdump.org/tcpdump
+
+formerly from Lawrence Berkeley National Laboratory
+ Network Research Group <tcpdump@ee.lbl.gov>
+ ftp://ftp.ee.lbl.gov/old/tcpdump.tar.Z (3.4)
+
+This directory contains source code for tcpdump, a tool for network
+monitoring and data acquisition. This software was originally
+developed by the Network Research Group at the Lawrence Berkeley
+National Laboratory. The original distribution is available via
+anonymous ftp to `ftp.ee.lbl.gov`, in `tcpdump.tar.Z`. More recent
+development is performed at tcpdump.org, https://www.tcpdump.org/.
+
+Tcpdump uses libpcap, a system-independent interface for user-level
+packet capture. Before building tcpdump, you must first retrieve and
+build libpcap, also originally from LBL and now being maintained by
+tcpdump.org; see https://www.tcpdump.org/.
+
+Once libpcap is built (either install it or make sure it's in
+`../libpcap`), you can build tcpdump using the procedure in the `INSTALL.txt`
+file.
+
+The program is loosely based on SMI's "etherfind" although none of the
+etherfind code remains. It was originally written by Van Jacobson as
+part of an ongoing research project to investigate and improve tcp and
+internet gateway performance. The parts of the program originally
+taken from Sun's etherfind were later re-written by Steven McCanne of
+LBL. To insure that there would be no vestige of proprietary code in
+tcpdump, Steve wrote these pieces from the specification given by the
+manual entry, with no access to the source of tcpdump or etherfind.
+
+Over the past few years, tcpdump has been steadily improved by the
+excellent contributions from the Internet community (just browse
+through the `CHANGES` file). We are grateful for all the input.
+
+Richard Stevens gives an excellent treatment of the Internet protocols
+in his book *"TCP/IP Illustrated, Volume 1"*. If you want to learn more
+about tcpdump and how to interpret its output, pick up this book.
+
+Some tools for viewing and analyzing tcpdump trace files are available
+from the Internet Traffic Archive:
+
+* http://ita.ee.lbl.gov/
+
+Another tool that tcpdump users might find useful is tcpslice:
+
+* https://github.com/the-tcpdump-group/tcpslice
+
+It is a program that can be used to extract portions of tcpdump binary
+trace files. See the above distribution for further details and
+documentation.
+
+Current versions can be found at https://www.tcpdump.org.
+
+ - The TCPdump group
+
+original text by: Steve McCanne, Craig Leres, Van Jacobson
+
+-------------------------------------
+```
+This directory also contains some short awk programs intended as
+examples of ways to reduce tcpdump data when you're tracking
+particular network problems:
+
+send-ack.awk
+ Simplifies the tcpdump trace for an ftp (or other unidirectional
+ tcp transfer). Since we assume that one host only sends and
+ the other only acks, all address information is left off and
+ we just note if the packet is a "send" or an "ack".
+
+ There is one output line per line of the original trace.
+ Field 1 is the packet time in decimal seconds, relative
+ to the start of the conversation. Field 2 is delta-time
+ from last packet. Field 3 is packet type/direction.
+ "Send" means data going from sender to receiver, "ack"
+ means an ack going from the receiver to the sender. A
+ preceding "*" indicates that the data is a retransmission.
+ A preceding "-" indicates a hole in the sequence space
+ (i.e., missing packet(s)), a "#" means an odd-size (not max
+ seg size) packet. Field 4 has the packet flags
+ (same format as raw trace). Field 5 is the sequence
+ number (start seq. num for sender, next expected seq number
+ for acks). The number in parens following an ack is
+ the delta-time from the first send of the packet to the
+ ack. A number in parens following a send is the
+ delta-time from the first send of the packet to the
+ current send (on duplicate packets only). Duplicate
+ sends or acks have a number in square brackets showing
+ the number of duplicates so far.
+
+ Here is a short sample from near the start of an ftp:
+ 3.00 0.20 send . 512
+ 3.20 0.20 ack . 1024 (0.20)
+ 3.20 0.00 send P 1024
+ 3.40 0.20 ack . 1536 (0.20)
+ 3.80 0.40 * send . 0 (3.80) [2]
+ 3.82 0.02 * ack . 1536 (0.62) [2]
+ Three seconds into the conversation, bytes 512 through 1023
+ were sent. 200ms later they were acked. Shortly thereafter
+ bytes 1024-1535 were sent and again acked after 200ms.
+ Then, for no apparent reason, 0-511 is retransmitted, 3.8
+ seconds after its initial send (the round trip time for this
+ ftp was 1sec, +-500ms). Since the receiver is expecting
+ 1536, 1536 is re-acked when 0 arrives.
+
+packetdat.awk
+ Computes chunk summary data for an ftp (or similar
+ unidirectional tcp transfer). [A "chunk" refers to
+ a chunk of the sequence space -- essentially the packet
+ sequence number divided by the max segment size.]
+
+ A summary line is printed showing the number of chunks,
+ the number of packets it took to send that many chunks
+ (if there are no lost or duplicated packets, the number
+ of packets should equal the number of chunks) and the
+ number of acks.
+
+ Following the summary line is one line of information
+ per chunk. The line contains eight fields:
+ 1 - the chunk number
+ 2 - the start sequence number for this chunk
+ 3 - time of first send
+ 4 - time of last send
+ 5 - time of first ack
+ 6 - time of last ack
+ 7 - number of times chunk was sent
+ 8 - number of times chunk was acked
+ (all times are in decimal seconds, relative to the start
+ of the conversation.)
+
+ As an example, here is the first part of the output for
+ an ftp trace:
+
+ # 134 chunks. 536 packets sent. 508 acks.
+ 1 1 0.00 5.80 0.20 0.20 4 1
+ 2 513 0.28 6.20 0.40 0.40 4 1
+ 3 1025 1.16 6.32 1.20 1.20 4 1
+ 4 1561 1.86 15.00 2.00 2.00 6 1
+ 5 2049 2.16 15.44 2.20 2.20 5 1
+ 6 2585 2.64 16.44 2.80 2.80 5 1
+ 7 3073 3.00 16.66 3.20 3.20 4 1
+ 8 3609 3.20 17.24 3.40 5.82 4 11
+ 9 4097 6.02 6.58 6.20 6.80 2 5
+
+ This says that 134 chunks were transferred (about 70K
+ since the average packet size was 512 bytes). It took
+ 536 packets to transfer the data (i.e., on the average
+ each chunk was transmitted four times). Looking at,
+ say, chunk 4, we see it represents the 512 bytes of
+ sequence space from 1561 to 2048. It was first sent
+ 1.86 seconds into the conversation. It was last
+ sent 15 seconds into the conversation and was sent
+ a total of 6 times (i.e., it was retransmitted every
+ 2 seconds on the average). It was acked once, 140ms
+ after it first arrived.
+
+stime.awk
+atime.awk
+ Output one line per send or ack, respectively, in the form
+ <time> <seq. number>
+ where <time> is the time in seconds since the start of the
+ transfer and <seq. number> is the sequence number being sent
+ or acked. I typically plot this data looking for suspicious
+ patterns.
+
+
+The problem I was looking at was the bulk-data-transfer
+throughput of medium delay network paths (1-6 sec. round trip
+time) under typical DARPA Internet conditions. The trace of the
+ftp transfer of a large file was used as the raw data source.
+The method was:
+
+ - On a local host (but not the Sun running tcpdump), connect to
+ the remote ftp.
+
+ - On the monitor Sun, start the trace going. E.g.,
+ tcpdump host local-host and remote-host and port ftp-data >tracefile
+
+ - On local, do either a get or put of a large file (~500KB),
+ preferably to the null device (to minimize effects like
+ closing the receive window while waiting for a disk write).
+
+ - When transfer is finished, stop tcpdump. Use awk to make up
+ two files of summary data (maxsize is the maximum packet size,
+ tracedata is the file of tcpdump tracedata):
+ awk -f send-ack.awk packetsize=avgsize tracedata >sa
+ awk -f packetdat.awk packetsize=avgsize tracedata >pd
+
+ - While the summary data files are printing, take a look at
+ how the transfer behaved:
+ awk -f stime.awk tracedata | xgraph
+ (90% of what you learn seems to happen in this step).
+
+ - Do all of the above steps several times, both directions,
+ at different times of day, with different protocol
+ implementations on the other end.
+
+ - Using one of the Unix data analysis packages (in my case,
+ S and Gary Perlman's Unix|Stat), spend a few months staring
+ at the data.
+
+ - Change something in the local protocol implementation and
+ redo the steps above.
+
+ - Once a week, tell your funding agent that you're discovering
+ wonderful things and you'll write up that research report
+ "real soon now".
+```
diff --git a/VERSION b/VERSION
new file mode 100644
index 0000000..60f4637
--- /dev/null
+++ b/VERSION
@@ -0,0 +1 @@
+4.99.0
diff --git a/addrtoname.c b/addrtoname.c
new file mode 100644
index 0000000..33b9378
--- /dev/null
+++ b/addrtoname.c
@@ -0,0 +1,1322 @@
+/*
+ * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Internet, ethernet, port, and protocol string to address
+ * and address to string conversion routines
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_CASPER
+#include <libcasper.h>
+#include <casper/cap_dns.h>
+#endif /* HAVE_CASPER */
+
+#include "netdissect-stdinc.h"
+
+#ifdef _WIN32
+ /*
+ * We have our own ether_ntohost(), reading from the system's
+ * Ethernet address file.
+ */
+ #include "missing/win_ether_ntohost.h"
+#else
+ #ifdef USE_ETHER_NTOHOST
+ #if defined(NET_ETHERNET_H_DECLARES_ETHER_NTOHOST)
+ /*
+ * OK, just include <net/ethernet.h>.
+ */
+ #include <net/ethernet.h>
+ #elif defined(NETINET_ETHER_H_DECLARES_ETHER_NTOHOST)
+ /*
+ * OK, just include <netinet/ether.h>
+ */
+ #include <netinet/ether.h>
+ #elif defined(SYS_ETHERNET_H_DECLARES_ETHER_NTOHOST)
+ /*
+ * OK, just include <sys/ethernet.h>
+ */
+ #include <sys/ethernet.h>
+ #elif defined(ARPA_INET_H_DECLARES_ETHER_NTOHOST)
+ /*
+ * OK, just include <arpa/inet.h>
+ */
+ #include <arpa/inet.h>
+ #elif defined(NETINET_IF_ETHER_H_DECLARES_ETHER_NTOHOST)
+ /*
+ * OK, include <netinet/if_ether.h>, after all the other stuff we
+ * need to include or define for its benefit.
+ */
+ #define NEED_NETINET_IF_ETHER_H
+ #else
+ /*
+ * We'll have to declare it ourselves.
+ * If <netinet/if_ether.h> defines struct ether_addr, include
+ * it. Otherwise, define it ourselves.
+ */
+ #ifdef HAVE_STRUCT_ETHER_ADDR
+ #define NEED_NETINET_IF_ETHER_H
+ #else /* HAVE_STRUCT_ETHER_ADDR */
+ struct ether_addr {
+ unsigned char ether_addr_octet[MAC_ADDR_LEN];
+ };
+ #endif /* HAVE_STRUCT_ETHER_ADDR */
+ #endif /* what declares ether_ntohost() */
+
+ #ifdef NEED_NETINET_IF_ETHER_H
+ #include <net/if.h> /* Needed on some platforms */
+ #include <netinet/in.h> /* Needed on some platforms */
+ #include <netinet/if_ether.h>
+ #endif /* NEED_NETINET_IF_ETHER_H */
+
+ #ifndef HAVE_DECL_ETHER_NTOHOST
+ /*
+ * No header declares it, so declare it ourselves.
+ */
+ extern int ether_ntohost(char *, const struct ether_addr *);
+ #endif /* !defined(HAVE_DECL_ETHER_NTOHOST) */
+ #endif /* USE_ETHER_NTOHOST */
+#endif /* _WIN32 */
+
+#include <pcap.h>
+#include <pcap-namedb.h>
+#ifndef HAVE_GETSERVENT
+#include <getservent.h>
+#endif
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "addrtostr.h"
+#include "ethertype.h"
+#include "llc.h"
+#include "extract.h"
+#include "oui.h"
+
+/*
+ * hash tables for whatever-to-name translations
+ *
+ * ndo_error() called on strdup(3) failure with S_ERR_ND_MEM_ALLOC status
+ */
+
+#define HASHNAMESIZE 4096
+
+struct hnamemem {
+ uint32_t addr;
+ const char *name;
+ struct hnamemem *nxt;
+};
+
+static struct hnamemem hnametable[HASHNAMESIZE];
+static struct hnamemem tporttable[HASHNAMESIZE];
+static struct hnamemem uporttable[HASHNAMESIZE];
+static struct hnamemem eprototable[HASHNAMESIZE];
+static struct hnamemem dnaddrtable[HASHNAMESIZE];
+static struct hnamemem ipxsaptable[HASHNAMESIZE];
+
+#ifdef _WIN32
+/*
+ * fake gethostbyaddr for Win2k/XP
+ * gethostbyaddr() returns incorrect value when AF_INET6 is passed
+ * to 3rd argument.
+ *
+ * h_name in struct hostent is only valid.
+ */
+static struct hostent *
+win32_gethostbyaddr(const char *addr, int len, int type)
+{
+ static struct hostent host;
+ static char hostbuf[NI_MAXHOST];
+ char hname[NI_MAXHOST];
+ struct sockaddr_in6 addr6;
+
+ host.h_name = hostbuf;
+ switch (type) {
+ case AF_INET:
+ return gethostbyaddr(addr, len, type);
+ break;
+ case AF_INET6:
+ memset(&addr6, 0, sizeof(addr6));
+ addr6.sin6_family = AF_INET6;
+ memcpy(&addr6.sin6_addr, addr, len);
+ if (getnameinfo((struct sockaddr *)&addr6, sizeof(addr6),
+ hname, sizeof(hname), NULL, 0, 0)) {
+ return NULL;
+ } else {
+ strlcpy(host.h_name, hname, NI_MAXHOST);
+ return &host;
+ }
+ break;
+ default:
+ return NULL;
+ }
+}
+#define gethostbyaddr win32_gethostbyaddr
+#endif /* _WIN32 */
+
+struct h6namemem {
+ nd_ipv6 addr;
+ char *name;
+ struct h6namemem *nxt;
+};
+
+static struct h6namemem h6nametable[HASHNAMESIZE];
+
+struct enamemem {
+ u_short e_addr0;
+ u_short e_addr1;
+ u_short e_addr2;
+ const char *e_name;
+ u_char *e_nsap; /* used only for nsaptable[] */
+ struct enamemem *e_nxt;
+};
+
+static struct enamemem enametable[HASHNAMESIZE];
+static struct enamemem nsaptable[HASHNAMESIZE];
+
+struct bsnamemem {
+ u_short bs_addr0;
+ u_short bs_addr1;
+ u_short bs_addr2;
+ const char *bs_name;
+ u_char *bs_bytes;
+ unsigned int bs_nbytes;
+ struct bsnamemem *bs_nxt;
+};
+
+static struct bsnamemem bytestringtable[HASHNAMESIZE];
+
+struct protoidmem {
+ uint32_t p_oui;
+ u_short p_proto;
+ const char *p_name;
+ struct protoidmem *p_nxt;
+};
+
+static struct protoidmem protoidtable[HASHNAMESIZE];
+
+/*
+ * A faster replacement for inet_ntoa().
+ */
+const char *
+intoa(uint32_t addr)
+{
+ char *cp;
+ u_int byte;
+ int n;
+ static char buf[sizeof(".xxx.xxx.xxx.xxx")];
+
+ addr = ntohl(addr);
+ cp = buf + sizeof(buf);
+ *--cp = '\0';
+
+ n = 4;
+ do {
+ byte = addr & 0xff;
+ *--cp = (char)(byte % 10) + '0';
+ byte /= 10;
+ if (byte > 0) {
+ *--cp = (char)(byte % 10) + '0';
+ byte /= 10;
+ if (byte > 0)
+ *--cp = (char)byte + '0';
+ }
+ *--cp = '.';
+ addr >>= 8;
+ } while (--n > 0);
+
+ return cp + 1;
+}
+
+static uint32_t f_netmask;
+static uint32_t f_localnet;
+#ifdef HAVE_CASPER
+extern cap_channel_t *capdns;
+#endif
+
+/*
+ * Return a name for the IP address pointed to by ap. This address
+ * is assumed to be in network byte order.
+ *
+ * NOTE: ap is *NOT* necessarily part of the packet data, so you
+ * *CANNOT* use the ND_TCHECK_* or ND_TTEST_* macros on it. Furthermore,
+ * even in cases where it *is* part of the packet data, the caller
+ * would still have to check for a null return value, even if it's
+ * just printing the return value with "%s" - not all versions of
+ * printf print "(null)" with "%s" and a null pointer, some of them
+ * don't check for a null pointer and crash in that case.
+ *
+ * The callers of this routine should, before handing this routine
+ * a pointer to packet data, be sure that the data is present in
+ * the packet buffer. They should probably do those checks anyway,
+ * as other data at that layer might not be IP addresses, and it
+ * also needs to check whether they're present in the packet buffer.
+ */
+const char *
+ipaddr_string(netdissect_options *ndo, const u_char *ap)
+{
+ struct hostent *hp;
+ uint32_t addr;
+ struct hnamemem *p;
+
+ memcpy(&addr, ap, sizeof(addr));
+ p = &hnametable[addr & (HASHNAMESIZE-1)];
+ for (; p->nxt; p = p->nxt) {
+ if (p->addr == addr)
+ return (p->name);
+ }
+ p->addr = addr;
+ p->nxt = newhnamemem(ndo);
+
+ /*
+ * Print names unless:
+ * (1) -n was given.
+ * (2) Address is foreign and -f was given. (If -f was not
+ * given, f_netmask and f_localnet are 0 and the test
+ * evaluates to true)
+ */
+ if (!ndo->ndo_nflag &&
+ (addr & f_netmask) == f_localnet) {
+#ifdef HAVE_CASPER
+ if (capdns != NULL) {
+ hp = cap_gethostbyaddr(capdns, (char *)&addr, 4,
+ AF_INET);
+ } else
+#endif
+ hp = gethostbyaddr((char *)&addr, 4, AF_INET);
+ if (hp) {
+ char *dotp;
+
+ p->name = strdup(hp->h_name);
+ if (p->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(hp->h_name)", __func__);
+ if (ndo->ndo_Nflag) {
+ /* Remove domain qualifications */
+ dotp = strchr(p->name, '.');
+ if (dotp)
+ *dotp = '\0';
+ }
+ return (p->name);
+ }
+ }
+ p->name = strdup(intoa(addr));
+ if (p->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(intoa(addr))", __func__);
+ return (p->name);
+}
+
+/*
+ * Return a name for the IP6 address pointed to by ap. This address
+ * is assumed to be in network byte order.
+ */
+const char *
+ip6addr_string(netdissect_options *ndo, const u_char *ap)
+{
+ struct hostent *hp;
+ union {
+ nd_ipv6 addr;
+ struct for_hash_addr {
+ char fill[14];
+ uint16_t d;
+ } addra;
+ } addr;
+ struct h6namemem *p;
+ const char *cp;
+ char ntop_buf[INET6_ADDRSTRLEN];
+
+ memcpy(&addr, ap, sizeof(addr));
+ p = &h6nametable[addr.addra.d & (HASHNAMESIZE-1)];
+ for (; p->nxt; p = p->nxt) {
+ if (memcmp(&p->addr, &addr, sizeof(addr)) == 0)
+ return (p->name);
+ }
+ memcpy(p->addr, addr.addr, sizeof(nd_ipv6));
+ p->nxt = newh6namemem(ndo);
+
+ /*
+ * Do not print names if -n was given.
+ */
+ if (!ndo->ndo_nflag) {
+#ifdef HAVE_CASPER
+ if (capdns != NULL) {
+ hp = cap_gethostbyaddr(capdns, (char *)&addr,
+ sizeof(addr), AF_INET6);
+ } else
+#endif
+ hp = gethostbyaddr((char *)&addr, sizeof(addr),
+ AF_INET6);
+ if (hp) {
+ char *dotp;
+
+ p->name = strdup(hp->h_name);
+ if (p->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(hp->h_name)", __func__);
+ if (ndo->ndo_Nflag) {
+ /* Remove domain qualifications */
+ dotp = strchr(p->name, '.');
+ if (dotp)
+ *dotp = '\0';
+ }
+ return (p->name);
+ }
+ }
+ cp = addrtostr6(ap, ntop_buf, sizeof(ntop_buf));
+ p->name = strdup(cp);
+ if (p->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(cp)", __func__);
+ return (p->name);
+}
+
+static const char hex[16] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
+};
+
+/*
+ * Convert an octet to two hex digits.
+ *
+ * Coverity appears either:
+ *
+ * not to believe the C standard when it asserts that a uint8_t is
+ * exactly 8 bits in size;
+ *
+ * not to believe that an unsigned type of exactly 8 bits has a value
+ * in the range of 0 to 255;
+ *
+ * not to believe that, for a range of unsigned values, if you shift
+ * one of those values right by 4 bits, the maximum result value is
+ * the maximum value shifted right by 4 bits, with no stray 1's shifted
+ * in;
+ *
+ * not to believe that 255 >> 4 is 15;
+ *
+ * so it gets upset that we're taking a "tainted" unsigned value, shifting
+ * it right 4 bits, and using it as an index into a 16-element array.
+ *
+ * So we do a stupid pointless masking of the result of the shift with
+ * 0xf, to hammer the point home to Coverity.
+ */
+static inline char *
+octet_to_hex(char *cp, uint8_t octet)
+{
+ *cp++ = hex[(octet >> 4) & 0xf];
+ *cp++ = hex[(octet >> 0) & 0xf];
+ return (cp);
+}
+
+/* Find the hash node that corresponds the ether address 'ep' */
+
+static struct enamemem *
+lookup_emem(netdissect_options *ndo, const u_char *ep)
+{
+ u_int i, j, k;
+ struct enamemem *tp;
+
+ k = (ep[0] << 8) | ep[1];
+ j = (ep[2] << 8) | ep[3];
+ i = (ep[4] << 8) | ep[5];
+
+ tp = &enametable[(i ^ j) & (HASHNAMESIZE-1)];
+ while (tp->e_nxt)
+ if (tp->e_addr0 == i &&
+ tp->e_addr1 == j &&
+ tp->e_addr2 == k)
+ return tp;
+ else
+ tp = tp->e_nxt;
+ tp->e_addr0 = (u_short)i;
+ tp->e_addr1 = (u_short)j;
+ tp->e_addr2 = (u_short)k;
+ tp->e_nxt = (struct enamemem *)calloc(1, sizeof(*tp));
+ if (tp->e_nxt == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "%s: calloc", __func__);
+
+ return tp;
+}
+
+/*
+ * Find the hash node that corresponds to the bytestring 'bs'
+ * with length 'nlen'
+ */
+
+static struct bsnamemem *
+lookup_bytestring(netdissect_options *ndo, const u_char *bs,
+ const unsigned int nlen)
+{
+ struct bsnamemem *tp;
+ u_int i, j, k;
+
+ if (nlen >= 6) {
+ k = (bs[0] << 8) | bs[1];
+ j = (bs[2] << 8) | bs[3];
+ i = (bs[4] << 8) | bs[5];
+ } else if (nlen >= 4) {
+ k = (bs[0] << 8) | bs[1];
+ j = (bs[2] << 8) | bs[3];
+ i = 0;
+ } else
+ i = j = k = 0;
+
+ tp = &bytestringtable[(i ^ j) & (HASHNAMESIZE-1)];
+ while (tp->bs_nxt)
+ if (nlen == tp->bs_nbytes &&
+ tp->bs_addr0 == i &&
+ tp->bs_addr1 == j &&
+ tp->bs_addr2 == k &&
+ memcmp((const char *)bs, (const char *)(tp->bs_bytes), nlen) == 0)
+ return tp;
+ else
+ tp = tp->bs_nxt;
+
+ tp->bs_addr0 = (u_short)i;
+ tp->bs_addr1 = (u_short)j;
+ tp->bs_addr2 = (u_short)k;
+
+ tp->bs_bytes = (u_char *) calloc(1, nlen);
+ if (tp->bs_bytes == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: calloc", __func__);
+
+ memcpy(tp->bs_bytes, bs, nlen);
+ tp->bs_nbytes = nlen;
+ tp->bs_nxt = (struct bsnamemem *)calloc(1, sizeof(*tp));
+ if (tp->bs_nxt == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: calloc", __func__);
+
+ return tp;
+}
+
+/* Find the hash node that corresponds the NSAP 'nsap' */
+
+static struct enamemem *
+lookup_nsap(netdissect_options *ndo, const u_char *nsap,
+ u_int nsap_length)
+{
+ u_int i, j, k;
+ struct enamemem *tp;
+ const u_char *ensap;
+
+ if (nsap_length > 6) {
+ ensap = nsap + nsap_length - 6;
+ k = (ensap[0] << 8) | ensap[1];
+ j = (ensap[2] << 8) | ensap[3];
+ i = (ensap[4] << 8) | ensap[5];
+ }
+ else
+ i = j = k = 0;
+
+ tp = &nsaptable[(i ^ j) & (HASHNAMESIZE-1)];
+ while (tp->e_nxt)
+ if (nsap_length == tp->e_nsap[0] &&
+ tp->e_addr0 == i &&
+ tp->e_addr1 == j &&
+ tp->e_addr2 == k &&
+ memcmp((const char *)nsap,
+ (char *)&(tp->e_nsap[1]), nsap_length) == 0)
+ return tp;
+ else
+ tp = tp->e_nxt;
+ tp->e_addr0 = (u_short)i;
+ tp->e_addr1 = (u_short)j;
+ tp->e_addr2 = (u_short)k;
+ tp->e_nsap = (u_char *)malloc(nsap_length + 1);
+ if (tp->e_nsap == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "%s: malloc", __func__);
+ tp->e_nsap[0] = (u_char)nsap_length; /* guaranteed < ISONSAP_MAX_LENGTH */
+ memcpy((char *)&tp->e_nsap[1], (const char *)nsap, nsap_length);
+ tp->e_nxt = (struct enamemem *)calloc(1, sizeof(*tp));
+ if (tp->e_nxt == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "%s: calloc", __func__);
+
+ return tp;
+}
+
+/* Find the hash node that corresponds the protoid 'pi'. */
+
+static struct protoidmem *
+lookup_protoid(netdissect_options *ndo, const u_char *pi)
+{
+ u_int i, j;
+ struct protoidmem *tp;
+
+ /* 5 octets won't be aligned */
+ i = (((pi[0] << 8) + pi[1]) << 8) + pi[2];
+ j = (pi[3] << 8) + pi[4];
+ /* XXX should be endian-insensitive, but do big-endian testing XXX */
+
+ tp = &protoidtable[(i ^ j) & (HASHNAMESIZE-1)];
+ while (tp->p_nxt)
+ if (tp->p_oui == i && tp->p_proto == j)
+ return tp;
+ else
+ tp = tp->p_nxt;
+ tp->p_oui = i;
+ tp->p_proto = (u_short)j;
+ tp->p_nxt = (struct protoidmem *)calloc(1, sizeof(*tp));
+ if (tp->p_nxt == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "%s: calloc", __func__);
+
+ return tp;
+}
+
+const char *
+etheraddr_string(netdissect_options *ndo, const uint8_t *ep)
+{
+ int i;
+ char *cp;
+ struct enamemem *tp;
+ int oui;
+ char buf[BUFSIZE];
+
+ tp = lookup_emem(ndo, ep);
+ if (tp->e_name)
+ return (tp->e_name);
+#ifdef USE_ETHER_NTOHOST
+ if (!ndo->ndo_nflag) {
+ char buf2[BUFSIZE];
+
+ if (ether_ntohost(buf2, (const struct ether_addr *)ep) == 0) {
+ tp->e_name = strdup(buf2);
+ if (tp->e_name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(buf2)", __func__);
+ return (tp->e_name);
+ }
+ }
+#endif
+ cp = buf;
+ oui = EXTRACT_BE_U_3(ep);
+ cp = octet_to_hex(cp, *ep++);
+ for (i = 5; --i >= 0;) {
+ *cp++ = ':';
+ cp = octet_to_hex(cp, *ep++);
+ }
+
+ if (!ndo->ndo_nflag) {
+ snprintf(cp, BUFSIZE - (2 + 5*3), " (oui %s)",
+ tok2str(oui_values, "Unknown", oui));
+ } else
+ *cp = '\0';
+ tp->e_name = strdup(buf);
+ if (tp->e_name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(buf)", __func__);
+ return (tp->e_name);
+}
+
+const char *
+le64addr_string(netdissect_options *ndo, const uint8_t *ep)
+{
+ const unsigned int len = 8;
+ u_int i;
+ char *cp;
+ struct bsnamemem *tp;
+ char buf[BUFSIZE];
+
+ tp = lookup_bytestring(ndo, ep, len);
+ if (tp->bs_name)
+ return (tp->bs_name);
+
+ cp = buf;
+ for (i = len; i > 0 ; --i) {
+ cp = octet_to_hex(cp, *(ep + i - 1));
+ *cp++ = ':';
+ }
+ cp --;
+
+ *cp = '\0';
+
+ tp->bs_name = strdup(buf);
+ if (tp->bs_name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(buf)", __func__);
+
+ return (tp->bs_name);
+}
+
+const char *
+linkaddr_string(netdissect_options *ndo, const uint8_t *ep,
+ const unsigned int type, const unsigned int len)
+{
+ u_int i;
+ char *cp;
+ struct bsnamemem *tp;
+
+ if (len == 0)
+ return ("<empty>");
+
+ if (type == LINKADDR_ETHER && len == MAC_ADDR_LEN)
+ return (etheraddr_string(ndo, ep));
+
+ if (type == LINKADDR_FRELAY)
+ return (q922_string(ndo, ep, len));
+
+ tp = lookup_bytestring(ndo, ep, len);
+ if (tp->bs_name)
+ return (tp->bs_name);
+
+ tp->bs_name = cp = (char *)malloc(len*3);
+ if (tp->bs_name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: malloc", __func__);
+ cp = octet_to_hex(cp, *ep++);
+ for (i = len-1; i > 0 ; --i) {
+ *cp++ = ':';
+ cp = octet_to_hex(cp, *ep++);
+ }
+ *cp = '\0';
+ return (tp->bs_name);
+}
+
+#define ISONSAP_MAX_LENGTH 20
+const char *
+isonsap_string(netdissect_options *ndo, const uint8_t *nsap,
+ u_int nsap_length)
+{
+ u_int nsap_idx;
+ char *cp;
+ struct enamemem *tp;
+
+ if (nsap_length < 1 || nsap_length > ISONSAP_MAX_LENGTH)
+ return ("isonsap_string: illegal length");
+
+ tp = lookup_nsap(ndo, nsap, nsap_length);
+ if (tp->e_name)
+ return tp->e_name;
+
+ tp->e_name = cp = (char *)malloc(sizeof("xx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xxxx.xx"));
+ if (cp == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: malloc", __func__);
+
+ for (nsap_idx = 0; nsap_idx < nsap_length; nsap_idx++) {
+ cp = octet_to_hex(cp, *nsap++);
+ if (((nsap_idx & 1) == 0) &&
+ (nsap_idx + 1 < nsap_length)) {
+ *cp++ = '.';
+ }
+ }
+ *cp = '\0';
+ return (tp->e_name);
+}
+
+const char *
+tcpport_string(netdissect_options *ndo, u_short port)
+{
+ struct hnamemem *tp;
+ uint32_t i = port;
+ char buf[sizeof("00000")];
+
+ for (tp = &tporttable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
+ if (tp->addr == i)
+ return (tp->name);
+
+ tp->addr = i;
+ tp->nxt = newhnamemem(ndo);
+
+ (void)snprintf(buf, sizeof(buf), "%u", i);
+ tp->name = strdup(buf);
+ if (tp->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(buf)", __func__);
+ return (tp->name);
+}
+
+const char *
+udpport_string(netdissect_options *ndo, u_short port)
+{
+ struct hnamemem *tp;
+ uint32_t i = port;
+ char buf[sizeof("00000")];
+
+ for (tp = &uporttable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
+ if (tp->addr == i)
+ return (tp->name);
+
+ tp->addr = i;
+ tp->nxt = newhnamemem(ndo);
+
+ (void)snprintf(buf, sizeof(buf), "%u", i);
+ tp->name = strdup(buf);
+ if (tp->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(buf)", __func__);
+ return (tp->name);
+}
+
+const char *
+ipxsap_string(netdissect_options *ndo, u_short port)
+{
+ char *cp;
+ struct hnamemem *tp;
+ uint32_t i = port;
+ char buf[sizeof("0000")];
+
+ for (tp = &ipxsaptable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
+ if (tp->addr == i)
+ return (tp->name);
+
+ tp->addr = i;
+ tp->nxt = newhnamemem(ndo);
+
+ cp = buf;
+ port = ntohs(port);
+ *cp++ = hex[port >> 12 & 0xf];
+ *cp++ = hex[port >> 8 & 0xf];
+ *cp++ = hex[port >> 4 & 0xf];
+ *cp++ = hex[port & 0xf];
+ *cp++ = '\0';
+ tp->name = strdup(buf);
+ if (tp->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(buf)", __func__);
+ return (tp->name);
+}
+
+static void
+init_servarray(netdissect_options *ndo)
+{
+ struct servent *sv;
+ struct hnamemem *table;
+ int i;
+ char buf[sizeof("0000000000")];
+
+ while ((sv = getservent()) != NULL) {
+ int port = ntohs(sv->s_port);
+ i = port & (HASHNAMESIZE-1);
+ if (strcmp(sv->s_proto, "tcp") == 0)
+ table = &tporttable[i];
+ else if (strcmp(sv->s_proto, "udp") == 0)
+ table = &uporttable[i];
+ else
+ continue;
+
+ while (table->name)
+ table = table->nxt;
+ if (ndo->ndo_nflag) {
+ (void)snprintf(buf, sizeof(buf), "%d", port);
+ table->name = strdup(buf);
+ } else
+ table->name = strdup(sv->s_name);
+ if (table->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup", __func__);
+
+ table->addr = port;
+ table->nxt = newhnamemem(ndo);
+ }
+ endservent();
+}
+
+static const struct eproto {
+ const char *s;
+ u_short p;
+} eproto_db[] = {
+ { "aarp", ETHERTYPE_AARP },
+ { "arp", ETHERTYPE_ARP },
+ { "atalk", ETHERTYPE_ATALK },
+ { "decnet", ETHERTYPE_DN },
+ { "ip", ETHERTYPE_IP },
+ { "ip6", ETHERTYPE_IPV6 },
+ { "lat", ETHERTYPE_LAT },
+ { "loopback", ETHERTYPE_LOOPBACK },
+ { "mopdl", ETHERTYPE_MOPDL },
+ { "moprc", ETHERTYPE_MOPRC },
+ { "rarp", ETHERTYPE_REVARP },
+ { "sca", ETHERTYPE_SCA },
+ { (char *)0, 0 }
+};
+
+static void
+init_eprotoarray(netdissect_options *ndo)
+{
+ int i;
+ struct hnamemem *table;
+
+ for (i = 0; eproto_db[i].s; i++) {
+ int j = htons(eproto_db[i].p) & (HASHNAMESIZE-1);
+ table = &eprototable[j];
+ while (table->name)
+ table = table->nxt;
+ table->name = eproto_db[i].s;
+ table->addr = htons(eproto_db[i].p);
+ table->nxt = newhnamemem(ndo);
+ }
+}
+
+static const struct protoidlist {
+ const u_char protoid[5];
+ const char *name;
+} protoidlist[] = {
+ {{ 0x00, 0x00, 0x0c, 0x01, 0x07 }, "CiscoMLS" },
+ {{ 0x00, 0x00, 0x0c, 0x20, 0x00 }, "CiscoCDP" },
+ {{ 0x00, 0x00, 0x0c, 0x20, 0x01 }, "CiscoCGMP" },
+ {{ 0x00, 0x00, 0x0c, 0x20, 0x03 }, "CiscoVTP" },
+ {{ 0x00, 0xe0, 0x2b, 0x00, 0xbb }, "ExtremeEDP" },
+ {{ 0x00, 0x00, 0x00, 0x00, 0x00 }, NULL }
+};
+
+/*
+ * SNAP proto IDs with org code 0:0:0 are actually encapsulated Ethernet
+ * types.
+ */
+static void
+init_protoidarray(netdissect_options *ndo)
+{
+ int i;
+ struct protoidmem *tp;
+ const struct protoidlist *pl;
+ u_char protoid[5];
+
+ protoid[0] = 0;
+ protoid[1] = 0;
+ protoid[2] = 0;
+ for (i = 0; eproto_db[i].s; i++) {
+ u_short etype = htons(eproto_db[i].p);
+
+ memcpy((char *)&protoid[3], (char *)&etype, 2);
+ tp = lookup_protoid(ndo, protoid);
+ tp->p_name = strdup(eproto_db[i].s);
+ if (tp->p_name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(eproto_db[i].s)", __func__);
+ }
+ /* Hardwire some SNAP proto ID names */
+ for (pl = protoidlist; pl->name != NULL; ++pl) {
+ tp = lookup_protoid(ndo, pl->protoid);
+ /* Don't override existing name */
+ if (tp->p_name != NULL)
+ continue;
+
+ tp->p_name = pl->name;
+ }
+}
+
+static const struct etherlist {
+ const nd_mac_addr addr;
+ const char *name;
+} etherlist[] = {
+ {{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, "Broadcast" },
+ {{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, NULL }
+};
+
+/*
+ * Initialize the ethers hash table. We take two different approaches
+ * depending on whether or not the system provides the ethers name
+ * service. If it does, we just wire in a few names at startup,
+ * and etheraddr_string() fills in the table on demand. If it doesn't,
+ * then we suck in the entire /etc/ethers file at startup. The idea
+ * is that parsing the local file will be fast, but spinning through
+ * all the ethers entries via NIS & next_etherent might be very slow.
+ *
+ * XXX pcap_next_etherent doesn't belong in the pcap interface, but
+ * since the pcap module already does name-to-address translation,
+ * it's already does most of the work for the ethernet address-to-name
+ * translation, so we just pcap_next_etherent as a convenience.
+ */
+static void
+init_etherarray(netdissect_options *ndo)
+{
+ const struct etherlist *el;
+ struct enamemem *tp;
+#ifdef USE_ETHER_NTOHOST
+ char name[256];
+#else
+ struct pcap_etherent *ep;
+ FILE *fp;
+
+ /* Suck in entire ethers file */
+ fp = fopen(PCAP_ETHERS_FILE, "r");
+ if (fp != NULL) {
+ while ((ep = pcap_next_etherent(fp)) != NULL) {
+ tp = lookup_emem(ndo, ep->addr);
+ tp->e_name = strdup(ep->name);
+ if (tp->e_name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(ep->addr)", __func__);
+ }
+ (void)fclose(fp);
+ }
+#endif
+
+ /* Hardwire some ethernet names */
+ for (el = etherlist; el->name != NULL; ++el) {
+ tp = lookup_emem(ndo, el->addr);
+ /* Don't override existing name */
+ if (tp->e_name != NULL)
+ continue;
+
+#ifdef USE_ETHER_NTOHOST
+ /*
+ * Use YP/NIS version of name if available.
+ */
+ if (ether_ntohost(name, (const struct ether_addr *)el->addr) == 0) {
+ tp->e_name = strdup(name);
+ if (tp->e_name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(name)", __func__);
+ continue;
+ }
+#endif
+ tp->e_name = el->name;
+ }
+}
+
+static const struct ipxsap_ent {
+ uint16_t v;
+ const char *s;
+} ipxsap_db[] = {
+ { 0x0000, "Unknown" },
+ { 0x0001, "User" },
+ { 0x0002, "User Group" },
+ { 0x0003, "PrintQueue" },
+ { 0x0004, "FileServer" },
+ { 0x0005, "JobServer" },
+ { 0x0006, "Gateway" },
+ { 0x0007, "PrintServer" },
+ { 0x0008, "ArchiveQueue" },
+ { 0x0009, "ArchiveServer" },
+ { 0x000a, "JobQueue" },
+ { 0x000b, "Administration" },
+ { 0x000F, "Novell TI-RPC" },
+ { 0x0017, "Diagnostics" },
+ { 0x0020, "NetBIOS" },
+ { 0x0021, "NAS SNA Gateway" },
+ { 0x0023, "NACS AsyncGateway" },
+ { 0x0024, "RemoteBridge/RoutingService" },
+ { 0x0026, "BridgeServer" },
+ { 0x0027, "TCP/IP Gateway" },
+ { 0x0028, "Point-to-point X.25 BridgeServer" },
+ { 0x0029, "3270 Gateway" },
+ { 0x002a, "CHI Corp" },
+ { 0x002c, "PC Chalkboard" },
+ { 0x002d, "TimeSynchServer" },
+ { 0x002e, "ARCserve5.0/PalindromeBackup" },
+ { 0x0045, "DI3270 Gateway" },
+ { 0x0047, "AdvertisingPrintServer" },
+ { 0x004a, "NetBlazerModems" },
+ { 0x004b, "BtrieveVAP" },
+ { 0x004c, "NetwareSQL" },
+ { 0x004d, "XtreeNetwork" },
+ { 0x0050, "BtrieveVAP4.11" },
+ { 0x0052, "QuickLink" },
+ { 0x0053, "PrintQueueUser" },
+ { 0x0058, "Multipoint X.25 Router" },
+ { 0x0060, "STLB/NLM" },
+ { 0x0064, "ARCserve" },
+ { 0x0066, "ARCserve3.0" },
+ { 0x0072, "WAN CopyUtility" },
+ { 0x007a, "TES-NetwareVMS" },
+ { 0x0092, "WATCOM Debugger/EmeraldTapeBackupServer" },
+ { 0x0095, "DDA OBGYN" },
+ { 0x0098, "NetwareAccessServer" },
+ { 0x009a, "Netware for VMS II/NamedPipeServer" },
+ { 0x009b, "NetwareAccessServer" },
+ { 0x009e, "PortableNetwareServer/SunLinkNVT" },
+ { 0x00a1, "PowerchuteAPC UPS" },
+ { 0x00aa, "LAWserve" },
+ { 0x00ac, "CompaqIDA StatusMonitor" },
+ { 0x0100, "PIPE STAIL" },
+ { 0x0102, "LAN ProtectBindery" },
+ { 0x0103, "OracleDataBaseServer" },
+ { 0x0107, "Netware386/RSPX RemoteConsole" },
+ { 0x010f, "NovellSNA Gateway" },
+ { 0x0111, "TestServer" },
+ { 0x0112, "HP PrintServer" },
+ { 0x0114, "CSA MUX" },
+ { 0x0115, "CSA LCA" },
+ { 0x0116, "CSA CM" },
+ { 0x0117, "CSA SMA" },
+ { 0x0118, "CSA DBA" },
+ { 0x0119, "CSA NMA" },
+ { 0x011a, "CSA SSA" },
+ { 0x011b, "CSA STATUS" },
+ { 0x011e, "CSA APPC" },
+ { 0x0126, "SNA TEST SSA Profile" },
+ { 0x012a, "CSA TRACE" },
+ { 0x012b, "NetwareSAA" },
+ { 0x012e, "IKARUS VirusScan" },
+ { 0x0130, "CommunicationsExecutive" },
+ { 0x0133, "NNS DomainServer/NetwareNamingServicesDomain" },
+ { 0x0135, "NetwareNamingServicesProfile" },
+ { 0x0137, "Netware386 PrintQueue/NNS PrintQueue" },
+ { 0x0141, "LAN SpoolServer" },
+ { 0x0152, "IRMALAN Gateway" },
+ { 0x0154, "NamedPipeServer" },
+ { 0x0166, "NetWareManagement" },
+ { 0x0168, "Intel PICKIT CommServer/Intel CAS TalkServer" },
+ { 0x0173, "Compaq" },
+ { 0x0174, "Compaq SNMP Agent" },
+ { 0x0175, "Compaq" },
+ { 0x0180, "XTreeServer/XTreeTools" },
+ { 0x018A, "NASI ServicesBroadcastServer" },
+ { 0x01b0, "GARP Gateway" },
+ { 0x01b1, "Binfview" },
+ { 0x01bf, "IntelLanDeskManager" },
+ { 0x01ca, "AXTEC" },
+ { 0x01cb, "ShivaNetModem/E" },
+ { 0x01cc, "ShivaLanRover/E" },
+ { 0x01cd, "ShivaLanRover/T" },
+ { 0x01ce, "ShivaUniversal" },
+ { 0x01d8, "CastelleFAXPressServer" },
+ { 0x01da, "CastelleLANPressPrintServer" },
+ { 0x01dc, "CastelleFAX/Xerox7033 FaxServer/ExcelLanFax" },
+ { 0x01f0, "LEGATO" },
+ { 0x01f5, "LEGATO" },
+ { 0x0233, "NMS Agent/NetwareManagementAgent" },
+ { 0x0237, "NMS IPX Discovery/LANternReadWriteChannel" },
+ { 0x0238, "NMS IP Discovery/LANternTrapAlarmChannel" },
+ { 0x023a, "LANtern" },
+ { 0x023c, "MAVERICK" },
+ { 0x023f, "NovellSMDR" },
+ { 0x024e, "NetwareConnect" },
+ { 0x024f, "NASI ServerBroadcast Cisco" },
+ { 0x026a, "NMS ServiceConsole" },
+ { 0x026b, "TimeSynchronizationServer Netware 4.x" },
+ { 0x0278, "DirectoryServer Netware 4.x" },
+ { 0x027b, "NetwareManagementAgent" },
+ { 0x0280, "Novell File and Printer Sharing Service for PC" },
+ { 0x0304, "NovellSAA Gateway" },
+ { 0x0308, "COM/VERMED" },
+ { 0x030a, "GalacticommWorldgroupServer" },
+ { 0x030c, "IntelNetport2/HP JetDirect/HP Quicksilver" },
+ { 0x0320, "AttachmateGateway" },
+ { 0x0327, "MicrosoftDiagnostiocs" },
+ { 0x0328, "WATCOM SQL Server" },
+ { 0x0335, "MultiTechSystems MultisynchCommServer" },
+ { 0x0343, "Xylogics RemoteAccessServer/LANModem" },
+ { 0x0355, "ArcadaBackupExec" },
+ { 0x0358, "MSLCD1" },
+ { 0x0361, "NETINELO" },
+ { 0x037e, "Powerchute UPS Monitoring" },
+ { 0x037f, "ViruSafeNotify" },
+ { 0x0386, "HP Bridge" },
+ { 0x0387, "HP Hub" },
+ { 0x0394, "NetWare SAA Gateway" },
+ { 0x039b, "LotusNotes" },
+ { 0x03b7, "CertusAntiVirus" },
+ { 0x03c4, "ARCserve4.0" },
+ { 0x03c7, "LANspool3.5" },
+ { 0x03d7, "LexmarkPrinterServer" },
+ { 0x03d8, "LexmarkXLE PrinterServer" },
+ { 0x03dd, "BanyanENS NetwareClient" },
+ { 0x03de, "GuptaSequelBaseServer/NetWareSQL" },
+ { 0x03e1, "UnivelUnixware" },
+ { 0x03e4, "UnivelUnixware" },
+ { 0x03fc, "IntelNetport" },
+ { 0x03fd, "PrintServerQueue" },
+ { 0x040A, "ipnServer" },
+ { 0x040D, "LVERRMAN" },
+ { 0x040E, "LVLIC" },
+ { 0x0414, "NET Silicon (DPI)/Kyocera" },
+ { 0x0429, "SiteLockVirus" },
+ { 0x0432, "UFHELPR???" },
+ { 0x0433, "Synoptics281xAdvancedSNMPAgent" },
+ { 0x0444, "MicrosoftNT SNA Server" },
+ { 0x0448, "Oracle" },
+ { 0x044c, "ARCserve5.01" },
+ { 0x0457, "CanonGP55" },
+ { 0x045a, "QMS Printers" },
+ { 0x045b, "DellSCSI Array" },
+ { 0x0491, "NetBlazerModems" },
+ { 0x04ac, "OnTimeScheduler" },
+ { 0x04b0, "CD-Net" },
+ { 0x0513, "EmulexNQA" },
+ { 0x0520, "SiteLockChecks" },
+ { 0x0529, "SiteLockChecks" },
+ { 0x052d, "CitrixOS2 AppServer" },
+ { 0x0535, "Tektronix" },
+ { 0x0536, "Milan" },
+ { 0x055d, "Attachmate SNA gateway" },
+ { 0x056b, "IBM8235 ModemServer" },
+ { 0x056c, "ShivaLanRover/E PLUS" },
+ { 0x056d, "ShivaLanRover/T PLUS" },
+ { 0x0580, "McAfeeNetShield" },
+ { 0x05B8, "NLM to workstation communication (Revelation Software)" },
+ { 0x05BA, "CompatibleSystemsRouters" },
+ { 0x05BE, "CheyenneHierarchicalStorageManager" },
+ { 0x0606, "JCWatermarkImaging" },
+ { 0x060c, "AXISNetworkPrinter" },
+ { 0x0610, "AdaptecSCSIManagement" },
+ { 0x0621, "IBM AntiVirus" },
+ { 0x0640, "Windows95 RemoteRegistryService" },
+ { 0x064e, "MicrosoftIIS" },
+ { 0x067b, "Microsoft Win95/98 File and Print Sharing for NetWare" },
+ { 0x067c, "Microsoft Win95/98 File and Print Sharing for NetWare" },
+ { 0x076C, "Xerox" },
+ { 0x079b, "ShivaLanRover/E 115" },
+ { 0x079c, "ShivaLanRover/T 115" },
+ { 0x07B4, "CubixWorldDesk" },
+ { 0x07c2, "Quarterdeck IWare Connect V2.x NLM" },
+ { 0x07c1, "Quarterdeck IWare Connect V3.x NLM" },
+ { 0x0810, "ELAN License Server Demo" },
+ { 0x0824, "ShivaLanRoverAccessSwitch/E" },
+ { 0x086a, "ISSC Collector" },
+ { 0x087f, "ISSC DAS AgentAIX" },
+ { 0x0880, "Intel Netport PRO" },
+ { 0x0881, "Intel Netport PRO" },
+ { 0x0b29, "SiteLock" },
+ { 0x0c29, "SiteLockApplications" },
+ { 0x0c2c, "LicensingServer" },
+ { 0x2101, "PerformanceTechnologyInstantInternet" },
+ { 0x2380, "LAI SiteLock" },
+ { 0x238c, "MeetingMaker" },
+ { 0x4808, "SiteLockServer/SiteLockMetering" },
+ { 0x5555, "SiteLockUser" },
+ { 0x6312, "Tapeware" },
+ { 0x6f00, "RabbitGateway" },
+ { 0x7703, "MODEM" },
+ { 0x8002, "NetPortPrinters" },
+ { 0x8008, "WordPerfectNetworkVersion" },
+ { 0x85BE, "Cisco EIGRP" },
+ { 0x8888, "WordPerfectNetworkVersion/QuickNetworkManagement" },
+ { 0x9000, "McAfeeNetShield" },
+ { 0x9604, "CSA-NT_MON" },
+ { 0xb6a8, "OceanIsleReachoutRemoteControl" },
+ { 0xf11f, "SiteLockMetering" },
+ { 0xf1ff, "SiteLock" },
+ { 0xf503, "Microsoft SQL Server" },
+ { 0xF905, "IBM TimeAndPlace" },
+ { 0xfbfb, "TopCallIII FaxServer" },
+ { 0xffff, "AnyService/Wildcard" },
+ { 0, (char *)0 }
+};
+
+static void
+init_ipxsaparray(netdissect_options *ndo)
+{
+ int i;
+ struct hnamemem *table;
+
+ for (i = 0; ipxsap_db[i].s != NULL; i++) {
+ u_int j = htons(ipxsap_db[i].v) & (HASHNAMESIZE-1);
+ table = &ipxsaptable[j];
+ while (table->name)
+ table = table->nxt;
+ table->name = ipxsap_db[i].s;
+ table->addr = htons(ipxsap_db[i].v);
+ table->nxt = newhnamemem(ndo);
+ }
+}
+
+/*
+ * Initialize the address to name translation machinery. We map all
+ * non-local IP addresses to numeric addresses if ndo->ndo_fflag is true
+ * (i.e., to prevent blocking on the nameserver). localnet is the IP address
+ * of the local network. mask is its subnet mask.
+ */
+void
+init_addrtoname(netdissect_options *ndo, uint32_t localnet, uint32_t mask)
+{
+ if (ndo->ndo_fflag) {
+ f_localnet = localnet;
+ f_netmask = mask;
+ }
+ if (ndo->ndo_nflag)
+ /*
+ * Simplest way to suppress names.
+ */
+ return;
+
+ init_etherarray(ndo);
+ init_servarray(ndo);
+ init_eprotoarray(ndo);
+ init_protoidarray(ndo);
+ init_ipxsaparray(ndo);
+}
+
+const char *
+dnaddr_string(netdissect_options *ndo, u_short dnaddr)
+{
+ struct hnamemem *tp;
+
+ for (tp = &dnaddrtable[dnaddr & (HASHNAMESIZE-1)]; tp->nxt != NULL;
+ tp = tp->nxt)
+ if (tp->addr == dnaddr)
+ return (tp->name);
+
+ tp->addr = dnaddr;
+ tp->nxt = newhnamemem(ndo);
+ tp->name = dnnum_string(ndo, dnaddr);
+
+ return(tp->name);
+}
+
+/* Return a zero'ed hnamemem struct and cuts down on calloc() overhead */
+struct hnamemem *
+newhnamemem(netdissect_options *ndo)
+{
+ struct hnamemem *p;
+ static struct hnamemem *ptr = NULL;
+ static u_int num = 0;
+
+ if (num <= 0) {
+ num = 64;
+ ptr = (struct hnamemem *)calloc(num, sizeof (*ptr));
+ if (ptr == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: calloc", __func__);
+ }
+ --num;
+ p = ptr++;
+ return (p);
+}
+
+/* Return a zero'ed h6namemem struct and cuts down on calloc() overhead */
+struct h6namemem *
+newh6namemem(netdissect_options *ndo)
+{
+ struct h6namemem *p;
+ static struct h6namemem *ptr = NULL;
+ static u_int num = 0;
+
+ if (num <= 0) {
+ num = 64;
+ ptr = (struct h6namemem *)calloc(num, sizeof (*ptr));
+ if (ptr == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: calloc", __func__);
+ }
+ --num;
+ p = ptr++;
+ return (p);
+}
+
+/* Represent TCI part of the 802.1Q 4-octet tag as text. */
+const char *
+ieee8021q_tci_string(const uint16_t tci)
+{
+ static char buf[128];
+ snprintf(buf, sizeof(buf), "vlan %u, p %u%s",
+ tci & 0xfff,
+ tci >> 13,
+ (tci & 0x1000) ? ", DEI" : "");
+ return buf;
+}
diff --git a/addrtoname.h b/addrtoname.h
new file mode 100644
index 0000000..94b41d7
--- /dev/null
+++ b/addrtoname.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1990, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "extract.h"
+
+/*
+ * Definition to let us compile most of the IPv6 code even on systems
+ * without IPv6 support.
+ */
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+/* Name to address translation routines. */
+
+enum {
+ LINKADDR_ETHER,
+ LINKADDR_FRELAY,
+ LINKADDR_IEEE1394,
+ LINKADDR_ATM,
+ LINKADDR_OTHER
+};
+
+#define BUFSIZE 128
+
+extern const char *linkaddr_string(netdissect_options *, const uint8_t *, const unsigned int, const unsigned int);
+extern const char *etheraddr_string(netdissect_options *, const uint8_t *);
+extern const char *le64addr_string(netdissect_options *, const uint8_t *);
+extern const char *tcpport_string(netdissect_options *, u_short);
+extern const char *udpport_string(netdissect_options *, u_short);
+extern const char *isonsap_string(netdissect_options *, const uint8_t *, u_int);
+extern const char *dnaddr_string(netdissect_options *, u_short);
+extern const char *ipxsap_string(netdissect_options *, u_short);
+extern const char *ipaddr_string(netdissect_options *, const u_char *);
+extern const char *ip6addr_string(netdissect_options *, const u_char *);
+extern const char *intoa(uint32_t);
+
+extern void init_addrtoname(netdissect_options *, uint32_t, uint32_t);
+extern struct hnamemem *newhnamemem(netdissect_options *);
+extern struct h6namemem *newh6namemem(netdissect_options *);
+extern const char * ieee8021q_tci_string(const uint16_t);
+
+/* macro(s) and inline function(s) with setjmp/longjmp logic to call
+ * the X_string() function(s) after bounds checking.
+ * The macro(s) must be used on a packet buffer pointer.
+ */
+
+static inline const char *
+get_linkaddr_string(netdissect_options *ndo, const uint8_t *p,
+ const unsigned int type, const unsigned int len)
+{
+ if (!ND_TTEST_LEN(p, len))
+ nd_trunc_longjmp(ndo);
+ return linkaddr_string(ndo, p, type, len);
+}
+
+static inline const char *
+get_etheraddr_string(netdissect_options *ndo, const uint8_t *p)
+{
+ if (!ND_TTEST_LEN(p, MAC_ADDR_LEN))
+ nd_trunc_longjmp(ndo);
+ return etheraddr_string(ndo, p);
+}
+
+static inline const char *
+get_le64addr_string(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_8(p))
+ nd_trunc_longjmp(ndo);
+ return le64addr_string(ndo, p);
+}
+
+static inline const char *
+get_isonsap_string(netdissect_options *ndo, const uint8_t *nsap,
+ u_int nsap_length)
+{
+ if (!ND_TTEST_LEN(nsap, nsap_length))
+ nd_trunc_longjmp(ndo);
+ return isonsap_string(ndo, nsap, nsap_length);
+}
+
+static inline const char *
+get_ipaddr_string(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return ipaddr_string(ndo, p);
+}
+
+static inline const char *
+get_ip6addr_string(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_16(p))
+ nd_trunc_longjmp(ndo);
+ return ip6addr_string(ndo, p);
+}
+
+#define GET_LINKADDR_STRING(p, type, len) get_linkaddr_string(ndo, (const u_char *)(p), type, len)
+#define GET_ETHERADDR_STRING(p) get_etheraddr_string(ndo, (const u_char *)(p))
+#define GET_LE64ADDR_STRING(p) get_le64addr_string(ndo, (const u_char *)(p))
+#define GET_ISONSAP_STRING(nsap, nsap_length) get_isonsap_string(ndo, (const u_char *)(nsap), nsap_length)
+#define GET_IPADDR_STRING(p) get_ipaddr_string(ndo, (const u_char *)(p))
+#define GET_IP6ADDR_STRING(p) get_ip6addr_string(ndo, (const u_char *)(p))
diff --git a/addrtostr.c b/addrtostr.c
new file mode 100644
index 0000000..62cc298
--- /dev/null
+++ b/addrtostr.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Kungliga Tekniska
+ * Högskolan and its contributors.
+ *
+ * 4. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "addrtostr.h"
+
+#include <stdio.h>
+#include <string.h>
+
+/*
+ *
+ */
+
+#ifndef IN6ADDRSZ
+#define IN6ADDRSZ 16 /* IPv6 T_AAAA */
+#endif
+
+#ifndef INT16SZ
+#define INT16SZ 2 /* word size */
+#endif
+
+const char *
+addrtostr (const void *src, char *dst, size_t size)
+{
+ const u_char *srcaddr = (const u_char *)src;
+ const char digits[] = "0123456789";
+ int i;
+ const char *orig_dst = dst;
+
+ if (size < INET_ADDRSTRLEN) {
+ errno = ENOSPC;
+ return NULL;
+ }
+ for (i = 0; i < 4; ++i) {
+ int n = *srcaddr++;
+ int non_zerop = 0;
+
+ if (non_zerop || n / 100 > 0) {
+ *dst++ = digits[n / 100];
+ n %= 100;
+ non_zerop = 1;
+ }
+ if (non_zerop || n / 10 > 0) {
+ *dst++ = digits[n / 10];
+ n %= 10;
+ non_zerop = 1;
+ }
+ *dst++ = digits[n];
+ if (i != 3)
+ *dst++ = '.';
+ }
+ *dst++ = '\0';
+ return orig_dst;
+}
+
+/*
+ * Convert IPv6 binary address into presentation (printable) format.
+ */
+const char *
+addrtostr6 (const void *src, char *dst, size_t size)
+{
+ /*
+ * Note that int32_t and int16_t need only be "at least" large enough
+ * to contain a value of the specified size. On some systems, like
+ * Crays, there is no such thing as an integer variable with 16 bits.
+ * Keep this in mind if you think this function should have been coded
+ * to use pointer overlays. All the world's not a VAX.
+ */
+ const u_char *srcaddr = (const u_char *)src;
+ char *dp;
+ size_t space_left, added_space;
+ int snprintfed;
+ struct {
+ int base;
+ int len;
+ } best, cur;
+ uint16_t words [IN6ADDRSZ / INT16SZ];
+ int i;
+
+ /* Preprocess:
+ * Copy the input (bytewise) array into a wordwise array.
+ * Find the longest run of 0x00's in src[] for :: shorthanding.
+ */
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ words[i] = (srcaddr[2*i] << 8) | srcaddr[2*i + 1];
+
+ best.len = 0;
+ best.base = -1;
+ cur.len = 0;
+ cur.base = -1;
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ {
+ if (words[i] == 0)
+ {
+ if (cur.base == -1)
+ cur.base = i, cur.len = 1;
+ else cur.len++;
+ }
+ else if (cur.base != -1)
+ {
+ if (best.base == -1 || cur.len > best.len)
+ best = cur;
+ cur.base = -1;
+ }
+ }
+ if ((cur.base != -1) && (best.base == -1 || cur.len > best.len))
+ best = cur;
+ if (best.base != -1 && best.len < 2)
+ best.base = -1;
+
+ /* Format the result.
+ */
+ dp = dst;
+ space_left = size;
+#define APPEND_CHAR(c) \
+ { \
+ if (space_left == 0) { \
+ errno = ENOSPC; \
+ return (NULL); \
+ } \
+ *dp++ = c; \
+ space_left--; \
+ }
+ for (i = 0; i < (IN6ADDRSZ / INT16SZ); i++)
+ {
+ /* Are we inside the best run of 0x00's?
+ */
+ if (best.base != -1 && i >= best.base && i < (best.base + best.len))
+ {
+ if (i == best.base)
+ APPEND_CHAR(':');
+ continue;
+ }
+
+ /* Are we following an initial run of 0x00s or any real hex?
+ */
+ if (i != 0)
+ APPEND_CHAR(':');
+
+ /* Is this address an encapsulated IPv4?
+ */
+ if (i == 6 && best.base == 0 &&
+ (best.len == 6 || (best.len == 5 && words[5] == 0xffff)))
+ {
+ if (!addrtostr(srcaddr+12, dp, space_left))
+ {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ added_space = strlen(dp);
+ dp += added_space;
+ space_left -= added_space;
+ break;
+ }
+ snprintfed = snprintf (dp, space_left, "%x", words[i]);
+ if (snprintfed < 0)
+ return (NULL);
+ if ((size_t) snprintfed >= space_left)
+ {
+ errno = ENOSPC;
+ return (NULL);
+ }
+ dp += snprintfed;
+ space_left -= snprintfed;
+ }
+
+ /* Was it a trailing run of 0x00's?
+ */
+ if (best.base != -1 && (best.base + best.len) == (IN6ADDRSZ / INT16SZ))
+ APPEND_CHAR(':');
+ APPEND_CHAR('\0');
+
+ return (dst);
+}
diff --git a/addrtostr.h b/addrtostr.h
new file mode 100644
index 0000000..2b95a16
--- /dev/null
+++ b/addrtostr.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1999 Kungliga Tekniska Högskolan
+ * (Royal Institute of Technology, Stockholm, Sweden).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Kungliga Tekniska
+ * Högskolan and its contributors.
+ *
+ * 4. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* Address to printable string translation routines. */
+
+extern const char *addrtostr(const void *src, char *dst, size_t size);
+extern const char *addrtostr6(const void *src, char *dst, size_t size);
diff --git a/af.c b/af.c
new file mode 100644
index 0000000..1153f10
--- /dev/null
+++ b/af.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1998-2006 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include "af.h"
+
+const struct tok af_values[] = {
+ { 0, "Reserved"},
+ { AFNUM_INET, "IPv4"},
+ { AFNUM_INET6, "IPv6"},
+ { AFNUM_NSAP, "NSAP"},
+ { AFNUM_HDLC, "HDLC"},
+ { AFNUM_BBN1822, "BBN 1822"},
+ { AFNUM_802, "802"},
+ { AFNUM_E163, "E.163"},
+ { AFNUM_E164, "E.164"},
+ { AFNUM_F69, "F.69"},
+ { AFNUM_X121, "X.121"},
+ { AFNUM_IPX, "Novell IPX"},
+ { AFNUM_ATALK, "Appletalk"},
+ { AFNUM_DECNET, "Decnet IV"},
+ { AFNUM_BANYAN, "Banyan Vines"},
+ { AFNUM_E164NSAP, "E.164 with NSAP subaddress"},
+ { AFNUM_L2VPN, "Layer-2 VPN"},
+ { AFNUM_VPLS, "VPLS"},
+ { 0, NULL},
+};
+
+const struct tok bsd_af_values[] = {
+ { BSD_AFNUM_INET, "IPv4" },
+ { BSD_AFNUM_NS, "NS" },
+ { BSD_AFNUM_ISO, "ISO" },
+ { BSD_AFNUM_APPLETALK, "Appletalk" },
+ { BSD_AFNUM_IPX, "IPX" },
+ { BSD_AFNUM_INET6_BSD, "IPv6" },
+ { BSD_AFNUM_INET6_FREEBSD, "IPv6" },
+ { BSD_AFNUM_INET6_DARWIN, "IPv6" },
+ { 0, NULL}
+};
diff --git a/af.h b/af.h
new file mode 100644
index 0000000..b9fec8e
--- /dev/null
+++ b/af.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1998-2006 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+extern const struct tok af_values[];
+extern const struct tok bsd_af_values[];
+
+/* RFC1700 address family numbers */
+#define AFNUM_INET 1
+#define AFNUM_INET6 2
+#define AFNUM_NSAP 3
+#define AFNUM_HDLC 4
+#define AFNUM_BBN1822 5
+#define AFNUM_802 6
+#define AFNUM_E163 7
+#define AFNUM_E164 8
+#define AFNUM_F69 9
+#define AFNUM_X121 10
+#define AFNUM_IPX 11
+#define AFNUM_ATALK 12
+#define AFNUM_DECNET 13
+#define AFNUM_BANYAN 14
+#define AFNUM_E164NSAP 15
+#define AFNUM_VPLS 25
+/* draft-kompella-ppvpn-l2vpn */
+#define AFNUM_L2VPN 196 /* still to be approved by IANA */
+
+/*
+ * BSD AF_ values.
+ *
+ * Unfortunately, the BSDs don't all use the same value for AF_INET6,
+ * so, because we want to be able to read captures from all of the BSDs,
+ * we check for all of them.
+ */
+#define BSD_AFNUM_INET 2
+#define BSD_AFNUM_NS 6 /* XEROX NS protocols */
+#define BSD_AFNUM_ISO 7
+#define BSD_AFNUM_APPLETALK 16
+#define BSD_AFNUM_IPX 23
+#define BSD_AFNUM_INET6_BSD 24 /* NetBSD, OpenBSD, BSD/OS, Npcap */
+#define BSD_AFNUM_INET6_FREEBSD 28 /* FreeBSD */
+#define BSD_AFNUM_INET6_DARWIN 30 /* macOS, iOS, other Darwin-based OSes */
diff --git a/ah.h b/ah.h
new file mode 100644
index 0000000..c60c563
--- /dev/null
+++ b/ah.h
@@ -0,0 +1,67 @@
+/* $NetBSD: ah.h,v 1.12 2000/07/23 05:23:04 itojun Exp $ */
+/* $KAME: ah.h,v 1.12 2000/07/20 17:41:01 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC4302 authentication header.
+ */
+
+#ifndef ND_AH_H_
+#define ND_AH_H_
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Next Header | Payload Len | RESERVED |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Security Parameters Index (SPI) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number Field |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + Integrity Check Value-ICV (variable) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Figure 1. AH Format
+ */
+
+struct ah {
+ nd_uint8_t ah_nxt; /* Next Header */
+ nd_uint8_t ah_len; /* Payload Len in 32bit words minus 2 */
+ nd_uint16_t ah_reserved; /* Reserved for future use */
+ nd_uint32_t ah_spi; /* Security Parameters Index */
+ nd_uint32_t ah_seq; /* Sequence Number Field */
+ /* variable size, 32bit bound*/ /* Integrity Check Value-ICV */
+};
+
+#endif /* ND_AH_H_ */
diff --git a/appletalk.h b/appletalk.h
new file mode 100644
index 0000000..ef4013f
--- /dev/null
+++ b/appletalk.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * AppleTalk protocol formats (courtesy Bill Croft of Stanford/SUMEX).
+ */
+
+struct LAP {
+ nd_uint8_t dst;
+ nd_uint8_t src;
+ nd_uint8_t type;
+};
+#define lapShortDDP 1 /* short DDP type */
+#define lapDDP 2 /* DDP type */
+#define lapKLAP 'K' /* Kinetics KLAP type */
+
+/* Datagram Delivery Protocol */
+
+struct atDDP {
+ nd_uint16_t length;
+ nd_uint16_t checksum;
+ nd_uint16_t dstNet;
+ nd_uint16_t srcNet;
+ nd_uint8_t dstNode;
+ nd_uint8_t srcNode;
+ nd_uint8_t dstSkt;
+ nd_uint8_t srcSkt;
+ nd_uint8_t type;
+};
+
+struct atShortDDP {
+ nd_uint16_t length;
+ nd_uint8_t dstSkt;
+ nd_uint8_t srcSkt;
+ nd_uint8_t type;
+};
+
+#define ddpMaxWKS 0x7F
+#define ddpMaxData 586
+#define ddpLengthMask 0x3FF
+#define ddpHopShift 10
+#define ddpSize 13 /* size of DDP header (avoid struct padding) */
+#define ddpSSize 5
+#define ddpWKS 128 /* boundary of DDP well known sockets */
+#define ddpRTMP 1 /* RTMP type */
+#define ddpRTMPrequest 5 /* RTMP request type */
+#define ddpNBP 2 /* NBP type */
+#define ddpATP 3 /* ATP type */
+#define ddpECHO 4 /* ECHO type */
+#define ddpIP 22 /* IP type */
+#define ddpARP 23 /* ARP type */
+#define ddpEIGRP 88 /* EIGRP over Appletalk */
+#define ddpKLAP 0x4b /* Kinetics KLAP type */
+
+
+/* AppleTalk Transaction Protocol */
+
+struct atATP {
+ nd_uint8_t control;
+ nd_uint8_t bitmap;
+ nd_uint16_t transID;
+ nd_uint32_t userData;
+};
+
+#define atpReqCode 0x40
+#define atpRspCode 0x80
+#define atpRelCode 0xC0
+#define atpXO 0x20
+#define atpEOM 0x10
+#define atpSTS 0x08
+#define atpFlagMask 0x3F
+#define atpControlMask 0xF8
+#define atpMaxNum 8
+#define atpMaxData 578
+
+
+/* AppleTalk Echo Protocol */
+
+struct atEcho {
+ nd_uint8_t echoFunction;
+ nd_uint8_t echoData[1]; /* Should be [], C99-style */
+};
+
+#define echoSkt 4 /* the echoer socket */
+#define echoSize 1 /* size of echo header */
+#define echoRequest 1 /* echo request */
+#define echoReply 2 /* echo request */
+
+
+/* Name Binding Protocol */
+
+struct atNBP {
+ nd_uint8_t control;
+ nd_uint8_t id;
+};
+
+struct atNBPtuple {
+ nd_uint16_t net;
+ nd_uint8_t node;
+ nd_uint8_t skt;
+ nd_uint8_t enumerator;
+};
+
+#define nbpBrRq 0x10
+#define nbpLkUp 0x20
+#define nbpLkUpReply 0x30
+
+#define nbpNIS 2
+#define nbpTupleMax 15
+
+#define nbpHeaderSize 2
+#define nbpTupleSize 5
+
+#define nbpSkt 2 /* NIS */
+
+
+/* Routing Table Maint. Protocol */
+
+#define rtmpSkt 1 /* number of RTMP socket */
+#define rtmpSize 4 /* minimum size */
+#define rtmpTupleSize 3
+
+
+/* Zone Information Protocol */
+
+struct zipHeader {
+ nd_uint8_t command;
+ nd_uint8_t netcount;
+};
+
+#define zipHeaderSize 2
+#define zipQuery 1
+#define zipReply 2
+#define zipTakedown 3
+#define zipBringup 4
+#define ddpZIP 6
+#define zipSkt 6
+#define GetMyZone 7
+#define GetZoneList 8
+
+/*
+ * UDP port range used for ddp-in-udp encapsulation is 16512-16639
+ * for client sockets (128-255) and 200-327 for server sockets
+ * (0-127). We also try to recognize the pre-April 88 server
+ * socket range of 768-895.
+ */
+#define atalk_port(p) \
+ (((unsigned)((p) - 16512) < 128) || \
+ ((unsigned)((p) - 200) < 128) || \
+ ((unsigned)((p) - 768) < 128))
diff --git a/ascii_strcasecmp.c b/ascii_strcasecmp.c
new file mode 100644
index 0000000..090f758
--- /dev/null
+++ b/ascii_strcasecmp.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1987 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific written prior permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+#include "ascii_strcasecmp.h"
+
+/*
+ * This array maps upper-case ASCII letters to their lower-case
+ * equivalents; all other byte values are mapped to themselves,
+ * so this is locale-independent and intended to be locale-independent,
+ * to avoid issues with, for example, "i" and "I" not being lower-case
+ * and upper-case versions of the same letter in Turkish, where
+ * there are separate "i with dot" and "i without dot" letters.
+ */
+static const unsigned char charmap[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+ 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
+};
+
+int
+ascii_strcasecmp(const char *s1, const char *s2)
+{
+ const unsigned char *cm = charmap,
+ *us1 = (const unsigned char *)s1,
+ *us2 = (const unsigned char *)s2;
+
+ while (cm[*us1] == cm[*us2++])
+ if (*us1++ == '\0')
+ return(0);
+ return(cm[*us1] - cm[*--us2]);
+}
+
+int
+ascii_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+ const unsigned char *cm = charmap,
+ *us1 = (const unsigned char *)s1,
+ *us2 = (const unsigned char *)s2;
+
+ for (;;) {
+ if (n == 0) {
+ /*
+ * We've run out of characters that we should
+ * compare, and they've all been equal; return
+ * 0, to indicate that the prefixes are the
+ * same.
+ */
+ return(0);
+ }
+ if (cm[*us1] != cm[*us2++]) {
+ /*
+ * We've found a mismatch.
+ */
+ break;
+ }
+ if (*us1++ == '\0') {
+ /*
+ * We've run out of characters *to* compare,
+ * and they've all been equal; return 0, to
+ * indicate that the strings are the same.
+ */
+ return(0);
+ }
+ n--;
+ }
+ return(cm[*us1] - cm[*--us2]);
+}
diff --git a/ascii_strcasecmp.h b/ascii_strcasecmp.h
new file mode 100644
index 0000000..7f8ddb9
--- /dev/null
+++ b/ascii_strcasecmp.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 1988-1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1998-2012 Michael Richardson <mcr@tcpdump.org>
+ * The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef netdissect_ascii_strcasecmp_h
+#define netdissect_ascii_strcasecmp_h
+
+#include <stddef.h>
+
+extern int ascii_strcasecmp(const char *, const char *);
+extern int ascii_strncasecmp(const char *, const char *, size_t);
+
+#endif /* netdissect_ascii_strcasecmp_h */
diff --git a/atm.h b/atm.h
new file mode 100644
index 0000000..8d1737e
--- /dev/null
+++ b/atm.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2002 Guy Harris.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * The name of Guy Harris may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Traffic types for ATM.
+ */
+#define ATM_UNKNOWN 0 /* Unknown */
+#define ATM_LANE 1 /* LANE */
+#define ATM_LLC 2 /* LLC encapsulation */
+
+/*
+ * some OAM cell captures (most notably Juniper's)
+ * do not deliver a heading HEC byte
+ */
+#define ATM_OAM_NOHEC 0
+#define ATM_OAM_HEC 1
+#define ATM_HDR_LEN_NOHEC 4
diff --git a/bpf_dump.c b/bpf_dump.c
new file mode 100644
index 0000000..7cfe49a
--- /dev/null
+++ b/bpf_dump.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+
+#include "netdissect.h"
+
+void
+bpf_dump(const struct bpf_program *p, int option)
+{
+ struct bpf_insn *insn;
+ int i;
+ int n = p->bf_len;
+
+ insn = p->bf_insns;
+ if (option > 2) {
+ printf("%d\n", n);
+ for (i = 0; i < n; ++insn, ++i) {
+ printf("%u %u %u %u\n", insn->code,
+ insn->jt, insn->jf, insn->k);
+ }
+ return ;
+ }
+ if (option > 1) {
+ for (i = 0; i < n; ++insn, ++i)
+ printf("{ 0x%x, %d, %d, 0x%08x },\n",
+ insn->code, insn->jt, insn->jf, insn->k);
+ return;
+ }
+ for (i = 0; i < n; ++insn, ++i) {
+#ifdef BDEBUG
+ extern int bids[];
+ printf(bids[i] > 0 ? "[%02d]" : " -- ", bids[i] - 1);
+#endif
+ puts(bpf_image(insn, i));
+ }
+}
diff --git a/chdlc.h b/chdlc.h
new file mode 100644
index 0000000..d5a2d91
--- /dev/null
+++ b/chdlc.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#define CHDLC_HDRLEN 4
+#define CHDLC_UNICAST 0x0f
+#define CHDLC_BCAST 0x8f
+#define CHDLC_TYPE_SLARP 0x8035
+#define CHDLC_TYPE_CDP 0x2000
diff --git a/checksum.c b/checksum.c
new file mode 100644
index 0000000..e6e84a2
--- /dev/null
+++ b/checksum.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (c) 1998-2006 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * miscellaneous checksumming routines
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "netdissect.h"
+
+/*
+ * CRC-10 table generated using the following Python snippet:
+
+import sys
+
+crc_table = []
+for i in range(256):
+ accum = i << 2
+ for j in range(8):
+ accum <<= 1
+ if accum & 0x400:
+ accum ^= 0x633
+ crc_table.append(accum)
+
+for i in range(len(crc_table)/8):
+ for j in range(8):
+ sys.stdout.write("0x%04x, " % crc_table[i*8+j])
+ sys.stdout.write("\n")
+
+ */
+static const uint16_t crc10_table[256] =
+{
+ 0x0000, 0x0233, 0x0255, 0x0066, 0x0299, 0x00aa, 0x00cc, 0x02ff,
+ 0x0301, 0x0132, 0x0154, 0x0367, 0x0198, 0x03ab, 0x03cd, 0x01fe,
+ 0x0031, 0x0202, 0x0264, 0x0057, 0x02a8, 0x009b, 0x00fd, 0x02ce,
+ 0x0330, 0x0103, 0x0165, 0x0356, 0x01a9, 0x039a, 0x03fc, 0x01cf,
+ 0x0062, 0x0251, 0x0237, 0x0004, 0x02fb, 0x00c8, 0x00ae, 0x029d,
+ 0x0363, 0x0150, 0x0136, 0x0305, 0x01fa, 0x03c9, 0x03af, 0x019c,
+ 0x0053, 0x0260, 0x0206, 0x0035, 0x02ca, 0x00f9, 0x009f, 0x02ac,
+ 0x0352, 0x0161, 0x0107, 0x0334, 0x01cb, 0x03f8, 0x039e, 0x01ad,
+ 0x00c4, 0x02f7, 0x0291, 0x00a2, 0x025d, 0x006e, 0x0008, 0x023b,
+ 0x03c5, 0x01f6, 0x0190, 0x03a3, 0x015c, 0x036f, 0x0309, 0x013a,
+ 0x00f5, 0x02c6, 0x02a0, 0x0093, 0x026c, 0x005f, 0x0039, 0x020a,
+ 0x03f4, 0x01c7, 0x01a1, 0x0392, 0x016d, 0x035e, 0x0338, 0x010b,
+ 0x00a6, 0x0295, 0x02f3, 0x00c0, 0x023f, 0x000c, 0x006a, 0x0259,
+ 0x03a7, 0x0194, 0x01f2, 0x03c1, 0x013e, 0x030d, 0x036b, 0x0158,
+ 0x0097, 0x02a4, 0x02c2, 0x00f1, 0x020e, 0x003d, 0x005b, 0x0268,
+ 0x0396, 0x01a5, 0x01c3, 0x03f0, 0x010f, 0x033c, 0x035a, 0x0169,
+ 0x0188, 0x03bb, 0x03dd, 0x01ee, 0x0311, 0x0122, 0x0144, 0x0377,
+ 0x0289, 0x00ba, 0x00dc, 0x02ef, 0x0010, 0x0223, 0x0245, 0x0076,
+ 0x01b9, 0x038a, 0x03ec, 0x01df, 0x0320, 0x0113, 0x0175, 0x0346,
+ 0x02b8, 0x008b, 0x00ed, 0x02de, 0x0021, 0x0212, 0x0274, 0x0047,
+ 0x01ea, 0x03d9, 0x03bf, 0x018c, 0x0373, 0x0140, 0x0126, 0x0315,
+ 0x02eb, 0x00d8, 0x00be, 0x028d, 0x0072, 0x0241, 0x0227, 0x0014,
+ 0x01db, 0x03e8, 0x038e, 0x01bd, 0x0342, 0x0171, 0x0117, 0x0324,
+ 0x02da, 0x00e9, 0x008f, 0x02bc, 0x0043, 0x0270, 0x0216, 0x0025,
+ 0x014c, 0x037f, 0x0319, 0x012a, 0x03d5, 0x01e6, 0x0180, 0x03b3,
+ 0x024d, 0x007e, 0x0018, 0x022b, 0x00d4, 0x02e7, 0x0281, 0x00b2,
+ 0x017d, 0x034e, 0x0328, 0x011b, 0x03e4, 0x01d7, 0x01b1, 0x0382,
+ 0x027c, 0x004f, 0x0029, 0x021a, 0x00e5, 0x02d6, 0x02b0, 0x0083,
+ 0x012e, 0x031d, 0x037b, 0x0148, 0x03b7, 0x0184, 0x01e2, 0x03d1,
+ 0x022f, 0x001c, 0x007a, 0x0249, 0x00b6, 0x0285, 0x02e3, 0x00d0,
+ 0x011f, 0x032c, 0x034a, 0x0179, 0x0386, 0x01b5, 0x01d3, 0x03e0,
+ 0x021e, 0x002d, 0x004b, 0x0278, 0x0087, 0x02b4, 0x02d2, 0x00e1
+};
+
+static void
+init_crc10_table(void)
+{
+#define CRC10_POLYNOMIAL 0x633
+ int i, j;
+ uint16_t accum;
+ uint16_t verify_crc10_table[256];
+
+ for ( i = 0; i < 256; i++ )
+ {
+ accum = ((unsigned short) i << 2);
+ for ( j = 0; j < 8; j++ )
+ {
+ if ((accum <<= 1) & 0x400) accum ^= CRC10_POLYNOMIAL;
+ }
+ verify_crc10_table[i] = accum;
+ }
+ assert(memcmp(verify_crc10_table,
+ crc10_table,
+ sizeof(verify_crc10_table)) == 0);
+#undef CRC10_POLYNOMIAL
+}
+
+uint16_t
+verify_crc10_cksum(uint16_t accum, const u_char *p, int length)
+{
+ int i;
+
+ for ( i = 0; i < length; i++ )
+ {
+ accum = ((accum << 8) & 0x3ff)
+ ^ crc10_table[( accum >> 2) & 0xff]
+ ^ *p++;
+ }
+ return accum;
+}
+
+/* precompute checksum tables */
+void
+init_checksum(void) {
+
+ init_crc10_table();
+
+}
+
+/*
+ * Creates the OSI Fletcher checksum. See 8473-1, Appendix C, section C.3.
+ * The checksum field of the passed PDU does not need to be reset to zero.
+ */
+uint16_t
+create_osi_cksum (const uint8_t *pptr, int checksum_offset, int length)
+{
+
+ int x;
+ int y;
+ uint32_t mul;
+ uint32_t c0;
+ uint32_t c1;
+ uint16_t checksum;
+ int idx;
+
+ c0 = 0;
+ c1 = 0;
+
+ for (idx = 0; idx < length; idx++) {
+ /*
+ * Ignore the contents of the checksum field.
+ */
+ if (idx == checksum_offset ||
+ idx == checksum_offset+1) {
+ c1 += c0;
+ pptr++;
+ } else {
+ c0 = c0 + *(pptr++);
+ c1 += c0;
+ }
+ }
+
+ c0 = c0 % 255;
+ c1 = c1 % 255;
+
+ mul = (length - checksum_offset)*(c0);
+
+ x = mul - c0 - c1;
+ y = c1 - mul - 1;
+
+ if ( y >= 0 ) y++;
+ if ( x < 0 ) x--;
+
+ x %= 255;
+ y %= 255;
+
+
+ if (x == 0) x = 255;
+ if (y == 0) y = 255;
+
+ y &= 0x00FF;
+ checksum = ((x << 8) | y);
+
+ return checksum;
+}
diff --git a/compiler-tests.h b/compiler-tests.h
new file mode 100644
index 0000000..4793b71
--- /dev/null
+++ b/compiler-tests.h
@@ -0,0 +1,151 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
+/*
+ * Copyright (c) 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef nd_compiler_tests_h
+#define nd_compiler_tests_h
+
+/*
+ * This was introduced by Clang:
+ *
+ * https://clang.llvm.org/docs/LanguageExtensions.html#has-attribute
+ *
+ * in some version (which version?); it has been picked up by GCC 5.0.
+ */
+#ifndef __has_attribute
+ /*
+ * It's a macro, so you can check whether it's defined to check
+ * whether it's supported.
+ *
+ * If it's not, define it to always return 0, so that we move on to
+ * the fallback checks.
+ */
+ #define __has_attribute(x) 0
+#endif
+
+/*
+ * Note that the C90 spec's "6.8.1 Conditional inclusion" and the
+ * C99 spec's and C11 spec's "6.10.1 Conditional inclusion" say:
+ *
+ * Prior to evaluation, macro invocations in the list of preprocessing
+ * tokens that will become the controlling constant expression are
+ * replaced (except for those macro names modified by the defined unary
+ * operator), just as in normal text. If the token "defined" is
+ * generated as a result of this replacement process or use of the
+ * "defined" unary operator does not match one of the two specified
+ * forms prior to macro replacement, the behavior is undefined.
+ *
+ * so you shouldn't use defined() in a #define that's used in #if or
+ * #elif. Some versions of Clang, for example, will warn about this.
+ *
+ * Instead, we check whether the pre-defined macros for particular
+ * compilers are defined and, if not, define the "is this version XXX
+ * or a later version of this compiler" macros as 0.
+ */
+
+/*
+ * Check whether this is GCC major.minor or a later release, or some
+ * compiler that claims to be "just like GCC" of that version or a
+ * later release.
+ */
+
+#if ! defined(__GNUC__)
+#define ND_IS_AT_LEAST_GNUC_VERSION(major, minor) 0
+#else
+#define ND_IS_AT_LEAST_GNUC_VERSION(major, minor) \
+ (__GNUC__ > (major) || \
+ (__GNUC__ == (major) && __GNUC_MINOR__ >= (minor)))
+#endif
+
+/*
+ * Check whether this is Sun C/SunPro C/Oracle Studio major.minor
+ * or a later release.
+ *
+ * The version number in __SUNPRO_C is encoded in hex BCD, with the
+ * uppermost hex digit being the major version number, the next
+ * one or two hex digits being the minor version number, and
+ * the last digit being the patch version.
+ *
+ * It represents the *compiler* version, not the product version;
+ * see
+ *
+ * https://sourceforge.net/p/predef/wiki/Compilers/
+ *
+ * for a partial mapping, which we assume continues for later
+ * 12.x product releases.
+ */
+
+#if ! defined(__SUNPRO_C)
+#define ND_IS_AT_LEAST_SUNC_VERSION(major,minor) 0
+#else
+#define ND_SUNPRO_VERSION_TO_BCD(major, minor) \
+ (((minor) >= 10) ? \
+ (((major) << 12) | (((minor)/10) << 8) | (((minor)%10) << 4)) : \
+ (((major) << 8) | ((minor) << 4)))
+#define ND_IS_AT_LEAST_SUNC_VERSION(major,minor) \
+ (__SUNPRO_C >= ND_SUNPRO_VERSION_TO_BCD((major), (minor)))
+#endif
+
+/*
+ * Check whether this is IBM XL C major.minor or a later release.
+ *
+ * The version number in __xlC__ has the major version in the
+ * upper 8 bits and the minor version in the lower 8 bits.
+ */
+
+#if ! defined(__xlC__)
+#define ND_IS_AT_LEAST_XL_C_VERSION(major,minor) 0
+#else
+#define ND_IS_AT_LEAST_XL_C_VERSION(major, minor) \
+ (__xlC__ >= (((major) << 8) | (minor)))
+#endif
+
+/*
+ * Check whether this is HP aC++/HP C major.minor or a later release.
+ *
+ * The version number in __HP_aCC is encoded in zero-padded decimal BCD,
+ * with the "A." stripped off, the uppermost two decimal digits being
+ * the major version number, the next two decimal digits being the minor
+ * version number, and the last two decimal digits being the patch version.
+ * (Strip off the A., remove the . between the major and minor version
+ * number, and add two digits of patch.)
+ */
+
+#if ! defined(__HP_aCC)
+#define ND_IS_AT_LEAST_HP_C_VERSION(major,minor) 0
+#else
+#define ND_IS_AT_LEAST_HP_C_VERSION(major,minor) \
+ (__HP_aCC >= ((major)*10000 + (minor)*100))
+#endif
+
+#endif /* nd_funcattrs_h */
diff --git a/config.h.in b/config.h.in
new file mode 100644
index 0000000..365651f
--- /dev/null
+++ b/config.h.in
@@ -0,0 +1,293 @@
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Define to 1 if arpa/inet.h declares `ether_ntohost' */
+#undef ARPA_INET_H_DECLARES_ETHER_NTOHOST
+
+/* define if you want to build the possibly-buggy SMB printer */
+#undef ENABLE_SMB
+
+/* Define to 1 if you have the `bpf_dump' function. */
+#undef HAVE_BPF_DUMP
+
+/* capsicum support available */
+#undef HAVE_CAPSICUM
+
+/* Define to 1 if you have the `cap_enter' function. */
+#undef HAVE_CAP_ENTER
+
+/* Define to 1 if you have the `cap_ioctls_limit' function. */
+#undef HAVE_CAP_IOCTLS_LIMIT
+
+/* Define to 1 if you have the <cap-ng.h> header file. */
+#undef HAVE_CAP_NG_H
+
+/* Define to 1 if you have the `cap_rights_limit' function. */
+#undef HAVE_CAP_RIGHTS_LIMIT
+
+/* Casper support available */
+#undef HAVE_CASPER
+
+/* Define to 1 if you have the declaration of `ether_ntohost' */
+#undef HAVE_DECL_ETHER_NTOHOST
+
+/* Define to 1 if you have the `ether_ntohost' function. */
+#undef HAVE_ETHER_NTOHOST
+
+/* Define to 1 if you have the `EVP_CIPHER_CTX_new' function. */
+#undef HAVE_EVP_CIPHER_CTX_NEW
+
+/* Define to 1 if you have the `EVP_DecryptInit_ex' function. */
+#undef HAVE_EVP_DECRYPTINIT_EX
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+
+/* Define to 1 if you have the `fork' function. */
+#undef HAVE_FORK
+
+/* Define to 1 if you have the `getopt_long' function. */
+#undef HAVE_GETOPT_LONG
+
+/* define if you have getrpcbynumber() */
+#undef HAVE_GETRPCBYNUMBER
+
+/* Define to 1 if you have the `getservent' function. */
+#undef HAVE_GETSERVENT
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#undef HAVE_INTTYPES_H
+
+/* Define to 1 if you have the `cap-ng' library (-lcap-ng). */
+#undef HAVE_LIBCAP_NG
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+#undef HAVE_LIBCRYPTO
+
+/* Define to 1 if you have the `rpc' library (-lrpc). */
+#undef HAVE_LIBRPC
+
+/* Define to 1 if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#undef HAVE_NET_IF_H
+
+/* Define to 1 if you have the <net/if_pflog.h> header file. */
+#undef HAVE_NET_IF_PFLOG_H
+
+/* Define to 1 if you have the <net/pfvar.h> header file. */
+#undef HAVE_NET_PFVAR_H
+
+/* Define to 1 if you have the `openat' function. */
+#undef HAVE_OPENAT
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+#undef HAVE_OPENSSL_EVP_H
+
+/* define if the OS provides AF_INET6 and struct in6_addr */
+#undef HAVE_OS_IPV6_SUPPORT
+
+/* if there's an os_proto.h for this platform, to use additional prototypes */
+#undef HAVE_OS_PROTO_H
+
+/* Define to 1 if you have the `pcap_breakloop' function. */
+#undef HAVE_PCAP_BREAKLOOP
+
+/* Define to 1 if you have the `pcap_create' function. */
+#undef HAVE_PCAP_CREATE
+
+/* define if libpcap has pcap_datalink_name_to_val() */
+#undef HAVE_PCAP_DATALINK_NAME_TO_VAL
+
+/* define if libpcap has pcap_datalink_val_to_description() */
+#undef HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION
+
+/* define if libpcap has pcap_debug */
+#undef HAVE_PCAP_DEBUG
+
+/* Define to 1 if you have the `pcap_dump_flush' function. */
+#undef HAVE_PCAP_DUMP_FLUSH
+
+/* Define to 1 if you have the `pcap_dump_ftell' function. */
+#undef HAVE_PCAP_DUMP_FTELL
+
+/* Define to 1 if you have the `pcap_dump_ftell64' function. */
+#undef HAVE_PCAP_DUMP_FTELL64
+
+/* Define to 1 if you have the `pcap_findalldevs' function. */
+#undef HAVE_PCAP_FINDALLDEVS
+
+/* Define to 1 if you have the `pcap_findalldevs_ex' function. */
+#undef HAVE_PCAP_FINDALLDEVS_EX
+
+/* Define to 1 if you have the `pcap_free_datalinks' function. */
+#undef HAVE_PCAP_FREE_DATALINKS
+
+/* Define to 1 if the system has the type `pcap_if_t'. */
+#undef HAVE_PCAP_IF_T
+
+/* Define to 1 if you have the `pcap_lib_version' function. */
+#undef HAVE_PCAP_LIB_VERSION
+
+/* define if libpcap has pcap_list_datalinks() */
+#undef HAVE_PCAP_LIST_DATALINKS
+
+/* Define to 1 if you have the `pcap_open' function. */
+#undef HAVE_PCAP_OPEN
+
+/* Define to 1 if you have the <pcap/pcap-inttypes.h> header file. */
+#undef HAVE_PCAP_PCAP_INTTYPES_H
+
+/* Define to 1 if you have the `pcap_setdirection' function. */
+#undef HAVE_PCAP_SETDIRECTION
+
+/* Define to 1 if you have the `pcap_set_datalink' function. */
+#undef HAVE_PCAP_SET_DATALINK
+
+/* Define to 1 if you have the `pcap_set_immediate_mode' function. */
+#undef HAVE_PCAP_SET_IMMEDIATE_MODE
+
+/* Define to 1 if you have the `pcap_set_optimizer_debug' function. */
+#undef HAVE_PCAP_SET_OPTIMIZER_DEBUG
+
+/* Define to 1 if you have the `pcap_set_parser_debug' function. */
+#undef HAVE_PCAP_SET_PARSER_DEBUG
+
+/* Define to 1 if you have the `pcap_set_tstamp_precision' function. */
+#undef HAVE_PCAP_SET_TSTAMP_PRECISION
+
+/* Define to 1 if you have the `pcap_set_tstamp_type' function. */
+#undef HAVE_PCAP_SET_TSTAMP_TYPE
+
+/* define if libpcap has pcap_version */
+#undef HAVE_PCAP_VERSION
+
+/* Define to 1 if you have the `pfopen' function. */
+#undef HAVE_PFOPEN
+
+/* Define to 1 if you have the <rpc/rpcent.h> header file. */
+#undef HAVE_RPC_RPCENT_H
+
+/* Define to 1 if you have the <rpc/rpc.h> header file. */
+#undef HAVE_RPC_RPC_H
+
+/* Define to 1 if you have the `setlinebuf' function. */
+#undef HAVE_SETLINEBUF
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#undef HAVE_STDINT_H
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#undef HAVE_STDLIB_H
+
+/* Define to 1 if you have the `strdup' function. */
+#undef HAVE_STRDUP
+
+/* Define to 1 if you have the `strftime' function. */
+#undef HAVE_STRFTIME
+
+/* Define to 1 if you have the <strings.h> header file. */
+#undef HAVE_STRINGS_H
+
+/* Define to 1 if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+
+/* Define to 1 if you have the `strlcat' function. */
+#undef HAVE_STRLCAT
+
+/* Define to 1 if you have the `strlcpy' function. */
+#undef HAVE_STRLCPY
+
+/* Define to 1 if you have the `strsep' function. */
+#undef HAVE_STRSEP
+
+/* Define to 1 if the system has the type `struct ether_addr'. */
+#undef HAVE_STRUCT_ETHER_ADDR
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#undef HAVE_SYS_STAT_H
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#undef HAVE_SYS_TYPES_H
+
+/* Define to 1 if the system has the type `uintptr_t'. */
+#undef HAVE_UINTPTR_T
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define to 1 if you have the `vfork' function. */
+#undef HAVE_VFORK
+
+/* define if libpcap has yydebug */
+#undef HAVE_YYDEBUG
+
+/* Define to 1 if netinet/ether.h declares `ether_ntohost' */
+#undef NETINET_ETHER_H_DECLARES_ETHER_NTOHOST
+
+/* Define to 1 if netinet/if_ether.h declares `ether_ntohost' */
+#undef NETINET_IF_ETHER_H_DECLARES_ETHER_NTOHOST
+
+/* Define to 1 if net/ethernet.h declares `ether_ntohost' */
+#undef NET_ETHERNET_H_DECLARES_ETHER_NTOHOST
+
+/* Define to the address where bug reports for this package should be sent. */
+#undef PACKAGE_BUGREPORT
+
+/* Define to the full name of this package. */
+#undef PACKAGE_NAME
+
+/* Define to the full name and version of this package. */
+#undef PACKAGE_STRING
+
+/* Define to the one symbol short name of this package. */
+#undef PACKAGE_TARNAME
+
+/* Define to the home page for this package. */
+#undef PACKAGE_URL
+
+/* Define to the version of this package. */
+#undef PACKAGE_VERSION
+
+/* Define to 1 if you have the ANSI C header files. */
+#undef STDC_HEADERS
+
+/* Define to 1 if sys/ethernet.h declares `ether_ntohost' */
+#undef SYS_ETHERNET_H_DECLARES_ETHER_NTOHOST
+
+/* define if you have ether_ntohost() and it works */
+#undef USE_ETHER_NTOHOST
+
+/* Define if you enable support for libsmi */
+#undef USE_LIBSMI
+
+/* define if should chroot when dropping privileges */
+#undef WITH_CHROOT
+
+/* define if should drop privileges by default */
+#undef WITH_USER
+
+/* define on AIX to get certain functions */
+#undef _SUN
+
+/* to handle Ultrix compilers that don't support const in prototypes */
+#undef const
+
+/* Define as token for inline if inlining supported */
+#undef inline
+
+/* Define to `uint16_t' if u_int16_t not defined. */
+#undef u_int16_t
+
+/* Define to `uint32_t' if u_int32_t not defined. */
+#undef u_int32_t
+
+/* Define to `uint64_t' if u_int64_t not defined. */
+#undef u_int64_t
+
+/* Define to `uint8_t' if u_int8_t not defined. */
+#undef u_int8_t
+
+/* Define to the type of an unsigned integer type wide enough to hold a
+ pointer, if such a type exists, and if the system does not define it. */
+#undef uintptr_t
diff --git a/cpack.c b/cpack.c
new file mode 100644
index 0000000..9be7b47
--- /dev/null
+++ b/cpack.c
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
+ * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+#include "cpack.h"
+
+const uint8_t *
+nd_cpack_next_boundary(const uint8_t *buf, const uint8_t *p, size_t alignment)
+{
+ size_t misalignment = (size_t)(p - buf) % alignment;
+
+ if (misalignment == 0)
+ return p;
+
+ return p + (alignment - misalignment);
+}
+
+/* Advance to the next wordsize boundary. Return NULL if fewer than
+ * wordsize bytes remain in the buffer after the boundary. Otherwise,
+ * return a pointer to the boundary.
+ */
+const uint8_t *
+nd_cpack_align_and_reserve(struct cpack_state *cs, size_t wordsize)
+{
+ const uint8_t *next;
+
+ /* Ensure alignment. */
+ next = nd_cpack_next_boundary(cs->c_buf, cs->c_next, wordsize);
+
+ /* Too little space for wordsize bytes? */
+ if (next - cs->c_buf + wordsize > cs->c_len)
+ return NULL;
+
+ return next;
+}
+
+/* Advance by N bytes without returning them. */
+int
+nd_cpack_advance(struct cpack_state *cs, const size_t toskip)
+{
+ /* No space left? */
+ if (cs->c_next - cs->c_buf + toskip > cs->c_len)
+ return -1;
+ cs->c_next += toskip;
+ return 0;
+}
+
+int
+nd_cpack_init(struct cpack_state *cs, const uint8_t *buf, size_t buflen)
+{
+ memset(cs, 0, sizeof(*cs));
+
+ cs->c_buf = buf;
+ cs->c_len = buflen;
+ cs->c_next = cs->c_buf;
+
+ return 0;
+}
+
+/* Unpack a 64-bit unsigned integer. */
+int
+nd_cpack_uint64(netdissect_options *ndo, struct cpack_state *cs, uint64_t *u)
+{
+ const uint8_t *next;
+
+ if ((next = nd_cpack_align_and_reserve(cs, sizeof(*u))) == NULL)
+ return -1;
+
+ *u = GET_LE_U_8(next);
+
+ /* Move pointer past the uint64_t. */
+ cs->c_next = next + sizeof(*u);
+ return 0;
+}
+
+/* Unpack a 64-bit signed integer. */
+int
+nd_cpack_int64(netdissect_options *ndo, struct cpack_state *cs, int64_t *u)
+{
+ const uint8_t *next;
+
+ if ((next = nd_cpack_align_and_reserve(cs, sizeof(*u))) == NULL)
+ return -1;
+
+ *u = GET_LE_S_8(next);
+
+ /* Move pointer past the int64_t. */
+ cs->c_next = next + sizeof(*u);
+ return 0;
+}
+
+/* Unpack a 32-bit unsigned integer. */
+int
+nd_cpack_uint32(netdissect_options *ndo, struct cpack_state *cs, uint32_t *u)
+{
+ const uint8_t *next;
+
+ if ((next = nd_cpack_align_and_reserve(cs, sizeof(*u))) == NULL)
+ return -1;
+
+ *u = GET_LE_U_4(next);
+
+ /* Move pointer past the uint32_t. */
+ cs->c_next = next + sizeof(*u);
+ return 0;
+}
+
+/* Unpack a 32-bit signed integer. */
+int
+nd_cpack_int32(netdissect_options *ndo, struct cpack_state *cs, int32_t *u)
+{
+ const uint8_t *next;
+
+ if ((next = nd_cpack_align_and_reserve(cs, sizeof(*u))) == NULL)
+ return -1;
+
+ *u = GET_LE_S_4(next);
+
+ /* Move pointer past the int32_t. */
+ cs->c_next = next + sizeof(*u);
+ return 0;
+}
+
+/* Unpack a 16-bit unsigned integer. */
+int
+nd_cpack_uint16(netdissect_options *ndo, struct cpack_state *cs, uint16_t *u)
+{
+ const uint8_t *next;
+
+ if ((next = nd_cpack_align_and_reserve(cs, sizeof(*u))) == NULL)
+ return -1;
+
+ *u = GET_LE_U_2(next);
+
+ /* Move pointer past the uint16_t. */
+ cs->c_next = next + sizeof(*u);
+ return 0;
+}
+
+/* Unpack a 16-bit signed integer. */
+int
+nd_cpack_int16(netdissect_options *ndo, struct cpack_state *cs, int16_t *u)
+{
+ const uint8_t *next;
+
+ if ((next = nd_cpack_align_and_reserve(cs, sizeof(*u))) == NULL)
+ return -1;
+
+ *u = GET_LE_S_2(next);
+
+ /* Move pointer past the int16_t. */
+ cs->c_next = next + sizeof(*u);
+ return 0;
+}
+
+/* Unpack an 8-bit unsigned integer. */
+int
+nd_cpack_uint8(netdissect_options *ndo, struct cpack_state *cs, uint8_t *u)
+{
+ /* No space left? */
+ if ((size_t)(cs->c_next - cs->c_buf) >= cs->c_len)
+ return -1;
+
+ *u = GET_U_1(cs->c_next);
+
+ /* Move pointer past the uint8_t. */
+ cs->c_next++;
+ return 0;
+}
+
+/* Unpack an 8-bit signed integer. */
+int
+nd_cpack_int8(netdissect_options *ndo, struct cpack_state *cs, int8_t *u)
+{
+ /* No space left? */
+ if ((size_t)(cs->c_next - cs->c_buf) >= cs->c_len)
+ return -1;
+
+ *u = GET_S_1(cs->c_next);
+
+ /* Move pointer past the int8_t. */
+ cs->c_next++;
+ return 0;
+}
diff --git a/cpack.h b/cpack.h
new file mode 100644
index 0000000..4e89300
--- /dev/null
+++ b/cpack.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
+ * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+#ifndef ND_CPACK_H
+#define ND_CPACK_H
+
+#include "netdissect.h"
+
+struct cpack_state {
+ const uint8_t *c_buf;
+ const uint8_t *c_next;
+ size_t c_len;
+};
+
+int nd_cpack_init(struct cpack_state *, const uint8_t *, size_t);
+
+int nd_cpack_uint8(netdissect_options *, struct cpack_state *, uint8_t *);
+int nd_cpack_int8(netdissect_options *, struct cpack_state *, int8_t *);
+int nd_cpack_uint16(netdissect_options *, struct cpack_state *, uint16_t *);
+int nd_cpack_int16(netdissect_options *, struct cpack_state *, int16_t *);
+int nd_cpack_uint32(netdissect_options *, struct cpack_state *, uint32_t *);
+int nd_cpack_int32(netdissect_options *, struct cpack_state *, int32_t *);
+int nd_cpack_uint64(netdissect_options *, struct cpack_state *, uint64_t *);
+int nd_cpack_int64(netdissect_options *, struct cpack_state *, int64_t *);
+
+const uint8_t *nd_cpack_next_boundary(const uint8_t *buf, const uint8_t *p, size_t alignment);
+const uint8_t *nd_cpack_align_and_reserve(struct cpack_state *cs, size_t wordsize);
+
+extern int nd_cpack_advance(struct cpack_state *, const size_t);
+
+#endif /* ND_CPACK_H */
diff --git a/ethertype.h b/ethertype.h
new file mode 100644
index 0000000..a757a39
--- /dev/null
+++ b/ethertype.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1993, 1994, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Maximum length of the length field in an Ethernet header; any value
+ * greater than this is not a length value, so it's either an Ethernet
+ * type or an invalid value.
+ */
+#define MAX_ETHERNET_LENGTH_VAL 1500
+
+/*
+ * Ethernet types.
+ *
+ * We wrap the declarations with #ifdef, so that if a file includes
+ * <netinet/if_ether.h>, which may declare some of these, we don't
+ * get a bunch of complaints from the C compiler about redefinitions
+ * of these values.
+ *
+ * We declare all of them here so that no file has to include
+ * <netinet/if_ether.h> if all it needs are ETHERTYPE_ values.
+ */
+
+#ifndef ETHERTYPE_LEN
+#define ETHERTYPE_LEN 2
+#endif
+
+#ifndef ETHERTYPE_GRE_ISO
+#define ETHERTYPE_GRE_ISO 0x00FE /* not really an ethertype only used in GRE */
+#endif
+#ifndef ETHERTYPE_PUP
+#define ETHERTYPE_PUP 0x0200 /* PUP protocol */
+#endif
+#ifndef ETHERTYPE_IP
+#define ETHERTYPE_IP 0x0800 /* IP protocol */
+#endif
+#ifndef ETHERTYPE_ARP
+#define ETHERTYPE_ARP 0x0806 /* Addr. resolution protocol */
+#endif
+#ifndef ETHERTYPE_REVARP
+#define ETHERTYPE_REVARP 0x8035 /* reverse Addr. resolution protocol */
+#endif
+#ifndef ETHERTYPE_NS
+#define ETHERTYPE_NS 0x0600
+#endif
+#ifndef ETHERTYPE_SPRITE
+#define ETHERTYPE_SPRITE 0x0500
+#endif
+#ifndef ETHERTYPE_TRAIL
+#define ETHERTYPE_TRAIL 0x1000
+#endif
+#ifndef ETHERTYPE_MOPDL
+#define ETHERTYPE_MOPDL 0x6001
+#endif
+#ifndef ETHERTYPE_MOPRC
+#define ETHERTYPE_MOPRC 0x6002
+#endif
+#ifndef ETHERTYPE_DN
+#define ETHERTYPE_DN 0x6003
+#endif
+#ifndef ETHERTYPE_LAT
+#define ETHERTYPE_LAT 0x6004
+#endif
+#ifndef ETHERTYPE_SCA
+#define ETHERTYPE_SCA 0x6007
+#endif
+#ifndef ETHERTYPE_TEB
+#define ETHERTYPE_TEB 0x6558
+#endif
+#ifndef ETHERTYPE_LANBRIDGE
+#define ETHERTYPE_LANBRIDGE 0x8038
+#endif
+#ifndef ETHERTYPE_DECDNS
+#define ETHERTYPE_DECDNS 0x803c
+#endif
+#ifndef ETHERTYPE_DECDTS
+#define ETHERTYPE_DECDTS 0x803e
+#endif
+#ifndef ETHERTYPE_VEXP
+#define ETHERTYPE_VEXP 0x805b
+#endif
+#ifndef ETHERTYPE_VPROD
+#define ETHERTYPE_VPROD 0x805c
+#endif
+#ifndef ETHERTYPE_ATALK
+#define ETHERTYPE_ATALK 0x809b
+#endif
+#ifndef ETHERTYPE_AARP
+#define ETHERTYPE_AARP 0x80f3
+#endif
+#ifndef ETHERTYPE_TIPC
+#define ETHERTYPE_TIPC 0x88ca
+#endif
+#ifndef ETHERTYPE_8021Q
+#define ETHERTYPE_8021Q 0x8100
+#endif
+
+/* see:
+ https://en.wikipedia.org/wiki/IEEE_802.1Q
+ and https://en.wikipedia.org/wiki/QinQ
+*/
+#ifndef ETHERTYPE_8021Q9100
+#define ETHERTYPE_8021Q9100 0x9100
+#endif
+#ifndef ETHERTYPE_8021Q9200
+#define ETHERTYPE_8021Q9200 0x9200
+#endif
+#ifndef ETHERTYPE_8021QinQ
+#define ETHERTYPE_8021QinQ 0x88a8
+#endif
+#ifndef ETHERTYPE_MACSEC
+#define ETHERTYPE_MACSEC 0x88e5
+#endif
+#ifndef ETHERTYPE_IPX
+#define ETHERTYPE_IPX 0x8137
+#endif
+#ifndef ETHERTYPE_IPV6
+#define ETHERTYPE_IPV6 0x86dd
+#endif
+#ifndef ETHERTYPE_PPP
+#define ETHERTYPE_PPP 0x880b
+#endif
+#ifndef ETHERTYPE_MPCP
+#define ETHERTYPE_MPCP 0x8808
+#endif
+#ifndef ETHERTYPE_SLOW
+#define ETHERTYPE_SLOW 0x8809
+#endif
+#ifndef ETHERTYPE_MPLS
+#define ETHERTYPE_MPLS 0x8847
+#endif
+#ifndef ETHERTYPE_MPLS_MULTI
+#define ETHERTYPE_MPLS_MULTI 0x8848
+#endif
+#ifndef ETHERTYPE_PPPOED
+#define ETHERTYPE_PPPOED 0x8863
+#endif
+#ifndef ETHERTYPE_PPPOES
+#define ETHERTYPE_PPPOES 0x8864
+#endif
+#ifndef ETHERTYPE_NSH
+#define ETHERTYPE_NSH 0x894F
+#endif
+#ifndef ETHERTYPE_PPPOED2
+#define ETHERTYPE_PPPOED2 0x3c12
+#endif
+#ifndef ETHERTYPE_PPPOES2
+#define ETHERTYPE_PPPOES2 0x3c13
+#endif
+#ifndef ETHERTYPE_MS_NLB_HB
+#define ETHERTYPE_MS_NLB_HB 0x886f /* MS Network Load Balancing Heartbeat */
+#endif
+#ifndef ETHERTYPE_JUMBO
+#define ETHERTYPE_JUMBO 0x8870
+#endif
+#ifndef ETHERTYPE_LLDP
+#define ETHERTYPE_LLDP 0x88cc
+#endif
+#ifndef ETHERTYPE_EAPOL
+#define ETHERTYPE_EAPOL 0x888e
+#endif
+#ifndef ETHERTYPE_RRCP
+#define ETHERTYPE_RRCP 0x8899
+#endif
+#ifndef ETHERTYPE_AOE
+#define ETHERTYPE_AOE 0x88a2
+#endif
+#ifndef ETHERTYPE_PTP
+#define ETHERTYPE_PTP 0x88f7
+#endif
+#ifndef ETHERTYPE_LOOPBACK
+#define ETHERTYPE_LOOPBACK 0x9000
+#endif
+#ifndef ETHERTYPE_VMAN
+#define ETHERTYPE_VMAN 0x9100 /* Extreme VMAN Protocol */
+#endif
+#ifndef ETHERTYPE_CFM_OLD
+#define ETHERTYPE_CFM_OLD 0xabcd /* 802.1ag depreciated */
+#endif
+#ifndef ETHERTYPE_CFM
+#define ETHERTYPE_CFM 0x8902 /* 802.1ag */
+#endif
+#ifndef ETHERTYPE_IEEE1905_1
+#define ETHERTYPE_IEEE1905_1 0x893a /* IEEE 1905.1 */
+#endif
+#ifndef ETHERTYPE_ISO
+#define ETHERTYPE_ISO 0xfefe /* nonstandard - used in Cisco HDLC encapsulation */
+#endif
+#ifndef ETHERTYPE_CALM_FAST
+#define ETHERTYPE_CALM_FAST 0x1111 /* ISO CALM FAST */
+#endif
+#ifndef ETHERTYPE_GEONET_OLD
+#define ETHERTYPE_GEONET_OLD 0x0707 /* ETSI GeoNetworking (before Jan 2013) */
+#endif
+#ifndef ETHERTYPE_GEONET
+#define ETHERTYPE_GEONET 0x8947 /* ETSI GeoNetworking (Official IEEE registration from Jan 2013) */
+#endif
+#ifndef ETHERTYPE_ARISTA
+#define ETHERTYPE_ARISTA 0xd28b /* Arista Networks vendor specific EtherType */
+#endif
+
+extern const struct tok ethertype_values[];
diff --git a/extract.h b/extract.h
new file mode 100644
index 0000000..2ea7763
--- /dev/null
+++ b/extract.h
@@ -0,0 +1,915 @@
+/*
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef EXTRACT_H
+#define EXTRACT_H
+
+#include <string.h>
+
+/*
+ * For 8-bit values; needed to fetch a one-byte value. Byte order
+ * isn't relevant, and alignment isn't an issue.
+ */
+#define EXTRACT_U_1(p) ((uint8_t)(*(p)))
+#define EXTRACT_S_1(p) ((int8_t)(*(p)))
+
+/*
+ * Inline functions or macros to extract possibly-unaligned big-endian
+ * integral values.
+ */
+#include "funcattrs.h"
+#include "netdissect.h"
+
+/*
+ * If we have versions of GCC or Clang that support an __attribute__
+ * to say "if we're building with unsigned behavior sanitization,
+ * don't complain about undefined behavior in this function", we
+ * label these functions with that attribute - we *know* it's undefined
+ * in the C standard, but we *also* know it does what we want with
+ * the ISA we're targeting and the compiler we're using.
+ *
+ * For GCC 4.9.0 and later, we use __attribute__((no_sanitize_undefined));
+ * pre-5.0 GCC doesn't have __has_attribute, and I'm not sure whether
+ * GCC or Clang first had __attribute__((no_sanitize(XXX)).
+ *
+ * For Clang, we check for __attribute__((no_sanitize(XXX)) with
+ * __has_attribute, as there are versions of Clang that support
+ * __attribute__((no_sanitize("undefined")) but don't support
+ * __attribute__((no_sanitize_undefined)).
+ *
+ * We define this here, rather than in funcattrs.h, because we
+ * only want it used here, we don't want it to be broadly used.
+ * (Any printer will get this defined, but this should at least
+ * make it harder for people to find.)
+ */
+#if defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 409)
+#define UNALIGNED_OK __attribute__((no_sanitize_undefined))
+#elif __has_attribute(no_sanitize)
+#define UNALIGNED_OK __attribute__((no_sanitize("undefined")))
+#else
+#define UNALIGNED_OK
+#endif
+
+#if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \
+ (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \
+ (defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64)) || \
+ (defined(__s390__) || defined(__s390x__) || defined(__zarch__))
+/*
+ * The processor natively handles unaligned loads, so we can just
+ * cast the pointer and fetch through it.
+ *
+ * XXX - are those all the x86 tests we need?
+ * XXX - are those the only 68k tests we need not to generated
+ * unaligned accesses if the target is the 68000 or 68010?
+ * XXX - are there any tests we don't need, because some definitions are for
+ * compilers that also predefine the GCC symbols?
+ * XXX - do we need to test for both 32-bit and 64-bit versions of those
+ * architectures in all cases?
+ */
+UNALIGNED_OK static inline uint16_t
+EXTRACT_BE_U_2(const void *p)
+{
+ return ((uint16_t)ntohs(*(const uint16_t *)(p)));
+}
+
+UNALIGNED_OK static inline int16_t
+EXTRACT_BE_S_2(const void *p)
+{
+ return ((int16_t)ntohs(*(const int16_t *)(p)));
+}
+
+UNALIGNED_OK static inline uint32_t
+EXTRACT_BE_U_4(const void *p)
+{
+ return ((uint32_t)ntohl(*(const uint32_t *)(p)));
+}
+
+UNALIGNED_OK static inline int32_t
+EXTRACT_BE_S_4(const void *p)
+{
+ return ((int32_t)ntohl(*(const int32_t *)(p)));
+}
+
+UNALIGNED_OK static inline uint64_t
+EXTRACT_BE_U_8(const void *p)
+{
+ return ((uint64_t)(((uint64_t)ntohl(*((const uint32_t *)(p) + 0))) << 32 |
+ ((uint64_t)ntohl(*((const uint32_t *)(p) + 1))) << 0));
+
+}
+
+UNALIGNED_OK static inline int64_t
+EXTRACT_BE_S_8(const void *p)
+{
+ return ((int64_t)(((int64_t)ntohl(*((const uint32_t *)(p) + 0))) << 32 |
+ ((uint64_t)ntohl(*((const uint32_t *)(p) + 1))) << 0));
+
+}
+
+/*
+ * Extract an IPv4 address, which is in network byte order, and not
+ * necessarily aligned, and provide the result in host byte order.
+ */
+UNALIGNED_OK static inline uint32_t
+EXTRACT_IPV4_TO_HOST_ORDER(const void *p)
+{
+ return ((uint32_t)ntohl(*(const uint32_t *)(p)));
+}
+#elif ND_IS_AT_LEAST_GNUC_VERSION(2,0) && \
+ (defined(__alpha) || defined(__alpha__) || \
+ defined(__mips) || defined(__mips__))
+/*
+ * This is MIPS or Alpha, which don't natively handle unaligned loads,
+ * but which have instructions that can help when doing unaligned
+ * loads, and this is GCC 2.0 or later or a compiler that claims to
+ * be GCC 2.0 or later, which we assume that mean we have
+ * __attribute__((packed)), which we can use to convince the compiler
+ * to generate those instructions.
+ *
+ * Declare packed structures containing a uint16_t and a uint32_t,
+ * cast the pointer to point to one of those, and fetch through it;
+ * the GCC manual doesn't appear to explicitly say that
+ * __attribute__((packed)) causes the compiler to generate unaligned-safe
+ * code, but it apppears to do so.
+ *
+ * We do this in case the compiler can generate code using those
+ * instructions to do an unaligned load and pass stuff to "ntohs()" or
+ * "ntohl()", which might be better than the code to fetch the
+ * bytes one at a time and assemble them. (That might not be the
+ * case on a little-endian platform, such as DEC's MIPS machines and
+ * Alpha machines, where "ntohs()" and "ntohl()" might not be done
+ * inline.)
+ *
+ * We do this only for specific architectures because, for example,
+ * at least some versions of GCC, when compiling for 64-bit SPARC,
+ * generate code that assumes alignment if we do this.
+ *
+ * XXX - add other architectures and compilers as possible and
+ * appropriate.
+ *
+ * HP's C compiler, indicated by __HP_cc being defined, supports
+ * "#pragma unaligned N" in version A.05.50 and later, where "N"
+ * specifies a number of bytes at which the typedef on the next
+ * line is aligned, e.g.
+ *
+ * #pragma unalign 1
+ * typedef uint16_t unaligned_uint16_t;
+ *
+ * to define unaligned_uint16_t as a 16-bit unaligned data type.
+ * This could be presumably used, in sufficiently recent versions of
+ * the compiler, with macros similar to those below. This would be
+ * useful only if that compiler could generate better code for PA-RISC
+ * or Itanium than would be generated by a bunch of shifts-and-ORs.
+ *
+ * DEC C, indicated by __DECC being defined, has, at least on Alpha,
+ * an __unaligned qualifier that can be applied to pointers to get the
+ * compiler to generate code that does unaligned loads and stores when
+ * dereferencing the pointer in question.
+ *
+ * XXX - what if the native C compiler doesn't support
+ * __attribute__((packed))? How can we get it to generate unaligned
+ * accesses for *specific* items?
+ */
+typedef struct {
+ uint16_t val;
+} __attribute__((packed)) unaligned_uint16_t;
+
+typedef struct {
+ int16_t val;
+} __attribute__((packed)) unaligned_int16_t;
+
+typedef struct {
+ uint32_t val;
+} __attribute__((packed)) unaligned_uint32_t;
+
+typedef struct {
+ int32_t val;
+} __attribute__((packed)) unaligned_int32_t;
+
+UNALIGNED_OK static inline uint16_t
+EXTRACT_BE_U_2(const void *p)
+{
+ return ((uint16_t)ntohs(((const unaligned_uint16_t *)(p))->val));
+}
+
+UNALIGNED_OK static inline int16_t
+EXTRACT_BE_S_2(const void *p)
+{
+ return ((int16_t)ntohs(((const unaligned_int16_t *)(p))->val));
+}
+
+UNALIGNED_OK static inline uint32_t
+EXTRACT_BE_U_4(const void *p)
+{
+ return ((uint32_t)ntohl(((const unaligned_uint32_t *)(p))->val));
+}
+
+UNALIGNED_OK static inline int32_t
+EXTRACT_BE_S_4(const void *p)
+{
+ return ((int32_t)ntohl(((const unaligned_int32_t *)(p))->val));
+}
+
+UNALIGNED_OK static inline uint64_t
+EXTRACT_BE_U_8(const void *p)
+{
+ return ((uint64_t)(((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 0)->val)) << 32 |
+ ((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 1)->val)) << 0));
+}
+
+UNALIGNED_OK static inline int64_t
+EXTRACT_BE_S_8(const void *p)
+{
+ return ((int64_t)(((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 0)->val)) << 32 |
+ ((uint64_t)ntohl(((const unaligned_uint32_t *)(p) + 1)->val)) << 0));
+}
+
+/*
+ * Extract an IPv4 address, which is in network byte order, and not
+ * necessarily aligned, and provide the result in host byte order.
+ */
+UNALIGNED_OK static inline uint32_t
+EXTRACT_IPV4_TO_HOST_ORDER(const void *p)
+{
+ return ((uint32_t)ntohl(((const unaligned_uint32_t *)(p))->val));
+}
+#else
+/*
+ * This architecture doesn't natively support unaligned loads, and either
+ * this isn't a GCC-compatible compiler, we don't have __attribute__,
+ * or we do but we don't know of any better way with this instruction
+ * set to do unaligned loads, so do unaligned loads of big-endian
+ * quantities the hard way - fetch the bytes one at a time and
+ * assemble them.
+ *
+ * XXX - ARM is a special case. ARMv1 through ARMv5 didn't suppory
+ * unaligned loads; ARMv6 and later support it *but* have a bit in
+ * the system control register that the OS can set and that causes
+ * unaligned loads to fault rather than succeeding.
+ *
+ * At least some OSes may set that flag, so we do *not* treat ARM
+ * as supporting unaligned loads. If your OS supports them on ARM,
+ * and you want to use them, please update the tests in the #if above
+ * to check for ARM *and* for your OS.
+ */
+#define EXTRACT_BE_U_2(p) \
+ ((uint16_t)(((uint16_t)(*((const uint8_t *)(p) + 0)) << 8) | \
+ ((uint16_t)(*((const uint8_t *)(p) + 1)) << 0)))
+#define EXTRACT_BE_S_2(p) \
+ ((int16_t)(((uint16_t)(*((const uint8_t *)(p) + 0)) << 8) | \
+ ((uint16_t)(*((const uint8_t *)(p) + 1)) << 0)))
+#define EXTRACT_BE_U_4(p) \
+ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 24) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 3)) << 0)))
+#define EXTRACT_BE_S_4(p) \
+ ((int32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 24) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 3)) << 0)))
+#define EXTRACT_BE_U_8(p) \
+ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 56) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 48) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 7)) << 0)))
+#define EXTRACT_BE_S_8(p) \
+ ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 56) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 48) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 7)) << 0)))
+
+/*
+ * Extract an IPv4 address, which is in network byte order, and not
+ * necessarily aligned, and provide the result in host byte order.
+ */
+#define EXTRACT_IPV4_TO_HOST_ORDER(p) \
+ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 24) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 3)) << 0)))
+#endif /* unaligned access checks */
+
+/*
+ * Extract numerical values in *host* byte order. (Some metadata
+ * headers are in the byte order of the host that wrote the file,
+ * and libpcap translate them to the byte order of the host
+ * reading the file. This means that if a program on that host
+ * reads with libpcap and writes to a new file, the new file will
+ * be written in the byte order of the host writing the file. Thus,
+ * the magic number in pcap files and byte-order magic in pcapng
+ * files can be used to determine the byte order in those metadata
+ * headers.)
+ *
+ * XXX - on platforms that can do unaligned accesses, just cast and
+ * dereference the pointer.
+ */
+static inline uint16_t
+EXTRACT_HE_U_2(const void *p)
+{
+ uint16_t val;
+
+ UNALIGNED_MEMCPY(&val, p, sizeof(uint16_t));
+ return val;
+}
+
+static inline int16_t
+EXTRACT_HE_S_2(const void *p)
+{
+ int16_t val;
+
+ UNALIGNED_MEMCPY(&val, p, sizeof(int16_t));
+ return val;
+}
+
+static inline uint32_t
+EXTRACT_HE_U_4(const void *p)
+{
+ uint32_t val;
+
+ UNALIGNED_MEMCPY(&val, p, sizeof(uint32_t));
+ return val;
+}
+
+static inline int32_t
+EXTRACT_HE_S_4(const void *p)
+{
+ int32_t val;
+
+ UNALIGNED_MEMCPY(&val, p, sizeof(int32_t));
+ return val;
+}
+
+/*
+ * Extract an IPv4 address, which is in network byte order, and which
+ * is not necessarily aligned on a 4-byte boundary, and provide the
+ * result in network byte order.
+ *
+ * This works the same way regardless of the host's byte order.
+ */
+static inline uint32_t
+EXTRACT_IPV4_TO_NETWORK_ORDER(const void *p)
+{
+ uint32_t addr;
+
+ UNALIGNED_MEMCPY(&addr, p, sizeof(uint32_t));
+ return addr;
+}
+
+/*
+ * Non-power-of-2 sizes.
+ */
+#define EXTRACT_BE_U_3(p) \
+ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 0)))
+
+#define EXTRACT_BE_S_3(p) \
+ (((*((const uint8_t *)(p) + 0)) & 0x80) ? \
+ ((int32_t)(((uint32_t)(*((const uint8_t *)(p) + 0)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 0))) : \
+ ((int32_t)(0xFF000000U | \
+ ((uint32_t)(*((const uint8_t *)(p) + 0)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 0))))
+
+#define EXTRACT_BE_U_5(p) \
+ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 0)))
+
+#define EXTRACT_BE_S_5(p) \
+ (((*((const uint8_t *)(p) + 0)) & 0x80) ? \
+ ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 0))) : \
+ ((int64_t)(INT64_T_CONSTANT(0xFFFFFF0000000000U) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 0))))
+
+#define EXTRACT_BE_U_6(p) \
+ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 0)))
+
+#define EXTRACT_BE_S_6(p) \
+ (((*((const uint8_t *)(p) + 0)) & 0x80) ? \
+ ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 0))) : \
+ ((int64_t)(INT64_T_CONSTANT(0xFFFFFFFF00000000U) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 0))))
+
+#define EXTRACT_BE_U_7(p) \
+ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 48) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 0)))
+
+#define EXTRACT_BE_S_7(p) \
+ (((*((const uint8_t *)(p) + 0)) & 0x80) ? \
+ ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 0)) << 48) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 0))) : \
+ ((int64_t)(INT64_T_CONSTANT(0xFFFFFFFFFF000000U) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 48) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 0))))
+
+/*
+ * Macros to extract possibly-unaligned little-endian integral values.
+ * XXX - do loads on little-endian machines that support unaligned loads?
+ */
+#define EXTRACT_LE_U_2(p) \
+ ((uint16_t)(((uint16_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint16_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_S_2(p) \
+ ((int16_t)(((uint16_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint16_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_U_4(p) \
+ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_S_4(p) \
+ ((int32_t)(((uint32_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_U_8(p) \
+ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 7)) << 56) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 48) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_S_8(p) \
+ ((int64_t)(((uint64_t)(*((const uint8_t *)(p) + 7)) << 56) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 6)) << 48) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 0)))
+
+/*
+ * Non-power-of-2 sizes.
+ */
+
+#define EXTRACT_LE_U_3(p) \
+ ((uint32_t)(((uint32_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_S_3(p) \
+ ((int32_t)(((uint32_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint32_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_U_5(p) \
+ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 4)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_U_6(p) \
+ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 5)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 0)))
+#define EXTRACT_LE_U_7(p) \
+ ((uint64_t)(((uint64_t)(*((const uint8_t *)(p) + 6)) << 48) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 5)) << 40) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 4)) << 32) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 3)) << 24) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 2)) << 16) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 1)) << 8) | \
+ ((uint64_t)(*((const uint8_t *)(p) + 0)) << 0)))
+
+/*
+ * Macros to check the presence of the values in question.
+ */
+#define ND_TTEST_1(p) ND_TTEST_LEN((p), 1)
+#define ND_TCHECK_1(p) ND_TCHECK_LEN((p), 1)
+
+#define ND_TTEST_2(p) ND_TTEST_LEN((p), 2)
+#define ND_TCHECK_2(p) ND_TCHECK_LEN((p), 2)
+
+#define ND_TTEST_3(p) ND_TTEST_LEN((p), 3)
+#define ND_TCHECK_3(p) ND_TCHECK_LEN((p), 3)
+
+#define ND_TTEST_4(p) ND_TTEST_LEN((p), 4)
+#define ND_TCHECK_4(p) ND_TCHECK_LEN((p), 4)
+
+#define ND_TTEST_5(p) ND_TTEST_LEN((p), 5)
+#define ND_TCHECK_5(p) ND_TCHECK_LEN((p), 5)
+
+#define ND_TTEST_6(p) ND_TTEST_LEN((p), 6)
+#define ND_TCHECK_6(p) ND_TCHECK_LEN((p), 6)
+
+#define ND_TTEST_7(p) ND_TTEST_LEN((p), 7)
+#define ND_TCHECK_7(p) ND_TCHECK_LEN((p), 7)
+
+#define ND_TTEST_8(p) ND_TTEST_LEN((p), 8)
+#define ND_TCHECK_8(p) ND_TCHECK_LEN((p), 8)
+
+#define ND_TTEST_16(p) ND_TTEST_LEN((p), 16)
+#define ND_TCHECK_16(p) ND_TCHECK_LEN((p), 16)
+
+static inline NORETURN void
+nd_trunc_longjmp(netdissect_options *ndo)
+{
+ longjmp(ndo->ndo_early_end, ND_TRUNCATED);
+}
+
+/* get_u_1 and get_s_1 */
+
+static inline uint8_t
+get_u_1(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_1(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_U_1(p);
+}
+
+static inline int8_t
+get_s_1(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_1(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_S_1(p);
+}
+
+/* get_be_u_N */
+
+static inline uint16_t
+get_be_u_2(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_2(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_U_2(p);
+}
+
+static inline uint32_t
+get_be_u_3(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_3(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_U_3(p);
+}
+
+static inline uint32_t
+get_be_u_4(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_U_4(p);
+}
+
+static inline uint64_t
+get_be_u_5(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_5(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_U_5(p);
+}
+
+static inline uint64_t
+get_be_u_6(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_6(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_U_6(p);
+}
+
+static inline uint64_t
+get_be_u_7(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_7(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_U_7(p);
+}
+
+static inline uint64_t
+get_be_u_8(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_8(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_U_8(p);
+}
+
+/* get_be_s_N */
+
+static inline int16_t
+get_be_s_2(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_2(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_S_2(p);
+}
+
+static inline int32_t
+get_be_s_3(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_3(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_S_3(p);
+}
+
+static inline int32_t
+get_be_s_4(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_S_4(p);
+}
+
+static inline int64_t
+get_be_s_5(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_5(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_S_5(p);
+}
+
+static inline int64_t
+get_be_s_6(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_6(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_S_6(p);
+}
+
+static inline int64_t
+get_be_s_7(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_7(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_S_7(p);
+}
+
+static inline int64_t
+get_be_s_8(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_8(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_BE_S_8(p);
+}
+
+/* get_he_u_N */
+
+static inline uint16_t
+get_he_u_2(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_2(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_HE_U_2(p);
+}
+
+static inline uint32_t
+get_he_u_4(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_HE_U_4(p);
+}
+
+/* get_he_s_N */
+
+static inline int16_t
+get_he_s_2(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_2(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_HE_S_2(p);
+}
+
+static inline int32_t
+get_he_s_4(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_HE_S_4(p);
+}
+
+/* get_le_u_N */
+
+static inline uint16_t
+get_le_u_2(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_2(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_U_2(p);
+}
+
+static inline uint32_t
+get_le_u_3(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_3(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_U_3(p);
+}
+
+static inline uint32_t
+get_le_u_4(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_U_4(p);
+}
+
+static inline uint64_t
+get_le_u_5(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_5(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_U_5(p);
+}
+
+static inline uint64_t
+get_le_u_6(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_6(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_U_6(p);
+}
+
+static inline uint64_t
+get_le_u_7(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_7(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_U_7(p);
+}
+
+static inline uint64_t
+get_le_u_8(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_8(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_U_8(p);
+}
+
+/* get_le_s_N */
+
+static inline int16_t
+get_le_s_2(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_2(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_S_2(p);
+}
+
+static inline int32_t
+get_le_s_3(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_3(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_S_3(p);
+}
+
+static inline int32_t
+get_le_s_4(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_S_4(p);
+}
+
+static inline int64_t
+get_le_s_8(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_8(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_LE_S_8(p);
+}
+
+/* get_ipv4_to_{host|network]_order */
+
+static inline uint32_t
+get_ipv4_to_host_order(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_IPV4_TO_HOST_ORDER(p);
+}
+
+static inline uint32_t
+get_ipv4_to_network_order(netdissect_options *ndo, const u_char *p)
+{
+ if (!ND_TTEST_4(p))
+ nd_trunc_longjmp(ndo);
+ return EXTRACT_IPV4_TO_NETWORK_ORDER(p);
+}
+
+static inline void
+get_cpy_bytes(netdissect_options *ndo, u_char *dst, const u_char *p, size_t len)
+{
+ if (!ND_TTEST_LEN(p, len))
+ nd_trunc_longjmp(ndo);
+ UNALIGNED_MEMCPY(dst, p, len);
+}
+
+#define GET_U_1(p) get_u_1(ndo, (const u_char *)(p))
+#define GET_S_1(p) get_s_1(ndo, (const u_char *)(p))
+
+#define GET_BE_U_2(p) get_be_u_2(ndo, (const u_char *)(p))
+#define GET_BE_U_3(p) get_be_u_3(ndo, (const u_char *)(p))
+#define GET_BE_U_4(p) get_be_u_4(ndo, (const u_char *)(p))
+#define GET_BE_U_5(p) get_be_u_5(ndo, (const u_char *)(p))
+#define GET_BE_U_6(p) get_be_u_6(ndo, (const u_char *)(p))
+#define GET_BE_U_7(p) get_be_u_7(ndo, (const u_char *)(p))
+#define GET_BE_U_8(p) get_be_u_8(ndo, (const u_char *)(p))
+
+#define GET_BE_S_2(p) get_be_s_2(ndo, (const u_char *)(p))
+#define GET_BE_S_3(p) get_be_s_3(ndo, (const u_char *)(p))
+#define GET_BE_S_4(p) get_be_s_4(ndo, (const u_char *)(p))
+#define GET_BE_S_5(p) get_be_s_5(ndo, (const u_char *)(p))
+#define GET_BE_S_6(p) get_be_s_6(ndo, (const u_char *)(p))
+#define GET_BE_S_7(p) get_be_s_7(ndo, (const u_char *)(p))
+#define GET_BE_S_8(p) get_be_s_8(ndo, (const u_char *)(p))
+
+#define GET_HE_U_2(p) get_he_u_2(ndo, (const u_char *)(p))
+#define GET_HE_U_4(p) get_he_u_4(ndo, (const u_char *)(p))
+
+#define GET_HE_S_2(p) get_he_s_2(ndo, (const u_char *)(p))
+#define GET_HE_S_4(p) get_he_s_4(ndo, (const u_char *)(p))
+
+#define GET_LE_U_2(p) get_le_u_2(ndo, (const u_char *)(p))
+#define GET_LE_U_3(p) get_le_u_3(ndo, (const u_char *)(p))
+#define GET_LE_U_4(p) get_le_u_4(ndo, (const u_char *)(p))
+#define GET_LE_U_5(p) get_le_u_5(ndo, (const u_char *)(p))
+#define GET_LE_U_6(p) get_le_u_6(ndo, (const u_char *)(p))
+#define GET_LE_U_7(p) get_le_u_7(ndo, (const u_char *)(p))
+#define GET_LE_U_8(p) get_le_u_8(ndo, (const u_char *)(p))
+
+#define GET_LE_S_2(p) get_le_s_2(ndo, (const u_char *)(p))
+#define GET_LE_S_3(p) get_le_s_3(ndo, (const u_char *)(p))
+#define GET_LE_S_4(p) get_le_s_4(ndo, (const u_char *)(p))
+#define GET_LE_S_8(p) get_le_s_8(ndo, (const u_char *)(p))
+
+#define GET_IPV4_TO_HOST_ORDER(p) get_ipv4_to_host_order(ndo, (const u_char *)(p))
+#define GET_IPV4_TO_NETWORK_ORDER(p) get_ipv4_to_network_order(ndo, (const u_char *)(p))
+
+#define GET_CPY_BYTES(dst, p, len) get_cpy_bytes(ndo, (u_char *)(dst), (const u_char *)(p), len)
+
+#endif /* EXTRACT_H */
diff --git a/fptype.c b/fptype.c
new file mode 100644
index 0000000..8a209bb
--- /dev/null
+++ b/fptype.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+
+#include "netdissect-stdinc.h"
+
+#include "fptype.h"
+
+void
+float_type_check(uint32_t in)
+{
+ union { /* int to float conversion buffer */
+ float f;
+ uint32_t i;
+ } f;
+
+ f.i = in;
+ printf("%.3f\n", f.f*8/1000000);
+}
diff --git a/fptype.h b/fptype.h
new file mode 100644
index 0000000..ad435bd
--- /dev/null
+++ b/fptype.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+extern void float_type_check(uint32_t);
diff --git a/funcattrs.h b/funcattrs.h
new file mode 100644
index 0000000..f37e07e
--- /dev/null
+++ b/funcattrs.h
@@ -0,0 +1,148 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
+/*
+ * Copyright (c) 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lib_funcattrs_h
+#define lib_funcattrs_h
+
+#include "compiler-tests.h"
+
+/*
+ * Attributes to apply to functions and their arguments, using various
+ * compiler-specific extensions.
+ */
+
+/*
+ * This was introduced by Clang:
+ *
+ * https://clang.llvm.org/docs/LanguageExtensions.html#has-attribute
+ *
+ * in some version (which version?); it has been picked up by GCC 5.0.
+ */
+#ifndef __has_attribute
+ /*
+ * It's a macro, so you can check whether it's defined to check
+ * whether it's supported.
+ *
+ * If it's not, define it to always return 0, so that we move on to
+ * the fallback checks.
+ */
+ #define __has_attribute(x) 0
+#endif
+
+/*
+ * NORETURN, before a function declaration, means "this function
+ * never returns". (It must go before the function declaration, e.g.
+ * "extern NORETURN func(...)" rather than after the function
+ * declaration, as the MSVC version has to go before the declaration.)
+ */
+#if __has_attribute(noreturn) \
+ || ND_IS_AT_LEAST_GNUC_VERSION(2,5) \
+ || ND_IS_AT_LEAST_SUNC_VERSION(5,9) \
+ || ND_IS_AT_LEAST_XL_C_VERSION(10,1) \
+ || ND_IS_AT_LEAST_HP_C_VERSION(6,10)
+ /*
+ * Compiler with support for __attribute((noreturn)), or GCC 2.5 and
+ * later, or Solaris Studio 12 (Sun C 5.9) and later, or IBM XL C 10.1
+ * and later (do any earlier versions of XL C support this?), or
+ * HP aCC A.06.10 and later.
+ */
+ #define NORETURN __attribute((noreturn))
+
+ /*
+ * However, GCC didn't support that for function *pointers* until GCC
+ * 4.1.0; see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3481.
+ *
+ * Sun C/Oracle Studio C doesn't seem to support it, either.
+ */
+ #if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) < 401)) \
+ || (defined(__SUNPRO_C))
+ #define NORETURN_FUNCPTR
+ #else
+ #define NORETURN_FUNCPTR __attribute((noreturn))
+ #endif
+#elif defined(_MSC_VER)
+ /*
+ * MSVC.
+ * It doesn't allow __declspec(noreturn) to be applied to function
+ * pointers.
+ */
+ #define NORETURN __declspec(noreturn)
+ #define NORETURN_FUNCPTR
+#else
+ #define NORETURN
+ #define NORETURN_FUNCPTR
+#endif
+
+/*
+ * PRINTFLIKE(x,y), after a function declaration, means "this function
+ * does printf-style formatting, with the xth argument being the format
+ * string and the yth argument being the first argument for the format
+ * string".
+ */
+#if __has_attribute(__format__) \
+ || ND_IS_AT_LEAST_GNUC_VERSION(2,3) \
+ || ND_IS_AT_LEAST_XL_C_VERSION(10,1) \
+ || ND_IS_AT_LEAST_HP_C_VERSION(6,10)
+ /*
+ * Compiler with support for it, or GCC 2.3 and later, or IBM XL C 10.1
+ * and later (do any earlier versions of XL C support this?),
+ * or HP aCC A.06.10 and later.
+ */
+ #define PRINTFLIKE(x,y) __attribute__((__format__(__printf__,x,y)))
+
+ /*
+ * However, GCC didn't support that for function *pointers* until GCC
+ * 4.1.0; see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3481.
+ */
+ #if (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) < 401))
+ #define PRINTFLIKE_FUNCPTR(x,y)
+ #else
+ #define PRINTFLIKE_FUNCPTR(x,y) __attribute__((__format__(__printf__,x,y)))
+ #endif
+#else
+ #define PRINTFLIKE(x,y)
+ #define PRINTFLIKE_FUNCPTR(x,y)
+#endif
+
+/*
+ * For flagging arguments as format strings in MSVC.
+ */
+#ifdef _MSC_VER
+ #include <sal.h>
+ #define FORMAT_STRING(p) _Printf_format_string_ p
+#else
+ #define FORMAT_STRING(p) p
+#endif
+
+#endif /* lib_funcattrs_h */
diff --git a/getservent.h b/getservent.h
new file mode 100644
index 0000000..6fa5f36
--- /dev/null
+++ b/getservent.h
@@ -0,0 +1,67 @@
+/*
+* Copyright (c) 1983, 1993 The Regents of the University of California.
+* Copyright (c) 1993 Digital Equipment Corporation.
+* Copyright (c) 2012 G. Vanem <gvanem@yahoo.no>.
+* Copyright (c) 2017 Ali Abdulkadir <autostart.ini@gmail.com>.
+* All rights reserved.
+*
+* Redistribution and use in source and binary forms, with or without
+* modification, are permitted provided that the following conditions
+* are met:
+* 1. Redistributions of source code must retain the above copyright
+* notice, this list of conditions and the following disclaimer.
+* 2. Redistributions in binary form must reproduce the above copyright
+* notice, this list of conditions and the following disclaimer in the
+* documentation and/or other materials provided with the distribution.
+* 3. All advertising materials mentioning features or use of this software
+* must display the following acknowledgement:
+* This product includes software developed by the University of
+* California, Berkeley and its contributors.
+* 4. Neither the name of the University nor the names of its contributors
+* may be used to endorse or promote products derived from this software
+* without specific prior written permission.
+*
+* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+* SUCH DAMAGE.
+*/
+
+#ifndef ND_GETSERVENT_H
+#define ND_GETSERVENT_H
+
+#ifdef _NETDB_H_
+/* Just in case... */
+#error netdb.h and getservent.h are incompatible
+#else
+#define _NETDB_H_
+#endif
+
+#ifdef _WIN32
+#define __PATH_SYSROOT "SYSTEMROOT"
+#define __PATH_ETC_INET "\\System32\\drivers\\etc\\"
+#define __PATH_SERVICES "services"
+#else
+/*
+* The idea here is to be able to replace "PREFIX" in __PATH_SYSROOT with a variable
+* that could, for example, point to an alternative install location.
+*/
+#define __PATH_SYSROOT "PREFIX"
+#define __PATH_ETC_INET "/etc/"
+#define __PATH_SERVICES __PATH_ETC_INET"services"
+#endif
+
+#define MAXALIASES 35
+
+void endservent (void);
+struct servent *getservent(void);
+void setservent (int f);
+
+#endif /* ! ND_GETSERVENT_H */
diff --git a/gmpls.c b/gmpls.c
new file mode 100644
index 0000000..f0646f1
--- /dev/null
+++ b/gmpls.c
@@ -0,0 +1,192 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "gmpls.h"
+
+/* rfc3471 */
+const struct tok gmpls_link_prot_values[] = {
+ { 0x01, "Extra Traffic"},
+ { 0x02, "Unprotected"},
+ { 0x04, "Shared"},
+ { 0x08, "Dedicated 1:1"},
+ { 0x10, "Dedicated 1+1"},
+ { 0x20, "Enhanced"},
+ { 0x40, "Reserved"},
+ { 0x80, "Reserved"},
+ { 0, NULL }
+};
+
+/* rfc3471 */
+const struct tok gmpls_switch_cap_values[] = {
+ { GMPLS_PSC1, "Packet-Switch Capable-1"},
+ { GMPLS_PSC2, "Packet-Switch Capable-2"},
+ { GMPLS_PSC3, "Packet-Switch Capable-3"},
+ { GMPLS_PSC4, "Packet-Switch Capable-4"},
+ { GMPLS_L2SC, "Layer-2 Switch Capable"},
+ { GMPLS_TSC, "Time-Division-Multiplex"},
+ { GMPLS_LSC, "Lambda-Switch Capable"},
+ { GMPLS_FSC, "Fiber-Switch Capable"},
+ { 0, NULL }
+};
+
+/* rfc4205 */
+const struct tok gmpls_switch_cap_tsc_indication_values[] = {
+ { 0, "Standard SONET/SDH" },
+ { 1, "Arbitrary SONET/SDH" },
+ { 0, NULL }
+};
+
+/* rfc3471 */
+const struct tok gmpls_encoding_values[] = {
+ { 1, "Packet"},
+ { 2, "Ethernet V2/DIX"},
+ { 3, "ANSI/ETSI PDH"},
+ { 4, "Reserved"},
+ { 5, "SDH ITU-T G.707/SONET ANSI T1.105"},
+ { 6, "Reserved"},
+ { 7, "Digital Wrapper"},
+ { 8, "Lambda (photonic)"},
+ { 9, "Fiber"},
+ { 10, "Reserved"},
+ { 11, "FiberChannel"},
+ { 0, NULL }
+};
+
+/* rfc3471 */
+const struct tok gmpls_payload_values[] = {
+ { 0, "Unknown"},
+ { 1, "Reserved"},
+ { 2, "Reserved"},
+ { 3, "Reserved"},
+ { 4, "Reserved"},
+ { 5, "Asynchronous mapping of E4"},
+ { 6, "Asynchronous mapping of DS3/T3"},
+ { 7, "Asynchronous mapping of E3"},
+ { 8, "Bit synchronous mapping of E3"},
+ { 9, "Byte synchronous mapping of E3"},
+ { 10, "Asynchronous mapping of DS2/T2"},
+ { 11, "Bit synchronous mapping of DS2/T2"},
+ { 12, "Reserved"},
+ { 13, "Asynchronous mapping of E1"},
+ { 14, "Byte synchronous mapping of E1"},
+ { 15, "Byte synchronous mapping of 31 * DS0"},
+ { 16, "Asynchronous mapping of DS1/T1"},
+ { 17, "Bit synchronous mapping of DS1/T1"},
+ { 18, "Byte synchronous mapping of DS1/T1"},
+ { 19, "VC-11 in VC-12"},
+ { 20, "Reserved"},
+ { 21, "Reserved"},
+ { 22, "DS1 SF Asynchronous"},
+ { 23, "DS1 ESF Asynchronous"},
+ { 24, "DS3 M23 Asynchronous"},
+ { 25, "DS3 C-Bit Parity Asynchronous"},
+ { 26, "VT/LOVC"},
+ { 27, "STS SPE/HOVC"},
+ { 28, "POS - No Scrambling, 16 bit CRC"},
+ { 29, "POS - No Scrambling, 32 bit CRC"},
+ { 30, "POS - Scrambling, 16 bit CRC"},
+ { 31, "POS - Scrambling, 32 bit CRC"},
+ { 32, "ATM mapping"},
+ { 33, "Ethernet PHY"},
+ { 34, "SONET/SDH"},
+ { 35, "Reserved (SONET deprecated)"},
+ { 36, "Digital Wrapper"},
+ { 37, "Lambda"},
+ { 38, "ANSI/ETSI PDH"},
+ { 39, "Reserved"},
+ { 40, "Link Access Protocol SDH (X.85 and X.86)"},
+ { 41, "FDDI"},
+ { 42, "DQDB (ETSI ETS 300 216)"},
+ { 43, "FiberChannel-3 (Services)"},
+ { 44, "HDLC"},
+ { 45, "Ethernet V2/DIX (only)"},
+ { 46, "Ethernet 802.3 (only)"},
+/* draft-ietf-ccamp-gmpls-g709-04.txt */
+ { 47, "G.709 ODUj"},
+ { 48, "G.709 OTUk(v)"},
+ { 49, "CBR/CBRa"},
+ { 50, "CBRb"},
+ { 51, "BSOT"},
+ { 52, "BSNT"},
+ { 53, "IP/PPP (GFP)"},
+ { 54, "Ethernet MAC (framed GFP)"},
+ { 55, "Ethernet PHY (transparent GFP)"},
+ { 56, "ESCON"},
+ { 57, "FICON"},
+ { 58, "Fiber Channel"},
+ { 0, NULL }
+};
+
+/*
+ * Link Type values used by LMP Service Discovery (specifically, the Client
+ * Port Service Attributes Object). See UNI 1.0 section 9.4.2 for details.
+ */
+const struct tok lmp_sd_service_config_cpsa_link_type_values[] = {
+ { 5, "SDH ITU-T G.707"},
+ { 6, "SONET ANSI T1.105"},
+ { 0, NULL}
+};
+
+/*
+ * Signal Type values for SDH links used by LMP Service Discovery (specifically,
+ * the Client Port Service Attributes Object). See UNI 1.0 section 9.4.2 for
+ * details.
+ */
+const struct tok lmp_sd_service_config_cpsa_signal_type_sdh_values[] = {
+ { 5, "VC-3"},
+ { 6, "VC-4"},
+ { 7, "STM-0"},
+ { 8, "STM-1"},
+ { 9, "STM-4"},
+ { 10, "STM-16"},
+ { 11, "STM-64"},
+ { 12, "STM-256"},
+ { 0, NULL}
+};
+
+/*
+ * Signal Type values for SONET links used by LMP Service Discovery (specifically,
+ * the Client Port Service Attributes Object). See UNI 1.0 section 9.4.2 for
+ * details.
+ */
+const struct tok lmp_sd_service_config_cpsa_signal_type_sonet_values[] = {
+ { 5, "STS-1 SPE"},
+ { 6, "STS-3c SPE"},
+ { 7, "STS-1"},
+ { 8, "STM-3"},
+ { 9, "STM-12"},
+ { 10, "STM-48"},
+ { 11, "STM-192"},
+ { 12, "STM-768"},
+ { 0, NULL}
+};
+
+#define DIFFSERV_BC_MODEL_RDM 0 /* draft-ietf-tewg-diff-te-proto-07 */
+#define DIFFSERV_BC_MODEL_MAM 1 /* draft-ietf-tewg-diff-te-proto-07 */
+#define DIFFSERV_BC_MODEL_EXTD_MAM 254 /* experimental */
+
+const struct tok diffserv_te_bc_values[] = {
+ { DIFFSERV_BC_MODEL_RDM, "Russian dolls"},
+ { DIFFSERV_BC_MODEL_MAM, "Maximum allocation"},
+ { DIFFSERV_BC_MODEL_EXTD_MAM, "Maximum allocation with E-LSP support"},
+ { 0, NULL }
+};
diff --git a/gmpls.h b/gmpls.h
new file mode 100644
index 0000000..32fa811
--- /dev/null
+++ b/gmpls.h
@@ -0,0 +1,33 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#define GMPLS_PSC1 1
+#define GMPLS_PSC2 2
+#define GMPLS_PSC3 3
+#define GMPLS_PSC4 4
+#define GMPLS_L2SC 51
+#define GMPLS_TSC 100
+#define GMPLS_LSC 150
+#define GMPLS_FSC 200
+
+extern const struct tok gmpls_link_prot_values[];
+extern const struct tok gmpls_switch_cap_values[];
+extern const struct tok gmpls_switch_cap_tsc_indication_values[];
+extern const struct tok gmpls_encoding_values[];
+extern const struct tok gmpls_payload_values[];
+extern const struct tok diffserv_te_bc_values[];
+extern const struct tok lmp_sd_service_config_cpsa_link_type_values[];
+extern const struct tok lmp_sd_service_config_cpsa_signal_type_sdh_values[];
+extern const struct tok lmp_sd_service_config_cpsa_signal_type_sonet_values[];
diff --git a/in_cksum.c b/in_cksum.c
new file mode 100644
index 0000000..eb7c634
--- /dev/null
+++ b/in_cksum.c
@@ -0,0 +1,200 @@
+/* in_cksum.c
+ * 4.4-Lite-2 Internet checksum routine, modified to take a vector of
+ * pointers/lengths giving the pieces to be checksummed. Also using
+ * Tahoe/CGI version of ADDCARRY(x) macro instead of from portable version.
+ */
+
+/*
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) {if ((x) > 65535) (x) -= 65535;}
+#define REDUCE {l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum);}
+
+uint16_t
+in_cksum(const struct cksum_vec *vec, int veclen)
+{
+ const uint16_t *w;
+ int sum = 0;
+ int mlen = 0;
+ int byte_swapped = 0;
+
+ union {
+ uint8_t c[2];
+ uint16_t s;
+ } s_util;
+ union {
+ uint16_t s[2];
+ uint32_t l;
+ } l_util;
+
+ for (; veclen != 0; vec++, veclen--) {
+ if (vec->len == 0)
+ continue;
+ w = (const uint16_t *)(const void *)vec->ptr;
+ if (mlen == -1) {
+ /*
+ * The first byte of this chunk is the continuation
+ * of a word spanning between this chunk and the
+ * last chunk.
+ *
+ * s_util.c[0] is already saved when scanning previous
+ * chunk.
+ */
+ s_util.c[1] = *(const uint8_t *)w;
+ sum += s_util.s;
+ w = (const uint16_t *)(const void *)((const uint8_t *)w + 1);
+ mlen = vec->len - 1;
+ } else
+ mlen = vec->len;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (uintptr_t) w) && (mlen > 0)) {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(const uint8_t *)w;
+ w = (const uint16_t *)(const void *)((const uint8_t *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ sum += w[4]; sum += w[5]; sum += w[6]; sum += w[7];
+ sum += w[8]; sum += w[9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0) {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ continue;
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+ if (byte_swapped) {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ s_util.c[1] = *(const uint8_t *)w;
+ sum += s_util.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ s_util.c[0] = *(const uint8_t *)w;
+ }
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
+
+/*
+ * Given the host-byte-order value of the checksum field in a packet
+ * header, and the network-byte-order computed checksum of the data
+ * that the checksum covers (including the checksum itself), compute
+ * what the checksum field *should* have been.
+ */
+uint16_t
+in_cksum_shouldbe(uint16_t sum, uint16_t computed_sum)
+{
+ uint32_t shouldbe;
+
+ /*
+ * The value that should have gone into the checksum field
+ * is the negative of the value gotten by summing up everything
+ * *but* the checksum field.
+ *
+ * We can compute that by subtracting the value of the checksum
+ * field from the sum of all the data in the packet, and then
+ * computing the negative of that value.
+ *
+ * "sum" is the value of the checksum field, and "computed_sum"
+ * is the negative of the sum of all the data in the packets,
+ * so that's -(-computed_sum - sum), or (sum + computed_sum).
+ *
+ * All the arithmetic in question is one's complement, so the
+ * addition must include an end-around carry; we do this by
+ * doing the arithmetic in 32 bits (with no sign-extension),
+ * and then adding the upper 16 bits of the sum, which contain
+ * the carry, to the lower 16 bits of the sum, and then do it
+ * again in case *that* sum produced a carry.
+ *
+ * As RFC 1071 notes, the checksum can be computed without
+ * byte-swapping the 16-bit words; summing 16-bit words
+ * on a big-endian machine gives a big-endian checksum, which
+ * can be directly stuffed into the big-endian checksum fields
+ * in protocol headers, and summing words on a little-endian
+ * machine gives a little-endian checksum, which must be
+ * byte-swapped before being stuffed into a big-endian checksum
+ * field.
+ *
+ * "computed_sum" is a network-byte-order value, so we must put
+ * it in host byte order before subtracting it from the
+ * host-byte-order value from the header; the adjusted checksum
+ * will be in host byte order, which is what we'll return.
+ */
+ shouldbe = sum;
+ shouldbe += ntohs(computed_sum);
+ shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
+ shouldbe = (shouldbe & 0xFFFF) + (shouldbe >> 16);
+ return (uint16_t)shouldbe;
+}
diff --git a/interface.h b/interface.h
new file mode 100644
index 0000000..d54172e
--- /dev/null
+++ b/interface.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 1988-2002
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef tcpdump_interface_h
+#define tcpdump_interface_h
+
+#ifdef HAVE_OS_PROTO_H
+#include "os-proto.h"
+#endif
+
+#include "funcattrs.h"
+
+#include <stdarg.h>
+
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+
+#ifndef HAVE_STRLCAT
+extern size_t strlcat(char *, const char *, size_t);
+#endif
+#ifndef HAVE_STRLCPY
+extern size_t strlcpy(char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRDUP
+extern char *strdup(const char *);
+#endif
+
+#ifndef HAVE_STRSEP
+extern char *strsep(char **, const char *);
+#endif
+
+#endif
+
+extern char *program_name; /* used to generate self-identifying messages */
+
+#include <pcap.h>
+
+#ifndef HAVE_BPF_DUMP
+struct bpf_program;
+
+extern void bpf_dump(const struct bpf_program *, int);
+
+#endif
diff --git a/ip.h b/ip.h
new file mode 100644
index 0000000..ca87548
--- /dev/null
+++ b/ip.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip.h 8.2 (Berkeley) 6/1/94
+ */
+
+#ifndef netdissect_ip_h
+#define netdissect_ip_h
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ *
+ * We declare ip_len and ip_off to be short, rather than u_short
+ * pragmatically since otherwise unsigned comparisons can result
+ * against negative integers quite easily, and fail in subtle ways.
+ */
+struct ip {
+ nd_uint8_t ip_vhl; /* header length, version */
+#define IP_V(ip) ((GET_U_1((ip)->ip_vhl) & 0xf0) >> 4)
+#define IP_HL(ip) (GET_U_1((ip)->ip_vhl) & 0x0f)
+ nd_uint8_t ip_tos; /* type of service */
+ nd_uint16_t ip_len; /* total length */
+ nd_uint16_t ip_id; /* identification */
+ nd_uint16_t ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* don't fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ nd_uint8_t ip_ttl; /* time to live */
+ nd_uint8_t ip_p; /* protocol */
+ nd_uint16_t ip_sum; /* checksum */
+ nd_ipv4 ip_src,ip_dst; /* source and dest address */
+};
+
+#define IP_MAXPACKET 65535 /* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+
+/*
+ * Definitions for IP precedence (also in ip_tos) (hopefully unused)
+ */
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x00
+
+/*
+ * Definitions for options.
+ */
+#define IPOPT_COPIED(o) ((o)&0x80)
+#define IPOPT_CLASS(o) ((o)&0x60)
+#define IPOPT_NUMBER(o) ((o)&0x1f)
+
+#define IPOPT_CONTROL 0x00
+#define IPOPT_RESERVED1 0x20
+#define IPOPT_DEBMEAS 0x40
+#define IPOPT_RESERVED2 0x60
+
+#define IPOPT_EOL 0 /* end of option list */
+#define IPOPT_NOP 1 /* no operation */
+
+#define IPOPT_RR 7 /* record packet route */
+#define IPOPT_TS 68 /* timestamp */
+#define IPOPT_RFC1393 82 /* traceroute RFC 1393 */
+#define IPOPT_SECURITY 130 /* provide s,c,h,tcc */
+#define IPOPT_LSRR 131 /* loose source route */
+#define IPOPT_SATID 136 /* satnet id */
+#define IPOPT_SSRR 137 /* strict source route */
+#define IPOPT_RA 148 /* router-alert, rfc2113 */
+
+/*
+ * Offsets to fields in options other than EOL and NOP.
+ */
+#define IPOPT_OPTVAL 0 /* option ID */
+#define IPOPT_OLEN 1 /* option length */
+#define IPOPT_OFFSET 2 /* offset within option */
+#define IPOPT_MINOFF 4 /* min value of above */
+
+/*
+ * Time stamp option structure.
+ */
+struct ip_timestamp {
+ nd_uint8_t ipt_code; /* IPOPT_TS */
+ nd_uint8_t ipt_len; /* size of structure (variable) */
+ nd_uint8_t ipt_ptr; /* index of current entry */
+ nd_uint8_t ipt_oflwflg; /* flags, overflow counter */
+#define IPTS_OFLW(ip) (((ipt)->ipt_oflwflg & 0xf0) >> 4)
+#define IPTS_FLG(ip) ((ipt)->ipt_oflwflg & 0x0f)
+ union ipt_timestamp {
+ nd_uint32_t ipt_time[1];
+ struct ipt_ta {
+ nd_ipv4 ipt_addr;
+ nd_uint32_t ipt_time;
+ } ipt_ta[1];
+ } ipt_timestamp;
+};
+
+/* flag bits for ipt_flg */
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 3 /* specified modules only */
+
+/* bits for security (not byte swapped) */
+#define IPOPT_SECUR_UNCLASS 0x0000
+#define IPOPT_SECUR_CONFID 0xf135
+#define IPOPT_SECUR_EFTO 0x789a
+#define IPOPT_SECUR_MMMM 0xbc4d
+#define IPOPT_SECUR_RESTR 0xaf13
+#define IPOPT_SECUR_SECRET 0xd788
+#define IPOPT_SECUR_TOPSECRET 0x6bc5
+
+/*
+ * Internet implementation parameters.
+ */
+#define MAXTTL 255 /* maximum time to live (seconds) */
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#define IPFRAGTTL 60 /* time to live for frags, slowhz */
+#define IPTTLDEC 1 /* subtracted when forwarding */
+
+#define IP_MSS 576 /* default maximum segment size */
+#endif /* netdissect_ip_h */
diff --git a/ip6.h b/ip6.h
new file mode 100644
index 0000000..28725d0
--- /dev/null
+++ b/ip6.h
@@ -0,0 +1,212 @@
+/* NetBSD: ip6.h,v 1.9 2000/07/13 05:34:21 itojun Exp */
+/* $KAME: ip6.h,v 1.9 2000/07/02 21:01:32 itojun Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ip.h 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef ND_IP6_H_
+#define ND_IP6_H_
+
+/*
+ * Definition for internet protocol version 6.
+ * RFC 2460
+ */
+
+struct ip6_hdr {
+ union {
+ struct ip6_hdrctl {
+ nd_uint32_t ip6_un1_flow; /* 20 bits of flow-ID */
+ nd_uint16_t ip6_un1_plen; /* payload length */
+ nd_uint8_t ip6_un1_nxt; /* next header */
+ nd_uint8_t ip6_un1_hlim; /* hop limit */
+ } ip6_un1;
+ nd_uint8_t ip6_un2_vfc; /* 4 bits version, top 4 bits class */
+ } ip6_ctlun;
+ nd_ipv6 ip6_src; /* source address */
+ nd_ipv6 ip6_dst; /* destination address */
+};
+
+#define ip6_vfc ip6_ctlun.ip6_un2_vfc
+#define IP6_VERSION(ip6_hdr) ((GET_U_1((ip6_hdr)->ip6_vfc) & 0xf0) >> 4)
+#define ip6_flow ip6_ctlun.ip6_un1.ip6_un1_flow
+#define ip6_plen ip6_ctlun.ip6_un1.ip6_un1_plen
+#define ip6_nxt ip6_ctlun.ip6_un1.ip6_un1_nxt
+#define ip6_hlim ip6_ctlun.ip6_un1.ip6_un1_hlim
+#define ip6_hops ip6_ctlun.ip6_un1.ip6_un1_hlim
+
+/* in network endian */
+#define IPV6_FLOWINFO_MASK ((uint32_t)htonl(0x0fffffff)) /* flow info (28 bits) */
+#define IPV6_FLOWLABEL_MASK ((uint32_t)htonl(0x000fffff)) /* flow label (20 bits) */
+
+/*
+ * Extension Headers
+ */
+
+struct ip6_ext {
+ nd_uint8_t ip6e_nxt;
+ nd_uint8_t ip6e_len;
+};
+
+/* Hop-by-Hop options header */
+struct ip6_hbh {
+ nd_uint8_t ip6h_nxt; /* next header */
+ nd_uint8_t ip6h_len; /* length in units of 8 octets */
+ /* followed by options */
+};
+
+/* Destination options header */
+struct ip6_dest {
+ nd_uint8_t ip6d_nxt; /* next header */
+ nd_uint8_t ip6d_len; /* length in units of 8 octets */
+ /* followed by options */
+};
+
+/* https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml */
+
+/* Option types and related macros */
+#define IP6OPT_PAD1 0x00 /* 00 0 00000 */
+#define IP6OPT_PADN 0x01 /* 00 0 00001 */
+#define IP6OPT_JUMBO 0xC2 /* 11 0 00010 = 194 */
+#define IP6OPT_JUMBO_LEN 6
+#define IP6OPT_RPL 0x63 /* 01 1 00011 */
+#define IP6OPT_TUN_ENC_LIMIT 0x04 /* 00 0 00100 */
+#define IP6OPT_ROUTER_ALERT 0x05 /* 00 0 00101 */
+
+#define IP6OPT_RTALERT_LEN 4
+#define IP6OPT_RTALERT_MLD 0 /* Datagram contains an MLD message */
+#define IP6OPT_RTALERT_RSVP 1 /* Datagram contains an RSVP message */
+#define IP6OPT_RTALERT_ACTNET 2 /* contains an Active Networks msg */
+#define IP6OPT_MINLEN 2
+
+#define IP6OPT_QUICK_START 0x26 /* 00 1 00110 */
+#define IP6OPT_CALIPSO 0x07 /* 00 0 00111 */
+#define IP6OPT_SMF_DPD 0x08 /* 00 0 01000 */
+#define IP6OPT_HOME_ADDRESS 0xc9 /* 11 0 01001 */
+#define IP6OPT_HOMEADDR_MINLEN 18
+#define IP6OPT_EID 0x8a /* 10 0 01010 */
+#define IP6OPT_ILNP_NOTICE 0x8b /* 10 0 01011 */
+#define IP6OPT_LINE_ID 0x8c /* 10 0 01100 */
+#define IP6OPT_MPL 0x6d /* 01 1 01101 */
+#define IP6OPT_IP_DFF 0xee /* 11 1 01110 */
+
+#define IP6OPT_TYPE(o) ((o) & 0xC0)
+#define IP6OPT_TYPE_SKIP 0x00
+#define IP6OPT_TYPE_DISCARD 0x40
+#define IP6OPT_TYPE_FORCEICMP 0x80
+#define IP6OPT_TYPE_ICMP 0xC0
+
+#define IP6OPT_MUTABLE 0x20
+
+/* Routing header */
+struct ip6_rthdr {
+ nd_uint8_t ip6r_nxt; /* next header */
+ nd_uint8_t ip6r_len; /* length in units of 8 octets */
+ nd_uint8_t ip6r_type; /* routing type */
+ nd_uint8_t ip6r_segleft; /* segments left */
+ /* followed by routing type specific data */
+};
+
+#define IPV6_RTHDR_TYPE_0 0
+#define IPV6_RTHDR_TYPE_2 2
+#define IPV6_RTHDR_TYPE_4 4
+
+/* Type 0 Routing header */
+/* Also used for Type 2 */
+struct ip6_rthdr0 {
+ nd_uint8_t ip6r0_nxt; /* next header */
+ nd_uint8_t ip6r0_len; /* length in units of 8 octets */
+ nd_uint8_t ip6r0_type; /* always zero */
+ nd_uint8_t ip6r0_segleft; /* segments left */
+ nd_uint32_t ip6r0_reserved; /* reserved field */
+ nd_ipv6 ip6r0_addr[1]; /* up to 23 addresses */
+};
+
+/**
+ * Type 4 Routing header
+ * known as Segment Routing Header 'SRH'
+ */
+struct ip6_srh {
+ nd_uint8_t srh_nxt; /* next header */
+ nd_uint8_t srh_len; /* length in units of 8 octets */
+ nd_uint8_t srh_type; /* Routing Type 4 */
+ nd_uint8_t srh_segleft; /* segments left */
+ nd_uint8_t srh_last_ent; /* Last Entry*/
+ nd_uint8_t srh_flags; /* Flags */
+ nd_uint16_t srh_tag; /* Tag */
+ nd_ipv6 srh_segments[1]; /* SRH segments list*/
+};
+
+/* Fragment header */
+struct ip6_frag {
+ nd_uint8_t ip6f_nxt; /* next header */
+ nd_uint8_t ip6f_reserved; /* reserved field */
+ nd_uint16_t ip6f_offlg; /* offset, reserved, and flag */
+ nd_uint32_t ip6f_ident; /* identification */
+};
+
+#define IP6F_OFF_MASK 0xfff8 /* mask out offset from ip6f_offlg */
+#define IP6F_RESERVED_MASK 0x0006 /* reserved bits in ip6f_offlg */
+#define IP6F_MORE_FRAG 0x0001 /* more-fragments flag */
+
+#endif /* not ND_IP6_H_ */
diff --git a/ipproto.c b/ipproto.c
new file mode 100644
index 0000000..a3b0714
--- /dev/null
+++ b/ipproto.c
@@ -0,0 +1,362 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "ipproto.h"
+
+const struct tok ipproto_values[] = {
+ { IPPROTO_HOPOPTS, "Options" },
+ { IPPROTO_ICMP, "ICMP" },
+ { IPPROTO_IGMP, "IGMP" },
+ { IPPROTO_IPV4, "IPIP" },
+ { IPPROTO_TCP, "TCP" },
+ { IPPROTO_EGP, "EGP" },
+ { IPPROTO_PIGP, "IGRP" },
+ { IPPROTO_UDP, "UDP" },
+ { IPPROTO_DCCP, "DCCP" },
+ { IPPROTO_IPV6, "IPv6" },
+ { IPPROTO_ROUTING, "Routing" },
+ { IPPROTO_FRAGMENT, "Fragment" },
+ { IPPROTO_RSVP, "RSVP" },
+ { IPPROTO_GRE, "GRE" },
+ { IPPROTO_ESP, "ESP" },
+ { IPPROTO_AH, "AH" },
+ { IPPROTO_MOBILE, "Mobile IP" },
+ { IPPROTO_ICMPV6, "ICMPv6" },
+ { IPPROTO_MOBILITY_OLD, "Mobile IP (old)" },
+ { IPPROTO_EIGRP, "EIGRP" },
+ { IPPROTO_OSPF, "OSPF" },
+ { IPPROTO_PIM, "PIM" },
+ { IPPROTO_IPCOMP, "Compressed IP" },
+ { IPPROTO_VRRP, "VRRP" }, /* See also CARP. */
+ { IPPROTO_PGM, "PGM" },
+ { IPPROTO_SCTP, "SCTP" },
+ { IPPROTO_MOBILITY, "Mobility" },
+ { IPPROTO_ETHERNET, "Ethernet" },
+ { 0, NULL }
+};
+
+/*
+ * For completeness the number space in the array below comes from IANA:
+ * https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
+ * However, the spelling tries to match that of /etc/protocols to achieve as
+ * much consistency as possible with the previously implemented behaviour,
+ * which was based on getprotobynumber (3).
+ */
+static const char *netdb_protocol_names[256] = {
+ "hopopt", /* 0 (IPPROTO_HOPOPTS, IPv6 Hop-by-Hop Option) */
+ "icmp", /* 1 (IPPROTO_ICMP, Internet Control Message) */
+ "igmp", /* 2 (IPPROTO_IGMP, Internet Group Management) */
+ "ggp", /* 3 (Gateway-to-Gateway) */
+ "ipencap", /* 4 (IPPROTO_IPV4, IPv4 encapsulation) */
+ "st", /* 5 (Stream, ST datagram mode) */
+ "tcp", /* 6 (IPPROTO_TCP, Transmission Control) */
+ "cbt", /* 7 (CBT) */
+ "egp", /* 8 (IPPROTO_EGP, Exterior Gateway Protocol) */
+ "igp", /* 9 (IPPROTO_PIGP, "any private interior gateway
+ * (used by Cisco for their IGRP)")
+ */
+ "bbn-rcc-mon", /* 10 (BBN RCC Monitoring) */
+ "nvp-ii", /* 11 (Network Voice Protocol) */
+ "pup", /* 12 (PARC universal packet protocol) */
+ "argus", /* 13 (ARGUS) */
+ "emcon", /* 14 (EMCON) */
+ "xnet", /* 15 (Cross Net Debugger) */
+ "chaos", /* 16 (Chaos) */
+ "udp", /* 17 (IPPROTO_UDP, User Datagram) */
+ "mux", /* 18 (Multiplexing) */
+ "dcn-meas", /* 19 (DCN Measurement Subsystems) */
+ "hmp", /* 20 (Host Monitoring) */
+ "prm", /* 21 (Packet Radio Measurement) */
+ "xns-idp", /* 22 (XEROX NS IDP) */
+ "trunk-1", /* 23 (Trunk-1) */
+ "trunk-2", /* 24 (Trunk-2) */
+ "leaf-1", /* 25 (Leaf-1) */
+ "leaf-2", /* 26 (Leaf-2) */
+ "rdp", /* 27 (Reliable Data Protocol) */
+ "irtp", /* 28 (Internet Reliable Transaction) */
+ "iso-tp4", /* 29 (ISO Transport Protocol Class 4) */
+ "netblt", /* 30 (Bulk Data Transfer Protocol) */
+ "mfe-nsp", /* 31 (MFE Network Services Protocol) */
+ "merit-inp", /* 32 (MERIT Internodal Protocol) */
+ "dccp", /* 33 (IPPROTO_DCCP, Datagram Congestion
+ * Control Protocol)
+ */
+ "3pc", /* 34 (Third Party Connect Protocol) */
+ "idpr", /* 35 (Inter-Domain Policy Routing Protocol) */
+ "xtp", /* 36 (Xpress Transfer Protocol) */
+ "ddp", /* 37 (Datagram Delivery Protocol) */
+ "idpr-cmtp", /* 38 (IDPR Control Message Transport Proto) */
+ "tp++", /* 39 (TP++ Transport Protocol) */
+ "il", /* 40 (IL Transport Protocol) */
+ "ipv6", /* 41 (IPPROTO_IPV6, IPv6 encapsulation) */
+ "sdrp", /* 42 (Source Demand Routing Protocol) */
+ "ipv6-route", /* 43 (IPPROTO_ROUTING, Routing Header for IPv6) */
+ "ipv6-frag", /* 44 (IPPROTO_FRAGMENT, Fragment Header for
+ * IPv6)
+ */
+ "idrp", /* 45 (Inter-Domain Routing Protocol) */
+ "rsvp", /* 46 (IPPROTO_RSVP, Reservation Protocol) */
+ "gre", /* 47 (IPPROTO_GRE, Generic Routing
+ * Encapsulation)
+ */
+ "dsr", /* 48 (Dynamic Source Routing Protocol) */
+ "bna", /* 49 (BNA) */
+ "esp", /* 50 (IPPROTO_ESP, Encap Security Payload) */
+ "ah", /* 51 (IPPROTO_AH, Authentication Header) */
+ "i-nlsp", /* 52 (Integrated Net Layer Security TUBA) */
+ "swipe", /* 53 (IP with Encryption) */
+ "narp", /* 54 (NBMA Address Resolution Protocol) */
+ "mobile", /* 55 (IPPROTO_MOBILE, IP Mobility) */
+ "tlsp", /* 56 (Transport Layer Security Protocol using
+ * Kryptonet key management)
+ */
+ "skip", /* 57 (SKIP) */
+ "ipv6-icmp", /* 58 (IPPROTO_ICMPV6, ICMP for IPv6) */
+ "ipv6-nonxt", /* 59 (IPPROTO_NONE, No Next Header for IPv6) */
+ "ipv6-opts", /* 60 (IPPROTO_DSTOPTS, Destination Options for
+ * IPv6)
+ */
+ NULL, /* 61 (any host internal protocol) */
+ "cftp", /* 62 (IPPROTO_MOBILITY_OLD, CFTP, see the note
+ * in ipproto.h)
+ */
+ NULL, /* 63 (any local network) */
+ "sat-expak", /* 64 (SATNET and Backroom EXPAK) */
+ "kryptolan", /* 65 (Kryptolan) */
+ "rvd", /* 66 (MIT Remote Virtual Disk Protocol) */
+ "ippc", /* 67 (Internet Pluribus Packet Core) */
+ NULL, /* 68 (any distributed file system) */
+ "sat-mon", /* 69 (SATNET Monitoring) */
+ "visa", /* 70 (VISA Protocol) */
+ "ipcv", /* 71 (Internet Packet Core Utility) */
+ "cpnx", /* 72 (Computer Protocol Network Executive) */
+ "rspf", /* 73 (Radio Shortest Path First, CPHB -- Computer
+ * Protocol Heart Beat -- in IANA)
+ */
+ "wsn", /* 74 (Wang Span Network) */
+ "pvp", /* 75 (Packet Video Protocol) */
+ "br-sat-mon", /* 76 (Backroom SATNET Monitoring) */
+ "sun-nd", /* 77 (IPPROTO_ND, SUN ND PROTOCOL-Temporary) */
+ "wb-mon", /* 78 (WIDEBAND Monitoring) */
+ "wb-expak", /* 79 (WIDEBAND EXPAK) */
+ "iso-ip", /* 80 (ISO Internet Protocol) */
+ "vmtp", /* 81 (Versatile Message Transport) */
+ "secure-vmtp", /* 82 (Secure VMTP) */
+ "vines", /* 83 (VINES) */
+ "ttp", /* 84 (Transaction Transport Protocol, also IPTM --
+ * Internet Protocol Traffic Manager)
+ */
+ "nsfnet-igp", /* 85 (NSFNET-IGP) */
+ "dgp", /* 86 (Dissimilar Gateway Protocol) */
+ "tcf", /* 87 (TCF) */
+ "eigrp", /* 88 (IPPROTO_EIGRP, Cisco EIGRP) */
+ "ospf", /* 89 (IPPROTO_OSPF, Open Shortest Path First
+ * IGP)
+ */
+ "sprite-rpc", /* 90 (Sprite RPC Protocol) */
+ "larp", /* 91 (Locus Address Resolution Protocol) */
+ "mtp", /* 92 (Multicast Transport Protocol) */
+ "ax.25", /* 93 (AX.25 Frames) */
+ "ipip", /* 94 (IP-within-IP Encapsulation Protocol) */
+ "micp", /* 95 (Mobile Internetworking Control Pro.) */
+ "scc-sp", /* 96 (Semaphore Communications Sec. Pro.) */
+ "etherip", /* 97 (Ethernet-within-IP Encapsulation) */
+ "encap", /* 98 (Encapsulation Header) */
+ NULL, /* 99 (any private encryption scheme) */
+ "gmtp", /* 100 (GMTP) */
+ "ifmp", /* 101 (Ipsilon Flow Management Protocol) */
+ "pnni", /* 102 (PNNI over IP) */
+ "pim", /* 103 (IPPROTO_PIM, Protocol Independent
+ * Multicast)
+ */
+ "aris", /* 104 (ARIS) */
+ "scps", /* 105 (SCPS) */
+ "qnx", /* 106 (QNX) */
+ "a/n", /* 107 (Active Networks) */
+ "ipcomp", /* 108 (IPPROTO_IPCOMP, IP Payload Compression
+ * Protocol)
+ */
+ "snp", /* 109 (Sitara Networks Protocol) */
+ "compaq-peer", /* 110 (Compaq Peer Protocol) */
+ "ipx-in-ip", /* 111 (IPX in IP) */
+ "vrrp", /* 112 (IPPROTO_VRRP, Virtual Router Redundancy
+ * Protocol)
+ */
+ "pgm", /* 113 (IPPROTO_PGM, PGM Reliable Transport
+ * Protocol)
+ */
+ NULL, /* 114 (any 0-hop protocol) */
+ "l2tp", /* 115 (Layer Two Tunneling Protocol) */
+ "ddx", /* 116 (D-II Data Exchange (DDX)) */
+ "iatp", /* 117 (Interactive Agent Transfer Protocol) */
+ "stp", /* 118 (Schedule Transfer Protocol) */
+ "srp", /* 119 (SpectraLink Radio Protocol) */
+ "uti", /* 120 (UTI) */
+ "smp", /* 121 (Simple Message Protocol) */
+ "sm", /* 122 (Simple Multicast Protocol) */
+ "ptp", /* 123 (Performance Transparency Protocol) */
+ "isis", /* 124 (ISIS over IPv4) */
+ "fire", /* 125 (FIRE) */
+ "crtp", /* 126 (Combat Radio Transport Protocol) */
+ "crudp", /* 127 (Combat Radio User Datagram) */
+ "sscopmce", /* 128 (SSCOPMCE) */
+ "iplt", /* 129 (IPLT) */
+ "sps", /* 130 (Secure Packet Shield) */
+ "pipe", /* 131 (Private IP Encapsulation within IP) */
+ "sctp", /* 132 (IPPROTO_SCTP, Stream Control Transmission
+ * Protocol)
+ */
+ "fc", /* 133 (Fibre Channel) */
+ "rsvp-e2e-ignore", /* 134 (RSVP-E2E-IGNORE) */
+ "mobility-header", /* 135 (IPPROTO_MOBILITY, Mobility Header) */
+ "udplite", /* 136 (UDPLite) */
+ "mpls-in-ip", /* 137 (MPLS-in-IP) */
+ "manet", /* 138 (MANET Protocols) */
+ "hip", /* 139 (Host Identity Protocol) */
+ "shim6", /* 140 (Shim6 Protocol) */
+ "wesp", /* 141 (Wrapped Encapsulating Security Payload) */
+ "rohc", /* 142 (Robust Header Compression) */
+ NULL, /* 143 (unassigned) */
+ NULL, /* 144 (unassigned) */
+ NULL, /* 145 (unassigned) */
+ NULL, /* 146 (unassigned) */
+ NULL, /* 147 (unassigned) */
+ NULL, /* 148 (unassigned) */
+ NULL, /* 149 (unassigned) */
+ NULL, /* 150 (unassigned) */
+ NULL, /* 151 (unassigned) */
+ NULL, /* 152 (unassigned) */
+ NULL, /* 153 (unassigned) */
+ NULL, /* 154 (unassigned) */
+ NULL, /* 155 (unassigned) */
+ NULL, /* 156 (unassigned) */
+ NULL, /* 157 (unassigned) */
+ NULL, /* 158 (unassigned) */
+ NULL, /* 159 (unassigned) */
+ NULL, /* 160 (unassigned) */
+ NULL, /* 161 (unassigned) */
+ NULL, /* 162 (unassigned) */
+ NULL, /* 163 (unassigned) */
+ NULL, /* 164 (unassigned) */
+ NULL, /* 165 (unassigned) */
+ NULL, /* 166 (unassigned) */
+ NULL, /* 167 (unassigned) */
+ NULL, /* 168 (unassigned) */
+ NULL, /* 169 (unassigned) */
+ NULL, /* 170 (unassigned) */
+ NULL, /* 171 (unassigned) */
+ NULL, /* 172 (unassigned) */
+ NULL, /* 173 (unassigned) */
+ NULL, /* 174 (unassigned) */
+ NULL, /* 175 (unassigned) */
+ NULL, /* 176 (unassigned) */
+ NULL, /* 177 (unassigned) */
+ NULL, /* 178 (unassigned) */
+ NULL, /* 179 (unassigned) */
+ NULL, /* 180 (unassigned) */
+ NULL, /* 181 (unassigned) */
+ NULL, /* 182 (unassigned) */
+ NULL, /* 183 (unassigned) */
+ NULL, /* 184 (unassigned) */
+ NULL, /* 185 (unassigned) */
+ NULL, /* 186 (unassigned) */
+ NULL, /* 187 (unassigned) */
+ NULL, /* 188 (unassigned) */
+ NULL, /* 189 (unassigned) */
+ NULL, /* 190 (unassigned) */
+ NULL, /* 191 (unassigned) */
+ NULL, /* 192 (unassigned) */
+ NULL, /* 193 (unassigned) */
+ NULL, /* 194 (unassigned) */
+ NULL, /* 195 (unassigned) */
+ NULL, /* 196 (unassigned) */
+ NULL, /* 197 (unassigned) */
+ NULL, /* 198 (unassigned) */
+ NULL, /* 199 (unassigned) */
+ NULL, /* 200 (unassigned) */
+ NULL, /* 201 (unassigned) */
+ NULL, /* 202 (unassigned) */
+ NULL, /* 203 (unassigned) */
+ NULL, /* 204 (unassigned) */
+ NULL, /* 205 (unassigned) */
+ NULL, /* 206 (unassigned) */
+ NULL, /* 207 (unassigned) */
+ NULL, /* 208 (unassigned) */
+ NULL, /* 209 (unassigned) */
+ NULL, /* 210 (unassigned) */
+ NULL, /* 211 (unassigned) */
+ NULL, /* 212 (unassigned) */
+ NULL, /* 213 (unassigned) */
+ NULL, /* 214 (unassigned) */
+ NULL, /* 215 (unassigned) */
+ NULL, /* 216 (unassigned) */
+ NULL, /* 217 (unassigned) */
+ NULL, /* 218 (unassigned) */
+ NULL, /* 219 (unassigned) */
+ NULL, /* 220 (unassigned) */
+ NULL, /* 221 (unassigned) */
+ NULL, /* 222 (unassigned) */
+ NULL, /* 223 (unassigned) */
+ NULL, /* 224 (unassigned) */
+ NULL, /* 225 (unassigned) */
+ NULL, /* 226 (unassigned) */
+ NULL, /* 227 (unassigned) */
+ NULL, /* 228 (unassigned) */
+ NULL, /* 229 (unassigned) */
+ NULL, /* 230 (unassigned) */
+ NULL, /* 231 (unassigned) */
+ NULL, /* 232 (unassigned) */
+ NULL, /* 233 (unassigned) */
+ NULL, /* 234 (unassigned) */
+ NULL, /* 235 (unassigned) */
+ NULL, /* 236 (unassigned) */
+ NULL, /* 237 (unassigned) */
+ NULL, /* 238 (unassigned) */
+ NULL, /* 239 (unassigned) */
+ NULL, /* 240 (unassigned) */
+ NULL, /* 241 (unassigned) */
+ NULL, /* 242 (unassigned) */
+ NULL, /* 243 (unassigned) */
+ NULL, /* 244 (unassigned) */
+ NULL, /* 245 (unassigned) */
+ NULL, /* 246 (unassigned) */
+ NULL, /* 247 (unassigned) */
+ NULL, /* 248 (unassigned) */
+ NULL, /* 249 (unassigned) */
+ NULL, /* 250 (unassigned) */
+ NULL, /* 251 (unassigned) */
+ NULL, /* 252 (unassigned) */
+ "exptest-253", /* 253 (Use for experimentation and testing,
+ * RFC 3692)
+ */
+ "exptest-254", /* 254 (Use for experimentation and testing,
+ * RFC 3692)
+ */
+ "reserved", /* 255 (reserved) */
+};
+
+/* The function enforces the array index to be 8-bit. */
+const char *
+netdb_protoname (const uint8_t protoid)
+{
+ return netdb_protocol_names[protoid];
+}
diff --git a/ipproto.h b/ipproto.h
new file mode 100644
index 0000000..baec4bd
--- /dev/null
+++ b/ipproto.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1982, 1986, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * From:
+ * @(#)in.h 8.3 (Berkeley) 1/3/94
+ * $FreeBSD: src/sys/netinet/in.h,v 1.38.2.3 1999/08/29 16:29:34 peter Exp $
+ */
+
+extern const struct tok ipproto_values[];
+extern const char *netdb_protoname (const uint8_t);
+
+#ifndef IPPROTO_IP
+#define IPPROTO_IP 0 /* dummy for IP */
+#endif
+#ifndef IPPROTO_HOPOPTS
+#define IPPROTO_HOPOPTS 0 /* IPv6 hop-by-hop options */
+#endif
+#ifndef IPPROTO_ICMP
+#define IPPROTO_ICMP 1 /* control message protocol */
+#endif
+#ifndef IPPROTO_IGMP
+#define IPPROTO_IGMP 2 /* group mgmt protocol */
+#endif
+#ifndef IPPROTO_IPV4
+#define IPPROTO_IPV4 4
+#endif
+#ifndef IPPROTO_TCP
+#define IPPROTO_TCP 6 /* tcp */
+#endif
+#ifndef IPPROTO_EGP
+#define IPPROTO_EGP 8 /* exterior gateway protocol */
+#endif
+#ifndef IPPROTO_PIGP
+#define IPPROTO_PIGP 9
+#endif
+#ifndef IPPROTO_UDP
+#define IPPROTO_UDP 17 /* user datagram protocol */
+#endif
+#ifndef IPPROTO_DCCP
+#define IPPROTO_DCCP 33 /* datagram congestion control protocol */
+#endif
+#ifndef IPPROTO_IPV6
+#define IPPROTO_IPV6 41
+#endif
+#ifndef IPPROTO_ROUTING
+#define IPPROTO_ROUTING 43 /* IPv6 routing header */
+#endif
+#ifndef IPPROTO_FRAGMENT
+#define IPPROTO_FRAGMENT 44 /* IPv6 fragmentation header */
+#endif
+#ifndef IPPROTO_RSVP
+#define IPPROTO_RSVP 46 /* resource reservation */
+#endif
+#ifndef IPPROTO_GRE
+#define IPPROTO_GRE 47 /* General Routing Encap. */
+#endif
+#ifndef IPPROTO_ESP
+#define IPPROTO_ESP 50 /* SIPP Encap Sec. Payload */
+#endif
+#ifndef IPPROTO_AH
+#define IPPROTO_AH 51 /* SIPP Auth Header */
+#endif
+#ifndef IPPROTO_MOBILE
+#define IPPROTO_MOBILE 55
+#endif
+#ifndef IPPROTO_ICMPV6
+#define IPPROTO_ICMPV6 58 /* ICMPv6 */
+#endif
+#ifndef IPPROTO_NONE
+#define IPPROTO_NONE 59 /* IPv6 no next header */
+#endif
+#ifndef IPPROTO_DSTOPTS
+#define IPPROTO_DSTOPTS 60 /* IPv6 destination options */
+#endif
+#ifndef IPPROTO_MOBILITY_OLD
+/*
+ * The current Protocol Numbers list says that the IP protocol number for
+ * mobility headers is 135; it cites RFC 6275 (obsoletes RFC 3775).
+ *
+ * It appears that 62 used to be used, even though that's assigned to
+ * a protocol called CFTP; however, the only reference for CFTP is a
+ * Network Message from BBN back in 1982, so, for now, we support 62,
+ * as well as 135, as a protocol number for mobility headers.
+ */
+#define IPPROTO_MOBILITY_OLD 62
+#endif
+#ifndef IPPROTO_ND
+#define IPPROTO_ND 77 /* Sun net disk proto (temp.) */
+#endif
+#ifndef IPPROTO_EIGRP
+#define IPPROTO_EIGRP 88 /* Cisco/GXS IGRP */
+#endif
+#ifndef IPPROTO_OSPF
+#define IPPROTO_OSPF 89
+#endif
+#ifndef IPPROTO_PIM
+#define IPPROTO_PIM 103
+#endif
+#ifndef IPPROTO_IPCOMP
+#define IPPROTO_IPCOMP 108
+#endif
+#ifndef IPPROTO_VRRP
+#define IPPROTO_VRRP 112 /* See also CARP. */
+#endif
+#ifndef IPPROTO_PGM
+#define IPPROTO_PGM 113
+#endif
+#ifndef IPPROTO_SCTP
+#define IPPROTO_SCTP 132
+#endif
+#ifndef IPPROTO_MOBILITY
+#define IPPROTO_MOBILITY 135
+#endif
+#ifndef IPPROTO_ETHERNET
+#define IPPROTO_ETHERNET 143 /* TEMPORARY - registered 2020-01-31, expires 2021-01-31 */
+#endif
diff --git a/l2vpn.c b/l2vpn.c
new file mode 100644
index 0000000..9111cf6
--- /dev/null
+++ b/l2vpn.c
@@ -0,0 +1,95 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include "l2vpn.h"
+
+/*
+ * BGP Layer 2 Encapsulation Types
+ *
+ * RFC 6624
+ *
+ * https://www.iana.org/assignments/bgp-parameters/bgp-parameters.xhtml#bgp-l2-encapsulation-types-registry
+ */
+const struct tok l2vpn_encaps_values[] = {
+ { 0, "Reserved"},
+ { 1, "Frame Relay"},
+ { 2, "ATM AAL5 SDU VCC transport"},
+ { 3, "ATM transparent cell transport"},
+ { 4, "Ethernet (VLAN) Tagged Mode"},
+ { 5, "Ethernet Raw Mode"},
+ { 6, "Cisco HDLC"},
+ { 7, "PPP"},
+ { 8, "SONET/SDH Circuit Emulation Service over MPLS"},
+ { 9, "ATM n-to-one VCC cell transport"},
+ { 10, "ATM n-to-one VPC cell transport"},
+ { 11, "IP layer 2 transport"},
+ { 15, "Frame Relay Port mode"},
+ { 17, "Structure-agnostic E1 over packet"},
+ { 18, "Structure-agnostic T1 (DS1) over packet"},
+ { 19, "VPLS"},
+ { 20, "Structure-agnostic T3 (DS3) over packet"},
+ { 21, "Nx64kbit/s Basic Service using Structure-aware"},
+ { 25, "Frame Relay DLCI"},
+ { 40, "Structure-agnostic E3 over packet"},
+ { 41, "Octet-aligned playload for Structure-agnostic DS1 circuits"},
+ { 42, "E1 Nx64kbit/s with CAS using Structure-aware"},
+ { 43, "DS1 (ESF) Nx64kbit/s with CAS using Structure-aware"},
+ { 44, "DS1 (SF) Nx64kbit/s with CAS using Structure-aware"},
+ { 0, NULL}
+};
+
+/*
+ * MPLS Pseudowire Types
+ *
+ * RFC 4446
+ *
+ * https://www.iana.org/assignments/pwe3-parameters/pwe3-parameters.xhtml#pwe3-parameters-2
+ */
+const struct tok mpls_pw_types_values[] = {
+ { 0x0000, "Reserved"},
+ { 0x0001, "Frame Relay DLCI (Martini Mode)"},
+ { 0x0002, "ATM AAL5 SDU VCC transport"},
+ { 0x0003, "ATM transparent cell transport"},
+ { 0x0004, "Ethernet VLAN"},
+ { 0x0005, "Ethernet"},
+ { 0x0006, "Cisco-HDLC"},
+ { 0x0007, "PPP"},
+ { 0x0008, "SONET/SDH Circuit Emulation Service over MPLS"},
+ { 0x0009, "ATM n-to-one VCC cell transport"},
+ { 0x000a, "ATM n-to-one VPC cell transport"},
+ { 0x000b, "IP Layer2 Transport"},
+ { 0x000c, "ATM one-to-one VCC Cell Mode"},
+ { 0x000d, "ATM one-to-one VPC Cell Mode"},
+ { 0x000e, "ATM AAL5 PDU VCC transport"},
+ { 0x000f, "Frame-Relay Port mode"},
+ { 0x0010, "SONET/SDH Circuit Emulation over Packet"},
+ { 0x0011, "Structure-agnostic E1 over Packet"},
+ { 0x0012, "Structure-agnostic T1 (DS1) over Packet"},
+ { 0x0013, "Structure-agnostic E3 over Packet"},
+ { 0x0014, "Structure-agnostic T3 (DS3) over Packet"},
+ { 0x0015, "CESoPSN basic mode"},
+ { 0x0016, "TDMoIP basic mode"},
+ { 0x0017, "CESoPSN TDM with CAS"},
+ { 0x0018, "TDMoIP TDM with CAS"},
+ { 0x0019, "Frame Relay DLCI"},
+ { 0x0040, "IP-interworking"},
+ { 0, NULL}
+};
diff --git a/l2vpn.h b/l2vpn.h
new file mode 100644
index 0000000..98b6fcc
--- /dev/null
+++ b/l2vpn.h
@@ -0,0 +1,17 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+extern const struct tok l2vpn_encaps_values[];
+extern const struct tok mpls_pw_types_values[];
diff --git a/llc.h b/llc.h
new file mode 100644
index 0000000..ec8d069
--- /dev/null
+++ b/llc.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1993, 1994, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Definitions for information in the LLC header.
+ */
+
+#define LLC_U_FMT 3
+#define LLC_GSAP 1
+#define LLC_IG 1 /* Individual / Group */
+#define LLC_S_FMT 1
+
+#define LLC_U_POLL 0x10
+#define LLC_IS_POLL 0x0100
+#define LLC_XID_FI 0x81
+
+#define LLC_U_CMD(u) ((u) & 0xef)
+#define LLC_UI 0x03
+#define LLC_UA 0x63
+#define LLC_DISC 0x43
+#define LLC_DM 0x0f
+#define LLC_SABME 0x6f
+#define LLC_TEST 0xe3
+#define LLC_XID 0xaf
+#define LLC_FRMR 0x87
+
+#define LLC_S_CMD(is) (((is) >> 2) & 0x03)
+#define LLC_RR 0x0001
+#define LLC_RNR 0x0005
+#define LLC_REJ 0x0009
+
+#define LLC_IS_NR(is) (((is) >> 9) & 0x7f)
+#define LLC_I_NS(is) (((is) >> 1) & 0x7f)
+
+#ifndef LLCSAP_NULL
+#define LLCSAP_NULL 0x00
+#endif
+#ifndef LLCSAP_GLOBAL
+#define LLCSAP_GLOBAL 0xff
+#endif
+#ifndef LLCSAP_8021B_I
+#define LLCSAP_8021B_I 0x02
+#endif
+#ifndef LLCSAP_8021B_G
+#define LLCSAP_8021B_G 0x03
+#endif
+#ifndef LLCSAP_SNA
+#define LLCSAP_SNA 0x04
+#endif
+#ifndef LLCSAP_IP
+#define LLCSAP_IP 0x06
+#endif
+#ifndef LLCSAP_PROWAYNM
+#define LLCSAP_PROWAYNM 0x0e
+#endif
+#ifndef LLCSAP_8021D
+#define LLCSAP_8021D 0x42
+#endif
+#ifndef LLCSAP_RS511
+#define LLCSAP_RS511 0x4e
+#endif
+#ifndef LLCSAP_ISO8208
+#define LLCSAP_ISO8208 0x7e
+#endif
+#ifndef LLCSAP_PROWAY
+#define LLCSAP_PROWAY 0x8e
+#endif
+#ifndef LLCSAP_SNAP
+#define LLCSAP_SNAP 0xaa
+#endif
+#ifndef LLCSAP_IPX
+#define LLCSAP_IPX 0xe0
+#endif
+#ifndef LLCSAP_NETBEUI
+#define LLCSAP_NETBEUI 0xf0
+#endif
+#ifndef LLCSAP_ISONS
+#define LLCSAP_ISONS 0xfe
+#endif
+
+/*
+ * PIDs for use with OUI_CISCO.
+ */
+#define PID_CISCO_CDP 0x2000 /* Cisco Discovery Protocol */
+#define PID_CISCO_VTP 0x2003 /* Cisco VLAN Trunk Protocol */
+#define PID_CISCO_DTP 0x2004 /* Cisco Dynamic Trunk Protocol */
+#define PID_CISCO_UDLD 0x0111 /* Unidirectional Link Detection */
+#define PID_CISCO_PVST 0x010b /* Per VLAN Spanning Tree+ and RPVST+ */
+#define PID_CISCO_VLANBRIDGE 0x010c /* "VLAN Bridge", according to Wireshark */
+
+/*
+ * PIDs for use with OUI_RFC2684.
+ */
+#define PID_RFC2684_ETH_FCS 0x0001 /* Ethernet, with FCS */
+#define PID_RFC2684_ETH_NOFCS 0x0007 /* Ethernet, without FCS */
+#define PID_RFC2684_802_4_FCS 0x0002 /* 802.4, with FCS */
+#define PID_RFC2684_802_4_NOFCS 0x0008 /* 802.4, without FCS */
+#define PID_RFC2684_802_5_FCS 0x0003 /* 802.5, with FCS */
+#define PID_RFC2684_802_5_NOFCS 0x0009 /* 802.5, without FCS */
+#define PID_RFC2684_FDDI_FCS 0x0004 /* FDDI, with FCS */
+#define PID_RFC2684_FDDI_NOFCS 0x000a /* FDDI, without FCS */
+#define PID_RFC2684_802_6_FCS 0x0005 /* 802.6, with FCS */
+#define PID_RFC2684_802_6_NOFCS 0x000b /* 802.6, without FCS */
+#define PID_RFC2684_BPDU 0x000e /* BPDUs */
diff --git a/machdep.c b/machdep.c
new file mode 100644
index 0000000..2578b73
--- /dev/null
+++ b/machdep.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stddef.h>
+
+#ifdef __osf__
+#include <stdio.h>
+#include <sys/sysinfo.h>
+#include <sys/proc.h>
+#endif /* __osf__ */
+
+#include "varattrs.h"
+#include "machdep.h"
+
+/*
+ * On platforms where the CPU doesn't support unaligned loads, force
+ * unaligned accesses to abort with SIGBUS, rather than being fixed
+ * up (slowly) by the OS kernel; on those platforms, misaligned accesses
+ * are bugs, and we want tcpdump to crash so that the bugs are reported.
+ *
+ * The only OS on which this is necessary is DEC OSF/1^W^WDigital
+ * UNIX^W^WTru64 UNIX.
+ */
+int
+abort_on_misalignment(char *ebuf _U_, size_t ebufsiz _U_)
+{
+#ifdef __osf__
+ static int buf[2] = { SSIN_UACPROC, UAC_SIGBUS };
+
+ if (setsysinfo(SSI_NVPAIRS, (caddr_t)buf, 1, 0, 0) < 0) {
+ (void)snprintf(ebuf, ebufsiz, "setsysinfo: errno %d", errno);
+ return (-1);
+ }
+#endif
+ return (0);
+}
diff --git a/machdep.h b/machdep.h
new file mode 100644
index 0000000..ba8ed38
--- /dev/null
+++ b/machdep.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#ifndef netdissect_machdep_h
+#define netdissect_machdep_h
+
+int abort_on_misalignment(char *, size_t);
+#endif
diff --git a/mib.h b/mib.h
new file mode 100644
index 0000000..6c8e63c
--- /dev/null
+++ b/mib.h
@@ -0,0 +1,1460 @@
+/*
+ * This file was generated by tcpdump/makemib on Wed Sep 26 12:12:31 EDT 1990
+ * You probably don't want to edit this by hand!
+ *
+ * struct mib somename = { desc, oid-octet, type, child-pointer, next-pointer
+};
+ */
+
+/* parse problem: new name "mib" for mgmt.mib(1) ignored */
+/* parse problem: no parent for 0.nullSpecific(0) */
+static struct obj
+_proteon_obj = {
+ "proteon", 1, 0,
+ NULL, NULL
+},
+_ibm_obj = {
+ "ibm", 2, 0,
+ NULL, &_proteon_obj
+},
+_cmu_obj = {
+ "cmu", 3, 0,
+ NULL, &_ibm_obj
+},
+_unix_obj = {
+ "unix", 4, 0,
+ NULL, &_cmu_obj
+},
+_acc_obj = {
+ "acc", 5, 0,
+ NULL, &_unix_obj
+},
+_twg_obj = {
+ "twg", 6, 0,
+ NULL, &_acc_obj
+},
+_cayman_obj = {
+ "cayman", 7, 0,
+ NULL, &_twg_obj
+},
+_nysernet_obj = {
+ "nysernet", 8, 0,
+ NULL, &_cayman_obj
+},
+_cisco_obj = {
+ "cisco", 9, 0,
+ NULL, &_nysernet_obj
+},
+_nsc_obj = {
+ "nsc", 10, 0,
+ NULL, &_cisco_obj
+},
+_hp_obj = {
+ "hp", 11, 0,
+ NULL, &_nsc_obj
+},
+_epilogue_obj = {
+ "epilogue", 12, 0,
+ NULL, &_hp_obj
+},
+_utennessee_obj = {
+ "utennessee", 13, 0,
+ NULL, &_epilogue_obj
+},
+_bbn_obj = {
+ "bbn", 14, 0,
+ NULL, &_utennessee_obj
+},
+_xylogics_obj = {
+ "xylogics", 15, 0,
+ NULL, &_bbn_obj
+},
+_unisys_obj = {
+ "unisys", 16, 0,
+ NULL, &_xylogics_obj
+},
+_canstar_obj = {
+ "canstar", 17, 0,
+ NULL, &_unisys_obj
+},
+_wellfleet_obj = {
+ "wellfleet", 18, 0,
+ NULL, &_canstar_obj
+},
+_trw_obj = {
+ "trw", 19, 0,
+ NULL, &_wellfleet_obj
+},
+_mit_obj = {
+ "mit", 20, 0,
+ NULL, &_trw_obj
+},
+_eon_obj = {
+ "eon", 21, 0,
+ NULL, &_mit_obj
+},
+_spartacus_obj = {
+ "spartacus", 22, 0,
+ NULL, &_eon_obj
+},
+_excelan_obj = {
+ "excelan", 23, 0,
+ NULL, &_spartacus_obj
+},
+_spider_obj = {
+ "spider", 24, 0,
+ NULL, &_excelan_obj
+},
+_nsfnet_obj = {
+ "nsfnet", 25, 0,
+ NULL, &_spider_obj
+},
+_sytek_obj = {
+ "sytek", 26, 0,
+ NULL, &_nsfnet_obj
+},
+_intergraph_obj = {
+ "intergraph", 27, 0,
+ NULL, &_sytek_obj
+},
+_interlan_obj = {
+ "interlan", 28, 0,
+ NULL, &_intergraph_obj
+},
+_vitalink_obj = {
+ "vitalink", 29, 0,
+ NULL, &_interlan_obj
+},
+_ulana_obj = {
+ "ulana", 30, 0,
+ NULL, &_vitalink_obj
+},
+_nswc_obj = {
+ "nswc", 31, 0,
+ NULL, &_ulana_obj
+},
+_santacruzoperation_obj = {
+ "santacruzoperation", 32, 0,
+ NULL, &_nswc_obj
+},
+_xyplex_obj = {
+ "xyplex", 33, 0,
+ NULL, &_santacruzoperation_obj
+},
+_cray_obj = {
+ "cray", 34, 0,
+ NULL, &_xyplex_obj
+},
+_bellnorthernresearch_obj = {
+ "bellnorthernresearch", 35, 0,
+ NULL, &_cray_obj
+},
+_dec_obj = {
+ "dec", 36, 0,
+ NULL, &_bellnorthernresearch_obj
+},
+_touch_obj = {
+ "touch", 37, 0,
+ NULL, &_dec_obj
+},
+_networkresearchcorp_obj = {
+ "networkresearchcorp", 38, 0,
+ NULL, &_touch_obj
+},
+_baylor_obj = {
+ "baylor", 39, 0,
+ NULL, &_networkresearchcorp_obj
+},
+_nmfeccllnl_obj = {
+ "nmfeccllnl", 40, 0,
+ NULL, &_baylor_obj
+},
+_sri_obj = {
+ "sri", 41, 0,
+ NULL, &_nmfeccllnl_obj
+},
+_sun_obj = {
+ "sun", 42, 0,
+ NULL, &_sri_obj
+},
+_3com_obj = {
+ "3com", 43, 0,
+ NULL, &_sun_obj
+},
+_cmc_obj = {
+ "cmc", 44, 0,
+ NULL, &_3com_obj
+},
+_synoptics_obj = {
+ "synoptics", 45, 0,
+ NULL, &_cmc_obj
+},
+_cheyenne_obj = {
+ "cheyenne", 46, 0,
+ NULL, &_synoptics_obj
+},
+_prime_obj = {
+ "prime", 47, 0,
+ NULL, &_cheyenne_obj
+},
+_mcnc_obj = {
+ "mcnc", 48, 0,
+ NULL, &_prime_obj
+},
+_chipcom_obj = {
+ "chipcom", 49, 0,
+ NULL, &_mcnc_obj
+},
+_opticaldatasystems_obj = {
+ "opticaldatasystems", 50, 0,
+ NULL, &_chipcom_obj
+},
+_gated_obj = {
+ "gated", 51, 0,
+ NULL, &_opticaldatasystems_obj
+},
+_cabletron_obj = {
+ "cabletron", 52, 0,
+ NULL, &_gated_obj
+},
+_apollo_obj = {
+ "apollo", 53, 0,
+ NULL, &_cabletron_obj
+},
+_desktalksystems_obj = {
+ "desktalksystems", 54, 0,
+ NULL, &_apollo_obj
+},
+_ssds_obj = {
+ "ssds", 55, 0,
+ NULL, &_desktalksystems_obj
+},
+_castlerock_obj = {
+ "castlerock", 56, 0,
+ NULL, &_ssds_obj
+},
+_mips_obj = {
+ "mips", 57, 0,
+ NULL, &_castlerock_obj
+},
+_tgv_obj = {
+ "tgv", 58, 0,
+ NULL, &_mips_obj
+},
+_silicongraphics_obj = {
+ "silicongraphics", 59, 0,
+ NULL, &_tgv_obj
+},
+_ubc_obj = {
+ "ubc", 60, 0,
+ NULL, &_silicongraphics_obj
+},
+_merit_obj = {
+ "merit", 61, 0,
+ NULL, &_ubc_obj
+},
+_fibercom_obj = {
+ "fibercom", 62, 0,
+ NULL, &_merit_obj
+},
+_apple_obj = {
+ "apple", 63, 0,
+ NULL, &_fibercom_obj
+},
+_gandalf_obj = {
+ "gandalf", 64, 0,
+ NULL, &_apple_obj
+},
+_dartmouth_obj = {
+ "dartmouth", 65, 0,
+ NULL, &_gandalf_obj
+},
+_davidsystems_obj = {
+ "davidsystems", 66, 0,
+ NULL, &_dartmouth_obj
+},
+_reuter_obj = {
+ "reuter", 67, 0,
+ NULL, &_davidsystems_obj
+},
+_cornell_obj = {
+ "cornell", 68, 0,
+ NULL, &_reuter_obj
+},
+_tmac_obj = {
+ "tmac", 69, 0,
+ NULL, &_cornell_obj
+},
+_locus_obj = {
+ "locus", 70, 0,
+ NULL, &_tmac_obj
+},
+_nasa_obj = {
+ "nasa", 71, 0,
+ NULL, &_locus_obj
+},
+_retix_obj = {
+ "retix", 72, 0,
+ NULL, &_nasa_obj
+},
+_boeing_obj = {
+ "boeing", 73, 0,
+ NULL, &_retix_obj
+},
+_att_obj = {
+ "att", 74, 0,
+ NULL, &_boeing_obj
+},
+_ungermannbass_obj = {
+ "ungermannbass", 75, 0,
+ NULL, &_att_obj
+},
+_digitalanalysis_obj = {
+ "digitalanalysis", 76, 0,
+ NULL, &_ungermannbass_obj
+},
+_hplanman_obj = {
+ "hplanman", 77, 0,
+ NULL, &_digitalanalysis_obj
+},
+_netlabs_obj = {
+ "netlabs", 78, 0,
+ NULL, &_hplanman_obj
+},
+_icl_obj = {
+ "icl", 79, 0,
+ NULL, &_netlabs_obj
+},
+_auspex_obj = {
+ "auspex", 80, 0,
+ NULL, &_icl_obj
+},
+_lannet_obj = {
+ "lannet", 81, 0,
+ NULL, &_auspex_obj
+},
+_ncd_obj = {
+ "ncd", 82, 0,
+ NULL, &_lannet_obj
+},
+_raycom_obj = {
+ "raycom", 83, 0,
+ NULL, &_ncd_obj
+},
+_pirellifocom_obj = {
+ "pirellifocom", 84, 0,
+ NULL, &_raycom_obj
+},
+_datability_obj = {
+ "datability", 85, 0,
+ NULL, &_pirellifocom_obj
+},
+_networkappltech_obj = {
+ "networkappltech", 86, 0,
+ NULL, &_datability_obj
+},
+_link_obj = {
+ "link", 87, 0,
+ NULL, &_networkappltech_obj
+},
+_nyu_obj = {
+ "nyu", 88, 0,
+ NULL, &_link_obj
+},
+_rnd_obj = {
+ "rnd", 89, 0,
+ NULL, &_nyu_obj
+},
+_intercon_obj = {
+ "intercon", 90, 0,
+ NULL, &_rnd_obj
+},
+_learningtree_obj = {
+ "learningtree", 91, 0,
+ NULL, &_intercon_obj
+},
+_webstercomputer_obj = {
+ "webstercomputer", 92, 0,
+ NULL, &_learningtree_obj
+},
+_frontier_obj = {
+ "frontier", 93, 0,
+ NULL, &_webstercomputer_obj
+},
+_nokia_obj = {
+ "nokia", 94, 0,
+ NULL, &_frontier_obj
+},
+_allenbradley_obj = {
+ "allenbradley", 95, 0,
+ NULL, &_nokia_obj
+},
+_cern_obj = {
+ "cern", 96, 0,
+ NULL, &_allenbradley_obj
+},
+_sigma_obj = {
+ "sigma", 97, 0,
+ NULL, &_cern_obj
+},
+_emergingtech_obj = {
+ "emergingtech", 98, 0,
+ NULL, &_sigma_obj
+},
+_snmpresearch_obj = {
+ "snmpresearch", 99, 0,
+ NULL, &_emergingtech_obj
+},
+_ohiostate_obj = {
+ "ohiostate", 100, 0,
+ NULL, &_snmpresearch_obj
+},
+_ultra_obj = {
+ "ultra", 101, 0,
+ NULL, &_ohiostate_obj
+},
+_ccur_obj = {
+ "ccur", 136, 0,
+ NULL, &_ultra_obj
+},
+_enterprises_obj = {
+ "enterprises", 1, 0,
+ &_ccur_obj, NULL
+},
+_snmpInPkts_obj = {
+ "snmpInPkts", 1, 0,
+ NULL, NULL
+},
+_snmpOutPkts_obj = {
+ "snmpOutPkts", 2, 0,
+ NULL, &_snmpInPkts_obj
+},
+_snmpInBadVersions_obj = {
+ "snmpInBadVersions", 3, 0,
+ NULL, &_snmpOutPkts_obj
+},
+_snmpInBadCommunityNames_obj = {
+ "snmpInBadCommunityNames", 4, 0,
+ NULL, &_snmpInBadVersions_obj
+},
+_snmpInBadCommunityUses_obj = {
+ "snmpInBadCommunityUses", 5, 0,
+ NULL, &_snmpInBadCommunityNames_obj
+},
+_snmpInASNParseErrs_obj = {
+ "snmpInASNParseErrs", 6, 0,
+ NULL, &_snmpInBadCommunityUses_obj
+},
+_snmpInBadTypes_obj = {
+ "snmpInBadTypes", 7, 0,
+ NULL, &_snmpInASNParseErrs_obj
+},
+_snmpInTooBigs_obj = {
+ "snmpInTooBigs", 8, 0,
+ NULL, &_snmpInBadTypes_obj
+},
+_snmpInNoSuchNames_obj = {
+ "snmpInNoSuchNames", 9, 0,
+ NULL, &_snmpInTooBigs_obj
+},
+_snmpInBadValues_obj = {
+ "snmpInBadValues", 10, 0,
+ NULL, &_snmpInNoSuchNames_obj
+},
+_snmpInReadOnlys_obj = {
+ "snmpInReadOnlys", 11, 0,
+ NULL, &_snmpInBadValues_obj
+},
+_snmpInGenErrs_obj = {
+ "snmpInGenErrs", 12, 0,
+ NULL, &_snmpInReadOnlys_obj
+},
+_snmpInTotalReqVars_obj = {
+ "snmpInTotalReqVars", 13, 0,
+ NULL, &_snmpInGenErrs_obj
+},
+_snmpInTotalSetVars_obj = {
+ "snmpInTotalSetVars", 14, 0,
+ NULL, &_snmpInTotalReqVars_obj
+},
+_snmpInGetRequests_obj = {
+ "snmpInGetRequests", 15, 0,
+ NULL, &_snmpInTotalSetVars_obj
+},
+_snmpInGetNexts_obj = {
+ "snmpInGetNexts", 16, 0,
+ NULL, &_snmpInGetRequests_obj
+},
+_snmpInSetRequests_obj = {
+ "snmpInSetRequests", 17, 0,
+ NULL, &_snmpInGetNexts_obj
+},
+_snmpInGetResponses_obj = {
+ "snmpInGetResponses", 18, 0,
+ NULL, &_snmpInSetRequests_obj
+},
+_snmpInTraps_obj = {
+ "snmpInTraps", 19, 0,
+ NULL, &_snmpInGetResponses_obj
+},
+_snmpOutTooBigs_obj = {
+ "snmpOutTooBigs", 20, 0,
+ NULL, &_snmpInTraps_obj
+},
+_snmpOutNoSuchNames_obj = {
+ "snmpOutNoSuchNames", 21, 0,
+ NULL, &_snmpOutTooBigs_obj
+},
+_snmpOutBadValues_obj = {
+ "snmpOutBadValues", 22, 0,
+ NULL, &_snmpOutNoSuchNames_obj
+},
+_snmpOutReadOnlys_obj = {
+ "snmpOutReadOnlys", 23, 0,
+ NULL, &_snmpOutBadValues_obj
+},
+_snmpOutGenErrs_obj = {
+ "snmpOutGenErrs", 24, 0,
+ NULL, &_snmpOutReadOnlys_obj
+},
+_snmpOutGetRequests_obj = {
+ "snmpOutGetRequests", 25, 0,
+ NULL, &_snmpOutGenErrs_obj
+},
+_snmpOutGetNexts_obj = {
+ "snmpOutGetNexts", 26, 0,
+ NULL, &_snmpOutGetRequests_obj
+},
+_snmpOutSetRequests_obj = {
+ "snmpOutSetRequests", 27, 0,
+ NULL, &_snmpOutGetNexts_obj
+},
+_snmpOutGetResponses_obj = {
+ "snmpOutGetResponses", 28, 0,
+ NULL, &_snmpOutSetRequests_obj
+},
+_snmpOutTraps_obj = {
+ "snmpOutTraps", 29, 0,
+ NULL, &_snmpOutGetResponses_obj
+},
+_snmpEnableAuthTraps_obj = {
+ "snmpEnableAuthTraps", 30, 0,
+ NULL, &_snmpOutTraps_obj
+},
+_egpNeighState_obj = {
+ "egpNeighState", 1, 0,
+ NULL, NULL
+},
+_egpNeighAddr_obj = {
+ "egpNeighAddr", 2, 0,
+ NULL, &_egpNeighState_obj
+},
+_egpNeighAs_obj = {
+ "egpNeighAs", 3, 0,
+ NULL, &_egpNeighAddr_obj
+},
+_egpNeighInMsgs_obj = {
+ "egpNeighInMsgs", 4, 0,
+ NULL, &_egpNeighAs_obj
+},
+_egpNeighInErrs_obj = {
+ "egpNeighInErrs", 5, 0,
+ NULL, &_egpNeighInMsgs_obj
+},
+_egpNeighOutMsgs_obj = {
+ "egpNeighOutMsgs", 6, 0,
+ NULL, &_egpNeighInErrs_obj
+},
+_egpNeighOutErrs_obj = {
+ "egpNeighOutErrs", 7, 0,
+ NULL, &_egpNeighOutMsgs_obj
+},
+_egpNeighInErrMsgs_obj = {
+ "egpNeighInErrMsgs", 8, 0,
+ NULL, &_egpNeighOutErrs_obj
+},
+_egpNeighOutErrMsgs_obj = {
+ "egpNeighOutErrMsgs", 9, 0,
+ NULL, &_egpNeighInErrMsgs_obj
+},
+_egpNeighStateUps_obj = {
+ "egpNeighStateUps", 10, 0,
+ NULL, &_egpNeighOutErrMsgs_obj
+},
+_egpNeighStateDowns_obj = {
+ "egpNeighStateDowns", 11, 0,
+ NULL, &_egpNeighStateUps_obj
+},
+_egpNeighIntervalHello_obj = {
+ "egpNeighIntervalHello", 12, 0,
+ NULL, &_egpNeighStateDowns_obj
+},
+_egpNeighIntervalPoll_obj = {
+ "egpNeighIntervalPoll", 13, 0,
+ NULL, &_egpNeighIntervalHello_obj
+},
+_egpNeighMode_obj = {
+ "egpNeighMode", 14, 0,
+ NULL, &_egpNeighIntervalPoll_obj
+},
+_egpNeighEventTrigger_obj = {
+ "egpNeighEventTrigger", 15, 0,
+ NULL, &_egpNeighMode_obj
+},
+_egpNeighEntry_obj = {
+ "egpNeighEntry", 1, 0,
+ &_egpNeighEventTrigger_obj, NULL
+},
+_egpInMsgs_obj = {
+ "egpInMsgs", 1, 0,
+ NULL, NULL
+},
+_egpInErrors_obj = {
+ "egpInErrors", 2, 0,
+ NULL, &_egpInMsgs_obj
+},
+_egpOutMsgs_obj = {
+ "egpOutMsgs", 3, 0,
+ NULL, &_egpInErrors_obj
+},
+_egpOutErrors_obj = {
+ "egpOutErrors", 4, 0,
+ NULL, &_egpOutMsgs_obj
+},
+_egpNeighTable_obj = {
+ "egpNeighTable", 5, 0,
+ &_egpNeighEntry_obj, &_egpOutErrors_obj
+},
+_egpAs_obj = {
+ "egpAs", 6, 0,
+ NULL, &_egpNeighTable_obj
+},
+_udpLocalAddress_obj = {
+ "udpLocalAddress", 1, 0,
+ NULL, NULL
+},
+_udpLocalPort_obj = {
+ "udpLocalPort", 2, 0,
+ NULL, &_udpLocalAddress_obj
+},
+_udpEntry_obj = {
+ "udpEntry", 1, 0,
+ &_udpLocalPort_obj, NULL
+},
+_udpInDatagrams_obj = {
+ "udpInDatagrams", 1, 0,
+ NULL, NULL
+},
+_udpNoPorts_obj = {
+ "udpNoPorts", 2, 0,
+ NULL, &_udpInDatagrams_obj
+},
+_udpInErrors_obj = {
+ "udpInErrors", 3, 0,
+ NULL, &_udpNoPorts_obj
+},
+_udpOutDatagrams_obj = {
+ "udpOutDatagrams", 4, 0,
+ NULL, &_udpInErrors_obj
+},
+_udpTable_obj = {
+ "udpTable", 5, 0,
+ &_udpEntry_obj, &_udpOutDatagrams_obj
+},
+_tcpConnState_obj = {
+ "tcpConnState", 1, 0,
+ NULL, NULL
+},
+_tcpConnLocalAddress_obj = {
+ "tcpConnLocalAddress", 2, 0,
+ NULL, &_tcpConnState_obj
+},
+_tcpConnLocalPort_obj = {
+ "tcpConnLocalPort", 3, 0,
+ NULL, &_tcpConnLocalAddress_obj
+},
+_tcpConnRemAddress_obj = {
+ "tcpConnRemAddress", 4, 0,
+ NULL, &_tcpConnLocalPort_obj
+},
+_tcpConnRemPort_obj = {
+ "tcpConnRemPort", 5, 0,
+ NULL, &_tcpConnRemAddress_obj
+},
+_tcpConnEntry_obj = {
+ "tcpConnEntry", 1, 0,
+ &_tcpConnRemPort_obj, NULL
+},
+_tcpRtoAlgorithm_obj = {
+ "tcpRtoAlgorithm", 1, 0,
+ NULL, NULL
+},
+_tcpRtoMin_obj = {
+ "tcpRtoMin", 2, 0,
+ NULL, &_tcpRtoAlgorithm_obj
+},
+_tcpRtoMax_obj = {
+ "tcpRtoMax", 3, 0,
+ NULL, &_tcpRtoMin_obj
+},
+_tcpMaxConn_obj = {
+ "tcpMaxConn", 4, 0,
+ NULL, &_tcpRtoMax_obj
+},
+_tcpActiveOpens_obj = {
+ "tcpActiveOpens", 5, 0,
+ NULL, &_tcpMaxConn_obj
+},
+_tcpPassiveOpens_obj = {
+ "tcpPassiveOpens", 6, 0,
+ NULL, &_tcpActiveOpens_obj
+},
+_tcpAttemptFails_obj = {
+ "tcpAttemptFails", 7, 0,
+ NULL, &_tcpPassiveOpens_obj
+},
+_tcpEstabResets_obj = {
+ "tcpEstabResets", 8, 0,
+ NULL, &_tcpAttemptFails_obj
+},
+_tcpCurrEstab_obj = {
+ "tcpCurrEstab", 9, 0,
+ NULL, &_tcpEstabResets_obj
+},
+_tcpInSegs_obj = {
+ "tcpInSegs", 10, 0,
+ NULL, &_tcpCurrEstab_obj
+},
+_tcpOutSegs_obj = {
+ "tcpOutSegs", 11, 0,
+ NULL, &_tcpInSegs_obj
+},
+_tcpRetransSegs_obj = {
+ "tcpRetransSegs", 12, 0,
+ NULL, &_tcpOutSegs_obj
+},
+_tcpConnTable_obj = {
+ "tcpConnTable", 13, 0,
+ &_tcpConnEntry_obj, &_tcpRetransSegs_obj
+},
+_tcpInErrs_obj = {
+ "tcpInErrs", 14, 0,
+ NULL, &_tcpConnTable_obj
+},
+_tcpOutRsts_obj = {
+ "tcpOutRsts", 15, 0,
+ NULL, &_tcpInErrs_obj
+},
+_icmpInMsgs_obj = {
+ "icmpInMsgs", 1, 0,
+ NULL, NULL
+},
+_icmpInErrors_obj = {
+ "icmpInErrors", 2, 0,
+ NULL, &_icmpInMsgs_obj
+},
+_icmpInDestUnreachs_obj = {
+ "icmpInDestUnreachs", 3, 0,
+ NULL, &_icmpInErrors_obj
+},
+_icmpInTimeExcds_obj = {
+ "icmpInTimeExcds", 4, 0,
+ NULL, &_icmpInDestUnreachs_obj
+},
+_icmpInParmProbs_obj = {
+ "icmpInParmProbs", 5, 0,
+ NULL, &_icmpInTimeExcds_obj
+},
+_icmpInSrcQuenchs_obj = {
+ "icmpInSrcQuenchs", 6, 0,
+ NULL, &_icmpInParmProbs_obj
+},
+_icmpInRedirects_obj = {
+ "icmpInRedirects", 7, 0,
+ NULL, &_icmpInSrcQuenchs_obj
+},
+_icmpInEchos_obj = {
+ "icmpInEchos", 8, 0,
+ NULL, &_icmpInRedirects_obj
+},
+_icmpInEchoReps_obj = {
+ "icmpInEchoReps", 9, 0,
+ NULL, &_icmpInEchos_obj
+},
+_icmpInTimestamps_obj = {
+ "icmpInTimestamps", 10, 0,
+ NULL, &_icmpInEchoReps_obj
+},
+_icmpInTimestampReps_obj = {
+ "icmpInTimestampReps", 11, 0,
+ NULL, &_icmpInTimestamps_obj
+},
+_icmpInAddrMasks_obj = {
+ "icmpInAddrMasks", 12, 0,
+ NULL, &_icmpInTimestampReps_obj
+},
+_icmpInAddrMaskReps_obj = {
+ "icmpInAddrMaskReps", 13, 0,
+ NULL, &_icmpInAddrMasks_obj
+},
+_icmpOutMsgs_obj = {
+ "icmpOutMsgs", 14, 0,
+ NULL, &_icmpInAddrMaskReps_obj
+},
+_icmpOutErrors_obj = {
+ "icmpOutErrors", 15, 0,
+ NULL, &_icmpOutMsgs_obj
+},
+_icmpOutDestUnreachs_obj = {
+ "icmpOutDestUnreachs", 16, 0,
+ NULL, &_icmpOutErrors_obj
+},
+_icmpOutTimeExcds_obj = {
+ "icmpOutTimeExcds", 17, 0,
+ NULL, &_icmpOutDestUnreachs_obj
+},
+_icmpOutParmProbs_obj = {
+ "icmpOutParmProbs", 18, 0,
+ NULL, &_icmpOutTimeExcds_obj
+},
+_icmpOutSrcQuenchs_obj = {
+ "icmpOutSrcQuenchs", 19, 0,
+ NULL, &_icmpOutParmProbs_obj
+},
+_icmpOutRedirects_obj = {
+ "icmpOutRedirects", 20, 0,
+ NULL, &_icmpOutSrcQuenchs_obj
+},
+_icmpOutEchos_obj = {
+ "icmpOutEchos", 21, 0,
+ NULL, &_icmpOutRedirects_obj
+},
+_icmpOutEchoReps_obj = {
+ "icmpOutEchoReps", 22, 0,
+ NULL, &_icmpOutEchos_obj
+},
+_icmpOutTimestamps_obj = {
+ "icmpOutTimestamps", 23, 0,
+ NULL, &_icmpOutEchoReps_obj
+},
+_icmpOutTimestampReps_obj = {
+ "icmpOutTimestampReps", 24, 0,
+ NULL, &_icmpOutTimestamps_obj
+},
+_icmpOutAddrMasks_obj = {
+ "icmpOutAddrMasks", 25, 0,
+ NULL, &_icmpOutTimestampReps_obj
+},
+_icmpOutAddrMaskReps_obj = {
+ "icmpOutAddrMaskReps", 26, 0,
+ NULL, &_icmpOutAddrMasks_obj
+},
+_ipNetToMediaIfIndex_obj = {
+ "ipNetToMediaIfIndex", 1, 0,
+ NULL, NULL
+},
+_ipNetToMediaPhysAddress_obj = {
+ "ipNetToMediaPhysAddress", 2, 0,
+ NULL, &_ipNetToMediaIfIndex_obj
+},
+_ipNetToMediaNetAddress_obj = {
+ "ipNetToMediaNetAddress", 3, 0,
+ NULL, &_ipNetToMediaPhysAddress_obj
+},
+_ipNetToMediaType_obj = {
+ "ipNetToMediaType", 4, 0,
+ NULL, &_ipNetToMediaNetAddress_obj
+},
+_ipNetToMediaEntry_obj = {
+ "ipNetToMediaEntry", 1, 0,
+ &_ipNetToMediaType_obj, NULL
+},
+_ipRouteDest_obj = {
+ "ipRouteDest", 1, 0,
+ NULL, NULL
+},
+_ipRouteIfIndex_obj = {
+ "ipRouteIfIndex", 2, 0,
+ NULL, &_ipRouteDest_obj
+},
+_ipRouteMetric1_obj = {
+ "ipRouteMetric1", 3, 0,
+ NULL, &_ipRouteIfIndex_obj
+},
+_ipRouteMetric2_obj = {
+ "ipRouteMetric2", 4, 0,
+ NULL, &_ipRouteMetric1_obj
+},
+_ipRouteMetric3_obj = {
+ "ipRouteMetric3", 5, 0,
+ NULL, &_ipRouteMetric2_obj
+},
+_ipRouteMetric4_obj = {
+ "ipRouteMetric4", 6, 0,
+ NULL, &_ipRouteMetric3_obj
+},
+_ipRouteNextHop_obj = {
+ "ipRouteNextHop", 7, 0,
+ NULL, &_ipRouteMetric4_obj
+},
+_ipRouteType_obj = {
+ "ipRouteType", 8, 0,
+ NULL, &_ipRouteNextHop_obj
+},
+_ipRouteProto_obj = {
+ "ipRouteProto", 9, 0,
+ NULL, &_ipRouteType_obj
+},
+_ipRouteAge_obj = {
+ "ipRouteAge", 10, 0,
+ NULL, &_ipRouteProto_obj
+},
+_ipRouteMask_obj = {
+ "ipRouteMask", 11, 0,
+ NULL, &_ipRouteAge_obj
+},
+_ipRouteEntry_obj = {
+ "ipRouteEntry", 1, 0,
+ &_ipRouteMask_obj, NULL
+},
+_ipAdEntAddr_obj = {
+ "ipAdEntAddr", 1, 0,
+ NULL, NULL
+},
+_ipAdEntIfIndex_obj = {
+ "ipAdEntIfIndex", 2, 0,
+ NULL, &_ipAdEntAddr_obj
+},
+_ipAdEntNetMask_obj = {
+ "ipAdEntNetMask", 3, 0,
+ NULL, &_ipAdEntIfIndex_obj
+},
+_ipAdEntBcastAddr_obj = {
+ "ipAdEntBcastAddr", 4, 0,
+ NULL, &_ipAdEntNetMask_obj
+},
+_ipAdEntReasmMaxSize_obj = {
+ "ipAdEntReasmMaxSize", 5, 0,
+ NULL, &_ipAdEntBcastAddr_obj
+},
+_ipAddrEntry_obj = {
+ "ipAddrEntry", 1, 0,
+ &_ipAdEntReasmMaxSize_obj, NULL
+},
+_ipForwarding_obj = {
+ "ipForwarding", 1, 0,
+ NULL, NULL
+},
+_ipDefaultTTL_obj = {
+ "ipDefaultTTL", 2, 0,
+ NULL, &_ipForwarding_obj
+},
+_ipInReceives_obj = {
+ "ipInReceives", 3, 0,
+ NULL, &_ipDefaultTTL_obj
+},
+_ipInHdrErrors_obj = {
+ "ipInHdrErrors", 4, 0,
+ NULL, &_ipInReceives_obj
+},
+_ipInAddrErrors_obj = {
+ "ipInAddrErrors", 5, 0,
+ NULL, &_ipInHdrErrors_obj
+},
+_ipForwDatagrams_obj = {
+ "ipForwDatagrams", 6, 0,
+ NULL, &_ipInAddrErrors_obj
+},
+_ipInUnknownProtos_obj = {
+ "ipInUnknownProtos", 7, 0,
+ NULL, &_ipForwDatagrams_obj
+},
+_ipInDiscards_obj = {
+ "ipInDiscards", 8, 0,
+ NULL, &_ipInUnknownProtos_obj
+},
+_ipInDelivers_obj = {
+ "ipInDelivers", 9, 0,
+ NULL, &_ipInDiscards_obj
+},
+_ipOutRequests_obj = {
+ "ipOutRequests", 10, 0,
+ NULL, &_ipInDelivers_obj
+},
+_ipOutDiscards_obj = {
+ "ipOutDiscards", 11, 0,
+ NULL, &_ipOutRequests_obj
+},
+_ipOutNoRoutes_obj = {
+ "ipOutNoRoutes", 12, 0,
+ NULL, &_ipOutDiscards_obj
+},
+_ipReasmTimeout_obj = {
+ "ipReasmTimeout", 13, 0,
+ NULL, &_ipOutNoRoutes_obj
+},
+_ipReasmReqds_obj = {
+ "ipReasmReqds", 14, 0,
+ NULL, &_ipReasmTimeout_obj
+},
+_ipReasmOKs_obj = {
+ "ipReasmOKs", 15, 0,
+ NULL, &_ipReasmReqds_obj
+},
+_ipReasmFails_obj = {
+ "ipReasmFails", 16, 0,
+ NULL, &_ipReasmOKs_obj
+},
+_ipFragOKs_obj = {
+ "ipFragOKs", 17, 0,
+ NULL, &_ipReasmFails_obj
+},
+_ipFragFails_obj = {
+ "ipFragFails", 18, 0,
+ NULL, &_ipFragOKs_obj
+},
+_ipFragCreates_obj = {
+ "ipFragCreates", 19, 0,
+ NULL, &_ipFragFails_obj
+},
+_ipAddrTable_obj = {
+ "ipAddrTable", 20, 0,
+ &_ipAddrEntry_obj, &_ipFragCreates_obj
+},
+_ipRoutingTable_obj = {
+ "ipRoutingTable", 21, 0,
+ &_ipRouteEntry_obj, &_ipAddrTable_obj
+},
+_ipNetToMediaTable_obj = {
+ "ipNetToMediaTable", 22, 0,
+ &_ipNetToMediaEntry_obj, &_ipRoutingTable_obj
+},
+_atIfIndex_obj = {
+ "atIfIndex", 1, 0,
+ NULL, NULL
+},
+_atPhysAddress_obj = {
+ "atPhysAddress", 2, 0,
+ NULL, &_atIfIndex_obj
+},
+_atNetAddress_obj = {
+ "atNetAddress", 3, 0,
+ NULL, &_atPhysAddress_obj
+},
+_atEntry_obj = {
+ "atEntry", 1, 0,
+ &_atNetAddress_obj, NULL
+},
+_atTable_obj = {
+ "atTable", 1, 0,
+ &_atEntry_obj, NULL
+},
+_ifIndex_obj = {
+ "ifIndex", 1, 0,
+ NULL, NULL
+},
+_ifDescr_obj = {
+ "ifDescr", 2, 0,
+ NULL, &_ifIndex_obj
+},
+_ifType_obj = {
+ "ifType", 3, 0,
+ NULL, &_ifDescr_obj
+},
+_ifMtu_obj = {
+ "ifMtu", 4, 0,
+ NULL, &_ifType_obj
+},
+_ifSpeed_obj = {
+ "ifSpeed", 5, 0,
+ NULL, &_ifMtu_obj
+},
+_ifPhysAddress_obj = {
+ "ifPhysAddress", 6, 0,
+ NULL, &_ifSpeed_obj
+},
+_ifAdminStatus_obj = {
+ "ifAdminStatus", 7, 0,
+ NULL, &_ifPhysAddress_obj
+},
+_ifOperStatus_obj = {
+ "ifOperStatus", 8, 0,
+ NULL, &_ifAdminStatus_obj
+},
+_ifLastChange_obj = {
+ "ifLastChange", 9, 0,
+ NULL, &_ifOperStatus_obj
+},
+_ifInOctets_obj = {
+ "ifInOctets", 10, 0,
+ NULL, &_ifLastChange_obj
+},
+_ifInUcastPkts_obj = {
+ "ifInUcastPkts", 11, 0,
+ NULL, &_ifInOctets_obj
+},
+_ifInNUcastPkts_obj = {
+ "ifInNUcastPkts", 12, 0,
+ NULL, &_ifInUcastPkts_obj
+},
+_ifInDiscards_obj = {
+ "ifInDiscards", 13, 0,
+ NULL, &_ifInNUcastPkts_obj
+},
+_ifInErrors_obj = {
+ "ifInErrors", 14, 0,
+ NULL, &_ifInDiscards_obj
+},
+_ifInUnknownProtos_obj = {
+ "ifInUnknownProtos", 15, 0,
+ NULL, &_ifInErrors_obj
+},
+_ifOutOctets_obj = {
+ "ifOutOctets", 16, 0,
+ NULL, &_ifInUnknownProtos_obj
+},
+_ifOutUcastPkts_obj = {
+ "ifOutUcastPkts", 17, 0,
+ NULL, &_ifOutOctets_obj
+},
+_ifOutNUcastPkts_obj = {
+ "ifOutNUcastPkts", 18, 0,
+ NULL, &_ifOutUcastPkts_obj
+},
+_ifOutDiscards_obj = {
+ "ifOutDiscards", 19, 0,
+ NULL, &_ifOutNUcastPkts_obj
+},
+_ifOutErrors_obj = {
+ "ifOutErrors", 20, 0,
+ NULL, &_ifOutDiscards_obj
+},
+_ifOutQLen_obj = {
+ "ifOutQLen", 21, 0,
+ NULL, &_ifOutErrors_obj
+},
+_ifSpecific_obj = {
+ "ifSpecific", 22, 0,
+ NULL, &_ifOutQLen_obj
+},
+_ifEntry_obj = {
+ "ifEntry", 1, 0,
+ &_ifSpecific_obj, NULL
+},
+_ifNumber_obj = {
+ "ifNumber", 1, 0,
+ NULL, NULL
+},
+_ifTable_obj = {
+ "ifTable", 2, 0,
+ &_ifEntry_obj, &_ifNumber_obj
+},
+_sysDescr_obj = {
+ "sysDescr", 1, 0,
+ NULL, NULL
+},
+_sysObjectID_obj = {
+ "sysObjectID", 2, 0,
+ NULL, &_sysDescr_obj
+},
+_sysUpTime_obj = {
+ "sysUpTime", 3, 0,
+ NULL, &_sysObjectID_obj
+},
+_sysContact_obj = {
+ "sysContact", 4, 0,
+ NULL, &_sysUpTime_obj
+},
+_sysName_obj = {
+ "sysName", 5, 0,
+ NULL, &_sysContact_obj
+},
+_sysLocation_obj = {
+ "sysLocation", 6, 0,
+ NULL, &_sysName_obj
+},
+_sysServices_obj = {
+ "sysServices", 7, 0,
+ NULL, &_sysLocation_obj
+},
+_system_obj = {
+ "system", 1, 0,
+ &_sysServices_obj, NULL
+},
+_interfaces_obj = {
+ "interfaces", 2, 0,
+ &_ifTable_obj, &_system_obj
+},
+_at_obj = {
+ "at", 3, 0,
+ &_atTable_obj, &_interfaces_obj
+},
+_ip_obj = {
+ "ip", 4, 0,
+ &_ipNetToMediaTable_obj, &_at_obj
+},
+_icmp_obj = {
+ "icmp", 5, 0,
+ &_icmpOutAddrMaskReps_obj, &_ip_obj
+},
+_tcp_obj = {
+ "tcp", 6, 0,
+ &_tcpOutRsts_obj, &_icmp_obj
+},
+_udp_obj = {
+ "udp", 7, 0,
+ &_udpTable_obj, &_tcp_obj
+},
+_egp_obj = {
+ "egp", 8, 0,
+ &_egpAs_obj, &_udp_obj
+},
+_transmission_obj = {
+ "transmission", 10, 0,
+ NULL, &_egp_obj
+},
+_snmp_obj = {
+ "snmp", 11, 0,
+ &_snmpEnableAuthTraps_obj, &_transmission_obj
+},
+_usmMIBCompliances_obj = {
+ "usmMIBCompliances", 1, 0,
+ NULL, NULL
+},
+_usmMIBGroups_obj = {
+ "usmMIBGroups", 2, 0,
+ NULL, &_usmMIBCompliances_obj
+},
+_usmUserEngineID_obj = {
+ "usmUserEngineID", 1, 0,
+ NULL, NULL
+},
+_usmUserName_obj = {
+ "usmUserName", 2, 0,
+ NULL, &_usmUserEngineID_obj
+},
+_usmUserSecurityName_obj = {
+ "usmUserSecurityName", 3, 0,
+ NULL, &_usmUserName_obj
+},
+_usmUserCloneFrom_obj = {
+ "usmUserCloneFrom", 4, 0,
+ NULL, &_usmUserSecurityName_obj
+},
+_usmUserAuthProtocol_obj = {
+ "usmUserAuthProtocol", 5, 0,
+ NULL, &_usmUserCloneFrom_obj
+},
+_usmUserAuthKeyChange_obj = {
+ "usmUserAuthKeyChange", 6, 0,
+ NULL, &_usmUserAuthProtocol_obj
+},
+_usmUserOwnAuthKeyChange_obj = {
+ "usmUserOwnAuthKeyChange", 7, 0,
+ NULL, &_usmUserAuthKeyChange_obj
+},
+_usmUserPrivProtocol_obj = {
+ "usmUserPrivProtocol", 8, 0,
+ NULL, &_usmUserOwnAuthKeyChange_obj
+},
+_usmUserPrivKeyChange_obj = {
+ "usmUserPrivKeyChange", 9, 0,
+ NULL, &_usmUserPrivProtocol_obj
+},
+_usmUserOwnPrivKeyChange_obj = {
+ "usmUserOwnPrivKeyChange", 10, 0,
+ NULL, &_usmUserPrivKeyChange_obj
+},
+_usmUserPublic_obj = {
+ "usmUserPublic", 11, 0,
+ NULL, &_usmUserOwnPrivKeyChange_obj
+},
+_usmUserStorageType_obj = {
+ "usmUserStorageType", 12, 0,
+ NULL, &_usmUserPublic_obj
+},
+_usmUserStatus_obj = {
+ "usmUserStatus", 13, 0,
+ NULL, &_usmUserStorageType_obj
+},
+_usmUserEntry_obj = {
+ "usmUserEntry", 1, 0,
+ &_usmUserStatus_obj, NULL
+},
+_usmUserSpinLock_obj = {
+ "usmUserSpinLock", 1, 0,
+ NULL, NULL
+},
+_usmUserTable_obj = {
+ "usmUserTable", 2, 0,
+ &_usmUserEntry_obj, &_usmUserSpinLock_obj
+},
+_usmStatsUnsupportedSecLevels_obj = {
+ "usmStatsUnsupportedSecLevels", 1, 0,
+ NULL, NULL
+},
+_usmStatsNotInTimeWindows_obj = {
+ "usmStatsNotInTimeWindows", 2, 0,
+ NULL, &_usmStatsUnsupportedSecLevels_obj
+},
+_usmStatsUnknownUserNames_obj = {
+ "usmStatsUnknownUserNames", 3, 0,
+ NULL, &_usmStatsNotInTimeWindows_obj
+},
+_usmStatsUnknownEngineIDs_obj = {
+ "usmStatsUnknownEngineIDs", 4, 0,
+ NULL, &_usmStatsUnknownUserNames_obj
+},
+_usmStatsWrongDigests_obj = {
+ "usmStatsWrongDigests", 5, 0,
+ NULL, &_usmStatsUnknownEngineIDs_obj
+},
+_usmStatsDecryptionErrors_obj = {
+ "usmStatsDecryptionErrors", 6, 0,
+ NULL, &_usmStatsWrongDigests_obj
+},
+_usmStats_obj = {
+ "usmStats", 1, 0,
+ &_usmStatsDecryptionErrors_obj, NULL
+},
+_usmUser_obj = {
+ "usmUser", 2, 0,
+ &_usmUserTable_obj, &_usmStats_obj
+},
+_usmMIBObjects_obj = {
+ "usmMIBObjects", 1, 0,
+ &_usmUser_obj, NULL
+},
+_usmMIBConformance_obj = {
+ "usmMIBConformance", 2, 0,
+ &_usmMIBGroups_obj, &_usmMIBObjects_obj
+},
+_snmpMPDMIBCompliances_obj = {
+ "snmpMPDMIBCompliances", 1, 0,
+ NULL, NULL
+},
+_snmpMPDMIBGroups_obj = {
+ "snmpMPDMIBGroups", 2, 0,
+ NULL, &_snmpMPDMIBCompliances_obj
+},
+_snmpUnknownSecurityModels_obj = {
+ "snmpUnknownSecurityModels", 1, 0,
+ NULL, NULL
+},
+_snmpInvalidMsgs_obj = {
+ "snmpInvalidMsgs", 2, 0,
+ NULL, &_snmpUnknownSecurityModels_obj
+},
+_snmpUnknownPDUHandlers_obj = {
+ "snmpUnknownPDUHandlers", 3, 0,
+ NULL, &_snmpInvalidMsgs_obj
+},
+_snmpMPDStats_obj = {
+ "snmpMPDStats", 1, 0,
+ &_snmpUnknownPDUHandlers_obj, NULL
+},
+_snmpMPDAdmin_obj = {
+ "snmpMPDAdmin", 1, 0,
+ NULL, NULL
+},
+_snmpMPDMIBObjects_obj = {
+ "snmpMPDMIBObjects", 2, 0,
+ &_snmpMPDStats_obj, &_snmpMPDAdmin_obj
+},
+_snmpMPDMIBConformance_obj = {
+ "snmpMPDMIBConformance", 3, 0,
+ &_snmpMPDMIBGroups_obj, &_snmpMPDMIBObjects_obj
+},
+_snmpEngineID_obj = {
+ "snmpEngineID", 1, 0,
+ NULL, NULL
+},
+_snmpEngineBoots_obj = {
+ "snmpEngineBoots", 2, 0,
+ NULL, &_snmpEngineID_obj
+},
+_snmpEngineTime_obj = {
+ "snmpEngineTime", 3, 0,
+ NULL, &_snmpEngineBoots_obj
+},
+_snmpEngineMaxMessageSize_obj = {
+ "snmpEngineMaxMessageSize", 4, 0,
+ NULL, &_snmpEngineTime_obj
+},
+_snmpEngine_obj = {
+ "snmpEngine", 1, 0,
+ &_snmpEngineMaxMessageSize_obj, NULL
+},
+_snmpFrameworkAdmin_obj = {
+ "snmpFrameworkAdmin", 1, 0,
+ NULL, NULL
+},
+_snmpFrameworkMIBObjects_obj = {
+ "snmpFrameworkMIBObjects", 2, 0,
+ &_snmpEngine_obj, &_snmpFrameworkAdmin_obj
+},
+_snmpFrameworkMIBConformance_obj = {
+ "snmpFrameworkMIBConformance", 3, 0,
+ NULL, &_snmpFrameworkMIBObjects_obj
+},
+_snmpFrameworkMIB_obj = {
+ "snmpFrameworkMIB", 10, 0,
+ &_snmpFrameworkMIBConformance_obj, NULL
+},
+_snmpMPDMIB_obj = {
+ "snmpMPDMIB", 11, 0,
+ &_snmpMPDMIBConformance_obj, &_snmpFrameworkMIB_obj
+},
+_snmpUsmMIB_obj = {
+ "snmpUsmMIB", 15, 0,
+ &_usmMIBConformance_obj, &_snmpMPDMIB_obj
+},
+_snmpModules_obj = {
+ "snmpModules", 3, 0,
+ &_snmpUsmMIB_obj, NULL
+},
+_mib_obj = {
+ "mib", 1, 0,
+ &_snmp_obj, NULL
+},
+_directory_obj = {
+ "directory", 1, 0,
+ NULL, NULL
+},
+_mgmt_obj = {
+ "mgmt", 2, 0,
+ &_mib_obj, &_directory_obj
+},
+_experimental_obj = {
+ "experimental", 3, 0,
+ NULL, &_mgmt_obj
+},
+_private_obj = {
+ "private", 4, 0,
+ &_enterprises_obj, &_experimental_obj
+},
+_security_obj = {
+ "security", 5, 0,
+ NULL, &_private_obj
+},
+_snmpV2_obj = {
+ "snmpV2", 6, 0,
+ &_snmpModules_obj, &_security_obj
+},
+_internet_obj = {
+ "internet", 1, 0,
+ &_snmpV2_obj, NULL
+},
+_dod_obj = {
+ "dod", 6, 0,
+ &_internet_obj, NULL
+},
+_org_obj = {
+ "org", 3, 0,
+ &_dod_obj, NULL
+},
+_iso_obj = {
+ "iso", 1, 0,
+ &_org_obj, NULL
+},
+*mibroot = &_iso_obj;
diff --git a/mpls.h b/mpls.h
new file mode 100644
index 0000000..03cb4bf
--- /dev/null
+++ b/mpls.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2001 WIDE Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define LABEL_MASK 0xfffff000
+#define LABEL_SHIFT 12
+#define EXP_MASK 0x00000e00
+#define EXP_SHIFT 9
+#define STACK_MASK 0x00000100
+#define STACK_SHIFT 8
+#define TTL_MASK 0x000000ff
+#define TTL_SHIFT 0
+
+#define MPLS_LABEL(x) (((x) & LABEL_MASK) >> LABEL_SHIFT)
+#define MPLS_EXP(x) (((x) & EXP_MASK) >> EXP_SHIFT)
+#define MPLS_STACK(x) (((x) & STACK_MASK) >> STACK_SHIFT)
+#define MPLS_TTL(x) (((x) & TTL_MASK) >> TTL_SHIFT)
diff --git a/nameser.h b/nameser.h
new file mode 100644
index 0000000..526c60c
--- /dev/null
+++ b/nameser.h
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nameser.h 8.2 (Berkeley) 2/16/94
+ * -
+ * Portions Copyright (c) 1993 by Digital Equipment Corporation.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies, and that
+ * the name of Digital Equipment Corporation not be used in advertising or
+ * publicity pertaining to distribution of the document or software without
+ * specific, written prior permission.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
+ * CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ * -
+ * --Copyright--
+ */
+
+#ifndef _NAMESER_H_
+#define _NAMESER_H_
+
+#include <sys/types.h>
+
+/*
+ * Define constants based on rfc883
+ */
+#define PACKETSZ 512 /* maximum packet size */
+#define MAXDNAME 256 /* maximum domain name */
+#define MAXCDNAME 255 /* maximum compressed domain name */
+#define MAXLABEL 63 /* maximum length of domain label */
+ /* Number of bytes of fixed size data in query structure */
+#define QFIXEDSZ 4
+ /* number of bytes of fixed size data in resource record */
+#define RRFIXEDSZ 10
+
+/*
+ * Currently defined opcodes
+ */
+#define QUERY 0x0 /* standard query */
+#define IQUERY 0x1 /* inverse query */
+#define STATUS 0x2 /* nameserver status query */
+#if 0
+#define xxx 0x3 /* 0x3 reserved */
+#endif
+ /* non standard - supports ALLOW_UPDATES stuff from Mike Schwartz */
+#define UPDATEA 0x9 /* add resource record */
+#define UPDATED 0xa /* delete a specific resource record */
+#define UPDATEDA 0xb /* delete all named resource record */
+#define UPDATEM 0xc /* modify a specific resource record */
+#define UPDATEMA 0xd /* modify all named resource record */
+
+#define ZONEINIT 0xe /* initial zone transfer */
+#define ZONEREF 0xf /* incremental zone referesh */
+
+/*
+ * Undefine various #defines from various System V-flavored OSes (Solaris,
+ * SINIX, HP-UX) so the compiler doesn't whine that we redefine them.
+ */
+#ifdef T_NULL
+#undef T_NULL
+#endif
+#ifdef T_OPT
+#undef T_OPT
+#endif
+#ifdef T_UNSPEC
+#undef T_UNSPEC
+#endif
+#ifdef NOERROR
+#undef NOERROR
+#endif
+
+/*
+ * Currently defined response codes
+ */
+#define NOERROR 0 /* no error */
+#define FORMERR 1 /* format error */
+#define SERVFAIL 2 /* server failure */
+#define NXDOMAIN 3 /* non existent domain */
+#define NOTIMP 4 /* not implemented */
+#define REFUSED 5 /* query refused */
+ /* non standard */
+#define NOCHANGE 0xf /* update failed to change db */
+
+/*
+ * Type values for resources and queries
+ */
+#define T_A 1 /* host address */
+#define T_NS 2 /* authoritative server */
+#define T_MD 3 /* mail destination */
+#define T_MF 4 /* mail forwarder */
+#define T_CNAME 5 /* connonical name */
+#define T_SOA 6 /* start of authority zone */
+#define T_MB 7 /* mailbox domain name */
+#define T_MG 8 /* mail group member */
+#define T_MR 9 /* mail rename name */
+#define T_NULL 10 /* null resource record */
+#define T_WKS 11 /* well known service */
+#define T_PTR 12 /* domain name pointer */
+#define T_HINFO 13 /* host information */
+#define T_MINFO 14 /* mailbox information */
+#define T_MX 15 /* mail routing information */
+#define T_TXT 16 /* text strings */
+#define T_RP 17 /* responsible person */
+#define T_AFSDB 18 /* AFS cell database */
+#define T_X25 19 /* X_25 calling address */
+#define T_ISDN 20 /* ISDN calling address */
+#define T_RT 21 /* router */
+#define T_NSAP 22 /* NSAP address */
+#define T_NSAP_PTR 23 /* reverse lookup for NSAP */
+#define T_SIG 24 /* security signature */
+#define T_KEY 25 /* security key */
+#define T_PX 26 /* X.400 mail mapping */
+#define T_GPOS 27 /* geographical position (withdrawn) */
+#define T_AAAA 28 /* IP6 Address */
+#define T_LOC 29 /* Location Information */
+#define T_NXT 30 /* Next Valid Name in Zone */
+#define T_EID 31 /* Endpoint identifier */
+#define T_NIMLOC 32 /* Nimrod locator */
+#define T_SRV 33 /* Server selection */
+#define T_ATMA 34 /* ATM Address */
+#define T_NAPTR 35 /* Naming Authority PoinTeR */
+#define T_KX 36 /* Key Exchanger */
+#define T_CERT 37 /* Certificates in the DNS */
+#define T_A6 38 /* IP6 address */
+#define T_DNAME 39 /* non-terminal redirection */
+#define T_SINK 40 /* unknown */
+#define T_OPT 41 /* EDNS0 option (meta-RR) */
+#define T_APL 42 /* lists of address prefixes */
+#define T_DS 43 /* Delegation Signer */
+#define T_SSHFP 44 /* SSH Fingerprint */
+#define T_IPSECKEY 45 /* IPsec keying material */
+#define T_RRSIG 46 /* new security signature */
+#define T_NSEC 47 /* provable insecure information */
+#define T_DNSKEY 48 /* new security key */
+ /* non standard */
+#define T_SPF 99 /* sender policy framework */
+#define T_UINFO 100 /* user (finger) information */
+#define T_UID 101 /* user ID */
+#define T_GID 102 /* group ID */
+#define T_UNSPEC 103 /* Unspecified format (binary data) */
+#define T_UNSPECA 104 /* "unspecified ASCII". Ugly MIT hack */
+ /* Query type values which do not appear in resource records */
+#define T_TKEY 249 /* Transaction Key [RFC2930] */
+#define T_TSIG 250 /* Transaction Signature [RFC2845] */
+#define T_IXFR 251 /* incremental transfer [RFC1995] */
+#define T_AXFR 252 /* transfer zone of authority */
+#define T_MAILB 253 /* transfer mailbox records */
+#define T_MAILA 254 /* transfer mail agent records */
+#define T_ANY 255 /* wildcard match */
+#define T_URI 256 /* uri records [RFC7553] */
+
+/*
+ * Values for class field
+ */
+
+#define C_IN 1 /* the arpa internet */
+#define C_CHAOS 3 /* for chaos net (MIT) */
+#define C_HS 4 /* for Hesiod name server (MIT) (XXX) */
+ /* Query class values which do not appear in resource records */
+#define C_ANY 255 /* wildcard match */
+#define C_QU 0x8000 /* mDNS QU flag in queries */
+#define C_CACHE_FLUSH 0x8000 /* mDNS cache flush flag in replies */
+
+/*
+ * Values for EDNS option types
+ */
+#define E_LLQ 1 /* long lived queries protocol */
+#define E_UL 2 /* dynamic dns update leases */
+#define E_NSID 3 /* name server identifier */
+#define E_DAU 5 /* signal DNSSEC algorithm understood */
+#define E_DHU 6 /* signal DS hash understood */
+#define E_N3U 7 /* signal NSEC3 hash understood */
+#define E_ECS 8 /* EDNS client subnet */
+#define E_EXPIRE 9 /* zone expiration */
+#define E_COOKIE 10 /* DNS cookies */
+#define E_KEEPALIVE 11 /* TCP keepalive */
+#define E_PADDING 12 /* pad DNS messages */
+#define E_CHAIN 13 /* chain DNS queries */
+#define E_KEYTAG 14 /* EDNS key tag */
+#define E_CLIENTTAG 16 /* EDNS client tag */
+#define E_SERVERTAG 17 /* EDNS server tag */
+
+/*
+ * Values for DNSSEC Algorithms
+ * https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml
+ */
+
+#define A_DELETE 0
+#define A_RSAMD5 1
+#define A_DH 2
+#define A_DSA 3
+#define A_RSASHA1 5
+#define A_DSA_NSEC3_SHA1 6
+#define A_RSASHA1_NSEC3_SHA1 7
+#define A_RSASHA256 8
+#define A_RSASHA512 10
+#define A_ECC_GOST 12
+#define A_ECDSAP256SHA256 13
+#define A_ECDSAP384SHA384 14
+#define A_ED25519 15
+#define A_ED448 16
+#define A_INDIRECT 252
+#define A_PRIVATEDNS 253
+#define A_PRIVATEOID 254
+
+/*
+ * Values for NSEC3 algorithms
+ * https://www.iana.org/assignments/dnssec-nsec3-parameters/dnssec-nsec3-parameters.xhtml
+ */
+#define NSEC_SHA1 1
+
+/*
+ * Values for delegation signer algorithms
+ * https://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml
+ */
+#define DS_SHA1 1
+#define DS_SHA256 2
+#define DS_GOST 3
+#define DS_SHA384 4
+
+
+/*
+ * Status return codes for T_UNSPEC conversion routines
+ */
+#define CONV_SUCCESS 0
+#define CONV_OVERFLOW -1
+#define CONV_BADFMT -2
+#define CONV_BADCKSUM -3
+#define CONV_BADBUFLEN -4
+
+/*
+ * Structure for query header.
+ */
+typedef struct {
+ nd_uint16_t id; /* query identification number */
+ nd_uint16_t flags; /* QR, Opcode, AA, TC, RD, RA, RCODE */
+ nd_uint16_t qdcount; /* number of question entries */
+ nd_uint16_t ancount; /* number of answer entries */
+ nd_uint16_t nscount; /* number of authority entries */
+ nd_uint16_t arcount; /* number of resource entries */
+} dns_header_t;
+
+/*
+ * Macros for subfields of flag fields.
+ */
+#define DNS_QR(flags) ((flags) & 0x8000) /* response flag */
+#define DNS_OPCODE(flags) (((flags) >> 11) & 0xF) /* purpose of message */
+#define DNS_AA(flags) (flags & 0x0400) /* authoritative answer */
+#define DNS_TC(flags) (flags & 0x0200) /* truncated message */
+#define DNS_RD(flags) (flags & 0x0100) /* recursion desired */
+#define DNS_RA(flags) (flags & 0x0080) /* recursion available */
+#define DNS_AD(flags) (flags & 0x0020) /* authentic data from named */
+#define DNS_CD(flags) (flags & 0x0010) /* checking disabled by resolver */
+#define DNS_RCODE(flags) (flags & 0x000F) /* response code */
+
+/*
+ * Defines for handling compressed domain names, EDNS0 labels, etc.
+ */
+#define TYPE_MASK 0xc0 /* mask for the type bits of the item */
+#define TYPE_INDIR 0xc0 /* 11.... - pointer */
+#define TYPE_RESERVED 0x80 /* 10.... - reserved */
+#define TYPE_EDNS0 0x40 /* 01.... - EDNS(0) label */
+#define TYPE_LABEL 0x00 /* 00.... - regular label */
+# define EDNS0_ELT_BITLABEL 0x01
+
+#endif /* !_NAMESER_H_ */
diff --git a/netdissect-alloc.c b/netdissect-alloc.c
new file mode 100644
index 0000000..bbae56e
--- /dev/null
+++ b/netdissect-alloc.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include "netdissect-alloc.h"
+
+/*
+ * nd_free_all() is intended to be used after a packet printing
+ */
+
+/* Add a memory chunk in allocation linked list */
+void
+nd_add_alloc_list(netdissect_options *ndo, nd_mem_chunk_t *chunkp)
+{
+ if (ndo->ndo_last_mem_p == NULL) /* first memory allocation */
+ chunkp->prev_mem_p = NULL;
+ else /* previous memory allocation */
+ chunkp->prev_mem_p = ndo->ndo_last_mem_p;
+ ndo->ndo_last_mem_p = chunkp;
+}
+
+/* malloc replacement, with tracking in a linked list */
+void *
+nd_malloc(netdissect_options *ndo, size_t size)
+{
+ nd_mem_chunk_t *chunkp = malloc(sizeof(nd_mem_chunk_t) + size);
+ if (chunkp == NULL)
+ return NULL;
+ nd_add_alloc_list(ndo, chunkp);
+ return chunkp + 1;
+}
+
+/* Free chunks in allocation linked list from last to first */
+void
+nd_free_all(netdissect_options *ndo)
+{
+ nd_mem_chunk_t *current, *previous;
+ current = ndo->ndo_last_mem_p;
+ while (current != NULL) {
+ previous = current->prev_mem_p;
+ free(current);
+ current = previous;
+ }
+ ndo->ndo_last_mem_p = NULL;
+}
diff --git a/netdissect-alloc.h b/netdissect-alloc.h
new file mode 100644
index 0000000..aa28a36
--- /dev/null
+++ b/netdissect-alloc.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef netdissect_alloc_h
+#define netdissect_alloc_h
+
+#include <stdarg.h>
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+
+typedef struct nd_mem_chunk {
+ void *prev_mem_p;
+ /* variable size data */
+} nd_mem_chunk_t;
+
+void nd_add_alloc_list(netdissect_options *, nd_mem_chunk_t *);
+void * nd_malloc(netdissect_options *, size_t);
+void nd_free_all(netdissect_options *);
+
+#endif /* netdissect_alloc_h */
diff --git a/netdissect-ctype.h b/netdissect-ctype.h
new file mode 100644
index 0000000..ae4a3ce
--- /dev/null
+++ b/netdissect-ctype.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1988-1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1998-2012 Michael Richardson <mcr@tcpdump.org>
+ * The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef netdissect_ctype_h
+#define netdissect_ctype_h
+
+/*
+ * Locale-independent macros for testing character properties and
+ * stripping the 8th bit from characters.
+ *
+ * Byte values outside the ASCII range are considered unprintable, so
+ * both ND_ASCII_ISPRINT() and ND_ASCII_ISGRAPH() return "false" for them.
+ *
+ * Assumed to be handed a value between 0 and 255, i.e. don't hand them
+ * a char, as those might be in the range -128 to 127.
+ */
+#define ND_ISASCII(c) (!((c) & 0x80)) /* value is an ASCII code point */
+#define ND_ASCII_ISPRINT(c) ((c) >= 0x20 && (c) <= 0x7E)
+#define ND_ASCII_ISGRAPH(c) ((c) > 0x20 && (c) <= 0x7E)
+#define ND_ASCII_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define ND_TOASCII(c) ((c) & 0x7F)
+
+/*
+ * Locale-independent macros for coverting to upper or lower case.
+ *
+ * Byte values outside the ASCII range are not converted. Byte values
+ * *in* the ASCII range are converted to byte values in the ASCII range;
+ * in particular, 'i' is upper-cased to 'I" and 'I' is lower-cased to 'i',
+ * even in Turkish locales.
+ */
+#define ND_ASCII_TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
+#define ND_ASCII_TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 'a' + 'A' : (c))
+
+#endif /* netdissect-ctype.h */
+
diff --git a/netdissect-stdinc.h b/netdissect-stdinc.h
new file mode 100644
index 0000000..0639b88
--- /dev/null
+++ b/netdissect-stdinc.h
@@ -0,0 +1,444 @@
+/*
+ * Copyright (c) 2002 - 2003
+ * NetGroup, Politecnico di Torino (Italy)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Politecnico di Torino nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Include the appropriate OS header files on Windows and various flavors
+ * of UNIX, include various non-OS header files on Windows, and define
+ * various items as needed, to isolate most of netdissect's platform
+ * differences to this one file.
+ */
+
+#ifndef netdissect_stdinc_h
+#define netdissect_stdinc_h
+
+#include <errno.h>
+
+#include "compiler-tests.h"
+
+#include "varattrs.h"
+
+/*
+ * If we're compiling with Visual Studio, make sure we have at least
+ * VS 2015 or later, so we have sufficient C99 support.
+ *
+ * XXX - verify that we have at least C99 support on UN*Xes?
+ *
+ * What about MinGW or various DOS toolchains? We're currently assuming
+ * sufficient C99 support there.
+ */
+#if defined(_MSC_VER)
+ /*
+ * Make sure we have VS 2015 or later.
+ */
+ #if _MSC_VER < 1900
+ #error "Building tcpdump requires VS 2015 or later"
+ #endif
+#endif
+
+/*
+ * Get the C99 types, and the PRI[doux]64 format strings, defined.
+ */
+#ifdef HAVE_PCAP_PCAP_INTTYPES_H
+ /*
+ * We have pcap/pcap-inttypes.h; use that, as it'll do all the
+ * work, and won't cause problems if a file includes this file
+ * and later includes a pcap header file that also includes
+ * pcap/pcap-inttypes.h.
+ */
+ #include <pcap/pcap-inttypes.h>
+#else
+ /*
+ * OK, we don't have pcap/pcap-inttypes.h, so we'll have to
+ * do the work ourselves, but at least we don't have to
+ * worry about other headers including it and causing
+ * clashes.
+ */
+
+ /*
+ * Include <inttypes.h> to get the integer types and PRi[doux]64 values
+ * defined.
+ *
+ * If the compiler is MSVC, we require VS 2015 or newer, so we
+ * have <inttypes.h> - and support for %zu in the formatted
+ * printing functions.
+ *
+ * If the compiler is MinGW, we assume we have <inttypes.h> - and
+ * support for %zu in the formatted printing functions.
+ *
+ * If the target is UN*X, we assume we have a C99-or-later development
+ * environment, and thus have <inttypes.h> - and support for %zu in
+ * the formatted printing functions.
+ *
+ * If the target is MS-DOS, we assume we have <inttypes.h> - and support
+ * for %zu in the formatted printing functions.
+ */
+ #include <inttypes.h>
+
+ #if defined(_MSC_VER)
+ /*
+ * Suppress definition of intN_t in bittypes.h, which might be included
+ * by <pcap/pcap.h> in older versions of WinPcap.
+ * (Yes, HAVE_U_INTn_T, as the definition guards are UN*X-oriented.)
+ */
+ #define HAVE_U_INT8_T
+ #define HAVE_U_INT16_T
+ #define HAVE_U_INT32_T
+ #define HAVE_U_INT64_T
+ #endif
+#endif /* HAVE_PCAP_PCAP_INTTYPES_H */
+
+#ifdef _WIN32
+
+/*
+ * Includes and definitions for Windows.
+ */
+
+#include <stdio.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <time.h>
+#include <io.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#ifdef _MSC_VER
+ /*
+ * Compiler is MSVC.
+ *
+ * We require VS 2015 or newer, so we have strtoll(). Use that for
+ * strtoint64_t().
+ */
+ #define strtoint64_t strtoll
+
+ /*
+ * And we have LL as a suffix for constants, so use that.
+ */
+ #define INT64_T_CONSTANT(constant) (constant##LL)
+#else
+ /*
+ * Non-Microsoft compiler.
+ *
+ * XXX - should we use strtoll or should we use _strtoi64()?
+ */
+ #define strtoint64_t strtoll
+
+ /*
+ * Assume LL works.
+ */
+ #define INT64_T_CONSTANT(constant) (constant##LL)
+#endif
+
+#ifdef _MSC_VER
+ /*
+ * Microsoft tries to avoid polluting the C namespace with UN*Xisms,
+ * by adding a preceding underscore; we *want* the UN*Xisms, so add
+ * #defines to let us use them.
+ */
+ #define isatty _isatty
+ #define stat _stat
+ #define strdup _strdup
+ #define open _open
+ #define read _read
+ #define close _close
+ #define O_RDONLY _O_RDONLY
+
+ /*
+ * We define our_fstat64 as _fstati64, and define our_statb as
+ * struct _stati64, so we get 64-bit file sizes.
+ */
+ #define our_fstat _fstati64
+ #define our_statb struct _stati64
+
+ /*
+ * If <crtdbg.h> has been included, and _DEBUG is defined, and
+ * __STDC__ is zero, <crtdbg.h> will define strdup() to call
+ * _strdup_dbg(). So if it's already defined, don't redefine
+ * it.
+ */
+ #ifndef strdup
+ #define strdup _strdup
+ #endif
+
+ /*
+ * Windows doesn't have ssize_t; routines such as _read() return int.
+ */
+ typedef int ssize_t;
+#endif /* _MSC_VER */
+
+/*
+ * With MSVC, for C, __inline is used to make a function an inline.
+ */
+#ifdef _MSC_VER
+#define inline __inline
+#endif
+
+#if defined(AF_INET6) && !defined(HAVE_OS_IPV6_SUPPORT)
+#define HAVE_OS_IPV6_SUPPORT
+#endif
+
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+/* It is in MSVC's <errno.h>, but not defined in MingW+Watcom.
+ */
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+
+#ifndef caddr_t
+typedef char* caddr_t;
+#endif /* caddr_t */
+
+#define MAXHOSTNAMELEN 64
+
+#else /* _WIN32 */
+
+/*
+ * Includes and definitions for various flavors of UN*X.
+ */
+
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h> /* concession to AIX */
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <time.h>
+
+#include <arpa/inet.h>
+
+/*
+ * We should have large file support enabled, if it's available,
+ * so just use fstat as our_fstat and struct stat as our_statb.
+ */
+#define our_fstat fstat
+#define our_statb struct stat
+
+/*
+ * Assume all UN*Xes have strtoll(), and use it for strtoint64_t().
+ */
+#define strtoint64_t strtoll
+
+/*
+ * Assume LL works.
+ */
+#define INT64_T_CONSTANT(constant) (constant##LL)
+#endif /* _WIN32 */
+
+/*
+ * Function attributes, for various compilers.
+ */
+#include "funcattrs.h"
+
+/*
+ * fopen() read and write modes for text files and binary files.
+ */
+#if defined(_WIN32) || defined(MSDOS)
+ #define FOPEN_READ_TXT "rt"
+ #define FOPEN_READ_BIN "rb"
+ #define FOPEN_WRITE_TXT "wt"
+ #define FOPEN_WRITE_BIN "wb"
+#else
+ #define FOPEN_READ_TXT "r"
+ #define FOPEN_READ_BIN FOPEN_READ_TXT
+ #define FOPEN_WRITE_TXT "w"
+ #define FOPEN_WRITE_BIN FOPEN_WRITE_TXT
+#endif
+
+/*
+ * Inline x86 assembler-language versions of ntoh[ls]() and hton[ls](),
+ * defined if the OS doesn't provide them. These assume no more than
+ * an 80386, so, for example, it avoids the bswap instruction added in
+ * the 80486.
+ *
+ * (We don't use them on macOS; Apple provides their own, which *doesn't*
+ * avoid the bswap instruction, as macOS only supports machines that
+ * have it.)
+ */
+#if defined(__GNUC__) && defined(__i386__) && !defined(__APPLE__) && !defined(__ntohl)
+ #undef ntohl
+ #undef ntohs
+ #undef htonl
+ #undef htons
+
+ static __inline__ unsigned long __ntohl (unsigned long x);
+ static __inline__ unsigned short __ntohs (unsigned short x);
+
+ #define ntohl(x) __ntohl(x)
+ #define ntohs(x) __ntohs(x)
+ #define htonl(x) __ntohl(x)
+ #define htons(x) __ntohs(x)
+
+ static __inline__ unsigned long __ntohl (unsigned long x)
+ {
+ __asm__ ("xchgb %b0, %h0\n\t" /* swap lower bytes */
+ "rorl $16, %0\n\t" /* swap words */
+ "xchgb %b0, %h0" /* swap higher bytes */
+ : "=q" (x) : "0" (x));
+ return (x);
+ }
+
+ static __inline__ unsigned short __ntohs (unsigned short x)
+ {
+ __asm__ ("xchgb %b0, %h0" /* swap bytes */
+ : "=q" (x) : "0" (x));
+ return (x);
+ }
+#endif
+
+/*
+ * If the OS doesn't define AF_INET6 and struct in6_addr:
+ *
+ * define AF_INET6, so we can use it internally as a "this is an
+ * IPv6 address" indication;
+ *
+ * define struct in6_addr so that we can use it for IPv6 addresses.
+ */
+#ifndef HAVE_OS_IPV6_SUPPORT
+#ifndef AF_INET6
+#define AF_INET6 24
+
+struct in6_addr {
+ union {
+ __uint8_t __u6_addr8[16];
+ __uint16_t __u6_addr16[8];
+ __uint32_t __u6_addr32[4];
+ } __u6_addr; /* 128-bit IP6 address */
+};
+#endif
+#endif
+
+#ifndef NI_MAXHOST
+#define NI_MAXHOST 1025
+#endif
+
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN 16
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/*
+ * The Apple deprecation workaround macros below were adopted from the
+ * FreeRADIUS server code under permission of Alan DeKok and Arran Cudbard-Bell.
+ */
+
+#define XSTRINGIFY(x) #x
+
+/*
+ * Macros for controlling warnings in GCC >= 4.2 and clang >= 2.8
+ */
+#define DIAG_JOINSTR(x,y) XSTRINGIFY(x ## y)
+#define DIAG_DO_PRAGMA(x) _Pragma (#x)
+
+/*
+ * The current clang compilers also define __GNUC__ and __GNUC_MINOR__
+ * thus we need to test the clang case before the GCC one
+ */
+#if defined(__clang__)
+# if (__clang_major__ * 100) + __clang_minor__ >= 208
+# define DIAG_PRAGMA(x) DIAG_DO_PRAGMA(clang diagnostic x)
+# define DIAG_OFF(x) DIAG_PRAGMA(push) DIAG_PRAGMA(ignored DIAG_JOINSTR(-W,x))
+# define DIAG_ON(x) DIAG_PRAGMA(pop)
+# else
+# define DIAG_OFF(x)
+# define DIAG_ON(x)
+# endif
+#elif defined(__GNUC__) && ((__GNUC__ * 100) + __GNUC_MINOR__) >= 402
+# define DIAG_PRAGMA(x) DIAG_DO_PRAGMA(GCC diagnostic x)
+# if ((__GNUC__ * 100) + __GNUC_MINOR__) >= 406
+# define DIAG_OFF(x) DIAG_PRAGMA(push) DIAG_PRAGMA(ignored DIAG_JOINSTR(-W,x))
+# define DIAG_ON(x) DIAG_PRAGMA(pop)
+# else
+# define DIAG_OFF(x) DIAG_PRAGMA(ignored DIAG_JOINSTR(-W,x))
+# define DIAG_ON(x) DIAG_PRAGMA(warning DIAG_JOINSTR(-W,x))
+# endif
+#else
+# define DIAG_OFF(x)
+# define DIAG_ON(x)
+#endif
+
+/* Use for clang specific warnings */
+#ifdef __clang__
+# define DIAG_OFF_CLANG(x) DIAG_OFF(x)
+# define DIAG_ON_CLANG(x) DIAG_ON(x)
+#else
+# define DIAG_OFF_CLANG(x)
+# define DIAG_ON_CLANG(x)
+#endif
+
+/*
+ * For dealing with APIs which are only deprecated in OSX (like the OpenSSL API)
+ */
+#ifdef __APPLE__
+# define USES_APPLE_DEPRECATED_API DIAG_OFF(deprecated-declarations)
+# define USES_APPLE_RST DIAG_ON(deprecated-declarations)
+#else
+# define USES_APPLE_DEPRECATED_API
+# define USES_APPLE_RST
+#endif
+
+/*
+ * end of Apple deprecation workaround macros
+ */
+
+/*
+ * Statement attributes, for various compilers.
+ *
+ * This was introduced sufficiently recently that compilers implementing
+ * it also implement __has_attribute() (for example, GCC 5.0 and later
+ * have __has_attribute(), and the "fallthrough" attribute was introduced
+ * in GCC 7).
+ *
+ * Unfortunately, Clang does this wrong - a statement
+ *
+ * __attribute__ ((fallthrough));
+ *
+ * produces bogus -Wmissing-declaration "declaration does not declare
+ * anything" warnings (dear Clang: that's not a declaration, it's an
+ * empty statement). GCC, however, has no trouble with this.
+ */
+#if __has_attribute(fallthrough) && !defined(__clang__)
+# define ND_FALL_THROUGH __attribute__ ((fallthrough))
+#else
+# define ND_FALL_THROUGH
+#endif /* __has_attribute(fallthrough) */
+
+#endif /* netdissect_stdinc_h */
diff --git a/netdissect.c b/netdissect.c
new file mode 100644
index 0000000..7e46d6a
--- /dev/null
+++ b/netdissect.c
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 1988-1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1998-2012 Michael Richardson <mcr@tcpdump.org>
+ * The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef USE_LIBSMI
+#include <smi.h>
+#endif
+
+/*
+ * Initialize anything that must be initialized before dissecting
+ * packets.
+ *
+ * This should be called at the beginning of the program; it does
+ * not need to be called, and should not be called, for every
+ * netdissect_options structure.
+ */
+int
+nd_init(char *errbuf, size_t errbuf_size)
+{
+#ifdef _WIN32
+ WORD wVersionRequested;
+ WSADATA wsaData;
+ int err;
+
+ /*
+ * Request Winsock 2.2; we expect Winsock 2.
+ */
+ wVersionRequested = MAKEWORD(2, 2);
+ err = WSAStartup(wVersionRequested, &wsaData);
+ if (err != 0) {
+ strlcpy(errbuf, "Attempting to initialize Winsock failed",
+ errbuf_size);
+ return (-1);
+ }
+#endif /* _WIN32 */
+
+#ifdef USE_LIBSMI
+ /*
+ * XXX - should we just fail if this fails? Some of the
+ * libsmi calls may fail.
+ */
+ smiInit("tcpdump");
+#endif
+
+ /*
+ * Clears the error buffer, and uses it so we don't get
+ * "unused argument" warnings at compile time.
+ */
+ strlcpy(errbuf, "", errbuf_size);
+ return (0);
+}
+
+/*
+ * Clean up anything that ndo_init() did.
+ */
+void
+nd_cleanup(void)
+{
+#ifdef USE_LIBSMI
+ /*
+ * This appears, in libsmi 0.4.8, to do nothing if smiInit()
+ * wasn't done or failed, so we call it unconditionally.
+ */
+ smiExit();
+#endif
+
+#ifdef _WIN32
+ /*
+ * Undo the WSAStartup() call above.
+ */
+ WSACleanup();
+#endif
+}
+
+int
+nd_have_smi_support(void)
+{
+#ifdef USE_LIBSMI
+ return (1);
+#else
+ return (0);
+#endif
+}
+
+/*
+ * Indicates whether an SMI module has been loaded, so that we can use
+ * libsmi to translate OIDs.
+ */
+int nd_smi_module_loaded;
+
+int
+nd_load_smi_module(const char *module, char *errbuf, size_t errbuf_size)
+{
+#ifdef USE_LIBSMI
+ if (smiLoadModule(module) == 0) {
+ snprintf(errbuf, errbuf_size, "could not load MIB module %s",
+ module);
+ return (-1);
+ }
+ nd_smi_module_loaded = 1;
+ return (0);
+#else
+ snprintf(errbuf, errbuf_size, "MIB module %s not loaded: no libsmi support",
+ module);
+ return (-1);
+#endif
+}
+
+const char *
+nd_smi_version_string(void)
+{
+#ifdef USE_LIBSMI
+ return (smi_version_string);
+#else
+ return (NULL);
+#endif
+}
+
+
+int
+nd_push_buffer(netdissect_options *ndo, u_char *new_buffer,
+ const u_char *new_packetp, const u_char *new_snapend)
+{
+ struct netdissect_saved_packet_info *ndspi;
+
+ ndspi = (struct netdissect_saved_packet_info *)malloc(sizeof(struct netdissect_saved_packet_info));
+ if (ndspi == NULL)
+ return (0); /* fail */
+ ndspi->ndspi_buffer = new_buffer;
+ ndspi->ndspi_packetp = ndo->ndo_packetp;
+ ndspi->ndspi_snapend = ndo->ndo_snapend;
+ ndspi->ndspi_prev = ndo->ndo_packet_info_stack;
+
+ ndo->ndo_packetp = new_packetp;
+ ndo->ndo_snapend = new_snapend;
+ ndo->ndo_packet_info_stack = ndspi;
+
+ return (1); /* success */
+}
+
+/*
+ * Set a new snapshot end to the minimum of the existing snapshot end
+ * and the new snapshot end.
+ */
+int
+nd_push_snapend(netdissect_options *ndo, const u_char *new_snapend)
+{
+ struct netdissect_saved_packet_info *ndspi;
+
+ ndspi = (struct netdissect_saved_packet_info *)malloc(sizeof(struct netdissect_saved_packet_info));
+ if (ndspi == NULL)
+ return (0); /* fail */
+ ndspi->ndspi_buffer = NULL; /* no new buffer */
+ ndspi->ndspi_packetp = ndo->ndo_packetp;
+ ndspi->ndspi_snapend = ndo->ndo_snapend;
+ ndspi->ndspi_prev = ndo->ndo_packet_info_stack;
+
+ /* No new packet pointer, either */
+ if (new_snapend < ndo->ndo_snapend)
+ ndo->ndo_snapend = new_snapend;
+ ndo->ndo_packet_info_stack = ndspi;
+
+ return (1); /* success */
+}
+
+/*
+ * Change an already-pushed snapshot end. This may increase the
+ * snapshot end, as it may be used, for example, for a Jumbo Payload
+ * option in IPv6. It must not increase it past the snapshot length
+ * atop which the current one was pushed, however.
+ */
+void
+nd_change_snapend(netdissect_options *ndo, const u_char *new_snapend)
+{
+ struct netdissect_saved_packet_info *ndspi;
+
+ ndspi = ndo->ndo_packet_info_stack;
+ if (ndspi->ndspi_prev != NULL) {
+ if (new_snapend <= ndspi->ndspi_prev->ndspi_snapend)
+ ndo->ndo_snapend = new_snapend;
+ } else {
+ if (new_snapend < ndo->ndo_snapend)
+ ndo->ndo_snapend = new_snapend;
+ }
+}
+
+void
+nd_pop_packet_info(netdissect_options *ndo)
+{
+ struct netdissect_saved_packet_info *ndspi;
+
+ ndspi = ndo->ndo_packet_info_stack;
+ ndo->ndo_packetp = ndspi->ndspi_packetp;
+ ndo->ndo_snapend = ndspi->ndspi_snapend;
+ ndo->ndo_packet_info_stack = ndspi->ndspi_prev;
+
+ free(ndspi->ndspi_buffer);
+ free(ndspi);
+}
+
+void
+nd_pop_all_packet_info(netdissect_options *ndo)
+{
+ while (ndo->ndo_packet_info_stack != NULL)
+ nd_pop_packet_info(ndo);
+}
diff --git a/netdissect.h b/netdissect.h
new file mode 100644
index 0000000..8595287
--- /dev/null
+++ b/netdissect.h
@@ -0,0 +1,757 @@
+/*
+ * Copyright (c) 1988-1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1998-2012 Michael Richardson <mcr@tcpdump.org>
+ * The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef netdissect_h
+#define netdissect_h
+
+#ifdef HAVE_OS_PROTO_H
+#include "os-proto.h"
+#endif
+#include <sys/types.h>
+#include <setjmp.h>
+#include "status-exit-codes.h"
+
+/*
+ * Data types corresponding to multi-byte integral values within data
+ * structures. These are defined as arrays of octets, so that they're
+ * not aligned on their "natural" boundaries, and so that you *must*
+ * use the EXTRACT_ macros to extract them (which you should be doing
+ * *anyway*, so as not to assume a particular byte order or alignment
+ * in your code).
+ *
+ * We even want EXTRACT_U_1 used for 8-bit integral values, so we
+ * define nd_uint8_t and nd_int8_t as arrays as well.
+ */
+typedef unsigned char nd_uint8_t[1];
+typedef unsigned char nd_uint16_t[2];
+typedef unsigned char nd_uint24_t[3];
+typedef unsigned char nd_uint32_t[4];
+typedef unsigned char nd_uint40_t[5];
+typedef unsigned char nd_uint48_t[6];
+typedef unsigned char nd_uint56_t[7];
+typedef unsigned char nd_uint64_t[8];
+
+typedef signed char nd_int8_t[1];
+
+/*
+ * "unsigned char" so that sign extension isn't done on the
+ * individual bytes while they're being assembled.
+ */
+typedef unsigned char nd_int32_t[4];
+typedef unsigned char nd_int64_t[8];
+
+#define FMAXINT (4294967296.0) /* floating point rep. of MAXINT */
+
+/*
+ * Use this for IPv4 addresses and netmasks.
+ *
+ * It's defined as an array of octets, so that it's not guaranteed to
+ * be aligned on its "natural" boundary (in some packet formats, it
+ * *isn't* so aligned). We have separate EXTRACT_ calls for them;
+ * sometimes you want the host-byte-order value, other times you want
+ * the network-byte-order value.
+ *
+ * Don't use EXTRACT_BE_U_4() on them, use EXTRACT_IPV4_TO_HOST_ORDER()
+ * if you want them in host byte order and EXTRACT_IPV4_TO_NETWORK_ORDER()
+ * if you want them in network byte order (which you want with system APIs
+ * that expect network-order IPv4 addresses, such as inet_ntop()).
+ *
+ * If, on your little-endian machine (e.g., an "IBM-compatible PC", no matter
+ * what the OS, or an Intel Mac, no matter what the OS), you get the wrong
+ * answer, and you've used EXTRACT_BE_U_4(), do *N*O*T* "fix" this by using
+ * EXTRACT_LE_U_4(), fix it by using EXTRACT_IPV4_TO_NETWORK_ORDER(),
+ * otherwise you're breaking the result on big-endian machines (e.g.,
+ * most PowerPC/Power ISA machines, System/390 and z/Architecture, SPARC,
+ * etc.).
+ *
+ * Yes, people do this; that's why Wireshark has tvb_get_ipv4(), to extract
+ * an IPv4 address from a packet data buffer; it was introduced in reaction
+ * to somebody who *had* done that.
+ */
+typedef unsigned char nd_ipv4[4];
+
+/*
+ * Use this for IPv6 addresses and netmasks.
+ */
+typedef unsigned char nd_ipv6[16];
+
+/*
+ * Use this for MAC addresses.
+ */
+#define MAC_ADDR_LEN 6 /* length of MAC addresses */
+typedef unsigned char nd_mac_addr[MAC_ADDR_LEN];
+
+/*
+ * Use this for blobs of bytes; make them arrays of nd_byte.
+ */
+typedef unsigned char nd_byte;
+
+/*
+ * Round up x to a multiple of y; y must be a power of 2.
+ */
+#ifndef roundup2
+#define roundup2(x, y) (((x)+((u_int)((y)-1)))&(~((u_int)((y)-1))))
+#endif
+
+#include <stdarg.h>
+#include <pcap.h>
+
+#include "ip.h" /* struct ip for nextproto4_cksum() */
+#include "ip6.h" /* struct ip6 for nextproto6_cksum() */
+
+#ifndef HAVE_STRLCAT
+extern size_t strlcat (char *, const char *, size_t);
+#endif
+#ifndef HAVE_STRLCPY
+extern size_t strlcpy (char *, const char *, size_t);
+#endif
+
+#ifndef HAVE_STRDUP
+extern char *strdup (const char *str);
+#endif
+
+#ifndef HAVE_STRSEP
+extern char *strsep(char **, const char *);
+#endif
+
+struct tok {
+ u_int v; /* value */
+ const char *s; /* string */
+};
+
+extern const char *tok2strbuf(const struct tok *, const char *, u_int,
+ char *buf, size_t bufsize);
+
+/* tok2str is deprecated */
+extern const char *tok2str(const struct tok *, const char *, u_int);
+extern char *bittok2str(const struct tok *, const char *, u_int);
+extern char *bittok2str_nosep(const struct tok *, const char *, u_int);
+
+/* Initialize netdissect. */
+extern int nd_init(char *, size_t);
+/* Clean up netdissect. */
+extern void nd_cleanup(void);
+
+/* Do we have libsmi support? */
+extern int nd_have_smi_support(void);
+/* Load an SMI module. */
+extern int nd_load_smi_module(const char *, char *, size_t);
+/* Flag indicating whether an SMI module has been loaded. */
+extern int nd_smi_module_loaded;
+/* Version number of the SMI library, or NULL if we don't have libsmi support. */
+extern const char *nd_smi_version_string(void);
+
+typedef struct netdissect_options netdissect_options;
+
+#define IF_PRINTER_ARGS (netdissect_options *, const struct pcap_pkthdr *, const u_char *)
+
+typedef void (*if_printer) IF_PRINTER_ARGS;
+
+/*
+ * In case the data in a buffer needs to be processed by being decrypted,
+ * decompressed, etc. before it's dissected, we can't process it in place,
+ * we have to allocate a new buffer for the processed data.
+ *
+ * We keep a stack of those buffers; when we allocate a new buffer, we
+ * push the current one onto a stack, and when we're done with the new
+ * buffer, we free the current buffer and pop the previous one off the
+ * stack.
+ *
+ * A buffer has a beginnning and end pointer, and a link to the previous
+ * buffer on the stack.
+ *
+ * In other cases, we temporarily adjust the snapshot end to reflect a
+ * packet-length field in the packet data and, when finished dissecting
+ * that part of the packet, restore the old snapshot end. We keep that
+ * on the stack with null buffer pointer, meaning there's nothing to
+ * free.
+ */
+struct netdissect_saved_packet_info {
+ u_char *ndspi_buffer; /* pointer to allocated buffer data */
+ const u_char *ndspi_packetp; /* saved beginning of data */
+ const u_char *ndspi_snapend; /* saved end of data */
+ struct netdissect_saved_packet_info *ndspi_prev; /* previous buffer on the stack */
+};
+
+/* 'val' value(s) for longjmp */
+#define ND_TRUNCATED 1
+
+struct netdissect_options {
+ int ndo_bflag; /* print 4 byte ASes in ASDOT notation */
+ int ndo_eflag; /* print ethernet header */
+ int ndo_fflag; /* don't translate "foreign" IP address */
+ int ndo_Kflag; /* don't check IP, TCP or UDP checksums */
+ int ndo_nflag; /* leave addresses as numbers */
+ int ndo_Nflag; /* remove domains from printed host names */
+ int ndo_qflag; /* quick (shorter) output */
+ int ndo_Sflag; /* print raw TCP sequence numbers */
+ int ndo_tflag; /* print packet arrival time */
+ int ndo_uflag; /* Print undecoded NFS handles */
+ int ndo_vflag; /* verbosity level */
+ int ndo_xflag; /* print packet in hex */
+ int ndo_Xflag; /* print packet in hex/ASCII */
+ int ndo_Aflag; /* print packet only in ASCII observing TAB,
+ * LF, CR and SPACE as graphical chars
+ */
+ int ndo_Hflag; /* dissect 802.11s draft mesh standard */
+ const char *ndo_protocol; /* protocol */
+ jmp_buf ndo_early_end; /* jmp_buf for setjmp()/longjmp() */
+ void *ndo_last_mem_p; /* pointer to the last allocated memory chunk */
+ int ndo_packet_number; /* print a packet number in the beginning of line */
+ int ndo_suppress_default_print; /* don't use default_print() for unknown packet types */
+ int ndo_tstamp_precision; /* requested time stamp precision */
+ const char *program_name; /* Name of the program using the library */
+
+ char *ndo_espsecret;
+ struct sa_list *ndo_sa_list_head; /* used by print-esp.c */
+ struct sa_list *ndo_sa_default;
+
+ char *ndo_sigsecret; /* Signature verification secret key */
+
+ int ndo_packettype; /* as specified by -T */
+
+ int ndo_snaplen;
+ int ndo_ll_hdr_len; /* link-layer header length */
+
+ /*global pointers to beginning and end of current packet (during printing) */
+ const u_char *ndo_packetp;
+ const u_char *ndo_snapend;
+
+ /* stack of saved packet boundary and buffer information */
+ struct netdissect_saved_packet_info *ndo_packet_info_stack;
+
+ /* pointer to the if_printer function */
+ if_printer ndo_if_printer;
+
+ /* pointer to void function to output stuff */
+ void (*ndo_default_print)(netdissect_options *,
+ const u_char *bp, u_int length);
+
+ /* pointer to function to do regular output */
+ int (*ndo_printf)(netdissect_options *,
+ const char *fmt, ...)
+ PRINTFLIKE_FUNCPTR(2, 3);
+ /* pointer to function to output errors */
+ void NORETURN_FUNCPTR (*ndo_error)(netdissect_options *,
+ status_exit_codes_t status,
+ const char *fmt, ...)
+ PRINTFLIKE_FUNCPTR(3, 4);
+ /* pointer to function to output warnings */
+ void (*ndo_warning)(netdissect_options *,
+ const char *fmt, ...)
+ PRINTFLIKE_FUNCPTR(2, 3);
+};
+
+extern int nd_push_buffer(netdissect_options *, u_char *, const u_char *,
+ const u_char *);
+extern int nd_push_snapend(netdissect_options *, const u_char *);
+extern void nd_change_snapend(netdissect_options *, const u_char *);
+extern void nd_pop_packet_info(netdissect_options *);
+extern void nd_pop_all_packet_info(netdissect_options *);
+
+#define PT_VAT 1 /* Visual Audio Tool */
+#define PT_WB 2 /* distributed White Board */
+#define PT_RPC 3 /* Remote Procedure Call */
+#define PT_RTP 4 /* Real-Time Applications protocol */
+#define PT_RTCP 5 /* Real-Time Applications control protocol */
+#define PT_SNMP 6 /* Simple Network Management Protocol */
+#define PT_CNFP 7 /* Cisco NetFlow protocol */
+#define PT_TFTP 8 /* trivial file transfer protocol */
+#define PT_AODV 9 /* Ad-hoc On-demand Distance Vector Protocol */
+#define PT_CARP 10 /* Common Address Redundancy Protocol */
+#define PT_RADIUS 11 /* RADIUS authentication Protocol */
+#define PT_ZMTP1 12 /* ZeroMQ Message Transport Protocol 1.0 */
+#define PT_VXLAN 13 /* Virtual eXtensible Local Area Network */
+#define PT_PGM 14 /* [UDP-encapsulated] Pragmatic General Multicast */
+#define PT_PGM_ZMTP1 15 /* ZMTP/1.0 inside PGM (native or UDP-encapsulated) */
+#define PT_LMP 16 /* Link Management Protocol */
+#define PT_RESP 17 /* RESP */
+#define PT_PTP 18 /* PTP */
+#define PT_SOMEIP 19 /* Autosar SOME/IP Protocol */
+#define PT_DOMAIN 20 /* Domain Name System (DNS) */
+
+#define ND_MIN(a,b) ((a)>(b)?(b):(a))
+#define ND_MAX(a,b) ((b)>(a)?(b):(a))
+
+/* For source or destination ports tests (UDP, TCP, ...) */
+#define IS_SRC_OR_DST_PORT(p) (sport == (p) || dport == (p))
+
+/*
+ * Maximum snapshot length. This should be enough to capture the full
+ * packet on most network interfaces.
+ *
+ *
+ * Somewhat arbitrary, but chosen to be:
+ *
+ * 1) big enough for maximum-size Linux loopback packets (65549)
+ * and some USB packets captured with USBPcap:
+ *
+ * https://desowin.org/usbpcap/
+ *
+ * (> 131072, < 262144)
+ *
+ * and
+ *
+ * 2) small enough not to cause attempts to allocate huge amounts of
+ * memory; some applications might use the snapshot length in a
+ * savefile header to control the size of the buffer they allocate,
+ * so a size of, say, 2^31-1 might not work well.
+ *
+ * XXX - does it need to be bigger still? Note that, for versions of
+ * libpcap with pcap_create()/pcap_activate(), if no -s flag is specified
+ * or -s 0 is specified, we won't set the snapshot length at all, and will
+ * let libpcap choose a snapshot length; newer versions may choose a bigger
+ * value than 262144 for D-Bus, for example.
+ */
+#define MAXIMUM_SNAPLEN 262144
+
+/*
+ * True if "l" bytes from "p" were captured.
+ *
+ * The "ndo->ndo_snapend - (l) <= ndo->ndo_snapend" checks to make sure
+ * "l" isn't so large that "ndo->ndo_snapend - (l)" underflows.
+ *
+ * The check is for <= rather than < because "l" might be 0.
+ *
+ * We cast the pointers to uintptr_t to make sure that the compiler
+ * doesn't optimize away any of these tests (which it is allowed to
+ * do, as adding an integer to, or subtracting an integer from, a
+ * pointer assumes that the pointer is a pointer to an element of an
+ * array and that the result of the addition or subtraction yields a
+ * pointer to another member of the array, so that, for example, if
+ * you subtract a positive integer from a pointer, the result is
+ * guaranteed to be less than the original pointer value). See
+ *
+ * https://www.kb.cert.org/vuls/id/162289
+ */
+
+/*
+ * Test in two parts to avoid these warnings:
+ * comparison of unsigned expression >= 0 is always true [-Wtype-limits],
+ * comparison is always true due to limited range of data type [-Wtype-limits].
+ */
+#define IS_NOT_NEGATIVE(x) (((x) > 0) || ((x) == 0))
+
+#define ND_TTEST_LEN(p, l) \
+ (IS_NOT_NEGATIVE(l) && \
+ ((uintptr_t)ndo->ndo_snapend - (l) <= (uintptr_t)ndo->ndo_snapend && \
+ (uintptr_t)(p) <= (uintptr_t)ndo->ndo_snapend - (l)))
+
+/* True if "*(p)" was captured */
+#define ND_TTEST_SIZE(p) ND_TTEST_LEN(p, sizeof(*(p)))
+
+/* Bail out if "l" bytes from "p" were not captured */
+#ifdef ND_LONGJMP_FROM_TCHECK
+#define ND_TCHECK_LEN(p, l) if (!ND_TTEST_LEN(p, l)) nd_trunc_longjmp(ndo)
+#else
+#define ND_TCHECK_LEN(p, l) if (!ND_TTEST_LEN(p, l)) goto trunc
+#endif
+
+/* Bail out if "*(p)" was not captured */
+#define ND_TCHECK_SIZE(p) ND_TCHECK_LEN(p, sizeof(*(p)))
+
+/*
+ * Number of bytes between two pointers.
+ */
+#define ND_BYTES_BETWEEN(p1, p2) ((u_int)(((const uint8_t *)(p1)) - (const uint8_t *)(p2)))
+
+/*
+ * Number of bytes remaining in the captured data, starting at the
+ * byte pointed to by the argument.
+ */
+#define ND_BYTES_AVAILABLE_AFTER(p) ND_BYTES_BETWEEN(ndo->ndo_snapend, (p))
+
+#define ND_PRINT(...) (ndo->ndo_printf)(ndo, __VA_ARGS__)
+#define ND_DEFAULTPRINT(ap, length) (*ndo->ndo_default_print)(ndo, ap, length)
+
+extern void ts_print(netdissect_options *, const struct timeval *);
+extern void signed_relts_print(netdissect_options *, int32_t);
+extern void unsigned_relts_print(netdissect_options *, uint32_t);
+
+extern void fn_print_char(netdissect_options *, u_char);
+extern void fn_print_str(netdissect_options *, const u_char *);
+extern int nd_print(netdissect_options *, const u_char *, const u_char *);
+extern u_int nd_printztn(netdissect_options *, const u_char *, u_int, const u_char *);
+extern int nd_printn(netdissect_options *, const u_char *, u_int, const u_char *);
+extern void nd_printjnp(netdissect_options *, const u_char *, u_int);
+
+/*
+ * Flags for txtproto_print().
+ */
+#define RESP_CODE_SECOND_TOKEN 0x00000001 /* response code is second token in response line */
+
+extern void txtproto_print(netdissect_options *, const u_char *, u_int,
+ const char **, u_int);
+
+#if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \
+ (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__)) || \
+ (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \
+ (defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64)) || \
+ (defined(__s390__) || defined(__s390x__) || defined(__zarch__)) || \
+ defined(__vax__)
+/*
+ * The procesor natively handles unaligned loads, so just use memcpy()
+ * and memcmp(), to enable those optimizations.
+ *
+ * XXX - are those all the x86 tests we need?
+ * XXX - do we need to worry about ARMv1 through ARMv5, which didn't
+ * support unaligned loads, and, if so, do we need to worry about all
+ * of them, or just some of them, e.g. ARMv5?
+ * XXX - are those the only 68k tests we need not to generated
+ * unaligned accesses if the target is the 68000 or 68010?
+ * XXX - are there any tests we don't need, because some definitions are for
+ * compilers that also predefine the GCC symbols?
+ * XXX - do we need to test for both 32-bit and 64-bit versions of those
+ * architectures in all cases?
+ */
+#define UNALIGNED_MEMCPY(p, q, l) memcpy((p), (q), (l))
+#define UNALIGNED_MEMCMP(p, q, l) memcmp((p), (q), (l))
+#else
+/*
+ * The processor doesn't natively handle unaligned loads,
+ * and the compiler might "helpfully" optimize memcpy()
+ * and memcmp(), when handed pointers that would normally
+ * be properly aligned, into sequences that assume proper
+ * alignment.
+ *
+ * Do copies and compares of possibly-unaligned data by
+ * calling routines that wrap memcpy() and memcmp(), to
+ * prevent that optimization.
+ */
+extern void unaligned_memcpy(void *, const void *, size_t);
+extern int unaligned_memcmp(const void *, const void *, size_t);
+#define UNALIGNED_MEMCPY(p, q, l) unaligned_memcpy((p), (q), (l))
+#define UNALIGNED_MEMCMP(p, q, l) unaligned_memcmp((p), (q), (l))
+#endif
+
+#define PLURAL_SUFFIX(n) \
+ (((n) != 1) ? "s" : "")
+
+extern const char *tok2strary_internal(const char **, int, const char *, int);
+#define tok2strary(a,f,i) tok2strary_internal(a, sizeof(a)/sizeof(a[0]),f,i)
+
+struct uint_tokary
+{
+ u_int uintval;
+ const struct tok *tokary;
+};
+
+extern const struct tok *uint2tokary_internal(const struct uint_tokary[], const size_t, const u_int);
+#define uint2tokary(a, i) uint2tokary_internal(a, sizeof(a)/sizeof(a[0]), i)
+
+extern if_printer lookup_printer(int);
+
+#define ND_DEBUG {printf(" [%s:%d %s] ", __FILE__, __LINE__, __func__); fflush(stdout);}
+
+/* The DLT printer routines */
+
+extern void ap1394_if_print IF_PRINTER_ARGS;
+extern void arcnet_if_print IF_PRINTER_ARGS;
+extern void arcnet_linux_if_print IF_PRINTER_ARGS;
+extern void atm_if_print IF_PRINTER_ARGS;
+extern void bt_if_print IF_PRINTER_ARGS;
+extern void brcm_tag_if_print IF_PRINTER_ARGS;
+extern void brcm_tag_prepend_if_print IF_PRINTER_ARGS;
+extern void chdlc_if_print IF_PRINTER_ARGS;
+extern void cip_if_print IF_PRINTER_ARGS;
+extern void dsa_if_print IF_PRINTER_ARGS;
+extern void edsa_if_print IF_PRINTER_ARGS;
+extern void enc_if_print IF_PRINTER_ARGS;
+extern void ether_if_print IF_PRINTER_ARGS;
+extern void fddi_if_print IF_PRINTER_ARGS;
+extern void fr_if_print IF_PRINTER_ARGS;
+extern void ieee802_11_if_print IF_PRINTER_ARGS;
+extern void ieee802_11_radio_avs_if_print IF_PRINTER_ARGS;
+extern void ieee802_11_radio_if_print IF_PRINTER_ARGS;
+extern void ieee802_15_4_if_print IF_PRINTER_ARGS;
+extern void ieee802_15_4_tap_if_print IF_PRINTER_ARGS;
+extern void ipfc_if_print IF_PRINTER_ARGS;
+extern void ipoib_if_print IF_PRINTER_ARGS;
+extern void ipnet_if_print IF_PRINTER_ARGS;
+extern void juniper_atm1_if_print IF_PRINTER_ARGS;
+extern void juniper_atm2_if_print IF_PRINTER_ARGS;
+extern void juniper_chdlc_if_print IF_PRINTER_ARGS;
+extern void juniper_es_if_print IF_PRINTER_ARGS;
+extern void juniper_ether_if_print IF_PRINTER_ARGS;
+extern void juniper_frelay_if_print IF_PRINTER_ARGS;
+extern void juniper_ggsn_if_print IF_PRINTER_ARGS;
+extern void juniper_mfr_if_print IF_PRINTER_ARGS;
+extern void juniper_mlfr_if_print IF_PRINTER_ARGS;
+extern void juniper_mlppp_if_print IF_PRINTER_ARGS;
+extern void juniper_monitor_if_print IF_PRINTER_ARGS;
+extern void juniper_ppp_if_print IF_PRINTER_ARGS;
+extern void juniper_pppoe_atm_if_print IF_PRINTER_ARGS;
+extern void juniper_pppoe_if_print IF_PRINTER_ARGS;
+extern void juniper_services_if_print IF_PRINTER_ARGS;
+extern void ltalk_if_print IF_PRINTER_ARGS;
+extern void mfr_if_print IF_PRINTER_ARGS;
+extern void netanalyzer_if_print IF_PRINTER_ARGS;
+extern void netanalyzer_transparent_if_print IF_PRINTER_ARGS;
+extern void nflog_if_print IF_PRINTER_ARGS;
+extern void null_if_print IF_PRINTER_ARGS;
+extern void pflog_if_print IF_PRINTER_ARGS;
+extern void pktap_if_print IF_PRINTER_ARGS;
+extern void ppi_if_print IF_PRINTER_ARGS;
+extern void ppp_bsdos_if_print IF_PRINTER_ARGS;
+extern void ppp_hdlc_if_print IF_PRINTER_ARGS;
+extern void ppp_if_print IF_PRINTER_ARGS;
+extern void pppoe_if_print IF_PRINTER_ARGS;
+extern void prism_if_print IF_PRINTER_ARGS;
+extern void raw_if_print IF_PRINTER_ARGS;
+extern void sl_bsdos_if_print IF_PRINTER_ARGS;
+extern void sl_if_print IF_PRINTER_ARGS;
+extern void sll_if_print IF_PRINTER_ARGS;
+extern void sll2_if_print IF_PRINTER_ARGS;
+extern void sunatm_if_print IF_PRINTER_ARGS;
+extern void symantec_if_print IF_PRINTER_ARGS;
+extern void token_if_print IF_PRINTER_ARGS;
+extern void unsupported_if_print IF_PRINTER_ARGS;
+extern void usb_linux_48_byte_if_print IF_PRINTER_ARGS;
+extern void usb_linux_64_byte_if_print IF_PRINTER_ARGS;
+extern void vsock_if_print IF_PRINTER_ARGS;
+
+/*
+ * Structure passed to some printers to allow them to print
+ * link-layer address information if ndo_eflag isn't set
+ * (because they are for protocols that don't have their
+ * own addresses, so that we'd want to report link-layer
+ * address information).
+ *
+ * This contains a pointer to an address and a pointer to a routine
+ * to which we pass that pointer in order to get a string.
+ */
+struct lladdr_info {
+ const char *(*addr_string)(netdissect_options *, const u_char *);
+ const u_char *addr;
+};
+
+/* The printer routines. */
+
+extern void aarp_print(netdissect_options *, const u_char *, u_int);
+extern int ah_print(netdissect_options *, const u_char *);
+extern void ahcp_print(netdissect_options *, const u_char *, u_int);
+extern void aodv_print(netdissect_options *, const u_char *, u_int, int);
+extern void aoe_print(netdissect_options *, const u_char *, const u_int);
+extern int arista_ethertype_print(netdissect_options *,const u_char *, u_int);
+extern void arp_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void ascii_print(netdissect_options *, const u_char *, u_int);
+extern void atalk_print(netdissect_options *, const u_char *, u_int);
+extern void atm_print(netdissect_options *, u_int, u_int, u_int, const u_char *, u_int, u_int);
+extern void babel_print(netdissect_options *, const u_char *, u_int);
+extern void bcm_li_print(netdissect_options *, const u_char *, u_int);
+extern void beep_print(netdissect_options *, const u_char *, u_int);
+extern void bfd_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void bgp_print(netdissect_options *, const u_char *, u_int);
+extern const char *bgp_vpn_rd_print(netdissect_options *, const u_char *);
+extern void bootp_print(netdissect_options *, const u_char *, u_int);
+extern void calm_fast_print(netdissect_options *, const u_char *, u_int, const struct lladdr_info *);
+extern void carp_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void cdp_print(netdissect_options *, const u_char *, u_int);
+extern void cfm_print(netdissect_options *, const u_char *, u_int);
+extern u_int chdlc_print(netdissect_options *, const u_char *, u_int);
+extern void cisco_autorp_print(netdissect_options *, const u_char *, u_int);
+extern void cnfp_print(netdissect_options *, const u_char *);
+extern void dccp_print(netdissect_options *, const u_char *, const u_char *, u_int);
+extern void decnet_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void dhcp6_print(netdissect_options *, const u_char *, u_int);
+extern int dstopt_process(netdissect_options *, const u_char *);
+extern void dtp_print(netdissect_options *, const u_char *, u_int);
+extern void dvmrp_print(netdissect_options *, const u_char *, u_int);
+extern void eap_print(netdissect_options *, const u_char *, u_int);
+extern void eapol_print(netdissect_options *, const u_char *);
+extern void egp_print(netdissect_options *, const u_char *, u_int);
+extern void eigrp_print(netdissect_options *, const u_char *, u_int);
+extern void esp_print(netdissect_options *, const u_char *, u_int, const u_char *, u_int, int, u_int);
+extern u_int ether_print(netdissect_options *, const u_char *, u_int, u_int, void (*)(netdissect_options *, const u_char *), const u_char *);
+extern u_int ether_switch_tag_print(netdissect_options *, const u_char *, u_int, u_int, void (*)(netdissect_options *, const u_char *), u_int);
+extern int ethertype_print(netdissect_options *, u_short, const u_char *, u_int, u_int, const struct lladdr_info *, const struct lladdr_info *);
+extern u_int fddi_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void forces_print(netdissect_options *, const u_char *, u_int);
+extern u_int fr_print(netdissect_options *, const u_char *, u_int);
+extern int frag6_print(netdissect_options *, const u_char *, const u_char *);
+extern void ftp_print(netdissect_options *, const u_char *, u_int);
+extern void geneve_print(netdissect_options *, const u_char *, u_int);
+extern void geonet_print(netdissect_options *, const u_char *, u_int, const struct lladdr_info *);
+extern void gre_print(netdissect_options *, const u_char *, u_int);
+extern int hbhopt_process(netdissect_options *, const u_char *, int *, uint32_t *);
+extern void hex_and_ascii_print(netdissect_options *, const char *, const u_char *, u_int);
+extern void hex_print(netdissect_options *, const char *ident, const u_char *cp, u_int);
+extern void hex_print_with_offset(netdissect_options *, const char *ident, const u_char *cp, u_int, u_int);
+extern void hncp_print(netdissect_options *, const u_char *, u_int);
+extern void hsrp_print(netdissect_options *, const u_char *, u_int);
+extern void http_print(netdissect_options *, const u_char *, u_int);
+extern void icmp6_print(netdissect_options *, const u_char *, u_int, const u_char *, int);
+extern void icmp_print(netdissect_options *, const u_char *, u_int, const u_char *, int);
+extern u_int ieee802_15_4_print(netdissect_options *, const u_char *, u_int);
+extern u_int ieee802_11_radio_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void igmp_print(netdissect_options *, const u_char *, u_int);
+extern void igrp_print(netdissect_options *, const u_char *, u_int);
+extern void ip6_print(netdissect_options *, const u_char *, u_int);
+extern void ipN_print(netdissect_options *, const u_char *, u_int);
+extern void ip_print(netdissect_options *, const u_char *, u_int);
+extern void ipcomp_print(netdissect_options *, const u_char *);
+extern void ipx_netbios_print(netdissect_options *, const u_char *, u_int);
+extern void ipx_print(netdissect_options *, const u_char *, u_int);
+extern void isakmp_print(netdissect_options *, const u_char *, u_int, const u_char *);
+extern void isakmp_rfc3948_print(netdissect_options *, const u_char *, u_int, const u_char *, int, int, u_int);
+extern void isoclns_print(netdissect_options *, const u_char *, u_int);
+extern void krb_print(netdissect_options *, const u_char *);
+extern void l2tp_print(netdissect_options *, const u_char *, u_int);
+extern void lane_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void ldp_print(netdissect_options *, const u_char *, u_int);
+extern void lisp_print(netdissect_options *, const u_char *, u_int);
+extern u_int llap_print(netdissect_options *, const u_char *, u_int);
+extern int llc_print(netdissect_options *, const u_char *, u_int, u_int, const struct lladdr_info *, const struct lladdr_info *);
+extern void lldp_print(netdissect_options *, const u_char *, u_int);
+extern void lmp_print(netdissect_options *, const u_char *, u_int);
+extern void loopback_print(netdissect_options *, const u_char *, u_int);
+extern void lspping_print(netdissect_options *, const u_char *, u_int);
+extern void lwapp_control_print(netdissect_options *, const u_char *, u_int, int);
+extern void lwapp_data_print(netdissect_options *, const u_char *, u_int);
+extern void lwres_print(netdissect_options *, const u_char *, u_int);
+extern void m3ua_print(netdissect_options *, const u_char *, const u_int);
+extern int macsec_print(netdissect_options *, const u_char **,
+ u_int *, u_int *, u_int *, const struct lladdr_info *,
+ const struct lladdr_info *);
+extern u_int mfr_print(netdissect_options *, const u_char *, u_int);
+extern void mobile_print(netdissect_options *, const u_char *, u_int);
+extern int mobility_print(netdissect_options *, const u_char *, const u_char *);
+extern void mpcp_print(netdissect_options *, const u_char *, u_int);
+extern void mpls_print(netdissect_options *, const u_char *, u_int);
+extern int mptcp_print(netdissect_options *, const u_char *, u_int, u_char);
+extern void msdp_print(netdissect_options *, const u_char *, u_int);
+extern void msnlb_print(netdissect_options *, const u_char *);
+extern void nbt_tcp_print(netdissect_options *, const u_char *, u_int);
+extern void nbt_udp137_print(netdissect_options *, const u_char *, u_int);
+extern void nbt_udp138_print(netdissect_options *, const u_char *, u_int);
+extern void netbeui_print(netdissect_options *, u_short, const u_char *, u_int);
+extern void nfsreply_print(netdissect_options *, const u_char *, u_int, const u_char *);
+extern void nfsreply_noaddr_print(netdissect_options *, const u_char *, u_int, const u_char *);
+extern void nfsreq_noaddr_print(netdissect_options *, const u_char *, u_int, const u_char *);
+extern const u_char *fqdn_print(netdissect_options *, const u_char *, const u_char *);
+extern void domain_print(netdissect_options *, const u_char *, u_int, int, int);
+extern void nsh_print(netdissect_options *, const u_char *, u_int);
+extern void ntp_print(netdissect_options *, const u_char *, u_int);
+extern void oam_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void olsr_print(netdissect_options *, const u_char *, u_int, int);
+extern void openflow_print(netdissect_options *, const u_char *, u_int);
+extern void ospf6_print(netdissect_options *, const u_char *, u_int);
+extern void ospf_print(netdissect_options *, const u_char *, u_int, const u_char *);
+extern int ospf_grace_lsa_print(netdissect_options *, const u_char *, u_int);
+extern int ospf_te_lsa_print(netdissect_options *, const u_char *, u_int);
+extern void otv_print(netdissect_options *, const u_char *, u_int);
+extern void pgm_print(netdissect_options *, const u_char *, u_int, const u_char *);
+extern void pim_print(netdissect_options *, const u_char *, u_int, const u_char *);
+extern void pimv1_print(netdissect_options *, const u_char *, u_int);
+extern u_int ppp_print(netdissect_options *, const u_char *, u_int);
+extern u_int pppoe_print(netdissect_options *, const u_char *, u_int);
+extern void pptp_print(netdissect_options *, const u_char *);
+extern void ptp_print(netdissect_options *, const u_char *, u_int);
+extern int print_unknown_data(netdissect_options *, const u_char *, const char *, u_int);
+extern const char *q922_string(netdissect_options *, const u_char *, u_int);
+extern void q933_print(netdissect_options *, const u_char *, u_int);
+extern void radius_print(netdissect_options *, const u_char *, u_int);
+extern void resp_print(netdissect_options *, const u_char *, u_int);
+extern void rip_print(netdissect_options *, const u_char *, u_int);
+extern void ripng_print(netdissect_options *, const u_char *, unsigned int);
+extern void rpki_rtr_print(netdissect_options *, const u_char *, u_int);
+extern void rrcp_print(netdissect_options *, const u_char *, u_int, const struct lladdr_info *, const struct lladdr_info *);
+extern void rsvp_print(netdissect_options *, const u_char *, u_int);
+extern int rt6_print(netdissect_options *, const u_char *, const u_char *);
+extern void rtsp_print(netdissect_options *, const u_char *, u_int);
+extern void rx_print(netdissect_options *, const u_char *, u_int, uint16_t, uint16_t, const u_char *);
+extern void sctp_print(netdissect_options *, const u_char *, const u_char *, u_int);
+extern void sflow_print(netdissect_options *, const u_char *, u_int);
+extern void ssh_print(netdissect_options *, const u_char *, u_int);
+extern void sip_print(netdissect_options *, const u_char *, u_int);
+extern void slow_print(netdissect_options *, const u_char *, u_int);
+extern void smb_tcp_print(netdissect_options *, const u_char *, u_int);
+extern void smtp_print(netdissect_options *, const u_char *, u_int);
+extern int snap_print(netdissect_options *, const u_char *, u_int, u_int, const struct lladdr_info *, const struct lladdr_info *, u_int);
+extern void snmp_print(netdissect_options *, const u_char *, u_int);
+extern void stp_print(netdissect_options *, const u_char *, u_int);
+extern void sunrpc_print(netdissect_options *, const u_char *, u_int, const u_char *);
+extern void syslog_print(netdissect_options *, const u_char *, u_int);
+extern void tcp_print(netdissect_options *, const u_char *, u_int, const u_char *, int);
+extern void telnet_print(netdissect_options *, const u_char *, u_int);
+extern void tftp_print(netdissect_options *, const u_char *, u_int);
+extern void timed_print(netdissect_options *, const u_char *);
+extern void tipc_print(netdissect_options *, const u_char *, u_int, u_int);
+extern u_int token_print(netdissect_options *, const u_char *, u_int, u_int);
+extern void udld_print(netdissect_options *, const u_char *, u_int);
+extern void udp_print(netdissect_options *, const u_char *, u_int, const u_char *, int, u_int);
+extern int vjc_print(netdissect_options *, const u_char *, u_short);
+extern void vqp_print(netdissect_options *, const u_char *, u_int);
+extern void vrrp_print(netdissect_options *, const u_char *, u_int, const u_char *, int);
+extern void vtp_print(netdissect_options *, const u_char *, u_int);
+extern void vxlan_gpe_print(netdissect_options *, const u_char *, u_int);
+extern void vxlan_print(netdissect_options *, const u_char *, u_int);
+extern void wb_print(netdissect_options *, const u_char *, u_int);
+extern void zep_print(netdissect_options *, const u_char *, u_int);
+extern void zephyr_print(netdissect_options *, const u_char *, int);
+extern void zmtp1_print(netdissect_options *, const u_char *, u_int);
+extern void zmtp1_datagram_print(netdissect_options *, const u_char *, const u_int);
+extern void someip_print(netdissect_options *, const u_char *, const u_int);
+
+/* checksum routines */
+extern void init_checksum(void);
+extern uint16_t verify_crc10_cksum(uint16_t, const u_char *, int);
+extern uint16_t create_osi_cksum(const uint8_t *, int, int);
+
+struct cksum_vec {
+ const uint8_t *ptr;
+ int len;
+};
+extern uint16_t in_cksum(const struct cksum_vec *, int);
+extern uint16_t in_cksum_shouldbe(uint16_t, uint16_t);
+
+/* IP protocol demuxing routines */
+extern void ip_demux_print(netdissect_options *, const u_char *, u_int, u_int, int, u_int, uint8_t, const u_char *);
+
+extern uint16_t nextproto4_cksum(netdissect_options *, const struct ip *, const uint8_t *, u_int, u_int, uint8_t);
+
+/* in print-ip6.c */
+extern uint16_t nextproto6_cksum(netdissect_options *, const struct ip6_hdr *, const uint8_t *, u_int, u_int, uint8_t);
+
+/* Utilities */
+extern void nd_print_trunc(netdissect_options *);
+extern void nd_print_protocol(netdissect_options *);
+extern void nd_print_protocol_caps(netdissect_options *);
+extern void nd_print_invalid(netdissect_options *);
+
+extern int mask2plen(uint32_t);
+extern int mask62plen(const u_char *);
+
+extern const char *dnnum_string(netdissect_options *, u_short);
+
+extern int decode_prefix4(netdissect_options *, const u_char *, u_int, char *, size_t);
+extern int decode_prefix6(netdissect_options *, const u_char *, u_int, char *, size_t);
+
+extern void esp_decodesecret_print(netdissect_options *);
+extern int esp_decrypt_buffer_by_ikev2_print(netdissect_options *, int,
+ const u_char spii[8],
+ const u_char spir[8],
+ const u_char *, const u_char *);
+
+#endif /* netdissect_h */
diff --git a/nfs.h b/nfs.h
new file mode 100644
index 0000000..76747f1
--- /dev/null
+++ b/nfs.h
@@ -0,0 +1,417 @@
+/* NetBSD: nfs.h,v 1.1 1996/05/23 22:49:53 fvdl Exp */
+
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsproto.h 8.2 (Berkeley) 3/30/95
+ */
+
+/*
+ * nfs definitions as per the Version 2 and 3 specs
+ */
+
+/*
+ * Constants as defined in the Sun NFS Version 2 and 3 specs.
+ * "NFS: Network File System Protocol Specification" RFC1094
+ * and in the "NFS: Network File System Version 3 Protocol
+ * Specification"
+ */
+
+#define NFS_PORT 2049
+#define NFS_PROG 100003
+#define NFS_VER2 2
+#define NFS_VER3 3
+#define NFS_V2MAXDATA 8192
+#define NFS_MAXDGRAMDATA 16384
+#define NFS_MAXDATA 32768
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_MAXPKTHDR 404
+#define NFS_MAXPACKET (NFS_MAXPKTHDR + NFS_MAXDATA)
+#define NFS_MINPACKET 20
+#define NFS_FABLKSIZE 512 /* Size in bytes of a block wrt fa_blocks */
+
+/* Stat numbers for rpc returns (version 2 and 3) */
+#define NFS_OK 0
+#define NFSERR_PERM 1
+#define NFSERR_NOENT 2
+#define NFSERR_IO 5
+#define NFSERR_NXIO 6
+#define NFSERR_ACCES 13
+#define NFSERR_EXIST 17
+#define NFSERR_XDEV 18 /* Version 3 only */
+#define NFSERR_NODEV 19
+#define NFSERR_NOTDIR 20
+#define NFSERR_ISDIR 21
+#define NFSERR_INVAL 22 /* Version 3 only */
+#define NFSERR_FBIG 27
+#define NFSERR_NOSPC 28
+#define NFSERR_ROFS 30
+#define NFSERR_MLINK 31 /* Version 3 only */
+#define NFSERR_NAMETOL 63
+#define NFSERR_NOTEMPTY 66
+#define NFSERR_DQUOT 69
+#define NFSERR_STALE 70
+#define NFSERR_REMOTE 71 /* Version 3 only */
+#define NFSERR_WFLUSH 99 /* Version 2 only */
+#define NFSERR_BADHANDLE 10001 /* The rest Version 3 only */
+#define NFSERR_NOT_SYNC 10002
+#define NFSERR_BAD_COOKIE 10003
+#define NFSERR_NOTSUPP 10004
+#define NFSERR_TOOSMALL 10005
+#define NFSERR_SERVERFAULT 10006
+#define NFSERR_BADTYPE 10007
+#define NFSERR_JUKEBOX 10008
+#define NFSERR_TRYLATER NFSERR_JUKEBOX
+#define NFSERR_STALEWRITEVERF 30001 /* Fake return for nfs_commit() */
+
+#define NFSERR_RETVOID 0x20000000 /* Return void, not error */
+#define NFSERR_AUTHERR 0x40000000 /* Mark an authentication error */
+#define NFSERR_RETERR 0x80000000 /* Mark an error return for V3 */
+
+/* Sizes in bytes of various nfs rpc components */
+#define NFSX_UNSIGNED 4
+
+/* specific to NFS Version 2 */
+#define NFSX_V2FH 32
+#define NFSX_V2FATTR 68
+#define NFSX_V2SATTR 32
+#define NFSX_V2COOKIE 4
+#define NFSX_V2STATFS 20
+
+/* specific to NFS Version 3 */
+#if 0
+#define NFSX_V3FH (sizeof (fhandle_t)) /* size this server uses */
+#endif
+#define NFSX_V3FHMAX 64 /* max. allowed by protocol */
+#define NFSX_V3FATTR 84
+#define NFSX_V3SATTR 60 /* max. all fields filled in */
+#define NFSX_V3POSTOPATTR (NFSX_V3FATTR + NFSX_UNSIGNED)
+#define NFSX_V3WCCDATA (NFSX_V3POSTOPATTR + 8 * NFSX_UNSIGNED)
+#define NFSX_V3COOKIEVERF 8
+#define NFSX_V3WRITEVERF 8
+#define NFSX_V3CREATEVERF 8
+#define NFSX_V3STATFS 52
+#define NFSX_V3FSINFO 48
+#define NFSX_V3PATHCONF 24
+
+/* variants for both versions */
+#define NFSX_FH(v3) ((v3) ? (NFSX_V3FHMAX + NFSX_UNSIGNED) : \
+ NFSX_V2FH)
+#define NFSX_SRVFH(v3) ((v3) ? NFSX_V3FH : NFSX_V2FH)
+#define NFSX_FATTR(v3) ((v3) ? NFSX_V3FATTR : NFSX_V2FATTR)
+#define NFSX_PREOPATTR(v3) ((v3) ? (7 * NFSX_UNSIGNED) : 0)
+#define NFSX_POSTOPATTR(v3) ((v3) ? (NFSX_V3FATTR + NFSX_UNSIGNED) : 0)
+#define NFSX_POSTOPORFATTR(v3) ((v3) ? (NFSX_V3FATTR + NFSX_UNSIGNED) : \
+ NFSX_V2FATTR)
+#define NFSX_WCCDATA(v3) ((v3) ? NFSX_V3WCCDATA : 0)
+#define NFSX_WCCORFATTR(v3) ((v3) ? NFSX_V3WCCDATA : NFSX_V2FATTR)
+#define NFSX_COOKIEVERF(v3) ((v3) ? NFSX_V3COOKIEVERF : 0)
+#define NFSX_WRITEVERF(v3) ((v3) ? NFSX_V3WRITEVERF : 0)
+#define NFSX_READDIR(v3) ((v3) ? (5 * NFSX_UNSIGNED) : \
+ (2 * NFSX_UNSIGNED))
+#define NFSX_STATFS(v3) ((v3) ? NFSX_V3STATFS : NFSX_V2STATFS)
+
+/* nfs rpc procedure numbers (before version mapping) */
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_LOOKUP 3
+#define NFSPROC_ACCESS 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITE 7
+#define NFSPROC_CREATE 8
+#define NFSPROC_MKDIR 9
+#define NFSPROC_SYMLINK 10
+#define NFSPROC_MKNOD 11
+#define NFSPROC_REMOVE 12
+#define NFSPROC_RMDIR 13
+#define NFSPROC_RENAME 14
+#define NFSPROC_LINK 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_READDIRPLUS 17
+#define NFSPROC_FSSTAT 18
+#define NFSPROC_FSINFO 19
+#define NFSPROC_PATHCONF 20
+#define NFSPROC_COMMIT 21
+
+/* And leasing (nqnfs) procedure numbers (must be last) */
+#define NQNFSPROC_GETLEASE 22
+#define NQNFSPROC_VACATED 23
+#define NQNFSPROC_EVICTED 24
+
+#define NFSPROC_NOOP 25
+#define NFS_NPROCS 26
+
+/* Actual Version 2 procedure numbers */
+#define NFSV2PROC_NULL 0
+#define NFSV2PROC_GETATTR 1
+#define NFSV2PROC_SETATTR 2
+#define NFSV2PROC_NOOP 3
+#define NFSV2PROC_ROOT NFSV2PROC_NOOP /* Obsolete */
+#define NFSV2PROC_LOOKUP 4
+#define NFSV2PROC_READLINK 5
+#define NFSV2PROC_READ 6
+#define NFSV2PROC_WRITECACHE NFSV2PROC_NOOP /* Obsolete */
+#define NFSV2PROC_WRITE 8
+#define NFSV2PROC_CREATE 9
+#define NFSV2PROC_REMOVE 10
+#define NFSV2PROC_RENAME 11
+#define NFSV2PROC_LINK 12
+#define NFSV2PROC_SYMLINK 13
+#define NFSV2PROC_MKDIR 14
+#define NFSV2PROC_RMDIR 15
+#define NFSV2PROC_READDIR 16
+#define NFSV2PROC_STATFS 17
+
+/*
+ * Constants used by the Version 3 protocol for various RPCs
+ */
+#define NFSV3SATTRTIME_DONTCHANGE 0
+#define NFSV3SATTRTIME_TOSERVER 1
+#define NFSV3SATTRTIME_TOCLIENT 2
+
+#define NFSV3ATTRTIME_NMODES 3
+
+#define NFSV3ACCESS_READ 0x01
+#define NFSV3ACCESS_LOOKUP 0x02
+#define NFSV3ACCESS_MODIFY 0x04
+#define NFSV3ACCESS_EXTEND 0x08
+#define NFSV3ACCESS_DELETE 0x10
+#define NFSV3ACCESS_EXECUTE 0x20
+#define NFSV3ACCESS_FULL 0x3f
+
+#define NFSV3WRITE_UNSTABLE 0
+#define NFSV3WRITE_DATASYNC 1
+#define NFSV3WRITE_FILESYNC 2
+
+#define NFSV3WRITE_NMODES 3
+
+#define NFSV3CREATE_UNCHECKED 0
+#define NFSV3CREATE_GUARDED 1
+#define NFSV3CREATE_EXCLUSIVE 2
+
+#define NFSV3CREATE_NMODES 3
+
+#define NFSV3FSINFO_LINK 0x01
+#define NFSV3FSINFO_SYMLINK 0x02
+#define NFSV3FSINFO_HOMOGENEOUS 0x08
+#define NFSV3FSINFO_CANSETTIME 0x10
+
+/* Conversion macros */
+#define vtonfsv2_mode(t,m) \
+ txdr_unsigned(((t) == VFIFO) ? MAKEIMODE(VCHR, (m)) : \
+ MAKEIMODE((t), (m)))
+#define vtonfsv3_mode(m) txdr_unsigned((m) & 07777)
+#define nfstov_mode(a) (fxdr_unsigned(uint16_t, (a))&07777)
+#define vtonfsv2_type(a) txdr_unsigned(nfsv2_type[((int32_t)(a))])
+#define vtonfsv3_type(a) txdr_unsigned(nfsv3_type[((int32_t)(a))])
+#define nfsv2tov_type(a) nv2tov_type[fxdr_unsigned(uint32_t,(a))&0x7]
+#define nfsv3tov_type(a) nv3tov_type[fxdr_unsigned(uint32_t,(a))&0x7]
+
+/* File types */
+typedef enum { NFNON=0, NFREG=1, NFDIR=2, NFBLK=3, NFCHR=4, NFLNK=5,
+ NFSOCK=6, NFFIFO=7 } nfs_type;
+
+/*
+ * Structs for common parts of the rpc's
+ *
+ * NOTE: these structures are not always overlaid directly on the
+ * packet data - sometimes we declare a local variable of that type,
+ * and fill it up with fields extracted using byte pointers - so we
+ * don't use nd_ types for their members.
+ */
+
+/*
+ * File Handle (32 bytes for version 2), variable up to 64 for version 3.
+ * File Handles of up to NFS_SMALLFH in size are stored directly in the
+ * nfs node, whereas larger ones are malloc'd. (This never happens when
+ * NFS_SMALLFH is set to 64.)
+ * NFS_SMALLFH should be in the range of 32 to 64 and be divisible by 4.
+ */
+#ifndef NFS_SMALLFH
+#define NFS_SMALLFH 64
+#endif
+union nfsfh {
+/* fhandle_t fh_generic; */
+ u_char fh_bytes[NFS_SMALLFH];
+};
+typedef union nfsfh nfsfh_t;
+
+struct nfsv2_time {
+ nd_uint32_t nfsv2_sec;
+ nd_uint32_t nfsv2_usec;
+};
+typedef struct nfsv2_time nfstime2;
+
+struct nfsv3_time {
+ nd_uint32_t nfsv3_sec;
+ nd_uint32_t nfsv3_nsec;
+};
+typedef struct nfsv3_time nfstime3;
+
+/*
+ * NFS Version 3 special file number.
+ */
+struct nfsv3_spec {
+ nd_uint32_t specdata1;
+ nd_uint32_t specdata2;
+};
+typedef struct nfsv3_spec nfsv3spec;
+
+/*
+ * File attributes and setable attributes. These structures cover both
+ * NFS version 2 and the version 3 protocol. Note that the union is only
+ * used so that one pointer can refer to both variants. These structures
+ * go out on the wire and must be densely packed, so no quad data types
+ * are used. (all fields are longs or u_longs or structures of same)
+ * NB: You can't do sizeof(struct nfs_fattr), you must use the
+ * NFSX_FATTR(v3) macro.
+ */
+struct nfs_fattr {
+ nd_uint32_t fa_type;
+ nd_uint32_t fa_mode;
+ nd_uint32_t fa_nlink;
+ nd_uint32_t fa_uid;
+ nd_uint32_t fa_gid;
+ union {
+ struct {
+ nd_uint32_t nfsv2fa_size;
+ nd_uint32_t nfsv2fa_blocksize;
+ nd_uint32_t nfsv2fa_rdev;
+ nd_uint32_t nfsv2fa_blocks;
+ nd_uint32_t nfsv2fa_fsid;
+ nd_uint32_t nfsv2fa_fileid;
+ nfstime2 nfsv2fa_atime;
+ nfstime2 nfsv2fa_mtime;
+ nfstime2 nfsv2fa_ctime;
+ } fa_nfsv2;
+ struct {
+ nd_uint64_t nfsv3fa_size;
+ nd_uint64_t nfsv3fa_used;
+ nfsv3spec nfsv3fa_rdev;
+ nd_uint64_t nfsv3fa_fsid;
+ nd_uint64_t nfsv3fa_fileid;
+ nfstime3 nfsv3fa_atime;
+ nfstime3 nfsv3fa_mtime;
+ nfstime3 nfsv3fa_ctime;
+ } fa_nfsv3;
+ } fa_un;
+};
+
+/* and some ugly defines for accessing union components */
+#define fa2_size fa_un.fa_nfsv2.nfsv2fa_size
+#define fa2_blocksize fa_un.fa_nfsv2.nfsv2fa_blocksize
+#define fa2_rdev fa_un.fa_nfsv2.nfsv2fa_rdev
+#define fa2_blocks fa_un.fa_nfsv2.nfsv2fa_blocks
+#define fa2_fsid fa_un.fa_nfsv2.nfsv2fa_fsid
+#define fa2_fileid fa_un.fa_nfsv2.nfsv2fa_fileid
+#define fa2_atime fa_un.fa_nfsv2.nfsv2fa_atime
+#define fa2_mtime fa_un.fa_nfsv2.nfsv2fa_mtime
+#define fa2_ctime fa_un.fa_nfsv2.nfsv2fa_ctime
+#define fa3_size fa_un.fa_nfsv3.nfsv3fa_size
+#define fa3_used fa_un.fa_nfsv3.nfsv3fa_used
+#define fa3_rdev fa_un.fa_nfsv3.nfsv3fa_rdev
+#define fa3_fsid fa_un.fa_nfsv3.nfsv3fa_fsid
+#define fa3_fileid fa_un.fa_nfsv3.nfsv3fa_fileid
+#define fa3_atime fa_un.fa_nfsv3.nfsv3fa_atime
+#define fa3_mtime fa_un.fa_nfsv3.nfsv3fa_mtime
+#define fa3_ctime fa_un.fa_nfsv3.nfsv3fa_ctime
+
+struct nfsv2_sattr {
+ nd_uint32_t sa_mode;
+ nd_uint32_t sa_uid;
+ nd_uint32_t sa_gid;
+ nd_uint32_t sa_size;
+ nfstime2 sa_atime;
+ nfstime2 sa_mtime;
+};
+
+struct nfs_statfs {
+ union {
+ struct {
+ nd_uint32_t nfsv2sf_tsize;
+ nd_uint32_t nfsv2sf_bsize;
+ nd_uint32_t nfsv2sf_blocks;
+ nd_uint32_t nfsv2sf_bfree;
+ nd_uint32_t nfsv2sf_bavail;
+ } sf_nfsv2;
+ struct {
+ nd_uint64_t nfsv3sf_tbytes;
+ nd_uint64_t nfsv3sf_fbytes;
+ nd_uint64_t nfsv3sf_abytes;
+ nd_uint64_t nfsv3sf_tfiles;
+ nd_uint64_t nfsv3sf_ffiles;
+ nd_uint64_t nfsv3sf_afiles;
+ nd_uint32_t nfsv3sf_invarsec;
+ } sf_nfsv3;
+ } sf_un;
+};
+
+#define sf_tsize sf_un.sf_nfsv2.nfsv2sf_tsize
+#define sf_bsize sf_un.sf_nfsv2.nfsv2sf_bsize
+#define sf_blocks sf_un.sf_nfsv2.nfsv2sf_blocks
+#define sf_bfree sf_un.sf_nfsv2.nfsv2sf_bfree
+#define sf_bavail sf_un.sf_nfsv2.nfsv2sf_bavail
+#define sf_tbytes sf_un.sf_nfsv3.nfsv3sf_tbytes
+#define sf_fbytes sf_un.sf_nfsv3.nfsv3sf_fbytes
+#define sf_abytes sf_un.sf_nfsv3.nfsv3sf_abytes
+#define sf_tfiles sf_un.sf_nfsv3.nfsv3sf_tfiles
+#define sf_ffiles sf_un.sf_nfsv3.nfsv3sf_ffiles
+#define sf_afiles sf_un.sf_nfsv3.nfsv3sf_afiles
+#define sf_invarsec sf_un.sf_nfsv3.nfsv3sf_invarsec
+
+struct nfsv3_fsinfo {
+ nd_uint32_t fs_rtmax;
+ nd_uint32_t fs_rtpref;
+ nd_uint32_t fs_rtmult;
+ nd_uint32_t fs_wtmax;
+ nd_uint32_t fs_wtpref;
+ nd_uint32_t fs_wtmult;
+ nd_uint32_t fs_dtpref;
+ nd_uint64_t fs_maxfilesize;
+ nfstime3 fs_timedelta;
+ nd_uint32_t fs_properties;
+};
+
+struct nfsv3_pathconf {
+ nd_uint32_t pc_linkmax;
+ nd_uint32_t pc_namemax;
+ nd_uint32_t pc_notrunc;
+ nd_uint32_t pc_chownrestricted;
+ nd_uint32_t pc_caseinsensitive;
+ nd_uint32_t pc_casepreserving;
+};
diff --git a/nfsfh.h b/nfsfh.h
new file mode 100644
index 0000000..bf378bb
--- /dev/null
+++ b/nfsfh.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1993, 1994 Jeffrey C. Mogul, Digital Equipment Corporation,
+ * Western Research Laboratory. All rights reserved.
+ * Copyright (c) 2001 Compaq Computer Corporation. All rights reserved.
+ *
+ * Permission to use, copy, and modify this software and its
+ * documentation is hereby granted only under the following terms and
+ * conditions. Both the above copyright notice and this permission
+ * notice must appear in all copies of the software, derivative works
+ * or modified versions, and any portions thereof, and both notices
+ * must appear in supporting documentation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND COMPAQ COMPUTER CORPORATION
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL COMPAQ COMPUTER CORPORATION BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * nfsfh.h - NFS file handle definitions (for portable use)
+ *
+ * Jeffrey C. Mogul
+ * Digital Equipment Corporation
+ * Western Research Laboratory
+ */
+
+/*
+ * Internal representation of dev_t, because different NFS servers
+ * that we might be spying upon use different external representations.
+ */
+typedef struct {
+ uint32_t Minor; /* upper case to avoid clashing with macro names */
+ uint32_t Major;
+} my_devt;
+
+#define dev_eq(a,b) ((a.Minor == b.Minor) && (a.Major == b.Major))
+
+/*
+ * Many file servers now use a large file system ID. This is
+ * our internal representation of that.
+ */
+typedef struct {
+ my_devt Fsid_dev; /* XXX avoid name conflict with AIX */
+ char Opaque_Handle[2 * 32 + 1];
+ uint32_t fsid_code;
+} my_fsid;
+
+#define fsid_eq(a,b) ((a.fsid_code == b.fsid_code) &&\
+ dev_eq(a.Fsid_dev, b.Fsid_dev))
+
+extern void Parse_fh(netdissect_options *, const unsigned char *, u_int, my_fsid *, uint32_t *, const char **, const char **, int);
diff --git a/nlpid.c b/nlpid.c
new file mode 100644
index 0000000..59f6e59
--- /dev/null
+++ b/nlpid.c
@@ -0,0 +1,41 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include "nlpid.h"
+
+const struct tok nlpid_values[] = {
+ { NLPID_NULLNS, "NULL" },
+ { NLPID_Q933, "Q.933" },
+ { NLPID_LMI, "LMI" },
+ { NLPID_SNAP, "SNAP" },
+ { NLPID_CLNP, "CLNP" },
+ { NLPID_ESIS, "ES-IS" },
+ { NLPID_ISIS, "IS-IS" },
+ { NLPID_CONS, "CONS" },
+ { NLPID_IDRP, "IDRP" },
+ { NLPID_SPB, "ISIS_SPB" },
+ { NLPID_MFR, "FRF.15" },
+ { NLPID_IP, "IPv4" },
+ { NLPID_PPP, "PPP" },
+ { NLPID_X25_ESIS, "X25 ES-IS" },
+ { NLPID_IP6, "IPv6" },
+ { 0, NULL }
+};
diff --git a/nlpid.h b/nlpid.h
new file mode 100644
index 0000000..a3a6905
--- /dev/null
+++ b/nlpid.h
@@ -0,0 +1,32 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+extern const struct tok nlpid_values[];
+
+#define NLPID_NULLNS 0x00
+#define NLPID_Q933 0x08 /* ANSI T1.617 Annex D or ITU-T Q.933 Annex A */
+#define NLPID_LMI 0x09 /* The original, aka Cisco, aka Gang of Four */
+#define NLPID_SNAP 0x80
+#define NLPID_CLNP 0x81 /* iso9577 */
+#define NLPID_ESIS 0x82 /* iso9577 */
+#define NLPID_ISIS 0x83 /* iso9577 */
+#define NLPID_CONS 0x84
+#define NLPID_IDRP 0x85
+#define NLPID_MFR 0xb1 /* FRF.15 */
+#define NLPID_SPB 0xc1 /* IEEE 802.1aq/D4.5 */
+#define NLPID_IP 0xcc
+#define NLPID_PPP 0xcf
+#define NLPID_X25_ESIS 0x8a
+#define NLPID_IP6 0x8e
diff --git a/ntp.c b/ntp.c
new file mode 100644
index 0000000..4d17932
--- /dev/null
+++ b/ntp.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "ntp.h"
+
+#include "extract.h"
+
+#define JAN_1970 INT64_T_CONSTANT(2208988800) /* 1970 - 1900 in seconds */
+
+void
+p_ntp_time(netdissect_options *ndo,
+ const struct l_fixedpt *lfp)
+{
+ uint32_t i;
+ uint32_t uf;
+ uint32_t f;
+ double ff;
+
+ i = GET_BE_U_4(lfp->int_part);
+ uf = GET_BE_U_4(lfp->fraction);
+ ff = uf;
+ if (ff < 0.0) /* some compilers are buggy */
+ ff += FMAXINT;
+ ff = ff / FMAXINT; /* shift radix point by 32 bits */
+ f = (uint32_t)(ff * 1000000000.0); /* treat fraction as parts per billion */
+ ND_PRINT("%u.%09u", i, f);
+
+#ifdef HAVE_STRFTIME
+ /*
+ * print the UTC time in human-readable format.
+ */
+ if (i) {
+ int64_t seconds_64bit = (int64_t)i - JAN_1970;
+ time_t seconds;
+ struct tm *tm;
+ char time_buf[128];
+
+ seconds = (time_t)seconds_64bit;
+ if (seconds != seconds_64bit) {
+ /*
+ * It doesn't fit into a time_t, so we can't hand it
+ * to gmtime.
+ */
+ ND_PRINT(" (unrepresentable)");
+ } else {
+ tm = gmtime(&seconds);
+ if (tm == NULL) {
+ /*
+ * gmtime() can't handle it.
+ * (Yes, that might happen with some version of
+ * Microsoft's C library.)
+ */
+ ND_PRINT(" (unrepresentable)");
+ } else {
+ /* use ISO 8601 (RFC3339) format */
+ strftime(time_buf, sizeof (time_buf), "%Y-%m-%dT%H:%M:%SZ", tm);
+ ND_PRINT(" (%s)", time_buf);
+ }
+ }
+ }
+#endif
+}
diff --git a/ntp.h b/ntp.h
new file mode 100644
index 0000000..78644e2
--- /dev/null
+++ b/ntp.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+/*
+ * Structure definitions for NTP fixed point values
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+struct l_fixedpt {
+ nd_uint32_t int_part;
+ nd_uint32_t fraction;
+};
+
+struct s_fixedpt {
+ nd_uint16_t int_part;
+ nd_uint16_t fraction;
+};
+
+void p_ntp_time(netdissect_options *, const struct l_fixedpt *);
diff --git a/openflow.h b/openflow.h
new file mode 100644
index 0000000..2d56d15
--- /dev/null
+++ b/openflow.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2013 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* OpenFlow: protocol between controller and datapath. */
+
+/* for netdissect_options */
+#include "netdissect.h"
+
+#define OF_FWD(n) { \
+ cp += (n); \
+ len -= (n); \
+}
+
+#define OF_CHK_FWD(n) { \
+ ND_TCHECK_LEN(cp, (n)); \
+ cp += (n); \
+ len -= (n); \
+}
+
+#define OF_VER_1_0 0x01U
+#define OF_VER_1_1 0x02U
+#define OF_VER_1_2 0x03U
+#define OF_VER_1_3 0x04U
+#define OF_VER_1_4 0x05U
+#define OF_VER_1_5 0x06U
+
+#define OF_HEADER_FIXLEN 8U
+
+#define ONF_EXP_ONF 0x4f4e4600
+#define ONF_EXP_BUTE 0xff000001
+#define ONF_EXP_NOVIFLOW 0xff000002
+#define ONF_EXP_L3 0xff000003
+#define ONF_EXP_L4L7 0xff000004
+#define ONF_EXP_WMOB 0xff000005
+#define ONF_EXP_FABS 0xff000006
+#define ONF_EXP_OTRANS 0xff000007
+#define ONF_EXP_NBLNCTU 0xff000008
+#define ONF_EXP_MPCE 0xff000009
+#define ONF_EXP_MPLSTPSPTN 0xff00000a
+extern const struct tok onf_exp_str[];
+
+extern const char * of_vendor_name(const uint32_t);
+extern void of_bitmap_print(netdissect_options *ndo,
+ const struct tok *, const uint32_t, const uint32_t);
+extern void of_data_print(netdissect_options *ndo,
+ const u_char *, const u_int);
+
+/*
+ * Routines to handle various versions of OpenFlow.
+ */
+
+struct of_msgtypeinfo {
+ /* Should not be NULL. */
+ const char *name;
+ /* May be NULL to mean "message body printing is not implemented". */
+ void (*decoder)(netdissect_options *ndo, const u_char *, const u_int);
+ enum {
+ REQ_NONE, /* Message body length may be anything. */
+ REQ_FIXLEN, /* Message body length must be == req_value. */
+ REQ_MINLEN, /* Message body length must be >= req_value. */
+ } req_what;
+ uint16_t req_value;
+};
+
+extern const struct of_msgtypeinfo *of10_identify_msgtype(const uint8_t);
+extern const struct of_msgtypeinfo *of13_identify_msgtype(const uint8_t);
diff --git a/ospf.h b/ospf.h
new file mode 100644
index 0000000..a4e90c8
--- /dev/null
+++ b/ospf.h
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * OSPF support contributed by Jeffrey Honig (jch@mitchell.cit.cornell.edu)
+ */
+#define OSPF_TYPE_HELLO 1 /* Hello */
+#define OSPF_TYPE_DD 2 /* Database Description */
+#define OSPF_TYPE_LS_REQ 3 /* Link State Request */
+#define OSPF_TYPE_LS_UPDATE 4 /* Link State Update */
+#define OSPF_TYPE_LS_ACK 5 /* Link State Ack */
+
+/* Options field
+ *
+ * +------------------------------------+
+ * | DN | O | DC | L | N/P | MC | E | T |
+ * +------------------------------------+
+ *
+ */
+
+#define OSPF_OPTION_MT 0x01 /* MT bit: multi-topology */
+#define OSPF_OPTION_E 0x02 /* E bit: External routes advertised */
+#define OSPF_OPTION_MC 0x04 /* MC bit: Multicast capable */
+#define OSPF_OPTION_NP 0x08 /* N/P bit: NSSA capable */
+#define OSPF_OPTION_L 0x10 /* L bit: Packet contains LLS data block */
+#define OSPF_OPTION_DC 0x20 /* DC bit: Demand circuit capable */
+#define OSPF_OPTION_O 0x40 /* O bit: Opaque LSA capable */
+#define OSPF_OPTION_DN 0x80 /* DN bit: Up/Down Bit capable - draft-ietf-ospf-2547-dnbit-04 */
+
+/* ospf_authtype */
+#define OSPF_AUTH_NONE 0 /* No auth-data */
+#define OSPF_AUTH_SIMPLE 1 /* Simple password */
+#define OSPF_AUTH_SIMPLE_LEN 8 /* max length of simple authentication */
+#define OSPF_AUTH_MD5 2 /* MD5 authentication */
+#define OSPF_AUTH_MD5_LEN 16 /* length of MD5 authentication */
+
+/* db_flags */
+#define OSPF_DB_INIT 0x04
+#define OSPF_DB_MORE 0x02
+#define OSPF_DB_MASTER 0x01
+#define OSPF_DB_RESYNC 0x08 /* RFC4811 */
+
+/* ls_type */
+#define LS_TYPE_ROUTER 1 /* router link */
+#define LS_TYPE_NETWORK 2 /* network link */
+#define LS_TYPE_SUM_IP 3 /* summary link */
+#define LS_TYPE_SUM_ABR 4 /* summary area link */
+#define LS_TYPE_ASE 5 /* ASE */
+#define LS_TYPE_GROUP 6 /* Group membership (multicast */
+ /* extensions 23 July 1991) */
+#define LS_TYPE_NSSA 7 /* rfc3101 - Not so Stubby Areas */
+#define LS_TYPE_OPAQUE_LL 9 /* rfc2370 - Opaque Link Local */
+#define LS_TYPE_OPAQUE_AL 10 /* rfc2370 - Opaque Link Local */
+#define LS_TYPE_OPAQUE_DW 11 /* rfc2370 - Opaque Domain Wide */
+
+#define LS_OPAQUE_TYPE_TE 1 /* rfc3630 */
+#define LS_OPAQUE_TYPE_GRACE 3 /* rfc3623 */
+#define LS_OPAQUE_TYPE_RI 4 /* draft-ietf-ospf-cap-03 */
+
+#define LS_OPAQUE_TE_TLV_ROUTER 1 /* rfc3630 */
+#define LS_OPAQUE_TE_TLV_LINK 2 /* rfc3630 */
+
+#define LS_OPAQUE_TE_LINK_SUBTLV_LINK_TYPE 1 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_LINK_ID 2 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_LOCAL_IP 3 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_REMOTE_IP 4 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_TE_METRIC 5 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_MAX_BW 6 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_MAX_RES_BW 7 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_UNRES_BW 8 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_ADMIN_GROUP 9 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_LINK_LOCAL_REMOTE_ID 11 /* rfc4203 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_LINK_PROTECTION_TYPE 14 /* rfc4203 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_INTF_SW_CAP_DESCR 15 /* rfc4203 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_SHARED_RISK_GROUP 16 /* rfc4203 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_BW_CONSTRAINTS 17 /* rfc4124 */
+
+#define LS_OPAQUE_TE_LINK_SUBTLV_LINK_TYPE_PTP 1 /* rfc3630 */
+#define LS_OPAQUE_TE_LINK_SUBTLV_LINK_TYPE_MA 2 /* rfc3630 */
+
+#define LS_OPAQUE_GRACE_TLV_PERIOD 1 /* rfc3623 */
+#define LS_OPAQUE_GRACE_TLV_REASON 2 /* rfc3623 */
+#define LS_OPAQUE_GRACE_TLV_INT_ADDRESS 3 /* rfc3623 */
+
+#define LS_OPAQUE_GRACE_TLV_REASON_UNKNOWN 0 /* rfc3623 */
+#define LS_OPAQUE_GRACE_TLV_REASON_SW_RESTART 1 /* rfc3623 */
+#define LS_OPAQUE_GRACE_TLV_REASON_SW_UPGRADE 2 /* rfc3623 */
+#define LS_OPAQUE_GRACE_TLV_REASON_CP_SWITCH 3 /* rfc3623 */
+
+#define LS_OPAQUE_RI_TLV_CAP 1 /* draft-ietf-ospf-cap-03 */
+
+
+/* rla_link.link_type */
+#define RLA_TYPE_ROUTER 1 /* point-to-point to another router */
+#define RLA_TYPE_TRANSIT 2 /* connection to transit network */
+#define RLA_TYPE_STUB 3 /* connection to stub network */
+#define RLA_TYPE_VIRTUAL 4 /* virtual link */
+
+/* rla_flags */
+#define RLA_FLAG_B 0x01
+#define RLA_FLAG_E 0x02
+#define RLA_FLAG_W1 0x04
+#define RLA_FLAG_W2 0x08
+
+/* sla_tosmetric breakdown */
+#define SLA_MASK_TOS 0x7f000000
+#define SLA_MASK_METRIC 0x00ffffff
+#define SLA_SHIFT_TOS 24
+
+/* asla_tosmetric breakdown */
+#define ASLA_FLAG_EXTERNAL 0x80000000
+#define ASLA_MASK_TOS 0x7f000000
+#define ASLA_SHIFT_TOS 24
+#define ASLA_MASK_METRIC 0x00ffffff
+
+/* multicast vertex type */
+#define MCLA_VERTEX_ROUTER 1
+#define MCLA_VERTEX_NETWORK 2
+
+/* Link-Local-Signaling */
+#define OSPF_LLS_HDRLEN 4U /* RFC5613 Section 2.2 */
+
+#define OSPF_LLS_EO 1 /* RFC4811, RFC4812 */
+#define OSPF_LLS_MD5 2 /* RFC4813 */
+
+#define OSPF_LLS_EO_LR 0x00000001 /* RFC4811 */
+#define OSPF_LLS_EO_RS 0x00000002 /* RFC4812 */
+
+/*
+ * TOS metric struct (will be 0 or more in router links update)
+ */
+struct tos_metric {
+ nd_uint8_t tos_type;
+ nd_uint8_t reserved;
+ nd_uint16_t tos_metric;
+};
+struct tos_link {
+ nd_uint8_t link_type;
+ nd_uint8_t link_tos_count;
+ nd_uint16_t tos_metric;
+};
+union un_tos {
+ struct tos_link link;
+ struct tos_metric metrics;
+};
+
+/* link state advertisement header */
+struct lsa_hdr {
+ nd_uint16_t ls_age;
+ nd_uint8_t ls_options;
+ nd_uint8_t ls_type;
+ union {
+ nd_ipv4 lsa_id;
+ struct { /* opaque LSAs change the LSA-ID field */
+ nd_uint8_t opaque_type;
+ nd_uint24_t opaque_id;
+ } opaque_field;
+ } un_lsa_id;
+ nd_ipv4 ls_router;
+ nd_uint32_t ls_seq;
+ nd_uint16_t ls_chksum;
+ nd_uint16_t ls_length;
+};
+
+/* link state advertisement */
+struct lsa {
+ struct lsa_hdr ls_hdr;
+
+ /* Link state types */
+ union {
+ /* Router links advertisements */
+ struct {
+ nd_uint8_t rla_flags;
+ nd_byte rla_zero;
+ nd_uint16_t rla_count;
+ struct rlalink {
+ nd_ipv4 link_id;
+ nd_ipv4 link_data;
+ union un_tos un_tos;
+ } rla_link[1]; /* may repeat */
+ } un_rla;
+
+ /* Network links advertisements */
+ struct {
+ nd_ipv4 nla_mask;
+ nd_ipv4 nla_router[1]; /* may repeat */
+ } un_nla;
+
+ /* Summary links advertisements */
+ struct {
+ nd_ipv4 sla_mask;
+ nd_uint32_t sla_tosmetric[1]; /* may repeat */
+ } un_sla;
+
+ /* AS external links advertisements */
+ struct {
+ nd_ipv4 asla_mask;
+ struct aslametric {
+ nd_uint32_t asla_tosmetric;
+ nd_ipv4 asla_forward;
+ nd_ipv4 asla_tag;
+ } asla_metric[1]; /* may repeat */
+ } un_asla;
+
+ /* Multicast group membership */
+ struct mcla {
+ nd_uint32_t mcla_vtype;
+ nd_ipv4 mcla_vid;
+ } un_mcla[1];
+
+ /* Opaque TE LSA */
+ struct {
+ nd_uint16_t type;
+ nd_uint16_t length;
+ nd_byte data[1]; /* may repeat */
+ } un_te_lsa_tlv[1]; /* may repeat */
+
+ /* Opaque Grace LSA */
+ struct {
+ nd_uint16_t type;
+ nd_uint16_t length;
+ nd_byte data[1]; /* may repeat */
+ } un_grace_tlv[1]; /* may repeat */
+
+ /* Opaque Router information LSA */
+ struct {
+ nd_uint16_t type;
+ nd_uint16_t length;
+ nd_byte data[1]; /* may repeat */
+ } un_ri_tlv[1]; /* may repeat */
+
+ /* Unknown LSA */
+ struct unknown {
+ nd_byte data[1]; /* may repeat */
+ } un_unknown[1];
+
+ } lsa_un;
+};
+
+#define OSPF_AUTH_SIZE 8
+
+/*
+ * the main header
+ */
+struct ospfhdr {
+ nd_uint8_t ospf_version;
+ nd_uint8_t ospf_type;
+ nd_uint16_t ospf_len;
+ nd_ipv4 ospf_routerid;
+ nd_ipv4 ospf_areaid;
+ nd_uint16_t ospf_chksum;
+ nd_uint16_t ospf_authtype;
+ nd_byte ospf_authdata[OSPF_AUTH_SIZE];
+ union {
+
+ /* Hello packet */
+ struct {
+ nd_ipv4 hello_mask;
+ nd_uint16_t hello_helloint;
+ nd_uint8_t hello_options;
+ nd_uint8_t hello_priority;
+ nd_uint32_t hello_deadint;
+ nd_ipv4 hello_dr;
+ nd_ipv4 hello_bdr;
+ nd_ipv4 hello_neighbor[1]; /* may repeat */
+ } un_hello;
+
+ /* Database Description packet */
+ struct {
+ nd_uint16_t db_ifmtu;
+ nd_uint8_t db_options;
+ nd_uint8_t db_flags;
+ nd_uint32_t db_seq;
+ struct lsa_hdr db_lshdr[1]; /* may repeat */
+ } un_db;
+
+ /* Link State Request */
+ struct lsr {
+ nd_uint32_t ls_type;
+ union {
+ nd_ipv4 ls_stateid;
+ struct { /* opaque LSAs change the LSA-ID field */
+ nd_uint8_t opaque_type;
+ nd_uint24_t opaque_id;
+ } opaque_field;
+ } un_ls_stateid;
+ nd_ipv4 ls_router;
+ } un_lsr[1]; /* may repeat */
+
+ /* Link State Update */
+ struct {
+ nd_uint32_t lsu_count;
+ struct lsa lsu_lsa[1]; /* may repeat */
+ } un_lsu;
+
+ /* Link State Acknowledgement */
+ struct {
+ struct lsa_hdr lsa_lshdr[1]; /* may repeat */
+ } un_lsa ;
+ } ospf_un ;
+};
+
+#define ospf_hello ospf_un.un_hello
+#define ospf_db ospf_un.un_db
+#define ospf_lsr ospf_un.un_lsr
+#define ospf_lsu ospf_un.un_lsu
+#define ospf_lsa ospf_un.un_lsa
diff --git a/oui.c b/oui.c
new file mode 100644
index 0000000..25d08e2
--- /dev/null
+++ b/oui.c
@@ -0,0 +1,131 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include "oui.h"
+
+/* FIXME complete OUI list using a script */
+
+const struct tok oui_values[] = {
+ { OUI_ENCAP_ETHER, "Ethernet" },
+ { OUI_CISCO, "Cisco" },
+ { OUI_IANA, "IANA" },
+ { OUI_NORTEL, "Nortel Networks SONMP" },
+ { OUI_CISCO_90, "Cisco bridged" },
+ { OUI_RFC2684, "Ethernet bridged" },
+ { OUI_ATM_FORUM, "ATM Forum" },
+ { OUI_CABLE_BPDU, "DOCSIS Spanning Tree" },
+ { OUI_APPLETALK, "Appletalk" },
+ { OUI_JUNIPER, "Juniper" },
+ { OUI_HP, "Hewlett-Packard" },
+ { OUI_IEEE_8021_PRIVATE, "IEEE 802.1 Private"},
+ { OUI_IEEE_8023_PRIVATE, "IEEE 802.3 Private"},
+ { OUI_TIA, "ANSI/TIA"},
+ { OUI_DCBX, "DCBX"},
+ { OUI_NICIRA, "Nicira Networks" },
+ { OUI_BSN, "Big Switch Networks" },
+ { OUI_VELLO, "Vello Systems" },
+ { OUI_HP2, "HP" },
+ { OUI_HPLABS, "HP-Labs" },
+ { OUI_INFOBLOX, "Infoblox Inc" },
+ { OUI_ONLAB, "Open Networking Lab" },
+ { OUI_FREESCALE, "Freescale" },
+ { OUI_NETRONOME, "Netronome" },
+ { OUI_BROADCOM, "Broadcom" },
+ { OUI_PMC_SIERRA, "PMC-Sierra" },
+ { OUI_ERICSSON, "Ericsson" },
+ { 0, NULL }
+};
+
+/*
+ * SMI Network Management Private Enterprise Codes for organizations.
+ *
+ * XXX - these also appear in FreeRadius dictionary files, with items such
+ * as
+ *
+ * VENDOR Cisco 9
+ *
+ * List taken from Ethereal's epan/sminmpec.c.
+ */
+const struct tok smi_values[] = {
+ { SMI_IETF, "IETF (reserved)"},
+ { SMI_ACC, "ACC"},
+ { SMI_CISCO, "Cisco"},
+ { SMI_HEWLETT_PACKARD, "Hewlett Packard"},
+ { SMI_SUN_MICROSYSTEMS, "Sun Microsystems"},
+ { SMI_MERIT, "Merit"},
+ { SMI_AT_AND_T, "AT&T"},
+ { SMI_MOTOROLA, "Motorola"},
+ { SMI_SHIVA, "Shiva"},
+ { SMI_ERICSSON, "Ericsson AB"},
+ { SMI_CISCO_VPN5000, "Cisco VPN 5000"},
+ { SMI_LIVINGSTON, "Livingston"},
+ { SMI_MICROSOFT, "Microsoft"},
+ { SMI_3COM, "3Com"},
+ { SMI_ASCEND, "Ascend"},
+ { SMI_BAY, "Bay Networks"},
+ { SMI_FOUNDRY, "Foundry"},
+ { SMI_VERSANET, "Versanet"},
+ { SMI_REDBACK, "Redback"},
+ { SMI_JUNIPER, "Juniper Networks"},
+ { SMI_APTIS, "Aptis"},
+ { SMI_DT_AG, "Deutsche Telekom AG"},
+ { SMI_IXIA, "Ixia Communications"},
+ { SMI_CISCO_VPN3000, "Cisco VPN 3000"},
+ { SMI_COSINE, "CoSine Communications"},
+ { SMI_NETSCREEN, "Netscreen"},
+ { SMI_SHASTA, "Shasta"},
+ { SMI_NOMADIX, "Nomadix"},
+ { SMI_T_MOBILE, "T-Mobile"},
+ { SMI_BROADBAND_FORUM, "The Broadband Forum"},
+ { SMI_ZTE, "ZTE"},
+ { SMI_SIEMENS, "Siemens"},
+ { SMI_CABLELABS, "CableLabs"},
+ { SMI_UNISPHERE, "Unisphere Networks"},
+ { SMI_CISCO_BBSM, "Cisco BBSM"},
+ { SMI_THE3GPP2, "3rd Generation Partnership Project 2 (3GPP2)"},
+ { SMI_SKT_TELECOM, "SK Telecom"},
+ { SMI_IP_UNPLUGGED, "ipUnplugged"},
+ { SMI_ISSANNI, "Issanni Communications"},
+ { SMI_NETSCALER, "Netscaler"},
+ { SMI_DE_TE_MOBIL, "T-Mobile"},
+ { SMI_QUINTUM, "Quintum"},
+ { SMI_INTERLINK, "Interlink"},
+ { SMI_CNCTC, "CNCTC"},
+ { SMI_STARENT_NETWORKS, "Starent Networks"},
+ { SMI_COLUBRIS, "Colubris"},
+ { SMI_THE3GPP, "3GPP"},
+ { SMI_GEMTEK_SYSTEMS, "Gemtek-Systems"},
+ { SMI_BARRACUDA, "Barracuda Networks"},
+ { SMI_ERICSSON_PKT_CORE, "Ericsson AB - Packet Core Networks"},
+ { SMI_DACOM, "dacom"},
+ { SMI_COLUMBIA_UNIVERSITY, "Columbia University"},
+ { SMI_FORTINET, "Fortinet"},
+ { SMI_VERIZON, "Verizon Wireless"},
+ { SMI_PLIXER, "Plixer"},
+ { SMI_WIFI_ALLIANCE, "Wi-Fi Alliance"},
+ { SMI_T_SYSTEMS_NOVA, "T-Systems Nova"},
+ { SMI_CHINATELECOM_GUANZHOU, "China Telecom - Guangzhou Research Institute"},
+ { SMI_GIGAMON, "Gigamon Systems"},
+ { SMI_CACE, "CACE Technologies"},
+ { SMI_NTOP, "ntop"},
+ { SMI_ERICSSON_CANADA_INC, "Ericsson Canada"},
+ { 0, NULL}
+};
diff --git a/oui.h b/oui.h
new file mode 100644
index 0000000..3c82475
--- /dev/null
+++ b/oui.h
@@ -0,0 +1,119 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+extern const struct tok oui_values[];
+extern const struct tok smi_values[];
+
+#define OUI_ENCAP_ETHER 0x000000 /* encapsulated Ethernet */
+#define OUI_CISCO 0x00000c /* Cisco protocols */
+#define OUI_IANA 0x00005E /* IANA */
+#define OUI_NORTEL 0x000081 /* Nortel SONMP */
+#define OUI_CISCO_90 0x0000f8 /* Cisco bridging */
+#define OUI_RFC2684 0x0080c2 /* RFC 2427/2684 bridged Ethernet */
+#define OUI_ATM_FORUM 0x00A03E /* ATM Forum */
+#define OUI_CABLE_BPDU 0x00E02F /* DOCSIS spanning tree BPDU */
+#define OUI_APPLETALK 0x080007 /* Appletalk */
+#define OUI_JUNIPER 0x009069 /* Juniper */
+#define OUI_HP 0x080009 /* Hewlett-Packard */
+#define OUI_IEEE_8021_PRIVATE 0x0080c2 /* IEEE 802.1 Organisation Specific - Annex F */
+#define OUI_IEEE_8023_PRIVATE 0x00120f /* IEEE 802.3 Organisation Specific - Annex G */
+#define OUI_TIA 0x0012bb /* TIA - Telecommunications Industry Association - ANSI/TIA-1057- 2006 */
+#define OUI_DCBX 0x001B21 /* DCBX */
+#define OUI_NICIRA 0x002320 /* Nicira Networks */
+#define OUI_BSN 0x5c16c7 /* Big Switch Networks */
+#define OUI_VELLO 0xb0d2f5 /* Vello Systems */
+#define OUI_HP2 0x002481 /* HP too */
+#define OUI_HPLABS 0x0004ea /* HP-Labs */
+#define OUI_INFOBLOX 0x748771 /* Infoblox Inc */
+#define OUI_ONLAB 0xa42305 /* Open Networking Lab */
+#define OUI_FREESCALE 0x00049f /* Freescale */
+#define OUI_NETRONOME 0x0015ad /* Netronome */
+#define OUI_BROADCOM 0x001018 /* Broadcom */
+#define OUI_PMC_SIERRA 0x00e004 /* PMC-Sierra */
+#define OUI_ERICSSON 0xd0f0db /* Ericsson */
+
+/*
+ * These are SMI Network Management Private Enterprise Codes for
+ * organizations; see
+ *
+ * https://www.iana.org/assignments/enterprise-numbers
+ *
+ * for a list.
+ *
+ * List taken from Ethereal's epan/sminmpec.h.
+ */
+#define SMI_IETF 0 /* reserved - used by the IETF in L2TP? */
+#define SMI_ACC 5
+#define SMI_CISCO 9
+#define SMI_HEWLETT_PACKARD 11
+#define SMI_SUN_MICROSYSTEMS 42
+#define SMI_MERIT 61
+#define SMI_AT_AND_T 74
+#define SMI_MOTOROLA 161
+#define SMI_SHIVA 166
+#define SMI_ERICSSON 193
+#define SMI_CISCO_VPN5000 255
+#define SMI_LIVINGSTON 307
+#define SMI_MICROSOFT 311
+#define SMI_3COM 429
+#define SMI_ASCEND 529
+#define SMI_BAY 1584
+#define SMI_FOUNDRY 1991
+#define SMI_VERSANET 2180
+#define SMI_REDBACK 2352
+#define SMI_JUNIPER 2636
+#define SMI_APTIS 2637
+#define SMI_DT_AG 2937
+#define SMI_IXIA 3054
+#define SMI_CISCO_VPN3000 3076
+#define SMI_COSINE 3085
+#define SMI_SHASTA 3199
+#define SMI_NETSCREEN 3224
+#define SMI_NOMADIX 3309
+#define SMI_T_MOBILE 3414
+#define SMI_BROADBAND_FORUM 3561
+#define SMI_ZTE 3902
+#define SMI_SIEMENS 4329
+#define SMI_CABLELABS 4491
+#define SMI_UNISPHERE 4874
+#define SMI_CISCO_BBSM 5263
+#define SMI_THE3GPP2 5535
+#define SMI_SKT_TELECOM 5806
+#define SMI_IP_UNPLUGGED 5925
+#define SMI_ISSANNI 5948
+#define SMI_NETSCALER 5951
+#define SMI_DE_TE_MOBIL 6490
+#define SMI_QUINTUM 6618
+#define SMI_INTERLINK 6728
+#define SMI_CNCTC 7951
+#define SMI_STARENT_NETWORKS 8164
+#define SMI_COLUBRIS 8744
+#define SMI_THE3GPP 10415
+#define SMI_GEMTEK_SYSTEMS 10529
+#define SMI_BARRACUDA 10704
+#define SMI_ERICSSON_PKT_CORE 10923
+#define SMI_DACOM 11665
+#define SMI_COLUMBIA_UNIVERSITY 11862
+#define SMI_FORTINET 12356
+#define SMI_VERIZON 12951
+#define SMI_PLIXER 13745
+#define SMI_WIFI_ALLIANCE 14122
+#define SMI_T_SYSTEMS_NOVA 16787
+#define SMI_CHINATELECOM_GUANZHOU 20942
+#define SMI_GIGAMON 26866
+#define SMI_CACE 32622
+/* Greater than 32,767 need to be tagged unsigned. */
+#define SMI_NTOP 35632u
+#define SMI_ERICSSON_CANADA_INC 46098u
diff --git a/parsenfsfh.c b/parsenfsfh.c
new file mode 100644
index 0000000..cd94369
--- /dev/null
+++ b/parsenfsfh.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 1993, 1994 Jeffrey C. Mogul, Digital Equipment Corporation,
+ * Western Research Laboratory. All rights reserved.
+ * Copyright (c) 2001 Compaq Computer Corporation. All rights reserved.
+ *
+ * Permission to use, copy, and modify this software and its
+ * documentation is hereby granted only under the following terms and
+ * conditions. Both the above copyright notice and this permission
+ * notice must appear in all copies of the software, derivative works
+ * or modified versions, and any portions thereof, and both notices
+ * must appear in supporting documentation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND COMPAQ COMPUTER CORPORATION
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
+ * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO
+ * EVENT SHALL COMPAQ COMPUTER CORPORATION BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * parsenfsfh.c - portable parser for NFS file handles
+ * uses all sorts of heuristics
+ *
+ * Jeffrey C. Mogul
+ * Digital Equipment Corporation
+ * Western Research Laboratory
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "nfsfh.h"
+
+/*
+ * This routine attempts to parse a file handle (in network byte order),
+ * using heuristics to guess what kind of format it is in. See the
+ * file "fhandle_layouts" for a detailed description of the various
+ * patterns we know about.
+ *
+ * The file handle is parsed into our internal representation of a
+ * file-system id, and an internal representation of an inode-number.
+ */
+
+#define FHT_UNKNOWN 0
+#define FHT_AUSPEX 1
+#define FHT_DECOSF 2
+#define FHT_IRIX4 3
+#define FHT_IRIX5 4
+#define FHT_SUNOS3 5
+#define FHT_SUNOS4 6
+#define FHT_ULTRIX 7
+#define FHT_VMSUCX 8
+#define FHT_SUNOS5 9
+#define FHT_AIX32 10
+#define FHT_HPUX9 11
+#define FHT_BSD44 12
+
+static int is_UCX(netdissect_options *, const unsigned char *, u_int);
+
+void
+Parse_fh(netdissect_options *ndo, const unsigned char *fh, u_int len,
+ my_fsid *fsidp, uint32_t *inop,
+ const char **osnamep, /* if non-NULL, return OS name here */
+ const char **fsnamep, /* if non-NULL, return server fs name here (for VMS) */
+ int ourself) /* true if file handle was generated on this host */
+{
+ const unsigned char *fhp = fh;
+ uint32_t temp;
+ int fhtype = FHT_UNKNOWN;
+ u_int i;
+
+ /*
+ * Require at least 16 bytes of file handle; it's variable-length
+ * in NFSv3. "len" is in units of 32-bit words, not bytes.
+ */
+ if (len < 16/4)
+ fhtype = FHT_UNKNOWN;
+ else {
+ if (ourself) {
+ /* File handle generated on this host, no need for guessing */
+#if defined(IRIX40)
+ fhtype = FHT_IRIX4;
+#endif
+#if defined(IRIX50)
+ fhtype = FHT_IRIX5;
+#endif
+#if defined(IRIX51)
+ fhtype = FHT_IRIX5;
+#endif
+#if defined(SUNOS4)
+ fhtype = FHT_SUNOS4;
+#endif
+#if defined(SUNOS5)
+ fhtype = FHT_SUNOS5;
+#endif
+#if defined(ultrix)
+ fhtype = FHT_ULTRIX;
+#endif
+#if defined(__osf__)
+ fhtype = FHT_DECOSF;
+#endif
+#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__DragonFly__) \
+ || defined(__OpenBSD__)
+ fhtype = FHT_BSD44;
+#endif
+ }
+ /*
+ * This is basically a big decision tree
+ */
+ else if ((GET_U_1(fhp) == 0) && (GET_U_1(fhp + 1) == 0)) {
+ /* bytes[0,1] == (0,0); rules out Ultrix, IRIX5, SUNOS5 */
+ /* probably rules out HP-UX, AIX unless they allow major=0 */
+ if ((GET_U_1(fhp + 2) == 0) && (GET_U_1(fhp + 3) == 0)) {
+ /* bytes[2,3] == (0,0); must be Auspex */
+ /* XXX or could be Ultrix+MASSBUS "hp" disk? */
+ fhtype = FHT_AUSPEX;
+ }
+ else {
+ /*
+ * bytes[2,3] != (0,0); rules out Auspex, could be
+ * DECOSF, SUNOS4, or IRIX4
+ */
+ if ((GET_U_1(fhp + 4) != 0) && (GET_U_1(fhp + 5) == 0) &&
+ (GET_U_1(fhp + 8) == 12) && (GET_U_1(fhp + 9) == 0)) {
+ /* seems to be DECOSF, with minor == 0 */
+ fhtype = FHT_DECOSF;
+ }
+ else {
+ /* could be SUNOS4 or IRIX4 */
+ /* XXX the test of fhp[5] == 8 could be wrong */
+ if ((GET_U_1(fhp + 4) == 0) && (GET_U_1(fhp + 5) == 8) && (GET_U_1(fhp + 6) == 0) &&
+ (GET_U_1(fhp + 7) == 0)) {
+ /* looks like a length, not a file system typecode */
+ fhtype = FHT_IRIX4;
+ }
+ else {
+ /* by elimination */
+ fhtype = FHT_SUNOS4;
+ }
+ }
+ }
+ }
+ else {
+ /*
+ * bytes[0,1] != (0,0); rules out Auspex, IRIX4, SUNOS4
+ * could be IRIX5, DECOSF, UCX, Ultrix, SUNOS5
+ * could be AIX, HP-UX
+ */
+ if ((GET_U_1(fhp + 2) == 0) && (GET_U_1(fhp + 3) == 0)) {
+ /*
+ * bytes[2,3] == (0,0); rules out OSF, probably not UCX
+ * (unless the exported device name is just one letter!),
+ * could be Ultrix, IRIX5, AIX, or SUNOS5
+ * might be HP-UX (depends on their values for minor devs)
+ */
+ if ((GET_U_1(fhp + 6) == 0) && (GET_U_1(fhp + 7) == 0)) {
+ fhtype = FHT_BSD44;
+ }
+ /*XXX we probably only need to test of these two bytes */
+ else if ((len >= 24/4) && (GET_U_1(fhp + 21) == 0) && (GET_U_1(fhp + 23) == 0)) {
+ fhtype = FHT_ULTRIX;
+ }
+ else {
+ /* Could be SUNOS5/IRIX5, maybe AIX */
+ /* XXX no obvious difference between SUNOS5 and IRIX5 */
+ if (GET_U_1(fhp + 9) == 10)
+ fhtype = FHT_SUNOS5;
+ /* XXX what about AIX? */
+ }
+ }
+ else {
+ /*
+ * bytes[2,3] != (0,0); rules out Ultrix, could be
+ * DECOSF, SUNOS5, IRIX5, AIX, HP-UX, or UCX
+ */
+ if ((GET_U_1(fhp + 8) == 12) && (GET_U_1(fhp + 9) == 0)) {
+ fhtype = FHT_DECOSF;
+ }
+ else if ((GET_U_1(fhp + 8) == 0) && (GET_U_1(fhp + 9) == 10)) {
+ /* could be SUNOS5/IRIX5, AIX, HP-UX */
+ if ((GET_U_1(fhp + 7) == 0) && (GET_U_1(fhp + 6) == 0) &&
+ (GET_U_1(fhp + 5) == 0) && (GET_U_1(fhp + 4) == 0)) {
+ /* XXX is this always true of HP-UX? */
+ fhtype = FHT_HPUX9;
+ }
+ else if (GET_U_1(fhp + 7) == 2) {
+ /* This would be MNT_NFS on AIX, which is impossible */
+ fhtype = FHT_SUNOS5; /* or maybe IRIX5 */
+ }
+ else {
+ /*
+ * XXX Could be SUNOS5/IRIX5 or AIX. I don't
+ * XXX see any way to disambiguate these, so
+ * XXX I'm going with the more likely guess.
+ * XXX Sorry, Big Blue.
+ */
+ fhtype = FHT_SUNOS5; /* or maybe IRIX5 */
+ }
+ }
+ else {
+ if (is_UCX(ndo, fhp, len)) {
+ fhtype = FHT_VMSUCX;
+ }
+ else {
+ fhtype = FHT_UNKNOWN;
+ }
+ }
+ }
+ }
+ }
+
+ /* XXX still needs to handle SUNOS3 */
+
+ switch (fhtype) {
+ case FHT_AUSPEX:
+ fsidp->Fsid_dev.Minor = GET_U_1(fhp + 7);
+ fsidp->Fsid_dev.Major = GET_U_1(fhp + 6);
+ fsidp->fsid_code = 0;
+
+ *inop = GET_BE_U_4(fhp + 12);
+
+ if (osnamep)
+ *osnamep = "Auspex";
+ break;
+
+ case FHT_BSD44:
+ fsidp->Fsid_dev.Minor = GET_U_1(fhp);
+ fsidp->Fsid_dev.Major = GET_U_1(fhp + 1);
+ fsidp->fsid_code = 0;
+
+ *inop = GET_LE_U_4(fhp + 12);
+
+ if (osnamep)
+ *osnamep = "BSD 4.4";
+ break;
+
+ case FHT_DECOSF:
+ fsidp->fsid_code = GET_LE_U_4(fhp + 4);
+ /* XXX could ignore 3 high-order bytes */
+
+ temp = GET_LE_U_4(fhp);
+ fsidp->Fsid_dev.Minor = temp & 0xFFFFF;
+ fsidp->Fsid_dev.Major = (temp>>20) & 0xFFF;
+
+ *inop = GET_LE_U_4(fhp + 12);
+ if (osnamep)
+ *osnamep = "OSF";
+ break;
+
+ case FHT_IRIX4:
+ fsidp->Fsid_dev.Minor = GET_U_1(fhp + 3);
+ fsidp->Fsid_dev.Major = GET_U_1(fhp + 2);
+ fsidp->fsid_code = 0;
+
+ *inop = GET_BE_U_4(fhp + 8);
+
+ if (osnamep)
+ *osnamep = "IRIX4";
+ break;
+
+ case FHT_IRIX5:
+ fsidp->Fsid_dev.Minor = GET_BE_U_2(fhp + 2);
+ fsidp->Fsid_dev.Major = GET_BE_U_2(fhp);
+ fsidp->fsid_code = GET_BE_U_4(fhp + 4);
+
+ *inop = GET_BE_U_4(fhp + 12);
+
+ if (osnamep)
+ *osnamep = "IRIX5";
+ break;
+
+#ifdef notdef
+ case FHT_SUNOS3:
+ /*
+ * XXX - none of the heuristics above return this.
+ * Are there any SunOS 3.x systems around to care about?
+ */
+ if (osnamep)
+ *osnamep = "SUNOS3";
+ break;
+#endif
+
+ case FHT_SUNOS4:
+ fsidp->Fsid_dev.Minor = GET_U_1(fhp + 3);
+ fsidp->Fsid_dev.Major = GET_U_1(fhp + 2);
+ fsidp->fsid_code = GET_BE_U_4(fhp + 4);
+
+ *inop = GET_BE_U_4(fhp + 12);
+
+ if (osnamep)
+ *osnamep = "SUNOS4";
+ break;
+
+ case FHT_SUNOS5:
+ temp = GET_BE_U_2(fhp);
+ fsidp->Fsid_dev.Major = (temp>>2) & 0x3FFF;
+ temp = GET_BE_U_3(fhp + 1);
+ fsidp->Fsid_dev.Minor = temp & 0x3FFFF;
+ fsidp->fsid_code = GET_BE_U_4(fhp + 4);
+
+ *inop = GET_BE_U_4(fhp + 12);
+
+ if (osnamep)
+ *osnamep = "SUNOS5";
+ break;
+
+ case FHT_ULTRIX:
+ fsidp->fsid_code = 0;
+ fsidp->Fsid_dev.Minor = GET_U_1(fhp);
+ fsidp->Fsid_dev.Major = GET_U_1(fhp + 1);
+
+ temp = GET_LE_U_4(fhp + 4);
+ *inop = temp;
+ if (osnamep)
+ *osnamep = "Ultrix";
+ break;
+
+ case FHT_VMSUCX:
+ /* No numeric file system ID, so hash on the device-name */
+ if (sizeof(*fsidp) >= 14) {
+ if (sizeof(*fsidp) > 14)
+ memset((char *)fsidp, 0, sizeof(*fsidp));
+ /* just use the whole thing */
+ memcpy((char *)fsidp, (const char *)fh, 14);
+ }
+ else {
+ uint32_t tempa[4]; /* at least 16 bytes, maybe more */
+
+ memset((char *)tempa, 0, sizeof(tempa));
+ memcpy((char *)tempa, (const char *)fh, 14); /* ensure alignment */
+ fsidp->Fsid_dev.Minor = tempa[0] + (tempa[1]<<1);
+ fsidp->Fsid_dev.Major = tempa[2] + (tempa[3]<<1);
+ fsidp->fsid_code = 0;
+ }
+
+ /* VMS file ID is: (RVN, FidHi, FidLo) */
+ *inop = (((uint32_t) GET_U_1(fhp + 26)) << 24) |
+ (((uint32_t) GET_U_1(fhp + 27)) << 16) |
+ (GET_LE_U_2(fhp + 22) << 0);
+
+ /* Caller must save (and null-terminate?) this value */
+ if (fsnamep)
+ *fsnamep = (const char *)(fhp + 1);
+
+ if (osnamep)
+ *osnamep = "VMS";
+ break;
+
+ case FHT_AIX32:
+ fsidp->Fsid_dev.Minor = GET_BE_U_2(fhp + 2);
+ fsidp->Fsid_dev.Major = GET_BE_U_2(fhp);
+ fsidp->fsid_code = GET_BE_U_4(fhp + 4);
+
+ *inop = GET_BE_U_4(fhp + 12);
+
+ if (osnamep)
+ *osnamep = "AIX32";
+ break;
+
+ case FHT_HPUX9:
+ fsidp->Fsid_dev.Major = GET_U_1(fhp);
+ temp = GET_BE_U_3(fhp + 1);
+ fsidp->Fsid_dev.Minor = temp;
+ fsidp->fsid_code = GET_BE_U_4(fhp + 4);
+
+ *inop = GET_BE_U_4(fhp + 12);
+
+ if (osnamep)
+ *osnamep = "HPUX9";
+ break;
+
+ case FHT_UNKNOWN:
+#ifdef DEBUG
+ /* XXX debugging */
+ for (i = 0; i < len*4; i++)
+ (void)fprintf(stderr, "%x.", GET_U_1(fhp + i));
+ (void)fprintf(stderr, "\n");
+#endif
+ /* Save the actual handle, so it can be display with -u */
+ for (i = 0; i < len*4 && i*2 < sizeof(fsidp->Opaque_Handle) - 1; i++)
+ (void)snprintf(&(fsidp->Opaque_Handle[i*2]), 3, "%.2X",
+ GET_U_1(fhp + i));
+ fsidp->Opaque_Handle[i*2] = '\0';
+
+ /* XXX for now, give "bogus" values to aid debugging */
+ fsidp->fsid_code = 0;
+ fsidp->Fsid_dev.Minor = 257;
+ fsidp->Fsid_dev.Major = 257;
+ *inop = 1;
+
+ /* display will show this string instead of (257,257) */
+ if (fsnamep)
+ *fsnamep = "Unknown";
+
+ if (osnamep)
+ *osnamep = "Unknown";
+ break;
+
+ }
+}
+
+/*
+ * Is this a VMS UCX file handle?
+ * Check for:
+ * (1) leading code byte [XXX not yet]
+ * (2) followed by string of printing chars & spaces
+ * (3) followed by string of nulls
+ */
+static int
+is_UCX(netdissect_options *ndo, const unsigned char *fhp, u_int len)
+{
+ u_int i;
+ int seen_null = 0;
+
+ /*
+ * Require at least 28 bytes of file handle; it's variable-length
+ * in NFSv3. "len" is in units of 32-bit words, not bytes.
+ */
+ if (len < 28/4)
+ return(0);
+
+ for (i = 1; i < 14; i++) {
+ if (ND_ASCII_ISPRINT(GET_U_1(fhp + i))) {
+ if (seen_null)
+ return(0);
+ else
+ continue;
+ }
+ else if (GET_U_1(fhp + i) == 0) {
+ seen_null = 1;
+ continue;
+ }
+ else
+ return(0);
+ }
+
+ return(1);
+}
diff --git a/pcap-missing.h b/pcap-missing.h
new file mode 100644
index 0000000..92706b1
--- /dev/null
+++ b/pcap-missing.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1988-2002
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef netdissect_pcap_missing_h
+#define netdissect_pcap_missing_h
+
+/*
+ * Declarations of functions that might be missing from libpcap.
+ */
+
+#ifndef HAVE_PCAP_LIST_DATALINKS
+extern int pcap_list_datalinks(pcap_t *, int **);
+#endif
+
+#ifndef HAVE_PCAP_DATALINK_NAME_TO_VAL
+/*
+ * We assume no platform has one but not the other.
+ */
+extern int pcap_datalink_name_to_val(const char *);
+extern const char *pcap_datalink_val_to_name(int);
+#endif
+
+#ifndef HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION
+extern const char *pcap_datalink_val_to_description(int);
+#endif
+
+#ifndef HAVE_PCAP_DUMP_FTELL
+extern long pcap_dump_ftell(pcap_dumper_t *);
+#endif
+
+#endif /* netdissect_pcap_missing_h */
diff --git a/ppp.h b/ppp.h
new file mode 100644
index 0000000..830fad9
--- /dev/null
+++ b/ppp.h
@@ -0,0 +1,68 @@
+/*
+ * Point to Point Protocol (PPP) RFC1331
+ *
+ * Copyright 1989 by Carnegie Mellon.
+ *
+ * Permission to use, copy, modify, and distribute this program for any
+ * purpose and without fee is hereby granted, provided that this copyright
+ * and permission notice appear on all copies and supporting documentation,
+ * the name of Carnegie Mellon not be used in advertising or publicity
+ * pertaining to distribution of the program without specific prior
+ * permission, and notice be given in supporting documentation that copying
+ * and distribution is by permission of Carnegie Mellon and Stanford
+ * University. Carnegie Mellon makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+#define PPP_HDRLEN 4 /* length of PPP header */
+
+#define PPP_ADDRESS 0xff /* The address byte value */
+#define PPP_CONTROL 0x03 /* The control byte value */
+
+#define PPP_PPPD_IN 0x00 /* non-standard for DLT_PPP_PPPD */
+#define PPP_PPPD_OUT 0x01 /* non-standard for DLT_PPP_PPPD */
+
+/* Protocol numbers */
+#define PPP_IP 0x0021 /* Raw IP */
+#define PPP_OSI 0x0023 /* OSI Network Layer */
+#define PPP_NS 0x0025 /* Xerox NS IDP */
+#define PPP_DECNET 0x0027 /* DECnet Phase IV */
+#define PPP_APPLE 0x0029 /* Appletalk */
+#define PPP_IPX 0x002b /* Novell IPX */
+#define PPP_VJC 0x002d /* Van Jacobson Compressed TCP/IP */
+#define PPP_VJNC 0x002f /* Van Jacobson Uncompressed TCP/IP */
+#define PPP_BRPDU 0x0031 /* Bridging PDU */
+#define PPP_STII 0x0033 /* Stream Protocol (ST-II) */
+#define PPP_VINES 0x0035 /* Banyan Vines */
+#define PPP_ML 0x003d /* Multi-Link PPP */
+#define PPP_IPV6 0x0057 /* IPv6 */
+#define PPP_COMP 0x00fd /* Compressed Datagram */
+
+#define PPP_HELLO 0x0201 /* 802.1d Hello Packets */
+#define PPP_LUXCOM 0x0231 /* Luxcom */
+#define PPP_SNS 0x0233 /* Sigma Network Systems */
+#define PPP_MPLS_UCAST 0x0281 /* rfc 3032 */
+#define PPP_MPLS_MCAST 0x0283 /* rfc 3022 */
+
+#define PPP_IPCP 0x8021 /* IP Control Protocol */
+#define PPP_OSICP 0x8023 /* OSI Network Layer Control Protocol */
+#define PPP_NSCP 0x8025 /* Xerox NS IDP Control Protocol */
+#define PPP_DECNETCP 0x8027 /* DECnet Control Protocol */
+#define PPP_APPLECP 0x8029 /* Appletalk Control Protocol */
+#define PPP_IPXCP 0x802b /* Novell IPX Control Protocol */
+#define PPP_STIICP 0x8033 /* Strean Protocol Control Protocol */
+#define PPP_VINESCP 0x8035 /* Banyan Vines Control Protocol */
+#define PPP_IPV6CP 0x8057 /* IPv6 Control Protocol */
+#define PPP_CCP 0x80fd /* Compress Control Protocol */
+#define PPP_MPLSCP 0x8281 /* rfc 3022 */
+
+#define PPP_LCP 0xc021 /* Link Control Protocol */
+#define PPP_PAP 0xc023 /* Password Authentication Protocol */
+#define PPP_LQM 0xc025 /* Link Quality Monitoring */
+#define PPP_SPAP 0xc027
+#define PPP_CHAP 0xc223 /* Challenge Handshake Authentication Protocol */
+#define PPP_BACP 0xc02b /* Bandwidth Allocation Control Protocol */
+#define PPP_BAP 0xc02d /* BAP */
+#define PPP_MPCP 0xc03d /* Multi-Link */
+#define PPP_SPAP_OLD 0xc123
+#define PPP_EAP 0xc227
diff --git a/print-802_11.c b/print-802_11.c
new file mode 100644
index 0000000..e901752
--- /dev/null
+++ b/print-802_11.c
@@ -0,0 +1,3519 @@
+/*
+ * Copyright (c) 2001
+ * Fortress Technologies, Inc. All rights reserved.
+ * Charlie Lenahan (clenahan@fortresstech.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IEEE 802.11 printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+
+#include "extract.h"
+
+#include "cpack.h"
+
+
+/* Lengths of 802.11 header components. */
+#define IEEE802_11_FC_LEN 2
+#define IEEE802_11_DUR_LEN 2
+#define IEEE802_11_DA_LEN 6
+#define IEEE802_11_SA_LEN 6
+#define IEEE802_11_BSSID_LEN 6
+#define IEEE802_11_RA_LEN 6
+#define IEEE802_11_TA_LEN 6
+#define IEEE802_11_ADDR1_LEN 6
+#define IEEE802_11_SEQ_LEN 2
+#define IEEE802_11_CTL_LEN 2
+#define IEEE802_11_CARRIED_FC_LEN 2
+#define IEEE802_11_HT_CONTROL_LEN 4
+#define IEEE802_11_IV_LEN 3
+#define IEEE802_11_KID_LEN 1
+
+/* Frame check sequence length. */
+#define IEEE802_11_FCS_LEN 4
+
+/* Lengths of beacon components. */
+#define IEEE802_11_TSTAMP_LEN 8
+#define IEEE802_11_BCNINT_LEN 2
+#define IEEE802_11_CAPINFO_LEN 2
+#define IEEE802_11_LISTENINT_LEN 2
+
+#define IEEE802_11_AID_LEN 2
+#define IEEE802_11_STATUS_LEN 2
+#define IEEE802_11_REASON_LEN 2
+
+/* Length of previous AP in reassocation frame */
+#define IEEE802_11_AP_LEN 6
+
+#define T_MGMT 0x0 /* management */
+#define T_CTRL 0x1 /* control */
+#define T_DATA 0x2 /* data */
+#define T_RESV 0x3 /* reserved */
+
+#define ST_ASSOC_REQUEST 0x0
+#define ST_ASSOC_RESPONSE 0x1
+#define ST_REASSOC_REQUEST 0x2
+#define ST_REASSOC_RESPONSE 0x3
+#define ST_PROBE_REQUEST 0x4
+#define ST_PROBE_RESPONSE 0x5
+/* RESERVED 0x6 */
+/* RESERVED 0x7 */
+#define ST_BEACON 0x8
+#define ST_ATIM 0x9
+#define ST_DISASSOC 0xA
+#define ST_AUTH 0xB
+#define ST_DEAUTH 0xC
+#define ST_ACTION 0xD
+/* RESERVED 0xE */
+/* RESERVED 0xF */
+
+static const struct tok st_str[] = {
+ { ST_ASSOC_REQUEST, "Assoc Request" },
+ { ST_ASSOC_RESPONSE, "Assoc Response" },
+ { ST_REASSOC_REQUEST, "ReAssoc Request" },
+ { ST_REASSOC_RESPONSE, "ReAssoc Response" },
+ { ST_PROBE_REQUEST, "Probe Request" },
+ { ST_PROBE_RESPONSE, "Probe Response" },
+ { ST_BEACON, "Beacon" },
+ { ST_ATIM, "ATIM" },
+ { ST_DISASSOC, "Disassociation" },
+ { ST_AUTH, "Authentication" },
+ { ST_DEAUTH, "DeAuthentication" },
+ { ST_ACTION, "Action" },
+ { 0, NULL }
+};
+
+#define CTRL_CONTROL_WRAPPER 0x7
+#define CTRL_BAR 0x8
+#define CTRL_BA 0x9
+#define CTRL_PS_POLL 0xA
+#define CTRL_RTS 0xB
+#define CTRL_CTS 0xC
+#define CTRL_ACK 0xD
+#define CTRL_CF_END 0xE
+#define CTRL_END_ACK 0xF
+
+static const struct tok ctrl_str[] = {
+ { CTRL_CONTROL_WRAPPER, "Control Wrapper" },
+ { CTRL_BAR, "BAR" },
+ { CTRL_BA, "BA" },
+ { CTRL_PS_POLL, "Power Save-Poll" },
+ { CTRL_RTS, "Request-To-Send" },
+ { CTRL_CTS, "Clear-To-Send" },
+ { CTRL_ACK, "Acknowledgment" },
+ { CTRL_CF_END, "CF-End" },
+ { CTRL_END_ACK, "CF-End+CF-Ack" },
+ { 0, NULL }
+};
+
+#define DATA_DATA 0x0
+#define DATA_DATA_CF_ACK 0x1
+#define DATA_DATA_CF_POLL 0x2
+#define DATA_DATA_CF_ACK_POLL 0x3
+#define DATA_NODATA 0x4
+#define DATA_NODATA_CF_ACK 0x5
+#define DATA_NODATA_CF_POLL 0x6
+#define DATA_NODATA_CF_ACK_POLL 0x7
+
+#define DATA_QOS_DATA 0x8
+#define DATA_QOS_DATA_CF_ACK 0x9
+#define DATA_QOS_DATA_CF_POLL 0xA
+#define DATA_QOS_DATA_CF_ACK_POLL 0xB
+#define DATA_QOS_NODATA 0xC
+#define DATA_QOS_CF_POLL_NODATA 0xE
+#define DATA_QOS_CF_ACK_POLL_NODATA 0xF
+
+/*
+ * The subtype field of a data frame is, in effect, composed of 4 flag
+ * bits - CF-Ack, CF-Poll, Null (means the frame doesn't actually have
+ * any data), and QoS.
+ */
+#define DATA_FRAME_IS_CF_ACK(x) ((x) & 0x01)
+#define DATA_FRAME_IS_CF_POLL(x) ((x) & 0x02)
+#define DATA_FRAME_IS_NULL(x) ((x) & 0x04)
+#define DATA_FRAME_IS_QOS(x) ((x) & 0x08)
+
+/*
+ * Bits in the frame control field.
+ */
+#define FC_VERSION(fc) ((fc) & 0x3)
+#define FC_TYPE(fc) (((fc) >> 2) & 0x3)
+#define FC_SUBTYPE(fc) (((fc) >> 4) & 0xF)
+#define FC_TO_DS(fc) ((fc) & 0x0100)
+#define FC_FROM_DS(fc) ((fc) & 0x0200)
+#define FC_MORE_FLAG(fc) ((fc) & 0x0400)
+#define FC_RETRY(fc) ((fc) & 0x0800)
+#define FC_POWER_MGMT(fc) ((fc) & 0x1000)
+#define FC_MORE_DATA(fc) ((fc) & 0x2000)
+#define FC_PROTECTED(fc) ((fc) & 0x4000)
+#define FC_ORDER(fc) ((fc) & 0x8000)
+
+struct mgmt_header_t {
+ nd_uint16_t fc;
+ nd_uint16_t duration;
+ nd_mac_addr da;
+ nd_mac_addr sa;
+ nd_mac_addr bssid;
+ nd_uint16_t seq_ctrl;
+};
+
+#define MGMT_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+\
+ IEEE802_11_DA_LEN+IEEE802_11_SA_LEN+\
+ IEEE802_11_BSSID_LEN+IEEE802_11_SEQ_LEN)
+
+#define CAPABILITY_ESS(cap) ((cap) & 0x0001)
+#define CAPABILITY_IBSS(cap) ((cap) & 0x0002)
+#define CAPABILITY_CFP(cap) ((cap) & 0x0004)
+#define CAPABILITY_CFP_REQ(cap) ((cap) & 0x0008)
+#define CAPABILITY_PRIVACY(cap) ((cap) & 0x0010)
+
+struct ssid_t {
+ uint8_t element_id;
+ uint8_t length;
+ u_char ssid[33]; /* 32 + 1 for null */
+};
+
+struct rates_t {
+ uint8_t element_id;
+ uint8_t length;
+ uint8_t rate[16];
+};
+
+struct challenge_t {
+ uint8_t element_id;
+ uint8_t length;
+ uint8_t text[254]; /* 1-253 + 1 for null */
+};
+
+struct fh_t {
+ uint8_t element_id;
+ uint8_t length;
+ uint16_t dwell_time;
+ uint8_t hop_set;
+ uint8_t hop_pattern;
+ uint8_t hop_index;
+};
+
+struct ds_t {
+ uint8_t element_id;
+ uint8_t length;
+ uint8_t channel;
+};
+
+struct cf_t {
+ uint8_t element_id;
+ uint8_t length;
+ uint8_t count;
+ uint8_t period;
+ uint16_t max_duration;
+ uint16_t dur_remaining;
+};
+
+struct tim_t {
+ uint8_t element_id;
+ uint8_t length;
+ uint8_t count;
+ uint8_t period;
+ uint8_t bitmap_control;
+ uint8_t bitmap[251];
+};
+
+#define E_SSID 0
+#define E_RATES 1
+#define E_FH 2
+#define E_DS 3
+#define E_CF 4
+#define E_TIM 5
+#define E_IBSS 6
+/* reserved 7 */
+/* reserved 8 */
+/* reserved 9 */
+/* reserved 10 */
+/* reserved 11 */
+/* reserved 12 */
+/* reserved 13 */
+/* reserved 14 */
+/* reserved 15 */
+/* reserved 16 */
+
+#define E_CHALLENGE 16
+/* reserved 17 */
+/* reserved 18 */
+/* reserved 19 */
+/* reserved 16 */
+/* reserved 16 */
+
+
+struct mgmt_body_t {
+ uint8_t timestamp[IEEE802_11_TSTAMP_LEN];
+ uint16_t beacon_interval;
+ uint16_t listen_interval;
+ uint16_t status_code;
+ uint16_t aid;
+ u_char ap[IEEE802_11_AP_LEN];
+ uint16_t reason_code;
+ uint16_t auth_alg;
+ uint16_t auth_trans_seq_num;
+ int challenge_present;
+ struct challenge_t challenge;
+ uint16_t capability_info;
+ int ssid_present;
+ struct ssid_t ssid;
+ int rates_present;
+ struct rates_t rates;
+ int ds_present;
+ struct ds_t ds;
+ int cf_present;
+ struct cf_t cf;
+ int fh_present;
+ struct fh_t fh;
+ int tim_present;
+ struct tim_t tim;
+};
+
+struct ctrl_control_wrapper_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t duration;
+ nd_mac_addr addr1;
+ nd_uint16_t carried_fc[IEEE802_11_CARRIED_FC_LEN];
+ nd_uint16_t ht_control[IEEE802_11_HT_CONTROL_LEN];
+};
+
+#define CTRL_CONTROL_WRAPPER_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+\
+ IEEE802_11_ADDR1_LEN+\
+ IEEE802_11_CARRIED_FC_LEN+\
+ IEEE802_11_HT_CONTROL_LEN)
+
+struct ctrl_rts_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t duration;
+ nd_mac_addr ra;
+ nd_mac_addr ta;
+};
+
+#define CTRL_RTS_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+\
+ IEEE802_11_RA_LEN+IEEE802_11_TA_LEN)
+
+struct ctrl_cts_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t duration;
+ nd_mac_addr ra;
+};
+
+#define CTRL_CTS_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+IEEE802_11_RA_LEN)
+
+struct ctrl_ack_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t duration;
+ nd_mac_addr ra;
+};
+
+#define CTRL_ACK_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+IEEE802_11_RA_LEN)
+
+struct ctrl_ps_poll_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t aid;
+ nd_mac_addr bssid;
+ nd_mac_addr ta;
+};
+
+#define CTRL_PS_POLL_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_AID_LEN+\
+ IEEE802_11_BSSID_LEN+IEEE802_11_TA_LEN)
+
+struct ctrl_end_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t duration;
+ nd_mac_addr ra;
+ nd_mac_addr bssid;
+};
+
+#define CTRL_END_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+\
+ IEEE802_11_RA_LEN+IEEE802_11_BSSID_LEN)
+
+struct ctrl_end_ack_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t duration;
+ nd_mac_addr ra;
+ nd_mac_addr bssid;
+};
+
+#define CTRL_END_ACK_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+\
+ IEEE802_11_RA_LEN+IEEE802_11_BSSID_LEN)
+
+struct ctrl_ba_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t duration;
+ nd_mac_addr ra;
+};
+
+#define CTRL_BA_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+IEEE802_11_RA_LEN)
+
+struct ctrl_bar_hdr_t {
+ nd_uint16_t fc;
+ nd_uint16_t dur;
+ nd_mac_addr ra;
+ nd_mac_addr ta;
+ nd_uint16_t ctl;
+ nd_uint16_t seq;
+};
+
+#define CTRL_BAR_HDRLEN (IEEE802_11_FC_LEN+IEEE802_11_DUR_LEN+\
+ IEEE802_11_RA_LEN+IEEE802_11_TA_LEN+\
+ IEEE802_11_CTL_LEN+IEEE802_11_SEQ_LEN)
+
+struct meshcntl_t {
+ nd_uint8_t flags;
+ nd_uint8_t ttl;
+ nd_uint32_t seq;
+ nd_mac_addr addr4;
+ nd_mac_addr addr5;
+ nd_mac_addr addr6;
+};
+
+#define IV_IV(iv) ((iv) & 0xFFFFFF)
+#define IV_PAD(iv) (((iv) >> 24) & 0x3F)
+#define IV_KEYID(iv) (((iv) >> 30) & 0x03)
+
+#define PRINT_SSID(p) \
+ if (p.ssid_present) { \
+ ND_PRINT(" ("); \
+ fn_print_str(ndo, p.ssid.ssid); \
+ ND_PRINT(")"); \
+ }
+
+#define PRINT_RATE(_sep, _r, _suf) \
+ ND_PRINT("%s%2.1f%s", _sep, (.5 * ((_r) & 0x7f)), _suf)
+#define PRINT_RATES(p) \
+ if (p.rates_present) { \
+ int z; \
+ const char *sep = " ["; \
+ for (z = 0; z < p.rates.length ; z++) { \
+ PRINT_RATE(sep, p.rates.rate[z], \
+ (p.rates.rate[z] & 0x80 ? "*" : "")); \
+ sep = " "; \
+ } \
+ if (p.rates.length != 0) \
+ ND_PRINT(" Mbit]"); \
+ }
+
+#define PRINT_DS_CHANNEL(p) \
+ if (p.ds_present) \
+ ND_PRINT(" CH: %u", p.ds.channel); \
+ ND_PRINT("%s", \
+ CAPABILITY_PRIVACY(p.capability_info) ? ", PRIVACY" : "");
+
+#define MAX_MCS_INDEX 76
+
+/*
+ * Indices are:
+ *
+ * the MCS index (0-76);
+ *
+ * 0 for 20 MHz, 1 for 40 MHz;
+ *
+ * 0 for a long guard interval, 1 for a short guard interval.
+ */
+static const float ieee80211_float_htrates[MAX_MCS_INDEX+1][2][2] = {
+ /* MCS 0 */
+ { /* 20 Mhz */ { 6.5f, /* SGI */ 7.2f, },
+ /* 40 Mhz */ { 13.5f, /* SGI */ 15.0f, },
+ },
+
+ /* MCS 1 */
+ { /* 20 Mhz */ { 13.0f, /* SGI */ 14.4f, },
+ /* 40 Mhz */ { 27.0f, /* SGI */ 30.0f, },
+ },
+
+ /* MCS 2 */
+ { /* 20 Mhz */ { 19.5f, /* SGI */ 21.7f, },
+ /* 40 Mhz */ { 40.5f, /* SGI */ 45.0f, },
+ },
+
+ /* MCS 3 */
+ { /* 20 Mhz */ { 26.0f, /* SGI */ 28.9f, },
+ /* 40 Mhz */ { 54.0f, /* SGI */ 60.0f, },
+ },
+
+ /* MCS 4 */
+ { /* 20 Mhz */ { 39.0f, /* SGI */ 43.3f, },
+ /* 40 Mhz */ { 81.0f, /* SGI */ 90.0f, },
+ },
+
+ /* MCS 5 */
+ { /* 20 Mhz */ { 52.0f, /* SGI */ 57.8f, },
+ /* 40 Mhz */ { 108.0f, /* SGI */ 120.0f, },
+ },
+
+ /* MCS 6 */
+ { /* 20 Mhz */ { 58.5f, /* SGI */ 65.0f, },
+ /* 40 Mhz */ { 121.5f, /* SGI */ 135.0f, },
+ },
+
+ /* MCS 7 */
+ { /* 20 Mhz */ { 65.0f, /* SGI */ 72.2f, },
+ /* 40 Mhz */ { 135.0f, /* SGI */ 150.0f, },
+ },
+
+ /* MCS 8 */
+ { /* 20 Mhz */ { 13.0f, /* SGI */ 14.4f, },
+ /* 40 Mhz */ { 27.0f, /* SGI */ 30.0f, },
+ },
+
+ /* MCS 9 */
+ { /* 20 Mhz */ { 26.0f, /* SGI */ 28.9f, },
+ /* 40 Mhz */ { 54.0f, /* SGI */ 60.0f, },
+ },
+
+ /* MCS 10 */
+ { /* 20 Mhz */ { 39.0f, /* SGI */ 43.3f, },
+ /* 40 Mhz */ { 81.0f, /* SGI */ 90.0f, },
+ },
+
+ /* MCS 11 */
+ { /* 20 Mhz */ { 52.0f, /* SGI */ 57.8f, },
+ /* 40 Mhz */ { 108.0f, /* SGI */ 120.0f, },
+ },
+
+ /* MCS 12 */
+ { /* 20 Mhz */ { 78.0f, /* SGI */ 86.7f, },
+ /* 40 Mhz */ { 162.0f, /* SGI */ 180.0f, },
+ },
+
+ /* MCS 13 */
+ { /* 20 Mhz */ { 104.0f, /* SGI */ 115.6f, },
+ /* 40 Mhz */ { 216.0f, /* SGI */ 240.0f, },
+ },
+
+ /* MCS 14 */
+ { /* 20 Mhz */ { 117.0f, /* SGI */ 130.0f, },
+ /* 40 Mhz */ { 243.0f, /* SGI */ 270.0f, },
+ },
+
+ /* MCS 15 */
+ { /* 20 Mhz */ { 130.0f, /* SGI */ 144.4f, },
+ /* 40 Mhz */ { 270.0f, /* SGI */ 300.0f, },
+ },
+
+ /* MCS 16 */
+ { /* 20 Mhz */ { 19.5f, /* SGI */ 21.7f, },
+ /* 40 Mhz */ { 40.5f, /* SGI */ 45.0f, },
+ },
+
+ /* MCS 17 */
+ { /* 20 Mhz */ { 39.0f, /* SGI */ 43.3f, },
+ /* 40 Mhz */ { 81.0f, /* SGI */ 90.0f, },
+ },
+
+ /* MCS 18 */
+ { /* 20 Mhz */ { 58.5f, /* SGI */ 65.0f, },
+ /* 40 Mhz */ { 121.5f, /* SGI */ 135.0f, },
+ },
+
+ /* MCS 19 */
+ { /* 20 Mhz */ { 78.0f, /* SGI */ 86.7f, },
+ /* 40 Mhz */ { 162.0f, /* SGI */ 180.0f, },
+ },
+
+ /* MCS 20 */
+ { /* 20 Mhz */ { 117.0f, /* SGI */ 130.0f, },
+ /* 40 Mhz */ { 243.0f, /* SGI */ 270.0f, },
+ },
+
+ /* MCS 21 */
+ { /* 20 Mhz */ { 156.0f, /* SGI */ 173.3f, },
+ /* 40 Mhz */ { 324.0f, /* SGI */ 360.0f, },
+ },
+
+ /* MCS 22 */
+ { /* 20 Mhz */ { 175.5f, /* SGI */ 195.0f, },
+ /* 40 Mhz */ { 364.5f, /* SGI */ 405.0f, },
+ },
+
+ /* MCS 23 */
+ { /* 20 Mhz */ { 195.0f, /* SGI */ 216.7f, },
+ /* 40 Mhz */ { 405.0f, /* SGI */ 450.0f, },
+ },
+
+ /* MCS 24 */
+ { /* 20 Mhz */ { 26.0f, /* SGI */ 28.9f, },
+ /* 40 Mhz */ { 54.0f, /* SGI */ 60.0f, },
+ },
+
+ /* MCS 25 */
+ { /* 20 Mhz */ { 52.0f, /* SGI */ 57.8f, },
+ /* 40 Mhz */ { 108.0f, /* SGI */ 120.0f, },
+ },
+
+ /* MCS 26 */
+ { /* 20 Mhz */ { 78.0f, /* SGI */ 86.7f, },
+ /* 40 Mhz */ { 162.0f, /* SGI */ 180.0f, },
+ },
+
+ /* MCS 27 */
+ { /* 20 Mhz */ { 104.0f, /* SGI */ 115.6f, },
+ /* 40 Mhz */ { 216.0f, /* SGI */ 240.0f, },
+ },
+
+ /* MCS 28 */
+ { /* 20 Mhz */ { 156.0f, /* SGI */ 173.3f, },
+ /* 40 Mhz */ { 324.0f, /* SGI */ 360.0f, },
+ },
+
+ /* MCS 29 */
+ { /* 20 Mhz */ { 208.0f, /* SGI */ 231.1f, },
+ /* 40 Mhz */ { 432.0f, /* SGI */ 480.0f, },
+ },
+
+ /* MCS 30 */
+ { /* 20 Mhz */ { 234.0f, /* SGI */ 260.0f, },
+ /* 40 Mhz */ { 486.0f, /* SGI */ 540.0f, },
+ },
+
+ /* MCS 31 */
+ { /* 20 Mhz */ { 260.0f, /* SGI */ 288.9f, },
+ /* 40 Mhz */ { 540.0f, /* SGI */ 600.0f, },
+ },
+
+ /* MCS 32 */
+ { /* 20 Mhz */ { 0.0f, /* SGI */ 0.0f, }, /* not valid */
+ /* 40 Mhz */ { 6.0f, /* SGI */ 6.7f, },
+ },
+
+ /* MCS 33 */
+ { /* 20 Mhz */ { 39.0f, /* SGI */ 43.3f, },
+ /* 40 Mhz */ { 81.0f, /* SGI */ 90.0f, },
+ },
+
+ /* MCS 34 */
+ { /* 20 Mhz */ { 52.0f, /* SGI */ 57.8f, },
+ /* 40 Mhz */ { 108.0f, /* SGI */ 120.0f, },
+ },
+
+ /* MCS 35 */
+ { /* 20 Mhz */ { 65.0f, /* SGI */ 72.2f, },
+ /* 40 Mhz */ { 135.0f, /* SGI */ 150.0f, },
+ },
+
+ /* MCS 36 */
+ { /* 20 Mhz */ { 58.5f, /* SGI */ 65.0f, },
+ /* 40 Mhz */ { 121.5f, /* SGI */ 135.0f, },
+ },
+
+ /* MCS 37 */
+ { /* 20 Mhz */ { 78.0f, /* SGI */ 86.7f, },
+ /* 40 Mhz */ { 162.0f, /* SGI */ 180.0f, },
+ },
+
+ /* MCS 38 */
+ { /* 20 Mhz */ { 97.5f, /* SGI */ 108.3f, },
+ /* 40 Mhz */ { 202.5f, /* SGI */ 225.0f, },
+ },
+
+ /* MCS 39 */
+ { /* 20 Mhz */ { 52.0f, /* SGI */ 57.8f, },
+ /* 40 Mhz */ { 108.0f, /* SGI */ 120.0f, },
+ },
+
+ /* MCS 40 */
+ { /* 20 Mhz */ { 65.0f, /* SGI */ 72.2f, },
+ /* 40 Mhz */ { 135.0f, /* SGI */ 150.0f, },
+ },
+
+ /* MCS 41 */
+ { /* 20 Mhz */ { 65.0f, /* SGI */ 72.2f, },
+ /* 40 Mhz */ { 135.0f, /* SGI */ 150.0f, },
+ },
+
+ /* MCS 42 */
+ { /* 20 Mhz */ { 78.0f, /* SGI */ 86.7f, },
+ /* 40 Mhz */ { 162.0f, /* SGI */ 180.0f, },
+ },
+
+ /* MCS 43 */
+ { /* 20 Mhz */ { 91.0f, /* SGI */ 101.1f, },
+ /* 40 Mhz */ { 189.0f, /* SGI */ 210.0f, },
+ },
+
+ /* MCS 44 */
+ { /* 20 Mhz */ { 91.0f, /* SGI */ 101.1f, },
+ /* 40 Mhz */ { 189.0f, /* SGI */ 210.0f, },
+ },
+
+ /* MCS 45 */
+ { /* 20 Mhz */ { 104.0f, /* SGI */ 115.6f, },
+ /* 40 Mhz */ { 216.0f, /* SGI */ 240.0f, },
+ },
+
+ /* MCS 46 */
+ { /* 20 Mhz */ { 78.0f, /* SGI */ 86.7f, },
+ /* 40 Mhz */ { 162.0f, /* SGI */ 180.0f, },
+ },
+
+ /* MCS 47 */
+ { /* 20 Mhz */ { 97.5f, /* SGI */ 108.3f, },
+ /* 40 Mhz */ { 202.5f, /* SGI */ 225.0f, },
+ },
+
+ /* MCS 48 */
+ { /* 20 Mhz */ { 97.5f, /* SGI */ 108.3f, },
+ /* 40 Mhz */ { 202.5f, /* SGI */ 225.0f, },
+ },
+
+ /* MCS 49 */
+ { /* 20 Mhz */ { 117.0f, /* SGI */ 130.0f, },
+ /* 40 Mhz */ { 243.0f, /* SGI */ 270.0f, },
+ },
+
+ /* MCS 50 */
+ { /* 20 Mhz */ { 136.5f, /* SGI */ 151.7f, },
+ /* 40 Mhz */ { 283.5f, /* SGI */ 315.0f, },
+ },
+
+ /* MCS 51 */
+ { /* 20 Mhz */ { 136.5f, /* SGI */ 151.7f, },
+ /* 40 Mhz */ { 283.5f, /* SGI */ 315.0f, },
+ },
+
+ /* MCS 52 */
+ { /* 20 Mhz */ { 156.0f, /* SGI */ 173.3f, },
+ /* 40 Mhz */ { 324.0f, /* SGI */ 360.0f, },
+ },
+
+ /* MCS 53 */
+ { /* 20 Mhz */ { 65.0f, /* SGI */ 72.2f, },
+ /* 40 Mhz */ { 135.0f, /* SGI */ 150.0f, },
+ },
+
+ /* MCS 54 */
+ { /* 20 Mhz */ { 78.0f, /* SGI */ 86.7f, },
+ /* 40 Mhz */ { 162.0f, /* SGI */ 180.0f, },
+ },
+
+ /* MCS 55 */
+ { /* 20 Mhz */ { 91.0f, /* SGI */ 101.1f, },
+ /* 40 Mhz */ { 189.0f, /* SGI */ 210.0f, },
+ },
+
+ /* MCS 56 */
+ { /* 20 Mhz */ { 78.0f, /* SGI */ 86.7f, },
+ /* 40 Mhz */ { 162.0f, /* SGI */ 180.0f, },
+ },
+
+ /* MCS 57 */
+ { /* 20 Mhz */ { 91.0f, /* SGI */ 101.1f, },
+ /* 40 Mhz */ { 189.0f, /* SGI */ 210.0f, },
+ },
+
+ /* MCS 58 */
+ { /* 20 Mhz */ { 104.0f, /* SGI */ 115.6f, },
+ /* 40 Mhz */ { 216.0f, /* SGI */ 240.0f, },
+ },
+
+ /* MCS 59 */
+ { /* 20 Mhz */ { 117.0f, /* SGI */ 130.0f, },
+ /* 40 Mhz */ { 243.0f, /* SGI */ 270.0f, },
+ },
+
+ /* MCS 60 */
+ { /* 20 Mhz */ { 104.0f, /* SGI */ 115.6f, },
+ /* 40 Mhz */ { 216.0f, /* SGI */ 240.0f, },
+ },
+
+ /* MCS 61 */
+ { /* 20 Mhz */ { 117.0f, /* SGI */ 130.0f, },
+ /* 40 Mhz */ { 243.0f, /* SGI */ 270.0f, },
+ },
+
+ /* MCS 62 */
+ { /* 20 Mhz */ { 130.0f, /* SGI */ 144.4f, },
+ /* 40 Mhz */ { 270.0f, /* SGI */ 300.0f, },
+ },
+
+ /* MCS 63 */
+ { /* 20 Mhz */ { 130.0f, /* SGI */ 144.4f, },
+ /* 40 Mhz */ { 270.0f, /* SGI */ 300.0f, },
+ },
+
+ /* MCS 64 */
+ { /* 20 Mhz */ { 143.0f, /* SGI */ 158.9f, },
+ /* 40 Mhz */ { 297.0f, /* SGI */ 330.0f, },
+ },
+
+ /* MCS 65 */
+ { /* 20 Mhz */ { 97.5f, /* SGI */ 108.3f, },
+ /* 40 Mhz */ { 202.5f, /* SGI */ 225.0f, },
+ },
+
+ /* MCS 66 */
+ { /* 20 Mhz */ { 117.0f, /* SGI */ 130.0f, },
+ /* 40 Mhz */ { 243.0f, /* SGI */ 270.0f, },
+ },
+
+ /* MCS 67 */
+ { /* 20 Mhz */ { 136.5f, /* SGI */ 151.7f, },
+ /* 40 Mhz */ { 283.5f, /* SGI */ 315.0f, },
+ },
+
+ /* MCS 68 */
+ { /* 20 Mhz */ { 117.0f, /* SGI */ 130.0f, },
+ /* 40 Mhz */ { 243.0f, /* SGI */ 270.0f, },
+ },
+
+ /* MCS 69 */
+ { /* 20 Mhz */ { 136.5f, /* SGI */ 151.7f, },
+ /* 40 Mhz */ { 283.5f, /* SGI */ 315.0f, },
+ },
+
+ /* MCS 70 */
+ { /* 20 Mhz */ { 156.0f, /* SGI */ 173.3f, },
+ /* 40 Mhz */ { 324.0f, /* SGI */ 360.0f, },
+ },
+
+ /* MCS 71 */
+ { /* 20 Mhz */ { 175.5f, /* SGI */ 195.0f, },
+ /* 40 Mhz */ { 364.5f, /* SGI */ 405.0f, },
+ },
+
+ /* MCS 72 */
+ { /* 20 Mhz */ { 156.0f, /* SGI */ 173.3f, },
+ /* 40 Mhz */ { 324.0f, /* SGI */ 360.0f, },
+ },
+
+ /* MCS 73 */
+ { /* 20 Mhz */ { 175.5f, /* SGI */ 195.0f, },
+ /* 40 Mhz */ { 364.5f, /* SGI */ 405.0f, },
+ },
+
+ /* MCS 74 */
+ { /* 20 Mhz */ { 195.0f, /* SGI */ 216.7f, },
+ /* 40 Mhz */ { 405.0f, /* SGI */ 450.0f, },
+ },
+
+ /* MCS 75 */
+ { /* 20 Mhz */ { 195.0f, /* SGI */ 216.7f, },
+ /* 40 Mhz */ { 405.0f, /* SGI */ 450.0f, },
+ },
+
+ /* MCS 76 */
+ { /* 20 Mhz */ { 214.5f, /* SGI */ 238.3f, },
+ /* 40 Mhz */ { 445.5f, /* SGI */ 495.0f, },
+ },
+};
+
+static const char *auth_alg_text[]={"Open System","Shared Key","EAP"};
+#define NUM_AUTH_ALGS (sizeof(auth_alg_text) / sizeof(auth_alg_text[0]))
+
+static const char *status_text[] = {
+ "Successful", /* 0 */
+ "Unspecified failure", /* 1 */
+ "TDLS wakeup schedule rejected but alternative schedule "
+ "provided", /* 2 */
+ "TDLS wakeup schedule rejected",/* 3 */
+ "Reserved", /* 4 */
+ "Security disabled", /* 5 */
+ "Unacceptable lifetime", /* 6 */
+ "Not in same BSS", /* 7 */
+ "Reserved", /* 8 */
+ "Reserved", /* 9 */
+ "Cannot Support all requested capabilities in the Capability "
+ "Information field", /* 10 */
+ "Reassociation denied due to inability to confirm that association "
+ "exists", /* 11 */
+ "Association denied due to reason outside the scope of this "
+ "standard", /* 12 */
+ "Responding STA does not support the specified authentication "
+ "algorithm", /* 13 */
+ "Received an Authentication frame with authentication transaction "
+ "sequence number out of expected sequence", /* 14 */
+ "Authentication rejected because of challenge failure", /* 15 */
+ "Authentication rejected due to timeout waiting for next frame in "
+ "sequence", /* 16 */
+ "Association denied because AP is unable to handle "
+ "additional associated STAs", /* 17 */
+ "Association denied due to requesting STA not supporting "
+ "all of the data rates in the BSSBasicRateSet parameter, "
+ "the Basic HT-MCS Set field of the HT Operation "
+ "parameter, or the Basic VHT-MCS and NSS Set field in "
+ "the VHT Operation parameter", /* 18 */
+ "Association denied due to requesting STA not supporting "
+ "the short preamble option", /* 19 */
+ "Reserved", /* 20 */
+ "Reserved", /* 21 */
+ "Association request rejected because Spectrum Management "
+ "capability is required", /* 22 */
+ "Association request rejected because the information in the "
+ "Power Capability element is unacceptable", /* 23 */
+ "Association request rejected because the information in the "
+ "Supported Channels element is unacceptable", /* 24 */
+ "Association denied due to requesting STA not supporting "
+ "the Short Slot Time option", /* 25 */
+ "Reserved", /* 26 */
+ "Association denied because the requested STA does not support HT "
+ "features", /* 27 */
+ "R0KH unreachable", /* 28 */
+ "Association denied because the requesting STA does not "
+ "support the phased coexistence operation (PCO) "
+ "transition time required by the AP", /* 29 */
+ "Association request rejected temporarily; try again "
+ "later", /* 30 */
+ "Robust management frame policy violation", /* 31 */
+ "Unspecified, QoS-related failure", /* 32 */
+ "Association denied because QoS AP or PCP has "
+ "insufficient bandwidth to handle another QoS "
+ "STA", /* 33 */
+ "Association denied due to excessive frame loss rates and/or "
+ "poor conditions on current operating channel", /* 34 */
+ "Association (with QoS BSS) denied because the requesting STA "
+ "does not support the QoS facility", /* 35 */
+ "Reserved", /* 36 */
+ "The request has been declined", /* 37 */
+ "The request has not been successful as one or more parameters "
+ "have invalid values", /* 38 */
+ "The allocation or TS has not been created because the request "
+ "cannot be honored; however, a suggested TSPEC/DMG TSPEC is "
+ "provided so that the initiating STA can attempt to set "
+ "another allocation or TS with the suggested changes to the "
+ "TSPEC/DMG TSPEC", /* 39 */
+ "Invalid element, i.e., an element defined in this standard "
+ "for which the content does not meet the specifications in "
+ "Clause 9", /* 40 */
+ "Invalid group cipher", /* 41 */
+ "Invalid pairwise cipher", /* 42 */
+ "Invalid AKMP", /* 43 */
+ "Unsupported RSNE version", /* 44 */
+ "Invalid RSNE capabilities", /* 45 */
+ "Cipher suite rejected because of security policy", /* 46 */
+ "The TS or allocation has not been created; however, the "
+ "HC or PCP might be capable of creating a TS or "
+ "allocation, in response to a request, after the time "
+ "indicated in the TS Delay element", /* 47 */
+ "Direct Link is not allowed in the BSS by policy", /* 48 */
+ "The Destination STA is not present within this BSS", /* 49 */
+ "The Destination STA is not a QoS STA", /* 50 */
+
+ "Association denied because the listen interval is "
+ "too large", /* 51 */
+ "Invalid FT Action frame count", /* 52 */
+ "Invalid pairwise master key identifier (PMKID)", /* 53 */
+ "Invalid MDE", /* 54 */
+ "Invalid FTE", /* 55 */
+ "Requested TCLAS processing is not supported by the AP "
+ "or PCP", /* 56 */
+ "The AP or PCP has insufficient TCLAS processing "
+ "resources to satisfy the request", /* 57 */
+ "The TS has not been created because the request "
+ "cannot be honored; however, the HC or PCP suggests "
+ "that the STA transition to a different BSS to set up "
+ "the TS", /* 58 */
+ "GAS Advertisement Protocol not supported", /* 59 */
+ "No outstanding GAS request", /* 60 */
+ "GAS Response not received from the Advertisement "
+ "Server", /* 61 */
+ "STA timed out waiting for GAS Query Response", /* 62 */
+ "LARGE GAS Response is larger than query response "
+ "length limit", /* 63 */
+ "Request refused because home network does not support "
+ "request", /* 64 */
+ "Advertisement Server in the network is not currently "
+ "reachable", /* 65 */
+ "Reserved", /* 66 */
+ "Request refused due to permissions received via SSPN "
+ "interface", /* 67 */
+ "Request refused because the AP or PCP does not "
+ "support unauthenticated access", /* 68 */
+ "Reserved", /* 69 */
+ "Reserved", /* 70 */
+ "Reserved", /* 71 */
+ "Invalid contents of RSNE", /* 72 */
+ "U-APSD coexistence is not supported", /* 73 */
+ "Requested U-APSD coexistence mode is not supported", /* 74 */
+ "Requested Interval/Duration value cannot be "
+ "supported with U-APSD coexistence", /* 75 */
+ "Authentication is rejected because an Anti-Clogging "
+ "Token is required", /* 76 */
+ "Authentication is rejected because the offered "
+ "finite cyclic group is not supported", /* 77 */
+ "The TBTT adjustment request has not been successful "
+ "because the STA could not find an alternative TBTT", /* 78 */
+ "Transmission failure", /* 79 */
+ "Requested TCLAS Not Supported", /* 80 */
+ "TCLAS Resources Exhausted", /* 81 */
+ "Rejected with Suggested BSS transition", /* 82 */
+ "Reject with recommended schedule", /* 83 */
+ "Reject because no wakeup schedule specified", /* 84 */
+ "Success, the destination STA is in power save mode", /* 85 */
+ "FST pending, in process of admitting FST session", /* 86 */
+ "Performing FST now", /* 87 */
+ "FST pending, gap(s) in block ack window", /* 88 */
+ "Reject because of U-PID setting", /* 89 */
+ "Reserved", /* 90 */
+ "Reserved", /* 91 */
+ "(Re)Association refused for some external reason", /* 92 */
+ "(Re)Association refused because of memory limits "
+ "at the AP", /* 93 */
+ "(Re)Association refused because emergency services "
+ "are not supported at the AP", /* 94 */
+ "GAS query response not yet received", /* 95 */
+ "Reject since the request is for transition to a "
+ "frequency band subject to DSE procedures and "
+ "FST Initiator is a dependent STA", /* 96 */
+ "Requested TCLAS processing has been terminated by "
+ "the AP", /* 97 */
+ "The TS schedule conflicts with an existing "
+ "schedule; an alternative schedule is provided", /* 98 */
+ "The association has been denied; however, one or "
+ "more Multi-band elements are included that can "
+ "be used by the receiving STA to join the BSS", /* 99 */
+ "The request failed due to a reservation conflict", /* 100 */
+ "The request failed due to exceeded MAF limit", /* 101 */
+ "The request failed due to exceeded MCCA track "
+ "limit", /* 102 */
+ "Association denied because the information in the"
+ "Spectrum Management field is unacceptable", /* 103 */
+ "Association denied because the requesting STA "
+ "does not support VHT features", /* 104 */
+ "Enablement denied", /* 105 */
+ "Enablement denied due to restriction from an "
+ "authorized GDB", /* 106 */
+ "Authorization deenabled", /* 107 */
+};
+#define NUM_STATUSES (sizeof(status_text) / sizeof(status_text[0]))
+
+static const char *reason_text[] = {
+ "Reserved", /* 0 */
+ "Unspecified reason", /* 1 */
+ "Previous authentication no longer valid", /* 2 */
+ "Deauthenticated because sending STA is leaving (or has left) "
+ "IBSS or ESS", /* 3 */
+ "Disassociated due to inactivity", /* 4 */
+ "Disassociated because AP is unable to handle all currently "
+ " associated STAs", /* 5 */
+ "Class 2 frame received from nonauthenticated STA", /* 6 */
+ "Class 3 frame received from nonassociated STA", /* 7 */
+ "Disassociated because sending STA is leaving "
+ "(or has left) BSS", /* 8 */
+ "STA requesting (re)association is not authenticated with "
+ "responding STA", /* 9 */
+ "Disassociated because the information in the Power Capability "
+ "element is unacceptable", /* 10 */
+ "Disassociated because the information in the Supported Channels "
+ "element is unacceptable", /* 11 */
+ "Disassociated due to BSS transition management", /* 12 */
+ "Invalid element, i.e., an element defined in this standard for "
+ "which the content does not meet the specifications "
+ "in Clause 9", /* 13 */
+ "Message integrity code (MIC) failure", /* 14 */
+ "4-Way Handshake timeout", /* 15 */
+ "Group key handshake timeout", /* 16 */
+ "Information element in 4-Way Handshake different from (Re)Association"
+ "Request/Probe Response/Beacon frame", /* 17 */
+ "Invalid group cipher", /* 18 */
+ "Invalid pairwise cipher", /* 19 */
+ "Invalid AKMP", /* 20 */
+ "Unsupported RSNE version", /* 21 */
+ "Invalid RSNE capabilities", /* 22 */
+ "IEEE 802.1X authentication failed", /* 23 */
+ "Cipher suite rejected because of the security policy", /* 24 */
+ "TDLS direct-link teardown due to TDLS peer STA "
+ "unreachable via the TDLS direct link", /* 25 */
+ "TDLS direct-link teardown for unspecified reason", /* 26 */
+ "Disassociated because session terminated by SSP request",/* 27 */
+ "Disassociated because of lack of SSP roaming agreement",/* 28 */
+ "Requested service rejected because of SSP cipher suite or "
+ "AKM requirement", /* 29 */
+ "Requested service not authorized in this location", /* 30 */
+ "TS deleted because QoS AP lacks sufficient bandwidth for this "
+ "QoS STA due to a change in BSS service characteristics or "
+ "operational mode (e.g. an HT BSS change from 40 MHz channel "
+ "to 20 MHz channel)", /* 31 */
+ "Disassociated for unspecified, QoS-related reason", /* 32 */
+ "Disassociated because QoS AP lacks sufficient bandwidth for this "
+ "QoS STA", /* 33 */
+ "Disassociated because of excessive number of frames that need to be "
+ "acknowledged, but are not acknowledged due to AP transmissions "
+ "and/or poor channel conditions", /* 34 */
+ "Disassociated because STA is transmitting outside the limits "
+ "of its TXOPs", /* 35 */
+ "Requested from peer STA as the STA is leaving the BSS "
+ "(or resetting)", /* 36 */
+ "Requested from peer STA as it does not want to use the "
+ "mechanism", /* 37 */
+ "Requested from peer STA as the STA received frames using the "
+ "mechanism for which a set up is required", /* 38 */
+ "Requested from peer STA due to time out", /* 39 */
+ "Reserved", /* 40 */
+ "Reserved", /* 41 */
+ "Reserved", /* 42 */
+ "Reserved", /* 43 */
+ "Reserved", /* 44 */
+ "Peer STA does not support the requested cipher suite", /* 45 */
+ "In a DLS Teardown frame: The teardown was initiated by the "
+ "DLS peer. In a Disassociation frame: Disassociated because "
+ "authorized access limit reached", /* 46 */
+ "In a DLS Teardown frame: The teardown was initiated by the "
+ "AP. In a Disassociation frame: Disassociated due to external "
+ "service requirements", /* 47 */
+ "Invalid FT Action frame count", /* 48 */
+ "Invalid pairwise master key identifier (PMKID)", /* 49 */
+ "Invalid MDE", /* 50 */
+ "Invalid FTE", /* 51 */
+ "Mesh peering canceled for unknown reasons", /* 52 */
+ "The mesh STA has reached the supported maximum number of "
+ "peer mesh STAs", /* 53 */
+ "The received information violates the Mesh Configuration "
+ "policy configured in the mesh STA profile", /* 54 */
+ "The mesh STA has received a Mesh Peering Close frame "
+ "requesting to close the mesh peering", /* 55 */
+ "The mesh STA has resent dot11MeshMaxRetries Mesh "
+ "Peering Open frames, without receiving a Mesh Peering "
+ "Confirm frame", /* 56 */
+ "The confirmTimer for the mesh peering instance times out", /* 57 */
+ "The mesh STA fails to unwrap the GTK or the values in the "
+ "wrapped contents do not match", /* 58 */
+ "The mesh STA receives inconsistent information about the "
+ "mesh parameters between mesh peering Management frames", /* 59 */
+ "The mesh STA fails the authenticated mesh peering exchange "
+ "because due to failure in selecting either the pairwise "
+ "ciphersuite or group ciphersuite", /* 60 */
+ "The mesh STA does not have proxy information for this "
+ "external destination", /* 61 */
+ "The mesh STA does not have forwarding information for this "
+ "destination", /* 62 */
+ "The mesh STA determines that the link to the next hop of an "
+ "active path in its forwarding information is no longer "
+ "usable", /* 63 */
+ "The Deauthentication frame was sent because the MAC "
+ "address of the STA already exists in the mesh BSS", /* 64 */
+ "The mesh STA performs channel switch to meet regulatory "
+ "requirements", /* 65 */
+ "The mesh STA performs channel switching with unspecified "
+ "reason", /* 66 */
+};
+#define NUM_REASONS (sizeof(reason_text) / sizeof(reason_text[0]))
+
+static int
+wep_print(netdissect_options *ndo,
+ const u_char *p)
+{
+ uint32_t iv;
+
+ ND_TCHECK_LEN(p, IEEE802_11_IV_LEN + IEEE802_11_KID_LEN);
+ iv = GET_LE_U_4(p);
+
+ ND_PRINT(" IV:%3x Pad %x KeyID %x", IV_IV(iv), IV_PAD(iv),
+ IV_KEYID(iv));
+
+ return 1;
+trunc:
+ return 0;
+}
+
+static int
+parse_elements(netdissect_options *ndo,
+ struct mgmt_body_t *pbody, const u_char *p, int offset,
+ u_int length)
+{
+ u_int elementlen;
+ struct ssid_t ssid;
+ struct challenge_t challenge;
+ struct rates_t rates;
+ struct ds_t ds;
+ struct cf_t cf;
+ struct tim_t tim;
+
+ /*
+ * We haven't seen any elements yet.
+ */
+ pbody->challenge_present = 0;
+ pbody->ssid_present = 0;
+ pbody->rates_present = 0;
+ pbody->ds_present = 0;
+ pbody->cf_present = 0;
+ pbody->tim_present = 0;
+
+ while (length != 0) {
+ /* Make sure we at least have the element ID and length. */
+ ND_TCHECK_2(p + offset);
+ if (length < 2)
+ goto trunc;
+ elementlen = GET_U_1(p + offset + 1);
+
+ /* Make sure we have the entire element. */
+ ND_TCHECK_LEN(p + offset + 2, elementlen);
+ if (length < elementlen + 2)
+ goto trunc;
+
+ switch (GET_U_1(p + offset)) {
+ case E_SSID:
+ memcpy(&ssid, p + offset, 2);
+ offset += 2;
+ length -= 2;
+ if (ssid.length != 0) {
+ if (ssid.length > sizeof(ssid.ssid) - 1)
+ return 0;
+ memcpy(&ssid.ssid, p + offset, ssid.length);
+ offset += ssid.length;
+ length -= ssid.length;
+ }
+ ssid.ssid[ssid.length] = '\0';
+ /*
+ * Present and not truncated.
+ *
+ * If we haven't already seen an SSID IE,
+ * copy this one, otherwise ignore this one,
+ * so we later report the first one we saw.
+ */
+ if (!pbody->ssid_present) {
+ pbody->ssid = ssid;
+ pbody->ssid_present = 1;
+ }
+ break;
+ case E_CHALLENGE:
+ memcpy(&challenge, p + offset, 2);
+ offset += 2;
+ length -= 2;
+ if (challenge.length != 0) {
+ if (challenge.length >
+ sizeof(challenge.text) - 1)
+ return 0;
+ memcpy(&challenge.text, p + offset,
+ challenge.length);
+ offset += challenge.length;
+ length -= challenge.length;
+ }
+ challenge.text[challenge.length] = '\0';
+ /*
+ * Present and not truncated.
+ *
+ * If we haven't already seen a challenge IE,
+ * copy this one, otherwise ignore this one,
+ * so we later report the first one we saw.
+ */
+ if (!pbody->challenge_present) {
+ pbody->challenge = challenge;
+ pbody->challenge_present = 1;
+ }
+ break;
+ case E_RATES:
+ memcpy(&rates, p + offset, 2);
+ offset += 2;
+ length -= 2;
+ if (rates.length != 0) {
+ if (rates.length > sizeof(rates.rate))
+ return 0;
+ memcpy(&rates.rate, p + offset, rates.length);
+ offset += rates.length;
+ length -= rates.length;
+ }
+ /*
+ * Present and not truncated.
+ *
+ * If we haven't already seen a rates IE,
+ * copy this one if it's not zero-length,
+ * otherwise ignore this one, so we later
+ * report the first one we saw.
+ *
+ * We ignore zero-length rates IEs as some
+ * devices seem to put a zero-length rates
+ * IE, followed by an SSID IE, followed by
+ * a non-zero-length rates IE into frames,
+ * even though IEEE Std 802.11-2007 doesn't
+ * seem to indicate that a zero-length rates
+ * IE is valid.
+ */
+ if (!pbody->rates_present && rates.length != 0) {
+ pbody->rates = rates;
+ pbody->rates_present = 1;
+ }
+ break;
+ case E_DS:
+ memcpy(&ds, p + offset, 2);
+ offset += 2;
+ length -= 2;
+ if (ds.length != 1) {
+ offset += ds.length;
+ length -= ds.length;
+ break;
+ }
+ ds.channel = GET_U_1(p + offset);
+ offset += 1;
+ length -= 1;
+ /*
+ * Present and not truncated.
+ *
+ * If we haven't already seen a DS IE,
+ * copy this one, otherwise ignore this one,
+ * so we later report the first one we saw.
+ */
+ if (!pbody->ds_present) {
+ pbody->ds = ds;
+ pbody->ds_present = 1;
+ }
+ break;
+ case E_CF:
+ memcpy(&cf, p + offset, 2);
+ offset += 2;
+ length -= 2;
+ if (cf.length != 6) {
+ offset += cf.length;
+ length -= cf.length;
+ break;
+ }
+ memcpy(&cf.count, p + offset, 6);
+ offset += 6;
+ length -= 6;
+ /*
+ * Present and not truncated.
+ *
+ * If we haven't already seen a CF IE,
+ * copy this one, otherwise ignore this one,
+ * so we later report the first one we saw.
+ */
+ if (!pbody->cf_present) {
+ pbody->cf = cf;
+ pbody->cf_present = 1;
+ }
+ break;
+ case E_TIM:
+ memcpy(&tim, p + offset, 2);
+ offset += 2;
+ length -= 2;
+ if (tim.length <= 3U) {
+ offset += tim.length;
+ length -= tim.length;
+ break;
+ }
+ if (tim.length - 3U > sizeof(tim.bitmap))
+ return 0;
+ memcpy(&tim.count, p + offset, 3);
+ offset += 3;
+ length -= 3;
+
+ memcpy(tim.bitmap, p + offset, tim.length - 3);
+ offset += tim.length - 3;
+ length -= tim.length - 3;
+ /*
+ * Present and not truncated.
+ *
+ * If we haven't already seen a TIM IE,
+ * copy this one, otherwise ignore this one,
+ * so we later report the first one we saw.
+ */
+ if (!pbody->tim_present) {
+ pbody->tim = tim;
+ pbody->tim_present = 1;
+ }
+ break;
+ default:
+#if 0
+ ND_PRINT("(1) unhandled element_id (%u) ",
+ GET_U_1(p + offset));
+#endif
+ offset += 2 + elementlen;
+ length -= 2 + elementlen;
+ break;
+ }
+ }
+
+ /* No problems found. */
+ return 1;
+trunc:
+ return 0;
+}
+
+/*********************************************************************************
+ * Print Handle functions for the management frame types
+ *********************************************************************************/
+
+static int
+handle_beacon(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+ int offset = 0;
+ int ret;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ND_TCHECK_LEN(p, IEEE802_11_TSTAMP_LEN + IEEE802_11_BCNINT_LEN +
+ IEEE802_11_CAPINFO_LEN);
+ if (length < IEEE802_11_TSTAMP_LEN + IEEE802_11_BCNINT_LEN +
+ IEEE802_11_CAPINFO_LEN)
+ goto trunc;
+ memcpy(&pbody.timestamp, p, IEEE802_11_TSTAMP_LEN);
+ offset += IEEE802_11_TSTAMP_LEN;
+ length -= IEEE802_11_TSTAMP_LEN;
+ pbody.beacon_interval = GET_LE_U_2(p + offset);
+ offset += IEEE802_11_BCNINT_LEN;
+ length -= IEEE802_11_BCNINT_LEN;
+ pbody.capability_info = GET_LE_U_2(p + offset);
+ offset += IEEE802_11_CAPINFO_LEN;
+ length -= IEEE802_11_CAPINFO_LEN;
+
+ ret = parse_elements(ndo, &pbody, p, offset, length);
+
+ PRINT_SSID(pbody);
+ PRINT_RATES(pbody);
+ ND_PRINT(" %s",
+ CAPABILITY_ESS(pbody.capability_info) ? "ESS" : "IBSS");
+ PRINT_DS_CHANNEL(pbody);
+
+ return ret;
+trunc:
+ return 0;
+}
+
+static int
+handle_assoc_request(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+ int offset = 0;
+ int ret;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ND_TCHECK_LEN(p, IEEE802_11_CAPINFO_LEN + IEEE802_11_LISTENINT_LEN);
+ if (length < IEEE802_11_CAPINFO_LEN + IEEE802_11_LISTENINT_LEN)
+ goto trunc;
+ pbody.capability_info = GET_LE_U_2(p);
+ offset += IEEE802_11_CAPINFO_LEN;
+ length -= IEEE802_11_CAPINFO_LEN;
+ pbody.listen_interval = GET_LE_U_2(p + offset);
+ offset += IEEE802_11_LISTENINT_LEN;
+ length -= IEEE802_11_LISTENINT_LEN;
+
+ ret = parse_elements(ndo, &pbody, p, offset, length);
+
+ PRINT_SSID(pbody);
+ PRINT_RATES(pbody);
+ return ret;
+trunc:
+ return 0;
+}
+
+static int
+handle_assoc_response(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+ int offset = 0;
+ int ret;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ND_TCHECK_LEN(p, IEEE802_11_CAPINFO_LEN + IEEE802_11_STATUS_LEN +
+ IEEE802_11_AID_LEN);
+ if (length < IEEE802_11_CAPINFO_LEN + IEEE802_11_STATUS_LEN +
+ IEEE802_11_AID_LEN)
+ goto trunc;
+ pbody.capability_info = GET_LE_U_2(p);
+ offset += IEEE802_11_CAPINFO_LEN;
+ length -= IEEE802_11_CAPINFO_LEN;
+ pbody.status_code = GET_LE_U_2(p + offset);
+ offset += IEEE802_11_STATUS_LEN;
+ length -= IEEE802_11_STATUS_LEN;
+ pbody.aid = GET_LE_U_2(p + offset);
+ offset += IEEE802_11_AID_LEN;
+ length -= IEEE802_11_AID_LEN;
+
+ ret = parse_elements(ndo, &pbody, p, offset, length);
+
+ ND_PRINT(" AID(%x) :%s: %s", ((uint16_t)(pbody.aid << 2 )) >> 2 ,
+ CAPABILITY_PRIVACY(pbody.capability_info) ? " PRIVACY " : "",
+ (pbody.status_code < NUM_STATUSES
+ ? status_text[pbody.status_code]
+ : "n/a"));
+
+ return ret;
+trunc:
+ return 0;
+}
+
+static int
+handle_reassoc_request(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+ int offset = 0;
+ int ret;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ND_TCHECK_LEN(p, IEEE802_11_CAPINFO_LEN + IEEE802_11_LISTENINT_LEN +
+ IEEE802_11_AP_LEN);
+ if (length < IEEE802_11_CAPINFO_LEN + IEEE802_11_LISTENINT_LEN +
+ IEEE802_11_AP_LEN)
+ goto trunc;
+ pbody.capability_info = GET_LE_U_2(p);
+ offset += IEEE802_11_CAPINFO_LEN;
+ length -= IEEE802_11_CAPINFO_LEN;
+ pbody.listen_interval = GET_LE_U_2(p + offset);
+ offset += IEEE802_11_LISTENINT_LEN;
+ length -= IEEE802_11_LISTENINT_LEN;
+ memcpy(&pbody.ap, p+offset, IEEE802_11_AP_LEN);
+ offset += IEEE802_11_AP_LEN;
+ length -= IEEE802_11_AP_LEN;
+
+ ret = parse_elements(ndo, &pbody, p, offset, length);
+
+ PRINT_SSID(pbody);
+ ND_PRINT(" AP : %s", etheraddr_string(ndo, pbody.ap ));
+
+ return ret;
+trunc:
+ return 0;
+}
+
+static int
+handle_reassoc_response(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ /* Same as a Association Response */
+ return handle_assoc_response(ndo, p, length);
+}
+
+static int
+handle_probe_request(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+ int offset = 0;
+ int ret;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ret = parse_elements(ndo, &pbody, p, offset, length);
+
+ PRINT_SSID(pbody);
+ PRINT_RATES(pbody);
+
+ return ret;
+}
+
+static int
+handle_probe_response(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+ int offset = 0;
+ int ret;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ND_TCHECK_LEN(p, IEEE802_11_TSTAMP_LEN + IEEE802_11_BCNINT_LEN +
+ IEEE802_11_CAPINFO_LEN);
+ if (length < IEEE802_11_TSTAMP_LEN + IEEE802_11_BCNINT_LEN +
+ IEEE802_11_CAPINFO_LEN)
+ goto trunc;
+ memcpy(&pbody.timestamp, p, IEEE802_11_TSTAMP_LEN);
+ offset += IEEE802_11_TSTAMP_LEN;
+ length -= IEEE802_11_TSTAMP_LEN;
+ pbody.beacon_interval = GET_LE_U_2(p + offset);
+ offset += IEEE802_11_BCNINT_LEN;
+ length -= IEEE802_11_BCNINT_LEN;
+ pbody.capability_info = GET_LE_U_2(p + offset);
+ offset += IEEE802_11_CAPINFO_LEN;
+ length -= IEEE802_11_CAPINFO_LEN;
+
+ ret = parse_elements(ndo, &pbody, p, offset, length);
+
+ PRINT_SSID(pbody);
+ PRINT_RATES(pbody);
+ PRINT_DS_CHANNEL(pbody);
+
+ return ret;
+trunc:
+ return 0;
+}
+
+static int
+handle_atim(void)
+{
+ /* the frame body for ATIM is null. */
+ return 1;
+}
+
+static int
+handle_disassoc(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ND_TCHECK_LEN(p, IEEE802_11_REASON_LEN);
+ if (length < IEEE802_11_REASON_LEN)
+ goto trunc;
+ pbody.reason_code = GET_LE_U_2(p);
+
+ ND_PRINT(": %s",
+ (pbody.reason_code < NUM_REASONS)
+ ? reason_text[pbody.reason_code]
+ : "Reserved");
+
+ return 1;
+trunc:
+ return 0;
+}
+
+static int
+handle_auth(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+ int offset = 0;
+ int ret;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ND_TCHECK_6(p);
+ if (length < 6)
+ goto trunc;
+ pbody.auth_alg = GET_LE_U_2(p);
+ offset += 2;
+ length -= 2;
+ pbody.auth_trans_seq_num = GET_LE_U_2(p + offset);
+ offset += 2;
+ length -= 2;
+ pbody.status_code = GET_LE_U_2(p + offset);
+ offset += 2;
+ length -= 2;
+
+ ret = parse_elements(ndo, &pbody, p, offset, length);
+
+ if ((pbody.auth_alg == 1) &&
+ ((pbody.auth_trans_seq_num == 2) ||
+ (pbody.auth_trans_seq_num == 3))) {
+ ND_PRINT(" (%s)-%x [Challenge Text] %s",
+ (pbody.auth_alg < NUM_AUTH_ALGS)
+ ? auth_alg_text[pbody.auth_alg]
+ : "Reserved",
+ pbody.auth_trans_seq_num,
+ ((pbody.auth_trans_seq_num % 2)
+ ? ((pbody.status_code < NUM_STATUSES)
+ ? status_text[pbody.status_code]
+ : "n/a") : ""));
+ return ret;
+ }
+ ND_PRINT(" (%s)-%x: %s",
+ (pbody.auth_alg < NUM_AUTH_ALGS)
+ ? auth_alg_text[pbody.auth_alg]
+ : "Reserved",
+ pbody.auth_trans_seq_num,
+ (pbody.auth_trans_seq_num % 2)
+ ? ((pbody.status_code < NUM_STATUSES)
+ ? status_text[pbody.status_code]
+ : "n/a")
+ : "");
+
+ return ret;
+trunc:
+ return 0;
+}
+
+static int
+handle_deauth(netdissect_options *ndo,
+ const uint8_t *src, const u_char *p, u_int length)
+{
+ struct mgmt_body_t pbody;
+ const char *reason = NULL;
+
+ memset(&pbody, 0, sizeof(pbody));
+
+ ND_TCHECK_LEN(p, IEEE802_11_REASON_LEN);
+ if (length < IEEE802_11_REASON_LEN)
+ goto trunc;
+ pbody.reason_code = GET_LE_U_2(p);
+
+ reason = (pbody.reason_code < NUM_REASONS)
+ ? reason_text[pbody.reason_code]
+ : "Reserved";
+
+ if (ndo->ndo_eflag) {
+ ND_PRINT(": %s", reason);
+ } else {
+ ND_PRINT(" (%s): %s", GET_ETHERADDR_STRING(src), reason);
+ }
+ return 1;
+trunc:
+ return 0;
+}
+
+#define PRINT_HT_ACTION(v) (\
+ (v) == 0 ? ND_PRINT("TxChWidth"): \
+ (v) == 1 ? ND_PRINT("MIMOPwrSave"): \
+ ND_PRINT("Act#%u", (v)))
+#define PRINT_BA_ACTION(v) (\
+ (v) == 0 ? ND_PRINT("ADDBA Request"): \
+ (v) == 1 ? ND_PRINT("ADDBA Response"): \
+ (v) == 2 ? ND_PRINT("DELBA"): \
+ ND_PRINT("Act#%u", (v)))
+#define PRINT_MESHLINK_ACTION(v) (\
+ (v) == 0 ? ND_PRINT("Request"): \
+ (v) == 1 ? ND_PRINT("Report"): \
+ ND_PRINT("Act#%u", (v)))
+#define PRINT_MESHPEERING_ACTION(v) (\
+ (v) == 0 ? ND_PRINT("Open"): \
+ (v) == 1 ? ND_PRINT("Confirm"): \
+ (v) == 2 ? ND_PRINT("Close"): \
+ ND_PRINT("Act#%u", (v)))
+#define PRINT_MESHPATH_ACTION(v) (\
+ (v) == 0 ? ND_PRINT("Request"): \
+ (v) == 1 ? ND_PRINT("Report"): \
+ (v) == 2 ? ND_PRINT("Error"): \
+ (v) == 3 ? ND_PRINT("RootAnnouncement"): \
+ ND_PRINT("Act#%u", (v)))
+
+#define PRINT_MESH_ACTION(v) (\
+ (v) == 0 ? ND_PRINT("MeshLink"): \
+ (v) == 1 ? ND_PRINT("HWMP"): \
+ (v) == 2 ? ND_PRINT("Gate Announcement"): \
+ (v) == 3 ? ND_PRINT("Congestion Control"): \
+ (v) == 4 ? ND_PRINT("MCCA Setup Request"): \
+ (v) == 5 ? ND_PRINT("MCCA Setup Reply"): \
+ (v) == 6 ? ND_PRINT("MCCA Advertisement Request"): \
+ (v) == 7 ? ND_PRINT("MCCA Advertisement"): \
+ (v) == 8 ? ND_PRINT("MCCA Teardown"): \
+ (v) == 9 ? ND_PRINT("TBTT Adjustment Request"): \
+ (v) == 10 ? ND_PRINT("TBTT Adjustment Response"): \
+ ND_PRINT("Act#%u", (v)))
+#define PRINT_MULTIHOP_ACTION(v) (\
+ (v) == 0 ? ND_PRINT("Proxy Update"): \
+ (v) == 1 ? ND_PRINT("Proxy Update Confirmation"): \
+ ND_PRINT("Act#%u", (v)))
+#define PRINT_SELFPROT_ACTION(v) (\
+ (v) == 1 ? ND_PRINT("Peering Open"): \
+ (v) == 2 ? ND_PRINT("Peering Confirm"): \
+ (v) == 3 ? ND_PRINT("Peering Close"): \
+ (v) == 4 ? ND_PRINT("Group Key Inform"): \
+ (v) == 5 ? ND_PRINT("Group Key Acknowledge"): \
+ ND_PRINT("Act#%u", (v)))
+
+static int
+handle_action(netdissect_options *ndo,
+ const uint8_t *src, const u_char *p, u_int length)
+{
+ ND_TCHECK_2(p);
+ if (length < 2)
+ goto trunc;
+ if (ndo->ndo_eflag) {
+ ND_PRINT(": ");
+ } else {
+ ND_PRINT(" (%s): ", GET_ETHERADDR_STRING(src));
+ }
+ switch (GET_U_1(p)) {
+ case 0: ND_PRINT("Spectrum Management Act#%u", GET_U_1(p + 1)); break;
+ case 1: ND_PRINT("QoS Act#%u", GET_U_1(p + 1)); break;
+ case 2: ND_PRINT("DLS Act#%u", GET_U_1(p + 1)); break;
+ case 3: ND_PRINT("BA "); PRINT_BA_ACTION(GET_U_1(p + 1)); break;
+ case 7: ND_PRINT("HT "); PRINT_HT_ACTION(GET_U_1(p + 1)); break;
+ case 13: ND_PRINT("MeshAction "); PRINT_MESH_ACTION(GET_U_1(p + 1)); break;
+ case 14:
+ ND_PRINT("MultiohopAction ");
+ PRINT_MULTIHOP_ACTION(GET_U_1(p + 1)); break;
+ case 15:
+ ND_PRINT("SelfprotectAction ");
+ PRINT_SELFPROT_ACTION(GET_U_1(p + 1)); break;
+ case 127: ND_PRINT("Vendor Act#%u", GET_U_1(p + 1)); break;
+ default:
+ ND_PRINT("Reserved(%u) Act#%u", GET_U_1(p), GET_U_1(p + 1));
+ break;
+ }
+ return 1;
+trunc:
+ return 0;
+}
+
+
+/*********************************************************************************
+ * Print Body funcs
+ *********************************************************************************/
+
+
+static int
+mgmt_body_print(netdissect_options *ndo,
+ uint16_t fc, const uint8_t *src, const u_char *p, u_int length)
+{
+ ND_PRINT("%s", tok2str(st_str, "Unhandled Management subtype(%x)", FC_SUBTYPE(fc)));
+
+ /* There may be a problem w/ AP not having this bit set */
+ if (FC_PROTECTED(fc))
+ return wep_print(ndo, p);
+ switch (FC_SUBTYPE(fc)) {
+ case ST_ASSOC_REQUEST:
+ return handle_assoc_request(ndo, p, length);
+ case ST_ASSOC_RESPONSE:
+ return handle_assoc_response(ndo, p, length);
+ case ST_REASSOC_REQUEST:
+ return handle_reassoc_request(ndo, p, length);
+ case ST_REASSOC_RESPONSE:
+ return handle_reassoc_response(ndo, p, length);
+ case ST_PROBE_REQUEST:
+ return handle_probe_request(ndo, p, length);
+ case ST_PROBE_RESPONSE:
+ return handle_probe_response(ndo, p, length);
+ case ST_BEACON:
+ return handle_beacon(ndo, p, length);
+ case ST_ATIM:
+ return handle_atim();
+ case ST_DISASSOC:
+ return handle_disassoc(ndo, p, length);
+ case ST_AUTH:
+ return handle_auth(ndo, p, length);
+ case ST_DEAUTH:
+ return handle_deauth(ndo, src, p, length);
+ case ST_ACTION:
+ return handle_action(ndo, src, p, length);
+ default:
+ return 1;
+ }
+}
+
+
+/*********************************************************************************
+ * Handles printing all the control frame types
+ *********************************************************************************/
+
+static int
+ctrl_body_print(netdissect_options *ndo,
+ uint16_t fc, const u_char *p)
+{
+ ND_PRINT("%s", tok2str(ctrl_str, "Unknown Ctrl Subtype", FC_SUBTYPE(fc)));
+ switch (FC_SUBTYPE(fc)) {
+ case CTRL_CONTROL_WRAPPER:
+ /* XXX - requires special handling */
+ break;
+ case CTRL_BAR:
+ ND_TCHECK_LEN(p, CTRL_BAR_HDRLEN);
+ if (!ndo->ndo_eflag)
+ ND_PRINT(" RA:%s TA:%s CTL(%x) SEQ(%u) ",
+ GET_ETHERADDR_STRING(((const struct ctrl_bar_hdr_t *)p)->ra),
+ GET_ETHERADDR_STRING(((const struct ctrl_bar_hdr_t *)p)->ta),
+ GET_LE_U_2(((const struct ctrl_bar_hdr_t *)p)->ctl),
+ GET_LE_U_2(((const struct ctrl_bar_hdr_t *)p)->seq));
+ break;
+ case CTRL_BA:
+ ND_TCHECK_LEN(p, CTRL_BA_HDRLEN);
+ if (!ndo->ndo_eflag)
+ ND_PRINT(" RA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_ba_hdr_t *)p)->ra));
+ break;
+ case CTRL_PS_POLL:
+ ND_TCHECK_LEN(p, CTRL_PS_POLL_HDRLEN);
+ ND_PRINT(" AID(%x)",
+ GET_LE_U_2(((const struct ctrl_ps_poll_hdr_t *)p)->aid));
+ break;
+ case CTRL_RTS:
+ ND_TCHECK_LEN(p, CTRL_RTS_HDRLEN);
+ if (!ndo->ndo_eflag)
+ ND_PRINT(" TA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_rts_hdr_t *)p)->ta));
+ break;
+ case CTRL_CTS:
+ ND_TCHECK_LEN(p, CTRL_CTS_HDRLEN);
+ if (!ndo->ndo_eflag)
+ ND_PRINT(" RA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_cts_hdr_t *)p)->ra));
+ break;
+ case CTRL_ACK:
+ ND_TCHECK_LEN(p, CTRL_ACK_HDRLEN);
+ if (!ndo->ndo_eflag)
+ ND_PRINT(" RA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_ack_hdr_t *)p)->ra));
+ break;
+ case CTRL_CF_END:
+ ND_TCHECK_LEN(p, CTRL_END_HDRLEN);
+ if (!ndo->ndo_eflag)
+ ND_PRINT(" RA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_end_hdr_t *)p)->ra));
+ break;
+ case CTRL_END_ACK:
+ ND_TCHECK_LEN(p, CTRL_END_ACK_HDRLEN);
+ if (!ndo->ndo_eflag)
+ ND_PRINT(" RA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_end_ack_hdr_t *)p)->ra));
+ break;
+ }
+ return 1;
+trunc:
+ return 0;
+}
+
+/*
+ * Data Frame - Address field contents
+ *
+ * To Ds | From DS | Addr 1 | Addr 2 | Addr 3 | Addr 4
+ * 0 | 0 | DA | SA | BSSID | n/a
+ * 0 | 1 | DA | BSSID | SA | n/a
+ * 1 | 0 | BSSID | SA | DA | n/a
+ * 1 | 1 | RA | TA | DA | SA
+ */
+
+/*
+ * Function to get source and destination MAC addresses for a data frame.
+ */
+static void
+get_data_src_dst_mac(uint16_t fc, const u_char *p, const uint8_t **srcp,
+ const uint8_t **dstp)
+{
+#define ADDR1 (p + 4)
+#define ADDR2 (p + 10)
+#define ADDR3 (p + 16)
+#define ADDR4 (p + 24)
+
+ if (!FC_TO_DS(fc)) {
+ if (!FC_FROM_DS(fc)) {
+ /* not To DS and not From DS */
+ *srcp = ADDR2;
+ *dstp = ADDR1;
+ } else {
+ /* not To DS and From DS */
+ *srcp = ADDR3;
+ *dstp = ADDR1;
+ }
+ } else {
+ if (!FC_FROM_DS(fc)) {
+ /* From DS and not To DS */
+ *srcp = ADDR2;
+ *dstp = ADDR3;
+ } else {
+ /* To DS and From DS */
+ *srcp = ADDR4;
+ *dstp = ADDR3;
+ }
+ }
+
+#undef ADDR1
+#undef ADDR2
+#undef ADDR3
+#undef ADDR4
+}
+
+static void
+get_mgmt_src_dst_mac(const u_char *p, const uint8_t **srcp, const uint8_t **dstp)
+{
+ const struct mgmt_header_t *hp = (const struct mgmt_header_t *) p;
+
+ if (srcp != NULL)
+ *srcp = hp->sa;
+ if (dstp != NULL)
+ *dstp = hp->da;
+}
+
+/*
+ * Print Header funcs
+ */
+
+static void
+data_header_print(netdissect_options *ndo, uint16_t fc, const u_char *p)
+{
+ u_int subtype = FC_SUBTYPE(fc);
+
+ if (DATA_FRAME_IS_CF_ACK(subtype) || DATA_FRAME_IS_CF_POLL(subtype) ||
+ DATA_FRAME_IS_QOS(subtype)) {
+ ND_PRINT("CF ");
+ if (DATA_FRAME_IS_CF_ACK(subtype)) {
+ if (DATA_FRAME_IS_CF_POLL(subtype))
+ ND_PRINT("Ack/Poll");
+ else
+ ND_PRINT("Ack");
+ } else {
+ if (DATA_FRAME_IS_CF_POLL(subtype))
+ ND_PRINT("Poll");
+ }
+ if (DATA_FRAME_IS_QOS(subtype))
+ ND_PRINT("+QoS");
+ ND_PRINT(" ");
+ }
+
+#define ADDR1 (p + 4)
+#define ADDR2 (p + 10)
+#define ADDR3 (p + 16)
+#define ADDR4 (p + 24)
+
+ if (!FC_TO_DS(fc) && !FC_FROM_DS(fc)) {
+ ND_PRINT("DA:%s SA:%s BSSID:%s ",
+ GET_ETHERADDR_STRING(ADDR1), GET_ETHERADDR_STRING(ADDR2),
+ GET_ETHERADDR_STRING(ADDR3));
+ } else if (!FC_TO_DS(fc) && FC_FROM_DS(fc)) {
+ ND_PRINT("DA:%s BSSID:%s SA:%s ",
+ GET_ETHERADDR_STRING(ADDR1), GET_ETHERADDR_STRING(ADDR2),
+ GET_ETHERADDR_STRING(ADDR3));
+ } else if (FC_TO_DS(fc) && !FC_FROM_DS(fc)) {
+ ND_PRINT("BSSID:%s SA:%s DA:%s ",
+ GET_ETHERADDR_STRING(ADDR1), GET_ETHERADDR_STRING(ADDR2),
+ GET_ETHERADDR_STRING(ADDR3));
+ } else if (FC_TO_DS(fc) && FC_FROM_DS(fc)) {
+ ND_PRINT("RA:%s TA:%s DA:%s SA:%s ",
+ GET_ETHERADDR_STRING(ADDR1), GET_ETHERADDR_STRING(ADDR2),
+ GET_ETHERADDR_STRING(ADDR3), GET_ETHERADDR_STRING(ADDR4));
+ }
+
+#undef ADDR1
+#undef ADDR2
+#undef ADDR3
+#undef ADDR4
+}
+
+static void
+mgmt_header_print(netdissect_options *ndo, const u_char *p)
+{
+ const struct mgmt_header_t *hp = (const struct mgmt_header_t *) p;
+
+ ND_PRINT("BSSID:%s DA:%s SA:%s ",
+ GET_ETHERADDR_STRING((hp)->bssid), GET_ETHERADDR_STRING((hp)->da),
+ GET_ETHERADDR_STRING((hp)->sa));
+}
+
+static void
+ctrl_header_print(netdissect_options *ndo, uint16_t fc, const u_char *p)
+{
+ switch (FC_SUBTYPE(fc)) {
+ case CTRL_BAR:
+ ND_PRINT(" RA:%s TA:%s CTL(%x) SEQ(%u) ",
+ GET_ETHERADDR_STRING(((const struct ctrl_bar_hdr_t *)p)->ra),
+ GET_ETHERADDR_STRING(((const struct ctrl_bar_hdr_t *)p)->ta),
+ GET_LE_U_2(((const struct ctrl_bar_hdr_t *)p)->ctl),
+ GET_LE_U_2(((const struct ctrl_bar_hdr_t *)p)->seq));
+ break;
+ case CTRL_BA:
+ ND_PRINT("RA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_ba_hdr_t *)p)->ra));
+ break;
+ case CTRL_PS_POLL:
+ ND_PRINT("BSSID:%s TA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_ps_poll_hdr_t *)p)->bssid),
+ GET_ETHERADDR_STRING(((const struct ctrl_ps_poll_hdr_t *)p)->ta));
+ break;
+ case CTRL_RTS:
+ ND_PRINT("RA:%s TA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_rts_hdr_t *)p)->ra),
+ GET_ETHERADDR_STRING(((const struct ctrl_rts_hdr_t *)p)->ta));
+ break;
+ case CTRL_CTS:
+ ND_PRINT("RA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_cts_hdr_t *)p)->ra));
+ break;
+ case CTRL_ACK:
+ ND_PRINT("RA:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_ack_hdr_t *)p)->ra));
+ break;
+ case CTRL_CF_END:
+ ND_PRINT("RA:%s BSSID:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_end_hdr_t *)p)->ra),
+ GET_ETHERADDR_STRING(((const struct ctrl_end_hdr_t *)p)->bssid));
+ break;
+ case CTRL_END_ACK:
+ ND_PRINT("RA:%s BSSID:%s ",
+ GET_ETHERADDR_STRING(((const struct ctrl_end_ack_hdr_t *)p)->ra),
+ GET_ETHERADDR_STRING(((const struct ctrl_end_ack_hdr_t *)p)->bssid));
+ break;
+ default:
+ /* We shouldn't get here - we should already have quit */
+ break;
+ }
+}
+
+static int
+extract_header_length(netdissect_options *ndo,
+ uint16_t fc)
+{
+ int len;
+
+ switch (FC_TYPE(fc)) {
+ case T_MGMT:
+ return MGMT_HDRLEN;
+ case T_CTRL:
+ switch (FC_SUBTYPE(fc)) {
+ case CTRL_CONTROL_WRAPPER:
+ return CTRL_CONTROL_WRAPPER_HDRLEN;
+ case CTRL_BAR:
+ return CTRL_BAR_HDRLEN;
+ case CTRL_BA:
+ return CTRL_BA_HDRLEN;
+ case CTRL_PS_POLL:
+ return CTRL_PS_POLL_HDRLEN;
+ case CTRL_RTS:
+ return CTRL_RTS_HDRLEN;
+ case CTRL_CTS:
+ return CTRL_CTS_HDRLEN;
+ case CTRL_ACK:
+ return CTRL_ACK_HDRLEN;
+ case CTRL_CF_END:
+ return CTRL_END_HDRLEN;
+ case CTRL_END_ACK:
+ return CTRL_END_ACK_HDRLEN;
+ default:
+ ND_PRINT("unknown 802.11 ctrl frame subtype (%u)", FC_SUBTYPE(fc));
+ return 0;
+ }
+ case T_DATA:
+ len = (FC_TO_DS(fc) && FC_FROM_DS(fc)) ? 30 : 24;
+ if (DATA_FRAME_IS_QOS(FC_SUBTYPE(fc)))
+ len += 2;
+ return len;
+ default:
+ ND_PRINT("unknown 802.11 frame type (%u)", FC_TYPE(fc));
+ return 0;
+ }
+}
+
+static int
+extract_mesh_header_length(netdissect_options *ndo, const u_char *p)
+{
+ return (GET_U_1(p) &~ 3) ? 0 : 6*(1 + (GET_U_1(p) & 3));
+}
+
+/*
+ * Print the 802.11 MAC header.
+ */
+static void
+ieee_802_11_hdr_print(netdissect_options *ndo,
+ uint16_t fc, const u_char *p, u_int hdrlen,
+ u_int meshdrlen)
+{
+ if (ndo->ndo_vflag) {
+ if (FC_MORE_DATA(fc))
+ ND_PRINT("More Data ");
+ if (FC_MORE_FLAG(fc))
+ ND_PRINT("More Fragments ");
+ if (FC_POWER_MGMT(fc))
+ ND_PRINT("Pwr Mgmt ");
+ if (FC_RETRY(fc))
+ ND_PRINT("Retry ");
+ if (FC_ORDER(fc))
+ ND_PRINT("Strictly Ordered ");
+ if (FC_PROTECTED(fc))
+ ND_PRINT("Protected ");
+ if (FC_TYPE(fc) != T_CTRL || FC_SUBTYPE(fc) != CTRL_PS_POLL)
+ ND_PRINT("%uus ",
+ GET_LE_U_2(((const struct mgmt_header_t *)p)->duration));
+ }
+ if (meshdrlen != 0) {
+ const struct meshcntl_t *mc =
+ (const struct meshcntl_t *)(p + hdrlen - meshdrlen);
+ u_int ae = GET_U_1(mc->flags) & 3;
+
+ ND_PRINT("MeshData (AE %u TTL %u seq %u", ae,
+ GET_U_1(mc->ttl), GET_LE_U_4(mc->seq));
+ if (ae > 0)
+ ND_PRINT(" A4:%s", GET_ETHERADDR_STRING(mc->addr4));
+ if (ae > 1)
+ ND_PRINT(" A5:%s", GET_ETHERADDR_STRING(mc->addr5));
+ if (ae > 2)
+ ND_PRINT(" A6:%s", GET_ETHERADDR_STRING(mc->addr6));
+ ND_PRINT(") ");
+ }
+
+ switch (FC_TYPE(fc)) {
+ case T_MGMT:
+ mgmt_header_print(ndo, p);
+ break;
+ case T_CTRL:
+ ctrl_header_print(ndo, fc, p);
+ break;
+ case T_DATA:
+ data_header_print(ndo, fc, p);
+ break;
+ default:
+ break;
+ }
+}
+
+static u_int
+ieee802_11_print(netdissect_options *ndo,
+ const u_char *p, u_int length, u_int orig_caplen, int pad,
+ u_int fcslen)
+{
+ uint16_t fc;
+ u_int caplen, hdrlen, meshdrlen;
+ struct lladdr_info src, dst;
+ int llc_hdrlen;
+
+ ndo->ndo_protocol = "802.11";
+ caplen = orig_caplen;
+ /* Remove FCS, if present */
+ if (length < fcslen) {
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+ length -= fcslen;
+ if (caplen > length) {
+ /* Amount of FCS in actual packet data, if any */
+ fcslen = caplen - length;
+ caplen -= fcslen;
+ ndo->ndo_snapend -= fcslen;
+ }
+
+ if (caplen < IEEE802_11_FC_LEN) {
+ nd_print_trunc(ndo);
+ return orig_caplen;
+ }
+
+ fc = GET_LE_U_2(p);
+ hdrlen = extract_header_length(ndo, fc);
+ if (hdrlen == 0) {
+ /* Unknown frame type or control frame subtype; quit. */
+ return (0);
+ }
+ if (pad)
+ hdrlen = roundup2(hdrlen, 4);
+ if (ndo->ndo_Hflag && FC_TYPE(fc) == T_DATA &&
+ DATA_FRAME_IS_QOS(FC_SUBTYPE(fc))) {
+ if(!ND_TTEST_1(p + hdrlen)) {
+ nd_print_trunc(ndo);
+ return hdrlen;
+ }
+ meshdrlen = extract_mesh_header_length(ndo, p + hdrlen);
+ hdrlen += meshdrlen;
+ } else
+ meshdrlen = 0;
+
+ if (caplen < hdrlen) {
+ nd_print_trunc(ndo);
+ return hdrlen;
+ }
+
+ if (ndo->ndo_eflag)
+ ieee_802_11_hdr_print(ndo, fc, p, hdrlen, meshdrlen);
+
+ /*
+ * Go past the 802.11 header.
+ */
+ length -= hdrlen;
+ caplen -= hdrlen;
+ p += hdrlen;
+
+ src.addr_string = etheraddr_string;
+ dst.addr_string = etheraddr_string;
+ switch (FC_TYPE(fc)) {
+ case T_MGMT:
+ get_mgmt_src_dst_mac(p - hdrlen, &src.addr, &dst.addr);
+ if (!mgmt_body_print(ndo, fc, src.addr, p, length)) {
+ nd_print_trunc(ndo);
+ return hdrlen;
+ }
+ break;
+ case T_CTRL:
+ if (!ctrl_body_print(ndo, fc, p - hdrlen)) {
+ nd_print_trunc(ndo);
+ return hdrlen;
+ }
+ break;
+ case T_DATA:
+ if (DATA_FRAME_IS_NULL(FC_SUBTYPE(fc)))
+ return hdrlen; /* no-data frame */
+ /* There may be a problem w/ AP not having this bit set */
+ if (FC_PROTECTED(fc)) {
+ ND_PRINT("Data");
+ if (!wep_print(ndo, p)) {
+ nd_print_trunc(ndo);
+ return hdrlen;
+ }
+ } else {
+ get_data_src_dst_mac(fc, p - hdrlen, &src.addr, &dst.addr);
+ llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst);
+ if (llc_hdrlen < 0) {
+ /*
+ * Some kinds of LLC packet we cannot
+ * handle intelligently
+ */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = -llc_hdrlen;
+ }
+ hdrlen += llc_hdrlen;
+ }
+ break;
+ default:
+ /* We shouldn't get here - we should already have quit */
+ break;
+ }
+
+ return hdrlen;
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the 802.11 header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+ieee802_11_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "802.11";
+ ndo->ndo_ll_hdr_len += ieee802_11_print(ndo, p, h->len, h->caplen, 0, 0);
+}
+
+
+/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */
+/* NetBSD: ieee802_11_radio.h,v 1.2 2006/02/26 03:04:03 dyoung Exp */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of David Young may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
+ * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ */
+
+/* A generic radio capture format is desirable. It must be
+ * rigidly defined (e.g., units for fields should be given),
+ * and easily extensible.
+ *
+ * The following is an extensible radio capture format. It is
+ * based on a bitmap indicating which fields are present.
+ *
+ * I am trying to describe precisely what the application programmer
+ * should expect in the following, and for that reason I tell the
+ * units and origin of each measurement (where it applies), or else I
+ * use sufficiently weaselly language ("is a monotonically nondecreasing
+ * function of...") that I cannot set false expectations for lawyerly
+ * readers.
+ */
+
+/*
+ * The radio capture header precedes the 802.11 header.
+ *
+ * Note well: all radiotap fields are little-endian.
+ */
+struct ieee80211_radiotap_header {
+ nd_uint8_t it_version; /* Version 0. Only increases
+ * for drastic changes,
+ * introduction of compatible
+ * new fields does not count.
+ */
+ nd_uint8_t it_pad;
+ nd_uint16_t it_len; /* length of the whole
+ * header in bytes, including
+ * it_version, it_pad,
+ * it_len, and data fields.
+ */
+ nd_uint32_t it_present; /* A bitmap telling which
+ * fields are present. Set bit 31
+ * (0x80000000) to extend the
+ * bitmap by another 32 bits.
+ * Additional extensions are made
+ * by setting bit 31.
+ */
+};
+
+/* Name Data type Units
+ * ---- --------- -----
+ *
+ * IEEE80211_RADIOTAP_TSFT uint64_t microseconds
+ *
+ * Value in microseconds of the MAC's 64-bit 802.11 Time
+ * Synchronization Function timer when the first bit of the
+ * MPDU arrived at the MAC. For received frames, only.
+ *
+ * IEEE80211_RADIOTAP_CHANNEL 2 x uint16_t MHz, bitmap
+ *
+ * Tx/Rx frequency in MHz, followed by flags (see below).
+ * Note that IEEE80211_RADIOTAP_XCHANNEL must be used to
+ * represent an HT channel as there is not enough room in
+ * the flags word.
+ *
+ * IEEE80211_RADIOTAP_FHSS uint16_t see below
+ *
+ * For frequency-hopping radios, the hop set (first byte)
+ * and pattern (second byte).
+ *
+ * IEEE80211_RADIOTAP_RATE uint8_t 500kb/s or index
+ *
+ * Tx/Rx data rate. If bit 0x80 is set then it represents an
+ * an MCS index and not an IEEE rate.
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL int8_t decibels from
+ * one milliwatt (dBm)
+ *
+ * RF signal power at the antenna, decibel difference from
+ * one milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE int8_t decibels from
+ * one milliwatt (dBm)
+ *
+ * RF noise power at the antenna, decibel difference from one
+ * milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL uint8_t decibel (dB)
+ *
+ * RF signal power at the antenna, decibel difference from an
+ * arbitrary, fixed reference.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTNOISE uint8_t decibel (dB)
+ *
+ * RF noise power at the antenna, decibel difference from an
+ * arbitrary, fixed reference point.
+ *
+ * IEEE80211_RADIOTAP_LOCK_QUALITY uint16_t unitless
+ *
+ * Quality of Barker code lock. Unitless. Monotonically
+ * nondecreasing with "better" lock strength. Called "Signal
+ * Quality" in datasheets. (Is there a standard way to measure
+ * this?)
+ *
+ * IEEE80211_RADIOTAP_TX_ATTENUATION uint16_t unitless
+ *
+ * Transmit power expressed as unitless distance from max
+ * power set at factory calibration. 0 is max power.
+ * Monotonically nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t decibels (dB)
+ *
+ * Transmit power expressed as decibel distance from max power
+ * set at factory calibration. 0 is max power. Monotonically
+ * nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DBM_TX_POWER int8_t decibels from
+ * one milliwatt (dBm)
+ *
+ * Transmit power expressed as dBm (decibels from a 1 milliwatt
+ * reference). This is the absolute power level measured at
+ * the antenna port.
+ *
+ * IEEE80211_RADIOTAP_FLAGS uint8_t bitmap
+ *
+ * Properties of transmitted and received frames. See flags
+ * defined below.
+ *
+ * IEEE80211_RADIOTAP_ANTENNA uint8_t antenna index
+ *
+ * Unitless indication of the Rx/Tx antenna for this packet.
+ * The first antenna is antenna 0.
+ *
+ * IEEE80211_RADIOTAP_RX_FLAGS uint16_t bitmap
+ *
+ * Properties of received frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_XCHANNEL uint32_t bitmap
+ * uint16_t MHz
+ * uint8_t channel number
+ * uint8_t .5 dBm
+ *
+ * Extended channel specification: flags (see below) followed by
+ * frequency in MHz, the corresponding IEEE channel number, and
+ * finally the maximum regulatory transmit power cap in .5 dBm
+ * units. This property supersedes IEEE80211_RADIOTAP_CHANNEL
+ * and only one of the two should be present.
+ *
+ * IEEE80211_RADIOTAP_MCS uint8_t known
+ * uint8_t flags
+ * uint8_t mcs
+ *
+ * Bitset indicating which fields have known values, followed
+ * by bitset of flag values, followed by the MCS rate index as
+ * in IEEE 802.11n.
+ *
+ *
+ * IEEE80211_RADIOTAP_AMPDU_STATUS u32, u16, u8, u8 unitless
+ *
+ * Contains the AMPDU information for the subframe.
+ *
+ * IEEE80211_RADIOTAP_VHT u16, u8, u8, u8[4], u8, u8, u16
+ *
+ * Contains VHT information about this frame.
+ *
+ * IEEE80211_RADIOTAP_VENDOR_NAMESPACE
+ * uint8_t OUI[3]
+ * uint8_t subspace
+ * uint16_t length
+ *
+ * The Vendor Namespace Field contains three sub-fields. The first
+ * sub-field is 3 bytes long. It contains the vendor's IEEE 802
+ * Organizationally Unique Identifier (OUI). The fourth byte is a
+ * vendor-specific "namespace selector."
+ *
+ */
+enum ieee80211_radiotap_type {
+ IEEE80211_RADIOTAP_TSFT = 0,
+ IEEE80211_RADIOTAP_FLAGS = 1,
+ IEEE80211_RADIOTAP_RATE = 2,
+ IEEE80211_RADIOTAP_CHANNEL = 3,
+ IEEE80211_RADIOTAP_FHSS = 4,
+ IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
+ IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
+ IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
+ IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
+ IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
+ IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
+ IEEE80211_RADIOTAP_ANTENNA = 11,
+ IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
+ IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+ IEEE80211_RADIOTAP_RX_FLAGS = 14,
+ /* NB: gap for netbsd definitions */
+ IEEE80211_RADIOTAP_XCHANNEL = 18,
+ IEEE80211_RADIOTAP_MCS = 19,
+ IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+ IEEE80211_RADIOTAP_VHT = 21,
+ IEEE80211_RADIOTAP_NAMESPACE = 29,
+ IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30,
+ IEEE80211_RADIOTAP_EXT = 31
+};
+
+/* channel attributes */
+#define IEEE80211_CHAN_TURBO 0x00010 /* Turbo channel */
+#define IEEE80211_CHAN_CCK 0x00020 /* CCK channel */
+#define IEEE80211_CHAN_OFDM 0x00040 /* OFDM channel */
+#define IEEE80211_CHAN_2GHZ 0x00080 /* 2 GHz spectrum channel. */
+#define IEEE80211_CHAN_5GHZ 0x00100 /* 5 GHz spectrum channel */
+#define IEEE80211_CHAN_PASSIVE 0x00200 /* Only passive scan allowed */
+#define IEEE80211_CHAN_DYN 0x00400 /* Dynamic CCK-OFDM channel */
+#define IEEE80211_CHAN_GFSK 0x00800 /* GFSK channel (FHSS PHY) */
+#define IEEE80211_CHAN_GSM 0x01000 /* 900 MHz spectrum channel */
+#define IEEE80211_CHAN_STURBO 0x02000 /* 11a static turbo channel only */
+#define IEEE80211_CHAN_HALF 0x04000 /* Half rate channel */
+#define IEEE80211_CHAN_QUARTER 0x08000 /* Quarter rate channel */
+#define IEEE80211_CHAN_HT20 0x10000 /* HT 20 channel */
+#define IEEE80211_CHAN_HT40U 0x20000 /* HT 40 channel w/ ext above */
+#define IEEE80211_CHAN_HT40D 0x40000 /* HT 40 channel w/ ext below */
+
+/* Useful combinations of channel characteristics, borrowed from Ethereal */
+#define IEEE80211_CHAN_A \
+ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
+#define IEEE80211_CHAN_B \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
+#define IEEE80211_CHAN_G \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
+#define IEEE80211_CHAN_TA \
+ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM | IEEE80211_CHAN_TURBO)
+#define IEEE80211_CHAN_TG \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN | IEEE80211_CHAN_TURBO)
+
+
+/* For IEEE80211_RADIOTAP_FLAGS */
+#define IEEE80211_RADIOTAP_F_CFP 0x01 /* sent/received
+ * during CFP
+ */
+#define IEEE80211_RADIOTAP_F_SHORTPRE 0x02 /* sent/received
+ * with short
+ * preamble
+ */
+#define IEEE80211_RADIOTAP_F_WEP 0x04 /* sent/received
+ * with WEP encryption
+ */
+#define IEEE80211_RADIOTAP_F_FRAG 0x08 /* sent/received
+ * with fragmentation
+ */
+#define IEEE80211_RADIOTAP_F_FCS 0x10 /* frame includes FCS */
+#define IEEE80211_RADIOTAP_F_DATAPAD 0x20 /* frame has padding between
+ * 802.11 header and payload
+ * (to 32-bit boundary)
+ */
+#define IEEE80211_RADIOTAP_F_BADFCS 0x40 /* does not pass FCS check */
+
+/* For IEEE80211_RADIOTAP_RX_FLAGS */
+#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001 /* frame failed crc check */
+#define IEEE80211_RADIOTAP_F_RX_PLCP_CRC 0x0002 /* frame failed PLCP CRC check */
+
+/* For IEEE80211_RADIOTAP_MCS known */
+#define IEEE80211_RADIOTAP_MCS_BANDWIDTH_KNOWN 0x01
+#define IEEE80211_RADIOTAP_MCS_MCS_INDEX_KNOWN 0x02 /* MCS index field */
+#define IEEE80211_RADIOTAP_MCS_GUARD_INTERVAL_KNOWN 0x04
+#define IEEE80211_RADIOTAP_MCS_HT_FORMAT_KNOWN 0x08
+#define IEEE80211_RADIOTAP_MCS_FEC_TYPE_KNOWN 0x10
+#define IEEE80211_RADIOTAP_MCS_STBC_KNOWN 0x20
+#define IEEE80211_RADIOTAP_MCS_NESS_KNOWN 0x40
+#define IEEE80211_RADIOTAP_MCS_NESS_BIT_1 0x80
+
+/* For IEEE80211_RADIOTAP_MCS flags */
+#define IEEE80211_RADIOTAP_MCS_BANDWIDTH_MASK 0x03
+#define IEEE80211_RADIOTAP_MCS_BANDWIDTH_20 0
+#define IEEE80211_RADIOTAP_MCS_BANDWIDTH_40 1
+#define IEEE80211_RADIOTAP_MCS_BANDWIDTH_20L 2
+#define IEEE80211_RADIOTAP_MCS_BANDWIDTH_20U 3
+#define IEEE80211_RADIOTAP_MCS_SHORT_GI 0x04 /* short guard interval */
+#define IEEE80211_RADIOTAP_MCS_HT_GREENFIELD 0x08
+#define IEEE80211_RADIOTAP_MCS_FEC_LDPC 0x10
+#define IEEE80211_RADIOTAP_MCS_STBC_MASK 0x60
+#define IEEE80211_RADIOTAP_MCS_STBC_1 1
+#define IEEE80211_RADIOTAP_MCS_STBC_2 2
+#define IEEE80211_RADIOTAP_MCS_STBC_3 3
+#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT 5
+#define IEEE80211_RADIOTAP_MCS_NESS_BIT_0 0x80
+
+/* For IEEE80211_RADIOTAP_AMPDU_STATUS */
+#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN 0x0001
+#define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN 0x0002
+#define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN 0x0004
+#define IEEE80211_RADIOTAP_AMPDU_IS_LAST 0x0008
+#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR 0x0010
+#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN 0x0020
+
+/* For IEEE80211_RADIOTAP_VHT known */
+#define IEEE80211_RADIOTAP_VHT_STBC_KNOWN 0x0001
+#define IEEE80211_RADIOTAP_VHT_TXOP_PS_NA_KNOWN 0x0002
+#define IEEE80211_RADIOTAP_VHT_GUARD_INTERVAL_KNOWN 0x0004
+#define IEEE80211_RADIOTAP_VHT_SGI_NSYM_DIS_KNOWN 0x0008
+#define IEEE80211_RADIOTAP_VHT_LDPC_EXTRA_OFDM_SYM_KNOWN 0x0010
+#define IEEE80211_RADIOTAP_VHT_BEAMFORMED_KNOWN 0x0020
+#define IEEE80211_RADIOTAP_VHT_BANDWIDTH_KNOWN 0x0040
+#define IEEE80211_RADIOTAP_VHT_GROUP_ID_KNOWN 0x0080
+#define IEEE80211_RADIOTAP_VHT_PARTIAL_AID_KNOWN 0x0100
+
+/* For IEEE80211_RADIOTAP_VHT flags */
+#define IEEE80211_RADIOTAP_VHT_STBC 0x01
+#define IEEE80211_RADIOTAP_VHT_TXOP_PS_NA 0x02
+#define IEEE80211_RADIOTAP_VHT_SHORT_GI 0x04
+#define IEEE80211_RADIOTAP_VHT_SGI_NSYM_M10_9 0x08
+#define IEEE80211_RADIOTAP_VHT_LDPC_EXTRA_OFDM_SYM 0x10
+#define IEEE80211_RADIOTAP_VHT_BEAMFORMED 0x20
+
+#define IEEE80211_RADIOTAP_VHT_BANDWIDTH_MASK 0x1f
+
+#define IEEE80211_RADIOTAP_VHT_NSS_MASK 0x0f
+#define IEEE80211_RADIOTAP_VHT_MCS_MASK 0xf0
+#define IEEE80211_RADIOTAP_VHT_MCS_SHIFT 4
+
+#define IEEE80211_RADIOTAP_CODING_LDPC_USERn 0x01
+
+#define IEEE80211_CHAN_FHSS \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_GFSK)
+#define IEEE80211_CHAN_A \
+ (IEEE80211_CHAN_5GHZ | IEEE80211_CHAN_OFDM)
+#define IEEE80211_CHAN_B \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_CCK)
+#define IEEE80211_CHAN_PUREG \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_OFDM)
+#define IEEE80211_CHAN_G \
+ (IEEE80211_CHAN_2GHZ | IEEE80211_CHAN_DYN)
+
+#define IS_CHAN_FHSS(flags) \
+ ((flags & IEEE80211_CHAN_FHSS) == IEEE80211_CHAN_FHSS)
+#define IS_CHAN_A(flags) \
+ ((flags & IEEE80211_CHAN_A) == IEEE80211_CHAN_A)
+#define IS_CHAN_B(flags) \
+ ((flags & IEEE80211_CHAN_B) == IEEE80211_CHAN_B)
+#define IS_CHAN_PUREG(flags) \
+ ((flags & IEEE80211_CHAN_PUREG) == IEEE80211_CHAN_PUREG)
+#define IS_CHAN_G(flags) \
+ ((flags & IEEE80211_CHAN_G) == IEEE80211_CHAN_G)
+#define IS_CHAN_ANYG(flags) \
+ (IS_CHAN_PUREG(flags) || IS_CHAN_G(flags))
+
+static void
+print_chaninfo(netdissect_options *ndo,
+ uint16_t freq, uint32_t flags, uint32_t presentflags)
+{
+ ND_PRINT("%u MHz", freq);
+ if (presentflags & (1 << IEEE80211_RADIOTAP_MCS)) {
+ /*
+ * We have the MCS field, so this is 11n, regardless
+ * of what the channel flags say.
+ */
+ ND_PRINT(" 11n");
+ } else {
+ if (IS_CHAN_FHSS(flags))
+ ND_PRINT(" FHSS");
+ if (IS_CHAN_A(flags)) {
+ if (flags & IEEE80211_CHAN_HALF)
+ ND_PRINT(" 11a/10Mhz");
+ else if (flags & IEEE80211_CHAN_QUARTER)
+ ND_PRINT(" 11a/5Mhz");
+ else
+ ND_PRINT(" 11a");
+ }
+ if (IS_CHAN_ANYG(flags)) {
+ if (flags & IEEE80211_CHAN_HALF)
+ ND_PRINT(" 11g/10Mhz");
+ else if (flags & IEEE80211_CHAN_QUARTER)
+ ND_PRINT(" 11g/5Mhz");
+ else
+ ND_PRINT(" 11g");
+ } else if (IS_CHAN_B(flags))
+ ND_PRINT(" 11b");
+ if (flags & IEEE80211_CHAN_TURBO)
+ ND_PRINT(" Turbo");
+ }
+ /*
+ * These apply to 11n.
+ */
+ if (flags & IEEE80211_CHAN_HT20)
+ ND_PRINT(" ht/20");
+ else if (flags & IEEE80211_CHAN_HT40D)
+ ND_PRINT(" ht/40-");
+ else if (flags & IEEE80211_CHAN_HT40U)
+ ND_PRINT(" ht/40+");
+ ND_PRINT(" ");
+}
+
+static int
+print_radiotap_field(netdissect_options *ndo,
+ struct cpack_state *s, uint32_t bit, uint8_t *flagsp,
+ uint32_t presentflags)
+{
+ u_int i;
+ int rc;
+
+ switch (bit) {
+
+ case IEEE80211_RADIOTAP_TSFT: {
+ uint64_t tsft;
+
+ rc = nd_cpack_uint64(ndo, s, &tsft);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%" PRIu64 "us tsft ", tsft);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_FLAGS: {
+ uint8_t flagsval;
+
+ rc = nd_cpack_uint8(ndo, s, &flagsval);
+ if (rc != 0)
+ goto trunc;
+ *flagsp = flagsval;
+ if (flagsval & IEEE80211_RADIOTAP_F_CFP)
+ ND_PRINT("cfp ");
+ if (flagsval & IEEE80211_RADIOTAP_F_SHORTPRE)
+ ND_PRINT("short preamble ");
+ if (flagsval & IEEE80211_RADIOTAP_F_WEP)
+ ND_PRINT("wep ");
+ if (flagsval & IEEE80211_RADIOTAP_F_FRAG)
+ ND_PRINT("fragmented ");
+ if (flagsval & IEEE80211_RADIOTAP_F_BADFCS)
+ ND_PRINT("bad-fcs ");
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_RATE: {
+ uint8_t rate;
+
+ rc = nd_cpack_uint8(ndo, s, &rate);
+ if (rc != 0)
+ goto trunc;
+ /*
+ * XXX On FreeBSD rate & 0x80 means we have an MCS. On
+ * Linux and AirPcap it does not. (What about
+ * macOS, NetBSD, OpenBSD, and DragonFly BSD?)
+ *
+ * This is an issue either for proprietary extensions
+ * to 11a or 11g, which do exist, or for 11n
+ * implementations that stuff a rate value into
+ * this field, which also appear to exist.
+ *
+ * We currently handle that by assuming that
+ * if the 0x80 bit is set *and* the remaining
+ * bits have a value between 0 and 15 it's
+ * an MCS value, otherwise it's a rate. If
+ * there are cases where systems that use
+ * "0x80 + MCS index" for MCS indices > 15,
+ * or stuff a rate value here between 64 and
+ * 71.5 Mb/s in here, we'll need a preference
+ * setting. Such rates do exist, e.g. 11n
+ * MCS 7 at 20 MHz with a long guard interval.
+ */
+ if (rate >= 0x80 && rate <= 0x8f) {
+ /*
+ * XXX - we don't know the channel width
+ * or guard interval length, so we can't
+ * convert this to a data rate.
+ *
+ * If you want us to show a data rate,
+ * use the MCS field, not the Rate field;
+ * the MCS field includes not only the
+ * MCS index, it also includes bandwidth
+ * and guard interval information.
+ *
+ * XXX - can we get the channel width
+ * from XChannel and the guard interval
+ * information from Flags, at least on
+ * FreeBSD?
+ */
+ ND_PRINT("MCS %u ", rate & 0x7f);
+ } else
+ ND_PRINT("%2.1f Mb/s ", .5 * rate);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_CHANNEL: {
+ uint16_t frequency;
+ uint16_t flags;
+
+ rc = nd_cpack_uint16(ndo, s, &frequency);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint16(ndo, s, &flags);
+ if (rc != 0)
+ goto trunc;
+ /*
+ * If CHANNEL and XCHANNEL are both present, skip
+ * CHANNEL.
+ */
+ if (presentflags & (1 << IEEE80211_RADIOTAP_XCHANNEL))
+ break;
+ print_chaninfo(ndo, frequency, flags, presentflags);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_FHSS: {
+ uint8_t hopset;
+ uint8_t hoppat;
+
+ rc = nd_cpack_uint8(ndo, s, &hopset);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &hoppat);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("fhset %u fhpat %u ", hopset, hoppat);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_DBM_ANTSIGNAL: {
+ int8_t dbm_antsignal;
+
+ rc = nd_cpack_int8(ndo, s, &dbm_antsignal);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%ddBm signal ", dbm_antsignal);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_DBM_ANTNOISE: {
+ int8_t dbm_antnoise;
+
+ rc = nd_cpack_int8(ndo, s, &dbm_antnoise);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%ddBm noise ", dbm_antnoise);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_LOCK_QUALITY: {
+ uint16_t lock_quality;
+
+ rc = nd_cpack_uint16(ndo, s, &lock_quality);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%u sq ", lock_quality);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_TX_ATTENUATION: {
+ int16_t tx_attenuation;
+
+ rc = nd_cpack_int16(ndo, s, &tx_attenuation);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%d tx power ", -tx_attenuation);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_DB_TX_ATTENUATION: {
+ int8_t db_tx_attenuation;
+
+ rc = nd_cpack_int8(ndo, s, &db_tx_attenuation);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%ddB tx attenuation ", -db_tx_attenuation);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_DBM_TX_POWER: {
+ int8_t dbm_tx_power;
+
+ rc = nd_cpack_int8(ndo, s, &dbm_tx_power);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%ddBm tx power ", dbm_tx_power);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_ANTENNA: {
+ uint8_t antenna;
+
+ rc = nd_cpack_uint8(ndo, s, &antenna);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("antenna %u ", antenna);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_DB_ANTSIGNAL: {
+ uint8_t db_antsignal;
+
+ rc = nd_cpack_uint8(ndo, s, &db_antsignal);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%udB signal ", db_antsignal);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_DB_ANTNOISE: {
+ uint8_t db_antnoise;
+
+ rc = nd_cpack_uint8(ndo, s, &db_antnoise);
+ if (rc != 0)
+ goto trunc;
+ ND_PRINT("%udB noise ", db_antnoise);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_RX_FLAGS: {
+ uint16_t rx_flags;
+
+ rc = nd_cpack_uint16(ndo, s, &rx_flags);
+ if (rc != 0)
+ goto trunc;
+ /* Do nothing for now */
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_XCHANNEL: {
+ uint32_t flags;
+ uint16_t frequency;
+ uint8_t channel;
+ uint8_t maxpower;
+
+ rc = nd_cpack_uint32(ndo, s, &flags);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint16(ndo, s, &frequency);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &channel);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &maxpower);
+ if (rc != 0)
+ goto trunc;
+ print_chaninfo(ndo, frequency, flags, presentflags);
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_MCS: {
+ uint8_t known;
+ uint8_t flags;
+ uint8_t mcs_index;
+ static const char *ht_bandwidth[4] = {
+ "20 MHz",
+ "40 MHz",
+ "20 MHz (L)",
+ "20 MHz (U)"
+ };
+ float htrate;
+
+ rc = nd_cpack_uint8(ndo, s, &known);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &flags);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &mcs_index);
+ if (rc != 0)
+ goto trunc;
+ if (known & IEEE80211_RADIOTAP_MCS_MCS_INDEX_KNOWN) {
+ /*
+ * We know the MCS index.
+ */
+ if (mcs_index <= MAX_MCS_INDEX) {
+ /*
+ * And it's in-range.
+ */
+ if (known & (IEEE80211_RADIOTAP_MCS_BANDWIDTH_KNOWN|IEEE80211_RADIOTAP_MCS_GUARD_INTERVAL_KNOWN)) {
+ /*
+ * And we know both the bandwidth and
+ * the guard interval, so we can look
+ * up the rate.
+ */
+ htrate =
+ ieee80211_float_htrates
+ [mcs_index]
+ [((flags & IEEE80211_RADIOTAP_MCS_BANDWIDTH_MASK) == IEEE80211_RADIOTAP_MCS_BANDWIDTH_40 ? 1 : 0)]
+ [((flags & IEEE80211_RADIOTAP_MCS_SHORT_GI) ? 1 : 0)];
+ } else {
+ /*
+ * We don't know both the bandwidth
+ * and the guard interval, so we can
+ * only report the MCS index.
+ */
+ htrate = 0.0;
+ }
+ } else {
+ /*
+ * The MCS value is out of range.
+ */
+ htrate = 0.0;
+ }
+ if (htrate != 0.0) {
+ /*
+ * We have the rate.
+ * Print it.
+ */
+ ND_PRINT("%.1f Mb/s MCS %u ", htrate, mcs_index);
+ } else {
+ /*
+ * We at least have the MCS index.
+ * Print it.
+ */
+ ND_PRINT("MCS %u ", mcs_index);
+ }
+ }
+ if (known & IEEE80211_RADIOTAP_MCS_BANDWIDTH_KNOWN) {
+ ND_PRINT("%s ",
+ ht_bandwidth[flags & IEEE80211_RADIOTAP_MCS_BANDWIDTH_MASK]);
+ }
+ if (known & IEEE80211_RADIOTAP_MCS_GUARD_INTERVAL_KNOWN) {
+ ND_PRINT("%s GI ",
+ (flags & IEEE80211_RADIOTAP_MCS_SHORT_GI) ?
+ "short" : "long");
+ }
+ if (known & IEEE80211_RADIOTAP_MCS_HT_FORMAT_KNOWN) {
+ ND_PRINT("%s ",
+ (flags & IEEE80211_RADIOTAP_MCS_HT_GREENFIELD) ?
+ "greenfield" : "mixed");
+ }
+ if (known & IEEE80211_RADIOTAP_MCS_FEC_TYPE_KNOWN) {
+ ND_PRINT("%s FEC ",
+ (flags & IEEE80211_RADIOTAP_MCS_FEC_LDPC) ?
+ "LDPC" : "BCC");
+ }
+ if (known & IEEE80211_RADIOTAP_MCS_STBC_KNOWN) {
+ ND_PRINT("RX-STBC%u ",
+ (flags & IEEE80211_RADIOTAP_MCS_STBC_MASK) >> IEEE80211_RADIOTAP_MCS_STBC_SHIFT);
+ }
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_AMPDU_STATUS: {
+ uint32_t reference_num;
+ uint16_t flags;
+ uint8_t delim_crc;
+ uint8_t reserved;
+
+ rc = nd_cpack_uint32(ndo, s, &reference_num);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint16(ndo, s, &flags);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &delim_crc);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &reserved);
+ if (rc != 0)
+ goto trunc;
+ /* Do nothing for now */
+ break;
+ }
+
+ case IEEE80211_RADIOTAP_VHT: {
+ uint16_t known;
+ uint8_t flags;
+ uint8_t bandwidth;
+ uint8_t mcs_nss[4];
+ uint8_t coding;
+ uint8_t group_id;
+ uint16_t partial_aid;
+ static const char *vht_bandwidth[32] = {
+ "20 MHz",
+ "40 MHz",
+ "20 MHz (L)",
+ "20 MHz (U)",
+ "80 MHz",
+ "80 MHz (L)",
+ "80 MHz (U)",
+ "80 MHz (LL)",
+ "80 MHz (LU)",
+ "80 MHz (UL)",
+ "80 MHz (UU)",
+ "160 MHz",
+ "160 MHz (L)",
+ "160 MHz (U)",
+ "160 MHz (LL)",
+ "160 MHz (LU)",
+ "160 MHz (UL)",
+ "160 MHz (UU)",
+ "160 MHz (LLL)",
+ "160 MHz (LLU)",
+ "160 MHz (LUL)",
+ "160 MHz (UUU)",
+ "160 MHz (ULL)",
+ "160 MHz (ULU)",
+ "160 MHz (UUL)",
+ "160 MHz (UUU)",
+ "unknown (26)",
+ "unknown (27)",
+ "unknown (28)",
+ "unknown (29)",
+ "unknown (30)",
+ "unknown (31)"
+ };
+
+ rc = nd_cpack_uint16(ndo, s, &known);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &flags);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &bandwidth);
+ if (rc != 0)
+ goto trunc;
+ for (i = 0; i < 4; i++) {
+ rc = nd_cpack_uint8(ndo, s, &mcs_nss[i]);
+ if (rc != 0)
+ goto trunc;
+ }
+ rc = nd_cpack_uint8(ndo, s, &coding);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint8(ndo, s, &group_id);
+ if (rc != 0)
+ goto trunc;
+ rc = nd_cpack_uint16(ndo, s, &partial_aid);
+ if (rc != 0)
+ goto trunc;
+ for (i = 0; i < 4; i++) {
+ u_int nss, mcs;
+ nss = mcs_nss[i] & IEEE80211_RADIOTAP_VHT_NSS_MASK;
+ mcs = (mcs_nss[i] & IEEE80211_RADIOTAP_VHT_MCS_MASK) >> IEEE80211_RADIOTAP_VHT_MCS_SHIFT;
+
+ if (nss == 0)
+ continue;
+
+ ND_PRINT("User %u MCS %u ", i, mcs);
+ ND_PRINT("%s FEC ",
+ (coding & (IEEE80211_RADIOTAP_CODING_LDPC_USERn << i)) ?
+ "LDPC" : "BCC");
+ }
+ if (known & IEEE80211_RADIOTAP_VHT_BANDWIDTH_KNOWN) {
+ ND_PRINT("%s ",
+ vht_bandwidth[bandwidth & IEEE80211_RADIOTAP_VHT_BANDWIDTH_MASK]);
+ }
+ if (known & IEEE80211_RADIOTAP_VHT_GUARD_INTERVAL_KNOWN) {
+ ND_PRINT("%s GI ",
+ (flags & IEEE80211_RADIOTAP_VHT_SHORT_GI) ?
+ "short" : "long");
+ }
+ break;
+ }
+
+ default:
+ /* this bit indicates a field whose
+ * size we do not know, so we cannot
+ * proceed. Just print the bit number.
+ */
+ ND_PRINT("[bit %u] ", bit);
+ return -1;
+ }
+
+ return 0;
+
+trunc:
+ nd_print_trunc(ndo);
+ return rc;
+}
+
+
+static int
+print_in_radiotap_namespace(netdissect_options *ndo,
+ struct cpack_state *s, uint8_t *flags,
+ uint32_t presentflags, int bit0)
+{
+#define BITNO_32(x) (((x) >> 16) ? 16 + BITNO_16((x) >> 16) : BITNO_16((x)))
+#define BITNO_16(x) (((x) >> 8) ? 8 + BITNO_8((x) >> 8) : BITNO_8((x)))
+#define BITNO_8(x) (((x) >> 4) ? 4 + BITNO_4((x) >> 4) : BITNO_4((x)))
+#define BITNO_4(x) (((x) >> 2) ? 2 + BITNO_2((x) >> 2) : BITNO_2((x)))
+#define BITNO_2(x) (((x) & 2) ? 1 : 0)
+ uint32_t present, next_present;
+ int bitno;
+ enum ieee80211_radiotap_type bit;
+ int rc;
+
+ for (present = presentflags; present; present = next_present) {
+ /*
+ * Clear the least significant bit that is set.
+ */
+ next_present = present & (present - 1);
+
+ /*
+ * Get the bit number, within this presence word,
+ * of the remaining least significant bit that
+ * is set.
+ */
+ bitno = BITNO_32(present ^ next_present);
+
+ /*
+ * Stop if this is one of the "same meaning
+ * in all presence flags" bits.
+ */
+ if (bitno >= IEEE80211_RADIOTAP_NAMESPACE)
+ break;
+
+ /*
+ * Get the radiotap bit number of that bit.
+ */
+ bit = (enum ieee80211_radiotap_type)(bit0 + bitno);
+
+ rc = print_radiotap_field(ndo, s, bit, flags, presentflags);
+ if (rc != 0)
+ return rc;
+ }
+
+ return 0;
+}
+
+u_int
+ieee802_11_radio_print(netdissect_options *ndo,
+ const u_char *p, u_int length, u_int caplen)
+{
+#define BIT(n) (1U << n)
+#define IS_EXTENDED(__p) \
+ (GET_LE_U_4(__p) & BIT(IEEE80211_RADIOTAP_EXT)) != 0
+
+ struct cpack_state cpacker;
+ const struct ieee80211_radiotap_header *hdr;
+ uint32_t presentflags;
+ const nd_uint32_t *presentp, *last_presentp;
+ int vendor_namespace;
+ uint8_t vendor_oui[3];
+ uint8_t vendor_subnamespace;
+ uint16_t skip_length;
+ int bit0;
+ u_int len;
+ uint8_t flags;
+ int pad;
+ u_int fcslen;
+
+ ndo->ndo_protocol = "802.11_radio";
+ if (caplen < sizeof(*hdr)) {
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+
+ hdr = (const struct ieee80211_radiotap_header *)p;
+
+ len = GET_LE_U_2(hdr->it_len);
+ if (len < sizeof(*hdr)) {
+ /*
+ * The length is the length of the entire header, so
+ * it must be as large as the fixed-length part of
+ * the header.
+ */
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+
+ /*
+ * If we don't have the entire radiotap header, just give up.
+ */
+ if (caplen < len) {
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+ nd_cpack_init(&cpacker, (const uint8_t *)hdr, len); /* align against header start */
+ nd_cpack_advance(&cpacker, sizeof(*hdr)); /* includes the 1st bitmap */
+ for (last_presentp = &hdr->it_present;
+ (const u_char*)(last_presentp + 1) <= p + len &&
+ IS_EXTENDED(last_presentp);
+ last_presentp++)
+ nd_cpack_advance(&cpacker, sizeof(hdr->it_present)); /* more bitmaps */
+
+ /* are there more bitmap extensions than bytes in header? */
+ if ((const u_char*)(last_presentp + 1) > p + len) {
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+
+ /*
+ * Start out at the beginning of the default radiotap namespace.
+ */
+ bit0 = 0;
+ vendor_namespace = 0;
+ memset(vendor_oui, 0, 3);
+ vendor_subnamespace = 0;
+ skip_length = 0;
+ /* Assume no flags */
+ flags = 0;
+ /* Assume no Atheros padding between 802.11 header and body */
+ pad = 0;
+ /* Assume no FCS at end of frame */
+ fcslen = 0;
+ for (presentp = &hdr->it_present; presentp <= last_presentp;
+ presentp++) {
+ presentflags = GET_LE_U_4(presentp);
+
+ /*
+ * If this is a vendor namespace, we don't handle it.
+ */
+ if (vendor_namespace) {
+ /*
+ * Skip past the stuff we don't understand.
+ * If we add support for any vendor namespaces,
+ * it'd be added here; use vendor_oui and
+ * vendor_subnamespace to interpret the fields.
+ */
+ if (nd_cpack_advance(&cpacker, skip_length) != 0) {
+ /*
+ * Ran out of space in the packet.
+ */
+ break;
+ }
+
+ /*
+ * We've skipped it all; nothing more to
+ * skip.
+ */
+ skip_length = 0;
+ } else {
+ if (print_in_radiotap_namespace(ndo, &cpacker,
+ &flags, presentflags, bit0) != 0) {
+ /*
+ * Fatal error - can't process anything
+ * more in the radiotap header.
+ */
+ break;
+ }
+ }
+
+ /*
+ * Handle the namespace switch bits; we've already handled
+ * the extension bit in all but the last word above.
+ */
+ switch (presentflags &
+ (BIT(IEEE80211_RADIOTAP_NAMESPACE)|BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE))) {
+
+ case 0:
+ /*
+ * We're not changing namespaces.
+ * advance to the next 32 bits in the current
+ * namespace.
+ */
+ bit0 += 32;
+ break;
+
+ case BIT(IEEE80211_RADIOTAP_NAMESPACE):
+ /*
+ * We're switching to the radiotap namespace.
+ * Reset the presence-bitmap index to 0, and
+ * reset the namespace to the default radiotap
+ * namespace.
+ */
+ bit0 = 0;
+ vendor_namespace = 0;
+ memset(vendor_oui, 0, 3);
+ vendor_subnamespace = 0;
+ skip_length = 0;
+ break;
+
+ case BIT(IEEE80211_RADIOTAP_VENDOR_NAMESPACE):
+ /*
+ * We're switching to a vendor namespace.
+ * Reset the presence-bitmap index to 0,
+ * note that we're in a vendor namespace,
+ * and fetch the fields of the Vendor Namespace
+ * item.
+ */
+ bit0 = 0;
+ vendor_namespace = 1;
+ if ((nd_cpack_align_and_reserve(&cpacker, 2)) == NULL) {
+ nd_print_trunc(ndo);
+ break;
+ }
+ if (nd_cpack_uint8(ndo, &cpacker, &vendor_oui[0]) != 0) {
+ nd_print_trunc(ndo);
+ break;
+ }
+ if (nd_cpack_uint8(ndo, &cpacker, &vendor_oui[1]) != 0) {
+ nd_print_trunc(ndo);
+ break;
+ }
+ if (nd_cpack_uint8(ndo, &cpacker, &vendor_oui[2]) != 0) {
+ nd_print_trunc(ndo);
+ break;
+ }
+ if (nd_cpack_uint8(ndo, &cpacker, &vendor_subnamespace) != 0) {
+ nd_print_trunc(ndo);
+ break;
+ }
+ if (nd_cpack_uint16(ndo, &cpacker, &skip_length) != 0) {
+ nd_print_trunc(ndo);
+ break;
+ }
+ break;
+
+ default:
+ /*
+ * Illegal combination. The behavior in this
+ * case is undefined by the radiotap spec; we
+ * just ignore both bits.
+ */
+ break;
+ }
+ }
+
+ if (flags & IEEE80211_RADIOTAP_F_DATAPAD)
+ pad = 1; /* Atheros padding */
+ if (flags & IEEE80211_RADIOTAP_F_FCS)
+ fcslen = 4; /* FCS at end of packet */
+ return len + ieee802_11_print(ndo, p + len, length - len, caplen - len, pad,
+ fcslen);
+#undef BITNO_32
+#undef BITNO_16
+#undef BITNO_8
+#undef BITNO_4
+#undef BITNO_2
+#undef BIT
+}
+
+static u_int
+ieee802_11_radio_avs_print(netdissect_options *ndo,
+ const u_char *p, u_int length, u_int caplen)
+{
+ uint32_t caphdr_len;
+
+ ndo->ndo_protocol = "802.11_radio_avs";
+ if (caplen < 8) {
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+
+ caphdr_len = GET_BE_U_4(p + 4);
+ if (caphdr_len < 8) {
+ /*
+ * Yow! The capture header length is claimed not
+ * to be large enough to include even the version
+ * cookie or capture header length!
+ */
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+
+ if (caplen < caphdr_len) {
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+
+ return caphdr_len + ieee802_11_print(ndo, p + caphdr_len,
+ length - caphdr_len, caplen - caphdr_len, 0, 0);
+}
+
+#define PRISM_HDR_LEN 144
+
+#define WLANCAP_MAGIC_COOKIE_BASE 0x80211000
+#define WLANCAP_MAGIC_COOKIE_V1 0x80211001
+#define WLANCAP_MAGIC_COOKIE_V2 0x80211002
+
+/*
+ * For DLT_PRISM_HEADER; like DLT_IEEE802_11, but with an extra header,
+ * containing information such as radio information, which we
+ * currently ignore.
+ *
+ * If, however, the packet begins with WLANCAP_MAGIC_COOKIE_V1 or
+ * WLANCAP_MAGIC_COOKIE_V2, it's really DLT_IEEE802_11_RADIO_AVS
+ * (currently, on Linux, there's no ARPHRD_ type for
+ * DLT_IEEE802_11_RADIO_AVS, as there is a ARPHRD_IEEE80211_PRISM
+ * for DLT_PRISM_HEADER, so ARPHRD_IEEE80211_PRISM is used for
+ * the AVS header, and the first 4 bytes of the header are used to
+ * indicate whether it's a Prism header or an AVS header).
+ */
+void
+prism_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ uint32_t msgcode;
+
+ ndo->ndo_protocol = "prism";
+ if (caplen < 4) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ msgcode = GET_BE_U_4(p);
+ if (msgcode == WLANCAP_MAGIC_COOKIE_V1 ||
+ msgcode == WLANCAP_MAGIC_COOKIE_V2) {
+ ndo->ndo_ll_hdr_len += ieee802_11_radio_avs_print(ndo, p, length, caplen);
+ return;
+ }
+
+ if (caplen < PRISM_HDR_LEN) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ p += PRISM_HDR_LEN;
+ length -= PRISM_HDR_LEN;
+ caplen -= PRISM_HDR_LEN;
+ ndo->ndo_ll_hdr_len += PRISM_HDR_LEN;
+ ndo->ndo_ll_hdr_len += ieee802_11_print(ndo, p, length, caplen, 0, 0);
+}
+
+/*
+ * For DLT_IEEE802_11_RADIO; like DLT_IEEE802_11, but with an extra
+ * header, containing information such as radio information.
+ */
+void
+ieee802_11_radio_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "802.11_radio";
+ ndo->ndo_ll_hdr_len += ieee802_11_radio_print(ndo, p, h->len, h->caplen);
+}
+
+/*
+ * For DLT_IEEE802_11_RADIO_AVS; like DLT_IEEE802_11, but with an
+ * extra header, containing information such as radio information,
+ * which we currently ignore.
+ */
+void
+ieee802_11_radio_avs_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "802.11_radio_avs";
+ ndo->ndo_ll_hdr_len += ieee802_11_radio_avs_print(ndo, p, h->len, h->caplen);
+}
diff --git a/print-802_15_4.c b/print-802_15_4.c
new file mode 100644
index 0000000..d337164
--- /dev/null
+++ b/print-802_15_4.c
@@ -0,0 +1,2534 @@
+/*
+ * Copyright (c) 2009
+ * Siemens AG, All rights reserved.
+ * Dmitry Eremin-Solenikov (dbaryshkov@gmail.com)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IEEE 802.15.4 printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+
+#include "extract.h"
+
+#define CHECK_BIT(num,bit) (((num) >> (bit)) & 0x1)
+
+#define BROKEN_6TISCH_PAN_ID_COMPRESSION 0
+
+/* Frame types from Table 7-1 of 802.15.4-2015 */
+static const char *ftypes[] = {
+ "Beacon", /* 0 */
+ "Data", /* 1 */
+ "ACK", /* 2 */
+ "Command", /* 3 */
+ "Reserved", /* 4 */
+ "Multipurpose", /* 5 */
+ "Fragment", /* 6 */
+ "Extended" /* 7 */
+};
+
+/* Element IDs for Header IEs from Table 7-7 of 802.15.4-2015 */
+static const char *h_ie_names[] = {
+ "Vendor Specific Header IE", /* 0x00 */
+ "Reserved 0x01", /* 0x01 */
+ "Reserved 0x02", /* 0x02 */
+ "Reserved 0x03", /* 0x03 */
+ "Reserved 0x04", /* 0x04 */
+ "Reserved 0x05", /* 0x05 */
+ "Reserved 0x06", /* 0x06 */
+ "Reserved 0x07", /* 0x07 */
+ "Reserved 0x08", /* 0x08 */
+ "Reserved 0x09", /* 0x09 */
+ "Reserved 0x0a", /* 0x0a */
+ "Reserved 0x0b", /* 0x0b */
+ "Reserved 0x0c", /* 0x0c */
+ "Reserved 0x0d", /* 0x0d */
+ "Reserved 0x0e", /* 0x0e */
+ "Reserved 0x0f", /* 0x0f */
+ "Reserved 0x10", /* 0x10 */
+ "Reserved 0x11", /* 0x11 */
+ "Reserved 0x12", /* 0x12 */
+ "Reserved 0x13", /* 0x13 */
+ "Reserved 0x14", /* 0x14 */
+ "Reserved 0x15", /* 0x15 */
+ "Reserved 0x16", /* 0x16 */
+ "Reserved 0x17", /* 0x17 */
+ "Reserved 0x18", /* 0x18 */
+ "Reserved 0x19", /* 0x19 */
+ "LE CSL IE", /* 0x1a */
+ "LE RIT IE", /* 0x1b */
+ "DSME PAN descriptor IE", /* 0x1c */
+ "Rendezvous Time IE", /* 0x1d */
+ "Time Correction IE", /* 0x1e */
+ "Reserved 0x1f", /* 0x1f */
+ "Reserved 0x20", /* 0x20 */
+ "Extended DSME PAN descriptor IE", /* 0x21 */
+ "Fragment Sequence Context Description IE", /* 0x22 */
+ "Simplified Superframe Specification IE", /* 0x23 */
+ "Simplified GTS Specification IE", /* 0x24 */
+ "LECIM Capabilities IE", /* 0x25 */
+ "TRLE Descriptor IE", /* 0x26 */
+ "RCC Capabilities IE", /* 0x27 */
+ "RCCN Descriptor IE", /* 0x28 */
+ "Global Time IE", /* 0x29 */
+ "Omnibus Header IE", /* 0x2a */
+ "DA IE", /* 0x2b */
+ "Reserved 0x2c", /* 0x2c */
+ "Reserved 0x2d", /* 0x2d */
+ "Reserved 0x2e", /* 0x2e */
+ "Reserved 0x2f", /* 0x2f */
+ "Reserved 0x30", /* 0x30 */
+ "Reserved 0x31", /* 0x31 */
+ "Reserved 0x32", /* 0x32 */
+ "Reserved 0x33", /* 0x33 */
+ "Reserved 0x34", /* 0x34 */
+ "Reserved 0x35", /* 0x35 */
+ "Reserved 0x36", /* 0x36 */
+ "Reserved 0x37", /* 0x37 */
+ "Reserved 0x38", /* 0x38 */
+ "Reserved 0x39", /* 0x39 */
+ "Reserved 0x3a", /* 0x3a */
+ "Reserved 0x3b", /* 0x3b */
+ "Reserved 0x3c", /* 0x3c */
+ "Reserved 0x3d", /* 0x3d */
+ "Reserved 0x3e", /* 0x3e */
+ "Reserved 0x3f", /* 0x3f */
+ "Reserved 0x40", /* 0x40 */
+ "Reserved 0x41", /* 0x41 */
+ "Reserved 0x42", /* 0x42 */
+ "Reserved 0x43", /* 0x43 */
+ "Reserved 0x44", /* 0x44 */
+ "Reserved 0x45", /* 0x45 */
+ "Reserved 0x46", /* 0x46 */
+ "Reserved 0x47", /* 0x47 */
+ "Reserved 0x48", /* 0x48 */
+ "Reserved 0x49", /* 0x49 */
+ "Reserved 0x4a", /* 0x4a */
+ "Reserved 0x4b", /* 0x4b */
+ "Reserved 0x4c", /* 0x4c */
+ "Reserved 0x4d", /* 0x4d */
+ "Reserved 0x4e", /* 0x4e */
+ "Reserved 0x4f", /* 0x4f */
+ "Reserved 0x50", /* 0x50 */
+ "Reserved 0x51", /* 0x51 */
+ "Reserved 0x52", /* 0x52 */
+ "Reserved 0x53", /* 0x53 */
+ "Reserved 0x54", /* 0x54 */
+ "Reserved 0x55", /* 0x55 */
+ "Reserved 0x56", /* 0x56 */
+ "Reserved 0x57", /* 0x57 */
+ "Reserved 0x58", /* 0x58 */
+ "Reserved 0x59", /* 0x59 */
+ "Reserved 0x5a", /* 0x5a */
+ "Reserved 0x5b", /* 0x5b */
+ "Reserved 0x5c", /* 0x5c */
+ "Reserved 0x5d", /* 0x5d */
+ "Reserved 0x5e", /* 0x5e */
+ "Reserved 0x5f", /* 0x5f */
+ "Reserved 0x60", /* 0x60 */
+ "Reserved 0x61", /* 0x61 */
+ "Reserved 0x62", /* 0x62 */
+ "Reserved 0x63", /* 0x63 */
+ "Reserved 0x64", /* 0x64 */
+ "Reserved 0x65", /* 0x65 */
+ "Reserved 0x66", /* 0x66 */
+ "Reserved 0x67", /* 0x67 */
+ "Reserved 0x68", /* 0x68 */
+ "Reserved 0x69", /* 0x69 */
+ "Reserved 0x6a", /* 0x6a */
+ "Reserved 0x6b", /* 0x6b */
+ "Reserved 0x6c", /* 0x6c */
+ "Reserved 0x6d", /* 0x6d */
+ "Reserved 0x6e", /* 0x6e */
+ "Reserved 0x6f", /* 0x6f */
+ "Reserved 0x70", /* 0x70 */
+ "Reserved 0x71", /* 0x71 */
+ "Reserved 0x72", /* 0x72 */
+ "Reserved 0x73", /* 0x73 */
+ "Reserved 0x74", /* 0x74 */
+ "Reserved 0x75", /* 0x75 */
+ "Reserved 0x76", /* 0x76 */
+ "Reserved 0x77", /* 0x77 */
+ "Reserved 0x78", /* 0x78 */
+ "Reserved 0x79", /* 0x79 */
+ "Reserved 0x7a", /* 0x7a */
+ "Reserved 0x7b", /* 0x7b */
+ "Reserved 0x7c", /* 0x7c */
+ "Reserved 0x7d", /* 0x7d */
+ "Header Termination 1 IE", /* 0x7e */
+ "Header Termination 2 IE" /* 0x7f */
+};
+
+/* Payload IE Group IDs from Table 7-15 of 802.15.4-2015 */
+static const char *p_ie_names[] = {
+ "ESDU IE", /* 0x00 */
+ "MLME IE", /* 0x01 */
+ "Vendor Specific Nested IE", /* 0x02 */
+ "Multiplexed IE (802.15.9)", /* 0x03 */
+ "Omnibus Payload Group IE", /* 0x04 */
+ "IETF IE", /* 0x05 */
+ "Reserved 0x06", /* 0x06 */
+ "Reserved 0x07", /* 0x07 */
+ "Reserved 0x08", /* 0x08 */
+ "Reserved 0x09", /* 0x09 */
+ "Reserved 0x0a", /* 0x0a */
+ "Reserved 0x0b", /* 0x0b */
+ "Reserved 0x0c", /* 0x0c */
+ "Reserved 0x0d", /* 0x0d */
+ "Reserved 0x0e", /* 0x0e */
+ "List termination" /* 0x0f */
+};
+
+/* Sub-ID for short format from Table 7-16 of 802.15.4-2015 */
+static const char *p_mlme_short_names[] = {
+ "Reserved for long format 0x0", /* 0x00 */
+ "Reserved for long format 0x1", /* 0x01 */
+ "Reserved for long format 0x2", /* 0x02 */
+ "Reserved for long format 0x3", /* 0x03 */
+ "Reserved for long format 0x4", /* 0x04 */
+ "Reserved for long format 0x5", /* 0x05 */
+ "Reserved for long format 0x6", /* 0x06 */
+ "Reserved for long format 0x7", /* 0x07 */
+ "Reserved for long format 0x8", /* 0x08 */
+ "Reserved for long format 0x9", /* 0x09 */
+ "Reserved for long format 0xa", /* 0x0a */
+ "Reserved for long format 0xb", /* 0x0b */
+ "Reserved for long format 0xc", /* 0x0c */
+ "Reserved for long format 0xd", /* 0x0d */
+ "Reserved for long format 0xe", /* 0x0e */
+ "Reserved for long format 0xf", /* 0x0f */
+ "Reserved 0x10", /* 0x10 */
+ "Reserved 0x11", /* 0x11 */
+ "Reserved 0x12", /* 0x12 */
+ "Reserved 0x13", /* 0x13 */
+ "Reserved 0x14", /* 0x14 */
+ "Reserved 0x15", /* 0x15 */
+ "Reserved 0x16", /* 0x16 */
+ "Reserved 0x17", /* 0x17 */
+ "Reserved 0x18", /* 0x18 */
+ "Reserved 0x19", /* 0x19 */
+ "TSCH Synchronization IE", /* 0x1a */
+ "TSCH Slotframe and Link IE", /* 0x1b */
+ "TSCH Timeslot IE", /* 0x1c */
+ "Hopping timing IE", /* 0x1d */
+ "Enhanced Beacon Filter IE", /* 0x1e */
+ "MAC Metrics IE", /* 0x1f */
+ "All MAC Metrics IE", /* 0x20 */
+ "Coexistence Specification IE", /* 0x21 */
+ "SUN Device Capabilities IE", /* 0x22 */
+ "SUN FSK Generic PHY IE", /* 0x23 */
+ "Mode Switch Parameter IE", /* 0x24 */
+ "PHY Parameter Change IE", /* 0x25 */
+ "O-QPSK PHY Mode IE", /* 0x26 */
+ "PCA Allocation IE", /* 0x27 */
+ "LECIM DSSS Operating Mode IE", /* 0x28 */
+ "LECIM FSK Operating Mode IE", /* 0x29 */
+ "Reserved 0x2a", /* 0x2a */
+ "TVWS PHY Operating Mode Description IE", /* 0x2b */
+ "TVWS Device Capabilities IE", /* 0x2c */
+ "TVWS Device Category IE", /* 0x2d */
+ "TVWS Device Identiication IE", /* 0x2e */
+ "TVWS Device Location IE", /* 0x2f */
+ "TVWS Channel Information Query IE", /* 0x30 */
+ "TVWS Channel Information Source IE", /* 0x31 */
+ "CTM IE", /* 0x32 */
+ "Timestamp IE", /* 0x33 */
+ "Timestamp Difference IE", /* 0x34 */
+ "TMCTP Specification IE", /* 0x35 */
+ "RCC PHY Operating Mode IE", /* 0x36 */
+ "Reserved 0x37", /* 0x37 */
+ "Reserved 0x38", /* 0x38 */
+ "Reserved 0x39", /* 0x39 */
+ "Reserved 0x3a", /* 0x3a */
+ "Reserved 0x3b", /* 0x3b */
+ "Reserved 0x3c", /* 0x3c */
+ "Reserved 0x3d", /* 0x3d */
+ "Reserved 0x3e", /* 0x3e */
+ "Reserved 0x3f", /* 0x3f */
+ "Reserved 0x40", /* 0x40 */
+ "Reserved 0x41", /* 0x41 */
+ "Reserved 0x42", /* 0x42 */
+ "Reserved 0x43", /* 0x43 */
+ "Reserved 0x44", /* 0x44 */
+ "Reserved 0x45", /* 0x45 */
+ "Reserved 0x46", /* 0x46 */
+ "Reserved 0x47", /* 0x47 */
+ "Reserved 0x48", /* 0x48 */
+ "Reserved 0x49", /* 0x49 */
+ "Reserved 0x4a", /* 0x4a */
+ "Reserved 0x4b", /* 0x4b */
+ "Reserved 0x4c", /* 0x4c */
+ "Reserved 0x4d", /* 0x4d */
+ "Reserved 0x4e", /* 0x4e */
+ "Reserved 0x4f", /* 0x4f */
+ "Reserved 0x50", /* 0x50 */
+ "Reserved 0x51", /* 0x51 */
+ "Reserved 0x52", /* 0x52 */
+ "Reserved 0x53", /* 0x53 */
+ "Reserved 0x54", /* 0x54 */
+ "Reserved 0x55", /* 0x55 */
+ "Reserved 0x56", /* 0x56 */
+ "Reserved 0x57", /* 0x57 */
+ "Reserved 0x58", /* 0x58 */
+ "Reserved 0x59", /* 0x59 */
+ "Reserved 0x5a", /* 0x5a */
+ "Reserved 0x5b", /* 0x5b */
+ "Reserved 0x5c", /* 0x5c */
+ "Reserved 0x5d", /* 0x5d */
+ "Reserved 0x5e", /* 0x5e */
+ "Reserved 0x5f", /* 0x5f */
+ "Reserved 0x60", /* 0x60 */
+ "Reserved 0x61", /* 0x61 */
+ "Reserved 0x62", /* 0x62 */
+ "Reserved 0x63", /* 0x63 */
+ "Reserved 0x64", /* 0x64 */
+ "Reserved 0x65", /* 0x65 */
+ "Reserved 0x66", /* 0x66 */
+ "Reserved 0x67", /* 0x67 */
+ "Reserved 0x68", /* 0x68 */
+ "Reserved 0x69", /* 0x69 */
+ "Reserved 0x6a", /* 0x6a */
+ "Reserved 0x6b", /* 0x6b */
+ "Reserved 0x6c", /* 0x6c */
+ "Reserved 0x6d", /* 0x6d */
+ "Reserved 0x6e", /* 0x6e */
+ "Reserved 0x6f", /* 0x6f */
+ "Reserved 0x70", /* 0x70 */
+ "Reserved 0x71", /* 0x71 */
+ "Reserved 0x72", /* 0x72 */
+ "Reserved 0x73", /* 0x73 */
+ "Reserved 0x74", /* 0x74 */
+ "Reserved 0x75", /* 0x75 */
+ "Reserved 0x76", /* 0x76 */
+ "Reserved 0x77", /* 0x77 */
+ "Reserved 0x78", /* 0x78 */
+ "Reserved 0x79", /* 0x79 */
+ "Reserved 0x7a", /* 0x7a */
+ "Reserved 0x7b", /* 0x7b */
+ "Reserved 0x7c", /* 0x7c */
+ "Reserved 0x7d", /* 0x7d */
+ "Reserved 0x7e", /* 0x7e */
+ "Reserved 0x7f" /* 0x7f */
+};
+
+/* Sub-ID for long format from Table 7-17 of 802.15.4-2015 */
+static const char *p_mlme_long_names[] = {
+ "Reserved 0x00", /* 0x00 */
+ "Reserved 0x01", /* 0x01 */
+ "Reserved 0x02", /* 0x02 */
+ "Reserved 0x03", /* 0x03 */
+ "Reserved 0x04", /* 0x04 */
+ "Reserved 0x05", /* 0x05 */
+ "Reserved 0x06", /* 0x06 */
+ "Reserved 0x07", /* 0x07 */
+ "Vendor Specific MLME Nested IE", /* 0x08 */
+ "Channel Hopping IE", /* 0x09 */
+ "Reserved 0x0a", /* 0x0a */
+ "Reserved 0x0b", /* 0x0b */
+ "Reserved 0x0c", /* 0x0c */
+ "Reserved 0x0d", /* 0x0d */
+ "Reserved 0x0e", /* 0x0e */
+ "Reserved 0x0f" /* 0x0f */
+};
+
+/* MAC commands from Table 7-49 of 802.15.4-2015 */
+static const char *mac_c_names[] = {
+ "Reserved 0x00", /* 0x00 */
+ "Association Request command", /* 0x01 */
+ "Association Response command", /* 0x02 */
+ "Disassociation Notification command", /* 0x03 */
+ "Data Request command", /* 0x04 */
+ "PAN ID Conflict Notification command", /* 0x05 */
+ "Orphan Notification command", /* 0x06 */
+ "Beacon Request command", /* 0x07 */
+ "Coordinator realignment command", /* 0x08 */
+ "GTS request command", /* 0x09 */
+ "TRLE Management Request command", /* 0x0a */
+ "TRLE Management Response command", /* 0x0b */
+ "Reserved 0x0c", /* 0x0c */
+ "Reserved 0x0d", /* 0x0d */
+ "Reserved 0x0e", /* 0x0e */
+ "Reserved 0x0f", /* 0x0f */
+ "Reserved 0x10", /* 0x10 */
+ "Reserved 0x11", /* 0x11 */
+ "Reserved 0x12", /* 0x12 */
+ "DSME Association Request command", /* 0x13 */
+ "DSME Association Response command", /* 0x14 */
+ "DSME GTS Request command", /* 0x15 */
+ "DSME GTS Response command", /* 0x16 */
+ "DSME GTS Notify command", /* 0x17 */
+ "DSME Information Request command", /* 0x18 */
+ "DSME Information Response command", /* 0x19 */
+ "DSME Beacon Allocation Notification command", /* 0x1a */
+ "DSME Beacon Collision Notification command", /* 0x1b */
+ "DSME Link Report command", /* 0x1c */
+ "Reserved 0x1d", /* 0x1d */
+ "Reserved 0x1e", /* 0x1e */
+ "Reserved 0x1f", /* 0x1f */
+ "RIT Data Request command", /* 0x20 */
+ "DBS Request command", /* 0x21 */
+ "DBS Response command", /* 0x22 */
+ "RIT Data Response command", /* 0x23 */
+ "Vendor Specific command", /* 0x24 */
+ "Reserved 0x25", /* 0x25 */
+ "Reserved 0x26", /* 0x26 */
+ "Reserved 0x27", /* 0x27 */
+ "Reserved 0x28", /* 0x28 */
+ "Reserved 0x29", /* 0x29 */
+ "Reserved 0x2a", /* 0x2a */
+ "Reserved 0x2b", /* 0x2b */
+ "Reserved 0x2c", /* 0x2c */
+ "Reserved 0x2d", /* 0x2d */
+ "Reserved 0x2e", /* 0x2e */
+ "Reserved 0x2f" /* 0x2f */
+};
+
+/*
+ * Frame Control subfields.
+ */
+#define FC_FRAME_TYPE(fc) ((fc) & 0x7)
+#define FC_FRAME_VERSION(fc) (((fc) >> 12) & 0x3)
+
+#define FC_ADDRESSING_MODE_NONE 0x00
+#define FC_ADDRESSING_MODE_RESERVED 0x01
+#define FC_ADDRESSING_MODE_SHORT 0x02
+#define FC_ADDRESSING_MODE_LONG 0x03
+
+/*
+ * IEEE 802.15.4 CRC 16 function. This is using CCITT polynomical of 0x1021,
+ * but the initial value is 0, and the bits are reversed for both in and out.
+ * See secton 7.2.10 of 802.15.4-2015 for more information.
+ */
+static uint16_t
+ieee802_15_4_crc16(netdissect_options *ndo, const u_char *p,
+ u_int data_len)
+{
+ uint16_t crc;
+ u_char x, y;
+
+ crc = 0x0000; /* Note, initial value is 0x0000 not 0xffff. */
+
+ while (data_len != 0){
+ y = GET_U_1(p);
+ p++;
+ /* Reverse bits on input */
+ y = (((y & 0xaa) >> 1) | ((y & 0x55) << 1));
+ y = (((y & 0xcc) >> 2) | ((y & 0x33) << 2));
+ y = (((y & 0xf0) >> 4) | ((y & 0x0f) << 4));
+ /* Update CRC */
+ x = crc >> 8 ^ y;
+ x ^= x >> 4;
+ crc = ((uint16_t)(crc << 8)) ^
+ ((uint16_t)(x << 12)) ^
+ ((uint16_t)(x << 5)) ^
+ ((uint16_t)x);
+ data_len--;
+ }
+ /* Reverse bits on output */
+ crc = (((crc & 0xaaaa) >> 1) | ((crc & 0x5555) << 1));
+ crc = (((crc & 0xcccc) >> 2) | ((crc & 0x3333) << 2));
+ crc = (((crc & 0xf0f0) >> 4) | ((crc & 0x0f0f) << 4));
+ crc = (((crc & 0xff00) >> 8) | ((crc & 0x00ff) << 8));
+ return crc;
+}
+
+/*
+ * Reverses the bits of the 32-bit word.
+ */
+static uint32_t
+ieee802_15_4_reverse32(uint32_t x)
+{
+ x = ((x & 0x55555555) << 1) | ((x >> 1) & 0x55555555);
+ x = ((x & 0x33333333) << 2) | ((x >> 2) & 0x33333333);
+ x = ((x & 0x0F0F0F0F) << 4) | ((x >> 4) & 0x0F0F0F0F);
+ x = (x << 24) | ((x & 0xFF00) << 8) |
+ ((x >> 8) & 0xFF00) | (x >> 24);
+ return x;
+}
+
+/*
+ * IEEE 802.15.4 CRC 32 function. This is using ANSI X3.66-1979 polynomical of
+ * 0x04C11DB7, but the initial value is 0, and the bits are reversed for both
+ * in and out. See secton 7.2.10 of 802.15.4-2015 for more information.
+ */
+static uint32_t
+ieee802_15_4_crc32(netdissect_options *ndo, const u_char *p,
+ u_int data_len)
+{
+ uint32_t crc, byte;
+ int b;
+
+ crc = 0x00000000; /* Note, initial value is 0x00000000 not 0xffffffff */
+
+ while (data_len != 0){
+ byte = GET_U_1(p);
+ p++;
+ /* Reverse bits on input */
+ byte = ieee802_15_4_reverse32(byte);
+ /* Update CRC */
+ for(b = 0; b <= 7; b++) {
+ if ((int) (crc ^ byte) < 0)
+ crc = (crc << 1) ^ 0x04C11DB7;
+ else
+ crc = crc << 1;
+ byte = byte << 1;
+ }
+ data_len--;
+ }
+ /* Reverse bits on output */
+ crc = ieee802_15_4_reverse32(crc);
+ return crc;
+}
+
+/*
+ * Find out the address length based on the address type. See table 7-3 of
+ * 802.15.4-2015. Returns the address length.
+ */
+static int
+ieee802_15_4_addr_len(uint16_t addr_type)
+{
+ switch (addr_type) {
+ case FC_ADDRESSING_MODE_NONE: /* None. */
+ return 0;
+ break;
+ case FC_ADDRESSING_MODE_RESERVED: /* Reserved, there used to be 8-bit
+ * address type in one amendment, but
+ * that and the feature using it was
+ * removed during 802.15.4-2015
+ * maintenance process. */
+ return -1;
+ break;
+ case FC_ADDRESSING_MODE_SHORT: /* Short. */
+ return 2;
+ break;
+ case FC_ADDRESSING_MODE_LONG: /* Extended. */
+ return 8;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Print out the ieee 802.15.4 address.
+ */
+static void
+ieee802_15_4_print_addr(netdissect_options *ndo, const u_char *p,
+ int dst_addr_len)
+{
+ switch (dst_addr_len) {
+ case 0:
+ ND_PRINT("none");
+ break;
+ case 2:
+ ND_PRINT("%04x", GET_LE_U_2(p));
+ break;
+ case 8:
+ ND_PRINT("%s", GET_LE64ADDR_STRING(p));
+ break;
+ }
+}
+
+/*
+ * Beacon frame superframe specification structure. Used in the old Beacon
+ * frames, and in the DSME PAN Descriptor IE. See section 7.3.1.3 of the
+ * 802.15.4-2015.
+ */
+static void
+ieee802_15_4_print_superframe_specification(netdissect_options *ndo,
+ uint16_t ss)
+{
+ if (ndo->ndo_vflag < 1) {
+ return;
+ }
+ ND_PRINT("\n\tBeacon order = %d, Superframe order = %d, ",
+ (ss & 0xf), ((ss >> 4) & 0xf));
+ ND_PRINT("Final CAP Slot = %d",
+ ((ss >> 8) & 0xf));
+ if (CHECK_BIT(ss, 12)) { ND_PRINT(", BLE enabled"); }
+ if (CHECK_BIT(ss, 14)) { ND_PRINT(", PAN Coordinator"); }
+ if (CHECK_BIT(ss, 15)) { ND_PRINT(", Association Permit"); }
+}
+
+/*
+ * Beacon frame gts info structure. Used in the old Beacon frames, and
+ * in the DSME PAN Descriptor IE. See section 7.3.1.4 of 802.15.4-2015.
+ *
+ * Returns number of byts consumed from the packet or -1 in case of error.
+ */
+static int
+ieee802_15_4_print_gts_info(netdissect_options *ndo,
+ const u_char *p,
+ u_int data_len)
+{
+ uint8_t gts_spec, gts_cnt;
+ u_int len;
+ int i;
+
+ gts_spec = GET_U_1(p);
+ gts_cnt = gts_spec & 0x7;
+
+ if (gts_cnt == 0) {
+ if (ndo->ndo_vflag > 0) {
+ ND_PRINT("\n\tGTS Descriptor Count = %d, ", gts_cnt);
+ }
+ return 1;
+ }
+ len = 1 + 1 + gts_cnt * 3;
+
+ if (data_len < len) {
+ ND_PRINT(" [ERROR: Truncated GTS Info List]");
+ return -1;
+ }
+ if (ndo->ndo_vflag < 2) {
+ return len;
+ }
+ ND_PRINT("GTS Descriptor Count = %d, ", gts_cnt);
+ ND_PRINT("GTS Directions Mask = %02x, [ ",
+ GET_U_1(p + 1) & 0x7f);
+
+ for(i = 0; i < gts_cnt; i++) {
+ ND_PRINT("[ ");
+ ieee802_15_4_print_addr(ndo, p + 2 + i * 3, 2);
+ ND_PRINT(", Start slot = %d, Length = %d ] ",
+ GET_U_1(p + 2 + i * 3 + 1) & 0x0f,
+ (GET_U_1(p + 2 + i * 3 + 1) >> 4) & 0x0f);
+ }
+ ND_PRINT("]");
+ return len;
+}
+
+/*
+ * Beacon frame pending address structure. Used in the old Beacon frames, and
+ * in the DSME PAN Descriptor IE. See section 7.3.1.5 of 802.15.4-2015.
+ *
+ * Returns number of byts consumed from the packet or -1 in case of error.
+ */
+static int16_t
+ieee802_15_4_print_pending_addresses(netdissect_options *ndo,
+ const u_char *p,
+ u_int data_len)
+{
+ uint8_t pas, s_cnt, e_cnt, len, i;
+
+ pas = GET_U_1(p);
+ s_cnt = pas & 0x7;
+ e_cnt = (pas >> 4) & 0x7;
+ len = 1 + s_cnt * 2 + e_cnt * 8;
+ if (ndo->ndo_vflag > 0) {
+ ND_PRINT("\n\tPending address list, "
+ "# short addresses = %d, # extended addresses = %d",
+ s_cnt, e_cnt);
+ }
+ if (data_len < len) {
+ ND_PRINT(" [ERROR: Pending address list truncated]");
+ return -1;
+ }
+ if (ndo->ndo_vflag < 2) {
+ return len;
+ }
+ if (s_cnt != 0) {
+ ND_PRINT(", Short address list = [ ");
+ for(i = 0; i < s_cnt; i++) {
+ ieee802_15_4_print_addr(ndo, p + 1 + i * 2, 2);
+ ND_PRINT(" ");
+ }
+ ND_PRINT("]");
+ }
+ if (e_cnt != 0) {
+ ND_PRINT(", Extended address list = [ ");
+ for(i = 0; i < e_cnt; i++) {
+ ieee802_15_4_print_addr(ndo, p + 1 + s_cnt * 2 +
+ i * 8, 8);
+ ND_PRINT(" ");
+ }
+ ND_PRINT("]");
+ }
+ return len;
+}
+
+/*
+ * Print header ie content.
+ */
+static void
+ieee802_15_4_print_header_ie(netdissect_options *ndo,
+ const u_char *p,
+ uint16_t ie_len,
+ int element_id)
+{
+ int i;
+
+ switch (element_id) {
+ case 0x00: /* Vendor Specific Header IE */
+ if (ie_len < 3) {
+ ND_PRINT("[ERROR: Vendor OUI missing]");
+ } else {
+ ND_PRINT("OUI = 0x%02x%02x%02x, ", GET_U_1(p),
+ GET_U_1(p + 1), GET_U_1(p + 2));
+ ND_PRINT("Data = ");
+ for(i = 3; i < ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ }
+ break;
+ case 0x1a: /* LE CSL IE */
+ if (ie_len < 4) {
+ ND_PRINT("[ERROR: Truncated CSL IE]");
+ } else {
+ ND_PRINT("CSL Phase = %d, CSL Period = %d",
+ GET_LE_U_2(p), GET_LE_U_2(p + 2));
+ if (ie_len >= 6) {
+ ND_PRINT(", Rendezvous time = %d",
+ GET_LE_U_2(p + 4));
+ }
+ if (ie_len != 4 && ie_len != 6) {
+ ND_PRINT(" [ERROR: CSL IE length wrong]");
+ }
+ }
+ break;
+ case 0x1b: /* LE RIT IE */
+ if (ie_len < 4) {
+ ND_PRINT("[ERROR: Truncated RIT IE]");
+ } else {
+ ND_PRINT("Time to First Listen = %d, # of Repeat Listen = %d, Repeat Listen Interval = %d",
+ GET_U_1(p),
+ GET_U_1(p + 1),
+ GET_LE_U_2(p + 2));
+ }
+ break;
+ case 0x1c: /* DSME PAN Descriptor IE */
+ /*FALLTHROUGH*/
+ case 0x21: /* Extended DSME PAN descriptor IE */
+ if (ie_len < 2) {
+ ND_PRINT("[ERROR: Truncated DSME PAN IE]");
+ } else {
+ uint16_t ss, ptr, ulen;
+ int16_t len;
+ int hopping_present;
+
+ hopping_present = 0;
+
+ ss = GET_LE_U_2(p);
+ ieee802_15_4_print_superframe_specification(ndo, ss);
+ if (ie_len < 3) {
+ ND_PRINT("[ERROR: Truncated before pending addresses field]");
+ break;
+ }
+ ptr = 2;
+ len = ieee802_15_4_print_pending_addresses(ndo,
+ p + ptr,
+ ie_len -
+ ptr);
+ if (len < 0) {
+ break;
+ }
+ ptr += len;
+
+ if (element_id == 0x21) {
+ /* Extended version. */
+ if (ie_len < ptr + 2) {
+ ND_PRINT("[ERROR: Truncated before DSME Superframe Specification]");
+ break;
+ }
+ ss = GET_LE_U_2(p + ptr);
+ ptr += 2;
+ ND_PRINT("Multi-superframe Order = %d", ss & 0xff);
+ ND_PRINT(", %s", ((ss & 0x100) ?
+ "Channel hopping mode" :
+ "Channel adaptation mode"));
+ if (ss & 0x400) {
+ ND_PRINT(", CAP reduction enabled");
+ }
+ if (ss & 0x800) {
+ ND_PRINT(", Deferred beacon enabled");
+ }
+ if (ss & 0x1000) {
+ ND_PRINT(", Hopping Sequence Present");
+ hopping_present = 1;
+ }
+ } else {
+ if (ie_len < ptr + 1) {
+ ND_PRINT("[ERROR: Truncated before DSME Superframe Specification]");
+ break;
+ }
+ ss = GET_U_1(p + ptr);
+ ptr++;
+ ND_PRINT("Multi-superframe Order = %d",
+ ss & 0x0f);
+ ND_PRINT(", %s", ((ss & 0x10) ?
+ "Channel hopping mode" :
+ "Channel adaptation mode"));
+ if (ss & 0x40) {
+ ND_PRINT(", CAP reduction enabled");
+ }
+ if (ss & 0x80) {
+ ND_PRINT(", Deferred beacon enabled");
+ }
+ }
+ if (ie_len < ptr + 8) {
+ ND_PRINT(" [ERROR: Truncated before Time synchronization specification]");
+ break;
+ }
+ ND_PRINT("Beacon timestamp = %" PRIu64 ", offset = %d",
+ GET_LE_U_6(p + ptr),
+ GET_LE_U_2(p + ptr + 6));
+ ptr += 8;
+ if (ie_len < ptr + 4) {
+ ND_PRINT(" [ERROR: Truncated before Beacon Bitmap]");
+ break;
+ }
+
+ ulen = GET_LE_U_2(p + ptr + 2);
+ ND_PRINT("SD Index = %d, Bitmap len = %d, ",
+ GET_LE_U_2(p + ptr), ulen);
+ ptr += 4;
+ if (ie_len < ptr + ulen) {
+ ND_PRINT(" [ERROR: Truncated in SD bitmap]");
+ break;
+ }
+ ND_PRINT(" SD Bitmap = ");
+ for(i = 0; i < ulen; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + ptr + i));
+ }
+ ptr += ulen;
+
+ if (ie_len < ptr + 5) {
+ ND_PRINT(" [ERROR: Truncated before Channel hopping specification]");
+ break;
+ }
+
+ ulen = GET_LE_U_2(p + ptr + 4);
+ ND_PRINT("Hopping Seq ID = %d, PAN Coordinator BSN = %d, "
+ "Channel offset = %d, Bitmap length = %d, ",
+ GET_U_1(p + ptr),
+ GET_U_1(p + ptr + 1),
+ GET_LE_U_2(p + ptr + 2),
+ ulen);
+ ptr += 5;
+ if (ie_len < ptr + ulen) {
+ ND_PRINT(" [ERROR: Truncated in Channel offset bitmap]");
+ break;
+ }
+ ND_PRINT(" Channel offset bitmap = ");
+ for(i = 0; i < ulen; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + ptr + i));
+ }
+ ptr += ulen;
+ if (hopping_present) {
+ if (ie_len < ptr + 1) {
+ ND_PRINT(" [ERROR: Truncated in Hopping Sequence length]");
+ break;
+ }
+ ulen = GET_U_1(p + ptr);
+ ptr++;
+ ND_PRINT("Hopping Seq length = %d [ ", ulen);
+
+ /* The specification is not clear how the
+ hopping sequence is encoded, I assume two
+ octet unsigned integers for each channel. */
+
+ if (ie_len < ptr + ulen * 2) {
+ ND_PRINT(" [ERROR: Truncated in Channel offset bitmap]");
+ break;
+ }
+ for(i = 0; i < ulen; i++) {
+ ND_PRINT("%02x ",
+ GET_LE_U_2(p + ptr + i * 2));
+ }
+ ND_PRINT("]");
+ ptr += ulen * 2;
+ }
+ }
+ break;
+ case 0x1d: /* Rendezvous Tome IE */
+ if (ie_len != 4) {
+ ND_PRINT("[ERROR: Length != 2]");
+ } else {
+ uint16_t r_time, w_u_interval;
+ r_time = GET_LE_U_2(p);
+ w_u_interval = GET_LE_U_2(p + 2);
+
+ ND_PRINT("Rendezvous time = %d, Wake-up Interval = %d",
+ r_time, w_u_interval);
+ }
+ break;
+ case 0x1e: /* Time correction IE */
+ if (ie_len != 2) {
+ ND_PRINT("[ERROR: Length != 2]");
+ } else {
+ uint16_t val;
+ int16_t timecorr;
+
+ val = GET_LE_U_2(p);
+ if (val & 0x8000) { ND_PRINT("Negative "); }
+ val &= 0xfff;
+ val <<= 4;
+ timecorr = val;
+ timecorr >>= 4;
+
+ ND_PRINT("Ack time correction = %d, ", timecorr);
+ }
+ break;
+ case 0x22: /* Frament Sequence Content Description IE */
+ /* XXX Not implemented */
+ case 0x23: /* Simplified Superframe Specification IE */
+ /* XXX Not implemented */
+ case 0x24: /* Simplified GTS Specification IE */
+ /* XXX Not implemented */
+ case 0x25: /* LECIM Capabilities IE */
+ /* XXX Not implemented */
+ case 0x26: /* TRLE Descriptor IE */
+ /* XXX Not implemented */
+ case 0x27: /* RCC Capabilities IE */
+ /* XXX Not implemented */
+ case 0x28: /* RCCN Descriptor IE */
+ /* XXX Not implemented */
+ case 0x29: /* Global Time IE */
+ /* XXX Not implemented */
+ case 0x2b: /* DA IE */
+ /* XXX Not implemented */
+ default:
+ ND_PRINT("IE Data = ");
+ for(i = 0; i < ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ break;
+ }
+}
+
+/*
+ * Parse and print Header IE list. See 7.4.2 of 802.15.4-2015 for
+ * more information.
+ *
+ * Returns number of byts consumed from the packet or -1 in case of error.
+ */
+static int
+ieee802_15_4_print_header_ie_list(netdissect_options *ndo,
+ const u_char *p,
+ u_int caplen,
+ int *payload_ie_present)
+{
+ int len, ie, element_id, i;
+ uint16_t ie_len;
+
+ *payload_ie_present = 0;
+ len = 0;
+ do {
+ if (caplen < 2) {
+ ND_PRINT("[ERROR: Truncated header IE]");
+ return -1;
+ }
+ /* Extract IE Header */
+ ie = GET_LE_U_2(p);
+ if (CHECK_BIT(ie, 15)) {
+ ND_PRINT("[ERROR: Header IE with type 1] ");
+ }
+ /* Get length and Element ID */
+ ie_len = ie & 0x7f;
+ element_id = (ie >> 7) & 0xff;
+ if (element_id > 127) {
+ ND_PRINT("Reserved Element ID %02x, length = %d ",
+ element_id, ie_len);
+ } else {
+ if (ie_len == 0) {
+ ND_PRINT("\n\t%s [", h_ie_names[element_id]);
+ } else {
+ ND_PRINT("\n\t%s [ length = %d, ",
+ h_ie_names[element_id], ie_len);
+ }
+ }
+ if (caplen < ie_len) {
+ ND_PRINT("[ERROR: Truncated IE data]");
+ return -1;
+ }
+ /* Skip header */
+ p += 2;
+
+ /* Parse and print content. */
+ if (ndo->ndo_vflag > 3 && ie_len != 0) {
+ ieee802_15_4_print_header_ie(ndo, p,
+ ie_len, element_id);
+ } else {
+ if (ie_len != 0) {
+ ND_PRINT("IE Data = ");
+ for(i = 0; i < ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ }
+ }
+ ND_PRINT("] ");
+ len += 2 + ie_len;
+ p += ie_len;
+ caplen -= 2 + ie_len;
+ if (element_id == 0x7e) {
+ *payload_ie_present = 1;
+ break;
+ }
+ if (element_id == 0x7f) {
+ break;
+ }
+ } while (caplen > 0);
+ return len;
+}
+
+/*
+ * Print MLME ie content.
+ */
+static void
+ieee802_15_4_print_mlme_ie(netdissect_options *ndo,
+ const u_char *p,
+ uint16_t sub_ie_len,
+ int sub_id)
+{
+ int i, j;
+ uint16_t len;
+
+ /* Note, as there is no overlap with the long and short
+ MLME sub IDs, we can just use one switch here. */
+ switch (sub_id) {
+ case 0x08: /* Vendor Specific Nested IE */
+ if (sub_ie_len < 3) {
+ ND_PRINT("[ERROR: Vendor OUI missing]");
+ } else {
+ ND_PRINT("OUI = 0x%02x%02x%02x, ",
+ GET_U_1(p),
+ GET_U_1(p + 1),
+ GET_U_1(p + 2));
+ ND_PRINT("Data = ");
+ for(i = 3; i < sub_ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ }
+ break;
+ case 0x09: /* Channel Hopping IE */
+ if (sub_ie_len < 1) {
+ ND_PRINT("[ERROR: Hopping sequence ID missing]");
+ } else if (sub_ie_len == 1) {
+ ND_PRINT("Hopping Sequence ID = %d", GET_U_1(p));
+ p++;
+ sub_ie_len--;
+ } else {
+ uint16_t channel_page, number_of_channels;
+
+ ND_PRINT("Hopping Sequence ID = %d", GET_U_1(p));
+ p++;
+ sub_ie_len--;
+ if (sub_ie_len < 7) {
+ ND_PRINT("[ERROR: IE truncated]");
+ break;
+ }
+ channel_page = GET_U_1(p);
+ number_of_channels = GET_LE_U_2(p + 1);
+ ND_PRINT("Channel Page = %d, Number of Channels = %d, ",
+ channel_page, number_of_channels);
+ ND_PRINT("Phy Configuration = 0x%08x, ",
+ GET_LE_U_4(p + 3));
+ p += 7;
+ sub_ie_len -= 7;
+ if (channel_page == 9 || channel_page == 10) {
+ len = (number_of_channels + 7) / 8;
+ if (sub_ie_len < len) {
+ ND_PRINT("[ERROR: IE truncated]");
+ break;
+ }
+ ND_PRINT("Extended bitmap = 0x");
+ for(i = 0; i < len; i++) {
+ ND_PRINT("%02x", GET_U_1(p + i));
+ }
+ ND_PRINT(", ");
+ p += len;
+ sub_ie_len -= len;
+ }
+ if (sub_ie_len < 2) {
+ ND_PRINT("[ERROR: IE truncated]");
+ break;
+ }
+ len = GET_LE_U_2(p);
+ p += 2;
+ sub_ie_len -= 2;
+ ND_PRINT("Hopping Seq length = %d [ ", len);
+
+ if (sub_ie_len < len * 2) {
+ ND_PRINT(" [ERROR: IE truncated]");
+ break;
+ }
+ for(i = 0; i < len; i++) {
+ ND_PRINT("%02x ", GET_LE_U_2(p + i * 2));
+ }
+ ND_PRINT("]");
+ p += len * 2;
+ sub_ie_len -= len * 2;
+ if (sub_ie_len < 2) {
+ ND_PRINT("[ERROR: IE truncated]");
+ break;
+ }
+ ND_PRINT("Current hop = %d", GET_LE_U_2(p));
+ }
+
+ break;
+ case 0x1a: /* TSCH Synchronization IE. */
+ if (sub_ie_len < 6) {
+ ND_PRINT("[ERROR: Length != 6]");
+ }
+ ND_PRINT("ASN = %010" PRIx64 ", Join Metric = %d ",
+ GET_LE_U_5(p), GET_U_1(p + 5));
+ break;
+ case 0x1b: /* TSCH Slotframe and Link IE. */
+ {
+ int sf_num, off, links, opts;
+
+ if (sub_ie_len < 1) {
+ ND_PRINT("[ERROR: Truncated IE]");
+ break;
+ }
+ sf_num = GET_U_1(p);
+ ND_PRINT("Slotframes = %d ", sf_num);
+ off = 1;
+ for(i = 0; i < sf_num; i++) {
+ if (sub_ie_len < off + 4) {
+ ND_PRINT("[ERROR: Truncated IE before slotframes]");
+ break;
+ }
+ links = GET_U_1(p + off + 3);
+ ND_PRINT("\n\t\t\t[ Handle %d, size = %d, links = %d ",
+ GET_U_1(p + off),
+ GET_LE_U_2(p + off + 1),
+ links);
+ off += 4;
+ for(j = 0; j < links; j++) {
+ if (sub_ie_len < off + 5) {
+ ND_PRINT("[ERROR: Truncated IE links]");
+ break;
+ }
+ opts = GET_U_1(p + off + 4);
+ ND_PRINT("\n\t\t\t\t[ Timeslot = %d, Offset = %d, Options = ",
+ GET_LE_U_2(p + off),
+ GET_LE_U_2(p + off + 2));
+ if (opts & 0x1) { ND_PRINT("TX "); }
+ if (opts & 0x2) { ND_PRINT("RX "); }
+ if (opts & 0x4) { ND_PRINT("Shared "); }
+ if (opts & 0x8) {
+ ND_PRINT("Timekeeping ");
+ }
+ if (opts & 0x10) {
+ ND_PRINT("Priority ");
+ }
+ off += 5;
+ ND_PRINT("] ");
+ }
+ ND_PRINT("] ");
+ }
+ }
+ break;
+ case 0x1c: /* TSCH Timeslot IE. */
+ if (sub_ie_len == 1) {
+ ND_PRINT("Time slot ID = %d ", GET_U_1(p));
+ } else if (sub_ie_len == 25) {
+ ND_PRINT("Time slot ID = %d, CCA Offset = %d, CCA = %d, TX Offset = %d, RX Offset = %d, RX Ack Delay = %d, TX Ack Delay = %d, RX Wait = %d, Ack Wait = %d, RX TX = %d, Max Ack = %d, Max TX = %d, Time slot Length = %d ",
+ GET_U_1(p),
+ GET_LE_U_2(p + 1),
+ GET_LE_U_2(p + 3),
+ GET_LE_U_2(p + 5),
+ GET_LE_U_2(p + 7),
+ GET_LE_U_2(p + 9),
+ GET_LE_U_2(p + 11),
+ GET_LE_U_2(p + 13),
+ GET_LE_U_2(p + 15),
+ GET_LE_U_2(p + 17),
+ GET_LE_U_2(p + 19),
+ GET_LE_U_2(p + 21),
+ GET_LE_U_2(p + 23));
+ } else if (sub_ie_len == 27) {
+ ND_PRINT("Time slot ID = %d, CCA Offset = %d, CCA = %d, TX Offset = %d, RX Offset = %d, RX Ack Delay = %d, TX Ack Delay = %d, RX Wait = %d, Ack Wait = %d, RX TX = %d, Max Ack = %d, Max TX = %d, Time slot Length = %d ",
+ GET_U_1(p),
+ GET_LE_U_2(p + 1),
+ GET_LE_U_2(p + 3),
+ GET_LE_U_2(p + 5),
+ GET_LE_U_2(p + 7),
+ GET_LE_U_2(p + 9),
+ GET_LE_U_2(p + 11),
+ GET_LE_U_2(p + 13),
+ GET_LE_U_2(p + 15),
+ GET_LE_U_2(p + 17),
+ GET_LE_U_2(p + 19),
+ GET_LE_U_3(p + 21),
+ GET_LE_U_3(p + 24));
+ } else {
+ ND_PRINT("[ERROR: Length not 1, 25, or 27]");
+ ND_PRINT("\n\t\t\tIE Data = ");
+ for(i = 0; i < sub_ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ }
+ break;
+ case 0x1d: /* Hopping timing IE */
+ /* XXX Not implemented */
+ case 0x1e: /* Enhanced Beacon Filter IE */
+ /* XXX Not implemented */
+ case 0x1f: /* MAC Metrics IE */
+ /* XXX Not implemented */
+ case 0x20: /* All MAC Metrics IE */
+ /* XXX Not implemented */
+ case 0x21: /* Coexistence Specification IE */
+ /* XXX Not implemented */
+ case 0x22: /* SUN Device Capabilities IE */
+ /* XXX Not implemented */
+ case 0x23: /* SUN FSK Generic PHY IE */
+ /* XXX Not implemented */
+ case 0x24: /* Mode Switch Parameter IE */
+ /* XXX Not implemented */
+ case 0x25: /* PHY Parameter Change IE */
+ /* XXX Not implemented */
+ case 0x26: /* O-QPSK PHY Mode IE */
+ /* XXX Not implemented */
+ case 0x27: /* PCA Allocation IE */
+ /* XXX Not implemented */
+ case 0x28: /* LECIM DSSS Operating Mode IE */
+ /* XXX Not implemented */
+ case 0x29: /* LECIM FSK Operating Mode IE */
+ /* XXX Not implemented */
+ case 0x2b: /* TVWS PHY Operating Mode Description IE */
+ /* XXX Not implemented */
+ case 0x2c: /* TVWS Device Capabilities IE */
+ /* XXX Not implemented */
+ case 0x2d: /* TVWS Device Category IE */
+ /* XXX Not implemented */
+ case 0x2e: /* TVWS Device Identification IE */
+ /* XXX Not implemented */
+ case 0x2f: /* TVWS Device Location IE */
+ /* XXX Not implemented */
+ case 0x30: /* TVWS Channel Information Query IE */
+ /* XXX Not implemented */
+ case 0x31: /* TVWS Channel Information Source IE */
+ /* XXX Not implemented */
+ case 0x32: /* CTM IE */
+ /* XXX Not implemented */
+ case 0x33: /* Timestamp IE */
+ /* XXX Not implemented */
+ case 0x34: /* Timestamp Difference IE */
+ /* XXX Not implemented */
+ case 0x35: /* TMCTP Specification IE */
+ /* XXX Not implemented */
+ case 0x36: /* TCC PHY Operating Mode IE */
+ /* XXX Not implemented */
+ default:
+ ND_PRINT("IE Data = ");
+ for(i = 0; i < sub_ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ break;
+ }
+}
+
+/*
+ * MLME IE list parsing and printing. See 7.4.3.2 of 802.15.4-2015
+ * for more information.
+ */
+static void
+ieee802_15_4_print_mlme_ie_list(netdissect_options *ndo,
+ const u_char *p,
+ uint16_t ie_len)
+{
+ int ie, sub_id, i, type;
+ uint16_t sub_ie_len;
+
+ do {
+ if (ie_len < 2) {
+ ND_PRINT("[ERROR: Truncated MLME IE]");
+ return;
+ }
+ /* Extract IE header */
+ ie = GET_LE_U_2(p);
+ type = CHECK_BIT(ie, 15);
+ if (type) {
+ /* Long type */
+ sub_ie_len = ie & 0x3ff;
+ sub_id = (ie >> 11) & 0x0f;
+ } else {
+ sub_ie_len = ie & 0xff;
+ sub_id = (ie >> 8) & 0x7f;
+ }
+
+ /* Skip the IE header */
+ p += 2;
+
+ if (type == 0) {
+ ND_PRINT("\n\t\t%s [ length = %d, ",
+ p_mlme_short_names[sub_id], sub_ie_len);
+ } else {
+ ND_PRINT("\n\t\t%s [ length = %d, ",
+ p_mlme_long_names[sub_id], sub_ie_len);
+ }
+
+ if (ie_len < sub_ie_len) {
+ ND_PRINT("[ERROR: Truncated IE data]");
+ return;
+ }
+ if (sub_ie_len != 0) {
+ if (ndo->ndo_vflag > 3) {
+ ieee802_15_4_print_mlme_ie(ndo, p, sub_ie_len, sub_id);
+ } else if (ndo->ndo_vflag > 2) {
+ ND_PRINT("IE Data = ");
+ for(i = 0; i < sub_ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ }
+ }
+ ND_PRINT("] ");
+ p += sub_ie_len;
+ ie_len -= 2 + sub_ie_len;
+ } while (ie_len > 0);
+}
+
+/*
+ * Multiplexd IE (802.15.9) parsing and printing.
+ *
+ * Returns number of bytes consumed from packet or -1 in case of error.
+ */
+static void
+ieee802_15_4_print_mpx_ie(netdissect_options *ndo,
+ const u_char *p,
+ uint16_t ie_len)
+{
+ int transfer_type, tid;
+ int fragment_number, data_start;
+ int i;
+
+ data_start = 0;
+ if (ie_len < 1) {
+ ND_PRINT("[ERROR: Transaction control byte missing]");
+ return;
+ }
+
+ transfer_type = GET_U_1(p) & 0x7;
+ tid = GET_U_1(p) >> 3;
+ switch (transfer_type) {
+ case 0x00: /* Full upper layer frame. */
+ case 0x01: /* Full upper layer frame with small Multiplex ID. */
+ ND_PRINT("Type = Full upper layer fragment%s, ",
+ (transfer_type == 0x01 ?
+ " with small Multiplex ID" : ""));
+ if (transfer_type == 0x00) {
+ if (ie_len < 3) {
+ ND_PRINT("[ERROR: Multiplex ID missing]");
+ return;
+ }
+ data_start = 3;
+ ND_PRINT("tid = 0x%02x, Multiplex ID = 0x%04x, ",
+ tid, GET_LE_U_2(p + 1));
+ } else {
+ data_start = 1;
+ ND_PRINT("Multiplex ID = 0x%04x, ", tid);
+ }
+ break;
+ case 0x02: /* First, or middle, Fragments */
+ case 0x04: /* Last fragment */
+ if (ie_len < 2) {
+ ND_PRINT("[ERROR: fragment number missing]");
+ return;
+ }
+
+ fragment_number = GET_U_1(p + 1);
+ ND_PRINT("Type = %s, tid = 0x%02x, fragment = 0x%02x, ",
+ (transfer_type == 0x02 ?
+ (fragment_number == 0 ?
+ "First fragment" : "Middle fragment") :
+ "Last fragment"), tid,
+ fragment_number);
+ data_start = 2;
+ if (fragment_number == 0) {
+ int total_size, multiplex_id;
+
+ if (ie_len < 6) {
+ ND_PRINT("[ERROR: Total upper layer size or multiplex ID missing]");
+ return;
+ }
+ total_size = GET_LE_U_2(p + 2);
+ multiplex_id = GET_LE_U_2(p + 4);
+ ND_PRINT("Total upper layer size = 0x%04x, Multiplex ID = 0x%04x, ",
+ total_size, multiplex_id);
+ data_start = 6;
+ }
+ break;
+ case 0x06: /* Abort code */
+ if (ie_len == 1) {
+ ND_PRINT("Type = Abort, tid = 0x%02x, no max size given",
+ tid);
+ } else if (ie_len == 3) {
+ ND_PRINT("Type = Abort, tid = 0x%02x, max size = 0x%04x",
+ tid, GET_LE_U_2(p + 1));
+ } else {
+ ND_PRINT("Type = Abort, tid = 0x%02x, invalid length = %d (not 1 or 3)",
+ tid, ie_len);
+ ND_PRINT("Abort data = ");
+ for(i = 1; i < ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ }
+ return;
+ /* NOTREACHED */
+ break;
+ case 0x03: /* Reserved */
+ case 0x05: /* Reserved */
+ case 0x07: /* Reserved */
+ ND_PRINT("Type = %d (Reserved), tid = 0x%02x, ",
+ transfer_type, tid);
+ data_start = 1;
+ break;
+ }
+
+ ND_PRINT("Upper layer data = ");
+ for(i = data_start; i < ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+}
+
+/*
+ * Payload IE list parsing and printing. See 7.4.3 of 802.15.4-2015
+ * for more information.
+ *
+ * Returns number of byts consumed from the packet or -1 in case of error.
+ */
+static int
+ieee802_15_4_print_payload_ie_list(netdissect_options *ndo,
+ const u_char *p,
+ u_int caplen)
+{
+ int len, ie, group_id, i;
+ uint16_t ie_len;
+
+ len = 0;
+ do {
+ if (caplen < 2) {
+ ND_PRINT("[ERROR: Truncated header IE]");
+ return -1;
+ }
+ /* Extract IE header */
+ ie = GET_LE_U_2(p);
+ if ((CHECK_BIT(ie, 15)) == 0) {
+ ND_PRINT("[ERROR: Payload IE with type 0] ");
+ }
+ ie_len = ie & 0x3ff;
+ group_id = (ie >> 11) & 0x0f;
+
+ /* Skip the IE header */
+ p += 2;
+ if (ie_len == 0) {
+ ND_PRINT("\n\t%s [", p_ie_names[group_id]);
+ } else {
+ ND_PRINT("\n\t%s [ length = %d, ",
+ p_ie_names[group_id], ie_len);
+ }
+ if (caplen < ie_len) {
+ ND_PRINT("[ERROR: Truncated IE data]");
+ return -1;
+ }
+ if (ndo->ndo_vflag > 3 && ie_len != 0) {
+ switch (group_id) {
+ case 0x1: /* MLME IE */
+ ieee802_15_4_print_mlme_ie_list(ndo, p, ie_len);
+ break;
+ case 0x2: /* Vendor Specific Nested IE */
+ if (ie_len < 3) {
+ ND_PRINT("[ERROR: Vendor OUI missing]");
+ } else {
+ ND_PRINT("OUI = 0x%02x%02x%02x, ",
+ GET_U_1(p),
+ GET_U_1(p + 1),
+ GET_U_1(p + 2));
+ ND_PRINT("Data = ");
+ for(i = 3; i < ie_len; i++) {
+ ND_PRINT("%02x ",
+ GET_U_1(p + i));
+ }
+ }
+ break;
+ case 0x3: /* Multiplexed IE (802.15.9) */
+ ieee802_15_4_print_mpx_ie(ndo, p, ie_len);
+ break;
+ case 0x5: /* IETF IE */
+ if (ie_len < 1) {
+ ND_PRINT("[ERROR: Subtype ID missing]");
+ } else {
+ ND_PRINT("Subtype ID = 0x%02x, Subtype content = ",
+ GET_U_1(p));
+ for(i = 1; i < ie_len; i++) {
+ ND_PRINT("%02x ",
+ GET_U_1(p + i));
+ }
+ }
+ break;
+ default:
+ ND_PRINT("IE Data = ");
+ for(i = 0; i < ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ break;
+ }
+ } else {
+ if (ie_len != 0) {
+ ND_PRINT("IE Data = ");
+ for(i = 0; i < ie_len; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ }
+ }
+ ND_PRINT("]\n\t");
+ len += 2 + ie_len;
+ p += ie_len;
+ caplen -= 2 + ie_len;
+ if (group_id == 0xf) {
+ break;
+ }
+ } while (caplen > 0);
+ return len;
+}
+
+/*
+ * Parse and print auxiliary security header.
+ *
+ * Returns number of byts consumed from the packet or -1 in case of error.
+ */
+static int
+ieee802_15_4_print_aux_sec_header(netdissect_options *ndo,
+ const u_char *p,
+ u_int caplen,
+ int *security_level)
+{
+ int sc, key_id_mode, len;
+
+ if (caplen < 1) {
+ ND_PRINT("[ERROR: Truncated before Aux Security Header]");
+ return -1;
+ }
+ sc = GET_U_1(p);
+ len = 1;
+ *security_level = sc & 0x7;
+ key_id_mode = (sc >> 3) & 0x3;
+
+ caplen -= 1;
+ p += 1;
+
+ if (ndo->ndo_vflag > 0) {
+ ND_PRINT("\n\tSecurity Level %d, Key Id Mode %d, ",
+ *security_level, key_id_mode);
+ }
+ if ((CHECK_BIT(sc, 5)) == 0) {
+ if (caplen < 4) {
+ ND_PRINT("[ERROR: Truncated before Frame Counter]");
+ return -1;
+ }
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT("Frame Counter 0x%08x ",
+ GET_LE_U_4(p));
+ }
+ p += 4;
+ caplen -= 4;
+ len += 4;
+ }
+ switch (key_id_mode) {
+ case 0x00: /* Implicit. */
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT("Implicit");
+ }
+ return len;
+ break;
+ case 0x01: /* Key Index, nothing to print here. */
+ break;
+ case 0x02: /* PAN and Short address Key Source, and Key Index. */
+ if (caplen < 4) {
+ ND_PRINT("[ERROR: Truncated before Key Source]");
+ return -1;
+ }
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT("KeySource 0x%04x:%0x4x, ",
+ GET_LE_U_2(p), GET_LE_U_2(p + 2));
+ }
+ p += 4;
+ caplen -= 4;
+ len += 4;
+ break;
+ case 0x03: /* Extended address and Key Index. */
+ if (caplen < 8) {
+ ND_PRINT("[ERROR: Truncated before Key Source]");
+ return -1;
+ }
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT("KeySource %s, ", GET_LE64ADDR_STRING(p));
+ }
+ p += 4;
+ caplen -= 4;
+ len += 4;
+ break;
+ }
+ if (caplen < 1) {
+ ND_PRINT("[ERROR: Truncated before Key Index]");
+ return -1;
+ }
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT("KeyIndex 0x%02x, ", GET_U_1(p));
+ }
+ caplen -= 1;
+ p += 1;
+ len += 1;
+ return len;
+}
+
+/*
+ * Print command data.
+ *
+ * Returns number of byts consumed from the packet or -1 in case of error.
+ */
+static int
+ieee802_15_4_print_command_data(netdissect_options *ndo,
+ uint8_t command_id,
+ const u_char *p,
+ u_int caplen)
+{
+ u_int i;
+
+ switch (command_id) {
+ case 0x01: /* Association Request */
+ if (caplen != 1) {
+ ND_PRINT("Invalid Association request command length");
+ return -1;
+ } else {
+ uint8_t cap_info;
+ cap_info = GET_U_1(p);
+ ND_PRINT("%s%s%s%s%s%s",
+ ((cap_info & 0x02) ?
+ "FFD, " : "RFD, "),
+ ((cap_info & 0x04) ?
+ "AC powered, " : ""),
+ ((cap_info & 0x08) ?
+ "Receiver on when idle, " : ""),
+ ((cap_info & 0x10) ?
+ "Fast association, " : ""),
+ ((cap_info & 0x40) ?
+ "Security supported, " : ""),
+ ((cap_info & 0x80) ?
+ "Allocate address, " : ""));
+ return caplen;
+ }
+ break;
+ case 0x02: /* Association Response */
+ if (caplen != 3) {
+ ND_PRINT("Invalid Association response command length");
+ return -1;
+ } else {
+ ND_PRINT("Short address = ");
+ ieee802_15_4_print_addr(ndo, p, 2);
+ switch (GET_U_1(p + 2)) {
+ case 0x00:
+ ND_PRINT(", Association successful");
+ break;
+ case 0x01:
+ ND_PRINT(", PAN at capacity");
+ break;
+ case 0x02:
+ ND_PRINT(", PAN access denied");
+ break;
+ case 0x03:
+ ND_PRINT(", Hooping sequence offset duplication");
+ break;
+ case 0x80:
+ ND_PRINT(", Fast association successful");
+ break;
+ default:
+ ND_PRINT(", Status = 0x%02x",
+ GET_U_1(p + 2));
+ break;
+ }
+ return caplen;
+ }
+ break;
+ case 0x03: /* Diassociation Notification command */
+ if (caplen != 1) {
+ ND_PRINT("Invalid Disassociation Notification command length");
+ return -1;
+ } else {
+ switch (GET_U_1(p)) {
+ case 0x00:
+ ND_PRINT("Reserved");
+ break;
+ case 0x01:
+ ND_PRINT("Reason = The coordinator wishes the device to leave PAN");
+ break;
+ case 0x02:
+ ND_PRINT("Reason = The device wishes to leave the PAN");
+ break;
+ default:
+ ND_PRINT("Reason = 0x%02x", GET_U_1(p + 2));
+ break;
+ }
+ return caplen;
+ }
+
+ /* Following ones do not have any data. */
+ case 0x04: /* Data Request command */
+ case 0x05: /* PAN ID Conflict Notification command */
+ case 0x06: /* Orphan Notification command */
+ case 0x07: /* Beacon Request command */
+ /* Should not have any data. */
+ return 0;
+ case 0x08: /* Coordinator Realignment command */
+ if (caplen < 7 || caplen > 8) {
+ ND_PRINT("Invalid Coordinator Realignment command length");
+ return -1;
+ } else {
+ uint16_t channel, page;
+
+ ND_PRINT("Pan ID = 0x%04x, Coordinator short address = ",
+ GET_LE_U_2(p));
+ ieee802_15_4_print_addr(ndo, p + 2, 2);
+ channel = GET_U_1(p + 4);
+
+ if (caplen == 8) {
+ page = GET_U_1(p + 7);
+ } else {
+ page = 0x80;
+ }
+ if (CHECK_BIT(page, 7)) {
+ /* No page present, instead we have msb of
+ channel in the page. */
+ channel |= (page & 0x7f) << 8;
+ ND_PRINT(", Channel Number = %d", channel);
+ } else {
+ ND_PRINT(", Channel Number = %d, page = %d",
+ channel, page);
+ }
+ ND_PRINT(", Short address = ");
+ ieee802_15_4_print_addr(ndo, p + 5, 2);
+ return caplen;
+ }
+ break;
+ case 0x09: /* GTS Request command */
+ if (caplen != 1) {
+ ND_PRINT("Invalid GTS Request command length");
+ return -1;
+ } else {
+ uint8_t gts;
+
+ gts = GET_U_1(p);
+ ND_PRINT("GTS Length = %d, %s, %s",
+ gts & 0xf,
+ (CHECK_BIT(gts, 4) ?
+ "Receive-only GTS" : "Transmit-only GTS"),
+ (CHECK_BIT(gts, 5) ?
+ "GTS allocation" : "GTS deallocations"));
+ return caplen;
+ }
+ break;
+ case 0x13: /* DSME Association Request command */
+ /* XXX Not implemented */
+ case 0x14: /* DSME Association Response command */
+ /* XXX Not implemented */
+ case 0x15: /* DSME GTS Request command */
+ /* XXX Not implemented */
+ case 0x16: /* DSME GTS Response command */
+ /* XXX Not implemented */
+ case 0x17: /* DSME GTS Notify command */
+ /* XXX Not implemented */
+ case 0x18: /* DSME Information Request command */
+ /* XXX Not implemented */
+ case 0x19: /* DSME Information Response command */
+ /* XXX Not implemented */
+ case 0x1a: /* DSME Beacon Allocation Notification command */
+ /* XXX Not implemented */
+ case 0x1b: /* DSME Beacon Collision Notification command */
+ /* XXX Not implemented */
+ case 0x1c: /* DSME Link Report command */
+ /* XXX Not implemented */
+ case 0x20: /* RIT Data Request command */
+ /* XXX Not implemented */
+ case 0x21: /* DBS Request command */
+ /* XXX Not implemented */
+ case 0x22: /* DBS Response command */
+ /* XXX Not implemented */
+ case 0x23: /* RIT Data Response command */
+ /* XXX Not implemented */
+ case 0x24: /* Vendor Specific command */
+ /* XXX Not implemented */
+ case 0x0a: /* TRLE Management Request command */
+ /* XXX Not implemented */
+ case 0x0b: /* TRLE Management Response command */
+ /* XXX Not implemented */
+ default:
+ ND_PRINT("Command Data = ");
+ for(i = 0; i < caplen; i++) {
+ ND_PRINT("%02x ", GET_U_1(p + i));
+ }
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Parse and print frames following standard format.
+ *
+ * Returns FALSE in case of error.
+ */
+static u_int
+ieee802_15_4_std_frames(netdissect_options *ndo,
+ const u_char *p, u_int caplen,
+ uint16_t fc)
+{
+ int len, frame_version, pan_id_comp;
+ int frame_type;
+ int src_pan, dst_pan, src_addr_len, dst_addr_len;
+ int security_level, miclen = 0;
+ int payload_ie_present;
+ uint8_t seq;
+ uint32_t fcs, crc_check;
+ const u_char *mic_start = NULL;
+
+ payload_ie_present = 0;
+
+ crc_check = 0;
+ /* Assume 2 octet FCS, the FCS length depends on the PHY, and we do not
+ know about that. */
+ if (caplen < 4) {
+ /* Cannot have FCS, assume no FCS. */
+ fcs = 0;
+ } else {
+ /* Test for 4 octet FCS. */
+ fcs = GET_LE_U_4(p + caplen - 4);
+ crc_check = ieee802_15_4_crc32(ndo, p, caplen - 4);
+ if (crc_check == fcs) {
+ /* Remove FCS */
+ caplen -= 4;
+ } else {
+ /* Test for 2 octet FCS. */
+ fcs = GET_LE_U_2(p + caplen - 2);
+ crc_check = ieee802_15_4_crc16(ndo, p, caplen - 2);
+ if (crc_check == fcs) {
+ /* Remove FCS */
+ caplen -= 2;
+ } else {
+ /* Wrong FCS, FCS might not be included in the
+ captured frame, do not remove it. */
+ }
+ }
+ }
+
+ /* Frame version. */
+ frame_version = FC_FRAME_VERSION(fc);
+ frame_type = FC_FRAME_TYPE(fc);
+ ND_PRINT("v%d ", frame_version);
+
+ if (ndo->ndo_vflag > 2) {
+ if (CHECK_BIT(fc, 3)) { ND_PRINT("Security Enabled, "); }
+ if (CHECK_BIT(fc, 4)) { ND_PRINT("Frame Pending, "); }
+ if (CHECK_BIT(fc, 5)) { ND_PRINT("AR, "); }
+ if (CHECK_BIT(fc, 6)) { ND_PRINT("PAN ID Compression, "); }
+ if (CHECK_BIT(fc, 8)) { ND_PRINT("Sequence Number Suppression, "); }
+ if (CHECK_BIT(fc, 9)) { ND_PRINT("IE present, "); }
+ }
+
+ /* Check for the sequence number suppression. */
+ if (CHECK_BIT(fc, 8)) {
+ /* Sequence number is suppressed. */
+ if (frame_version < 2) {
+ /* Sequence number can only be suppressed for frame
+ version 2 or higher, this is invalid frame. */
+ ND_PRINT("[ERROR: Sequence number suppressed on frames where version < 2]");
+ }
+ if (ndo->ndo_vflag)
+ ND_PRINT("seq suppressed ");
+ p += 2;
+ caplen -= 2;
+ } else {
+ seq = GET_U_1(p + 2);
+ p += 3;
+ caplen -= 3;
+ if (ndo->ndo_vflag)
+ ND_PRINT("seq %02x ", seq);
+ }
+
+ /* See which parts of addresses we have. */
+ dst_addr_len = ieee802_15_4_addr_len((fc >> 10) & 0x3);
+ src_addr_len = ieee802_15_4_addr_len((fc >> 14) & 0x3);
+ if (src_addr_len < 0) {
+ ND_PRINT("[ERROR: Invalid src address mode]");
+ return 0;
+ }
+ if (dst_addr_len < 0) {
+ ND_PRINT("[ERROR: Invalid dst address mode]");
+ return 0;
+ }
+ src_pan = 0;
+ dst_pan = 0;
+ pan_id_comp = CHECK_BIT(fc, 6);
+
+ /* The PAN ID Compression rules are complicated. */
+
+ /* First check old versions, where the rules are simple. */
+ if (frame_version < 2) {
+ if (pan_id_comp) {
+ src_pan = 0;
+ dst_pan = 1;
+ if (dst_addr_len <= 0 || src_addr_len <= 0) {
+ /* Invalid frame, PAN ID Compression must be 0
+ if only one address in the frame. */
+ ND_PRINT("[ERROR: PAN ID Compression != 0, and only one address with frame version < 2]");
+ }
+ } else {
+ src_pan = 1;
+ dst_pan = 1;
+ }
+ if (dst_addr_len <= 0) {
+ dst_pan = 0;
+ }
+ if (src_addr_len <= 0) {
+ src_pan = 0;
+ }
+ } else {
+ /* Frame version 2 rules are more complicated, and they depend
+ on the address modes of the frame, generic rules are same,
+ but then there are some special cases. */
+ if (pan_id_comp) {
+ src_pan = 0;
+ dst_pan = 1;
+ } else {
+ src_pan = 1;
+ dst_pan = 1;
+ }
+ if (dst_addr_len <= 0) {
+ dst_pan = 0;
+ }
+ if (src_addr_len <= 0) {
+ src_pan = 0;
+ }
+ if (pan_id_comp) {
+ if (src_addr_len == 0 &&
+ dst_addr_len == 0) {
+ /* Both addresses are missing, but PAN ID
+ compression set, special case we have
+ destination PAN but no addresses. */
+ dst_pan = 1;
+ } else if ((src_addr_len == 0 &&
+ dst_addr_len > 0) ||
+ (src_addr_len > 0 &&
+ dst_addr_len == 0)) {
+ /* Only one address present, and PAN ID
+ compression is set, we do not have PAN id at
+ all. */
+ dst_pan = 0;
+ src_pan = 0;
+ } else if (src_addr_len == 8 &&
+ dst_addr_len == 8) {
+ /* Both addresses are Extended, and PAN ID
+ compression set, we do not have PAN ID at
+ all. */
+ dst_pan = 0;
+ src_pan = 0;
+ }
+ } else {
+ /* Special cases where PAN ID Compression is not set. */
+ if (src_addr_len == 8 &&
+ dst_addr_len == 8) {
+ /* Both addresses are Extended, and PAN ID
+ compression not set, we do have only one PAN
+ ID (destination). */
+ dst_pan = 1;
+ src_pan = 0;
+ }
+#ifdef BROKEN_6TISCH_PAN_ID_COMPRESSION
+ if (src_addr_len == 8 &&
+ dst_addr_len == 2) {
+ /* Special case for the broken 6tisch
+ implementations. */
+ src_pan = 0;
+ }
+#endif /* BROKEN_6TISCH_PAN_ID_COMPRESSION */
+ }
+ }
+
+ /* Print dst PAN and address. */
+ if (dst_pan) {
+ if (caplen < 2) {
+ ND_PRINT("[ERROR: Truncated before dst_pan]");
+ return 0;
+ }
+ ND_PRINT("%04x:", GET_LE_U_2(p));
+ p += 2;
+ caplen -= 2;
+ } else {
+ ND_PRINT("-:");
+ }
+ if (caplen < (u_int) dst_addr_len) {
+ ND_PRINT("[ERROR: Truncated before dst_addr]");
+ return 0;
+ }
+ ieee802_15_4_print_addr(ndo, p, dst_addr_len);
+ p += dst_addr_len;
+ caplen -= dst_addr_len;
+
+ ND_PRINT(" < ");
+
+ /* Print src PAN and address. */
+ if (src_pan) {
+ if (caplen < 2) {
+ ND_PRINT("[ERROR: Truncated before dst_pan]");
+ return 0;
+ }
+ ND_PRINT("%04x:", GET_LE_U_2(p));
+ p += 2;
+ caplen -= 2;
+ } else {
+ ND_PRINT("-:");
+ }
+ if (caplen < (u_int) src_addr_len) {
+ ND_PRINT("[ERROR: Truncated before dst_addr]");
+ return 0;
+ }
+ ieee802_15_4_print_addr(ndo, p, src_addr_len);
+ ND_PRINT(" ");
+ p += src_addr_len;
+ caplen -= src_addr_len;
+ if (CHECK_BIT(fc, 3)) {
+ /*
+ * XXX - if frame_version is 0, this is the 2003
+ * spec, and you don't have the auxiliary security
+ * header, you have a frame counter and key index
+ * for the AES-CTR and AES-CCM security suites but
+ * not for the AES-CBC-MAC security suite.
+ */
+ len = ieee802_15_4_print_aux_sec_header(ndo, p, caplen,
+ &security_level);
+ if (len < 0) {
+ return 0;
+ }
+ p += len;
+ caplen -= len;
+ } else {
+ security_level = 0;
+ }
+
+ switch (security_level) {
+ case 0: /*FALLTHOUGH */
+ case 4:
+ miclen = 0;
+ break;
+ case 1: /*FALLTHOUGH */
+ case 5:
+ miclen = 4;
+ break;
+ case 2: /*FALLTHOUGH */
+ case 6:
+ miclen = 8;
+ break;
+ case 3: /*FALLTHOUGH */
+ case 7:
+ miclen = 16;
+ break;
+ }
+
+ /* Remove MIC */
+ if (miclen > 0) {
+ if (caplen < (u_int) miclen) {
+ ND_PRINT("[ERROR: Truncated before MIC]");
+ return 0;
+ }
+ caplen -= miclen;
+ mic_start = p + caplen;
+ }
+
+ /* Parse Information elements if present */
+ if (CHECK_BIT(fc, 9)) {
+ /* Yes we have those. */
+ len = ieee802_15_4_print_header_ie_list(ndo, p, caplen,
+ &payload_ie_present);
+ if (len < 0) {
+ return 0;
+ }
+ p += len;
+ caplen -= len;
+ }
+
+ if (payload_ie_present) {
+ if (security_level >= 4) {
+ ND_PRINT("Payload IEs present, but encrypted, cannot print ");
+ } else {
+ len = ieee802_15_4_print_payload_ie_list(ndo, p, caplen);
+ if (len < 0) {
+ return 0;
+ }
+ p += len;
+ caplen -= len;
+ }
+ }
+
+ /* Print MIC */
+ if (ndo->ndo_vflag > 2 && miclen != 0) {
+ ND_PRINT("\n\tMIC ");
+
+ for(len = 0; len < miclen; len++) {
+ ND_PRINT("%02x", GET_U_1(mic_start + len));
+ }
+ ND_PRINT(" ");
+ }
+
+ /* Print FCS */
+ if (ndo->ndo_vflag > 2) {
+ if (crc_check == fcs) {
+ ND_PRINT("FCS %x ", fcs);
+ } else {
+ ND_PRINT("wrong FCS %x vs %x (assume no FCS stored) ",
+ fcs, crc_check);
+ }
+ }
+
+ /* Payload print */
+ switch (frame_type) {
+ case 0x00: /* Beacon */
+ if (frame_version < 2) {
+ if (caplen < 2) {
+ ND_PRINT("[ERROR: Truncated before beacon information]");
+ break;
+ } else {
+ uint16_t ss;
+
+ ss = GET_LE_U_2(p);
+ ieee802_15_4_print_superframe_specification(ndo, ss);
+ p += 2;
+ caplen -= 2;
+
+ /* GTS */
+ if (caplen < 1) {
+ ND_PRINT("[ERROR: Truncated before GTS info]");
+ break;
+ }
+
+ len = ieee802_15_4_print_gts_info(ndo, p, caplen);
+ if (len < 0) {
+ break;
+ }
+
+ p += len;
+ caplen -= len;
+
+ /* Pending Addresses */
+ if (caplen < 1) {
+ ND_PRINT("[ERROR: Truncated before pending addresses]");
+ break;
+ }
+ len = ieee802_15_4_print_pending_addresses(ndo, p, caplen);
+ if (len < 0) {
+ break;
+ }
+ p += len;
+ caplen -= len;
+ }
+ }
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+
+ break;
+ case 0x01: /* Data */
+ case 0x02: /* Acknowledgement */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ break;
+ case 0x03: /* MAC Command */
+ if (caplen < 1) {
+ ND_PRINT("[ERROR: Truncated before Command ID]");
+ } else {
+ uint8_t command_id;
+
+ command_id = GET_U_1(p);
+ if (command_id >= 0x30) {
+ ND_PRINT("Command ID = Reserved 0x%02x ",
+ command_id);
+ } else {
+ ND_PRINT("Command ID = %s ",
+ mac_c_names[command_id]);
+ }
+ p++;
+ caplen--;
+ if (caplen != 0) {
+ len = ieee802_15_4_print_command_data(ndo, command_id, p, caplen);
+ if (len >= 0) {
+ p += len;
+ caplen -= len;
+ }
+ }
+ }
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ break;
+ }
+ return 1;
+}
+
+/*
+ * Print and parse Multipurpose frames.
+ *
+ * Returns FALSE in case of error.
+ */
+static u_int
+ieee802_15_4_mp_frame(netdissect_options *ndo,
+ const u_char *p, u_int caplen,
+ uint16_t fc)
+{
+ int len, frame_version, pan_id_present;
+ int src_addr_len, dst_addr_len;
+ int security_level, miclen = 0;
+ int ie_present, payload_ie_present, security_enabled;
+ uint8_t seq;
+ uint32_t fcs, crc_check;
+ const u_char *mic_start = NULL;
+
+ pan_id_present = 0;
+ ie_present = 0;
+ payload_ie_present = 0;
+ security_enabled = 0;
+ crc_check = 0;
+
+ /* Assume 2 octet FCS, the FCS length depends on the PHY, and we do not
+ know about that. */
+ if (caplen < 3) {
+ /* Cannot have FCS, assume no FCS. */
+ fcs = 0;
+ } else {
+ if (caplen > 4) {
+ /* Test for 4 octet FCS. */
+ fcs = GET_LE_U_4(p + caplen - 4);
+ crc_check = ieee802_15_4_crc32(ndo, p, caplen - 4);
+ if (crc_check == fcs) {
+ /* Remove FCS */
+ caplen -= 4;
+ } else {
+ fcs = GET_LE_U_2(p + caplen - 2);
+ crc_check = ieee802_15_4_crc16(ndo, p, caplen - 2);
+ if (crc_check == fcs) {
+ /* Remove FCS */
+ caplen -= 2;
+ }
+ }
+ } else {
+ fcs = GET_LE_U_2(p + caplen - 2);
+ crc_check = ieee802_15_4_crc16(ndo, p, caplen - 2);
+ if (crc_check == fcs) {
+ /* Remove FCS */
+ caplen -= 2;
+ }
+ }
+ }
+
+ if (CHECK_BIT(fc, 3)) {
+ /* Long Frame Control */
+
+ /* Frame version. */
+ frame_version = FC_FRAME_VERSION(fc);
+ ND_PRINT("v%d ", frame_version);
+
+ pan_id_present = CHECK_BIT(fc, 8);
+ ie_present = CHECK_BIT(fc, 15);
+ security_enabled = CHECK_BIT(fc, 9);
+
+ if (ndo->ndo_vflag > 2) {
+ if (security_enabled) { ND_PRINT("Security Enabled, "); }
+ if (CHECK_BIT(fc, 11)) { ND_PRINT("Frame Pending, "); }
+ if (CHECK_BIT(fc, 14)) { ND_PRINT("AR, "); }
+ if (pan_id_present) { ND_PRINT("PAN ID Present, "); }
+ if (CHECK_BIT(fc, 10)) {
+ ND_PRINT("Sequence Number Suppression, ");
+ }
+ if (ie_present) { ND_PRINT("IE present, "); }
+ }
+
+ /* Check for the sequence number suppression. */
+ if (CHECK_BIT(fc, 10)) {
+ /* Sequence number is suppressed, but long version. */
+ p += 2;
+ caplen -= 2;
+ } else {
+ seq = GET_U_1(p + 2);
+ p += 3;
+ caplen -= 3;
+ if (ndo->ndo_vflag)
+ ND_PRINT("seq %02x ", seq);
+ }
+ } else {
+ /* Short format of header, but with seq no */
+ seq = GET_U_1(p + 1);
+ p += 2;
+ caplen -= 2;
+ if (ndo->ndo_vflag)
+ ND_PRINT("seq %02x ", seq);
+ }
+
+ /* See which parts of addresses we have. */
+ dst_addr_len = ieee802_15_4_addr_len((fc >> 4) & 0x3);
+ src_addr_len = ieee802_15_4_addr_len((fc >> 6) & 0x3);
+ if (src_addr_len < 0) {
+ ND_PRINT("[ERROR: Invalid src address mode]");
+ return 0;
+ }
+ if (dst_addr_len < 0) {
+ ND_PRINT("[ERROR: Invalid dst address mode]");
+ return 0;
+ }
+
+ /* Print dst PAN and address. */
+ if (pan_id_present) {
+ if (caplen < 2) {
+ ND_PRINT("[ERROR: Truncated before dst_pan]");
+ return 0;
+ }
+ ND_PRINT("%04x:", GET_LE_U_2(p));
+ p += 2;
+ caplen -= 2;
+ } else {
+ ND_PRINT("-:");
+ }
+ if (caplen < (u_int) dst_addr_len) {
+ ND_PRINT("[ERROR: Truncated before dst_addr]");
+ return 0;
+ }
+ ieee802_15_4_print_addr(ndo, p, dst_addr_len);
+ p += dst_addr_len;
+ caplen -= dst_addr_len;
+
+ ND_PRINT(" < ");
+
+ /* Print src PAN and address. */
+ ND_PRINT(" -:");
+ if (caplen < (u_int) src_addr_len) {
+ ND_PRINT("[ERROR: Truncated before dst_addr]");
+ return 0;
+ }
+ ieee802_15_4_print_addr(ndo, p, src_addr_len);
+ ND_PRINT(" ");
+ p += src_addr_len;
+ caplen -= src_addr_len;
+
+ if (security_enabled) {
+ len = ieee802_15_4_print_aux_sec_header(ndo, p, caplen,
+ &security_level);
+ if (len < 0) {
+ return 0;
+ }
+ p += len;
+ caplen -= len;
+ } else {
+ security_level = 0;
+ }
+
+ switch (security_level) {
+ case 0: /*FALLTHOUGH */
+ case 4:
+ miclen = 0;
+ break;
+ case 1: /*FALLTHOUGH */
+ case 5:
+ miclen = 4;
+ break;
+ case 2: /*FALLTHOUGH */
+ case 6:
+ miclen = 8;
+ break;
+ case 3: /*FALLTHOUGH */
+ case 7:
+ miclen = 16;
+ break;
+ }
+
+ /* Remove MIC */
+ if (miclen > 0) {
+ if (caplen < (u_int) miclen) {
+ ND_PRINT("[ERROR: Truncated before MIC]");
+ return 0;
+ }
+ caplen -= miclen;
+ mic_start = p + caplen;
+ }
+
+ /* Parse Information elements if present */
+ if (ie_present) {
+ /* Yes we have those. */
+ len = ieee802_15_4_print_header_ie_list(ndo, p, caplen,
+ &payload_ie_present);
+ if (len < 0) {
+ return 0;
+ }
+ p += len;
+ caplen -= len;
+ }
+
+ if (payload_ie_present) {
+ if (security_level >= 4) {
+ ND_PRINT("Payload IEs present, but encrypted, cannot print ");
+ } else {
+ len = ieee802_15_4_print_payload_ie_list(ndo, p,
+ caplen);
+ if (len < 0) {
+ return 0;
+ }
+ p += len;
+ caplen -= len;
+ }
+ }
+
+ /* Print MIC */
+ if (ndo->ndo_vflag > 2 && miclen != 0) {
+ ND_PRINT("\n\tMIC ");
+
+ for(len = 0; len < miclen; len++) {
+ ND_PRINT("%02x", GET_U_1(mic_start + len));
+ }
+ ND_PRINT(" ");
+ }
+
+
+ /* Print FCS */
+ if (ndo->ndo_vflag > 2) {
+ if (crc_check == fcs) {
+ ND_PRINT("FCS %x ", fcs);
+ } else {
+ ND_PRINT("wrong FCS %x vs %x (assume no FCS stored) ",
+ fcs, crc_check);
+ }
+ }
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+
+ return 1;
+}
+
+/*
+ * Print frag frame.
+ *
+ * Returns FALSE in case of error.
+ */
+static u_int
+ieee802_15_4_frag_frame(netdissect_options *ndo _U_,
+ const u_char *p _U_,
+ u_int caplen _U_,
+ uint16_t fc _U_)
+{
+ /* Not implement yet, might be bit hard to implement, as the
+ * information to set up the fragment is coming in the previous frame
+ * in the Fragment Sequence Context Description IE, thus we need to
+ * store information from there, so we can use it here. */
+ return 0;
+}
+
+/*
+ * Internal call to dissector taking packet + len instead of pcap_pkthdr.
+ *
+ * Returns FALSE in case of error.
+ */
+u_int
+ieee802_15_4_print(netdissect_options *ndo,
+ const u_char *p, u_int caplen)
+{
+ int frame_type;
+ uint16_t fc;
+
+ ndo->ndo_protocol = "802.15.4";
+
+ if (caplen < 2) {
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+
+ fc = GET_LE_U_2(p);
+
+ /* First we need to check the frame type to know how to parse the rest
+ of the FC. Frame type is the first 3 bit of the frame control field.
+ */
+
+ frame_type = FC_FRAME_TYPE(fc);
+ ND_PRINT("IEEE 802.15.4 %s packet ", ftypes[frame_type]);
+
+ switch (frame_type) {
+ case 0x00: /* Beacon */
+ case 0x01: /* Data */
+ case 0x02: /* Acknowledgement */
+ case 0x03: /* MAC Command */
+ return ieee802_15_4_std_frames(ndo, p, caplen, fc);
+ break;
+ case 0x04: /* Reserved */
+ return 0;
+ break;
+ case 0x05: /* Multipurpose */
+ return ieee802_15_4_mp_frame(ndo, p, caplen, fc);
+ break;
+ case 0x06: /* Fragment or Frak */
+ return ieee802_15_4_frag_frame(ndo, p, caplen, fc);
+ break;
+ case 0x07: /* Extended */
+ return 0;
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Main function to print packets.
+ */
+
+void
+ieee802_15_4_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ ndo->ndo_protocol = "802.15.4";
+ ndo->ndo_ll_hdr_len += ieee802_15_4_print(ndo, p, caplen);
+}
+
+/* For DLT_IEEE802_15_4_TAP */
+/* https://github.com/jkcko/ieee802.15.4-tap */
+void
+ieee802_15_4_tap_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ uint8_t version;
+ uint16_t length;
+
+ ndo->ndo_protocol = "802.15.4_tap";
+ if (h->caplen < 4) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += h->caplen;
+ return;
+ }
+
+ version = GET_U_1(p);
+ length = GET_LE_U_2(p + 2);
+ if (version != 0 || length < 4) {
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ if (h->caplen < length) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += h->caplen;
+ return;
+ }
+
+ ndo->ndo_ll_hdr_len += ieee802_15_4_print(ndo, p+length, h->caplen-length) + length;
+}
diff --git a/print-ah.c b/print-ah.c
new file mode 100644
index 0000000..a3d0554
--- /dev/null
+++ b/print-ah.c
@@ -0,0 +1,75 @@
+/* $NetBSD: print-ah.c,v 1.4 1996/05/20 00:41:16 fvdl Exp $ */
+
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IPSEC Authentication Header printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+#include "ah.h"
+
+int
+ah_print(netdissect_options *ndo, const u_char *bp)
+{
+ const struct ah *ah;
+ uint8_t ah_len;
+ u_int ah_hdr_len;
+ uint16_t reserved;
+ const u_char *p;
+
+ ndo->ndo_protocol = "ah";
+ ah = (const struct ah *)bp;
+
+ nd_print_protocol_caps(ndo);
+/*
+ * RFC4302
+ *
+ * 2.2. Payload Length
+ *
+ * This 8-bit field specifies the length of AH in 32-bit words (4-byte
+ * units), minus "2".
+ */
+ ah_len = GET_U_1(ah->ah_len);
+ ah_hdr_len = (ah_len + 2) * 4;
+
+ ND_PRINT("(");
+ if (ndo->ndo_vflag)
+ ND_PRINT("length=%u(%u-bytes),", ah_len, ah_hdr_len);
+ reserved = GET_BE_U_2(ah->ah_reserved);
+ if (reserved)
+ ND_PRINT("reserved=0x%x[MustBeZero],", reserved);
+ ND_PRINT("spi=0x%08x,", GET_BE_U_4(ah->ah_spi));
+ ND_PRINT("seq=0x%x,", GET_BE_U_4(ah->ah_seq));
+ ND_PRINT("icv=0x");
+ for (p = (const u_char *)(ah + 1); p < bp + ah_hdr_len; p++)
+ ND_PRINT("%02x", GET_U_1(p));
+ ND_PRINT("): ");
+
+ return ah_hdr_len;
+}
diff --git a/print-ahcp.c b/print-ahcp.c
new file mode 100644
index 0000000..9859f76
--- /dev/null
+++ b/print-ahcp.c
@@ -0,0 +1,388 @@
+/*
+ * Copyright (c) 2013 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: Ad Hoc Configuration Protocol (AHCP) printer */
+
+/* Based on draft-chroboczek-ahcp-00 and source code of ahcpd-0.53 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+#define AHCP_MAGIC_NUMBER 43
+#define AHCP_VERSION_1 1
+#define AHCP1_HEADER_FIX_LEN 24
+#define AHCP1_BODY_MIN_LEN 4
+
+#define AHCP1_MSG_DISCOVER 0
+#define AHCP1_MSG_OFFER 1
+#define AHCP1_MSG_REQUEST 2
+#define AHCP1_MSG_ACK 3
+#define AHCP1_MSG_NACK 4
+#define AHCP1_MSG_RELEASE 5
+
+static const struct tok ahcp1_msg_str[] = {
+ { AHCP1_MSG_DISCOVER, "Discover" },
+ { AHCP1_MSG_OFFER, "Offer" },
+ { AHCP1_MSG_REQUEST, "Request" },
+ { AHCP1_MSG_ACK, "Ack" },
+ { AHCP1_MSG_NACK, "Nack" },
+ { AHCP1_MSG_RELEASE, "Release" },
+ { 0, NULL }
+};
+
+#define AHCP1_OPT_PAD 0
+#define AHCP1_OPT_MANDATORY 1
+#define AHCP1_OPT_ORIGIN_TIME 2
+#define AHCP1_OPT_EXPIRES 3
+#define AHCP1_OPT_MY_IPV6_ADDRESS 4
+#define AHCP1_OPT_MY_IPV4_ADDRESS 5
+#define AHCP1_OPT_IPV6_PREFIX 6
+#define AHCP1_OPT_IPV4_PREFIX 7
+#define AHCP1_OPT_IPV6_ADDRESS 8
+#define AHCP1_OPT_IPV4_ADDRESS 9
+#define AHCP1_OPT_IPV6_PREFIX_DELEGATION 10
+#define AHCP1_OPT_IPV4_PREFIX_DELEGATION 11
+#define AHCP1_OPT_NAME_SERVER 12
+#define AHCP1_OPT_NTP_SERVER 13
+#define AHCP1_OPT_MAX 13
+
+static const struct tok ahcp1_opt_str[] = {
+ { AHCP1_OPT_PAD, "Pad" },
+ { AHCP1_OPT_MANDATORY, "Mandatory" },
+ { AHCP1_OPT_ORIGIN_TIME, "Origin Time" },
+ { AHCP1_OPT_EXPIRES, "Expires" },
+ { AHCP1_OPT_MY_IPV6_ADDRESS, "My-IPv6-Address" },
+ { AHCP1_OPT_MY_IPV4_ADDRESS, "My-IPv4-Address" },
+ { AHCP1_OPT_IPV6_PREFIX, "IPv6 Prefix" },
+ { AHCP1_OPT_IPV4_PREFIX, "IPv4 Prefix" },
+ { AHCP1_OPT_IPV6_ADDRESS, "IPv6 Address" },
+ { AHCP1_OPT_IPV4_ADDRESS, "IPv4 Address" },
+ { AHCP1_OPT_IPV6_PREFIX_DELEGATION, "IPv6 Prefix Delegation" },
+ { AHCP1_OPT_IPV4_PREFIX_DELEGATION, "IPv4 Prefix Delegation" },
+ { AHCP1_OPT_NAME_SERVER, "Name Server" },
+ { AHCP1_OPT_NTP_SERVER, "NTP Server" },
+ { 0, NULL }
+};
+
+static void
+ahcp_time_print(netdissect_options *ndo,
+ const u_char *cp, uint8_t len)
+{
+ time_t t;
+ struct tm *tm;
+ char buf[BUFSIZE];
+
+ if (len != 4)
+ goto invalid;
+ t = GET_BE_U_4(cp);
+ if (NULL == (tm = gmtime(&t)))
+ ND_PRINT(": gmtime() error");
+ else if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm))
+ ND_PRINT(": strftime() error");
+ else
+ ND_PRINT(": %s UTC", buf);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+ahcp_seconds_print(netdissect_options *ndo,
+ const u_char *cp, uint8_t len)
+{
+ if (len != 4)
+ goto invalid;
+ ND_PRINT(": %us", GET_BE_U_4(cp));
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+ahcp_ipv6_addresses_print(netdissect_options *ndo,
+ const u_char *cp, uint8_t len)
+{
+ const char *sep = ": ";
+
+ while (len) {
+ if (len < 16)
+ goto invalid;
+ ND_PRINT("%s%s", sep, GET_IP6ADDR_STRING(cp));
+ cp += 16;
+ len -= 16;
+ sep = ", ";
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+ahcp_ipv4_addresses_print(netdissect_options *ndo,
+ const u_char *cp, uint8_t len)
+{
+ const char *sep = ": ";
+
+ while (len) {
+ if (len < 4)
+ goto invalid;
+ ND_PRINT("%s%s", sep, GET_IPADDR_STRING(cp));
+ cp += 4;
+ len -= 4;
+ sep = ", ";
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+ahcp_ipv6_prefixes_print(netdissect_options *ndo,
+ const u_char *cp, uint8_t len)
+{
+ const char *sep = ": ";
+
+ while (len) {
+ if (len < 17)
+ goto invalid;
+ ND_PRINT("%s%s/%u", sep, GET_IP6ADDR_STRING(cp), GET_U_1(cp + 16));
+ cp += 17;
+ len -= 17;
+ sep = ", ";
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+ahcp_ipv4_prefixes_print(netdissect_options *ndo,
+ const u_char *cp, uint8_t len)
+{
+ const char *sep = ": ";
+
+ while (len) {
+ if (len < 5)
+ goto invalid;
+ ND_PRINT("%s%s/%u", sep, GET_IPADDR_STRING(cp), GET_U_1(cp + 4));
+ cp += 5;
+ len -= 5;
+ sep = ", ";
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+(* const data_decoders[AHCP1_OPT_MAX + 1])(netdissect_options *, const u_char *, uint8_t) = {
+ /* [AHCP1_OPT_PAD] = */ NULL,
+ /* [AHCP1_OPT_MANDATORY] = */ NULL,
+ /* [AHCP1_OPT_ORIGIN_TIME] = */ ahcp_time_print,
+ /* [AHCP1_OPT_EXPIRES] = */ ahcp_seconds_print,
+ /* [AHCP1_OPT_MY_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
+ /* [AHCP1_OPT_MY_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
+ /* [AHCP1_OPT_IPV6_PREFIX] = */ ahcp_ipv6_prefixes_print,
+ /* [AHCP1_OPT_IPV4_PREFIX] = */ NULL,
+ /* [AHCP1_OPT_IPV6_ADDRESS] = */ ahcp_ipv6_addresses_print,
+ /* [AHCP1_OPT_IPV4_ADDRESS] = */ ahcp_ipv4_addresses_print,
+ /* [AHCP1_OPT_IPV6_PREFIX_DELEGATION] = */ ahcp_ipv6_prefixes_print,
+ /* [AHCP1_OPT_IPV4_PREFIX_DELEGATION] = */ ahcp_ipv4_prefixes_print,
+ /* [AHCP1_OPT_NAME_SERVER] = */ ahcp_ipv6_addresses_print,
+ /* [AHCP1_OPT_NTP_SERVER] = */ ahcp_ipv6_addresses_print,
+};
+
+static void
+ahcp1_options_print(netdissect_options *ndo,
+ const u_char *cp, uint16_t len)
+{
+ while (len) {
+ uint8_t option_no, option_len;
+
+ /* Option no */
+ option_no = GET_U_1(cp);
+ cp += 1;
+ len -= 1;
+ ND_PRINT("\n\t %s", tok2str(ahcp1_opt_str, "Unknown-%u", option_no));
+ if (option_no == AHCP1_OPT_PAD || option_no == AHCP1_OPT_MANDATORY)
+ continue;
+ /* Length */
+ if (!len)
+ goto invalid;
+ option_len = GET_U_1(cp);
+ cp += 1;
+ len -= 1;
+ if (option_len > len)
+ goto invalid;
+ /* Value */
+ if (option_no <= AHCP1_OPT_MAX && data_decoders[option_no] != NULL) {
+ data_decoders[option_no](ndo, cp, option_len);
+ } else {
+ ND_PRINT(" (Length %u)", option_len);
+ ND_TCHECK_LEN(cp, option_len);
+ }
+ cp += option_len;
+ len -= option_len;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+ahcp1_body_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint8_t type, mbz;
+ uint16_t body_len;
+
+ if (len < AHCP1_BODY_MIN_LEN)
+ goto invalid;
+ /* Type */
+ type = GET_U_1(cp);
+ cp += 1;
+ len -= 1;
+ /* MBZ */
+ mbz = GET_U_1(cp);
+ cp += 1;
+ len -= 1;
+ /* Length */
+ body_len = GET_BE_U_2(cp);
+ cp += 2;
+ len -= 2;
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("\n\t%s", tok2str(ahcp1_msg_str, "Unknown-%u", type));
+ if (mbz != 0)
+ ND_PRINT(", MBZ %u", mbz);
+ ND_PRINT(", Length %u", body_len);
+ }
+ if (body_len > len)
+ goto invalid;
+
+ /* Options */
+ /* Here use "body_len", not "len" (ignore any extra data). */
+ if (ndo->ndo_vflag >= 2)
+ ahcp1_options_print(ndo, cp, body_len);
+ else
+ ND_TCHECK_LEN(cp, body_len);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+
+}
+
+void
+ahcp_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint8_t version;
+
+ ndo->ndo_protocol = "ahcp";
+ nd_print_protocol_caps(ndo);
+ if (len < 2)
+ goto invalid;
+ /* Magic */
+ if (GET_U_1(cp) != AHCP_MAGIC_NUMBER)
+ goto invalid;
+ cp += 1;
+ len -= 1;
+ /* Version */
+ version = GET_U_1(cp);
+ cp += 1;
+ len -= 1;
+ switch (version) {
+ case AHCP_VERSION_1: {
+ ND_PRINT(" Version 1");
+ if (len < AHCP1_HEADER_FIX_LEN - 2)
+ goto invalid;
+ if (!ndo->ndo_vflag) {
+ ND_TCHECK_LEN(cp, AHCP1_HEADER_FIX_LEN - 2);
+ cp += AHCP1_HEADER_FIX_LEN - 2;
+ len -= AHCP1_HEADER_FIX_LEN - 2;
+ } else {
+ /* Hopcount */
+ ND_PRINT("\n\tHopcount %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* Original Hopcount */
+ ND_PRINT(", Original Hopcount %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* Nonce */
+ ND_PRINT(", Nonce 0x%08x", GET_BE_U_4(cp));
+ cp += 4;
+ len -= 4;
+ /* Source Id */
+ ND_PRINT(", Source Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8));
+ cp += 8;
+ len -= 8;
+ /* Destination Id */
+ ND_PRINT(", Destination Id %s", GET_LINKADDR_STRING(cp, LINKADDR_OTHER, 8));
+ cp += 8;
+ len -= 8;
+ }
+ /* Body */
+ ahcp1_body_print(ndo, cp, len);
+ break;
+ }
+ default:
+ ND_PRINT(" Version %u (unknown)", version);
+ ND_TCHECK_LEN(cp, len);
+ break;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
diff --git a/print-aodv.c b/print-aodv.c
new file mode 100644
index 0000000..9742143
--- /dev/null
+++ b/print-aodv.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 2003 Bruce M. Simpson <bms@spc.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bruce M. Simpson.
+ * 4. Neither the name of Bruce M. Simpson nor the names of co-
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bruce M. Simpson AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Bruce M. Simpson OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: Ad hoc On-Demand Distance Vector (AODV) Routing printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+/*
+ * RFC 3561
+ */
+struct aodv_rreq {
+ nd_uint8_t rreq_type; /* AODV message type (1) */
+ nd_uint8_t rreq_flags; /* various flags */
+ nd_uint8_t rreq_zero0; /* reserved, set to zero */
+ nd_uint8_t rreq_hops; /* number of hops from originator */
+ nd_uint32_t rreq_id; /* request ID */
+ nd_ipv4 rreq_da; /* destination IPv4 address */
+ nd_uint32_t rreq_ds; /* destination sequence number */
+ nd_ipv4 rreq_oa; /* originator IPv4 address */
+ nd_uint32_t rreq_os; /* originator sequence number */
+};
+struct aodv_rreq6 {
+ nd_uint8_t rreq_type; /* AODV message type (1) */
+ nd_uint8_t rreq_flags; /* various flags */
+ nd_uint8_t rreq_zero0; /* reserved, set to zero */
+ nd_uint8_t rreq_hops; /* number of hops from originator */
+ nd_uint32_t rreq_id; /* request ID */
+ nd_ipv6 rreq_da; /* destination IPv6 address */
+ nd_uint32_t rreq_ds; /* destination sequence number */
+ nd_ipv6 rreq_oa; /* originator IPv6 address */
+ nd_uint32_t rreq_os; /* originator sequence number */
+};
+struct aodv_rreq6_draft_01 {
+ nd_uint8_t rreq_type; /* AODV message type (16) */
+ nd_uint8_t rreq_flags; /* various flags */
+ nd_uint8_t rreq_zero0; /* reserved, set to zero */
+ nd_uint8_t rreq_hops; /* number of hops from originator */
+ nd_uint32_t rreq_id; /* request ID */
+ nd_uint32_t rreq_ds; /* destination sequence number */
+ nd_uint32_t rreq_os; /* originator sequence number */
+ nd_ipv6 rreq_da; /* destination IPv6 address */
+ nd_ipv6 rreq_oa; /* originator IPv6 address */
+};
+
+#define RREQ_JOIN 0x80 /* join (reserved for multicast */
+#define RREQ_REPAIR 0x40 /* repair (reserved for multicast */
+#define RREQ_GRAT 0x20 /* gratuitous RREP */
+#define RREQ_DEST 0x10 /* destination only */
+#define RREQ_UNKNOWN 0x08 /* unknown destination sequence num */
+#define RREQ_FLAGS_MASK 0xF8 /* mask for rreq_flags */
+
+struct aodv_rrep {
+ nd_uint8_t rrep_type; /* AODV message type (2) */
+ nd_uint8_t rrep_flags; /* various flags */
+ nd_uint8_t rrep_ps; /* prefix size */
+ nd_uint8_t rrep_hops; /* number of hops from o to d */
+ nd_ipv4 rrep_da; /* destination IPv4 address */
+ nd_uint32_t rrep_ds; /* destination sequence number */
+ nd_ipv4 rrep_oa; /* originator IPv4 address */
+ nd_uint32_t rrep_life; /* lifetime of this route */
+};
+struct aodv_rrep6 {
+ nd_uint8_t rrep_type; /* AODV message type (2) */
+ nd_uint8_t rrep_flags; /* various flags */
+ nd_uint8_t rrep_ps; /* prefix size */
+ nd_uint8_t rrep_hops; /* number of hops from o to d */
+ nd_ipv6 rrep_da; /* destination IPv6 address */
+ nd_uint32_t rrep_ds; /* destination sequence number */
+ nd_ipv6 rrep_oa; /* originator IPv6 address */
+ nd_uint32_t rrep_life; /* lifetime of this route */
+};
+struct aodv_rrep6_draft_01 {
+ nd_uint8_t rrep_type; /* AODV message type (17) */
+ nd_uint8_t rrep_flags; /* various flags */
+ nd_uint8_t rrep_ps; /* prefix size */
+ nd_uint8_t rrep_hops; /* number of hops from o to d */
+ nd_uint32_t rrep_ds; /* destination sequence number */
+ nd_ipv6 rrep_da; /* destination IPv6 address */
+ nd_ipv6 rrep_oa; /* originator IPv6 address */
+ nd_uint32_t rrep_life; /* lifetime of this route */
+};
+
+#define RREP_REPAIR 0x80 /* repair (reserved for multicast */
+#define RREP_ACK 0x40 /* acknowledgement required */
+#define RREP_FLAGS_MASK 0xC0 /* mask for rrep_flags */
+#define RREP_PREFIX_MASK 0x1F /* mask for prefix size */
+
+struct rerr_unreach {
+ nd_ipv4 u_da; /* IPv4 address */
+ nd_uint32_t u_ds; /* sequence number */
+};
+struct rerr_unreach6 {
+ nd_ipv6 u_da; /* IPv6 address */
+ nd_uint32_t u_ds; /* sequence number */
+};
+struct rerr_unreach6_draft_01 {
+ nd_ipv6 u_da; /* IPv6 address */
+ nd_uint32_t u_ds; /* sequence number */
+};
+
+struct aodv_rerr {
+ nd_uint8_t rerr_type; /* AODV message type (3 or 18) */
+ nd_uint8_t rerr_flags; /* various flags */
+ nd_uint8_t rerr_zero0; /* reserved, set to zero */
+ nd_uint8_t rerr_dc; /* destination count */
+};
+
+#define RERR_NODELETE 0x80 /* don't delete the link */
+#define RERR_FLAGS_MASK 0x80 /* mask for rerr_flags */
+
+struct aodv_rrep_ack {
+ nd_uint8_t ra_type;
+ nd_uint8_t ra_zero0;
+};
+
+#define AODV_RREQ 1 /* route request */
+#define AODV_RREP 2 /* route response */
+#define AODV_RERR 3 /* error report */
+#define AODV_RREP_ACK 4 /* route response acknowledgement */
+
+#define AODV_V6_DRAFT_01_RREQ 16 /* IPv6 route request */
+#define AODV_V6_DRAFT_01_RREP 17 /* IPv6 route response */
+#define AODV_V6_DRAFT_01_RERR 18 /* IPv6 error report */
+#define AODV_V6_DRAFT_01_RREP_ACK 19 /* IPV6 route response acknowledgment */
+
+struct aodv_ext {
+ nd_uint8_t type; /* extension type */
+ nd_uint8_t length; /* extension length */
+};
+
+struct aodv_hello {
+ struct aodv_ext eh; /* extension header */
+ nd_uint32_t interval; /* expect my next hello in
+ * (n) ms
+ * NOTE: this is not aligned */
+};
+
+#define AODV_EXT_HELLO 1
+
+static void
+aodv_extension(netdissect_options *ndo,
+ const struct aodv_ext *ep, u_int length)
+{
+ const struct aodv_hello *ah;
+
+ ND_TCHECK_SIZE(ep);
+ switch (GET_U_1(ep->type)) {
+ case AODV_EXT_HELLO:
+ ah = (const struct aodv_hello *)(const void *)ep;
+ ND_TCHECK_SIZE(ah);
+ if (length < sizeof(struct aodv_hello))
+ goto trunc;
+ if (GET_U_1(ep->length) < 4) {
+ ND_PRINT("\n\text HELLO - bad length %u",
+ GET_U_1(ep->length));
+ break;
+ }
+ ND_PRINT("\n\text HELLO %u ms",
+ GET_BE_U_4(ah->interval));
+ break;
+
+ default:
+ ND_PRINT("\n\text %u %u", GET_U_1(ep->type),
+ GET_U_1(ep->length));
+ break;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i;
+ const struct aodv_rreq *ap = (const struct aodv_rreq *)dat;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" rreq %u %s%s%s%s%shops %u id 0x%08x\n"
+ "\tdst %s seq %u src %s seq %u", length,
+ GET_U_1(ap->rreq_type) & RREQ_JOIN ? "[J]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_REPAIR ? "[R]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_GRAT ? "[G]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_DEST ? "[D]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_UNKNOWN ? "[U] " : " ",
+ GET_U_1(ap->rreq_hops),
+ GET_BE_U_4(ap->rreq_id),
+ GET_IPADDR_STRING(ap->rreq_da),
+ GET_BE_U_4(ap->rreq_ds),
+ GET_IPADDR_STRING(ap->rreq_oa),
+ GET_BE_U_4(ap->rreq_os));
+ i = length - sizeof(*ap);
+ if (i >= sizeof(struct aodv_ext))
+ aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i;
+ const struct aodv_rrep *ap = (const struct aodv_rrep *)dat;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" rrep %u %s%sprefix %u hops %u\n"
+ "\tdst %s dseq %u src %s %u ms", length,
+ GET_U_1(ap->rrep_type) & RREP_REPAIR ? "[R]" : "",
+ GET_U_1(ap->rrep_type) & RREP_ACK ? "[A] " : " ",
+ GET_U_1(ap->rrep_ps) & RREP_PREFIX_MASK,
+ GET_U_1(ap->rrep_hops),
+ GET_IPADDR_STRING(ap->rrep_da),
+ GET_BE_U_4(ap->rrep_ds),
+ GET_IPADDR_STRING(ap->rrep_oa),
+ GET_BE_U_4(ap->rrep_life));
+ i = length - sizeof(*ap);
+ if (i >= sizeof(struct aodv_ext))
+ aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i, dc;
+ const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
+ const struct rerr_unreach *dp;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" rerr %s [items %u] [%u]:",
+ GET_U_1(ap->rerr_flags) & RERR_NODELETE ? "[D]" : "",
+ GET_U_1(ap->rerr_dc), length);
+ dp = (const struct rerr_unreach *)(dat + sizeof(*ap));
+ i = length - sizeof(*ap);
+ for (dc = GET_U_1(ap->rerr_dc); dc != 0; dc--) {
+ ND_TCHECK_SIZE(dp);
+ if (i < sizeof(*dp))
+ goto trunc;
+ ND_PRINT(" {%s}(%u)", GET_IPADDR_STRING(dp->u_da),
+ GET_BE_U_4(dp->u_ds));
+ dp++;
+ i -= sizeof(*dp);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_v6_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i;
+ const struct aodv_rreq6 *ap = (const struct aodv_rreq6 *)dat;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" v6 rreq %u %s%s%s%s%shops %u id 0x%08x\n"
+ "\tdst %s seq %u src %s seq %u", length,
+ GET_U_1(ap->rreq_type) & RREQ_JOIN ? "[J]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_REPAIR ? "[R]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_GRAT ? "[G]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_DEST ? "[D]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_UNKNOWN ? "[U] " : " ",
+ GET_U_1(ap->rreq_hops),
+ GET_BE_U_4(ap->rreq_id),
+ GET_IP6ADDR_STRING(ap->rreq_da),
+ GET_BE_U_4(ap->rreq_ds),
+ GET_IP6ADDR_STRING(ap->rreq_oa),
+ GET_BE_U_4(ap->rreq_os));
+ i = length - sizeof(*ap);
+ if (i >= sizeof(struct aodv_ext))
+ aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_v6_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i;
+ const struct aodv_rrep6 *ap = (const struct aodv_rrep6 *)dat;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" rrep %u %s%sprefix %u hops %u\n"
+ "\tdst %s dseq %u src %s %u ms", length,
+ GET_U_1(ap->rrep_type) & RREP_REPAIR ? "[R]" : "",
+ GET_U_1(ap->rrep_type) & RREP_ACK ? "[A] " : " ",
+ GET_U_1(ap->rrep_ps) & RREP_PREFIX_MASK,
+ GET_U_1(ap->rrep_hops),
+ GET_IP6ADDR_STRING(ap->rrep_da),
+ GET_BE_U_4(ap->rrep_ds),
+ GET_IP6ADDR_STRING(ap->rrep_oa),
+ GET_BE_U_4(ap->rrep_life));
+ i = length - sizeof(*ap);
+ if (i >= sizeof(struct aodv_ext))
+ aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_v6_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i, dc;
+ const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
+ const struct rerr_unreach6 *dp6;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" rerr %s [items %u] [%u]:",
+ GET_U_1(ap->rerr_flags) & RERR_NODELETE ? "[D]" : "",
+ GET_U_1(ap->rerr_dc), length);
+ dp6 = (const struct rerr_unreach6 *)(const void *)(ap + 1);
+ i = length - sizeof(*ap);
+ for (dc = GET_U_1(ap->rerr_dc); dc != 0; dc--) {
+ ND_TCHECK_SIZE(dp6);
+ if (i < sizeof(*dp6))
+ goto trunc;
+ ND_PRINT(" {%s}(%u)", GET_IP6ADDR_STRING(dp6->u_da),
+ GET_BE_U_4(dp6->u_ds));
+ dp6++;
+ i -= sizeof(*dp6);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_v6_draft_01_rreq(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i;
+ const struct aodv_rreq6_draft_01 *ap = (const struct aodv_rreq6_draft_01 *)dat;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" rreq %u %s%s%s%s%shops %u id 0x%08x\n"
+ "\tdst %s seq %u src %s seq %u", length,
+ GET_U_1(ap->rreq_type) & RREQ_JOIN ? "[J]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_REPAIR ? "[R]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_GRAT ? "[G]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_DEST ? "[D]" : "",
+ GET_U_1(ap->rreq_type) & RREQ_UNKNOWN ? "[U] " : " ",
+ GET_U_1(ap->rreq_hops),
+ GET_BE_U_4(ap->rreq_id),
+ GET_IP6ADDR_STRING(ap->rreq_da),
+ GET_BE_U_4(ap->rreq_ds),
+ GET_IP6ADDR_STRING(ap->rreq_oa),
+ GET_BE_U_4(ap->rreq_os));
+ i = length - sizeof(*ap);
+ if (i >= sizeof(struct aodv_ext))
+ aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_v6_draft_01_rrep(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i;
+ const struct aodv_rrep6_draft_01 *ap = (const struct aodv_rrep6_draft_01 *)dat;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" rrep %u %s%sprefix %u hops %u\n"
+ "\tdst %s dseq %u src %s %u ms", length,
+ GET_U_1(ap->rrep_type) & RREP_REPAIR ? "[R]" : "",
+ GET_U_1(ap->rrep_type) & RREP_ACK ? "[A] " : " ",
+ GET_U_1(ap->rrep_ps) & RREP_PREFIX_MASK,
+ GET_U_1(ap->rrep_hops),
+ GET_IP6ADDR_STRING(ap->rrep_da),
+ GET_BE_U_4(ap->rrep_ds),
+ GET_IP6ADDR_STRING(ap->rrep_oa),
+ GET_BE_U_4(ap->rrep_life));
+ i = length - sizeof(*ap);
+ if (i >= sizeof(struct aodv_ext))
+ aodv_extension(ndo, (const struct aodv_ext *)(dat + sizeof(*ap)), i);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+aodv_v6_draft_01_rerr(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i, dc;
+ const struct aodv_rerr *ap = (const struct aodv_rerr *)dat;
+ const struct rerr_unreach6_draft_01 *dp6;
+
+ ND_TCHECK_SIZE(ap);
+ if (length < sizeof(*ap))
+ goto trunc;
+ ND_PRINT(" rerr %s [items %u] [%u]:",
+ GET_U_1(ap->rerr_flags) & RERR_NODELETE ? "[D]" : "",
+ GET_U_1(ap->rerr_dc), length);
+ dp6 = (const struct rerr_unreach6_draft_01 *)(const void *)(ap + 1);
+ i = length - sizeof(*ap);
+ for (dc = GET_U_1(ap->rerr_dc); dc != 0; dc--) {
+ ND_TCHECK_SIZE(dp6);
+ if (i < sizeof(*dp6))
+ goto trunc;
+ ND_PRINT(" {%s}(%u)", GET_IP6ADDR_STRING(dp6->u_da),
+ GET_BE_U_4(dp6->u_ds));
+ dp6++;
+ i -= sizeof(*dp6);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+aodv_print(netdissect_options *ndo,
+ const u_char *dat, u_int length, int is_ip6)
+{
+ uint8_t msg_type;
+
+ ndo->ndo_protocol = "aodv";
+ /*
+ * The message type is the first byte; make sure we have it
+ * and then fetch it.
+ */
+ msg_type = GET_U_1(dat);
+ ND_PRINT(" aodv");
+
+ switch (msg_type) {
+
+ case AODV_RREQ:
+ if (is_ip6)
+ aodv_v6_rreq(ndo, dat, length);
+ else
+ aodv_rreq(ndo, dat, length);
+ break;
+
+ case AODV_RREP:
+ if (is_ip6)
+ aodv_v6_rrep(ndo, dat, length);
+ else
+ aodv_rrep(ndo, dat, length);
+ break;
+
+ case AODV_RERR:
+ if (is_ip6)
+ aodv_v6_rerr(ndo, dat, length);
+ else
+ aodv_rerr(ndo, dat, length);
+ break;
+
+ case AODV_RREP_ACK:
+ ND_PRINT(" rrep-ack %u", length);
+ break;
+
+ case AODV_V6_DRAFT_01_RREQ:
+ aodv_v6_draft_01_rreq(ndo, dat, length);
+ break;
+
+ case AODV_V6_DRAFT_01_RREP:
+ aodv_v6_draft_01_rrep(ndo, dat, length);
+ break;
+
+ case AODV_V6_DRAFT_01_RERR:
+ aodv_v6_draft_01_rerr(ndo, dat, length);
+ break;
+
+ case AODV_V6_DRAFT_01_RREP_ACK:
+ ND_PRINT(" rrep-ack %u", length);
+ break;
+
+ default:
+ ND_PRINT(" type %u %u", msg_type, length);
+ }
+}
diff --git a/print-aoe.c b/print-aoe.c
new file mode 100644
index 0000000..c4f3758
--- /dev/null
+++ b/print-aoe.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (c) 2014 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: ATA over Ethernet (AoE) protocol printer */
+
+/* specification:
+ * https://web.archive.org/web/20161025044402/http://brantleycoilecompany.com/AoEr11.pdf
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+#define AOE_V1 1
+#define ATA_SECTOR_SIZE 512
+
+#define AOEV1_CMD_ISSUE_ATA_COMMAND 0
+#define AOEV1_CMD_QUERY_CONFIG_INFORMATION 1
+#define AOEV1_CMD_MAC_MASK_LIST 2
+#define AOEV1_CMD_RESERVE_RELEASE 3
+
+static const struct tok cmdcode_str[] = {
+ { AOEV1_CMD_ISSUE_ATA_COMMAND, "Issue ATA Command" },
+ { AOEV1_CMD_QUERY_CONFIG_INFORMATION, "Query Config Information" },
+ { AOEV1_CMD_MAC_MASK_LIST, "MAC Mask List" },
+ { AOEV1_CMD_RESERVE_RELEASE, "Reserve/Release" },
+ { 0, NULL }
+};
+
+#define AOEV1_COMMON_HDR_LEN 10U /* up to but w/o Arg */
+#define AOEV1_ISSUE_ARG_LEN 12U /* up to but w/o Data */
+#define AOEV1_QUERY_ARG_LEN 8U /* up to but w/o Config String */
+#define AOEV1_MAC_ARG_LEN 4U /* up to but w/o Directive 0 */
+#define AOEV1_RESERVE_ARG_LEN 2U /* up to but w/o Ethernet address 0 */
+#define AOEV1_MAX_CONFSTR_LEN 1024U
+
+#define AOEV1_FLAG_R 0x08
+#define AOEV1_FLAG_E 0x04
+
+static const struct tok aoev1_flag_str[] = {
+ { AOEV1_FLAG_R, "Response" },
+ { AOEV1_FLAG_E, "Error" },
+ { 0x02, "MBZ-1" },
+ { 0x01, "MBZ-0" },
+ { 0, NULL }
+};
+
+static const struct tok aoev1_errcode_str[] = {
+ { 1, "Unrecognized command code" },
+ { 2, "Bad argument parameter" },
+ { 3, "Device unavailable" },
+ { 4, "Config string present" },
+ { 5, "Unsupported version" },
+ { 6, "Target is reserved" },
+ { 0, NULL }
+};
+
+#define AOEV1_AFLAG_E 0x40
+#define AOEV1_AFLAG_D 0x10
+#define AOEV1_AFLAG_A 0x02
+#define AOEV1_AFLAG_W 0x01
+
+static const struct tok aoev1_aflag_bitmap_str[] = {
+ { 0x80, "MBZ-7" },
+ { AOEV1_AFLAG_E, "Ext48" },
+ { 0x20, "MBZ-5" },
+ { AOEV1_AFLAG_D, "Device" },
+ { 0x08, "MBZ-3" },
+ { 0x04, "MBZ-2" },
+ { AOEV1_AFLAG_A, "Async" },
+ { AOEV1_AFLAG_W, "Write" },
+ { 0, NULL }
+};
+
+static const struct tok aoev1_ccmd_str[] = {
+ { 0, "read config string" },
+ { 1, "test config string" },
+ { 2, "test config string prefix" },
+ { 3, "set config string" },
+ { 4, "force set config string" },
+ { 0, NULL }
+};
+
+static const struct tok aoev1_mcmd_str[] = {
+ { 0, "Read Mac Mask List" },
+ { 1, "Edit Mac Mask List" },
+ { 0, NULL }
+};
+
+static const struct tok aoev1_merror_str[] = {
+ { 1, "Unspecified Error" },
+ { 2, "Bad DCmd directive" },
+ { 3, "Mask list full" },
+ { 0, NULL }
+};
+
+static const struct tok aoev1_dcmd_str[] = {
+ { 0, "No Directive" },
+ { 1, "Add mac address to mask list" },
+ { 2, "Delete mac address from mask list" },
+ { 0, NULL }
+};
+
+static const struct tok aoev1_rcmd_str[] = {
+ { 0, "Read reserve list" },
+ { 1, "Set reserve list" },
+ { 2, "Force set reserve list" },
+ { 0, NULL }
+};
+
+static void
+aoev1_issue_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ if (len < AOEV1_ISSUE_ARG_LEN)
+ goto invalid;
+ /* AFlags */
+ ND_PRINT("\n\tAFlags: [%s]",
+ bittok2str(aoev1_aflag_bitmap_str, "none", GET_U_1(cp)));
+ cp += 1;
+ len -= 1;
+ /* Err/Feature */
+ ND_PRINT(", Err/Feature: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* Sector Count (not correlated with the length) */
+ ND_PRINT(", Sector Count: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* Cmd/Status */
+ ND_PRINT(", Cmd/Status: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* lba0 */
+ ND_PRINT("\n\tlba0: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* lba1 */
+ ND_PRINT(", lba1: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* lba2 */
+ ND_PRINT(", lba2: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* lba3 */
+ ND_PRINT(", lba3: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* lba4 */
+ ND_PRINT(", lba4: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* lba5 */
+ ND_PRINT(", lba5: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* Reserved */
+ ND_TCHECK_2(cp);
+ cp += 2;
+ len -= 2;
+ /* Data */
+ if (len)
+ ND_PRINT("\n\tData: %u bytes", len);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+aoev1_query_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t cslen;
+
+ if (len < AOEV1_QUERY_ARG_LEN)
+ goto invalid;
+ /* Buffer Count */
+ ND_PRINT("\n\tBuffer Count: %u", GET_BE_U_2(cp));
+ cp += 2;
+ len -= 2;
+ /* Firmware Version */
+ ND_PRINT(", Firmware Version: %u", GET_BE_U_2(cp));
+ cp += 2;
+ len -= 2;
+ /* Sector Count */
+ ND_PRINT(", Sector Count: %u", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* AoE/CCmd */
+ ND_PRINT(", AoE: %u, CCmd: %s", (GET_U_1(cp) & 0xF0) >> 4,
+ tok2str(aoev1_ccmd_str, "Unknown (0x02x)", GET_U_1(cp) & 0x0F));
+ cp += 1;
+ len -= 1;
+ /* Config String Length */
+ cslen = GET_BE_U_2(cp);
+ cp += 2;
+ len -= 2;
+ if (cslen > AOEV1_MAX_CONFSTR_LEN || cslen > len)
+ goto invalid;
+ /* Config String */
+ if (cslen) {
+ ND_PRINT("\n\tConfig String (length %u): ", cslen);
+ (void)nd_printn(ndo, cp, cslen, NULL);
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+aoev1_mac_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint8_t dircount, i;
+
+ if (len < AOEV1_MAC_ARG_LEN)
+ goto invalid;
+ /* Reserved */
+ cp += 1;
+ len -= 1;
+ /* MCmd */
+ ND_PRINT("\n\tMCmd: %s",
+ tok2str(aoev1_mcmd_str, "Unknown (0x%02x)", GET_U_1(cp)));
+ cp += 1;
+ len -= 1;
+ /* MError */
+ ND_PRINT(", MError: %s",
+ tok2str(aoev1_merror_str, "Unknown (0x%02x)", GET_U_1(cp)));
+ cp += 1;
+ len -= 1;
+ /* Dir Count */
+ dircount = GET_U_1(cp);
+ cp += 1;
+ len -= 1;
+ ND_PRINT(", Dir Count: %u", dircount);
+ if (dircount * 8 > len)
+ goto invalid;
+ /* directives */
+ for (i = 0; i < dircount; i++) {
+ /* Reserved */
+ cp += 1;
+ len -= 1;
+ /* DCmd */
+ ND_PRINT("\n\t DCmd: %s",
+ tok2str(aoev1_dcmd_str, "Unknown (0x%02x)", GET_U_1(cp)));
+ cp += 1;
+ len -= 1;
+ /* Ethernet Address */
+ ND_PRINT(", Ethernet Address: %s", GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ len -= MAC_ADDR_LEN;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+aoev1_reserve_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint8_t nmacs, i;
+
+ if (len < AOEV1_RESERVE_ARG_LEN || (len - AOEV1_RESERVE_ARG_LEN) % MAC_ADDR_LEN)
+ goto invalid;
+ /* RCmd */
+ ND_PRINT("\n\tRCmd: %s",
+ tok2str(aoev1_rcmd_str, "Unknown (0x%02x)", GET_U_1(cp)));
+ cp += 1;
+ len -= 1;
+ /* NMacs (correlated with the length) */
+ nmacs = GET_U_1(cp);
+ cp += 1;
+ len -= 1;
+ ND_PRINT(", NMacs: %u", nmacs);
+ if (nmacs * MAC_ADDR_LEN != len)
+ goto invalid;
+ /* addresses */
+ for (i = 0; i < nmacs; i++) {
+ ND_PRINT("\n\tEthernet Address %u: %s", i, GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ len -= MAC_ADDR_LEN;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* cp points to the Ver/Flags octet */
+static void
+aoev1_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint8_t flags, command;
+ void (*cmd_decoder)(netdissect_options *, const u_char *, u_int);
+
+ if (len < AOEV1_COMMON_HDR_LEN)
+ goto invalid;
+ /* Flags */
+ flags = GET_U_1(cp) & 0x0F;
+ ND_PRINT(", Flags: [%s]", bittok2str(aoev1_flag_str, "none", flags));
+ cp += 1;
+ len -= 1;
+ if (! ndo->ndo_vflag)
+ return;
+ /* Error */
+ if (flags & AOEV1_FLAG_E)
+ ND_PRINT("\n\tError: %s",
+ tok2str(aoev1_errcode_str, "Invalid (%u)", GET_U_1(cp)));
+ cp += 1;
+ len -= 1;
+ /* Major */
+ ND_PRINT("\n\tMajor: 0x%04x", GET_BE_U_2(cp));
+ cp += 2;
+ len -= 2;
+ /* Minor */
+ ND_PRINT(", Minor: 0x%02x", GET_U_1(cp));
+ cp += 1;
+ len -= 1;
+ /* Command */
+ command = GET_U_1(cp);
+ cp += 1;
+ len -= 1;
+ ND_PRINT(", Command: %s", tok2str(cmdcode_str, "Unknown (0x%02x)", command));
+ /* Tag */
+ ND_PRINT(", Tag: 0x%08x", GET_BE_U_4(cp));
+ cp += 4;
+ len -= 4;
+ /* Arg */
+ cmd_decoder =
+ command == AOEV1_CMD_ISSUE_ATA_COMMAND ? aoev1_issue_print :
+ command == AOEV1_CMD_QUERY_CONFIG_INFORMATION ? aoev1_query_print :
+ command == AOEV1_CMD_MAC_MASK_LIST ? aoev1_mac_print :
+ command == AOEV1_CMD_RESERVE_RELEASE ? aoev1_reserve_print :
+ NULL;
+ if (cmd_decoder != NULL)
+ cmd_decoder(ndo, cp, len);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+void
+aoe_print(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ uint8_t ver;
+
+ ndo->ndo_protocol = "aoe";
+ ND_PRINT("AoE length %u", len);
+
+ if (len < 1)
+ goto invalid;
+ /* Ver/Flags */
+ ver = (GET_U_1(cp) & 0xF0) >> 4;
+ /* Don't advance cp yet: low order 4 bits are version-specific. */
+ ND_PRINT(", Ver %u", ver);
+
+ switch (ver) {
+ case AOE_V1:
+ aoev1_print(ndo, cp, len);
+ break;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
diff --git a/print-ap1394.c b/print-ap1394.c
new file mode 100644
index 0000000..b1988f2
--- /dev/null
+++ b/print-ap1394.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Apple IP-over-IEEE 1394 printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+
+/*
+ * Structure of a header for Apple's IP-over-IEEE 1384 BPF header.
+ */
+#define FIREWIRE_EUI64_LEN 8
+struct firewire_header {
+ nd_byte firewire_dhost[FIREWIRE_EUI64_LEN];
+ nd_byte firewire_shost[FIREWIRE_EUI64_LEN];
+ nd_uint16_t firewire_type;
+};
+
+/*
+ * Length of that header; note that some compilers may pad
+ * "struct firewire_header" to a multiple of 4 bytes, for example, so
+ * "sizeof (struct firewire_header)" may not give the right answer.
+ */
+#define FIREWIRE_HDRLEN 18
+
+static const char *
+fwaddr_string(netdissect_options *ndo, const u_char *addr)
+{
+ return GET_LINKADDR_STRING(addr, LINKADDR_IEEE1394, FIREWIRE_EUI64_LEN);
+}
+
+static void
+ap1394_hdr_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const struct firewire_header *fp;
+ uint16_t firewire_type;
+
+ fp = (const struct firewire_header *)bp;
+
+ ND_PRINT("%s > %s",
+ fwaddr_string(ndo, fp->firewire_shost),
+ fwaddr_string(ndo, fp->firewire_dhost));
+
+ firewire_type = GET_BE_U_2(fp->firewire_type);
+ if (!ndo->ndo_qflag) {
+ ND_PRINT(", ethertype %s (0x%04x)",
+ tok2str(ethertype_values,"Unknown", firewire_type),
+ firewire_type);
+ } else {
+ ND_PRINT(", %s", tok2str(ethertype_values,"Unknown Ethertype (0x%04x)", firewire_type));
+ }
+
+ ND_PRINT(", length %u: ", length);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+ap1394_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+ const struct firewire_header *fp;
+ u_short ether_type;
+ struct lladdr_info src, dst;
+
+ ndo->ndo_protocol = "ap1394";
+ ND_TCHECK_LEN(p, FIREWIRE_HDRLEN);
+ ndo->ndo_ll_hdr_len += FIREWIRE_HDRLEN;
+
+ if (ndo->ndo_eflag)
+ ap1394_hdr_print(ndo, p, length);
+
+ length -= FIREWIRE_HDRLEN;
+ caplen -= FIREWIRE_HDRLEN;
+ fp = (const struct firewire_header *)p;
+ p += FIREWIRE_HDRLEN;
+
+ ether_type = GET_BE_U_2(fp->firewire_type);
+ src.addr = fp->firewire_shost;
+ src.addr_string = fwaddr_string;
+ dst.addr = fp->firewire_dhost;
+ dst.addr_string = fwaddr_string;
+ if (ethertype_print(ndo, ether_type, p, length, caplen, &src, &dst) == 0) {
+ /* ether_type not known, print raw packet */
+ if (!ndo->ndo_eflag)
+ ap1394_hdr_print(ndo, (const u_char *)fp, length + FIREWIRE_HDRLEN);
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+}
diff --git a/print-arcnet.c b/print-arcnet.c
new file mode 100644
index 0000000..5f6aaf9
--- /dev/null
+++ b/print-arcnet.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * From: NetBSD: print-arcnet.c,v 1.2 2000/04/24 13:02:28 itojun Exp
+ */
+
+/* \summary: Attached Resource Computer NETwork (ARCNET) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+/*
+ * from: NetBSD: if_arc.h,v 1.13 1999/11/19 20:41:19 thorpej Exp
+ */
+
+/*
+ * Structure of a 2.5MB/s Arcnet header on the BSDs,
+ * as given to interface code.
+ */
+struct arc_header {
+ nd_uint8_t arc_shost;
+ nd_uint8_t arc_dhost;
+ nd_uint8_t arc_type;
+ /*
+ * only present for newstyle encoding with LL fragmentation.
+ * Don't use sizeof(anything), use ARC_HDR{,NEW}LEN instead.
+ */
+ nd_uint8_t arc_flag;
+ nd_uint16_t arc_seqid;
+
+ /*
+ * only present in exception packets (arc_flag == 0xff)
+ */
+ nd_uint8_t arc_type2; /* same as arc_type */
+ nd_uint8_t arc_flag2; /* real flag value */
+ nd_uint16_t arc_seqid2; /* real seqid value */
+};
+
+#define ARC_HDRLEN 3
+#define ARC_HDRNEWLEN 6
+#define ARC_HDRNEWLEN_EXC 10
+
+/* RFC 1051 */
+#define ARCTYPE_IP_OLD 240 /* IP protocol */
+#define ARCTYPE_ARP_OLD 241 /* address resolution protocol */
+
+/* RFC 1201 */
+#define ARCTYPE_IP 212 /* IP protocol */
+#define ARCTYPE_ARP 213 /* address resolution protocol */
+#define ARCTYPE_REVARP 214 /* reverse addr resolution protocol */
+
+#define ARCTYPE_ATALK 221 /* Appletalk */
+#define ARCTYPE_BANIAN 247 /* Banyan Vines */
+#define ARCTYPE_IPX 250 /* Novell IPX */
+
+#define ARCTYPE_INET6 0xc4 /* IPng */
+#define ARCTYPE_DIAGNOSE 0x80 /* as per ANSI/ATA 878.1 */
+
+/*
+ * Structure of a 2.5MB/s Arcnet header on Linux. Linux has
+ * an extra "offset" field when given to interface code, and
+ * never presents packets that look like exception frames.
+ */
+struct arc_linux_header {
+ nd_uint8_t arc_shost;
+ nd_uint8_t arc_dhost;
+ nd_uint16_t arc_offset;
+ nd_uint8_t arc_type;
+ /*
+ * only present for newstyle encoding with LL fragmentation.
+ * Don't use sizeof(anything), use ARC_LINUX_HDR{,NEW}LEN
+ * instead.
+ */
+ nd_uint8_t arc_flag;
+ nd_uint16_t arc_seqid;
+};
+
+#define ARC_LINUX_HDRLEN 5
+#define ARC_LINUX_HDRNEWLEN 8
+
+static int arcnet_encap_print(netdissect_options *, u_char arctype, const u_char *p,
+ u_int length, u_int caplen);
+
+static const struct tok arctypemap[] = {
+ { ARCTYPE_IP_OLD, "oldip" },
+ { ARCTYPE_ARP_OLD, "oldarp" },
+ { ARCTYPE_IP, "ip" },
+ { ARCTYPE_ARP, "arp" },
+ { ARCTYPE_REVARP, "rarp" },
+ { ARCTYPE_ATALK, "atalk" },
+ { ARCTYPE_BANIAN, "banyan" },
+ { ARCTYPE_IPX, "ipx" },
+ { ARCTYPE_INET6, "ipv6" },
+ { ARCTYPE_DIAGNOSE, "diag" },
+ { 0, NULL }
+};
+
+static void
+arcnet_print(netdissect_options *ndo, const u_char *bp, u_int length, int phds,
+ u_int flag, u_int seqid)
+{
+ const struct arc_header *ap;
+ const char *arctypename;
+
+ ndo->ndo_protocol = "arcnet";
+ ap = (const struct arc_header *)bp;
+
+ if (ndo->ndo_qflag) {
+ ND_PRINT("%02x %02x %u: ",
+ GET_U_1(ap->arc_shost),
+ GET_U_1(ap->arc_dhost),
+ length);
+ return;
+ }
+
+ arctypename = tok2str(arctypemap, "%02x", GET_U_1(ap->arc_type));
+
+ if (!phds) {
+ ND_PRINT("%02x %02x %s %u: ",
+ GET_U_1(ap->arc_shost),
+ GET_U_1(ap->arc_dhost),
+ arctypename,
+ length);
+ return;
+ }
+
+ if (flag == 0) {
+ ND_PRINT("%02x %02x %s seqid %04x %u: ",
+ GET_U_1(ap->arc_shost),
+ GET_U_1(ap->arc_dhost),
+ arctypename, seqid,
+ length);
+ return;
+ }
+
+ if (flag & 1)
+ ND_PRINT("%02x %02x %s seqid %04x "
+ "(first of %u fragments) %u: ",
+ GET_U_1(ap->arc_shost),
+ GET_U_1(ap->arc_dhost),
+ arctypename, seqid,
+ (flag + 3) / 2, length);
+ else
+ ND_PRINT("%02x %02x %s seqid %04x "
+ "(fragment %u) %u: ",
+ GET_U_1(ap->arc_shost),
+ GET_U_1(ap->arc_dhost),
+ arctypename, seqid,
+ flag/2 + 1, length);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ARCNET header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+arcnet_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ const struct arc_header *ap;
+
+ int phds;
+ u_int flag = 0, archdrlen = 0;
+ u_int seqid = 0;
+ u_char arc_type;
+
+ ndo->ndo_protocol = "arcnet";
+ if (caplen < ARC_HDRLEN) {
+ ndo->ndo_ll_hdr_len += caplen;
+ nd_trunc_longjmp(ndo);
+ }
+
+ ap = (const struct arc_header *)p;
+ arc_type = GET_U_1(ap->arc_type);
+
+ switch (arc_type) {
+ default:
+ phds = 1;
+ break;
+ case ARCTYPE_IP_OLD:
+ case ARCTYPE_ARP_OLD:
+ case ARCTYPE_DIAGNOSE:
+ phds = 0;
+ archdrlen = ARC_HDRLEN;
+ break;
+ }
+
+ if (phds) {
+ if (caplen < ARC_HDRNEWLEN) {
+ arcnet_print(ndo, p, length, 0, 0, 0);
+ ND_PRINT(" phds");
+ ndo->ndo_ll_hdr_len += caplen;
+ nd_trunc_longjmp(ndo);
+ }
+
+ flag = GET_U_1(ap->arc_flag);
+ if (flag == 0xff) {
+ if (caplen < ARC_HDRNEWLEN_EXC) {
+ arcnet_print(ndo, p, length, 0, 0, 0);
+ ND_PRINT(" phds extended");
+ ndo->ndo_ll_hdr_len += caplen;
+ nd_trunc_longjmp(ndo);
+ }
+ flag = GET_U_1(ap->arc_flag2);
+ seqid = GET_BE_U_2(ap->arc_seqid2);
+ archdrlen = ARC_HDRNEWLEN_EXC;
+ } else {
+ seqid = GET_BE_U_2(ap->arc_seqid);
+ archdrlen = ARC_HDRNEWLEN;
+ }
+ }
+
+
+ if (ndo->ndo_eflag)
+ arcnet_print(ndo, p, length, phds, flag, seqid);
+
+ /*
+ * Go past the ARCNET header.
+ */
+ length -= archdrlen;
+ caplen -= archdrlen;
+ p += archdrlen;
+
+ if (phds && flag && (flag & 1) == 0) {
+ /*
+ * This is a middle fragment.
+ */
+ ndo->ndo_ll_hdr_len += archdrlen;
+ return;
+ }
+
+ if (!arcnet_encap_print(ndo, arc_type, p, length, caplen))
+ ND_DEFAULTPRINT(p, caplen);
+
+ ndo->ndo_ll_hdr_len += archdrlen;
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ARCNET header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured. It is quite similar
+ * to the non-Linux style printer except that Linux doesn't ever
+ * supply packets that look like exception frames, it always supplies
+ * reassembled packets rather than raw frames, and headers have an
+ * extra "offset" field between the src/dest and packet type.
+ */
+void
+arcnet_linux_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ const struct arc_linux_header *ap;
+
+ int archdrlen = 0;
+ u_char arc_type;
+
+ ndo->ndo_protocol = "arcnet_linux";
+ if (caplen < ARC_LINUX_HDRLEN) {
+ ndo->ndo_ll_hdr_len += caplen;
+ nd_trunc_longjmp(ndo);
+ }
+
+ ap = (const struct arc_linux_header *)p;
+ arc_type = GET_U_1(ap->arc_type);
+
+ switch (arc_type) {
+ default:
+ archdrlen = ARC_LINUX_HDRNEWLEN;
+ if (caplen < ARC_LINUX_HDRNEWLEN) {
+ ndo->ndo_ll_hdr_len += caplen;
+ nd_trunc_longjmp(ndo);
+ }
+ break;
+ case ARCTYPE_IP_OLD:
+ case ARCTYPE_ARP_OLD:
+ case ARCTYPE_DIAGNOSE:
+ archdrlen = ARC_LINUX_HDRLEN;
+ break;
+ }
+
+ if (ndo->ndo_eflag)
+ arcnet_print(ndo, p, length, 0, 0, 0);
+
+ /*
+ * Go past the ARCNET header.
+ */
+ length -= archdrlen;
+ caplen -= archdrlen;
+ p += archdrlen;
+
+ if (!arcnet_encap_print(ndo, arc_type, p, length, caplen))
+ ND_DEFAULTPRINT(p, caplen);
+
+ ndo->ndo_ll_hdr_len += archdrlen;
+}
+
+/*
+ * Prints the packet encapsulated in an ARCnet data field,
+ * given the ARCnet system code.
+ *
+ * Returns non-zero if it can do so, zero if the system code is unknown.
+ */
+
+
+static int
+arcnet_encap_print(netdissect_options *ndo, u_char arctype, const u_char *p,
+ u_int length, u_int caplen)
+{
+ switch (arctype) {
+
+ case ARCTYPE_IP_OLD:
+ case ARCTYPE_IP:
+ ip_print(ndo, p, length);
+ return (1);
+
+ case ARCTYPE_INET6:
+ ip6_print(ndo, p, length);
+ return (1);
+
+ case ARCTYPE_ARP_OLD:
+ case ARCTYPE_ARP:
+ case ARCTYPE_REVARP:
+ arp_print(ndo, p, length, caplen);
+ return (1);
+
+ case ARCTYPE_ATALK: /* XXX was this ever used? */
+ if (ndo->ndo_vflag)
+ ND_PRINT("et1 ");
+ atalk_print(ndo, p, length);
+ return (1);
+
+ case ARCTYPE_IPX:
+ ipx_print(ndo, p, length);
+ return (1);
+
+ default:
+ return (0);
+ }
+}
diff --git a/print-arista.c b/print-arista.c
new file mode 100644
index 0000000..0d1d5d9
--- /dev/null
+++ b/print-arista.c
@@ -0,0 +1,92 @@
+// Copyright (c) 2018 Arista Networks, Inc. All rights reserved.
+
+/* \summary: EtherType protocol for Arista Networks printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+#define ARISTA_SUBTYPE_TIMESTAMP 0x01
+
+#define ARISTA_TIMESTAMP_64_TAI 0x0010
+#define ARISTA_TIMESTAMP_64_UTC 0x0110
+#define ARISTA_TIMESTAMP_48_TAI 0x0020
+#define ARISTA_TIMESTAMP_48_UTC 0x0120
+
+static const struct tok ts_version_name[] = {
+ { ARISTA_TIMESTAMP_64_TAI, "TAI(64-bit)" },
+ { ARISTA_TIMESTAMP_64_UTC, "UTC(64-bit)" },
+ { ARISTA_TIMESTAMP_48_TAI, "TAI(48-bit)" },
+ { ARISTA_TIMESTAMP_48_UTC, "UTC(48-bit)" },
+ { 0, NULL }
+};
+
+static inline void
+arista_print_date_hms_time(netdissect_options *ndo, uint32_t seconds,
+ uint32_t nanoseconds)
+{
+ time_t ts;
+ struct tm *tm;
+ char buf[BUFSIZE];
+
+ ts = seconds + (nanoseconds / 1000000000);
+ if (NULL == (tm = gmtime(&ts)))
+ ND_PRINT(": gmtime() error");
+ else if (0 == strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm))
+ ND_PRINT(": strftime() error");
+ else
+ ND_PRINT(": %s, %09u ns, ", buf, nanoseconds);
+}
+
+int
+arista_ethertype_print(netdissect_options *ndo, const u_char *bp, u_int len _U_)
+{
+ uint16_t subTypeId;
+ uint16_t version;
+ u_short bytesConsumed = 0;
+ u_short size = 0;
+ uint32_t seconds, nanoseconds;
+
+ ndo->ndo_protocol = "arista";
+
+ subTypeId = GET_BE_U_2(bp);
+ bp += 2;
+ version = GET_BE_U_2(bp);
+ bp += 2;
+ bytesConsumed += 4;
+
+ ND_PRINT("SubType: 0x%1x, Version: 0x%04x, ", subTypeId, version);
+
+ // TapAgg Header Timestamping
+ if (subTypeId == ARISTA_SUBTYPE_TIMESTAMP) {
+ // Timestamp has 32-bit lsb in nanosec and remaining msb in sec
+ ND_PRINT("Timestamp %s", tok2str(ts_version_name,
+ "Unknown timestamp Version 0x%04x ", version));
+ switch (version) {
+ case ARISTA_TIMESTAMP_64_TAI:
+ case ARISTA_TIMESTAMP_64_UTC:
+ seconds = GET_BE_U_4(bp);
+ nanoseconds = GET_BE_U_4(bp + 4);
+ arista_print_date_hms_time(ndo, seconds, nanoseconds);
+ bytesConsumed += size + 8;
+ break;
+ case ARISTA_TIMESTAMP_48_TAI:
+ case ARISTA_TIMESTAMP_48_UTC:
+ ND_PRINT(": Seconds %u,", GET_BE_U_2(bp));
+ ND_PRINT(" Nanoseconds %u, ", GET_BE_U_4(bp + 2));
+ bytesConsumed += size + 6;
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+ return bytesConsumed;
+}
diff --git a/print-arp.c b/print-arp.c
new file mode 100644
index 0000000..4855332
--- /dev/null
+++ b/print-arp.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Address Resolution Protocol (ARP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+#include "extract.h"
+
+
+/*
+ * Address Resolution Protocol.
+ *
+ * See RFC 826 for protocol description. ARP packets are variable
+ * in size; the arphdr structure defines the fixed-length portion.
+ * Protocol type values are the same as those for 10 Mb/s Ethernet.
+ * It is followed by the variable-sized fields ar_sha, arp_spa,
+ * arp_tha and arp_tpa in that order, according to the lengths
+ * specified. Field names used correspond to RFC 826.
+ */
+struct arp_pkthdr {
+ nd_uint16_t ar_hrd; /* format of hardware address */
+#define ARPHRD_ETHER 1 /* ethernet hardware format */
+#define ARPHRD_IEEE802 6 /* token-ring hardware format */
+#define ARPHRD_ARCNET 7 /* arcnet hardware format */
+#define ARPHRD_FRELAY 15 /* frame relay hardware format */
+#define ARPHRD_ATM2225 19 /* ATM (RFC 2225) */
+#define ARPHRD_STRIP 23 /* Ricochet Starmode Radio hardware format */
+#define ARPHRD_IEEE1394 24 /* IEEE 1394 (FireWire) hardware format */
+#define ARPHRD_INFINIBAND 32 /* InfiniBand RFC 4391 */
+ nd_uint16_t ar_pro; /* format of protocol address */
+ nd_uint8_t ar_hln; /* length of hardware address */
+ nd_uint8_t ar_pln; /* length of protocol address */
+ nd_uint16_t ar_op; /* one of: */
+#define ARPOP_REQUEST 1 /* request to resolve address */
+#define ARPOP_REPLY 2 /* response to previous request */
+#define ARPOP_REVREQUEST 3 /* request protocol address given hardware */
+#define ARPOP_REVREPLY 4 /* response giving protocol address */
+#define ARPOP_INVREQUEST 8 /* request to identify peer */
+#define ARPOP_INVREPLY 9 /* response identifying peer */
+#define ARPOP_NAK 10 /* NAK - only valif for ATM ARP */
+
+/*
+ * The remaining fields are variable in size,
+ * according to the sizes above.
+ */
+#ifdef COMMENT_ONLY
+ nd_byte ar_sha[]; /* sender hardware address */
+ nd_byte ar_spa[]; /* sender protocol address */
+ nd_byte ar_tha[]; /* target hardware address */
+ nd_byte ar_tpa[]; /* target protocol address */
+#endif
+#define ar_sha(ap) (((const u_char *)((ap)+1))+ 0)
+#define ar_spa(ap) (((const u_char *)((ap)+1))+ GET_U_1((ap)->ar_hln))
+#define ar_tha(ap) (((const u_char *)((ap)+1))+ GET_U_1((ap)->ar_hln)+GET_U_1((ap)->ar_pln))
+#define ar_tpa(ap) (((const u_char *)((ap)+1))+2*GET_U_1((ap)->ar_hln)+GET_U_1((ap)->ar_pln))
+};
+
+#define ARP_HDRLEN 8
+
+#define HRD(ap) GET_BE_U_2((ap)->ar_hrd)
+#define HRD_LEN(ap) GET_U_1((ap)->ar_hln)
+#define PROTO_LEN(ap) GET_U_1((ap)->ar_pln)
+#define OP(ap) GET_BE_U_2((ap)->ar_op)
+#define PRO(ap) GET_BE_U_2((ap)->ar_pro)
+#define SHA(ap) (ar_sha(ap))
+#define SPA(ap) (ar_spa(ap))
+#define THA(ap) (ar_tha(ap))
+#define TPA(ap) (ar_tpa(ap))
+
+
+static const struct tok arpop_values[] = {
+ { ARPOP_REQUEST, "Request" },
+ { ARPOP_REPLY, "Reply" },
+ { ARPOP_REVREQUEST, "Reverse Request" },
+ { ARPOP_REVREPLY, "Reverse Reply" },
+ { ARPOP_INVREQUEST, "Inverse Request" },
+ { ARPOP_INVREPLY, "Inverse Reply" },
+ { ARPOP_NAK, "NACK Reply" },
+ { 0, NULL }
+};
+
+static const struct tok arphrd_values[] = {
+ { ARPHRD_ETHER, "Ethernet" },
+ { ARPHRD_IEEE802, "TokenRing" },
+ { ARPHRD_ARCNET, "ArcNet" },
+ { ARPHRD_FRELAY, "FrameRelay" },
+ { ARPHRD_STRIP, "Strip" },
+ { ARPHRD_IEEE1394, "IEEE 1394" },
+ { ARPHRD_ATM2225, "ATM" },
+ { ARPHRD_INFINIBAND, "InfiniBand" },
+ { 0, NULL }
+};
+
+/*
+ * ATM Address Resolution Protocol.
+ *
+ * See RFC 2225 for protocol description. ATMARP packets are similar
+ * to ARP packets, except that there are no length fields for the
+ * protocol address - instead, there are type/length fields for
+ * the ATM number and subaddress - and the hardware addresses consist
+ * of an ATM number and an ATM subaddress.
+ */
+struct atmarp_pkthdr {
+ nd_uint16_t aar_hrd; /* format of hardware address */
+ nd_uint16_t aar_pro; /* format of protocol address */
+ nd_uint8_t aar_shtl; /* length of source ATM number */
+ nd_uint8_t aar_sstl; /* length of source ATM subaddress */
+#define ATMARP_IS_E164 0x40 /* bit in type/length for E.164 format */
+#define ATMARP_LEN_MASK 0x3F /* length of {sub}address in type/length */
+ nd_uint16_t aar_op; /* same as regular ARP */
+ nd_uint8_t aar_spln; /* length of source protocol address */
+ nd_uint8_t aar_thtl; /* length of target ATM number */
+ nd_uint8_t aar_tstl; /* length of target ATM subaddress */
+ nd_uint8_t aar_tpln; /* length of target protocol address */
+/*
+ * The remaining fields are variable in size,
+ * according to the sizes above.
+ */
+#ifdef COMMENT_ONLY
+ nd_byte aar_sha[]; /* source ATM number */
+ nd_byte aar_ssa[]; /* source ATM subaddress */
+ nd_byte aar_spa[]; /* sender protocol address */
+ nd_byte aar_tha[]; /* target ATM number */
+ nd_byte aar_tsa[]; /* target ATM subaddress */
+ nd_byte aar_tpa[]; /* target protocol address */
+#endif
+
+#define ATMHRD(ap) GET_BE_U_2((ap)->aar_hrd)
+#define ATMSHRD_LEN(ap) (GET_U_1((ap)->aar_shtl) & ATMARP_LEN_MASK)
+#define ATMSSLN(ap) (GET_U_1((ap)->aar_sstl) & ATMARP_LEN_MASK)
+#define ATMSPROTO_LEN(ap) GET_U_1((ap)->aar_spln)
+#define ATMOP(ap) GET_BE_U_2((ap)->aar_op)
+#define ATMPRO(ap) GET_BE_U_2((ap)->aar_pro)
+#define ATMTHRD_LEN(ap) (GET_U_1((ap)->aar_thtl) & ATMARP_LEN_MASK)
+#define ATMTSLN(ap) (GET_U_1((ap)->aar_tstl) & ATMARP_LEN_MASK)
+#define ATMTPROTO_LEN(ap) GET_U_1((ap)->aar_tpln)
+#define aar_sha(ap) ((const u_char *)((ap)+1))
+#define aar_ssa(ap) (aar_sha(ap) + ATMSHRD_LEN(ap))
+#define aar_spa(ap) (aar_ssa(ap) + ATMSSLN(ap))
+#define aar_tha(ap) (aar_spa(ap) + ATMSPROTO_LEN(ap))
+#define aar_tsa(ap) (aar_tha(ap) + ATMTHRD_LEN(ap))
+#define aar_tpa(ap) (aar_tsa(ap) + ATMTSLN(ap))
+};
+
+#define ATMSHA(ap) (aar_sha(ap))
+#define ATMSSA(ap) (aar_ssa(ap))
+#define ATMSPA(ap) (aar_spa(ap))
+#define ATMTHA(ap) (aar_tha(ap))
+#define ATMTSA(ap) (aar_tsa(ap))
+#define ATMTPA(ap) (aar_tpa(ap))
+
+static int
+isnonzero(netdissect_options *ndo, const u_char *a, size_t len)
+{
+ while (len > 0) {
+ if (GET_U_1(a) != 0)
+ return (1);
+ a++;
+ len--;
+ }
+ return (0);
+}
+
+static void
+tpaddr_print_ip(netdissect_options *ndo,
+ const struct arp_pkthdr *ap, u_short pro)
+{
+ if (pro != ETHERTYPE_IP && pro != ETHERTYPE_TRAIL)
+ ND_PRINT("<wrong proto type>");
+ else if (PROTO_LEN(ap) != 4)
+ ND_PRINT("<wrong len>");
+ else
+ ND_PRINT("%s", GET_IPADDR_STRING(TPA(ap)));
+}
+
+static void
+spaddr_print_ip(netdissect_options *ndo,
+ const struct arp_pkthdr *ap, u_short pro)
+{
+ if (pro != ETHERTYPE_IP && pro != ETHERTYPE_TRAIL)
+ ND_PRINT("<wrong proto type>");
+ else if (PROTO_LEN(ap) != 4)
+ ND_PRINT("<wrong len>");
+ else
+ ND_PRINT("%s", GET_IPADDR_STRING(SPA(ap)));
+}
+
+static void
+atmarp_addr_print(netdissect_options *ndo,
+ const u_char *ha, u_int ha_len, const u_char *srca,
+ u_int srca_len)
+{
+ if (ha_len == 0)
+ ND_PRINT("<No address>");
+ else {
+ ND_PRINT("%s", GET_LINKADDR_STRING(ha, LINKADDR_ATM, ha_len));
+ if (srca_len != 0)
+ ND_PRINT(",%s",
+ GET_LINKADDR_STRING(srca, LINKADDR_ATM, srca_len));
+ }
+}
+
+static void
+atmarp_tpaddr_print(netdissect_options *ndo,
+ const struct atmarp_pkthdr *ap, u_short pro)
+{
+ if (pro != ETHERTYPE_IP && pro != ETHERTYPE_TRAIL)
+ ND_PRINT("<wrong proto type>");
+ else if (ATMTPROTO_LEN(ap) != 4)
+ ND_PRINT("<wrong tplen>");
+ else
+ ND_PRINT("%s", GET_IPADDR_STRING(ATMTPA(ap)));
+}
+
+static void
+atmarp_spaddr_print(netdissect_options *ndo,
+ const struct atmarp_pkthdr *ap, u_short pro)
+{
+ if (pro != ETHERTYPE_IP && pro != ETHERTYPE_TRAIL)
+ ND_PRINT("<wrong proto type>");
+ else if (ATMSPROTO_LEN(ap) != 4)
+ ND_PRINT("<wrong splen>");
+ else
+ ND_PRINT("%s", GET_IPADDR_STRING(ATMSPA(ap)));
+}
+
+static void
+atmarp_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, u_int caplen)
+{
+ const struct atmarp_pkthdr *ap;
+ u_short pro, hrd, op;
+
+ ap = (const struct atmarp_pkthdr *)bp;
+ ND_TCHECK_SIZE(ap);
+
+ hrd = ATMHRD(ap);
+ pro = ATMPRO(ap);
+ op = ATMOP(ap);
+
+ ND_TCHECK_LEN(ATMTPA(ap), ATMTPROTO_LEN(ap));
+
+ if (!ndo->ndo_eflag) {
+ ND_PRINT("ARP, ");
+ }
+
+ if ((pro != ETHERTYPE_IP && pro != ETHERTYPE_TRAIL) ||
+ ATMSPROTO_LEN(ap) != 4 ||
+ ATMTPROTO_LEN(ap) != 4 ||
+ ndo->ndo_vflag) {
+ ND_PRINT("%s, %s (len %u/%u)",
+ tok2str(arphrd_values, "Unknown Hardware (%u)", hrd),
+ tok2str(ethertype_values, "Unknown Protocol (0x%04x)", pro),
+ ATMSPROTO_LEN(ap),
+ ATMTPROTO_LEN(ap));
+
+ /* don't know about the address formats */
+ if (!ndo->ndo_vflag) {
+ goto out;
+ }
+ }
+
+ /* print operation */
+ ND_PRINT("%s%s ",
+ ndo->ndo_vflag ? ", " : "",
+ tok2str(arpop_values, "Unknown (%u)", op));
+
+ switch (op) {
+
+ case ARPOP_REQUEST:
+ ND_PRINT("who-has ");
+ atmarp_tpaddr_print(ndo, ap, pro);
+ if (ATMTHRD_LEN(ap) != 0) {
+ ND_PRINT(" (");
+ atmarp_addr_print(ndo, ATMTHA(ap), ATMTHRD_LEN(ap),
+ ATMTSA(ap), ATMTSLN(ap));
+ ND_PRINT(")");
+ }
+ ND_PRINT(" tell ");
+ atmarp_spaddr_print(ndo, ap, pro);
+ break;
+
+ case ARPOP_REPLY:
+ atmarp_spaddr_print(ndo, ap, pro);
+ ND_PRINT(" is-at ");
+ atmarp_addr_print(ndo, ATMSHA(ap), ATMSHRD_LEN(ap), ATMSSA(ap),
+ ATMSSLN(ap));
+ break;
+
+ case ARPOP_INVREQUEST:
+ ND_PRINT("who-is ");
+ atmarp_addr_print(ndo, ATMTHA(ap), ATMTHRD_LEN(ap), ATMTSA(ap),
+ ATMTSLN(ap));
+ ND_PRINT(" tell ");
+ atmarp_addr_print(ndo, ATMSHA(ap), ATMSHRD_LEN(ap), ATMSSA(ap),
+ ATMSSLN(ap));
+ break;
+
+ case ARPOP_INVREPLY:
+ atmarp_addr_print(ndo, ATMSHA(ap), ATMSHRD_LEN(ap), ATMSSA(ap),
+ ATMSSLN(ap));
+ ND_PRINT("at ");
+ atmarp_spaddr_print(ndo, ap, pro);
+ break;
+
+ case ARPOP_NAK:
+ ND_PRINT("for ");
+ atmarp_spaddr_print(ndo, ap, pro);
+ break;
+
+ default:
+ ND_DEFAULTPRINT((const u_char *)ap, caplen);
+ return;
+ }
+
+ out:
+ ND_PRINT(", length %u", length);
+}
+
+void
+arp_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, u_int caplen)
+{
+ const struct arp_pkthdr *ap;
+ u_short pro, hrd, op, linkaddr;
+
+ ndo->ndo_protocol = "arp";
+ ap = (const struct arp_pkthdr *)bp;
+ ND_TCHECK_SIZE(ap);
+
+ hrd = HRD(ap);
+ pro = PRO(ap);
+ op = OP(ap);
+
+
+ /* if its ATM then call the ATM ARP printer
+ for Frame-relay ARP most of the fields
+ are similar to Ethernet so overload the Ethernet Printer
+ and set the linkaddr type for GET_LINKADDR_STRING() accordingly */
+
+ switch(hrd) {
+ case ARPHRD_ATM2225:
+ atmarp_print(ndo, bp, length, caplen);
+ return;
+ case ARPHRD_FRELAY:
+ linkaddr = LINKADDR_FRELAY;
+ break;
+ default:
+ linkaddr = LINKADDR_ETHER;
+ break;
+ }
+
+ ND_TCHECK_LEN(TPA(ap), PROTO_LEN(ap));
+
+ if (!ndo->ndo_eflag) {
+ ND_PRINT("ARP, ");
+ }
+
+ /* print hardware type/len and proto type/len */
+ if ((pro != ETHERTYPE_IP && pro != ETHERTYPE_TRAIL) ||
+ PROTO_LEN(ap) != 4 ||
+ HRD_LEN(ap) == 0 ||
+ ndo->ndo_vflag) {
+ ND_PRINT("%s (len %u), %s (len %u)",
+ tok2str(arphrd_values, "Unknown Hardware (%u)", hrd),
+ HRD_LEN(ap),
+ tok2str(ethertype_values, "Unknown Protocol (0x%04x)", pro),
+ PROTO_LEN(ap));
+
+ /* don't know about the address formats */
+ if (!ndo->ndo_vflag) {
+ goto out;
+ }
+ }
+
+ /* print operation */
+ ND_PRINT("%s%s ",
+ ndo->ndo_vflag ? ", " : "",
+ tok2str(arpop_values, "Unknown (%u)", op));
+
+ switch (op) {
+
+ case ARPOP_REQUEST:
+ ND_PRINT("who-has ");
+ tpaddr_print_ip(ndo, ap, pro);
+ if (isnonzero(ndo, (const u_char *)THA(ap), HRD_LEN(ap)))
+ ND_PRINT(" (%s)",
+ GET_LINKADDR_STRING(THA(ap), linkaddr, HRD_LEN(ap)));
+ ND_PRINT(" tell ");
+ spaddr_print_ip(ndo, ap, pro);
+ break;
+
+ case ARPOP_REPLY:
+ spaddr_print_ip(ndo, ap, pro);
+ ND_PRINT(" is-at %s",
+ GET_LINKADDR_STRING(SHA(ap), linkaddr, HRD_LEN(ap)));
+ break;
+
+ case ARPOP_REVREQUEST:
+ ND_PRINT("who-is %s tell %s",
+ GET_LINKADDR_STRING(THA(ap), linkaddr, HRD_LEN(ap)),
+ GET_LINKADDR_STRING(SHA(ap), linkaddr, HRD_LEN(ap)));
+ break;
+
+ case ARPOP_REVREPLY:
+ ND_PRINT("%s at ",
+ GET_LINKADDR_STRING(THA(ap), linkaddr, HRD_LEN(ap)));
+ tpaddr_print_ip(ndo, ap, pro);
+ break;
+
+ case ARPOP_INVREQUEST:
+ ND_PRINT("who-is %s tell %s",
+ GET_LINKADDR_STRING(THA(ap), linkaddr, HRD_LEN(ap)),
+ GET_LINKADDR_STRING(SHA(ap), linkaddr, HRD_LEN(ap)));
+ break;
+
+ case ARPOP_INVREPLY:
+ ND_PRINT("%s at ",
+ GET_LINKADDR_STRING(SHA(ap), linkaddr, HRD_LEN(ap)));
+ spaddr_print_ip(ndo, ap, pro);
+ break;
+
+ default:
+ ND_DEFAULTPRINT((const u_char *)ap, caplen);
+ return;
+ }
+
+ out:
+ ND_PRINT(", length %u", length);
+}
diff --git a/print-ascii.c b/print-ascii.c
new file mode 100644
index 0000000..e5b7a58
--- /dev/null
+++ b/print-ascii.c
@@ -0,0 +1,222 @@
+/* $NetBSD: print-ascii.c,v 1.1 1999/09/30 14:49:12 sjg Exp $ */
+
+/*-
+ * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Alan Barrett and Simon J. Gerraty.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: ASCII packet dump printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+#define ASCII_LINELENGTH 300
+#define HEXDUMP_BYTES_PER_LINE 16
+#define HEXDUMP_SHORTS_PER_LINE (HEXDUMP_BYTES_PER_LINE / 2)
+#define HEXDUMP_HEXSTUFF_PER_SHORT 5 /* 4 hex digits and a space */
+#define HEXDUMP_HEXSTUFF_PER_LINE \
+ (HEXDUMP_HEXSTUFF_PER_SHORT * HEXDUMP_SHORTS_PER_LINE)
+
+void
+ascii_print(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ u_int caplength;
+ u_char s;
+
+ ndo->ndo_protocol = "ascii";
+ caplength = (ndo->ndo_snapend > cp) ? ND_BYTES_AVAILABLE_AFTER(cp) : 0;
+ if (length > caplength)
+ length = caplength;
+ ND_PRINT("\n");
+ while (length > 0) {
+ s = GET_U_1(cp);
+ cp++;
+ length--;
+ if (s == '\r') {
+ /*
+ * Don't print CRs at the end of the line; they
+ * don't belong at the ends of lines on UN*X,
+ * and the standard I/O library will give us one
+ * on Windows so we don't need to print one
+ * ourselves.
+ *
+ * In the middle of a line, just print a '.'.
+ */
+ if (length > 1 && GET_U_1(cp) != '\n')
+ ND_PRINT(".");
+ } else {
+ if (!ND_ASCII_ISGRAPH(s) &&
+ (s != '\t' && s != ' ' && s != '\n'))
+ ND_PRINT(".");
+ else
+ ND_PRINT("%c", s);
+ }
+ }
+}
+
+static void
+hex_and_ascii_print_with_offset(netdissect_options *ndo, const char *ident,
+ const u_char *cp, u_int length, u_int oset)
+{
+ u_int caplength;
+ u_int i;
+ u_int s1, s2;
+ u_int nshorts;
+ char hexstuff[HEXDUMP_SHORTS_PER_LINE*HEXDUMP_HEXSTUFF_PER_SHORT+1], *hsp;
+ char asciistuff[ASCII_LINELENGTH+1], *asp;
+
+ caplength = (ndo->ndo_snapend > cp) ? ND_BYTES_AVAILABLE_AFTER(cp) : 0;
+ if (length > caplength)
+ length = caplength;
+ nshorts = length / sizeof(u_short);
+ i = 0;
+ hsp = hexstuff; asp = asciistuff;
+ while (nshorts != 0) {
+ s1 = GET_U_1(cp);
+ cp++;
+ s2 = GET_U_1(cp);
+ cp++;
+ (void)snprintf(hsp, sizeof(hexstuff) - (hsp - hexstuff),
+ " %02x%02x", s1, s2);
+ hsp += HEXDUMP_HEXSTUFF_PER_SHORT;
+ *(asp++) = (char)(ND_ASCII_ISGRAPH(s1) ? s1 : '.');
+ *(asp++) = (char)(ND_ASCII_ISGRAPH(s2) ? s2 : '.');
+ i++;
+ if (i >= HEXDUMP_SHORTS_PER_LINE) {
+ *hsp = *asp = '\0';
+ ND_PRINT("%s0x%04x: %-*s %s",
+ ident, oset, HEXDUMP_HEXSTUFF_PER_LINE,
+ hexstuff, asciistuff);
+ i = 0; hsp = hexstuff; asp = asciistuff;
+ oset += HEXDUMP_BYTES_PER_LINE;
+ }
+ nshorts--;
+ }
+ if (length & 1) {
+ s1 = GET_U_1(cp);
+ cp++;
+ (void)snprintf(hsp, sizeof(hexstuff) - (hsp - hexstuff),
+ " %02x", s1);
+ hsp += 3;
+ *(asp++) = (char)(ND_ASCII_ISGRAPH(s1) ? s1 : '.');
+ ++i;
+ }
+ if (i > 0) {
+ *hsp = *asp = '\0';
+ ND_PRINT("%s0x%04x: %-*s %s",
+ ident, oset, HEXDUMP_HEXSTUFF_PER_LINE,
+ hexstuff, asciistuff);
+ }
+}
+
+void
+hex_and_ascii_print(netdissect_options *ndo, const char *ident,
+ const u_char *cp, u_int length)
+{
+ hex_and_ascii_print_with_offset(ndo, ident, cp, length, 0);
+}
+
+/*
+ * telnet_print() wants this. It is essentially default_print_unaligned()
+ */
+void
+hex_print_with_offset(netdissect_options *ndo,
+ const char *ident, const u_char *cp, u_int length,
+ u_int oset)
+{
+ u_int caplength;
+ u_int i, s;
+ u_int nshorts;
+
+ caplength = (ndo->ndo_snapend > cp) ? ND_BYTES_AVAILABLE_AFTER(cp) : 0;
+ if (length > caplength)
+ length = caplength;
+ nshorts = length / sizeof(u_short);
+ i = 0;
+ while (nshorts != 0) {
+ if ((i++ % 8) == 0) {
+ ND_PRINT("%s0x%04x: ", ident, oset);
+ oset += HEXDUMP_BYTES_PER_LINE;
+ }
+ s = GET_U_1(cp);
+ cp++;
+ ND_PRINT(" %02x%02x", s, GET_U_1(cp));
+ cp++;
+ nshorts--;
+ }
+ if (length & 1) {
+ if ((i % 8) == 0)
+ ND_PRINT("%s0x%04x: ", ident, oset);
+ ND_PRINT(" %02x", GET_U_1(cp));
+ }
+}
+
+/*
+ * just for completeness
+ */
+void
+hex_print(netdissect_options *ndo,const char *ident, const u_char *cp, u_int length)
+{
+ hex_print_with_offset(ndo, ident, cp, length, 0);
+}
+
+#ifdef MAIN
+int
+main(int argc, char *argv[])
+{
+ hex_print("\n\t", "Hello, World!\n", 14);
+ printf("\n");
+ hex_and_ascii_print("\n\t", "Hello, World!\n", 14);
+ printf("\n");
+ ascii_print("Hello, World!\n", 14);
+ printf("\n");
+#define TMSG "Now is the winter of our discontent...\n"
+ hex_print_with_offset("\n\t", TMSG, sizeof(TMSG) - 1, 0x100);
+ printf("\n");
+ hex_and_ascii_print_with_offset("\n\t", TMSG, sizeof(TMSG) - 1, 0x100);
+ printf("\n");
+ exit(0);
+}
+#endif /* MAIN */
diff --git a/print-atalk.c b/print-atalk.c
new file mode 100644
index 0000000..d9f86c7
--- /dev/null
+++ b/print-atalk.c
@@ -0,0 +1,701 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: AppleTalk printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+#include "extract.h"
+#include "appletalk.h"
+
+
+static const struct tok type2str[] = {
+ { ddpRTMP, "rtmp" },
+ { ddpRTMPrequest, "rtmpReq" },
+ { ddpECHO, "echo" },
+ { ddpIP, "IP" },
+ { ddpARP, "ARP" },
+ { ddpKLAP, "KLAP" },
+ { 0, NULL }
+};
+
+struct aarp {
+ nd_uint16_t htype, ptype;
+ nd_uint8_t halen, palen;
+ nd_uint16_t op;
+ nd_mac_addr hsaddr;
+ uint8_t psaddr[4];
+ nd_mac_addr hdaddr;
+ uint8_t pdaddr[4];
+};
+
+static void atp_print(netdissect_options *, const struct atATP *, u_int);
+static void atp_bitmap_print(netdissect_options *, u_char);
+static void nbp_print(netdissect_options *, const struct atNBP *, u_int, u_short, u_char, u_char);
+static const struct atNBPtuple *nbp_tuple_print(netdissect_options *ndo, const struct atNBPtuple *,
+ const u_char *,
+ u_short, u_char, u_char);
+static const struct atNBPtuple *nbp_name_print(netdissect_options *, const struct atNBPtuple *,
+ const u_char *);
+static const char *ataddr_string(netdissect_options *, u_short, u_char);
+static void ddp_print(netdissect_options *, const u_char *, u_int, u_int, u_short, u_char, u_char);
+static const char *ddpskt_string(netdissect_options *, u_int);
+
+/*
+ * Print LLAP packets received on a physical LocalTalk interface.
+ */
+void
+ltalk_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int hdrlen;
+
+ ndo->ndo_protocol = "ltalk";
+ hdrlen = llap_print(ndo, p, h->len);
+ if (hdrlen == 0) {
+ /* Cut short by the snapshot length. */
+ ndo->ndo_ll_hdr_len += h->caplen;
+ return;
+ }
+ ndo->ndo_ll_hdr_len += hdrlen;
+}
+
+/*
+ * Print AppleTalk LLAP packets.
+ */
+u_int
+llap_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const struct LAP *lp;
+ const struct atDDP *dp;
+ const struct atShortDDP *sdp;
+ u_short snet;
+ u_int hdrlen;
+
+ ndo->ndo_protocol = "llap";
+ if (length < sizeof(*lp)) {
+ ND_PRINT(" [|llap %u]", length);
+ return (length);
+ }
+ if (!ND_TTEST_LEN(bp, sizeof(*lp))) {
+ nd_print_trunc(ndo);
+ return (0); /* cut short by the snapshot length */
+ }
+ lp = (const struct LAP *)bp;
+ bp += sizeof(*lp);
+ length -= sizeof(*lp);
+ hdrlen = sizeof(*lp);
+ switch (GET_U_1(lp->type)) {
+
+ case lapShortDDP:
+ if (length < ddpSSize) {
+ ND_PRINT(" [|sddp %u]", length);
+ return (length);
+ }
+ if (!ND_TTEST_LEN(bp, ddpSSize)) {
+ ND_PRINT(" [|sddp]");
+ return (0); /* cut short by the snapshot length */
+ }
+ sdp = (const struct atShortDDP *)bp;
+ ND_PRINT("%s.%s",
+ ataddr_string(ndo, 0, GET_U_1(lp->src)),
+ ddpskt_string(ndo, GET_U_1(sdp->srcSkt)));
+ ND_PRINT(" > %s.%s:",
+ ataddr_string(ndo, 0, GET_U_1(lp->dst)),
+ ddpskt_string(ndo, GET_U_1(sdp->dstSkt)));
+ bp += ddpSSize;
+ length -= ddpSSize;
+ hdrlen += ddpSSize;
+ ddp_print(ndo, bp, length, GET_U_1(sdp->type), 0,
+ GET_U_1(lp->src), GET_U_1(sdp->srcSkt));
+ break;
+
+ case lapDDP:
+ if (length < ddpSize) {
+ ND_PRINT(" [|ddp %u]", length);
+ return (length);
+ }
+ if (!ND_TTEST_LEN(bp, ddpSize)) {
+ ND_PRINT(" [|ddp]");
+ return (0); /* cut short by the snapshot length */
+ }
+ dp = (const struct atDDP *)bp;
+ snet = GET_BE_U_2(dp->srcNet);
+ ND_PRINT("%s.%s",
+ ataddr_string(ndo, snet, GET_U_1(dp->srcNode)),
+ ddpskt_string(ndo, GET_U_1(dp->srcSkt)));
+ ND_PRINT(" > %s.%s:",
+ ataddr_string(ndo, GET_BE_U_2(dp->dstNet), GET_U_1(dp->dstNode)),
+ ddpskt_string(ndo, GET_U_1(dp->dstSkt)));
+ bp += ddpSize;
+ length -= ddpSize;
+ hdrlen += ddpSize;
+ ddp_print(ndo, bp, length, GET_U_1(dp->type), snet,
+ GET_U_1(dp->srcNode), GET_U_1(dp->srcSkt));
+ break;
+
+#ifdef notdef
+ case lapKLAP:
+ klap_print(bp, length);
+ break;
+#endif
+
+ default:
+ ND_PRINT("%u > %u at-lap#%u %u",
+ GET_U_1(lp->src), GET_U_1(lp->dst), GET_U_1(lp->type),
+ length);
+ break;
+ }
+ return (hdrlen);
+}
+
+/*
+ * Print EtherTalk/TokenTalk packets (or FDDITalk, or whatever it's called
+ * when it runs over FDDI; yes, I've seen FDDI captures with AppleTalk
+ * packets in them).
+ */
+void
+atalk_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const struct atDDP *dp;
+ u_short snet;
+
+ ndo->ndo_protocol = "atalk";
+ if(!ndo->ndo_eflag)
+ ND_PRINT("AT ");
+
+ if (length < ddpSize) {
+ ND_PRINT(" [|ddp %u]", length);
+ return;
+ }
+ if (!ND_TTEST_LEN(bp, ddpSize)) {
+ ND_PRINT(" [|ddp]");
+ return;
+ }
+ dp = (const struct atDDP *)bp;
+ snet = GET_BE_U_2(dp->srcNet);
+ ND_PRINT("%s.%s", ataddr_string(ndo, snet, GET_U_1(dp->srcNode)),
+ ddpskt_string(ndo, GET_U_1(dp->srcSkt)));
+ ND_PRINT(" > %s.%s: ",
+ ataddr_string(ndo, GET_BE_U_2(dp->dstNet), GET_U_1(dp->dstNode)),
+ ddpskt_string(ndo, GET_U_1(dp->dstSkt)));
+ bp += ddpSize;
+ length -= ddpSize;
+ ddp_print(ndo, bp, length, GET_U_1(dp->type), snet,
+ GET_U_1(dp->srcNode), GET_U_1(dp->srcSkt));
+}
+
+/* XXX should probably pass in the snap header and do checks like arp_print() */
+void
+aarp_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const struct aarp *ap;
+
+#define AT(member) ataddr_string(ndo, (ap->member[1]<<8)|ap->member[2],ap->member[3])
+
+ ndo->ndo_protocol = "aarp";
+ ND_PRINT("aarp ");
+ ap = (const struct aarp *)bp;
+ if (!ND_TTEST_SIZE(ap)) {
+ /* Just bail if we don't have the whole chunk. */
+ nd_print_trunc(ndo);
+ return;
+ }
+ if (length < sizeof(*ap)) {
+ ND_PRINT(" [|aarp %u]", length);
+ return;
+ }
+ if (GET_BE_U_2(ap->htype) == 1 &&
+ GET_BE_U_2(ap->ptype) == ETHERTYPE_ATALK &&
+ GET_U_1(ap->halen) == MAC_ADDR_LEN && GET_U_1(ap->palen) == 4)
+ switch (GET_BE_U_2(ap->op)) {
+
+ case 1: /* request */
+ ND_PRINT("who-has %s tell %s", AT(pdaddr), AT(psaddr));
+ return;
+
+ case 2: /* response */
+ ND_PRINT("reply %s is-at %s", AT(psaddr), GET_ETHERADDR_STRING(ap->hsaddr));
+ return;
+
+ case 3: /* probe (oy!) */
+ ND_PRINT("probe %s tell %s", AT(pdaddr), AT(psaddr));
+ return;
+ }
+ ND_PRINT("len %u op %u htype %u ptype %#x halen %u palen %u",
+ length, GET_BE_U_2(ap->op), GET_BE_U_2(ap->htype),
+ GET_BE_U_2(ap->ptype), GET_U_1(ap->halen), GET_U_1(ap->palen));
+}
+
+/*
+ * Print AppleTalk Datagram Delivery Protocol packets.
+ */
+static void
+ddp_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, u_int t,
+ u_short snet, u_char snode, u_char skt)
+{
+
+ switch (t) {
+
+ case ddpNBP:
+ nbp_print(ndo, (const struct atNBP *)bp, length, snet, snode, skt);
+ break;
+
+ case ddpATP:
+ atp_print(ndo, (const struct atATP *)bp, length);
+ break;
+
+ case ddpEIGRP:
+ eigrp_print(ndo, bp, length);
+ break;
+
+ default:
+ ND_PRINT(" at-%s %u", tok2str(type2str, NULL, t), length);
+ break;
+ }
+}
+
+static void
+atp_print(netdissect_options *ndo,
+ const struct atATP *ap, u_int length)
+{
+ uint8_t control;
+ uint32_t data;
+
+ if ((const u_char *)(ap + 1) > ndo->ndo_snapend) {
+ /* Just bail if we don't have the whole chunk. */
+ nd_print_trunc(ndo);
+ return;
+ }
+ if (length < sizeof(*ap)) {
+ ND_PRINT(" [|atp %u]", length);
+ return;
+ }
+ length -= sizeof(*ap);
+ control = GET_U_1(ap->control);
+ switch (control & 0xc0) {
+
+ case atpReqCode:
+ ND_PRINT(" atp-req%s %u",
+ control & atpXO? " " : "*",
+ GET_BE_U_2(ap->transID));
+
+ atp_bitmap_print(ndo, GET_U_1(ap->bitmap));
+
+ if (length != 0)
+ ND_PRINT(" [len=%u]", length);
+
+ switch (control & (atpEOM|atpSTS)) {
+ case atpEOM:
+ ND_PRINT(" [EOM]");
+ break;
+ case atpSTS:
+ ND_PRINT(" [STS]");
+ break;
+ case atpEOM|atpSTS:
+ ND_PRINT(" [EOM,STS]");
+ break;
+ }
+ break;
+
+ case atpRspCode:
+ ND_PRINT(" atp-resp%s%u:%u (%u)",
+ control & atpEOM? "*" : " ",
+ GET_BE_U_2(ap->transID), GET_U_1(ap->bitmap),
+ length);
+ switch (control & (atpXO|atpSTS)) {
+ case atpXO:
+ ND_PRINT(" [XO]");
+ break;
+ case atpSTS:
+ ND_PRINT(" [STS]");
+ break;
+ case atpXO|atpSTS:
+ ND_PRINT(" [XO,STS]");
+ break;
+ }
+ break;
+
+ case atpRelCode:
+ ND_PRINT(" atp-rel %u", GET_BE_U_2(ap->transID));
+
+ atp_bitmap_print(ndo, GET_U_1(ap->bitmap));
+
+ /* length should be zero */
+ if (length)
+ ND_PRINT(" [len=%u]", length);
+
+ /* there shouldn't be any control flags */
+ if (control & (atpXO|atpEOM|atpSTS)) {
+ char c = '[';
+ if (control & atpXO) {
+ ND_PRINT("%cXO", c);
+ c = ',';
+ }
+ if (control & atpEOM) {
+ ND_PRINT("%cEOM", c);
+ c = ',';
+ }
+ if (control & atpSTS) {
+ ND_PRINT("%cSTS", c);
+ }
+ ND_PRINT("]");
+ }
+ break;
+
+ default:
+ ND_PRINT(" atp-0x%x %u (%u)", control,
+ GET_BE_U_2(ap->transID), length);
+ break;
+ }
+ data = GET_BE_U_4(ap->userData);
+ if (data != 0)
+ ND_PRINT(" 0x%x", data);
+}
+
+static void
+atp_bitmap_print(netdissect_options *ndo,
+ u_char bm)
+{
+ u_int i;
+
+ /*
+ * The '& 0xff' below is needed for compilers that want to sign
+ * extend a u_char, which is the case with the Ultrix compiler.
+ * (gcc is smart enough to eliminate it, at least on the Sparc).
+ */
+ if ((bm + 1) & (bm & 0xff)) {
+ char c = '<';
+ for (i = 0; bm; ++i) {
+ if (bm & 1) {
+ ND_PRINT("%c%u", c, i);
+ c = ',';
+ }
+ bm >>= 1;
+ }
+ ND_PRINT(">");
+ } else {
+ for (i = 0; bm; ++i)
+ bm >>= 1;
+ if (i > 1)
+ ND_PRINT("<0-%u>", i - 1);
+ else
+ ND_PRINT("<0>");
+ }
+}
+
+static void
+nbp_print(netdissect_options *ndo,
+ const struct atNBP *np, u_int length, u_short snet,
+ u_char snode, u_char skt)
+{
+ const struct atNBPtuple *tp =
+ (const struct atNBPtuple *)((const u_char *)np + nbpHeaderSize);
+ uint8_t control;
+ u_int i;
+ const u_char *ep;
+
+ if (length < nbpHeaderSize) {
+ ND_PRINT(" truncated-nbp %u", length);
+ return;
+ }
+
+ length -= nbpHeaderSize;
+ if (length < 8) {
+ /* must be room for at least one tuple */
+ ND_PRINT(" truncated-nbp %u", length + nbpHeaderSize);
+ return;
+ }
+ /* ep points to end of available data */
+ ep = ndo->ndo_snapend;
+ if ((const u_char *)tp > ep) {
+ nd_print_trunc(ndo);
+ return;
+ }
+ control = GET_U_1(np->control);
+ switch (i = (control & 0xf0)) {
+
+ case nbpBrRq:
+ case nbpLkUp:
+ ND_PRINT(i == nbpLkUp? " nbp-lkup %u:":" nbp-brRq %u:",
+ GET_U_1(np->id));
+ if ((const u_char *)(tp + 1) > ep) {
+ nd_print_trunc(ndo);
+ return;
+ }
+ (void)nbp_name_print(ndo, tp, ep);
+ /*
+ * look for anomalies: the spec says there can only
+ * be one tuple, the address must match the source
+ * address and the enumerator should be zero.
+ */
+ if ((control & 0xf) != 1)
+ ND_PRINT(" [ntup=%u]", control & 0xf);
+ if (GET_U_1(tp->enumerator))
+ ND_PRINT(" [enum=%u]", GET_U_1(tp->enumerator));
+ if (GET_BE_U_2(tp->net) != snet ||
+ GET_U_1(tp->node) != snode ||
+ GET_U_1(tp->skt) != skt)
+ ND_PRINT(" [addr=%s.%u]",
+ ataddr_string(ndo, GET_BE_U_2(tp->net),
+ GET_U_1(tp->node)),
+ GET_U_1(tp->skt));
+ break;
+
+ case nbpLkUpReply:
+ ND_PRINT(" nbp-reply %u:", GET_U_1(np->id));
+
+ /* print each of the tuples in the reply */
+ for (i = control & 0xf; i != 0 && tp; i--)
+ tp = nbp_tuple_print(ndo, tp, ep, snet, snode, skt);
+ break;
+
+ default:
+ ND_PRINT(" nbp-0x%x %u (%u)", control, GET_U_1(np->id),
+ length);
+ break;
+ }
+}
+
+/* print a counted string */
+static const u_char *
+print_cstring(netdissect_options *ndo,
+ const u_char *cp, const u_char *ep)
+{
+ u_int length;
+
+ if (cp >= ep) {
+ nd_print_trunc(ndo);
+ return (0);
+ }
+ length = GET_U_1(cp);
+ cp++;
+
+ /* Spec says string can be at most 32 bytes long */
+ if (length > 32) {
+ ND_PRINT("[len=%u]", length);
+ return (0);
+ }
+ while (length != 0) {
+ if (cp >= ep) {
+ nd_print_trunc(ndo);
+ return (0);
+ }
+ fn_print_char(ndo, GET_U_1(cp));
+ cp++;
+ length--;
+ }
+ return (cp);
+}
+
+static const struct atNBPtuple *
+nbp_tuple_print(netdissect_options *ndo,
+ const struct atNBPtuple *tp, const u_char *ep,
+ u_short snet, u_char snode, u_char skt)
+{
+ const struct atNBPtuple *tpn;
+
+ if ((const u_char *)(tp + 1) > ep) {
+ nd_print_trunc(ndo);
+ return 0;
+ }
+ tpn = nbp_name_print(ndo, tp, ep);
+
+ /* if the enumerator isn't 1, print it */
+ if (GET_U_1(tp->enumerator) != 1)
+ ND_PRINT("(%u)", GET_U_1(tp->enumerator));
+
+ /* if the socket doesn't match the src socket, print it */
+ if (GET_U_1(tp->skt) != skt)
+ ND_PRINT(" %u", GET_U_1(tp->skt));
+
+ /* if the address doesn't match the src address, it's an anomaly */
+ if (GET_BE_U_2(tp->net) != snet ||
+ GET_U_1(tp->node) != snode)
+ ND_PRINT(" [addr=%s]",
+ ataddr_string(ndo, GET_BE_U_2(tp->net), GET_U_1(tp->node)));
+
+ return (tpn);
+}
+
+static const struct atNBPtuple *
+nbp_name_print(netdissect_options *ndo,
+ const struct atNBPtuple *tp, const u_char *ep)
+{
+ const u_char *cp = (const u_char *)tp + nbpTupleSize;
+
+ ND_PRINT(" ");
+
+ /* Object */
+ ND_PRINT("\"");
+ if ((cp = print_cstring(ndo, cp, ep)) != NULL) {
+ /* Type */
+ ND_PRINT(":");
+ if ((cp = print_cstring(ndo, cp, ep)) != NULL) {
+ /* Zone */
+ ND_PRINT("@");
+ if ((cp = print_cstring(ndo, cp, ep)) != NULL)
+ ND_PRINT("\"");
+ }
+ }
+ return ((const struct atNBPtuple *)cp);
+}
+
+
+#define HASHNAMESIZE 4096
+
+struct hnamemem {
+ u_int addr;
+ char *name;
+ struct hnamemem *nxt;
+};
+
+static struct hnamemem hnametable[HASHNAMESIZE];
+
+static const char *
+ataddr_string(netdissect_options *ndo,
+ u_short atnet, u_char athost)
+{
+ struct hnamemem *tp, *tp2;
+ u_int i = (atnet << 8) | athost;
+ char nambuf[256+1];
+ static int first = 1;
+ FILE *fp;
+
+ /*
+ * Are we doing address to name resolution?
+ */
+ if (!ndo->ndo_nflag) {
+ /*
+ * Yes. Have we tried to open and read an AppleTalk
+ * number to name map file?
+ */
+ if (!first) {
+ /*
+ * No; try to do so.
+ */
+ first = 0;
+ fp = fopen("/etc/atalk.names", "r");
+ if (fp != NULL) {
+ char line[256];
+ u_int i1, i2;
+
+ while (fgets(line, sizeof(line), fp)) {
+ if (line[0] == '\n' || line[0] == 0 ||
+ line[0] == '#')
+ continue;
+ if (sscanf(line, "%u.%u %256s", &i1,
+ &i2, nambuf) == 3)
+ /* got a hostname. */
+ i2 |= (i1 << 8);
+ else if (sscanf(line, "%u %256s", &i1,
+ nambuf) == 2)
+ /* got a net name */
+ i2 = (i1 << 8) | 255;
+ else
+ continue;
+
+ for (tp = &hnametable[i2 & (HASHNAMESIZE-1)];
+ tp->nxt; tp = tp->nxt)
+ ;
+ tp->addr = i2;
+ tp->nxt = newhnamemem(ndo);
+ tp->name = strdup(nambuf);
+ if (tp->name == NULL)
+ (*ndo->ndo_error)(ndo,
+ S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(nambuf)", __func__);
+ }
+ fclose(fp);
+ }
+ }
+ }
+
+ /*
+ * Now try to look up the address in the table.
+ */
+ for (tp = &hnametable[i & (HASHNAMESIZE-1)]; tp->nxt; tp = tp->nxt)
+ if (tp->addr == i)
+ return (tp->name);
+
+ /* didn't have the node name -- see if we've got the net name */
+ i |= 255;
+ for (tp2 = &hnametable[i & (HASHNAMESIZE-1)]; tp2->nxt; tp2 = tp2->nxt)
+ if (tp2->addr == i) {
+ tp->addr = (atnet << 8) | athost;
+ tp->nxt = newhnamemem(ndo);
+ (void)snprintf(nambuf, sizeof(nambuf), "%s.%u",
+ tp2->name, athost);
+ tp->name = strdup(nambuf);
+ if (tp->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(nambuf)", __func__);
+ return (tp->name);
+ }
+
+ tp->addr = (atnet << 8) | athost;
+ tp->nxt = newhnamemem(ndo);
+ if (athost != 255)
+ (void)snprintf(nambuf, sizeof(nambuf), "%u.%u", atnet, athost);
+ else
+ (void)snprintf(nambuf, sizeof(nambuf), "%u", atnet);
+ tp->name = strdup(nambuf);
+ if (tp->name == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: strdup(nambuf)", __func__);
+
+ return (tp->name);
+}
+
+static const struct tok skt2str[] = {
+ { rtmpSkt, "rtmp" }, /* routing table maintenance */
+ { nbpSkt, "nis" }, /* name info socket */
+ { echoSkt, "echo" }, /* AppleTalk echo protocol */
+ { zipSkt, "zip" }, /* zone info protocol */
+ { 0, NULL }
+};
+
+static const char *
+ddpskt_string(netdissect_options *ndo,
+ u_int skt)
+{
+ static char buf[8];
+
+ if (ndo->ndo_nflag) {
+ (void)snprintf(buf, sizeof(buf), "%u", skt);
+ return (buf);
+ }
+ return (tok2str(skt2str, "%u", skt));
+}
diff --git a/print-atm.c b/print-atm.c
new file mode 100644
index 0000000..904fc47
--- /dev/null
+++ b/print-atm.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Asynchronous Transfer Mode (ATM) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "atm.h"
+#include "llc.h"
+
+/* start of the original atmuni31.h */
+
+/*
+ * Copyright (c) 1997 Yen Yen Lim and North Dakota State University
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Yen Yen Lim and
+ North Dakota State University
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Based on UNI3.1 standard by ATM Forum */
+
+/* ATM traffic types based on VPI=0 and (the following VCI */
+#define VCI_PPC 0x05 /* Point-to-point signal msg */
+#define VCI_BCC 0x02 /* Broadcast signal msg */
+#define VCI_OAMF4SC 0x03 /* Segment OAM F4 flow cell */
+#define VCI_OAMF4EC 0x04 /* End-to-end OAM F4 flow cell */
+#define VCI_METAC 0x01 /* Meta signal msg */
+#define VCI_ILMIC 0x10 /* ILMI msg */
+
+/* Q.2931 signalling messages */
+#define CALL_PROCEED 0x02 /* call proceeding */
+#define CONNECT 0x07 /* connect */
+#define CONNECT_ACK 0x0f /* connect_ack */
+#define SETUP 0x05 /* setup */
+#define RELEASE 0x4d /* release */
+#define RELEASE_DONE 0x5a /* release_done */
+#define RESTART 0x46 /* restart */
+#define RESTART_ACK 0x4e /* restart ack */
+#define STATUS 0x7d /* status */
+#define STATUS_ENQ 0x75 /* status ack */
+#define ADD_PARTY 0x80 /* add party */
+#define ADD_PARTY_ACK 0x81 /* add party ack */
+#define ADD_PARTY_REJ 0x82 /* add party rej */
+#define DROP_PARTY 0x83 /* drop party */
+#define DROP_PARTY_ACK 0x84 /* drop party ack */
+
+/* Information Element Parameters in the signalling messages */
+#define CAUSE 0x08 /* cause */
+#define ENDPT_REF 0x54 /* endpoint reference */
+#define AAL_PARA 0x58 /* ATM adaptation layer parameters */
+#define TRAFF_DESCRIP 0x59 /* atm traffic descriptors */
+#define CONNECT_ID 0x5a /* connection identifier */
+#define QOS_PARA 0x5c /* quality of service parameters */
+#define B_HIGHER 0x5d /* broadband higher layer information */
+#define B_BEARER 0x5e /* broadband bearer capability */
+#define B_LOWER 0x5f /* broadband lower information */
+#define CALLING_PARTY 0x6c /* calling party number */
+#define CALLED_PARTY 0x70 /* called party nmber */
+
+#define Q2931 0x09
+
+/* Q.2931 signalling general messages format */
+#define PROTO_POS 0 /* offset of protocol discriminator */
+#define CALL_REF_POS 2 /* offset of call reference value */
+#define MSG_TYPE_POS 5 /* offset of message type */
+#if 0
+#define MSG_LEN_POS 7 /* offset of message length */
+#define IE_BEGIN_POS 9 /* offset of first information element */
+
+/* format of signalling messages */
+#define TYPE_POS 0
+#define LEN_POS 2
+#define FIELD_BEGIN_POS 4
+#endif
+
+/* end of the original atmuni31.h */
+
+
+#define OAM_CRC10_MASK 0x3ff
+#define OAM_PAYLOAD_LEN 48
+#define OAM_FUNCTION_SPECIFIC_LEN 45 /* this excludes crc10 and cell-type/function-type */
+#define OAM_CELLTYPE_FUNCTYPE_LEN 1
+
+static const struct tok oam_f_values[] = {
+ { VCI_OAMF4SC, "OAM F4 (segment)" },
+ { VCI_OAMF4EC, "OAM F4 (end)" },
+ { 0, NULL }
+};
+
+static const struct tok atm_pty_values[] = {
+ { 0x0, "user data, uncongested, SDU 0" },
+ { 0x1, "user data, uncongested, SDU 1" },
+ { 0x2, "user data, congested, SDU 0" },
+ { 0x3, "user data, congested, SDU 1" },
+ { 0x4, "VCC OAM F5 flow segment" },
+ { 0x5, "VCC OAM F5 flow end-to-end" },
+ { 0x6, "Traffic Control and resource Mgmt" },
+ { 0, NULL }
+};
+
+#define OAM_CELLTYPE_FM 0x1
+#define OAM_CELLTYPE_PM 0x2
+#define OAM_CELLTYPE_AD 0x8
+#define OAM_CELLTYPE_SM 0xf
+
+static const struct tok oam_celltype_values[] = {
+ { OAM_CELLTYPE_FM, "Fault Management" },
+ { OAM_CELLTYPE_PM, "Performance Management" },
+ { OAM_CELLTYPE_AD, "activate/deactivate" },
+ { OAM_CELLTYPE_SM, "System Management" },
+ { 0, NULL }
+};
+
+#define OAM_FM_FUNCTYPE_AIS 0x0
+#define OAM_FM_FUNCTYPE_RDI 0x1
+#define OAM_FM_FUNCTYPE_CONTCHECK 0x4
+#define OAM_FM_FUNCTYPE_LOOPBACK 0x8
+
+static const struct tok oam_fm_functype_values[] = {
+ { OAM_FM_FUNCTYPE_AIS, "AIS" },
+ { OAM_FM_FUNCTYPE_RDI, "RDI" },
+ { OAM_FM_FUNCTYPE_CONTCHECK, "Continuity Check" },
+ { OAM_FM_FUNCTYPE_LOOPBACK, "Loopback" },
+ { 0, NULL }
+};
+
+static const struct tok oam_pm_functype_values[] = {
+ { 0x0, "Forward Monitoring" },
+ { 0x1, "Backward Reporting" },
+ { 0x2, "Monitoring and Reporting" },
+ { 0, NULL }
+};
+
+static const struct tok oam_ad_functype_values[] = {
+ { 0x0, "Performance Monitoring" },
+ { 0x1, "Continuity Check" },
+ { 0, NULL }
+};
+
+#define OAM_FM_LOOPBACK_INDICATOR_MASK 0x1
+
+static const struct tok oam_fm_loopback_indicator_values[] = {
+ { 0x0, "Reply" },
+ { 0x1, "Request" },
+ { 0, NULL }
+};
+
+static const struct uint_tokary oam_celltype2tokary[] = {
+ { OAM_CELLTYPE_FM, oam_fm_functype_values },
+ { OAM_CELLTYPE_PM, oam_pm_functype_values },
+ { OAM_CELLTYPE_AD, oam_ad_functype_values },
+ /* uint2tokary() does not use array termination. */
+};
+
+/*
+ * Print an RFC 1483 LLC-encapsulated ATM frame.
+ */
+static u_int
+atm_llc_print(netdissect_options *ndo,
+ const u_char *p, int length, int caplen)
+{
+ int llc_hdrlen;
+
+ llc_hdrlen = llc_print(ndo, p, length, caplen, NULL, NULL);
+ if (llc_hdrlen < 0) {
+ /* packet not known, print raw packet */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = -llc_hdrlen;
+ }
+ return (llc_hdrlen);
+}
+
+/*
+ * Given a SAP value, generate the LLC header value for a UI packet
+ * with that SAP as the source and destination SAP.
+ */
+#define LLC_UI_HDR(sap) ((sap)<<16 | (sap<<8) | 0x03)
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the LLC/SNAP header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+atm_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ uint32_t llchdr;
+ u_int hdrlen = 0;
+
+ ndo->ndo_protocol = "atm";
+
+ /* Cisco Style NLPID ? */
+ if (GET_U_1(p) == LLC_UI) {
+ if (ndo->ndo_eflag)
+ ND_PRINT("CNLPID ");
+ ndo->ndo_ll_hdr_len += 1;
+ isoclns_print(ndo, p + 1, length - 1);
+ return;
+ }
+
+ /*
+ * Must have at least a DSAP, an SSAP, and the first byte of the
+ * control field.
+ */
+
+ /*
+ * Extract the presumed LLC header into a variable, for quick
+ * testing.
+ * Then check for a header that's neither a header for a SNAP
+ * packet nor an RFC 2684 routed NLPID-formatted PDU nor
+ * an 802.2-but-no-SNAP IP packet.
+ */
+ llchdr = GET_BE_U_3(p);
+ if (llchdr != LLC_UI_HDR(LLCSAP_SNAP) &&
+ llchdr != LLC_UI_HDR(LLCSAP_ISONS) &&
+ llchdr != LLC_UI_HDR(LLCSAP_IP)) {
+ /*
+ * XXX - assume 802.6 MAC header from Fore driver.
+ *
+ * Unfortunately, the above list doesn't check for
+ * all known SAPs, doesn't check for headers where
+ * the source and destination SAP aren't the same,
+ * and doesn't check for non-UI frames. It also
+ * runs the risk of an 802.6 MAC header that happens
+ * to begin with one of those values being
+ * incorrectly treated as an 802.2 header.
+ *
+ * So is that Fore driver still around? And, if so,
+ * is it still putting 802.6 MAC headers on ATM
+ * packets? If so, could it be changed to use a
+ * new DLT_IEEE802_6 value if we added it?
+ */
+ if (ndo->ndo_eflag)
+ ND_PRINT("%08x%08x %08x%08x ",
+ GET_BE_U_4(p),
+ GET_BE_U_4(p + 4),
+ GET_BE_U_4(p + 8),
+ GET_BE_U_4(p + 12));
+ /* Always cover the full header. */
+ ND_TCHECK_LEN(p, 20);
+ p += 20;
+ length -= 20;
+ caplen -= 20;
+ hdrlen += 20;
+ }
+ ndo->ndo_ll_hdr_len += hdrlen;
+ ndo->ndo_ll_hdr_len += atm_llc_print(ndo, p, length, caplen);
+}
+
+/*
+ * ATM signalling.
+ */
+static const struct tok msgtype2str[] = {
+ { CALL_PROCEED, "Call_proceeding" },
+ { CONNECT, "Connect" },
+ { CONNECT_ACK, "Connect_ack" },
+ { SETUP, "Setup" },
+ { RELEASE, "Release" },
+ { RELEASE_DONE, "Release_complete" },
+ { RESTART, "Restart" },
+ { RESTART_ACK, "Restart_ack" },
+ { STATUS, "Status" },
+ { STATUS_ENQ, "Status_enquiry" },
+ { ADD_PARTY, "Add_party" },
+ { ADD_PARTY_ACK, "Add_party_ack" },
+ { ADD_PARTY_REJ, "Add_party_reject" },
+ { DROP_PARTY, "Drop_party" },
+ { DROP_PARTY_ACK, "Drop_party_ack" },
+ { 0, NULL }
+};
+
+static void
+sig_print(netdissect_options *ndo,
+ const u_char *p)
+{
+ uint32_t call_ref;
+
+ if (GET_U_1(p + PROTO_POS) == Q2931) {
+ /*
+ * protocol:Q.2931 for User to Network Interface
+ * (UNI 3.1) signalling
+ */
+ ND_PRINT("Q.2931");
+ ND_PRINT(":%s ",
+ tok2str(msgtype2str, "msgtype#%u", GET_U_1(p + MSG_TYPE_POS)));
+
+ /*
+ * The call reference comes before the message type,
+ * so if we know we have the message type, which we
+ * do from the caplen test above, we also know we have
+ * the call reference.
+ */
+ call_ref = GET_BE_U_3(p + CALL_REF_POS);
+ ND_PRINT("CALL_REF:0x%06x", call_ref);
+ } else {
+ /* SSCOP with some unknown protocol atop it */
+ ND_PRINT("SSCOP, proto %u ", GET_U_1(p + PROTO_POS));
+ }
+}
+
+/*
+ * Print an ATM PDU (such as an AAL5 PDU).
+ */
+void
+atm_print(netdissect_options *ndo,
+ u_int vpi, u_int vci, u_int traftype, const u_char *p, u_int length,
+ u_int caplen)
+{
+ ndo->ndo_protocol = "atm";
+ if (ndo->ndo_eflag)
+ ND_PRINT("VPI:%u VCI:%u ", vpi, vci);
+
+ if (vpi == 0) {
+ switch (vci) {
+
+ case VCI_PPC:
+ sig_print(ndo, p);
+ return;
+
+ case VCI_BCC:
+ ND_PRINT("broadcast sig: ");
+ return;
+
+ case VCI_OAMF4SC: /* fall through */
+ case VCI_OAMF4EC:
+ oam_print(ndo, p, length, ATM_OAM_HEC);
+ return;
+
+ case VCI_METAC:
+ ND_PRINT("meta: ");
+ return;
+
+ case VCI_ILMIC:
+ ND_PRINT("ilmi: ");
+ snmp_print(ndo, p, length);
+ return;
+ }
+ }
+
+ switch (traftype) {
+
+ case ATM_LLC:
+ default:
+ /*
+ * Assumes traffic is LLC if unknown.
+ */
+ atm_llc_print(ndo, p, length, caplen);
+ break;
+
+ case ATM_LANE:
+ lane_print(ndo, p, length, caplen);
+ break;
+ }
+}
+
+struct oam_fm_loopback_t {
+ nd_uint8_t loopback_indicator;
+ nd_uint32_t correlation_tag;
+ nd_byte loopback_id[12];
+ nd_byte source_id[12];
+ nd_byte unused[16];
+};
+
+struct oam_fm_ais_rdi_t {
+ nd_uint8_t failure_type;
+ nd_byte failure_location[16];
+ nd_byte unused[28];
+};
+
+void
+oam_print(netdissect_options *ndo,
+ const u_char *p, u_int length, u_int hec)
+{
+ uint32_t cell_header;
+ uint16_t vpi, vci, cksum, cksum_shouldbe, idx;
+ uint8_t cell_type, func_type, payload, clp;
+ const struct tok *oam_functype_str;
+
+ union {
+ const struct oam_fm_loopback_t *oam_fm_loopback;
+ const struct oam_fm_ais_rdi_t *oam_fm_ais_rdi;
+ } oam_ptr;
+
+ ndo->ndo_protocol = "oam";
+ cell_header = GET_BE_U_4(p + hec);
+ cell_type = (GET_U_1((p + ATM_HDR_LEN_NOHEC + hec)) >> 4) & 0x0f;
+ func_type = GET_U_1((p + ATM_HDR_LEN_NOHEC + hec)) & 0x0f;
+
+ vpi = (cell_header>>20)&0xff;
+ vci = (cell_header>>4)&0xffff;
+ payload = (cell_header>>1)&0x7;
+ clp = cell_header&0x1;
+
+ ND_PRINT("%s, vpi %u, vci %u, payload [ %s ], clp %u, length %u",
+ tok2str(oam_f_values, "OAM F5", vci),
+ vpi, vci,
+ tok2str(atm_pty_values, "Unknown", payload),
+ clp, length);
+
+ if (!ndo->ndo_vflag) {
+ return;
+ }
+
+ ND_PRINT("\n\tcell-type %s (%u)",
+ tok2str(oam_celltype_values, "unknown", cell_type),
+ cell_type);
+
+ oam_functype_str = uint2tokary(oam_celltype2tokary, cell_type);
+ if (oam_functype_str == NULL)
+ ND_PRINT(", func-type unknown (%u)", func_type);
+ else
+ ND_PRINT(", func-type %s (%u)",
+ tok2str(oam_functype_str, "none", func_type),
+ func_type);
+
+ p += ATM_HDR_LEN_NOHEC + hec;
+
+ switch (cell_type << 4 | func_type) {
+ case (OAM_CELLTYPE_FM << 4 | OAM_FM_FUNCTYPE_LOOPBACK):
+ oam_ptr.oam_fm_loopback = (const struct oam_fm_loopback_t *)(p + OAM_CELLTYPE_FUNCTYPE_LEN);
+ ND_TCHECK_SIZE(oam_ptr.oam_fm_loopback);
+ ND_PRINT("\n\tLoopback-Indicator %s, Correlation-Tag 0x%08x",
+ tok2str(oam_fm_loopback_indicator_values,
+ "Unknown",
+ GET_U_1(oam_ptr.oam_fm_loopback->loopback_indicator) & OAM_FM_LOOPBACK_INDICATOR_MASK),
+ GET_BE_U_4(oam_ptr.oam_fm_loopback->correlation_tag));
+ ND_PRINT("\n\tLocation-ID ");
+ for (idx = 0; idx < sizeof(oam_ptr.oam_fm_loopback->loopback_id); idx++) {
+ if (idx % 2) {
+ ND_PRINT("%04x ",
+ GET_BE_U_2(&oam_ptr.oam_fm_loopback->loopback_id[idx]));
+ }
+ }
+ ND_PRINT("\n\tSource-ID ");
+ for (idx = 0; idx < sizeof(oam_ptr.oam_fm_loopback->source_id); idx++) {
+ if (idx % 2) {
+ ND_PRINT("%04x ",
+ GET_BE_U_2(&oam_ptr.oam_fm_loopback->source_id[idx]));
+ }
+ }
+ break;
+
+ case (OAM_CELLTYPE_FM << 4 | OAM_FM_FUNCTYPE_AIS):
+ case (OAM_CELLTYPE_FM << 4 | OAM_FM_FUNCTYPE_RDI):
+ oam_ptr.oam_fm_ais_rdi = (const struct oam_fm_ais_rdi_t *)(p + OAM_CELLTYPE_FUNCTYPE_LEN);
+ ND_TCHECK_SIZE(oam_ptr.oam_fm_ais_rdi);
+ ND_PRINT("\n\tFailure-type 0x%02x",
+ GET_U_1(oam_ptr.oam_fm_ais_rdi->failure_type));
+ ND_PRINT("\n\tLocation-ID ");
+ for (idx = 0; idx < sizeof(oam_ptr.oam_fm_ais_rdi->failure_location); idx++) {
+ if (idx % 2) {
+ ND_PRINT("%04x ",
+ GET_BE_U_2(&oam_ptr.oam_fm_ais_rdi->failure_location[idx]));
+ }
+ }
+ break;
+
+ case (OAM_CELLTYPE_FM << 4 | OAM_FM_FUNCTYPE_CONTCHECK):
+ /* FIXME */
+ break;
+
+ default:
+ break;
+ }
+
+ /* crc10 checksum verification */
+ cksum = GET_BE_U_2(p + OAM_CELLTYPE_FUNCTYPE_LEN + OAM_FUNCTION_SPECIFIC_LEN)
+ & OAM_CRC10_MASK;
+ cksum_shouldbe = verify_crc10_cksum(0, p, OAM_PAYLOAD_LEN);
+
+ ND_PRINT("\n\tcksum 0x%03x (%scorrect)",
+ cksum,
+ cksum_shouldbe == 0 ? "" : "in");
+}
diff --git a/print-babel.c b/print-babel.c
new file mode 100644
index 0000000..d802a72
--- /dev/null
+++ b/print-babel.c
@@ -0,0 +1,863 @@
+/*
+ * Copyright (c) 2007-2011 Grégoire Henry, Juliusz Chroboczek
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: Babel Routing Protocol printer */
+/* Specifications:
+ *
+ * RFC 6126
+ * RFC 7298
+ * RFC 7557
+ * draft-ietf-babel-rfc6126bis-17
+ * draft-ietf-babel-hmac-10
+ * draft-ietf-babel-source-specific-0
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+static void babel_print_v2(netdissect_options *, const u_char *cp, u_int length);
+
+void
+babel_print(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ ndo->ndo_protocol = "babel";
+ ND_PRINT("babel");
+
+ ND_TCHECK_4(cp);
+
+ if(GET_U_1(cp) != 42) {
+ ND_PRINT(" invalid header");
+ return;
+ } else {
+ ND_PRINT(" %u", GET_U_1(cp + 1));
+ }
+
+ switch(GET_U_1(cp + 1)) {
+ case 2:
+ babel_print_v2(ndo, cp, length);
+ break;
+ default:
+ ND_PRINT(" unknown version");
+ break;
+ }
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+/* TLVs */
+#define MESSAGE_PAD1 0
+#define MESSAGE_PADN 1
+#define MESSAGE_ACK_REQ 2
+#define MESSAGE_ACK 3
+#define MESSAGE_HELLO 4
+#define MESSAGE_IHU 5
+#define MESSAGE_ROUTER_ID 6
+#define MESSAGE_NH 7
+#define MESSAGE_UPDATE 8
+#define MESSAGE_ROUTE_REQUEST 9
+#define MESSAGE_SEQNO_REQUEST 10
+#define MESSAGE_TSPC 11
+#define MESSAGE_HMAC 12
+#define MESSAGE_UPDATE_SRC_SPECIFIC 13 /* last appearance in draft-boutier-babel-source-specific-01 */
+#define MESSAGE_REQUEST_SRC_SPECIFIC 14 /* idem */
+#define MESSAGE_MH_REQUEST_SRC_SPECIFIC 15 /* idem */
+#define MESSAGE_MAC 16
+#define MESSAGE_PC 17
+#define MESSAGE_CHALLENGE_REQUEST 18
+#define MESSAGE_CHALLENGE_REPLY 19
+
+/* sub-TLVs */
+#define MESSAGE_SUB_PAD1 0
+#define MESSAGE_SUB_PADN 1
+#define MESSAGE_SUB_DIVERSITY 2
+#define MESSAGE_SUB_TIMESTAMP 3
+
+/* "Mandatory" bit in sub-TLV types */
+#define MANDATORY_MASK 0x80
+
+/* Flags for the Hello TLV */
+#define UNICAST_MASK 0x8000
+
+/* Diversity sub-TLV channel codes */
+static const struct tok diversity_str[] = {
+ { 0, "reserved" },
+ { 255, "all" },
+ { 0, NULL }
+};
+
+static const char *
+format_id(netdissect_options *ndo, const u_char *id)
+{
+ static char buf[25];
+ snprintf(buf, 25, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
+ GET_U_1(id), GET_U_1(id + 1), GET_U_1(id + 2),
+ GET_U_1(id + 3), GET_U_1(id + 4), GET_U_1(id + 5),
+ GET_U_1(id + 6), GET_U_1(id + 7));
+ buf[24] = '\0';
+ return buf;
+}
+
+static const unsigned char v4prefix[16] =
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
+
+static const char *
+format_prefix(netdissect_options *ndo, const u_char *prefix, unsigned char plen)
+{
+ static char buf[50];
+
+ /*
+ * prefix points to a buffer on the stack into which the prefix has
+ * been placed, so we can't use GET_IPADDR_STRING() or
+ * GET_IP6ADDR_STRING() on it.
+ */
+ if(plen >= 96 && memcmp(prefix, v4prefix, 12) == 0)
+ snprintf(buf, 50, "%s/%u", ipaddr_string(ndo, prefix + 12), plen - 96);
+ else
+ snprintf(buf, 50, "%s/%u", ip6addr_string(ndo, prefix), plen);
+ buf[49] = '\0';
+ return buf;
+}
+
+static const char *
+format_address(netdissect_options *ndo, const u_char *prefix)
+{
+ /*
+ * prefix points to a buffer on the stack into which the prefix has
+ * been placed, so we can't use GET_IPADDR_STRING() or
+ * GET_IP6ADDR_STRING() on it.
+ */
+ if(memcmp(prefix, v4prefix, 12) == 0)
+ return ipaddr_string(ndo, prefix + 12);
+ else
+ return ip6addr_string(ndo, prefix);
+}
+
+static const char *
+format_interval(const uint16_t i)
+{
+ static char buf[sizeof("000.00s")];
+
+ if (i == 0)
+ return "0.0s (bogus)";
+ snprintf(buf, sizeof(buf), "%u.%02us", i / 100, i % 100);
+ return buf;
+}
+
+static const char *
+format_interval_update(const uint16_t i)
+{
+ return i == 0xFFFF ? "infinity" : format_interval(i);
+}
+
+static const char *
+format_timestamp(const uint32_t i)
+{
+ static char buf[sizeof("0000.000000s")];
+ snprintf(buf, sizeof(buf), "%u.%06us", i / 1000000, i % 1000000);
+ return buf;
+}
+
+/* Return number of octets consumed from the input buffer (not the prefix length
+ * in bytes), or -1 for encoding error. */
+static int
+network_prefix(int ae, int plen, unsigned int omitted,
+ const unsigned char *p, const unsigned char *dp,
+ unsigned int len, unsigned char *p_r)
+{
+ unsigned pb;
+ unsigned char prefix[16];
+ int consumed = 0;
+
+ if(plen >= 0)
+ pb = (plen + 7) / 8;
+ else if(ae == 1)
+ pb = 4;
+ else
+ pb = 16;
+
+ if(pb > 16)
+ return -1;
+
+ memset(prefix, 0, 16);
+
+ switch(ae) {
+ case 0: break;
+ case 1:
+ if(omitted > 4 || pb > 4 || (pb > omitted && len < pb - omitted))
+ return -1;
+ memcpy(prefix, v4prefix, 12);
+ if(omitted) {
+ if (dp == NULL) return -1;
+ memcpy(prefix, dp, 12 + omitted);
+ }
+ if(pb > omitted) {
+ memcpy(prefix + 12 + omitted, p, pb - omitted);
+ consumed = pb - omitted;
+ }
+ break;
+ case 2:
+ if(omitted > 16 || (pb > omitted && len < pb - omitted))
+ return -1;
+ if(omitted) {
+ if (dp == NULL) return -1;
+ memcpy(prefix, dp, omitted);
+ }
+ if(pb > omitted) {
+ memcpy(prefix + omitted, p, pb - omitted);
+ consumed = pb - omitted;
+ }
+ break;
+ case 3:
+ if(pb > 8 && len < pb - 8) return -1;
+ prefix[0] = 0xfe;
+ prefix[1] = 0x80;
+ if(pb > 8) {
+ memcpy(prefix + 8, p, pb - 8);
+ consumed = pb - 8;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ memcpy(p_r, prefix, 16);
+ return consumed;
+}
+
+static int
+network_address(int ae, const unsigned char *a, unsigned int len,
+ unsigned char *a_r)
+{
+ return network_prefix(ae, -1, 0, a, NULL, len, a_r);
+}
+
+/*
+ * Sub-TLVs consume the "extra data" of Babel TLVs (see Section 4.3 of RFC6126),
+ * their encoding is similar to the encoding of TLVs, but the type namespace is
+ * different:
+ *
+ * o Type 0 stands for Pad1 sub-TLV with the same encoding as the Pad1 TLV.
+ * o Type 1 stands for PadN sub-TLV with the same encoding as the PadN TLV.
+ * o Type 2 stands for Diversity sub-TLV, which propagates diversity routing
+ * data. Its body is a variable-length sequence of 8-bit unsigned integers,
+ * each representing per-hop number of interfering radio channel for the
+ * prefix. Channel 0 is invalid and must not be used in the sub-TLV, channel
+ * 255 interferes with any other channel.
+ * o Type 3 stands for Timestamp sub-TLV, used to compute RTT between
+ * neighbours. In the case of a Hello TLV, the body stores a 32-bits
+ * timestamp, while in the case of a IHU TLV, two 32-bits timestamps are
+ * stored.
+ *
+ * Sub-TLV types 0 and 1 are valid for any TLV type, whether sub-TLV type 2 is
+ * only valid for TLV type 8 (Update). Note that within an Update TLV a missing
+ * Diversity sub-TLV is not the same as a Diversity sub-TLV with an empty body.
+ * The former would mean a lack of any claims about the interference, and the
+ * latter would state that interference is definitely absent.
+ * A type 3 sub-TLV is valid both for Hello and IHU TLVs, though the exact
+ * semantic of the sub-TLV is different in each case.
+ */
+static void
+subtlvs_print(netdissect_options *ndo,
+ const u_char *cp, const u_char *ep, const uint8_t tlv_type)
+{
+ uint8_t subtype, sublen;
+ const char *sep;
+ uint32_t t1, t2;
+
+ while (cp < ep) {
+ subtype = GET_U_1(cp);
+ cp++;
+ if(subtype == MESSAGE_SUB_PAD1) {
+ ND_PRINT(" sub-pad1");
+ continue;
+ }
+ if ((MANDATORY_MASK & subtype) != 0)
+ ND_PRINT(" (M)");
+ if(cp == ep)
+ goto invalid;
+ sublen = GET_U_1(cp);
+ cp++;
+ if(cp + sublen > ep)
+ goto invalid;
+
+ switch(subtype) {
+ case MESSAGE_SUB_PADN:
+ ND_PRINT(" sub-padn");
+ cp += sublen;
+ break;
+ case MESSAGE_SUB_DIVERSITY:
+ ND_PRINT(" sub-diversity");
+ if (sublen == 0) {
+ ND_PRINT(" empty");
+ break;
+ }
+ sep = " ";
+ while (sublen) {
+ ND_PRINT("%s%s", sep,
+ tok2str(diversity_str, "%u", GET_U_1(cp)));
+ cp++;
+ sep = "-";
+ sublen--;
+ }
+ if(tlv_type != MESSAGE_UPDATE &&
+ tlv_type != MESSAGE_UPDATE_SRC_SPECIFIC)
+ ND_PRINT(" (bogus)");
+ break;
+ case MESSAGE_SUB_TIMESTAMP:
+ ND_PRINT(" sub-timestamp");
+ if(tlv_type == MESSAGE_HELLO) {
+ if(sublen < 4)
+ goto invalid;
+ t1 = GET_BE_U_4(cp);
+ ND_PRINT(" %s", format_timestamp(t1));
+ } else if(tlv_type == MESSAGE_IHU) {
+ if(sublen < 8)
+ goto invalid;
+ t1 = GET_BE_U_4(cp);
+ ND_PRINT(" %s", format_timestamp(t1));
+ t2 = GET_BE_U_4(cp + 4);
+ ND_PRINT("|%s", format_timestamp(t2));
+ } else
+ ND_PRINT(" (bogus)");
+ cp += sublen;
+ break;
+ default:
+ ND_PRINT(" sub-unknown-0x%02x", subtype);
+ cp += sublen;
+ } /* switch */
+ } /* while */
+ return;
+
+ invalid:
+ nd_print_invalid(ndo);
+}
+
+#define ICHECK(i, l) \
+ if ((i) + (l) > tlvs_length || (i) + (l) > packet_length_remaining) \
+ goto invalid;
+
+static int
+babel_print_v2_tlvs(netdissect_options *ndo,
+ const u_char *cp, u_int tlvs_length,
+ u_int packet_length_remaining)
+{
+ u_int i;
+ u_char v4_prefix[16] =
+ {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0, 0, 0, 0 };
+ u_char v6_prefix[16] = {0};
+
+ i = 0;
+ while(i < tlvs_length) {
+ const u_char *message;
+ uint8_t type;
+ u_int len;
+
+ message = cp + i;
+
+ ICHECK(i, 1);
+ if((type = GET_U_1(message)) == MESSAGE_PAD1) {
+ ND_PRINT(ndo->ndo_vflag ? "\n\tPad 1" : " pad1");
+ i += 1;
+ continue;
+ }
+
+ ICHECK(i, 2);
+ ND_TCHECK_2(message);
+ len = GET_U_1(message + 1);
+
+ ICHECK(i, 2 + len);
+ ND_TCHECK_LEN(message, 2 + len);
+
+ switch(type) {
+ case MESSAGE_PADN: {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" padN");
+ else
+ ND_PRINT("\n\tPad %u", len + 2);
+ }
+ break;
+
+ case MESSAGE_ACK_REQ: {
+ u_short nonce, interval;
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" ack-req");
+ else {
+ ND_PRINT("\n\tAcknowledgment Request ");
+ if(len < 6) goto invalid;
+ nonce = GET_BE_U_2(message + 4);
+ interval = GET_BE_U_2(message + 6);
+ ND_PRINT("%04x %s", nonce, format_interval(interval));
+ }
+ }
+ break;
+
+ case MESSAGE_ACK: {
+ u_short nonce;
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" ack");
+ else {
+ ND_PRINT("\n\tAcknowledgment ");
+ if(len < 2) goto invalid;
+ nonce = GET_BE_U_2(message + 2);
+ ND_PRINT("%04x", nonce);
+ }
+ }
+ break;
+
+ case MESSAGE_HELLO: {
+ u_short seqno, interval, unicast;
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" hello");
+ else {
+ ND_PRINT("\n\tHello ");
+ if(len < 6) goto invalid;
+ unicast = (GET_BE_U_2(message + 2) & UNICAST_MASK);
+ seqno = GET_BE_U_2(message + 4);
+ interval = GET_BE_U_2(message + 6);
+ if(unicast)
+ ND_PRINT("(Unicast) ");
+ ND_PRINT("seqno %u ", seqno);
+ if(interval!=0)
+ ND_PRINT("interval %s", format_interval(interval));
+ else
+ ND_PRINT("unscheduled");
+ /* Extra data. */
+ if(len > 6)
+ subtlvs_print(ndo, message + 8, message + 2 + len, type);
+ }
+ }
+ break;
+
+ case MESSAGE_IHU: {
+ unsigned short rxcost, interval;
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" ihu");
+ else {
+ u_char address[16];
+ u_char ae;
+ int rc;
+ ND_PRINT("\n\tIHU ");
+ if(len < 6) goto invalid;
+ rxcost = GET_BE_U_2(message + 4);
+ interval = GET_BE_U_2(message + 6);
+ ae = GET_U_1(message + 2);
+ rc = network_address(ae, message + 8,
+ len - 6, address);
+ if(rc < 0) { nd_print_trunc(ndo); break; }
+ ND_PRINT("%s rxcost %u interval %s",
+ ae == 0 ? "any" : format_address(ndo, address),
+ rxcost, format_interval(interval));
+ /* Extra data. */
+ if((u_int)rc < len - 6)
+ subtlvs_print(ndo, message + 8 + rc, message + 2 + len,
+ type);
+ }
+ }
+ break;
+
+ case MESSAGE_ROUTER_ID: {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" router-id");
+ else {
+ ND_PRINT("\n\tRouter Id");
+ if(len < 10) goto invalid;
+ ND_PRINT(" %s", format_id(ndo, message + 4));
+ }
+ }
+ break;
+
+ case MESSAGE_NH: {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" nh");
+ else {
+ int rc;
+ u_char ae;
+ u_char nh[16];
+ ND_PRINT("\n\tNext Hop");
+ if(len < 2) goto invalid;
+ ae = GET_U_1(message + 2);
+ rc = network_address(ae, message + 4,
+ len - 2, nh);
+ if(rc < 0) goto invalid;
+ ND_PRINT(" %s", ae == 0 ? "invalid AE 0" : format_address(ndo, nh));
+ }
+ }
+ break;
+
+ case MESSAGE_UPDATE: {
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(" update");
+ if(len < 10)
+ goto invalid;
+ else
+ ND_PRINT("%s%s%s",
+ (GET_U_1(message + 3) & 0x80) ? "/prefix": "",
+ (GET_U_1(message + 3) & 0x40) ? "/id" : "",
+ (GET_U_1(message + 3) & 0x3f) ? "/unknown" : "");
+ } else {
+ u_short interval, seqno, metric;
+ u_char ae, plen;
+ int rc;
+ u_char prefix[16];
+ ND_PRINT("\n\tUpdate");
+ if(len < 10) goto invalid;
+ ae = GET_U_1(message + 2);
+ plen = GET_U_1(message + 4) + (GET_U_1(message + 2) == 1 ? 96 : 0);
+ rc = network_prefix(ae,
+ GET_U_1(message + 4),
+ GET_U_1(message + 5),
+ message + 12,
+ GET_U_1(message + 2) == 1 ? v4_prefix : v6_prefix,
+ len - 10, prefix);
+ if(rc < 0) goto invalid;
+ interval = GET_BE_U_2(message + 6);
+ seqno = GET_BE_U_2(message + 8);
+ metric = GET_BE_U_2(message + 10);
+ ND_PRINT("%s%s%s %s metric %u seqno %u interval %s",
+ (GET_U_1(message + 3) & 0x80) ? "/prefix": "",
+ (GET_U_1(message + 3) & 0x40) ? "/id" : "",
+ (GET_U_1(message + 3) & 0x3f) ? "/unknown" : "",
+ ae == 0 ? "any" : format_prefix(ndo, prefix, plen),
+ metric, seqno, format_interval_update(interval));
+ if(GET_U_1(message + 3) & 0x80) {
+ if(GET_U_1(message + 2) == 1)
+ memcpy(v4_prefix, prefix, 16);
+ else
+ memcpy(v6_prefix, prefix, 16);
+ }
+ /* extra data? */
+ if((u_int)rc < len - 10)
+ subtlvs_print(ndo, message + 12 + rc, message + 2 + len, type);
+ }
+ }
+ break;
+
+ case MESSAGE_ROUTE_REQUEST: {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" route-request");
+ else {
+ int rc;
+ u_char prefix[16], ae, plen;
+ ND_PRINT("\n\tRoute Request ");
+ if(len < 2) goto invalid;
+ ae = GET_U_1(message + 2);
+ plen = GET_U_1(message + 3) + (GET_U_1(message + 2) == 1 ? 96 : 0);
+ rc = network_prefix(ae,
+ GET_U_1(message + 3), 0,
+ message + 4, NULL, len - 2, prefix);
+ if(rc < 0) goto invalid;
+ ND_PRINT("for %s",
+ ae == 0 ? "any" : format_prefix(ndo, prefix, plen));
+ }
+ }
+ break;
+
+ case MESSAGE_SEQNO_REQUEST : {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" seqno-request");
+ else {
+ int rc;
+ u_short seqno;
+ u_char prefix[16], ae, plen;
+ ND_PRINT("\n\tSeqno Request ");
+ if(len < 14) goto invalid;
+ ae = GET_U_1(message + 2);
+ seqno = GET_BE_U_2(message + 4);
+ rc = network_prefix(ae,
+ GET_U_1(message + 3), 0,
+ message + 16, NULL, len - 14, prefix);
+ if(rc < 0) goto invalid;
+ plen = GET_U_1(message + 3) + (GET_U_1(message + 2) == 1 ? 96 : 0);
+ ND_PRINT("(%u hops) for %s seqno %u id %s",
+ GET_U_1(message + 6),
+ ae == 0 ? "invalid AE 0" : format_prefix(ndo, prefix, plen),
+ seqno, format_id(ndo, message + 8));
+ }
+ }
+ break;
+ case MESSAGE_TSPC :
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" tspc");
+ else {
+ ND_PRINT("\n\tTS/PC ");
+ if(len < 6) goto invalid;
+ ND_PRINT("timestamp %u packetcounter %u",
+ GET_BE_U_4(message + 4),
+ GET_BE_U_2(message + 2));
+ }
+ break;
+ case MESSAGE_HMAC : {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" hmac");
+ else {
+ unsigned j;
+ ND_PRINT("\n\tHMAC ");
+ if(len < 18) goto invalid;
+ ND_PRINT("key-id %u digest-%u ", GET_BE_U_2(message + 2),
+ len - 2);
+ for (j = 0; j < len - 2; j++)
+ ND_PRINT("%02X", GET_U_1(message + j + 4));
+ }
+ }
+ break;
+
+ case MESSAGE_UPDATE_SRC_SPECIFIC : {
+ if(!ndo->ndo_vflag) {
+ ND_PRINT(" ss-update");
+ } else {
+ u_char prefix[16], src_prefix[16];
+ u_short interval, seqno, metric;
+ u_char ae, plen, src_plen, omitted;
+ int rc;
+ int parsed_len = 10;
+ ND_PRINT("\n\tSS-Update");
+ if(len < 10) goto invalid;
+ ae = GET_U_1(message + 2);
+ src_plen = GET_U_1(message + 3);
+ plen = GET_U_1(message + 4);
+ omitted = GET_U_1(message + 5);
+ interval = GET_BE_U_2(message + 6);
+ seqno = GET_BE_U_2(message + 8);
+ metric = GET_BE_U_2(message + 10);
+ rc = network_prefix(ae, plen, omitted, message + 2 + parsed_len,
+ ae == 1 ? v4_prefix : v6_prefix,
+ len - parsed_len, prefix);
+ if(rc < 0) goto invalid;
+ if(ae == 1)
+ plen += 96;
+ parsed_len += rc;
+ rc = network_prefix(ae, src_plen, 0, message + 2 + parsed_len,
+ NULL, len - parsed_len, src_prefix);
+ if(rc < 0) goto invalid;
+ if(ae == 1)
+ src_plen += 96;
+ parsed_len += rc;
+
+ ND_PRINT(" %s from", format_prefix(ndo, prefix, plen));
+ ND_PRINT(" %s metric %u seqno %u interval %s",
+ format_prefix(ndo, src_prefix, src_plen),
+ metric, seqno, format_interval_update(interval));
+ /* extra data? */
+ if((u_int)parsed_len < len)
+ subtlvs_print(ndo, message + 2 + parsed_len,
+ message + 2 + len, type);
+ }
+ }
+ break;
+
+ case MESSAGE_REQUEST_SRC_SPECIFIC : {
+ if(!ndo->ndo_vflag)
+ ND_PRINT(" ss-request");
+ else {
+ int rc, parsed_len = 3;
+ u_char ae, plen, src_plen, prefix[16], src_prefix[16];
+ ND_PRINT("\n\tSS-Request ");
+ if(len < 3) goto invalid;
+ ae = GET_U_1(message + 2);
+ plen = GET_U_1(message + 3);
+ src_plen = GET_U_1(message + 4);
+ rc = network_prefix(ae, plen, 0, message + 2 + parsed_len,
+ NULL, len - parsed_len, prefix);
+ if(rc < 0) goto invalid;
+ if(ae == 1)
+ plen += 96;
+ parsed_len += rc;
+ rc = network_prefix(ae, src_plen, 0, message + 2 + parsed_len,
+ NULL, len - parsed_len, src_prefix);
+ if(rc < 0) goto invalid;
+ if(ae == 1)
+ src_plen += 96;
+ parsed_len += rc;
+ if(ae == 0) {
+ ND_PRINT("for any");
+ } else {
+ ND_PRINT("for (%s, ", format_prefix(ndo, prefix, plen));
+ ND_PRINT("%s)", format_prefix(ndo, src_prefix, src_plen));
+ }
+ }
+ }
+ break;
+
+ case MESSAGE_MH_REQUEST_SRC_SPECIFIC : {
+ if(!ndo->ndo_vflag)
+ ND_PRINT(" ss-mh-request");
+ else {
+ int rc, parsed_len = 14;
+ u_short seqno;
+ u_char ae, plen, src_plen, prefix[16], src_prefix[16], hopc;
+ const u_char *router_id = NULL;
+ ND_PRINT("\n\tSS-MH-Request ");
+ if(len < 14) goto invalid;
+ ae = GET_U_1(message + 2);
+ plen = GET_U_1(message + 3);
+ seqno = GET_BE_U_2(message + 4);
+ hopc = GET_U_1(message + 6);
+ src_plen = GET_U_1(message + 7);
+ router_id = message + 8;
+ rc = network_prefix(ae, plen, 0, message + 2 + parsed_len,
+ NULL, len - parsed_len, prefix);
+ if(rc < 0) goto invalid;
+ if(ae == 1)
+ plen += 96;
+ parsed_len += rc;
+ rc = network_prefix(ae, src_plen, 0, message + 2 + parsed_len,
+ NULL, len - parsed_len, src_prefix);
+ if(rc < 0) goto invalid;
+ if(ae == 1)
+ src_plen += 96;
+ ND_PRINT("(%u hops) for (%s, ",
+ hopc, format_prefix(ndo, prefix, plen));
+ ND_PRINT("%s) seqno %u id %s",
+ format_prefix(ndo, src_prefix, src_plen),
+ seqno, format_id(ndo, router_id));
+ }
+ }
+ break;
+
+ case MESSAGE_MAC: {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" mac");
+ else {
+ ND_PRINT("\n\tMAC ");
+ ND_PRINT("len %u", len);
+ }
+ }
+ break;
+
+ case MESSAGE_PC: {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" pc");
+ else {
+ ND_PRINT("\n\tPC");
+ if(len < 4) goto invalid;
+ ND_PRINT(" value %u",
+ GET_BE_U_4(message + 2));
+ ND_PRINT(" index len %u", len-4);
+ }
+ }
+ break;
+
+ case MESSAGE_CHALLENGE_REQUEST: {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" challenge_request");
+ else {
+ ND_PRINT("\n\tChallenge Request");
+ if(len > 192) goto invalid;
+ ND_PRINT(" len %u", len);
+ }
+ }
+ break;
+
+ case MESSAGE_CHALLENGE_REPLY: {
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" challenge_reply");
+ else {
+ ND_PRINT("\n\tChallenge Reply");
+ if (len > 192) goto invalid;
+ ND_PRINT(" len %u", len);
+ }
+ }
+ break;
+
+ default:
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" unknown");
+ else
+ ND_PRINT("\n\tUnknown message type %u", type);
+ }
+ i += len + 2;
+ }
+
+ return 0; /* OK */
+
+trunc:
+ return -1; /* packet truncated by capture process */
+
+invalid:
+ return -2; /* packet is invalid */
+}
+
+static void
+babel_print_v2(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ u_short bodylen;
+ int ret;
+
+ ND_TCHECK_4(cp);
+ if (length < 4)
+ goto invalid;
+ bodylen = GET_BE_U_2(cp + 2);
+ ND_PRINT(" (%u)", bodylen);
+ length -= 4;
+ cp += 4;
+
+ /* Process the TLVs in the body */
+ if (length < bodylen)
+ goto invalid;
+ ret = babel_print_v2_tlvs(ndo, cp, bodylen, length);
+ if (ret == -1)
+ goto trunc;
+ if (ret == -2)
+ goto invalid;
+ length -= bodylen;
+ cp += bodylen;
+
+ /* If there's a trailer, process the TLVs in the trailer */
+ if (length != 0) {
+ if(ndo->ndo_vflag) ND_PRINT("\n\t----");
+ else ND_PRINT(" |");
+ ret = babel_print_v2_tlvs(ndo, cp, length, length);
+ if (ret == -1)
+ goto trunc;
+ if (ret == -2)
+ goto invalid;
+ }
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+ return;
+
+ invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-bcm-li.c b/print-bcm-li.c
new file mode 100644
index 0000000..12b1ebb
--- /dev/null
+++ b/print-bcm-li.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Broadcom LI Printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#define BCM_LI_SHIM_LEN 4
+
+static const struct tok bcm_li_direction_values[] = {
+ { 1, "unused" },
+ { 2, "egress" },
+ { 3, "ingress" },
+ { 0, NULL}
+};
+
+#define BCM_LI_PKT_TYPE_UNDECIDED 4
+#define BCM_LI_PKT_TYPE_IPV4 5
+#define BCM_LI_PKT_TYPE_IPV6 6
+#define BCM_LI_PKT_TYPE_ETHERNET 7
+
+static const struct tok bcm_li_pkt_type_values[] = {
+ { BCM_LI_PKT_TYPE_UNDECIDED, "undecided" },
+ { BCM_LI_PKT_TYPE_IPV4, "ipv4" },
+ { BCM_LI_PKT_TYPE_IPV6, "ipv6" },
+ { BCM_LI_PKT_TYPE_ETHERNET, "ethernet" },
+ { 0, NULL}
+};
+
+static const struct tok bcm_li_pkt_subtype_values[] = {
+ { 1, "single VLAN tag" },
+ { 2, "double VLAN tag" },
+ { 3, "untagged" },
+ { 0, NULL}
+};
+
+void
+bcm_li_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ u_int shim, direction, pkt_type, pkt_subtype, li_id;
+
+ ndo->ndo_protocol = "bcm_li";
+ if (length < BCM_LI_SHIM_LEN) {
+ ND_PRINT(" (length %u < %u)", length, BCM_LI_SHIM_LEN);
+ goto invalid;
+ }
+ shim = GET_BE_U_4(bp);
+
+ direction = (shim >> 29) & 0x7;
+ pkt_type = (shim >> 25) & 0xf;
+ pkt_subtype = (shim >> 22) & 0x7;
+ li_id = shim & 0x3fffff;
+
+ length -= BCM_LI_SHIM_LEN;
+ bp += BCM_LI_SHIM_LEN;
+
+ ND_PRINT("%sBCM-LI-SHIM: direction %s, pkt-type %s, pkt-subtype %s, li-id %u%s",
+ ndo->ndo_vflag ? "\n " : "",
+ tok2str(bcm_li_direction_values, "unknown", direction),
+ tok2str(bcm_li_pkt_type_values, "unknown", pkt_type),
+ tok2str(bcm_li_pkt_subtype_values, "unknown", pkt_subtype),
+ li_id,
+ ndo->ndo_vflag ? "\n ": "");
+
+ if (!ndo->ndo_vflag) {
+ ND_TCHECK_LEN(bp, length);
+ return;
+ }
+
+ switch (pkt_type) {
+ case BCM_LI_PKT_TYPE_ETHERNET:
+ ether_print(ndo, bp, length, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+ break;
+ case BCM_LI_PKT_TYPE_IPV4:
+ ip_print(ndo, bp, length);
+ break;
+ case BCM_LI_PKT_TYPE_IPV6:
+ ip6_print(ndo, bp, length);
+ break;
+ case BCM_LI_PKT_TYPE_UNDECIDED:
+
+ /*
+ * Guess IP version from first nibble.
+ */
+ if ((GET_U_1(bp) >> 4) == 4) {
+ ip_print(ndo, bp, length);
+ } else if ((GET_U_1(bp) >> 4) == 6) {
+ ip6_print(ndo, bp, length);
+ } else {
+ ND_PRINT("unknown payload");
+ }
+ break;
+
+ default:
+ goto invalid;
+ }
+
+ return;
+invalid:
+ nd_print_invalid(ndo);
+}
+
diff --git a/print-beep.c b/print-beep.c
new file mode 100644
index 0000000..76017ea
--- /dev/null
+++ b/print-beep.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2000, Richard Sharpe
+ *
+ * This software may be distributed either under the terms of the
+ * BSD-style license that accompanies tcpdump or under the GNU GPL
+ * version 2 or later.
+ *
+ * print-beep.c
+ *
+ */
+
+/* \summary: Blocks Extensible Exchange Protocol (BEEP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+
+/* Check for a string but not go beyond length
+ * Return TRUE on match, FALSE otherwise
+ *
+ * Looks at the first few chars up to tl1 ...
+ */
+
+static int
+l_strnstart(netdissect_options *ndo, const char *tstr1, u_int tl1,
+ const char *str2, u_int l2)
+{
+ if (!ND_TTEST_LEN(str2, tl1)) {
+ /*
+ * We don't have tl1 bytes worth of captured data
+ * for the string, so we can't check for this
+ * string.
+ */
+ return 0;
+ }
+ if (tl1 > l2)
+ return 0;
+
+ return (strncmp(tstr1, str2, tl1) == 0 ? 1 : 0);
+}
+
+void
+beep_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+
+ ndo->ndo_protocol = "beep";
+ if (l_strnstart(ndo, "MSG", 4, (const char *)bp, length)) /* A REQuest */
+ ND_PRINT(" BEEP MSG");
+ else if (l_strnstart(ndo, "RPY ", 4, (const char *)bp, length))
+ ND_PRINT(" BEEP RPY");
+ else if (l_strnstart(ndo, "ERR ", 4, (const char *)bp, length))
+ ND_PRINT(" BEEP ERR");
+ else if (l_strnstart(ndo, "ANS ", 4, (const char *)bp, length))
+ ND_PRINT(" BEEP ANS");
+ else if (l_strnstart(ndo, "NUL ", 4, (const char *)bp, length))
+ ND_PRINT(" BEEP NUL");
+ else if (l_strnstart(ndo, "SEQ ", 4, (const char *)bp, length))
+ ND_PRINT(" BEEP SEQ");
+ else if (l_strnstart(ndo, "END", 4, (const char *)bp, length))
+ ND_PRINT(" BEEP END");
+ else
+ ND_PRINT(" BEEP (payload or undecoded)");
+}
diff --git a/print-bfd.c b/print-bfd.c
new file mode 100644
index 0000000..8c04735
--- /dev/null
+++ b/print-bfd.c
@@ -0,0 +1,426 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* \summary: Bidirectional Forwarding Detection (BFD) printer */
+
+/*
+ * specification: draft-ietf-bfd-base-01 for version 0,
+ * RFC 5880 for version 1, and RFC 5881
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+#include "udp.h"
+
+/*
+ * Control packet, BFDv0, draft-ietf-bfd-base-01
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Vers | Diag |H|D|P|F|C|A|Rsv| Detect Mult | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | My Discriminator |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Your Discriminator |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Desired Min TX Interval |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Required Min RX Interval |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Required Min Echo RX Interval |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/*
+ * Control packet, BFDv1, RFC 5880
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Vers | Diag |Sta|P|F|C|A|D|M| Detect Mult | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | My Discriminator |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Your Discriminator |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Desired Min TX Interval |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Required Min RX Interval |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Required Min Echo RX Interval |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct bfd_header_t {
+ nd_uint8_t version_diag;
+ nd_uint8_t flags;
+ nd_uint8_t detect_time_multiplier;
+ nd_uint8_t length;
+ nd_uint32_t my_discriminator;
+ nd_uint32_t your_discriminator;
+ nd_uint32_t desired_min_tx_interval;
+ nd_uint32_t required_min_rx_interval;
+ nd_uint32_t required_min_echo_interval;
+};
+
+/*
+ * An optional Authentication Header may be present
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Auth Type | Auth Len | Authentication Data... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct bfd_auth_header_t {
+ nd_uint8_t auth_type;
+ nd_uint8_t auth_len;
+ nd_uint8_t auth_data;
+ nd_uint8_t dummy; /* minimum 4 bytes */
+};
+
+enum auth_type {
+ AUTH_PASSWORD = 1,
+ AUTH_MD5 = 2,
+ AUTH_MET_MD5 = 3,
+ AUTH_SHA1 = 4,
+ AUTH_MET_SHA1 = 5
+};
+
+static const struct tok bfd_v1_authentication_values[] = {
+ { AUTH_PASSWORD, "Simple Password" },
+ { AUTH_MD5, "Keyed MD5" },
+ { AUTH_MET_MD5, "Meticulous Keyed MD5" },
+ { AUTH_SHA1, "Keyed SHA1" },
+ { AUTH_MET_SHA1, "Meticulous Keyed SHA1" },
+ { 0, NULL }
+};
+
+enum auth_length {
+ AUTH_PASSWORD_FIELD_MIN_LEN = 4, /* header + password min: 3 + 1 */
+ AUTH_PASSWORD_FIELD_MAX_LEN = 19, /* header + password max: 3 + 16 */
+ AUTH_MD5_FIELD_LEN = 24,
+ AUTH_MD5_HASH_LEN = 16,
+ AUTH_SHA1_FIELD_LEN = 28,
+ AUTH_SHA1_HASH_LEN = 20
+};
+
+#define BFD_EXTRACT_VERSION(x) (((x)&0xe0)>>5)
+#define BFD_EXTRACT_DIAG(x) ((x)&0x1f)
+
+static const struct tok bfd_diag_values[] = {
+ { 0, "No Diagnostic" },
+ { 1, "Control Detection Time Expired" },
+ { 2, "Echo Function Failed" },
+ { 3, "Neighbor Signaled Session Down" },
+ { 4, "Forwarding Plane Reset" },
+ { 5, "Path Down" },
+ { 6, "Concatenated Path Down" },
+ { 7, "Administratively Down" },
+ { 8, "Reverse Concatenated Path Down" },
+ { 0, NULL }
+};
+
+static const struct tok bfd_port_values[] = {
+ { BFD_CONTROL_PORT, "Control" },
+ { BFD_MULTIHOP_PORT, "Multihop" },
+ { BFD_LAG_PORT, "Lag" },
+ { 0, NULL }
+};
+
+#define BFD_FLAG_AUTH 0x04
+
+static const struct tok bfd_v0_flag_values[] = {
+ { 0x80, "I Hear You" },
+ { 0x40, "Demand" },
+ { 0x20, "Poll" },
+ { 0x10, "Final" },
+ { 0x08, "Control Plane Independent" },
+ { BFD_FLAG_AUTH, "Authentication Present" },
+ { 0x02, "Reserved" },
+ { 0x01, "Reserved" },
+ { 0, NULL }
+};
+
+static const struct tok bfd_v1_flag_values[] = {
+ { 0x20, "Poll" },
+ { 0x10, "Final" },
+ { 0x08, "Control Plane Independent" },
+ { BFD_FLAG_AUTH, "Authentication Present" },
+ { 0x02, "Demand" },
+ { 0x01, "Multipoint" },
+ { 0, NULL }
+};
+
+static const struct tok bfd_v1_state_values[] = {
+ { 0, "AdminDown" },
+ { 1, "Down" },
+ { 2, "Init" },
+ { 3, "Up" },
+ { 0, NULL }
+};
+
+static void
+auth_print(netdissect_options *ndo, const u_char *pptr)
+{
+ const struct bfd_auth_header_t *bfd_auth_header;
+ uint8_t auth_type, auth_len;
+ int i;
+
+ pptr += sizeof (struct bfd_header_t);
+ bfd_auth_header = (const struct bfd_auth_header_t *)pptr;
+ ND_TCHECK_SIZE(bfd_auth_header);
+ auth_type = GET_U_1(bfd_auth_header->auth_type);
+ auth_len = GET_U_1(bfd_auth_header->auth_len);
+ ND_PRINT("\n\tAuthentication: %s (%u), length: %u",
+ tok2str(bfd_v1_authentication_values,"Unknown",auth_type),
+ auth_type, auth_len);
+ pptr += 2;
+ ND_PRINT("\n\t Auth Key ID: %u", GET_U_1(pptr));
+
+ switch(auth_type) {
+ case AUTH_PASSWORD:
+/*
+ * Simple Password Authentication Section Format
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Auth Type | Auth Len | Auth Key ID | Password... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ if (auth_len < AUTH_PASSWORD_FIELD_MIN_LEN ||
+ auth_len > AUTH_PASSWORD_FIELD_MAX_LEN) {
+ ND_PRINT("[invalid length %u]",
+ auth_len);
+ break;
+ }
+ pptr++;
+ ND_PRINT(", Password: ");
+ /* the length is equal to the password length plus three */
+ (void)nd_printn(ndo, pptr, auth_len - 3, NULL);
+ break;
+ case AUTH_MD5:
+ case AUTH_MET_MD5:
+/*
+ * Keyed MD5 and Meticulous Keyed MD5 Authentication Section Format
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Auth Type | Auth Len | Auth Key ID | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Auth Key/Digest... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ if (auth_len != AUTH_MD5_FIELD_LEN) {
+ ND_PRINT("[invalid length %u]",
+ auth_len);
+ break;
+ }
+ pptr += 2;
+ ND_PRINT(", Sequence Number: 0x%08x", GET_BE_U_4(pptr));
+ pptr += 4;
+ ND_TCHECK_LEN(pptr, AUTH_MD5_HASH_LEN);
+ ND_PRINT("\n\t Digest: ");
+ for(i = 0; i < AUTH_MD5_HASH_LEN; i++)
+ ND_PRINT("%02x", GET_U_1(pptr + i));
+ break;
+ case AUTH_SHA1:
+ case AUTH_MET_SHA1:
+/*
+ * Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section Format
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Auth Type | Auth Len | Auth Key ID | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Auth Key/Hash... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ if (auth_len != AUTH_SHA1_FIELD_LEN) {
+ ND_PRINT("[invalid length %u]",
+ auth_len);
+ break;
+ }
+ pptr += 2;
+ ND_PRINT(", Sequence Number: 0x%08x", GET_BE_U_4(pptr));
+ pptr += 4;
+ ND_TCHECK_LEN(pptr, AUTH_SHA1_HASH_LEN);
+ ND_PRINT("\n\t Hash: ");
+ for(i = 0; i < AUTH_SHA1_HASH_LEN; i++)
+ ND_PRINT("%02x", GET_U_1(pptr + i));
+ break;
+ }
+}
+
+void
+bfd_print(netdissect_options *ndo, const u_char *pptr,
+ u_int len, u_int port)
+{
+ ndo->ndo_protocol = "bfd";
+ if (port == BFD_CONTROL_PORT ||
+ port == BFD_MULTIHOP_PORT ||
+ port == BFD_LAG_PORT) {
+ /*
+ * Control packet.
+ */
+ const struct bfd_header_t *bfd_header;
+ uint8_t version_diag;
+ uint8_t version = 0;
+ uint8_t flags;
+
+ bfd_header = (const struct bfd_header_t *)pptr;
+ ND_TCHECK_SIZE(bfd_header);
+ version_diag = GET_U_1(bfd_header->version_diag);
+ version = BFD_EXTRACT_VERSION(version_diag);
+ flags = GET_U_1(bfd_header->flags);
+
+ switch (version) {
+
+ /* BFDv0 */
+ case 0:
+ if (ndo->ndo_vflag < 1)
+ {
+ ND_PRINT("BFDv0, Control, Flags: [%s], length: %u",
+ bittok2str(bfd_v0_flag_values, "none", flags),
+ len);
+ return;
+ }
+
+ ND_PRINT("BFDv0, length: %u\n\tControl, Flags: [%s], Diagnostic: %s (0x%02x)",
+ len,
+ bittok2str(bfd_v0_flag_values, "none", flags),
+ tok2str(bfd_diag_values,"unknown",BFD_EXTRACT_DIAG(version_diag)),
+ BFD_EXTRACT_DIAG(version_diag));
+
+ ND_PRINT("\n\tDetection Timer Multiplier: %u (%u ms Detection time), BFD Length: %u",
+ GET_U_1(bfd_header->detect_time_multiplier),
+ GET_U_1(bfd_header->detect_time_multiplier) * GET_BE_U_4(bfd_header->desired_min_tx_interval)/1000,
+ GET_U_1(bfd_header->length));
+
+
+ ND_PRINT("\n\tMy Discriminator: 0x%08x",
+ GET_BE_U_4(bfd_header->my_discriminator));
+ ND_PRINT(", Your Discriminator: 0x%08x",
+ GET_BE_U_4(bfd_header->your_discriminator));
+ ND_PRINT("\n\t Desired min Tx Interval: %4u ms",
+ GET_BE_U_4(bfd_header->desired_min_tx_interval)/1000);
+ ND_PRINT("\n\t Required min Rx Interval: %4u ms",
+ GET_BE_U_4(bfd_header->required_min_rx_interval)/1000);
+ ND_PRINT("\n\t Required min Echo Interval: %4u ms",
+ GET_BE_U_4(bfd_header->required_min_echo_interval)/1000);
+
+ if (flags & BFD_FLAG_AUTH) {
+ auth_print(ndo, pptr);
+ }
+ break;
+
+ /* BFDv1 */
+ case 1:
+ if (ndo->ndo_vflag < 1)
+ {
+ ND_PRINT("BFDv1, %s, State %s, Flags: [%s], length: %u",
+ tok2str(bfd_port_values, "unknown (%u)", port),
+ tok2str(bfd_v1_state_values, "unknown (%u)", (flags & 0xc0) >> 6),
+ bittok2str(bfd_v1_flag_values, "none", flags & 0x3f),
+ len);
+ return;
+ }
+
+ ND_PRINT("BFDv1, length: %u\n\t%s, State %s, Flags: [%s], Diagnostic: %s (0x%02x)",
+ len,
+ tok2str(bfd_port_values, "unknown (%u)", port),
+ tok2str(bfd_v1_state_values, "unknown (%u)", (flags & 0xc0) >> 6),
+ bittok2str(bfd_v1_flag_values, "none", flags & 0x3f),
+ tok2str(bfd_diag_values,"unknown",BFD_EXTRACT_DIAG(version_diag)),
+ BFD_EXTRACT_DIAG(version_diag));
+
+ ND_PRINT("\n\tDetection Timer Multiplier: %u (%u ms Detection time), BFD Length: %u",
+ GET_U_1(bfd_header->detect_time_multiplier),
+ GET_U_1(bfd_header->detect_time_multiplier) * GET_BE_U_4(bfd_header->desired_min_tx_interval)/1000,
+ GET_U_1(bfd_header->length));
+
+
+ ND_PRINT("\n\tMy Discriminator: 0x%08x",
+ GET_BE_U_4(bfd_header->my_discriminator));
+ ND_PRINT(", Your Discriminator: 0x%08x",
+ GET_BE_U_4(bfd_header->your_discriminator));
+ ND_PRINT("\n\t Desired min Tx Interval: %4u ms",
+ GET_BE_U_4(bfd_header->desired_min_tx_interval)/1000);
+ ND_PRINT("\n\t Required min Rx Interval: %4u ms",
+ GET_BE_U_4(bfd_header->required_min_rx_interval)/1000);
+ ND_PRINT("\n\t Required min Echo Interval: %4u ms",
+ GET_BE_U_4(bfd_header->required_min_echo_interval)/1000);
+
+ if (flags & BFD_FLAG_AUTH) {
+ auth_print(ndo, pptr);
+ }
+ break;
+
+ default:
+ ND_PRINT("BFDv%u, Control, length: %u",
+ version,
+ len);
+ if (ndo->ndo_vflag >= 1) {
+ if(!print_unknown_data(ndo, pptr,"\n\t",len))
+ return;
+ }
+ break;
+ }
+ } else if (port == BFD_ECHO_PORT) {
+ /*
+ * Echo packet.
+ */
+ ND_PRINT("BFD, Echo, length: %u",
+ len);
+ if (ndo->ndo_vflag >= 1) {
+ if(!print_unknown_data(ndo, pptr,"\n\t",len))
+ return;
+ }
+ } else {
+ /*
+ * Unknown packet type.
+ */
+ ND_PRINT("BFD, unknown (%u), length: %u",
+ port,
+ len);
+ if (ndo->ndo_vflag >= 1) {
+ if(!print_unknown_data(ndo, pptr,"\n\t",len))
+ return;
+ }
+ }
+}
diff --git a/print-bgp.c b/print-bgp.c
new file mode 100644
index 0000000..5de1003
--- /dev/null
+++ b/print-bgp.c
@@ -0,0 +1,3199 @@
+/*
+ * Copyright (C) 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Extensively modified by Hannes Gredler (hannes@gredler.at) for more
+ * complete BGP support.
+ */
+
+/* \summary: Border Gateway Protocol (BGP) printer */
+
+/* specification: RFC 4271 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+#include "af.h"
+#include "l2vpn.h"
+
+struct bgp {
+ nd_byte bgp_marker[16];
+ nd_uint16_t bgp_len;
+ nd_uint8_t bgp_type;
+};
+#define BGP_SIZE 19 /* unaligned */
+
+#define BGP_OPEN 1
+#define BGP_UPDATE 2
+#define BGP_NOTIFICATION 3
+#define BGP_KEEPALIVE 4
+#define BGP_ROUTE_REFRESH 5
+
+static const struct tok bgp_msg_values[] = {
+ { BGP_OPEN, "Open"},
+ { BGP_UPDATE, "Update"},
+ { BGP_NOTIFICATION, "Notification"},
+ { BGP_KEEPALIVE, "Keepalive"},
+ { BGP_ROUTE_REFRESH, "Route Refresh"},
+ { 0, NULL}
+};
+
+struct bgp_open {
+ nd_byte bgpo_marker[16];
+ nd_uint16_t bgpo_len;
+ nd_uint8_t bgpo_type;
+ nd_uint8_t bgpo_version;
+ nd_uint16_t bgpo_myas;
+ nd_uint16_t bgpo_holdtime;
+ nd_uint32_t bgpo_id;
+ nd_uint8_t bgpo_optlen;
+ /* options should follow */
+};
+#define BGP_OPEN_SIZE 29 /* unaligned */
+
+struct bgp_opt {
+ nd_uint8_t bgpopt_type;
+ nd_uint8_t bgpopt_len;
+ /* variable length */
+};
+#define BGP_OPT_SIZE 2 /* some compilers may pad to 4 bytes */
+#define BGP_CAP_HEADER_SIZE 2 /* some compilers may pad to 4 bytes */
+
+struct bgp_notification {
+ nd_byte bgpn_marker[16];
+ nd_uint16_t bgpn_len;
+ nd_uint8_t bgpn_type;
+ nd_uint8_t bgpn_major;
+ nd_uint8_t bgpn_minor;
+};
+#define BGP_NOTIFICATION_SIZE 21 /* unaligned */
+
+struct bgp_route_refresh {
+ nd_byte bgp_marker[16];
+ nd_uint16_t len;
+ nd_uint8_t type; /* No padding after this; afi is, in fact, not aligned */
+ nd_uint16_t afi;
+ nd_uint8_t res;
+ nd_uint8_t safi;
+};
+#define BGP_ROUTE_REFRESH_SIZE 23
+
+#define bgp_attr_lenlen(flags, p) \
+ (((flags) & 0x10) ? 2U : 1U)
+#define bgp_attr_len(flags, p) \
+ (((flags) & 0x10) ? GET_BE_U_2(p) : GET_U_1(p))
+
+#define BGPTYPE_ORIGIN 1
+#define BGPTYPE_AS_PATH 2
+#define BGPTYPE_NEXT_HOP 3
+#define BGPTYPE_MULTI_EXIT_DISC 4
+#define BGPTYPE_LOCAL_PREF 5
+#define BGPTYPE_ATOMIC_AGGREGATE 6
+#define BGPTYPE_AGGREGATOR 7
+#define BGPTYPE_COMMUNITIES 8 /* RFC1997 */
+#define BGPTYPE_ORIGINATOR_ID 9 /* RFC4456 */
+#define BGPTYPE_CLUSTER_LIST 10 /* RFC4456 */
+#define BGPTYPE_DPA 11 /* deprecated, draft-ietf-idr-bgp-dpa */
+#define BGPTYPE_ADVERTISERS 12 /* deprecated RFC1863 */
+#define BGPTYPE_RCID_PATH 13 /* deprecated RFC1863 */
+#define BGPTYPE_MP_REACH_NLRI 14 /* RFC4760 */
+#define BGPTYPE_MP_UNREACH_NLRI 15 /* RFC4760 */
+#define BGPTYPE_EXTD_COMMUNITIES 16 /* RFC4360 */
+#define BGPTYPE_AS4_PATH 17 /* RFC6793 */
+#define BGPTYPE_AGGREGATOR4 18 /* RFC6793 */
+#define BGPTYPE_PMSI_TUNNEL 22 /* RFC6514 */
+#define BGPTYPE_TUNNEL_ENCAP 23 /* RFC5512 */
+#define BGPTYPE_TRAFFIC_ENG 24 /* RFC5543 */
+#define BGPTYPE_IPV6_EXTD_COMMUNITIES 25 /* RFC5701 */
+#define BGPTYPE_AIGP 26 /* RFC7311 */
+#define BGPTYPE_PE_DISTINGUISHER_LABEL 27 /* RFC6514 */
+#define BGPTYPE_ENTROPY_LABEL 28 /* RFC6790 */
+#define BGPTYPE_LARGE_COMMUNITY 32 /* draft-ietf-idr-large-community-05 */
+#define BGPTYPE_ATTR_SET 128 /* RFC6368 */
+
+#define BGP_MP_NLRI_MINSIZE 3 /* End of RIB Marker detection */
+
+static const struct tok bgp_attr_values[] = {
+ { BGPTYPE_ORIGIN, "Origin"},
+ { BGPTYPE_AS_PATH, "AS Path"},
+ { BGPTYPE_AS4_PATH, "AS4 Path"},
+ { BGPTYPE_NEXT_HOP, "Next Hop"},
+ { BGPTYPE_MULTI_EXIT_DISC, "Multi Exit Discriminator"},
+ { BGPTYPE_LOCAL_PREF, "Local Preference"},
+ { BGPTYPE_ATOMIC_AGGREGATE, "Atomic Aggregate"},
+ { BGPTYPE_AGGREGATOR, "Aggregator"},
+ { BGPTYPE_AGGREGATOR4, "Aggregator4"},
+ { BGPTYPE_COMMUNITIES, "Community"},
+ { BGPTYPE_ORIGINATOR_ID, "Originator ID"},
+ { BGPTYPE_CLUSTER_LIST, "Cluster List"},
+ { BGPTYPE_DPA, "DPA"},
+ { BGPTYPE_ADVERTISERS, "Advertisers"},
+ { BGPTYPE_RCID_PATH, "RCID Path / Cluster ID"},
+ { BGPTYPE_MP_REACH_NLRI, "Multi-Protocol Reach NLRI"},
+ { BGPTYPE_MP_UNREACH_NLRI, "Multi-Protocol Unreach NLRI"},
+ { BGPTYPE_EXTD_COMMUNITIES, "Extended Community"},
+ { BGPTYPE_PMSI_TUNNEL, "PMSI Tunnel"},
+ { BGPTYPE_TUNNEL_ENCAP, "Tunnel Encapsulation"},
+ { BGPTYPE_TRAFFIC_ENG, "Traffic Engineering"},
+ { BGPTYPE_IPV6_EXTD_COMMUNITIES, "IPv6 Extended Community"},
+ { BGPTYPE_AIGP, "Accumulated IGP Metric"},
+ { BGPTYPE_PE_DISTINGUISHER_LABEL, "PE Distinguisher Label"},
+ { BGPTYPE_ENTROPY_LABEL, "Entropy Label"},
+ { BGPTYPE_LARGE_COMMUNITY, "Large Community"},
+ { BGPTYPE_ATTR_SET, "Attribute Set"},
+ { 255, "Reserved for development"},
+ { 0, NULL}
+};
+
+#define BGP_AS_SET 1
+#define BGP_AS_SEQUENCE 2
+#define BGP_CONFED_AS_SEQUENCE 3 /* draft-ietf-idr-rfc3065bis-01 */
+#define BGP_CONFED_AS_SET 4 /* draft-ietf-idr-rfc3065bis-01 */
+
+#define BGP_AS_SEG_TYPE_MIN BGP_AS_SET
+#define BGP_AS_SEG_TYPE_MAX BGP_CONFED_AS_SET
+
+static const struct tok bgp_as_path_segment_open_values[] = {
+ { BGP_AS_SEQUENCE, ""},
+ { BGP_AS_SET, "{ "},
+ { BGP_CONFED_AS_SEQUENCE, "( "},
+ { BGP_CONFED_AS_SET, "({ "},
+ { 0, NULL}
+};
+
+static const struct tok bgp_as_path_segment_close_values[] = {
+ { BGP_AS_SEQUENCE, ""},
+ { BGP_AS_SET, "}"},
+ { BGP_CONFED_AS_SEQUENCE, ")"},
+ { BGP_CONFED_AS_SET, "})"},
+ { 0, NULL}
+};
+
+#define BGP_OPT_AUTH 1
+#define BGP_OPT_CAP 2
+
+static const struct tok bgp_opt_values[] = {
+ { BGP_OPT_AUTH, "Authentication Information"},
+ { BGP_OPT_CAP, "Capabilities Advertisement"},
+ { 0, NULL}
+};
+
+#define BGP_CAPCODE_MP 1 /* RFC2858 */
+#define BGP_CAPCODE_RR 2 /* RFC2918 */
+#define BGP_CAPCODE_ORF 3 /* RFC5291 */
+#define BGP_CAPCODE_MR 4 /* RFC3107 */
+#define BGP_CAPCODE_EXT_NH 5 /* RFC5549 */
+#define BGP_CAPCODE_ML 8 /* RFC8277 */
+#define BGP_CAPCODE_RESTART 64 /* RFC4724 */
+#define BGP_CAPCODE_AS_NEW 65 /* RFC6793 */
+#define BGP_CAPCODE_DYN_CAP 67 /* draft-ietf-idr-dynamic-cap */
+#define BGP_CAPCODE_MULTISESS 68 /* draft-ietf-idr-bgp-multisession */
+#define BGP_CAPCODE_ADD_PATH 69 /* RFC7911 */
+#define BGP_CAPCODE_ENH_RR 70 /* draft-keyur-bgp-enhanced-route-refresh */
+#define BGP_CAPCODE_LLGR 71 /* draft-uttaro-idr-bgp-persistence-05 */
+#define BGP_CAPCODE_RR_CISCO 128
+
+static const struct tok bgp_capcode_values[] = {
+ { BGP_CAPCODE_MP, "Multiprotocol Extensions"},
+ { BGP_CAPCODE_RR, "Route Refresh"},
+ { BGP_CAPCODE_ORF, "Cooperative Route Filtering"},
+ { BGP_CAPCODE_MR, "Multiple Routes to a Destination"},
+ { BGP_CAPCODE_EXT_NH, "Extended Next Hop Encoding"},
+ { BGP_CAPCODE_ML, "Multiple Labels"},
+ { BGP_CAPCODE_RESTART, "Graceful Restart"},
+ { BGP_CAPCODE_AS_NEW, "32-Bit AS Number"},
+ { BGP_CAPCODE_DYN_CAP, "Dynamic Capability"},
+ { BGP_CAPCODE_MULTISESS, "Multisession BGP"},
+ { BGP_CAPCODE_ADD_PATH, "Multiple Paths"},
+ { BGP_CAPCODE_ENH_RR, "Enhanced Route Refresh"},
+ { BGP_CAPCODE_LLGR, "Long-lived Graceful Restart"},
+ { BGP_CAPCODE_RR_CISCO, "Route Refresh (Cisco)"},
+ { 0, NULL}
+};
+
+#define BGP_NOTIFY_MAJOR_MSG 1
+#define BGP_NOTIFY_MAJOR_OPEN 2
+#define BGP_NOTIFY_MAJOR_UPDATE 3
+#define BGP_NOTIFY_MAJOR_HOLDTIME 4
+#define BGP_NOTIFY_MAJOR_FSM 5
+#define BGP_NOTIFY_MAJOR_CEASE 6
+#define BGP_NOTIFY_MAJOR_CAP 7
+
+static const struct tok bgp_notify_major_values[] = {
+ { BGP_NOTIFY_MAJOR_MSG, "Message Header Error"},
+ { BGP_NOTIFY_MAJOR_OPEN, "OPEN Message Error"},
+ { BGP_NOTIFY_MAJOR_UPDATE, "UPDATE Message Error"},
+ { BGP_NOTIFY_MAJOR_HOLDTIME,"Hold Timer Expired"},
+ { BGP_NOTIFY_MAJOR_FSM, "Finite State Machine Error"},
+ { BGP_NOTIFY_MAJOR_CEASE, "Cease"},
+ { BGP_NOTIFY_MAJOR_CAP, "Capability Message Error"},
+ { 0, NULL}
+};
+
+/* draft-ietf-idr-cease-subcode-02 */
+#define BGP_NOTIFY_MINOR_CEASE_MAXPRFX 1
+/* draft-ietf-idr-shutdown-07 */
+#define BGP_NOTIFY_MINOR_CEASE_SHUT 2
+#define BGP_NOTIFY_MINOR_CEASE_RESET 4
+#define BGP_NOTIFY_MINOR_CEASE_ADMIN_SHUTDOWN_LEN 128
+static const struct tok bgp_notify_minor_cease_values[] = {
+ { BGP_NOTIFY_MINOR_CEASE_MAXPRFX, "Maximum Number of Prefixes Reached"},
+ { BGP_NOTIFY_MINOR_CEASE_SHUT, "Administrative Shutdown"},
+ { 3, "Peer Unconfigured"},
+ { BGP_NOTIFY_MINOR_CEASE_RESET, "Administrative Reset"},
+ { 5, "Connection Rejected"},
+ { 6, "Other Configuration Change"},
+ { 7, "Connection Collision Resolution"},
+ { 0, NULL}
+};
+
+static const struct tok bgp_notify_minor_msg_values[] = {
+ { 1, "Connection Not Synchronized"},
+ { 2, "Bad Message Length"},
+ { 3, "Bad Message Type"},
+ { 0, NULL}
+};
+
+static const struct tok bgp_notify_minor_open_values[] = {
+ { 1, "Unsupported Version Number"},
+ { 2, "Bad Peer AS"},
+ { 3, "Bad BGP Identifier"},
+ { 4, "Unsupported Optional Parameter"},
+ { 5, "Authentication Failure"},
+ { 6, "Unacceptable Hold Time"},
+ { 7, "Capability Message Error"},
+ { 0, NULL}
+};
+
+static const struct tok bgp_notify_minor_update_values[] = {
+ { 1, "Malformed Attribute List"},
+ { 2, "Unrecognized Well-known Attribute"},
+ { 3, "Missing Well-known Attribute"},
+ { 4, "Attribute Flags Error"},
+ { 5, "Attribute Length Error"},
+ { 6, "Invalid ORIGIN Attribute"},
+ { 7, "AS Routing Loop"},
+ { 8, "Invalid NEXT_HOP Attribute"},
+ { 9, "Optional Attribute Error"},
+ { 10, "Invalid Network Field"},
+ { 11, "Malformed AS_PATH"},
+ { 0, NULL}
+};
+
+static const struct tok bgp_notify_minor_fsm_values[] = {
+ { 0, "Unspecified Error"},
+ { 1, "In OpenSent State"},
+ { 2, "In OpenConfirm State"},
+ { 3, "In Established State"},
+ { 0, NULL }
+};
+
+static const struct tok bgp_notify_minor_cap_values[] = {
+ { 1, "Invalid Action Value" },
+ { 2, "Invalid Capability Length" },
+ { 3, "Malformed Capability Value" },
+ { 4, "Unsupported Capability Code" },
+ { 0, NULL }
+};
+
+static const struct tok bgp_origin_values[] = {
+ { 0, "IGP"},
+ { 1, "EGP"},
+ { 2, "Incomplete"},
+ { 0, NULL}
+};
+
+#define BGP_PMSI_TUNNEL_RSVP_P2MP 1
+#define BGP_PMSI_TUNNEL_LDP_P2MP 2
+#define BGP_PMSI_TUNNEL_PIM_SSM 3
+#define BGP_PMSI_TUNNEL_PIM_SM 4
+#define BGP_PMSI_TUNNEL_PIM_BIDIR 5
+#define BGP_PMSI_TUNNEL_INGRESS 6
+#define BGP_PMSI_TUNNEL_LDP_MP2MP 7
+
+static const struct tok bgp_pmsi_tunnel_values[] = {
+ { BGP_PMSI_TUNNEL_RSVP_P2MP, "RSVP-TE P2MP LSP"},
+ { BGP_PMSI_TUNNEL_LDP_P2MP, "LDP P2MP LSP"},
+ { BGP_PMSI_TUNNEL_PIM_SSM, "PIM-SSM Tree"},
+ { BGP_PMSI_TUNNEL_PIM_SM, "PIM-SM Tree"},
+ { BGP_PMSI_TUNNEL_PIM_BIDIR, "PIM-Bidir Tree"},
+ { BGP_PMSI_TUNNEL_INGRESS, "Ingress Replication"},
+ { BGP_PMSI_TUNNEL_LDP_MP2MP, "LDP MP2MP LSP"},
+ { 0, NULL}
+};
+
+static const struct tok bgp_pmsi_flag_values[] = {
+ { 0x01, "Leaf Information required"},
+ { 0, NULL}
+};
+
+#define BGP_AIGP_TLV 1
+
+static const struct tok bgp_aigp_values[] = {
+ { BGP_AIGP_TLV, "AIGP"},
+ { 0, NULL}
+};
+
+/* Subsequent address family identifier, RFC2283 section 7 */
+#define SAFNUM_RES 0
+#define SAFNUM_UNICAST 1
+#define SAFNUM_MULTICAST 2
+#define SAFNUM_UNIMULTICAST 3 /* deprecated now */
+/* labeled BGP RFC3107 */
+#define SAFNUM_LABUNICAST 4
+/* RFC6514 */
+#define SAFNUM_MULTICAST_VPN 5
+/* draft-nalawade-kapoor-tunnel-safi */
+#define SAFNUM_TUNNEL 64
+/* RFC4761 */
+#define SAFNUM_VPLS 65
+/* RFC6037 */
+#define SAFNUM_MDT 66
+/* RFC7432 */
+#define SAFNUM_EVPN 70
+/* RFC4364 */
+#define SAFNUM_VPNUNICAST 128
+/* RFC6513 */
+#define SAFNUM_VPNMULTICAST 129
+#define SAFNUM_VPNUNIMULTICAST 130 /* deprecated now */
+/* RFC4684 */
+#define SAFNUM_RT_ROUTING_INFO 132
+
+#define BGP_VPN_RD_LEN 8
+
+static const struct tok bgp_safi_values[] = {
+ { SAFNUM_RES, "Reserved"},
+ { SAFNUM_UNICAST, "Unicast"},
+ { SAFNUM_MULTICAST, "Multicast"},
+ { SAFNUM_UNIMULTICAST, "Unicast+Multicast"},
+ { SAFNUM_LABUNICAST, "labeled Unicast"},
+ { SAFNUM_TUNNEL, "Tunnel"},
+ { SAFNUM_VPLS, "VPLS"},
+ { SAFNUM_MDT, "MDT"},
+ { SAFNUM_EVPN, "EVPN"},
+ { SAFNUM_VPNUNICAST, "labeled VPN Unicast"},
+ { SAFNUM_VPNMULTICAST, "labeled VPN Multicast"},
+ { SAFNUM_VPNUNIMULTICAST, "labeled VPN Unicast+Multicast"},
+ { SAFNUM_RT_ROUTING_INFO, "Route Target Routing Information"},
+ { SAFNUM_MULTICAST_VPN, "Multicast VPN"},
+ { 0, NULL }
+};
+
+/* well-known community */
+#define BGP_COMMUNITY_NO_EXPORT 0xffffff01
+#define BGP_COMMUNITY_NO_ADVERT 0xffffff02
+#define BGP_COMMUNITY_NO_EXPORT_SUBCONFED 0xffffff03
+
+/* Extended community type - RFC 4360 */
+#define BGP_EXT_COM_RT_0 0x0002 /* Route Target,Format AS(2bytes):AN(4bytes) */
+#define BGP_EXT_COM_RT_1 0x0102 /* Route Target,Format IP address:AN(2bytes) */
+#define BGP_EXT_COM_RT_2 0x0202 /* Route Target,Format AN(4bytes):local(2bytes) */
+#define BGP_EXT_COM_RO_0 0x0003 /* Route Origin,Format AS(2bytes):AN(4bytes) */
+#define BGP_EXT_COM_RO_1 0x0103 /* Route Origin,Format IP address:AN(2bytes) */
+#define BGP_EXT_COM_RO_2 0x0203 /* Route Origin,Format AN(4bytes):local(2bytes) */
+#define BGP_EXT_COM_LINKBAND 0x4004 /* Link Bandwidth,Format AS(2B):Bandwidth(4B) */
+ /* rfc2547 bgp-mpls-vpns */
+#define BGP_EXT_COM_VPN_ORIGIN 0x0005 /* OSPF Domain ID / VPN of Origin - draft-rosen-vpns-ospf-bgp-mpls */
+#define BGP_EXT_COM_VPN_ORIGIN2 0x0105 /* duplicate - keep for backwards compatibility */
+#define BGP_EXT_COM_VPN_ORIGIN3 0x0205 /* duplicate - keep for backwards compatibility */
+#define BGP_EXT_COM_VPN_ORIGIN4 0x8005 /* duplicate - keep for backwards compatibility */
+
+#define BGP_EXT_COM_OSPF_RTYPE 0x0306 /* OSPF Route Type,Format Area(4B):RouteType(1B):Options(1B) */
+#define BGP_EXT_COM_OSPF_RTYPE2 0x8000 /* duplicate - keep for backwards compatibility */
+#define BGP_EXT_COM_ENCAP 0x030c /* rfc5512 */
+
+#define BGP_EXT_COM_OSPF_RID 0x0107 /* OSPF Router ID,Format RouterID(4B):Unused(2B) */
+#define BGP_EXT_COM_OSPF_RID2 0x8001 /* duplicate - keep for backwards compatibility */
+
+#define BGP_EXT_COM_L2INFO 0x800a /* draft-kompella-ppvpn-l2vpn */
+
+#define BGP_EXT_COM_SOURCE_AS 0x0009 /* RFC-ietf-l3vpn-2547bis-mcast-bgp-08.txt */
+#define BGP_EXT_COM_VRF_RT_IMP 0x010b /* RFC-ietf-l3vpn-2547bis-mcast-bgp-08.txt */
+#define BGP_EXT_COM_L2VPN_RT_0 0x000a /* L2VPN Identifier,Format AS(2bytes):AN(4bytes) */
+#define BGP_EXT_COM_L2VPN_RT_1 0xF10a /* L2VPN Identifier,Format IP address:AN(2bytes) */
+
+/* https://www.cisco.com/en/US/tech/tk436/tk428/technologies_tech_note09186a00801eb09a.shtml */
+#define BGP_EXT_COM_EIGRP_GEN 0x8800
+#define BGP_EXT_COM_EIGRP_METRIC_AS_DELAY 0x8801
+#define BGP_EXT_COM_EIGRP_METRIC_REL_NH_BW 0x8802
+#define BGP_EXT_COM_EIGRP_METRIC_LOAD_MTU 0x8803
+#define BGP_EXT_COM_EIGRP_EXT_REMAS_REMID 0x8804
+#define BGP_EXT_COM_EIGRP_EXT_REMPROTO_REMMETRIC 0x8805
+
+static const struct tok bgp_extd_comm_flag_values[] = {
+ { 0x8000, "vendor-specific"},
+ { 0x4000, "non-transitive"},
+ { 0, NULL},
+};
+
+static const struct tok bgp_extd_comm_subtype_values[] = {
+ { BGP_EXT_COM_RT_0, "target"},
+ { BGP_EXT_COM_RT_1, "target"},
+ { BGP_EXT_COM_RT_2, "target"},
+ { BGP_EXT_COM_RO_0, "origin"},
+ { BGP_EXT_COM_RO_1, "origin"},
+ { BGP_EXT_COM_RO_2, "origin"},
+ { BGP_EXT_COM_LINKBAND, "link-BW"},
+ { BGP_EXT_COM_VPN_ORIGIN, "ospf-domain"},
+ { BGP_EXT_COM_VPN_ORIGIN2, "ospf-domain"},
+ { BGP_EXT_COM_VPN_ORIGIN3, "ospf-domain"},
+ { BGP_EXT_COM_VPN_ORIGIN4, "ospf-domain"},
+ { BGP_EXT_COM_OSPF_RTYPE, "ospf-route-type"},
+ { BGP_EXT_COM_OSPF_RTYPE2, "ospf-route-type"},
+ { BGP_EXT_COM_ENCAP, "encapsulation"},
+ { BGP_EXT_COM_OSPF_RID, "ospf-router-id"},
+ { BGP_EXT_COM_OSPF_RID2, "ospf-router-id"},
+ { BGP_EXT_COM_L2INFO, "layer2-info"},
+ { BGP_EXT_COM_EIGRP_GEN, "eigrp-general-route (flag, tag)" },
+ { BGP_EXT_COM_EIGRP_METRIC_AS_DELAY, "eigrp-route-metric (AS, delay)" },
+ { BGP_EXT_COM_EIGRP_METRIC_REL_NH_BW, "eigrp-route-metric (reliability, nexthop, bandwidth)" },
+ { BGP_EXT_COM_EIGRP_METRIC_LOAD_MTU, "eigrp-route-metric (load, MTU)" },
+ { BGP_EXT_COM_EIGRP_EXT_REMAS_REMID, "eigrp-external-route (remote-AS, remote-ID)" },
+ { BGP_EXT_COM_EIGRP_EXT_REMPROTO_REMMETRIC, "eigrp-external-route (remote-proto, remote-metric)" },
+ { BGP_EXT_COM_SOURCE_AS, "source-AS" },
+ { BGP_EXT_COM_VRF_RT_IMP, "vrf-route-import"},
+ { BGP_EXT_COM_L2VPN_RT_0, "l2vpn-id"},
+ { BGP_EXT_COM_L2VPN_RT_1, "l2vpn-id"},
+ { 0, NULL},
+};
+
+/* RFC RFC5512 BGP Tunnel Encapsulation Attribute Tunnel Types */
+#define BGP_ENCAP_TUNNEL_L2TPV3_IP 1
+#define BGP_ENCAP_TUNNEL_GRE 2
+#define BGP_ENCAP_TUNNEL_TRANSMIT 3
+#define BGP_ENCAP_TUNNEL_IPSEC 4
+#define BGP_ENCAP_TUNNEL_IP_IPSEC 5
+#define BGP_ENCAP_TUNNEL_MPLS_IP 6
+#define BGP_ENCAP_TUNNEL_IP_IP 7
+#define BGP_ENCAP_TUNNEL_VXLAN 8
+#define BGP_ENCAP_TUNNEL_NVGRE 9
+#define BGP_ENCAP_TUNNEL_MPLS 10
+#define BGP_ENCAP_TUNNEL_MPLS_GRE 11
+#define BGP_ENCAP_TUNNEL_VXLAN_GPE 12
+#define BGP_ENCAP_TUNNEL_MPLS_UDP 13
+#define BGP_ENCAP_TUNNEL_IPV6 14
+#define BGP_ENCAP_TUNNEL_SR_TE 15
+#define BGP_ENCAP_TUNNEL_BARE 16
+#define BGP_ENCAP_TUNNEL_SR 17
+
+static const struct tok bgp_extd_comm_encap_tunnel_values[] = {
+ { BGP_ENCAP_TUNNEL_L2TPV3_IP, "L2TPv3 over IP"},
+ { BGP_ENCAP_TUNNEL_GRE, "GRE"},
+ { BGP_ENCAP_TUNNEL_TRANSMIT, "Transmit Tunnel"},
+ { BGP_ENCAP_TUNNEL_IPSEC, "IPsec"},
+ { BGP_ENCAP_TUNNEL_IP_IPSEC, "IP in IP with IPsec"},
+ { BGP_ENCAP_TUNNEL_MPLS_IP, "MPLS in IP with IPsec"},
+ { BGP_ENCAP_TUNNEL_IP_IP, "IP in IP"},
+ { BGP_ENCAP_TUNNEL_VXLAN, "VXLAN"},
+ { BGP_ENCAP_TUNNEL_NVGRE, "NVGRE"},
+ { BGP_ENCAP_TUNNEL_MPLS, "MPLS"},
+ { BGP_ENCAP_TUNNEL_MPLS_GRE, "MPLS in GRE"},
+ { BGP_ENCAP_TUNNEL_VXLAN_GPE, "VXLAN GPE"},
+ { BGP_ENCAP_TUNNEL_MPLS_UDP, "MPLS in UDP"},
+ { BGP_ENCAP_TUNNEL_IPV6, "IPv6"},
+ { BGP_ENCAP_TUNNEL_SR_TE, "SR TE"},
+ { BGP_ENCAP_TUNNEL_BARE, "Bare"},
+ { BGP_ENCAP_TUNNEL_SR, "SR"},
+ { 0, NULL},
+};
+
+/* OSPF codes for BGP_EXT_COM_OSPF_RTYPE draft-rosen-vpns-ospf-bgp-mpls */
+#define BGP_OSPF_RTYPE_RTR 1 /* OSPF Router LSA */
+#define BGP_OSPF_RTYPE_NET 2 /* OSPF Network LSA */
+#define BGP_OSPF_RTYPE_SUM 3 /* OSPF Summary LSA */
+#define BGP_OSPF_RTYPE_EXT 5 /* OSPF External LSA, note that ASBR doesn't apply to MPLS-VPN */
+#define BGP_OSPF_RTYPE_NSSA 7 /* OSPF NSSA External*/
+#define BGP_OSPF_RTYPE_SHAM 129 /* OSPF-MPLS-VPN Sham link */
+#define BGP_OSPF_RTYPE_METRIC_TYPE 0x1 /* LSB of RTYPE Options Field */
+
+static const struct tok bgp_extd_comm_ospf_rtype_values[] = {
+ { BGP_OSPF_RTYPE_RTR, "Router" },
+ { BGP_OSPF_RTYPE_NET, "Network" },
+ { BGP_OSPF_RTYPE_SUM, "Summary" },
+ { BGP_OSPF_RTYPE_EXT, "External" },
+ { BGP_OSPF_RTYPE_NSSA,"NSSA External" },
+ { BGP_OSPF_RTYPE_SHAM,"MPLS-VPN Sham" },
+ { 0, NULL },
+};
+
+/* ADD-PATH Send/Receive field values */
+static const struct tok bgp_add_path_recvsend[] = {
+ { 1, "Receive" },
+ { 2, "Send" },
+ { 3, "Both" },
+ { 0, NULL },
+};
+
+/* allocate space for the largest possible string */
+static char astostr[sizeof("xxxxx.xxxxx")];
+
+/*
+ * as_printf
+ *
+ * Convert an AS number into a string and return string pointer.
+ *
+ * Depending on bflag is set or not, AS number is converted into ASDOT notation
+ * or plain number notation.
+ *
+ */
+static char *
+as_printf(netdissect_options *ndo,
+ char *str, size_t size, u_int asnum)
+{
+ if (!ndo->ndo_bflag || asnum <= 0xFFFF) {
+ snprintf(str, size, "%u", asnum);
+ } else {
+ snprintf(str, size, "%u.%u", asnum >> 16, asnum & 0xFFFF);
+ }
+ return str;
+}
+
+#define ITEMCHECK(minlen) if (itemlen < minlen) goto badtlv;
+
+int
+decode_prefix4(netdissect_options *ndo,
+ const u_char *pptr, u_int itemlen, char *buf, size_t buflen)
+{
+ nd_ipv4 addr;
+ u_int plen, plenbytes;
+
+ ITEMCHECK(1);
+ plen = GET_U_1(pptr);
+ if (32 < plen)
+ return -1;
+ itemlen -= 1;
+
+ memset(&addr, 0, sizeof(addr));
+ plenbytes = (plen + 7) / 8;
+ ND_TCHECK_LEN(pptr + 1, plenbytes);
+ ITEMCHECK(plenbytes);
+ memcpy(&addr, pptr + 1, plenbytes);
+ if (plen % 8) {
+ ((u_char *)&addr)[plenbytes - 1] &= ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ snprintf(buf, buflen, "%s/%u", ipaddr_string(ndo, (const u_char *)&addr), plen);
+ return 1 + plenbytes;
+
+trunc:
+ return -2;
+
+badtlv:
+ return -3;
+}
+
+static int
+decode_labeled_prefix4(netdissect_options *ndo,
+ const u_char *pptr, u_int itemlen, char *buf,
+ size_t buflen)
+{
+ nd_ipv4 addr;
+ u_int plen, plenbytes;
+
+ /* prefix length and label = 4 bytes */
+ ND_TCHECK_4(pptr);
+ ITEMCHECK(4);
+ plen = GET_U_1(pptr); /* get prefix length */
+
+ /* this is one of the weirdnesses of rfc3107
+ the label length (actually the label + COS bits)
+ is added to the prefix length;
+ we also do only read out just one label -
+ there is no real application for advertisement of
+ stacked labels in a single BGP message
+ */
+
+ if (24 > plen)
+ return -1;
+
+ plen-=24; /* adjust prefixlen - labellength */
+
+ if (32 < plen)
+ return -1;
+ itemlen -= 4;
+
+ memset(&addr, 0, sizeof(addr));
+ plenbytes = (plen + 7) / 8;
+ ND_TCHECK_LEN(pptr + 4, plenbytes);
+ ITEMCHECK(plenbytes);
+ memcpy(&addr, pptr + 4, plenbytes);
+ if (plen % 8) {
+ ((u_char *)&addr)[plenbytes - 1] &= ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ /* the label may get offsetted by 4 bits so lets shift it right */
+ snprintf(buf, buflen, "%s/%u, label:%u %s",
+ ipaddr_string(ndo, (const u_char *)&addr),
+ plen,
+ GET_BE_U_3(pptr + 1)>>4,
+ ((GET_U_1(pptr + 3) & 1) == 0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );
+
+ return 4 + plenbytes;
+
+trunc:
+ return -2;
+
+badtlv:
+ return -3;
+}
+
+/*
+ * bgp_vpn_ip_print
+ *
+ * print an ipv4 or ipv6 address into a buffer dependent on address length.
+ */
+static char *
+bgp_vpn_ip_print(netdissect_options *ndo,
+ const u_char *pptr, u_int addr_length)
+{
+
+ /* worst case string is s fully formatted v6 address */
+ static char addr[sizeof("1234:5678:89ab:cdef:1234:5678:89ab:cdef")];
+ char *pos = addr;
+
+ switch(addr_length) {
+ case (sizeof(nd_ipv4) << 3): /* 32 */
+ snprintf(pos, sizeof(addr), "%s", GET_IPADDR_STRING(pptr));
+ break;
+ case (sizeof(nd_ipv6) << 3): /* 128 */
+ snprintf(pos, sizeof(addr), "%s", GET_IP6ADDR_STRING(pptr));
+ break;
+ default:
+ snprintf(pos, sizeof(addr), "bogus address length %u", addr_length);
+ break;
+ }
+ pos += strlen(pos);
+
+ *(pos) = '\0';
+ return (addr);
+}
+
+/*
+ * bgp_vpn_sg_print
+ *
+ * print an multicast s,g entry into a buffer.
+ * the s,g entry is encoded like this.
+ *
+ * +-----------------------------------+
+ * | Multicast Source Length (1 octet) |
+ * +-----------------------------------+
+ * | Multicast Source (Variable) |
+ * +-----------------------------------+
+ * | Multicast Group Length (1 octet) |
+ * +-----------------------------------+
+ * | Multicast Group (Variable) |
+ * +-----------------------------------+
+ *
+ * return the number of bytes read from the wire.
+ */
+static u_int
+bgp_vpn_sg_print(netdissect_options *ndo,
+ const u_char *pptr, char *buf, size_t buflen)
+{
+ uint8_t addr_length;
+ u_int total_length, offset;
+
+ total_length = 0;
+
+ /* Source address length, encoded in bits */
+ addr_length = GET_U_1(pptr);
+ pptr++;
+
+ /* Source address */
+ ND_TCHECK_LEN(pptr, (addr_length >> 3));
+ total_length += (addr_length >> 3) + 1;
+ offset = (u_int)strlen(buf);
+ if (addr_length) {
+ snprintf(buf + offset, buflen - offset, ", Source %s",
+ bgp_vpn_ip_print(ndo, pptr, addr_length));
+ pptr += (addr_length >> 3);
+ }
+
+ /* Group address length, encoded in bits */
+ addr_length = GET_U_1(pptr);
+ pptr++;
+
+ /* Group address */
+ ND_TCHECK_LEN(pptr, (addr_length >> 3));
+ total_length += (addr_length >> 3) + 1;
+ offset = (u_int)strlen(buf);
+ if (addr_length) {
+ snprintf(buf + offset, buflen - offset, ", Group %s",
+ bgp_vpn_ip_print(ndo, pptr, addr_length));
+ pptr += (addr_length >> 3);
+ }
+
+trunc:
+ return (total_length);
+}
+
+/* Print an RFC 4364 Route Distinguisher */
+const char *
+bgp_vpn_rd_print(netdissect_options *ndo,
+ const u_char *pptr)
+{
+ /* allocate space for the largest possible string */
+ static char rd[sizeof("xxxxx.xxxxx:xxxxx (xxx.xxx.xxx.xxx:xxxxx)")];
+ char *pos = rd;
+
+ /* ok lets load the RD format */
+ switch (GET_BE_U_2(pptr)) {
+
+ case 0:
+ /* 2-byte-AS:number fmt */
+ snprintf(pos, sizeof(rd) - (pos - rd), "%u:%u (= %u.%u.%u.%u)",
+ GET_BE_U_2(pptr + 2),
+ GET_BE_U_4(pptr + 4),
+ GET_U_1(pptr + 4), GET_U_1(pptr + 5),
+ GET_U_1(pptr + 6), GET_U_1(pptr + 7));
+ break;
+
+ case 1:
+ /* IP-address:AS fmt */
+ snprintf(pos, sizeof(rd) - (pos - rd), "%u.%u.%u.%u:%u",
+ GET_U_1(pptr + 2), GET_U_1(pptr + 3),
+ GET_U_1(pptr + 4), GET_U_1(pptr + 5),
+ GET_BE_U_2(pptr + 6));
+ break;
+
+ case 2:
+ /* 4-byte-AS:number fmt */
+ snprintf(pos, sizeof(rd) - (pos - rd), "%s:%u (%u.%u.%u.%u:%u)",
+ as_printf(ndo, astostr, sizeof(astostr), GET_BE_U_4(pptr + 2)),
+ GET_BE_U_2(pptr + 6), GET_U_1(pptr + 2),
+ GET_U_1(pptr + 3), GET_U_1(pptr + 4),
+ GET_U_1(pptr + 5), GET_BE_U_2(pptr + 6));
+ break;
+ default:
+ snprintf(pos, sizeof(rd) - (pos - rd), "unknown RD format");
+ break;
+ }
+ pos += strlen(pos);
+ *(pos) = '\0';
+ return (rd);
+}
+
+/*
+ * Print an RFC 4360 Extended Community.
+ */
+static void
+bgp_extended_community_print(netdissect_options *ndo,
+ const u_char *pptr)
+{
+ union { /* copy buffer for bandwidth values */
+ float f;
+ uint32_t i;
+ } bw;
+
+ switch (GET_BE_U_2(pptr)) {
+
+ case BGP_EXT_COM_RT_0:
+ case BGP_EXT_COM_RO_0:
+ case BGP_EXT_COM_L2VPN_RT_0:
+ ND_PRINT("%u:%u (= %s)",
+ GET_BE_U_2(pptr + 2),
+ GET_BE_U_4(pptr + 4),
+ GET_IPADDR_STRING(pptr+4));
+ break;
+
+ case BGP_EXT_COM_RT_1:
+ case BGP_EXT_COM_RO_1:
+ case BGP_EXT_COM_L2VPN_RT_1:
+ case BGP_EXT_COM_VRF_RT_IMP:
+ ND_PRINT("%s:%u",
+ GET_IPADDR_STRING(pptr+2),
+ GET_BE_U_2(pptr + 6));
+ break;
+
+ case BGP_EXT_COM_RT_2:
+ case BGP_EXT_COM_RO_2:
+ ND_PRINT("%s:%u",
+ as_printf(ndo, astostr, sizeof(astostr),
+ GET_BE_U_4(pptr + 2)), GET_BE_U_2(pptr + 6));
+ break;
+
+ case BGP_EXT_COM_LINKBAND:
+ bw.i = GET_BE_U_4(pptr + 2);
+ ND_PRINT("bandwidth: %.3f Mbps",
+ bw.f*8/1000000);
+ break;
+
+ case BGP_EXT_COM_VPN_ORIGIN:
+ case BGP_EXT_COM_VPN_ORIGIN2:
+ case BGP_EXT_COM_VPN_ORIGIN3:
+ case BGP_EXT_COM_VPN_ORIGIN4:
+ case BGP_EXT_COM_OSPF_RID:
+ case BGP_EXT_COM_OSPF_RID2:
+ ND_PRINT("%s", GET_IPADDR_STRING(pptr+2));
+ break;
+
+ case BGP_EXT_COM_OSPF_RTYPE:
+ case BGP_EXT_COM_OSPF_RTYPE2:
+ ND_PRINT("area:%s, router-type:%s, metric-type:%s%s",
+ GET_IPADDR_STRING(pptr+2),
+ tok2str(bgp_extd_comm_ospf_rtype_values,
+ "unknown (0x%02x)",
+ GET_U_1((pptr + 6))),
+ (GET_U_1(pptr + 7) & BGP_OSPF_RTYPE_METRIC_TYPE) ? "E2" : "",
+ ((GET_U_1(pptr + 6) == BGP_OSPF_RTYPE_EXT) || (GET_U_1(pptr + 6) == BGP_OSPF_RTYPE_NSSA)) ? "E1" : "");
+ break;
+
+ case BGP_EXT_COM_L2INFO:
+ ND_PRINT("%s Control Flags [0x%02x]:MTU %u",
+ tok2str(l2vpn_encaps_values,
+ "unknown encaps",
+ GET_U_1((pptr + 2))),
+ GET_U_1((pptr + 3)),
+ GET_BE_U_2(pptr + 4));
+ break;
+
+ case BGP_EXT_COM_SOURCE_AS:
+ ND_PRINT("AS %u", GET_BE_U_2(pptr + 2));
+ break;
+
+ case BGP_EXT_COM_ENCAP:
+ ND_PRINT("Tunnel type: %s", tok2str(bgp_extd_comm_encap_tunnel_values,
+ "unknown encaps",
+ GET_BE_U_2(pptr + 6)));
+ break;
+
+ default:
+ ND_PRINT("%02x%02x%02x%02x%02x%02x",
+ GET_U_1(pptr + 2),
+ GET_U_1(pptr + 3),
+ GET_U_1(pptr + 4),
+ GET_U_1(pptr + 5),
+ GET_U_1(pptr + 6),
+ GET_U_1(pptr + 7));
+ break;
+ }
+}
+
+/*
+ * RFC4684 (Section 4)/RFC2858 (Section 4).
+ * RTC membership prefix is structured as follows
+ * [prefix-len] [origin-as] [route-target]
+ * The route-target is encoded as RT ext-comms.
+ * Prefix-len may be 0, 32..96
+ *
+ * Note that pptr is not packet data - it is
+ * a buffer owned by our caller - therefore GET_*
+ * macros can not be used.
+ */
+static char *
+bgp_rt_prefix_print(netdissect_options *ndo,
+ const u_char *pptr,
+ u_int plen)
+{
+ /* allocate space for the largest possible string */
+ char rtc_prefix_in_hex[20] = "";
+ u_int rtc_prefix_in_hex_len = 0;
+ static char output[61]; /* max response string */
+ uint16_t ec_type = 0;
+ u_int octet_count;
+ u_int i;
+
+ if (plen == 0) {
+ snprintf(output, sizeof(output), "route-target: 0:0/0");
+ return (output);
+ }
+
+ /* hex representation of the prefix */
+ octet_count = (plen+7)/8;
+ for (i=0; i<octet_count; i++) {
+ rtc_prefix_in_hex_len += snprintf(rtc_prefix_in_hex+rtc_prefix_in_hex_len,
+ sizeof(rtc_prefix_in_hex)-rtc_prefix_in_hex_len,
+ "%02x%s", *(pptr+i),
+ ((i%2 == 1) && (i<octet_count-1)) ? " " : "");
+ }
+
+ if (plen < 16) {
+ /*
+ * The prefix is too short to include the full ext-comm type,
+ * so we have no way to parse it further.
+ */
+ snprintf(output, sizeof(output), "route-target: partial-type: (%s/%d)",
+ rtc_prefix_in_hex, plen);
+ return (output);
+ }
+
+ /*
+ * get the ext-comm type
+ * Note: pptr references a static 8 octet buffer with unused bits set to 0,
+ * hense EXTRACT_*() macros are safe.
+ */
+ ec_type = EXTRACT_BE_U_2(pptr);
+ switch (ec_type) {
+ case BGP_EXT_COM_RT_0:
+ /* 2-byte-AS:number fmt */
+ snprintf(output, sizeof(output), "route-target: %u:%u/%d (%s)",
+ EXTRACT_BE_U_2(pptr+2),
+ EXTRACT_BE_U_4(pptr+4),
+ plen, rtc_prefix_in_hex);
+ break;
+
+ case BGP_EXT_COM_RT_1:
+ /* IP-address:AS fmt */
+ snprintf(output, sizeof(output), "route-target: %u.%u.%u.%u:%u/%d (%s)",
+ *(pptr+2), *(pptr+3), *(pptr+4), *(pptr+5),
+ EXTRACT_BE_U_2(pptr+6), plen, rtc_prefix_in_hex);
+ break;
+
+ case BGP_EXT_COM_RT_2:
+ /* 4-byte-AS:number fmt */
+ snprintf(output, sizeof(output), "route-target: %s:%u/%d (%s)",
+ as_printf(ndo, astostr, sizeof(astostr), EXTRACT_BE_U_4(pptr+2)),
+ EXTRACT_BE_U_2(pptr+6), plen, rtc_prefix_in_hex);
+ break;
+
+ default:
+ snprintf(output, sizeof(output), "route target: unknown-type(%04x) (%s/%d)",
+ ec_type,
+ rtc_prefix_in_hex, plen);
+ break;
+ }
+ return (output);
+}
+
+/* RFC 4684 */
+static int
+decode_rt_routing_info(netdissect_options *ndo,
+ const u_char *pptr)
+{
+ uint8_t route_target[8];
+ u_int plen;
+ char asbuf[sizeof(astostr)]; /* bgp_vpn_rd_print() overwrites astostr */
+ u_int num_octets;
+
+ /* NLRI "prefix length" from RFC 2858 Section 4. */
+ plen = GET_U_1(pptr); /* get prefix length */
+
+ /* NLRI "prefix" (ibid), valid lengths are { 0, 32, 33, ..., 96 } bits.
+ * RFC 4684 Section 4 defines the layout of "origin AS" and "route
+ * target" fields inside the "prefix" depending on its length.
+ */
+ if (0 == plen) {
+ /* Without "origin AS", without "route target". */
+ ND_PRINT("\n\t default route target");
+ return 1;
+ }
+
+ if (32 > plen) {
+ ND_PRINT("\n\t (illegal prefix length)");
+ return -1;
+ }
+
+ /* With at least "origin AS", possibly with "route target". */
+ as_printf(ndo, asbuf, sizeof(asbuf), GET_BE_U_4(pptr + 1));
+
+ plen -= 32; /* adjust prefix length */
+
+ if (64 < plen) {
+ ND_PRINT("\n\t (illegal prefix length)");
+ return -1;
+ }
+
+ /* From now on (plen + 7) / 8 evaluates to { 0, 1, 2, ..., 8 }
+ * and gives the number of octets in the variable-length "route
+ * target" field inside this NLRI "prefix". Look for it.
+ */
+ memset(&route_target, 0, sizeof(route_target));
+ num_octets = (plen + 7) / 8;
+ ND_TCHECK_LEN(pptr + 5, num_octets);
+ memcpy(&route_target, pptr + 5, num_octets);
+ /* If mask-len is not on octet boundary, ensure all extra bits are 0 */
+ if (plen % 8) {
+ ((u_char *)&route_target)[num_octets - 1] &=
+ ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ ND_PRINT("\n\t origin AS: %s, %s",
+ asbuf,
+ bgp_rt_prefix_print(ndo, (u_char *)&route_target, plen));
+
+ return 5 + num_octets;
+trunc:
+ return -2;
+}
+
+static int
+decode_labeled_vpn_prefix4(netdissect_options *ndo,
+ const u_char *pptr, char *buf, size_t buflen)
+{
+ nd_ipv4 addr;
+ u_int plen;
+
+ plen = GET_U_1(pptr); /* get prefix length */
+
+ if ((24+64) > plen)
+ return -1;
+
+ plen -= (24+64); /* adjust prefixlen - labellength - RD len*/
+
+ if (32 < plen)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ ND_TCHECK_LEN(pptr + 12, (plen + 7) / 8);
+ memcpy(&addr, pptr + 12, (plen + 7) / 8);
+ if (plen % 8) {
+ ((u_char *)&addr)[(plen + 7) / 8 - 1] &=
+ ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ /* the label may get offsetted by 4 bits so lets shift it right */
+ snprintf(buf, buflen, "RD: %s, %s/%u, label:%u %s",
+ bgp_vpn_rd_print(ndo, pptr+4),
+ ipaddr_string(ndo, (const u_char *)&addr),
+ plen,
+ GET_BE_U_3(pptr + 1)>>4,
+ ((GET_U_1(pptr + 3) & 1) == 0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );
+
+ return 12 + (plen + 7) / 8;
+
+trunc:
+ return -2;
+}
+
+/*
+ * +-------------------------------+
+ * | |
+ * | RD:IPv4-address (12 octets) |
+ * | |
+ * +-------------------------------+
+ * | MDT Group-address (4 octets) |
+ * +-------------------------------+
+ */
+
+#define MDT_VPN_NLRI_LEN 16
+
+static int
+decode_mdt_vpn_nlri(netdissect_options *ndo,
+ const u_char *pptr, char *buf, size_t buflen)
+{
+ const u_char *rd;
+ const u_char *vpn_ip;
+
+ /* if the NLRI is not predefined length, quit.*/
+ if (GET_U_1(pptr) != MDT_VPN_NLRI_LEN * 8)
+ return -1;
+ pptr++;
+
+ /* RD */
+ ND_TCHECK_8(pptr);
+ rd = pptr;
+ pptr += 8;
+
+ /* IPv4 address */
+ vpn_ip = pptr;
+ pptr += sizeof(nd_ipv4);
+
+ /* MDT Group Address */
+ snprintf(buf, buflen, "RD: %s, VPN IP Address: %s, MC Group Address: %s",
+ bgp_vpn_rd_print(ndo, rd), GET_IPADDR_STRING(vpn_ip), GET_IPADDR_STRING(pptr));
+
+ return MDT_VPN_NLRI_LEN + 1;
+
+ trunc:
+ return -2;
+}
+
+#define BGP_MULTICAST_VPN_ROUTE_TYPE_INTRA_AS_I_PMSI 1
+#define BGP_MULTICAST_VPN_ROUTE_TYPE_INTER_AS_I_PMSI 2
+#define BGP_MULTICAST_VPN_ROUTE_TYPE_S_PMSI 3
+#define BGP_MULTICAST_VPN_ROUTE_TYPE_INTRA_AS_SEG_LEAF 4
+#define BGP_MULTICAST_VPN_ROUTE_TYPE_SOURCE_ACTIVE 5
+#define BGP_MULTICAST_VPN_ROUTE_TYPE_SHARED_TREE_JOIN 6
+#define BGP_MULTICAST_VPN_ROUTE_TYPE_SOURCE_TREE_JOIN 7
+
+static const struct tok bgp_multicast_vpn_route_type_values[] = {
+ { BGP_MULTICAST_VPN_ROUTE_TYPE_INTRA_AS_I_PMSI, "Intra-AS I-PMSI"},
+ { BGP_MULTICAST_VPN_ROUTE_TYPE_INTER_AS_I_PMSI, "Inter-AS I-PMSI"},
+ { BGP_MULTICAST_VPN_ROUTE_TYPE_S_PMSI, "S-PMSI"},
+ { BGP_MULTICAST_VPN_ROUTE_TYPE_INTRA_AS_SEG_LEAF, "Intra-AS Segment-Leaf"},
+ { BGP_MULTICAST_VPN_ROUTE_TYPE_SOURCE_ACTIVE, "Source-Active"},
+ { BGP_MULTICAST_VPN_ROUTE_TYPE_SHARED_TREE_JOIN, "Shared Tree Join"},
+ { BGP_MULTICAST_VPN_ROUTE_TYPE_SOURCE_TREE_JOIN, "Source Tree Join"},
+ { 0, NULL}
+};
+
+static int
+decode_multicast_vpn(netdissect_options *ndo,
+ const u_char *pptr, char *buf, size_t buflen)
+{
+ uint8_t route_type, route_length;
+ u_int addr_length, sg_length;
+ u_int offset;
+
+ route_type = GET_U_1(pptr);
+ pptr++;
+ route_length = GET_U_1(pptr);
+ pptr++;
+
+ snprintf(buf, buflen, "Route-Type: %s (%u), length: %u",
+ tok2str(bgp_multicast_vpn_route_type_values,
+ "Unknown", route_type),
+ route_type, route_length);
+
+ switch(route_type) {
+ case BGP_MULTICAST_VPN_ROUTE_TYPE_INTRA_AS_I_PMSI:
+ ND_TCHECK_LEN(pptr, BGP_VPN_RD_LEN);
+ offset = (u_int)strlen(buf);
+ snprintf(buf + offset, buflen - offset, ", RD: %s, Originator %s",
+ bgp_vpn_rd_print(ndo, pptr),
+ bgp_vpn_ip_print(ndo, pptr + BGP_VPN_RD_LEN,
+ (route_length - BGP_VPN_RD_LEN) << 3));
+ break;
+ case BGP_MULTICAST_VPN_ROUTE_TYPE_INTER_AS_I_PMSI:
+ ND_TCHECK_LEN(pptr, BGP_VPN_RD_LEN + 4);
+ offset = (u_int)strlen(buf);
+ snprintf(buf + offset, buflen - offset, ", RD: %s, Source-AS %s",
+ bgp_vpn_rd_print(ndo, pptr),
+ as_printf(ndo, astostr, sizeof(astostr),
+ GET_BE_U_4(pptr + BGP_VPN_RD_LEN)));
+ break;
+
+ case BGP_MULTICAST_VPN_ROUTE_TYPE_S_PMSI:
+ ND_TCHECK_LEN(pptr, BGP_VPN_RD_LEN);
+ offset = (u_int)strlen(buf);
+ snprintf(buf + offset, buflen - offset, ", RD: %s",
+ bgp_vpn_rd_print(ndo, pptr));
+ pptr += BGP_VPN_RD_LEN;
+
+ sg_length = bgp_vpn_sg_print(ndo, pptr, buf, buflen);
+ addr_length = route_length - sg_length;
+
+ ND_TCHECK_LEN(pptr, addr_length);
+ offset = (u_int)strlen(buf);
+ snprintf(buf + offset, buflen - offset, ", Originator %s",
+ bgp_vpn_ip_print(ndo, pptr, addr_length << 3));
+ break;
+
+ case BGP_MULTICAST_VPN_ROUTE_TYPE_SOURCE_ACTIVE:
+ ND_TCHECK_LEN(pptr, BGP_VPN_RD_LEN);
+ offset = (u_int)strlen(buf);
+ snprintf(buf + offset, buflen - offset, ", RD: %s",
+ bgp_vpn_rd_print(ndo, pptr));
+ pptr += BGP_VPN_RD_LEN;
+
+ bgp_vpn_sg_print(ndo, pptr, buf, buflen);
+ break;
+
+ case BGP_MULTICAST_VPN_ROUTE_TYPE_SHARED_TREE_JOIN: /* fall through */
+ case BGP_MULTICAST_VPN_ROUTE_TYPE_SOURCE_TREE_JOIN:
+ ND_TCHECK_LEN(pptr, BGP_VPN_RD_LEN + 4);
+ offset = (u_int)strlen(buf);
+ snprintf(buf + offset, buflen - offset, ", RD: %s, Source-AS %s",
+ bgp_vpn_rd_print(ndo, pptr),
+ as_printf(ndo, astostr, sizeof(astostr),
+ GET_BE_U_4(pptr + BGP_VPN_RD_LEN)));
+ pptr += BGP_VPN_RD_LEN + 4;
+
+ bgp_vpn_sg_print(ndo, pptr, buf, buflen);
+ break;
+
+ /*
+ * no per route-type printing yet.
+ */
+ case BGP_MULTICAST_VPN_ROUTE_TYPE_INTRA_AS_SEG_LEAF:
+ default:
+ break;
+ }
+
+ return route_length + 2;
+
+trunc:
+ return -2;
+}
+
+/*
+ * As I remember, some versions of systems have an snprintf() that
+ * returns -1 if the buffer would have overflowed. If the return
+ * value is negative, set buflen to 0, to indicate that we've filled
+ * the buffer up.
+ *
+ * If the return value is greater than buflen, that means that
+ * the buffer would have overflowed; again, set buflen to 0 in
+ * that case.
+ */
+#define UPDATE_BUF_BUFLEN(buf, buflen, stringlen) \
+ if (stringlen<0) \
+ buflen=0; \
+ else if ((u_int)stringlen>buflen) \
+ buflen=0; \
+ else { \
+ buflen-=stringlen; \
+ buf+=stringlen; \
+ }
+
+static int
+decode_labeled_vpn_l2(netdissect_options *ndo,
+ const u_char *pptr, char *buf, size_t buflen)
+{
+ u_int plen, tlen, tlv_type, tlv_len, ttlv_len;
+ int stringlen;
+
+ plen = GET_BE_U_2(pptr);
+ tlen = plen;
+ pptr += 2;
+ /* Old and new L2VPN NLRI share AFI/SAFI
+ * -> Assume a 12 Byte-length NLRI is auto-discovery-only
+ * and > 17 as old format. Complain for the middle case
+ */
+ if (plen == 12) {
+ /* assume AD-only with RD, BGPNH */
+ ND_TCHECK_LEN(pptr, 12);
+ buf[0] = '\0';
+ stringlen = snprintf(buf, buflen, "RD: %s, BGPNH: %s",
+ bgp_vpn_rd_print(ndo, pptr),
+ GET_IPADDR_STRING(pptr+8));
+ UPDATE_BUF_BUFLEN(buf, buflen, stringlen);
+ pptr += 12;
+ tlen -= 12;
+ return plen + 2;
+ } else if (plen > 17) {
+ /* assume old format */
+ /* RD, ID, LBLKOFF, LBLBASE */
+
+ ND_TCHECK_LEN(pptr, 15);
+ buf[0] = '\0';
+ stringlen = snprintf(buf, buflen, "RD: %s, CE-ID: %u, Label-Block Offset: %u, Label Base %u",
+ bgp_vpn_rd_print(ndo, pptr),
+ GET_BE_U_2(pptr + 8),
+ GET_BE_U_2(pptr + 10),
+ GET_BE_U_3(pptr + 12)>>4); /* the label is offsetted by 4 bits so lets shift it right */
+ UPDATE_BUF_BUFLEN(buf, buflen, stringlen);
+ pptr += 15;
+ tlen -= 15;
+
+ /* ok now the variable part - lets read out TLVs*/
+ while (tlen != 0) {
+ if (tlen < 3) {
+ if (buflen != 0) {
+ stringlen=snprintf(buf,buflen, "\n\t\tran past the end");
+ UPDATE_BUF_BUFLEN(buf, buflen, stringlen);
+ }
+ return plen + 2;
+ }
+ tlv_type = GET_U_1(pptr);
+ pptr++;
+ tlv_len = GET_BE_U_2(pptr); /* length, in *bits* */
+ ttlv_len = (tlv_len + 7)/8; /* length, in *bytes* */
+ pptr += 2;
+
+ switch(tlv_type) {
+ case 1:
+ if (buflen != 0) {
+ stringlen=snprintf(buf,buflen, "\n\t\tcircuit status vector (%u) length: %u: 0x",
+ tlv_type,
+ tlv_len);
+ UPDATE_BUF_BUFLEN(buf, buflen, stringlen);
+ }
+ while (ttlv_len != 0) {
+ if (tlen < 1) {
+ if (buflen != 0) {
+ stringlen=snprintf(buf,buflen, " (ran past the end)");
+ UPDATE_BUF_BUFLEN(buf, buflen, stringlen);
+ }
+ return plen + 2;
+ }
+ ND_TCHECK_1(pptr);
+ if (buflen != 0) {
+ stringlen=snprintf(buf,buflen, "%02x",
+ GET_U_1(pptr));
+ pptr++;
+ UPDATE_BUF_BUFLEN(buf, buflen, stringlen);
+ }
+ ttlv_len--;
+ tlen--;
+ }
+ break;
+ default:
+ if (buflen != 0) {
+ stringlen=snprintf(buf,buflen, "\n\t\tunknown TLV #%u, length: %u",
+ tlv_type,
+ tlv_len);
+ UPDATE_BUF_BUFLEN(buf, buflen, stringlen);
+ }
+ if (tlen < ttlv_len) {
+ if (buflen != 0) {
+ stringlen=snprintf(buf,buflen, " (ran past the end)");
+ UPDATE_BUF_BUFLEN(buf, buflen, stringlen);
+ }
+ return plen + 2;
+ }
+ tlen -= ttlv_len;
+ break;
+ }
+ }
+ return plen + 2;
+ } else {
+ /* complain bitterly ? */
+ /* fall through */
+ goto trunc;
+ }
+
+trunc:
+ return -2;
+}
+
+int
+decode_prefix6(netdissect_options *ndo,
+ const u_char *pd, u_int itemlen, char *buf, size_t buflen)
+{
+ nd_ipv6 addr;
+ u_int plen, plenbytes;
+
+ ITEMCHECK(1);
+ plen = GET_U_1(pd);
+ if (128 < plen)
+ return -1;
+ itemlen -= 1;
+
+ memset(&addr, 0, sizeof(addr));
+ plenbytes = (plen + 7) / 8;
+ ND_TCHECK_LEN(pd + 1, plenbytes);
+ ITEMCHECK(plenbytes);
+ memcpy(&addr, pd + 1, plenbytes);
+ if (plen % 8) {
+ addr[plenbytes - 1] &=
+ ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ snprintf(buf, buflen, "%s/%u", ip6addr_string(ndo, (const u_char *)&addr), plen);
+ return 1 + plenbytes;
+
+trunc:
+ return -2;
+
+badtlv:
+ return -3;
+}
+
+static int
+decode_labeled_prefix6(netdissect_options *ndo,
+ const u_char *pptr, u_int itemlen, char *buf, size_t buflen)
+{
+ nd_ipv6 addr;
+ u_int plen, plenbytes;
+
+ /* prefix length and label = 4 bytes */
+ ND_TCHECK_4(pptr);
+ ITEMCHECK(4);
+ plen = GET_U_1(pptr); /* get prefix length */
+
+ if (24 > plen)
+ return -1;
+
+ plen -= 24; /* adjust prefixlen - labellength */
+
+ if (128 < plen)
+ return -1;
+ itemlen -= 4;
+
+ memset(&addr, 0, sizeof(addr));
+ plenbytes = (plen + 7) / 8;
+ ND_TCHECK_LEN(pptr + 4, plenbytes);
+ memcpy(&addr, pptr + 4, plenbytes);
+ if (plen % 8) {
+ addr[plenbytes - 1] &=
+ ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ /* the label may get offsetted by 4 bits so lets shift it right */
+ snprintf(buf, buflen, "%s/%u, label:%u %s",
+ ip6addr_string(ndo, (const u_char *)&addr),
+ plen,
+ GET_BE_U_3(pptr + 1)>>4,
+ ((GET_U_1(pptr + 3) & 1) == 0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );
+
+ return 4 + plenbytes;
+
+trunc:
+ return -2;
+
+badtlv:
+ return -3;
+}
+
+static int
+decode_labeled_vpn_prefix6(netdissect_options *ndo,
+ const u_char *pptr, char *buf, size_t buflen)
+{
+ nd_ipv6 addr;
+ u_int plen;
+
+ plen = GET_U_1(pptr); /* get prefix length */
+
+ if ((24+64) > plen)
+ return -1;
+
+ plen -= (24+64); /* adjust prefixlen - labellength - RD len*/
+
+ if (128 < plen)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ ND_TCHECK_LEN(pptr + 12, (plen + 7) / 8);
+ memcpy(&addr, pptr + 12, (plen + 7) / 8);
+ if (plen % 8) {
+ addr[(plen + 7) / 8 - 1] &=
+ ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ /* the label may get offsetted by 4 bits so lets shift it right */
+ snprintf(buf, buflen, "RD: %s, %s/%u, label:%u %s",
+ bgp_vpn_rd_print(ndo, pptr+4),
+ ip6addr_string(ndo, (const u_char *)&addr),
+ plen,
+ GET_BE_U_3(pptr + 1)>>4,
+ ((GET_U_1(pptr + 3) & 1) == 0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );
+
+ return 12 + (plen + 7) / 8;
+
+trunc:
+ return -2;
+}
+
+static int
+decode_clnp_prefix(netdissect_options *ndo,
+ const u_char *pptr, char *buf, size_t buflen)
+{
+ uint8_t addr[19];
+ u_int plen;
+
+ plen = GET_U_1(pptr); /* get prefix length */
+
+ if (152 < plen)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ ND_TCHECK_LEN(pptr + 4, (plen + 7) / 8);
+ memcpy(&addr, pptr + 4, (plen + 7) / 8);
+ if (plen % 8) {
+ addr[(plen + 7) / 8 - 1] &=
+ ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ /* Cannot use GET_ISONSAP_STRING (not packet buffer pointer) */
+ snprintf(buf, buflen, "%s/%u",
+ isonsap_string(ndo, addr,(plen + 7) / 8),
+ plen);
+
+ return 1 + (plen + 7) / 8;
+
+trunc:
+ return -2;
+}
+
+static int
+decode_labeled_vpn_clnp_prefix(netdissect_options *ndo,
+ const u_char *pptr, char *buf, size_t buflen)
+{
+ uint8_t addr[19];
+ u_int plen;
+
+ plen = GET_U_1(pptr); /* get prefix length */
+
+ if ((24+64) > plen)
+ return -1;
+
+ plen -= (24+64); /* adjust prefixlen - labellength - RD len*/
+
+ if (152 < plen)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ ND_TCHECK_LEN(pptr + 12, (plen + 7) / 8);
+ memcpy(&addr, pptr + 12, (plen + 7) / 8);
+ if (plen % 8) {
+ addr[(plen + 7) / 8 - 1] &= ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ /* the label may get offsetted by 4 bits so lets shift it right */
+ /* Cannot use GET_ISONSAP_STRING (not packet buffer pointer) */
+ snprintf(buf, buflen, "RD: %s, %s/%u, label:%u %s",
+ bgp_vpn_rd_print(ndo, pptr+4),
+ isonsap_string(ndo, addr,(plen + 7) / 8),
+ plen,
+ GET_BE_U_3(pptr + 1)>>4,
+ ((GET_U_1(pptr + 3) & 1) == 0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );
+
+ return 12 + (plen + 7) / 8;
+
+trunc:
+ return -2;
+}
+
+/*
+ * bgp_attr_get_as_size
+ *
+ * Try to find the size of the ASs encoded in an as-path. It is not obvious, as
+ * both Old speakers that do not support 4 byte AS, and the new speakers that do
+ * support, exchange AS-Path with the same path-attribute type value 0x02.
+ */
+static u_int
+bgp_attr_get_as_size(netdissect_options *ndo,
+ uint8_t bgpa_type, const u_char *pptr, u_int len)
+{
+ const u_char *tptr = pptr;
+
+ /*
+ * If the path attribute is the optional AS4 path type, then we already
+ * know, that ASs must be encoded in 4 byte format.
+ */
+ if (bgpa_type == BGPTYPE_AS4_PATH) {
+ return 4;
+ }
+
+ /*
+ * Let us assume that ASs are of 2 bytes in size, and check if the AS-Path
+ * TLV is good. If not, ask the caller to try with AS encoded as 4 bytes
+ * each.
+ */
+ while (tptr < pptr + len) {
+ /*
+ * If we do not find a valid segment type, our guess might be wrong.
+ */
+ if (GET_U_1(tptr) < BGP_AS_SEG_TYPE_MIN || GET_U_1(tptr) > BGP_AS_SEG_TYPE_MAX) {
+ goto trunc;
+ }
+ tptr += 2 + GET_U_1(tptr + 1) * 2;
+ }
+
+ /*
+ * If we correctly reached end of the AS path attribute data content,
+ * then most likely ASs were indeed encoded as 2 bytes.
+ */
+ if (tptr == pptr + len) {
+ return 2;
+ }
+
+trunc:
+
+ /*
+ * We can come here, either we did not have enough data, or if we
+ * try to decode 4 byte ASs in 2 byte format. Either way, return 4,
+ * so that calller can try to decode each AS as of 4 bytes. If indeed
+ * there was not enough data, it will crib and end the parse anyways.
+ */
+ return 4;
+}
+
+/*
+ * The only way to know that a BGP UPDATE message is using add path is
+ * by checking if the capability is in the OPEN message which we may have missed.
+ * So this function checks if it is possible that the update could contain add path
+ * and if so it checks that standard BGP doesn't make sense.
+ */
+static int
+check_add_path(netdissect_options *ndo, const u_char *pptr, u_int length,
+ u_int max_prefix_length)
+{
+ u_int offset, prefix_length;
+
+ if (length < 5) {
+ return 0;
+ }
+
+ /*
+ * Scan through the NLRI information under the assumpetion that
+ * it doesn't have path IDs.
+ */
+ for (offset = 0; offset < length;) {
+ offset += 4;
+ if (!ND_TTEST_1(pptr + offset)) {
+ /* We ran out of captured data; quit scanning. */
+ break;
+ }
+ prefix_length = GET_U_1(pptr + offset);
+ /*
+ * Add 4 to cover the path id
+ * and check the prefix length isn't greater than 32/128.
+ */
+ if (prefix_length > max_prefix_length) {
+ return 0;
+ }
+ /* Add 1 for the prefix_length byte and prefix_length to cover the address */
+ offset += 1 + ((prefix_length + 7) / 8);
+ }
+ /* check we haven't gone past the end of the section */
+ if (offset > length) {
+ return 0;
+ }
+
+ /* check it's not standard BGP */
+ for (offset = 0; offset < length; ) {
+ if (!ND_TTEST_1(pptr + offset)) {
+ /* We ran out of captured data; quit scanning. */
+ break;
+ }
+ prefix_length = GET_U_1(pptr + offset);
+ /*
+ * If the prefix_length is zero (0.0.0.0/0)
+ * and since it's not the only address (length >= 5)
+ * then it is add-path
+ */
+ if (prefix_length < 1 || prefix_length > max_prefix_length) {
+ return 1;
+ }
+ offset += 1 + ((prefix_length + 7) / 8);
+ }
+ if (offset > length) {
+ return 1;
+ }
+
+ /* assume not add-path by default */
+ return 0;
+}
+
+static int
+bgp_mp_af_print(netdissect_options *ndo,
+ const u_char *tptr, u_int tlen,
+ uint16_t *afp, uint8_t *safip)
+{
+ uint16_t af;
+ uint8_t safi;
+
+ af = GET_BE_U_2(tptr);
+ *afp = af;
+ safi = GET_U_1(tptr + 2);
+ *safip = safi;
+
+ ND_PRINT("\n\t AFI: %s (%u), %sSAFI: %s (%u)",
+ tok2str(af_values, "Unknown AFI", af),
+ af,
+ (safi>128) ? "vendor specific " : "", /* 128 is meanwhile wellknown */
+ tok2str(bgp_safi_values, "Unknown SAFI", safi),
+ safi);
+
+ switch(af<<8 | safi) {
+ case (AFNUM_INET<<8 | SAFNUM_UNICAST):
+ case (AFNUM_INET<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_UNIMULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_LABUNICAST):
+ case (AFNUM_INET<<8 | SAFNUM_RT_ROUTING_INFO):
+ case (AFNUM_INET<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_INET<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_VPNUNIMULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_MULTICAST_VPN):
+ case (AFNUM_INET<<8 | SAFNUM_MDT):
+ case (AFNUM_INET6<<8 | SAFNUM_UNICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_UNIMULTICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_LABUNICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_VPNUNIMULTICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_UNICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_UNIMULTICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNUNIMULTICAST):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNUNIMULTICAST):
+ case (AFNUM_VPLS<<8 | SAFNUM_VPLS):
+ break;
+ default:
+ ND_TCHECK_LEN(tptr, tlen);
+ ND_PRINT("\n\t no AFI %u / SAFI %u decoder", af, safi);
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr, "\n\t ", tlen);
+ return -1;
+ }
+ return 0;
+trunc:
+ return -2;
+}
+
+static int
+bgp_nlri_print(netdissect_options *ndo, uint16_t af, uint8_t safi,
+ const u_char *tptr, u_int len,
+ char *buf, size_t buflen,
+ int add_path4, int add_path6)
+{
+ int advance;
+ u_int path_id = 0;
+
+ switch (af<<8 | safi) {
+ case (AFNUM_INET<<8 | SAFNUM_UNICAST):
+ case (AFNUM_INET<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_UNIMULTICAST):
+ if (add_path4) {
+ path_id = GET_BE_U_4(tptr);
+ tptr += 4;
+ }
+ advance = decode_prefix4(ndo, tptr, len, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else if (advance == -3)
+ break; /* bytes left, but not enough */
+ else
+ ND_PRINT("\n\t %s", buf);
+ if (add_path4) {
+ ND_PRINT(" Path Id: %u", path_id);
+ advance += 4;
+ }
+ break;
+ case (AFNUM_INET<<8 | SAFNUM_LABUNICAST):
+ advance = decode_labeled_prefix4(ndo, tptr, len, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else if (advance == -3)
+ break; /* bytes left, but not enough */
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+ case (AFNUM_INET<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_INET<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_VPNUNIMULTICAST):
+ advance = decode_labeled_vpn_prefix4(ndo, tptr, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+ case (AFNUM_INET<<8 | SAFNUM_RT_ROUTING_INFO):
+ advance = decode_rt_routing_info(ndo, tptr);
+ if (advance == -2)
+ goto trunc;
+ break;
+ case (AFNUM_INET<<8 | SAFNUM_MULTICAST_VPN): /* fall through */
+ case (AFNUM_INET6<<8 | SAFNUM_MULTICAST_VPN):
+ advance = decode_multicast_vpn(ndo, tptr, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+
+ case (AFNUM_INET<<8 | SAFNUM_MDT):
+ advance = decode_mdt_vpn_nlri(ndo, tptr, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+ case (AFNUM_INET6<<8 | SAFNUM_UNICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_UNIMULTICAST):
+ if (add_path6) {
+ path_id = GET_BE_U_4(tptr);
+ tptr += 4;
+ }
+ advance = decode_prefix6(ndo, tptr, len, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else if (advance == -3)
+ break; /* bytes left, but not enough */
+ else
+ ND_PRINT("\n\t %s", buf);
+ if (add_path6) {
+ ND_PRINT(" Path Id: %u", path_id);
+ advance += 4;
+ }
+ break;
+ case (AFNUM_INET6<<8 | SAFNUM_LABUNICAST):
+ advance = decode_labeled_prefix6(ndo, tptr, len, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else if (advance == -3)
+ break; /* bytes left, but not enough */
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+ case (AFNUM_INET6<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_VPNUNIMULTICAST):
+ advance = decode_labeled_vpn_prefix6(ndo, tptr, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+ case (AFNUM_VPLS<<8 | SAFNUM_VPLS):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNUNIMULTICAST):
+ advance = decode_labeled_vpn_l2(ndo, tptr, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal length)");
+ else if (advance == -2)
+ goto trunc;
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+ case (AFNUM_NSAP<<8 | SAFNUM_UNICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_UNIMULTICAST):
+ advance = decode_clnp_prefix(ndo, tptr, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNUNIMULTICAST):
+ advance = decode_labeled_vpn_clnp_prefix(ndo, tptr, buf, buflen);
+ if (advance == -1)
+ ND_PRINT("\n\t (illegal prefix length)");
+ else if (advance == -2)
+ goto trunc;
+ else
+ ND_PRINT("\n\t %s", buf);
+ break;
+ default:
+ /*
+ * This should not happen, we should have been protected
+ * by bgp_mp_af_print()'s return value.
+ */
+ ND_PRINT("\n\t ERROR: no AFI %u / SAFI %u decoder", af, safi);
+ advance = -4;
+ break;
+ }
+ return advance;
+trunc: /* we rely on the caller to recognize -2 return value */
+ return -2;
+}
+
+static int
+bgp_attr_print(netdissect_options *ndo,
+ uint8_t atype, const u_char *pptr, u_int len,
+ const unsigned attr_set_level)
+{
+ u_int i;
+ uint16_t af;
+ uint8_t safi, snpa, nhlen;
+ int advance;
+ u_int tlen;
+ const u_char *tptr;
+ char buf[MAXHOSTNAMELEN + 100];
+ u_int as_size;
+ int add_path4, add_path6;
+ int ret;
+
+ tptr = pptr;
+ tlen = len;
+
+ switch (atype) {
+ case BGPTYPE_ORIGIN:
+ if (len != 1)
+ ND_PRINT("invalid len");
+ else {
+ ND_PRINT("%s", tok2str(bgp_origin_values,
+ "Unknown Origin Typecode",
+ GET_U_1(tptr)));
+ }
+ break;
+
+ /*
+ * Process AS4 byte path and AS2 byte path attributes here.
+ */
+ case BGPTYPE_AS4_PATH:
+ case BGPTYPE_AS_PATH:
+ if (len % 2) {
+ ND_PRINT("invalid len");
+ break;
+ }
+ if (!len) {
+ ND_PRINT("empty");
+ break;
+ }
+
+ /*
+ * BGP updates exchanged between New speakers that support 4
+ * byte AS, ASs are always encoded in 4 bytes. There is no
+ * definitive way to find this, just by the packet's
+ * contents. So, check for packet's TLV's sanity assuming
+ * 2 bytes first, and it does not pass, assume that ASs are
+ * encoded in 4 bytes format and move on.
+ */
+ as_size = bgp_attr_get_as_size(ndo, atype, pptr, len);
+
+ while (tptr < pptr + len) {
+ ND_PRINT("%s", tok2str(bgp_as_path_segment_open_values,
+ "?", GET_U_1(tptr)));
+ for (i = 0; i < GET_U_1(tptr + 1) * as_size; i += as_size) {
+ ND_TCHECK_LEN(tptr + 2 + i, as_size);
+ ND_PRINT("%s ",
+ as_printf(ndo, astostr, sizeof(astostr),
+ as_size == 2 ?
+ GET_BE_U_2(tptr + i + 2) :
+ GET_BE_U_4(tptr + i + 2)));
+ }
+ ND_PRINT("%s", tok2str(bgp_as_path_segment_close_values,
+ "?", GET_U_1(tptr)));
+ tptr += 2 + GET_U_1(tptr + 1) * as_size;
+ }
+ break;
+ case BGPTYPE_NEXT_HOP:
+ if (len != 4)
+ ND_PRINT("invalid len");
+ else {
+ ND_PRINT("%s", GET_IPADDR_STRING(tptr));
+ }
+ break;
+ case BGPTYPE_MULTI_EXIT_DISC:
+ case BGPTYPE_LOCAL_PREF:
+ if (len != 4)
+ ND_PRINT("invalid len");
+ else {
+ ND_PRINT("%u", GET_BE_U_4(tptr));
+ }
+ break;
+ case BGPTYPE_ATOMIC_AGGREGATE:
+ if (len != 0)
+ ND_PRINT("invalid len");
+ break;
+ case BGPTYPE_AGGREGATOR:
+
+ /*
+ * Depending on the AS encoded is of 2 bytes or of 4 bytes,
+ * the length of this PA can be either 6 bytes or 8 bytes.
+ */
+ if (len != 6 && len != 8) {
+ ND_PRINT("invalid len");
+ break;
+ }
+ ND_TCHECK_LEN(tptr, len);
+ if (len == 6) {
+ ND_PRINT(" AS #%s, origin %s",
+ as_printf(ndo, astostr, sizeof(astostr), GET_BE_U_2(tptr)),
+ GET_IPADDR_STRING(tptr + 2));
+ } else {
+ ND_PRINT(" AS #%s, origin %s",
+ as_printf(ndo, astostr, sizeof(astostr),
+ GET_BE_U_4(tptr)), GET_IPADDR_STRING(tptr + 4));
+ }
+ break;
+ case BGPTYPE_AGGREGATOR4:
+ if (len != 8) {
+ ND_PRINT("invalid len");
+ break;
+ }
+ ND_PRINT(" AS #%s, origin %s",
+ as_printf(ndo, astostr, sizeof(astostr), GET_BE_U_4(tptr)),
+ GET_IPADDR_STRING(tptr + 4));
+ break;
+ case BGPTYPE_COMMUNITIES:
+ if (len % 4) {
+ ND_PRINT("invalid len");
+ break;
+ }
+ while (tlen != 0) {
+ uint32_t comm;
+ ND_TCHECK_4(tptr);
+ if (tlen < 4)
+ goto trunc;
+ comm = GET_BE_U_4(tptr);
+ switch (comm) {
+ case BGP_COMMUNITY_NO_EXPORT:
+ ND_PRINT(" NO_EXPORT");
+ break;
+ case BGP_COMMUNITY_NO_ADVERT:
+ ND_PRINT(" NO_ADVERTISE");
+ break;
+ case BGP_COMMUNITY_NO_EXPORT_SUBCONFED:
+ ND_PRINT(" NO_EXPORT_SUBCONFED");
+ break;
+ default:
+ ND_PRINT("%u:%u%s",
+ (comm >> 16) & 0xffff,
+ comm & 0xffff,
+ (tlen>4) ? ", " : "");
+ break;
+ }
+ tlen -=4;
+ tptr +=4;
+ }
+ break;
+ case BGPTYPE_ORIGINATOR_ID:
+ if (len != 4) {
+ ND_PRINT("invalid len");
+ break;
+ }
+ ND_PRINT("%s",GET_IPADDR_STRING(tptr));
+ break;
+ case BGPTYPE_CLUSTER_LIST:
+ if (len % 4) {
+ ND_PRINT("invalid len");
+ break;
+ }
+ while (tlen != 0) {
+ if (tlen < 4)
+ goto trunc;
+ ND_PRINT("%s%s",
+ GET_IPADDR_STRING(tptr),
+ (tlen>4) ? ", " : "");
+ tlen -=4;
+ tptr +=4;
+ }
+ break;
+ case BGPTYPE_MP_REACH_NLRI:
+ ND_TCHECK_3(tptr);
+ if (tlen < 3)
+ goto trunc;
+ ret = bgp_mp_af_print(ndo, tptr, tlen, &af, &safi);
+ if (ret == -2)
+ goto trunc;
+ if (ret < 0)
+ break;
+
+ tptr += 3;
+ tlen -= 3;
+
+ ND_TCHECK_1(tptr);
+ if (tlen < 1)
+ goto trunc;
+ nhlen = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+
+ if (nhlen) {
+ u_int nnh = 0;
+ uint8_t tnhlen = nhlen;
+ if (tlen < tnhlen)
+ goto trunc;
+ ND_PRINT("\n\t nexthop: ");
+ while (tnhlen != 0) {
+ if (nnh++ > 0) {
+ ND_PRINT(", " );
+ }
+ switch(af<<8 | safi) {
+ case (AFNUM_INET<<8 | SAFNUM_UNICAST):
+ case (AFNUM_INET<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_UNIMULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_LABUNICAST):
+ case (AFNUM_INET<<8 | SAFNUM_RT_ROUTING_INFO):
+ case (AFNUM_INET<<8 | SAFNUM_MULTICAST_VPN):
+ case (AFNUM_INET<<8 | SAFNUM_MDT):
+ if (tnhlen < sizeof(nd_ipv4)) {
+ ND_PRINT("invalid len");
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ } else {
+ ND_PRINT("%s",GET_IPADDR_STRING(tptr));
+ tptr += sizeof(nd_ipv4);
+ tnhlen -= sizeof(nd_ipv4);
+ tlen -= sizeof(nd_ipv4);
+ }
+ break;
+ case (AFNUM_INET<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_INET<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_INET<<8 | SAFNUM_VPNUNIMULTICAST):
+ if (tnhlen < sizeof(nd_ipv4)+BGP_VPN_RD_LEN) {
+ ND_PRINT("invalid len");
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ } else {
+ ND_PRINT("RD: %s, %s",
+ bgp_vpn_rd_print(ndo, tptr),
+ GET_IPADDR_STRING(tptr+BGP_VPN_RD_LEN));
+ tptr += (sizeof(nd_ipv4)+BGP_VPN_RD_LEN);
+ tlen -= (sizeof(nd_ipv4)+BGP_VPN_RD_LEN);
+ tnhlen -= (sizeof(nd_ipv4)+BGP_VPN_RD_LEN);
+ }
+ break;
+ case (AFNUM_INET6<<8 | SAFNUM_UNICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_UNIMULTICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_LABUNICAST):
+ if (tnhlen < sizeof(nd_ipv6)) {
+ ND_PRINT("invalid len");
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ } else {
+ ND_PRINT("%s", GET_IP6ADDR_STRING(tptr));
+ tptr += sizeof(nd_ipv6);
+ tlen -= sizeof(nd_ipv6);
+ tnhlen -= sizeof(nd_ipv6);
+ }
+ break;
+ case (AFNUM_INET6<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_INET6<<8 | SAFNUM_VPNUNIMULTICAST):
+ if (tnhlen < sizeof(nd_ipv6)+BGP_VPN_RD_LEN) {
+ ND_PRINT("invalid len");
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ } else {
+ ND_PRINT("RD: %s, %s",
+ bgp_vpn_rd_print(ndo, tptr),
+ GET_IP6ADDR_STRING(tptr+BGP_VPN_RD_LEN));
+ tptr += (sizeof(nd_ipv6)+BGP_VPN_RD_LEN);
+ tlen -= (sizeof(nd_ipv6)+BGP_VPN_RD_LEN);
+ tnhlen -= (sizeof(nd_ipv6)+BGP_VPN_RD_LEN);
+ }
+ break;
+ case (AFNUM_VPLS<<8 | SAFNUM_VPLS):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_L2VPN<<8 | SAFNUM_VPNUNIMULTICAST):
+ if (tnhlen < sizeof(nd_ipv4)) {
+ ND_PRINT("invalid len");
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ } else {
+ ND_PRINT("%s", GET_IPADDR_STRING(tptr));
+ tptr += (sizeof(nd_ipv4));
+ tlen -= (sizeof(nd_ipv4));
+ tnhlen -= (sizeof(nd_ipv4));
+ }
+ break;
+ case (AFNUM_NSAP<<8 | SAFNUM_UNICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_MULTICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_UNIMULTICAST):
+ ND_PRINT("%s", GET_ISONSAP_STRING(tptr, tnhlen));
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ break;
+
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNUNICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNMULTICAST):
+ case (AFNUM_NSAP<<8 | SAFNUM_VPNUNIMULTICAST):
+ if (tnhlen < BGP_VPN_RD_LEN+1) {
+ ND_PRINT("invalid len");
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ } else {
+ ND_TCHECK_LEN(tptr, tnhlen);
+ ND_PRINT("RD: %s, %s",
+ bgp_vpn_rd_print(ndo, tptr),
+ GET_ISONSAP_STRING(tptr+BGP_VPN_RD_LEN,tnhlen-BGP_VPN_RD_LEN));
+ /* rfc986 mapped IPv4 address ? */
+ if (GET_BE_U_4(tptr + BGP_VPN_RD_LEN) == 0x47000601)
+ ND_PRINT(" = %s", GET_IPADDR_STRING(tptr+BGP_VPN_RD_LEN+4));
+ /* rfc1888 mapped IPv6 address ? */
+ else if (GET_BE_U_3(tptr + BGP_VPN_RD_LEN) == 0x350000)
+ ND_PRINT(" = %s", GET_IP6ADDR_STRING(tptr+BGP_VPN_RD_LEN+3));
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ }
+ break;
+ default:
+ /*
+ * bgp_mp_af_print() should have saved us from
+ * an unsupported AFI/SAFI.
+ */
+ ND_PRINT("ERROR: no AFI %u/SAFI %u nexthop decoder", af, safi);
+ tptr += tnhlen;
+ tlen -= tnhlen;
+ tnhlen = 0;
+ goto done;
+ break;
+ }
+ }
+ }
+ ND_PRINT(", nh-length: %u", nhlen);
+
+ /* As per RFC 2858; this is reserved in RFC 4760 */
+ if (tlen < 1)
+ goto trunc;
+ snpa = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+
+ if (snpa) {
+ ND_PRINT("\n\t %u SNPA", snpa);
+ for (/*nothing*/; snpa != 0; snpa--) {
+ uint8_t snpalen;
+ if (tlen < 1)
+ goto trunc;
+ snpalen = GET_U_1(tptr);
+ ND_PRINT("\n\t %u bytes", snpalen);
+ tptr++;
+ tlen--;
+ if (tlen < snpalen)
+ goto trunc;
+ ND_TCHECK_LEN(tptr, snpalen);
+ tptr += snpalen;
+ tlen -= snpalen;
+ }
+ } else {
+ ND_PRINT(", no SNPA");
+ }
+
+ add_path4 = check_add_path(ndo, tptr, (len-ND_BYTES_BETWEEN(tptr, pptr)), 32);
+ add_path6 = check_add_path(ndo, tptr, (len-ND_BYTES_BETWEEN(tptr, pptr)), 128);
+
+ while (tptr < pptr + len) {
+ advance = bgp_nlri_print(ndo, af, safi, tptr, len, buf, sizeof(buf),
+ add_path4, add_path6);
+ if (advance == -2)
+ goto trunc;
+ if (advance < 0)
+ break;
+ tptr += advance;
+ }
+ break;
+
+ case BGPTYPE_MP_UNREACH_NLRI:
+ ND_TCHECK_LEN(tptr, BGP_MP_NLRI_MINSIZE);
+ ret = bgp_mp_af_print(ndo, tptr, tlen, &af, &safi);
+ if (ret == -2)
+ goto trunc;
+ if (ret < 0)
+ break;
+
+ if (len == BGP_MP_NLRI_MINSIZE)
+ ND_PRINT("\n\t End-of-Rib Marker (empty NLRI)");
+
+ tptr += 3;
+
+ add_path4 = check_add_path(ndo, tptr, (len-ND_BYTES_BETWEEN(tptr, pptr)), 32);
+ add_path6 = check_add_path(ndo, tptr, (len-ND_BYTES_BETWEEN(tptr, pptr)), 128);
+
+ while (tptr < pptr + len) {
+ advance = bgp_nlri_print(ndo, af, safi, tptr, len, buf, sizeof(buf),
+ add_path4, add_path6);
+ if (advance == -2)
+ goto trunc;
+ if (advance < 0)
+ break;
+ tptr += advance;
+ }
+ break;
+ case BGPTYPE_EXTD_COMMUNITIES:
+ if (len % 8) {
+ ND_PRINT("invalid len");
+ break;
+ }
+ while (tlen != 0) {
+ uint16_t extd_comm;
+
+ ND_TCHECK_2(tptr);
+ if (tlen < 2)
+ goto trunc;
+ extd_comm=GET_BE_U_2(tptr);
+
+ ND_PRINT("\n\t %s (0x%04x), Flags [%s]",
+ tok2str(bgp_extd_comm_subtype_values,
+ "unknown extd community typecode",
+ extd_comm),
+ extd_comm,
+ bittok2str(bgp_extd_comm_flag_values, "none", extd_comm));
+
+ ND_TCHECK_8(tptr);
+ if (tlen < 8)
+ goto trunc;
+ ND_PRINT(": ");
+ bgp_extended_community_print(ndo, tptr);
+ tlen -= 8;
+ tptr += 8;
+ }
+ break;
+
+ case BGPTYPE_PMSI_TUNNEL:
+ {
+ uint8_t tunnel_type, flags;
+
+ ND_TCHECK_5(tptr);
+ if (tlen < 5)
+ goto trunc;
+ flags = GET_U_1(tptr);
+ tunnel_type = GET_U_1(tptr + 1);
+
+ ND_PRINT("\n\t Tunnel-type %s (%u), Flags [%s], MPLS Label %u",
+ tok2str(bgp_pmsi_tunnel_values, "Unknown", tunnel_type),
+ tunnel_type,
+ bittok2str(bgp_pmsi_flag_values, "none", flags),
+ GET_BE_U_3(tptr + 2)>>4);
+
+ tptr +=5;
+ tlen -= 5;
+
+ switch (tunnel_type) {
+ case BGP_PMSI_TUNNEL_PIM_SM: /* fall through */
+ case BGP_PMSI_TUNNEL_PIM_BIDIR:
+ ND_PRINT("\n\t Sender %s, P-Group %s",
+ GET_IPADDR_STRING(tptr),
+ GET_IPADDR_STRING(tptr+4));
+ break;
+
+ case BGP_PMSI_TUNNEL_PIM_SSM:
+ ND_PRINT("\n\t Root-Node %s, P-Group %s",
+ GET_IPADDR_STRING(tptr),
+ GET_IPADDR_STRING(tptr+4));
+ break;
+ case BGP_PMSI_TUNNEL_INGRESS:
+ ND_PRINT("\n\t Tunnel-Endpoint %s",
+ GET_IPADDR_STRING(tptr));
+ break;
+ case BGP_PMSI_TUNNEL_LDP_P2MP: /* fall through */
+ case BGP_PMSI_TUNNEL_LDP_MP2MP:
+ ND_PRINT("\n\t Root-Node %s, LSP-ID 0x%08x",
+ GET_IPADDR_STRING(tptr),
+ GET_BE_U_4(tptr + 4));
+ break;
+ case BGP_PMSI_TUNNEL_RSVP_P2MP:
+ ND_PRINT("\n\t Extended-Tunnel-ID %s, P2MP-ID 0x%08x",
+ GET_IPADDR_STRING(tptr),
+ GET_BE_U_4(tptr + 4));
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ print_unknown_data(ndo, tptr, "\n\t ", tlen);
+ }
+ }
+ break;
+ }
+ case BGPTYPE_AIGP:
+ {
+ uint8_t type;
+ uint16_t length;
+
+ while (tlen >= 3) {
+ type = GET_U_1(tptr);
+ length = GET_BE_U_2(tptr + 1);
+ tptr += 3;
+ tlen -= 3;
+
+ ND_PRINT("\n\t %s TLV (%u), length %u",
+ tok2str(bgp_aigp_values, "Unknown", type),
+ type, length);
+
+ if (length < 3)
+ goto trunc;
+ length -= 3;
+
+ /*
+ * Check if we can read the TLV data.
+ */
+ ND_TCHECK_LEN(tptr + 3, length);
+ if (tlen < length)
+ goto trunc;
+
+ switch (type) {
+
+ case BGP_AIGP_TLV:
+ if (length < 8)
+ goto trunc;
+ ND_PRINT(", metric %" PRIu64,
+ GET_BE_U_8(tptr));
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ print_unknown_data(ndo, tptr,"\n\t ", length);
+ }
+ }
+
+ tptr += length;
+ tlen -= length;
+ }
+ break;
+ }
+ case BGPTYPE_ATTR_SET:
+ ND_TCHECK_4(tptr);
+ if (len < 4)
+ goto trunc;
+ ND_PRINT("\n\t Origin AS: %s",
+ as_printf(ndo, astostr, sizeof(astostr), GET_BE_U_4(tptr)));
+ tptr += 4;
+ len -= 4;
+
+ while (len) {
+ u_int aflags, alenlen, alen;
+
+ ND_TCHECK_2(tptr);
+ if (len < 2) {
+ ND_PRINT(" [path attr too short]");
+ tptr += len;
+ break;
+ }
+ aflags = GET_U_1(tptr);
+ atype = GET_U_1(tptr + 1);
+ tptr += 2;
+ len -= 2;
+ alenlen = bgp_attr_lenlen(aflags, tptr);
+ ND_TCHECK_LEN(tptr, alenlen);
+ if (len < alenlen) {
+ ND_PRINT(" [path attr too short]");
+ tptr += len;
+ break;
+ }
+ alen = bgp_attr_len(aflags, tptr);
+ tptr += alenlen;
+ len -= alenlen;
+
+ ND_PRINT("\n\t %s (%u), length: %u",
+ tok2str(bgp_attr_values,
+ "Unknown Attribute", atype),
+ atype,
+ alen);
+
+ if (aflags) {
+ ND_PRINT(", Flags [%s%s%s%s",
+ aflags & 0x80 ? "O" : "",
+ aflags & 0x40 ? "T" : "",
+ aflags & 0x20 ? "P" : "",
+ aflags & 0x10 ? "E" : "");
+ if (aflags & 0xf)
+ ND_PRINT("+%x", aflags & 0xf);
+ ND_PRINT("]");
+ }
+ ND_PRINT(": ");
+ if (len < alen) {
+ ND_PRINT(" [path attr too short]");
+ tptr += len;
+ break;
+ }
+ /*
+ * The protocol encoding per se allows ATTR_SET to be nested
+ * as many times as the message can accommodate. This printer
+ * used to be able to recurse into ATTR_SET contents until the
+ * stack exhaustion, but now there is a limit on that (if live
+ * protocol exchange goes that many levels deep, something is
+ * probably wrong anyway). Feel free to refine this value if
+ * you can find the spec with respective normative text.
+ */
+ if (attr_set_level == 10)
+ ND_PRINT("(too many nested levels, not recursing)");
+ else if (!bgp_attr_print(ndo, atype, tptr, alen, attr_set_level + 1))
+ return 0;
+ tptr += alen;
+ len -= alen;
+ }
+ break;
+
+ case BGPTYPE_LARGE_COMMUNITY:
+ if (len == 0 || len % 12) {
+ ND_PRINT("invalid len");
+ break;
+ }
+ ND_PRINT("\n\t ");
+ while (len != 0) {
+ ND_PRINT("%u:%u:%u%s",
+ GET_BE_U_4(tptr),
+ GET_BE_U_4(tptr + 4),
+ GET_BE_U_4(tptr + 8),
+ (len > 12) ? ", " : "");
+ tptr += 12;
+ /*
+ * len will always be a multiple of 12, as per the above,
+ * so this will never underflow.
+ */
+ len -= 12;
+ }
+ break;
+ default:
+ ND_TCHECK_LEN(pptr, len);
+ ND_PRINT("\n\t no Attribute %u decoder", atype); /* we have no decoder for the attribute */
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, pptr, "\n\t ", len);
+ break;
+ }
+done:
+ if (ndo->ndo_vflag > 1 && len) { /* omit zero length attributes*/
+ ND_TCHECK_LEN(pptr, len);
+ print_unknown_data(ndo, pptr, "\n\t ", len);
+ }
+ return 1;
+
+trunc:
+ return 0;
+}
+
+static void
+bgp_capabilities_print(netdissect_options *ndo,
+ const u_char *opt, u_int caps_len)
+{
+ u_int cap_type, cap_len, tcap_len, cap_offset;
+ u_int i = 0;
+
+ while (i < caps_len) {
+ ND_TCHECK_LEN(opt + i, BGP_CAP_HEADER_SIZE);
+ cap_type=GET_U_1(opt + i);
+ cap_len=GET_U_1(opt + i + 1);
+ ND_PRINT("\n\t %s (%u), length: %u",
+ tok2str(bgp_capcode_values, "Unknown", cap_type),
+ cap_type,
+ cap_len);
+ ND_TCHECK_LEN(opt + 2 + i, cap_len);
+ switch (cap_type) {
+ case BGP_CAPCODE_MP:
+ /* AFI (16 bits), Reserved (8 bits), SAFI (8 bits) */
+ if (cap_len < 4) {
+ ND_PRINT(" (too short, < 4)");
+ return;
+ }
+ ND_PRINT("\n\t\tAFI %s (%u), SAFI %s (%u)",
+ tok2str(af_values, "Unknown", GET_BE_U_2(opt + i + 2)),
+ GET_BE_U_2(opt + i + 2),
+ tok2str(bgp_safi_values, "Unknown", GET_U_1(opt + i + 5)),
+ GET_U_1(opt + i + 5));
+ break;
+ case BGP_CAPCODE_ML:
+ cap_offset = 2;
+ tcap_len = cap_len;
+ while (tcap_len >= 4) {
+ ND_PRINT( "\n\t\tAFI %s (%u), SAFI %s (%u), Count: %u",
+ tok2str(af_values, "Unknown",
+ GET_BE_U_2(opt + i + cap_offset)),
+ GET_BE_U_2(opt + i + cap_offset),
+ tok2str(bgp_safi_values, "Unknown",
+ GET_U_1(opt + i + cap_offset + 2)),
+ GET_U_1(opt + i + cap_offset + 2),
+ GET_U_1(opt + i + cap_offset + 3));
+ tcap_len -= 4;
+ cap_offset += 4;
+ }
+ break;
+ case BGP_CAPCODE_RESTART:
+ /* Restart Flags (4 bits), Restart Time in seconds (12 bits) */
+ if (cap_len < 2) {
+ ND_PRINT(" (too short, < 2)");
+ return;
+ }
+ tcap_len=cap_len;
+ ND_PRINT("\n\t\tRestart Flags: [%s], Restart Time %us",
+ ((GET_U_1(opt + i + 2))&0x80) ? "R" : "none",
+ GET_BE_U_2(opt + i + 2)&0xfff);
+ tcap_len-=2;
+ cap_offset=4;
+ while(tcap_len>=4) {
+ ND_PRINT("\n\t\t AFI %s (%u), SAFI %s (%u), Forwarding state preserved: %s",
+ tok2str(af_values,"Unknown",
+ GET_BE_U_2(opt + i + cap_offset)),
+ GET_BE_U_2(opt + i + cap_offset),
+ tok2str(bgp_safi_values,"Unknown",
+ GET_U_1(opt + i + cap_offset + 2)),
+ GET_U_1(opt + (i + cap_offset + 2)),
+ ((GET_U_1(opt + (i + cap_offset + 3)))&0x80) ? "yes" : "no" );
+ tcap_len -= 4;
+ cap_offset += 4;
+ }
+ break;
+ case BGP_CAPCODE_RR:
+ case BGP_CAPCODE_LLGR:
+ case BGP_CAPCODE_RR_CISCO:
+ break;
+ case BGP_CAPCODE_AS_NEW:
+ /*
+ * Extract the 4 byte AS number encoded.
+ */
+ if (cap_len < 4) {
+ ND_PRINT(" (too short, < 4)");
+ return;
+ }
+ ND_PRINT("\n\t\t 4 Byte AS %s",
+ as_printf(ndo, astostr, sizeof(astostr),
+ GET_BE_U_4(opt + i + 2)));
+ break;
+ case BGP_CAPCODE_ADD_PATH:
+ if (cap_len == 0) {
+ ND_PRINT(" (bogus)"); /* length */
+ break;
+ }
+ tcap_len=cap_len;
+ cap_offset=2;
+ while (tcap_len != 0) {
+ if (tcap_len < 4) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT("\n\t\tAFI %s (%u), SAFI %s (%u), Send/Receive: %s",
+ tok2str(af_values,"Unknown",GET_BE_U_2(opt + i + cap_offset)),
+ GET_BE_U_2(opt + i + cap_offset),
+ tok2str(bgp_safi_values,"Unknown",GET_U_1(opt + i + cap_offset + 2)),
+ GET_U_1(opt + (i + cap_offset + 2)),
+ tok2str(bgp_add_path_recvsend,"Bogus (0x%02x)",GET_U_1(opt + i + cap_offset + 3))
+ );
+ tcap_len -= 4;
+ cap_offset += 4;
+ }
+ break;
+ default:
+ ND_PRINT("\n\t\tno decoder for Capability %u",
+ cap_type);
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, opt + i + 2, "\n\t\t",
+ cap_len);
+ break;
+ }
+ if (ndo->ndo_vflag > 1 && cap_len != 0) {
+ print_unknown_data(ndo, opt + i + 2, "\n\t\t", cap_len);
+ }
+ i += BGP_CAP_HEADER_SIZE + cap_len;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+bgp_open_print(netdissect_options *ndo,
+ const u_char *dat, u_int length)
+{
+ const struct bgp_open *bgp_open_header;
+ u_int optslen;
+ const struct bgp_opt *bgpopt;
+ const u_char *opt;
+ u_int i;
+
+ ND_TCHECK_LEN(dat, BGP_OPEN_SIZE);
+ if (length < BGP_OPEN_SIZE)
+ goto trunc;
+
+ bgp_open_header = (const struct bgp_open *)dat;
+
+ ND_PRINT("\n\t Version %u, ",
+ GET_U_1(bgp_open_header->bgpo_version));
+ ND_PRINT("my AS %s, ",
+ as_printf(ndo, astostr, sizeof(astostr), GET_BE_U_2(bgp_open_header->bgpo_myas)));
+ ND_PRINT("Holdtime %us, ",
+ GET_BE_U_2(bgp_open_header->bgpo_holdtime));
+ ND_PRINT("ID %s", GET_IPADDR_STRING(bgp_open_header->bgpo_id));
+ optslen = GET_U_1(bgp_open_header->bgpo_optlen);
+ ND_PRINT("\n\t Optional parameters, length: %u", optslen);
+
+ opt = dat + BGP_OPEN_SIZE;
+ length -= BGP_OPEN_SIZE;
+
+ i = 0;
+ while (i < optslen) {
+ uint8_t opt_type, opt_len;
+
+ ND_TCHECK_LEN(opt + i, BGP_OPT_SIZE);
+ if (length < BGP_OPT_SIZE + i)
+ goto trunc;
+ bgpopt = (const struct bgp_opt *)(opt + i);
+ opt_type = GET_U_1(bgpopt->bgpopt_type);
+ opt_len = GET_U_1(bgpopt->bgpopt_len);
+ if (BGP_OPT_SIZE + i + opt_len > optslen) {
+ ND_PRINT("\n\t Option %u, length: %u, goes past the end of the options",
+ opt_type, opt_len);
+ break;
+ }
+
+ ND_PRINT("\n\t Option %s (%u), length: %u",
+ tok2str(bgp_opt_values,"Unknown",opt_type),
+ opt_type,
+ opt_len);
+
+ /* now let's decode the options we know*/
+ switch(opt_type) {
+
+ case BGP_OPT_CAP:
+ bgp_capabilities_print(ndo, opt + BGP_OPT_SIZE + i,
+ opt_len);
+ break;
+
+ case BGP_OPT_AUTH:
+ default:
+ ND_PRINT("\n\t no decoder for option %u",
+ opt_type);
+ break;
+ }
+ i += BGP_OPT_SIZE + opt_len;
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+bgp_update_print(netdissect_options *ndo,
+ const u_char *dat, u_int length)
+{
+ const u_char *p;
+ u_int withdrawn_routes_len;
+ char buf[MAXHOSTNAMELEN + 100];
+ int wpfx;
+ u_int len;
+ int i;
+ int add_path;
+ u_int path_id = 0;
+
+ ND_TCHECK_LEN(dat, BGP_SIZE);
+ if (length < BGP_SIZE)
+ goto trunc;
+ p = dat + BGP_SIZE;
+ length -= BGP_SIZE;
+
+ /* Unfeasible routes */
+ ND_TCHECK_2(p);
+ if (length < 2)
+ goto trunc;
+ withdrawn_routes_len = GET_BE_U_2(p);
+ p += 2;
+ length -= 2;
+ if (withdrawn_routes_len > 1) {
+ /*
+ * Without keeping state from the original NLRI message,
+ * it's not possible to tell if this a v4 or v6 route,
+ * so only try to decode it if we're not v6 enabled.
+ */
+ ND_TCHECK_LEN(p, withdrawn_routes_len);
+ if (length < withdrawn_routes_len)
+ goto trunc;
+ ND_PRINT("\n\t Withdrawn routes:");
+ add_path = check_add_path(ndo, p, withdrawn_routes_len, 32);
+ while (withdrawn_routes_len != 0) {
+ if (add_path) {
+ if (withdrawn_routes_len < 4) {
+ p += withdrawn_routes_len;
+ length -= withdrawn_routes_len;
+ break;
+ }
+ path_id = GET_BE_U_4(p);
+ p += 4;
+ length -= 4;
+ withdrawn_routes_len -= 4;
+ }
+ wpfx = decode_prefix4(ndo, p, withdrawn_routes_len, buf, sizeof(buf));
+ if (wpfx == -1) {
+ ND_PRINT("\n\t (illegal prefix length)");
+ break;
+ } else if (wpfx == -2)
+ goto trunc;
+ else if (wpfx == -3)
+ goto trunc; /* bytes left, but not enough */
+ else {
+ ND_PRINT("\n\t %s", buf);
+ if (add_path) {
+ ND_PRINT(" Path Id: %u", path_id);
+ }
+ p += wpfx;
+ length -= wpfx;
+ withdrawn_routes_len -= wpfx;
+ }
+ }
+ } else {
+ ND_TCHECK_LEN(p, withdrawn_routes_len);
+ if (length < withdrawn_routes_len)
+ goto trunc;
+ p += withdrawn_routes_len;
+ length -= withdrawn_routes_len;
+ }
+
+ ND_TCHECK_2(p);
+ if (length < 2)
+ goto trunc;
+ len = GET_BE_U_2(p);
+ p += 2;
+ length -= 2;
+
+ if (withdrawn_routes_len == 0 && len == 0 && length == 0) {
+ /* No withdrawn routes, no path attributes, no NLRI */
+ ND_PRINT("\n\t End-of-Rib Marker (empty NLRI)");
+ return;
+ }
+
+ if (len) {
+ /* do something more useful!*/
+ while (len) {
+ uint8_t aflags, atype, alenlen;
+ uint16_t alen;
+
+ ND_TCHECK_2(p);
+ if (length < 2)
+ goto trunc;
+ if (len < 2) {
+ ND_PRINT("\n\t [path attrs too short]");
+ p += len;
+ length -= len;
+ break;
+ }
+ aflags = GET_U_1(p);
+ atype = GET_U_1(p + 1);
+ p += 2;
+ len -= 2;
+ length -= 2;
+ alenlen = bgp_attr_lenlen(aflags, p);
+ ND_TCHECK_LEN(p, alenlen);
+ if (length < alenlen)
+ goto trunc;
+ if (len < alenlen) {
+ ND_PRINT("\n\t [path attrs too short]");
+ p += len;
+ length -= len;
+ break;
+ }
+ alen = bgp_attr_len(aflags, p);
+ p += alenlen;
+ len -= alenlen;
+ length -= alenlen;
+
+ ND_PRINT("\n\t %s (%u), length: %u",
+ tok2str(bgp_attr_values, "Unknown Attribute", atype),
+ atype,
+ alen);
+
+ if (aflags) {
+ ND_PRINT(", Flags [%s%s%s%s",
+ aflags & 0x80 ? "O" : "",
+ aflags & 0x40 ? "T" : "",
+ aflags & 0x20 ? "P" : "",
+ aflags & 0x10 ? "E" : "");
+ if (aflags & 0xf)
+ ND_PRINT("+%x", aflags & 0xf);
+ ND_PRINT("]: ");
+ }
+ if (len < alen) {
+ ND_PRINT(" [path attrs too short]");
+ p += len;
+ length -= len;
+ break;
+ }
+ if (length < alen)
+ goto trunc;
+ if (!bgp_attr_print(ndo, atype, p, alen, 0))
+ goto trunc;
+ p += alen;
+ len -= alen;
+ length -= alen;
+ }
+ }
+
+ if (length) {
+ add_path = check_add_path(ndo, p, length, 32);
+ ND_PRINT("\n\t Updated routes:");
+ while (length != 0) {
+ if (add_path) {
+ ND_TCHECK_4(p);
+ if (length < 4)
+ goto trunc;
+ path_id = GET_BE_U_4(p);
+ p += 4;
+ length -= 4;
+ }
+ i = decode_prefix4(ndo, p, length, buf, sizeof(buf));
+ if (i == -1) {
+ ND_PRINT("\n\t (illegal prefix length)");
+ break;
+ } else if (i == -2)
+ goto trunc;
+ else if (i == -3)
+ goto trunc; /* bytes left, but not enough */
+ else {
+ ND_PRINT("\n\t %s", buf);
+ if (add_path) {
+ ND_PRINT(" Path Id: %u", path_id);
+ }
+ p += i;
+ length -= i;
+ }
+ }
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+bgp_notification_print(netdissect_options *ndo,
+ const u_char *dat, u_int length)
+{
+ const struct bgp_notification *bgp_notification_header;
+ const u_char *tptr;
+ uint8_t bgpn_major, bgpn_minor;
+ uint8_t shutdown_comm_length;
+ uint8_t remainder_offset;
+
+ ND_TCHECK_LEN(dat, BGP_NOTIFICATION_SIZE);
+ if (length<BGP_NOTIFICATION_SIZE)
+ return;
+
+ bgp_notification_header = (const struct bgp_notification *)dat;
+ bgpn_major = GET_U_1(bgp_notification_header->bgpn_major);
+ bgpn_minor = GET_U_1(bgp_notification_header->bgpn_minor);
+
+ ND_PRINT(", %s (%u)",
+ tok2str(bgp_notify_major_values, "Unknown Error",
+ bgpn_major),
+ bgpn_major);
+
+ switch (bgpn_major) {
+
+ case BGP_NOTIFY_MAJOR_MSG:
+ ND_PRINT(", subcode %s (%u)",
+ tok2str(bgp_notify_minor_msg_values, "Unknown",
+ bgpn_minor),
+ bgpn_minor);
+ break;
+ case BGP_NOTIFY_MAJOR_OPEN:
+ ND_PRINT(", subcode %s (%u)",
+ tok2str(bgp_notify_minor_open_values, "Unknown",
+ bgpn_minor),
+ bgpn_minor);
+ break;
+ case BGP_NOTIFY_MAJOR_UPDATE:
+ ND_PRINT(", subcode %s (%u)",
+ tok2str(bgp_notify_minor_update_values, "Unknown",
+ bgpn_minor),
+ bgpn_minor);
+ break;
+ case BGP_NOTIFY_MAJOR_FSM:
+ ND_PRINT(" subcode %s (%u)",
+ tok2str(bgp_notify_minor_fsm_values, "Unknown",
+ bgpn_minor),
+ bgpn_minor);
+ break;
+ case BGP_NOTIFY_MAJOR_CAP:
+ ND_PRINT(" subcode %s (%u)",
+ tok2str(bgp_notify_minor_cap_values, "Unknown",
+ bgpn_minor),
+ bgpn_minor);
+ break;
+ case BGP_NOTIFY_MAJOR_CEASE:
+ ND_PRINT(", subcode %s (%u)",
+ tok2str(bgp_notify_minor_cease_values, "Unknown",
+ bgpn_minor),
+ bgpn_minor);
+
+ /* draft-ietf-idr-cease-subcode-02 mentions optionally 7 bytes
+ * for the maxprefix subtype, which may contain AFI, SAFI and MAXPREFIXES
+ */
+ if(bgpn_minor == BGP_NOTIFY_MINOR_CEASE_MAXPRFX && length >= BGP_NOTIFICATION_SIZE + 7) {
+ tptr = dat + BGP_NOTIFICATION_SIZE;
+ ND_PRINT(", AFI %s (%u), SAFI %s (%u), Max Prefixes: %u",
+ tok2str(af_values, "Unknown", GET_BE_U_2(tptr)),
+ GET_BE_U_2(tptr),
+ tok2str(bgp_safi_values, "Unknown", GET_U_1((tptr + 2))),
+ GET_U_1((tptr + 2)),
+ GET_BE_U_4(tptr + 3));
+ }
+ /*
+ * draft-ietf-idr-shutdown describes a method to send a communication
+ * intended for human consumption regarding the Administrative Shutdown
+ */
+ if ((bgpn_minor == BGP_NOTIFY_MINOR_CEASE_SHUT ||
+ bgpn_minor == BGP_NOTIFY_MINOR_CEASE_RESET) &&
+ length >= BGP_NOTIFICATION_SIZE + 1) {
+ tptr = dat + BGP_NOTIFICATION_SIZE;
+ shutdown_comm_length = GET_U_1(tptr);
+ remainder_offset = 0;
+ /* garbage, hexdump it all */
+ if (shutdown_comm_length > BGP_NOTIFY_MINOR_CEASE_ADMIN_SHUTDOWN_LEN ||
+ shutdown_comm_length > length - (BGP_NOTIFICATION_SIZE + 1)) {
+ ND_PRINT(", invalid Shutdown Communication length");
+ }
+ else if (shutdown_comm_length == 0) {
+ ND_PRINT(", empty Shutdown Communication");
+ remainder_offset += 1;
+ }
+ /* a proper shutdown communication */
+ else {
+ ND_TCHECK_LEN(tptr + 1, shutdown_comm_length);
+ ND_PRINT(", Shutdown Communication (length: %u): \"", shutdown_comm_length);
+ (void)nd_printn(ndo, tptr+1, shutdown_comm_length, NULL);
+ ND_PRINT("\"");
+ remainder_offset += shutdown_comm_length + 1;
+ }
+ /* if there is trailing data, hexdump it */
+ if(length - (remainder_offset + BGP_NOTIFICATION_SIZE) > 0) {
+ ND_PRINT(", Data: (length: %u)", length - (remainder_offset + BGP_NOTIFICATION_SIZE));
+ hex_print(ndo, "\n\t\t", tptr + remainder_offset, length - (remainder_offset + BGP_NOTIFICATION_SIZE));
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+bgp_route_refresh_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ const struct bgp_route_refresh *bgp_route_refresh_header;
+
+ ND_TCHECK_LEN(pptr, BGP_ROUTE_REFRESH_SIZE);
+
+ /* some little sanity checking */
+ if (len<BGP_ROUTE_REFRESH_SIZE)
+ return;
+
+ bgp_route_refresh_header = (const struct bgp_route_refresh *)pptr;
+
+ ND_PRINT("\n\t AFI %s (%u), SAFI %s (%u)",
+ tok2str(af_values,"Unknown",
+ GET_BE_U_2(bgp_route_refresh_header->afi)),
+ GET_BE_U_2(bgp_route_refresh_header->afi),
+ tok2str(bgp_safi_values,"Unknown",
+ GET_U_1(bgp_route_refresh_header->safi)),
+ GET_U_1(bgp_route_refresh_header->safi));
+
+ if (ndo->ndo_vflag > 1) {
+ ND_TCHECK_LEN(pptr, len);
+ print_unknown_data(ndo, pptr, "\n\t ", len);
+ }
+
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static int
+bgp_pdu_print(netdissect_options *ndo,
+ const u_char *dat, u_int length)
+{
+ const struct bgp *bgp_header;
+ uint8_t bgp_type;
+
+ ND_TCHECK_LEN(dat, BGP_SIZE);
+ bgp_header = (const struct bgp *)dat;
+ bgp_type = GET_U_1(bgp_header->bgp_type);
+
+ ND_PRINT("\n\t%s Message (%u), length: %u",
+ tok2str(bgp_msg_values, "Unknown", bgp_type),
+ bgp_type,
+ length);
+
+ switch (bgp_type) {
+ case BGP_OPEN:
+ bgp_open_print(ndo, dat, length);
+ break;
+ case BGP_UPDATE:
+ bgp_update_print(ndo, dat, length);
+ break;
+ case BGP_NOTIFICATION:
+ bgp_notification_print(ndo, dat, length);
+ break;
+ case BGP_KEEPALIVE:
+ break;
+ case BGP_ROUTE_REFRESH:
+ bgp_route_refresh_print(ndo, dat, length);
+ break;
+ default:
+ /* we have no decoder for the BGP message */
+ ND_TCHECK_LEN(dat, length);
+ ND_PRINT("\n\t no Message %u decoder", bgp_type);
+ print_unknown_data(ndo, dat, "\n\t ", length);
+ break;
+ }
+ return 1;
+trunc:
+ nd_print_trunc(ndo);
+ return 0;
+}
+
+void
+bgp_print(netdissect_options *ndo,
+ const u_char *dat, u_int length _U_)
+{
+ const u_char *p;
+ const u_char *ep = ndo->ndo_snapend;
+ const u_char *start;
+ const u_char marker[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ };
+ const struct bgp *bgp_header;
+ uint16_t hlen;
+
+ ndo->ndo_protocol = "bgp";
+ ND_PRINT(": BGP");
+
+ if (ndo->ndo_vflag < 1) /* lets be less chatty */
+ return;
+
+ p = dat;
+ start = p;
+ while (p < ep) {
+ if (!ND_TTEST_1(p))
+ break;
+ if (GET_U_1(p) != 0xff) {
+ p++;
+ continue;
+ }
+
+ if (!ND_TTEST_LEN(p, sizeof(marker)))
+ break;
+ if (memcmp(p, marker, sizeof(marker)) != 0) {
+ p++;
+ continue;
+ }
+
+ /* found BGP header */
+ ND_TCHECK_LEN(p, BGP_SIZE);
+ bgp_header = (const struct bgp *)p;
+
+ if (start != p)
+ nd_print_trunc(ndo);
+
+ hlen = GET_BE_U_2(bgp_header->bgp_len);
+ if (hlen < BGP_SIZE) {
+ ND_PRINT("\nmessage length %u < %u", hlen, BGP_SIZE);
+ nd_print_invalid(ndo);
+ break;
+ }
+
+ if (ND_TTEST_LEN(p, hlen)) {
+ if (!bgp_pdu_print(ndo, p, hlen))
+ return;
+ p += hlen;
+ start = p;
+ } else {
+ ND_PRINT("\n[|BGP %s]",
+ tok2str(bgp_msg_values,
+ "Unknown Message Type",
+ GET_U_1(bgp_header->bgp_type)));
+ break;
+ }
+ }
+
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-bootp.c b/print-bootp.c
new file mode 100644
index 0000000..f84b77c
--- /dev/null
+++ b/print-bootp.c
@@ -0,0 +1,1074 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: BOOTP and IPv4 DHCP printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+
+/*
+ * Bootstrap Protocol (BOOTP). RFC951 and RFC1048.
+ *
+ * This file specifies the "implementation-independent" BOOTP protocol
+ * information which is common to both client and server.
+ *
+ * Copyright 1988 by Carnegie Mellon.
+ *
+ * Permission to use, copy, modify, and distribute this program for any
+ * purpose and without fee is hereby granted, provided that this copyright
+ * and permission notice appear on all copies and supporting documentation,
+ * the name of Carnegie Mellon not be used in advertising or publicity
+ * pertaining to distribution of the program without specific prior
+ * permission, and notice be given in supporting documentation that copying
+ * and distribution is by permission of Carnegie Mellon and Stanford
+ * University. Carnegie Mellon makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ */
+
+struct bootp {
+ nd_uint8_t bp_op; /* packet opcode type */
+ nd_uint8_t bp_htype; /* hardware addr type */
+ nd_uint8_t bp_hlen; /* hardware addr length */
+ nd_uint8_t bp_hops; /* gateway hops */
+ nd_uint32_t bp_xid; /* transaction ID */
+ nd_uint16_t bp_secs; /* seconds since boot began */
+ nd_uint16_t bp_flags; /* flags - see bootp_flag_values[]
+ in print-bootp.c */
+ nd_ipv4 bp_ciaddr; /* client IP address */
+ nd_ipv4 bp_yiaddr; /* 'your' IP address */
+ nd_ipv4 bp_siaddr; /* server IP address */
+ nd_ipv4 bp_giaddr; /* gateway IP address */
+ nd_byte bp_chaddr[16]; /* client hardware address */
+ nd_byte bp_sname[64]; /* server host name */
+ nd_byte bp_file[128]; /* boot file name */
+ nd_byte bp_vend[64]; /* vendor-specific area */
+};
+
+#define BOOTPREPLY 2
+#define BOOTPREQUEST 1
+
+/*
+ * Vendor magic cookie (v_magic) for CMU
+ */
+#define VM_CMU "CMU"
+
+/*
+ * Vendor magic cookie (v_magic) for RFC1048
+ */
+#define VM_RFC1048 { 99, 130, 83, 99 }
+
+/*
+ * RFC1048 tag values used to specify what information is being supplied in
+ * the vendor field of the packet.
+ */
+
+#define TAG_PAD ((uint8_t) 0)
+#define TAG_SUBNET_MASK ((uint8_t) 1)
+#define TAG_TIME_OFFSET ((uint8_t) 2)
+#define TAG_GATEWAY ((uint8_t) 3)
+#define TAG_TIME_SERVER ((uint8_t) 4)
+#define TAG_NAME_SERVER ((uint8_t) 5)
+#define TAG_DOMAIN_SERVER ((uint8_t) 6)
+#define TAG_LOG_SERVER ((uint8_t) 7)
+#define TAG_COOKIE_SERVER ((uint8_t) 8)
+#define TAG_LPR_SERVER ((uint8_t) 9)
+#define TAG_IMPRESS_SERVER ((uint8_t) 10)
+#define TAG_RLP_SERVER ((uint8_t) 11)
+#define TAG_HOSTNAME ((uint8_t) 12)
+#define TAG_BOOTSIZE ((uint8_t) 13)
+#define TAG_END ((uint8_t) 255)
+/* RFC1497 tags */
+#define TAG_DUMPPATH ((uint8_t) 14)
+#define TAG_DOMAINNAME ((uint8_t) 15)
+#define TAG_SWAP_SERVER ((uint8_t) 16)
+#define TAG_ROOTPATH ((uint8_t) 17)
+#define TAG_EXTPATH ((uint8_t) 18)
+/* RFC2132 */
+#define TAG_IP_FORWARD ((uint8_t) 19)
+#define TAG_NL_SRCRT ((uint8_t) 20)
+#define TAG_PFILTERS ((uint8_t) 21)
+#define TAG_REASS_SIZE ((uint8_t) 22)
+#define TAG_DEF_TTL ((uint8_t) 23)
+#define TAG_MTU_TIMEOUT ((uint8_t) 24)
+#define TAG_MTU_TABLE ((uint8_t) 25)
+#define TAG_INT_MTU ((uint8_t) 26)
+#define TAG_LOCAL_SUBNETS ((uint8_t) 27)
+#define TAG_BROAD_ADDR ((uint8_t) 28)
+#define TAG_DO_MASK_DISC ((uint8_t) 29)
+#define TAG_SUPPLY_MASK ((uint8_t) 30)
+#define TAG_DO_RDISC ((uint8_t) 31)
+#define TAG_RTR_SOL_ADDR ((uint8_t) 32)
+#define TAG_STATIC_ROUTE ((uint8_t) 33)
+#define TAG_USE_TRAILERS ((uint8_t) 34)
+#define TAG_ARP_TIMEOUT ((uint8_t) 35)
+#define TAG_ETH_ENCAP ((uint8_t) 36)
+#define TAG_TCP_TTL ((uint8_t) 37)
+#define TAG_TCP_KEEPALIVE ((uint8_t) 38)
+#define TAG_KEEPALIVE_GO ((uint8_t) 39)
+#define TAG_NIS_DOMAIN ((uint8_t) 40)
+#define TAG_NIS_SERVERS ((uint8_t) 41)
+#define TAG_NTP_SERVERS ((uint8_t) 42)
+#define TAG_VENDOR_OPTS ((uint8_t) 43)
+#define TAG_NETBIOS_NS ((uint8_t) 44)
+#define TAG_NETBIOS_DDS ((uint8_t) 45)
+#define TAG_NETBIOS_NODE ((uint8_t) 46)
+#define TAG_NETBIOS_SCOPE ((uint8_t) 47)
+#define TAG_XWIN_FS ((uint8_t) 48)
+#define TAG_XWIN_DM ((uint8_t) 49)
+#define TAG_NIS_P_DOMAIN ((uint8_t) 64)
+#define TAG_NIS_P_SERVERS ((uint8_t) 65)
+#define TAG_MOBILE_HOME ((uint8_t) 68)
+#define TAG_SMPT_SERVER ((uint8_t) 69)
+#define TAG_POP3_SERVER ((uint8_t) 70)
+#define TAG_NNTP_SERVER ((uint8_t) 71)
+#define TAG_WWW_SERVER ((uint8_t) 72)
+#define TAG_FINGER_SERVER ((uint8_t) 73)
+#define TAG_IRC_SERVER ((uint8_t) 74)
+#define TAG_STREETTALK_SRVR ((uint8_t) 75)
+#define TAG_STREETTALK_STDA ((uint8_t) 76)
+/* DHCP options */
+#define TAG_REQUESTED_IP ((uint8_t) 50)
+#define TAG_IP_LEASE ((uint8_t) 51)
+#define TAG_OPT_OVERLOAD ((uint8_t) 52)
+#define TAG_TFTP_SERVER ((uint8_t) 66)
+#define TAG_BOOTFILENAME ((uint8_t) 67)
+#define TAG_DHCP_MESSAGE ((uint8_t) 53)
+#define TAG_SERVER_ID ((uint8_t) 54)
+#define TAG_PARM_REQUEST ((uint8_t) 55)
+#define TAG_MESSAGE ((uint8_t) 56)
+#define TAG_MAX_MSG_SIZE ((uint8_t) 57)
+#define TAG_RENEWAL_TIME ((uint8_t) 58)
+#define TAG_REBIND_TIME ((uint8_t) 59)
+#define TAG_VENDOR_CLASS ((uint8_t) 60)
+#define TAG_CLIENT_ID ((uint8_t) 61)
+/* RFC 2241 */
+#define TAG_NDS_SERVERS ((uint8_t) 85)
+#define TAG_NDS_TREE_NAME ((uint8_t) 86)
+#define TAG_NDS_CONTEXT ((uint8_t) 87)
+/* RFC 2242 */
+#define TAG_NDS_IPDOMAIN ((uint8_t) 62)
+#define TAG_NDS_IPINFO ((uint8_t) 63)
+/* RFC 2485 */
+#define TAG_OPEN_GROUP_UAP ((uint8_t) 98)
+/* RFC 2563 */
+#define TAG_DISABLE_AUTOCONF ((uint8_t) 116)
+/* RFC 2610 */
+#define TAG_SLP_DA ((uint8_t) 78)
+#define TAG_SLP_SCOPE ((uint8_t) 79)
+/* RFC 2937 */
+#define TAG_NS_SEARCH ((uint8_t) 117)
+/* RFC 3004 - The User Class Option for DHCP */
+#define TAG_USER_CLASS ((uint8_t) 77)
+/* RFC 3011 */
+#define TAG_IP4_SUBNET_SELECT ((uint8_t) 118)
+/* RFC 3442 */
+#define TAG_CLASSLESS_STATIC_RT ((uint8_t) 121)
+#define TAG_CLASSLESS_STA_RT_MS ((uint8_t) 249)
+/* RFC 5859 - TFTP Server Address Option for DHCPv4 */
+#define TAG_TFTP_SERVER_ADDRESS ((uint8_t) 150)
+/* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml */
+#define TAG_SLP_NAMING_AUTH ((uint8_t) 80)
+#define TAG_CLIENT_FQDN ((uint8_t) 81)
+#define TAG_AGENT_CIRCUIT ((uint8_t) 82)
+#define TAG_AGENT_REMOTE ((uint8_t) 83)
+#define TAG_TZ_STRING ((uint8_t) 88)
+#define TAG_FQDN_OPTION ((uint8_t) 89)
+#define TAG_AUTH ((uint8_t) 90)
+#define TAG_CLIENT_LAST_TRANSACTION_TIME ((uint8_t) 91)
+#define TAG_ASSOCIATED_IP ((uint8_t) 92)
+#define TAG_CLIENT_ARCH ((uint8_t) 93)
+#define TAG_CLIENT_NDI ((uint8_t) 94)
+#define TAG_CLIENT_GUID ((uint8_t) 97)
+#define TAG_LDAP_URL ((uint8_t) 95)
+/* RFC 4833, TZ codes */
+#define TAG_TZ_PCODE ((uint8_t) 100)
+#define TAG_TZ_TCODE ((uint8_t) 101)
+#define TAG_NETINFO_PARENT ((uint8_t) 112)
+#define TAG_NETINFO_PARENT_TAG ((uint8_t) 113)
+#define TAG_URL ((uint8_t) 114)
+#define TAG_MUDURL ((uint8_t) 161)
+
+/* DHCP Message types (values for TAG_DHCP_MESSAGE option) */
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+/* Defined in RFC4388 */
+#define DHCPLEASEQUERY 10
+#define DHCPLEASEUNASSIGNED 11
+#define DHCPLEASEUNKNOWN 12
+#define DHCPLEASEACTIVE 13
+
+
+/*
+ * "vendor" data permitted for CMU bootp clients.
+ */
+
+struct cmu_vend {
+ nd_byte v_magic[4]; /* magic number */
+ nd_uint32_t v_flags; /* flags/opcodes, etc. */
+ nd_ipv4 v_smask; /* Subnet mask */
+ nd_ipv4 v_dgate; /* Default gateway */
+ nd_ipv4 v_dns1, v_dns2; /* Domain name servers */
+ nd_ipv4 v_ins1, v_ins2; /* IEN-116 name servers */
+ nd_ipv4 v_ts1, v_ts2; /* Time servers */
+ nd_byte v_unused[24]; /* currently unused */
+};
+
+
+/* v_flags values */
+#define VF_SMASK 1 /* Subnet mask field contains valid data */
+
+/* RFC 4702 DHCP Client FQDN Option */
+
+#define CLIENT_FQDN_FLAGS_S 0x01
+#define CLIENT_FQDN_FLAGS_O 0x02
+#define CLIENT_FQDN_FLAGS_E 0x04
+#define CLIENT_FQDN_FLAGS_N 0x08
+/* end of original bootp.h */
+
+static void rfc1048_print(netdissect_options *, const u_char *);
+static void cmu_print(netdissect_options *, const u_char *);
+static char *client_fqdn_flags(u_int flags);
+
+static const struct tok bootp_flag_values[] = {
+ { 0x8000, "Broadcast" },
+ { 0, NULL}
+};
+
+static const struct tok bootp_op_values[] = {
+ { BOOTPREQUEST, "Request" },
+ { BOOTPREPLY, "Reply" },
+ { 0, NULL}
+};
+
+/*
+ * Print bootp requests
+ */
+void
+bootp_print(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ const struct bootp *bp;
+ static const u_char vm_cmu[4] = VM_CMU;
+ static const u_char vm_rfc1048[4] = VM_RFC1048;
+ uint8_t bp_op, bp_htype, bp_hlen;
+
+ ndo->ndo_protocol = "bootp";
+ bp = (const struct bootp *)cp;
+ bp_op = GET_U_1(bp->bp_op);
+ ND_PRINT("BOOTP/DHCP, %s",
+ tok2str(bootp_op_values, "unknown (0x%02x)", bp_op));
+
+ bp_htype = GET_U_1(bp->bp_htype);
+ bp_hlen = GET_U_1(bp->bp_hlen);
+ if (bp_htype == 1 && bp_hlen == MAC_ADDR_LEN && bp_op == BOOTPREQUEST) {
+ ND_PRINT(" from %s", GET_ETHERADDR_STRING(bp->bp_chaddr));
+ }
+
+ ND_PRINT(", length %u", length);
+
+ if (!ndo->ndo_vflag)
+ return;
+
+ ND_TCHECK_2(bp->bp_secs);
+
+ /* The usual hardware address type is 1 (10Mb Ethernet) */
+ if (bp_htype != 1)
+ ND_PRINT(", htype %u", bp_htype);
+
+ /* The usual length for 10Mb Ethernet address is 6 bytes */
+ if (bp_htype != 1 || bp_hlen != MAC_ADDR_LEN)
+ ND_PRINT(", hlen %u", bp_hlen);
+
+ /* Only print interesting fields */
+ if (GET_U_1(bp->bp_hops))
+ ND_PRINT(", hops %u", GET_U_1(bp->bp_hops));
+ if (GET_BE_U_4(bp->bp_xid))
+ ND_PRINT(", xid 0x%x", GET_BE_U_4(bp->bp_xid));
+ if (GET_BE_U_2(bp->bp_secs))
+ ND_PRINT(", secs %u", GET_BE_U_2(bp->bp_secs));
+
+ ND_PRINT(", Flags [%s]",
+ bittok2str(bootp_flag_values, "none", GET_BE_U_2(bp->bp_flags)));
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT(" (0x%04x)", GET_BE_U_2(bp->bp_flags));
+
+ /* Client's ip address */
+ if (GET_IPV4_TO_NETWORK_ORDER(bp->bp_ciaddr))
+ ND_PRINT("\n\t Client-IP %s", GET_IPADDR_STRING(bp->bp_ciaddr));
+
+ /* 'your' ip address (bootp client) */
+ if (GET_IPV4_TO_NETWORK_ORDER(bp->bp_yiaddr))
+ ND_PRINT("\n\t Your-IP %s", GET_IPADDR_STRING(bp->bp_yiaddr));
+
+ /* Server's ip address */
+ if (GET_IPV4_TO_NETWORK_ORDER(bp->bp_siaddr))
+ ND_PRINT("\n\t Server-IP %s", GET_IPADDR_STRING(bp->bp_siaddr));
+
+ /* Gateway's ip address */
+ if (GET_IPV4_TO_NETWORK_ORDER(bp->bp_giaddr))
+ ND_PRINT("\n\t Gateway-IP %s", GET_IPADDR_STRING(bp->bp_giaddr));
+
+ /* Client's Ethernet address */
+ if (bp_htype == 1 && bp_hlen == MAC_ADDR_LEN) {
+ ND_PRINT("\n\t Client-Ethernet-Address %s", GET_ETHERADDR_STRING(bp->bp_chaddr));
+ }
+
+ if (GET_U_1(bp->bp_sname)) { /* get first char only */
+ ND_PRINT("\n\t sname \"");
+ if (nd_printztn(ndo, bp->bp_sname, (u_int)sizeof(bp->bp_sname),
+ ndo->ndo_snapend) == 0) {
+ ND_PRINT("\"");
+ nd_print_trunc(ndo);
+ return;
+ }
+ ND_PRINT("\"");
+ }
+ if (GET_U_1(bp->bp_file)) { /* get first char only */
+ ND_PRINT("\n\t file \"");
+ if (nd_printztn(ndo, bp->bp_file, (u_int)sizeof(bp->bp_file),
+ ndo->ndo_snapend) == 0) {
+ ND_PRINT("\"");
+ nd_print_trunc(ndo);
+ return;
+ }
+ ND_PRINT("\"");
+ }
+
+ /* Decode the vendor buffer */
+ ND_TCHECK_4(bp->bp_vend);
+ if (memcmp((const char *)bp->bp_vend, vm_rfc1048,
+ sizeof(uint32_t)) == 0)
+ rfc1048_print(ndo, bp->bp_vend);
+ else if (memcmp((const char *)bp->bp_vend, vm_cmu,
+ sizeof(uint32_t)) == 0)
+ cmu_print(ndo, bp->bp_vend);
+ else {
+ uint32_t ul;
+
+ ul = GET_BE_U_4(bp->bp_vend);
+ if (ul != 0)
+ ND_PRINT("\n\t Vendor-#0x%x", ul);
+ }
+
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+/*
+ * The first character specifies the format to print:
+ * i - ip address (32 bits)
+ * p - ip address pairs (32 bits + 32 bits)
+ * l - long (32 bits)
+ * L - unsigned long (32 bits)
+ * s - short (16 bits)
+ * b - period-separated decimal bytes (variable length)
+ * x - colon-separated hex bytes (variable length)
+ * a - ASCII string (variable length)
+ * B - on/off (8 bits)
+ * $ - special (explicit code to handle)
+ */
+static const struct tok tag2str[] = {
+/* RFC1048 tags */
+ { TAG_PAD, " PAD" },
+ { TAG_SUBNET_MASK, "iSubnet-Mask" }, /* subnet mask (RFC950) */
+ { TAG_TIME_OFFSET, "LTime-Zone" }, /* seconds from UTC */
+ { TAG_GATEWAY, "iDefault-Gateway" }, /* default gateway */
+ { TAG_TIME_SERVER, "iTime-Server" }, /* time servers (RFC868) */
+ { TAG_NAME_SERVER, "iIEN-Name-Server" }, /* IEN name servers (IEN116) */
+ { TAG_DOMAIN_SERVER, "iDomain-Name-Server" }, /* domain name (RFC1035) */
+ { TAG_LOG_SERVER, "iLOG" }, /* MIT log servers */
+ { TAG_COOKIE_SERVER, "iCS" }, /* cookie servers (RFC865) */
+ { TAG_LPR_SERVER, "iLPR-Server" }, /* lpr server (RFC1179) */
+ { TAG_IMPRESS_SERVER, "iIM" }, /* impress servers (Imagen) */
+ { TAG_RLP_SERVER, "iRL" }, /* resource location (RFC887) */
+ { TAG_HOSTNAME, "aHostname" }, /* ASCII hostname */
+ { TAG_BOOTSIZE, "sBS" }, /* 512 byte blocks */
+ { TAG_END, " END" },
+/* RFC1497 tags */
+ { TAG_DUMPPATH, "aDP" },
+ { TAG_DOMAINNAME, "aDomain-Name" },
+ { TAG_SWAP_SERVER, "iSS" },
+ { TAG_ROOTPATH, "aRP" },
+ { TAG_EXTPATH, "aEP" },
+/* RFC2132 tags */
+ { TAG_IP_FORWARD, "BIPF" },
+ { TAG_NL_SRCRT, "BSRT" },
+ { TAG_PFILTERS, "pPF" },
+ { TAG_REASS_SIZE, "sRSZ" },
+ { TAG_DEF_TTL, "bTTL" },
+ { TAG_MTU_TIMEOUT, "lMTU-Timeout" },
+ { TAG_MTU_TABLE, "sMTU-Table" },
+ { TAG_INT_MTU, "sMTU" },
+ { TAG_LOCAL_SUBNETS, "BLSN" },
+ { TAG_BROAD_ADDR, "iBR" },
+ { TAG_DO_MASK_DISC, "BMD" },
+ { TAG_SUPPLY_MASK, "BMS" },
+ { TAG_DO_RDISC, "BRouter-Discovery" },
+ { TAG_RTR_SOL_ADDR, "iRSA" },
+ { TAG_STATIC_ROUTE, "pStatic-Route" },
+ { TAG_USE_TRAILERS, "BUT" },
+ { TAG_ARP_TIMEOUT, "lAT" },
+ { TAG_ETH_ENCAP, "BIE" },
+ { TAG_TCP_TTL, "bTT" },
+ { TAG_TCP_KEEPALIVE, "lKI" },
+ { TAG_KEEPALIVE_GO, "BKG" },
+ { TAG_NIS_DOMAIN, "aYD" },
+ { TAG_NIS_SERVERS, "iYS" },
+ { TAG_NTP_SERVERS, "iNTP" },
+ { TAG_VENDOR_OPTS, "bVendor-Option" },
+ { TAG_NETBIOS_NS, "iNetbios-Name-Server" },
+ { TAG_NETBIOS_DDS, "iWDD" },
+ { TAG_NETBIOS_NODE, "$Netbios-Node" },
+ { TAG_NETBIOS_SCOPE, "aNetbios-Scope" },
+ { TAG_XWIN_FS, "iXFS" },
+ { TAG_XWIN_DM, "iXDM" },
+ { TAG_NIS_P_DOMAIN, "sN+D" },
+ { TAG_NIS_P_SERVERS, "iN+S" },
+ { TAG_MOBILE_HOME, "iMH" },
+ { TAG_SMPT_SERVER, "iSMTP" },
+ { TAG_POP3_SERVER, "iPOP3" },
+ { TAG_NNTP_SERVER, "iNNTP" },
+ { TAG_WWW_SERVER, "iWWW" },
+ { TAG_FINGER_SERVER, "iFG" },
+ { TAG_IRC_SERVER, "iIRC" },
+ { TAG_STREETTALK_SRVR, "iSTS" },
+ { TAG_STREETTALK_STDA, "iSTDA" },
+ { TAG_REQUESTED_IP, "iRequested-IP" },
+ { TAG_IP_LEASE, "lLease-Time" },
+ { TAG_OPT_OVERLOAD, "$OO" },
+ { TAG_TFTP_SERVER, "aTFTP" },
+ { TAG_BOOTFILENAME, "aBF" },
+ { TAG_DHCP_MESSAGE, " DHCP-Message" },
+ { TAG_SERVER_ID, "iServer-ID" },
+ { TAG_PARM_REQUEST, "bParameter-Request" },
+ { TAG_MESSAGE, "aMSG" },
+ { TAG_MAX_MSG_SIZE, "sMSZ" },
+ { TAG_RENEWAL_TIME, "lRN" },
+ { TAG_REBIND_TIME, "lRB" },
+ { TAG_VENDOR_CLASS, "aVendor-Class" },
+ { TAG_CLIENT_ID, "$Client-ID" },
+/* RFC 2485 */
+ { TAG_OPEN_GROUP_UAP, "aUAP" },
+/* RFC 2563 */
+ { TAG_DISABLE_AUTOCONF, "BNOAUTO" },
+/* RFC 2610 */
+ { TAG_SLP_DA, "bSLP-DA" }, /*"b" is a little wrong */
+ { TAG_SLP_SCOPE, "bSLP-SCOPE" }, /*"b" is a little wrong */
+/* RFC 2937 */
+ { TAG_NS_SEARCH, "sNSSEARCH" }, /* XXX 's' */
+/* RFC 3004 - The User Class Option for DHCP */
+ { TAG_USER_CLASS, "$User-Class" },
+/* RFC 3011 */
+ { TAG_IP4_SUBNET_SELECT, "iSUBNET" },
+/* RFC 3442 */
+ { TAG_CLASSLESS_STATIC_RT, "$Classless-Static-Route" },
+ { TAG_CLASSLESS_STA_RT_MS, "$Classless-Static-Route-Microsoft" },
+/* RFC 5859 - TFTP Server Address Option for DHCPv4 */
+ { TAG_TFTP_SERVER_ADDRESS, "iTFTP-Server-Address" },
+/* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options */
+ { TAG_SLP_NAMING_AUTH, "aSLP-NA" },
+ { TAG_CLIENT_FQDN, "$FQDN" },
+ { TAG_AGENT_CIRCUIT, "$Agent-Information" },
+ { TAG_AGENT_REMOTE, "bARMT" },
+ { TAG_TZ_STRING, "aTZSTR" },
+ { TAG_FQDN_OPTION, "bFQDNS" }, /* XXX 'b' */
+ { TAG_AUTH, "bAUTH" }, /* XXX 'b' */
+ { TAG_CLIENT_LAST_TRANSACTION_TIME, "LLast-Transaction-Time" },
+ { TAG_ASSOCIATED_IP, "iAssociated-IP" },
+ { TAG_CLIENT_ARCH, "sARCH" },
+ { TAG_CLIENT_NDI, "bNDI" }, /* XXX 'b' */
+ { TAG_CLIENT_GUID, "bGUID" }, /* XXX 'b' */
+ { TAG_LDAP_URL, "aLDAP" },
+ { TAG_TZ_PCODE, "aPOSIX-TZ" },
+ { TAG_TZ_TCODE, "aTZ-Name" },
+ { TAG_NETINFO_PARENT, "iNI" },
+ { TAG_NETINFO_PARENT_TAG, "aNITAG" },
+ { TAG_URL, "aURL" },
+ { TAG_MUDURL, "aMUD-URL" },
+ { 0, NULL }
+};
+
+/* DHCP "options overload" types */
+static const struct tok oo2str[] = {
+ { 1, "file" },
+ { 2, "sname" },
+ { 3, "file+sname" },
+ { 0, NULL }
+};
+
+/* NETBIOS over TCP/IP node type options */
+static const struct tok nbo2str[] = {
+ { 0x1, "b-node" },
+ { 0x2, "p-node" },
+ { 0x4, "m-node" },
+ { 0x8, "h-node" },
+ { 0, NULL }
+};
+
+/* ARP Hardware types, for Client-ID option */
+static const struct tok arp2str[] = {
+ { 0x1, "ether" },
+ { 0x6, "ieee802" },
+ { 0x7, "arcnet" },
+ { 0xf, "frelay" },
+ { 0x17, "strip" },
+ { 0x18, "ieee1394" },
+ { 0, NULL }
+};
+
+static const struct tok dhcp_msg_values[] = {
+ { DHCPDISCOVER, "Discover" },
+ { DHCPOFFER, "Offer" },
+ { DHCPREQUEST, "Request" },
+ { DHCPDECLINE, "Decline" },
+ { DHCPACK, "ACK" },
+ { DHCPNAK, "NACK" },
+ { DHCPRELEASE, "Release" },
+ { DHCPINFORM, "Inform" },
+ { DHCPLEASEQUERY, "LeaseQuery" },
+ { DHCPLEASEUNASSIGNED, "LeaseUnassigned" },
+ { DHCPLEASEUNKNOWN, "LeaseUnknown" },
+ { DHCPLEASEACTIVE, "LeaseActive" },
+ { 0, NULL }
+};
+
+#define AGENT_SUBOPTION_CIRCUIT_ID 1 /* RFC 3046 */
+#define AGENT_SUBOPTION_REMOTE_ID 2 /* RFC 3046 */
+#define AGENT_SUBOPTION_SUBSCRIBER_ID 6 /* RFC 3993 */
+static const struct tok agent_suboption_values[] = {
+ { AGENT_SUBOPTION_CIRCUIT_ID, "Circuit-ID" },
+ { AGENT_SUBOPTION_REMOTE_ID, "Remote-ID" },
+ { AGENT_SUBOPTION_SUBSCRIBER_ID, "Subscriber-ID" },
+ { 0, NULL }
+};
+
+
+static void
+rfc1048_print(netdissect_options *ndo,
+ const u_char *bp)
+{
+ uint16_t tag;
+ u_int len;
+ const char *cp;
+ char c;
+ int first, idx;
+ uint8_t subopt, suboptlen;
+
+ ND_PRINT("\n\t Vendor-rfc1048 Extensions");
+
+ /* Step over magic cookie */
+ ND_PRINT("\n\t Magic Cookie 0x%08x", GET_BE_U_4(bp));
+ bp += sizeof(int32_t);
+
+ /* Loop while we there is a tag left in the buffer */
+ while (ND_TTEST_1(bp)) {
+ tag = GET_U_1(bp);
+ bp++;
+ if (tag == TAG_PAD && ndo->ndo_vflag < 3)
+ continue;
+ if (tag == TAG_END && ndo->ndo_vflag < 3)
+ return;
+ cp = tok2str(tag2str, "?Unknown", tag);
+ c = *cp++;
+
+ if (tag == TAG_PAD || tag == TAG_END)
+ len = 0;
+ else {
+ /* Get the length; check for truncation */
+ len = GET_U_1(bp);
+ bp++;
+ }
+
+ ND_PRINT("\n\t %s (%u), length %u%s", cp, tag, len,
+ len > 0 ? ": " : "");
+
+ if (tag == TAG_PAD && ndo->ndo_vflag > 2) {
+ u_int ntag = 1;
+ while (ND_TTEST_1(bp) &&
+ GET_U_1(bp) == TAG_PAD) {
+ bp++;
+ ntag++;
+ }
+ if (ntag > 1)
+ ND_PRINT(", occurs %u", ntag);
+ }
+
+ ND_TCHECK_LEN(bp, len);
+
+ if (tag == TAG_DHCP_MESSAGE && len == 1) {
+ ND_PRINT("%s",
+ tok2str(dhcp_msg_values, "Unknown (%u)", GET_U_1(bp)));
+ bp++;
+ continue;
+ }
+
+ if (tag == TAG_PARM_REQUEST) {
+ idx = 0;
+ while (len > 0) {
+ uint8_t innertag = GET_U_1(bp);
+ bp++;
+ len--;
+ cp = tok2str(tag2str, "?Unknown", innertag);
+ if (idx % 4 == 0)
+ ND_PRINT("\n\t ");
+ else
+ ND_PRINT(", ");
+ ND_PRINT("%s (%u)", cp + 1, innertag);
+ idx++;
+ }
+ continue;
+ }
+
+ /* Print data */
+ if (c == '?') {
+ /* Base default formats for unknown tags on data size */
+ if (len & 1)
+ c = 'b';
+ else if (len & 2)
+ c = 's';
+ else
+ c = 'l';
+ }
+ first = 1;
+ switch (c) {
+
+ case 'a':
+ /* ASCII strings */
+ ND_PRINT("\"");
+ if (nd_printn(ndo, bp, len, ndo->ndo_snapend)) {
+ ND_PRINT("\"");
+ goto trunc;
+ }
+ ND_PRINT("\"");
+ bp += len;
+ len = 0;
+ break;
+
+ case 'i':
+ case 'l':
+ case 'L':
+ /* ip addresses/32-bit words */
+ while (len >= 4) {
+ if (!first)
+ ND_PRINT(",");
+ if (c == 'i')
+ ND_PRINT("%s", GET_IPADDR_STRING(bp));
+ else if (c == 'L')
+ ND_PRINT("%d", GET_BE_S_4(bp));
+ else
+ ND_PRINT("%u", GET_BE_U_4(bp));
+ bp += 4;
+ len -= 4;
+ first = 0;
+ }
+ break;
+
+ case 'p':
+ /* IP address pairs */
+ while (len >= 2*4) {
+ if (!first)
+ ND_PRINT(",");
+ ND_PRINT("(%s:", GET_IPADDR_STRING(bp));
+ bp += 4;
+ len -= 4;
+ ND_PRINT("%s)", GET_IPADDR_STRING(bp));
+ bp += 4;
+ len -= 4;
+ first = 0;
+ }
+ break;
+
+ case 's':
+ /* shorts */
+ while (len >= 2) {
+ if (!first)
+ ND_PRINT(",");
+ ND_PRINT("%u", GET_BE_U_2(bp));
+ bp += 2;
+ len -= 2;
+ first = 0;
+ }
+ break;
+
+ case 'B':
+ /* boolean */
+ while (len > 0) {
+ uint8_t bool_value;
+ if (!first)
+ ND_PRINT(",");
+ bool_value = GET_U_1(bp);
+ switch (bool_value) {
+ case 0:
+ ND_PRINT("N");
+ break;
+ case 1:
+ ND_PRINT("Y");
+ break;
+ default:
+ ND_PRINT("%u?", bool_value);
+ break;
+ }
+ ++bp;
+ --len;
+ first = 0;
+ }
+ break;
+
+ case 'b':
+ case 'x':
+ default:
+ /* Bytes */
+ while (len > 0) {
+ uint8_t byte_value;
+ if (!first)
+ ND_PRINT(c == 'x' ? ":" : ".");
+ byte_value = GET_U_1(bp);
+ if (c == 'x')
+ ND_PRINT("%02x", byte_value);
+ else
+ ND_PRINT("%u", byte_value);
+ ++bp;
+ --len;
+ first = 0;
+ }
+ break;
+
+ case '$':
+ /* Guys we can't handle with one of the usual cases */
+ switch (tag) {
+
+ case TAG_NETBIOS_NODE:
+ /* this option should be at least 1 byte long */
+ if (len < 1) {
+ ND_PRINT("[ERROR: length < 1 bytes]");
+ break;
+ }
+ tag = GET_U_1(bp);
+ ++bp;
+ --len;
+ ND_PRINT("%s", tok2str(nbo2str, NULL, tag));
+ break;
+
+ case TAG_OPT_OVERLOAD:
+ /* this option should be at least 1 byte long */
+ if (len < 1) {
+ ND_PRINT("[ERROR: length < 1 bytes]");
+ break;
+ }
+ tag = GET_U_1(bp);
+ ++bp;
+ --len;
+ ND_PRINT("%s", tok2str(oo2str, NULL, tag));
+ break;
+
+ case TAG_CLIENT_FQDN:
+ /* this option should be at least 3 bytes long */
+ if (len < 3) {
+ ND_PRINT("[ERROR: length < 3 bytes]");
+ bp += len;
+ len = 0;
+ break;
+ }
+ if (GET_U_1(bp) & 0xf0) {
+ ND_PRINT("[ERROR: MBZ nibble 0x%x != 0] ",
+ (GET_U_1(bp) & 0xf0) >> 4);
+ }
+ if (GET_U_1(bp) & 0x0f)
+ ND_PRINT("[%s] ",
+ client_fqdn_flags(GET_U_1(bp)));
+ bp++;
+ if (GET_U_1(bp) || GET_U_1(bp + 1))
+ ND_PRINT("%u/%u ", GET_U_1(bp),
+ GET_U_1(bp + 1));
+ bp += 2;
+ ND_PRINT("\"");
+ if (nd_printn(ndo, bp, len - 3, ndo->ndo_snapend)) {
+ ND_PRINT("\"");
+ goto trunc;
+ }
+ ND_PRINT("\"");
+ bp += len - 3;
+ len = 0;
+ break;
+
+ case TAG_CLIENT_ID:
+ {
+ int type;
+
+ /* this option should be at least 1 byte long */
+ if (len < 1) {
+ ND_PRINT("[ERROR: length < 1 bytes]");
+ break;
+ }
+ type = GET_U_1(bp);
+ bp++;
+ len--;
+ if (type == 0) {
+ ND_PRINT("\"");
+ if (nd_printn(ndo, bp, len, ndo->ndo_snapend)) {
+ ND_PRINT("\"");
+ goto trunc;
+ }
+ ND_PRINT("\"");
+ bp += len;
+ len = 0;
+ break;
+ } else {
+ ND_PRINT("%s ", tok2str(arp2str, "hardware-type %u,", type));
+ while (len > 0) {
+ if (!first)
+ ND_PRINT(":");
+ ND_PRINT("%02x", GET_U_1(bp));
+ ++bp;
+ --len;
+ first = 0;
+ }
+ }
+ break;
+ }
+
+ case TAG_AGENT_CIRCUIT:
+ while (len >= 2) {
+ subopt = GET_U_1(bp);
+ suboptlen = GET_U_1(bp + 1);
+ bp += 2;
+ len -= 2;
+ if (suboptlen > len) {
+ ND_PRINT("\n\t %s SubOption %u, length %u: length goes past end of option",
+ tok2str(agent_suboption_values, "Unknown", subopt),
+ subopt,
+ suboptlen);
+ bp += len;
+ len = 0;
+ break;
+ }
+ ND_PRINT("\n\t %s SubOption %u, length %u: ",
+ tok2str(agent_suboption_values, "Unknown", subopt),
+ subopt,
+ suboptlen);
+ switch (subopt) {
+
+ case AGENT_SUBOPTION_CIRCUIT_ID: /* fall through */
+ case AGENT_SUBOPTION_REMOTE_ID:
+ case AGENT_SUBOPTION_SUBSCRIBER_ID:
+ if (nd_printn(ndo, bp, suboptlen, ndo->ndo_snapend))
+ goto trunc;
+ break;
+
+ default:
+ print_unknown_data(ndo, bp, "\n\t\t", suboptlen);
+ }
+
+ len -= suboptlen;
+ bp += suboptlen;
+ }
+ break;
+
+ case TAG_CLASSLESS_STATIC_RT:
+ case TAG_CLASSLESS_STA_RT_MS:
+ {
+ u_int mask_width, significant_octets, i;
+
+ /* this option should be at least 5 bytes long */
+ if (len < 5) {
+ ND_PRINT("[ERROR: length < 5 bytes]");
+ bp += len;
+ len = 0;
+ break;
+ }
+ while (len > 0) {
+ if (!first)
+ ND_PRINT(",");
+ mask_width = GET_U_1(bp);
+ bp++;
+ len--;
+ /* mask_width <= 32 */
+ if (mask_width > 32) {
+ ND_PRINT("[ERROR: Mask width (%u) > 32]", mask_width);
+ bp += len;
+ len = 0;
+ break;
+ }
+ significant_octets = (mask_width + 7) / 8;
+ /* significant octets + router(4) */
+ if (len < significant_octets + 4) {
+ ND_PRINT("[ERROR: Remaining length (%u) < %u bytes]", len, significant_octets + 4);
+ bp += len;
+ len = 0;
+ break;
+ }
+ ND_PRINT("(");
+ if (mask_width == 0)
+ ND_PRINT("default");
+ else {
+ for (i = 0; i < significant_octets ; i++) {
+ if (i > 0)
+ ND_PRINT(".");
+ ND_PRINT("%u",
+ GET_U_1(bp));
+ bp++;
+ }
+ for (i = significant_octets ; i < 4 ; i++)
+ ND_PRINT(".0");
+ ND_PRINT("/%u", mask_width);
+ }
+ ND_PRINT(":%s)", GET_IPADDR_STRING(bp));
+ bp += 4;
+ len -= (significant_octets + 4);
+ first = 0;
+ }
+ break;
+ }
+
+ case TAG_USER_CLASS:
+ {
+ u_int suboptnumber = 1;
+
+ first = 1;
+ if (len < 2) {
+ ND_PRINT("[ERROR: length < 2 bytes]");
+ bp += len;
+ len = 0;
+ break;
+ }
+ while (len > 0) {
+ suboptlen = GET_U_1(bp);
+ bp++;
+ len--;
+ ND_PRINT("\n\t ");
+ ND_PRINT("instance#%u: ", suboptnumber);
+ if (suboptlen == 0) {
+ ND_PRINT("[ERROR: suboption length must be non-zero]");
+ bp += len;
+ len = 0;
+ break;
+ }
+ if (len < suboptlen) {
+ ND_PRINT("[ERROR: invalid option]");
+ bp += len;
+ len = 0;
+ break;
+ }
+ ND_PRINT("\"");
+ if (nd_printn(ndo, bp, suboptlen, ndo->ndo_snapend)) {
+ ND_PRINT("\"");
+ goto trunc;
+ }
+ ND_PRINT("\"");
+ ND_PRINT(", length %u", suboptlen);
+ suboptnumber++;
+ len -= suboptlen;
+ bp += suboptlen;
+ }
+ break;
+ }
+
+ default:
+ ND_PRINT("[unknown special tag %u, size %u]",
+ tag, len);
+ bp += len;
+ len = 0;
+ break;
+ }
+ break;
+ }
+ /* Data left over? */
+ if (len) {
+ ND_PRINT("\n\t trailing data length %u", len);
+ bp += len;
+ }
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+#define PRINTCMUADDR(m, s) { ND_TCHECK_4(cmu->m); \
+ if (GET_IPV4_TO_NETWORK_ORDER(cmu->m) != 0) \
+ ND_PRINT(" %s:%s", s, GET_IPADDR_STRING(cmu->m)); }
+
+static void
+cmu_print(netdissect_options *ndo,
+ const u_char *bp)
+{
+ const struct cmu_vend *cmu;
+ uint8_t v_flags;
+
+ ND_PRINT(" vend-cmu");
+ cmu = (const struct cmu_vend *)bp;
+
+ /* Only print if there are unknown bits */
+ ND_TCHECK_4(cmu->v_flags);
+ v_flags = GET_U_1(cmu->v_flags);
+ if ((v_flags & ~(VF_SMASK)) != 0)
+ ND_PRINT(" F:0x%x", v_flags);
+ PRINTCMUADDR(v_dgate, "DG");
+ PRINTCMUADDR(v_smask, v_flags & VF_SMASK ? "SM" : "SM*");
+ PRINTCMUADDR(v_dns1, "NS1");
+ PRINTCMUADDR(v_dns2, "NS2");
+ PRINTCMUADDR(v_ins1, "IEN1");
+ PRINTCMUADDR(v_ins2, "IEN2");
+ PRINTCMUADDR(v_ts1, "TS1");
+ PRINTCMUADDR(v_ts2, "TS2");
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+#undef PRINTCMUADDR
+
+static char *
+client_fqdn_flags(u_int flags)
+{
+ static char buf[8+1];
+ int i = 0;
+
+ if (flags & CLIENT_FQDN_FLAGS_S)
+ buf[i++] = 'S';
+ if (flags & CLIENT_FQDN_FLAGS_O)
+ buf[i++] = 'O';
+ if (flags & CLIENT_FQDN_FLAGS_E)
+ buf[i++] = 'E';
+ if (flags & CLIENT_FQDN_FLAGS_N)
+ buf[i++] = 'N';
+ buf[i] = '\0';
+
+ return buf;
+}
diff --git a/print-brcmtag.c b/print-brcmtag.c
new file mode 100644
index 0000000..efc6a1e
--- /dev/null
+++ b/print-brcmtag.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Broadcom Ethernet switches tag (4 bytes) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "ethertype.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#define ETHER_TYPE_LEN 2
+
+#define BRCM_TAG_LEN 4
+#define BRCM_OPCODE_SHIFT 5
+#define BRCM_OPCODE_MASK 0x7
+
+/* Ingress fields */
+#define BRCM_IG_TC_SHIFT 2
+#define BRCM_IG_TC_MASK 0x7
+#define BRCM_IG_TE_MASK 0x3
+#define BRCM_IG_TS_SHIFT 7
+#define BRCM_IG_DSTMAP_MASK 0x1ff
+
+/* Egress fields */
+#define BRCM_EG_CID_MASK 0xff
+#define BRCM_EG_RC_MASK 0xff
+#define BRCM_EG_RC_RSVD (3 << 6)
+#define BRCM_EG_RC_EXCEPTION (1 << 5)
+#define BRCM_EG_RC_PROT_SNOOP (1 << 4)
+#define BRCM_EG_RC_PROT_TERM (1 << 3)
+#define BRCM_EG_RC_SWITCH (1 << 2)
+#define BRCM_EG_RC_MAC_LEARN (1 << 1)
+#define BRCM_EG_RC_MIRROR (1 << 0)
+#define BRCM_EG_TC_SHIFT 5
+#define BRCM_EG_TC_MASK 0x7
+#define BRCM_EG_PID_MASK 0x1f
+
+static const struct tok brcm_tag_te_values[] = {
+ { 0, "None" },
+ { 1, "Untag" },
+ { 2, "Header"},
+ { 3, "Reserved" },
+ { 0, NULL }
+};
+
+static const struct tok brcm_tag_rc_values[] = {
+ { 1, "mirror" },
+ { 2, "MAC learning" },
+ { 4, "switching" },
+ { 8, "prot term" },
+ { 16, "prot snoop" },
+ { 32, "exception" },
+ { 0, NULL }
+};
+
+static void
+brcm_tag_print(netdissect_options *ndo, const u_char *bp)
+{
+ uint8_t tag[BRCM_TAG_LEN];
+ uint16_t dst_map;
+ unsigned int i;
+
+ for (i = 0; i < BRCM_TAG_LEN; i++)
+ tag[i] = GET_U_1(bp + i);
+
+ ND_PRINT("BRCM tag OP: %s", tag[0] ? "IG" : "EG");
+ if (tag[0] & (1 << BRCM_OPCODE_SHIFT)) {
+ /* Ingress Broadcom tag */
+ ND_PRINT(", TC: %d", (tag[1] >> BRCM_IG_TC_SHIFT) &
+ BRCM_IG_TC_MASK);
+ ND_PRINT(", TE: %s",
+ tok2str(brcm_tag_te_values, "unknown",
+ (tag[1] & BRCM_IG_TE_MASK)));
+ ND_PRINT(", TS: %d", tag[1] >> BRCM_IG_TS_SHIFT);
+ dst_map = (uint16_t)tag[2] << 8 | tag[3];
+ ND_PRINT(", DST map: 0x%04x", dst_map & BRCM_IG_DSTMAP_MASK);
+ } else {
+ /* Egress Broadcom tag */
+ ND_PRINT(", CID: %d", tag[1]);
+ ND_PRINT(", RC: %s", tok2str(brcm_tag_rc_values,
+ "reserved", tag[2]));
+ ND_PRINT(", TC: %d", (tag[3] >> BRCM_EG_TC_SHIFT) &
+ BRCM_EG_TC_MASK);
+ ND_PRINT(", port: %d", tag[3] & BRCM_EG_PID_MASK);
+ }
+ ND_PRINT(", ");
+}
+
+void
+brcm_tag_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h,
+ const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+
+ ndo->ndo_protocol = "brcm-tag";
+ ndo->ndo_ll_hdr_len +=
+ ether_switch_tag_print(ndo, p, length, caplen,
+ brcm_tag_print, BRCM_TAG_LEN);
+}
+
+void
+brcm_tag_prepend_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h,
+ const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+
+ ndo->ndo_protocol = "brcm-tag-prepend";
+ ND_TCHECK_LEN(p, BRCM_TAG_LEN);
+ ndo->ndo_ll_hdr_len += BRCM_TAG_LEN;
+
+ if (ndo->ndo_eflag) {
+ /* Print the prepended Broadcom tag. */
+ brcm_tag_print(ndo, p);
+ }
+ p += BRCM_TAG_LEN;
+ length -= BRCM_TAG_LEN;
+ caplen -= BRCM_TAG_LEN;
+
+ /*
+ * Now print the Ethernet frame following it.
+ */
+ ndo->ndo_ll_hdr_len +=
+ ether_print(ndo, p, length, caplen, NULL, NULL);
+}
diff --git a/print-bt.c b/print-bt.c
new file mode 100644
index 0000000..131bc71
--- /dev/null
+++ b/print-bt.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2007
+ * paolo.abeni@email.it All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by Paolo Abeni.''
+ * The name of author may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Bluetooth printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+#ifdef DLT_BLUETOOTH_HCI_H4_WITH_PHDR
+
+/*
+ * Header prepended by libpcap to each bluetooth h4 frame;
+ * the direction field is in network byte order.
+ */
+typedef struct _bluetooth_h4_header {
+ nd_uint32_t direction; /* if first bit is set direction is incoming */
+} bluetooth_h4_header;
+
+#define BT_HDRLEN sizeof(bluetooth_h4_header)
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the bluetooth header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+bt_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+ const bluetooth_h4_header* hdr = (const bluetooth_h4_header*)p;
+
+ ndo->ndo_protocol = "bluetooth";
+ nd_print_protocol(ndo);
+ ND_TCHECK_LEN(p, BT_HDRLEN);
+ ndo->ndo_ll_hdr_len += BT_HDRLEN;
+ caplen -= BT_HDRLEN;
+ length -= BT_HDRLEN;
+ p += BT_HDRLEN;
+ if (ndo->ndo_eflag)
+ ND_PRINT(", hci length %u, direction %s", length,
+ (GET_BE_U_4(hdr->direction)&0x1) ? "in" : "out");
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+}
+#endif
diff --git a/print-calm-fast.c b/print-calm-fast.c
new file mode 100644
index 0000000..1220d86
--- /dev/null
+++ b/print-calm-fast.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Ola Martin Lykkja (ola.lykkja@q-free.com)
+ */
+
+/* \summary: Communication access for land mobiles (CALM) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+/*
+ ISO 29281:2009
+ Intelligent Transport Systems . Communications access for land mobiles (CALM)
+ CALM non-IP networking
+*/
+
+/*
+ * This is the top level routine of the printer. 'bp' points
+ * to the calm header of the packet.
+ */
+void
+calm_fast_print(netdissect_options *ndo, const u_char *bp, u_int length, const struct lladdr_info *src)
+{
+ ndo->ndo_protocol = "calm_fast";
+
+ ND_PRINT("CALM FAST");
+ if (src != NULL)
+ ND_PRINT(" src:%s", (src->addr_string)(ndo, src->addr));
+ ND_PRINT("; ");
+
+ if (length < 2) {
+ ND_PRINT(" (length %u < 2)", length);
+ goto invalid;
+ }
+
+ ND_PRINT("SrcNwref:%u; ", GET_U_1(bp));
+ length -= 1;
+ bp += 1;
+
+ ND_PRINT("DstNwref:%u; ", GET_U_1(bp));
+ length -= 1;
+ bp += 1;
+
+ if (ndo->ndo_vflag)
+ ND_DEFAULTPRINT(bp, length);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(bp, length);
+}
diff --git a/print-carp.c b/print-carp.c
new file mode 100644
index 0000000..75f5066
--- /dev/null
+++ b/print-carp.c
@@ -0,0 +1,78 @@
+/* $OpenBSD: print-carp.c,v 1.6 2009/10/27 23:59:55 deraadt Exp $ */
+
+/*
+ * Copyright (c) 2000 William C. Fenner.
+ * All rights reserved.
+ *
+ * Kevin Steves <ks@hp.se> July 2000
+ * Modified to:
+ * - print version, type string and packet length
+ * - print IP address count if > 1 (-v)
+ * - verify checksum (-v)
+ * - print authentication string (-v)
+ *
+ * Copyright (c) 2011 Advanced Computing Technologies
+ * George V. Neille-Neil
+ *
+ * Modified to:
+ * - work correctly with CARP
+ * - compile into the latest tcpdump
+ * - print out the counter
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * The name of William C. Fenner may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/* \summary: Common Address Redundancy Protocol (CARP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h" /* for checksum structure and functions */
+#include "extract.h"
+
+void
+carp_print(netdissect_options *ndo, const u_char *bp, u_int len, u_int ttl)
+{
+ u_int version, type;
+ const char *type_s;
+
+ ndo->ndo_protocol = "carp";
+ version = (GET_U_1(bp) & 0xf0) >> 4;
+ type = GET_U_1(bp) & 0x0f;
+ if (type == 1)
+ type_s = "advertise";
+ else
+ type_s = "unknown";
+ ND_PRINT("CARPv%u-%s %u: ", version, type_s, len);
+ if (ttl != 255)
+ ND_PRINT("[ttl=%u!] ", ttl);
+ if (version != 2 || type != 1)
+ return;
+ ND_PRINT("vhid=%u advbase=%u advskew=%u authlen=%u ",
+ GET_U_1(bp + 1), GET_U_1(bp + 5), GET_U_1(bp + 2),
+ GET_U_1(bp + 3));
+ if (ndo->ndo_vflag) {
+ struct cksum_vec vec[1];
+ vec[0].ptr = (const uint8_t *)bp;
+ vec[0].len = len;
+ if (ND_TTEST_LEN(bp, len) && in_cksum(vec, 1))
+ ND_PRINT(" (bad carp cksum %x!)",
+ GET_BE_U_2(bp + 6));
+ }
+ ND_PRINT("counter=%" PRIu64, GET_BE_U_8(bp + 8));
+}
diff --git a/print-cdp.c b/print-cdp.c
new file mode 100644
index 0000000..9f5c24b
--- /dev/null
+++ b/print-cdp.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Code by Gert Doering, SpaceNet GmbH, gert@space.net
+ *
+ * Reference documentation:
+ * https://web.archive.org/web/20000914194913/http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.pdf
+ */
+
+/* \summary: Cisco Discovery Protocol (CDP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+#include "nlpid.h"
+
+
+#define CDP_HEADER_LEN 4
+#define CDP_HEADER_VERSION_OFFSET 0
+#define CDP_HEADER_TTL_OFFSET 1
+#define CDP_HEADER_CHECKSUM_OFFSET 2
+
+#define CDP_TLV_HEADER_LEN 4
+#define CDP_TLV_TYPE_OFFSET 0
+#define CDP_TLV_LEN_OFFSET 2
+
+static const struct tok cdp_capability_values[] = {
+ { 0x01, "Router" },
+ { 0x02, "Transparent Bridge" },
+ { 0x04, "Source Route Bridge" },
+ { 0x08, "L2 Switch" },
+ { 0x10, "L3 capable" },
+ { 0x20, "IGMP snooping" },
+ { 0x40, "L1 capable" },
+ { 0, NULL }
+};
+
+static void cdp_print_addr(netdissect_options *, const u_char *, u_int);
+static void cdp_print_prefixes(netdissect_options *, const u_char *, u_int);
+
+static void
+cdp_print_string(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ ND_PRINT("'");
+ (void)nd_printn(ndo, cp, len, NULL);
+ ND_PRINT("'");
+}
+
+static void
+cdp_print_power(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ u_int val = 0;
+
+ switch (len) {
+ case 1:
+ val = GET_U_1(cp);
+ break;
+ case 2:
+ val = GET_BE_U_2(cp);
+ break;
+ case 3:
+ val = GET_BE_U_3(cp);
+ break;
+ }
+ ND_PRINT("%1.2fW", val / 1000.0);
+}
+
+static void
+cdp_print_capability(netdissect_options *ndo,
+ const u_char *cp, const u_int len _U_)
+{
+ uint32_t val = GET_BE_U_4(cp);
+
+ ND_PRINT("(0x%08x): %s", val,
+ bittok2str(cdp_capability_values, "none", val));
+}
+
+/* Rework the version string to get a nice indentation. */
+static void
+cdp_print_version(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ unsigned i;
+
+ ND_PRINT("\n\t ");
+ for (i = 0; i < len; i++) {
+ u_char c = GET_U_1(cp + i);
+
+ if (c == '\n')
+ ND_PRINT("\n\t ");
+ else
+ fn_print_char(ndo, c);
+ }
+}
+
+static void
+cdp_print_uint16(netdissect_options *ndo,
+ const u_char *cp, const u_int len _U_)
+{
+ ND_PRINT("%u", GET_BE_U_2(cp));
+}
+
+static void
+cdp_print_duplex(netdissect_options *ndo,
+ const u_char *cp, const u_int len _U_)
+{
+ ND_PRINT("%s", GET_U_1(cp) ? "full": "half");
+}
+
+/* https://www.cisco.com/c/en/us/td/docs/voice_ip_comm/cata/186/2_12_m/english/release/notes/186rn21m.html
+* plus more details from other sources
+*
+* There are apparently versions of the request with both
+* 2 bytes and 3 bytes of value. The 3 bytes of value
+* appear to be a 1-byte application type followed by a
+* 2-byte VLAN ID; the 2 bytes of value are unknown
+* (they're 0x20 0x00 in some captures I've seen; that
+* is not a valid VLAN ID, as VLAN IDs are 12 bits).
+*
+* The replies all appear to be 3 bytes long.
+*/
+static void
+cdp_print_ata186(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ if (len == 2)
+ ND_PRINT("unknown 0x%04x", GET_BE_U_2(cp));
+ else
+ ND_PRINT("app %u, vlan %u", GET_U_1(cp), GET_BE_U_2(cp + 1));
+}
+
+static void
+cdp_print_mtu(netdissect_options *ndo,
+ const u_char *cp, const u_int len _U_)
+{
+ ND_PRINT("%u bytes", GET_BE_U_4(cp));
+}
+
+static void
+cdp_print_uint8x(netdissect_options *ndo,
+ const u_char *cp, const u_int len _U_)
+{
+ ND_PRINT("0x%02x", GET_U_1(cp));
+}
+
+static void
+cdp_print_phys_loc(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ ND_PRINT("0x%02x", GET_U_1(cp));
+ if (len > 1) {
+ ND_PRINT("/");
+ (void)nd_printn(ndo, cp + 1, len - 1, NULL);
+ }
+}
+
+struct cdp_tlvinfo {
+ const char *name;
+ void (*printer)(netdissect_options *ndo, const u_char *, u_int);
+ int min_len, max_len;
+};
+
+#define T_DEV_ID 0x01
+#define T_MAX 0x17
+static const struct cdp_tlvinfo cdptlvs[T_MAX + 1] = {
+ /* 0x00 */
+ [ T_DEV_ID ] = { "Device-ID", cdp_print_string, -1, -1 },
+ [ 0x02 ] = { "Address", cdp_print_addr, -1, -1 },
+ [ 0x03 ] = { "Port-ID", cdp_print_string, -1, -1 },
+ [ 0x04 ] = { "Capability", cdp_print_capability, 4, 4 },
+ [ 0x05 ] = { "Version String", cdp_print_version, -1, -1 },
+ [ 0x06 ] = { "Platform", cdp_print_string, -1, -1 },
+ [ 0x07 ] = { "Prefixes", cdp_print_prefixes, -1, -1 },
+ /* not documented */
+ [ 0x08 ] = { "Protocol-Hello option", NULL, -1, -1 },
+ /* CDPv2 */
+ [ 0x09 ] = { "VTP Management Domain", cdp_print_string, -1, -1 },
+ /* CDPv2 */
+ [ 0x0a ] = { "Native VLAN ID", cdp_print_uint16, 2, 2 },
+ /* CDPv2 */
+ [ 0x0b ] = { "Duplex", cdp_print_duplex, 1, 1 },
+ /* 0x0c */
+ /* 0x0d */
+ /* incomplete doc. */
+ [ 0x0e ] = { "ATA-186 VoIP VLAN assignment", cdp_print_ata186, 3, 3 },
+ /* incomplete doc. */
+ [ 0x0f ] = { "ATA-186 VoIP VLAN request", cdp_print_ata186, 2, 3 },
+ /* not documented */
+ [ 0x10 ] = { "power consumption", cdp_print_power, 1, 3 },
+ /* not documented */
+ [ 0x11 ] = { "MTU", cdp_print_mtu, 4, 4 },
+ /* not documented */
+ [ 0x12 ] = { "AVVID trust bitmap", cdp_print_uint8x, 1, 1 },
+ /* not documented */
+ [ 0x13 ] = { "AVVID untrusted ports CoS", cdp_print_uint8x, 1, 1 },
+ /* not documented */
+ [ 0x14 ] = { "System Name", cdp_print_string, -1, -1 },
+ /* not documented */
+ [ 0x15 ] = { "System Object ID (not decoded)", NULL, -1, -1 },
+ [ 0x16 ] = { "Management Addresses", cdp_print_addr, 4, -1 },
+ /* not documented */
+ [ 0x17 ] = { "Physical Location", cdp_print_phys_loc, 1, -1 },
+};
+
+void
+cdp_print(netdissect_options *ndo,
+ const u_char *tptr, u_int length)
+{
+ u_int orig_length = length;
+ uint16_t checksum;
+
+ ndo->ndo_protocol = "cdp";
+
+ if (length < CDP_HEADER_LEN) {
+ ND_PRINT(" (packet length %u < %u)", length, CDP_HEADER_LEN);
+ goto invalid;
+ }
+ ND_PRINT("CDPv%u, ttl: %us",
+ GET_U_1(tptr + CDP_HEADER_VERSION_OFFSET),
+ GET_U_1(tptr + CDP_HEADER_TTL_OFFSET));
+ checksum = GET_BE_U_2(tptr + CDP_HEADER_CHECKSUM_OFFSET);
+ if (ndo->ndo_vflag)
+ ND_PRINT(", checksum: 0x%04x (unverified), length %u",
+ checksum, orig_length);
+ tptr += CDP_HEADER_LEN;
+ length -= CDP_HEADER_LEN;
+
+ while (length) {
+ u_int type, len;
+ const struct cdp_tlvinfo *info;
+ const char *name;
+ u_char covered = 0;
+
+ if (length < CDP_TLV_HEADER_LEN) {
+ ND_PRINT(" (remaining packet length %u < %u)",
+ length, CDP_TLV_HEADER_LEN);
+ goto invalid;
+ }
+ type = GET_BE_U_2(tptr + CDP_TLV_TYPE_OFFSET);
+ len = GET_BE_U_2(tptr + CDP_TLV_LEN_OFFSET); /* object length includes the 4 bytes header length */
+ info = type <= T_MAX ? &cdptlvs[type] : NULL;
+ name = (info && info->name) ? info->name : "unknown field type";
+ if (len < CDP_TLV_HEADER_LEN) {
+ if (ndo->ndo_vflag)
+ ND_PRINT("\n\t%s (0x%02x), TLV length: %u byte%s (too short)",
+ name, type, len, PLURAL_SUFFIX(len));
+ else
+ ND_PRINT(", %s TLV length %u too short",
+ name, len);
+ goto invalid;
+ }
+ if (len > length) {
+ ND_PRINT(" (TLV length %u > %u)", len, length);
+ goto invalid;
+ }
+ tptr += CDP_TLV_HEADER_LEN;
+ length -= CDP_TLV_HEADER_LEN;
+ len -= CDP_TLV_HEADER_LEN;
+
+ /* In non-verbose mode just print Device-ID. */
+ if (!ndo->ndo_vflag && type == T_DEV_ID)
+ ND_PRINT(", Device-ID ");
+ else if (ndo->ndo_vflag)
+ ND_PRINT("\n\t%s (0x%02x), value length: %u byte%s: ",
+ name, type, len, PLURAL_SUFFIX(len));
+
+ if (info) {
+ if ((info->min_len > 0 && len < (unsigned)info->min_len) ||
+ (info->max_len > 0 && len > (unsigned)info->max_len))
+ ND_PRINT(" (malformed TLV)");
+ else if (ndo->ndo_vflag || type == T_DEV_ID) {
+ if (info->printer)
+ info->printer(ndo, tptr, len);
+ else
+ ND_TCHECK_LEN(tptr, len);
+ /*
+ * When the type is defined without a printer,
+ * do not print the hex dump.
+ */
+ covered = 1;
+ }
+ }
+
+ if (!covered) {
+ ND_TCHECK_LEN(tptr, len);
+ print_unknown_data(ndo, tptr, "\n\t ", len);
+ }
+ tptr += len;
+ length -= len;
+ }
+ if (ndo->ndo_vflag < 1)
+ ND_PRINT(", length %u", orig_length);
+
+ return;
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(tptr, length);
+}
+
+/*
+ * Protocol type values.
+ *
+ * PT_NLPID means that the protocol type field contains an OSI NLPID.
+ *
+ * PT_IEEE_802_2 means that the protocol type field contains an IEEE 802.2
+ * LLC header that specifies that the payload is for that protocol.
+ */
+#define PT_NLPID 1 /* OSI NLPID */
+#define PT_IEEE_802_2 2 /* IEEE 802.2 LLC header */
+
+static void
+cdp_print_addr(netdissect_options *ndo,
+ const u_char * p, u_int l)
+{
+ u_int num;
+ static const u_char prot_ipv6[] = {
+ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x86, 0xdd
+ };
+
+ if (l < 4) {
+ ND_PRINT(" (not enough space for num)");
+ goto invalid;
+ }
+ num = GET_BE_U_4(p);
+ p += 4;
+ l -= 4;
+
+ while (num) {
+ u_int pt, pl, al;
+
+ if (l < 2) {
+ ND_PRINT(" (not enough space for PT+PL)");
+ goto invalid;
+ }
+ pt = GET_U_1(p); /* type of "protocol" field */
+ pl = GET_U_1(p + 1); /* length of "protocol" field */
+ p += 2;
+ l -= 2;
+
+ if (l < pl + 2) {
+ ND_PRINT(" (not enough space for P+AL)");
+ goto invalid;
+ }
+ /* Skip the protocol for now. */
+ al = GET_BE_U_2(p + pl); /* address length */
+
+ if (pt == PT_NLPID && pl == 1 && GET_U_1(p) == NLPID_IP &&
+ al == 4) {
+ /*
+ * IPv4: protocol type = NLPID, protocol length = 1
+ * (1-byte NLPID), protocol = 0xcc (NLPID for IPv4),
+ * address length = 4
+ */
+ p += pl + 2;
+ l -= pl + 2;
+ /* p is just beyond al now. */
+ if (l < al) {
+ ND_PRINT(" (not enough space for A)");
+ goto invalid;
+ }
+ ND_PRINT("IPv4 (%u) %s", num, GET_IPADDR_STRING(p));
+ p += al;
+ l -= al;
+ }
+ else if (pt == PT_IEEE_802_2 && pl == 8 &&
+ memcmp(p, prot_ipv6, 8) == 0 && al == 16) {
+ /*
+ * IPv6: protocol type = IEEE 802.2 header,
+ * protocol length = 8 (size of LLC+SNAP header),
+ * protocol = LLC+SNAP header with the IPv6
+ * Ethertype, address length = 16
+ */
+ p += pl + 2;
+ l -= pl + 2;
+ /* p is just beyond al now. */
+ if (l < al) {
+ ND_PRINT(" (not enough space for A)");
+ goto invalid;
+ }
+ ND_PRINT("IPv6 (%u) %s", num, GET_IP6ADDR_STRING(p));
+ p += al;
+ l -= al;
+ }
+ else {
+ /*
+ * Generic case: just print raw data
+ */
+ ND_PRINT("pt=0x%02x, pl=%u, pb=", pt, pl);
+ while (pl != 0) {
+ ND_PRINT(" %02x", GET_U_1(p));
+ p++;
+ l--;
+ pl--;
+ }
+ ND_PRINT(", al=%u, a=", al);
+ p += 2;
+ l -= 2;
+ /* p is just beyond al now. */
+ if (l < al) {
+ ND_PRINT(" (not enough space for A)");
+ goto invalid;
+ }
+ while (al != 0) {
+ ND_PRINT(" %02x", GET_U_1(p));
+ p++;
+ l--;
+ al--;
+ }
+ }
+ num--;
+ if (num)
+ ND_PRINT(" ");
+ }
+ if (l)
+ ND_PRINT(" (%u bytes of stray data)", l);
+ return;
+
+invalid:
+ ND_TCHECK_LEN(p, l);
+}
+
+static void
+cdp_print_prefixes(netdissect_options *ndo,
+ const u_char * p, u_int l)
+{
+ if (l % 5) {
+ ND_PRINT(" [length %u is not a multiple of 5]", l);
+ goto invalid;
+ }
+
+ ND_PRINT(" IPv4 Prefixes (%u):", l / 5);
+
+ while (l > 0) {
+ ND_PRINT(" %u.%u.%u.%u/%u",
+ GET_U_1(p), GET_U_1(p + 1), GET_U_1(p + 2),
+ GET_U_1(p + 3), GET_U_1(p + 4));
+ l -= 5;
+ p += 5;
+ }
+ return;
+
+invalid:
+ ND_TCHECK_LEN(p, l);
+}
diff --git a/print-cfm.c b/print-cfm.c
new file mode 100644
index 0000000..e950719
--- /dev/null
+++ b/print-cfm.c
@@ -0,0 +1,766 @@
+/*
+ * Copyright (c) 1998-2006 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* \summary: IEEE 802.1ag Connectivity Fault Management (CFM) protocols printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "oui.h"
+#include "af.h"
+
+
+struct cfm_common_header_t {
+ nd_uint8_t mdlevel_version;
+ nd_uint8_t opcode;
+ nd_uint8_t flags;
+ nd_uint8_t first_tlv_offset;
+};
+
+#define CFM_VERSION 0
+#define CFM_EXTRACT_VERSION(x) ((x)&0x1f)
+#define CFM_EXTRACT_MD_LEVEL(x) (((x)&0xe0)>>5)
+
+#define CFM_OPCODE_CCM 1
+#define CFM_OPCODE_LBR 2
+#define CFM_OPCODE_LBM 3
+#define CFM_OPCODE_LTR 4
+#define CFM_OPCODE_LTM 5
+
+static const struct tok cfm_opcode_values[] = {
+ { CFM_OPCODE_CCM, "Continuity Check Message"},
+ { CFM_OPCODE_LBR, "Loopback Reply"},
+ { CFM_OPCODE_LBM, "Loopback Message"},
+ { CFM_OPCODE_LTR, "Linktrace Reply"},
+ { CFM_OPCODE_LTM, "Linktrace Message"},
+ { 0, NULL}
+};
+
+/*
+ * Message Formats.
+ */
+struct cfm_ccm_t {
+ nd_uint32_t sequence;
+ nd_uint16_t ma_epi;
+ nd_byte names[48];
+ nd_byte itu_t_y_1731[16];
+};
+
+/*
+ * Timer Bases for the CCM Interval field.
+ * Expressed in units of seconds.
+ */
+static const float ccm_interval_base[8] = {0.0f, 0.003333f, 0.01f, 0.1f, 1.0f, 10.0f, 60.0f, 600.0f};
+#define CCM_INTERVAL_MIN_MULTIPLIER 3.25
+#define CCM_INTERVAL_MAX_MULTIPLIER 3.5
+
+#define CFM_CCM_RDI_FLAG 0x80
+#define CFM_EXTRACT_CCM_INTERVAL(x) ((x)&0x07)
+
+#define CFM_CCM_MD_FORMAT_8021 0
+#define CFM_CCM_MD_FORMAT_NONE 1
+#define CFM_CCM_MD_FORMAT_DNS 2
+#define CFM_CCM_MD_FORMAT_MAC 3
+#define CFM_CCM_MD_FORMAT_CHAR 4
+
+static const struct tok cfm_md_nameformat_values[] = {
+ { CFM_CCM_MD_FORMAT_8021, "IEEE 802.1"},
+ { CFM_CCM_MD_FORMAT_NONE, "No MD Name present"},
+ { CFM_CCM_MD_FORMAT_DNS, "DNS string"},
+ { CFM_CCM_MD_FORMAT_MAC, "MAC + 16Bit Integer"},
+ { CFM_CCM_MD_FORMAT_CHAR, "Character string"},
+ { 0, NULL}
+};
+
+#define CFM_CCM_MA_FORMAT_8021 0
+#define CFM_CCM_MA_FORMAT_VID 1
+#define CFM_CCM_MA_FORMAT_CHAR 2
+#define CFM_CCM_MA_FORMAT_INT 3
+#define CFM_CCM_MA_FORMAT_VPN 4
+
+static const struct tok cfm_ma_nameformat_values[] = {
+ { CFM_CCM_MA_FORMAT_8021, "IEEE 802.1"},
+ { CFM_CCM_MA_FORMAT_VID, "Primary VID"},
+ { CFM_CCM_MA_FORMAT_CHAR, "Character string"},
+ { CFM_CCM_MA_FORMAT_INT, "16Bit Integer"},
+ { CFM_CCM_MA_FORMAT_VPN, "RFC2685 VPN-ID"},
+ { 0, NULL}
+};
+
+struct cfm_lbm_t {
+ nd_uint32_t transaction_id;
+};
+
+struct cfm_ltm_t {
+ nd_uint32_t transaction_id;
+ nd_uint8_t ttl;
+ nd_mac_addr original_mac;
+ nd_mac_addr target_mac;
+};
+
+static const struct tok cfm_ltm_flag_values[] = {
+ { 0x80, "Use Forwarding-DB only"},
+ { 0, NULL}
+};
+
+struct cfm_ltr_t {
+ nd_uint32_t transaction_id;
+ nd_uint8_t ttl;
+ nd_uint8_t replay_action;
+};
+
+static const struct tok cfm_ltr_flag_values[] = {
+ { 0x80, "UseFDB Only"},
+ { 0x40, "FwdYes"},
+ { 0x20, "Terminal MEP"},
+ { 0, NULL}
+};
+
+static const struct tok cfm_ltr_replay_action_values[] = {
+ { 1, "Exact Match"},
+ { 2, "Filtering DB"},
+ { 3, "MIP CCM DB"},
+ { 0, NULL}
+};
+
+
+#define CFM_TLV_END 0
+#define CFM_TLV_SENDER_ID 1
+#define CFM_TLV_PORT_STATUS 2
+#define CFM_TLV_INTERFACE_STATUS 3
+#define CFM_TLV_DATA 4
+#define CFM_TLV_REPLY_INGRESS 5
+#define CFM_TLV_REPLY_EGRESS 6
+#define CFM_TLV_PRIVATE 31
+
+static const struct tok cfm_tlv_values[] = {
+ { CFM_TLV_END, "End"},
+ { CFM_TLV_SENDER_ID, "Sender ID"},
+ { CFM_TLV_PORT_STATUS, "Port status"},
+ { CFM_TLV_INTERFACE_STATUS, "Interface status"},
+ { CFM_TLV_DATA, "Data"},
+ { CFM_TLV_REPLY_INGRESS, "Reply Ingress"},
+ { CFM_TLV_REPLY_EGRESS, "Reply Egress"},
+ { CFM_TLV_PRIVATE, "Organization Specific"},
+ { 0, NULL}
+};
+
+/*
+ * TLVs
+ */
+
+struct cfm_tlv_header_t {
+ nd_uint8_t type;
+ nd_uint16_t length;
+};
+
+/* FIXME define TLV formats */
+
+static const struct tok cfm_tlv_port_status_values[] = {
+ { 1, "Blocked"},
+ { 2, "Up"},
+ { 0, NULL}
+};
+
+static const struct tok cfm_tlv_interface_status_values[] = {
+ { 1, "Up"},
+ { 2, "Down"},
+ { 3, "Testing"},
+ { 5, "Dormant"},
+ { 6, "not present"},
+ { 7, "lower Layer down"},
+ { 0, NULL}
+};
+
+#define CFM_CHASSIS_ID_CHASSIS_COMPONENT 1
+#define CFM_CHASSIS_ID_INTERFACE_ALIAS 2
+#define CFM_CHASSIS_ID_PORT_COMPONENT 3
+#define CFM_CHASSIS_ID_MAC_ADDRESS 4
+#define CFM_CHASSIS_ID_NETWORK_ADDRESS 5
+#define CFM_CHASSIS_ID_INTERFACE_NAME 6
+#define CFM_CHASSIS_ID_LOCAL 7
+
+static const struct tok cfm_tlv_senderid_chassisid_values[] = {
+ { 0, "Reserved"},
+ { CFM_CHASSIS_ID_CHASSIS_COMPONENT, "Chassis component"},
+ { CFM_CHASSIS_ID_INTERFACE_ALIAS, "Interface alias"},
+ { CFM_CHASSIS_ID_PORT_COMPONENT, "Port component"},
+ { CFM_CHASSIS_ID_MAC_ADDRESS, "MAC address"},
+ { CFM_CHASSIS_ID_NETWORK_ADDRESS, "Network address"},
+ { CFM_CHASSIS_ID_INTERFACE_NAME, "Interface name"},
+ { CFM_CHASSIS_ID_LOCAL, "Locally assigned"},
+ { 0, NULL}
+};
+
+
+static int
+cfm_network_addr_print(netdissect_options *ndo,
+ const u_char *tptr, const u_int length)
+{
+ u_int network_addr_type;
+ u_int hexdump = FALSE;
+
+ /*
+ * Although AFIs are typically 2 octets wide,
+ * 802.1ab specifies that this field width
+ * is only one octet.
+ */
+ if (length < 1) {
+ ND_PRINT("\n\t Network Address Type (invalid, no data");
+ return hexdump;
+ }
+ /* The calling function must make any due ND_TCHECK calls. */
+ network_addr_type = GET_U_1(tptr);
+ ND_PRINT("\n\t Network Address Type %s (%u)",
+ tok2str(af_values, "Unknown", network_addr_type),
+ network_addr_type);
+
+ /*
+ * Resolve the passed in Address.
+ */
+ switch(network_addr_type) {
+ case AFNUM_INET:
+ if (length != 1 + 4) {
+ ND_PRINT("(invalid IPv4 address length %u)", length - 1);
+ hexdump = TRUE;
+ break;
+ }
+ ND_PRINT(", %s", GET_IPADDR_STRING(tptr + 1));
+ break;
+
+ case AFNUM_INET6:
+ if (length != 1 + 16) {
+ ND_PRINT("(invalid IPv6 address length %u)", length - 1);
+ hexdump = TRUE;
+ break;
+ }
+ ND_PRINT(", %s", GET_IP6ADDR_STRING(tptr + 1));
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ return hexdump;
+}
+
+void
+cfm_print(netdissect_options *ndo,
+ const u_char *pptr, u_int length)
+{
+ const struct cfm_common_header_t *cfm_common_header;
+ uint8_t mdlevel_version, opcode, flags, first_tlv_offset;
+ const struct cfm_tlv_header_t *cfm_tlv_header;
+ const uint8_t *tptr, *tlv_ptr;
+ const uint8_t *namesp;
+ u_int names_data_remaining;
+ uint8_t md_nameformat, md_namelength;
+ const uint8_t *md_name;
+ uint8_t ma_nameformat, ma_namelength;
+ const uint8_t *ma_name;
+ u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval;
+
+
+ union {
+ const struct cfm_ccm_t *cfm_ccm;
+ const struct cfm_lbm_t *cfm_lbm;
+ const struct cfm_ltm_t *cfm_ltm;
+ const struct cfm_ltr_t *cfm_ltr;
+ } msg_ptr;
+
+ ndo->ndo_protocol = "cfm";
+ tptr=pptr;
+ cfm_common_header = (const struct cfm_common_header_t *)pptr;
+ if (length < sizeof(*cfm_common_header))
+ goto tooshort;
+ ND_TCHECK_SIZE(cfm_common_header);
+
+ /*
+ * Sanity checking of the header.
+ */
+ mdlevel_version = GET_U_1(cfm_common_header->mdlevel_version);
+ if (CFM_EXTRACT_VERSION(mdlevel_version) != CFM_VERSION) {
+ ND_PRINT("CFMv%u not supported, length %u",
+ CFM_EXTRACT_VERSION(mdlevel_version), length);
+ return;
+ }
+
+ opcode = GET_U_1(cfm_common_header->opcode);
+ ND_PRINT("CFMv%u %s, MD Level %u, length %u",
+ CFM_EXTRACT_VERSION(mdlevel_version),
+ tok2str(cfm_opcode_values, "unknown (%u)", opcode),
+ CFM_EXTRACT_MD_LEVEL(mdlevel_version),
+ length);
+
+ /*
+ * In non-verbose mode just print the opcode and md-level.
+ */
+ if (ndo->ndo_vflag < 1) {
+ return;
+ }
+
+ flags = GET_U_1(cfm_common_header->flags);
+ first_tlv_offset = GET_U_1(cfm_common_header->first_tlv_offset);
+ ND_PRINT("\n\tFirst TLV offset %u", first_tlv_offset);
+
+ tptr += sizeof(struct cfm_common_header_t);
+ tlen = length - sizeof(struct cfm_common_header_t);
+
+ /*
+ * Sanity check the first TLV offset.
+ */
+ if (first_tlv_offset > tlen) {
+ ND_PRINT(" (too large, must be <= %u)", tlen);
+ return;
+ }
+
+ switch (opcode) {
+ case CFM_OPCODE_CCM:
+ msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr;
+ if (first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) {
+ ND_PRINT(" (too small 1, must be >= %zu)",
+ sizeof(*msg_ptr.cfm_ccm));
+ return;
+ }
+ if (tlen < sizeof(*msg_ptr.cfm_ccm))
+ goto tooshort;
+ ND_TCHECK_SIZE(msg_ptr.cfm_ccm);
+
+ ccm_interval = CFM_EXTRACT_CCM_INTERVAL(flags);
+ ND_PRINT(", Flags [CCM Interval %u%s]",
+ ccm_interval,
+ flags & CFM_CCM_RDI_FLAG ?
+ ", RDI" : "");
+
+ /*
+ * Resolve the CCM interval field.
+ */
+ if (ccm_interval) {
+ ND_PRINT("\n\t CCM Interval %.3fs"
+ ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs",
+ ccm_interval_base[ccm_interval],
+ ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER,
+ ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER);
+ }
+
+ ND_PRINT("\n\t Sequence Number 0x%08x, MA-End-Point-ID 0x%04x",
+ GET_BE_U_4(msg_ptr.cfm_ccm->sequence),
+ GET_BE_U_2(msg_ptr.cfm_ccm->ma_epi));
+
+ namesp = msg_ptr.cfm_ccm->names;
+ names_data_remaining = sizeof(msg_ptr.cfm_ccm->names);
+
+ /*
+ * Resolve the MD fields.
+ */
+ md_nameformat = GET_U_1(namesp);
+ namesp++;
+ names_data_remaining--; /* We know this is != 0 */
+ if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) {
+ md_namelength = GET_U_1(namesp);
+ namesp++;
+ names_data_remaining--; /* We know this is !=0 */
+ ND_PRINT("\n\t MD Name Format %s (%u), MD Name length %u",
+ tok2str(cfm_md_nameformat_values, "Unknown",
+ md_nameformat),
+ md_nameformat,
+ md_namelength);
+
+ /*
+ * -3 for the MA short name format and length and one byte
+ * of MA short name.
+ */
+ if (md_namelength > names_data_remaining - 3) {
+ ND_PRINT(" (too large, must be <= %u)", names_data_remaining - 2);
+ return;
+ }
+
+ md_name = namesp;
+ ND_PRINT("\n\t MD Name: ");
+ switch (md_nameformat) {
+ case CFM_CCM_MD_FORMAT_DNS:
+ case CFM_CCM_MD_FORMAT_CHAR:
+ nd_printjnp(ndo, md_name, md_namelength);
+ break;
+
+ case CFM_CCM_MD_FORMAT_MAC:
+ if (md_namelength == MAC_ADDR_LEN) {
+ ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(md_name));
+ } else {
+ ND_PRINT("\n\t MAC (length invalid)");
+ }
+ break;
+
+ /* FIXME add printers for those MD formats - hexdump for now */
+ case CFM_CCM_MA_FORMAT_8021:
+ default:
+ print_unknown_data(ndo, md_name, "\n\t ",
+ md_namelength);
+ }
+ namesp += md_namelength;
+ names_data_remaining -= md_namelength;
+ } else {
+ ND_PRINT("\n\t MD Name Format %s (%u)",
+ tok2str(cfm_md_nameformat_values, "Unknown",
+ md_nameformat),
+ md_nameformat);
+ }
+
+
+ /*
+ * Resolve the MA fields.
+ */
+ ma_nameformat = GET_U_1(namesp);
+ namesp++;
+ names_data_remaining--; /* We know this is != 0 */
+ ma_namelength = GET_U_1(namesp);
+ namesp++;
+ names_data_remaining--; /* We know this is != 0 */
+ ND_PRINT("\n\t MA Name-Format %s (%u), MA name length %u",
+ tok2str(cfm_ma_nameformat_values, "Unknown",
+ ma_nameformat),
+ ma_nameformat,
+ ma_namelength);
+
+ if (ma_namelength > names_data_remaining) {
+ ND_PRINT(" (too large, must be <= %u)", names_data_remaining);
+ return;
+ }
+
+ ma_name = namesp;
+ ND_PRINT("\n\t MA Name: ");
+ switch (ma_nameformat) {
+ case CFM_CCM_MA_FORMAT_CHAR:
+ nd_printjnp(ndo, ma_name, ma_namelength);
+ break;
+
+ /* FIXME add printers for those MA formats - hexdump for now */
+ case CFM_CCM_MA_FORMAT_8021:
+ case CFM_CCM_MA_FORMAT_VID:
+ case CFM_CCM_MA_FORMAT_INT:
+ case CFM_CCM_MA_FORMAT_VPN:
+ default:
+ print_unknown_data(ndo, ma_name, "\n\t ", ma_namelength);
+ }
+ break;
+
+ case CFM_OPCODE_LTM:
+ msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr;
+ if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) {
+ ND_PRINT(" (too small 4, must be >= %zu)",
+ sizeof(*msg_ptr.cfm_ltm));
+ return;
+ }
+ if (tlen < sizeof(*msg_ptr.cfm_ltm))
+ goto tooshort;
+ ND_TCHECK_SIZE(msg_ptr.cfm_ltm);
+
+ ND_PRINT(", Flags [%s]",
+ bittok2str(cfm_ltm_flag_values, "none", flags));
+
+ ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u",
+ GET_BE_U_4(msg_ptr.cfm_ltm->transaction_id),
+ GET_U_1(msg_ptr.cfm_ltm->ttl));
+
+ ND_PRINT("\n\t Original-MAC %s, Target-MAC %s",
+ GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->original_mac),
+ GET_ETHERADDR_STRING(msg_ptr.cfm_ltm->target_mac));
+ break;
+
+ case CFM_OPCODE_LTR:
+ msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr;
+ if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) {
+ ND_PRINT(" (too small 5, must be >= %zu)",
+ sizeof(*msg_ptr.cfm_ltr));
+ return;
+ }
+ if (tlen < sizeof(*msg_ptr.cfm_ltr))
+ goto tooshort;
+ ND_TCHECK_SIZE(msg_ptr.cfm_ltr);
+
+ ND_PRINT(", Flags [%s]",
+ bittok2str(cfm_ltr_flag_values, "none", flags));
+
+ ND_PRINT("\n\t Transaction-ID 0x%08x, ttl %u",
+ GET_BE_U_4(msg_ptr.cfm_ltr->transaction_id),
+ GET_U_1(msg_ptr.cfm_ltr->ttl));
+
+ ND_PRINT("\n\t Replay-Action %s (%u)",
+ tok2str(cfm_ltr_replay_action_values,
+ "Unknown",
+ GET_U_1(msg_ptr.cfm_ltr->replay_action)),
+ GET_U_1(msg_ptr.cfm_ltr->replay_action));
+ break;
+
+ /*
+ * No message decoder yet.
+ * Hexdump everything up until the start of the TLVs
+ */
+ case CFM_OPCODE_LBR:
+ case CFM_OPCODE_LBM:
+ default:
+ print_unknown_data(ndo, tptr, "\n\t ",
+ tlen - first_tlv_offset);
+ break;
+ }
+
+ tptr += first_tlv_offset;
+ tlen -= first_tlv_offset;
+
+ while (tlen > 0) {
+ cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr;
+
+ /* Enough to read the tlv type ? */
+ cfm_tlv_type = GET_U_1(cfm_tlv_header->type);
+
+ ND_PRINT("\n\t%s TLV (0x%02x)",
+ tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type),
+ cfm_tlv_type);
+
+ if (cfm_tlv_type == CFM_TLV_END) {
+ /* Length is "Not present if the Type field is 0." */
+ return;
+ }
+
+ /* do we have the full tlv header ? */
+ if (tlen < sizeof(struct cfm_tlv_header_t))
+ goto tooshort;
+ ND_TCHECK_LEN(tptr, sizeof(struct cfm_tlv_header_t));
+ cfm_tlv_len=GET_BE_U_2(cfm_tlv_header->length);
+
+ ND_PRINT(", length %u", cfm_tlv_len);
+
+ tptr += sizeof(struct cfm_tlv_header_t);
+ tlen -= sizeof(struct cfm_tlv_header_t);
+ tlv_ptr = tptr;
+
+ /* do we have the full tlv ? */
+ if (tlen < cfm_tlv_len)
+ goto tooshort;
+ ND_TCHECK_LEN(tptr, cfm_tlv_len);
+ hexdump = FALSE;
+
+ switch(cfm_tlv_type) {
+ case CFM_TLV_PORT_STATUS:
+ if (cfm_tlv_len < 1) {
+ ND_PRINT(" (too short, must be >= 1)");
+ return;
+ }
+ ND_PRINT(", Status: %s (%u)",
+ tok2str(cfm_tlv_port_status_values, "Unknown", GET_U_1(tptr)),
+ GET_U_1(tptr));
+ break;
+
+ case CFM_TLV_INTERFACE_STATUS:
+ if (cfm_tlv_len < 1) {
+ ND_PRINT(" (too short, must be >= 1)");
+ return;
+ }
+ ND_PRINT(", Status: %s (%u)",
+ tok2str(cfm_tlv_interface_status_values, "Unknown", GET_U_1(tptr)),
+ GET_U_1(tptr));
+ break;
+
+ case CFM_TLV_PRIVATE:
+ if (cfm_tlv_len < 4) {
+ ND_PRINT(" (too short, must be >= 4)");
+ return;
+ }
+ ND_PRINT(", Vendor: %s (%u), Sub-Type %u",
+ tok2str(oui_values,"Unknown", GET_BE_U_3(tptr)),
+ GET_BE_U_3(tptr),
+ GET_U_1(tptr + 3));
+ hexdump = TRUE;
+ break;
+
+ case CFM_TLV_SENDER_ID:
+ {
+ u_int chassis_id_type, chassis_id_length;
+ u_int mgmt_addr_length;
+
+ if (cfm_tlv_len < 1) {
+ ND_PRINT(" (too short, must be >= 1)");
+ goto next_tlv;
+ }
+
+ /*
+ * Get the Chassis ID length and check it.
+ * IEEE 802.1Q-2014 Section 21.5.3.1
+ */
+ chassis_id_length = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+ cfm_tlv_len--;
+
+ if (chassis_id_length) {
+ /*
+ * IEEE 802.1Q-2014 Section 21.5.3.2: Chassis ID Subtype, references
+ * IEEE 802.1AB-2005 Section 9.5.2.2, subsequently
+ * IEEE 802.1AB-2016 Section 8.5.2.2: chassis ID subtype
+ */
+ if (cfm_tlv_len < 1) {
+ ND_PRINT("\n\t (TLV too short)");
+ goto next_tlv;
+ }
+ chassis_id_type = GET_U_1(tptr);
+ cfm_tlv_len--;
+ ND_PRINT("\n\t Chassis-ID Type %s (%u), Chassis-ID length %u",
+ tok2str(cfm_tlv_senderid_chassisid_values,
+ "Unknown",
+ chassis_id_type),
+ chassis_id_type,
+ chassis_id_length);
+
+ if (cfm_tlv_len < chassis_id_length) {
+ ND_PRINT("\n\t (TLV too short)");
+ goto next_tlv;
+ }
+
+ /* IEEE 802.1Q-2014 Section 21.5.3.3: Chassis ID */
+ switch (chassis_id_type) {
+ case CFM_CHASSIS_ID_MAC_ADDRESS:
+ if (chassis_id_length != MAC_ADDR_LEN) {
+ ND_PRINT(" (invalid MAC address length)");
+ hexdump = TRUE;
+ break;
+ }
+ ND_PRINT("\n\t MAC %s", GET_ETHERADDR_STRING(tptr + 1));
+ break;
+
+ case CFM_CHASSIS_ID_NETWORK_ADDRESS:
+ hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length);
+ break;
+
+ case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */
+ case CFM_CHASSIS_ID_INTERFACE_ALIAS:
+ case CFM_CHASSIS_ID_LOCAL:
+ case CFM_CHASSIS_ID_CHASSIS_COMPONENT:
+ case CFM_CHASSIS_ID_PORT_COMPONENT:
+ nd_printjnp(ndo, tptr + 1, chassis_id_length);
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+ cfm_tlv_len -= chassis_id_length;
+
+ tptr += 1 + chassis_id_length;
+ tlen -= 1 + chassis_id_length;
+ }
+
+ /*
+ * Check if there is a Management Address.
+ * IEEE 802.1Q-2014 Section 21.5.3.4: Management Address Domain Length
+ * This and all subsequent fields are not present if the TLV length
+ * allows only the above fields.
+ */
+ if (cfm_tlv_len == 0) {
+ /* No, there isn't; we're done. */
+ break;
+ }
+
+ /* Here mgmt_addr_length stands for the management domain length. */
+ mgmt_addr_length = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+ cfm_tlv_len--;
+ ND_PRINT("\n\t Management Address Domain Length %u", mgmt_addr_length);
+ if (mgmt_addr_length) {
+ /* IEEE 802.1Q-2014 Section 21.5.3.5: Management Address Domain */
+ if (cfm_tlv_len < mgmt_addr_length) {
+ ND_PRINT("\n\t (TLV too short)");
+ goto next_tlv;
+ }
+ cfm_tlv_len -= mgmt_addr_length;
+ /*
+ * XXX - this is an OID; print it as such.
+ */
+ hex_print(ndo, "\n\t Management Address Domain: ", tptr, mgmt_addr_length);
+ tptr += mgmt_addr_length;
+ tlen -= mgmt_addr_length;
+
+ /*
+ * IEEE 802.1Q-2014 Section 21.5.3.6: Management Address Length
+ * This field is present if Management Address Domain Length is not 0.
+ */
+ if (cfm_tlv_len < 1) {
+ ND_PRINT(" (Management Address Length is missing)");
+ hexdump = TRUE;
+ break;
+ }
+
+ /* Here mgmt_addr_length stands for the management address length. */
+ mgmt_addr_length = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+ cfm_tlv_len--;
+ ND_PRINT("\n\t Management Address Length %u", mgmt_addr_length);
+ if (mgmt_addr_length) {
+ /* IEEE 802.1Q-2014 Section 21.5.3.7: Management Address */
+ if (cfm_tlv_len < mgmt_addr_length) {
+ ND_PRINT("\n\t (TLV too short)");
+ return;
+ }
+ cfm_tlv_len -= mgmt_addr_length;
+ /*
+ * XXX - this is a TransportDomain; print it as such.
+ */
+ hex_print(ndo, "\n\t Management Address: ", tptr, mgmt_addr_length);
+ tptr += mgmt_addr_length;
+ tlen -= mgmt_addr_length;
+ }
+ }
+ break;
+ }
+
+ /*
+ * FIXME those are the defined TLVs that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case CFM_TLV_DATA:
+ case CFM_TLV_REPLY_INGRESS:
+ case CFM_TLV_REPLY_EGRESS:
+ default:
+ hexdump = TRUE;
+ break;
+ }
+ /* do we want to see an additional hexdump ? */
+ if (hexdump || ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, tlv_ptr, "\n\t ", cfm_tlv_len);
+
+next_tlv:
+ tptr+=cfm_tlv_len;
+ tlen-=cfm_tlv_len;
+ }
+ return;
+
+tooshort:
+ ND_PRINT("\n\t\t packet is too short");
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-chdlc.c b/print-chdlc.c
new file mode 100644
index 0000000..235675d
--- /dev/null
+++ b/print-chdlc.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Cisco HDLC printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+#include "extract.h"
+#include "chdlc.h"
+#include "nlpid.h"
+
+static void chdlc_slarp_print(netdissect_options *, const u_char *, u_int);
+
+static const struct tok chdlc_cast_values[] = {
+ { CHDLC_UNICAST, "unicast" },
+ { CHDLC_BCAST, "bcast" },
+ { 0, NULL}
+};
+
+
+/* Standard CHDLC printer */
+void
+chdlc_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "chdlc";
+ ndo->ndo_ll_hdr_len += chdlc_print(ndo, p, h->len);
+}
+
+u_int
+chdlc_print(netdissect_options *ndo, const u_char *p, u_int length)
+{
+ u_int proto;
+ const u_char *bp = p;
+
+ ndo->ndo_protocol = "chdlc";
+ if (length < CHDLC_HDRLEN)
+ goto trunc;
+ proto = GET_BE_U_2(p + 2);
+ if (ndo->ndo_eflag) {
+ ND_PRINT("%s, ethertype %s (0x%04x), length %u: ",
+ tok2str(chdlc_cast_values, "0x%02x", GET_U_1(p)),
+ tok2str(ethertype_values, "Unknown", proto),
+ proto,
+ length);
+ }
+
+ length -= CHDLC_HDRLEN;
+ p += CHDLC_HDRLEN;
+
+ switch (proto) {
+ case ETHERTYPE_IP:
+ ip_print(ndo, p, length);
+ break;
+ case ETHERTYPE_IPV6:
+ ip6_print(ndo, p, length);
+ break;
+ case CHDLC_TYPE_SLARP:
+ chdlc_slarp_print(ndo, p, length);
+ break;
+ case ETHERTYPE_MPLS:
+ case ETHERTYPE_MPLS_MULTI:
+ mpls_print(ndo, p, length);
+ break;
+ case ETHERTYPE_ISO:
+ /* is the fudge byte set ? lets verify by spotting ISO headers */
+ if (length < 2)
+ goto trunc;
+ if (GET_U_1(p + 1) == NLPID_CLNP ||
+ GET_U_1(p + 1) == NLPID_ESIS ||
+ GET_U_1(p + 1) == NLPID_ISIS)
+ isoclns_print(ndo, p + 1, length - 1);
+ else
+ isoclns_print(ndo, p, length);
+ break;
+ default:
+ if (!ndo->ndo_eflag)
+ ND_PRINT("unknown CHDLC protocol (0x%04x)", proto);
+ break;
+ }
+
+ return (CHDLC_HDRLEN);
+
+trunc:
+ nd_print_trunc(ndo);
+ return (ND_BYTES_AVAILABLE_AFTER(bp));
+}
+
+/*
+ * The fixed-length portion of a SLARP packet.
+ */
+struct cisco_slarp {
+ nd_uint32_t code;
+#define SLARP_REQUEST 0
+#define SLARP_REPLY 1
+#define SLARP_KEEPALIVE 2
+ union {
+ struct {
+ uint8_t addr[4];
+ uint8_t mask[4];
+ } addr;
+ struct {
+ nd_uint32_t myseq;
+ nd_uint32_t yourseq;
+ nd_uint16_t rel;
+ } keep;
+ } un;
+};
+
+#define SLARP_MIN_LEN 14
+#define SLARP_MAX_LEN 18
+
+static void
+chdlc_slarp_print(netdissect_options *ndo, const u_char *cp, u_int length)
+{
+ const struct cisco_slarp *slarp;
+ u_int sec,min,hrs,days;
+
+ ndo->ndo_protocol = "chdlc_slarp";
+ ND_PRINT("SLARP (length: %u), ",length);
+ if (length < SLARP_MIN_LEN)
+ goto trunc;
+
+ slarp = (const struct cisco_slarp *)cp;
+ ND_TCHECK_LEN(slarp, SLARP_MIN_LEN);
+ switch (GET_BE_U_4(slarp->code)) {
+ case SLARP_REQUEST:
+ ND_PRINT("request");
+ /*
+ * At least according to William "Chops" Westfield's
+ * message in
+ *
+ * https://web.archive.org/web/20190725151313/www.nethelp.no/net/cisco-hdlc.txt
+ *
+ * the address and mask aren't used in requests -
+ * they're just zero.
+ */
+ break;
+ case SLARP_REPLY:
+ ND_PRINT("reply %s/%s",
+ GET_IPADDR_STRING(slarp->un.addr.addr),
+ GET_IPADDR_STRING(slarp->un.addr.mask));
+ break;
+ case SLARP_KEEPALIVE:
+ ND_PRINT("keepalive: mineseen=0x%08x, yourseen=0x%08x, reliability=0x%04x",
+ GET_BE_U_4(slarp->un.keep.myseq),
+ GET_BE_U_4(slarp->un.keep.yourseq),
+ GET_BE_U_2(slarp->un.keep.rel));
+
+ if (length >= SLARP_MAX_LEN) { /* uptime-stamp is optional */
+ cp += SLARP_MIN_LEN;
+ sec = GET_BE_U_4(cp) / 1000;
+ min = sec / 60; sec -= min * 60;
+ hrs = min / 60; min -= hrs * 60;
+ days = hrs / 24; hrs -= days * 24;
+ ND_PRINT(", link uptime=%ud%uh%um%us",days,hrs,min,sec);
+ }
+ break;
+ default:
+ ND_PRINT("0x%02x unknown", GET_BE_U_4(slarp->code));
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo,cp+4,"\n\t",length-4);
+ break;
+ }
+
+ if (SLARP_MAX_LEN < length && ndo->ndo_vflag)
+ ND_PRINT(", (trailing junk: %u bytes)", length - SLARP_MAX_LEN);
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo,cp+4,"\n\t",length-4);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-cip.c b/print-cip.c
new file mode 100644
index 0000000..b8ef77f
--- /dev/null
+++ b/print-cip.c
@@ -0,0 +1,87 @@
+/*
+ * Marko Kiiskila carnil@cs.tut.fi
+ *
+ * Tampere University of Technology - Telecommunications Laboratory
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ *
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ *
+ */
+
+/* \summary: Linux Classical IP over ATM printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+
+static const unsigned char rfcllc[] = {
+ 0xaa, /* DSAP: non-ISO */
+ 0xaa, /* SSAP: non-ISO */
+ 0x03, /* Ctrl: Unnumbered Information Command PDU */
+ 0x00, /* OUI: EtherType */
+ 0x00,
+ 0x00 };
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the LLC/SNAP or raw header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+cip_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ int llc_hdrlen;
+
+ ndo->ndo_protocol = "cip";
+
+ if (ndo->ndo_eflag)
+ /*
+ * There is no MAC-layer header, so just print the length.
+ */
+ ND_PRINT("%u: ", length);
+
+ ND_TCHECK_LEN(p, sizeof(rfcllc));
+ if (memcmp(rfcllc, p, sizeof(rfcllc)) == 0) {
+ /*
+ * LLC header is present. Try to print it & higher layers.
+ */
+ llc_hdrlen = llc_print(ndo, p, length, caplen, NULL, NULL);
+ if (llc_hdrlen < 0) {
+ /* packet type not known, print raw packet */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = -llc_hdrlen;
+ }
+ } else {
+ /*
+ * LLC header is absent; treat it as just IP.
+ */
+ llc_hdrlen = 0;
+ ip_print(ndo, p, length);
+ }
+
+ ndo->ndo_ll_hdr_len += llc_hdrlen;
+}
diff --git a/print-cnfp.c b/print-cnfp.c
new file mode 100644
index 0000000..101148f
--- /dev/null
+++ b/print-cnfp.c
@@ -0,0 +1,486 @@
+/* $OpenBSD: print-cnfp.c,v 1.2 1998/06/25 20:26:59 mickey Exp $ */
+
+/*
+ * Copyright (c) 1998 Michael Shalayeff
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Michael Shalayeff.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: Cisco NetFlow protocol printer */
+
+/*
+ * Cisco NetFlow protocol
+ *
+ * See
+ *
+ * https://www.cisco.com/c/en/us/td/docs/net_mgmt/netflow_collection_engine/3-6/user/guide/format.html#wp1005892
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "tcp.h"
+#include "ipproto.h"
+
+struct nfhdr_v1 {
+ nd_uint16_t version; /* version number */
+ nd_uint16_t count; /* # of records */
+ nd_uint32_t msys_uptime;
+ nd_uint32_t utc_sec;
+ nd_uint32_t utc_nsec;
+};
+
+struct nfrec_v1 {
+ nd_ipv4 src_ina;
+ nd_ipv4 dst_ina;
+ nd_ipv4 nhop_ina;
+ nd_uint16_t input; /* SNMP index of input interface */
+ nd_uint16_t output; /* SNMP index of output interface */
+ nd_uint32_t packets; /* packets in the flow */
+ nd_uint32_t octets; /* layer 3 octets in the packets of the flow */
+ nd_uint32_t start_time; /* sys_uptime value at start of flow */
+ nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */
+ nd_uint16_t srcport; /* TCP/UDP source port or equivalent */
+ nd_uint16_t dstport; /* TCP/UDP source port or equivalent */
+ nd_byte pad1[2]; /* pad */
+ nd_uint8_t proto; /* IP protocol type */
+ nd_uint8_t tos; /* IP type of service */
+ nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */
+ nd_byte pad[3]; /* padding */
+ nd_uint32_t reserved; /* unused */
+};
+
+struct nfhdr_v5 {
+ nd_uint16_t version; /* version number */
+ nd_uint16_t count; /* # of records */
+ nd_uint32_t msys_uptime;
+ nd_uint32_t utc_sec;
+ nd_uint32_t utc_nsec;
+ nd_uint32_t sequence; /* flow sequence number */
+ nd_uint8_t engine_type; /* type of flow-switching engine */
+ nd_uint8_t engine_id; /* slot number of the flow-switching engine */
+ nd_uint16_t sampling_interval; /* sampling mode and interval */
+};
+
+struct nfrec_v5 {
+ nd_ipv4 src_ina;
+ nd_ipv4 dst_ina;
+ nd_ipv4 nhop_ina;
+ nd_uint16_t input; /* SNMP index of input interface */
+ nd_uint16_t output; /* SNMP index of output interface */
+ nd_uint32_t packets; /* packets in the flow */
+ nd_uint32_t octets; /* layer 3 octets in the packets of the flow */
+ nd_uint32_t start_time; /* sys_uptime value at start of flow */
+ nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */
+ nd_uint16_t srcport; /* TCP/UDP source port or equivalent */
+ nd_uint16_t dstport; /* TCP/UDP source port or equivalent */
+ nd_byte pad1; /* pad */
+ nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */
+ nd_uint8_t proto; /* IP protocol type */
+ nd_uint8_t tos; /* IP type of service */
+ nd_uint16_t src_as; /* AS number of the source */
+ nd_uint16_t dst_as; /* AS number of the destination */
+ nd_uint8_t src_mask; /* source address mask bits */
+ nd_uint8_t dst_mask; /* destination address prefix mask bits */
+ nd_byte pad2[2];
+ nd_ipv4 peer_nexthop; /* v6: IP address of the nexthop within the peer (FIB)*/
+};
+
+struct nfhdr_v6 {
+ nd_uint16_t version; /* version number */
+ nd_uint16_t count; /* # of records */
+ nd_uint32_t msys_uptime;
+ nd_uint32_t utc_sec;
+ nd_uint32_t utc_nsec;
+ nd_uint32_t sequence; /* v5 flow sequence number */
+ nd_uint32_t reserved; /* v5 only */
+};
+
+struct nfrec_v6 {
+ nd_ipv4 src_ina;
+ nd_ipv4 dst_ina;
+ nd_ipv4 nhop_ina;
+ nd_uint16_t input; /* SNMP index of input interface */
+ nd_uint16_t output; /* SNMP index of output interface */
+ nd_uint32_t packets; /* packets in the flow */
+ nd_uint32_t octets; /* layer 3 octets in the packets of the flow */
+ nd_uint32_t start_time; /* sys_uptime value at start of flow */
+ nd_uint32_t last_time; /* sys_uptime value when last packet of flow was received */
+ nd_uint16_t srcport; /* TCP/UDP source port or equivalent */
+ nd_uint16_t dstport; /* TCP/UDP source port or equivalent */
+ nd_byte pad1; /* pad */
+ nd_uint8_t tcp_flags; /* cumulative OR of TCP flags */
+ nd_uint8_t proto; /* IP protocol type */
+ nd_uint8_t tos; /* IP type of service */
+ nd_uint16_t src_as; /* AS number of the source */
+ nd_uint16_t dst_as; /* AS number of the destination */
+ nd_uint8_t src_mask; /* source address mask bits */
+ nd_uint8_t dst_mask; /* destination address prefix mask bits */
+ nd_uint16_t flags;
+ nd_ipv4 peer_nexthop; /* v6: IP address of the nexthop within the peer (FIB)*/
+};
+
+static void
+cnfp_v1_print(netdissect_options *ndo, const u_char *cp)
+{
+ const struct nfhdr_v1 *nh;
+ const struct nfrec_v1 *nr;
+ const char *p_name;
+ uint8_t proto;
+ u_int nrecs, ver;
+#if 0
+ time_t t;
+#endif
+
+ nh = (const struct nfhdr_v1 *)cp;
+ ND_TCHECK_SIZE(nh);
+
+ ver = GET_BE_U_2(nh->version);
+ nrecs = GET_BE_U_4(nh->count);
+#if 0
+ /*
+ * This is seconds since the UN*X epoch, and is followed by
+ * nanoseconds. XXX - format it, rather than just dumping the
+ * raw seconds-since-the-Epoch.
+ */
+ t = GET_BE_U_4(nh->utc_sec);
+#endif
+
+ ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver,
+ GET_BE_U_4(nh->msys_uptime)/1000,
+ GET_BE_U_4(nh->msys_uptime)%1000,
+ GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec));
+
+ nr = (const struct nfrec_v1 *)&nh[1];
+
+ ND_PRINT("%2u recs", nrecs);
+
+ for (; nrecs != 0; nr++, nrecs--) {
+ char buf[20];
+ char asbuf[20];
+
+ /*
+ * Make sure we have the entire record.
+ */
+ ND_TCHECK_SIZE(nr);
+ ND_PRINT("\n started %u.%03u, last %u.%03u",
+ GET_BE_U_4(nr->start_time)/1000,
+ GET_BE_U_4(nr->start_time)%1000,
+ GET_BE_U_4(nr->last_time)/1000,
+ GET_BE_U_4(nr->last_time)%1000);
+
+ asbuf[0] = buf[0] = '\0';
+ ND_PRINT("\n %s%s%s:%u ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)),
+ buf, asbuf,
+ GET_BE_U_2(nr->srcport));
+
+ ND_PRINT("> %s%s%s:%u ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)),
+ buf, asbuf,
+ GET_BE_U_2(nr->dstport));
+
+ ND_PRINT(">> %s\n ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina)));
+
+ proto = GET_U_1(nr->proto);
+ if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL)
+ ND_PRINT("%s ", p_name);
+ else
+ ND_PRINT("%u ", proto);
+
+ /* tcp flags for tcp only */
+ if (proto == IPPROTO_TCP) {
+ u_int flags;
+ flags = GET_U_1(nr->tcp_flags);
+ ND_PRINT("%s%s%s%s%s%s%s",
+ flags & TH_FIN ? "F" : "",
+ flags & TH_SYN ? "S" : "",
+ flags & TH_RST ? "R" : "",
+ flags & TH_PUSH ? "P" : "",
+ flags & TH_ACK ? "A" : "",
+ flags & TH_URG ? "U" : "",
+ flags ? " " : "");
+ }
+
+ buf[0]='\0';
+ ND_PRINT("tos %u, %u (%u octets) %s",
+ GET_U_1(nr->tos),
+ GET_BE_U_4(nr->packets),
+ GET_BE_U_4(nr->octets), buf);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+cnfp_v5_print(netdissect_options *ndo, const u_char *cp)
+{
+ const struct nfhdr_v5 *nh;
+ const struct nfrec_v5 *nr;
+ const char *p_name;
+ uint8_t proto;
+ u_int nrecs, ver;
+#if 0
+ time_t t;
+#endif
+
+ nh = (const struct nfhdr_v5 *)cp;
+ ND_TCHECK_SIZE(nh);
+
+ ver = GET_BE_U_2(nh->version);
+ nrecs = GET_BE_U_4(nh->count);
+#if 0
+ /*
+ * This is seconds since the UN*X epoch, and is followed by
+ * nanoseconds. XXX - format it, rather than just dumping the
+ * raw seconds-since-the-Epoch.
+ */
+ t = GET_BE_U_4(nh->utc_sec);
+#endif
+
+ ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver,
+ GET_BE_U_4(nh->msys_uptime)/1000,
+ GET_BE_U_4(nh->msys_uptime)%1000,
+ GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec));
+
+ ND_PRINT("#%u, ", GET_BE_U_4(nh->sequence));
+ nr = (const struct nfrec_v5 *)&nh[1];
+
+ ND_PRINT("%2u recs", nrecs);
+
+ for (; nrecs != 0; nr++, nrecs--) {
+ char buf[20];
+ char asbuf[20];
+
+ /*
+ * Make sure we have the entire record.
+ */
+ ND_TCHECK_SIZE(nr);
+ ND_PRINT("\n started %u.%03u, last %u.%03u",
+ GET_BE_U_4(nr->start_time)/1000,
+ GET_BE_U_4(nr->start_time)%1000,
+ GET_BE_U_4(nr->last_time)/1000,
+ GET_BE_U_4(nr->last_time)%1000);
+
+ asbuf[0] = buf[0] = '\0';
+ snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->src_mask));
+ snprintf(asbuf, sizeof(asbuf), ":%u",
+ GET_BE_U_2(nr->src_as));
+ ND_PRINT("\n %s%s%s:%u ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)),
+ buf, asbuf,
+ GET_BE_U_2(nr->srcport));
+
+ snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->dst_mask));
+ snprintf(asbuf, sizeof(asbuf), ":%u",
+ GET_BE_U_2(nr->dst_as));
+ ND_PRINT("> %s%s%s:%u ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)),
+ buf, asbuf,
+ GET_BE_U_2(nr->dstport));
+
+ ND_PRINT(">> %s\n ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina)));
+
+ proto = GET_U_1(nr->proto);
+ if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL)
+ ND_PRINT("%s ", p_name);
+ else
+ ND_PRINT("%u ", proto);
+
+ /* tcp flags for tcp only */
+ if (proto == IPPROTO_TCP) {
+ u_int flags;
+ flags = GET_U_1(nr->tcp_flags);
+ ND_PRINT("%s%s%s%s%s%s%s",
+ flags & TH_FIN ? "F" : "",
+ flags & TH_SYN ? "S" : "",
+ flags & TH_RST ? "R" : "",
+ flags & TH_PUSH ? "P" : "",
+ flags & TH_ACK ? "A" : "",
+ flags & TH_URG ? "U" : "",
+ flags ? " " : "");
+ }
+
+ buf[0]='\0';
+ ND_PRINT("tos %u, %u (%u octets) %s",
+ GET_U_1(nr->tos),
+ GET_BE_U_4(nr->packets),
+ GET_BE_U_4(nr->octets), buf);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+cnfp_v6_print(netdissect_options *ndo, const u_char *cp)
+{
+ const struct nfhdr_v6 *nh;
+ const struct nfrec_v6 *nr;
+ const char *p_name;
+ uint8_t proto;
+ u_int nrecs, ver;
+#if 0
+ time_t t;
+#endif
+
+ nh = (const struct nfhdr_v6 *)cp;
+ ND_TCHECK_SIZE(nh);
+
+ ver = GET_BE_U_2(nh->version);
+ nrecs = GET_BE_U_4(nh->count);
+#if 0
+ /*
+ * This is seconds since the UN*X epoch, and is followed by
+ * nanoseconds. XXX - format it, rather than just dumping the
+ * raw seconds-since-the-Epoch.
+ */
+ t = GET_BE_U_4(nh->utc_sec);
+#endif
+
+ ND_PRINT("NetFlow v%x, %u.%03u uptime, %u.%09u, ", ver,
+ GET_BE_U_4(nh->msys_uptime)/1000,
+ GET_BE_U_4(nh->msys_uptime)%1000,
+ GET_BE_U_4(nh->utc_sec), GET_BE_U_4(nh->utc_nsec));
+
+ ND_PRINT("#%u, ", GET_BE_U_4(nh->sequence));
+ nr = (const struct nfrec_v6 *)&nh[1];
+
+ ND_PRINT("%2u recs", nrecs);
+
+ for (; nrecs != 0; nr++, nrecs--) {
+ char buf[20];
+ char asbuf[20];
+
+ /*
+ * Make sure we have the entire record.
+ */
+ ND_TCHECK_SIZE(nr);
+ ND_PRINT("\n started %u.%03u, last %u.%03u",
+ GET_BE_U_4(nr->start_time)/1000,
+ GET_BE_U_4(nr->start_time)%1000,
+ GET_BE_U_4(nr->last_time)/1000,
+ GET_BE_U_4(nr->last_time)%1000);
+
+ asbuf[0] = buf[0] = '\0';
+ snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->src_mask));
+ snprintf(asbuf, sizeof(asbuf), ":%u",
+ GET_BE_U_2(nr->src_as));
+ ND_PRINT("\n %s%s%s:%u ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->src_ina)),
+ buf, asbuf,
+ GET_BE_U_2(nr->srcport));
+
+ snprintf(buf, sizeof(buf), "/%u", GET_U_1(nr->dst_mask));
+ snprintf(asbuf, sizeof(asbuf), ":%u",
+ GET_BE_U_2(nr->dst_as));
+ ND_PRINT("> %s%s%s:%u ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->dst_ina)),
+ buf, asbuf,
+ GET_BE_U_2(nr->dstport));
+
+ ND_PRINT(">> %s\n ",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(nr->nhop_ina)));
+
+ proto = GET_U_1(nr->proto);
+ if (!ndo->ndo_nflag && (p_name = netdb_protoname(proto)) != NULL)
+ ND_PRINT("%s ", p_name);
+ else
+ ND_PRINT("%u ", proto);
+
+ /* tcp flags for tcp only */
+ if (proto == IPPROTO_TCP) {
+ u_int flags;
+ flags = GET_U_1(nr->tcp_flags);
+ ND_PRINT("%s%s%s%s%s%s%s",
+ flags & TH_FIN ? "F" : "",
+ flags & TH_SYN ? "S" : "",
+ flags & TH_RST ? "R" : "",
+ flags & TH_PUSH ? "P" : "",
+ flags & TH_ACK ? "A" : "",
+ flags & TH_URG ? "U" : "",
+ flags ? " " : "");
+ }
+
+ buf[0]='\0';
+ snprintf(buf, sizeof(buf), "(%u<>%u encaps)",
+ (GET_BE_U_2(nr->flags) >> 8) & 0xff,
+ (GET_BE_U_2(nr->flags)) & 0xff);
+ ND_PRINT("tos %u, %u (%u octets) %s",
+ GET_U_1(nr->tos),
+ GET_BE_U_4(nr->packets),
+ GET_BE_U_4(nr->octets), buf);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+cnfp_print(netdissect_options *ndo, const u_char *cp)
+{
+ int ver;
+
+ /*
+ * First 2 bytes are the version number.
+ */
+ ndo->ndo_protocol = "cnfp";
+ ver = GET_BE_U_2(cp);
+ switch (ver) {
+
+ case 1:
+ cnfp_v1_print(ndo, cp);
+ break;
+
+ case 5:
+ cnfp_v5_print(ndo, cp);
+ break;
+
+ case 6:
+ cnfp_v6_print(ndo, cp);
+ break;
+
+ default:
+ ND_PRINT("NetFlow v%x", ver);
+ break;
+ }
+}
diff --git a/print-dccp.c b/print-dccp.c
new file mode 100644
index 0000000..9714c3c
--- /dev/null
+++ b/print-dccp.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (C) Arnaldo Carvalho de Melo 2004
+ * Copyright (C) Ian McDonald 2005
+ * Copyright (C) Yoshifumi Nishida 2005
+ *
+ * This software may be distributed either under the terms of the
+ * BSD-style license that accompanies tcpdump or the GNU GPL version 2
+ */
+
+/* \summary: Datagram Congestion Control Protocol (DCCP) printer */
+
+/* specification: RFC 4340 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+#include "ip.h"
+#include "ip6.h"
+#include "ipproto.h"
+
+/* RFC4340: Datagram Congestion Control Protocol (DCCP) */
+
+/**
+ * struct dccp_hdr - generic part of DCCP packet header, with a 24-bit
+ * sequence number
+ *
+ * @dccph_sport - Relevant port on the endpoint that sent this packet
+ * @dccph_dport - Relevant port on the other endpoint
+ * @dccph_doff - Data Offset from the start of the DCCP header, in 32-bit words
+ * @dccph_ccval - Used by the HC-Sender CCID
+ * @dccph_cscov - Parts of the packet that are covered by the Checksum field
+ * @dccph_checksum - Internet checksum, depends on dccph_cscov
+ * @dccph_x - 0 = 24 bit sequence number, 1 = 48
+ * @dccph_type - packet type, see DCCP_PKT_ prefixed macros
+ * @dccph_seq - 24-bit sequence number
+ */
+struct dccp_hdr {
+ nd_uint16_t dccph_sport,
+ dccph_dport;
+ nd_uint8_t dccph_doff;
+ nd_uint8_t dccph_ccval_cscov;
+ nd_uint16_t dccph_checksum;
+ nd_uint8_t dccph_xtr;
+ nd_uint24_t dccph_seq;
+};
+
+/**
+ * struct dccp_hdr_ext - generic part of DCCP packet header, with a 48-bit
+ * sequence number
+ *
+ * @dccph_sport - Relevant port on the endpoint that sent this packet
+ * @dccph_dport - Relevant port on the other endpoint
+ * @dccph_doff - Data Offset from the start of the DCCP header, in 32-bit words
+ * @dccph_ccval - Used by the HC-Sender CCID
+ * @dccph_cscov - Parts of the packet that are covered by the Checksum field
+ * @dccph_checksum - Internet checksum, depends on dccph_cscov
+ * @dccph_x - 0 = 24 bit sequence number, 1 = 48
+ * @dccph_type - packet type, see DCCP_PKT_ prefixed macros
+ * @dccph_seq - 48-bit sequence number
+ */
+struct dccp_hdr_ext {
+ nd_uint16_t dccph_sport,
+ dccph_dport;
+ nd_uint8_t dccph_doff;
+ nd_uint8_t dccph_ccval_cscov;
+ nd_uint16_t dccph_checksum;
+ nd_uint8_t dccph_xtr;
+ nd_uint8_t reserved;
+ nd_uint48_t dccph_seq;
+};
+
+#define DCCPH_CCVAL(dh) ((GET_U_1((dh)->dccph_ccval_cscov) >> 4) & 0xF)
+#define DCCPH_CSCOV(dh) (GET_U_1((dh)->dccph_ccval_cscov) & 0xF)
+
+#define DCCPH_X(dh) (GET_U_1((dh)->dccph_xtr) & 1)
+#define DCCPH_TYPE(dh) ((GET_U_1((dh)->dccph_xtr) >> 1) & 0xF)
+
+/**
+ * struct dccp_hdr_request - Connection initiation request header
+ *
+ * @dccph_req_service - Service to which the client app wants to connect
+ */
+struct dccp_hdr_request {
+ nd_uint32_t dccph_req_service;
+};
+
+/**
+ * struct dccp_hdr_response - Connection initiation response header
+ *
+ * @dccph_resp_ack - 48 bit ack number, contains GSR
+ * @dccph_resp_service - Echoes the Service Code on a received DCCP-Request
+ */
+struct dccp_hdr_response {
+ nd_uint64_t dccph_resp_ack; /* always 8 bytes, first 2 reserved */
+ nd_uint32_t dccph_resp_service;
+};
+
+/**
+ * struct dccp_hdr_reset - Unconditionally shut down a connection
+ *
+ * @dccph_resp_ack - 48 bit ack number
+ * @dccph_reset_service - Echoes the Service Code on a received DCCP-Request
+ */
+struct dccp_hdr_reset {
+ nd_uint64_t dccph_reset_ack; /* always 8 bytes, first 2 reserved */
+ nd_uint8_t dccph_reset_code;
+ nd_uint8_t dccph_reset_data1;
+ nd_uint8_t dccph_reset_data2;
+ nd_uint8_t dccph_reset_data3;
+};
+
+enum dccp_pkt_type {
+ DCCP_PKT_REQUEST = 0,
+ DCCP_PKT_RESPONSE,
+ DCCP_PKT_DATA,
+ DCCP_PKT_ACK,
+ DCCP_PKT_DATAACK,
+ DCCP_PKT_CLOSEREQ,
+ DCCP_PKT_CLOSE,
+ DCCP_PKT_RESET,
+ DCCP_PKT_SYNC,
+ DCCP_PKT_SYNCACK
+};
+
+static const struct tok dccp_pkt_type_str[] = {
+ { DCCP_PKT_REQUEST, "DCCP-Request" },
+ { DCCP_PKT_RESPONSE, "DCCP-Response" },
+ { DCCP_PKT_DATA, "DCCP-Data" },
+ { DCCP_PKT_ACK, "DCCP-Ack" },
+ { DCCP_PKT_DATAACK, "DCCP-DataAck" },
+ { DCCP_PKT_CLOSEREQ, "DCCP-CloseReq" },
+ { DCCP_PKT_CLOSE, "DCCP-Close" },
+ { DCCP_PKT_RESET, "DCCP-Reset" },
+ { DCCP_PKT_SYNC, "DCCP-Sync" },
+ { DCCP_PKT_SYNCACK, "DCCP-SyncAck" },
+ { 0, NULL}
+};
+
+enum dccp_reset_codes {
+ DCCP_RESET_CODE_UNSPECIFIED = 0,
+ DCCP_RESET_CODE_CLOSED,
+ DCCP_RESET_CODE_ABORTED,
+ DCCP_RESET_CODE_NO_CONNECTION,
+ DCCP_RESET_CODE_PACKET_ERROR,
+ DCCP_RESET_CODE_OPTION_ERROR,
+ DCCP_RESET_CODE_MANDATORY_ERROR,
+ DCCP_RESET_CODE_CONNECTION_REFUSED,
+ DCCP_RESET_CODE_BAD_SERVICE_CODE,
+ DCCP_RESET_CODE_TOO_BUSY,
+ DCCP_RESET_CODE_BAD_INIT_COOKIE,
+ DCCP_RESET_CODE_AGGRESSION_PENALTY,
+ __DCCP_RESET_CODE_LAST
+};
+
+
+static const char *dccp_reset_codes[] = {
+ "unspecified",
+ "closed",
+ "aborted",
+ "no_connection",
+ "packet_error",
+ "option_error",
+ "mandatory_error",
+ "connection_refused",
+ "bad_service_code",
+ "too_busy",
+ "bad_init_cookie",
+ "aggression_penalty",
+};
+
+static const char *dccp_feature_nums[] = {
+ "reserved",
+ "ccid",
+ "allow_short_seqno",
+ "sequence_window",
+ "ecn_incapable",
+ "ack_ratio",
+ "send_ack_vector",
+ "send_ndp_count",
+ "minimum checksum coverage",
+ "check data checksum",
+};
+
+static u_int
+dccp_csum_coverage(netdissect_options *ndo,
+ const struct dccp_hdr* dh, u_int len)
+{
+ u_int cov;
+
+ if (DCCPH_CSCOV(dh) == 0)
+ return len;
+ cov = (GET_U_1(dh->dccph_doff) + DCCPH_CSCOV(dh) - 1) * sizeof(uint32_t);
+ return (cov > len)? len : cov;
+}
+
+static uint16_t dccp_cksum(netdissect_options *ndo, const struct ip *ip,
+ const struct dccp_hdr *dh, u_int len)
+{
+ return nextproto4_cksum(ndo, ip, (const uint8_t *)(const void *)dh, len,
+ dccp_csum_coverage(ndo, dh, len), IPPROTO_DCCP);
+}
+
+static uint16_t dccp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
+ const struct dccp_hdr *dh, u_int len)
+{
+ return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)dh, len,
+ dccp_csum_coverage(ndo, dh, len), IPPROTO_DCCP);
+}
+
+static const char *dccp_reset_code(uint8_t code)
+{
+ if (code >= __DCCP_RESET_CODE_LAST)
+ return "invalid";
+ return dccp_reset_codes[code];
+}
+
+static uint64_t
+dccp_seqno(netdissect_options *ndo, const u_char *bp)
+{
+ const struct dccp_hdr *dh = (const struct dccp_hdr *)bp;
+ uint64_t seqno;
+
+ if (DCCPH_X(dh) != 0) {
+ const struct dccp_hdr_ext *dhx = (const struct dccp_hdr_ext *)bp;
+ seqno = GET_BE_U_6(dhx->dccph_seq);
+ } else {
+ seqno = GET_BE_U_3(dh->dccph_seq);
+ }
+
+ return seqno;
+}
+
+static unsigned int
+dccp_basic_hdr_len(netdissect_options *ndo, const struct dccp_hdr *dh)
+{
+ return DCCPH_X(dh) ? sizeof(struct dccp_hdr_ext) : sizeof(struct dccp_hdr);
+}
+
+static void dccp_print_ack_no(netdissect_options *ndo, const u_char *bp)
+{
+ const struct dccp_hdr *dh = (const struct dccp_hdr *)bp;
+ const u_char *ackp = bp + dccp_basic_hdr_len(ndo, dh);
+ uint64_t ackno;
+
+ if (DCCPH_X(dh) != 0) {
+ ackno = GET_BE_U_6(ackp + 2);
+ } else {
+ ackno = GET_BE_U_3(ackp + 1);
+ }
+
+ ND_PRINT("(ack=%" PRIu64 ") ", ackno);
+}
+
+static u_int dccp_print_option(netdissect_options *, const u_char *, u_int);
+
+/**
+ * dccp_print - show dccp packet
+ * @bp - beginning of dccp packet
+ * @data2 - beginning of enclosing
+ * @len - length of ip packet
+ */
+void
+dccp_print(netdissect_options *ndo, const u_char *bp, const u_char *data2,
+ u_int len)
+{
+ const struct dccp_hdr *dh;
+ const struct ip *ip;
+ const struct ip6_hdr *ip6;
+ const u_char *cp;
+ u_short sport, dport;
+ u_int hlen;
+ u_int fixed_hdrlen;
+ uint8_t dccph_type;
+
+ ndo->ndo_protocol = "dccp";
+ dh = (const struct dccp_hdr *)bp;
+
+ ip = (const struct ip *)data2;
+ if (IP_V(ip) == 6)
+ ip6 = (const struct ip6_hdr *)data2;
+ else
+ ip6 = NULL;
+
+ /* make sure we have enough data to look at the X bit */
+ cp = (const u_char *)(dh + 1);
+ if (cp > ndo->ndo_snapend)
+ goto trunc;
+ if (len < sizeof(struct dccp_hdr)) {
+ ND_PRINT("truncated-dccp - %zu bytes missing!",
+ sizeof(struct dccp_hdr) - len);
+ return;
+ }
+
+ /* get the length of the generic header */
+ fixed_hdrlen = dccp_basic_hdr_len(ndo, dh);
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-dccp - %u bytes missing!",
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_TCHECK_LEN(dh, fixed_hdrlen);
+
+ sport = GET_BE_U_2(dh->dccph_sport);
+ dport = GET_BE_U_2(dh->dccph_dport);
+ hlen = GET_U_1(dh->dccph_doff) * 4;
+
+ if (ip6) {
+ ND_PRINT("%s.%u > %s.%u: ",
+ GET_IP6ADDR_STRING(ip6->ip6_src), sport,
+ GET_IP6ADDR_STRING(ip6->ip6_dst), dport);
+ } else {
+ ND_PRINT("%s.%u > %s.%u: ",
+ GET_IPADDR_STRING(ip->ip_src), sport,
+ GET_IPADDR_STRING(ip->ip_dst), dport);
+ }
+
+ nd_print_protocol_caps(ndo);
+
+ if (ndo->ndo_qflag) {
+ ND_PRINT(" %u", len - hlen);
+ if (hlen > len) {
+ ND_PRINT(" [bad hdr length %u - too long, > %u]",
+ hlen, len);
+ }
+ return;
+ }
+
+ /* other variables in generic header */
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" (CCVal %u, CsCov %u, ", DCCPH_CCVAL(dh), DCCPH_CSCOV(dh));
+ }
+
+ /* checksum calculation */
+ if (ndo->ndo_vflag && ND_TTEST_LEN(bp, len)) {
+ uint16_t sum = 0, dccp_sum;
+
+ dccp_sum = GET_BE_U_2(dh->dccph_checksum);
+ ND_PRINT("cksum 0x%04x ", dccp_sum);
+ if (IP_V(ip) == 4)
+ sum = dccp_cksum(ndo, ip, dh, len);
+ else if (IP_V(ip) == 6)
+ sum = dccp6_cksum(ndo, ip6, dh, len);
+ if (sum != 0)
+ ND_PRINT("(incorrect -> 0x%04x)",in_cksum_shouldbe(dccp_sum, sum));
+ else
+ ND_PRINT("(correct)");
+ }
+
+ if (ndo->ndo_vflag)
+ ND_PRINT(")");
+ ND_PRINT(" ");
+
+ dccph_type = DCCPH_TYPE(dh);
+ switch (dccph_type) {
+ case DCCP_PKT_REQUEST: {
+ const struct dccp_hdr_request *dhr =
+ (const struct dccp_hdr_request *)(bp + fixed_hdrlen);
+ fixed_hdrlen += 4;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_TCHECK_SIZE(dhr);
+ ND_PRINT("%s (service=%u) ",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ GET_BE_U_4(dhr->dccph_req_service));
+ break;
+ }
+ case DCCP_PKT_RESPONSE: {
+ const struct dccp_hdr_response *dhr =
+ (const struct dccp_hdr_response *)(bp + fixed_hdrlen);
+ fixed_hdrlen += 12;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_TCHECK_SIZE(dhr);
+ ND_PRINT("%s (service=%u) ",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ GET_BE_U_4(dhr->dccph_resp_service));
+ break;
+ }
+ case DCCP_PKT_DATA:
+ ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
+ break;
+ case DCCP_PKT_ACK: {
+ fixed_hdrlen += 8;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
+ break;
+ }
+ case DCCP_PKT_DATAACK: {
+ fixed_hdrlen += 8;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
+ break;
+ }
+ case DCCP_PKT_CLOSEREQ:
+ fixed_hdrlen += 8;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
+ break;
+ case DCCP_PKT_CLOSE:
+ fixed_hdrlen += 8;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
+ break;
+ case DCCP_PKT_RESET: {
+ const struct dccp_hdr_reset *dhr =
+ (const struct dccp_hdr_reset *)(bp + fixed_hdrlen);
+ fixed_hdrlen += 12;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_TCHECK_SIZE(dhr);
+ ND_PRINT("%s (code=%s) ",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ dccp_reset_code(GET_U_1(dhr->dccph_reset_code)));
+ break;
+ }
+ case DCCP_PKT_SYNC:
+ fixed_hdrlen += 8;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
+ break;
+ case DCCP_PKT_SYNCACK:
+ fixed_hdrlen += 8;
+ if (len < fixed_hdrlen) {
+ ND_PRINT("truncated-%s - %u bytes missing!",
+ tok2str(dccp_pkt_type_str, "", dccph_type),
+ fixed_hdrlen - len);
+ return;
+ }
+ ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type));
+ break;
+ default:
+ ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "unknown-type-%u", dccph_type));
+ break;
+ }
+
+ if ((DCCPH_TYPE(dh) != DCCP_PKT_DATA) &&
+ (DCCPH_TYPE(dh) != DCCP_PKT_REQUEST))
+ dccp_print_ack_no(ndo, bp);
+
+ if (ndo->ndo_vflag < 2)
+ return;
+
+ ND_PRINT("seq %" PRIu64, dccp_seqno(ndo, bp));
+
+ /* process options */
+ if (hlen > fixed_hdrlen){
+ u_int optlen;
+ cp = bp + fixed_hdrlen;
+ ND_PRINT(" <");
+
+ hlen -= fixed_hdrlen;
+ while(1){
+ optlen = dccp_print_option(ndo, cp, hlen);
+ if (!optlen)
+ break;
+ if (hlen <= optlen)
+ break;
+ hlen -= optlen;
+ cp += optlen;
+ ND_PRINT(", ");
+ }
+ ND_PRINT(">");
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static const struct tok dccp_option_values[] = {
+ { 0, "nop" },
+ { 1, "mandatory" },
+ { 2, "slowreceiver" },
+ { 32, "change_l" },
+ { 33, "confirm_l" },
+ { 34, "change_r" },
+ { 35, "confirm_r" },
+ { 36, "initcookie" },
+ { 37, "ndp_count" },
+ { 38, "ack_vector0" },
+ { 39, "ack_vector1" },
+ { 40, "data_dropped" },
+ { 41, "timestamp" },
+ { 42, "timestamp_echo" },
+ { 43, "elapsed_time" },
+ { 44, "data_checksum" },
+ { 0, NULL }
+};
+
+static u_int
+dccp_print_option(netdissect_options *ndo, const u_char *option, u_int hlen)
+{
+ uint8_t optlen, i;
+
+ if (GET_U_1(option) >= 32) {
+ optlen = GET_U_1(option + 1);
+ if (optlen < 2) {
+ if (GET_U_1(option) >= 128)
+ ND_PRINT("CCID option %u optlen too short",
+ GET_U_1(option));
+ else
+ ND_PRINT("%s optlen too short",
+ tok2str(dccp_option_values, "Option %u", GET_U_1(option)));
+ return 0;
+ }
+ } else
+ optlen = 1;
+
+ if (hlen < optlen) {
+ if (GET_U_1(option) >= 128)
+ ND_PRINT("CCID option %u optlen goes past header length",
+ GET_U_1(option));
+ else
+ ND_PRINT("%s optlen goes past header length",
+ tok2str(dccp_option_values, "Option %u", GET_U_1(option)));
+ return 0;
+ }
+ ND_TCHECK_LEN(option, optlen);
+
+ if (GET_U_1(option) >= 128) {
+ ND_PRINT("CCID option %u", GET_U_1(option));
+ switch (optlen) {
+ case 4:
+ ND_PRINT(" %u", GET_BE_U_2(option + 2));
+ break;
+ case 6:
+ ND_PRINT(" %u", GET_BE_U_4(option + 2));
+ break;
+ default:
+ break;
+ }
+ } else {
+ ND_PRINT("%s",
+ tok2str(dccp_option_values, "Option %u", GET_U_1(option)));
+ switch (GET_U_1(option)) {
+ case 32:
+ case 33:
+ case 34:
+ case 35:
+ if (optlen < 3) {
+ ND_PRINT(" optlen too short");
+ return optlen;
+ }
+ if (GET_U_1(option + 2) < 10){
+ ND_PRINT(" %s",
+ dccp_feature_nums[GET_U_1(option + 2)]);
+ for (i = 0; i < optlen - 3; i++)
+ ND_PRINT(" %u",
+ GET_U_1(option + 3 + i));
+ }
+ break;
+ case 36:
+ if (optlen > 2) {
+ ND_PRINT(" 0x");
+ for (i = 0; i < optlen - 2; i++)
+ ND_PRINT("%02x",
+ GET_U_1(option + 2 + i));
+ }
+ break;
+ case 37:
+ for (i = 0; i < optlen - 2; i++)
+ ND_PRINT(" %u", GET_U_1(option + 2 + i));
+ break;
+ case 38:
+ if (optlen > 2) {
+ ND_PRINT(" 0x");
+ for (i = 0; i < optlen - 2; i++)
+ ND_PRINT("%02x",
+ GET_U_1(option + 2 + i));
+ }
+ break;
+ case 39:
+ if (optlen > 2) {
+ ND_PRINT(" 0x");
+ for (i = 0; i < optlen - 2; i++)
+ ND_PRINT("%02x",
+ GET_U_1(option + 2 + i));
+ }
+ break;
+ case 40:
+ if (optlen > 2) {
+ ND_PRINT(" 0x");
+ for (i = 0; i < optlen - 2; i++)
+ ND_PRINT("%02x",
+ GET_U_1(option + 2 + i));
+ }
+ break;
+ case 41:
+ /*
+ * 13.1. Timestamp Option
+ *
+ * +--------+--------+--------+--------+--------+--------+
+ * |00101001|00000110| Timestamp Value |
+ * +--------+--------+--------+--------+--------+--------+
+ * Type=41 Length=6
+ */
+ if (optlen == 6)
+ ND_PRINT(" %u", GET_BE_U_4(option + 2));
+ else
+ ND_PRINT(" [optlen != 6]");
+ break;
+ case 42:
+ /*
+ * 13.3. Timestamp Echo Option
+ *
+ * +--------+--------+--------+--------+--------+--------+
+ * |00101010|00000110| Timestamp Echo |
+ * +--------+--------+--------+--------+--------+--------+
+ * Type=42 Len=6
+ *
+ * +--------+--------+------- ... -------+--------+--------+
+ * |00101010|00001000| Timestamp Echo | Elapsed Time |
+ * +--------+--------+------- ... -------+--------+--------+
+ * Type=42 Len=8 (4 bytes)
+ *
+ * +--------+--------+------- ... -------+------- ... -------+
+ * |00101010|00001010| Timestamp Echo | Elapsed Time |
+ * +--------+--------+------- ... -------+------- ... -------+
+ * Type=42 Len=10 (4 bytes) (4 bytes)
+ */
+ switch (optlen) {
+ case 6:
+ ND_PRINT(" %u", GET_BE_U_4(option + 2));
+ break;
+ case 8:
+ ND_PRINT(" %u", GET_BE_U_4(option + 2));
+ ND_PRINT(" (elapsed time %u)",
+ GET_BE_U_2(option + 6));
+ break;
+ case 10:
+ ND_PRINT(" %u", GET_BE_U_4(option + 2));
+ ND_PRINT(" (elapsed time %u)",
+ GET_BE_U_4(option + 6));
+ break;
+ default:
+ ND_PRINT(" [optlen != 6 or 8 or 10]");
+ break;
+ }
+ break;
+ case 43:
+ if (optlen == 6)
+ ND_PRINT(" %u", GET_BE_U_4(option + 2));
+ else if (optlen == 4)
+ ND_PRINT(" %u", GET_BE_U_2(option + 2));
+ else
+ ND_PRINT(" [optlen != 4 or 6]");
+ break;
+ case 44:
+ if (optlen > 2) {
+ ND_PRINT(" ");
+ for (i = 0; i < optlen - 2; i++)
+ ND_PRINT("%02x",
+ GET_U_1(option + 2 + i));
+ }
+ break;
+ }
+ }
+
+ return optlen;
+trunc:
+ nd_print_trunc(ndo);
+ return 0;
+}
diff --git a/print-decnet.c b/print-decnet.c
new file mode 100644
index 0000000..6a6dd25
--- /dev/null
+++ b/print-decnet.c
@@ -0,0 +1,1182 @@
+/*
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: DECnet printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+#ifndef _WIN32
+typedef nd_uint8_t byte; /* single byte field */
+#else
+/*
+ * the keyword 'byte' generates conflicts in Windows
+ */
+typedef nd_uint8_t Byte; /* single byte field */
+#define byte Byte
+#endif /* _WIN32 */
+typedef nd_uint16_t word; /* 2 byte field */
+typedef nd_uint32_t longword; /* 4 bytes field */
+
+/*
+ * Definitions for DECNET Phase IV protocol headers
+ */
+typedef union {
+ nd_mac_addr dne_addr; /* full Ethernet address */
+ struct {
+ nd_byte dne_hiord[4]; /* DECnet HIORD prefix */
+ nd_byte dne_nodeaddr[2]; /* DECnet node address */
+ } dne_remote;
+} etheraddr; /* Ethernet address */
+
+#define HIORD 0x000400aa /* high 32-bits of address (swapped) */
+
+#define AREAMASK 0176000 /* mask for area field */
+#define AREASHIFT 10 /* bit-offset for area field */
+#define NODEMASK 01777 /* mask for node address field */
+
+/*
+ * Define long and short header formats.
+ */
+struct shorthdr
+ {
+ byte sh_flags; /* route flags */
+ word sh_dst; /* destination node address */
+ word sh_src; /* source node address */
+ byte sh_visits; /* visit count */
+ };
+
+struct longhdr
+ {
+ byte lg_flags; /* route flags */
+ byte lg_darea; /* destination area (reserved) */
+ byte lg_dsarea; /* destination subarea (reserved) */
+ etheraddr lg_dst; /* destination id */
+ byte lg_sarea; /* source area (reserved) */
+ byte lg_ssarea; /* source subarea (reserved) */
+ etheraddr lg_src; /* source id */
+ byte lg_nextl2; /* next level 2 router (reserved) */
+ byte lg_visits; /* visit count */
+ byte lg_service; /* service class (reserved) */
+ byte lg_pt; /* protocol type (reserved) */
+ };
+
+union routehdr
+ {
+ struct shorthdr rh_short; /* short route header */
+ struct longhdr rh_long; /* long route header */
+ };
+
+/*
+ * Define the values of various fields in the protocol messages.
+ *
+ * 1. Data packet formats.
+ */
+#define RMF_MASK 7 /* mask for message type */
+#define RMF_SHORT 2 /* short message format */
+#define RMF_LONG 6 /* long message format */
+#ifndef RMF_RQR
+#define RMF_RQR 010 /* request return to sender */
+#define RMF_RTS 020 /* returning to sender */
+#define RMF_IE 040 /* intra-ethernet packet */
+#endif /* RMR_RQR */
+#define RMF_FVER 0100 /* future version flag */
+#define RMF_PAD 0200 /* pad field */
+#define RMF_PADMASK 0177 /* pad field mask */
+
+#define VIS_MASK 077 /* visit field mask */
+
+/*
+ * 2. Control packet formats.
+ */
+#define RMF_CTLMASK 017 /* mask for message type */
+#define RMF_CTLMSG 01 /* control message indicator */
+#define RMF_INIT 01 /* initialization message */
+#define RMF_VER 03 /* verification message */
+#define RMF_TEST 05 /* hello and test message */
+#define RMF_L1ROUT 07 /* level 1 routing message */
+#define RMF_L2ROUT 011 /* level 2 routing message */
+#define RMF_RHELLO 013 /* router hello message */
+#define RMF_EHELLO 015 /* endnode hello message */
+
+#define TI_L2ROUT 01 /* level 2 router */
+#define TI_L1ROUT 02 /* level 1 router */
+#define TI_ENDNODE 03 /* endnode */
+#define TI_VERIF 04 /* verification required */
+#define TI_BLOCK 010 /* blocking requested */
+
+#define VE_VERS 2 /* version number (2) */
+#define VE_ECO 0 /* ECO number */
+#define VE_UECO 0 /* user ECO number (0) */
+
+#define P3_VERS 1 /* phase III version number (1) */
+#define P3_ECO 3 /* ECO number (3) */
+#define P3_UECO 0 /* user ECO number (0) */
+
+#define II_L2ROUT 01 /* level 2 router */
+#define II_L1ROUT 02 /* level 1 router */
+#define II_ENDNODE 03 /* endnode */
+#define II_VERIF 04 /* verification required */
+#define II_NOMCAST 040 /* no multicast traffic accepted */
+#define II_BLOCK 0100 /* blocking requested */
+#define II_TYPEMASK 03 /* mask for node type */
+
+#define TESTDATA 0252 /* test data bytes */
+#define TESTLEN 1 /* length of transmitted test data */
+
+/*
+ * Define control message formats.
+ */
+struct initmsg /* initialization message */
+ {
+ byte in_flags; /* route flags */
+ word in_src; /* source node address */
+ byte in_info; /* routing layer information */
+ word in_blksize; /* maximum data link block size */
+ byte in_vers; /* version number */
+ byte in_eco; /* ECO number */
+ byte in_ueco; /* user ECO number */
+ word in_hello; /* hello timer */
+ byte in_rsvd; /* reserved image field */
+ };
+
+struct verifmsg /* verification message */
+ {
+ byte ve_flags; /* route flags */
+ word ve_src; /* source node address */
+ byte ve_fcnval; /* function value image field */
+ };
+
+struct testmsg /* hello and test message */
+ {
+ byte te_flags; /* route flags */
+ word te_src; /* source node address */
+ byte te_data; /* test data image field */
+ };
+
+struct l1rout /* level 1 routing message */
+ {
+ byte r1_flags; /* route flags */
+ word r1_src; /* source node address */
+ byte r1_rsvd; /* reserved field */
+ };
+
+struct l2rout /* level 2 routing message */
+ {
+ byte r2_flags; /* route flags */
+ word r2_src; /* source node address */
+ byte r2_rsvd; /* reserved field */
+ };
+
+struct rhellomsg /* router hello message */
+ {
+ byte rh_flags; /* route flags */
+ byte rh_vers; /* version number */
+ byte rh_eco; /* ECO number */
+ byte rh_ueco; /* user ECO number */
+ etheraddr rh_src; /* source id */
+ byte rh_info; /* routing layer information */
+ word rh_blksize; /* maximum data link block size */
+ byte rh_priority; /* router's priority */
+ byte rh_area; /* reserved */
+ word rh_hello; /* hello timer */
+ byte rh_mpd; /* reserved */
+ };
+
+struct ehellomsg /* endnode hello message */
+ {
+ byte eh_flags; /* route flags */
+ byte eh_vers; /* version number */
+ byte eh_eco; /* ECO number */
+ byte eh_ueco; /* user ECO number */
+ etheraddr eh_src; /* source id */
+ byte eh_info; /* routing layer information */
+ word eh_blksize; /* maximum data link block size */
+ byte eh_area; /* area (reserved) */
+ byte eh_seed[8]; /* verification seed */
+ etheraddr eh_router; /* designated router */
+ word eh_hello; /* hello timer */
+ byte eh_mpd; /* (reserved) */
+ byte eh_data; /* test data image field */
+ };
+
+union controlmsg
+ {
+ struct initmsg cm_init; /* initialization message */
+ struct verifmsg cm_ver; /* verification message */
+ struct testmsg cm_test; /* hello and test message */
+ struct l1rout cm_l1rou; /* level 1 routing message */
+ struct l2rout cm_l2rout; /* level 2 routing message */
+ struct rhellomsg cm_rhello; /* router hello message */
+ struct ehellomsg cm_ehello; /* endnode hello message */
+ };
+
+/* Macros for decoding routing-info fields */
+#define RI_COST(x) ((x)&0777)
+#define RI_HOPS(x) (((x)>>10)&037)
+
+/*
+ * NSP protocol fields and values.
+ */
+
+#define NSP_TYPEMASK 014 /* mask to isolate type code */
+#define NSP_SUBMASK 0160 /* mask to isolate subtype code */
+#define NSP_SUBSHFT 4 /* shift to move subtype code */
+
+#define MFT_DATA 0 /* data message */
+#define MFT_ACK 04 /* acknowledgement message */
+#define MFT_CTL 010 /* control message */
+
+#define MFS_ILS 020 /* data or I/LS indicator */
+#define MFS_BOM 040 /* beginning of message (data) */
+#define MFS_MOM 0 /* middle of message (data) */
+#define MFS_EOM 0100 /* end of message (data) */
+#define MFS_INT 040 /* interrupt message */
+
+#define MFS_DACK 0 /* data acknowledgement */
+#define MFS_IACK 020 /* I/LS acknowledgement */
+#define MFS_CACK 040 /* connect acknowledgement */
+
+#define MFS_NOP 0 /* no operation */
+#define MFS_CI 020 /* connect initiate */
+#define MFS_CC 040 /* connect confirm */
+#define MFS_DI 060 /* disconnect initiate */
+#define MFS_DC 0100 /* disconnect confirm */
+#define MFS_RCI 0140 /* retransmitted connect initiate */
+
+#define SGQ_ACK 0100000 /* ack */
+#define SGQ_NAK 0110000 /* negative ack */
+#define SGQ_OACK 0120000 /* other channel ack */
+#define SGQ_ONAK 0130000 /* other channel negative ack */
+#define SGQ_MASK 07777 /* mask to isolate seq # */
+#define SGQ_OTHER 020000 /* other channel qualifier */
+#define SGQ_DELAY 010000 /* ack delay flag */
+
+#define SGQ_EOM 0100000 /* pseudo flag for end-of-message */
+
+#define LSM_MASK 03 /* mask for modifier field */
+#define LSM_NOCHANGE 0 /* no change */
+#define LSM_DONOTSEND 1 /* do not send data */
+#define LSM_SEND 2 /* send data */
+
+#define LSI_MASK 014 /* mask for interpretation field */
+#define LSI_DATA 0 /* data segment or message count */
+#define LSI_INTR 4 /* interrupt request count */
+#define LSI_INTM 0377 /* funny marker for int. message */
+
+#define COS_MASK 014 /* mask for flow control field */
+#define COS_NONE 0 /* no flow control */
+#define COS_SEGMENT 04 /* segment flow control */
+#define COS_MESSAGE 010 /* message flow control */
+#define COS_DEFAULT 1 /* default value for field */
+
+#define COI_MASK 3 /* mask for version field */
+#define COI_32 0 /* version 3.2 */
+#define COI_31 1 /* version 3.1 */
+#define COI_40 2 /* version 4.0 */
+#define COI_41 3 /* version 4.1 */
+
+#define MNU_MASK 140 /* mask for session control version */
+#define MNU_10 000 /* session V1.0 */
+#define MNU_20 040 /* session V2.0 */
+#define MNU_ACCESS 1 /* access control present */
+#define MNU_USRDATA 2 /* user data field present */
+#define MNU_INVKPROXY 4 /* invoke proxy field present */
+#define MNU_UICPROXY 8 /* use uic-based proxy */
+
+#define DC_NORESOURCES 1 /* no resource reason code */
+#define DC_NOLINK 41 /* no link terminate reason code */
+#define DC_COMPLETE 42 /* disconnect complete reason code */
+
+#define DI_NOERROR 0 /* user disconnect */
+#define DI_SHUT 3 /* node is shutting down */
+#define DI_NOUSER 4 /* destination end user does not exist */
+#define DI_INVDEST 5 /* invalid end user destination */
+#define DI_REMRESRC 6 /* insufficient remote resources */
+#define DI_TPA 8 /* third party abort */
+#define DI_PROTOCOL 7 /* protocol error discovered */
+#define DI_ABORT 9 /* user abort */
+#define DI_LOCALRESRC 32 /* insufficient local resources */
+#define DI_REMUSERRESRC 33 /* insufficient remote user resources */
+#define DI_BADACCESS 34 /* bad access control information */
+#define DI_BADACCNT 36 /* bad ACCOUNT information */
+#define DI_CONNECTABORT 38 /* connect request cancelled */
+#define DI_TIMEDOUT 38 /* remote node or user crashed */
+#define DI_UNREACHABLE 39 /* local timers expired due to ... */
+#define DI_BADIMAGE 43 /* bad image data in connect */
+#define DI_SERVMISMATCH 54 /* cryptographic service mismatch */
+
+#define UC_OBJREJECT 0 /* object rejected connect */
+#define UC_USERDISCONNECT 0 /* user disconnect */
+#define UC_RESOURCES 1 /* insufficient resources (local or remote) */
+#define UC_NOSUCHNODE 2 /* unrecognized node name */
+#define UC_REMOTESHUT 3 /* remote node shutting down */
+#define UC_NOSUCHOBJ 4 /* unrecognized object */
+#define UC_INVOBJFORMAT 5 /* invalid object name format */
+#define UC_OBJTOOBUSY 6 /* object too busy */
+#define UC_NETWORKABORT 8 /* network abort */
+#define UC_USERABORT 9 /* user abort */
+#define UC_INVNODEFORMAT 10 /* invalid node name format */
+#define UC_LOCALSHUT 11 /* local node shutting down */
+#define UC_ACCESSREJECT 34 /* invalid access control information */
+#define UC_NORESPONSE 38 /* no response from object */
+#define UC_UNREACHABLE 39 /* node unreachable */
+
+/*
+ * NSP message formats.
+ */
+struct nsphdr /* general nsp header */
+ {
+ byte nh_flags; /* message flags */
+ word nh_dst; /* destination link address */
+ word nh_src; /* source link address */
+ };
+
+struct seghdr /* data segment header */
+ {
+ byte sh_flags; /* message flags */
+ word sh_dst; /* destination link address */
+ word sh_src; /* source link address */
+ word sh_seq[3]; /* sequence numbers */
+ };
+
+struct minseghdr /* minimum data segment header */
+ {
+ byte ms_flags; /* message flags */
+ word ms_dst; /* destination link address */
+ word ms_src; /* source link address */
+ word ms_seq; /* sequence number */
+ };
+
+struct lsmsg /* link service message (after hdr) */
+ {
+ byte ls_lsflags; /* link service flags */
+ byte ls_fcval; /* flow control value */
+ };
+
+struct ackmsg /* acknowledgement message */
+ {
+ byte ak_flags; /* message flags */
+ word ak_dst; /* destination link address */
+ word ak_src; /* source link address */
+ word ak_acknum[2]; /* acknowledgement numbers */
+ };
+
+struct minackmsg /* minimum acknowledgement message */
+ {
+ byte mk_flags; /* message flags */
+ word mk_dst; /* destination link address */
+ word mk_src; /* source link address */
+ word mk_acknum; /* acknowledgement number */
+ };
+
+struct ciackmsg /* connect acknowledgement message */
+ {
+ byte ck_flags; /* message flags */
+ word ck_dst; /* destination link address */
+ };
+
+struct cimsg /* connect initiate message */
+ {
+ byte ci_flags; /* message flags */
+ word ci_dst; /* destination link address (0) */
+ word ci_src; /* source link address */
+ byte ci_services; /* requested services */
+ byte ci_info; /* information */
+ word ci_segsize; /* maximum segment size */
+ };
+
+struct ccmsg /* connect confirm message */
+ {
+ byte cc_flags; /* message flags */
+ word cc_dst; /* destination link address */
+ word cc_src; /* source link address */
+ byte cc_services; /* requested services */
+ byte cc_info; /* information */
+ word cc_segsize; /* maximum segment size */
+ byte cc_optlen; /* optional data length */
+ };
+
+struct cnmsg /* generic connect message */
+ {
+ byte cn_flags; /* message flags */
+ word cn_dst; /* destination link address */
+ word cn_src; /* source link address */
+ byte cn_services; /* requested services */
+ byte cn_info; /* information */
+ word cn_segsize; /* maximum segment size */
+ };
+
+struct dimsg /* disconnect initiate message */
+ {
+ byte di_flags; /* message flags */
+ word di_dst; /* destination link address */
+ word di_src; /* source link address */
+ word di_reason; /* reason code */
+ byte di_optlen; /* optional data length */
+ };
+
+struct dcmsg /* disconnect confirm message */
+ {
+ byte dc_flags; /* message flags */
+ word dc_dst; /* destination link address */
+ word dc_src; /* source link address */
+ word dc_reason; /* reason code */
+ };
+
+/* Forwards */
+static int print_decnet_ctlmsg(netdissect_options *, const union routehdr *, u_int, u_int);
+static void print_t_info(netdissect_options *, u_int);
+static void print_l1_routes(netdissect_options *, const u_char *, u_int);
+static void print_l2_routes(netdissect_options *, const u_char *, u_int);
+static void print_i_info(netdissect_options *, u_int);
+static void print_elist(const u_char *, u_int);
+static int print_nsp(netdissect_options *, const u_char *, u_int);
+static void print_reason(netdissect_options *, u_int);
+
+void
+decnet_print(netdissect_options *ndo,
+ const u_char *ap, u_int length,
+ u_int caplen)
+{
+ const union routehdr *rhp;
+ u_int mflags;
+ uint16_t dst, src;
+ u_int hops;
+ u_int nsplen, pktlen;
+ const u_char *nspp;
+
+ ndo->ndo_protocol = "decnet";
+ if (length < sizeof(struct shorthdr)) {
+ ND_PRINT(" (length %u < %zu)", length, sizeof(struct shorthdr));
+ goto invalid;
+ }
+
+ pktlen = GET_LE_U_2(ap);
+ if (pktlen < sizeof(struct shorthdr)) {
+ ND_PRINT(" (pktlen %u < %zu)", pktlen, sizeof(struct shorthdr));
+ goto invalid;
+ }
+ if (pktlen > length) {
+ ND_PRINT(" (pktlen %u > %u)", pktlen, length);
+ goto invalid;
+ }
+ length = pktlen;
+
+ rhp = (const union routehdr *)(ap + sizeof(short));
+ mflags = GET_U_1(rhp->rh_short.sh_flags);
+
+ if (mflags & RMF_PAD) {
+ /* pad bytes of some sort in front of message */
+ u_int padlen = mflags & RMF_PADMASK;
+ if (ndo->ndo_vflag)
+ ND_PRINT("[pad:%u] ", padlen);
+ if (length < padlen + 2) {
+ ND_PRINT(" (length %u < %u)", length, padlen + 2);
+ goto invalid;
+ }
+ ND_TCHECK_LEN(ap + sizeof(short), padlen);
+ ap += padlen;
+ length -= padlen;
+ caplen -= padlen;
+ rhp = (const union routehdr *)(ap + sizeof(short));
+ mflags = GET_U_1(rhp->rh_short.sh_flags);
+ }
+
+ if (mflags & RMF_FVER) {
+ ND_PRINT("future-version-decnet");
+ ND_DEFAULTPRINT(ap, ND_MIN(length, caplen));
+ return;
+ }
+
+ /* is it a control message? */
+ if (mflags & RMF_CTLMSG) {
+ if (!print_decnet_ctlmsg(ndo, rhp, length, caplen))
+ goto invalid;
+ return;
+ }
+
+ switch (mflags & RMF_MASK) {
+ case RMF_LONG:
+ if (length < sizeof(struct longhdr)) {
+ ND_PRINT(" (length %u < %zu)", length, sizeof(struct longhdr));
+ goto invalid;
+ }
+ ND_TCHECK_SIZE(&rhp->rh_long);
+ dst =
+ GET_LE_U_2(rhp->rh_long.lg_dst.dne_remote.dne_nodeaddr);
+ src =
+ GET_LE_U_2(rhp->rh_long.lg_src.dne_remote.dne_nodeaddr);
+ hops = GET_U_1(rhp->rh_long.lg_visits);
+ nspp = ap + sizeof(short) + sizeof(struct longhdr);
+ nsplen = length - sizeof(struct longhdr);
+ break;
+ case RMF_SHORT:
+ dst = GET_LE_U_2(rhp->rh_short.sh_dst);
+ src = GET_LE_U_2(rhp->rh_short.sh_src);
+ hops = (GET_U_1(rhp->rh_short.sh_visits) & VIS_MASK)+1;
+ nspp = ap + sizeof(short) + sizeof(struct shorthdr);
+ nsplen = length - sizeof(struct shorthdr);
+ break;
+ default:
+ ND_PRINT("unknown message flags under mask");
+ ND_DEFAULTPRINT((const u_char *)ap, ND_MIN(length, caplen));
+ return;
+ }
+
+ ND_PRINT("%s > %s %u ",
+ dnaddr_string(ndo, src), dnaddr_string(ndo, dst), pktlen);
+ if (ndo->ndo_vflag) {
+ if (mflags & RMF_RQR)
+ ND_PRINT("RQR ");
+ if (mflags & RMF_RTS)
+ ND_PRINT("RTS ");
+ if (mflags & RMF_IE)
+ ND_PRINT("IE ");
+ ND_PRINT("%u hops ", hops);
+ }
+
+ if (!print_nsp(ndo, nspp, nsplen))
+ goto invalid;
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+
+static int
+print_decnet_ctlmsg(netdissect_options *ndo,
+ const union routehdr *rhp, u_int length,
+ u_int caplen)
+{
+ /* Our caller has already checked for mflags */
+ u_int mflags = GET_U_1(rhp->rh_short.sh_flags);
+ const union controlmsg *cmp = (const union controlmsg *)rhp;
+ uint16_t src, dst;
+ u_int info, blksize, eco, ueco, hello, other, vers;
+ u_int priority;
+ const u_char *rhpx = (const u_char *)rhp;
+
+ switch (mflags & RMF_CTLMASK) {
+ case RMF_INIT:
+ ND_PRINT("init ");
+ if (length < sizeof(struct initmsg))
+ goto invalid;
+ ND_TCHECK_SIZE(&cmp->cm_init);
+ src = GET_LE_U_2(cmp->cm_init.in_src);
+ info = GET_U_1(cmp->cm_init.in_info);
+ blksize = GET_LE_U_2(cmp->cm_init.in_blksize);
+ vers = GET_U_1(cmp->cm_init.in_vers);
+ eco = GET_U_1(cmp->cm_init.in_eco);
+ ueco = GET_U_1(cmp->cm_init.in_ueco);
+ hello = GET_LE_U_2(cmp->cm_init.in_hello);
+ print_t_info(ndo, info);
+ ND_PRINT("src %sblksize %u vers %u eco %u ueco %u hello %u",
+ dnaddr_string(ndo, src), blksize, vers, eco, ueco,
+ hello);
+ break;
+ case RMF_VER:
+ ND_PRINT("verification ");
+ if (length < sizeof(struct verifmsg))
+ goto invalid;
+ src = GET_LE_U_2(cmp->cm_ver.ve_src);
+ other = GET_U_1(cmp->cm_ver.ve_fcnval);
+ ND_PRINT("src %s fcnval %o", dnaddr_string(ndo, src), other);
+ break;
+ case RMF_TEST:
+ ND_PRINT("test ");
+ if (length < sizeof(struct testmsg))
+ goto invalid;
+ src = GET_LE_U_2(cmp->cm_test.te_src);
+ other = GET_U_1(cmp->cm_test.te_data);
+ ND_PRINT("src %s data %o", dnaddr_string(ndo, src), other);
+ break;
+ case RMF_L1ROUT:
+ ND_PRINT("lev-1-routing ");
+ if (length < sizeof(struct l1rout))
+ goto invalid;
+ ND_TCHECK_SIZE(&cmp->cm_l1rou);
+ src = GET_LE_U_2(cmp->cm_l1rou.r1_src);
+ ND_PRINT("src %s ", dnaddr_string(ndo, src));
+ print_l1_routes(ndo, &(rhpx[sizeof(struct l1rout)]),
+ length - sizeof(struct l1rout));
+ break;
+ case RMF_L2ROUT:
+ ND_PRINT("lev-2-routing ");
+ if (length < sizeof(struct l2rout))
+ goto invalid;
+ ND_TCHECK_SIZE(&cmp->cm_l2rout);
+ src = GET_LE_U_2(cmp->cm_l2rout.r2_src);
+ ND_PRINT("src %s ", dnaddr_string(ndo, src));
+ print_l2_routes(ndo, &(rhpx[sizeof(struct l2rout)]),
+ length - sizeof(struct l2rout));
+ break;
+ case RMF_RHELLO:
+ ND_PRINT("router-hello ");
+ if (length < sizeof(struct rhellomsg))
+ goto invalid;
+ ND_TCHECK_SIZE(&cmp->cm_rhello);
+ vers = GET_U_1(cmp->cm_rhello.rh_vers);
+ eco = GET_U_1(cmp->cm_rhello.rh_eco);
+ ueco = GET_U_1(cmp->cm_rhello.rh_ueco);
+ src =
+ GET_LE_U_2(cmp->cm_rhello.rh_src.dne_remote.dne_nodeaddr);
+ info = GET_U_1(cmp->cm_rhello.rh_info);
+ blksize = GET_LE_U_2(cmp->cm_rhello.rh_blksize);
+ priority = GET_U_1(cmp->cm_rhello.rh_priority);
+ hello = GET_LE_U_2(cmp->cm_rhello.rh_hello);
+ print_i_info(ndo, info);
+ ND_PRINT("vers %u eco %u ueco %u src %s blksize %u pri %u hello %u",
+ vers, eco, ueco, dnaddr_string(ndo, src),
+ blksize, priority, hello);
+ print_elist(&(rhpx[sizeof(struct rhellomsg)]),
+ length - sizeof(struct rhellomsg));
+ break;
+ case RMF_EHELLO:
+ ND_PRINT("endnode-hello ");
+ if (length < sizeof(struct ehellomsg))
+ goto invalid;
+ vers = GET_U_1(cmp->cm_ehello.eh_vers);
+ eco = GET_U_1(cmp->cm_ehello.eh_eco);
+ ueco = GET_U_1(cmp->cm_ehello.eh_ueco);
+ src =
+ GET_LE_U_2(cmp->cm_ehello.eh_src.dne_remote.dne_nodeaddr);
+ info = GET_U_1(cmp->cm_ehello.eh_info);
+ blksize = GET_LE_U_2(cmp->cm_ehello.eh_blksize);
+ /*seed*/
+ dst =
+ GET_LE_U_2(cmp->cm_ehello.eh_router.dne_remote.dne_nodeaddr);
+ hello = GET_LE_U_2(cmp->cm_ehello.eh_hello);
+ other = GET_U_1(cmp->cm_ehello.eh_data);
+ print_i_info(ndo, info);
+ ND_PRINT("vers %u eco %u ueco %u src %s blksize %u rtr %s hello %u data %o",
+ vers, eco, ueco, dnaddr_string(ndo, src),
+ blksize, dnaddr_string(ndo, dst), hello, other);
+ break;
+
+ default:
+ ND_PRINT("unknown control message");
+ ND_DEFAULTPRINT((const u_char *)rhp, ND_MIN(length, caplen));
+ break;
+ }
+ return (1);
+
+invalid:
+ return (0);
+}
+
+static void
+print_t_info(netdissect_options *ndo,
+ u_int info)
+{
+ u_int ntype = info & 3;
+ switch (ntype) {
+ case 0: ND_PRINT("reserved-ntype? "); break;
+ case TI_L2ROUT: ND_PRINT("l2rout "); break;
+ case TI_L1ROUT: ND_PRINT("l1rout "); break;
+ case TI_ENDNODE: ND_PRINT("endnode "); break;
+ }
+ if (info & TI_VERIF)
+ ND_PRINT("verif ");
+ if (info & TI_BLOCK)
+ ND_PRINT("blo ");
+}
+
+static void
+print_l1_routes(netdissect_options *ndo,
+ const u_char *rp, u_int len)
+{
+ u_int count;
+ u_int id;
+ u_int info;
+
+ /* The last short is a checksum */
+ while (len > (3 * sizeof(short))) {
+ ND_TCHECK_LEN(rp, 3 * sizeof(short));
+ count = GET_LE_U_2(rp);
+ if (count > 1024)
+ return; /* seems to be bogus from here on */
+ rp += sizeof(short);
+ len -= sizeof(short);
+ id = GET_LE_U_2(rp);
+ rp += sizeof(short);
+ len -= sizeof(short);
+ info = GET_LE_U_2(rp);
+ rp += sizeof(short);
+ len -= sizeof(short);
+ ND_PRINT("{ids %u-%u cost %u hops %u} ", id, id + count,
+ RI_COST(info), RI_HOPS(info));
+ }
+}
+
+static void
+print_l2_routes(netdissect_options *ndo,
+ const u_char *rp, u_int len)
+{
+ u_int count;
+ u_int area;
+ u_int info;
+
+ /* The last short is a checksum */
+ while (len > (3 * sizeof(short))) {
+ ND_TCHECK_LEN(rp, 3 * sizeof(short));
+ count = GET_LE_U_2(rp);
+ if (count > 1024)
+ return; /* seems to be bogus from here on */
+ rp += sizeof(short);
+ len -= sizeof(short);
+ area = GET_LE_U_2(rp);
+ rp += sizeof(short);
+ len -= sizeof(short);
+ info = GET_LE_U_2(rp);
+ rp += sizeof(short);
+ len -= sizeof(short);
+ ND_PRINT("{areas %u-%u cost %u hops %u} ", area, area + count,
+ RI_COST(info), RI_HOPS(info));
+ }
+}
+
+static void
+print_i_info(netdissect_options *ndo,
+ u_int info)
+{
+ u_int ntype = info & II_TYPEMASK;
+ switch (ntype) {
+ case 0: ND_PRINT("reserved-ntype? "); break;
+ case II_L2ROUT: ND_PRINT("l2rout "); break;
+ case II_L1ROUT: ND_PRINT("l1rout "); break;
+ case II_ENDNODE: ND_PRINT("endnode "); break;
+ }
+ if (info & II_VERIF)
+ ND_PRINT("verif ");
+ if (info & II_NOMCAST)
+ ND_PRINT("nomcast ");
+ if (info & II_BLOCK)
+ ND_PRINT("blo ");
+}
+
+static void
+print_elist(const u_char *elp _U_, u_int len _U_)
+{
+ /* Not enough examples available for me to debug this */
+}
+
+static int
+print_nsp(netdissect_options *ndo,
+ const u_char *nspp, u_int nsplen)
+{
+ const struct nsphdr *nsphp = (const struct nsphdr *)nspp;
+ u_int dst, src, flags;
+
+ if (nsplen < sizeof(struct nsphdr)) {
+ ND_PRINT(" (nsplen %u < %zu)", nsplen, sizeof(struct nsphdr));
+ goto invalid;
+ }
+ flags = GET_U_1(nsphp->nh_flags);
+ dst = GET_LE_U_2(nsphp->nh_dst);
+ src = GET_LE_U_2(nsphp->nh_src);
+
+ switch (flags & NSP_TYPEMASK) {
+ case MFT_DATA:
+ switch (flags & NSP_SUBMASK) {
+ case MFS_BOM:
+ case MFS_MOM:
+ case MFS_EOM:
+ case MFS_BOM+MFS_EOM:
+ ND_PRINT("data %u>%u ", src, dst);
+ {
+ const struct seghdr *shp = (const struct seghdr *)nspp;
+ u_int ack;
+ u_int data_off = sizeof(struct minseghdr);
+
+ if (nsplen < data_off)
+ goto invalid;
+ ack = GET_LE_U_2(shp->sh_seq[0]);
+ if (ack & SGQ_ACK) { /* acknum field */
+ if ((ack & SGQ_NAK) == SGQ_NAK)
+ ND_PRINT("nak %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("ack %u ", ack & SGQ_MASK);
+ data_off += sizeof(short);
+ if (nsplen < data_off)
+ goto invalid;
+ ack = GET_LE_U_2(shp->sh_seq[1]);
+ if (ack & SGQ_OACK) { /* ackoth field */
+ if ((ack & SGQ_ONAK) == SGQ_ONAK)
+ ND_PRINT("onak %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("oack %u ", ack & SGQ_MASK);
+ data_off += sizeof(short);
+ if (nsplen < data_off)
+ goto invalid;
+ ack = GET_LE_U_2(shp->sh_seq[2]);
+ }
+ }
+ ND_PRINT("seg %u ", ack & SGQ_MASK);
+ }
+ break;
+ case MFS_ILS+MFS_INT:
+ ND_PRINT("intr ");
+ {
+ const struct seghdr *shp = (const struct seghdr *)nspp;
+ u_int ack;
+ u_int data_off = sizeof(struct minseghdr);
+
+ if (nsplen < data_off)
+ goto invalid;
+ ack = GET_LE_U_2(shp->sh_seq[0]);
+ if (ack & SGQ_ACK) { /* acknum field */
+ if ((ack & SGQ_NAK) == SGQ_NAK)
+ ND_PRINT("nak %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("ack %u ", ack & SGQ_MASK);
+ data_off += sizeof(short);
+ if (nsplen < data_off)
+ goto invalid;
+ ack = GET_LE_U_2(shp->sh_seq[1]);
+ if (ack & SGQ_OACK) { /* ackdat field */
+ if ((ack & SGQ_ONAK) == SGQ_ONAK)
+ ND_PRINT("nakdat %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("ackdat %u ", ack & SGQ_MASK);
+ data_off += sizeof(short);
+ if (nsplen < data_off)
+ goto invalid;
+ ack = GET_LE_U_2(shp->sh_seq[2]);
+ }
+ }
+ ND_PRINT("seg %u ", ack & SGQ_MASK);
+ }
+ break;
+ case MFS_ILS:
+ ND_PRINT("link-service %u>%u ", src, dst);
+ {
+ const struct seghdr *shp = (const struct seghdr *)nspp;
+ const struct lsmsg *lsmp =
+ (const struct lsmsg *)(nspp + sizeof(struct seghdr));
+ u_int ack;
+ u_int lsflags, fcval;
+
+ if (nsplen < sizeof(struct seghdr) + sizeof(struct lsmsg))
+ goto invalid;
+ ack = GET_LE_U_2(shp->sh_seq[0]);
+ if (ack & SGQ_ACK) { /* acknum field */
+ if ((ack & SGQ_NAK) == SGQ_NAK)
+ ND_PRINT("nak %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("ack %u ", ack & SGQ_MASK);
+ ack = GET_LE_U_2(shp->sh_seq[1]);
+ if (ack & SGQ_OACK) { /* ackdat field */
+ if ((ack & SGQ_ONAK) == SGQ_ONAK)
+ ND_PRINT("nakdat %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("ackdat %u ", ack & SGQ_MASK);
+ ack = GET_LE_U_2(shp->sh_seq[2]);
+ }
+ }
+ ND_PRINT("seg %u ", ack & SGQ_MASK);
+ lsflags = GET_U_1(lsmp->ls_lsflags);
+ fcval = GET_U_1(lsmp->ls_fcval);
+ switch (lsflags & LSI_MASK) {
+ case LSI_DATA:
+ ND_PRINT("dat seg count %u ", fcval);
+ switch (lsflags & LSM_MASK) {
+ case LSM_NOCHANGE:
+ break;
+ case LSM_DONOTSEND:
+ ND_PRINT("donotsend-data ");
+ break;
+ case LSM_SEND:
+ ND_PRINT("send-data ");
+ break;
+ default:
+ ND_PRINT("reserved-fcmod? %x", lsflags);
+ break;
+ }
+ break;
+ case LSI_INTR:
+ ND_PRINT("intr req count %u ", fcval);
+ break;
+ default:
+ ND_PRINT("reserved-fcval-int? %x", lsflags);
+ break;
+ }
+ }
+ break;
+ default:
+ ND_PRINT("reserved-subtype? %x %u > %u", flags, src, dst);
+ break;
+ }
+ break;
+ case MFT_ACK:
+ switch (flags & NSP_SUBMASK) {
+ case MFS_DACK:
+ ND_PRINT("data-ack %u>%u ", src, dst);
+ {
+ const struct ackmsg *amp = (const struct ackmsg *)nspp;
+ u_int ack;
+
+ if (nsplen < sizeof(struct ackmsg))
+ goto invalid;
+ ND_TCHECK_SIZE(amp);
+ ack = GET_LE_U_2(amp->ak_acknum[0]);
+ if (ack & SGQ_ACK) { /* acknum field */
+ if ((ack & SGQ_NAK) == SGQ_NAK)
+ ND_PRINT("nak %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("ack %u ", ack & SGQ_MASK);
+ ack = GET_LE_U_2(amp->ak_acknum[1]);
+ if (ack & SGQ_OACK) { /* ackoth field */
+ if ((ack & SGQ_ONAK) == SGQ_ONAK)
+ ND_PRINT("onak %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("oack %u ", ack & SGQ_MASK);
+ }
+ }
+ }
+ break;
+ case MFS_IACK:
+ ND_PRINT("ils-ack %u>%u ", src, dst);
+ {
+ const struct ackmsg *amp = (const struct ackmsg *)nspp;
+ u_int ack;
+
+ if (nsplen < sizeof(struct ackmsg))
+ goto invalid;
+ ND_TCHECK_SIZE(amp);
+ ack = GET_LE_U_2(amp->ak_acknum[0]);
+ if (ack & SGQ_ACK) { /* acknum field */
+ if ((ack & SGQ_NAK) == SGQ_NAK)
+ ND_PRINT("nak %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("ack %u ", ack & SGQ_MASK);
+ ack = GET_LE_U_2(amp->ak_acknum[1]);
+ if (ack & SGQ_OACK) { /* ackdat field */
+ if ((ack & SGQ_ONAK) == SGQ_ONAK)
+ ND_PRINT("nakdat %u ", ack & SGQ_MASK);
+ else
+ ND_PRINT("ackdat %u ", ack & SGQ_MASK);
+ }
+ }
+ }
+ break;
+ case MFS_CACK:
+ ND_PRINT("conn-ack %u", dst);
+ break;
+ default:
+ ND_PRINT("reserved-acktype? %x %u > %u", flags, src, dst);
+ break;
+ }
+ break;
+ case MFT_CTL:
+ switch (flags & NSP_SUBMASK) {
+ case MFS_CI:
+ case MFS_RCI:
+ if ((flags & NSP_SUBMASK) == MFS_CI)
+ ND_PRINT("conn-initiate ");
+ else
+ ND_PRINT("retrans-conn-initiate ");
+ ND_PRINT("%u>%u ", src, dst);
+ {
+ const struct cimsg *cimp = (const struct cimsg *)nspp;
+ u_int services, info, segsize;
+
+ if (nsplen < sizeof(struct cimsg))
+ goto invalid;
+ services = GET_U_1(cimp->ci_services);
+ info = GET_U_1(cimp->ci_info);
+ segsize = GET_LE_U_2(cimp->ci_segsize);
+
+ switch (services & COS_MASK) {
+ case COS_NONE:
+ break;
+ case COS_SEGMENT:
+ ND_PRINT("seg ");
+ break;
+ case COS_MESSAGE:
+ ND_PRINT("msg ");
+ break;
+ }
+ switch (info & COI_MASK) {
+ case COI_32:
+ ND_PRINT("ver 3.2 ");
+ break;
+ case COI_31:
+ ND_PRINT("ver 3.1 ");
+ break;
+ case COI_40:
+ ND_PRINT("ver 4.0 ");
+ break;
+ case COI_41:
+ ND_PRINT("ver 4.1 ");
+ break;
+ }
+ ND_PRINT("segsize %u ", segsize);
+ }
+ break;
+ case MFS_CC:
+ ND_PRINT("conn-confirm %u>%u ", src, dst);
+ {
+ const struct ccmsg *ccmp = (const struct ccmsg *)nspp;
+ u_int services, info;
+ u_int segsize, optlen;
+
+ if (nsplen < sizeof(struct ccmsg))
+ goto invalid;
+ services = GET_U_1(ccmp->cc_services);
+ info = GET_U_1(ccmp->cc_info);
+ segsize = GET_LE_U_2(ccmp->cc_segsize);
+ optlen = GET_U_1(ccmp->cc_optlen);
+
+ switch (services & COS_MASK) {
+ case COS_NONE:
+ break;
+ case COS_SEGMENT:
+ ND_PRINT("seg ");
+ break;
+ case COS_MESSAGE:
+ ND_PRINT("msg ");
+ break;
+ }
+ switch (info & COI_MASK) {
+ case COI_32:
+ ND_PRINT("ver 3.2 ");
+ break;
+ case COI_31:
+ ND_PRINT("ver 3.1 ");
+ break;
+ case COI_40:
+ ND_PRINT("ver 4.0 ");
+ break;
+ case COI_41:
+ ND_PRINT("ver 4.1 ");
+ break;
+ }
+ ND_PRINT("segsize %u ", segsize);
+ if (optlen) {
+ ND_PRINT("optlen %u ", optlen);
+ }
+ }
+ break;
+ case MFS_DI:
+ ND_PRINT("disconn-initiate %u>%u ", src, dst);
+ {
+ const struct dimsg *dimp = (const struct dimsg *)nspp;
+ u_int reason;
+ u_int optlen;
+
+ if (nsplen < sizeof(struct dimsg))
+ goto invalid;
+ reason = GET_LE_U_2(dimp->di_reason);
+ optlen = GET_U_1(dimp->di_optlen);
+
+ print_reason(ndo, reason);
+ if (optlen) {
+ ND_PRINT("optlen %u ", optlen);
+ }
+ }
+ break;
+ case MFS_DC:
+ ND_PRINT("disconn-confirm %u>%u ", src, dst);
+ {
+ const struct dcmsg *dcmp = (const struct dcmsg *)nspp;
+ u_int reason;
+
+ reason = GET_LE_U_2(dcmp->dc_reason);
+
+ print_reason(ndo, reason);
+ }
+ break;
+ default:
+ ND_PRINT("reserved-ctltype? %x %u > %u", flags, src, dst);
+ break;
+ }
+ break;
+ default:
+ ND_PRINT("reserved-type? %x %u > %u", flags, src, dst);
+ break;
+ }
+ return (1);
+
+invalid:
+ return (0);
+}
+
+static const struct tok reason2str[] = {
+ { UC_OBJREJECT, "object rejected connect" },
+ { UC_RESOURCES, "insufficient resources" },
+ { UC_NOSUCHNODE, "unrecognized node name" },
+ { DI_SHUT, "node is shutting down" },
+ { UC_NOSUCHOBJ, "unrecognized object" },
+ { UC_INVOBJFORMAT, "invalid object name format" },
+ { UC_OBJTOOBUSY, "object too busy" },
+ { DI_PROTOCOL, "protocol error discovered" },
+ { DI_TPA, "third party abort" },
+ { UC_USERABORT, "user abort" },
+ { UC_INVNODEFORMAT, "invalid node name format" },
+ { UC_LOCALSHUT, "local node shutting down" },
+ { DI_LOCALRESRC, "insufficient local resources" },
+ { DI_REMUSERRESRC, "insufficient remote user resources" },
+ { UC_ACCESSREJECT, "invalid access control information" },
+ { DI_BADACCNT, "bad ACCOUNT information" },
+ { UC_NORESPONSE, "no response from object" },
+ { UC_UNREACHABLE, "node unreachable" },
+ { DC_NOLINK, "no link terminate" },
+ { DC_COMPLETE, "disconnect complete" },
+ { DI_BADIMAGE, "bad image data in connect" },
+ { DI_SERVMISMATCH, "cryptographic service mismatch" },
+ { 0, NULL }
+};
+
+static void
+print_reason(netdissect_options *ndo,
+ u_int reason)
+{
+ ND_PRINT("%s ", tok2str(reason2str, "reason-%u", reason));
+}
+
+const char *
+dnnum_string(netdissect_options *ndo, u_short dnaddr)
+{
+ char *str;
+ size_t siz;
+ u_int area = (u_short)(dnaddr & AREAMASK) >> AREASHIFT;
+ u_int node = dnaddr & NODEMASK;
+
+ /* malloc() return used by the 'dnaddrtable' hash table: do not free() */
+ str = (char *)malloc(siz = sizeof("00.0000"));
+ if (str == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC, "%s: malloc", __func__);
+ snprintf(str, siz, "%u.%u", area, node);
+ return(str);
+}
diff --git a/print-dhcp6.c b/print-dhcp6.c
new file mode 100644
index 0000000..dba13e9
--- /dev/null
+++ b/print-dhcp6.c
@@ -0,0 +1,838 @@
+/*
+ * Copyright (C) 1998 and 1999 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: IPv6 DHCP printer */
+
+/*
+ * RFC3315: DHCPv6
+ * supported DHCPv6 options:
+ * RFC3319: Session Initiation Protocol (SIP) Servers options,
+ * RFC3633: IPv6 Prefix options,
+ * RFC3646: DNS Configuration options,
+ * RFC3898: Network Information Service (NIS) Configuration options,
+ * RFC4075: Simple Network Time Protocol (SNTP) Configuration option,
+ * RFC4242: Information Refresh Time option,
+ * RFC4280: Broadcast and Multicast Control Servers options,
+ * RFC5908: Network Time Protocol (NTP) Server Option for DHCPv6
+ * RFC6334: Dual-Stack Lite option,
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+/* lease duration */
+#define DHCP6_DURATION_INFINITE 0xffffffff
+
+/* Error Values */
+#define DH6ERR_FAILURE 16
+#define DH6ERR_AUTHFAIL 17
+#define DH6ERR_POORLYFORMED 18
+#define DH6ERR_UNAVAIL 19
+#define DH6ERR_OPTUNAVAIL 20
+
+/* Message type */
+#define DH6_SOLICIT 1
+#define DH6_ADVERTISE 2
+#define DH6_REQUEST 3
+#define DH6_CONFIRM 4
+#define DH6_RENEW 5
+#define DH6_REBIND 6
+#define DH6_REPLY 7
+#define DH6_RELEASE 8
+#define DH6_DECLINE 9
+#define DH6_RECONFIGURE 10
+#define DH6_INFORM_REQ 11
+#define DH6_RELAY_FORW 12
+#define DH6_RELAY_REPLY 13
+#define DH6_LEASEQUERY 14
+#define DH6_LQ_REPLY 15
+
+static const struct tok dh6_msgtype_str[] = {
+ { DH6_SOLICIT, "solicit" },
+ { DH6_ADVERTISE, "advertise" },
+ { DH6_REQUEST, "request" },
+ { DH6_CONFIRM, "confirm" },
+ { DH6_RENEW, "renew" },
+ { DH6_REBIND, "rebind" },
+ { DH6_REPLY, "reply" },
+ { DH6_RELEASE, "release" },
+ { DH6_DECLINE, "decline" },
+ { DH6_RECONFIGURE, "reconfigure" },
+ { DH6_INFORM_REQ, "inf-req" },
+ { DH6_RELAY_FORW, "relay-fwd" },
+ { DH6_RELAY_REPLY, "relay-reply" },
+ { DH6_LEASEQUERY, "leasequery" },
+ { DH6_LQ_REPLY, "leasequery-reply" },
+ { 0, NULL }
+};
+
+/* DHCP6 base packet format */
+struct dhcp6 {
+ union {
+ nd_uint8_t msgtype;
+ nd_uint32_t xid;
+ } dh6_msgtypexid;
+ /* options follow */
+};
+#define DH6_XIDMASK 0x00ffffff
+
+/* DHCPv6 relay messages */
+struct dhcp6_relay {
+ nd_uint8_t dh6relay_msgtype;
+ nd_uint8_t dh6relay_hcnt;
+ nd_ipv6 dh6relay_linkaddr; /* XXX: badly aligned */
+ nd_ipv6 dh6relay_peeraddr;
+ /* options follow */
+};
+
+/* options */
+#define DH6OPT_CLIENTID 1
+#define DH6OPT_SERVERID 2
+#define DH6OPT_IA_NA 3
+#define DH6OPT_IA_TA 4
+#define DH6OPT_IA_ADDR 5
+#define DH6OPT_ORO 6
+#define DH6OPT_PREFERENCE 7
+# define DH6OPT_PREF_MAX 255
+#define DH6OPT_ELAPSED_TIME 8
+#define DH6OPT_RELAY_MSG 9
+/*#define DH6OPT_SERVER_MSG 10 deprecated */
+#define DH6OPT_AUTH 11
+# define DH6OPT_AUTHPROTO_DELAYED 2
+# define DH6OPT_AUTHPROTO_RECONFIG 3
+# define DH6OPT_AUTHALG_HMACMD5 1
+# define DH6OPT_AUTHRDM_MONOCOUNTER 0
+# define DH6OPT_AUTHRECONFIG_KEY 1
+# define DH6OPT_AUTHRECONFIG_HMACMD5 2
+#define DH6OPT_UNICAST 12
+#define DH6OPT_STATUS_CODE 13
+# define DH6OPT_STCODE_SUCCESS 0
+# define DH6OPT_STCODE_UNSPECFAIL 1
+# define DH6OPT_STCODE_NOADDRAVAIL 2
+# define DH6OPT_STCODE_NOBINDING 3
+# define DH6OPT_STCODE_NOTONLINK 4
+# define DH6OPT_STCODE_USEMULTICAST 5
+# define DH6OPT_STCODE_NOPREFIXAVAIL 6
+# define DH6OPT_STCODE_UNKNOWNQUERYTYPE 7
+# define DH6OPT_STCODE_MALFORMEDQUERY 8
+# define DH6OPT_STCODE_NOTCONFIGURED 9
+# define DH6OPT_STCODE_NOTALLOWED 10
+#define DH6OPT_RAPID_COMMIT 14
+#define DH6OPT_USER_CLASS 15
+#define DH6OPT_VENDOR_CLASS 16
+#define DH6OPT_VENDOR_OPTS 17
+#define DH6OPT_INTERFACE_ID 18
+#define DH6OPT_RECONF_MSG 19
+#define DH6OPT_RECONF_ACCEPT 20
+#define DH6OPT_SIP_SERVER_D 21
+#define DH6OPT_SIP_SERVER_A 22
+#define DH6OPT_DNS_SERVERS 23
+#define DH6OPT_DOMAIN_LIST 24
+#define DH6OPT_IA_PD 25
+#define DH6OPT_IA_PD_PREFIX 26
+#define DH6OPT_NIS_SERVERS 27
+#define DH6OPT_NISP_SERVERS 28
+#define DH6OPT_NIS_NAME 29
+#define DH6OPT_NISP_NAME 30
+#define DH6OPT_SNTP_SERVERS 31
+#define DH6OPT_LIFETIME 32
+#define DH6OPT_BCMCS_SERVER_D 33
+#define DH6OPT_BCMCS_SERVER_A 34
+#define DH6OPT_GEOCONF_CIVIC 36
+#define DH6OPT_REMOTE_ID 37
+#define DH6OPT_SUBSCRIBER_ID 38
+#define DH6OPT_CLIENT_FQDN 39
+#define DH6OPT_PANA_AGENT 40
+#define DH6OPT_NEW_POSIX_TIMEZONE 41
+#define DH6OPT_NEW_TZDB_TIMEZONE 42
+#define DH6OPT_ERO 43
+#define DH6OPT_LQ_QUERY 44
+#define DH6OPT_CLIENT_DATA 45
+#define DH6OPT_CLT_TIME 46
+#define DH6OPT_LQ_RELAY_DATA 47
+#define DH6OPT_LQ_CLIENT_LINK 48
+#define DH6OPT_NTP_SERVER 56
+# define DH6OPT_NTP_SUBOPTION_SRV_ADDR 1
+# define DH6OPT_NTP_SUBOPTION_MC_ADDR 2
+# define DH6OPT_NTP_SUBOPTION_SRV_FQDN 3
+#define DH6OPT_AFTR_NAME 64
+#define DH6OPT_MUDURL 112
+
+static const struct tok dh6opt_str[] = {
+ { DH6OPT_CLIENTID, "client-ID" },
+ { DH6OPT_SERVERID, "server-ID" },
+ { DH6OPT_IA_NA, "IA_NA" },
+ { DH6OPT_IA_TA, "IA_TA" },
+ { DH6OPT_IA_ADDR, "IA_ADDR" },
+ { DH6OPT_ORO, "option-request" },
+ { DH6OPT_PREFERENCE, "preference" },
+ { DH6OPT_ELAPSED_TIME, "elapsed-time" },
+ { DH6OPT_RELAY_MSG, "relay-message" },
+ { DH6OPT_AUTH, "authentication" },
+ { DH6OPT_UNICAST, "server-unicast" },
+ { DH6OPT_STATUS_CODE, "status-code" },
+ { DH6OPT_RAPID_COMMIT, "rapid-commit" },
+ { DH6OPT_USER_CLASS, "user-class" },
+ { DH6OPT_VENDOR_CLASS, "vendor-class" },
+ { DH6OPT_VENDOR_OPTS, "vendor-specific-info" },
+ { DH6OPT_INTERFACE_ID, "interface-ID" },
+ { DH6OPT_RECONF_MSG, "reconfigure-message" },
+ { DH6OPT_RECONF_ACCEPT, "reconfigure-accept" },
+ { DH6OPT_SIP_SERVER_D, "SIP-servers-domain" },
+ { DH6OPT_SIP_SERVER_A, "SIP-servers-address" },
+ { DH6OPT_DNS_SERVERS, "DNS-server" },
+ { DH6OPT_DOMAIN_LIST, "DNS-search-list" },
+ { DH6OPT_IA_PD, "IA_PD" },
+ { DH6OPT_IA_PD_PREFIX, "IA_PD-prefix" },
+ { DH6OPT_SNTP_SERVERS, "SNTP-servers" },
+ { DH6OPT_LIFETIME, "lifetime" },
+ { DH6OPT_NIS_SERVERS, "NIS-server" },
+ { DH6OPT_NISP_SERVERS, "NIS+-server" },
+ { DH6OPT_NIS_NAME, "NIS-domain-name" },
+ { DH6OPT_NISP_NAME, "NIS+-domain-name" },
+ { DH6OPT_BCMCS_SERVER_D, "BCMCS-domain-name" },
+ { DH6OPT_BCMCS_SERVER_A, "BCMCS-server" },
+ { DH6OPT_GEOCONF_CIVIC, "Geoconf-Civic" },
+ { DH6OPT_REMOTE_ID, "Remote-ID" },
+ { DH6OPT_SUBSCRIBER_ID, "Subscriber-ID" },
+ { DH6OPT_CLIENT_FQDN, "Client-FQDN" },
+ { DH6OPT_PANA_AGENT, "PANA-agent" },
+ { DH6OPT_NEW_POSIX_TIMEZONE, "POSIX-timezone" },
+ { DH6OPT_NEW_TZDB_TIMEZONE, "POSIX-tz-database" },
+ { DH6OPT_ERO, "Echo-request-option" },
+ { DH6OPT_LQ_QUERY, "Lease-query" },
+ { DH6OPT_CLIENT_DATA, "LQ-client-data" },
+ { DH6OPT_CLT_TIME, "Clt-time" },
+ { DH6OPT_LQ_RELAY_DATA, "LQ-relay-data" },
+ { DH6OPT_LQ_CLIENT_LINK, "LQ-client-link" },
+ { DH6OPT_NTP_SERVER, "NTP-server" },
+ { DH6OPT_AFTR_NAME, "AFTR-Name" },
+ { DH6OPT_MUDURL, "MUD-URL" },
+ { 0, NULL }
+};
+
+static const struct tok dh6opt_stcode_str[] = {
+ { DH6OPT_STCODE_SUCCESS, "Success" }, /* RFC3315 */
+ { DH6OPT_STCODE_UNSPECFAIL, "UnspecFail" }, /* RFC3315 */
+ { DH6OPT_STCODE_NOADDRAVAIL, "NoAddrsAvail" }, /* RFC3315 */
+ { DH6OPT_STCODE_NOBINDING, "NoBinding" }, /* RFC3315 */
+ { DH6OPT_STCODE_NOTONLINK, "NotOnLink" }, /* RFC3315 */
+ { DH6OPT_STCODE_USEMULTICAST, "UseMulticast" }, /* RFC3315 */
+ { DH6OPT_STCODE_NOPREFIXAVAIL, "NoPrefixAvail" }, /* RFC3633 */
+ { DH6OPT_STCODE_UNKNOWNQUERYTYPE, "UnknownQueryType" }, /* RFC5007 */
+ { DH6OPT_STCODE_MALFORMEDQUERY, "MalformedQuery" }, /* RFC5007 */
+ { DH6OPT_STCODE_NOTCONFIGURED, "NotConfigured" }, /* RFC5007 */
+ { DH6OPT_STCODE_NOTALLOWED, "NotAllowed" }, /* RFC5007 */
+ { 0, NULL }
+};
+
+struct dhcp6opt {
+ nd_uint16_t dh6opt_type;
+ nd_uint16_t dh6opt_len;
+ /* type-dependent data follows */
+};
+
+static const char *
+dhcp6stcode(const uint16_t code)
+{
+ return code > 255 ? "INVALID code" : tok2str(dh6opt_stcode_str, "code%u", code);
+}
+
+static void
+dhcp6opt_print(netdissect_options *ndo,
+ const u_char *cp, const u_char *ep)
+{
+ const struct dhcp6opt *dh6o;
+ const u_char *tp;
+ u_int i;
+ uint16_t opttype;
+ uint16_t optlen;
+ uint8_t auth_proto;
+ uint8_t auth_alg;
+ uint8_t auth_rdm;
+ u_int authinfolen, authrealmlen;
+ u_int remain_len; /* Length of remaining options */
+ u_int label_len; /* Label length */
+ uint16_t subopt_code;
+ uint16_t subopt_len;
+ uint8_t dh6_reconf_type;
+ uint8_t dh6_lq_query_type;
+
+ if (cp == ep)
+ return;
+ while (cp < ep) {
+ if (ep < cp + sizeof(*dh6o))
+ goto trunc;
+ dh6o = (const struct dhcp6opt *)cp;
+ ND_TCHECK_SIZE(dh6o);
+ optlen = GET_BE_U_2(dh6o->dh6opt_len);
+ if (ep < cp + sizeof(*dh6o) + optlen)
+ goto trunc;
+ opttype = GET_BE_U_2(dh6o->dh6opt_type);
+ ND_PRINT(" (%s", tok2str(dh6opt_str, "opt_%u", opttype));
+ ND_TCHECK_LEN(cp + sizeof(*dh6o), optlen);
+ switch (opttype) {
+ case DH6OPT_CLIENTID:
+ case DH6OPT_SERVERID:
+ if (optlen < 2) {
+ /*(*/
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ switch (GET_BE_U_2(tp)) {
+ case 1:
+ if (optlen >= 2 + 6) {
+ ND_PRINT(" hwaddr/time type %u time %u ",
+ GET_BE_U_2(tp + 2),
+ GET_BE_U_4(tp + 4));
+ for (i = 8; i < optlen; i++)
+ ND_PRINT("%02x",
+ GET_U_1(tp + i));
+ /*(*/
+ ND_PRINT(")");
+ } else {
+ /*(*/
+ ND_PRINT(" ?)");
+ }
+ break;
+ case 2:
+ if (optlen >= 2 + 8) {
+ ND_PRINT(" vid ");
+ for (i = 2; i < 2 + 8; i++)
+ ND_PRINT("%02x",
+ GET_U_1(tp + i));
+ /*(*/
+ ND_PRINT(")");
+ } else {
+ /*(*/
+ ND_PRINT(" ?)");
+ }
+ break;
+ case 3:
+ if (optlen >= 2 + 2) {
+ ND_PRINT(" hwaddr type %u ",
+ GET_BE_U_2(tp + 2));
+ for (i = 4; i < optlen; i++)
+ ND_PRINT("%02x",
+ GET_U_1(tp + i));
+ /*(*/
+ ND_PRINT(")");
+ } else {
+ /*(*/
+ ND_PRINT(" ?)");
+ }
+ break;
+ default:
+ ND_PRINT(" type %u)", GET_BE_U_2(tp));
+ break;
+ }
+ break;
+ case DH6OPT_IA_ADDR:
+ if (optlen < 24) {
+ /*(*/
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(tp));
+ ND_PRINT(" pltime:%u vltime:%u",
+ GET_BE_U_4(tp + 16),
+ GET_BE_U_4(tp + 20));
+ if (optlen > 24) {
+ /* there are sub-options */
+ dhcp6opt_print(ndo, tp + 24, tp + optlen);
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_ORO:
+ case DH6OPT_ERO:
+ if (optlen % 2) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ for (i = 0; i < optlen; i += 2) {
+ ND_PRINT(" %s",
+ tok2str(dh6opt_str, "opt_%u", GET_BE_U_2(tp + i)));
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_PREFERENCE:
+ if (optlen != 1) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" %u)", GET_U_1(tp));
+ break;
+ case DH6OPT_ELAPSED_TIME:
+ if (optlen != 2) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" %u)", GET_BE_U_2(tp));
+ break;
+ case DH6OPT_RELAY_MSG:
+ ND_PRINT(" (");
+ tp = (const u_char *)(dh6o + 1);
+ dhcp6_print(ndo, tp, optlen);
+ ND_PRINT(")");
+ break;
+ case DH6OPT_AUTH:
+ if (optlen < 11) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ auth_proto = GET_U_1(tp);
+ switch (auth_proto) {
+ case DH6OPT_AUTHPROTO_DELAYED:
+ ND_PRINT(" proto: delayed");
+ break;
+ case DH6OPT_AUTHPROTO_RECONFIG:
+ ND_PRINT(" proto: reconfigure");
+ break;
+ default:
+ ND_PRINT(" proto: %u", auth_proto);
+ break;
+ }
+ tp++;
+ auth_alg = GET_U_1(tp);
+ switch (auth_alg) {
+ case DH6OPT_AUTHALG_HMACMD5:
+ /* XXX: may depend on the protocol */
+ ND_PRINT(", alg: HMAC-MD5");
+ break;
+ default:
+ ND_PRINT(", alg: %u", auth_alg);
+ break;
+ }
+ tp++;
+ auth_rdm = GET_U_1(tp);
+ switch (auth_rdm) {
+ case DH6OPT_AUTHRDM_MONOCOUNTER:
+ ND_PRINT(", RDM: mono");
+ break;
+ default:
+ ND_PRINT(", RDM: %u", auth_rdm);
+ break;
+ }
+ tp++;
+ ND_PRINT(", RD:");
+ for (i = 0; i < 4; i++, tp += 2)
+ ND_PRINT(" %04x", GET_BE_U_2(tp));
+
+ /* protocol dependent part */
+ authinfolen = optlen - 11;
+ switch (auth_proto) {
+ case DH6OPT_AUTHPROTO_DELAYED:
+ if (authinfolen == 0)
+ break;
+ if (authinfolen < 20) {
+ ND_PRINT(" ??");
+ break;
+ }
+ authrealmlen = authinfolen - 20;
+ if (authrealmlen > 0) {
+ ND_PRINT(", realm: ");
+ }
+ for (i = 0; i < authrealmlen; i++, tp++)
+ ND_PRINT("%02x", GET_U_1(tp));
+ ND_PRINT(", key ID: %08x", GET_BE_U_4(tp));
+ tp += 4;
+ ND_PRINT(", HMAC-MD5:");
+ for (i = 0; i < 4; i++, tp+= 4)
+ ND_PRINT(" %08x", GET_BE_U_4(tp));
+ break;
+ case DH6OPT_AUTHPROTO_RECONFIG:
+ if (authinfolen != 17) {
+ ND_PRINT(" ??");
+ break;
+ }
+ switch (GET_U_1(tp)) {
+ case DH6OPT_AUTHRECONFIG_KEY:
+ ND_PRINT(" reconfig-key");
+ break;
+ case DH6OPT_AUTHRECONFIG_HMACMD5:
+ ND_PRINT(" type: HMAC-MD5");
+ break;
+ default:
+ ND_PRINT(" type: ??");
+ break;
+ }
+ tp++;
+ ND_PRINT(" value:");
+ for (i = 0; i < 4; i++, tp+= 4)
+ ND_PRINT(" %08x", GET_BE_U_4(tp));
+ break;
+ default:
+ ND_PRINT(" ??");
+ break;
+ }
+
+ ND_PRINT(")");
+ break;
+ case DH6OPT_RAPID_COMMIT: /* nothing todo */
+ ND_PRINT(")");
+ break;
+ case DH6OPT_INTERFACE_ID:
+ case DH6OPT_SUBSCRIBER_ID:
+ /*
+ * Since we cannot predict the encoding, print hex dump
+ * at most 10 characters.
+ */
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" ");
+ for (i = 0; i < optlen && i < 10; i++)
+ ND_PRINT("%02x", GET_U_1(tp + i));
+ ND_PRINT("...)");
+ break;
+ case DH6OPT_RECONF_MSG:
+ if (optlen != 1) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ dh6_reconf_type = GET_U_1(tp);
+ switch (dh6_reconf_type) {
+ case DH6_RENEW:
+ ND_PRINT(" for renew)");
+ break;
+ case DH6_INFORM_REQ:
+ ND_PRINT(" for inf-req)");
+ break;
+ default:
+ ND_PRINT(" for ?\?\?(%02x))", dh6_reconf_type);
+ break;
+ }
+ break;
+ case DH6OPT_RECONF_ACCEPT: /* nothing todo */
+ ND_PRINT(")");
+ break;
+ case DH6OPT_SIP_SERVER_A:
+ case DH6OPT_DNS_SERVERS:
+ case DH6OPT_SNTP_SERVERS:
+ case DH6OPT_NIS_SERVERS:
+ case DH6OPT_NISP_SERVERS:
+ case DH6OPT_BCMCS_SERVER_A:
+ case DH6OPT_PANA_AGENT:
+ case DH6OPT_LQ_CLIENT_LINK:
+ if (optlen % 16) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ for (i = 0; i < optlen; i += 16)
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(tp + i));
+ ND_PRINT(")");
+ break;
+ case DH6OPT_SIP_SERVER_D:
+ case DH6OPT_DOMAIN_LIST:
+ tp = (const u_char *)(dh6o + 1);
+ while (tp < cp + sizeof(*dh6o) + optlen) {
+ ND_PRINT(" ");
+ if ((tp = fqdn_print(ndo, tp, cp + sizeof(*dh6o) + optlen)) == NULL)
+ goto trunc;
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_STATUS_CODE:
+ if (optlen < 2) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" %s)", dhcp6stcode(GET_BE_U_2(tp)));
+ break;
+ case DH6OPT_IA_NA:
+ case DH6OPT_IA_PD:
+ if (optlen < 12) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" IAID:%u T1:%u T2:%u",
+ GET_BE_U_4(tp),
+ GET_BE_U_4(tp + 4),
+ GET_BE_U_4(tp + 8));
+ if (optlen > 12) {
+ /* there are sub-options */
+ dhcp6opt_print(ndo, tp + 12, tp + optlen);
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_IA_TA:
+ if (optlen < 4) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" IAID:%u", GET_BE_U_4(tp));
+ if (optlen > 4) {
+ /* there are sub-options */
+ dhcp6opt_print(ndo, tp + 4, tp + optlen);
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_IA_PD_PREFIX:
+ if (optlen < 25) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" %s/%u", GET_IP6ADDR_STRING(tp + 9),
+ GET_U_1(tp + 8));
+ ND_PRINT(" pltime:%u vltime:%u",
+ GET_BE_U_4(tp),
+ GET_BE_U_4(tp + 4));
+ if (optlen > 25) {
+ /* there are sub-options */
+ dhcp6opt_print(ndo, tp + 25, tp + optlen);
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_LIFETIME:
+ case DH6OPT_CLT_TIME:
+ if (optlen != 4) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" %u)", GET_BE_U_4(tp));
+ break;
+ case DH6OPT_REMOTE_ID:
+ if (optlen < 4) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" %u ", GET_BE_U_4(tp));
+ /*
+ * Print hex dump first 10 characters.
+ */
+ for (i = 4; i < optlen && i < 14; i++)
+ ND_PRINT("%02x", GET_U_1(tp + i));
+ ND_PRINT("...)");
+ break;
+ case DH6OPT_LQ_QUERY:
+ if (optlen < 17) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ dh6_lq_query_type = GET_U_1(tp);
+ switch (dh6_lq_query_type) {
+ case 1:
+ ND_PRINT(" by-address");
+ break;
+ case 2:
+ ND_PRINT(" by-clientID");
+ break;
+ default:
+ ND_PRINT(" type_%u", dh6_lq_query_type);
+ break;
+ }
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(tp + 1));
+ if (optlen > 17) {
+ /* there are query-options */
+ dhcp6opt_print(ndo, tp + 17, tp + optlen);
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_CLIENT_DATA:
+ tp = (const u_char *)(dh6o + 1);
+ if (optlen > 0) {
+ /* there are encapsulated options */
+ dhcp6opt_print(ndo, tp, tp + optlen);
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_LQ_RELAY_DATA:
+ if (optlen < 16) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" %s ", GET_IP6ADDR_STRING(tp));
+ /*
+ * Print hex dump first 10 characters.
+ */
+ for (i = 16; i < optlen && i < 26; i++)
+ ND_PRINT("%02x", GET_U_1(tp + i));
+ ND_PRINT("...)");
+ break;
+ case DH6OPT_NTP_SERVER:
+ if (optlen < 4) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ while (tp < cp + sizeof(*dh6o) + optlen - 4) {
+ subopt_code = GET_BE_U_2(tp);
+ tp += 2;
+ subopt_len = GET_BE_U_2(tp);
+ tp += 2;
+ if (tp + subopt_len > cp + sizeof(*dh6o) + optlen)
+ goto trunc;
+ ND_PRINT(" subopt:%u", subopt_code);
+ switch (subopt_code) {
+ case DH6OPT_NTP_SUBOPTION_SRV_ADDR:
+ case DH6OPT_NTP_SUBOPTION_MC_ADDR:
+ if (subopt_len != 16) {
+ ND_PRINT(" ?");
+ break;
+ }
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(tp));
+ break;
+ case DH6OPT_NTP_SUBOPTION_SRV_FQDN:
+ ND_PRINT(" ");
+ if (fqdn_print(ndo, tp, tp + subopt_len) == NULL)
+ goto trunc;
+ break;
+ default:
+ ND_PRINT(" ?");
+ break;
+ }
+ tp += subopt_len;
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_AFTR_NAME:
+ if (optlen < 3) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ remain_len = optlen;
+ ND_PRINT(" ");
+ /* Encoding is described in section 3.1 of RFC 1035 */
+ while (remain_len && GET_U_1(tp)) {
+ label_len = GET_U_1(tp);
+ tp++;
+ if (label_len < remain_len - 1) {
+ nd_printjnp(ndo, tp, label_len);
+ tp += label_len;
+ remain_len -= (label_len + 1);
+ if(GET_U_1(tp)) ND_PRINT(".");
+ } else {
+ ND_PRINT(" ?");
+ break;
+ }
+ }
+ ND_PRINT(")");
+ break;
+ case DH6OPT_NEW_POSIX_TIMEZONE: /* all three of these options */
+ case DH6OPT_NEW_TZDB_TIMEZONE: /* are encoded similarly */
+ case DH6OPT_MUDURL: /* although GMT might not work */
+ if (optlen < 5) {
+ ND_PRINT(" ?)");
+ break;
+ }
+ tp = (const u_char *)(dh6o + 1);
+ ND_PRINT(" ");
+ nd_printjnp(ndo, tp, optlen);
+ ND_PRINT(")");
+ break;
+
+ default:
+ ND_PRINT(")");
+ break;
+ }
+
+ cp += sizeof(*dh6o) + optlen;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+/*
+ * Print dhcp6 packets
+ */
+void
+dhcp6_print(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ const struct dhcp6 *dh6;
+ const struct dhcp6_relay *dh6relay;
+ uint8_t msgtype;
+ const u_char *ep;
+ const u_char *extp;
+ const char *name;
+
+ ndo->ndo_protocol = "dhcp6";
+ ND_PRINT("dhcp6");
+
+ ep = ndo->ndo_snapend;
+ if (cp + length < ep)
+ ep = cp + length;
+
+ dh6 = (const struct dhcp6 *)cp;
+ dh6relay = (const struct dhcp6_relay *)cp;
+ ND_TCHECK_4(dh6->dh6_msgtypexid.xid);
+ msgtype = GET_U_1(dh6->dh6_msgtypexid.msgtype);
+ name = tok2str(dh6_msgtype_str, "msgtype-%u", msgtype);
+
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(" %s", name);
+ return;
+ }
+
+ /* XXX relay agent messages have to be handled differently */
+
+ ND_PRINT(" %s (", name); /*)*/
+ if (msgtype != DH6_RELAY_FORW && msgtype != DH6_RELAY_REPLY) {
+ ND_PRINT("xid=%x",
+ GET_BE_U_4(dh6->dh6_msgtypexid.xid) & DH6_XIDMASK);
+ extp = (const u_char *)(dh6 + 1);
+ dhcp6opt_print(ndo, extp, ep);
+ } else { /* relay messages */
+ ND_PRINT("linkaddr=%s", GET_IP6ADDR_STRING(dh6relay->dh6relay_linkaddr));
+
+ ND_PRINT(" peeraddr=%s", GET_IP6ADDR_STRING(dh6relay->dh6relay_peeraddr));
+
+ dhcp6opt_print(ndo, (const u_char *)(dh6relay + 1), ep);
+ }
+ /*(*/
+ ND_PRINT(")");
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-domain.c b/print-domain.c
new file mode 100644
index 0000000..74c71db
--- /dev/null
+++ b/print-domain.c
@@ -0,0 +1,1153 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Domain Name System (DNS) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "addrtostr.h"
+#include "extract.h"
+
+#include "nameser.h"
+
+static const char *ns_ops[] = {
+ "", " inv_q", " stat", " op3", " notify", " update", " op6", " op7",
+ " op8", " updateA", " updateD", " updateDA",
+ " updateM", " updateMA", " zoneInit", " zoneRef",
+};
+
+static const char *ns_resp[] = {
+ "", " FormErr", " ServFail", " NXDomain",
+ " NotImp", " Refused", " YXDomain", " YXRRSet",
+ " NXRRSet", " NotAuth", " NotZone", " Resp11",
+ " Resp12", " Resp13", " Resp14", " NoChange",
+ " BadVers", "Resp17", " Resp18", " Resp19",
+ " Resp20", "Resp21", " Resp22", " BadCookie",
+};
+
+static const char *
+ns_rcode(u_int rcode) {
+ static char buf[sizeof(" Resp4095")];
+
+ if (rcode < sizeof(ns_resp)/sizeof(ns_resp[0])) {
+ return (ns_resp[rcode]);
+ }
+ snprintf(buf, sizeof(buf), " Resp%u", rcode & 0xfff);
+ return (buf);
+}
+
+/* skip over a domain name */
+static const u_char *
+ns_nskip(netdissect_options *ndo,
+ const u_char *cp)
+{
+ u_char i;
+
+ if (!ND_TTEST_1(cp))
+ return (NULL);
+ i = GET_U_1(cp);
+ cp++;
+ while (i) {
+ switch (i & TYPE_MASK) {
+
+ case TYPE_INDIR:
+ return (cp + 1);
+
+ case TYPE_EDNS0: {
+ int bitlen, bytelen;
+
+ if ((i & ~TYPE_MASK) != EDNS0_ELT_BITLABEL)
+ return(NULL); /* unknown ELT */
+ if (!ND_TTEST_1(cp))
+ return (NULL);
+ if ((bitlen = GET_U_1(cp)) == 0)
+ bitlen = 256;
+ cp++;
+ bytelen = (bitlen + 7) / 8;
+ cp += bytelen;
+ }
+ break;
+
+ case TYPE_RESERVED:
+ return (NULL);
+
+ case TYPE_LABEL:
+ cp += i;
+ break;
+ }
+ if (!ND_TTEST_1(cp))
+ return (NULL);
+ i = GET_U_1(cp);
+ cp++;
+ }
+ return (cp);
+}
+
+static const u_char *
+blabel_print(netdissect_options *ndo,
+ const u_char *cp)
+{
+ u_int bitlen, slen, b;
+ const u_char *bitp, *lim;
+ uint8_t tc;
+
+ if (!ND_TTEST_1(cp))
+ return(NULL);
+ if ((bitlen = GET_U_1(cp)) == 0)
+ bitlen = 256;
+ slen = (bitlen + 3) / 4;
+ lim = cp + 1 + slen;
+
+ /* print the bit string as a hex string */
+ ND_PRINT("\\[x");
+ for (bitp = cp + 1, b = bitlen; bitp < lim && b > 7; b -= 8, bitp++) {
+ ND_PRINT("%02x", GET_U_1(bitp));
+ }
+ if (b > 4) {
+ tc = GET_U_1(bitp);
+ bitp++;
+ ND_PRINT("%02x", tc & (0xff << (8 - b)));
+ } else if (b > 0) {
+ tc = GET_U_1(bitp);
+ bitp++;
+ ND_PRINT("%1x", ((tc >> 4) & 0x0f) & (0x0f << (4 - b)));
+ }
+ ND_PRINT("/%u]", bitlen);
+ return lim;
+}
+
+static int
+labellen(netdissect_options *ndo,
+ const u_char *cp)
+{
+ u_int i;
+
+ if (!ND_TTEST_1(cp))
+ return(-1);
+ i = GET_U_1(cp);
+ switch (i & TYPE_MASK) {
+
+ case TYPE_EDNS0: {
+ u_int bitlen, elt;
+ if ((elt = (i & ~TYPE_MASK)) != EDNS0_ELT_BITLABEL) {
+ ND_PRINT("<ELT %d>", elt);
+ return(-1);
+ }
+ if (!ND_TTEST_1(cp + 1))
+ return(-1);
+ if ((bitlen = GET_U_1(cp + 1)) == 0)
+ bitlen = 256;
+ return(((bitlen + 7) / 8) + 1);
+ }
+
+ case TYPE_INDIR:
+ case TYPE_LABEL:
+ return(i);
+
+ default:
+ /*
+ * TYPE_RESERVED, but we use default to suppress compiler
+ * warnings about falling out of the switch statement.
+ */
+ ND_PRINT("<BAD LABEL TYPE>");
+ return(-1);
+ }
+}
+
+/* print a <domain-name> */
+const u_char *
+fqdn_print(netdissect_options *ndo,
+ const u_char *cp, const u_char *bp)
+{
+ u_int i, l;
+ const u_char *rp = NULL;
+ int compress = 0;
+ u_int elt;
+ u_int offset, max_offset;
+ u_int name_chars = 0;
+
+ if ((l = labellen(ndo, cp)) == (u_int)-1)
+ return(NULL);
+ if (!ND_TTEST_1(cp))
+ return(NULL);
+ max_offset = (u_int)(cp - bp);
+ i = GET_U_1(cp);
+ cp++;
+ if ((i & TYPE_MASK) != TYPE_INDIR) {
+ compress = 0;
+ rp = cp + l;
+ }
+
+ if (i != 0) {
+ while (i && cp < ndo->ndo_snapend) {
+ switch (i & TYPE_MASK) {
+
+ case TYPE_INDIR:
+ if (!compress) {
+ rp = cp + 1;
+ compress = 1;
+ }
+ if (!ND_TTEST_1(cp))
+ return(NULL);
+ offset = (((i << 8) | GET_U_1(cp)) & 0x3fff);
+ /*
+ * This must move backwards in the packet.
+ * No RFC explicitly says that, but BIND's
+ * name decompression code requires it,
+ * as a way of preventing infinite loops
+ * and other bad behavior, and it's probably
+ * what was intended (compress by pointing
+ * to domain name suffixes already seen in
+ * the packet).
+ */
+ if (offset >= max_offset) {
+ ND_PRINT("<BAD PTR>");
+ return(NULL);
+ }
+ max_offset = offset;
+ cp = bp + offset;
+ if (!ND_TTEST_1(cp))
+ return(NULL);
+ i = GET_U_1(cp);
+ if ((l = labellen(ndo, cp)) == (u_int)-1)
+ return(NULL);
+ cp++;
+ continue;
+
+ case TYPE_EDNS0:
+ elt = (i & ~TYPE_MASK);
+ switch(elt) {
+ case EDNS0_ELT_BITLABEL:
+ if (blabel_print(ndo, cp) == NULL)
+ return (NULL);
+ break;
+ default:
+ /* unknown ELT */
+ ND_PRINT("<ELT %u>", elt);
+ return(NULL);
+ }
+ break;
+
+ case TYPE_RESERVED:
+ ND_PRINT("<BAD LABEL TYPE>");
+ return(NULL);
+
+ case TYPE_LABEL:
+ if (name_chars + l <= MAXCDNAME) {
+ if (nd_printn(ndo, cp, l, ndo->ndo_snapend))
+ return(NULL);
+ } else if (name_chars < MAXCDNAME) {
+ if (nd_printn(ndo, cp,
+ MAXCDNAME - name_chars, ndo->ndo_snapend))
+ return(NULL);
+ }
+ name_chars += l;
+ break;
+ }
+
+ cp += l;
+ if (name_chars <= MAXCDNAME)
+ ND_PRINT(".");
+ name_chars++;
+ if (!ND_TTEST_1(cp))
+ return(NULL);
+ i = GET_U_1(cp);
+ if ((l = labellen(ndo, cp)) == (u_int)-1)
+ return(NULL);
+ cp++;
+ if (!compress)
+ rp += l + 1;
+ }
+ if (name_chars > MAXCDNAME)
+ ND_PRINT("<DOMAIN NAME TOO LONG>");
+ } else
+ ND_PRINT(".");
+ return (rp);
+}
+
+/* print a <character-string> */
+static const u_char *
+ns_cprint(netdissect_options *ndo,
+ const u_char *cp)
+{
+ u_int i;
+
+ if (!ND_TTEST_1(cp))
+ return (NULL);
+ i = GET_U_1(cp);
+ cp++;
+ if (nd_printn(ndo, cp, i, ndo->ndo_snapend))
+ return (NULL);
+ return (cp + i);
+}
+
+static void
+print_eopt_ecs(netdissect_options *ndo, const u_char *cp,
+ u_int data_len)
+{
+ u_int family, addr_bits, src_len, scope_len;
+
+ u_char padded[32];
+ char addr[INET6_ADDRSTRLEN];
+
+ /* ecs option must at least contain family, src len, and scope len */
+ if (data_len < 4) {
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ family = GET_BE_U_2(cp);
+ cp += 2;
+ src_len = GET_U_1(cp);
+ cp += 1;
+ scope_len = GET_U_1(cp);
+ cp += 1;
+
+ if (family == 1)
+ addr_bits = 32;
+ else if (family == 2)
+ addr_bits = 128;
+ else {
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ if (data_len - 4 > (addr_bits / 8)) {
+ nd_print_invalid(ndo);
+ return;
+ }
+ /* checks for invalid ecs scope or source length */
+ if (src_len > addr_bits || scope_len > addr_bits || ((src_len + 7) / 8) != (data_len - 4)) {
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ /* pad the truncated address from ecs with zeros */
+ memset(padded, 0, sizeof(padded));
+ memcpy(padded, cp, data_len - 4);
+
+
+ if (family == 1)
+ ND_PRINT("%s/%d/%d", addrtostr(padded, addr, INET_ADDRSTRLEN),
+ src_len, scope_len);
+ else
+ ND_PRINT("%s/%d/%d", addrtostr6(padded, addr, INET6_ADDRSTRLEN),
+ src_len, scope_len);
+
+}
+
+extern const struct tok edns_opt2str[];
+extern const struct tok dau_alg2str[];
+extern const struct tok dhu_alg2str[];
+extern const struct tok n3u_alg2str[];
+
+
+/* print an <EDNS-option> */
+static const u_char *
+eopt_print(netdissect_options *ndo,
+ const u_char *cp)
+{
+ u_int opt, data_len, i;
+
+ if (!ND_TTEST_2(cp))
+ return (NULL);
+ opt = GET_BE_U_2(cp);
+ cp += 2;
+ ND_PRINT("%s", tok2str(edns_opt2str, "Opt%u", opt));
+ if (!ND_TTEST_2(cp))
+ return (NULL);
+ data_len = GET_BE_U_2(cp);
+ cp += 2;
+
+ ND_TCHECK_LEN(cp, data_len);
+
+ if (data_len > 0) {
+ ND_PRINT(" ");
+ switch (opt) {
+
+ case E_ECS:
+ print_eopt_ecs(ndo, cp, data_len);
+ break;
+ case E_COOKIE:
+ if (data_len < 8 || (data_len > 8 && data_len < 16) || data_len > 40)
+ nd_print_invalid(ndo);
+ else {
+ for (i = 0; i < data_len; ++i) {
+ /* split client and server cookie */
+ if (i == 8)
+ ND_PRINT(" ");
+ ND_PRINT("%02x", GET_U_1(cp + i));
+ }
+ }
+ break;
+ case E_KEEPALIVE:
+ if (data_len != 2)
+ nd_print_invalid(ndo);
+ else
+ /* keepalive is in increments of 100ms. Convert to seconds */
+ ND_PRINT("%0.1f sec", (GET_BE_U_2(cp) / 10.0));
+ break;
+ case E_EXPIRE:
+ if (data_len != 4)
+ nd_print_invalid(ndo);
+ else
+ ND_PRINT("%u sec", GET_BE_U_4(cp));
+ break;
+ case E_PADDING:
+ /* ignore contents and just print length */
+ ND_PRINT("(%u)", data_len);
+ break;
+ case E_KEYTAG:
+ if (data_len % 2 != 0)
+ nd_print_invalid(ndo);
+ else
+ for (i = 0; i < data_len; i += 2) {
+ if (i > 0)
+ ND_PRINT(" ");
+ ND_PRINT("%u", GET_BE_U_2(cp + i));
+ }
+ break;
+ case E_DAU:
+ for (i = 0; i < data_len; ++i) {
+ if (i > 0)
+ ND_PRINT(" ");
+ ND_PRINT("%s", tok2str(dau_alg2str, "Alg_%u", GET_U_1(cp + i)));
+ }
+ break;
+ case E_DHU:
+ for (i = 0; i < data_len; ++i) {
+ if (i > 0)
+ ND_PRINT(" ");
+ ND_PRINT("%s", tok2str(dhu_alg2str, "Alg_%u", GET_U_1(cp + i)));
+ }
+ break;
+ case E_N3U:
+ for (i = 0; i < data_len; ++i) {
+ if (i > 0)
+ ND_PRINT(" ");
+ ND_PRINT("%s", tok2str(n3u_alg2str, "Alg_%u", GET_U_1(cp + i)));
+ }
+ break;
+ case E_CHAIN:
+ fqdn_print(ndo, cp, cp + data_len);
+ break;
+ case E_NSID:
+ /* intentional fall-through. NSID is an undefined byte string */
+ default:
+ for (i = 0; i < data_len; ++i)
+ ND_PRINT("%02x", GET_U_1(cp + i));
+ break;
+ }
+ }
+ return (cp + data_len);
+
+ trunc:
+ return (NULL);
+
+}
+
+
+
+extern const struct tok ns_type2str[];
+
+/* https://www.iana.org/assignments/dns-parameters */
+const struct tok ns_type2str[] = {
+ { T_A, "A" }, /* RFC 1035 */
+ { T_NS, "NS" }, /* RFC 1035 */
+ { T_MD, "MD" }, /* RFC 1035 */
+ { T_MF, "MF" }, /* RFC 1035 */
+ { T_CNAME, "CNAME" }, /* RFC 1035 */
+ { T_SOA, "SOA" }, /* RFC 1035 */
+ { T_MB, "MB" }, /* RFC 1035 */
+ { T_MG, "MG" }, /* RFC 1035 */
+ { T_MR, "MR" }, /* RFC 1035 */
+ { T_NULL, "NULL" }, /* RFC 1035 */
+ { T_WKS, "WKS" }, /* RFC 1035 */
+ { T_PTR, "PTR" }, /* RFC 1035 */
+ { T_HINFO, "HINFO" }, /* RFC 1035 */
+ { T_MINFO, "MINFO" }, /* RFC 1035 */
+ { T_MX, "MX" }, /* RFC 1035 */
+ { T_TXT, "TXT" }, /* RFC 1035 */
+ { T_RP, "RP" }, /* RFC 1183 */
+ { T_AFSDB, "AFSDB" }, /* RFC 1183 */
+ { T_X25, "X25" }, /* RFC 1183 */
+ { T_ISDN, "ISDN" }, /* RFC 1183 */
+ { T_RT, "RT" }, /* RFC 1183 */
+ { T_NSAP, "NSAP" }, /* RFC 1706 */
+ { T_NSAP_PTR, "NSAP_PTR" },
+ { T_SIG, "SIG" }, /* RFC 2535 */
+ { T_KEY, "KEY" }, /* RFC 2535 */
+ { T_PX, "PX" }, /* RFC 2163 */
+ { T_GPOS, "GPOS" }, /* RFC 1712 */
+ { T_AAAA, "AAAA" }, /* RFC 1886 */
+ { T_LOC, "LOC" }, /* RFC 1876 */
+ { T_NXT, "NXT" }, /* RFC 2535 */
+ { T_EID, "EID" }, /* Nimrod */
+ { T_NIMLOC, "NIMLOC" }, /* Nimrod */
+ { T_SRV, "SRV" }, /* RFC 2782 */
+ { T_ATMA, "ATMA" }, /* ATM Forum */
+ { T_NAPTR, "NAPTR" }, /* RFC 2168, RFC 2915 */
+ { T_KX, "KX" }, /* RFC 2230 */
+ { T_CERT, "CERT" }, /* RFC 2538 */
+ { T_A6, "A6" }, /* RFC 2874 */
+ { T_DNAME, "DNAME" }, /* RFC 2672 */
+ { T_SINK, "SINK" },
+ { T_OPT, "OPT" }, /* RFC 2671 */
+ { T_APL, "APL" }, /* RFC 3123 */
+ { T_DS, "DS" }, /* RFC 4034 */
+ { T_SSHFP, "SSHFP" }, /* RFC 4255 */
+ { T_IPSECKEY, "IPSECKEY" }, /* RFC 4025 */
+ { T_RRSIG, "RRSIG" }, /* RFC 4034 */
+ { T_NSEC, "NSEC" }, /* RFC 4034 */
+ { T_DNSKEY, "DNSKEY" }, /* RFC 4034 */
+ { T_SPF, "SPF" }, /* RFC-schlitt-spf-classic-02.txt */
+ { T_UINFO, "UINFO" },
+ { T_UID, "UID" },
+ { T_GID, "GID" },
+ { T_UNSPEC, "UNSPEC" },
+ { T_UNSPECA, "UNSPECA" },
+ { T_TKEY, "TKEY" }, /* RFC 2930 */
+ { T_TSIG, "TSIG" }, /* RFC 2845 */
+ { T_IXFR, "IXFR" }, /* RFC 1995 */
+ { T_AXFR, "AXFR" }, /* RFC 1035 */
+ { T_MAILB, "MAILB" }, /* RFC 1035 */
+ { T_MAILA, "MAILA" }, /* RFC 1035 */
+ { T_ANY, "ANY" },
+ { T_URI, "URI" }, /* RFC 7553 */
+ { 0, NULL }
+};
+
+extern const struct tok ns_class2str[];
+
+const struct tok ns_class2str[] = {
+ { C_IN, "IN" }, /* Not used */
+ { C_CHAOS, "CHAOS" },
+ { C_HS, "HS" },
+ { C_ANY, "ANY" },
+ { 0, NULL }
+};
+
+const struct tok edns_opt2str[] = {
+ { E_LLQ, "LLQ" },
+ { E_UL, "UL" },
+ { E_NSID, "NSID" },
+ { E_DAU, "DAU" },
+ { E_DHU, "DHU" },
+ { E_N3U, "N3U" },
+ { E_ECS, "ECS" },
+ { E_EXPIRE, "EXPIRE" },
+ { E_COOKIE, "COOKIE" },
+ { E_KEEPALIVE, "KEEPALIVE" },
+ { E_PADDING, "PADDING" },
+ { E_CHAIN, "CHAIN" },
+ { E_KEYTAG, "KEY-TAG" },
+ { E_CLIENTTAG, "CLIENT-TAG" },
+ { E_SERVERTAG, "SERVER-TAG" },
+ { 0, NULL }
+};
+
+const struct tok dau_alg2str[] = {
+ { A_DELETE, "DELETE" },
+ { A_RSAMD5, "RSAMD5" },
+ { A_DH, "DH" },
+ { A_DSA, "DS" },
+ { A_RSASHA1, "RSASHA1" },
+ { A_DSA_NSEC3_SHA1, "DSA-NSEC3-SHA1" },
+ { A_RSASHA1_NSEC3_SHA1, "RSASHA1-NSEC3-SHA1" },
+ { A_RSASHA256, "RSASHA256" },
+ { A_RSASHA512, "RSASHA512" },
+ { A_ECC_GOST, "ECC-GOST" },
+ { A_ECDSAP256SHA256, "ECDSAP256SHA256" },
+ { A_ECDSAP384SHA384, "ECDSAP384SHA384" },
+ { A_ED25519, "ED25519" },
+ { A_ED448, "ED448" },
+ { A_INDIRECT, "INDIRECT" },
+ { A_PRIVATEDNS, "PRIVATEDNS" },
+ { A_PRIVATEOID, "PRIVATEOID" },
+ { 0, NULL }
+};
+
+const struct tok dhu_alg2str[] = {
+ { DS_SHA1, "SHA-1" },
+ { DS_SHA256,"SHA-256" },
+ { DS_GOST, "GOST_R_34.11-94" },
+ { DS_SHA384,"SHA-384" },
+ { 0, NULL }
+};
+
+const struct tok n3u_alg2str[] = {
+ { NSEC_SHA1,"SHA-1" },
+ { 0, NULL }
+};
+
+/* print a query */
+static const u_char *
+ns_qprint(netdissect_options *ndo,
+ const u_char *cp, const u_char *bp, int is_mdns)
+{
+ const u_char *np = cp;
+ u_int i, class;
+
+ cp = ns_nskip(ndo, cp);
+
+ if (cp == NULL || !ND_TTEST_4(cp))
+ return(NULL);
+
+ /* print the qtype */
+ i = GET_BE_U_2(cp);
+ cp += 2;
+ ND_PRINT(" %s", tok2str(ns_type2str, "Type%u", i));
+ /* print the qclass (if it's not IN) */
+ i = GET_BE_U_2(cp);
+ cp += 2;
+ if (is_mdns)
+ class = (i & ~C_QU);
+ else
+ class = i;
+ if (class != C_IN)
+ ND_PRINT(" %s", tok2str(ns_class2str, "(Class %u)", class));
+ if (is_mdns) {
+ ND_PRINT(i & C_QU ? " (QU)" : " (QM)");
+ }
+
+ ND_PRINT("? ");
+ cp = fqdn_print(ndo, np, bp);
+ return(cp ? cp + 4 : NULL);
+}
+
+/* print a reply */
+static const u_char *
+ns_rprint(netdissect_options *ndo,
+ const u_char *cp, const u_char *bp, int is_mdns)
+{
+ u_int i, class, opt_flags = 0;
+ u_short typ, len;
+ const u_char *rp;
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" ");
+ if ((cp = fqdn_print(ndo, cp, bp)) == NULL)
+ return NULL;
+ } else
+ cp = ns_nskip(ndo, cp);
+
+ if (cp == NULL || !ND_TTEST_LEN(cp, 10))
+ return (ndo->ndo_snapend);
+
+ /* print the type/qtype */
+ typ = GET_BE_U_2(cp);
+ cp += 2;
+ /* print the class (if it's not IN and the type isn't OPT) */
+ i = GET_BE_U_2(cp);
+ cp += 2;
+ if (is_mdns)
+ class = (i & ~C_CACHE_FLUSH);
+ else
+ class = i;
+ if (class != C_IN && typ != T_OPT)
+ ND_PRINT(" %s", tok2str(ns_class2str, "(Class %u)", class));
+ if (is_mdns) {
+ if (i & C_CACHE_FLUSH)
+ ND_PRINT(" (Cache flush)");
+ }
+
+ if (typ == T_OPT) {
+ /* get opt flags */
+ cp += 2;
+ opt_flags = GET_BE_U_2(cp);
+ /* ignore rest of ttl field */
+ cp += 2;
+ } else if (ndo->ndo_vflag > 2) {
+ /* print ttl */
+ ND_PRINT(" [");
+ unsigned_relts_print(ndo, GET_BE_U_4(cp));
+ ND_PRINT("]");
+ cp += 4;
+ } else {
+ /* ignore ttl */
+ cp += 4;
+ }
+
+ len = GET_BE_U_2(cp);
+ cp += 2;
+
+ rp = cp + len;
+
+ ND_PRINT(" %s", tok2str(ns_type2str, "Type%u", typ));
+ if (rp > ndo->ndo_snapend)
+ return(NULL);
+
+ switch (typ) {
+ case T_A:
+ if (!ND_TTEST_LEN(cp, sizeof(nd_ipv4)))
+ return(NULL);
+ ND_PRINT(" %s", intoa(GET_IPV4_TO_NETWORK_ORDER(cp)));
+ break;
+
+ case T_NS:
+ case T_CNAME:
+ case T_PTR:
+ case T_DNAME:
+ ND_PRINT(" ");
+ if (fqdn_print(ndo, cp, bp) == NULL)
+ return(NULL);
+ break;
+
+ case T_SOA:
+ if (!ndo->ndo_vflag)
+ break;
+ ND_PRINT(" ");
+ if ((cp = fqdn_print(ndo, cp, bp)) == NULL)
+ return(NULL);
+ ND_PRINT(" ");
+ if ((cp = fqdn_print(ndo, cp, bp)) == NULL)
+ return(NULL);
+ if (!ND_TTEST_LEN(cp, 5 * 4))
+ return(NULL);
+ ND_PRINT(" %u", GET_BE_U_4(cp));
+ cp += 4;
+ ND_PRINT(" %u", GET_BE_U_4(cp));
+ cp += 4;
+ ND_PRINT(" %u", GET_BE_U_4(cp));
+ cp += 4;
+ ND_PRINT(" %u", GET_BE_U_4(cp));
+ cp += 4;
+ ND_PRINT(" %u", GET_BE_U_4(cp));
+ cp += 4;
+ break;
+ case T_MX:
+ ND_PRINT(" ");
+ if (!ND_TTEST_2(cp))
+ return(NULL);
+ if (fqdn_print(ndo, cp + 2, bp) == NULL)
+ return(NULL);
+ ND_PRINT(" %u", GET_BE_U_2(cp));
+ break;
+
+ case T_TXT:
+ while (cp < rp) {
+ ND_PRINT(" \"");
+ cp = ns_cprint(ndo, cp);
+ if (cp == NULL)
+ return(NULL);
+ ND_PRINT("\"");
+ }
+ break;
+
+ case T_SRV:
+ ND_PRINT(" ");
+ if (!ND_TTEST_6(cp))
+ return(NULL);
+ if (fqdn_print(ndo, cp + 6, bp) == NULL)
+ return(NULL);
+ ND_PRINT(":%u %u %u", GET_BE_U_2(cp + 4),
+ GET_BE_U_2(cp), GET_BE_U_2(cp + 2));
+ break;
+
+ case T_AAAA:
+ {
+ char ntop_buf[INET6_ADDRSTRLEN];
+
+ if (!ND_TTEST_LEN(cp, sizeof(nd_ipv6)))
+ return(NULL);
+ ND_PRINT(" %s",
+ addrtostr6(cp, ntop_buf, sizeof(ntop_buf)));
+
+ break;
+ }
+
+ case T_A6:
+ {
+ nd_ipv6 a;
+ int pbit, pbyte;
+ char ntop_buf[INET6_ADDRSTRLEN];
+
+ if (!ND_TTEST_1(cp))
+ return(NULL);
+ pbit = GET_U_1(cp);
+ pbyte = (pbit & ~7) / 8;
+ if (pbit > 128) {
+ ND_PRINT(" %u(bad plen)", pbit);
+ break;
+ } else if (pbit < 128) {
+ if (!ND_TTEST_LEN(cp + 1, sizeof(a) - pbyte))
+ return(NULL);
+ memset(a, 0, sizeof(a));
+ memcpy(a + pbyte, cp + 1, sizeof(a) - pbyte);
+ ND_PRINT(" %u %s", pbit,
+ addrtostr6(&a, ntop_buf, sizeof(ntop_buf)));
+ }
+ if (pbit > 0) {
+ ND_PRINT(" ");
+ if (fqdn_print(ndo, cp + 1 + sizeof(a) - pbyte, bp) == NULL)
+ return(NULL);
+ }
+ break;
+ }
+
+ case T_URI:
+ if (!ND_TTEST_LEN(cp, len))
+ return(NULL);
+ ND_PRINT(" %u %u ", GET_BE_U_2(cp), GET_BE_U_2(cp + 2));
+ if (nd_printn(ndo, cp + 4, len - 4, ndo->ndo_snapend))
+ return(NULL);
+ break;
+
+ case T_OPT:
+ ND_PRINT(" UDPsize=%u", class);
+ if (opt_flags & 0x8000)
+ ND_PRINT(" DO");
+ if (cp < rp) {
+ ND_PRINT(" [");
+ while (cp < rp) {
+ cp = eopt_print(ndo, cp);
+ if (cp == NULL)
+ return(NULL);
+ if (cp < rp)
+ ND_PRINT(",");
+ }
+ ND_PRINT("]");
+ }
+ break;
+
+ case T_UNSPECA: /* One long string */
+ if (!ND_TTEST_LEN(cp, len))
+ return(NULL);
+ if (nd_printn(ndo, cp, len, ndo->ndo_snapend))
+ return(NULL);
+ break;
+
+ case T_TSIG:
+ {
+ if (cp + len > ndo->ndo_snapend)
+ return(NULL);
+ if (!ndo->ndo_vflag)
+ break;
+ ND_PRINT(" ");
+ if ((cp = fqdn_print(ndo, cp, bp)) == NULL)
+ return(NULL);
+ cp += 6;
+ if (!ND_TTEST_2(cp))
+ return(NULL);
+ ND_PRINT(" fudge=%u", GET_BE_U_2(cp));
+ cp += 2;
+ if (!ND_TTEST_2(cp))
+ return(NULL);
+ ND_PRINT(" maclen=%u", GET_BE_U_2(cp));
+ cp += 2 + GET_BE_U_2(cp);
+ if (!ND_TTEST_2(cp))
+ return(NULL);
+ ND_PRINT(" origid=%u", GET_BE_U_2(cp));
+ cp += 2;
+ if (!ND_TTEST_2(cp))
+ return(NULL);
+ ND_PRINT(" error=%u", GET_BE_U_2(cp));
+ cp += 2;
+ if (!ND_TTEST_2(cp))
+ return(NULL);
+ ND_PRINT(" otherlen=%u", GET_BE_U_2(cp));
+ cp += 2;
+ }
+ }
+ return (rp); /* XXX This isn't always right */
+}
+
+void
+domain_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, int over_tcp, int is_mdns)
+{
+ const dns_header_t *np;
+ uint16_t flags, rcode, rdlen, type;
+ u_int qdcount, ancount, nscount, arcount;
+ u_int i;
+ const u_char *cp;
+ uint16_t b2;
+
+ ndo->ndo_protocol = "domain";
+
+ if (over_tcp) {
+ /*
+ * The message is prefixed with a two byte length field
+ * which gives the message length, excluding the two byte
+ * length field. (RFC 1035 - 4.2.2. TCP usage)
+ */
+ if (length < 2) {
+ ND_PRINT(" [DNS over TCP: length %u < 2]", length);
+ nd_print_invalid(ndo);
+ return;
+ } else {
+ length -= 2; /* excluding the two byte length field */
+ if (GET_BE_U_2(bp) != length) {
+ ND_PRINT(" [prefix length(%u) != length(%u)]",
+ GET_BE_U_2(bp), length);
+ nd_print_invalid(ndo);
+ return;
+ } else {
+ bp += 2;
+ /* in over TCP case, we need to prepend a space
+ * (not needed in over UDP case)
+ */
+ ND_PRINT(" ");
+ }
+ }
+ }
+
+ np = (const dns_header_t *)bp;
+
+ if(length < sizeof(*np)) {
+ nd_print_protocol(ndo);
+ ND_PRINT(" [length %u < %zu]", length, sizeof(*np));
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ ND_TCHECK_SIZE(np);
+ flags = GET_BE_U_2(np->flags);
+ /* get the byte-order right */
+ qdcount = GET_BE_U_2(np->qdcount);
+ ancount = GET_BE_U_2(np->ancount);
+ nscount = GET_BE_U_2(np->nscount);
+ arcount = GET_BE_U_2(np->arcount);
+
+ /* find the opt record to extract extended rcode */
+ cp = (const u_char *)(np + 1);
+ rcode = DNS_RCODE(flags);
+ for (i = 0; i < qdcount; i++) {
+ if ((cp = ns_nskip(ndo, cp)) == NULL)
+ goto print;
+ cp += 4; /* skip QTYPE and QCLASS */
+ if (cp >= ndo->ndo_snapend)
+ goto print;
+ }
+ for (i = 0; i < ancount + nscount; i++) {
+ if ((cp = ns_nskip(ndo, cp)) == NULL)
+ goto print;
+ cp += 8; /* skip TYPE, CLASS and TTL */
+ if (cp + 2 > ndo->ndo_snapend)
+ goto print;
+ rdlen = GET_BE_U_2(cp);
+ cp += 2 + rdlen;
+ if (cp >= ndo->ndo_snapend)
+ goto print;
+ }
+ for (i = 0; i < arcount; i++) {
+ if ((cp = ns_nskip(ndo, cp)) == NULL)
+ goto print;
+ if (cp + 2 > ndo->ndo_snapend)
+ goto print;
+ type = GET_BE_U_2(cp);
+ cp += 4; /* skip TYPE and CLASS */
+ if (cp + 1 > ndo->ndo_snapend)
+ goto print;
+ if (type == T_OPT) {
+ rcode |= (GET_U_1(cp) << 4);
+ goto print;
+ }
+ cp += 4;
+ if (cp + 2 > ndo->ndo_snapend)
+ goto print;
+ rdlen = GET_BE_U_2(cp);
+ cp += 2 + rdlen;
+ if (cp >= ndo->ndo_snapend)
+ goto print;
+ }
+
+ print:
+ if (DNS_QR(flags)) {
+ /* this is a response */
+ ND_PRINT("%u%s%s%s%s%s%s",
+ GET_BE_U_2(np->id),
+ ns_ops[DNS_OPCODE(flags)],
+ ns_rcode(rcode),
+ DNS_AA(flags)? "*" : "",
+ DNS_RA(flags)? "" : "-",
+ DNS_TC(flags)? "|" : "",
+ DNS_AD(flags)? "$" : "");
+
+ if (qdcount != 1)
+ ND_PRINT(" [%uq]", qdcount);
+ /* Print QUESTION section on -vv */
+ cp = (const u_char *)(np + 1);
+ for (i = 0; i < qdcount; i++) {
+ if (i != 0)
+ ND_PRINT(",");
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT(" q:");
+ if ((cp = ns_qprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ } else {
+ if ((cp = ns_nskip(ndo, cp)) == NULL)
+ goto trunc;
+ cp += 4; /* skip QTYPE and QCLASS */
+ }
+ }
+ ND_PRINT(" %u/%u/%u", ancount, nscount, arcount);
+ if (ancount) {
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ ancount--;
+ while (cp < ndo->ndo_snapend && ancount) {
+ ND_PRINT(",");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ ancount--;
+ }
+ }
+ if (ancount)
+ goto trunc;
+ /* Print NS and AR sections on -vv */
+ if (ndo->ndo_vflag > 1) {
+ if (cp < ndo->ndo_snapend && nscount) {
+ ND_PRINT(" ns:");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ nscount--;
+ while (cp < ndo->ndo_snapend && nscount) {
+ ND_PRINT(",");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ nscount--;
+ }
+ }
+ if (nscount)
+ goto trunc;
+ if (cp < ndo->ndo_snapend && arcount) {
+ ND_PRINT(" ar:");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ arcount--;
+ while (cp < ndo->ndo_snapend && arcount) {
+ ND_PRINT(",");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ arcount--;
+ }
+ }
+ if (arcount)
+ goto trunc;
+ }
+ }
+ else {
+ /* this is a request */
+ ND_PRINT("%u%s%s%s", GET_BE_U_2(np->id),
+ ns_ops[DNS_OPCODE(flags)],
+ DNS_RD(flags) ? "+" : "",
+ DNS_CD(flags) ? "%" : "");
+
+ /* any weirdness? */
+ b2 = GET_BE_U_2(((const u_short *)np) + 1);
+ if (b2 & 0x6cf)
+ ND_PRINT(" [b2&3=0x%x]", b2);
+
+ if (DNS_OPCODE(flags) == IQUERY) {
+ if (qdcount)
+ ND_PRINT(" [%uq]", qdcount);
+ if (ancount != 1)
+ ND_PRINT(" [%ua]", ancount);
+ }
+ else {
+ if (ancount)
+ ND_PRINT(" [%ua]", ancount);
+ if (qdcount != 1)
+ ND_PRINT(" [%uq]", qdcount);
+ }
+ if (nscount)
+ ND_PRINT(" [%un]", nscount);
+ if (arcount)
+ ND_PRINT(" [%uau]", arcount);
+
+ cp = (const u_char *)(np + 1);
+ if (qdcount) {
+ cp = ns_qprint(ndo, cp, (const u_char *)np, is_mdns);
+ if (!cp)
+ goto trunc;
+ qdcount--;
+ while (cp < ndo->ndo_snapend && qdcount) {
+ cp = ns_qprint(ndo, (const u_char *)cp,
+ (const u_char *)np,
+ is_mdns);
+ if (!cp)
+ goto trunc;
+ qdcount--;
+ }
+ }
+ if (qdcount)
+ goto trunc;
+
+ /* Print remaining sections on -vv */
+ if (ndo->ndo_vflag > 1) {
+ if (ancount) {
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ ancount--;
+ while (cp < ndo->ndo_snapend && ancount) {
+ ND_PRINT(",");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ ancount--;
+ }
+ }
+ if (ancount)
+ goto trunc;
+ if (cp < ndo->ndo_snapend && nscount) {
+ ND_PRINT(" ns:");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ nscount--;
+ while (cp < ndo->ndo_snapend && nscount) {
+ ND_PRINT(",");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ nscount--;
+ }
+ }
+ if (nscount > 0)
+ goto trunc;
+ if (cp < ndo->ndo_snapend && arcount) {
+ ND_PRINT(" ar:");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ arcount--;
+ while (cp < ndo->ndo_snapend && arcount) {
+ ND_PRINT(",");
+ if ((cp = ns_rprint(ndo, cp, bp, is_mdns)) == NULL)
+ goto trunc;
+ arcount--;
+ }
+ }
+ if (arcount)
+ goto trunc;
+ }
+ }
+ ND_PRINT(" (%u)", length);
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-dsa.c b/print-dsa.c
new file mode 100644
index 0000000..e45dc53
--- /dev/null
+++ b/print-dsa.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Marvell (Ethertype) Distributed Switch Architecture printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "ethertype.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+/*
+ * Format of (Ethertyped or not) DSA tagged frames:
+ *
+ * 7 6 5 4 3 2 1 0
+ * . . . . . . . . .
+ * 0 +---+---+---+---+---+---+---+---+
+ * | Ether Destination Address |
+ * +6 +---+---+---+---+---+---+---+---+
+ * | Ether Source Address |
+ * +6 +---+---+---+---+---+---+---+---+ +-
+ * | Prog. DSA Ether Type [15:8] | | (8-byte) EDSA Tag
+ * +1 +---+---+---+---+---+---+---+---+ | Contains a programmable Ether type,
+ * | Prog. DSA Ether Type [7:0] | | two reserved bytes (always 0),
+ * +1 +---+---+---+---+---+---+---+---+ | and a standard DSA tag.
+ * | Reserved (0x00 0x00) | |
+ * +2 +---+---+---+---+---+---+---+---+ | +-
+ * | Mode |b29| Src/Trg Dev | | | (4-byte) DSA Tag
+ * +1 +---+---+---+---+---+---+---+---+ | | Contains a DSA tag mode,
+ * |Src/Trg Port/Trunk |b18|b17|b16| | | source or target switch device,
+ * +1 +---+---+---+---+---+---+---+---+ | | source or target port or trunk,
+ * | PRI [2:0] |b12| VID [11:8] | | | and misc (IEEE and FPri) bits.
+ * +1 +---+---+---+---+---+---+---+---+ | |
+ * | VID [7:0] | | |
+ * +1 +---+---+---+---+---+---+---+---+ +- +-
+ * | Ether Length/Type |
+ * +2 +---+---+---+---+---+---+---+---+
+ * . . . . . . . . .
+ *
+ * Mode: Forward, To_CPU, From_CPU, To_Sniffer
+ * b29: (Source or Target) IEEE Tag Mode
+ * b18: Forward's Src_Is_Trunk, To_CPU's Code[2], To_Sniffer's Rx_Sniff
+ * b17: To_CPU's Code[1]
+ * b16: Original frame's CFI
+ * b12: To_CPU's Code[0]
+ */
+
+#define TOK(tag, byte, mask, shift) ((GET_U_1(&(((const u_char *) tag)[byte])) & (mask)) >> (shift))
+
+#define DSA_LEN 4
+#define DSA_MODE(tag) TOK(tag, 0, 0xc0, 6)
+#define DSA_MODE_TO_CPU 0x0
+#define DSA_MODE_FROM_CPU 0x1
+#define DSA_MODE_TO_SNIFFER 0x2
+#define DSA_MODE_FORWARD 0x3
+#define DSA_TAGGED(tag) TOK(tag, 0, 0x20, 5)
+#define DSA_DEV(tag) TOK(tag, 0, 0x1f, 0)
+#define DSA_PORT(tag) TOK(tag, 1, 0xf8, 3)
+#define DSA_TRUNK(tag) TOK(tag, 1, 0x04, 2)
+#define DSA_RX_SNIFF(tag) TOK(tag, 1, 0x04, 2)
+#define DSA_CFI(tag) TOK(tag, 1, 0x01, 0)
+#define DSA_PRI(tag) TOK(tag, 2, 0xe0, 5)
+#define DSA_VID(tag) ((u_short)((TOK(tag, 2, 0xe0, 5) << 8) | (TOK(tag, 3, 0xff, 0))))
+#define DSA_CODE(tag) ((TOK(tag, 1, 0x06, 1) << 1) | TOK(tag, 2, 0x10, 4))
+
+#define EDSA_LEN 8
+
+static const struct tok dsa_mode_values[] = {
+ { DSA_MODE_TO_CPU, "To CPU" },
+ { DSA_MODE_FROM_CPU, "From CPU" },
+ { DSA_MODE_TO_SNIFFER, "To Sniffer"},
+ { DSA_MODE_FORWARD, "Forward" },
+ { 0, NULL }
+};
+
+static const struct tok dsa_code_values[] = {
+ { 0x0, "BPDU (MGMT) Trap" },
+ { 0x1, "Frame2Reg" },
+ { 0x2, "IGMP/MLD Trap" },
+ { 0x3, "Policy Trap" },
+ { 0x4, "ARP Mirror" },
+ { 0x5, "Policy Mirror" },
+ { 0, NULL }
+};
+
+static void
+tag_common_print(netdissect_options *ndo, const u_char *p)
+{
+ if (ndo->ndo_eflag) {
+ ND_PRINT("mode %s, ", tok2str(dsa_mode_values, "unknown", DSA_MODE(p)));
+
+ switch (DSA_MODE(p)) {
+ case DSA_MODE_FORWARD:
+ ND_PRINT("dev %u, %s %u, ", DSA_DEV(p),
+ DSA_TRUNK(p) ? "trunk" : "port", DSA_PORT(p));
+ break;
+ case DSA_MODE_FROM_CPU:
+ ND_PRINT("target dev %u, port %u, ",
+ DSA_DEV(p), DSA_PORT(p));
+ break;
+ case DSA_MODE_TO_CPU:
+ ND_PRINT("source dev %u, port %u, ",
+ DSA_DEV(p), DSA_PORT(p));
+ ND_PRINT("code %s, ",
+ tok2str(dsa_code_values, "reserved", DSA_CODE(p)));
+ break;
+ case DSA_MODE_TO_SNIFFER:
+ ND_PRINT("source dev %u, port %u, ",
+ DSA_DEV(p), DSA_PORT(p));
+ ND_PRINT("%s sniff, ",
+ DSA_RX_SNIFF(p) ? "ingress" : "egress");
+ break;
+ default:
+ break;
+ }
+
+ ND_PRINT("%s, ", DSA_TAGGED(p) ? "tagged" : "untagged");
+ ND_PRINT("%s", DSA_CFI(p) ? "CFI, " : "");
+ ND_PRINT("VID %u, ", DSA_VID(p));
+ ND_PRINT("FPri %u, ", DSA_PRI(p));
+ } else {
+ switch (DSA_MODE(p)) {
+ case DSA_MODE_FORWARD:
+ ND_PRINT("Forward %s %u.%u, ",
+ DSA_TRUNK(p) ? "trunk" : "port",
+ DSA_DEV(p), DSA_PORT(p));
+ break;
+ case DSA_MODE_FROM_CPU:
+ ND_PRINT("CPU > port %u.%u, ",
+ DSA_DEV(p), DSA_PORT(p));
+ break;
+ case DSA_MODE_TO_CPU:
+ ND_PRINT("port %u.%u > CPU, ",
+ DSA_DEV(p), DSA_PORT(p));
+ break;
+ case DSA_MODE_TO_SNIFFER:
+ ND_PRINT("port %u.%u > %s Sniffer, ",
+ DSA_DEV(p), DSA_PORT(p),
+ DSA_RX_SNIFF(p) ? "Rx" : "Tx");
+ break;
+ default:
+ break;
+ }
+
+ ND_PRINT("VLAN %u%c, ", DSA_VID(p), DSA_TAGGED(p) ? 't' : 'u');
+ }
+}
+
+static void
+dsa_tag_print(netdissect_options *ndo, const u_char *bp)
+{
+ if (ndo->ndo_eflag)
+ ND_PRINT("Marvell DSA ");
+ else
+ ND_PRINT("DSA ");
+ tag_common_print(ndo, bp);
+}
+
+static void
+edsa_tag_print(netdissect_options *ndo, const u_char *bp)
+{
+ const u_char *p = bp;
+ uint16_t edsa_etype;
+
+ edsa_etype = GET_BE_U_2(p);
+ if (ndo->ndo_eflag) {
+ ND_PRINT("Marvell EDSA ethertype 0x%04x (%s), ", edsa_etype,
+ tok2str(ethertype_values, "Unknown", edsa_etype));
+ ND_PRINT("rsvd %u %u, ", GET_U_1(p + 2), GET_U_1(p + 3));
+ } else
+ ND_PRINT("EDSA 0x%04x, ", edsa_etype);
+ p += 4;
+ tag_common_print(ndo, p);
+}
+
+void
+dsa_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+
+ ndo->ndo_protocol = "dsa";
+ ndo->ndo_ll_hdr_len +=
+ ether_switch_tag_print(ndo, p, length, caplen, dsa_tag_print, DSA_LEN);
+}
+
+void
+edsa_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+
+ ndo->ndo_protocol = "edsa";
+ ndo->ndo_ll_hdr_len +=
+ ether_switch_tag_print(ndo, p, length, caplen, edsa_tag_print, EDSA_LEN);
+}
diff --git a/print-dtp.c b/print-dtp.c
new file mode 100644
index 0000000..a1ae4ba
--- /dev/null
+++ b/print-dtp.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1998-2007 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Carles Kishimoto <carles.kishimoto@gmail.com>
+ */
+
+/* \summary: Dynamic Trunking Protocol (DTP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+
+#define DTP_HEADER_LEN 1
+#define DTP_DOMAIN_TLV 0x0001
+#define DTP_STATUS_TLV 0x0002
+#define DTP_DTP_TYPE_TLV 0x0003
+#define DTP_NEIGHBOR_TLV 0x0004
+
+static const struct tok dtp_tlv_values[] = {
+ { DTP_DOMAIN_TLV, "Domain" },
+ { DTP_STATUS_TLV, "Status" },
+ { DTP_DTP_TYPE_TLV, "DTP type" },
+ { DTP_NEIGHBOR_TLV, "Neighbor" },
+ { 0, NULL}
+};
+
+void
+dtp_print(netdissect_options *ndo, const u_char *tptr, u_int length)
+{
+ ndo->ndo_protocol = "dtp";
+ if (length < DTP_HEADER_LEN) {
+ ND_PRINT("[zero packet length]");
+ goto invalid;
+ }
+
+ ND_PRINT("DTPv%u, length %u",
+ GET_U_1(tptr),
+ length);
+
+ /*
+ * In non-verbose mode, just print version.
+ */
+ if (ndo->ndo_vflag < 1) {
+ return;
+ }
+
+ tptr += DTP_HEADER_LEN;
+ length -= DTP_HEADER_LEN;
+
+ while (length) {
+ uint16_t type, len;
+
+ if (length < 4) {
+ ND_PRINT("[%u bytes remaining]", length);
+ goto invalid;
+ }
+ type = GET_BE_U_2(tptr);
+ len = GET_BE_U_2(tptr + 2);
+ /* XXX: should not be but sometimes it is, see the test captures */
+ if (type == 0)
+ return;
+ ND_PRINT("\n\t%s (0x%04x) TLV, length %u",
+ tok2str(dtp_tlv_values, "Unknown", type),
+ type, len);
+
+ /* infinite loop check */
+ if (len < 4 || len > length) {
+ ND_PRINT("[invalid TLV length %u]", len);
+ goto invalid;
+ }
+
+ switch (type) {
+ case DTP_DOMAIN_TLV:
+ ND_PRINT(", ");
+ nd_printjnp(ndo, tptr+4, len-4);
+ break;
+
+ case DTP_STATUS_TLV:
+ case DTP_DTP_TYPE_TLV:
+ if (len != 5)
+ goto invalid;
+ ND_PRINT(", 0x%x", GET_U_1(tptr + 4));
+ break;
+
+ case DTP_NEIGHBOR_TLV:
+ if (len != 10)
+ goto invalid;
+ ND_PRINT(", %s", GET_ETHERADDR_STRING(tptr+4));
+ break;
+
+ default:
+ ND_TCHECK_LEN(tptr, len);
+ break;
+ }
+ tptr += len;
+ length -= len;
+ }
+ return;
+
+ invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(tptr, length);
+}
diff --git a/print-dvmrp.c b/print-dvmrp.c
new file mode 100644
index 0000000..7d7ca07
--- /dev/null
+++ b/print-dvmrp.c
@@ -0,0 +1,332 @@
+/*
+ * Copyright (c) 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Distance Vector Multicast Routing Protocol printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+/*
+ * See: RFC 1075 and draft-ietf-idmr-dvmrp-v3
+ *
+ * DVMRP message types and flag values shamelessly stolen from
+ * mrouted/dvmrp.h.
+ */
+#define DVMRP_PROBE 1 /* for finding neighbors */
+#define DVMRP_REPORT 2 /* for reporting some or all routes */
+#define DVMRP_ASK_NEIGHBORS 3 /* sent by mapper, asking for a list */
+ /* of this router's neighbors */
+#define DVMRP_NEIGHBORS 4 /* response to such a request */
+#define DVMRP_ASK_NEIGHBORS2 5 /* as above, want new format reply */
+#define DVMRP_NEIGHBORS2 6
+#define DVMRP_PRUNE 7 /* prune message */
+#define DVMRP_GRAFT 8 /* graft message */
+#define DVMRP_GRAFT_ACK 9 /* graft acknowledgement */
+static const struct tok dvmrp_msgtype_str[] = {
+ { DVMRP_PROBE, "Probe" },
+ { DVMRP_REPORT, "Report" },
+ { DVMRP_ASK_NEIGHBORS, "Ask-neighbors(old)" },
+ { DVMRP_NEIGHBORS, "Neighbors(old)" },
+ { DVMRP_ASK_NEIGHBORS2, "Ask-neighbors2" },
+ { DVMRP_NEIGHBORS2, "Neighbors2" },
+ { DVMRP_PRUNE, "Prune" },
+ { DVMRP_GRAFT, "Graft" },
+ { DVMRP_GRAFT_ACK, "Graft-ACK" },
+ { 0, NULL }
+};
+
+/*
+ * 'flags' byte values in DVMRP_NEIGHBORS2 reply.
+ */
+#define DVMRP_NF_TUNNEL 0x01 /* neighbors reached via tunnel */
+#define DVMRP_NF_SRCRT 0x02 /* tunnel uses IP source routing */
+#define DVMRP_NF_DOWN 0x10 /* kernel state of interface */
+#define DVMRP_NF_DISABLED 0x20 /* administratively disabled */
+#define DVMRP_NF_QUERIER 0x40 /* I am the subnet's querier */
+
+static void print_probe(netdissect_options *, const u_char *, u_int);
+static void print_report(netdissect_options *, const u_char *, u_int);
+static void print_neighbors(netdissect_options *, const u_char *, u_int);
+static void print_neighbors2(netdissect_options *, const u_char *, u_int, uint8_t, uint8_t);
+
+void
+dvmrp_print(netdissect_options *ndo,
+ const u_char *bp, u_int len)
+{
+ u_char type;
+ uint8_t major_version, minor_version;
+
+ ndo->ndo_protocol = "dvmrp";
+ if (len < 8) {
+ ND_PRINT(" [length %u < 8]", len);
+ goto invalid;
+ }
+
+ type = GET_U_1(bp + 1);
+
+ /* Skip IGMP header */
+ bp += 8;
+ len -= 8;
+
+ ND_PRINT(" %s", tok2str(dvmrp_msgtype_str, "[type %u]", type));
+ switch (type) {
+
+ case DVMRP_PROBE:
+ if (ndo->ndo_vflag) {
+ print_probe(ndo, bp, len);
+ }
+ break;
+
+ case DVMRP_REPORT:
+ if (ndo->ndo_vflag > 1) {
+ print_report(ndo, bp, len);
+ }
+ break;
+
+ case DVMRP_NEIGHBORS:
+ print_neighbors(ndo, bp, len);
+ break;
+
+ case DVMRP_NEIGHBORS2:
+ /*
+ * extract version from IGMP group address field
+ */
+ bp -= 4;
+ major_version = GET_U_1(bp + 3);
+ minor_version = GET_U_1(bp + 2);
+ bp += 4;
+ print_neighbors2(ndo, bp, len, major_version, minor_version);
+ break;
+
+ case DVMRP_PRUNE:
+ ND_PRINT(" src %s grp %s", GET_IPADDR_STRING(bp), GET_IPADDR_STRING(bp + 4));
+ ND_PRINT(" timer ");
+ unsigned_relts_print(ndo, GET_BE_U_4(bp + 8));
+ break;
+
+ case DVMRP_GRAFT:
+ ND_PRINT(" src %s grp %s", GET_IPADDR_STRING(bp), GET_IPADDR_STRING(bp + 4));
+ break;
+
+ case DVMRP_GRAFT_ACK:
+ ND_PRINT(" src %s grp %s", GET_IPADDR_STRING(bp), GET_IPADDR_STRING(bp + 4));
+ break;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+
+static void
+print_report(netdissect_options *ndo,
+ const u_char *bp,
+ u_int len)
+{
+ uint32_t mask, origin;
+ u_int metric, done;
+ u_int i, width;
+
+ while (len > 0) {
+ if (len < 3) {
+ ND_PRINT(" [length %u < 3]", len);
+ goto invalid;
+ }
+ mask = (uint32_t)0xff << 24 | GET_U_1(bp) << 16 |
+ GET_U_1(bp + 1) << 8 | GET_U_1(bp + 2);
+ width = 1;
+ if (GET_U_1(bp))
+ width = 2;
+ if (GET_U_1(bp + 1))
+ width = 3;
+ if (GET_U_1(bp + 2))
+ width = 4;
+
+ ND_PRINT("\n\tMask %s", intoa(htonl(mask)));
+ bp += 3;
+ len -= 3;
+ do {
+ if (len < width + 1) {
+ ND_PRINT("\n\t [Truncated Report]");
+ goto invalid;
+ }
+ origin = 0;
+ for (i = 0; i < width; ++i) {
+ origin = origin << 8 | GET_U_1(bp);
+ bp++;
+ }
+ for ( ; i < 4; ++i)
+ origin <<= 8;
+
+ metric = GET_U_1(bp);
+ bp++;
+ done = metric & 0x80;
+ metric &= 0x7f;
+ ND_PRINT("\n\t %s metric %u", intoa(htonl(origin)),
+ metric);
+ len -= width + 1;
+ } while (!done);
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+
+static void
+print_probe(netdissect_options *ndo,
+ const u_char *bp,
+ u_int len)
+{
+ if (len < 4) {
+ ND_PRINT(" [full length %u < 4]", len);
+ goto invalid;
+ }
+ ND_PRINT(ndo->ndo_vflag > 1 ? "\n\t" : " ");
+ ND_PRINT("genid %u", GET_BE_U_4(bp));
+ if (ndo->ndo_vflag < 2)
+ return;
+
+ bp += 4;
+ len -= 4;
+ while (len > 0) {
+ if (len < 4) {
+ ND_PRINT("[remaining length %u < 4]", len);
+ goto invalid;
+ }
+ ND_PRINT("\n\tneighbor %s", GET_IPADDR_STRING(bp));
+ bp += 4; len -= 4;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+
+static void
+print_neighbors(netdissect_options *ndo,
+ const u_char *bp,
+ u_int len)
+{
+ const u_char *laddr;
+ u_char metric;
+ u_char thresh;
+ int ncount;
+
+ while (len > 0) {
+ if (len < 7) {
+ ND_PRINT(" [length %u < 7]", len);
+ goto invalid;
+ }
+ laddr = bp;
+ bp += 4;
+ metric = GET_U_1(bp);
+ bp++;
+ thresh = GET_U_1(bp);
+ bp++;
+ ncount = GET_U_1(bp);
+ bp++;
+ len -= 7;
+ while (--ncount >= 0) {
+ if (len < 4) {
+ ND_PRINT(" [length %u < 4]", len);
+ goto invalid;
+ }
+ ND_PRINT(" [%s ->", GET_IPADDR_STRING(laddr));
+ ND_PRINT(" %s, (%u/%u)]",
+ GET_IPADDR_STRING(bp), metric, thresh);
+ bp += 4;
+ len -= 4;
+ }
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+
+static void
+print_neighbors2(netdissect_options *ndo,
+ const u_char *bp,
+ u_int len, uint8_t major_version,
+ uint8_t minor_version)
+{
+ const u_char *laddr;
+ u_char metric, thresh, flags;
+ int ncount;
+
+ ND_PRINT(" (v %u.%u):", major_version, minor_version);
+
+ while (len > 0) {
+ if (len < 8) {
+ ND_PRINT(" [length %u < 8]", len);
+ goto invalid;
+ }
+ laddr = bp;
+ bp += 4;
+ metric = GET_U_1(bp);
+ bp++;
+ thresh = GET_U_1(bp);
+ bp++;
+ flags = GET_U_1(bp);
+ bp++;
+ ncount = GET_U_1(bp);
+ bp++;
+ len -= 8;
+ while (--ncount >= 0 && len > 0) {
+ if (len < 4) {
+ ND_PRINT(" [length %u < 4]", len);
+ goto invalid;
+ }
+ ND_PRINT(" [%s -> ", GET_IPADDR_STRING(laddr));
+ ND_PRINT("%s (%u/%u", GET_IPADDR_STRING(bp),
+ metric, thresh);
+ if (flags & DVMRP_NF_TUNNEL)
+ ND_PRINT("/tunnel");
+ if (flags & DVMRP_NF_SRCRT)
+ ND_PRINT("/srcrt");
+ if (flags & DVMRP_NF_QUERIER)
+ ND_PRINT("/querier");
+ if (flags & DVMRP_NF_DISABLED)
+ ND_PRINT("/disabled");
+ if (flags & DVMRP_NF_DOWN)
+ ND_PRINT("/down");
+ ND_PRINT(")]");
+ bp += 4;
+ len -= 4;
+ }
+ if (ncount != -1) {
+ ND_PRINT(" [invalid ncount]");
+ goto invalid;
+ }
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-eap.c b/print-eap.c
new file mode 100644
index 0000000..b0542ad
--- /dev/null
+++ b/print-eap.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2004 - Michael Richardson <mcr@xelerance.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Extensible Authentication Protocol (EAP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+#define EAP_FRAME_TYPE_PACKET 0
+#define EAP_FRAME_TYPE_START 1
+#define EAP_FRAME_TYPE_LOGOFF 2
+#define EAP_FRAME_TYPE_KEY 3
+#define EAP_FRAME_TYPE_ENCAP_ASF_ALERT 4
+
+struct eap_frame_t {
+ nd_uint8_t version;
+ nd_uint8_t type;
+ nd_uint16_t length;
+};
+
+static const struct tok eap_frame_type_values[] = {
+ { EAP_FRAME_TYPE_PACKET, "EAP packet" },
+ { EAP_FRAME_TYPE_START, "EAPOL start" },
+ { EAP_FRAME_TYPE_LOGOFF, "EAPOL logoff" },
+ { EAP_FRAME_TYPE_KEY, "EAPOL key" },
+ { EAP_FRAME_TYPE_ENCAP_ASF_ALERT, "Encapsulated ASF alert" },
+ { 0, NULL}
+};
+
+/* RFC 3748 */
+struct eap_packet_t {
+ nd_uint8_t code;
+ nd_uint8_t id;
+ nd_uint16_t length;
+};
+
+#define EAP_REQUEST 1
+#define EAP_RESPONSE 2
+#define EAP_SUCCESS 3
+#define EAP_FAILURE 4
+
+static const struct tok eap_code_values[] = {
+ { EAP_REQUEST, "Request" },
+ { EAP_RESPONSE, "Response" },
+ { EAP_SUCCESS, "Success" },
+ { EAP_FAILURE, "Failure" },
+ { 0, NULL}
+};
+
+#define EAP_TYPE_NO_PROPOSED 0
+#define EAP_TYPE_IDENTITY 1
+#define EAP_TYPE_NOTIFICATION 2
+#define EAP_TYPE_NAK 3
+#define EAP_TYPE_MD5_CHALLENGE 4
+#define EAP_TYPE_OTP 5
+#define EAP_TYPE_GTC 6
+#define EAP_TYPE_TLS 13 /* RFC 2716 */
+#define EAP_TYPE_SIM 18 /* RFC 4186 */
+#define EAP_TYPE_TTLS 21 /* draft-funk-eap-ttls-v0-01.txt */
+#define EAP_TYPE_AKA 23 /* RFC 4187 */
+#define EAP_TYPE_FAST 43 /* RFC 4851 */
+#define EAP_TYPE_EXPANDED_TYPES 254
+#define EAP_TYPE_EXPERIMENTAL 255
+
+static const struct tok eap_type_values[] = {
+ { EAP_TYPE_NO_PROPOSED, "No proposed" },
+ { EAP_TYPE_IDENTITY, "Identity" },
+ { EAP_TYPE_NOTIFICATION, "Notification" },
+ { EAP_TYPE_NAK, "Nak" },
+ { EAP_TYPE_MD5_CHALLENGE, "MD5-challenge" },
+ { EAP_TYPE_OTP, "OTP" },
+ { EAP_TYPE_GTC, "GTC" },
+ { EAP_TYPE_TLS, "TLS" },
+ { EAP_TYPE_SIM, "SIM" },
+ { EAP_TYPE_TTLS, "TTLS" },
+ { EAP_TYPE_AKA, "AKA" },
+ { EAP_TYPE_FAST, "FAST" },
+ { EAP_TYPE_EXPANDED_TYPES, "Expanded types" },
+ { EAP_TYPE_EXPERIMENTAL, "Experimental" },
+ { 0, NULL}
+};
+
+#define EAP_TLS_EXTRACT_BIT_L(x) (((x)&0x80)>>7)
+
+/* RFC 2716 - EAP TLS bits */
+#define EAP_TLS_FLAGS_LEN_INCLUDED (1 << 7)
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS (1 << 6)
+#define EAP_TLS_FLAGS_START (1 << 5)
+
+static const struct tok eap_tls_flags_values[] = {
+ { EAP_TLS_FLAGS_LEN_INCLUDED, "L bit" },
+ { EAP_TLS_FLAGS_MORE_FRAGMENTS, "More fragments bit"},
+ { EAP_TLS_FLAGS_START, "Start bit"},
+ { 0, NULL}
+};
+
+#define EAP_TTLS_VERSION(x) ((x)&0x07)
+
+/* EAP-AKA and EAP-SIM - RFC 4187 */
+#define EAP_AKA_CHALLENGE 1
+#define EAP_AKA_AUTH_REJECT 2
+#define EAP_AKA_SYNC_FAILURE 4
+#define EAP_AKA_IDENTITY 5
+#define EAP_SIM_START 10
+#define EAP_SIM_CHALLENGE 11
+#define EAP_AKA_NOTIFICATION 12
+#define EAP_AKA_REAUTH 13
+#define EAP_AKA_CLIENT_ERROR 14
+
+static const struct tok eap_aka_subtype_values[] = {
+ { EAP_AKA_CHALLENGE, "Challenge" },
+ { EAP_AKA_AUTH_REJECT, "Auth reject" },
+ { EAP_AKA_SYNC_FAILURE, "Sync failure" },
+ { EAP_AKA_IDENTITY, "Identity" },
+ { EAP_SIM_START, "Start" },
+ { EAP_SIM_CHALLENGE, "Challenge" },
+ { EAP_AKA_NOTIFICATION, "Notification" },
+ { EAP_AKA_REAUTH, "Reauth" },
+ { EAP_AKA_CLIENT_ERROR, "Client error" },
+ { 0, NULL}
+};
+
+/*
+ * Print EAP requests / responses
+ */
+void
+eap_print(netdissect_options *ndo,
+ const u_char *cp,
+ u_int length)
+{
+ u_int type, subtype, len;
+ int count;
+
+ type = GET_U_1(cp);
+ len = GET_BE_U_2(cp + 2);
+ if(len != length) {
+ goto trunc;
+ }
+ ND_PRINT("%s (%u), id %u, len %u",
+ tok2str(eap_code_values, "unknown", type),
+ type,
+ GET_U_1((cp + 1)),
+ len);
+
+ ND_TCHECK_LEN(cp, len);
+
+ if (type == EAP_REQUEST || type == EAP_RESPONSE) {
+ /* RFC 3748 Section 4.1 */
+ subtype = GET_U_1(cp + 4);
+ ND_PRINT("\n\t\t Type %s (%u)",
+ tok2str(eap_type_values, "unknown", subtype),
+ subtype);
+
+ switch (subtype) {
+ case EAP_TYPE_IDENTITY:
+ if (len - 5 > 0) {
+ ND_PRINT(", Identity: ");
+ nd_printjnp(ndo, cp + 5, len - 5);
+ }
+ break;
+
+ case EAP_TYPE_NOTIFICATION:
+ if (len - 5 > 0) {
+ ND_PRINT(", Notification: ");
+ nd_printjnp(ndo, cp + 5, len - 5);
+ }
+ break;
+
+ case EAP_TYPE_NAK:
+ count = 5;
+
+ /*
+ * one or more octets indicating
+ * the desired authentication
+ * type one octet per type
+ */
+ while (count < (int)len) {
+ ND_PRINT(" %s (%u),",
+ tok2str(eap_type_values, "unknown", GET_U_1((cp + count))),
+ GET_U_1(cp + count));
+ count++;
+ }
+ break;
+
+ case EAP_TYPE_TTLS:
+ case EAP_TYPE_TLS:
+ if (subtype == EAP_TYPE_TTLS)
+ ND_PRINT(" TTLSv%u",
+ EAP_TTLS_VERSION(GET_U_1((cp + 5))));
+ ND_PRINT(" flags [%s] 0x%02x,",
+ bittok2str(eap_tls_flags_values, "none", GET_U_1((cp + 5))),
+ GET_U_1(cp + 5));
+
+ if (EAP_TLS_EXTRACT_BIT_L(GET_U_1(cp + 5))) {
+ ND_PRINT(" len %u", GET_BE_U_4(cp + 6));
+ }
+ break;
+
+ case EAP_TYPE_FAST:
+ ND_PRINT(" FASTv%u",
+ EAP_TTLS_VERSION(GET_U_1((cp + 5))));
+ ND_PRINT(" flags [%s] 0x%02x,",
+ bittok2str(eap_tls_flags_values, "none", GET_U_1((cp + 5))),
+ GET_U_1(cp + 5));
+
+ if (EAP_TLS_EXTRACT_BIT_L(GET_U_1(cp + 5))) {
+ ND_PRINT(" len %u", GET_BE_U_4(cp + 6));
+ }
+
+ /* FIXME - TLV attributes follow */
+ break;
+
+ case EAP_TYPE_AKA:
+ case EAP_TYPE_SIM:
+ ND_PRINT(" subtype [%s] 0x%02x,",
+ tok2str(eap_aka_subtype_values, "unknown", GET_U_1((cp + 5))),
+ GET_U_1(cp + 5));
+
+ /* FIXME - TLV attributes follow */
+ break;
+
+ case EAP_TYPE_MD5_CHALLENGE:
+ case EAP_TYPE_OTP:
+ case EAP_TYPE_GTC:
+ case EAP_TYPE_EXPANDED_TYPES:
+ case EAP_TYPE_EXPERIMENTAL:
+ default:
+ break;
+ }
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+eapol_print(netdissect_options *ndo,
+ const u_char *cp)
+{
+ const struct eap_frame_t *eap;
+ u_int eap_type, eap_len;
+
+ ndo->ndo_protocol = "eap";
+ eap = (const struct eap_frame_t *)cp;
+ ND_TCHECK_SIZE(eap);
+ eap_type = GET_U_1(eap->type);
+
+ ND_PRINT("%s (%u) v%u, len %u",
+ tok2str(eap_frame_type_values, "unknown", eap_type),
+ eap_type,
+ GET_U_1(eap->version),
+ GET_BE_U_2(eap->length));
+ if (ndo->ndo_vflag < 1)
+ return;
+
+ cp += sizeof(struct eap_frame_t);
+ eap_len = GET_BE_U_2(eap->length);
+
+ switch (eap_type) {
+ case EAP_FRAME_TYPE_PACKET:
+ if (eap_len == 0)
+ goto trunc;
+ ND_PRINT(", ");
+ eap_print(ndo, cp, eap_len);
+ return;
+ case EAP_FRAME_TYPE_LOGOFF:
+ case EAP_FRAME_TYPE_ENCAP_ASF_ALERT:
+ default:
+ break;
+ }
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-egp.c b/print-egp.c
new file mode 100644
index 0000000..d20e5be
--- /dev/null
+++ b/print-egp.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Lawrence Berkeley Laboratory,
+ * Berkeley, CA. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Initial contribution from Jeff Honig (jch@MITCHELL.CIT.CORNELL.EDU).
+ */
+
+/* \summary: Exterior Gateway Protocol (EGP) printer */
+
+/* specification: RFC 827 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+struct egp_packet {
+ nd_uint8_t egp_version;
+#define EGP_VERSION 2
+ nd_uint8_t egp_type;
+#define EGPT_ACQUIRE 3
+#define EGPT_REACH 5
+#define EGPT_POLL 2
+#define EGPT_UPDATE 1
+#define EGPT_ERROR 8
+ nd_uint8_t egp_code;
+#define EGPC_REQUEST 0
+#define EGPC_CONFIRM 1
+#define EGPC_REFUSE 2
+#define EGPC_CEASE 3
+#define EGPC_CEASEACK 4
+#define EGPC_HELLO 0
+#define EGPC_HEARDU 1
+ nd_uint8_t egp_status;
+#define EGPS_UNSPEC 0
+#define EGPS_ACTIVE 1
+#define EGPS_PASSIVE 2
+#define EGPS_NORES 3
+#define EGPS_ADMIN 4
+#define EGPS_GODOWN 5
+#define EGPS_PARAM 6
+#define EGPS_PROTO 7
+#define EGPS_INDET 0
+#define EGPS_UP 1
+#define EGPS_DOWN 2
+#define EGPS_UNSOL 0x80
+ nd_uint16_t egp_checksum;
+ nd_uint16_t egp_as;
+ nd_uint16_t egp_sequence;
+ union {
+ nd_uint16_t egpu_hello;
+ nd_uint8_t egpu_gws[2];
+ nd_uint16_t egpu_reason;
+#define EGPR_UNSPEC 0
+#define EGPR_BADHEAD 1
+#define EGPR_BADDATA 2
+#define EGPR_NOREACH 3
+#define EGPR_XSPOLL 4
+#define EGPR_NORESP 5
+#define EGPR_UVERSION 6
+ } egp_handg;
+#define egp_hello egp_handg.egpu_hello
+#define egp_intgw egp_handg.egpu_gws[0]
+#define egp_extgw egp_handg.egpu_gws[1]
+#define egp_reason egp_handg.egpu_reason
+ union {
+ nd_uint16_t egpu_poll;
+ nd_ipv4 egpu_sourcenet;
+ } egp_pands;
+#define egp_poll egp_pands.egpu_poll
+#define egp_sourcenet egp_pands.egpu_sourcenet
+};
+
+static const char *egp_acquire_codes[] = {
+ "request",
+ "confirm",
+ "refuse",
+ "cease",
+ "cease_ack"
+};
+
+static const char *egp_acquire_status[] = {
+ "unspecified",
+ "active_mode",
+ "passive_mode",
+ "insufficient_resources",
+ "administratively_prohibited",
+ "going_down",
+ "parameter_violation",
+ "protocol_violation"
+};
+
+static const char *egp_reach_codes[] = {
+ "hello",
+ "i-h-u"
+};
+
+static const char *egp_status_updown[] = {
+ "indeterminate",
+ "up",
+ "down"
+};
+
+static const char *egp_reasons[] = {
+ "unspecified",
+ "bad_EGP_header_format",
+ "bad_EGP_data_field_format",
+ "reachability_info_unavailable",
+ "excessive_polling_rate",
+ "no_response",
+ "unsupported_version"
+};
+
+static void
+egpnr_print(netdissect_options *ndo,
+ const struct egp_packet *egp, u_int length)
+{
+ const uint8_t *cp;
+ uint32_t addr;
+ uint32_t net;
+ u_int netlen;
+ u_int gateways, distances, networks;
+ u_int intgw, extgw, t_gateways;
+ const char *comma;
+
+ addr = GET_IPV4_TO_NETWORK_ORDER(egp->egp_sourcenet);
+ if (IN_CLASSA(addr)) {
+ net = addr & IN_CLASSA_NET;
+ netlen = 1;
+ } else if (IN_CLASSB(addr)) {
+ net = addr & IN_CLASSB_NET;
+ netlen = 2;
+ } else if (IN_CLASSC(addr)) {
+ net = addr & IN_CLASSC_NET;
+ netlen = 3;
+ } else {
+ net = 0;
+ netlen = 0;
+ }
+ cp = (const uint8_t *)(egp + 1);
+ length -= sizeof(*egp);
+
+ intgw = GET_U_1(egp->egp_intgw);
+ extgw = GET_U_1(egp->egp_extgw);
+ t_gateways = intgw + extgw;
+ for (gateways = 0; gateways < t_gateways; ++gateways) {
+ /* Pickup host part of gateway address */
+ addr = 0;
+ if (length < 4 - netlen)
+ goto trunc;
+ ND_TCHECK_LEN(cp, 4 - netlen);
+ switch (netlen) {
+
+ case 1:
+ addr = GET_U_1(cp);
+ cp++;
+ /* fall through */
+ case 2:
+ addr = (addr << 8) | GET_U_1(cp);
+ cp++;
+ /* fall through */
+ case 3:
+ addr = (addr << 8) | GET_U_1(cp);
+ cp++;
+ break;
+ }
+ addr |= net;
+ length -= 4 - netlen;
+ if (length < 1)
+ goto trunc;
+ distances = GET_U_1(cp);
+ cp++;
+ length--;
+ ND_PRINT(" %s %s ",
+ gateways < intgw ? "int" : "ext",
+ ipaddr_string(ndo, (const u_char *)&addr));
+
+ comma = "";
+ ND_PRINT("(");
+ while (distances != 0) {
+ if (length < 2)
+ goto trunc;
+ ND_PRINT("%sd%u:", comma, GET_U_1(cp));
+ cp++;
+ comma = ", ";
+ networks = GET_U_1(cp);
+ cp++;
+ length -= 2;
+ while (networks != 0) {
+ /* Pickup network number */
+ if (length < 1)
+ goto trunc;
+ addr = ((uint32_t) GET_U_1(cp)) << 24;
+ cp++;
+ length--;
+ if (IN_CLASSB(addr)) {
+ if (length < 1)
+ goto trunc;
+ addr |= ((uint32_t) GET_U_1(cp)) << 16;
+ cp++;
+ length--;
+ } else if (!IN_CLASSA(addr)) {
+ if (length < 2)
+ goto trunc;
+ addr |= ((uint32_t) GET_U_1(cp)) << 16;
+ cp++;
+ addr |= ((uint32_t) GET_U_1(cp)) << 8;
+ cp++;
+ length -= 2;
+ }
+ ND_PRINT(" %s", ipaddr_string(ndo, (const u_char *)&addr));
+ networks--;
+ }
+ distances--;
+ }
+ ND_PRINT(")");
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+egp_print(netdissect_options *ndo,
+ const uint8_t *bp, u_int length)
+{
+ const struct egp_packet *egp;
+ u_int version;
+ u_int type;
+ u_int code;
+ u_int status;
+
+ ndo->ndo_protocol = "egp";
+ egp = (const struct egp_packet *)bp;
+ if (length < sizeof(*egp) || !ND_TTEST_SIZE(egp)) {
+ nd_print_trunc(ndo);
+ return;
+ }
+
+ version = GET_U_1(egp->egp_version);
+ if (!ndo->ndo_vflag) {
+ ND_PRINT("EGPv%u, AS %u, seq %u, length %u",
+ version,
+ GET_BE_U_2(egp->egp_as),
+ GET_BE_U_2(egp->egp_sequence),
+ length);
+ return;
+ } else
+ ND_PRINT("EGPv%u, length %u",
+ version,
+ length);
+
+ if (version != EGP_VERSION) {
+ ND_PRINT("[version %u]", version);
+ return;
+ }
+
+ type = GET_U_1(egp->egp_type);
+ code = GET_U_1(egp->egp_code);
+ status = GET_U_1(egp->egp_status);
+
+ switch (type) {
+ case EGPT_ACQUIRE:
+ ND_PRINT(" acquire");
+ switch (code) {
+ case EGPC_REQUEST:
+ case EGPC_CONFIRM:
+ ND_PRINT(" %s", egp_acquire_codes[code]);
+ switch (status) {
+ case EGPS_UNSPEC:
+ case EGPS_ACTIVE:
+ case EGPS_PASSIVE:
+ ND_PRINT(" %s", egp_acquire_status[status]);
+ break;
+
+ default:
+ ND_PRINT(" [status %u]", status);
+ break;
+ }
+ ND_PRINT(" hello:%u poll:%u",
+ GET_BE_U_2(egp->egp_hello),
+ GET_BE_U_2(egp->egp_poll));
+ break;
+
+ case EGPC_REFUSE:
+ case EGPC_CEASE:
+ case EGPC_CEASEACK:
+ ND_PRINT(" %s", egp_acquire_codes[code]);
+ switch (status ) {
+ case EGPS_UNSPEC:
+ case EGPS_NORES:
+ case EGPS_ADMIN:
+ case EGPS_GODOWN:
+ case EGPS_PARAM:
+ case EGPS_PROTO:
+ ND_PRINT(" %s", egp_acquire_status[status]);
+ break;
+
+ default:
+ ND_PRINT("[status %u]", status);
+ break;
+ }
+ break;
+
+ default:
+ ND_PRINT("[code %u]", code);
+ break;
+ }
+ break;
+
+ case EGPT_REACH:
+ switch (code) {
+
+ case EGPC_HELLO:
+ case EGPC_HEARDU:
+ ND_PRINT(" %s", egp_reach_codes[code]);
+ if (status <= EGPS_DOWN)
+ ND_PRINT(" state:%s", egp_status_updown[status]);
+ else
+ ND_PRINT(" [status %u]", status);
+ break;
+
+ default:
+ ND_PRINT("[reach code %u]", code);
+ break;
+ }
+ break;
+
+ case EGPT_POLL:
+ ND_PRINT(" poll");
+ if (status <= EGPS_DOWN)
+ ND_PRINT(" state:%s", egp_status_updown[status]);
+ else
+ ND_PRINT(" [status %u]", status);
+ ND_PRINT(" net:%s", GET_IPADDR_STRING(egp->egp_sourcenet));
+ break;
+
+ case EGPT_UPDATE:
+ ND_PRINT(" update");
+ if (status & EGPS_UNSOL) {
+ status &= ~EGPS_UNSOL;
+ ND_PRINT(" unsolicited");
+ }
+ if (status <= EGPS_DOWN)
+ ND_PRINT(" state:%s", egp_status_updown[status]);
+ else
+ ND_PRINT(" [status %u]", status);
+ ND_PRINT(" %s int %u ext %u",
+ GET_IPADDR_STRING(egp->egp_sourcenet),
+ GET_U_1(egp->egp_intgw),
+ GET_U_1(egp->egp_extgw));
+ if (ndo->ndo_vflag)
+ egpnr_print(ndo, egp, length);
+ break;
+
+ case EGPT_ERROR:
+ ND_PRINT(" error");
+ if (status <= EGPS_DOWN)
+ ND_PRINT(" state:%s", egp_status_updown[status]);
+ else
+ ND_PRINT(" [status %u]", status);
+
+ if (GET_BE_U_2(egp->egp_reason) <= EGPR_UVERSION)
+ ND_PRINT(" %s",
+ egp_reasons[GET_BE_U_2(egp->egp_reason)]);
+ else
+ ND_PRINT(" [reason %u]", GET_BE_U_2(egp->egp_reason));
+ break;
+
+ default:
+ ND_PRINT("[type %u]", type);
+ break;
+ }
+}
diff --git a/print-eigrp.c b/print-eigrp.c
new file mode 100644
index 0000000..136efd0
--- /dev/null
+++ b/print-eigrp.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 1998-2004 Hannes Gredler <hannes@gredler.at>
+ * The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Enhanced Interior Gateway Routing Protocol (EIGRP) printer */
+
+/*
+ * specification:
+ *
+ * https://web.archive.org/web/20190722221712/https://www.rhyshaden.com/eigrp.htm
+ * RFC 7868
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+struct eigrp_common_header {
+ nd_uint8_t version;
+ nd_uint8_t opcode;
+ nd_uint16_t checksum;
+ nd_uint32_t flags;
+ nd_uint32_t seq;
+ nd_uint32_t ack;
+ nd_uint32_t asn;
+};
+
+#define EIGRP_VERSION 2
+
+#define EIGRP_OPCODE_UPDATE 1
+#define EIGRP_OPCODE_QUERY 3
+#define EIGRP_OPCODE_REPLY 4
+#define EIGRP_OPCODE_HELLO 5
+#define EIGRP_OPCODE_IPXSAP 6
+#define EIGRP_OPCODE_PROBE 7
+
+static const struct tok eigrp_opcode_values[] = {
+ { EIGRP_OPCODE_UPDATE, "Update" },
+ { EIGRP_OPCODE_QUERY, "Query" },
+ { EIGRP_OPCODE_REPLY, "Reply" },
+ { EIGRP_OPCODE_HELLO, "Hello" },
+ { EIGRP_OPCODE_IPXSAP, "IPX SAP" },
+ { EIGRP_OPCODE_PROBE, "Probe" },
+ { 0, NULL}
+};
+
+static const struct tok eigrp_common_header_flag_values[] = {
+ { 0x01, "Init" },
+ { 0x02, "Conditionally Received" },
+ { 0, NULL}
+};
+
+struct eigrp_tlv_header {
+ nd_uint16_t type;
+ nd_uint16_t length;
+};
+
+#define EIGRP_TLV_GENERAL_PARM 0x0001
+#define EIGRP_TLV_AUTH 0x0002
+#define EIGRP_TLV_SEQ 0x0003
+#define EIGRP_TLV_SW_VERSION 0x0004
+#define EIGRP_TLV_MCAST_SEQ 0x0005
+#define EIGRP_TLV_IP_INT 0x0102
+#define EIGRP_TLV_IP_EXT 0x0103
+#define EIGRP_TLV_AT_INT 0x0202
+#define EIGRP_TLV_AT_EXT 0x0203
+#define EIGRP_TLV_AT_CABLE_SETUP 0x0204
+#define EIGRP_TLV_IPX_INT 0x0302
+#define EIGRP_TLV_IPX_EXT 0x0303
+
+static const struct tok eigrp_tlv_values[] = {
+ { EIGRP_TLV_GENERAL_PARM, "General Parameters"},
+ { EIGRP_TLV_AUTH, "Authentication"},
+ { EIGRP_TLV_SEQ, "Sequence"},
+ { EIGRP_TLV_SW_VERSION, "Software Version"},
+ { EIGRP_TLV_MCAST_SEQ, "Next Multicast Sequence"},
+ { EIGRP_TLV_IP_INT, "IP Internal routes"},
+ { EIGRP_TLV_IP_EXT, "IP External routes"},
+ { EIGRP_TLV_AT_INT, "AppleTalk Internal routes"},
+ { EIGRP_TLV_AT_EXT, "AppleTalk External routes"},
+ { EIGRP_TLV_AT_CABLE_SETUP, "AppleTalk Cable setup"},
+ { EIGRP_TLV_IPX_INT, "IPX Internal routes"},
+ { EIGRP_TLV_IPX_EXT, "IPX External routes"},
+ { 0, NULL}
+};
+
+struct eigrp_tlv_general_parm_t {
+ nd_uint8_t k1;
+ nd_uint8_t k2;
+ nd_uint8_t k3;
+ nd_uint8_t k4;
+ nd_uint8_t k5;
+ nd_uint8_t res;
+ nd_uint16_t holdtime;
+};
+
+struct eigrp_tlv_sw_version_t {
+ nd_uint8_t ios_major;
+ nd_uint8_t ios_minor;
+ nd_uint8_t eigrp_major;
+ nd_uint8_t eigrp_minor;
+};
+
+struct eigrp_tlv_ip_int_t {
+ nd_ipv4 nexthop;
+ nd_uint32_t delay;
+ nd_uint32_t bandwidth;
+ nd_uint24_t mtu;
+ nd_uint8_t hopcount;
+ nd_uint8_t reliability;
+ nd_uint8_t load;
+ nd_byte reserved[2];
+ nd_uint8_t plen;
+ nd_uint8_t destination; /* variable length [1-4] bytes encoding */
+};
+
+struct eigrp_tlv_ip_ext_t {
+ nd_ipv4 nexthop;
+ nd_ipv4 origin_router;
+ nd_uint32_t origin_as;
+ nd_uint32_t tag;
+ nd_uint32_t metric;
+ nd_byte reserved[2];
+ nd_uint8_t proto_id;
+ nd_uint8_t flags;
+ nd_uint32_t delay;
+ nd_uint32_t bandwidth;
+ nd_uint24_t mtu;
+ nd_uint8_t hopcount;
+ nd_uint8_t reliability;
+ nd_uint8_t load;
+ nd_byte reserved2[2];
+ nd_uint8_t plen;
+ nd_uint8_t destination; /* variable length [1-4] bytes encoding */
+};
+
+struct eigrp_tlv_at_cable_setup_t {
+ nd_uint16_t cable_start;
+ nd_uint16_t cable_end;
+ nd_uint32_t router_id;
+};
+
+struct eigrp_tlv_at_int_t {
+ nd_byte nexthop[4];
+ nd_uint32_t delay;
+ nd_uint32_t bandwidth;
+ nd_uint24_t mtu;
+ nd_uint8_t hopcount;
+ nd_uint8_t reliability;
+ nd_uint8_t load;
+ nd_byte reserved[2];
+ nd_uint16_t cable_start;
+ nd_uint16_t cable_end;
+};
+
+struct eigrp_tlv_at_ext_t {
+ nd_byte nexthop[4];
+ nd_uint32_t origin_router;
+ nd_uint32_t origin_as;
+ nd_uint32_t tag;
+ nd_uint8_t proto_id;
+ nd_uint8_t flags;
+ nd_uint16_t metric;
+ nd_uint32_t delay;
+ nd_uint32_t bandwidth;
+ nd_uint24_t mtu;
+ nd_uint8_t hopcount;
+ nd_uint8_t reliability;
+ nd_uint8_t load;
+ nd_byte reserved2[2];
+ nd_uint16_t cable_start;
+ nd_uint16_t cable_end;
+};
+
+static const struct tok eigrp_ext_proto_id_values[] = {
+ { 0x01, "IGRP" },
+ { 0x02, "EIGRP" },
+ { 0x03, "Static" },
+ { 0x04, "RIP" },
+ { 0x05, "Hello" },
+ { 0x06, "OSPF" },
+ { 0x07, "IS-IS" },
+ { 0x08, "EGP" },
+ { 0x09, "BGP" },
+ { 0x0a, "IDRP" },
+ { 0x0b, "Connected" },
+ { 0, NULL}
+};
+
+void
+eigrp_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ const struct eigrp_common_header *eigrp_com_header;
+ const struct eigrp_tlv_header *eigrp_tlv_header;
+ const u_char *tptr,*tlv_tptr;
+ u_int tlen,eigrp_tlv_len,eigrp_tlv_type,tlv_tlen, byte_length, bit_length;
+ uint8_t prefix[4];
+
+ union {
+ const struct eigrp_tlv_general_parm_t *eigrp_tlv_general_parm;
+ const struct eigrp_tlv_sw_version_t *eigrp_tlv_sw_version;
+ const struct eigrp_tlv_ip_int_t *eigrp_tlv_ip_int;
+ const struct eigrp_tlv_ip_ext_t *eigrp_tlv_ip_ext;
+ const struct eigrp_tlv_at_cable_setup_t *eigrp_tlv_at_cable_setup;
+ const struct eigrp_tlv_at_int_t *eigrp_tlv_at_int;
+ const struct eigrp_tlv_at_ext_t *eigrp_tlv_at_ext;
+ } tlv_ptr;
+
+ ndo->ndo_protocol = "eigrp";
+ tptr=pptr;
+ eigrp_com_header = (const struct eigrp_common_header *)pptr;
+ ND_TCHECK_SIZE(eigrp_com_header);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (GET_U_1(eigrp_com_header->version) != EIGRP_VERSION) {
+ ND_PRINT("EIGRP version %u packet not supported",
+ GET_U_1(eigrp_com_header->version));
+ return;
+ }
+
+ /* in non-verbose mode just lets print the basic Message Type*/
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("EIGRP %s, length: %u",
+ tok2str(eigrp_opcode_values, "unknown (%u)",GET_U_1(eigrp_com_header->opcode)),
+ len);
+ return;
+ }
+
+ /* ok they seem to want to know everything - lets fully decode it */
+
+ if (len < sizeof(struct eigrp_common_header)) {
+ ND_PRINT("EIGRP %s, length: %u (too short, < %zu)",
+ tok2str(eigrp_opcode_values, "unknown (%u)",GET_U_1(eigrp_com_header->opcode)),
+ len, sizeof(struct eigrp_common_header));
+ return;
+ }
+ tlen=len-sizeof(struct eigrp_common_header);
+
+ /* FIXME print other header info */
+ ND_PRINT("\n\tEIGRP v%u, opcode: %s (%u), chksum: 0x%04x, Flags: [%s]\n\tseq: 0x%08x, ack: 0x%08x, AS: %u, length: %u",
+ GET_U_1(eigrp_com_header->version),
+ tok2str(eigrp_opcode_values, "unknown, type: %u",GET_U_1(eigrp_com_header->opcode)),
+ GET_U_1(eigrp_com_header->opcode),
+ GET_BE_U_2(eigrp_com_header->checksum),
+ tok2str(eigrp_common_header_flag_values,
+ "none",
+ GET_BE_U_4(eigrp_com_header->flags)),
+ GET_BE_U_4(eigrp_com_header->seq),
+ GET_BE_U_4(eigrp_com_header->ack),
+ GET_BE_U_4(eigrp_com_header->asn),
+ tlen);
+
+ tptr+=sizeof(struct eigrp_common_header);
+
+ while(tlen>0) {
+ /* did we capture enough for fully decoding the object header ? */
+ ND_TCHECK_LEN(tptr, sizeof(struct eigrp_tlv_header));
+
+ eigrp_tlv_header = (const struct eigrp_tlv_header *)tptr;
+ eigrp_tlv_len=GET_BE_U_2(eigrp_tlv_header->length);
+ eigrp_tlv_type=GET_BE_U_2(eigrp_tlv_header->type);
+
+
+ if (eigrp_tlv_len < sizeof(struct eigrp_tlv_header) ||
+ eigrp_tlv_len > tlen) {
+ print_unknown_data(ndo,tptr+sizeof(struct eigrp_tlv_header),"\n\t ",tlen);
+ return;
+ }
+
+ ND_PRINT("\n\t %s TLV (0x%04x), length: %u",
+ tok2str(eigrp_tlv_values,
+ "Unknown",
+ eigrp_tlv_type),
+ eigrp_tlv_type,
+ eigrp_tlv_len);
+
+ if (eigrp_tlv_len < sizeof(struct eigrp_tlv_header)) {
+ ND_PRINT(" (too short, < %zu)",
+ sizeof(struct eigrp_tlv_header));
+ break;
+ }
+ tlv_tptr=tptr+sizeof(struct eigrp_tlv_header);
+ tlv_tlen=eigrp_tlv_len-sizeof(struct eigrp_tlv_header);
+
+ /* did we capture enough for fully decoding the object ? */
+ ND_TCHECK_LEN(tptr, eigrp_tlv_len);
+
+ switch(eigrp_tlv_type) {
+
+ case EIGRP_TLV_GENERAL_PARM:
+ tlv_ptr.eigrp_tlv_general_parm = (const struct eigrp_tlv_general_parm_t *)tlv_tptr;
+ if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_general_parm)) {
+ ND_PRINT(" (too short, < %zu)",
+ sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_general_parm));
+ break;
+ }
+
+ ND_PRINT("\n\t holdtime: %us, k1 %u, k2 %u, k3 %u, k4 %u, k5 %u",
+ GET_BE_U_2(tlv_ptr.eigrp_tlv_general_parm->holdtime),
+ GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k1),
+ GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k2),
+ GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k3),
+ GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k4),
+ GET_U_1(tlv_ptr.eigrp_tlv_general_parm->k5));
+ break;
+
+ case EIGRP_TLV_SW_VERSION:
+ tlv_ptr.eigrp_tlv_sw_version = (const struct eigrp_tlv_sw_version_t *)tlv_tptr;
+ if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_sw_version)) {
+ ND_PRINT(" (too short, < %zu)",
+ sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_sw_version));
+ break;
+ }
+
+ ND_PRINT("\n\t IOS version: %u.%u, EIGRP version %u.%u",
+ GET_U_1(tlv_ptr.eigrp_tlv_sw_version->ios_major),
+ GET_U_1(tlv_ptr.eigrp_tlv_sw_version->ios_minor),
+ GET_U_1(tlv_ptr.eigrp_tlv_sw_version->eigrp_major),
+ GET_U_1(tlv_ptr.eigrp_tlv_sw_version->eigrp_minor));
+ break;
+
+ case EIGRP_TLV_IP_INT:
+ tlv_ptr.eigrp_tlv_ip_int = (const struct eigrp_tlv_ip_int_t *)tlv_tptr;
+ if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_ip_int)) {
+ ND_PRINT(" (too short, < %zu)",
+ sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_ip_int));
+ break;
+ }
+
+ bit_length = GET_U_1(tlv_ptr.eigrp_tlv_ip_int->plen);
+ if (bit_length > 32) {
+ ND_PRINT("\n\t illegal prefix length %u",bit_length);
+ break;
+ }
+ byte_length = (bit_length + 7) / 8; /* variable length encoding */
+ memset(prefix, 0, 4);
+ GET_CPY_BYTES(prefix, tlv_ptr.eigrp_tlv_ip_int->destination, byte_length);
+
+ ND_PRINT("\n\t IPv4 prefix: %15s/%u, nexthop: ",
+ ipaddr_string(ndo, prefix), /* local buffer, not packet data; don't use GET_IPADDR_STRING() */
+ bit_length);
+ if (GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_int->nexthop) == 0)
+ ND_PRINT("self");
+ else
+ ND_PRINT("%s",
+ GET_IPADDR_STRING(tlv_ptr.eigrp_tlv_ip_int->nexthop));
+
+ ND_PRINT("\n\t delay %u ms, bandwidth %u Kbps, mtu %u, hop %u, reliability %u, load %u",
+ (GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_int->delay)/100),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_int->bandwidth),
+ GET_BE_U_3(tlv_ptr.eigrp_tlv_ip_int->mtu),
+ GET_U_1(tlv_ptr.eigrp_tlv_ip_int->hopcount),
+ GET_U_1(tlv_ptr.eigrp_tlv_ip_int->reliability),
+ GET_U_1(tlv_ptr.eigrp_tlv_ip_int->load));
+ break;
+
+ case EIGRP_TLV_IP_EXT:
+ tlv_ptr.eigrp_tlv_ip_ext = (const struct eigrp_tlv_ip_ext_t *)tlv_tptr;
+ if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_ip_ext)) {
+ ND_PRINT(" (too short, < %zu)",
+ sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_ip_ext));
+ break;
+ }
+
+ bit_length = GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->plen);
+ if (bit_length > 32) {
+ ND_PRINT("\n\t illegal prefix length %u",bit_length);
+ break;
+ }
+ byte_length = (bit_length + 7) / 8; /* variable length encoding */
+ memset(prefix, 0, 4);
+ GET_CPY_BYTES(prefix, tlv_ptr.eigrp_tlv_ip_ext->destination, byte_length);
+
+ ND_PRINT("\n\t IPv4 prefix: %15s/%u, nexthop: ",
+ ipaddr_string(ndo, prefix), /* local buffer, not packet data; don't use GET_IPADDR_STRING() */
+ bit_length);
+ if (GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->nexthop) == 0)
+ ND_PRINT("self");
+ else
+ ND_PRINT("%s",
+ GET_IPADDR_STRING(tlv_ptr.eigrp_tlv_ip_ext->nexthop));
+
+ ND_PRINT("\n\t origin-router %s, origin-as %u, origin-proto %s, flags [0x%02x], tag 0x%08x, metric %u",
+ GET_IPADDR_STRING(tlv_ptr.eigrp_tlv_ip_ext->origin_router),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->origin_as),
+ tok2str(eigrp_ext_proto_id_values,"unknown",GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->proto_id)),
+ GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->flags),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->tag),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->metric));
+
+ ND_PRINT("\n\t delay %u ms, bandwidth %u Kbps, mtu %u, hop %u, reliability %u, load %u",
+ (GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->delay)/100),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_ip_ext->bandwidth),
+ GET_BE_U_3(tlv_ptr.eigrp_tlv_ip_ext->mtu),
+ GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->hopcount),
+ GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->reliability),
+ GET_U_1(tlv_ptr.eigrp_tlv_ip_ext->load));
+ break;
+
+ case EIGRP_TLV_AT_CABLE_SETUP:
+ tlv_ptr.eigrp_tlv_at_cable_setup = (const struct eigrp_tlv_at_cable_setup_t *)tlv_tptr;
+ if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_cable_setup)) {
+ ND_PRINT(" (too short, < %zu)",
+ sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_cable_setup));
+ break;
+ }
+
+ ND_PRINT("\n\t Cable-range: %u-%u, Router-ID %u",
+ GET_BE_U_2(tlv_ptr.eigrp_tlv_at_cable_setup->cable_start),
+ GET_BE_U_2(tlv_ptr.eigrp_tlv_at_cable_setup->cable_end),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_at_cable_setup->router_id));
+ break;
+
+ case EIGRP_TLV_AT_INT:
+ tlv_ptr.eigrp_tlv_at_int = (const struct eigrp_tlv_at_int_t *)tlv_tptr;
+ if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_int)) {
+ ND_PRINT(" (too short, < %zu)",
+ sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_int));
+ break;
+ }
+
+ ND_PRINT("\n\t Cable-Range: %u-%u, nexthop: ",
+ GET_BE_U_2(tlv_ptr.eigrp_tlv_at_int->cable_start),
+ GET_BE_U_2(tlv_ptr.eigrp_tlv_at_int->cable_end));
+
+ if (GET_BE_U_4(tlv_ptr.eigrp_tlv_at_int->nexthop) == 0)
+ ND_PRINT("self");
+ else
+ ND_PRINT("%u.%u",
+ GET_BE_U_2(&tlv_ptr.eigrp_tlv_at_int->nexthop[0]),
+ GET_BE_U_2(&tlv_ptr.eigrp_tlv_at_int->nexthop[2]));
+
+ ND_PRINT("\n\t delay %u ms, bandwidth %u Kbps, mtu %u, hop %u, reliability %u, load %u",
+ (GET_BE_U_4(tlv_ptr.eigrp_tlv_at_int->delay)/100),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_at_int->bandwidth),
+ GET_BE_U_3(tlv_ptr.eigrp_tlv_at_int->mtu),
+ GET_U_1(tlv_ptr.eigrp_tlv_at_int->hopcount),
+ GET_U_1(tlv_ptr.eigrp_tlv_at_int->reliability),
+ GET_U_1(tlv_ptr.eigrp_tlv_at_int->load));
+ break;
+
+ case EIGRP_TLV_AT_EXT:
+ tlv_ptr.eigrp_tlv_at_ext = (const struct eigrp_tlv_at_ext_t *)tlv_tptr;
+ if (tlv_tlen < sizeof(*tlv_ptr.eigrp_tlv_at_ext)) {
+ ND_PRINT(" (too short, < %zu)",
+ sizeof(struct eigrp_tlv_header) + sizeof(*tlv_ptr.eigrp_tlv_at_ext));
+ break;
+ }
+
+ ND_PRINT("\n\t Cable-Range: %u-%u, nexthop: ",
+ GET_BE_U_2(tlv_ptr.eigrp_tlv_at_ext->cable_start),
+ GET_BE_U_2(tlv_ptr.eigrp_tlv_at_ext->cable_end));
+
+ if (GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->nexthop) == 0)
+ ND_PRINT("self");
+ else
+ ND_PRINT("%u.%u",
+ GET_BE_U_2(&tlv_ptr.eigrp_tlv_at_ext->nexthop[0]),
+ GET_BE_U_2(&tlv_ptr.eigrp_tlv_at_ext->nexthop[2]));
+
+ ND_PRINT("\n\t origin-router %u, origin-as %u, origin-proto %s, flags [0x%02x], tag 0x%08x, metric %u",
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->origin_router),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->origin_as),
+ tok2str(eigrp_ext_proto_id_values,"unknown",GET_U_1(tlv_ptr.eigrp_tlv_at_ext->proto_id)),
+ GET_U_1(tlv_ptr.eigrp_tlv_at_ext->flags),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->tag),
+ GET_BE_U_2(tlv_ptr.eigrp_tlv_at_ext->metric));
+
+ ND_PRINT("\n\t delay %u ms, bandwidth %u Kbps, mtu %u, hop %u, reliability %u, load %u",
+ (GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->delay)/100),
+ GET_BE_U_4(tlv_ptr.eigrp_tlv_at_ext->bandwidth),
+ GET_BE_U_3(tlv_ptr.eigrp_tlv_at_ext->mtu),
+ GET_U_1(tlv_ptr.eigrp_tlv_at_ext->hopcount),
+ GET_U_1(tlv_ptr.eigrp_tlv_at_ext->reliability),
+ GET_U_1(tlv_ptr.eigrp_tlv_at_ext->load));
+ break;
+
+ /*
+ * FIXME those are the defined TLVs that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case EIGRP_TLV_AUTH:
+ case EIGRP_TLV_SEQ:
+ case EIGRP_TLV_MCAST_SEQ:
+ case EIGRP_TLV_IPX_INT:
+ case EIGRP_TLV_IPX_EXT:
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo,tlv_tptr,"\n\t ",tlv_tlen);
+ break;
+ }
+ /* do we want to see an additionally hexdump ? */
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo,tptr+sizeof(struct eigrp_tlv_header),"\n\t ",
+ eigrp_tlv_len-sizeof(struct eigrp_tlv_header));
+
+ tptr+=eigrp_tlv_len;
+ tlen-=eigrp_tlv_len;
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-enc.c b/print-enc.c
new file mode 100644
index 0000000..9f541c3
--- /dev/null
+++ b/print-enc.c
@@ -0,0 +1,160 @@
+/* $OpenBSD: print-enc.c,v 1.7 2002/02/19 19:39:40 millert Exp $ */
+
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: OpenBSD IPsec encapsulation BPF layer printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "af.h"
+
+/* From $OpenBSD: if_enc.h,v 1.8 2001/06/25 05:14:00 angelos Exp $ */
+/*
+ * The authors of this code are John Ioannidis (ji@tla.org),
+ * Angelos D. Keromytis (kermit@csd.uch.gr) and
+ * Niels Provos (provos@physnet.uni-hamburg.de).
+ *
+ * This code was written by John Ioannidis for BSD/OS in Athens, Greece,
+ * in November 1995.
+ *
+ * Ported to OpenBSD and NetBSD, with additional transforms, in December 1996,
+ * by Angelos D. Keromytis.
+ *
+ * Additional transforms and features in 1997 and 1998 by Angelos D. Keromytis
+ * and Niels Provos.
+ *
+ * Copyright (C) 1995, 1996, 1997, 1998 by John Ioannidis, Angelos D. Keromytis
+ * and Niels Provos.
+ * Copyright (c) 2001, Angelos D. Keromytis.
+ *
+ * Permission to use, copy, and modify this software with or without fee
+ * is hereby granted, provided that this entire notice is included in
+ * all copies of any software which is or includes a copy or
+ * modification of this software.
+ * You may use this code under the GNU public license if you so wish. Please
+ * contribute changes back to the authors under this freer than GPL license
+ * so that we may further the use of strong encryption without limitations to
+ * all.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
+ * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
+ * PURPOSE.
+ */
+
+#define ENC_HDRLEN 12
+
+/* From $OpenBSD: mbuf.h,v 1.56 2002/01/25 15:50:23 art Exp $ */
+#define M_CONF 0x0400 /* packet was encrypted (ESP-transport) */
+#define M_AUTH 0x0800 /* packet was authenticated (AH) */
+
+struct enchdr {
+ nd_uint32_t af;
+ nd_uint32_t spi;
+ nd_uint32_t flags;
+};
+
+#define ENC_PRINT_TYPE(wh, xf, name) \
+ if ((wh) & (xf)) { \
+ ND_PRINT("%s%s", name, (wh) == (xf) ? "): " : ","); \
+ (wh) &= ~(xf); \
+ }
+
+/*
+ * Byte-swap a 32-bit number.
+ * ("htonl()" or "ntohl()" won't work - we want to byte-swap even on
+ * big-endian platforms.)
+ */
+#define SWAPLONG(y) \
+((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
+
+void
+enc_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+ u_int af, flags;
+ const struct enchdr *hdr;
+
+ ndo->ndo_protocol = "enc";
+ ND_TCHECK_LEN(p, ENC_HDRLEN);
+ ndo->ndo_ll_hdr_len += ENC_HDRLEN;
+
+ hdr = (const struct enchdr *)p;
+ /*
+ * The address family and flags fields are in the byte order
+ * of the host that originally captured the traffic.
+ *
+ * To determine that, look at the address family. It's 32-bit,
+ * it is not likely ever to be > 65535 (I doubt there will
+ * ever be > 65535 address families and, so far, AF_ values have
+ * not been allocated very sparsely) so it should not have the
+ * upper 16 bits set, and it is not likely ever to be AF_UNSPEC,
+ * i.e. it's not likely ever to be 0, so if it's byte-swapped,
+ * it should have at least one of the upper 16 bits set.
+ *
+ * So if any of the upper 16 bits are set, we assume it, and
+ * the flags field, are byte-swapped.
+ *
+ * The SPI field is always in network byte order, i.e. big-
+ * endian.
+ */
+ UNALIGNED_MEMCPY(&af, &hdr->af, sizeof (af));
+ UNALIGNED_MEMCPY(&flags, &hdr->flags, sizeof (flags));
+ if ((af & 0xFFFF0000) != 0) {
+ af = SWAPLONG(af);
+ flags = SWAPLONG(flags);
+ }
+
+ if (flags == 0)
+ ND_PRINT("(unprotected): ");
+ else
+ ND_PRINT("(");
+ ENC_PRINT_TYPE(flags, M_AUTH, "authentic");
+ ENC_PRINT_TYPE(flags, M_CONF, "confidential");
+ /* ENC_PRINT_TYPE(flags, M_TUNNEL, "tunnel"); */
+ ND_PRINT("SPI 0x%08x: ", GET_BE_U_4(hdr->spi));
+
+ length -= ENC_HDRLEN;
+ caplen -= ENC_HDRLEN;
+ p += ENC_HDRLEN;
+
+ switch (af) {
+ case BSD_AFNUM_INET:
+ ip_print(ndo, p, length);
+ break;
+ case BSD_AFNUM_INET6_BSD:
+ case BSD_AFNUM_INET6_FREEBSD:
+ case BSD_AFNUM_INET6_DARWIN:
+ ip6_print(ndo, p, length);
+ break;
+ }
+}
diff --git a/print-esp.c b/print-esp.c
new file mode 100644
index 0000000..8b664b6
--- /dev/null
+++ b/print-esp.c
@@ -0,0 +1,916 @@
+/* $NetBSD: print-ah.c,v 1.4 1996/05/20 00:41:16 fvdl Exp $ */
+
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IPSEC Encapsulating Security Payload (ESP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/* Any code in this file that depends on HAVE_LIBCRYPTO depends on
+ * HAVE_OPENSSL_EVP_H too. Undefining the former when the latter isn't defined
+ * is the simplest way of handling the dependency.
+ */
+#ifdef HAVE_LIBCRYPTO
+#ifdef HAVE_OPENSSL_EVP_H
+#include <openssl/evp.h>
+#else
+#undef HAVE_LIBCRYPTO
+#endif
+#endif
+
+#include "netdissect.h"
+#include "extract.h"
+
+#ifdef HAVE_LIBCRYPTO
+#include "strtoaddr.h"
+#include "ascii_strcasecmp.h"
+#endif
+
+#include "ip.h"
+#include "ip6.h"
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * RFC1827/2406 Encapsulated Security Payload.
+ */
+
+struct newesp {
+ nd_uint32_t esp_spi; /* ESP */
+ nd_uint32_t esp_seq; /* Sequence number */
+ /*variable size*/ /* (IV and) Payload data */
+ /*variable size*/ /* padding */
+ /*8bit*/ /* pad size */
+ /*8bit*/ /* next header */
+ /*8bit*/ /* next header */
+ /*variable size, 32bit bound*/ /* Authentication data */
+};
+
+#ifdef HAVE_LIBCRYPTO
+union inaddr_u {
+ nd_ipv4 in4;
+ nd_ipv6 in6;
+};
+struct sa_list {
+ struct sa_list *next;
+ u_int daddr_version;
+ union inaddr_u daddr;
+ uint32_t spi; /* if == 0, then IKEv2 */
+ int initiator;
+ u_char spii[8]; /* for IKEv2 */
+ u_char spir[8];
+ const EVP_CIPHER *evp;
+ u_int ivlen;
+ int authlen;
+ u_char authsecret[256];
+ int authsecret_len;
+ u_char secret[256]; /* is that big enough for all secrets? */
+ int secretlen;
+};
+
+#ifndef HAVE_EVP_CIPHER_CTX_NEW
+/*
+ * Allocate an EVP_CIPHER_CTX.
+ * Used if we have an older version of OpenSSL that doesn't provide
+ * routines to allocate and free them.
+ */
+static EVP_CIPHER_CTX *
+EVP_CIPHER_CTX_new(void)
+{
+ EVP_CIPHER_CTX *ctx;
+
+ ctx = malloc(sizeof(*ctx));
+ if (ctx == NULL)
+ return (NULL);
+ memset(ctx, 0, sizeof(*ctx));
+ return (ctx);
+}
+
+static void
+EVP_CIPHER_CTX_free(EVP_CIPHER_CTX *ctx)
+{
+ EVP_CIPHER_CTX_cleanup(ctx);
+ free(ctx);
+}
+#endif
+
+#ifdef HAVE_EVP_DECRYPTINIT_EX
+/*
+ * Initialize the cipher by calling EVP_DecryptInit_ex(), because
+ * calling EVP_DecryptInit() will reset the cipher context, clearing
+ * the cipher, so calling it twice, with the second call having a
+ * null cipher, will clear the already-set cipher. EVP_DecryptInit_ex(),
+ * however, won't reset the cipher context, so you can use it to specify
+ * the IV in a second call after a first call to EVP_DecryptInit_ex()
+ * to set the cipher and the key.
+ *
+ * XXX - is there some reason why we need to make two calls?
+ */
+static int
+set_cipher_parameters(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+ const unsigned char *key,
+ const unsigned char *iv)
+{
+ return EVP_DecryptInit_ex(ctx, cipher, NULL, key, iv);
+}
+#else
+/*
+ * Initialize the cipher by calling EVP_DecryptInit(), because we don't
+ * have EVP_DecryptInit_ex(); we rely on it not trashing the context.
+ */
+static int
+set_cipher_parameters(EVP_CIPHER_CTX *ctx, const EVP_CIPHER *cipher,
+ const unsigned char *key,
+ const unsigned char *iv)
+{
+ return EVP_DecryptInit(ctx, cipher, key, iv);
+}
+#endif
+
+static u_char *
+do_decrypt(netdissect_options *ndo, const char *caller, struct sa_list *sa,
+ const u_char *iv, const u_char *ct, unsigned int ctlen)
+{
+ EVP_CIPHER_CTX *ctx;
+ unsigned int block_size;
+ unsigned int ptlen;
+ u_char *pt;
+ int len;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ /*
+ * Failed to initialize the cipher context.
+ * From a look at the OpenSSL code, this appears to
+ * mean "couldn't allocate memory for the cipher context";
+ * note that we're not passing any parameters, so there's
+ * not much else it can mean.
+ */
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: can't allocate memory for cipher context", caller);
+ return NULL;
+ }
+
+ if (set_cipher_parameters(ctx, sa->evp, sa->secret, NULL) < 0) {
+ EVP_CIPHER_CTX_free(ctx);
+ (*ndo->ndo_warning)(ndo, "%s: espkey init failed", caller);
+ return NULL;
+ }
+ if (set_cipher_parameters(ctx, NULL, NULL, iv) < 0) {
+ EVP_CIPHER_CTX_free(ctx);
+ (*ndo->ndo_warning)(ndo, "%s: IV init failed", caller);
+ return NULL;
+ }
+
+ /*
+ * At least as I read RFC 5996 section 3.14 and RFC 4303 section 2.4,
+ * if the cipher has a block size of which the ciphertext's size must
+ * be a multiple, the payload must be padded to make that happen, so
+ * the ciphertext length must be a multiple of the block size. Fail
+ * if that's not the case.
+ */
+ block_size = (unsigned int)EVP_CIPHER_CTX_block_size(ctx);
+ if ((ctlen % block_size) != 0) {
+ EVP_CIPHER_CTX_free(ctx);
+ (*ndo->ndo_warning)(ndo,
+ "%s: ciphertext size %u is not a multiple of the cipher block size %u",
+ caller, ctlen, block_size);
+ return NULL;
+ }
+
+ /*
+ * Attempt to allocate a buffer for the decrypted data, because
+ * we can't decrypt on top of the input buffer.
+ */
+ ptlen = ctlen;
+ pt = (u_char *)malloc(ptlen);
+ if (pt == NULL) {
+ EVP_CIPHER_CTX_free(ctx);
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: can't allocate memory for decryption buffer", caller);
+ return NULL;
+ }
+
+ /*
+ * The size of the ciphertext handed to us is a multiple of the
+ * cipher block size, so we don't need to worry about padding.
+ */
+ if (!EVP_CIPHER_CTX_set_padding(ctx, 0)) {
+ free(pt);
+ EVP_CIPHER_CTX_free(ctx);
+ (*ndo->ndo_warning)(ndo,
+ "%s: EVP_CIPHER_CTX_set_padding failed", caller);
+ return NULL;
+ }
+ if (!EVP_DecryptUpdate(ctx, pt, &len, ct, ctlen)) {
+ free(pt);
+ EVP_CIPHER_CTX_free(ctx);
+ (*ndo->ndo_warning)(ndo, "%s: EVP_DecryptUpdate failed",
+ caller);
+ return NULL;
+ }
+ EVP_CIPHER_CTX_free(ctx);
+ return pt;
+}
+
+/*
+ * This will allocate a new buffer containing the decrypted data.
+ * It returns 1 on success and 0 on failure.
+ *
+ * It will push the new buffer and the values of ndo->ndo_packetp and
+ * ndo->ndo_snapend onto the buffer stack, and change ndo->ndo_packetp
+ * and ndo->ndo_snapend to refer to the new buffer.
+ *
+ * Our caller must pop the buffer off the stack when it's finished
+ * dissecting anything in it and before it does any dissection of
+ * anything in the old buffer. That will free the new buffer.
+ */
+USES_APPLE_DEPRECATED_API
+int esp_decrypt_buffer_by_ikev2_print(netdissect_options *ndo,
+ int initiator,
+ const u_char spii[8],
+ const u_char spir[8],
+ const u_char *buf, const u_char *end)
+{
+ struct sa_list *sa;
+ const u_char *iv;
+ const u_char *ct;
+ unsigned int ctlen;
+ u_char *pt;
+
+ /* initiator arg is any non-zero value */
+ if(initiator) initiator=1;
+
+ /* see if we can find the SA, and if so, decode it */
+ for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
+ if (sa->spi == 0
+ && initiator == sa->initiator
+ && memcmp(spii, sa->spii, 8) == 0
+ && memcmp(spir, sa->spir, 8) == 0)
+ break;
+ }
+
+ if(sa == NULL) return 0;
+ if(sa->evp == NULL) return 0;
+
+ /*
+ * remove authenticator, and see if we still have something to
+ * work with
+ */
+ end = end - sa->authlen;
+ iv = buf;
+ ct = iv + sa->ivlen;
+ ctlen = end-ct;
+
+ if(end <= ct) return 0;
+
+ pt = do_decrypt(ndo, "esp_decrypt_buffer_by_ikev2_print", sa, iv,
+ ct, ctlen);
+ if (pt == NULL)
+ return 0;
+
+ /*
+ * Switch to the output buffer for dissection, and save it
+ * on the buffer stack so it can be freed; our caller must
+ * pop it when done.
+ */
+ if (!nd_push_buffer(ndo, pt, pt, pt + ctlen)) {
+ free(pt);
+ return 0;
+ }
+
+ return 1;
+}
+USES_APPLE_RST
+
+static void esp_print_addsa(netdissect_options *ndo,
+ struct sa_list *sa, int sa_def)
+{
+ /* copy the "sa" */
+
+ struct sa_list *nsa;
+
+ /* malloc() return used in a 'struct sa_list': do not free() */
+ nsa = (struct sa_list *)malloc(sizeof(struct sa_list));
+ if (nsa == NULL)
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: malloc", __func__);
+
+ *nsa = *sa;
+
+ if (sa_def)
+ ndo->ndo_sa_default = nsa;
+
+ nsa->next = ndo->ndo_sa_list_head;
+ ndo->ndo_sa_list_head = nsa;
+}
+
+
+static u_int hexdigit(netdissect_options *ndo, char hex)
+{
+ if (hex >= '0' && hex <= '9')
+ return (hex - '0');
+ else if (hex >= 'A' && hex <= 'F')
+ return (hex - 'A' + 10);
+ else if (hex >= 'a' && hex <= 'f')
+ return (hex - 'a' + 10);
+ else {
+ (*ndo->ndo_error)(ndo, S_ERR_ND_ESP_SECRET,
+ "invalid hex digit %c in espsecret\n", hex);
+ }
+}
+
+static u_int hex2byte(netdissect_options *ndo, char *hexstring)
+{
+ u_int byte;
+
+ byte = (hexdigit(ndo, hexstring[0]) << 4) + hexdigit(ndo, hexstring[1]);
+ return byte;
+}
+
+/*
+ * returns size of binary, 0 on failure.
+ */
+static
+int espprint_decode_hex(netdissect_options *ndo,
+ u_char *binbuf, unsigned int binbuf_len,
+ char *hex)
+{
+ unsigned int len;
+ int i;
+
+ len = strlen(hex) / 2;
+
+ if (len > binbuf_len) {
+ (*ndo->ndo_warning)(ndo, "secret is too big: %u\n", len);
+ return 0;
+ }
+
+ i = 0;
+ while (hex[0] != '\0' && hex[1]!='\0') {
+ binbuf[i] = hex2byte(ndo, hex);
+ hex += 2;
+ i++;
+ }
+
+ return i;
+}
+
+/*
+ * decode the form: SPINUM@IP <tab> ALGONAME:0xsecret
+ */
+
+USES_APPLE_DEPRECATED_API
+static int
+espprint_decode_encalgo(netdissect_options *ndo,
+ char *decode, struct sa_list *sa)
+{
+ size_t i;
+ const EVP_CIPHER *evp;
+ int authlen = 0;
+ char *colon, *p;
+
+ colon = strchr(decode, ':');
+ if (colon == NULL) {
+ (*ndo->ndo_warning)(ndo, "failed to decode espsecret: %s\n", decode);
+ return 0;
+ }
+ *colon = '\0';
+
+ if (strlen(decode) > strlen("-hmac96") &&
+ !strcmp(decode + strlen(decode) - strlen("-hmac96"),
+ "-hmac96")) {
+ p = strstr(decode, "-hmac96");
+ *p = '\0';
+ authlen = 12;
+ }
+ if (strlen(decode) > strlen("-cbc") &&
+ !strcmp(decode + strlen(decode) - strlen("-cbc"), "-cbc")) {
+ p = strstr(decode, "-cbc");
+ *p = '\0';
+ }
+ evp = EVP_get_cipherbyname(decode);
+
+ if (!evp) {
+ (*ndo->ndo_warning)(ndo, "failed to find cipher algo %s\n", decode);
+ sa->evp = NULL;
+ sa->authlen = 0;
+ sa->ivlen = 0;
+ return 0;
+ }
+
+ sa->evp = evp;
+ sa->authlen = authlen;
+ /* This returns an int, but it should never be negative */
+ sa->ivlen = EVP_CIPHER_iv_length(evp);
+
+ colon++;
+ if (colon[0] == '0' && colon[1] == 'x') {
+ /* decode some hex! */
+
+ colon += 2;
+ sa->secretlen = espprint_decode_hex(ndo, sa->secret, sizeof(sa->secret), colon);
+ if(sa->secretlen == 0) return 0;
+ } else {
+ i = strlen(colon);
+
+ if (i < sizeof(sa->secret)) {
+ memcpy(sa->secret, colon, i);
+ sa->secretlen = i;
+ } else {
+ memcpy(sa->secret, colon, sizeof(sa->secret));
+ sa->secretlen = sizeof(sa->secret);
+ }
+ }
+
+ return 1;
+}
+USES_APPLE_RST
+
+/*
+ * for the moment, ignore the auth algorithm, just hard code the authenticator
+ * length. Need to research how openssl looks up HMAC stuff.
+ */
+static int
+espprint_decode_authalgo(netdissect_options *ndo,
+ char *decode, struct sa_list *sa)
+{
+ char *colon;
+
+ colon = strchr(decode, ':');
+ if (colon == NULL) {
+ (*ndo->ndo_warning)(ndo, "failed to decode espsecret: %s\n", decode);
+ return 0;
+ }
+ *colon = '\0';
+
+ if(ascii_strcasecmp(decode,"sha1") == 0 ||
+ ascii_strcasecmp(decode,"md5") == 0) {
+ sa->authlen = 12;
+ }
+ return 1;
+}
+
+static void esp_print_decode_ikeline(netdissect_options *ndo, char *line,
+ const char *file, int lineno)
+{
+ /* it's an IKEv2 secret, store it instead */
+ struct sa_list sa1;
+
+ char *init;
+ char *icookie, *rcookie;
+ int ilen, rlen;
+ char *authkey;
+ char *enckey;
+
+ init = strsep(&line, " \t");
+ icookie = strsep(&line, " \t");
+ rcookie = strsep(&line, " \t");
+ authkey = strsep(&line, " \t");
+ enckey = strsep(&line, " \t");
+
+ /* if any fields are missing */
+ if(!init || !icookie || !rcookie || !authkey || !enckey) {
+ (*ndo->ndo_warning)(ndo, "print_esp: failed to find all fields for ikev2 at %s:%u",
+ file, lineno);
+
+ return;
+ }
+
+ ilen = strlen(icookie);
+ rlen = strlen(rcookie);
+
+ if((init[0]!='I' && init[0]!='R')
+ || icookie[0]!='0' || icookie[1]!='x'
+ || rcookie[0]!='0' || rcookie[1]!='x'
+ || ilen!=18
+ || rlen!=18) {
+ (*ndo->ndo_warning)(ndo, "print_esp: line %s:%u improperly formatted.",
+ file, lineno);
+
+ (*ndo->ndo_warning)(ndo, "init=%s icookie=%s(%u) rcookie=%s(%u)",
+ init, icookie, ilen, rcookie, rlen);
+
+ return;
+ }
+
+ sa1.spi = 0;
+ sa1.initiator = (init[0] == 'I');
+ if(espprint_decode_hex(ndo, sa1.spii, sizeof(sa1.spii), icookie+2)!=8)
+ return;
+
+ if(espprint_decode_hex(ndo, sa1.spir, sizeof(sa1.spir), rcookie+2)!=8)
+ return;
+
+ if(!espprint_decode_encalgo(ndo, enckey, &sa1)) return;
+
+ if(!espprint_decode_authalgo(ndo, authkey, &sa1)) return;
+
+ esp_print_addsa(ndo, &sa1, FALSE);
+}
+
+/*
+ *
+ * special form: file /name
+ * causes us to go read from this file instead.
+ *
+ */
+static void esp_print_decode_onesecret(netdissect_options *ndo, char *line,
+ const char *file, int lineno)
+{
+ struct sa_list sa1;
+ int sa_def;
+
+ char *spikey;
+ char *decode;
+
+ spikey = strsep(&line, " \t");
+ sa_def = 0;
+ memset(&sa1, 0, sizeof(struct sa_list));
+
+ /* if there is only one token, then it is an algo:key token */
+ if (line == NULL) {
+ decode = spikey;
+ spikey = NULL;
+ /* sa1.daddr.version = 0; */
+ /* memset(&sa1.daddr, 0, sizeof(sa1.daddr)); */
+ /* sa1.spi = 0; */
+ sa_def = 1;
+ } else
+ decode = line;
+
+ if (spikey && ascii_strcasecmp(spikey, "file") == 0) {
+ /* open file and read it */
+ FILE *secretfile;
+ char fileline[1024];
+ int subfile_lineno=0;
+ char *nl;
+ char *filename = line;
+
+ secretfile = fopen(filename, FOPEN_READ_TXT);
+ if (secretfile == NULL) {
+ (*ndo->ndo_error)(ndo, S_ERR_ND_OPEN_FILE,
+ "%s: can't open %s: %s\n",
+ __func__, filename, strerror(errno));
+ }
+
+ while (fgets(fileline, sizeof(fileline)-1, secretfile) != NULL) {
+ subfile_lineno++;
+ /* remove newline from the line */
+ nl = strchr(fileline, '\n');
+ if (nl)
+ *nl = '\0';
+ if (fileline[0] == '#') continue;
+ if (fileline[0] == '\0') continue;
+
+ esp_print_decode_onesecret(ndo, fileline, filename, subfile_lineno);
+ }
+ fclose(secretfile);
+
+ return;
+ }
+
+ if (spikey && ascii_strcasecmp(spikey, "ikev2") == 0) {
+ esp_print_decode_ikeline(ndo, line, file, lineno);
+ return;
+ }
+
+ if (spikey) {
+
+ char *spistr, *foo;
+ uint32_t spino;
+
+ spistr = strsep(&spikey, "@");
+ if (spistr == NULL) {
+ (*ndo->ndo_warning)(ndo, "print_esp: failed to find the @ token");
+ return;
+ }
+
+ spino = strtoul(spistr, &foo, 0);
+ if (spistr == foo || !spikey) {
+ (*ndo->ndo_warning)(ndo, "print_esp: failed to decode spi# %s\n", foo);
+ return;
+ }
+
+ sa1.spi = spino;
+
+ if (strtoaddr6(spikey, &sa1.daddr.in6) == 1) {
+ sa1.daddr_version = 6;
+ } else if (strtoaddr(spikey, &sa1.daddr.in4) == 1) {
+ sa1.daddr_version = 4;
+ } else {
+ (*ndo->ndo_warning)(ndo, "print_esp: can not decode IP# %s\n", spikey);
+ return;
+ }
+ }
+
+ if (decode) {
+ /* skip any blank spaces */
+ while (*decode == ' ' || *decode == '\t' || *decode == '\r' || *decode == '\n')
+ decode++;
+
+ if(!espprint_decode_encalgo(ndo, decode, &sa1)) {
+ return;
+ }
+ }
+
+ esp_print_addsa(ndo, &sa1, sa_def);
+}
+
+USES_APPLE_DEPRECATED_API
+static void esp_init(netdissect_options *ndo _U_)
+{
+ /*
+ * 0.9.6 doesn't appear to define OPENSSL_API_COMPAT, so
+ * we check whether it's undefined or it's less than the
+ * value for 1.1.0.
+ */
+#if !defined(OPENSSL_API_COMPAT) || OPENSSL_API_COMPAT < 0x10100000L
+ OpenSSL_add_all_algorithms();
+#endif
+ EVP_add_cipher_alias(SN_des_ede3_cbc, "3des");
+}
+USES_APPLE_RST
+
+void esp_decodesecret_print(netdissect_options *ndo)
+{
+ char *line;
+ char *p;
+ static int initialized = 0;
+
+ if (!initialized) {
+ esp_init(ndo);
+ initialized = 1;
+ }
+
+ p = ndo->ndo_espsecret;
+
+ while (p && p[0] != '\0') {
+ /* pick out the first line or first thing until a comma */
+ if ((line = strsep(&p, "\n,")) == NULL) {
+ line = p;
+ p = NULL;
+ }
+
+ esp_print_decode_onesecret(ndo, line, "cmdline", 0);
+ }
+
+ ndo->ndo_espsecret = NULL;
+}
+
+#endif
+
+#ifdef HAVE_LIBCRYPTO
+#define USED_IF_LIBCRYPTO
+#else
+#define USED_IF_LIBCRYPTO _U_
+#endif
+
+#ifdef HAVE_LIBCRYPTO
+USES_APPLE_DEPRECATED_API
+#endif
+void
+esp_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2 USED_IF_LIBCRYPTO,
+ u_int ver USED_IF_LIBCRYPTO,
+ int fragmented USED_IF_LIBCRYPTO,
+ u_int ttl_hl USED_IF_LIBCRYPTO)
+{
+ const struct newesp *esp;
+ const u_char *ep;
+#ifdef HAVE_LIBCRYPTO
+ const struct ip *ip;
+ struct sa_list *sa = NULL;
+ const struct ip6_hdr *ip6 = NULL;
+ const u_char *iv;
+ u_int ivlen;
+ u_int payloadlen;
+ const u_char *ct;
+ u_char *pt;
+ u_int padlen;
+ u_int nh;
+#endif
+
+ ndo->ndo_protocol = "esp";
+ esp = (const struct newesp *)bp;
+
+ /* 'ep' points to the end of available data. */
+ ep = ndo->ndo_snapend;
+
+ if ((const u_char *)(esp + 1) >= ep) {
+ nd_print_trunc(ndo);
+ return;
+ }
+ ND_PRINT("ESP(spi=0x%08x", GET_BE_U_4(esp->esp_spi));
+ ND_PRINT(",seq=0x%x)", GET_BE_U_4(esp->esp_seq));
+ ND_PRINT(", length %u", length);
+
+#ifdef HAVE_LIBCRYPTO
+ /* initiailize SAs */
+ if (ndo->ndo_sa_list_head == NULL) {
+ if (!ndo->ndo_espsecret)
+ return;
+
+ esp_decodesecret_print(ndo);
+ }
+
+ if (ndo->ndo_sa_list_head == NULL)
+ return;
+
+ ip = (const struct ip *)bp2;
+ switch (ver) {
+ case 6:
+ ip6 = (const struct ip6_hdr *)bp2;
+ /* we do not attempt to decrypt jumbograms */
+ if (!GET_BE_U_2(ip6->ip6_plen))
+ return;
+ /* XXX - check whether it's fragmented? */
+ /* if we can't get nexthdr, we do not need to decrypt it */
+
+ /* see if we can find the SA, and if so, decode it */
+ for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
+ if (sa->spi == GET_BE_U_4(esp->esp_spi) &&
+ sa->daddr_version == 6 &&
+ UNALIGNED_MEMCMP(&sa->daddr.in6, &ip6->ip6_dst,
+ sizeof(nd_ipv6)) == 0) {
+ break;
+ }
+ }
+ break;
+ case 4:
+ /* nexthdr & padding are in the last fragment */
+ if (fragmented)
+ return;
+
+ /* see if we can find the SA, and if so, decode it */
+ for (sa = ndo->ndo_sa_list_head; sa != NULL; sa = sa->next) {
+ if (sa->spi == GET_BE_U_4(esp->esp_spi) &&
+ sa->daddr_version == 4 &&
+ UNALIGNED_MEMCMP(&sa->daddr.in4, &ip->ip_dst,
+ sizeof(nd_ipv4)) == 0) {
+ break;
+ }
+ }
+ break;
+ default:
+ return;
+ }
+
+ /* if we didn't find the specific one, then look for
+ * an unspecified one.
+ */
+ if (sa == NULL)
+ sa = ndo->ndo_sa_default;
+
+ /* if not found fail */
+ if (sa == NULL)
+ return;
+
+ /* pointer to the IV, if there is one */
+ iv = (const u_char *)(esp + 1) + 0;
+ /* length of the IV, if there is one; 0, if there isn't */
+ ivlen = sa->ivlen;
+
+ /*
+ * Get a pointer to the ciphertext.
+ *
+ * p points to the beginning of the payload, i.e. to the
+ * initialization vector, so if we skip past the initialization
+ * vector, it points to the beginning of the ciphertext.
+ */
+ ct = iv + ivlen;
+
+ /*
+ * Make sure the authentication data/integrity check value length
+ * isn't bigger than the total amount of data available after
+ * the ESP header and initialization vector is removed and,
+ * if not, slice the authentication data/ICV off.
+ */
+ if (ep - ct < sa->authlen) {
+ nd_print_trunc(ndo);
+ return;
+ }
+ ep = ep - sa->authlen;
+
+ /*
+ * Calculate the length of the ciphertext. ep points to
+ * the beginning of the authentication data/integrity check
+ * value, i.e. right past the end of the ciphertext;
+ */
+ payloadlen = ep - ct;
+
+ if (sa->evp == NULL)
+ return;
+
+ /*
+ * If the next header value is past the end of the available
+ * data, we won't be able to fetch it once we've decrypted
+ * the ciphertext, so there's no point in decrypting the data.
+ *
+ * Report it as truncation.
+ */
+ if (!ND_TTEST_1(ep - 1)) {
+ nd_print_trunc(ndo);
+ return;
+ }
+
+ pt = do_decrypt(ndo, "esp_print", sa, iv, ct, payloadlen);
+ if (pt == NULL)
+ return;
+
+ /*
+ * Switch to the output buffer for dissection, and
+ * save it on the buffer stack so it can be freed.
+ */
+ ep = pt + payloadlen;
+ if (!nd_push_buffer(ndo, pt, pt, ep)) {
+ free(pt);
+ (*ndo->ndo_error)(ndo, S_ERR_ND_MEM_ALLOC,
+ "%s: can't push buffer on buffer stack", __func__);
+ }
+
+ /*
+ * Sanity check for pad length; if it, plus 2 for the pad
+ * length and next header fields, is bigger than the ciphertext
+ * length (which is also the plaintext length), it's too big.
+ *
+ * XXX - the check can fail if the packet is corrupt *or* if
+ * it was not decrypted with the correct key, so that the
+ * "plaintext" is not what was being sent.
+ */
+ padlen = GET_U_1(ep - 2);
+ if (padlen + 2 > payloadlen) {
+ nd_print_trunc(ndo);
+ return;
+ }
+
+ /* Get the next header */
+ nh = GET_U_1(ep - 1);
+
+ ND_PRINT(": ");
+
+ /* Now dissect the plaintext. */
+ ip_demux_print(ndo, pt, payloadlen - (padlen + 2), ver, fragmented,
+ ttl_hl, nh, bp2);
+
+ /* Pop the buffer, freeing it. */
+ nd_pop_packet_info(ndo);
+#endif
+}
+#ifdef HAVE_LIBCRYPTO
+USES_APPLE_RST
+#endif
diff --git a/print-ether.c b/print-ether.c
new file mode 100644
index 0000000..2596cd6
--- /dev/null
+++ b/print-ether.c
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Ethernet printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+
+/*
+ * Structure of an Ethernet header.
+ */
+struct ether_header {
+ nd_mac_addr ether_dhost;
+ nd_mac_addr ether_shost;
+ nd_uint16_t ether_length_type;
+};
+
+/*
+ * Length of an Ethernet header; note that some compilers may pad
+ * "struct ether_header" to a multiple of 4 bytes, for example, so
+ * "sizeof (struct ether_header)" may not give the right answer.
+ */
+#define ETHER_HDRLEN 14
+
+const struct tok ethertype_values[] = {
+ { ETHERTYPE_IP, "IPv4" },
+ { ETHERTYPE_MPLS, "MPLS unicast" },
+ { ETHERTYPE_MPLS_MULTI, "MPLS multicast" },
+ { ETHERTYPE_IPV6, "IPv6" },
+ { ETHERTYPE_8021Q, "802.1Q" },
+ { ETHERTYPE_8021Q9100, "802.1Q-9100" },
+ { ETHERTYPE_8021QinQ, "802.1Q-QinQ" },
+ { ETHERTYPE_8021Q9200, "802.1Q-9200" },
+ { ETHERTYPE_MACSEC, "802.1AE MACsec" },
+ { ETHERTYPE_VMAN, "VMAN" },
+ { ETHERTYPE_PUP, "PUP" },
+ { ETHERTYPE_ARP, "ARP"},
+ { ETHERTYPE_REVARP, "Reverse ARP"},
+ { ETHERTYPE_NS, "NS" },
+ { ETHERTYPE_SPRITE, "Sprite" },
+ { ETHERTYPE_TRAIL, "Trail" },
+ { ETHERTYPE_MOPDL, "MOP DL" },
+ { ETHERTYPE_MOPRC, "MOP RC" },
+ { ETHERTYPE_DN, "DN" },
+ { ETHERTYPE_LAT, "LAT" },
+ { ETHERTYPE_SCA, "SCA" },
+ { ETHERTYPE_TEB, "TEB" },
+ { ETHERTYPE_LANBRIDGE, "Lanbridge" },
+ { ETHERTYPE_DECDNS, "DEC DNS" },
+ { ETHERTYPE_DECDTS, "DEC DTS" },
+ { ETHERTYPE_VEXP, "VEXP" },
+ { ETHERTYPE_VPROD, "VPROD" },
+ { ETHERTYPE_ATALK, "Appletalk" },
+ { ETHERTYPE_AARP, "Appletalk ARP" },
+ { ETHERTYPE_IPX, "IPX" },
+ { ETHERTYPE_PPP, "PPP" },
+ { ETHERTYPE_MPCP, "MPCP" },
+ { ETHERTYPE_SLOW, "Slow Protocols" },
+ { ETHERTYPE_PPPOED, "PPPoE D" },
+ { ETHERTYPE_PPPOES, "PPPoE S" },
+ { ETHERTYPE_EAPOL, "EAPOL" },
+ { ETHERTYPE_RRCP, "RRCP" },
+ { ETHERTYPE_MS_NLB_HB, "MS NLB heartbeat" },
+ { ETHERTYPE_JUMBO, "Jumbo" },
+ { ETHERTYPE_NSH, "NSH" },
+ { ETHERTYPE_LOOPBACK, "Loopback" },
+ { ETHERTYPE_ISO, "OSI" },
+ { ETHERTYPE_GRE_ISO, "GRE-OSI" },
+ { ETHERTYPE_CFM_OLD, "CFM (old)" },
+ { ETHERTYPE_CFM, "CFM" },
+ { ETHERTYPE_IEEE1905_1, "IEEE1905.1" },
+ { ETHERTYPE_LLDP, "LLDP" },
+ { ETHERTYPE_TIPC, "TIPC"},
+ { ETHERTYPE_GEONET_OLD, "GeoNet (old)"},
+ { ETHERTYPE_GEONET, "GeoNet"},
+ { ETHERTYPE_CALM_FAST, "CALM FAST"},
+ { ETHERTYPE_AOE, "AoE" },
+ { ETHERTYPE_PTP, "PTP" },
+ { ETHERTYPE_ARISTA, "Arista Vendor Specific Protocol" },
+ { 0, NULL}
+};
+
+static void
+ether_addresses_print(netdissect_options *ndo, const u_char *src,
+ const u_char *dst)
+{
+ ND_PRINT("%s > %s, ",
+ GET_ETHERADDR_STRING(src), GET_ETHERADDR_STRING(dst));
+}
+
+static void
+ether_type_print(netdissect_options *ndo, uint16_t type)
+{
+ if (!ndo->ndo_qflag)
+ ND_PRINT("ethertype %s (0x%04x)",
+ tok2str(ethertype_values, "Unknown", type), type);
+ else
+ ND_PRINT("%s",
+ tok2str(ethertype_values, "Unknown Ethertype (0x%04x)", type));
+}
+
+/*
+ * Common code for printing Ethernet frames.
+ *
+ * It can handle Ethernet headers with extra tag information inserted
+ * after the destination and source addresses, as is inserted by some
+ * switch chips, and extra encapsulation header information before
+ * printing Ethernet header information (such as a LANE ID for ATM LANE).
+ */
+static u_int
+ether_common_print(netdissect_options *ndo, const u_char *p, u_int length,
+ u_int caplen,
+ void (*print_switch_tag)(netdissect_options *ndo, const u_char *),
+ u_int switch_tag_len,
+ void (*print_encap_header)(netdissect_options *ndo, const u_char *),
+ const u_char *encap_header_arg)
+{
+ const struct ether_header *ehp;
+ u_int orig_length;
+ u_int hdrlen;
+ u_short length_type;
+ int printed_length;
+ int llc_hdrlen;
+ struct lladdr_info src, dst;
+
+ if (caplen < ETHER_HDRLEN + switch_tag_len) {
+ nd_print_trunc(ndo);
+ return caplen;
+ }
+ if (length < ETHER_HDRLEN + switch_tag_len) {
+ nd_print_trunc(ndo);
+ return length;
+ }
+
+ if (print_encap_header != NULL)
+ (*print_encap_header)(ndo, encap_header_arg);
+
+ orig_length = length;
+
+ /*
+ * Get the source and destination addresses, skip past them,
+ * and print them if we're printing the link-layer header.
+ */
+ ehp = (const struct ether_header *)p;
+ src.addr = ehp->ether_shost;
+ src.addr_string = etheraddr_string;
+ dst.addr = ehp->ether_dhost;
+ dst.addr_string = etheraddr_string;
+
+ length -= 2*MAC_ADDR_LEN;
+ caplen -= 2*MAC_ADDR_LEN;
+ p += 2*MAC_ADDR_LEN;
+ hdrlen = 2*MAC_ADDR_LEN;
+
+ if (ndo->ndo_eflag)
+ ether_addresses_print(ndo, src.addr, dst.addr);
+
+ /*
+ * Print the switch tag, if we have one, and skip past it.
+ */
+ if (print_switch_tag != NULL)
+ (*print_switch_tag)(ndo, p);
+
+ length -= switch_tag_len;
+ caplen -= switch_tag_len;
+ p += switch_tag_len;
+ hdrlen += switch_tag_len;
+
+ /*
+ * Get the length/type field, skip past it, and print it
+ * if we're printing the link-layer header.
+ */
+recurse:
+ length_type = GET_BE_U_2(p);
+
+ length -= 2;
+ caplen -= 2;
+ p += 2;
+ hdrlen += 2;
+
+ /*
+ * Process 802.1AE MACsec headers.
+ */
+ printed_length = 0;
+ if (length_type == ETHERTYPE_MACSEC) {
+ /*
+ * MACsec, aka IEEE 802.1AE-2006
+ * Print the header, and try to print the payload if it's not encrypted
+ */
+ if (ndo->ndo_eflag) {
+ ether_type_print(ndo, length_type);
+ ND_PRINT(", length %u: ", orig_length);
+ printed_length = 1;
+ }
+
+ int ret = macsec_print(ndo, &p, &length, &caplen, &hdrlen,
+ &src, &dst);
+
+ if (ret == 0) {
+ /* Payload is encrypted; print it as raw data. */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ return hdrlen;
+ } else if (ret > 0) {
+ /* Problem printing the header; just quit. */
+ return ret;
+ } else {
+ /*
+ * Keep processing type/length fields.
+ */
+ length_type = GET_BE_U_2(p);
+
+ length -= 2;
+ caplen -= 2;
+ p += 2;
+ hdrlen += 2;
+ }
+ }
+
+ /*
+ * Process VLAN tag types.
+ */
+ while (length_type == ETHERTYPE_8021Q ||
+ length_type == ETHERTYPE_8021Q9100 ||
+ length_type == ETHERTYPE_8021Q9200 ||
+ length_type == ETHERTYPE_8021QinQ) {
+ /*
+ * It has a VLAN tag.
+ * Print VLAN information, and then go back and process
+ * the enclosed type field.
+ */
+ if (caplen < 4) {
+ ndo->ndo_protocol = "vlan";
+ nd_print_trunc(ndo);
+ return hdrlen + caplen;
+ }
+ if (length < 4) {
+ ndo->ndo_protocol = "vlan";
+ nd_print_trunc(ndo);
+ return hdrlen + length;
+ }
+ if (ndo->ndo_eflag) {
+ uint16_t tag = GET_BE_U_2(p);
+
+ ether_type_print(ndo, length_type);
+ if (!printed_length) {
+ ND_PRINT(", length %u: ", orig_length);
+ printed_length = 1;
+ } else
+ ND_PRINT(", ");
+ ND_PRINT("%s, ", ieee8021q_tci_string(tag));
+ }
+
+ length_type = GET_BE_U_2(p + 2);
+ p += 4;
+ length -= 4;
+ caplen -= 4;
+ hdrlen += 4;
+ }
+
+ /*
+ * We now have the final length/type field.
+ */
+ if (length_type <= MAX_ETHERNET_LENGTH_VAL) {
+ /*
+ * It's a length field, containing the length of the
+ * remaining payload; use it as such, as long as
+ * it's not too large (bigger than the actual payload).
+ */
+ if (length_type < length) {
+ length = length_type;
+ if (caplen > length)
+ caplen = length;
+ }
+
+ /*
+ * Cut off the snapshot length to the end of the
+ * payload.
+ */
+ nd_push_snapend(ndo, p + length);
+
+ if (ndo->ndo_eflag) {
+ ND_PRINT("802.3");
+ if (!printed_length)
+ ND_PRINT(", length %u: ", length);
+ }
+
+ /*
+ * An LLC header follows the length. Print that and
+ * higher layers.
+ */
+ llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst);
+ if (llc_hdrlen < 0) {
+ /* packet type not known, print raw packet */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = -llc_hdrlen;
+ }
+ hdrlen += llc_hdrlen;
+ nd_pop_packet_info(ndo);
+ } else if (length_type == ETHERTYPE_JUMBO) {
+ /*
+ * It's a type field, with the type for Alteon jumbo frames.
+ * See
+ *
+ * https://tools.ietf.org/html/draft-ietf-isis-ext-eth-01
+ *
+ * which indicates that, following the type field,
+ * there's an LLC header and payload.
+ */
+ /* Try to print the LLC-layer header & higher layers */
+ llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst);
+ if (llc_hdrlen < 0) {
+ /* packet type not known, print raw packet */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = -llc_hdrlen;
+ }
+ hdrlen += llc_hdrlen;
+ } else if (length_type == ETHERTYPE_ARISTA) {
+ if (caplen < 2) {
+ ND_PRINT("[|arista]");
+ return hdrlen + caplen;
+ }
+ if (length < 2) {
+ ND_PRINT("[|arista]");
+ return hdrlen + length;
+ }
+ ether_type_print(ndo, length_type);
+ ND_PRINT(", length %u: ", orig_length);
+ int bytesConsumed = arista_ethertype_print(ndo, p, length);
+ if (bytesConsumed > 0) {
+ p += bytesConsumed;
+ length -= bytesConsumed;
+ caplen -= bytesConsumed;
+ hdrlen += bytesConsumed;
+ goto recurse;
+ } else {
+ /* subtype/version not known, print raw packet */
+ if (!ndo->ndo_eflag && length_type > MAX_ETHERNET_LENGTH_VAL) {
+ ether_addresses_print(ndo, src.addr, dst.addr);
+ ether_type_print(ndo, length_type);
+ ND_PRINT(", length %u: ", orig_length);
+ }
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+ } else {
+ /*
+ * It's a type field with some other value.
+ */
+ if (ndo->ndo_eflag) {
+ ether_type_print(ndo, length_type);
+ if (!printed_length)
+ ND_PRINT(", length %u: ", orig_length);
+ else
+ ND_PRINT(", ");
+ }
+ if (ethertype_print(ndo, length_type, p, length, caplen, &src, &dst) == 0) {
+ /* type not known, print raw packet */
+ if (!ndo->ndo_eflag) {
+ /*
+ * We didn't print the full link-layer
+ * header, as -e wasn't specified, so
+ * print only the source and destination
+ * MAC addresses and the final Ethernet
+ * type.
+ */
+ ether_addresses_print(ndo, src.addr, dst.addr);
+ ether_type_print(ndo, length_type);
+ ND_PRINT(", length %u: ", orig_length);
+ }
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+ }
+ return hdrlen;
+}
+
+/*
+ * Print an Ethernet frame while specyfing a non-standard Ethernet header
+ * length.
+ * This might be encapsulated within another frame; we might be passed
+ * a pointer to a function that can print header information for that
+ * frame's protocol, and an argument to pass to that function.
+ *
+ * FIXME: caplen can and should be derived from ndo->ndo_snapend and p.
+ */
+u_int
+ether_switch_tag_print(netdissect_options *ndo, const u_char *p, u_int length,
+ u_int caplen,
+ void (*print_switch_tag)(netdissect_options *, const u_char *),
+ u_int switch_tag_len)
+{
+ return ether_common_print(ndo, p, length, caplen, print_switch_tag,
+ switch_tag_len, NULL, NULL);
+}
+
+/*
+ * Print an Ethernet frame.
+ * This might be encapsulated within another frame; we might be passed
+ * a pointer to a function that can print header information for that
+ * frame's protocol, and an argument to pass to that function.
+ *
+ * FIXME: caplen can and should be derived from ndo->ndo_snapend and p.
+ */
+u_int
+ether_print(netdissect_options *ndo,
+ const u_char *p, u_int length, u_int caplen,
+ void (*print_encap_header)(netdissect_options *ndo, const u_char *),
+ const u_char *encap_header_arg)
+{
+ ndo->ndo_protocol = "ether";
+ return ether_common_print(ndo, p, length, caplen, NULL, 0,
+ print_encap_header, encap_header_arg);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->len' is the length
+ * of the packet off the wire, and 'h->caplen' is the number
+ * of bytes actually captured.
+ */
+void
+ether_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h,
+ const u_char *p)
+{
+ ndo->ndo_protocol = "ether";
+ ndo->ndo_ll_hdr_len +=
+ ether_print(ndo, p, h->len, h->caplen, NULL, NULL);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->len' is the length
+ * of the packet off the wire, and 'h->caplen' is the number
+ * of bytes actually captured.
+ *
+ * This is for DLT_NETANALYZER, which has a 4-byte pseudo-header
+ * before the Ethernet header.
+ */
+void
+netanalyzer_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h,
+ const u_char *p)
+{
+ /*
+ * Fail if we don't have enough data for the Hilscher pseudo-header.
+ */
+ ndo->ndo_protocol = "netanalyzer";
+ ND_TCHECK_LEN(p, 4);
+
+ /* Skip the pseudo-header. */
+ ndo->ndo_ll_hdr_len += 4;
+ ndo->ndo_ll_hdr_len +=
+ ether_print(ndo, p + 4, h->len - 4, h->caplen - 4, NULL, NULL);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->len' is the length
+ * of the packet off the wire, and 'h->caplen' is the number
+ * of bytes actually captured.
+ *
+ * This is for DLT_NETANALYZER_TRANSPARENT, which has a 4-byte
+ * pseudo-header, a 7-byte Ethernet preamble, and a 1-byte Ethernet SOF
+ * before the Ethernet header.
+ */
+void
+netanalyzer_transparent_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h,
+ const u_char *p)
+{
+ /*
+ * Fail if we don't have enough data for the Hilscher pseudo-header,
+ * preamble, and SOF.
+ */
+ ndo->ndo_protocol = "netanalyzer_transparent";
+ ND_TCHECK_LEN(p, 12);
+
+ /* Skip the pseudo-header, preamble, and SOF. */
+ ndo->ndo_ll_hdr_len += 12;
+ ndo->ndo_ll_hdr_len +=
+ ether_print(ndo, p + 12, h->len - 12, h->caplen - 12, NULL, NULL);
+}
+
+/*
+ * Prints the packet payload, given an Ethernet type code for the payload's
+ * protocol.
+ *
+ * Returns non-zero if it can do so, zero if the ethertype is unknown.
+ */
+
+int
+ethertype_print(netdissect_options *ndo,
+ u_short ether_type, const u_char *p,
+ u_int length, u_int caplen,
+ const struct lladdr_info *src, const struct lladdr_info *dst)
+{
+ switch (ether_type) {
+
+ case ETHERTYPE_IP:
+ ip_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_IPV6:
+ ip6_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_ARP:
+ case ETHERTYPE_REVARP:
+ arp_print(ndo, p, length, caplen);
+ return (1);
+
+ case ETHERTYPE_DN:
+ decnet_print(ndo, p, length, caplen);
+ return (1);
+
+ case ETHERTYPE_ATALK:
+ if (ndo->ndo_vflag)
+ ND_PRINT("et1 ");
+ atalk_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_AARP:
+ aarp_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_IPX:
+ ND_PRINT("(NOV-ETHII) ");
+ ipx_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_ISO:
+ if (length == 0 || caplen == 0) {
+ ndo->ndo_protocol = "isoclns";
+ nd_print_trunc(ndo);
+ return (1);
+ }
+ isoclns_print(ndo, p + 1, length - 1);
+ return(1);
+
+ case ETHERTYPE_PPPOED:
+ case ETHERTYPE_PPPOES:
+ case ETHERTYPE_PPPOED2:
+ case ETHERTYPE_PPPOES2:
+ pppoe_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_EAPOL:
+ eapol_print(ndo, p);
+ return (1);
+
+ case ETHERTYPE_RRCP:
+ rrcp_print(ndo, p, length, src, dst);
+ return (1);
+
+ case ETHERTYPE_PPP:
+ if (length) {
+ ND_PRINT(": ");
+ ppp_print(ndo, p, length);
+ }
+ return (1);
+
+ case ETHERTYPE_MPCP:
+ mpcp_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_SLOW:
+ slow_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_CFM:
+ case ETHERTYPE_CFM_OLD:
+ cfm_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_LLDP:
+ lldp_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_NSH:
+ nsh_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_LOOPBACK:
+ loopback_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_MPLS:
+ case ETHERTYPE_MPLS_MULTI:
+ mpls_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_TIPC:
+ tipc_print(ndo, p, length, caplen);
+ return (1);
+
+ case ETHERTYPE_MS_NLB_HB:
+ msnlb_print(ndo, p);
+ return (1);
+
+ case ETHERTYPE_GEONET_OLD:
+ case ETHERTYPE_GEONET:
+ geonet_print(ndo, p, length, src);
+ return (1);
+
+ case ETHERTYPE_CALM_FAST:
+ calm_fast_print(ndo, p, length, src);
+ return (1);
+
+ case ETHERTYPE_AOE:
+ aoe_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_PTP:
+ ptp_print(ndo, p, length);
+ return (1);
+
+ case ETHERTYPE_LAT:
+ case ETHERTYPE_SCA:
+ case ETHERTYPE_MOPRC:
+ case ETHERTYPE_MOPDL:
+ case ETHERTYPE_IEEE1905_1:
+ /* default_print for now */
+ default:
+ return (0);
+ }
+}
diff --git a/print-fddi.c b/print-fddi.c
new file mode 100644
index 0000000..fb8d3ed
--- /dev/null
+++ b/print-fddi.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Fiber Distributed Data Interface (FDDI) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+/*
+ * Based on Ultrix if_fddi.h
+ */
+
+struct fddi_header {
+ nd_uint8_t fddi_fc; /* frame control */
+ nd_mac_addr fddi_dhost;
+ nd_mac_addr fddi_shost;
+};
+
+/*
+ * Length of an FDDI header; note that some compilers may pad
+ * "struct fddi_header" to a multiple of 4 bytes, for example, so
+ * "sizeof (struct fddi_header)" may not give the right
+ * answer.
+ */
+#define FDDI_HDRLEN 13
+
+/* Useful values for fddi_fc (frame control) field */
+
+/*
+ * FDDI Frame Control bits
+ */
+#define FDDIFC_C 0x80 /* Class bit */
+#define FDDIFC_L 0x40 /* Address length bit */
+#define FDDIFC_F 0x30 /* Frame format bits */
+#define FDDIFC_Z 0x0f /* Control bits */
+
+/*
+ * FDDI Frame Control values. (48-bit addressing only).
+ */
+#define FDDIFC_VOID 0x40 /* Void frame */
+#define FDDIFC_NRT 0x80 /* Nonrestricted token */
+#define FDDIFC_RT 0xc0 /* Restricted token */
+#define FDDIFC_SMT_INFO 0x41 /* SMT Info */
+#define FDDIFC_SMT_NSA 0x4F /* SMT Next station adrs */
+#define FDDIFC_MAC_BEACON 0xc2 /* MAC Beacon frame */
+#define FDDIFC_MAC_CLAIM 0xc3 /* MAC Claim frame */
+#define FDDIFC_LLC_ASYNC 0x50 /* Async. LLC frame */
+#define FDDIFC_LLC_SYNC 0xd0 /* Sync. LLC frame */
+#define FDDIFC_IMP_ASYNC 0x60 /* Implementor Async. */
+#define FDDIFC_IMP_SYNC 0xe0 /* Implementor Synch. */
+#define FDDIFC_SMT 0x40 /* SMT frame */
+#define FDDIFC_MAC 0xc0 /* MAC frame */
+
+#define FDDIFC_CLFF 0xF0 /* Class/Length/Format bits */
+#define FDDIFC_ZZZZ 0x0F /* Control bits */
+
+/*
+ * Some FDDI interfaces use bit-swapped addresses.
+ */
+#if defined(ultrix) || defined(__alpha) || defined(__bsdi) || defined(__NetBSD__) || defined(__linux__)
+static int fddi_bitswap = 0;
+#else
+static int fddi_bitswap = 1;
+#endif
+
+/*
+ * FDDI support for tcpdump, by Jeffrey Mogul [DECWRL], June 1992
+ *
+ * Based in part on code by Van Jacobson, which bears this note:
+ *
+ * NOTE: This is a very preliminary hack for FDDI support.
+ * There are all sorts of wired in constants & nothing (yet)
+ * to print SMT packets as anything other than hex dumps.
+ * Most of the necessary changes are waiting on my redoing
+ * the "header" that a kernel fddi driver supplies to bpf: I
+ * want it to look like one byte of 'direction' (0 or 1
+ * depending on whether the packet was inbound or outbound),
+ * two bytes of system/driver dependent data (anything an
+ * implementor thinks would be useful to filter on and/or
+ * save per-packet, then the real 21-byte FDDI header.
+ * Steve McCanne & I have also talked about adding the
+ * 'direction' byte to all bpf headers (e.g., in the two
+ * bytes of padding on an ethernet header). It's not clear
+ * we could do this in a backwards compatible way & we hate
+ * the idea of an incompatible bpf change. Discussions are
+ * proceeding.
+ *
+ * Also, to really support FDDI (and better support 802.2
+ * over ethernet) we really need to re-think the rather simple
+ * minded assumptions about fixed length & fixed format link
+ * level headers made in gencode.c. One day...
+ *
+ * - vj
+ */
+
+static const u_char fddi_bit_swap[] = {
+ 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+ 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+ 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+ 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+ 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+ 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+ 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+ 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+ 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+ 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+ 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+ 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+ 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+ 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+ 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+ 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+ 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+ 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+ 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+ 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+ 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+ 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+ 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+ 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+ 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+ 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+ 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+ 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+ 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+ 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+ 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+ 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff,
+};
+
+/*
+ * Print FDDI frame-control bits
+ */
+static void
+print_fddi_fc(netdissect_options *ndo, u_char fc)
+{
+ switch (fc) {
+
+ case FDDIFC_VOID: /* Void frame */
+ ND_PRINT("void ");
+ break;
+
+ case FDDIFC_NRT: /* Nonrestricted token */
+ ND_PRINT("nrt ");
+ break;
+
+ case FDDIFC_RT: /* Restricted token */
+ ND_PRINT("rt ");
+ break;
+
+ case FDDIFC_SMT_INFO: /* SMT Info */
+ ND_PRINT("info ");
+ break;
+
+ case FDDIFC_SMT_NSA: /* SMT Next station adrs */
+ ND_PRINT("nsa ");
+ break;
+
+ case FDDIFC_MAC_BEACON: /* MAC Beacon frame */
+ ND_PRINT("beacon ");
+ break;
+
+ case FDDIFC_MAC_CLAIM: /* MAC Claim frame */
+ ND_PRINT("claim ");
+ break;
+
+ default:
+ switch (fc & FDDIFC_CLFF) {
+
+ case FDDIFC_MAC:
+ ND_PRINT("mac%1x ", fc & FDDIFC_ZZZZ);
+ break;
+
+ case FDDIFC_SMT:
+ ND_PRINT("smt%1x ", fc & FDDIFC_ZZZZ);
+ break;
+
+ case FDDIFC_LLC_ASYNC:
+ ND_PRINT("async%1x ", fc & FDDIFC_ZZZZ);
+ break;
+
+ case FDDIFC_LLC_SYNC:
+ ND_PRINT("sync%1x ", fc & FDDIFC_ZZZZ);
+ break;
+
+ case FDDIFC_IMP_ASYNC:
+ ND_PRINT("imp_async%1x ", fc & FDDIFC_ZZZZ);
+ break;
+
+ case FDDIFC_IMP_SYNC:
+ ND_PRINT("imp_sync%1x ", fc & FDDIFC_ZZZZ);
+ break;
+
+ default:
+ ND_PRINT("%02x ", fc);
+ break;
+ }
+ }
+}
+
+/* Extract src, dst addresses */
+static void
+extract_fddi_addrs(const struct fddi_header *fddip, char *fsrc, char *fdst)
+{
+ int i;
+
+ if (fddi_bitswap) {
+ /*
+ * bit-swap the fddi addresses (isn't the IEEE standards
+ * process wonderful!) then convert them to names.
+ */
+ for (i = 0; i < 6; ++i)
+ fdst[i] = fddi_bit_swap[fddip->fddi_dhost[i]];
+ for (i = 0; i < 6; ++i)
+ fsrc[i] = fddi_bit_swap[fddip->fddi_shost[i]];
+ }
+ else {
+ memcpy(fdst, (const char *)fddip->fddi_dhost, 6);
+ memcpy(fsrc, (const char *)fddip->fddi_shost, 6);
+ }
+}
+
+/*
+ * Print the FDDI MAC header
+ */
+static void
+fddi_hdr_print(netdissect_options *ndo,
+ const struct fddi_header *fddip, u_int length,
+ const u_char *fsrc, const u_char *fdst)
+{
+ const char *srcname, *dstname;
+
+ srcname = etheraddr_string(ndo, fsrc);
+ dstname = etheraddr_string(ndo, fdst);
+
+ if (!ndo->ndo_qflag)
+ print_fddi_fc(ndo, GET_U_1(fddip->fddi_fc));
+ ND_PRINT("%s > %s, length %u: ",
+ srcname, dstname,
+ length);
+}
+
+static void
+fddi_smt_print(netdissect_options *ndo, const u_char *p _U_, u_int length _U_)
+{
+ ND_PRINT("<SMT printer not yet implemented>");
+}
+
+u_int
+fddi_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen)
+{
+ const struct fddi_header *fddip = (const struct fddi_header *)p;
+ uint8_t fc;
+ nd_mac_addr srcmac, dstmac;
+ struct lladdr_info src, dst;
+ int llc_hdrlen;
+
+ ndo->ndo_protocol = "fddi";
+ if (caplen < FDDI_HDRLEN) {
+ nd_print_trunc(ndo);
+ return (caplen);
+ }
+
+ fc = GET_U_1(fddip->fddi_fc);
+
+ /*
+ * Get the FDDI addresses into a canonical form
+ */
+ extract_fddi_addrs(fddip, (char *)srcmac, (char *)dstmac);
+
+ if (ndo->ndo_eflag)
+ fddi_hdr_print(ndo, fddip, length, srcmac, dstmac);
+
+ src.addr = srcmac;
+ src.addr_string = etheraddr_string;
+ dst.addr = dstmac;
+ dst.addr_string = etheraddr_string;
+
+ /* Skip over FDDI MAC header */
+ length -= FDDI_HDRLEN;
+ p += FDDI_HDRLEN;
+ caplen -= FDDI_HDRLEN;
+
+ /* Frame Control field determines interpretation of packet */
+ if ((fc & FDDIFC_CLFF) == FDDIFC_LLC_ASYNC) {
+ /* Try to print the LLC-layer header & higher layers */
+ llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst);
+ if (llc_hdrlen < 0) {
+ /*
+ * Some kinds of LLC packet we cannot
+ * handle intelligently
+ */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = -llc_hdrlen;
+ }
+ } else if ((fc & FDDIFC_CLFF) == FDDIFC_SMT) {
+ fddi_smt_print(ndo, p, caplen);
+ llc_hdrlen = 0;
+ } else {
+ /* Some kinds of FDDI packet we cannot handle intelligently */
+ if (!ndo->ndo_eflag)
+ fddi_hdr_print(ndo, fddip, length + FDDI_HDRLEN, srcmac,
+ dstmac);
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = 0;
+ }
+ return (FDDI_HDRLEN + llc_hdrlen);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the FDDI header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+fddi_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "fddi";
+ ndo->ndo_ll_hdr_len += fddi_print(ndo, p, h->len, h->caplen);
+}
diff --git a/print-forces.c b/print-forces.c
new file mode 100644
index 0000000..b95eab7
--- /dev/null
+++ b/print-forces.c
@@ -0,0 +1,1716 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 2009 Mojatatu Networks, Inc
+ *
+ */
+
+/* \summary: Forwarding and Control Element Separation (ForCES) Protocol printer */
+
+/* specification: RFC 5810 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+
+#define ForCES_VERS 1
+#define ForCES_HDRL 24
+#define ForCES_ALNL 4U
+#define TLV_HDRL 4
+#define ILV_HDRL 8
+
+#define TOM_RSVD 0x0
+#define TOM_ASSNSETUP 0x1
+#define TOM_ASSNTEARD 0x2
+#define TOM_CONFIG 0x3
+#define TOM_QUERY 0x4
+#define TOM_EVENTNOT 0x5
+#define TOM_PKTREDIR 0x6
+#define TOM_HEARTBT 0x0F
+#define TOM_ASSNSETREP 0x11
+#define TOM_CONFIGREP 0x13
+#define TOM_QUERYREP 0x14
+
+/*
+ * tom_h Flags: resv1(8b):maxtlvs(4b):resv2(2b):mintlv(2b)
+*/
+#define ZERO_TTLV 0x01
+#define ZERO_MORE_TTLV 0x02
+#define ONE_MORE_TTLV 0x04
+#define ZERO_TLV 0x00
+#define ONE_TLV 0x10
+#define TWO_TLV 0x20
+#define MAX_TLV 0xF0
+
+#define TTLV_T1 (ONE_MORE_TTLV|ONE_TLV)
+#define TTLV_T2 (ONE_MORE_TTLV|MAX_TLV)
+
+struct tom_h {
+ uint32_t v;
+ uint16_t flags;
+ uint16_t op_msk;
+ const char *s;
+ int (*print) (netdissect_options *ndo, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+};
+
+enum {
+ TOM_RSV_I,
+ TOM_ASS_I,
+ TOM_AST_I,
+ TOM_CFG_I,
+ TOM_QRY_I,
+ TOM_EVN_I,
+ TOM_RED_I,
+ TOM_HBT_I,
+ TOM_ASR_I,
+ TOM_CNR_I,
+ TOM_QRR_I,
+ _TOM_RSV_MAX
+};
+#define TOM_MAX_IND (_TOM_RSV_MAX - 1)
+
+static int
+tom_valid(uint8_t tom)
+{
+ if (tom > 0) {
+ if (tom >= 0x7 && tom <= 0xe)
+ return 0;
+ if (tom == 0x10)
+ return 0;
+ if (tom > 0x14)
+ return 0;
+ return 1;
+ } else
+ return 0;
+}
+
+static const char *
+ForCES_node(uint32_t node)
+{
+ if (node <= 0x3FFFFFFF)
+ return "FE";
+ if (node >= 0x40000000 && node <= 0x7FFFFFFF)
+ return "CE";
+ if (node >= 0xC0000000 && node <= 0xFFFFFFEF)
+ return "AllMulticast";
+ if (node == 0xFFFFFFFD)
+ return "AllCEsBroadcast";
+ if (node == 0xFFFFFFFE)
+ return "AllFEsBroadcast";
+ if (node == 0xFFFFFFFF)
+ return "AllBroadcast";
+
+ return "ForCESreserved";
+
+}
+
+static const struct tok ForCES_ACKs[] = {
+ {0x0, "NoACK"},
+ {0x1, "SuccessACK"},
+ {0x2, "FailureACK"},
+ {0x3, "AlwaysACK"},
+ {0, NULL}
+};
+
+static const struct tok ForCES_EMs[] = {
+ {0x0, "EMReserved"},
+ {0x1, "execute-all-or-none"},
+ {0x2, "execute-until-failure"},
+ {0x3, "continue-execute-on-failure"},
+ {0, NULL}
+};
+
+static const struct tok ForCES_ATs[] = {
+ {0x0, "Standalone"},
+ {0x1, "2PCtransaction"},
+ {0, NULL}
+};
+
+static const struct tok ForCES_TPs[] = {
+ {0x0, "StartofTransaction"},
+ {0x1, "MiddleofTransaction"},
+ {0x2, "EndofTransaction"},
+ {0x3, "abort"},
+ {0, NULL}
+};
+
+/*
+ * Structure of forces header, naked of TLVs.
+ */
+struct forcesh {
+ nd_uint8_t fm_vrsvd; /* version and reserved */
+#define ForCES_V(forcesh) (GET_U_1((forcesh)->fm_vrsvd) >> 4)
+ nd_uint8_t fm_tom; /* type of message */
+ nd_uint16_t fm_len; /* total length * 4 bytes */
+#define ForCES_BLN(forcesh) ((uint32_t)(GET_BE_U_2((forcesh)->fm_len) << 2))
+ nd_uint32_t fm_sid; /* Source ID */
+#define ForCES_SID(forcesh) GET_BE_U_4((forcesh)->fm_sid)
+ nd_uint32_t fm_did; /* Destination ID */
+#define ForCES_DID(forcesh) GET_BE_U_4((forcesh)->fm_did)
+ nd_uint8_t fm_cor[8]; /* correlator */
+ nd_uint32_t fm_flags; /* flags */
+#define ForCES_ACK(forcesh) ((GET_BE_U_4((forcesh)->fm_flags)&0xC0000000) >> 30)
+#define ForCES_PRI(forcesh) ((GET_BE_U_4((forcesh)->fm_flags)&0x38000000) >> 27)
+#define ForCES_RS1(forcesh) ((GET_BE_U_4((forcesh)->fm_flags)&0x07000000) >> 24)
+#define ForCES_EM(forcesh) ((GET_BE_U_4((forcesh)->fm_flags)&0x00C00000) >> 22)
+#define ForCES_AT(forcesh) ((GET_BE_U_4((forcesh)->fm_flags)&0x00200000) >> 21)
+#define ForCES_TP(forcesh) ((GET_BE_U_4((forcesh)->fm_flags)&0x00180000) >> 19)
+#define ForCES_RS2(forcesh) ((GET_BE_U_4((forcesh)->fm_flags)&0x0007FFFF) >> 0)
+};
+
+#define ForCES_HLN_VALID(fhl,tlen) ((tlen) >= ForCES_HDRL && \
+ (fhl) >= ForCES_HDRL && \
+ (fhl) == (tlen))
+
+#define F_LFB_RSVD 0x0
+#define F_LFB_FEO 0x1
+#define F_LFB_FEPO 0x2
+static const struct tok ForCES_LFBs[] = {
+ {F_LFB_RSVD, "Invalid TLV"},
+ {F_LFB_FEO, "FEObj LFB"},
+ {F_LFB_FEPO, "FEProtoObj LFB"},
+ {0, NULL}
+};
+
+/* this is defined in RFC5810 section A.2 */
+/* https://www.iana.org/assignments/forces/forces.xhtml#oper-tlv-types */
+enum {
+ F_OP_RSV = 0,
+ F_OP_SET = 1,
+ F_OP_SETPROP = 2,
+ F_OP_SETRESP = 3,
+ F_OP_SETPRESP = 4,
+ F_OP_DEL = 5,
+ F_OP_DELRESP = 6,
+ F_OP_GET = 7,
+ F_OP_GETPROP = 8,
+ F_OP_GETRESP = 9,
+ F_OP_GETPRESP = 10,
+ F_OP_REPORT = 11,
+ F_OP_COMMIT = 12,
+ F_OP_RCOMMIT = 13,
+ F_OP_RTRCOMP = 14,
+ _F_OP_MAX
+};
+#define F_OP_MAX (_F_OP_MAX - 1)
+
+enum {
+ B_OP_SET = 1 << (F_OP_SET - 1),
+ B_OP_SETPROP = 1 << (F_OP_SETPROP - 1),
+ B_OP_SETRESP = 1 << (F_OP_SETRESP - 1),
+ B_OP_SETPRESP = 1 << (F_OP_SETPRESP - 1),
+ B_OP_DEL = 1 << (F_OP_DEL - 1),
+ B_OP_DELRESP = 1 << (F_OP_DELRESP - 1),
+ B_OP_GET = 1 << (F_OP_GET - 1),
+ B_OP_GETPROP = 1 << (F_OP_GETPROP - 1),
+ B_OP_GETRESP = 1 << (F_OP_GETRESP - 1),
+ B_OP_GETPRESP = 1 << (F_OP_GETPRESP - 1),
+ B_OP_REPORT = 1 << (F_OP_REPORT - 1),
+ B_OP_COMMIT = 1 << (F_OP_COMMIT - 1),
+ B_OP_RCOMMIT = 1 << (F_OP_RCOMMIT - 1),
+ B_OP_RTRCOMP = 1 << (F_OP_RTRCOMP - 1)
+};
+
+struct optlv_h {
+ uint16_t flags;
+ uint16_t op_msk;
+ const char *s;
+ int (*print) (netdissect_options *ndo, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+};
+
+static int genoptlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+static int recpdoptlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+static int invoptlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+
+#define OP_MIN_SIZ 8
+struct pathdata_h {
+ nd_uint16_t pflags;
+ nd_uint16_t pIDcnt;
+};
+
+#define B_FULLD 0x1
+#define B_SPARD 0x2
+#define B_RESTV 0x4
+#define B_KEYIN 0x8
+#define B_APPND 0x10
+#define B_TRNG 0x20
+
+static const struct optlv_h OPTLV_msg[F_OP_MAX + 1] = {
+ /* F_OP_RSV */ {ZERO_TTLV, 0, "Invalid OPTLV", invoptlv_print},
+ /* F_OP_SET */ {TTLV_T2, B_FULLD | B_SPARD, " Set", recpdoptlv_print},
+ /* F_OP_SETPROP */
+ {TTLV_T2, B_FULLD | B_SPARD, " SetProp", recpdoptlv_print},
+ /* F_OP_SETRESP */ {TTLV_T2, B_RESTV, " SetResp", recpdoptlv_print},
+ /* F_OP_SETPRESP */ {TTLV_T2, B_RESTV, " SetPropResp", recpdoptlv_print},
+ /* F_OP_DEL */ {ZERO_TTLV, 0, " Del", recpdoptlv_print},
+ /* F_OP_DELRESP */ {TTLV_T2, B_RESTV, " DelResp", recpdoptlv_print},
+ /* F_OP_GET */ {ZERO_TTLV, 0, " Get", recpdoptlv_print},
+ /* F_OP_GETPROP */ {ZERO_TTLV, 0, " GetProp", recpdoptlv_print},
+ /* F_OP_GETRESP */
+ {TTLV_T2, B_FULLD | B_SPARD | B_RESTV, " GetResp", recpdoptlv_print},
+ /* F_OP_GETPRESP */
+ {TTLV_T2, B_FULLD | B_RESTV, " GetPropResp", recpdoptlv_print},
+ /* F_OP_REPORT */
+ {TTLV_T2, B_FULLD | B_SPARD, " Report", recpdoptlv_print},
+ /* F_OP_COMMIT */ {ZERO_TTLV, 0, " Commit", NULL},
+ /* F_OP_RCOMMIT */ {TTLV_T1, B_RESTV, " RCommit", genoptlv_print},
+ /* F_OP_RTRCOMP */ {ZERO_TTLV, 0, " RTRCOMP", NULL},
+};
+
+static const struct optlv_h *
+get_forces_optlv_h(uint16_t opt)
+{
+ if (opt > F_OP_MAX || opt == F_OP_RSV)
+ return &OPTLV_msg[F_OP_RSV];
+
+ return &OPTLV_msg[opt];
+}
+
+#define IND_SIZE 256
+#define IND_CHR ' '
+#define IND_PREF '\n'
+#define IND_SUF 0x0
+static char ind_buf[IND_SIZE];
+
+static char *
+indent_pr(int indent, int nlpref)
+{
+ int i = 0;
+ char *r = ind_buf;
+
+ if (indent > (IND_SIZE - 1))
+ indent = IND_SIZE - 1;
+
+ if (nlpref) {
+ r[i] = IND_PREF;
+ i++;
+ indent--;
+ }
+
+ while (--indent >= 0)
+ r[i++] = IND_CHR;
+
+ r[i] = IND_SUF;
+ return r;
+}
+
+static int
+op_valid(uint16_t op, uint16_t mask)
+{
+ if (op == 0)
+ return 0;
+ if (op <= F_OP_MAX)
+ return (1 << (op - 1)) & mask; /* works only for 0x0001 through 0x0010 */
+ /* I guess we should allow vendor operations? */
+ if (op >= 0x8000)
+ return 1;
+ return 0;
+}
+
+#define F_TLV_RSVD 0x0000
+#define F_TLV_REDR 0x0001
+#define F_TLV_ASRS 0x0010
+#define F_TLV_ASRT 0x0011
+#define F_TLV_LFBS 0x1000
+#define F_TLV_PDAT 0x0110
+#define F_TLV_KEYI 0x0111
+#define F_TLV_FULD 0x0112
+#define F_TLV_SPAD 0x0113
+#define F_TLV_REST 0x0114
+#define F_TLV_METD 0x0115
+#define F_TLV_REDD 0x0116
+#define F_TLV_TRNG 0x0117
+
+
+#define F_TLV_VNST 0x8000
+
+static const struct tok ForCES_TLV[] = {
+ {F_TLV_RSVD, "Invalid TLV"},
+ {F_TLV_REDR, "REDIRECT TLV"},
+ {F_TLV_ASRS, "ASResult TLV"},
+ {F_TLV_ASRT, "ASTreason TLV"},
+ {F_TLV_LFBS, "LFBselect TLV"},
+ {F_TLV_PDAT, "PATH-DATA TLV"},
+ {F_TLV_KEYI, "KEYINFO TLV"},
+ {F_TLV_FULD, "FULLDATA TLV"},
+ {F_TLV_SPAD, "SPARSEDATA TLV"},
+ {F_TLV_REST, "RESULT TLV"},
+ {F_TLV_METD, "METADATA TLV"},
+ {F_TLV_REDD, "REDIRECTDATA TLV"},
+ {0, NULL}
+};
+
+#define TLV_HLN 4
+static int
+ttlv_valid(uint16_t ttlv)
+{
+ if (ttlv > 0) {
+ if (ttlv == 1 || ttlv == 0x1000)
+ return 1;
+ if (ttlv >= 0x10 && ttlv <= 0x11)
+ return 1;
+ if (ttlv >= 0x110 && ttlv <= 0x116)
+ return 1;
+ if (ttlv >= 0x8000)
+ return 0; /* XXX: */
+ }
+
+ return 0;
+}
+
+struct forces_ilv {
+ nd_uint32_t type;
+ nd_uint32_t length;
+};
+
+struct forces_tlv {
+ nd_uint16_t type;
+ nd_uint16_t length;
+};
+
+#define F_ALN_LEN(len) roundup2(len, ForCES_ALNL)
+#define GET_TOP_TLV(fhdr) ((const struct forces_tlv *)((fhdr) + sizeof (struct forcesh)))
+#define TLV_SET_LEN(len) (F_ALN_LEN(TLV_HDRL) + (len))
+#define TLV_DATA(tlvp) ((const void*)(((const char*)(tlvp)) + TLV_SET_LEN(0)))
+#define GO_NXT_TLV(tlv,rlen) ((rlen) -= F_ALN_LEN(GET_BE_U_2((tlv)->length)), \
+ (const struct forces_tlv*)(((const char*)(tlv)) \
+ + F_ALN_LEN(GET_BE_U_2((tlv)->length))))
+#define ILV_SET_LEN(len) (F_ALN_LEN(ILV_HDRL) + (len))
+#define ILV_DATA(ilvp) ((const void*)(((const char*)(ilvp)) + ILV_SET_LEN(0)))
+#define GO_NXT_ILV(ilv,rlen) ((rlen) -= F_ALN_LEN(GET_BE_U_4((ilv)->length)), \
+ (const struct forces_ilv *)(((const char*)(ilv)) \
+ + F_ALN_LEN(GET_BE_U_4((ilv)->length))))
+#define INVALID_RLEN 1
+#define INVALID_STLN 2
+#define INVALID_LTLN 3
+#define INVALID_ALEN 4
+
+static const struct tok ForCES_TLV_err[] = {
+ {INVALID_RLEN, "Invalid total length"},
+ {INVALID_STLN, "xLV too short"},
+ {INVALID_LTLN, "xLV too long"},
+ {INVALID_ALEN, "data padding missing"},
+ {0, NULL}
+};
+
+static u_int
+tlv_valid(u_int tlvl, u_int rlen)
+{
+ if (rlen < TLV_HDRL)
+ return INVALID_RLEN;
+ if (tlvl < TLV_HDRL)
+ return INVALID_STLN;
+ if (tlvl > rlen)
+ return INVALID_LTLN;
+ if (rlen < F_ALN_LEN(tlvl))
+ return INVALID_ALEN;
+
+ return 0;
+}
+
+static int
+ilv_valid(netdissect_options *ndo, const struct forces_ilv *ilv, u_int rlen)
+{
+ if (rlen < ILV_HDRL)
+ return INVALID_RLEN;
+ if (GET_BE_U_4(ilv->length) < ILV_HDRL)
+ return INVALID_STLN;
+ if (GET_BE_U_4(ilv->length) > rlen)
+ return INVALID_LTLN;
+ if (rlen < F_ALN_LEN(GET_BE_U_4(ilv->length)))
+ return INVALID_ALEN;
+
+ return 0;
+}
+
+static int lfbselect_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+static int redirect_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+static int asrtlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+static int asttlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+
+struct forces_lfbsh {
+ nd_uint32_t class;
+ nd_uint32_t instance;
+};
+
+#define ASSNS_OPS (B_OP_REPORT)
+#define CFG_OPS (B_OP_SET|B_OP_SETPROP|B_OP_DEL|B_OP_COMMIT|B_OP_RTRCOMP)
+#define CFG_ROPS (B_OP_SETRESP|B_OP_SETPRESP|B_OP_DELRESP|B_OP_RCOMMIT)
+#define CFG_QY (B_OP_GET|B_OP_GETPROP)
+#define CFG_QYR (B_OP_GETRESP|B_OP_GETPRESP)
+#define CFG_EVN (B_OP_REPORT)
+
+static const struct tom_h ForCES_msg[TOM_MAX_IND + 1] = {
+ /* TOM_RSV_I */ {TOM_RSVD, ZERO_TTLV, 0, "Invalid message", NULL},
+ /* TOM_ASS_I */ {TOM_ASSNSETUP, ZERO_MORE_TTLV | TWO_TLV, ASSNS_OPS,
+ "Association Setup", lfbselect_print},
+ /* TOM_AST_I */
+ {TOM_ASSNTEARD, TTLV_T1, 0, "Association TearDown", asttlv_print},
+ /* TOM_CFG_I */ {TOM_CONFIG, TTLV_T2, CFG_OPS, "Config", lfbselect_print},
+ /* TOM_QRY_I */ {TOM_QUERY, TTLV_T2, CFG_QY, "Query", lfbselect_print},
+ /* TOM_EVN_I */ {TOM_EVENTNOT, TTLV_T1, CFG_EVN, "Event Notification",
+ lfbselect_print},
+ /* TOM_RED_I */
+ {TOM_PKTREDIR, TTLV_T2, 0, "Packet Redirect", redirect_print},
+ /* TOM_HBT_I */ {TOM_HEARTBT, ZERO_TTLV, 0, "HeartBeat", NULL},
+ /* TOM_ASR_I */
+ {TOM_ASSNSETREP, TTLV_T1, 0, "Association Response", asrtlv_print},
+ /* TOM_CNR_I */ {TOM_CONFIGREP, TTLV_T2, CFG_ROPS, "Config Response",
+ lfbselect_print},
+ /* TOM_QRR_I */
+ {TOM_QUERYREP, TTLV_T2, CFG_QYR, "Query Response", lfbselect_print},
+};
+
+static const struct tom_h *
+get_forces_tom(uint8_t tom)
+{
+ int i;
+ for (i = TOM_RSV_I; i <= TOM_MAX_IND; i++) {
+ const struct tom_h *th = &ForCES_msg[i];
+ if (th->v == tom)
+ return th;
+ }
+ return &ForCES_msg[TOM_RSV_I];
+}
+
+struct pdata_ops {
+ uint32_t v;
+ uint16_t flags;
+ uint16_t op_msk;
+ const char *s;
+ int (*print) (netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+};
+
+enum {
+ PD_RSV_I,
+ PD_SEL_I,
+ PD_FDT_I,
+ PD_SDT_I,
+ PD_RES_I,
+ PD_PDT_I,
+ _PD_RSV_MAX
+};
+#define PD_MAX_IND (_TOM_RSV_MAX - 1)
+
+static int
+pd_valid(uint16_t pd)
+{
+ if (pd >= F_TLV_PDAT && pd <= F_TLV_REST)
+ return 1;
+ return 0;
+}
+
+static void
+chk_op_type(netdissect_options *ndo,
+ uint16_t type, uint16_t msk, uint16_t omsk)
+{
+ if (type != F_TLV_PDAT) {
+ if (msk & B_KEYIN) {
+ if (type != F_TLV_KEYI) {
+ ND_PRINT("Based on flags expected KEYINFO TLV!\n");
+ }
+ } else {
+ if (!(msk & omsk)) {
+ ND_PRINT("Illegal DATA encoding for type 0x%x programmed %x got %x\n",
+ type, omsk, msk);
+ }
+ }
+ }
+
+}
+
+#define F_SELKEY 1
+#define F_SELTABRANGE 2
+#define F_TABAPPEND 4
+
+struct res_val {
+ nd_uint8_t result;
+ nd_uint8_t resv1;
+ nd_uint16_t resv2;
+};
+
+static int prestlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+static int pkeyitlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+static int fdatatlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+static int sdatatlv_print(netdissect_options *, const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent);
+
+static const struct pdata_ops ForCES_pdata[PD_MAX_IND + 1] = {
+ /* PD_RSV_I */ {0, 0, 0, "Invalid message", NULL},
+ /* PD_SEL_I */ {F_TLV_KEYI, 0, 0, "KEYINFO TLV", pkeyitlv_print},
+ /* PD_FDT_I */ {F_TLV_FULD, 0, B_FULLD, "FULLDATA TLV", fdatatlv_print},
+ /* PD_SDT_I */ {F_TLV_SPAD, 0, B_SPARD, "SPARSEDATA TLV", sdatatlv_print},
+ /* PD_RES_I */ {F_TLV_REST, 0, B_RESTV, "RESULT TLV", prestlv_print},
+ /* PD_PDT_I */
+ {F_TLV_PDAT, 0, 0, "Inner PATH-DATA TLV", recpdoptlv_print},
+};
+
+static const struct pdata_ops *
+get_forces_pd(uint16_t pd)
+{
+ int i;
+ for (i = PD_RSV_I + 1; i <= PD_MAX_IND; i++) {
+ const struct pdata_ops *pdo = &ForCES_pdata[i];
+ if (pdo->v == pd)
+ return pdo;
+ }
+ return &ForCES_pdata[TOM_RSV_I];
+}
+
+enum {
+ E_SUCCESS,
+ E_INVALID_HEADER,
+ E_LENGTH_MISMATCH,
+ E_VERSION_MISMATCH,
+ E_INVALID_DESTINATION_PID,
+ E_LFB_UNKNOWN,
+ E_LFB_NOT_FOUND,
+ E_LFB_INSTANCE_ID_NOT_FOUND,
+ E_INVALID_PATH,
+ E_COMPONENT_DOES_NOT_EXIST,
+ E_EXISTS,
+ E_NOT_FOUND,
+ E_READ_ONLY,
+ E_INVALID_ARRAY_CREATION,
+ E_VALUE_OUT_OF_RANGE,
+ E_CONTENTS_TOO_LONG,
+ E_INVALID_PARAMETERS,
+ E_INVALID_MESSAGE_TYPE,
+ E_INVALID_FLAGS,
+ E_INVALID_TLV,
+ E_EVENT_ERROR,
+ E_NOT_SUPPORTED,
+ E_MEMORY_ERROR,
+ E_INTERNAL_ERROR,
+ /* 0x18-0xFE are reserved .. */
+ E_UNSPECIFIED_ERROR = 0XFF
+};
+
+static const struct tok ForCES_errs[] = {
+ {E_SUCCESS, "SUCCESS"},
+ {E_INVALID_HEADER, "INVALID HEADER"},
+ {E_LENGTH_MISMATCH, "LENGTH MISMATCH"},
+ {E_VERSION_MISMATCH, "VERSION MISMATCH"},
+ {E_INVALID_DESTINATION_PID, "INVALID DESTINATION PID"},
+ {E_LFB_UNKNOWN, "LFB UNKNOWN"},
+ {E_LFB_NOT_FOUND, "LFB NOT FOUND"},
+ {E_LFB_INSTANCE_ID_NOT_FOUND, "LFB INSTANCE ID NOT FOUND"},
+ {E_INVALID_PATH, "INVALID PATH"},
+ {E_COMPONENT_DOES_NOT_EXIST, "COMPONENT DOES NOT EXIST"},
+ {E_EXISTS, "EXISTS ALREADY"},
+ {E_NOT_FOUND, "NOT FOUND"},
+ {E_READ_ONLY, "READ ONLY"},
+ {E_INVALID_ARRAY_CREATION, "INVALID ARRAY CREATION"},
+ {E_VALUE_OUT_OF_RANGE, "VALUE OUT OF RANGE"},
+ {E_CONTENTS_TOO_LONG, "CONTENTS TOO LONG"},
+ {E_INVALID_PARAMETERS, "INVALID PARAMETERS"},
+ {E_INVALID_MESSAGE_TYPE, "INVALID MESSAGE TYPE"},
+ {E_INVALID_FLAGS, "INVALID FLAGS"},
+ {E_INVALID_TLV, "INVALID TLV"},
+ {E_EVENT_ERROR, "EVENT ERROR"},
+ {E_NOT_SUPPORTED, "NOT SUPPORTED"},
+ {E_MEMORY_ERROR, "MEMORY ERROR"},
+ {E_INTERNAL_ERROR, "INTERNAL ERROR"},
+ {E_UNSPECIFIED_ERROR, "UNSPECIFIED ERROR"},
+ {0, NULL}
+};
+
+#define RESLEN 4
+
+static int
+prestlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
+ const u_char *tdp = (const u_char *) TLV_DATA(tlv);
+ const struct res_val *r = (const struct res_val *)tdp;
+ u_int dlen;
+ uint8_t result;
+
+ /*
+ * pdatacnt_print() has ensured that len (the TLV length)
+ * >= TLV_HDRL.
+ */
+ dlen = len - TLV_HDRL;
+ if (dlen != RESLEN) {
+ ND_PRINT("illegal RESULT-TLV: %u bytes!\n", dlen);
+ return -1;
+ }
+
+ ND_TCHECK_SIZE(r);
+ result = GET_U_1(r->result);
+ if (result >= 0x18 && result <= 0xFE) {
+ ND_PRINT("illegal reserved result code: 0x%x!\n", result);
+ return -1;
+ }
+
+ if (ndo->ndo_vflag >= 3) {
+ char *ib = indent_pr(indent, 0);
+ ND_PRINT("%s Result: %s (code 0x%x)\n", ib,
+ tok2str(ForCES_errs, NULL, result), result);
+ }
+ return 0;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+static int
+fdatatlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
+ u_int rlen;
+ const u_char *tdp = (const u_char *) TLV_DATA(tlv);
+ uint16_t type;
+
+ /*
+ * pdatacnt_print() or pkeyitlv_print() has ensured that len
+ * (the TLV length) >= TLV_HDRL.
+ */
+ rlen = len - TLV_HDRL;
+ ND_TCHECK_SIZE(tlv);
+ type = GET_BE_U_2(tlv->type);
+ if (type != F_TLV_FULD) {
+ ND_PRINT("Error: expecting FULLDATA!\n");
+ return -1;
+ }
+
+ if (ndo->ndo_vflag >= 3) {
+ char *ib = indent_pr(indent + 2, 1);
+ ND_PRINT("%s[", ib + 1);
+ hex_print(ndo, ib, tdp, rlen);
+ ND_PRINT("\n%s]", ib + 1);
+ }
+ return 0;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+static int
+sdatailv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ u_int rlen;
+ const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
+ int invilv;
+
+ if (len < ILV_HDRL) {
+ ND_PRINT("Error: BAD SPARSEDATA-TLV!\n");
+ return -1;
+ }
+ rlen = len;
+ indent += 1;
+ while (rlen != 0) {
+#if 0
+ ND_PRINT("Jamal - outstanding length <%u>\n", rlen);
+#endif
+ char *ib = indent_pr(indent, 1);
+ const u_char *tdp = (const u_char *) ILV_DATA(ilv);
+ invilv = ilv_valid(ndo, ilv, rlen);
+ if (invilv) {
+ ND_PRINT("%s[", ib + 1);
+ hex_print(ndo, ib, tdp, rlen);
+ ND_PRINT("\n%s]\n", ib + 1);
+ return -1;
+ }
+ if (ndo->ndo_vflag >= 3) {
+ u_int ilvl = GET_BE_U_4(ilv->length);
+ ND_PRINT("\n%s ILV: type %x length %u\n", ib + 1,
+ GET_BE_U_4(ilv->type), ilvl);
+ hex_print(ndo, "\t\t[", tdp, ilvl-ILV_HDRL);
+ }
+
+ ilv = GO_NXT_ILV(ilv, rlen);
+ }
+
+ return 0;
+}
+
+static int
+sdatatlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent)
+{
+ const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
+ u_int rlen;
+ const u_char *tdp = (const u_char *) TLV_DATA(tlv);
+ uint16_t type;
+
+ /*
+ * pdatacnt_print() has ensured that len (the TLV length)
+ * >= TLV_HDRL.
+ */
+ rlen = len - TLV_HDRL;
+ ND_TCHECK_SIZE(tlv);
+ type = GET_BE_U_2(tlv->type);
+ if (type != F_TLV_SPAD) {
+ ND_PRINT("Error: expecting SPARSEDATA!\n");
+ return -1;
+ }
+
+ return sdatailv_print(ndo, tdp, rlen, op_msk, indent);
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+static int
+pkeyitlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent)
+{
+ const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
+ const u_char *tdp = (const u_char *) TLV_DATA(tlv);
+ const u_char *dp = tdp + 4;
+ const struct forces_tlv *kdtlv = (const struct forces_tlv *)dp;
+ uint32_t id;
+ char *ib = indent_pr(indent, 0);
+ uint16_t type, tll;
+ u_int invtlv;
+
+ id = GET_BE_U_4(tdp);
+ ND_PRINT("%sKeyinfo: Key 0x%x\n", ib, id);
+ type = GET_BE_U_2(kdtlv->type);
+ tll = GET_BE_U_2(kdtlv->length);
+ invtlv = tlv_valid(tll, len);
+
+ if (invtlv) {
+ ND_PRINT("%s TLV type 0x%x len %u\n",
+ tok2str(ForCES_TLV_err, NULL, invtlv), type,
+ tll);
+ return -1;
+ }
+ /*
+ * At this point, tlv_valid() has ensured that the TLV
+ * length is large enough but not too large (it doesn't
+ * go past the end of the containing TLV).
+ */
+ tll = GET_BE_U_2(kdtlv->length);
+ dp = (const u_char *) TLV_DATA(kdtlv);
+ return fdatatlv_print(ndo, dp, tll, op_msk, indent);
+}
+
+#define PTH_DESC_SIZE 12
+
+static int
+pdatacnt_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t IDcnt, uint16_t op_msk, int indent)
+{
+ u_int i;
+ uint32_t id;
+ char *ib = indent_pr(indent, 0);
+
+ if ((op_msk & B_APPND) && ndo->ndo_vflag >= 3) {
+ ND_PRINT("%sTABLE APPEND\n", ib);
+ }
+ for (i = 0; i < IDcnt; i++) {
+ ND_TCHECK_4(pptr);
+ if (len < 4)
+ goto trunc;
+ id = GET_BE_U_4(pptr);
+ if (ndo->ndo_vflag >= 3)
+ ND_PRINT("%sID#%02u: %u\n", ib, i + 1, id);
+ len -= 4;
+ pptr += 4;
+ }
+
+ if ((op_msk & B_TRNG) || (op_msk & B_KEYIN)) {
+ if (op_msk & B_TRNG) {
+ uint32_t starti, endi;
+
+ if (len < PTH_DESC_SIZE) {
+ ND_PRINT("pathlength %u with key/range too short %u\n",
+ len, PTH_DESC_SIZE);
+ return -1;
+ }
+
+ pptr += sizeof(struct forces_tlv);
+ len -= sizeof(struct forces_tlv);
+
+ starti = GET_BE_U_4(pptr);
+ pptr += 4;
+ len -= 4;
+
+ endi = GET_BE_U_4(pptr);
+ pptr += 4;
+ len -= 4;
+
+ if (ndo->ndo_vflag >= 3)
+ ND_PRINT("%sTable range: [%u,%u]\n", ib, starti, endi);
+ }
+
+ if (op_msk & B_KEYIN) {
+ const struct forces_tlv *keytlv;
+ uint16_t tll;
+
+ if (len < PTH_DESC_SIZE) {
+ ND_PRINT("pathlength %u with key/range too short %u\n",
+ len, PTH_DESC_SIZE);
+ return -1;
+ }
+
+ /* skip keyid */
+ pptr += 4;
+ len -= 4;
+ keytlv = (const struct forces_tlv *)pptr;
+ /* skip header */
+ pptr += sizeof(struct forces_tlv);
+ len -= sizeof(struct forces_tlv);
+ /* skip key content */
+ tll = GET_BE_U_2(keytlv->length);
+ if (tll < TLV_HDRL) {
+ ND_PRINT("key content length %u < %u\n",
+ tll, TLV_HDRL);
+ return -1;
+ }
+ tll -= TLV_HDRL;
+ if (len < tll) {
+ ND_PRINT("key content too short\n");
+ return -1;
+ }
+ pptr += tll;
+ len -= tll;
+ }
+
+ }
+
+ if (len) {
+ const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
+ uint16_t type;
+ uint16_t tlvl, tll;
+ u_int pad = 0;
+ u_int aln;
+ u_int invtlv;
+
+ type = GET_BE_U_2(pdtlv->type);
+ tlvl = GET_BE_U_2(pdtlv->length);
+ invtlv = tlv_valid(tlvl, len);
+ if (invtlv) {
+ ND_PRINT("%s Outstanding bytes %u for TLV type 0x%x TLV len %u\n",
+ tok2str(ForCES_TLV_err, NULL, invtlv), len, type,
+ tlvl);
+ goto pd_err;
+ }
+ /*
+ * At this point, tlv_valid() has ensured that the TLV
+ * length is large enough but not too large (it doesn't
+ * go past the end of the containing TLV).
+ */
+ tll = tlvl - TLV_HDRL;
+ aln = F_ALN_LEN(tlvl);
+ if (aln > tlvl) {
+ if (aln > len) {
+ ND_PRINT("Invalid padded pathdata TLV type 0x%x len %u missing %u pad bytes\n",
+ type, tlvl, aln - len);
+ } else {
+ pad = aln - tlvl;
+ }
+ }
+ if (pd_valid(type)) {
+ const struct pdata_ops *ops = get_forces_pd(type);
+
+ if (ndo->ndo_vflag >= 3 && ops->v != F_TLV_PDAT) {
+ if (pad)
+ ND_PRINT("%s %s (Length %u DataLen %u pad %u Bytes)\n",
+ ib, ops->s, tlvl, tll, pad);
+ else
+ ND_PRINT("%s %s (Length %u DataLen %u Bytes)\n",
+ ib, ops->s, tlvl, tll);
+ }
+
+ chk_op_type(ndo, type, op_msk, ops->op_msk);
+
+ if (ops->print(ndo, (const u_char *)pdtlv,
+ tll + pad + TLV_HDRL, op_msk,
+ indent + 2) == -1)
+ return -1;
+ len -= (TLV_HDRL + pad + tll);
+ } else {
+ ND_PRINT("Invalid path data content type 0x%x len %u\n",
+ type, tlvl);
+pd_err:
+ if (tlvl) {
+ hex_print(ndo, "Bad Data val\n\t [",
+ pptr, len);
+ ND_PRINT("]\n");
+
+ return -1;
+ }
+ }
+ }
+ return len;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+static int
+pdata_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent)
+{
+ const struct pathdata_h *pdh = (const struct pathdata_h *)pptr;
+ char *ib = indent_pr(indent, 0);
+ u_int minsize = 0;
+ int more_pd = 0;
+ uint16_t idcnt = 0;
+
+ ND_TCHECK_SIZE(pdh);
+ if (len < sizeof(struct pathdata_h))
+ goto trunc;
+ if (ndo->ndo_vflag >= 3) {
+ ND_PRINT("\n%sPathdata: Flags 0x%x ID count %u\n",
+ ib, GET_BE_U_2(pdh->pflags),
+ GET_BE_U_2(pdh->pIDcnt));
+ }
+
+ if (GET_BE_U_2(pdh->pflags) & F_SELKEY) {
+ op_msk |= B_KEYIN;
+ }
+
+ /* Table GET Range operation */
+ if (GET_BE_U_2(pdh->pflags) & F_SELTABRANGE) {
+ op_msk |= B_TRNG;
+ }
+ /* Table SET append operation */
+ if (GET_BE_U_2(pdh->pflags) & F_TABAPPEND) {
+ op_msk |= B_APPND;
+ }
+
+ pptr += sizeof(struct pathdata_h);
+ len -= sizeof(struct pathdata_h);
+ idcnt = GET_BE_U_2(pdh->pIDcnt);
+ minsize = idcnt * 4;
+ if (len < minsize) {
+ ND_PRINT("\t\t\ttruncated IDs expected %uB got %uB\n", minsize,
+ len);
+ hex_print(ndo, "\t\t\tID Data[", pptr, len);
+ ND_PRINT("]\n");
+ return -1;
+ }
+
+ if ((op_msk & B_TRNG) && (op_msk & B_KEYIN)) {
+ ND_PRINT("\t\t\tIllegal to have both Table ranges and keys\n");
+ return -1;
+ }
+
+ more_pd = pdatacnt_print(ndo, pptr, len, idcnt, op_msk, indent);
+ if (more_pd > 0) {
+ int consumed = len - more_pd;
+ pptr += consumed;
+ len = more_pd;
+ /* XXX: Argh, recurse some more */
+ return recpdoptlv_print(ndo, pptr, len, op_msk, indent+1);
+ } else
+ return 0;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+static int
+genoptlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent)
+{
+ const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
+ uint16_t type;
+ u_int tlvl;
+ u_int invtlv;
+ char *ib = indent_pr(indent, 0);
+
+ type = GET_BE_U_2(pdtlv->type);
+ tlvl = GET_BE_U_2(pdtlv->length);
+ invtlv = tlv_valid(tlvl, len);
+ ND_PRINT("genoptlvprint - %s TLV type 0x%x len %u\n",
+ tok2str(ForCES_TLV, NULL, type), type, tlvl);
+ if (!invtlv) {
+ /*
+ * At this point, tlv_valid() has ensured that the TLV
+ * length is large enough but not too large (it doesn't
+ * go past the end of the containing TLV).
+ */
+ const u_char *dp = (const u_char *) TLV_DATA(pdtlv);
+
+ if (!ttlv_valid(type)) {
+ ND_PRINT("%s TLV type 0x%x len %u\n",
+ tok2str(ForCES_TLV_err, NULL, invtlv), type,
+ tlvl);
+ return -1;
+ }
+ if (ndo->ndo_vflag >= 3)
+ ND_PRINT("%s%s, length %u (data length %u Bytes)",
+ ib, tok2str(ForCES_TLV, NULL, type),
+ tlvl, tlvl - TLV_HDRL);
+
+ return pdata_print(ndo, dp, tlvl - TLV_HDRL, op_msk, indent + 1);
+ } else {
+ ND_PRINT("\t\t\tInvalid ForCES TLV type=%x", type);
+ return -1;
+ }
+}
+
+static int
+recpdoptlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent)
+{
+ const struct forces_tlv *pdtlv = (const struct forces_tlv *)pptr;
+
+ while (len != 0) {
+ uint16_t type, tlvl;
+ u_int invtlv;
+ char *ib;
+ const u_char *dp;
+
+ tlvl = GET_BE_U_2(pdtlv->length);
+ invtlv = tlv_valid(tlvl, len);
+ if (invtlv) {
+ break;
+ }
+
+ /*
+ * At this point, tlv_valid() has ensured that the TLV
+ * length is large enough but not too large (it doesn't
+ * go past the end of the containing TLV).
+ */
+ ib = indent_pr(indent, 0);
+ type = GET_BE_U_2(pdtlv->type);
+ dp = (const u_char *) TLV_DATA(pdtlv);
+
+ if (ndo->ndo_vflag >= 3)
+ ND_PRINT("%s%s, length %u (data encapsulated %u Bytes)",
+ ib, tok2str(ForCES_TLV, NULL, type),
+ tlvl,
+ tlvl - TLV_HDRL);
+
+ if (pdata_print(ndo, dp, tlvl - TLV_HDRL, op_msk, indent + 1) == -1)
+ return -1;
+ pdtlv = GO_NXT_TLV(pdtlv, len);
+ }
+
+ if (len) {
+ ND_PRINT("\n\t\tMessy PATHDATA TLV header, type (0x%x)\n\t\texcess of %u Bytes ",
+ GET_BE_U_2(pdtlv->type),
+ len - GET_BE_U_2(pdtlv->length));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+invoptlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ char *ib = indent_pr(indent, 1);
+
+ if (ndo->ndo_vflag >= 3) {
+ ND_PRINT("%sData[", ib + 1);
+ hex_print(ndo, ib, pptr, len);
+ ND_PRINT("%s]\n", ib);
+ }
+ return -1;
+}
+
+static int
+otlv_print(netdissect_options *ndo,
+ const struct forces_tlv *otlv, uint16_t op_msk _U_, int indent)
+{
+ int rc = 0;
+ const u_char *dp = (const u_char *) TLV_DATA(otlv);
+ uint16_t type;
+ u_int tll;
+ char *ib = indent_pr(indent, 0);
+ const struct optlv_h *ops;
+
+ /*
+ * lfbselect_print() has ensured that GET_BE_U_2(otlv->length)
+ * >= TLV_HDRL.
+ */
+ type = GET_BE_U_2(otlv->type);
+ tll = GET_BE_U_2(otlv->length) - TLV_HDRL;
+ ops = get_forces_optlv_h(type);
+ if (ndo->ndo_vflag >= 3) {
+ ND_PRINT("%sOper TLV %s(0x%x) length %u\n", ib, ops->s, type,
+ GET_BE_U_2(otlv->length));
+ }
+ /* rest of ops must at least have 12B {pathinfo} */
+ if (tll < OP_MIN_SIZ) {
+ ND_PRINT("\t\tOper TLV %s(0x%x) length %u\n", ops->s, type,
+ GET_BE_U_2(otlv->length));
+ ND_PRINT("\t\tTruncated data size %u minimum required %u\n", tll,
+ OP_MIN_SIZ);
+ return invoptlv_print(ndo, dp, tll, ops->op_msk, indent);
+
+ }
+
+ /* XXX - do anything with ops->flags? */
+ if(ops->print) {
+ rc = ops->print(ndo, dp, tll, ops->op_msk, indent + 1);
+ }
+ return rc;
+}
+
+#define ASTDLN 4
+#define ASTMCD 255
+static int
+asttlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ uint32_t rescode;
+ u_int dlen;
+ char *ib = indent_pr(indent, 0);
+
+ /*
+ * forces_type_print() has ensured that len (the TLV length)
+ * >= TLV_HDRL.
+ */
+ dlen = len - TLV_HDRL;
+ if (dlen != ASTDLN) {
+ ND_PRINT("illegal ASTresult-TLV: %u bytes!\n", dlen);
+ return -1;
+ }
+ rescode = GET_BE_U_4(pptr);
+ if (rescode > ASTMCD) {
+ ND_PRINT("illegal ASTresult result code: %u!\n", rescode);
+ return -1;
+ }
+
+ if (ndo->ndo_vflag >= 3) {
+ ND_PRINT("Teardown reason:\n%s", ib);
+ switch (rescode) {
+ case 0:
+ ND_PRINT("Normal Teardown");
+ break;
+ case 1:
+ ND_PRINT("Loss of Heartbeats");
+ break;
+ case 2:
+ ND_PRINT("Out of bandwidth");
+ break;
+ case 3:
+ ND_PRINT("Out of Memory");
+ break;
+ case 4:
+ ND_PRINT("Application Crash");
+ break;
+ default:
+ ND_PRINT("Unknown Teardown reason");
+ break;
+ }
+ ND_PRINT("(%x)\n%s", rescode, ib);
+ }
+ return 0;
+}
+
+#define ASRDLN 4
+#define ASRMCD 3
+static int
+asrtlv_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ uint32_t rescode;
+ u_int dlen;
+ char *ib = indent_pr(indent, 0);
+
+ /*
+ * forces_type_print() has ensured that len (the TLV length)
+ * >= TLV_HDRL.
+ */
+ dlen = len - TLV_HDRL;
+ if (dlen != ASRDLN) { /* id, instance, oper tlv */
+ ND_PRINT("illegal ASRresult-TLV: %u bytes!\n", dlen);
+ return -1;
+ }
+ rescode = GET_BE_U_4(pptr);
+
+ if (rescode > ASRMCD) {
+ ND_PRINT("illegal ASRresult result code: %u!\n", rescode);
+ return -1;
+ }
+
+ if (ndo->ndo_vflag >= 3) {
+ ND_PRINT("\n%s", ib);
+ switch (rescode) {
+ case 0:
+ ND_PRINT("Success ");
+ break;
+ case 1:
+ ND_PRINT("FE ID invalid ");
+ break;
+ case 2:
+ ND_PRINT("permission denied ");
+ break;
+ default:
+ ND_PRINT("Unknown ");
+ break;
+ }
+ ND_PRINT("(%x)\n%s", rescode, ib);
+ }
+ return 0;
+}
+
+#if 0
+/*
+ * XXX - not used.
+ */
+static int
+gentltlv_print(netdissect_options *ndo,
+ const u_char * pptr _U_, u_int len,
+ uint16_t op_msk _U_, int indent _U_)
+{
+ u_int dlen = len - TLV_HDRL;
+
+ if (dlen < 4) { /* at least 32 bits must exist */
+ ND_PRINT("truncated TLV: %u bytes missing! ", 4 - dlen);
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+#define RD_MIN 8
+
+static int
+print_metailv(netdissect_options *ndo,
+ const u_char * pptr, uint16_t op_msk _U_, int indent)
+{
+ u_int rlen;
+ char *ib = indent_pr(indent, 0);
+ /* XXX: check header length */
+ const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
+
+ /*
+ * print_metatlv() has ensured that len (what remains in the
+ * ILV) >= ILV_HDRL.
+ */
+ rlen = GET_BE_U_4(ilv->length) - ILV_HDRL;
+ ND_PRINT("%sMetaID 0x%x length %u\n", ib, GET_BE_U_4(ilv->type),
+ GET_BE_U_4(ilv->length));
+ if (ndo->ndo_vflag >= 3) {
+ hex_print(ndo, "\t\t[", ILV_DATA(ilv), rlen);
+ ND_PRINT(" ]\n");
+ }
+ return 0;
+}
+
+static int
+print_metatlv(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ u_int dlen;
+ char *ib = indent_pr(indent, 0);
+ u_int rlen;
+ const struct forces_ilv *ilv = (const struct forces_ilv *)pptr;
+ int invilv;
+
+ /*
+ * redirect_print() has ensured that len (what remains in the
+ * TLV) >= TLV_HDRL.
+ */
+ dlen = len - TLV_HDRL;
+ rlen = dlen;
+ ND_PRINT("\n%s METADATA length %u\n", ib, rlen);
+ while (rlen != 0) {
+ invilv = ilv_valid(ndo, ilv, rlen);
+ if (invilv) {
+ break;
+ }
+
+ /*
+ * At this point, ilv_valid() has ensured that the ILV
+ * length is large enough but not too large (it doesn't
+ * go past the end of the containing TLV).
+ */
+ print_metailv(ndo, (const u_char *) ilv, 0, indent + 1);
+ ilv = GO_NXT_ILV(ilv, rlen);
+ }
+
+ return 0;
+}
+
+
+static int
+print_reddata(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ u_int dlen;
+ char *ib = indent_pr(indent, 0);
+ u_int rlen;
+
+ dlen = len - TLV_HDRL;
+ rlen = dlen;
+ ND_PRINT("\n%s Redirect Data length %u\n", ib, rlen);
+
+ if (ndo->ndo_vflag >= 3) {
+ ND_PRINT("\t\t[");
+ hex_print(ndo, "\n\t\t", pptr, rlen);
+ ND_PRINT("\n\t\t]");
+ }
+
+ return 0;
+}
+
+static int
+redirect_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk _U_, int indent)
+{
+ const struct forces_tlv *tlv = (const struct forces_tlv *)pptr;
+ u_int dlen;
+ u_int rlen;
+ u_int invtlv;
+
+ /*
+ * forces_type_print() has ensured that len (the TLV length)
+ * >= TLV_HDRL.
+ */
+ dlen = len - TLV_HDRL;
+ if (dlen <= RD_MIN) {
+ ND_PRINT("\n\t\ttruncated Redirect TLV: %u bytes missing! ",
+ RD_MIN - dlen);
+ return -1;
+ }
+
+ rlen = dlen;
+ indent += 1;
+ while (rlen != 0) {
+ uint16_t type, tlvl;
+
+ type = GET_BE_U_2(tlv->type);
+ tlvl = GET_BE_U_2(tlv->length);
+ invtlv = tlv_valid(tlvl, rlen);
+ if (invtlv) {
+ ND_PRINT("Bad Redirect data\n");
+ break;
+ }
+
+ /*
+ * At this point, tlv_valid() has ensured that the TLV
+ * length is large enough but not too large (it doesn't
+ * go past the end of the containing TLV).
+ */
+ if (type == F_TLV_METD) {
+ print_metatlv(ndo, (const u_char *) TLV_DATA(tlv),
+ tlvl, 0,
+ indent);
+ } else if (type == F_TLV_REDD) {
+ print_reddata(ndo, (const u_char *) TLV_DATA(tlv),
+ tlvl, 0,
+ indent);
+ } else {
+ ND_PRINT("Unknown REDIRECT TLV 0x%x len %u\n",
+ type,
+ tlvl);
+ }
+
+ tlv = GO_NXT_TLV(tlv, rlen);
+ }
+
+ if (rlen) {
+ ND_PRINT("\n\t\tMessy Redirect TLV header, type (0x%x)\n\t\texcess of %u Bytes ",
+ GET_BE_U_2(tlv->type),
+ rlen - GET_BE_U_2(tlv->length));
+ return -1;
+ }
+
+ return 0;
+}
+
+#define OP_OFF 8
+#define OP_MIN 12
+
+static int
+lfbselect_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len,
+ uint16_t op_msk, int indent)
+{
+ const struct forces_lfbsh *lfbs;
+ const struct forces_tlv *otlv;
+ char *ib = indent_pr(indent, 0);
+ u_int dlen;
+ u_int rlen;
+ u_int invtlv;
+
+ /*
+ * forces_type_print() has ensured that len (the TLV length)
+ * >= TLV_HDRL.
+ */
+ dlen = len - TLV_HDRL;
+ if (dlen <= OP_MIN) { /* id, instance, oper tlv header .. */
+ ND_PRINT("\n\t\ttruncated lfb selector: %u bytes missing! ",
+ OP_MIN - dlen);
+ return -1;
+ }
+
+ /*
+ * At this point, we know that dlen > OP_MIN; OP_OFF < OP_MIN, so
+ * we also know that it's > OP_OFF.
+ */
+ rlen = dlen - OP_OFF;
+
+ lfbs = (const struct forces_lfbsh *)pptr;
+ ND_TCHECK_SIZE(lfbs);
+ if (ndo->ndo_vflag >= 3) {
+ ND_PRINT("\n%s%s(Classid %x) instance %x\n",
+ ib,
+ tok2str(ForCES_LFBs, NULL, GET_BE_U_4(lfbs->class)),
+ GET_BE_U_4(lfbs->class),
+ GET_BE_U_4(lfbs->instance));
+ }
+
+ otlv = (const struct forces_tlv *)(lfbs + 1);
+
+ indent += 1;
+ while (rlen != 0) {
+ uint16_t type, tlvl;
+
+ type = GET_BE_U_2(otlv->type);
+ tlvl = GET_BE_U_2(otlv->length);
+ invtlv = tlv_valid(tlvl, rlen);
+ if (invtlv)
+ break;
+
+ /*
+ * At this point, tlv_valid() has ensured that the TLV
+ * length is large enough but not too large (it doesn't
+ * go past the end of the containing TLV).
+ */
+ if (op_valid(type, op_msk)) {
+ otlv_print(ndo, otlv, 0, indent);
+ } else {
+ if (ndo->ndo_vflag < 3)
+ ND_PRINT("\n");
+ ND_PRINT("\t\tINValid oper-TLV type 0x%x length %u for this ForCES message\n",
+ type, tlvl);
+ invoptlv_print(ndo, (const u_char *)otlv, rlen, 0, indent);
+ }
+ otlv = GO_NXT_TLV(otlv, rlen);
+ }
+
+ if (rlen) {
+ ND_PRINT("\n\t\tMessy oper TLV header, type (0x%x)\n\t\texcess of %u Bytes ",
+ GET_BE_U_2(otlv->type),
+ rlen - GET_BE_U_2(otlv->length));
+ return -1;
+ }
+
+ return 0;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+static int
+forces_type_print(netdissect_options *ndo,
+ const u_char * pptr, const struct forcesh *fhdr _U_,
+ u_int mlen, const struct tom_h *tops)
+{
+ const struct forces_tlv *tltlv;
+ u_int rlen;
+ u_int invtlv;
+ int rc = 0;
+ u_int ttlv = 0;
+
+ /*
+ * forces_print() has already checked that mlen >= ForCES_HDRL
+ * by calling ForCES_HLN_VALID().
+ */
+ rlen = mlen - ForCES_HDRL;
+
+ if (rlen > TLV_HLN) {
+ if (tops->flags & ZERO_TTLV) {
+ ND_PRINT("<0x%x>Illegal Top level TLV!\n", tops->flags);
+ return -1;
+ }
+ } else {
+ if (tops->flags & ZERO_MORE_TTLV)
+ return 0;
+ if (tops->flags & ONE_MORE_TTLV) {
+ ND_PRINT("\tTop level TLV Data missing!\n");
+ return -1;
+ }
+ }
+
+ if (tops->flags & ZERO_TTLV) {
+ return 0;
+ }
+
+ ttlv = tops->flags >> 4;
+ tltlv = GET_TOP_TLV(pptr);
+
+ /*XXX: 15 top level tlvs will probably be fine
+ You are nuts if you send more ;-> */
+ while (rlen != 0) {
+ uint16_t type, tlvl;
+
+ type = GET_BE_U_2(tltlv->type);
+ tlvl = GET_BE_U_2(tltlv->length);
+ invtlv = tlv_valid(tlvl, rlen);
+ if (invtlv)
+ break;
+
+ /*
+ * At this point, tlv_valid() has ensured that the TLV
+ * length is large enough but not too large (it doesn't
+ * go past the end of the packet).
+ */
+ if (!ttlv_valid(type)) {
+ ND_PRINT("\n\tInvalid ForCES Top TLV type=0x%x",
+ type);
+ return -1;
+ }
+
+ if (ndo->ndo_vflag >= 3)
+ ND_PRINT("\t%s, length %u (data length %u Bytes)",
+ tok2str(ForCES_TLV, NULL, type),
+ tlvl,
+ tlvl - TLV_HDRL);
+
+ rc = tops->print(ndo, (const u_char *) TLV_DATA(tltlv),
+ tlvl,
+ tops->op_msk, 9);
+ if (rc < 0) {
+ return -1;
+ }
+ tltlv = GO_NXT_TLV(tltlv, rlen);
+ ttlv--;
+ if (ttlv <= 0)
+ break;
+ }
+ /*
+ * XXX - if ttlv != 0, does that mean that the packet was too
+ * short, and didn't have *enough* TLVs in it?
+ */
+ if (rlen) {
+ ND_PRINT("\tMess TopTLV header: min %u, total %u advertised %u ",
+ TLV_HDRL, rlen, GET_BE_U_2(tltlv->length));
+ return -1;
+ }
+
+ return 0;
+}
+
+void
+forces_print(netdissect_options *ndo,
+ const u_char * pptr, u_int len)
+{
+ const struct forcesh *fhdr;
+ u_int mlen;
+ uint32_t flg_raw;
+ uint8_t tom;
+ const struct tom_h *tops;
+ int rc = 0;
+
+ ndo->ndo_protocol = "forces";
+ fhdr = (const struct forcesh *)pptr;
+ ND_TCHECK_SIZE(fhdr);
+ tom = GET_U_1(fhdr->fm_tom);
+ if (!tom_valid(tom)) {
+ ND_PRINT("Invalid ForCES message type %u\n", tom);
+ goto error;
+ }
+
+ mlen = ForCES_BLN(fhdr);
+
+ tops = get_forces_tom(tom);
+ if (tops->v == TOM_RSVD) {
+ ND_PRINT("\n\tUnknown ForCES message type=0x%x", tom);
+ goto error;
+ }
+
+ ND_PRINT("\n\tForCES %s ", tops->s);
+ if (!ForCES_HLN_VALID(mlen, len)) {
+ ND_PRINT("Illegal ForCES pkt len - min %u, total recvd %u, advertised %u ",
+ ForCES_HDRL, len, ForCES_BLN(fhdr));
+ goto error;
+ }
+
+ flg_raw = GET_BE_U_4(pptr + 20);
+ if (ndo->ndo_vflag >= 1) {
+ ND_PRINT("\n\tForCES Version %u len %uB flags 0x%08x ",
+ ForCES_V(fhdr), mlen, flg_raw);
+ ND_PRINT("\n\tSrcID 0x%x(%s) DstID 0x%x(%s) Correlator 0x%" PRIx64,
+ ForCES_SID(fhdr), ForCES_node(ForCES_SID(fhdr)),
+ ForCES_DID(fhdr), ForCES_node(ForCES_DID(fhdr)),
+ GET_BE_U_8(fhdr->fm_cor));
+
+ }
+ if (ndo->ndo_vflag >= 2) {
+ ND_PRINT("\n\tForCES flags:\n\t %s(0x%x), prio=%u, %s(0x%x),\n\t %s(0x%x), %s(0x%x)\n",
+ tok2str(ForCES_ACKs, "ACKUnknown", ForCES_ACK(fhdr)),
+ ForCES_ACK(fhdr),
+ ForCES_PRI(fhdr),
+ tok2str(ForCES_EMs, "EMUnknown", ForCES_EM(fhdr)),
+ ForCES_EM(fhdr),
+ tok2str(ForCES_ATs, "ATUnknown", ForCES_AT(fhdr)),
+ ForCES_AT(fhdr),
+ tok2str(ForCES_TPs, "TPUnknown", ForCES_TP(fhdr)),
+ ForCES_TP(fhdr));
+ ND_PRINT("\t Extra flags: rsv(b5-7) 0x%x rsv(b13-31) 0x%x\n",
+ ForCES_RS1(fhdr), ForCES_RS2(fhdr));
+ }
+ rc = forces_type_print(ndo, pptr, fhdr, mlen, tops);
+ if (rc < 0) {
+error:
+ hex_print(ndo, "\n\t[", pptr, len);
+ ND_PRINT("\n\t]");
+ return;
+ }
+
+ if (ndo->ndo_vflag >= 4) {
+ ND_PRINT("\n\t Raw ForCES message\n\t [");
+ hex_print(ndo, "\n\t ", pptr, len);
+ ND_PRINT("\n\t ]");
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-fr.c b/print-fr.c
new file mode 100644
index 0000000..98f436f
--- /dev/null
+++ b/print-fr.c
@@ -0,0 +1,1149 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Frame Relay printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+#include "llc.h"
+#include "nlpid.h"
+#include "extract.h"
+
+static void frf15_print(netdissect_options *ndo, const u_char *, u_int);
+
+/*
+ * the frame relay header has a variable length
+ *
+ * the EA bit determines if there is another byte
+ * in the header
+ *
+ * minimum header length is 2 bytes
+ * maximum header length is 4 bytes
+ *
+ * 7 6 5 4 3 2 1 0
+ * +----+----+----+----+----+----+----+----+
+ * | DLCI (6 bits) | CR | EA |
+ * +----+----+----+----+----+----+----+----+
+ * | DLCI (4 bits) |FECN|BECN| DE | EA |
+ * +----+----+----+----+----+----+----+----+
+ * | DLCI (7 bits) | EA |
+ * +----+----+----+----+----+----+----+----+
+ * | DLCI (6 bits) |SDLC| EA |
+ * +----+----+----+----+----+----+----+----+
+ */
+
+#define FR_EA_BIT 0x01
+
+#define FR_CR_BIT 0x02000000
+#define FR_DE_BIT 0x00020000
+#define FR_BECN_BIT 0x00040000
+#define FR_FECN_BIT 0x00080000
+#define FR_SDLC_BIT 0x00000002
+
+
+static const struct tok fr_header_flag_values[] = {
+ { FR_CR_BIT, "C!" },
+ { FR_DE_BIT, "DE" },
+ { FR_BECN_BIT, "BECN" },
+ { FR_FECN_BIT, "FECN" },
+ { FR_SDLC_BIT, "sdlcore" },
+ { 0, NULL }
+};
+
+/* FRF.15 / FRF.16 */
+#define MFR_B_BIT 0x80
+#define MFR_E_BIT 0x40
+#define MFR_C_BIT 0x20
+#define MFR_BEC_MASK (MFR_B_BIT | MFR_E_BIT | MFR_C_BIT)
+#define MFR_CTRL_FRAME (MFR_B_BIT | MFR_E_BIT | MFR_C_BIT)
+#define MFR_FRAG_FRAME (MFR_B_BIT | MFR_E_BIT )
+
+static const struct tok frf_flag_values[] = {
+ { MFR_B_BIT, "Begin" },
+ { MFR_E_BIT, "End" },
+ { MFR_C_BIT, "Control" },
+ { 0, NULL }
+};
+
+/* Finds out Q.922 address length, DLCI and flags. Returns 1 on success,
+ * 0 on invalid address, -1 on truncated packet
+ * save the flags dep. on address length
+ */
+static int parse_q922_header(netdissect_options *ndo,
+ const u_char *p, u_int *dlci,
+ u_int *addr_len, uint32_t *flags, u_int length)
+{
+ if (!ND_TTEST_1(p) || length < 1)
+ return -1;
+ if ((GET_U_1(p) & FR_EA_BIT))
+ return 0;
+
+ if (!ND_TTEST_1(p + 1) || length < 2)
+ return -1;
+ *addr_len = 2;
+ *dlci = ((GET_U_1(p) & 0xFC) << 2) | ((GET_U_1(p + 1) & 0xF0) >> 4);
+
+ *flags = ((GET_U_1(p) & 0x02) << 24) | /* CR flag */
+ ((GET_U_1(p + 1) & 0x0e) << 16); /* FECN,BECN,DE flags */
+
+ if (GET_U_1(p + 1) & FR_EA_BIT)
+ return 1; /* 2-byte Q.922 address */
+
+ p += 2;
+ length -= 2;
+ if (!ND_TTEST_1(p) || length < 1)
+ return -1;
+ (*addr_len)++; /* 3- or 4-byte Q.922 address */
+ if ((GET_U_1(p) & FR_EA_BIT) == 0) {
+ *dlci = (*dlci << 7) | (GET_U_1(p) >> 1);
+ (*addr_len)++; /* 4-byte Q.922 address */
+ p++;
+ length--;
+ }
+
+ if (!ND_TTEST_1(p) || length < 1)
+ return -1;
+ if ((GET_U_1(p) & FR_EA_BIT) == 0)
+ return 0; /* more than 4 bytes of Q.922 address? */
+
+ *flags = *flags | (GET_U_1(p) & 0x02); /* SDLC flag */
+
+ *dlci = (*dlci << 6) | (GET_U_1(p) >> 2);
+
+ return 1;
+}
+
+const char *
+q922_string(netdissect_options *ndo, const u_char *p, u_int length)
+{
+
+ static u_int dlci, addr_len;
+ static uint32_t flags;
+ static char buffer[sizeof("DLCI xxxxxxxxxx")];
+ memset(buffer, 0, sizeof(buffer));
+
+ if (parse_q922_header(ndo, p, &dlci, &addr_len, &flags, length) == 1){
+ snprintf(buffer, sizeof(buffer), "DLCI %u", dlci);
+ }
+
+ return buffer;
+}
+
+
+/* Frame Relay packet structure, with flags and CRC removed
+
+ +---------------------------+
+ | Q.922 Address* |
+ +-- --+
+ | |
+ +---------------------------+
+ | Control (UI = 0x03) |
+ +---------------------------+
+ | Optional Pad (0x00) |
+ +---------------------------+
+ | NLPID |
+ +---------------------------+
+ | . |
+ | . |
+ | . |
+ | Data |
+ | . |
+ | . |
+ +---------------------------+
+
+ * Q.922 addresses, as presently defined, are two octets and
+ contain a 10-bit DLCI. In some networks Q.922 addresses
+ may optionally be increased to three or four octets.
+*/
+
+static void
+fr_hdr_print(netdissect_options *ndo, int length, u_int addr_len,
+ u_int dlci, uint32_t flags, uint16_t nlpid)
+{
+ if (ndo->ndo_qflag) {
+ ND_PRINT("Q.922, DLCI %u, length %u: ",
+ dlci,
+ length);
+ } else {
+ if (nlpid <= 0xff) /* if its smaller than 256 then its a NLPID */
+ ND_PRINT("Q.922, hdr-len %u, DLCI %u, Flags [%s], NLPID %s (0x%02x), length %u: ",
+ addr_len,
+ dlci,
+ bittok2str(fr_header_flag_values, "none", flags),
+ tok2str(nlpid_values,"unknown", nlpid),
+ nlpid,
+ length);
+ else /* must be an ethertype */
+ ND_PRINT("Q.922, hdr-len %u, DLCI %u, Flags [%s], cisco-ethertype %s (0x%04x), length %u: ",
+ addr_len,
+ dlci,
+ bittok2str(fr_header_flag_values, "none", flags),
+ tok2str(ethertype_values, "unknown", nlpid),
+ nlpid,
+ length);
+ }
+}
+
+/* Frame Relay */
+void
+fr_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+
+ ndo->ndo_protocol = "fr";
+ if (caplen < 4) { /* minimum frame header length */
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ ndo->ndo_ll_hdr_len += fr_print(ndo, p, length);
+}
+
+u_int
+fr_print(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ int ret;
+ uint16_t extracted_ethertype;
+ u_int dlci;
+ u_int addr_len;
+ uint16_t nlpid;
+ u_int hdr_len;
+ uint32_t flags;
+
+ ndo->ndo_protocol = "fr";
+ ret = parse_q922_header(ndo, p, &dlci, &addr_len, &flags, length);
+ if (ret == -1)
+ goto trunc;
+ if (ret == 0) {
+ ND_PRINT("Q.922, invalid address");
+ return 0;
+ }
+
+ ND_TCHECK_1(p + addr_len);
+ if (length < addr_len + 1)
+ goto trunc;
+
+ if (GET_U_1(p + addr_len) != LLC_UI && dlci != 0) {
+ /*
+ * Let's figure out if we have Cisco-style encapsulation,
+ * with an Ethernet type (Cisco HDLC type?) following the
+ * address.
+ */
+ if (!ND_TTEST_2(p + addr_len) || length < addr_len + 2) {
+ /* no Ethertype */
+ ND_PRINT("UI %02x! ", GET_U_1(p + addr_len));
+ } else {
+ extracted_ethertype = GET_BE_U_2(p + addr_len);
+
+ if (ndo->ndo_eflag)
+ fr_hdr_print(ndo, length, addr_len, dlci,
+ flags, extracted_ethertype);
+
+ if (ethertype_print(ndo, extracted_ethertype,
+ p+addr_len+ETHERTYPE_LEN,
+ length-addr_len-ETHERTYPE_LEN,
+ ND_BYTES_AVAILABLE_AFTER(p)-addr_len-ETHERTYPE_LEN,
+ NULL, NULL) == 0)
+ /* ether_type not known, probably it wasn't one */
+ ND_PRINT("UI %02x! ", GET_U_1(p + addr_len));
+ else
+ return addr_len + 2;
+ }
+ }
+
+ ND_TCHECK_1(p + addr_len + 1);
+ if (length < addr_len + 2)
+ goto trunc;
+
+ if (GET_U_1(p + addr_len + 1) == 0) {
+ /*
+ * Assume a pad byte after the control (UI) byte.
+ * A pad byte should only be used with 3-byte Q.922.
+ */
+ if (addr_len != 3)
+ ND_PRINT("Pad! ");
+ hdr_len = addr_len + 1 /* UI */ + 1 /* pad */ + 1 /* NLPID */;
+ } else {
+ /*
+ * Not a pad byte.
+ * A pad byte should be used with 3-byte Q.922.
+ */
+ if (addr_len == 3)
+ ND_PRINT("No pad! ");
+ hdr_len = addr_len + 1 /* UI */ + 1 /* NLPID */;
+ }
+
+ ND_TCHECK_1(p + hdr_len - 1);
+ if (length < hdr_len)
+ goto trunc;
+ nlpid = GET_U_1(p + hdr_len - 1);
+
+ if (ndo->ndo_eflag)
+ fr_hdr_print(ndo, length, addr_len, dlci, flags, nlpid);
+ p += hdr_len;
+ length -= hdr_len;
+
+ switch (nlpid) {
+ case NLPID_IP:
+ ip_print(ndo, p, length);
+ break;
+
+ case NLPID_IP6:
+ ip6_print(ndo, p, length);
+ break;
+
+ case NLPID_CLNP:
+ case NLPID_ESIS:
+ case NLPID_ISIS:
+ isoclns_print(ndo, p - 1, length + 1); /* OSI printers need the NLPID field */
+ break;
+
+ case NLPID_SNAP:
+ if (snap_print(ndo, p, length, ND_BYTES_AVAILABLE_AFTER(p), NULL, NULL, 0) == 0) {
+ /* ether_type not known, print raw packet */
+ if (!ndo->ndo_eflag)
+ fr_hdr_print(ndo, length + hdr_len, hdr_len,
+ dlci, flags, nlpid);
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p - hdr_len, length + hdr_len);
+ }
+ break;
+
+ case NLPID_Q933:
+ q933_print(ndo, p, length);
+ break;
+
+ case NLPID_MFR:
+ frf15_print(ndo, p, length);
+ break;
+
+ case NLPID_PPP:
+ ppp_print(ndo, p, length);
+ break;
+
+ default:
+ if (!ndo->ndo_eflag)
+ fr_hdr_print(ndo, length + hdr_len, addr_len,
+ dlci, flags, nlpid);
+ if (!ndo->ndo_xflag)
+ ND_DEFAULTPRINT(p, length);
+ }
+
+ return hdr_len;
+
+trunc:
+ nd_print_trunc(ndo);
+ return 0;
+
+}
+
+/* Multi Link Frame Relay (FRF.16) */
+void
+mfr_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+
+ ndo->ndo_protocol = "mfr";
+ if (caplen < 2) { /* minimum frame header length */
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ ndo->ndo_ll_hdr_len += mfr_print(ndo, p, length);
+}
+
+
+#define MFR_CTRL_MSG_ADD_LINK 1
+#define MFR_CTRL_MSG_ADD_LINK_ACK 2
+#define MFR_CTRL_MSG_ADD_LINK_REJ 3
+#define MFR_CTRL_MSG_HELLO 4
+#define MFR_CTRL_MSG_HELLO_ACK 5
+#define MFR_CTRL_MSG_REMOVE_LINK 6
+#define MFR_CTRL_MSG_REMOVE_LINK_ACK 7
+
+static const struct tok mfr_ctrl_msg_values[] = {
+ { MFR_CTRL_MSG_ADD_LINK, "Add Link" },
+ { MFR_CTRL_MSG_ADD_LINK_ACK, "Add Link ACK" },
+ { MFR_CTRL_MSG_ADD_LINK_REJ, "Add Link Reject" },
+ { MFR_CTRL_MSG_HELLO, "Hello" },
+ { MFR_CTRL_MSG_HELLO_ACK, "Hello ACK" },
+ { MFR_CTRL_MSG_REMOVE_LINK, "Remove Link" },
+ { MFR_CTRL_MSG_REMOVE_LINK_ACK, "Remove Link ACK" },
+ { 0, NULL }
+};
+
+#define MFR_CTRL_IE_BUNDLE_ID 1
+#define MFR_CTRL_IE_LINK_ID 2
+#define MFR_CTRL_IE_MAGIC_NUM 3
+#define MFR_CTRL_IE_TIMESTAMP 5
+#define MFR_CTRL_IE_VENDOR_EXT 6
+#define MFR_CTRL_IE_CAUSE 7
+
+static const struct tok mfr_ctrl_ie_values[] = {
+ { MFR_CTRL_IE_BUNDLE_ID, "Bundle ID"},
+ { MFR_CTRL_IE_LINK_ID, "Link ID"},
+ { MFR_CTRL_IE_MAGIC_NUM, "Magic Number"},
+ { MFR_CTRL_IE_TIMESTAMP, "Timestamp"},
+ { MFR_CTRL_IE_VENDOR_EXT, "Vendor Extension"},
+ { MFR_CTRL_IE_CAUSE, "Cause"},
+ { 0, NULL }
+};
+
+#define MFR_ID_STRING_MAXLEN 50
+
+struct ie_tlv_header_t {
+ uint8_t ie_type;
+ uint8_t ie_len;
+};
+
+u_int
+mfr_print(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int tlen,idx,hdr_len = 0;
+ uint16_t sequence_num;
+ uint8_t ie_type,ie_len;
+ const uint8_t *tptr;
+
+
+/*
+ * FRF.16 Link Integrity Control Frame
+ *
+ * 7 6 5 4 3 2 1 0
+ * +----+----+----+----+----+----+----+----+
+ * | B | E | C=1| 0 0 0 0 | EA |
+ * +----+----+----+----+----+----+----+----+
+ * | 0 0 0 0 0 0 0 0 |
+ * +----+----+----+----+----+----+----+----+
+ * | message type |
+ * +----+----+----+----+----+----+----+----+
+ */
+
+ ndo->ndo_protocol = "mfr";
+
+ if (length < 4) { /* minimum frame header length */
+ ND_PRINT("[length %u < 4]", length);
+ nd_print_invalid(ndo);
+ return length;
+ }
+ ND_TCHECK_4(p);
+
+ if ((GET_U_1(p) & MFR_BEC_MASK) == MFR_CTRL_FRAME && GET_U_1(p + 1) == 0) {
+ ND_PRINT("FRF.16 Control, Flags [%s], %s, length %u",
+ bittok2str(frf_flag_values,"none",(GET_U_1(p) & MFR_BEC_MASK)),
+ tok2str(mfr_ctrl_msg_values,"Unknown Message (0x%02x)",GET_U_1(p + 2)),
+ length);
+ tptr = p + 3;
+ tlen = length -3;
+ hdr_len = 3;
+
+ if (!ndo->ndo_vflag)
+ return hdr_len;
+
+ while (tlen>sizeof(struct ie_tlv_header_t)) {
+ ND_TCHECK_LEN(tptr, sizeof(struct ie_tlv_header_t));
+ ie_type=GET_U_1(tptr);
+ ie_len=GET_U_1(tptr + 1);
+
+ ND_PRINT("\n\tIE %s (%u), length %u: ",
+ tok2str(mfr_ctrl_ie_values,"Unknown",ie_type),
+ ie_type,
+ ie_len);
+
+ /* infinite loop check */
+ if (ie_type == 0 || ie_len <= sizeof(struct ie_tlv_header_t))
+ return hdr_len;
+
+ ND_TCHECK_LEN(tptr, ie_len);
+ tptr+=sizeof(struct ie_tlv_header_t);
+ /* tlv len includes header */
+ ie_len-=sizeof(struct ie_tlv_header_t);
+ tlen-=sizeof(struct ie_tlv_header_t);
+
+ switch (ie_type) {
+
+ case MFR_CTRL_IE_MAGIC_NUM:
+ /* FRF.16.1 Section 3.4.3 Magic Number Information Element */
+ if (ie_len != 4) {
+ ND_PRINT("[IE data length %d != 4]", ie_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT("0x%08x", GET_BE_U_4(tptr));
+ break;
+
+ case MFR_CTRL_IE_BUNDLE_ID: /* same message format */
+ case MFR_CTRL_IE_LINK_ID:
+ for (idx = 0; idx < ie_len && idx < MFR_ID_STRING_MAXLEN; idx++) {
+ if (GET_U_1(tptr + idx) != 0) /* don't print null termination */
+ fn_print_char(ndo, GET_U_1(tptr + idx));
+ else
+ break;
+ }
+ break;
+
+ case MFR_CTRL_IE_TIMESTAMP:
+ if (ie_len == sizeof(struct timeval)) {
+ ts_print(ndo, (const struct timeval *)tptr);
+ break;
+ }
+ /* fall through and hexdump if no unix timestamp */
+ ND_FALL_THROUGH;
+
+ /*
+ * FIXME those are the defined IEs that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case MFR_CTRL_IE_VENDOR_EXT:
+ case MFR_CTRL_IE_CAUSE:
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr, "\n\t ", ie_len);
+ break;
+ }
+
+ /* do we want to see a hexdump of the IE ? */
+ if (ndo->ndo_vflag > 1 )
+ print_unknown_data(ndo, tptr, "\n\t ", ie_len);
+
+ tlen-=ie_len;
+ tptr+=ie_len;
+ }
+ return hdr_len;
+ }
+/*
+ * FRF.16 Fragmentation Frame
+ *
+ * 7 6 5 4 3 2 1 0
+ * +----+----+----+----+----+----+----+----+
+ * | B | E | C=0|seq. (high 4 bits) | EA |
+ * +----+----+----+----+----+----+----+----+
+ * | sequence (low 8 bits) |
+ * +----+----+----+----+----+----+----+----+
+ * | DLCI (6 bits) | CR | EA |
+ * +----+----+----+----+----+----+----+----+
+ * | DLCI (4 bits) |FECN|BECN| DE | EA |
+ * +----+----+----+----+----+----+----+----+
+ */
+
+ sequence_num = (GET_U_1(p)&0x1e)<<7 | GET_U_1(p + 1);
+ /* whole packet or first fragment ? */
+ if ((GET_U_1(p) & MFR_BEC_MASK) == MFR_FRAG_FRAME ||
+ (GET_U_1(p) & MFR_BEC_MASK) == MFR_B_BIT) {
+ ND_PRINT("FRF.16 Frag, seq %u, Flags [%s], ",
+ sequence_num,
+ bittok2str(frf_flag_values,"none",(GET_U_1(p) & MFR_BEC_MASK)));
+ hdr_len = 2;
+ fr_print(ndo, p+hdr_len,length-hdr_len);
+ return hdr_len;
+ }
+
+ /* must be a middle or the last fragment */
+ ND_PRINT("FRF.16 Frag, seq %u, Flags [%s]",
+ sequence_num,
+ bittok2str(frf_flag_values,"none",(GET_U_1(p) & MFR_BEC_MASK)));
+ print_unknown_data(ndo, p, "\n\t", length);
+
+ return hdr_len;
+
+trunc:
+ nd_print_trunc(ndo);
+ return length;
+}
+
+/* an NLPID of 0xb1 indicates a 2-byte
+ * FRF.15 header
+ *
+ * 7 6 5 4 3 2 1 0
+ * +----+----+----+----+----+----+----+----+
+ * ~ Q.922 header ~
+ * +----+----+----+----+----+----+----+----+
+ * | NLPID (8 bits) | NLPID=0xb1
+ * +----+----+----+----+----+----+----+----+
+ * | B | E | C |seq. (high 4 bits) | R |
+ * +----+----+----+----+----+----+----+----+
+ * | sequence (low 8 bits) |
+ * +----+----+----+----+----+----+----+----+
+ */
+
+#define FR_FRF15_FRAGTYPE 0x01
+
+static void
+frf15_print(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ uint16_t sequence_num, flags;
+
+ if (length < 2)
+ goto trunc;
+
+ flags = GET_U_1(p)&MFR_BEC_MASK;
+ sequence_num = (GET_U_1(p)&0x1e)<<7 | GET_U_1(p + 1);
+
+ ND_PRINT("FRF.15, seq 0x%03x, Flags [%s],%s Fragmentation, length %u",
+ sequence_num,
+ bittok2str(frf_flag_values,"none",flags),
+ GET_U_1(p)&FR_FRF15_FRAGTYPE ? "Interface" : "End-to-End",
+ length);
+
+/* TODO:
+ * depending on all permutations of the B, E and C bit
+ * dig as deep as we can - e.g. on the first (B) fragment
+ * there is enough payload to print the IP header
+ * on non (B) fragments it depends if the fragmentation
+ * model is end-to-end or interface based whether we want to print
+ * another Q.922 header
+ */
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+/*
+ * Q.933 decoding portion for framerelay specific.
+ */
+
+/* Q.933 packet format
+ Format of Other Protocols
+ using Q.933 NLPID
+ +-------------------------------+
+ | Q.922 Address |
+ +---------------+---------------+
+ |Control 0x03 | NLPID 0x08 |
+ +---------------+---------------+
+ | L2 Protocol ID |
+ | octet 1 | octet 2 |
+ +-------------------------------+
+ | L3 Protocol ID |
+ | octet 2 | octet 2 |
+ +-------------------------------+
+ | Protocol Data |
+ +-------------------------------+
+ | FCS |
+ +-------------------------------+
+ */
+
+/* L2 (Octet 1)- Call Reference Usually is 0x0 */
+
+/*
+ * L2 (Octet 2)- Message Types definition 1 byte long.
+ */
+/* Call Establish */
+#define MSG_TYPE_ESC_TO_NATIONAL 0x00
+#define MSG_TYPE_ALERT 0x01
+#define MSG_TYPE_CALL_PROCEEDING 0x02
+#define MSG_TYPE_CONNECT 0x07
+#define MSG_TYPE_CONNECT_ACK 0x0F
+#define MSG_TYPE_PROGRESS 0x03
+#define MSG_TYPE_SETUP 0x05
+/* Call Clear */
+#define MSG_TYPE_DISCONNECT 0x45
+#define MSG_TYPE_RELEASE 0x4D
+#define MSG_TYPE_RELEASE_COMPLETE 0x5A
+#define MSG_TYPE_RESTART 0x46
+#define MSG_TYPE_RESTART_ACK 0x4E
+/* Status */
+#define MSG_TYPE_STATUS 0x7D
+#define MSG_TYPE_STATUS_ENQ 0x75
+
+static const struct tok fr_q933_msg_values[] = {
+ { MSG_TYPE_ESC_TO_NATIONAL, "ESC to National" },
+ { MSG_TYPE_ALERT, "Alert" },
+ { MSG_TYPE_CALL_PROCEEDING, "Call proceeding" },
+ { MSG_TYPE_CONNECT, "Connect" },
+ { MSG_TYPE_CONNECT_ACK, "Connect ACK" },
+ { MSG_TYPE_PROGRESS, "Progress" },
+ { MSG_TYPE_SETUP, "Setup" },
+ { MSG_TYPE_DISCONNECT, "Disconnect" },
+ { MSG_TYPE_RELEASE, "Release" },
+ { MSG_TYPE_RELEASE_COMPLETE, "Release Complete" },
+ { MSG_TYPE_RESTART, "Restart" },
+ { MSG_TYPE_RESTART_ACK, "Restart ACK" },
+ { MSG_TYPE_STATUS, "Status Reply" },
+ { MSG_TYPE_STATUS_ENQ, "Status Enquiry" },
+ { 0, NULL }
+};
+
+#define IE_IS_SINGLE_OCTET(iecode) ((iecode) & 0x80)
+#define IE_IS_SHIFT(iecode) (((iecode) & 0xF0) == 0x90)
+#define IE_SHIFT_IS_NON_LOCKING(iecode) ((iecode) & 0x08)
+#define IE_SHIFT_IS_LOCKING(iecode) (!(IE_SHIFT_IS_NON_LOCKING(iecode)))
+#define IE_SHIFT_CODESET(iecode) ((iecode) & 0x07)
+
+#define FR_LMI_ANSI_REPORT_TYPE_IE 0x01
+#define FR_LMI_ANSI_LINK_VERIFY_IE_91 0x19 /* details? */
+#define FR_LMI_ANSI_LINK_VERIFY_IE 0x03
+#define FR_LMI_ANSI_PVC_STATUS_IE 0x07
+
+#define FR_LMI_CCITT_REPORT_TYPE_IE 0x51
+#define FR_LMI_CCITT_LINK_VERIFY_IE 0x53
+#define FR_LMI_CCITT_PVC_STATUS_IE 0x57
+
+static const struct tok fr_q933_ie_values_codeset_0_5[] = {
+ { FR_LMI_ANSI_REPORT_TYPE_IE, "ANSI Report Type" },
+ { FR_LMI_ANSI_LINK_VERIFY_IE_91, "ANSI Link Verify" },
+ { FR_LMI_ANSI_LINK_VERIFY_IE, "ANSI Link Verify" },
+ { FR_LMI_ANSI_PVC_STATUS_IE, "ANSI PVC Status" },
+ { FR_LMI_CCITT_REPORT_TYPE_IE, "CCITT Report Type" },
+ { FR_LMI_CCITT_LINK_VERIFY_IE, "CCITT Link Verify" },
+ { FR_LMI_CCITT_PVC_STATUS_IE, "CCITT PVC Status" },
+ { 0, NULL }
+};
+
+#define FR_LMI_REPORT_TYPE_IE_FULL_STATUS 0
+#define FR_LMI_REPORT_TYPE_IE_LINK_VERIFY 1
+#define FR_LMI_REPORT_TYPE_IE_ASYNC_PVC 2
+
+static const struct tok fr_lmi_report_type_ie_values[] = {
+ { FR_LMI_REPORT_TYPE_IE_FULL_STATUS, "Full Status" },
+ { FR_LMI_REPORT_TYPE_IE_LINK_VERIFY, "Link verify" },
+ { FR_LMI_REPORT_TYPE_IE_ASYNC_PVC, "Async PVC Status" },
+ { 0, NULL }
+};
+
+/* array of 16 codesets - currently we only support codepage 0 and 5 */
+static const struct tok *fr_q933_ie_codesets[] = {
+ fr_q933_ie_values_codeset_0_5,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ fr_q933_ie_values_codeset_0_5,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+static int fr_q933_print_ie_codeset_0_5(netdissect_options *ndo, u_int iecode,
+ u_int ielength, const u_char *p);
+
+typedef int (*codeset_pr_func_t)(netdissect_options *, u_int iecode,
+ u_int ielength, const u_char *p);
+
+/* array of 16 codesets - currently we only support codepage 0 and 5 */
+static const codeset_pr_func_t fr_q933_print_ie_codeset[] = {
+ fr_q933_print_ie_codeset_0_5,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ fr_q933_print_ie_codeset_0_5,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ * ITU-T Q.933.
+ *
+ * p points to octet 2, the octet containing the length of the
+ * call reference value, so p[n] is octet n+2 ("octet X" is as
+ * used in Q.931/Q.933).
+ *
+ * XXX - actually used both for Q.931 and Q.933.
+ */
+void
+q933_print(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int olen;
+ u_int call_ref_length, i;
+ uint8_t call_ref[15]; /* maximum length - length field is 4 bits */
+ u_int msgtype;
+ u_int iecode;
+ u_int ielength;
+ u_int codeset = 0;
+ u_int is_ansi = 0;
+ u_int ie_is_known;
+ u_int non_locking_shift;
+ u_int unshift_codeset;
+
+ ndo->ndo_protocol = "q.933";
+ ND_PRINT("%s", ndo->ndo_eflag ? "" : "Q.933");
+
+ if (length == 0 || !ND_TTEST_1(p)) {
+ if (!ndo->ndo_eflag)
+ ND_PRINT(", ");
+ ND_PRINT("length %u", length);
+ goto trunc;
+ }
+
+ /*
+ * Get the length of the call reference value.
+ */
+ olen = length; /* preserve the original length for display */
+ call_ref_length = GET_U_1(p) & 0x0f;
+ p++;
+ length--;
+
+ /*
+ * Get the call reference value.
+ */
+ for (i = 0; i < call_ref_length; i++) {
+ if (length == 0 || !ND_TTEST_1(p)) {
+ if (!ndo->ndo_eflag)
+ ND_PRINT(", ");
+ ND_PRINT("length %u", olen);
+ goto trunc;
+ }
+ call_ref[i] = GET_U_1(p);
+ p++;
+ length--;
+ }
+
+ /*
+ * Get the message type.
+ */
+ if (length == 0 || !ND_TTEST_1(p)) {
+ if (!ndo->ndo_eflag)
+ ND_PRINT(", ");
+ ND_PRINT("length %u", olen);
+ goto trunc;
+ }
+ msgtype = GET_U_1(p);
+ p++;
+ length--;
+
+ /*
+ * Peek ahead to see if we start with a shift.
+ */
+ non_locking_shift = 0;
+ unshift_codeset = codeset;
+ if (length != 0) {
+ if (!ND_TTEST_1(p)) {
+ if (!ndo->ndo_eflag)
+ ND_PRINT(", ");
+ ND_PRINT("length %u", olen);
+ goto trunc;
+ }
+ iecode = GET_U_1(p);
+ if (IE_IS_SHIFT(iecode)) {
+ /*
+ * It's a shift. Skip over it.
+ */
+ p++;
+ length--;
+
+ /*
+ * Get the codeset.
+ */
+ codeset = IE_SHIFT_CODESET(iecode);
+
+ /*
+ * If it's a locking shift to codeset 5,
+ * mark this as ANSI. (XXX - 5 is actually
+ * for national variants in general, not
+ * the US variant in particular, but maybe
+ * this is more American exceptionalism. :-))
+ */
+ if (IE_SHIFT_IS_LOCKING(iecode)) {
+ /*
+ * It's a locking shift.
+ */
+ if (codeset == 5) {
+ /*
+ * It's a locking shift to
+ * codeset 5, so this is
+ * T1.617 Annex D.
+ */
+ is_ansi = 1;
+ }
+ } else {
+ /*
+ * It's a non-locking shift.
+ * Remember the current codeset, so we
+ * can revert to it after the next IE.
+ */
+ non_locking_shift = 1;
+ unshift_codeset = 0;
+ }
+ }
+ }
+
+ /* printing out header part */
+ if (!ndo->ndo_eflag)
+ ND_PRINT(", ");
+ ND_PRINT("%s, codeset %u", is_ansi ? "ANSI" : "CCITT", codeset);
+
+ if (call_ref_length != 0) {
+ if (call_ref_length > 1 || GET_U_1(p) != 0) {
+ /*
+ * Not a dummy call reference.
+ */
+ ND_PRINT(", Call Ref: 0x");
+ for (i = 0; i < call_ref_length; i++)
+ ND_PRINT("%02x", call_ref[i]);
+ }
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT(", %s (0x%02x), length %u",
+ tok2str(fr_q933_msg_values,
+ "unknown message", msgtype),
+ msgtype,
+ olen);
+ } else {
+ ND_PRINT(", %s",
+ tok2str(fr_q933_msg_values,
+ "unknown message 0x%02x", msgtype));
+ }
+
+ /* Loop through the rest of the IEs */
+ while (length != 0) {
+ /*
+ * What's the state of any non-locking shifts?
+ */
+ if (non_locking_shift == 1) {
+ /*
+ * There's a non-locking shift in effect for
+ * this IE. Count it, so we reset the codeset
+ * before the next IE.
+ */
+ non_locking_shift = 2;
+ } else if (non_locking_shift == 2) {
+ /*
+ * Unshift.
+ */
+ codeset = unshift_codeset;
+ non_locking_shift = 0;
+ }
+
+ /*
+ * Get the first octet of the IE.
+ */
+ if (!ND_TTEST_1(p)) {
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", length %u", olen);
+ }
+ goto trunc;
+ }
+ iecode = GET_U_1(p);
+ p++;
+ length--;
+
+ /* Single-octet IE? */
+ if (IE_IS_SINGLE_OCTET(iecode)) {
+ /*
+ * Yes. Is it a shift?
+ */
+ if (IE_IS_SHIFT(iecode)) {
+ /*
+ * Yes. Is it locking?
+ */
+ if (IE_SHIFT_IS_LOCKING(iecode)) {
+ /*
+ * Yes.
+ */
+ non_locking_shift = 0;
+ } else {
+ /*
+ * No. Remember the current
+ * codeset, so we can revert
+ * to it after the next IE.
+ */
+ non_locking_shift = 1;
+ unshift_codeset = codeset;
+ }
+
+ /*
+ * Get the codeset.
+ */
+ codeset = IE_SHIFT_CODESET(iecode);
+ }
+ } else {
+ /*
+ * No. Get the IE length.
+ */
+ if (length == 0 || !ND_TTEST_1(p)) {
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", length %u", olen);
+ }
+ goto trunc;
+ }
+ ielength = GET_U_1(p);
+ p++;
+ length--;
+
+ /* lets do the full IE parsing only in verbose mode
+ * however some IEs (DLCI Status, Link Verify)
+ * are also interesting in non-verbose mode */
+ if (ndo->ndo_vflag) {
+ ND_PRINT("\n\t%s IE (0x%02x), length %u: ",
+ tok2str(fr_q933_ie_codesets[codeset],
+ "unknown", iecode),
+ iecode,
+ ielength);
+ }
+
+ /* sanity checks */
+ if (iecode == 0 || ielength == 0) {
+ return;
+ }
+ if (length < ielength || !ND_TTEST_LEN(p, ielength)) {
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", length %u", olen);
+ }
+ goto trunc;
+ }
+
+ ie_is_known = 0;
+ if (fr_q933_print_ie_codeset[codeset] != NULL) {
+ ie_is_known = fr_q933_print_ie_codeset[codeset](ndo, iecode, ielength, p);
+ }
+
+ if (ie_is_known) {
+ /*
+ * Known IE; do we want to see a hexdump
+ * of it?
+ */
+ if (ndo->ndo_vflag > 1) {
+ /* Yes. */
+ print_unknown_data(ndo, p, "\n\t ", ielength);
+ }
+ } else {
+ /*
+ * Unknown IE; if we're printing verbosely,
+ * print its content in hex.
+ */
+ if (ndo->ndo_vflag >= 1) {
+ print_unknown_data(ndo, p, "\n\t", ielength);
+ }
+ }
+
+ length -= ielength;
+ p += ielength;
+ }
+ }
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", length %u", olen);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static int
+fr_q933_print_ie_codeset_0_5(netdissect_options *ndo, u_int iecode,
+ u_int ielength, const u_char *p)
+{
+ u_int dlci;
+
+ switch (iecode) {
+
+ case FR_LMI_ANSI_REPORT_TYPE_IE: /* fall through */
+ case FR_LMI_CCITT_REPORT_TYPE_IE:
+ if (ielength < 1) {
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", ");
+ }
+ ND_PRINT("Invalid REPORT TYPE IE");
+ return 1;
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT("%s (%u)",
+ tok2str(fr_lmi_report_type_ie_values,"unknown",GET_U_1(p)),
+ GET_U_1(p));
+ }
+ return 1;
+
+ case FR_LMI_ANSI_LINK_VERIFY_IE: /* fall through */
+ case FR_LMI_CCITT_LINK_VERIFY_IE:
+ case FR_LMI_ANSI_LINK_VERIFY_IE_91:
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", ");
+ }
+ if (ielength < 2) {
+ ND_PRINT("Invalid LINK VERIFY IE");
+ return 1;
+ }
+ ND_PRINT("TX Seq: %3d, RX Seq: %3d", GET_U_1(p), GET_U_1(p + 1));
+ return 1;
+
+ case FR_LMI_ANSI_PVC_STATUS_IE: /* fall through */
+ case FR_LMI_CCITT_PVC_STATUS_IE:
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", ");
+ }
+ /* now parse the DLCI information element. */
+ if ((ielength < 3) ||
+ (GET_U_1(p) & 0x80) ||
+ ((ielength == 3) && !(GET_U_1(p + 1) & 0x80)) ||
+ ((ielength == 4) &&
+ ((GET_U_1(p + 1) & 0x80) || !(GET_U_1(p + 2) & 0x80))) ||
+ ((ielength == 5) &&
+ ((GET_U_1(p + 1) & 0x80) || (GET_U_1(p + 2) & 0x80) ||
+ !(GET_U_1(p + 3) & 0x80))) ||
+ (ielength > 5) ||
+ !(GET_U_1(p + ielength - 1) & 0x80)) {
+ ND_PRINT("Invalid DLCI in PVC STATUS IE");
+ return 1;
+ }
+
+ dlci = ((GET_U_1(p) & 0x3F) << 4) | ((GET_U_1(p + 1) & 0x78) >> 3);
+ if (ielength == 4) {
+ dlci = (dlci << 6) | ((GET_U_1(p + 2) & 0x7E) >> 1);
+ }
+ else if (ielength == 5) {
+ dlci = (dlci << 13) | (GET_U_1(p + 2) & 0x7F) | ((GET_U_1(p + 3) & 0x7E) >> 1);
+ }
+
+ ND_PRINT("DLCI %u: status %s%s", dlci,
+ GET_U_1(p + ielength - 1) & 0x8 ? "New, " : "",
+ GET_U_1(p + ielength - 1) & 0x2 ? "Active" : "Inactive");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/print-frag6.c b/print-frag6.c
new file mode 100644
index 0000000..16e8a4b
--- /dev/null
+++ b/print-frag6.c
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IPv6 fragmentation header printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+#include "ip6.h"
+
+int
+frag6_print(netdissect_options *ndo, const u_char *bp, const u_char *bp2)
+{
+ const struct ip6_frag *dp;
+ const struct ip6_hdr *ip6;
+
+ ndo->ndo_protocol = "frag6";
+ dp = (const struct ip6_frag *)bp;
+ ip6 = (const struct ip6_hdr *)bp2;
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("frag (0x%08x:%u|%zu)",
+ GET_BE_U_4(dp->ip6f_ident),
+ GET_BE_U_2(dp->ip6f_offlg) & IP6F_OFF_MASK,
+ sizeof(struct ip6_hdr) + GET_BE_U_2(ip6->ip6_plen) -
+ (bp - bp2) - sizeof(struct ip6_frag));
+ } else {
+ ND_PRINT("frag (%u|%zu)",
+ GET_BE_U_2(dp->ip6f_offlg) & IP6F_OFF_MASK,
+ sizeof(struct ip6_hdr) + GET_BE_U_2(ip6->ip6_plen) -
+ (bp - bp2) - sizeof(struct ip6_frag));
+ }
+
+ /* it is meaningless to decode non-first fragment */
+ if ((GET_BE_U_2(dp->ip6f_offlg) & IP6F_OFF_MASK) != 0)
+ return -1;
+ else
+ {
+ ND_PRINT(" ");
+ return sizeof(struct ip6_frag);
+ }
+}
diff --git a/print-ftp.c b/print-ftp.c
new file mode 100644
index 0000000..b55937b
--- /dev/null
+++ b/print-ftp.c
@@ -0,0 +1,29 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: File Transfer Protocol (FTP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+void
+ftp_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ ndo->ndo_protocol = "ftp";
+ txtproto_print(ndo, pptr, len, NULL, 0);
+}
diff --git a/print-geneve.c b/print-geneve.c
new file mode 100644
index 0000000..0b7ff6e
--- /dev/null
+++ b/print-geneve.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2014 VMware, Inc. All Rights Reserved.
+ *
+ * Jesse Gross <jesse@nicira.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Generic Network Virtualization Encapsulation (Geneve) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "ethertype.h"
+
+/*
+ * Geneve header, draft-ietf-nvo3-geneve
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Ver| Opt Len |O|C| Rsvd. | Protocol Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Virtual Network Identifier (VNI) | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Variable Length Options |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Options:
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Option Class | Type |R|R|R| Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Variable Option Data |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+#define VER_SHIFT 6
+#define HDR_OPTS_LEN_MASK 0x3F
+
+#define FLAG_OAM (1 << 7)
+#define FLAG_CRITICAL (1 << 6)
+#define FLAG_R1 (1 << 5)
+#define FLAG_R2 (1 << 4)
+#define FLAG_R3 (1 << 3)
+#define FLAG_R4 (1 << 2)
+#define FLAG_R5 (1 << 1)
+#define FLAG_R6 (1 << 0)
+
+#define OPT_TYPE_CRITICAL (1 << 7)
+#define OPT_LEN_MASK 0x1F
+
+static const struct tok geneve_flag_values[] = {
+ { FLAG_OAM, "O" },
+ { FLAG_CRITICAL, "C" },
+ { FLAG_R1, "R1" },
+ { FLAG_R2, "R2" },
+ { FLAG_R3, "R3" },
+ { FLAG_R4, "R4" },
+ { FLAG_R5, "R5" },
+ { FLAG_R6, "R6" },
+ { 0, NULL }
+};
+
+static const char *
+format_opt_class(uint16_t opt_class)
+{
+ switch (opt_class) {
+ case 0x0100:
+ return "Linux";
+ case 0x0101:
+ return "Open vSwitch";
+ case 0x0102:
+ return "Open Virtual Networking (OVN)";
+ case 0x0103:
+ return "In-band Network Telemetry (INT)";
+ case 0x0104:
+ return "VMware";
+ default:
+ if (opt_class <= 0x00ff)
+ return "Standard";
+ else if (opt_class >= 0xfff0)
+ return "Experimental";
+ }
+
+ return "Unknown";
+}
+
+static void
+geneve_opts_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ const char *sep = "";
+
+ while (len > 0) {
+ uint16_t opt_class;
+ uint8_t opt_type;
+ uint8_t opt_len;
+
+ ND_PRINT("%s", sep);
+ sep = ", ";
+
+ opt_class = GET_BE_U_2(bp);
+ opt_type = GET_U_1(bp + 2);
+ opt_len = 4 + ((GET_U_1(bp + 3) & OPT_LEN_MASK) * 4);
+
+ ND_PRINT("class %s (0x%x) type 0x%x%s len %u",
+ format_opt_class(opt_class), opt_class, opt_type,
+ opt_type & OPT_TYPE_CRITICAL ? "(C)" : "", opt_len);
+
+ if (opt_len > len) {
+ ND_PRINT(" [bad length]");
+ return;
+ }
+
+ if (ndo->ndo_vflag > 1 && opt_len > 4) {
+ const uint32_t *data = (const uint32_t *)(bp + 4);
+ int i;
+
+ ND_PRINT(" data");
+
+ for (i = 4; i < opt_len; i += 4) {
+ ND_PRINT(" %08x", GET_BE_U_4(data));
+ data++;
+ }
+ }
+
+ bp += opt_len;
+ len -= opt_len;
+ }
+}
+
+void
+geneve_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ uint8_t ver_opt;
+ u_int version;
+ uint8_t flags;
+ uint16_t prot;
+ uint32_t vni;
+ uint8_t reserved;
+ u_int opts_len;
+
+ ndo->ndo_protocol = "geneve";
+ ND_PRINT("Geneve");
+
+ if (len < 8) {
+ ND_PRINT(" [length %u < 8]", len);
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ ND_TCHECK_8(bp);
+
+ ver_opt = GET_U_1(bp);
+ bp += 1;
+ len -= 1;
+
+ version = ver_opt >> VER_SHIFT;
+ if (version != 0) {
+ ND_PRINT(" ERROR: unknown-version %u", version);
+ return;
+ }
+
+ flags = GET_U_1(bp);
+ bp += 1;
+ len -= 1;
+
+ prot = GET_BE_U_2(bp);
+ bp += 2;
+ len -= 2;
+
+ vni = GET_BE_U_3(bp);
+ bp += 3;
+ len -= 3;
+
+ reserved = GET_U_1(bp);
+ bp += 1;
+ len -= 1;
+
+ ND_PRINT(", Flags [%s]",
+ bittok2str_nosep(geneve_flag_values, "none", flags));
+ ND_PRINT(", vni 0x%x", vni);
+
+ if (reserved)
+ ND_PRINT(", rsvd 0x%x", reserved);
+
+ if (ndo->ndo_eflag)
+ ND_PRINT(", proto %s (0x%04x)",
+ tok2str(ethertype_values, "unknown", prot), prot);
+
+ opts_len = (ver_opt & HDR_OPTS_LEN_MASK) * 4;
+
+ if (len < opts_len) {
+ ND_PRINT(" truncated-geneve - %u bytes missing",
+ opts_len - len);
+ return;
+ }
+
+ ND_TCHECK_LEN(bp, opts_len);
+
+ if (opts_len > 0) {
+ ND_PRINT(", options [");
+
+ if (ndo->ndo_vflag)
+ geneve_opts_print(ndo, bp, opts_len);
+ else
+ ND_PRINT("%u bytes", opts_len);
+
+ ND_PRINT("]");
+ }
+
+ bp += opts_len;
+ len -= opts_len;
+
+ if (ndo->ndo_vflag < 1)
+ ND_PRINT(": ");
+ else
+ ND_PRINT("\n\t");
+
+ if (ethertype_print(ndo, prot, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL) == 0) {
+ if (prot == ETHERTYPE_TEB)
+ ether_print(ndo, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+ else
+ ND_PRINT("geneve-proto-0x%x", prot);
+ }
+
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-geonet.c b/print-geonet.c
new file mode 100644
index 0000000..dfb19db
--- /dev/null
+++ b/print-geonet.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 2013 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Ola Martin Lykkja (ola.lykkja@q-free.com)
+ */
+
+/* \summary: ISO CALM FAST and ETSI GeoNetworking printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+/*
+ ETSI TS 102 636-5-1 V1.1.1 (2011-02)
+ Intelligent Transport Systems (ITS); Vehicular Communications; GeoNetworking;
+ Part 5: Transport Protocols; Sub-part 1: Basic Transport Protocol
+
+ ETSI TS 102 636-4-1 V1.1.1 (2011-06)
+ Intelligent Transport Systems (ITS); Vehicular communications; GeoNetworking;
+ Part 4: Geographical addressing and forwarding for point-to-point and point-to-multipoint communications;
+ Sub-part 1: Media-Independent Functionality
+*/
+
+#define GEONET_ADDR_LEN 8
+
+static const struct tok msg_type_values[] = {
+ { 0, "CAM" },
+ { 1, "DENM" },
+ { 101, "TPEGM" },
+ { 102, "TSPDM" },
+ { 103, "VPM" },
+ { 104, "SRM" },
+ { 105, "SLAM" },
+ { 106, "ecoCAM" },
+ { 107, "ITM" },
+ { 150, "SA" },
+ { 0, NULL }
+};
+
+static void
+print_btp_body(netdissect_options *ndo,
+ const u_char *bp)
+{
+ u_int msg_type;
+
+ /* Assuming ItsPduHeader */
+ ND_PRINT("; ItsPduHeader v:%u", GET_U_1(bp));
+
+ msg_type = GET_U_1(bp + 1);
+ ND_PRINT(" t:%u-%s", msg_type,
+ tok2str(msg_type_values, "unknown (%u)", msg_type));
+}
+
+/* EN 302 636-5-1 V2.2.1 Section 7.2: BTP-A header */
+static void
+print_btp(netdissect_options *ndo,
+ const u_char *bp)
+{
+ ND_PRINT("; BTP Dst:%u", GET_BE_U_2(bp + 0));
+ ND_PRINT(" Src:%u", GET_BE_U_2(bp + 2));
+}
+
+static void
+print_long_pos_vector(netdissect_options *ndo,
+ const u_char *bp)
+{
+ ND_PRINT("GN_ADDR:%s ", GET_LINKADDR_STRING(bp, LINKADDR_OTHER, GEONET_ADDR_LEN));
+ ND_PRINT("lat:%u ", GET_BE_U_4(bp + 12));
+ ND_PRINT("lon:%u", GET_BE_U_4(bp + 16));
+}
+
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the geonet header of the packet.
+ */
+void
+geonet_print(netdissect_options *ndo, const u_char *bp, u_int length,
+ const struct lladdr_info *src)
+{
+ u_int version;
+ u_int next_hdr;
+ u_int hdr_type;
+ u_int hdr_subtype;
+ uint16_t payload_length;
+ u_int hop_limit;
+ const char *next_hdr_txt = "Unknown";
+ const char *hdr_type_txt = "Unknown";
+ int hdr_size = -1;
+
+ ndo->ndo_protocol = "geonet";
+ ND_PRINT("GeoNet ");
+ if (src != NULL)
+ ND_PRINT("src:%s", (src->addr_string)(ndo, src->addr));
+ ND_PRINT("; ");
+
+ /* Process Common Header */
+ if (length < 36) {
+ ND_PRINT(" (common header length %u < 36)", length);
+ goto invalid;
+ }
+
+ version = GET_U_1(bp) >> 4;
+ next_hdr = GET_U_1(bp) & 0x0f;
+ hdr_type = GET_U_1(bp + 1) >> 4;
+ hdr_subtype = GET_U_1(bp + 1) & 0x0f;
+ payload_length = GET_BE_U_2(bp + 4);
+ hop_limit = GET_U_1(bp + 7);
+
+ switch (next_hdr) {
+ case 0: next_hdr_txt = "Any"; break;
+ case 1: next_hdr_txt = "BTP-A"; break;
+ case 2: next_hdr_txt = "BTP-B"; break;
+ case 3: next_hdr_txt = "IPv6"; break;
+ }
+
+ switch (hdr_type) {
+ case 0: hdr_type_txt = "Any"; break;
+ case 1: hdr_type_txt = "Beacon"; break;
+ case 2: hdr_type_txt = "GeoUnicast"; break;
+ case 3: switch (hdr_subtype) {
+ case 0: hdr_type_txt = "GeoAnycastCircle"; break;
+ case 1: hdr_type_txt = "GeoAnycastRect"; break;
+ case 2: hdr_type_txt = "GeoAnycastElipse"; break;
+ }
+ break;
+ case 4: switch (hdr_subtype) {
+ case 0: hdr_type_txt = "GeoBroadcastCircle"; break;
+ case 1: hdr_type_txt = "GeoBroadcastRect"; break;
+ case 2: hdr_type_txt = "GeoBroadcastElipse"; break;
+ }
+ break;
+ case 5: switch (hdr_subtype) {
+ case 0: hdr_type_txt = "TopoScopeBcast-SH"; break;
+ case 1: hdr_type_txt = "TopoScopeBcast-MH"; break;
+ }
+ break;
+ case 6: switch (hdr_subtype) {
+ case 0: hdr_type_txt = "LocService-Request"; break;
+ case 1: hdr_type_txt = "LocService-Reply"; break;
+ }
+ break;
+ }
+
+ ND_PRINT("v:%u ", version);
+ ND_PRINT("NH:%u-%s ", next_hdr, next_hdr_txt);
+ ND_PRINT("HT:%u-%u-%s ", hdr_type, hdr_subtype, hdr_type_txt);
+ ND_PRINT("HopLim:%u ", hop_limit);
+ ND_PRINT("Payload:%u ", payload_length);
+ print_long_pos_vector(ndo, bp + 8);
+
+ /* Skip Common Header */
+ length -= 36;
+ bp += 36;
+
+ /* Process Extended Headers */
+ switch (hdr_type) {
+ case 0: /* Any */
+ hdr_size = 0;
+ break;
+ case 1: /* Beacon */
+ hdr_size = 0;
+ break;
+ case 2: /* GeoUnicast */
+ break;
+ case 3: switch (hdr_subtype) {
+ case 0: /* GeoAnycastCircle */
+ break;
+ case 1: /* GeoAnycastRect */
+ break;
+ case 2: /* GeoAnycastElipse */
+ break;
+ }
+ break;
+ case 4: switch (hdr_subtype) {
+ case 0: /* GeoBroadcastCircle */
+ break;
+ case 1: /* GeoBroadcastRect */
+ break;
+ case 2: /* GeoBroadcastElipse */
+ break;
+ }
+ break;
+ case 5: switch (hdr_subtype) {
+ case 0: /* TopoScopeBcast-SH */
+ hdr_size = 0;
+ break;
+ case 1: /* TopoScopeBcast-MH */
+ hdr_size = 68 - 36;
+ break;
+ }
+ break;
+ case 6: switch (hdr_subtype) {
+ case 0: /* LocService-Request */
+ break;
+ case 1: /* LocService-Reply */
+ break;
+ }
+ break;
+ }
+
+ /* Skip Extended headers */
+ if (hdr_size >= 0) {
+ if (length < (u_int)hdr_size) {
+ ND_PRINT(" (header size %d > %u)", hdr_size, length);
+ goto invalid;
+ }
+ ND_TCHECK_LEN(bp, hdr_size);
+ length -= hdr_size;
+ bp += hdr_size;
+ switch (next_hdr) {
+ case 0: /* Any */
+ break;
+ case 1:
+ case 2: /* BTP A/B */
+ if (length < 4) {
+ ND_PRINT(" (BTP length %u < 4)", length);
+ goto invalid;
+ }
+ print_btp(ndo, bp);
+ length -= 4;
+ bp += 4;
+ if (length >= 2) {
+ /*
+ * XXX - did print_btp_body()
+ * return if length < 2
+ * because this is optional,
+ * or was that just not
+ * reporting genuine errors?
+ */
+ print_btp_body(ndo, bp);
+ }
+ break;
+ case 3: /* IPv6 */
+ break;
+ }
+ }
+
+ /* Print user data part */
+ if (ndo->ndo_vflag)
+ ND_DEFAULTPRINT(bp, length);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ /* XXX - print the remaining data as hex? */
+}
diff --git a/print-gre.c b/print-gre.c
new file mode 100644
index 0000000..b1a8142
--- /dev/null
+++ b/print-gre.c
@@ -0,0 +1,414 @@
+/* $OpenBSD: print-gre.c,v 1.6 2002/10/30 03:04:04 fgsch Exp $ */
+
+/*
+ * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: Generic Routing Encapsulation (GRE) printer */
+
+/*
+ * netdissect printer for GRE - Generic Routing Encapsulation
+ * RFC1701 (GRE), RFC1702 (GRE IPv4), and RFC2637 (Enhanced GRE)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtostr.h"
+#include "extract.h"
+#include "ethertype.h"
+
+
+#define GRE_CP 0x8000 /* checksum present */
+#define GRE_RP 0x4000 /* routing present */
+#define GRE_KP 0x2000 /* key present */
+#define GRE_SP 0x1000 /* sequence# present */
+#define GRE_sP 0x0800 /* source routing */
+#define GRE_AP 0x0080 /* acknowledgment# present */
+
+static const struct tok gre_flag_values[] = {
+ { GRE_CP, "checksum present"},
+ { GRE_RP, "routing present"},
+ { GRE_KP, "key present"},
+ { GRE_SP, "sequence# present"},
+ { GRE_sP, "source routing present"},
+ { GRE_AP, "ack present"},
+ { 0, NULL }
+};
+
+#define GRE_RECRS_MASK 0x0700 /* recursion count */
+#define GRE_VERS_MASK 0x0007 /* protocol version */
+
+/* source route entry types */
+#define GRESRE_IP 0x0800 /* IP */
+#define GRESRE_ASN 0xfffe /* ASN */
+
+static void gre_print_0(netdissect_options *, const u_char *, u_int);
+static void gre_print_1(netdissect_options *, const u_char *, u_int);
+static int gre_sre_print(netdissect_options *, uint16_t, uint8_t, uint8_t, const u_char *, u_int);
+static int gre_sre_ip_print(netdissect_options *, uint8_t, uint8_t, const u_char *, u_int);
+static int gre_sre_asn_print(netdissect_options *, uint8_t, uint8_t, const u_char *, u_int);
+
+void
+gre_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ u_int len = length, vers;
+
+ ndo->ndo_protocol = "gre";
+ ND_TCHECK_2(bp);
+ if (len < 2)
+ goto trunc;
+ vers = GET_BE_U_2(bp) & GRE_VERS_MASK;
+ ND_PRINT("GREv%u",vers);
+
+ switch(vers) {
+ case 0:
+ gre_print_0(ndo, bp, len);
+ break;
+ case 1:
+ gre_print_1(ndo, bp, len);
+ break;
+ default:
+ ND_PRINT(" ERROR: unknown-version");
+ break;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+gre_print_0(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ u_int len = length;
+ uint16_t flags, prot;
+
+ /* 16 bits ND_TCHECKed in gre_print() */
+ flags = GET_BE_U_2(bp);
+ if (ndo->ndo_vflag)
+ ND_PRINT(", Flags [%s]",
+ bittok2str(gre_flag_values,"none",flags));
+
+ len -= 2;
+ bp += 2;
+
+ ND_TCHECK_2(bp);
+ if (len < 2)
+ goto trunc;
+ prot = GET_BE_U_2(bp);
+ len -= 2;
+ bp += 2;
+
+ if ((flags & GRE_CP) | (flags & GRE_RP)) {
+ ND_TCHECK_2(bp);
+ if (len < 2)
+ goto trunc;
+ if (ndo->ndo_vflag)
+ ND_PRINT(", sum 0x%x", GET_BE_U_2(bp));
+ bp += 2;
+ len -= 2;
+
+ ND_TCHECK_2(bp);
+ if (len < 2)
+ goto trunc;
+ ND_PRINT(", off 0x%x", GET_BE_U_2(bp));
+ bp += 2;
+ len -= 2;
+ }
+
+ if (flags & GRE_KP) {
+ ND_TCHECK_4(bp);
+ if (len < 4)
+ goto trunc;
+ ND_PRINT(", key=0x%x", GET_BE_U_4(bp));
+ bp += 4;
+ len -= 4;
+ }
+
+ if (flags & GRE_SP) {
+ ND_TCHECK_4(bp);
+ if (len < 4)
+ goto trunc;
+ ND_PRINT(", seq %u", GET_BE_U_4(bp));
+ bp += 4;
+ len -= 4;
+ }
+
+ if (flags & GRE_RP) {
+ for (;;) {
+ uint16_t af;
+ uint8_t sreoff;
+ uint8_t srelen;
+
+ ND_TCHECK_4(bp);
+ if (len < 4)
+ goto trunc;
+ af = GET_BE_U_2(bp);
+ sreoff = GET_U_1(bp + 2);
+ srelen = GET_U_1(bp + 3);
+ bp += 4;
+ len -= 4;
+
+ if (af == 0 && srelen == 0)
+ break;
+
+ if (!gre_sre_print(ndo, af, sreoff, srelen, bp, len))
+ goto trunc;
+
+ if (len < srelen)
+ goto trunc;
+ bp += srelen;
+ len -= srelen;
+ }
+ }
+
+ if (ndo->ndo_eflag)
+ ND_PRINT(", proto %s (0x%04x)",
+ tok2str(ethertype_values,"unknown",prot), prot);
+
+ ND_PRINT(", length %u",length);
+
+ if (ndo->ndo_vflag < 1)
+ ND_PRINT(": "); /* put in a colon as protocol demarc */
+ else
+ ND_PRINT("\n\t"); /* if verbose go multiline */
+
+ switch (prot) {
+ case ETHERTYPE_IP:
+ ip_print(ndo, bp, len);
+ break;
+ case ETHERTYPE_IPV6:
+ ip6_print(ndo, bp, len);
+ break;
+ case ETHERTYPE_MPLS:
+ mpls_print(ndo, bp, len);
+ break;
+ case ETHERTYPE_IPX:
+ ipx_print(ndo, bp, len);
+ break;
+ case ETHERTYPE_ATALK:
+ atalk_print(ndo, bp, len);
+ break;
+ case ETHERTYPE_GRE_ISO:
+ isoclns_print(ndo, bp, len);
+ break;
+ case ETHERTYPE_TEB:
+ ether_print(ndo, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+ break;
+ default:
+ ND_PRINT("gre-proto-0x%x", prot);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+gre_print_1(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ u_int len = length;
+ uint16_t flags, prot;
+
+ /* 16 bits ND_TCHECKed in gre_print() */
+ flags = GET_BE_U_2(bp);
+ len -= 2;
+ bp += 2;
+
+ if (ndo->ndo_vflag)
+ ND_PRINT(", Flags [%s]",
+ bittok2str(gre_flag_values,"none",flags));
+
+ ND_TCHECK_2(bp);
+ if (len < 2)
+ goto trunc;
+ prot = GET_BE_U_2(bp);
+ len -= 2;
+ bp += 2;
+
+
+ if (flags & GRE_KP) {
+ uint32_t k;
+
+ ND_TCHECK_4(bp);
+ if (len < 4)
+ goto trunc;
+ k = GET_BE_U_4(bp);
+ ND_PRINT(", call %u", k & 0xffff);
+ len -= 4;
+ bp += 4;
+ }
+
+ if (flags & GRE_SP) {
+ ND_TCHECK_4(bp);
+ if (len < 4)
+ goto trunc;
+ ND_PRINT(", seq %u", GET_BE_U_4(bp));
+ bp += 4;
+ len -= 4;
+ }
+
+ if (flags & GRE_AP) {
+ ND_TCHECK_4(bp);
+ if (len < 4)
+ goto trunc;
+ ND_PRINT(", ack %u", GET_BE_U_4(bp));
+ bp += 4;
+ len -= 4;
+ }
+
+ if ((flags & GRE_SP) == 0)
+ ND_PRINT(", no-payload");
+
+ if (ndo->ndo_eflag)
+ ND_PRINT(", proto %s (0x%04x)",
+ tok2str(ethertype_values,"unknown",prot), prot);
+
+ ND_PRINT(", length %u",length);
+
+ if ((flags & GRE_SP) == 0)
+ return;
+
+ if (ndo->ndo_vflag < 1)
+ ND_PRINT(": "); /* put in a colon as protocol demarc */
+ else
+ ND_PRINT("\n\t"); /* if verbose go multiline */
+
+ switch (prot) {
+ case ETHERTYPE_PPP:
+ ppp_print(ndo, bp, len);
+ break;
+ default:
+ ND_PRINT("gre-proto-0x%x", prot);
+ break;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static int
+gre_sre_print(netdissect_options *ndo, uint16_t af, uint8_t sreoff,
+ uint8_t srelen, const u_char *bp, u_int len)
+{
+ int ret;
+
+ switch (af) {
+ case GRESRE_IP:
+ ND_PRINT(", (rtaf=ip");
+ ret = gre_sre_ip_print(ndo, sreoff, srelen, bp, len);
+ ND_PRINT(")");
+ break;
+ case GRESRE_ASN:
+ ND_PRINT(", (rtaf=asn");
+ ret = gre_sre_asn_print(ndo, sreoff, srelen, bp, len);
+ ND_PRINT(")");
+ break;
+ default:
+ ND_PRINT(", (rtaf=0x%x)", af);
+ ret = 1;
+ }
+ return (ret);
+}
+
+static int
+gre_sre_ip_print(netdissect_options *ndo, uint8_t sreoff, uint8_t srelen,
+ const u_char *bp, u_int len)
+{
+ const u_char *up = bp;
+ char buf[INET_ADDRSTRLEN];
+
+ if (sreoff & 3) {
+ ND_PRINT(", badoffset=%u", sreoff);
+ return (1);
+ }
+ if (srelen & 3) {
+ ND_PRINT(", badlength=%u", srelen);
+ return (1);
+ }
+ if (sreoff >= srelen) {
+ ND_PRINT(", badoff/len=%u/%u", sreoff, srelen);
+ return (1);
+ }
+
+ while (srelen != 0) {
+ ND_TCHECK_4(bp);
+ if (len < 4)
+ return (0);
+
+ addrtostr(bp, buf, sizeof(buf));
+ ND_PRINT(" %s%s",
+ ((bp - up) == sreoff) ? "*" : "", buf);
+
+ bp += 4;
+ len -= 4;
+ srelen -= 4;
+ }
+ return (1);
+trunc:
+ return 0;
+}
+
+static int
+gre_sre_asn_print(netdissect_options *ndo, uint8_t sreoff, uint8_t srelen,
+ const u_char *bp, u_int len)
+{
+ const u_char *up = bp;
+
+ if (sreoff & 1) {
+ ND_PRINT(", badoffset=%u", sreoff);
+ return (1);
+ }
+ if (srelen & 1) {
+ ND_PRINT(", badlength=%u", srelen);
+ return (1);
+ }
+ if (sreoff >= srelen) {
+ ND_PRINT(", badoff/len=%u/%u", sreoff, srelen);
+ return (1);
+ }
+
+ while (srelen != 0) {
+ ND_TCHECK_2(bp);
+ if (len < 2)
+ return (0);
+
+ ND_PRINT(" %s%x",
+ ((bp - up) == sreoff) ? "*" : "", GET_BE_U_2(bp));
+
+ bp += 2;
+ len -= 2;
+ srelen -= 2;
+ }
+ return (1);
+trunc:
+ return 0;
+}
diff --git a/print-hncp.c b/print-hncp.c
new file mode 100644
index 0000000..b288160
--- /dev/null
+++ b/print-hncp.c
@@ -0,0 +1,866 @@
+/*
+ * Copyright (c) 2016 Antonin Décimo, Jean-Raphaël Gaglione
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: Home Networking Control Protocol (HNCP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+static void
+hncp_print_rec(netdissect_options *ndo,
+ const u_char *cp, u_int length, int indent);
+
+void
+hncp_print(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ ndo->ndo_protocol = "hncp";
+ ND_PRINT("hncp (%u)", length);
+ hncp_print_rec(ndo, cp, length, 1);
+}
+
+/* RFC7787 */
+#define DNCP_REQUEST_NETWORK_STATE 1
+#define DNCP_REQUEST_NODE_STATE 2
+#define DNCP_NODE_ENDPOINT 3
+#define DNCP_NETWORK_STATE 4
+#define DNCP_NODE_STATE 5
+#define DNCP_PEER 8
+#define DNCP_KEEP_ALIVE_INTERVAL 9
+#define DNCP_TRUST_VERDICT 10
+
+/* RFC7788 */
+#define HNCP_HNCP_VERSION 32
+#define HNCP_EXTERNAL_CONNECTION 33
+#define HNCP_DELEGATED_PREFIX 34
+#define HNCP_PREFIX_POLICY 43
+#define HNCP_DHCPV4_DATA 37 /* This is correct, see RFC 7788 Errata ID 5113. */
+#define HNCP_DHCPV6_DATA 38 /* idem */
+#define HNCP_ASSIGNED_PREFIX 35
+#define HNCP_NODE_ADDRESS 36
+#define HNCP_DNS_DELEGATED_ZONE 39
+#define HNCP_DOMAIN_NAME 40
+#define HNCP_NODE_NAME 41
+#define HNCP_MANAGED_PSK 42
+
+/* See type_mask in hncp_print_rec below */
+#define RANGE_DNCP_RESERVED 0x10000
+#define RANGE_HNCP_UNASSIGNED 0x10001
+#define RANGE_DNCP_PRIVATE_USE 0x10002
+#define RANGE_DNCP_FUTURE_USE 0x10003
+
+static const struct tok type_values[] = {
+ { DNCP_REQUEST_NETWORK_STATE, "Request network state" },
+ { DNCP_REQUEST_NODE_STATE, "Request node state" },
+ { DNCP_NODE_ENDPOINT, "Node endpoint" },
+ { DNCP_NETWORK_STATE, "Network state" },
+ { DNCP_NODE_STATE, "Node state" },
+ { DNCP_PEER, "Peer" },
+ { DNCP_KEEP_ALIVE_INTERVAL, "Keep-alive interval" },
+ { DNCP_TRUST_VERDICT, "Trust-Verdict" },
+
+ { HNCP_HNCP_VERSION, "HNCP-Version" },
+ { HNCP_EXTERNAL_CONNECTION, "External-Connection" },
+ { HNCP_DELEGATED_PREFIX, "Delegated-Prefix" },
+ { HNCP_PREFIX_POLICY, "Prefix-Policy" },
+ { HNCP_DHCPV4_DATA, "DHCPv4-Data" },
+ { HNCP_DHCPV6_DATA, "DHCPv6-Data" },
+ { HNCP_ASSIGNED_PREFIX, "Assigned-Prefix" },
+ { HNCP_NODE_ADDRESS, "Node-Address" },
+ { HNCP_DNS_DELEGATED_ZONE, "DNS-Delegated-Zone" },
+ { HNCP_DOMAIN_NAME, "Domain-Name" },
+ { HNCP_NODE_NAME, "Node-Name" },
+ { HNCP_MANAGED_PSK, "Managed-PSK" },
+
+ { RANGE_DNCP_RESERVED, "Reserved" },
+ { RANGE_HNCP_UNASSIGNED, "Unassigned" },
+ { RANGE_DNCP_PRIVATE_USE, "Private use" },
+ { RANGE_DNCP_FUTURE_USE, "Future use" },
+
+ { 0, NULL}
+};
+
+#define DH4OPT_DNS_SERVERS 6 /* RFC2132 */
+#define DH4OPT_NTP_SERVERS 42 /* RFC2132 */
+#define DH4OPT_DOMAIN_SEARCH 119 /* RFC3397 */
+
+static const struct tok dh4opt_str[] = {
+ { DH4OPT_DNS_SERVERS, "DNS-server" },
+ { DH4OPT_NTP_SERVERS, "NTP-server"},
+ { DH4OPT_DOMAIN_SEARCH, "DNS-search" },
+ { 0, NULL }
+};
+
+#define DH6OPT_DNS_SERVERS 23 /* RFC3646 */
+#define DH6OPT_DOMAIN_LIST 24 /* RFC3646 */
+#define DH6OPT_SNTP_SERVERS 31 /* RFC4075 */
+
+static const struct tok dh6opt_str[] = {
+ { DH6OPT_DNS_SERVERS, "DNS-server" },
+ { DH6OPT_DOMAIN_LIST, "DNS-search-list" },
+ { DH6OPT_SNTP_SERVERS, "SNTP-servers" },
+ { 0, NULL }
+};
+
+/*
+ * For IPv4-mapped IPv6 addresses, length of the prefix that precedes
+ * the 4 bytes of IPv4 address at the end of the IPv6 address.
+ */
+#define IPV4_MAPPED_HEADING_LEN 12
+
+/*
+ * Is an IPv6 address an IPv4-mapped address?
+ */
+static int
+is_ipv4_mapped_address(const u_char *addr)
+{
+ /* The value of the prefix */
+ static const u_char ipv4_mapped_heading[IPV4_MAPPED_HEADING_LEN] =
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF };
+
+ return memcmp(addr, ipv4_mapped_heading, IPV4_MAPPED_HEADING_LEN) == 0;
+}
+
+static const char *
+format_nid(netdissect_options *ndo, const u_char *data)
+{
+ static char buf[4][sizeof("01:01:01:01")];
+ static int i = 0;
+ i = (i + 1) % 4;
+ snprintf(buf[i], sizeof(buf[i]), "%02x:%02x:%02x:%02x",
+ GET_U_1(data), GET_U_1(data + 1), GET_U_1(data + 2),
+ GET_U_1(data + 3));
+ return buf[i];
+}
+
+static const char *
+format_256(netdissect_options *ndo, const u_char *data)
+{
+ static char buf[4][sizeof("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")];
+ static int i = 0;
+ i = (i + 1) % 4;
+ snprintf(buf[i], sizeof(buf[i]), "%016" PRIx64 "%016" PRIx64 "%016" PRIx64 "%016" PRIx64,
+ GET_BE_U_8(data),
+ GET_BE_U_8(data + 8),
+ GET_BE_U_8(data + 16),
+ GET_BE_U_8(data + 24)
+ );
+ return buf[i];
+}
+
+static const char *
+format_interval(const uint32_t n)
+{
+ static char buf[4][sizeof("0000000.000s")];
+ static int i = 0;
+ i = (i + 1) % 4;
+ snprintf(buf[i], sizeof(buf[i]), "%u.%03us", n / 1000, n % 1000);
+ return buf[i];
+}
+
+static const char *
+format_ip6addr(netdissect_options *ndo, const u_char *cp)
+{
+ if (is_ipv4_mapped_address(cp))
+ return GET_IPADDR_STRING(cp + IPV4_MAPPED_HEADING_LEN);
+ else
+ return GET_IP6ADDR_STRING(cp);
+}
+
+static int
+print_prefix(netdissect_options *ndo, const u_char *prefix, u_int max_length)
+{
+ int plenbytes;
+ char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx::/128")];
+
+ if (GET_U_1(prefix) >= 96 && max_length >= IPV4_MAPPED_HEADING_LEN + 1 &&
+ is_ipv4_mapped_address(prefix + 1)) {
+ nd_ipv4 addr;
+ u_int plen;
+
+ plen = GET_U_1(prefix) - 96;
+ if (32 < plen)
+ return -1;
+ max_length -= 1;
+
+ memset(&addr, 0, sizeof(addr));
+ plenbytes = (plen + 7) / 8;
+ if (max_length < (u_int)plenbytes + IPV4_MAPPED_HEADING_LEN)
+ return -3;
+ memcpy(&addr, prefix + IPV4_MAPPED_HEADING_LEN + 1, plenbytes);
+ if (plen % 8) {
+ ((u_char *)&addr)[plenbytes - 1] &=
+ ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ snprintf(buf, sizeof(buf), "%s/%u", ipaddr_string(ndo, (const u_char *)&addr), plen);
+ plenbytes += 1 + IPV4_MAPPED_HEADING_LEN;
+ } else {
+ plenbytes = decode_prefix6(ndo, prefix, max_length, buf, sizeof(buf));
+ if (plenbytes < 0)
+ return plenbytes;
+ }
+
+ ND_PRINT("%s", buf);
+ return plenbytes;
+}
+
+static int
+print_dns_label(netdissect_options *ndo,
+ const u_char *cp, u_int max_length, int print)
+{
+ u_int length = 0;
+ while (length < max_length) {
+ u_int lab_length = GET_U_1(cp + length);
+ length++;
+ if (lab_length == 0)
+ return (int)length;
+ if (length > 1 && print)
+ ND_PRINT(".");
+ if (length+lab_length > max_length) {
+ if (print)
+ nd_printjnp(ndo, cp+length, max_length-length);
+ break;
+ }
+ if (print)
+ nd_printjnp(ndo, cp+length, lab_length);
+ length += lab_length;
+ }
+ if (print)
+ ND_PRINT("[|DNS]");
+ return -1;
+}
+
+static int
+dhcpv4_print(netdissect_options *ndo,
+ const u_char *cp, u_int length, int indent)
+{
+ u_int i, t;
+ const uint8_t *tlv, *value;
+ uint8_t type, optlen;
+
+ i = 0;
+ while (i < length) {
+ if (i + 2 > length)
+ return -1;
+ tlv = cp + i;
+ type = GET_U_1(tlv);
+ optlen = GET_U_1(tlv + 1);
+ value = tlv + 2;
+
+ ND_PRINT("\n");
+ for (t = indent; t > 0; t--)
+ ND_PRINT("\t");
+
+ ND_PRINT("%s", tok2str(dh4opt_str, "Unknown", type));
+ ND_PRINT(" (%u)", optlen + 2 );
+ if (i + 2 + optlen > length)
+ return -1;
+
+ switch (type) {
+ case DH4OPT_DNS_SERVERS:
+ case DH4OPT_NTP_SERVERS: {
+ if (optlen < 4 || optlen % 4 != 0) {
+ return -1;
+ }
+ for (t = 0; t < optlen; t += 4)
+ ND_PRINT(" %s", GET_IPADDR_STRING(value + t));
+ }
+ break;
+ case DH4OPT_DOMAIN_SEARCH: {
+ const u_char *tp = value;
+ while (tp < value + optlen) {
+ ND_PRINT(" ");
+ if ((tp = fqdn_print(ndo, tp, value + optlen)) == NULL)
+ return -1;
+ }
+ }
+ break;
+ }
+
+ i += 2 + optlen;
+ }
+ return 0;
+}
+
+static int
+dhcpv6_print(netdissect_options *ndo,
+ const u_char *cp, u_int length, int indent)
+{
+ u_int i, t;
+ const u_char *tlv, *value;
+ uint16_t type, optlen;
+
+ i = 0;
+ while (i < length) {
+ if (i + 4 > length)
+ return -1;
+ tlv = cp + i;
+ type = GET_BE_U_2(tlv);
+ optlen = GET_BE_U_2(tlv + 2);
+ value = tlv + 4;
+
+ ND_PRINT("\n");
+ for (t = indent; t > 0; t--)
+ ND_PRINT("\t");
+
+ ND_PRINT("%s", tok2str(dh6opt_str, "Unknown", type));
+ ND_PRINT(" (%u)", optlen + 4 );
+ if (i + 4 + optlen > length)
+ return -1;
+
+ switch (type) {
+ case DH6OPT_DNS_SERVERS:
+ case DH6OPT_SNTP_SERVERS: {
+ if (optlen % 16 != 0) {
+ nd_print_invalid(ndo);
+ return -1;
+ }
+ for (t = 0; t < optlen; t += 16)
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(value + t));
+ }
+ break;
+ case DH6OPT_DOMAIN_LIST: {
+ const u_char *tp = value;
+ while (tp < value + optlen) {
+ ND_PRINT(" ");
+ if ((tp = fqdn_print(ndo, tp, value + optlen)) == NULL)
+ return -1;
+ }
+ }
+ break;
+ }
+
+ i += 4 + optlen;
+ }
+ return 0;
+}
+
+/* Determine in-line mode */
+static int
+is_in_line(netdissect_options *ndo, int indent)
+{
+ return indent - 1 >= ndo->ndo_vflag && ndo->ndo_vflag < 3;
+}
+
+static void
+print_type_in_line(netdissect_options *ndo,
+ uint32_t type, int count, int indent, int *first_one)
+{
+ if (count > 0) {
+ if (*first_one) {
+ *first_one = 0;
+ if (indent > 1) {
+ u_int t;
+ ND_PRINT("\n");
+ for (t = indent; t > 0; t--)
+ ND_PRINT("\t");
+ } else {
+ ND_PRINT(" ");
+ }
+ } else {
+ ND_PRINT(", ");
+ }
+ ND_PRINT("%s", tok2str(type_values, "Easter Egg", type));
+ if (count > 1)
+ ND_PRINT(" (x%d)", count);
+ }
+}
+
+static void
+hncp_print_rec(netdissect_options *ndo,
+ const u_char *cp, u_int length, int indent)
+{
+ const int in_line = is_in_line(ndo, indent);
+ int first_one = 1;
+
+ u_int i, t;
+
+ uint32_t last_type_mask = 0xffffffffU;
+ int last_type_count = -1;
+
+ const uint8_t *tlv, *value;
+ uint16_t type, bodylen;
+ uint32_t type_mask;
+
+ i = 0;
+ while (i < length) {
+ tlv = cp + i;
+
+ if (!in_line) {
+ ND_PRINT("\n");
+ for (t = indent; t > 0; t--)
+ ND_PRINT("\t");
+ }
+
+ ND_TCHECK_4(tlv);
+ if (i + 4 > length)
+ goto invalid;
+
+ type = GET_BE_U_2(tlv);
+ bodylen = GET_BE_U_2(tlv + 2);
+ value = tlv + 4;
+ ND_TCHECK_LEN(value, bodylen);
+ if (i + bodylen + 4 > length)
+ goto invalid;
+
+ type_mask =
+ (type == 0) ? RANGE_DNCP_RESERVED:
+ (44 <= type && type <= 511) ? RANGE_HNCP_UNASSIGNED:
+ (768 <= type && type <= 1023) ? RANGE_DNCP_PRIVATE_USE:
+ RANGE_DNCP_FUTURE_USE;
+ if (type == 6 || type == 7)
+ type_mask = RANGE_DNCP_FUTURE_USE;
+
+ /* defined types */
+ {
+ t = 0;
+ while (1) {
+ u_int key = type_values[t++].v;
+ if (key > 0xffff)
+ break;
+ if (key == type) {
+ type_mask = type;
+ break;
+ }
+ }
+ }
+
+ if (in_line) {
+ if (last_type_mask == type_mask) {
+ last_type_count++;
+ } else {
+ print_type_in_line(ndo, last_type_mask, last_type_count, indent, &first_one);
+ last_type_mask = type_mask;
+ last_type_count = 1;
+ }
+
+ goto skip_multiline;
+ }
+
+ ND_PRINT("%s", tok2str(type_values, "Easter Egg (42)", type_mask) );
+ if (type_mask > 0xffff)
+ ND_PRINT(": type=%u", type );
+ ND_PRINT(" (%u)", bodylen + 4 );
+
+ switch (type_mask) {
+
+ case DNCP_REQUEST_NETWORK_STATE: {
+ if (bodylen != 0)
+ nd_print_invalid(ndo);
+ }
+ break;
+
+ case DNCP_REQUEST_NODE_STATE: {
+ const char *node_identifier;
+ if (bodylen != 4) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ node_identifier = format_nid(ndo, value);
+ ND_PRINT(" NID: %s", node_identifier);
+ }
+ break;
+
+ case DNCP_NODE_ENDPOINT: {
+ const char *node_identifier;
+ uint32_t endpoint_identifier;
+ if (bodylen != 8) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ node_identifier = format_nid(ndo, value);
+ endpoint_identifier = GET_BE_U_4(value + 4);
+ ND_PRINT(" NID: %s EPID: %08x",
+ node_identifier,
+ endpoint_identifier
+ );
+ }
+ break;
+
+ case DNCP_NETWORK_STATE: {
+ uint64_t hash;
+ if (bodylen != 8) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ hash = GET_BE_U_8(value);
+ ND_PRINT(" hash: %016" PRIx64, hash);
+ }
+ break;
+
+ case DNCP_NODE_STATE: {
+ const char *node_identifier, *interval;
+ uint32_t sequence_number;
+ uint64_t hash;
+ if (bodylen < 20) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ node_identifier = format_nid(ndo, value);
+ sequence_number = GET_BE_U_4(value + 4);
+ interval = format_interval(GET_BE_U_4(value + 8));
+ hash = GET_BE_U_8(value + 12);
+ ND_PRINT(" NID: %s seqno: %u %s hash: %016" PRIx64,
+ node_identifier,
+ sequence_number,
+ interval,
+ hash
+ );
+ hncp_print_rec(ndo, value+20, bodylen-20, indent+1);
+ }
+ break;
+
+ case DNCP_PEER: {
+ const char *peer_node_identifier;
+ uint32_t peer_endpoint_identifier, endpoint_identifier;
+ if (bodylen != 12) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ peer_node_identifier = format_nid(ndo, value);
+ peer_endpoint_identifier = GET_BE_U_4(value + 4);
+ endpoint_identifier = GET_BE_U_4(value + 8);
+ ND_PRINT(" Peer-NID: %s Peer-EPID: %08x Local-EPID: %08x",
+ peer_node_identifier,
+ peer_endpoint_identifier,
+ endpoint_identifier
+ );
+ }
+ break;
+
+ case DNCP_KEEP_ALIVE_INTERVAL: {
+ uint32_t endpoint_identifier;
+ const char *interval;
+ if (bodylen < 8) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ endpoint_identifier = GET_BE_U_4(value);
+ interval = format_interval(GET_BE_U_4(value + 4));
+ ND_PRINT(" EPID: %08x Interval: %s",
+ endpoint_identifier,
+ interval
+ );
+ }
+ break;
+
+ case DNCP_TRUST_VERDICT: {
+ if (bodylen <= 36) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT(" Verdict: %u Fingerprint: %s Common Name: ",
+ GET_U_1(value),
+ format_256(ndo, value + 4));
+ nd_printjnp(ndo, value + 36, bodylen - 36);
+ }
+ break;
+
+ case HNCP_HNCP_VERSION: {
+ uint16_t capabilities;
+ uint8_t M, P, H, L;
+ if (bodylen < 5) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ capabilities = GET_BE_U_2(value + 2);
+ M = (uint8_t)((capabilities >> 12) & 0xf);
+ P = (uint8_t)((capabilities >> 8) & 0xf);
+ H = (uint8_t)((capabilities >> 4) & 0xf);
+ L = (uint8_t)(capabilities & 0xf);
+ ND_PRINT(" M: %u P: %u H: %u L: %u User-agent: ",
+ M, P, H, L
+ );
+ nd_printjnp(ndo, value + 4, bodylen - 4);
+ }
+ break;
+
+ case HNCP_EXTERNAL_CONNECTION: {
+ /* Container TLV */
+ hncp_print_rec(ndo, value, bodylen, indent+1);
+ }
+ break;
+
+ case HNCP_DELEGATED_PREFIX: {
+ int l;
+ if (bodylen < 9 || bodylen < 9 + (GET_U_1(value + 8) + 7) / 8) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT(" VLSO: %s PLSO: %s Prefix: ",
+ format_interval(GET_BE_U_4(value)),
+ format_interval(GET_BE_U_4(value + 4))
+ );
+ l = print_prefix(ndo, value + 8, bodylen - 8);
+ if (l == -1) {
+ ND_PRINT("(length is invalid)");
+ break;
+ }
+ if (l < 0) {
+ /*
+ * We've already checked that we've captured the
+ * entire TLV, based on its length, so this will
+ * either be -1, meaning "the prefix length is
+ * greater than the longest possible address of
+ * that type" (i.e., > 32 for IPv4 or > 128 for
+ * IPv6", or -3, meaning "the prefix runs past
+ * the end of the TLV".
+ */
+ nd_print_invalid(ndo);
+ break;
+ }
+ l += 8 + (-l & 3);
+
+ if (bodylen >= l)
+ hncp_print_rec(ndo, value + l, bodylen - l, indent+1);
+ }
+ break;
+
+ case HNCP_PREFIX_POLICY: {
+ uint8_t policy;
+ int l;
+ if (bodylen < 1) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ policy = GET_U_1(value);
+ ND_PRINT(" type: ");
+ if (policy == 0) {
+ if (bodylen != 1) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT("Internet connectivity");
+ } else if (policy >= 1 && policy <= 128) {
+ ND_PRINT("Dest-Prefix: ");
+ l = print_prefix(ndo, value, bodylen);
+ if (l == -1) {
+ ND_PRINT("(length is invalid)");
+ break;
+ }
+ if (l < 0) {
+ /*
+ * We've already checked that we've captured the
+ * entire TLV, based on its length, so this will
+ * either be -1, meaning "the prefix length is
+ * greater than the longest possible address of
+ * that type" (i.e., > 32 for IPv4 or > 128 for
+ * IPv6", or -3, meaning "the prefix runs past
+ * the end of the TLV".
+ */
+ nd_print_invalid(ndo);
+ break;
+ }
+ } else if (policy == 129) {
+ ND_PRINT("DNS domain: ");
+ print_dns_label(ndo, value+1, bodylen-1, 1);
+ } else if (policy == 130) {
+ ND_PRINT("Opaque UTF-8: ");
+ nd_printjnp(ndo, value + 1, bodylen - 1);
+ } else if (policy == 131) {
+ if (bodylen != 1) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT("Restrictive assignment");
+ } else if (policy >= 132) {
+ ND_PRINT("Unknown (%u)", policy); /* Reserved for future additions */
+ }
+ }
+ break;
+
+ case HNCP_DHCPV4_DATA: {
+ if (bodylen == 0) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ if (dhcpv4_print(ndo, value, bodylen, indent+1) != 0)
+ goto invalid;
+ }
+ break;
+
+ case HNCP_DHCPV6_DATA: {
+ if (bodylen == 0) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ if (dhcpv6_print(ndo, value, bodylen, indent+1) != 0) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ }
+ break;
+
+ case HNCP_ASSIGNED_PREFIX: {
+ uint8_t prty;
+ int l;
+ if (bodylen < 6 || bodylen < 6 + (GET_U_1(value + 5) + 7) / 8) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ prty = GET_U_1(value + 4) & 0xf;
+ ND_PRINT(" EPID: %08x Prty: %u",
+ GET_BE_U_4(value),
+ prty
+ );
+ ND_PRINT(" Prefix: ");
+ if ((l = print_prefix(ndo, value + 5, bodylen - 5)) < 0) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ l += 5;
+ l += -l & 3;
+
+ if (bodylen >= l)
+ hncp_print_rec(ndo, value + l, bodylen - l, indent+1);
+ }
+ break;
+
+ case HNCP_NODE_ADDRESS: {
+ uint32_t endpoint_identifier;
+ const char *ip_address;
+ if (bodylen < 20) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ endpoint_identifier = GET_BE_U_4(value);
+ ip_address = format_ip6addr(ndo, value + 4);
+ ND_PRINT(" EPID: %08x IP Address: %s",
+ endpoint_identifier,
+ ip_address
+ );
+
+ hncp_print_rec(ndo, value + 20, bodylen - 20, indent+1);
+ }
+ break;
+
+ case HNCP_DNS_DELEGATED_ZONE: {
+ const char *ip_address;
+ int len;
+ if (bodylen < 17) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ip_address = format_ip6addr(ndo, value);
+ ND_PRINT(" IP-Address: %s %c%c%c ",
+ ip_address,
+ (GET_U_1(value + 16) & 4) ? 'l' : '-',
+ (GET_U_1(value + 16) & 2) ? 'b' : '-',
+ (GET_U_1(value + 16) & 1) ? 's' : '-'
+ );
+ len = print_dns_label(ndo, value+17, bodylen-17, 1);
+ if (len < 0) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ len += 17;
+ len += -len & 3;
+ if (bodylen >= len)
+ hncp_print_rec(ndo, value+len, bodylen-len, indent+1);
+ }
+ break;
+
+ case HNCP_DOMAIN_NAME: {
+ if (bodylen == 0) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT(" Domain: ");
+ print_dns_label(ndo, value, bodylen, 1);
+ }
+ break;
+
+ case HNCP_NODE_NAME: {
+ u_int l;
+ if (bodylen < 17) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ l = GET_U_1(value + 16);
+ if (bodylen < 17 + l) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT(" IP-Address: %s Name: ",
+ format_ip6addr(ndo, value)
+ );
+ if (l < 64) {
+ ND_PRINT("\"");
+ nd_printjnp(ndo, value + 17, l);
+ ND_PRINT("\"");
+ } else {
+ nd_print_invalid(ndo);
+ }
+ l += 17;
+ l = roundup2(l, 4);
+ if (bodylen >= l)
+ hncp_print_rec(ndo, value + l, bodylen - l, indent+1);
+ }
+ break;
+
+ case HNCP_MANAGED_PSK: {
+ if (bodylen < 32) {
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT(" PSK: %s", format_256(ndo, value));
+ hncp_print_rec(ndo, value + 32, bodylen - 32, indent+1);
+ }
+ break;
+
+ case RANGE_DNCP_RESERVED:
+ case RANGE_HNCP_UNASSIGNED:
+ case RANGE_DNCP_PRIVATE_USE:
+ case RANGE_DNCP_FUTURE_USE:
+ break;
+
+ }
+ skip_multiline:
+
+ i += 4 + roundup2(bodylen, 4);
+ }
+ print_type_in_line(ndo, last_type_mask, last_type_count, indent, &first_one);
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+ return;
+
+ invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-hsrp.c b/print-hsrp.c
new file mode 100644
index 0000000..3027826
--- /dev/null
+++ b/print-hsrp.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2001 Julian Cowley
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: Cisco Hot Standby Router Protocol (HSRP) printer */
+
+/* specification: RFC 2281 for version 1 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+/* HSRP op code types. */
+static const char *op_code_str[] = {
+ "hello",
+ "coup",
+ "resign"
+};
+
+/* HSRP states and associated names. */
+static const struct tok states[] = {
+ { 0, "initial" },
+ { 1, "learn" },
+ { 2, "listen" },
+ { 4, "speak" },
+ { 8, "standby" },
+ { 16, "active" },
+ { 0, NULL }
+};
+
+/*
+ * RFC 2281:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version | Op Code | State | Hellotime |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Holdtime | Priority | Group | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Authentication Data |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Authentication Data |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Virtual IP Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+#define HSRP_AUTH_SIZE 8
+
+/* HSRP protocol header. */
+struct hsrp {
+ nd_uint8_t hsrp_version;
+ nd_uint8_t hsrp_op_code;
+ nd_uint8_t hsrp_state;
+ nd_uint8_t hsrp_hellotime;
+ nd_uint8_t hsrp_holdtime;
+ nd_uint8_t hsrp_priority;
+ nd_uint8_t hsrp_group;
+ nd_uint8_t hsrp_reserved;
+ nd_byte hsrp_authdata[HSRP_AUTH_SIZE];
+ nd_ipv4 hsrp_virtaddr;
+};
+
+void
+hsrp_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ const struct hsrp *hp = (const struct hsrp *) bp;
+ uint8_t version;
+
+ ndo->ndo_protocol = "hsrp";
+ version = GET_U_1(hp->hsrp_version);
+ ND_PRINT("HSRPv%u", version);
+ if (version != 0)
+ return;
+ ND_PRINT("-");
+ ND_PRINT("%s ",
+ tok2strary(op_code_str, "unknown (%u)", GET_U_1(hp->hsrp_op_code)));
+ ND_PRINT("%u: ", len);
+ ND_PRINT("state=%s ",
+ tok2str(states, "Unknown (%u)", GET_U_1(hp->hsrp_state)));
+ ND_PRINT("group=%u ", GET_U_1(hp->hsrp_group));
+ if (GET_U_1(hp->hsrp_reserved) != 0) {
+ ND_PRINT("[reserved=%u!] ", GET_U_1(hp->hsrp_reserved));
+ }
+ ND_PRINT("addr=%s", GET_IPADDR_STRING(hp->hsrp_virtaddr));
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" hellotime=");
+ unsigned_relts_print(ndo, GET_U_1(hp->hsrp_hellotime));
+ ND_PRINT(" holdtime=");
+ unsigned_relts_print(ndo, GET_U_1(hp->hsrp_holdtime));
+ ND_PRINT(" priority=%u", GET_U_1(hp->hsrp_priority));
+ ND_PRINT(" auth=\"");
+ /*
+ * RFC 2281 Section 5.1 does not specify the encoding of
+ * Authentication Data explicitly, but zero padding can be
+ * inferred from the "recommended default value".
+ */
+ nd_printjnp(ndo, hp->hsrp_authdata, HSRP_AUTH_SIZE);
+ ND_PRINT("\"");
+ }
+}
diff --git a/print-http.c b/print-http.c
new file mode 100644
index 0000000..6845d0d
--- /dev/null
+++ b/print-http.c
@@ -0,0 +1,74 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Hypertext Transfer Protocol (HTTP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+/*
+ * Includes WebDAV requests.
+ */
+static const char *httpcmds[] = {
+ "GET",
+ "PUT",
+ "COPY",
+ "HEAD",
+ "LOCK",
+ "MOVE",
+ "POLL",
+ "POST",
+ "BCOPY",
+ "BMOVE",
+ "MKCOL",
+ "TRACE",
+ "LABEL",
+ "MERGE",
+ "DELETE",
+ "SEARCH",
+ "UNLOCK",
+ "REPORT",
+ "UPDATE",
+ "NOTIFY",
+ "BDELETE",
+ "CONNECT",
+ "OPTIONS",
+ "CHECKIN",
+ "PROPFIND",
+ "CHECKOUT",
+ "CCM_POST",
+ "SUBSCRIBE",
+ "PROPPATCH",
+ "BPROPFIND",
+ "BPROPPATCH",
+ "UNCHECKOUT",
+ "MKACTIVITY",
+ "MKWORKSPACE",
+ "UNSUBSCRIBE",
+ "RPC_CONNECT",
+ "VERSION-CONTROL",
+ "BASELINE-CONTROL",
+ NULL
+};
+
+void
+http_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ ndo->ndo_protocol = "http";
+ txtproto_print(ndo, pptr, len, httpcmds, RESP_CODE_SECOND_TOKEN);
+}
diff --git a/print-icmp.c b/print-icmp.c
new file mode 100644
index 0000000..eebb6e3
--- /dev/null
+++ b/print-icmp.c
@@ -0,0 +1,770 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Internet Control Message Protocol (ICMP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip.h"
+#include "udp.h"
+#include "ipproto.h"
+#include "mpls.h"
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp {
+ nd_uint8_t icmp_type; /* type of message, see below */
+ nd_uint8_t icmp_code; /* type sub code */
+ nd_uint16_t icmp_cksum; /* ones complement cksum of struct */
+ union {
+ nd_uint8_t ih_pptr; /* ICMP_PARAMPROB */
+ nd_ipv4 ih_gwaddr; /* ICMP_REDIRECT */
+ struct ih_idseq {
+ nd_uint16_t icd_id;
+ nd_uint16_t icd_seq;
+ } ih_idseq;
+ nd_uint32_t ih_void;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+ union {
+ struct id_ts {
+ nd_uint32_t its_otime;
+ nd_uint32_t its_rtime;
+ nd_uint32_t its_ttime;
+ } id_ts;
+ struct id_ip {
+ struct ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ nd_uint32_t id_mask;
+ nd_byte id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+};
+
+#define ICMP_MPLS_EXT_EXTRACT_VERSION(x) (((x)&0xf0)>>4)
+#define ICMP_MPLS_EXT_VERSION 2
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enough to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define ICMP_MINLEN 8 /* abs minimum */
+#define ICMP_EXTD_MINLEN (156 - sizeof (struct ip)) /* draft-bonica-internet-icmp-08 */
+#define ICMP_TSLEN (8 + 3 * sizeof (uint32_t)) /* timestamp */
+#define ICMP_MASKLEN 12 /* address mask */
+#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
+#define ICMP_ADVLEN(p) (8 + (IP_HL(&(p)->icmp_ip) << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
+#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
+#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
+#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_REDIRECT 5 /* shorter route, codes: */
+#define ICMP_REDIRECT_NET 0 /* for network */
+#define ICMP_REDIRECT_HOST 1 /* for host */
+#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
+#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
+#define ICMP_ECHO 8 /* echo service */
+#define ICMP_ROUTERADVERT 9 /* router advertisement */
+#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
+#define ICMP_TIMXCEED 11 /* time exceeded, code: */
+#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
+#define ICMP_PARAMPROB 12 /* ip header bad */
+#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
+#define ICMP_TSTAMP 13 /* timestamp request */
+#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
+#define ICMP_IREQ 15 /* information request */
+#define ICMP_IREQREPLY 16 /* information reply */
+#define ICMP_MASKREQ 17 /* address mask request */
+#define ICMP_MASKREPLY 18 /* address mask reply */
+
+#define ICMP_MAXTYPE 18
+
+#define ICMP_ERRTYPE(type) \
+ ((type) == ICMP_UNREACH || (type) == ICMP_SOURCEQUENCH || \
+ (type) == ICMP_REDIRECT || (type) == ICMP_TIMXCEED || \
+ (type) == ICMP_PARAMPROB)
+#define ICMP_MPLS_EXT_TYPE(type) \
+ ((type) == ICMP_UNREACH || \
+ (type) == ICMP_TIMXCEED || \
+ (type) == ICMP_PARAMPROB)
+/* rfc1700 */
+#ifndef ICMP_UNREACH_NET_UNKNOWN
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* destination net unknown */
+#endif
+#ifndef ICMP_UNREACH_HOST_UNKNOWN
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* destination host unknown */
+#endif
+#ifndef ICMP_UNREACH_ISOLATED
+#define ICMP_UNREACH_ISOLATED 8 /* source host isolated */
+#endif
+#ifndef ICMP_UNREACH_NET_PROHIB
+#define ICMP_UNREACH_NET_PROHIB 9 /* admin prohibited net */
+#endif
+#ifndef ICMP_UNREACH_HOST_PROHIB
+#define ICMP_UNREACH_HOST_PROHIB 10 /* admin prohibited host */
+#endif
+#ifndef ICMP_UNREACH_TOSNET
+#define ICMP_UNREACH_TOSNET 11 /* tos prohibited net */
+#endif
+#ifndef ICMP_UNREACH_TOSHOST
+#define ICMP_UNREACH_TOSHOST 12 /* tos prohibited host */
+#endif
+
+/* rfc1716 */
+#ifndef ICMP_UNREACH_FILTER_PROHIB
+#define ICMP_UNREACH_FILTER_PROHIB 13 /* admin prohibited filter */
+#endif
+#ifndef ICMP_UNREACH_HOST_PRECEDENCE
+#define ICMP_UNREACH_HOST_PRECEDENCE 14 /* host precedence violation */
+#endif
+#ifndef ICMP_UNREACH_PRECEDENCE_CUTOFF
+#define ICMP_UNREACH_PRECEDENCE_CUTOFF 15 /* precedence cutoff */
+#endif
+
+/* Most of the icmp types */
+static const struct tok icmp2str[] = {
+ { ICMP_ECHOREPLY, "echo reply" },
+ { ICMP_SOURCEQUENCH, "source quench" },
+ { ICMP_ECHO, "echo request" },
+ { ICMP_ROUTERSOLICIT, "router solicitation" },
+ { ICMP_TSTAMP, "time stamp request" },
+ { ICMP_TSTAMPREPLY, "time stamp reply" },
+ { ICMP_IREQ, "information request" },
+ { ICMP_IREQREPLY, "information reply" },
+ { ICMP_MASKREQ, "address mask request" },
+ { 0, NULL }
+};
+
+/* rfc1191 */
+struct mtu_discovery {
+ nd_uint16_t unused;
+ nd_uint16_t nexthopmtu;
+};
+
+/* rfc1256 */
+struct ih_rdiscovery {
+ nd_uint8_t ird_addrnum;
+ nd_uint8_t ird_addrsiz;
+ nd_uint16_t ird_lifetime;
+};
+
+struct id_rdiscovery {
+ nd_uint32_t ird_addr;
+ nd_uint32_t ird_pref;
+};
+
+/*
+ * draft-bonica-internet-icmp-08
+ *
+ * The Destination Unreachable, Time Exceeded
+ * and Parameter Problem messages are slightly changed as per
+ * the above draft. A new Length field gets added to give
+ * the caller an idea about the length of the piggypacked
+ * IP packet before the MPLS extension header starts.
+ *
+ * The Length field represents length of the padded "original datagram"
+ * field measured in 32-bit words.
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Code | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | unused | Length | unused |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Internet Header + leading octets of original datagram |
+ * | |
+ * | // |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct icmp_ext_t {
+ nd_uint8_t icmp_type;
+ nd_uint8_t icmp_code;
+ nd_uint16_t icmp_checksum;
+ nd_byte icmp_reserved;
+ nd_uint8_t icmp_length;
+ nd_byte icmp_reserved2[2];
+ nd_byte icmp_ext_legacy_header[128]; /* extension header starts 128 bytes after ICMP header */
+ nd_byte icmp_ext_version_res[2];
+ nd_uint16_t icmp_ext_checksum;
+ nd_byte icmp_ext_data[1];
+};
+
+struct icmp_mpls_ext_object_header_t {
+ nd_uint16_t length;
+ nd_uint8_t class_num;
+ nd_uint8_t ctype;
+};
+
+static const struct tok icmp_mpls_ext_obj_values[] = {
+ { 1, "MPLS Stack Entry" },
+ { 2, "Extended Payload" },
+ { 0, NULL}
+};
+
+/* prototypes */
+const char *icmp_tstamp_print(u_int);
+
+/* print the milliseconds since midnight UTC */
+const char *
+icmp_tstamp_print(u_int tstamp)
+{
+ u_int msec,sec,min,hrs;
+
+ static char buf[64];
+
+ msec = tstamp % 1000;
+ sec = tstamp / 1000;
+ min = sec / 60; sec -= min * 60;
+ hrs = min / 60; min -= hrs * 60;
+ snprintf(buf, sizeof(buf), "%02u:%02u:%02u.%03u",hrs,min,sec,msec);
+ return buf;
+}
+
+void
+icmp_print(netdissect_options *ndo, const u_char *bp, u_int plen, const u_char *bp2,
+ int fragmented)
+{
+ char *cp;
+ const struct icmp *dp;
+ uint8_t icmp_type, icmp_code;
+ const struct icmp_ext_t *ext_dp;
+ const struct ip *ip;
+ const char *str;
+ const struct ip *oip;
+ uint8_t ip_proto;
+ const struct udphdr *ouh;
+ const uint8_t *obj_tptr;
+ uint32_t raw_label;
+ const u_char *snapend_save;
+ const struct icmp_mpls_ext_object_header_t *icmp_mpls_ext_object_header;
+ u_int hlen, mtu, obj_tlen, obj_class_num, obj_ctype;
+ uint16_t dport;
+ char buf[MAXHOSTNAMELEN + 100];
+ struct cksum_vec vec[1];
+
+ ndo->ndo_protocol = "icmp";
+ dp = (const struct icmp *)bp;
+ ext_dp = (const struct icmp_ext_t *)bp;
+ ip = (const struct ip *)bp2;
+ str = buf;
+
+ icmp_type = GET_U_1(dp->icmp_type);
+ icmp_code = GET_U_1(dp->icmp_code);
+ switch (icmp_type) {
+
+ case ICMP_ECHO:
+ case ICMP_ECHOREPLY:
+ (void)snprintf(buf, sizeof(buf), "echo %s, id %u, seq %u",
+ icmp_type == ICMP_ECHO ?
+ "request" : "reply",
+ GET_BE_U_2(dp->icmp_id),
+ GET_BE_U_2(dp->icmp_seq));
+ break;
+
+ case ICMP_UNREACH:
+ switch (icmp_code) {
+
+ case ICMP_UNREACH_NET:
+ (void)snprintf(buf, sizeof(buf),
+ "net %s unreachable",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_HOST:
+ (void)snprintf(buf, sizeof(buf),
+ "host %s unreachable",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_PROTOCOL:
+ (void)snprintf(buf, sizeof(buf),
+ "%s protocol %u unreachable",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
+ GET_U_1(dp->icmp_ip.ip_p));
+ break;
+
+ case ICMP_UNREACH_PORT:
+ ND_TCHECK_1(dp->icmp_ip.ip_p);
+ oip = &dp->icmp_ip;
+ hlen = IP_HL(oip) * 4;
+ ouh = (const struct udphdr *)(((const u_char *)oip) + hlen);
+ dport = GET_BE_U_2(ouh->uh_dport);
+ ip_proto = GET_U_1(oip->ip_p);
+ switch (ip_proto) {
+
+ case IPPROTO_TCP:
+ (void)snprintf(buf, sizeof(buf),
+ "%s tcp port %s unreachable",
+ GET_IPADDR_STRING(oip->ip_dst),
+ tcpport_string(ndo, dport));
+ break;
+
+ case IPPROTO_UDP:
+ (void)snprintf(buf, sizeof(buf),
+ "%s udp port %s unreachable",
+ GET_IPADDR_STRING(oip->ip_dst),
+ udpport_string(ndo, dport));
+ break;
+
+ default:
+ (void)snprintf(buf, sizeof(buf),
+ "%s protocol %u port %u unreachable",
+ GET_IPADDR_STRING(oip->ip_dst),
+ ip_proto, dport);
+ break;
+ }
+ break;
+
+ case ICMP_UNREACH_NEEDFRAG:
+ {
+ const struct mtu_discovery *mp;
+ mp = (const struct mtu_discovery *)(const u_char *)&dp->icmp_void;
+ mtu = GET_BE_U_2(mp->nexthopmtu);
+ if (mtu) {
+ (void)snprintf(buf, sizeof(buf),
+ "%s unreachable - need to frag (mtu %u)",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst), mtu);
+ } else {
+ (void)snprintf(buf, sizeof(buf),
+ "%s unreachable - need to frag",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ }
+ }
+ break;
+
+ case ICMP_UNREACH_SRCFAIL:
+ (void)snprintf(buf, sizeof(buf),
+ "%s unreachable - source route failed",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_NET_UNKNOWN:
+ (void)snprintf(buf, sizeof(buf),
+ "net %s unreachable - unknown",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_HOST_UNKNOWN:
+ (void)snprintf(buf, sizeof(buf),
+ "host %s unreachable - unknown",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_ISOLATED:
+ (void)snprintf(buf, sizeof(buf),
+ "%s unreachable - source host isolated",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_NET_PROHIB:
+ (void)snprintf(buf, sizeof(buf),
+ "net %s unreachable - admin prohibited",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_HOST_PROHIB:
+ (void)snprintf(buf, sizeof(buf),
+ "host %s unreachable - admin prohibited",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_TOSNET:
+ (void)snprintf(buf, sizeof(buf),
+ "net %s unreachable - tos prohibited",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_TOSHOST:
+ (void)snprintf(buf, sizeof(buf),
+ "host %s unreachable - tos prohibited",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_FILTER_PROHIB:
+ (void)snprintf(buf, sizeof(buf),
+ "host %s unreachable - admin prohibited filter",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_HOST_PRECEDENCE:
+ (void)snprintf(buf, sizeof(buf),
+ "host %s unreachable - host precedence violation",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ case ICMP_UNREACH_PRECEDENCE_CUTOFF:
+ (void)snprintf(buf, sizeof(buf),
+ "host %s unreachable - precedence cutoff",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst));
+ break;
+
+ default:
+ (void)snprintf(buf, sizeof(buf),
+ "%s unreachable - #%u",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
+ icmp_code);
+ break;
+ }
+ break;
+
+ case ICMP_REDIRECT:
+ switch (icmp_code) {
+
+ case ICMP_REDIRECT_NET:
+ (void)snprintf(buf, sizeof(buf),
+ "redirect %s to net %s",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
+ GET_IPADDR_STRING(dp->icmp_gwaddr));
+ break;
+
+ case ICMP_REDIRECT_HOST:
+ (void)snprintf(buf, sizeof(buf),
+ "redirect %s to host %s",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
+ GET_IPADDR_STRING(dp->icmp_gwaddr));
+ break;
+
+ case ICMP_REDIRECT_TOSNET:
+ (void)snprintf(buf, sizeof(buf),
+ "redirect-tos %s to net %s",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
+ GET_IPADDR_STRING(dp->icmp_gwaddr));
+ break;
+
+ case ICMP_REDIRECT_TOSHOST:
+ (void)snprintf(buf, sizeof(buf),
+ "redirect-tos %s to host %s",
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
+ GET_IPADDR_STRING(dp->icmp_gwaddr));
+ break;
+
+ default:
+ (void)snprintf(buf, sizeof(buf),
+ "redirect-#%u %s to %s", icmp_code,
+ GET_IPADDR_STRING(dp->icmp_ip.ip_dst),
+ GET_IPADDR_STRING(dp->icmp_gwaddr));
+ break;
+ }
+ break;
+
+ case ICMP_ROUTERADVERT:
+ {
+ const struct ih_rdiscovery *ihp;
+ const struct id_rdiscovery *idp;
+ u_int lifetime, num, size;
+
+ (void)snprintf(buf, sizeof(buf), "router advertisement");
+ cp = buf + strlen(buf);
+
+ ihp = (const struct ih_rdiscovery *)&dp->icmp_void;
+ ND_TCHECK_SIZE(ihp);
+ (void)strncpy(cp, " lifetime ", sizeof(buf) - (cp - buf));
+ cp = buf + strlen(buf);
+ lifetime = GET_BE_U_2(ihp->ird_lifetime);
+ if (lifetime < 60) {
+ (void)snprintf(cp, sizeof(buf) - (cp - buf), "%u",
+ lifetime);
+ } else if (lifetime < 60 * 60) {
+ (void)snprintf(cp, sizeof(buf) - (cp - buf), "%u:%02u",
+ lifetime / 60, lifetime % 60);
+ } else {
+ (void)snprintf(cp, sizeof(buf) - (cp - buf),
+ "%u:%02u:%02u",
+ lifetime / 3600,
+ (lifetime % 3600) / 60,
+ lifetime % 60);
+ }
+ cp = buf + strlen(buf);
+
+ num = GET_U_1(ihp->ird_addrnum);
+ (void)snprintf(cp, sizeof(buf) - (cp - buf), " %u:", num);
+ cp = buf + strlen(buf);
+
+ size = GET_U_1(ihp->ird_addrsiz);
+ if (size != 2) {
+ (void)snprintf(cp, sizeof(buf) - (cp - buf),
+ " [size %u]", size);
+ break;
+ }
+ idp = (const struct id_rdiscovery *)&dp->icmp_data;
+ while (num > 0) {
+ ND_TCHECK_SIZE(idp);
+ (void)snprintf(cp, sizeof(buf) - (cp - buf), " {%s %u}",
+ GET_IPADDR_STRING(idp->ird_addr),
+ GET_BE_U_4(idp->ird_pref));
+ cp = buf + strlen(buf);
+ ++idp;
+ num--;
+ }
+ }
+ break;
+
+ case ICMP_TIMXCEED:
+ ND_TCHECK_4(dp->icmp_ip.ip_dst);
+ switch (icmp_code) {
+
+ case ICMP_TIMXCEED_INTRANS:
+ str = "time exceeded in-transit";
+ break;
+
+ case ICMP_TIMXCEED_REASS:
+ str = "ip reassembly time exceeded";
+ break;
+
+ default:
+ (void)snprintf(buf, sizeof(buf), "time exceeded-#%u",
+ icmp_code);
+ break;
+ }
+ break;
+
+ case ICMP_PARAMPROB:
+ if (icmp_code)
+ (void)snprintf(buf, sizeof(buf),
+ "parameter problem - code %u", icmp_code);
+ else {
+ (void)snprintf(buf, sizeof(buf),
+ "parameter problem - octet %u",
+ GET_U_1(dp->icmp_pptr));
+ }
+ break;
+
+ case ICMP_MASKREPLY:
+ (void)snprintf(buf, sizeof(buf), "address mask is 0x%08x",
+ GET_BE_U_4(dp->icmp_mask));
+ break;
+
+ case ICMP_TSTAMP:
+ (void)snprintf(buf, sizeof(buf),
+ "time stamp query id %u seq %u",
+ GET_BE_U_2(dp->icmp_id),
+ GET_BE_U_2(dp->icmp_seq));
+ break;
+
+ case ICMP_TSTAMPREPLY:
+ ND_TCHECK_4(dp->icmp_ttime);
+ (void)snprintf(buf, sizeof(buf),
+ "time stamp reply id %u seq %u: org %s",
+ GET_BE_U_2(dp->icmp_id),
+ GET_BE_U_2(dp->icmp_seq),
+ icmp_tstamp_print(GET_BE_U_4(dp->icmp_otime)));
+
+ (void)snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),", recv %s",
+ icmp_tstamp_print(GET_BE_U_4(dp->icmp_rtime)));
+ (void)snprintf(buf+strlen(buf),sizeof(buf)-strlen(buf),", xmit %s",
+ icmp_tstamp_print(GET_BE_U_4(dp->icmp_ttime)));
+ break;
+
+ default:
+ str = tok2str(icmp2str, "type-#%u", icmp_type);
+ break;
+ }
+ ND_PRINT("ICMP %s, length %u", str, plen);
+ if (ndo->ndo_vflag && !fragmented) { /* don't attempt checksumming if this is a frag */
+ if (ND_TTEST_LEN(bp, plen)) {
+ uint16_t sum;
+
+ vec[0].ptr = (const uint8_t *)(const void *)dp;
+ vec[0].len = plen;
+ sum = in_cksum(vec, 1);
+ if (sum != 0) {
+ uint16_t icmp_sum = GET_BE_U_2(dp->icmp_cksum);
+ ND_PRINT(" (wrong icmp cksum %x (->%x)!)",
+ icmp_sum,
+ in_cksum_shouldbe(icmp_sum, sum));
+ }
+ }
+ }
+
+ /*
+ * print the remnants of the IP packet.
+ * save the snaplength as this may get overridden in the IP printer.
+ */
+ if (ndo->ndo_vflag >= 1 && ICMP_ERRTYPE(icmp_type)) {
+ bp += 8;
+ ND_PRINT("\n\t");
+ ip = (const struct ip *)bp;
+ snapend_save = ndo->ndo_snapend;
+ ip_print(ndo, bp, GET_BE_U_2(ip->ip_len));
+ ndo->ndo_snapend = snapend_save;
+ }
+
+ /* ndo_protocol reassignment after ip_print() call */
+ ndo->ndo_protocol = "icmp";
+
+ /*
+ * Attempt to decode the MPLS extensions only for some ICMP types.
+ */
+ if (ndo->ndo_vflag >= 1 && plen > ICMP_EXTD_MINLEN && ICMP_MPLS_EXT_TYPE(icmp_type)) {
+
+ ND_TCHECK_SIZE(ext_dp);
+
+ /*
+ * Check first if the mpls extension header shows a non-zero length.
+ * If the length field is not set then silently verify the checksum
+ * to check if an extension header is present. This is expedient,
+ * however not all implementations set the length field proper.
+ */
+ if (GET_U_1(ext_dp->icmp_length) == 0 &&
+ ND_TTEST_LEN(ext_dp->icmp_ext_version_res, plen - ICMP_EXTD_MINLEN)) {
+ vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
+ vec[0].len = plen - ICMP_EXTD_MINLEN;
+ if (in_cksum(vec, 1)) {
+ return;
+ }
+ }
+
+ ND_PRINT("\n\tMPLS extension v%u",
+ ICMP_MPLS_EXT_EXTRACT_VERSION(*(ext_dp->icmp_ext_version_res)));
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (ICMP_MPLS_EXT_EXTRACT_VERSION(*(ext_dp->icmp_ext_version_res)) !=
+ ICMP_MPLS_EXT_VERSION) {
+ ND_PRINT(" packet not supported");
+ return;
+ }
+
+ hlen = plen - ICMP_EXTD_MINLEN;
+ if (ND_TTEST_LEN(ext_dp->icmp_ext_version_res, hlen)) {
+ vec[0].ptr = (const uint8_t *)(const void *)&ext_dp->icmp_ext_version_res;
+ vec[0].len = hlen;
+ ND_PRINT(", checksum 0x%04x (%scorrect), length %u",
+ GET_BE_U_2(ext_dp->icmp_ext_checksum),
+ in_cksum(vec, 1) ? "in" : "",
+ hlen);
+ }
+
+ hlen -= 4; /* subtract common header size */
+ obj_tptr = (const uint8_t *)ext_dp->icmp_ext_data;
+
+ while (hlen > sizeof(struct icmp_mpls_ext_object_header_t)) {
+
+ icmp_mpls_ext_object_header = (const struct icmp_mpls_ext_object_header_t *)obj_tptr;
+ ND_TCHECK_SIZE(icmp_mpls_ext_object_header);
+ obj_tlen = GET_BE_U_2(icmp_mpls_ext_object_header->length);
+ obj_class_num = GET_U_1(icmp_mpls_ext_object_header->class_num);
+ obj_ctype = GET_U_1(icmp_mpls_ext_object_header->ctype);
+ obj_tptr += sizeof(struct icmp_mpls_ext_object_header_t);
+
+ ND_PRINT("\n\t %s Object (%u), Class-Type: %u, length %u",
+ tok2str(icmp_mpls_ext_obj_values,"unknown",obj_class_num),
+ obj_class_num,
+ obj_ctype,
+ obj_tlen);
+
+ hlen-=sizeof(struct icmp_mpls_ext_object_header_t); /* length field includes tlv header */
+
+ /* infinite loop protection */
+ if ((obj_class_num == 0) ||
+ (obj_tlen < sizeof(struct icmp_mpls_ext_object_header_t))) {
+ return;
+ }
+ obj_tlen-=sizeof(struct icmp_mpls_ext_object_header_t);
+
+ switch (obj_class_num) {
+ case 1:
+ switch(obj_ctype) {
+ case 1:
+ raw_label = GET_BE_U_4(obj_tptr);
+ ND_PRINT("\n\t label %u, exp %u", MPLS_LABEL(raw_label), MPLS_EXP(raw_label));
+ if (MPLS_STACK(raw_label))
+ ND_PRINT(", [S]");
+ ND_PRINT(", ttl %u", MPLS_TTL(raw_label));
+ break;
+ default:
+ print_unknown_data(ndo, obj_tptr, "\n\t ", obj_tlen);
+ }
+ break;
+
+ /*
+ * FIXME those are the defined objects that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+ case 2:
+ default:
+ print_unknown_data(ndo, obj_tptr, "\n\t ", obj_tlen);
+ break;
+ }
+ if (hlen < obj_tlen)
+ break;
+ hlen -= obj_tlen;
+ obj_tptr += obj_tlen;
+ }
+ }
+
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-icmp6.c b/print-icmp6.c
new file mode 100644
index 0000000..ba1f6e6
--- /dev/null
+++ b/print-icmp6.c
@@ -0,0 +1,2098 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IPv6 Internet Control Message Protocol (ICMPv6) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "addrtostr.h"
+#include "extract.h"
+
+#include "ip6.h"
+#include "ipproto.h"
+
+#include "udp.h"
+#include "ah.h"
+
+/* NetBSD: icmp6.h,v 1.13 2000/08/03 16:30:37 itojun Exp */
+/* $KAME: icmp6.h,v 1.22 2000/08/03 15:25:16 jinmei Exp $ */
+
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+struct icmp6_hdr {
+ nd_uint8_t icmp6_type; /* type field */
+ nd_uint8_t icmp6_code; /* code field */
+ nd_uint16_t icmp6_cksum; /* checksum field */
+ union {
+ nd_uint32_t icmp6_un_data32[1]; /* type-specific field */
+ nd_uint16_t icmp6_un_data16[2]; /* type-specific field */
+ nd_uint8_t icmp6_un_data8[4]; /* type-specific field */
+ nd_byte icmp6_un_data[1]; /* type-specific field */
+ } icmp6_dataun;
+};
+
+#define icmp6_data32 icmp6_dataun.icmp6_un_data32
+#define icmp6_data16 icmp6_dataun.icmp6_un_data16
+#define icmp6_data8 icmp6_dataun.icmp6_un_data8
+#define icmp6_data icmp6_dataun.icmp6_un_data
+#define icmp6_pptr icmp6_data32[0] /* parameter prob */
+#define icmp6_mtu icmp6_data32[0] /* packet too big */
+#define icmp6_id icmp6_data16[0] /* echo request/reply */
+#define icmp6_seq icmp6_data16[1] /* echo request/reply */
+#define icmp6_maxdelay icmp6_data16[0] /* mcast group membership */
+
+#define ICMP6_DST_UNREACH 1 /* dest unreachable, codes: */
+#define ICMP6_PACKET_TOO_BIG 2 /* packet too big */
+#define ICMP6_TIME_EXCEEDED 3 /* time exceeded, code: */
+#define ICMP6_PARAM_PROB 4 /* ip6 header bad */
+
+#define ICMP6_ECHO_REQUEST 128 /* echo service */
+#define ICMP6_ECHO_REPLY 129 /* echo reply */
+#define ICMP6_MEMBERSHIP_QUERY 130 /* group membership query */
+#define MLD6_LISTENER_QUERY 130 /* multicast listener query */
+#define ICMP6_MEMBERSHIP_REPORT 131 /* group membership report */
+#define MLD6_LISTENER_REPORT 131 /* multicast listener report */
+#define ICMP6_MEMBERSHIP_REDUCTION 132 /* group membership termination */
+#define MLD6_LISTENER_DONE 132 /* multicast listener done */
+
+#define ND_ROUTER_SOLICIT 133 /* router solicitation */
+#define ND_ROUTER_ADVERT 134 /* router advertisement */
+#define ND_NEIGHBOR_SOLICIT 135 /* neighbor solicitation */
+#define ND_NEIGHBOR_ADVERT 136 /* neighbor advertisement */
+#define ND_REDIRECT 137 /* redirect */
+
+#define ICMP6_ROUTER_RENUMBERING 138 /* router renumbering */
+
+#define ICMP6_WRUREQUEST 139 /* who are you request */
+#define ICMP6_WRUREPLY 140 /* who are you reply */
+#define ICMP6_FQDN_QUERY 139 /* FQDN query */
+#define ICMP6_FQDN_REPLY 140 /* FQDN reply */
+#define ICMP6_NI_QUERY 139 /* node information request - RFC 4620 */
+#define ICMP6_NI_REPLY 140 /* node information reply - RFC 4620 */
+#define IND_SOLICIT 141 /* inverse neighbor solicitation */
+#define IND_ADVERT 142 /* inverse neighbor advertisement */
+
+#define ICMP6_V2_MEMBERSHIP_REPORT 143 /* v2 membership report */
+#define MLDV2_LISTENER_REPORT 143 /* v2 multicast listener report */
+#define ICMP6_HADISCOV_REQUEST 144
+#define ICMP6_HADISCOV_REPLY 145
+#define ICMP6_MOBILEPREFIX_SOLICIT 146
+#define ICMP6_MOBILEPREFIX_ADVERT 147
+
+#define MLD6_MTRACE_RESP 200 /* mtrace response(to sender) */
+#define MLD6_MTRACE 201 /* mtrace messages */
+
+#define ICMP6_MAXTYPE 201
+
+#define ICMP6_DST_UNREACH_NOROUTE 0 /* no route to destination */
+#define ICMP6_DST_UNREACH_ADMIN 1 /* administratively prohibited */
+#define ICMP6_DST_UNREACH_NOTNEIGHBOR 2 /* not a neighbor(obsolete) */
+#define ICMP6_DST_UNREACH_BEYONDSCOPE 2 /* beyond scope of source address */
+#define ICMP6_DST_UNREACH_ADDR 3 /* address unreachable */
+#define ICMP6_DST_UNREACH_NOPORT 4 /* port unreachable */
+
+#define ICMP6_TIME_EXCEED_TRANSIT 0 /* ttl==0 in transit */
+#define ICMP6_TIME_EXCEED_REASSEMBLY 1 /* ttl==0 in reass */
+
+#define ICMP6_PARAMPROB_HEADER 0 /* erroneous header field */
+#define ICMP6_PARAMPROB_NEXTHEADER 1 /* unrecognized next header */
+#define ICMP6_PARAMPROB_OPTION 2 /* unrecognized option */
+#define ICMP6_PARAMPROB_FRAGHDRCHAIN 3 /* incomplete header chain */
+
+#define ICMP6_INFOMSG_MASK 0x80 /* all informational messages */
+
+#define ICMP6_NI_SUBJ_IPV6 0 /* Query Subject is an IPv6 address */
+#define ICMP6_NI_SUBJ_FQDN 1 /* Query Subject is a Domain name */
+#define ICMP6_NI_SUBJ_IPV4 2 /* Query Subject is an IPv4 address */
+
+#define ICMP6_NI_SUCCESS 0 /* node information successful reply */
+#define ICMP6_NI_REFUSED 1 /* node information request is refused */
+#define ICMP6_NI_UNKNOWN 2 /* unknown Qtype */
+
+#define ICMP6_ROUTER_RENUMBERING_COMMAND 0 /* rr command */
+#define ICMP6_ROUTER_RENUMBERING_RESULT 1 /* rr result */
+#define ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET 255 /* rr seq num reset */
+
+/* Used in kernel only */
+#define ND_REDIRECT_ONLINK 0 /* redirect to an on-link node */
+#define ND_REDIRECT_ROUTER 1 /* redirect to a better router */
+
+/*
+ * Multicast Listener Discovery
+ */
+struct mld6_hdr {
+ struct icmp6_hdr mld6_hdr;
+ nd_ipv6 mld6_addr; /* multicast address */
+};
+
+#define mld6_type mld6_hdr.icmp6_type
+#define mld6_code mld6_hdr.icmp6_code
+#define mld6_cksum mld6_hdr.icmp6_cksum
+#define mld6_maxdelay mld6_hdr.icmp6_data16[0]
+#define mld6_reserved mld6_hdr.icmp6_data16[1]
+
+#define MLD_MINLEN 24
+#define MLDV2_MINLEN 28
+
+/*
+ * Neighbor Discovery
+ */
+
+struct nd_router_solicit { /* router solicitation */
+ struct icmp6_hdr nd_rs_hdr;
+ /* could be followed by options */
+};
+
+#define nd_rs_type nd_rs_hdr.icmp6_type
+#define nd_rs_code nd_rs_hdr.icmp6_code
+#define nd_rs_cksum nd_rs_hdr.icmp6_cksum
+#define nd_rs_reserved nd_rs_hdr.icmp6_data32[0]
+
+struct nd_router_advert { /* router advertisement */
+ struct icmp6_hdr nd_ra_hdr;
+ nd_uint32_t nd_ra_reachable; /* reachable time */
+ nd_uint32_t nd_ra_retransmit; /* retransmit timer */
+ /* could be followed by options */
+};
+
+#define nd_ra_type nd_ra_hdr.icmp6_type
+#define nd_ra_code nd_ra_hdr.icmp6_code
+#define nd_ra_cksum nd_ra_hdr.icmp6_cksum
+#define nd_ra_curhoplimit nd_ra_hdr.icmp6_data8[0]
+#define nd_ra_flags_reserved nd_ra_hdr.icmp6_data8[1]
+#define ND_RA_FLAG_MANAGED 0x80
+#define ND_RA_FLAG_OTHER 0x40
+#define ND_RA_FLAG_HOME_AGENT 0x20
+#define ND_RA_FLAG_IPV6ONLY 0x02
+
+/*
+ * Router preference values based on draft-draves-ipngwg-router-selection-01.
+ * These are non-standard definitions.
+ */
+#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */
+
+#define ND_RA_FLAG_RTPREF_HIGH 0x08 /* 00001000 */
+#define ND_RA_FLAG_RTPREF_MEDIUM 0x00 /* 00000000 */
+#define ND_RA_FLAG_RTPREF_LOW 0x18 /* 00011000 */
+#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */
+
+#define nd_ra_router_lifetime nd_ra_hdr.icmp6_data16[1]
+
+struct nd_neighbor_solicit { /* neighbor solicitation */
+ struct icmp6_hdr nd_ns_hdr;
+ nd_ipv6 nd_ns_target; /*target address */
+ /* could be followed by options */
+};
+
+#define nd_ns_type nd_ns_hdr.icmp6_type
+#define nd_ns_code nd_ns_hdr.icmp6_code
+#define nd_ns_cksum nd_ns_hdr.icmp6_cksum
+#define nd_ns_reserved nd_ns_hdr.icmp6_data32[0]
+
+struct nd_neighbor_advert { /* neighbor advertisement */
+ struct icmp6_hdr nd_na_hdr;
+ nd_ipv6 nd_na_target; /* target address */
+ /* could be followed by options */
+};
+
+#define nd_na_type nd_na_hdr.icmp6_type
+#define nd_na_code nd_na_hdr.icmp6_code
+#define nd_na_cksum nd_na_hdr.icmp6_cksum
+#define nd_na_flags_reserved nd_na_hdr.icmp6_data32[0]
+
+#define ND_NA_FLAG_ROUTER 0x80000000
+#define ND_NA_FLAG_SOLICITED 0x40000000
+#define ND_NA_FLAG_OVERRIDE 0x20000000
+
+struct nd_redirect { /* redirect */
+ struct icmp6_hdr nd_rd_hdr;
+ nd_ipv6 nd_rd_target; /* target address */
+ nd_ipv6 nd_rd_dst; /* destination address */
+ /* could be followed by options */
+};
+
+#define nd_rd_type nd_rd_hdr.icmp6_type
+#define nd_rd_code nd_rd_hdr.icmp6_code
+#define nd_rd_cksum nd_rd_hdr.icmp6_cksum
+#define nd_rd_reserved nd_rd_hdr.icmp6_data32[0]
+
+struct nd_opt_hdr { /* Neighbor discovery option header */
+ nd_uint8_t nd_opt_type;
+ nd_uint8_t nd_opt_len;
+ /* followed by option specific data*/
+};
+
+#define ND_OPT_SOURCE_LINKADDR 1
+#define ND_OPT_TARGET_LINKADDR 2
+#define ND_OPT_PREFIX_INFORMATION 3
+#define ND_OPT_REDIRECTED_HEADER 4
+#define ND_OPT_MTU 5
+#define ND_OPT_ADVINTERVAL 7
+#define ND_OPT_HOMEAGENT_INFO 8
+#define ND_OPT_ROUTE_INFO 24 /* RFC4191 */
+#define ND_OPT_RDNSS 25
+#define ND_OPT_DNSSL 31
+
+struct nd_opt_prefix_info { /* prefix information */
+ nd_uint8_t nd_opt_pi_type;
+ nd_uint8_t nd_opt_pi_len;
+ nd_uint8_t nd_opt_pi_prefix_len;
+ nd_uint8_t nd_opt_pi_flags_reserved;
+ nd_uint32_t nd_opt_pi_valid_time;
+ nd_uint32_t nd_opt_pi_preferred_time;
+ nd_uint32_t nd_opt_pi_reserved2;
+ nd_ipv6 nd_opt_pi_prefix;
+};
+
+#define ND_OPT_PI_FLAG_ONLINK 0x80
+#define ND_OPT_PI_FLAG_AUTO 0x40
+#define ND_OPT_PI_FLAG_ROUTER 0x20 /*2292bis*/
+
+struct nd_opt_rd_hdr { /* redirected header */
+ nd_uint8_t nd_opt_rh_type;
+ nd_uint8_t nd_opt_rh_len;
+ nd_uint16_t nd_opt_rh_reserved1;
+ nd_uint32_t nd_opt_rh_reserved2;
+ /* followed by IP header and data */
+};
+
+struct nd_opt_mtu { /* MTU option */
+ nd_uint8_t nd_opt_mtu_type;
+ nd_uint8_t nd_opt_mtu_len;
+ nd_uint16_t nd_opt_mtu_reserved;
+ nd_uint32_t nd_opt_mtu_mtu;
+};
+
+struct nd_opt_rdnss { /* RDNSS RFC 6106 5.1 */
+ nd_uint8_t nd_opt_rdnss_type;
+ nd_uint8_t nd_opt_rdnss_len;
+ nd_uint16_t nd_opt_rdnss_reserved;
+ nd_uint32_t nd_opt_rdnss_lifetime;
+ nd_ipv6 nd_opt_rdnss_addr[1]; /* variable-length */
+};
+
+struct nd_opt_dnssl { /* DNSSL RFC 6106 5.2 */
+ nd_uint8_t nd_opt_dnssl_type;
+ nd_uint8_t nd_opt_dnssl_len;
+ nd_uint16_t nd_opt_dnssl_reserved;
+ nd_uint32_t nd_opt_dnssl_lifetime;
+ /* followed by list of DNS search domains, variable-length */
+};
+
+struct nd_opt_advinterval { /* Advertisement interval option */
+ nd_uint8_t nd_opt_adv_type;
+ nd_uint8_t nd_opt_adv_len;
+ nd_uint16_t nd_opt_adv_reserved;
+ nd_uint32_t nd_opt_adv_interval;
+};
+
+struct nd_opt_homeagent_info { /* Home Agent info */
+ nd_uint8_t nd_opt_hai_type;
+ nd_uint8_t nd_opt_hai_len;
+ nd_uint16_t nd_opt_hai_reserved;
+ nd_uint16_t nd_opt_hai_preference;
+ nd_uint16_t nd_opt_hai_lifetime;
+};
+
+struct nd_opt_route_info { /* route info */
+ nd_uint8_t nd_opt_rti_type;
+ nd_uint8_t nd_opt_rti_len;
+ nd_uint8_t nd_opt_rti_prefixlen;
+ nd_uint8_t nd_opt_rti_flags;
+ nd_uint32_t nd_opt_rti_lifetime;
+ /* prefix follows */
+};
+
+/*
+ * icmp6 namelookup
+ */
+
+struct icmp6_namelookup {
+ struct icmp6_hdr icmp6_nl_hdr;
+ nd_byte icmp6_nl_nonce[8];
+ nd_int32_t icmp6_nl_ttl;
+#if 0
+ nd_uint8_t icmp6_nl_len;
+ nd_byte icmp6_nl_name[3];
+#endif
+ /* could be followed by options */
+};
+
+/*
+ * icmp6 node information
+ */
+struct icmp6_nodeinfo {
+ struct icmp6_hdr icmp6_ni_hdr;
+ nd_byte icmp6_ni_nonce[8];
+ /* could be followed by reply data */
+};
+
+#define ni_type icmp6_ni_hdr.icmp6_type
+#define ni_code icmp6_ni_hdr.icmp6_code
+#define ni_cksum icmp6_ni_hdr.icmp6_cksum
+#define ni_qtype icmp6_ni_hdr.icmp6_data16[0]
+#define ni_flags icmp6_ni_hdr.icmp6_data16[1]
+
+#define NI_QTYPE_NOOP 0 /* NOOP */
+#define NI_QTYPE_SUPTYPES 1 /* Supported Qtypes (drafts up to 09) */
+#define NI_QTYPE_FQDN 2 /* FQDN (draft 04) */
+#define NI_QTYPE_DNSNAME 2 /* DNS Name */
+#define NI_QTYPE_NODEADDR 3 /* Node Addresses */
+#define NI_QTYPE_IPV4ADDR 4 /* IPv4 Addresses */
+
+/* network endian */
+#define NI_SUPTYPE_FLAG_COMPRESS ((uint16_t)htons(0x1))
+#define NI_FQDN_FLAG_VALIDTTL ((uint16_t)htons(0x1))
+
+/* network endian */
+#define NI_NODEADDR_FLAG_TRUNCATE ((uint16_t)htons(0x1))
+#define NI_NODEADDR_FLAG_ALL ((uint16_t)htons(0x2))
+#define NI_NODEADDR_FLAG_COMPAT ((uint16_t)htons(0x4))
+#define NI_NODEADDR_FLAG_LINKLOCAL ((uint16_t)htons(0x8))
+#define NI_NODEADDR_FLAG_SITELOCAL ((uint16_t)htons(0x10))
+#define NI_NODEADDR_FLAG_GLOBAL ((uint16_t)htons(0x20))
+#define NI_NODEADDR_FLAG_ANYCAST ((uint16_t)htons(0x40)) /* just experimental. not in spec */
+
+struct ni_reply_fqdn {
+ nd_uint32_t ni_fqdn_ttl; /* TTL */
+ nd_uint8_t ni_fqdn_namelen; /* length in octets of the FQDN */
+ nd_byte ni_fqdn_name[3]; /* XXX: alignment */
+};
+
+/*
+ * Router Renumbering. as router-renum-08.txt
+ */
+struct icmp6_router_renum { /* router renumbering header */
+ struct icmp6_hdr rr_hdr;
+ nd_uint8_t rr_segnum;
+ nd_uint8_t rr_flags;
+ nd_uint16_t rr_maxdelay;
+ nd_uint32_t rr_reserved;
+};
+#define ICMP6_RR_FLAGS_TEST 0x80
+#define ICMP6_RR_FLAGS_REQRESULT 0x40
+#define ICMP6_RR_FLAGS_FORCEAPPLY 0x20
+#define ICMP6_RR_FLAGS_SPECSITE 0x10
+#define ICMP6_RR_FLAGS_PREVDONE 0x08
+
+#define rr_type rr_hdr.icmp6_type
+#define rr_code rr_hdr.icmp6_code
+#define rr_cksum rr_hdr.icmp6_cksum
+#define rr_seqnum rr_hdr.icmp6_data32[0]
+
+struct rr_pco_match { /* match prefix part */
+ nd_uint8_t rpm_code;
+ nd_uint8_t rpm_len;
+ nd_uint8_t rpm_ordinal;
+ nd_uint8_t rpm_matchlen;
+ nd_uint8_t rpm_minlen;
+ nd_uint8_t rpm_maxlen;
+ nd_uint16_t rpm_reserved;
+ nd_ipv6 rpm_prefix;
+};
+
+#define RPM_PCO_ADD 1
+#define RPM_PCO_CHANGE 2
+#define RPM_PCO_SETGLOBAL 3
+#define RPM_PCO_MAX 4
+
+struct rr_pco_use { /* use prefix part */
+ nd_uint8_t rpu_uselen;
+ nd_uint8_t rpu_keeplen;
+ nd_uint8_t rpu_ramask;
+ nd_uint8_t rpu_raflags;
+ nd_uint32_t rpu_vltime;
+ nd_uint32_t rpu_pltime;
+ nd_uint32_t rpu_flags;
+ nd_ipv6 rpu_prefix;
+};
+#define ICMP6_RR_PCOUSE_RAFLAGS_ONLINK 0x80
+#define ICMP6_RR_PCOUSE_RAFLAGS_AUTO 0x40
+
+/* network endian */
+#define ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME ((uint32_t)htonl(0x80000000))
+#define ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME ((uint32_t)htonl(0x40000000))
+
+struct rr_result { /* router renumbering result message */
+ nd_uint16_t rrr_flags;
+ nd_uint8_t rrr_ordinal;
+ nd_uint8_t rrr_matchedlen;
+ nd_uint32_t rrr_ifid;
+ nd_ipv6 rrr_prefix;
+};
+/* network endian */
+#define ICMP6_RR_RESULT_FLAGS_OOB ((uint16_t)htons(0x0002))
+#define ICMP6_RR_RESULT_FLAGS_FORBIDDEN ((uint16_t)htons(0x0001))
+
+static const char *get_rtpref(u_int);
+static const char *get_lifetime(uint32_t);
+static void print_lladdr(netdissect_options *ndo, const u_char *, size_t);
+static int icmp6_opt_print(netdissect_options *ndo, const u_char *, int);
+static void mld6_print(netdissect_options *ndo, const u_char *);
+static void mldv2_report_print(netdissect_options *ndo, const u_char *, u_int);
+static void mldv2_query_print(netdissect_options *ndo, const u_char *, u_int);
+static const struct udphdr *get_upperlayer(netdissect_options *ndo, const u_char *, u_int *);
+static void dnsname_print(netdissect_options *ndo, const u_char *, const u_char *);
+static void icmp6_nodeinfo_print(netdissect_options *ndo, u_int, const u_char *, const u_char *);
+static void icmp6_rrenum_print(netdissect_options *ndo, const u_char *, const u_char *);
+
+#ifndef abs
+#define abs(a) ((0 < (a)) ? (a) : -(a))
+#endif
+
+/*
+ * DIO: Updated to RFC6550, as published in 2012: section 6. (page 30)
+ */
+
+#define ND_RPL_MESSAGE 155 /* 0x9B */
+
+enum ND_RPL_CODE {
+ ND_RPL_DAG_IS=0x00,
+ ND_RPL_DAG_IO=0x01,
+ ND_RPL_DAO =0x02,
+ ND_RPL_DAO_ACK=0x03,
+ ND_RPL_SEC_DAG_IS = 0x80,
+ ND_RPL_SEC_DAG_IO = 0x81,
+ ND_RPL_SEC_DAG = 0x82,
+ ND_RPL_SEC_DAG_ACK= 0x83,
+ ND_RPL_SEC_CONSIST= 0x8A
+};
+
+enum ND_RPL_DIO_FLAGS {
+ ND_RPL_DIO_GROUNDED = 0x80,
+ ND_RPL_DIO_DATRIG = 0x40,
+ ND_RPL_DIO_DASUPPORT= 0x20,
+ ND_RPL_DIO_RES4 = 0x10,
+ ND_RPL_DIO_RES3 = 0x08,
+ ND_RPL_DIO_PRF_MASK = 0x07 /* 3-bit preference */
+};
+
+#define DAGID_LEN 16
+
+/* section 6 of draft-ietf-roll-rpl-19 */
+struct nd_rpl_security {
+ nd_uint8_t rpl_sec_t_reserved; /* bit 7 is T-bit */
+ nd_uint8_t rpl_sec_algo;
+ nd_uint16_t rpl_sec_kim_lvl_flags; /* bit 15/14, KIM */
+ /* bit 10-8, LVL, bit 7-0 flags */
+ nd_uint32_t rpl_sec_counter;
+#if 0
+ nd_byte rpl_sec_ki[0]; /* depends upon kim */
+#endif
+};
+
+/* section 6.2.1, DODAG Information Solication (DIS_IS) */
+struct nd_rpl_dis_is {
+ nd_uint8_t rpl_dis_flags;
+ nd_uint8_t rpl_dis_reserved;
+#if 0
+ nd_byte rpl_dis_options[0];
+#endif
+};
+
+/* section 6.3.1, DODAG Information Object (DIO) */
+struct nd_rpl_dio {
+ nd_uint8_t rpl_instanceid;
+ nd_uint8_t rpl_version;
+ nd_uint16_t rpl_dagrank;
+ nd_uint8_t rpl_mopprf; /* bit 7=G, 5-3=MOP, 2-0=PRF */
+ nd_uint8_t rpl_dtsn; /* Dest. Advertisement Trigger Sequence Number */
+ nd_uint8_t rpl_flags; /* no flags defined yet */
+ nd_uint8_t rpl_resv1;
+ nd_byte rpl_dagid[DAGID_LEN];
+};
+#define RPL_DIO_GROUND_FLAG 0x80
+#define RPL_DIO_MOP_SHIFT 3
+#define RPL_DIO_MOP_MASK (7 << RPL_DIO_MOP_SHIFT)
+#define RPL_DIO_PRF_SHIFT 0
+#define RPL_DIO_PRF_MASK (7 << RPL_DIO_PRF_SHIFT)
+#define RPL_DIO_GROUNDED(X) ((X)&RPL_DIO_GROUND_FLAG)
+#define RPL_DIO_MOP(X) (enum RPL_DIO_MOP)(((X)&RPL_DIO_MOP_MASK) >> RPL_DIO_MOP_SHIFT)
+#define RPL_DIO_PRF(X) (((X)&RPL_DIO_PRF_MASK) >> RPL_DIO_PRF_SHIFT)
+
+enum RPL_DIO_MOP {
+ RPL_DIO_NONSTORING= 0x0,
+ RPL_DIO_STORING = 0x1,
+ RPL_DIO_NONSTORING_MULTICAST = 0x2,
+ RPL_DIO_STORING_MULTICAST = 0x3
+};
+
+enum RPL_SUBOPT {
+ RPL_OPT_PAD1 = 0,
+ RPL_OPT_PADN = 1,
+ RPL_DIO_METRICS = 2,
+ RPL_DIO_ROUTINGINFO = 3,
+ RPL_DIO_CONFIG = 4,
+ RPL_DAO_RPLTARGET = 5,
+ RPL_DAO_TRANSITINFO = 6,
+ RPL_DIO_DESTPREFIX = 8,
+ RPL_DAO_RPLTARGET_DESC=9
+};
+
+struct rpl_genoption {
+ nd_uint8_t rpl_dio_type;
+ nd_uint8_t rpl_dio_len; /* suboption length, not including type/len */
+};
+#define RPL_GENOPTION_LEN 2
+
+#define RPL_DIO_LIFETIME_INFINITE 0xffffffff
+#define RPL_DIO_LIFETIME_DISCONNECT 0
+
+struct rpl_dio_destprefix {
+ nd_uint8_t rpl_dio_type;
+ nd_uint8_t rpl_dio_len;
+ nd_uint8_t rpl_dio_prefixlen; /* in bits */
+ nd_uint8_t rpl_dio_prf; /* flags, including Route Preference */
+ nd_uint32_t rpl_dio_prefixlifetime; /* in seconds */
+#if 0
+ nd_byte rpl_dio_prefix[0]; /* variable number of bytes */
+#endif
+};
+
+/* section 6.4.1, DODAG Information Object (DIO) */
+struct nd_rpl_dao {
+ nd_uint8_t rpl_instanceid;
+ nd_uint8_t rpl_flags; /* bit 7=K, 6=D */
+ nd_uint8_t rpl_resv;
+ nd_uint8_t rpl_daoseq;
+ nd_byte rpl_dagid[DAGID_LEN]; /* present when D set. */
+};
+#define ND_RPL_DAO_MIN_LEN 4 /* length without DAGID */
+
+/* indicates if this DAO is to be acK'ed */
+#define RPL_DAO_K_SHIFT 7
+#define RPL_DAO_K_MASK (1 << RPL_DAO_K_SHIFT)
+#define RPL_DAO_K(X) (((X)&RPL_DAO_K_MASK) >> RPL_DAO_K_SHIFT)
+
+/* indicates if the DAGID is present */
+#define RPL_DAO_D_SHIFT 6
+#define RPL_DAO_D_MASK (1 << RPL_DAO_D_SHIFT)
+#define RPL_DAO_D(X) (((X)&RPL_DAO_D_MASK) >> RPL_DAO_D_SHIFT)
+
+struct rpl_dao_target {
+ nd_uint8_t rpl_dao_type;
+ nd_uint8_t rpl_dao_len;
+ nd_uint8_t rpl_dao_flags; /* unused */
+ nd_uint8_t rpl_dao_prefixlen; /* in bits */
+#if 0
+ nd_byte rpl_dao_prefix[0]; /* variable number of bytes */
+#endif
+};
+
+/* section 6.5.1, Destination Advertisement Object Acknowledgement (DAO-ACK) */
+struct nd_rpl_daoack {
+ nd_uint8_t rpl_instanceid;
+ nd_uint8_t rpl_flags; /* bit 7=D */
+ nd_uint8_t rpl_daoseq;
+ nd_uint8_t rpl_status;
+ nd_byte rpl_dagid[DAGID_LEN]; /* present when D set. */
+};
+#define ND_RPL_DAOACK_MIN_LEN 4 /* length without DAGID */
+/* indicates if the DAGID is present */
+#define RPL_DAOACK_D_SHIFT 7
+#define RPL_DAOACK_D_MASK (1 << RPL_DAOACK_D_SHIFT)
+#define RPL_DAOACK_D(X) (((X)&RPL_DAOACK_D_MASK) >> RPL_DAOACK_D_SHIFT)
+
+static const struct tok icmp6_type_values[] = {
+ { ICMP6_DST_UNREACH, "destination unreachable"},
+ { ICMP6_PACKET_TOO_BIG, "packet too big"},
+ { ICMP6_TIME_EXCEEDED, "time exceeded in-transit"},
+ { ICMP6_PARAM_PROB, "parameter problem"},
+ { ICMP6_ECHO_REQUEST, "echo request"},
+ { ICMP6_ECHO_REPLY, "echo reply"},
+ { MLD6_LISTENER_QUERY, "multicast listener query"},
+ { MLD6_LISTENER_REPORT, "multicast listener report"},
+ { MLD6_LISTENER_DONE, "multicast listener done"},
+ { ND_ROUTER_SOLICIT, "router solicitation"},
+ { ND_ROUTER_ADVERT, "router advertisement"},
+ { ND_NEIGHBOR_SOLICIT, "neighbor solicitation"},
+ { ND_NEIGHBOR_ADVERT, "neighbor advertisement"},
+ { ND_REDIRECT, "redirect"},
+ { ICMP6_ROUTER_RENUMBERING, "router renumbering"},
+ { IND_SOLICIT, "inverse neighbor solicitation"},
+ { IND_ADVERT, "inverse neighbor advertisement"},
+ { MLDV2_LISTENER_REPORT, "multicast listener report v2"},
+ { ICMP6_HADISCOV_REQUEST, "ha discovery request"},
+ { ICMP6_HADISCOV_REPLY, "ha discovery reply"},
+ { ICMP6_MOBILEPREFIX_SOLICIT, "mobile router solicitation"},
+ { ICMP6_MOBILEPREFIX_ADVERT, "mobile router advertisement"},
+ { ICMP6_WRUREQUEST, "who-are-you request"},
+ { ICMP6_WRUREPLY, "who-are-you reply"},
+ { ICMP6_NI_QUERY, "node information query"},
+ { ICMP6_NI_REPLY, "node information reply"},
+ { MLD6_MTRACE, "mtrace message"},
+ { MLD6_MTRACE_RESP, "mtrace response"},
+ { ND_RPL_MESSAGE, "RPL"},
+ { 0, NULL }
+};
+
+static const struct tok icmp6_dst_unreach_code_values[] = {
+ { ICMP6_DST_UNREACH_NOROUTE, "unreachable route" },
+ { ICMP6_DST_UNREACH_ADMIN, " unreachable prohibited"},
+ { ICMP6_DST_UNREACH_BEYONDSCOPE, "beyond scope"},
+ { ICMP6_DST_UNREACH_ADDR, "unreachable address"},
+ { ICMP6_DST_UNREACH_NOPORT, "unreachable port"},
+ { 0, NULL }
+};
+
+static const struct tok icmp6_opt_pi_flag_values[] = {
+ { ND_OPT_PI_FLAG_ONLINK, "onlink" },
+ { ND_OPT_PI_FLAG_AUTO, "auto" },
+ { ND_OPT_PI_FLAG_ROUTER, "router" },
+ { 0, NULL }
+};
+
+static const struct tok icmp6_opt_ra_flag_values[] = {
+ { ND_RA_FLAG_MANAGED, "managed" },
+ { ND_RA_FLAG_OTHER, "other stateful"},
+ { ND_RA_FLAG_HOME_AGENT, "home agent"},
+ { ND_RA_FLAG_IPV6ONLY, "ipv6 only"},
+ { 0, NULL }
+};
+
+static const struct tok icmp6_nd_na_flag_values[] = {
+ { ND_NA_FLAG_ROUTER, "router" },
+ { ND_NA_FLAG_SOLICITED, "solicited" },
+ { ND_NA_FLAG_OVERRIDE, "override" },
+ { 0, NULL }
+};
+
+
+static const struct tok icmp6_opt_values[] = {
+ { ND_OPT_SOURCE_LINKADDR, "source link-address"},
+ { ND_OPT_TARGET_LINKADDR, "destination link-address"},
+ { ND_OPT_PREFIX_INFORMATION, "prefix info"},
+ { ND_OPT_REDIRECTED_HEADER, "redirected header"},
+ { ND_OPT_MTU, "mtu"},
+ { ND_OPT_RDNSS, "rdnss"},
+ { ND_OPT_DNSSL, "dnssl"},
+ { ND_OPT_ADVINTERVAL, "advertisement interval"},
+ { ND_OPT_HOMEAGENT_INFO, "homeagent information"},
+ { ND_OPT_ROUTE_INFO, "route info"},
+ { 0, NULL }
+};
+
+/* mldv2 report types */
+static const struct tok mldv2report2str[] = {
+ { 1, "is_in" },
+ { 2, "is_ex" },
+ { 3, "to_in" },
+ { 4, "to_ex" },
+ { 5, "allow" },
+ { 6, "block" },
+ { 0, NULL }
+};
+
+static const char *
+get_rtpref(u_int v)
+{
+ static const char *rtpref_str[] = {
+ "medium", /* 00 */
+ "high", /* 01 */
+ "rsv", /* 10 */
+ "low" /* 11 */
+ };
+
+ return rtpref_str[((v & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff];
+}
+
+static const char *
+get_lifetime(uint32_t v)
+{
+ static char buf[20];
+
+ if (v == (uint32_t)~0UL)
+ return "infinity";
+ else {
+ snprintf(buf, sizeof(buf), "%us", v);
+ return buf;
+ }
+}
+
+static void
+print_lladdr(netdissect_options *ndo, const uint8_t *p, size_t l)
+{
+ const uint8_t *ep, *q;
+
+ q = p;
+ ep = p + l;
+ while (l > 0 && q < ep) {
+ if (q > p)
+ ND_PRINT(":");
+ ND_PRINT("%02x", GET_U_1(q));
+ q++;
+ l--;
+ }
+}
+
+static uint16_t icmp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
+ const struct icmp6_hdr *icp, u_int len)
+{
+ return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)icp, len, len,
+ IPPROTO_ICMPV6);
+}
+
+static const struct tok rpl_mop_values[] = {
+ { RPL_DIO_NONSTORING, "nonstoring"},
+ { RPL_DIO_STORING, "storing"},
+ { RPL_DIO_NONSTORING_MULTICAST, "nonstoring-multicast"},
+ { RPL_DIO_STORING_MULTICAST, "storing-multicast"},
+ { 0, NULL},
+};
+
+static const struct tok rpl_subopt_values[] = {
+ { RPL_OPT_PAD1, "pad1"},
+ { RPL_OPT_PADN, "padN"},
+ { RPL_DIO_METRICS, "metrics"},
+ { RPL_DIO_ROUTINGINFO, "routinginfo"},
+ { RPL_DIO_CONFIG, "config"},
+ { RPL_DAO_RPLTARGET, "rpltarget"},
+ { RPL_DAO_TRANSITINFO, "transitinfo"},
+ { RPL_DIO_DESTPREFIX, "destprefix"},
+ { RPL_DAO_RPLTARGET_DESC, "rpltargetdesc"},
+ { 0, NULL},
+};
+
+static void
+rpl_printopts(netdissect_options *ndo, const uint8_t *opts, u_int length)
+{
+ const struct rpl_genoption *opt;
+ uint8_t dio_type;
+ u_int optlen;
+
+ while (length != 0) {
+ opt = (const struct rpl_genoption *)opts;
+ dio_type = GET_U_1(opt->rpl_dio_type);
+ if (dio_type == RPL_OPT_PAD1) {
+ optlen = 1;
+ ND_PRINT(" opt:pad1");
+ } else {
+ if (length < RPL_GENOPTION_LEN)
+ goto trunc;
+ optlen = GET_U_1(opt->rpl_dio_len)+RPL_GENOPTION_LEN;
+ ND_PRINT(" opt:%s len:%u ",
+ tok2str(rpl_subopt_values, "subopt:%u", dio_type),
+ optlen);
+ ND_TCHECK_LEN(opt, optlen);
+ if (length < optlen)
+ goto trunc;
+ if (ndo->ndo_vflag > 2) {
+ hex_print(ndo,
+ " ",
+ opts + RPL_GENOPTION_LEN, /* content of DIO option */
+ optlen - RPL_GENOPTION_LEN);
+ }
+ }
+ opts += optlen;
+ length -= optlen;
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+rpl_dio_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const struct nd_rpl_dio *dio = (const struct nd_rpl_dio *)bp;
+ const char *dagid_str;
+
+ ND_TCHECK_SIZE(dio);
+ dagid_str = ip6addr_string (ndo, dio->rpl_dagid);
+
+ ND_PRINT(" [dagid:%s,seq:%u,instance:%u,rank:%u,%smop:%s,prf:%u]",
+ dagid_str,
+ GET_U_1(dio->rpl_dtsn),
+ GET_U_1(dio->rpl_instanceid),
+ GET_BE_U_2(dio->rpl_dagrank),
+ RPL_DIO_GROUNDED(GET_U_1(dio->rpl_mopprf)) ? "grounded,":"",
+ tok2str(rpl_mop_values, "mop%u", RPL_DIO_MOP(GET_U_1(dio->rpl_mopprf))),
+ RPL_DIO_PRF(GET_U_1(dio->rpl_mopprf)));
+
+ if(ndo->ndo_vflag > 1) {
+ rpl_printopts(ndo, bp + sizeof(struct nd_rpl_dio),
+ length - sizeof(struct nd_rpl_dio));
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+rpl_dao_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const struct nd_rpl_dao *dao = (const struct nd_rpl_dao *)bp;
+ const char *dagid_str = "<elided>";
+ uint8_t rpl_flags;
+
+ ND_TCHECK_SIZE(dao);
+ if (length < ND_RPL_DAO_MIN_LEN)
+ goto tooshort;
+
+ bp += ND_RPL_DAO_MIN_LEN;
+ length -= ND_RPL_DAO_MIN_LEN;
+ rpl_flags = GET_U_1(dao->rpl_flags);
+ if(RPL_DAO_D(rpl_flags)) {
+ ND_TCHECK_LEN(dao->rpl_dagid, DAGID_LEN);
+ if (length < DAGID_LEN)
+ goto tooshort;
+ dagid_str = ip6addr_string (ndo, dao->rpl_dagid);
+ bp += DAGID_LEN;
+ length -= DAGID_LEN;
+ }
+
+ ND_PRINT(" [dagid:%s,seq:%u,instance:%u%s%s,%02x]",
+ dagid_str,
+ GET_U_1(dao->rpl_daoseq),
+ GET_U_1(dao->rpl_instanceid),
+ RPL_DAO_K(rpl_flags) ? ",acK":"",
+ RPL_DAO_D(rpl_flags) ? ",Dagid":"",
+ rpl_flags);
+
+ if(ndo->ndo_vflag > 1) {
+ rpl_printopts(ndo, bp, length);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+ return;
+
+tooshort:
+ ND_PRINT(" [|length too short]");
+}
+
+static void
+rpl_daoack_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const struct nd_rpl_daoack *daoack = (const struct nd_rpl_daoack *)bp;
+ const char *dagid_str = "<elided>";
+
+ ND_TCHECK_LEN(daoack, ND_RPL_DAOACK_MIN_LEN);
+ if (length < ND_RPL_DAOACK_MIN_LEN)
+ goto tooshort;
+
+ bp += ND_RPL_DAOACK_MIN_LEN;
+ length -= ND_RPL_DAOACK_MIN_LEN;
+ if(RPL_DAOACK_D(GET_U_1(daoack->rpl_flags))) {
+ ND_TCHECK_LEN(daoack->rpl_dagid, DAGID_LEN);
+ if (length < DAGID_LEN)
+ goto tooshort;
+ dagid_str = ip6addr_string (ndo, daoack->rpl_dagid);
+ bp += DAGID_LEN;
+ length -= DAGID_LEN;
+ }
+
+ ND_PRINT(" [dagid:%s,seq:%u,instance:%u,status:%u]",
+ dagid_str,
+ GET_U_1(daoack->rpl_daoseq),
+ GET_U_1(daoack->rpl_instanceid),
+ GET_U_1(daoack->rpl_status));
+
+ /* no officially defined options for DAOACK, but print any we find */
+ if(ndo->ndo_vflag > 1) {
+ rpl_printopts(ndo, bp, length);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+ return;
+
+tooshort:
+ ND_PRINT(" [|dao-length too short]");
+}
+
+static void
+rpl_print(netdissect_options *ndo,
+ uint8_t icmp6_code,
+ const u_char *bp, u_int length)
+{
+ int secured = icmp6_code & 0x80;
+ int basecode= icmp6_code & 0x7f;
+
+ if(secured) {
+ ND_PRINT(", (SEC) [worktodo]");
+ /* XXX
+ * the next header pointer needs to move forward to
+ * skip the secure part.
+ */
+ return;
+ } else {
+ ND_PRINT(", (CLR)");
+ }
+
+ switch(basecode) {
+ case ND_RPL_DAG_IS:
+ ND_PRINT("DODAG Information Solicitation");
+ if(ndo->ndo_vflag) {
+ }
+ break;
+ case ND_RPL_DAG_IO:
+ ND_PRINT("DODAG Information Object");
+ if(ndo->ndo_vflag) {
+ rpl_dio_print(ndo, bp, length);
+ }
+ break;
+ case ND_RPL_DAO:
+ ND_PRINT("Destination Advertisement Object");
+ if(ndo->ndo_vflag) {
+ rpl_dao_print(ndo, bp, length);
+ }
+ break;
+ case ND_RPL_DAO_ACK:
+ ND_PRINT("Destination Advertisement Object Ack");
+ if(ndo->ndo_vflag) {
+ rpl_daoack_print(ndo, bp, length);
+ }
+ break;
+ default:
+ ND_PRINT("RPL message, unknown code %u",icmp6_code);
+ break;
+ }
+ return;
+
+#if 0
+trunc:
+ nd_print_trunc(ndo);
+ return;
+#endif
+
+}
+
+
+void
+icmp6_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, const u_char *bp2, int fragmented)
+{
+ const struct icmp6_hdr *dp;
+ uint8_t icmp6_type, icmp6_code;
+ const struct ip6_hdr *ip;
+ const struct ip6_hdr *oip;
+ const struct udphdr *ouh;
+ uint16_t dport;
+ const u_char *ep;
+ u_int prot;
+
+ ndo->ndo_protocol = "icmp6";
+ dp = (const struct icmp6_hdr *)bp;
+ ip = (const struct ip6_hdr *)bp2;
+ oip = (const struct ip6_hdr *)(dp + 1);
+ /* 'ep' points to the end of available data. */
+ ep = ndo->ndo_snapend;
+ if (length == 0) {
+ ND_PRINT("ICMP6, length 0");
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ if (ndo->ndo_vflag && !fragmented) {
+ uint16_t sum, udp_sum;
+
+ if (ND_TTEST_LEN(bp, length)) {
+ udp_sum = GET_BE_U_2(dp->icmp6_cksum);
+ sum = icmp6_cksum(ndo, ip, dp, length);
+ if (sum != 0)
+ ND_PRINT("[bad icmp6 cksum 0x%04x -> 0x%04x!] ",
+ udp_sum,
+ in_cksum_shouldbe(udp_sum, sum));
+ else
+ ND_PRINT("[icmp6 sum ok] ");
+ }
+ }
+
+ icmp6_type = GET_U_1(dp->icmp6_type);
+ ND_PRINT("ICMP6, %s", tok2str(icmp6_type_values,"unknown icmp6 type (%u)",icmp6_type));
+
+ /* display cosmetics: print the packet length for printer that use the vflag now */
+ if (ndo->ndo_vflag && (icmp6_type == ND_ROUTER_SOLICIT ||
+ icmp6_type == ND_ROUTER_ADVERT ||
+ icmp6_type == ND_NEIGHBOR_ADVERT ||
+ icmp6_type == ND_NEIGHBOR_SOLICIT ||
+ icmp6_type == ND_REDIRECT ||
+ icmp6_type == ICMP6_HADISCOV_REPLY ||
+ icmp6_type == ICMP6_MOBILEPREFIX_ADVERT ))
+ ND_PRINT(", length %u", length);
+
+ icmp6_code = GET_U_1(dp->icmp6_code);
+
+ switch (icmp6_type) {
+ case ICMP6_DST_UNREACH:
+ ND_PRINT(", %s", tok2str(icmp6_dst_unreach_code_values,"unknown unreach code (%u)",icmp6_code));
+ switch (icmp6_code) {
+
+ case ICMP6_DST_UNREACH_NOROUTE: /* fall through */
+ case ICMP6_DST_UNREACH_ADMIN:
+ case ICMP6_DST_UNREACH_ADDR:
+ ND_PRINT(" %s",GET_IP6ADDR_STRING(oip->ip6_dst));
+ break;
+ case ICMP6_DST_UNREACH_BEYONDSCOPE:
+ ND_PRINT(" %s, source address %s",
+ GET_IP6ADDR_STRING(oip->ip6_dst),
+ GET_IP6ADDR_STRING(oip->ip6_src));
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ if ((ouh = get_upperlayer(ndo, (const u_char *)oip, &prot))
+ == NULL)
+ goto trunc;
+
+ dport = GET_BE_U_2(ouh->uh_dport);
+ switch (prot) {
+ case IPPROTO_TCP:
+ ND_PRINT(", %s tcp port %s",
+ GET_IP6ADDR_STRING(oip->ip6_dst),
+ tcpport_string(ndo, dport));
+ break;
+ case IPPROTO_UDP:
+ ND_PRINT(", %s udp port %s",
+ GET_IP6ADDR_STRING(oip->ip6_dst),
+ udpport_string(ndo, dport));
+ break;
+ default:
+ ND_PRINT(", %s protocol %u port %u unreachable",
+ GET_IP6ADDR_STRING(oip->ip6_dst),
+ prot, dport);
+ break;
+ }
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ print_unknown_data(ndo, bp,"\n\t",length);
+ return;
+ }
+ break;
+ }
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ ND_PRINT(", mtu %u", GET_BE_U_4(dp->icmp6_mtu));
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ switch (icmp6_code) {
+ case ICMP6_TIME_EXCEED_TRANSIT:
+ ND_PRINT(" for %s",
+ GET_IP6ADDR_STRING(oip->ip6_dst));
+ break;
+ case ICMP6_TIME_EXCEED_REASSEMBLY:
+ ND_PRINT(" (reassembly)");
+ break;
+ default:
+ ND_PRINT(", unknown code (%u)", icmp6_code);
+ break;
+ }
+ break;
+ case ICMP6_PARAM_PROB:
+ ND_TCHECK_16(oip->ip6_dst);
+ switch (icmp6_code) {
+ case ICMP6_PARAMPROB_HEADER:
+ ND_PRINT(", erroneous - octet %u",
+ GET_BE_U_4(dp->icmp6_pptr));
+ break;
+ case ICMP6_PARAMPROB_NEXTHEADER:
+ ND_PRINT(", next header - octet %u",
+ GET_BE_U_4(dp->icmp6_pptr));
+ break;
+ case ICMP6_PARAMPROB_OPTION:
+ ND_PRINT(", option - octet %u",
+ GET_BE_U_4(dp->icmp6_pptr));
+ break;
+ case ICMP6_PARAMPROB_FRAGHDRCHAIN:
+ ND_PRINT(", incomplete header chain - octet %u",
+ GET_BE_U_4(dp->icmp6_pptr));
+ break;
+ default:
+ ND_PRINT(", code-#%u",
+ icmp6_code);
+ break;
+ }
+ break;
+ case ICMP6_ECHO_REQUEST:
+ case ICMP6_ECHO_REPLY:
+ ND_PRINT(", id %u, seq %u", GET_BE_U_2(dp->icmp6_id),
+ GET_BE_U_2(dp->icmp6_seq));
+ break;
+ case ICMP6_MEMBERSHIP_QUERY:
+ if (length == MLD_MINLEN) {
+ mld6_print(ndo, (const u_char *)dp);
+ } else if (length >= MLDV2_MINLEN) {
+ ND_PRINT(" v2");
+ mldv2_query_print(ndo, (const u_char *)dp, length);
+ } else {
+ ND_PRINT(" unknown-version (len %u) ", length);
+ }
+ break;
+ case ICMP6_MEMBERSHIP_REPORT:
+ mld6_print(ndo, (const u_char *)dp);
+ break;
+ case ICMP6_MEMBERSHIP_REDUCTION:
+ mld6_print(ndo, (const u_char *)dp);
+ break;
+ case ND_ROUTER_SOLICIT:
+#define RTSOLLEN 8
+ if (ndo->ndo_vflag) {
+ if (icmp6_opt_print(ndo, (const u_char *)dp + RTSOLLEN,
+ length - RTSOLLEN) == -1)
+ goto trunc;
+ }
+ break;
+ case ND_ROUTER_ADVERT:
+#define RTADVLEN 16
+ if (ndo->ndo_vflag) {
+ const struct nd_router_advert *p;
+
+ p = (const struct nd_router_advert *)dp;
+ ND_PRINT("\n\thop limit %u, Flags [%s]"
+ ", pref %s, router lifetime %us, reachable time %ums, retrans timer %ums",
+ GET_U_1(p->nd_ra_curhoplimit),
+ bittok2str(icmp6_opt_ra_flag_values,"none",GET_U_1(p->nd_ra_flags_reserved)),
+ get_rtpref(GET_U_1(p->nd_ra_flags_reserved)),
+ GET_BE_U_2(p->nd_ra_router_lifetime),
+ GET_BE_U_4(p->nd_ra_reachable),
+ GET_BE_U_4(p->nd_ra_retransmit));
+
+ if (icmp6_opt_print(ndo, (const u_char *)dp + RTADVLEN,
+ length - RTADVLEN) == -1)
+ goto trunc;
+ }
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ {
+ const struct nd_neighbor_solicit *p;
+ p = (const struct nd_neighbor_solicit *)dp;
+ ND_PRINT(", who has %s", GET_IP6ADDR_STRING(p->nd_ns_target));
+ if (ndo->ndo_vflag) {
+#define NDSOLLEN 24
+ if (icmp6_opt_print(ndo, (const u_char *)dp + NDSOLLEN,
+ length - NDSOLLEN) == -1)
+ goto trunc;
+ }
+ }
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ {
+ const struct nd_neighbor_advert *p;
+
+ p = (const struct nd_neighbor_advert *)dp;
+ ND_PRINT(", tgt is %s",
+ GET_IP6ADDR_STRING(p->nd_na_target));
+ if (ndo->ndo_vflag) {
+ ND_PRINT(", Flags [%s]",
+ bittok2str(icmp6_nd_na_flag_values,
+ "none",
+ GET_BE_U_4(p->nd_na_flags_reserved)));
+#define NDADVLEN 24
+ if (icmp6_opt_print(ndo, (const u_char *)dp + NDADVLEN,
+ length - NDADVLEN) == -1)
+ goto trunc;
+#undef NDADVLEN
+ }
+ }
+ break;
+ case ND_REDIRECT:
+ {
+ const struct nd_redirect *p;
+
+ p = (const struct nd_redirect *)dp;
+ ND_PRINT(", %s", GET_IP6ADDR_STRING(p->nd_rd_dst));
+ ND_PRINT(" to %s", GET_IP6ADDR_STRING(p->nd_rd_target));
+#define REDIRECTLEN 40
+ if (ndo->ndo_vflag) {
+ if (icmp6_opt_print(ndo, (const u_char *)dp + REDIRECTLEN,
+ length - REDIRECTLEN) == -1)
+ goto trunc;
+#undef REDIRECTLEN
+ }
+ }
+ break;
+ case ICMP6_ROUTER_RENUMBERING:
+ icmp6_rrenum_print(ndo, bp, ep);
+ break;
+ case ICMP6_NI_QUERY:
+ case ICMP6_NI_REPLY:
+ icmp6_nodeinfo_print(ndo, length, bp, ep);
+ break;
+ case IND_SOLICIT:
+ case IND_ADVERT:
+ break;
+ case ICMP6_V2_MEMBERSHIP_REPORT:
+ mldv2_report_print(ndo, (const u_char *) dp, length);
+ break;
+ case ICMP6_MOBILEPREFIX_SOLICIT: /* fall through */
+ case ICMP6_HADISCOV_REQUEST:
+ ND_PRINT(", id 0x%04x", GET_BE_U_2(dp->icmp6_data16[0]));
+ break;
+ case ICMP6_HADISCOV_REPLY:
+ if (ndo->ndo_vflag) {
+ const u_char *cp;
+ const u_char *p;
+
+ ND_PRINT(", id 0x%04x",
+ GET_BE_U_2(dp->icmp6_data16[0]));
+ cp = (const u_char *)dp + length;
+ p = (const u_char *)(dp + 1);
+ while (p < cp) {
+ ND_PRINT(", %s", GET_IP6ADDR_STRING(p));
+ p += 16;
+ }
+ }
+ break;
+ case ICMP6_MOBILEPREFIX_ADVERT:
+ if (ndo->ndo_vflag) {
+ uint16_t flags;
+
+ ND_PRINT(", id 0x%04x",
+ GET_BE_U_2(dp->icmp6_data16[0]));
+ flags = GET_BE_U_2(dp->icmp6_data16[1]);
+ if (flags & 0xc000)
+ ND_PRINT(" ");
+ if (flags & 0x8000)
+ ND_PRINT("M");
+ if (flags & 0x4000)
+ ND_PRINT("O");
+#define MPADVLEN 8
+ if (icmp6_opt_print(ndo, (const u_char *)dp + MPADVLEN,
+ length - MPADVLEN) == -1)
+ goto trunc;
+ }
+ break;
+ case ND_RPL_MESSAGE:
+ /* plus 4, because struct icmp6_hdr contains 4 bytes of icmp payload */
+ rpl_print(ndo, icmp6_code, dp->icmp6_data, length-sizeof(struct icmp6_hdr)+4);
+ break;
+ default:
+ ND_PRINT(", length %u", length);
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, bp,"\n\t", length);
+ return;
+ }
+ if (!ndo->ndo_vflag)
+ ND_PRINT(", length %u", length);
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static const struct udphdr *
+get_upperlayer(netdissect_options *ndo, const u_char *bp, u_int *prot)
+{
+ const u_char *ep;
+ const struct ip6_hdr *ip6 = (const struct ip6_hdr *)bp;
+ const struct udphdr *uh;
+ const struct ip6_hbh *hbh;
+ const struct ip6_frag *fragh;
+ const struct ah *ah;
+ u_int nh;
+ int hlen;
+
+ /* 'ep' points to the end of available data. */
+ ep = ndo->ndo_snapend;
+
+ if (!ND_TTEST_1(ip6->ip6_nxt))
+ return NULL;
+
+ nh = GET_U_1(ip6->ip6_nxt);
+ hlen = sizeof(struct ip6_hdr);
+
+ while (bp < ep) {
+ bp += hlen;
+
+ switch(nh) {
+ case IPPROTO_UDP:
+ case IPPROTO_TCP:
+ uh = (const struct udphdr *)bp;
+ if (ND_TTEST_2(uh->uh_dport)) {
+ *prot = nh;
+ return(uh);
+ }
+ else
+ return(NULL);
+ /* NOTREACHED */
+
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_ROUTING:
+ hbh = (const struct ip6_hbh *)bp;
+ if (!ND_TTEST_1(hbh->ip6h_len))
+ return(NULL);
+ nh = GET_U_1(hbh->ip6h_nxt);
+ hlen = (GET_U_1(hbh->ip6h_len) + 1) << 3;
+ break;
+
+ case IPPROTO_FRAGMENT: /* this should be odd, but try anyway */
+ fragh = (const struct ip6_frag *)bp;
+ if (!ND_TTEST_2(fragh->ip6f_offlg))
+ return(NULL);
+ /* fragments with non-zero offset are meaningless */
+ if ((GET_BE_U_2(fragh->ip6f_offlg) & IP6F_OFF_MASK) != 0)
+ return(NULL);
+ nh = GET_U_1(fragh->ip6f_nxt);
+ hlen = sizeof(struct ip6_frag);
+ break;
+
+ case IPPROTO_AH:
+ ah = (const struct ah *)bp;
+ if (!ND_TTEST_1(ah->ah_len))
+ return(NULL);
+ nh = GET_U_1(ah->ah_nxt);
+ hlen = (GET_U_1(ah->ah_len) + 2) << 2;
+ break;
+
+ default: /* unknown or undecodable header */
+ *prot = nh; /* meaningless, but set here anyway */
+ return(NULL);
+ }
+ }
+
+ return(NULL); /* should be notreached, though */
+}
+
+static int
+icmp6_opt_print(netdissect_options *ndo, const u_char *bp, int resid)
+{
+ const struct nd_opt_hdr *op;
+ uint8_t opt_type;
+ u_int opt_len;
+ const struct nd_opt_prefix_info *opp;
+ const struct nd_opt_mtu *opm;
+ const struct nd_opt_rdnss *oprd;
+ const struct nd_opt_dnssl *opds;
+ const struct nd_opt_advinterval *opa;
+ const struct nd_opt_homeagent_info *oph;
+ const struct nd_opt_route_info *opri;
+ const u_char *cp, *ep, *domp;
+ nd_ipv6 in6;
+ size_t l;
+ u_int i;
+
+ cp = bp;
+ /* 'ep' points to the end of available data. */
+ ep = ndo->ndo_snapend;
+
+ while (cp < ep) {
+ op = (const struct nd_opt_hdr *)cp;
+
+ ND_TCHECK_1(op->nd_opt_len);
+ if (resid <= 0)
+ return 0;
+ opt_type = GET_U_1(op->nd_opt_type);
+ opt_len = GET_U_1(op->nd_opt_len);
+ if (opt_len == 0)
+ goto trunc;
+ if (cp + (opt_len << 3) > ep)
+ goto trunc;
+
+ ND_PRINT("\n\t %s option (%u), length %u (%u): ",
+ tok2str(icmp6_opt_values, "unknown", opt_type),
+ opt_type,
+ opt_len << 3,
+ opt_len);
+
+ switch (opt_type) {
+ case ND_OPT_SOURCE_LINKADDR:
+ l = (opt_len << 3) - 2;
+ print_lladdr(ndo, cp + 2, l);
+ break;
+ case ND_OPT_TARGET_LINKADDR:
+ l = (opt_len << 3) - 2;
+ print_lladdr(ndo, cp + 2, l);
+ break;
+ case ND_OPT_PREFIX_INFORMATION:
+ opp = (const struct nd_opt_prefix_info *)op;
+ ND_PRINT("%s/%u%s, Flags [%s], valid time %s",
+ GET_IP6ADDR_STRING(opp->nd_opt_pi_prefix),
+ GET_U_1(opp->nd_opt_pi_prefix_len),
+ (opt_len != 4) ? "badlen" : "",
+ bittok2str(icmp6_opt_pi_flag_values, "none", GET_U_1(opp->nd_opt_pi_flags_reserved)),
+ get_lifetime(GET_BE_U_4(opp->nd_opt_pi_valid_time)));
+ ND_PRINT(", pref. time %s",
+ get_lifetime(GET_BE_U_4(opp->nd_opt_pi_preferred_time)));
+ break;
+ case ND_OPT_REDIRECTED_HEADER:
+ print_unknown_data(ndo, bp,"\n\t ",opt_len<<3);
+ /* xxx */
+ break;
+ case ND_OPT_MTU:
+ opm = (const struct nd_opt_mtu *)op;
+ ND_PRINT(" %u%s",
+ GET_BE_U_4(opm->nd_opt_mtu_mtu),
+ (opt_len != 1) ? "bad option length" : "" );
+ break;
+ case ND_OPT_RDNSS:
+ oprd = (const struct nd_opt_rdnss *)op;
+ l = (opt_len - 1) / 2;
+ ND_PRINT(" lifetime %us,",
+ GET_BE_U_4(oprd->nd_opt_rdnss_lifetime));
+ for (i = 0; i < l; i++) {
+ ND_PRINT(" addr: %s",
+ GET_IP6ADDR_STRING(oprd->nd_opt_rdnss_addr[i]));
+ }
+ break;
+ case ND_OPT_DNSSL:
+ opds = (const struct nd_opt_dnssl *)op;
+ ND_PRINT(" lifetime %us, domain(s):",
+ GET_BE_U_4(opds->nd_opt_dnssl_lifetime));
+ domp = cp + 8; /* domain names, variable-sized, RFC1035-encoded */
+ while (domp < cp + (opt_len << 3) && GET_U_1(domp) != '\0')
+ {
+ ND_PRINT(" ");
+ if ((domp = fqdn_print(ndo, domp, bp)) == NULL)
+ goto trunc;
+ }
+ break;
+ case ND_OPT_ADVINTERVAL:
+ opa = (const struct nd_opt_advinterval *)op;
+ ND_PRINT(" %ums",
+ GET_BE_U_4(opa->nd_opt_adv_interval));
+ break;
+ case ND_OPT_HOMEAGENT_INFO:
+ oph = (const struct nd_opt_homeagent_info *)op;
+ ND_PRINT(" preference %u, lifetime %u",
+ GET_BE_U_2(oph->nd_opt_hai_preference),
+ GET_BE_U_2(oph->nd_opt_hai_lifetime));
+ break;
+ case ND_OPT_ROUTE_INFO:
+ opri = (const struct nd_opt_route_info *)op;
+ ND_TCHECK_4(opri->nd_opt_rti_lifetime);
+ memset(&in6, 0, sizeof(in6));
+ switch (opt_len) {
+ case 1:
+ break;
+ case 2:
+ ND_TCHECK_8(opri + 1);
+ memcpy(&in6, opri + 1, 8);
+ break;
+ case 3:
+ ND_TCHECK_16(opri + 1);
+ memcpy(&in6, opri + 1, 16);
+ break;
+ default:
+ goto trunc;
+ }
+ ND_PRINT(" %s/%u", ip6addr_string(ndo, (const u_char *)&in6),
+ GET_U_1(opri->nd_opt_rti_prefixlen));
+ ND_PRINT(", pref=%s",
+ get_rtpref(GET_U_1(opri->nd_opt_rti_flags)));
+ ND_PRINT(", lifetime=%s",
+ get_lifetime(GET_BE_U_4(opri->nd_opt_rti_lifetime)));
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ print_unknown_data(ndo,cp+2,"\n\t ", (opt_len << 3) - 2); /* skip option header */
+ return 0;
+ }
+ break;
+ }
+ /* do we want to see an additional hexdump ? */
+ if (ndo->ndo_vflag> 1)
+ print_unknown_data(ndo, cp+2,"\n\t ", (opt_len << 3) - 2); /* skip option header */
+
+ cp += opt_len << 3;
+ resid -= opt_len << 3;
+ }
+ return 0;
+
+trunc:
+ return -1;
+}
+
+static void
+mld6_print(netdissect_options *ndo, const u_char *bp)
+{
+ const struct mld6_hdr *mp = (const struct mld6_hdr *)bp;
+ const u_char *ep;
+
+ /* 'ep' points to the end of available data. */
+ ep = ndo->ndo_snapend;
+
+ if ((const u_char *)mp + sizeof(*mp) > ep)
+ return;
+
+ ND_PRINT("max resp delay: %u ", GET_BE_U_2(mp->mld6_maxdelay));
+ ND_PRINT("addr: %s", GET_IP6ADDR_STRING(mp->mld6_addr));
+}
+
+static void
+mldv2_report_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ const struct icmp6_hdr *icp = (const struct icmp6_hdr *) bp;
+ u_int group, nsrcs, ngroups;
+ u_int i, j;
+
+ /* Minimum len is 8 */
+ if (len < 8) {
+ ND_PRINT(" [invalid len %u]", len);
+ return;
+ }
+
+ ngroups = GET_BE_U_2(icp->icmp6_data16[1]);
+ ND_PRINT(", %u group record(s)", ngroups);
+ if (ndo->ndo_vflag > 0) {
+ /* Print the group records */
+ group = 8;
+ for (i = 0; i < ngroups; i++) {
+ /* type(1) + auxlen(1) + numsrc(2) + grp(16) */
+ if (len < group + 20) {
+ ND_PRINT(" [invalid number of groups]");
+ return;
+ }
+ ND_PRINT(" [gaddr %s", GET_IP6ADDR_STRING(bp + group + 4));
+ ND_PRINT(" %s", tok2str(mldv2report2str, " [v2-report-#%u]",
+ GET_U_1(bp + group)));
+ nsrcs = GET_BE_U_2(bp + group + 2);
+ /* Check the number of sources and print them */
+ if (len < group + 20 + (nsrcs * sizeof(nd_ipv6))) {
+ ND_PRINT(" [invalid number of sources %u]", nsrcs);
+ return;
+ }
+ if (ndo->ndo_vflag == 1)
+ ND_PRINT(", %u source(s)", nsrcs);
+ else {
+ /* Print the sources */
+ ND_PRINT(" {");
+ for (j = 0; j < nsrcs; j++) {
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(bp + group + 20 + (j * sizeof(nd_ipv6))));
+ }
+ ND_PRINT(" }");
+ }
+ /* Next group record */
+ group += 20 + nsrcs * sizeof(nd_ipv6);
+ ND_PRINT("]");
+ }
+ }
+}
+
+static void
+mldv2_query_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ const struct icmp6_hdr *icp = (const struct icmp6_hdr *) bp;
+ u_int mrc;
+ u_int mrt, qqi;
+ u_int nsrcs;
+ u_int i;
+
+ /* Minimum len is 28 */
+ if (len < 28) {
+ ND_PRINT(" [invalid len %u]", len);
+ return;
+ }
+ mrc = GET_BE_U_2(icp->icmp6_data16[0]);
+ if (mrc < 32768) {
+ mrt = mrc;
+ } else {
+ mrt = ((mrc & 0x0fff) | 0x1000) << (((mrc & 0x7000) >> 12) + 3);
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" [max resp delay=%u]", mrt);
+ }
+ ND_PRINT(" [gaddr %s", GET_IP6ADDR_STRING(bp + 8));
+
+ if (ndo->ndo_vflag) {
+ if (GET_U_1(bp + 24) & 0x08) {
+ ND_PRINT(" sflag");
+ }
+ if (GET_U_1(bp + 24) & 0x07) {
+ ND_PRINT(" robustness=%u", GET_U_1(bp + 24) & 0x07);
+ }
+ if (GET_U_1(bp + 25) < 128) {
+ qqi = GET_U_1(bp + 25);
+ } else {
+ qqi = ((GET_U_1(bp + 25) & 0x0f) | 0x10) <<
+ (((GET_U_1(bp + 25) & 0x70) >> 4) + 3);
+ }
+ ND_PRINT(" qqi=%u", qqi);
+ }
+
+ nsrcs = GET_BE_U_2(bp + 26);
+ if (nsrcs > 0) {
+ if (len < 28 + nsrcs * sizeof(nd_ipv6))
+ ND_PRINT(" [invalid number of sources]");
+ else if (ndo->ndo_vflag > 1) {
+ ND_PRINT(" {");
+ for (i = 0; i < nsrcs; i++) {
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(bp + 28 + (i * sizeof(nd_ipv6))));
+ }
+ ND_PRINT(" }");
+ } else
+ ND_PRINT(", %u source(s)", nsrcs);
+ }
+ ND_PRINT("]");
+}
+
+static void
+dnsname_print(netdissect_options *ndo, const u_char *cp, const u_char *ep)
+{
+ int i;
+
+ /* DNS name decoding - no decompression */
+ ND_PRINT(", \"");
+ while (cp < ep) {
+ i = GET_U_1(cp);
+ cp++;
+ if (i) {
+ if (i > ep - cp) {
+ ND_PRINT("???");
+ break;
+ }
+ while (i-- && cp < ep) {
+ fn_print_char(ndo, GET_U_1(cp));
+ cp++;
+ }
+ if (cp + 1 < ep && GET_U_1(cp))
+ ND_PRINT(".");
+ } else {
+ if (cp == ep) {
+ /* FQDN */
+ ND_PRINT(".");
+ } else if (cp + 1 == ep && GET_U_1(cp) == '\0') {
+ /* truncated */
+ } else {
+ /* invalid */
+ ND_PRINT("???");
+ }
+ break;
+ }
+ }
+ ND_PRINT("\"");
+}
+
+static void
+icmp6_nodeinfo_print(netdissect_options *ndo, u_int icmp6len, const u_char *bp, const u_char *ep)
+{
+ const struct icmp6_nodeinfo *ni6;
+ const struct icmp6_hdr *dp;
+ const u_char *cp;
+ size_t siz, i;
+ int needcomma;
+
+ if (ep < bp)
+ return;
+ dp = (const struct icmp6_hdr *)bp;
+ ni6 = (const struct icmp6_nodeinfo *)bp;
+ siz = ep - bp;
+
+ switch (GET_U_1(ni6->ni_type)) {
+ case ICMP6_NI_QUERY:
+ if (siz == sizeof(*dp) + 4) {
+ /* KAME who-are-you */
+ ND_PRINT(" who-are-you request");
+ break;
+ }
+ ND_PRINT(" node information query");
+
+ ND_TCHECK_LEN(dp, sizeof(*ni6));
+ ni6 = (const struct icmp6_nodeinfo *)dp;
+ ND_PRINT(" ("); /*)*/
+ switch (GET_BE_U_2(ni6->ni_qtype)) {
+ case NI_QTYPE_NOOP:
+ ND_PRINT("noop");
+ break;
+ case NI_QTYPE_SUPTYPES:
+ ND_PRINT("supported qtypes");
+ i = GET_BE_U_2(ni6->ni_flags);
+ if (i)
+ ND_PRINT(" [%s]", (i & 0x01) ? "C" : "");
+ break;
+ case NI_QTYPE_FQDN:
+ ND_PRINT("DNS name");
+ break;
+ case NI_QTYPE_NODEADDR:
+ ND_PRINT("node addresses");
+ i = GET_BE_U_2(ni6->ni_flags);
+ if (!i)
+ break;
+ /* NI_NODEADDR_FLAG_TRUNCATE undefined for query */
+ ND_PRINT(" [%s%s%s%s%s%s]",
+ (i & NI_NODEADDR_FLAG_ANYCAST) ? "a" : "",
+ (i & NI_NODEADDR_FLAG_GLOBAL) ? "G" : "",
+ (i & NI_NODEADDR_FLAG_SITELOCAL) ? "S" : "",
+ (i & NI_NODEADDR_FLAG_LINKLOCAL) ? "L" : "",
+ (i & NI_NODEADDR_FLAG_COMPAT) ? "C" : "",
+ (i & NI_NODEADDR_FLAG_ALL) ? "A" : "");
+ break;
+ default:
+ ND_PRINT("unknown");
+ break;
+ }
+
+ if (GET_BE_U_2(ni6->ni_qtype) == NI_QTYPE_NOOP ||
+ GET_BE_U_2(ni6->ni_qtype) == NI_QTYPE_SUPTYPES) {
+ if (siz != sizeof(*ni6))
+ if (ndo->ndo_vflag)
+ ND_PRINT(", invalid len");
+ /*(*/
+ ND_PRINT(")");
+ break;
+ }
+
+
+ /* XXX backward compat, icmp-name-lookup-03 */
+ if (siz == sizeof(*ni6)) {
+ ND_PRINT(", 03 draft");
+ /*(*/
+ ND_PRINT(")");
+ break;
+ }
+
+ cp = (const u_char *)(ni6 + 1);
+ switch (GET_U_1(ni6->ni_code)) {
+ case ICMP6_NI_SUBJ_IPV6:
+ if (!ND_TTEST_LEN(dp, sizeof(*ni6) + sizeof(nd_ipv6)))
+ break;
+ if (siz != sizeof(*ni6) + sizeof(nd_ipv6)) {
+ if (ndo->ndo_vflag)
+ ND_PRINT(", invalid subject len");
+ break;
+ }
+ ND_PRINT(", subject=%s",
+ GET_IP6ADDR_STRING(cp));
+ break;
+ case ICMP6_NI_SUBJ_FQDN:
+ ND_PRINT(", subject=DNS name");
+ if (GET_U_1(cp) == ep - cp - 1) {
+ /* icmp-name-lookup-03, pascal string */
+ if (ndo->ndo_vflag)
+ ND_PRINT(", 03 draft");
+ cp++;
+ ND_PRINT(", \"");
+ while (cp < ep) {
+ fn_print_char(ndo, GET_U_1(cp));
+ cp++;
+ }
+ ND_PRINT("\"");
+ } else
+ dnsname_print(ndo, cp, ep);
+ break;
+ case ICMP6_NI_SUBJ_IPV4:
+ if (!ND_TTEST_LEN(dp, sizeof(*ni6) + sizeof(nd_ipv4)))
+ break;
+ if (siz != sizeof(*ni6) + sizeof(nd_ipv4)) {
+ if (ndo->ndo_vflag)
+ ND_PRINT(", invalid subject len");
+ break;
+ }
+ ND_PRINT(", subject=%s",
+ GET_IPADDR_STRING(cp));
+ break;
+ default:
+ ND_PRINT(", unknown subject");
+ break;
+ }
+
+ /*(*/
+ ND_PRINT(")");
+ break;
+
+ case ICMP6_NI_REPLY:
+ if (icmp6len > siz)
+ goto trunc;
+
+ needcomma = 0;
+
+ ND_TCHECK_LEN(dp, sizeof(*ni6));
+ ni6 = (const struct icmp6_nodeinfo *)dp;
+ ND_PRINT(" node information reply");
+ ND_PRINT(" ("); /*)*/
+ switch (GET_U_1(ni6->ni_code)) {
+ case ICMP6_NI_SUCCESS:
+ if (ndo->ndo_vflag) {
+ ND_PRINT("success");
+ needcomma++;
+ }
+ break;
+ case ICMP6_NI_REFUSED:
+ ND_PRINT("refused");
+ needcomma++;
+ if (siz != sizeof(*ni6))
+ if (ndo->ndo_vflag)
+ ND_PRINT(", invalid length");
+ break;
+ case ICMP6_NI_UNKNOWN:
+ ND_PRINT("unknown");
+ needcomma++;
+ if (siz != sizeof(*ni6))
+ if (ndo->ndo_vflag)
+ ND_PRINT(", invalid length");
+ break;
+ }
+
+ if (GET_U_1(ni6->ni_code) != ICMP6_NI_SUCCESS) {
+ /*(*/
+ ND_PRINT(")");
+ break;
+ }
+
+ switch (GET_BE_U_2(ni6->ni_qtype)) {
+ case NI_QTYPE_NOOP:
+ if (needcomma)
+ ND_PRINT(", ");
+ ND_PRINT("noop");
+ if (siz != sizeof(*ni6))
+ if (ndo->ndo_vflag)
+ ND_PRINT(", invalid length");
+ break;
+ case NI_QTYPE_SUPTYPES:
+ if (needcomma)
+ ND_PRINT(", ");
+ ND_PRINT("supported qtypes");
+ i = GET_BE_U_2(ni6->ni_flags);
+ if (i)
+ ND_PRINT(" [%s]", (i & 0x01) ? "C" : "");
+ break;
+ case NI_QTYPE_FQDN:
+ if (needcomma)
+ ND_PRINT(", ");
+ ND_PRINT("DNS name");
+ cp = (const u_char *)(ni6 + 1) + 4;
+ if (GET_U_1(cp) == ep - cp - 1) {
+ /* icmp-name-lookup-03, pascal string */
+ if (ndo->ndo_vflag)
+ ND_PRINT(", 03 draft");
+ cp++;
+ ND_PRINT(", \"");
+ while (cp < ep) {
+ fn_print_char(ndo, GET_U_1(cp));
+ cp++;
+ }
+ ND_PRINT("\"");
+ } else
+ dnsname_print(ndo, cp, ep);
+ if ((GET_BE_U_2(ni6->ni_flags) & 0x01) != 0)
+ ND_PRINT(" [TTL=%u]", GET_BE_U_4(ni6 + 1));
+ break;
+ case NI_QTYPE_NODEADDR:
+ if (needcomma)
+ ND_PRINT(", ");
+ ND_PRINT("node addresses");
+ i = sizeof(*ni6);
+ while (i < siz) {
+ if (i + sizeof(uint32_t) + sizeof(nd_ipv6) > siz)
+ break;
+ ND_PRINT(" %s(%u)",
+ GET_IP6ADDR_STRING(bp + i + sizeof(uint32_t)),
+ GET_BE_U_4(bp + i));
+ i += sizeof(uint32_t) + sizeof(nd_ipv6);
+ }
+ i = GET_BE_U_2(ni6->ni_flags);
+ if (!i)
+ break;
+ ND_PRINT(" [%s%s%s%s%s%s%s]",
+ (i & NI_NODEADDR_FLAG_ANYCAST) ? "a" : "",
+ (i & NI_NODEADDR_FLAG_GLOBAL) ? "G" : "",
+ (i & NI_NODEADDR_FLAG_SITELOCAL) ? "S" : "",
+ (i & NI_NODEADDR_FLAG_LINKLOCAL) ? "L" : "",
+ (i & NI_NODEADDR_FLAG_COMPAT) ? "C" : "",
+ (i & NI_NODEADDR_FLAG_ALL) ? "A" : "",
+ (i & NI_NODEADDR_FLAG_TRUNCATE) ? "T" : "");
+ break;
+ default:
+ if (needcomma)
+ ND_PRINT(", ");
+ ND_PRINT("unknown");
+ break;
+ }
+
+ /*(*/
+ ND_PRINT(")");
+ break;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+icmp6_rrenum_print(netdissect_options *ndo, const u_char *bp, const u_char *ep)
+{
+ const struct icmp6_router_renum *rr6;
+ const char *cp;
+ const struct rr_pco_match *match;
+ const struct rr_pco_use *use;
+ char hbuf[NI_MAXHOST];
+ int n;
+
+ if (ep < bp)
+ return;
+ rr6 = (const struct icmp6_router_renum *)bp;
+ cp = (const char *)(rr6 + 1);
+
+ ND_TCHECK_4(rr6->rr_reserved);
+ switch (GET_U_1(rr6->rr_code)) {
+ case ICMP6_ROUTER_RENUMBERING_COMMAND:
+ ND_PRINT("router renum: command");
+ break;
+ case ICMP6_ROUTER_RENUMBERING_RESULT:
+ ND_PRINT("router renum: result");
+ break;
+ case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
+ ND_PRINT("router renum: sequence number reset");
+ break;
+ default:
+ ND_PRINT("router renum: code-#%u", GET_U_1(rr6->rr_code));
+ break;
+ }
+
+ ND_PRINT(", seq=%u", GET_BE_U_4(rr6->rr_seqnum));
+
+ if (ndo->ndo_vflag) {
+ uint8_t rr_flags = GET_U_1(rr6->rr_flags);
+#define F(x, y) (rr_flags & (x) ? (y) : "")
+ ND_PRINT("["); /*]*/
+ if (rr_flags) {
+ ND_PRINT("%s%s%s%s%s,", F(ICMP6_RR_FLAGS_TEST, "T"),
+ F(ICMP6_RR_FLAGS_REQRESULT, "R"),
+ F(ICMP6_RR_FLAGS_FORCEAPPLY, "A"),
+ F(ICMP6_RR_FLAGS_SPECSITE, "S"),
+ F(ICMP6_RR_FLAGS_PREVDONE, "P"));
+ }
+ ND_PRINT("seg=%u,", GET_U_1(rr6->rr_segnum));
+ ND_PRINT("maxdelay=%u", GET_BE_U_2(rr6->rr_maxdelay));
+ if (GET_BE_U_4(rr6->rr_reserved))
+ ND_PRINT("rsvd=0x%x", GET_BE_U_4(rr6->rr_reserved));
+ /*[*/
+ ND_PRINT("]");
+#undef F
+ }
+
+ if (GET_U_1(rr6->rr_code) == ICMP6_ROUTER_RENUMBERING_COMMAND) {
+ match = (const struct rr_pco_match *)cp;
+ cp = (const char *)(match + 1);
+
+ ND_TCHECK_16(match->rpm_prefix);
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n\t");
+ else
+ ND_PRINT(" ");
+ ND_PRINT("match("); /*)*/
+ switch (GET_U_1(match->rpm_code)) {
+ case RPM_PCO_ADD: ND_PRINT("add"); break;
+ case RPM_PCO_CHANGE: ND_PRINT("change"); break;
+ case RPM_PCO_SETGLOBAL: ND_PRINT("setglobal"); break;
+ default: ND_PRINT("#%u",
+ GET_U_1(match->rpm_code)); break;
+ }
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT(",ord=%u", GET_U_1(match->rpm_ordinal));
+ ND_PRINT(",min=%u", GET_U_1(match->rpm_minlen));
+ ND_PRINT(",max=%u", GET_U_1(match->rpm_maxlen));
+ }
+ if (addrtostr6(match->rpm_prefix, hbuf, sizeof(hbuf)))
+ ND_PRINT(",%s/%u", hbuf, GET_U_1(match->rpm_matchlen));
+ else
+ ND_PRINT(",?/%u", GET_U_1(match->rpm_matchlen));
+ /*(*/
+ ND_PRINT(")");
+
+ n = GET_U_1(match->rpm_len) - 3;
+ if (n % 4)
+ goto trunc;
+ n /= 4;
+ while (n-- > 0) {
+ use = (const struct rr_pco_use *)cp;
+ cp = (const char *)(use + 1);
+
+ ND_TCHECK_16(use->rpu_prefix);
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n\t");
+ else
+ ND_PRINT(" ");
+ ND_PRINT("use("); /*)*/
+ if (GET_U_1(use->rpu_flags)) {
+#define F(x, y) (GET_U_1(use->rpu_flags) & (x) ? (y) : "")
+ ND_PRINT("%s%s,",
+ F(ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME, "V"),
+ F(ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME, "P"));
+#undef F
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT("mask=0x%x,",
+ GET_U_1(use->rpu_ramask));
+ ND_PRINT("raflags=0x%x,",
+ GET_U_1(use->rpu_raflags));
+ if (GET_BE_U_4(use->rpu_vltime) == 0xffffffff)
+ ND_PRINT("vltime=infty,");
+ else
+ ND_PRINT("vltime=%u,",
+ GET_BE_U_4(use->rpu_vltime));
+ if (GET_BE_U_4(use->rpu_pltime) == 0xffffffff)
+ ND_PRINT("pltime=infty,");
+ else
+ ND_PRINT("pltime=%u,",
+ GET_BE_U_4(use->rpu_pltime));
+ }
+ if (addrtostr6(use->rpu_prefix, hbuf, sizeof(hbuf)))
+ ND_PRINT("%s/%u/%u", hbuf,
+ GET_U_1(use->rpu_uselen),
+ GET_U_1(use->rpu_keeplen));
+ else
+ ND_PRINT("?/%u/%u", GET_U_1(use->rpu_uselen),
+ GET_U_1(use->rpu_keeplen));
+ /*(*/
+ ND_PRINT(")");
+ }
+ }
+
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-igmp.c b/print-igmp.c
new file mode 100644
index 0000000..914e3d6
--- /dev/null
+++ b/print-igmp.c
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Internet Group Management Protocol (IGMP) printer */
+
+/*
+ * specification:
+ *
+ * RFC 2236 for IGMPv2
+ * RFC 3376 for IGMPv3
+ * draft-asaeda-mboned-mtrace-v2 for the mtrace message
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#ifndef IN_CLASSD
+#define IN_CLASSD(i) (((int32_t)(i) & 0xf0000000) == 0xe0000000)
+#endif
+
+
+/* (following from ipmulti/mrouted/prune.h) */
+
+/*
+ * The packet format for a traceroute request.
+ */
+struct tr_query {
+ nd_uint32_t tr_src; /* traceroute source */
+ nd_uint32_t tr_dst; /* traceroute destination */
+ nd_uint32_t tr_raddr; /* traceroute response address */
+ nd_uint8_t tr_rttl; /* response ttl */
+ nd_uint24_t tr_qid; /* qid */
+};
+
+/*
+ * Traceroute response format. A traceroute response has a tr_query at the
+ * beginning, followed by one tr_resp for each hop taken.
+ */
+struct tr_resp {
+ nd_uint32_t tr_qarr; /* query arrival time */
+ nd_uint32_t tr_inaddr; /* incoming interface address */
+ nd_uint32_t tr_outaddr; /* outgoing interface address */
+ nd_uint32_t tr_rmtaddr; /* parent address in source tree */
+ nd_uint32_t tr_vifin; /* input packet count on interface */
+ nd_uint32_t tr_vifout; /* output packet count on interface */
+ nd_uint32_t tr_pktcnt; /* total incoming packets for src-grp */
+ nd_uint8_t tr_rproto; /* routing proto deployed on router */
+ nd_uint8_t tr_fttl; /* ttl required to forward on outvif */
+ nd_uint8_t tr_smask; /* subnet mask for src addr */
+ nd_uint8_t tr_rflags; /* forwarding error codes */
+};
+
+/* defs within mtrace */
+#define TR_QUERY 1
+#define TR_RESP 2
+
+/* fields for tr_rflags (forwarding error codes) */
+#define TR_NO_ERR 0
+#define TR_WRONG_IF 1
+#define TR_PRUNED 2
+#define TR_OPRUNED 3
+#define TR_SCOPED 4
+#define TR_NO_RTE 5
+#define TR_NO_FWD 7
+#define TR_NO_SPACE 0x81
+#define TR_OLD_ROUTER 0x82
+
+/* fields for tr_rproto (routing protocol) */
+#define TR_PROTO_DVMRP 1
+#define TR_PROTO_MOSPF 2
+#define TR_PROTO_PIM 3
+#define TR_PROTO_CBT 4
+
+/* igmpv3 report types */
+static const struct tok igmpv3report2str[] = {
+ { 1, "is_in" },
+ { 2, "is_ex" },
+ { 3, "to_in" },
+ { 4, "to_ex" },
+ { 5, "allow" },
+ { 6, "block" },
+ { 0, NULL }
+};
+
+static void
+print_mtrace(netdissect_options *ndo,
+ const char *typename,
+ const u_char *bp, u_int len)
+{
+ const struct tr_query *tr = (const struct tr_query *)(bp + 8);
+
+ if (len < 8 + sizeof (struct tr_query)) {
+ ND_PRINT(" [invalid len %u]", len);
+ return;
+ }
+ ND_PRINT("%s %u: %s to %s reply-to %s",
+ typename,
+ GET_BE_U_3(tr->tr_qid),
+ GET_IPADDR_STRING(tr->tr_src), GET_IPADDR_STRING(tr->tr_dst),
+ GET_IPADDR_STRING(tr->tr_raddr));
+ if (IN_CLASSD(GET_BE_U_4(tr->tr_raddr)))
+ ND_PRINT(" with-ttl %u", GET_U_1(tr->tr_rttl));
+}
+
+static void
+print_igmpv3_report(netdissect_options *ndo,
+ const u_char *bp, u_int len)
+{
+ u_int group, nsrcs, ngroups;
+ u_int i, j;
+
+ /* Minimum len is 16, and should be a multiple of 4 */
+ if (len < 16 || len & 0x03) {
+ ND_PRINT(" [invalid len %u]", len);
+ return;
+ }
+ ngroups = GET_BE_U_2(bp + 6);
+ ND_PRINT(", %u group record(s)", ngroups);
+ if (ndo->ndo_vflag > 0) {
+ /* Print the group records */
+ group = 8;
+ for (i=0; i<ngroups; i++) {
+ if (len < group+8) {
+ ND_PRINT(" [invalid number of groups]");
+ return;
+ }
+ ND_PRINT(" [gaddr %s", GET_IPADDR_STRING(bp + group + 4));
+ ND_PRINT(" %s", tok2str(igmpv3report2str, " [v3-report-#%u]",
+ GET_U_1(bp + group)));
+ nsrcs = GET_BE_U_2(bp + group + 2);
+ /* Check the number of sources and print them */
+ if (len < group+8+(nsrcs<<2)) {
+ ND_PRINT(" [invalid number of sources %u]", nsrcs);
+ return;
+ }
+ if (ndo->ndo_vflag == 1)
+ ND_PRINT(", %u source(s)", nsrcs);
+ else {
+ /* Print the sources */
+ ND_PRINT(" {");
+ for (j=0; j<nsrcs; j++) {
+ ND_PRINT(" %s", GET_IPADDR_STRING(bp + group + 8 + (j << 2)));
+ }
+ ND_PRINT(" }");
+ }
+ /* Next group record */
+ group += 8 + (nsrcs << 2);
+ ND_PRINT("]");
+ }
+ }
+}
+
+static void
+print_igmpv3_query(netdissect_options *ndo,
+ const u_char *bp, u_int len)
+{
+ u_int mrc;
+ u_int mrt;
+ u_int nsrcs;
+ u_int i;
+
+ ND_PRINT(" v3");
+ /* Minimum len is 12, and should be a multiple of 4 */
+ if (len < 12 || len & 0x03) {
+ ND_PRINT(" [invalid len %u]", len);
+ return;
+ }
+ mrc = GET_U_1(bp + 1);
+ if (mrc < 128) {
+ mrt = mrc;
+ } else {
+ mrt = ((mrc & 0x0f) | 0x10) << (((mrc & 0x70) >> 4) + 3);
+ }
+ if (mrc != 100) {
+ ND_PRINT(" [max resp time ");
+ if (mrt < 600) {
+ ND_PRINT("%.1fs", mrt * 0.1);
+ } else {
+ unsigned_relts_print(ndo, mrt / 10);
+ }
+ ND_PRINT("]");
+ }
+ if (GET_BE_U_4(bp + 4) == 0)
+ return;
+ ND_PRINT(" [gaddr %s", GET_IPADDR_STRING(bp + 4));
+ nsrcs = GET_BE_U_2(bp + 10);
+ if (nsrcs > 0) {
+ if (len < 12 + (nsrcs << 2))
+ ND_PRINT(" [invalid number of sources]");
+ else if (ndo->ndo_vflag > 1) {
+ ND_PRINT(" {");
+ for (i=0; i<nsrcs; i++) {
+ ND_PRINT(" %s", GET_IPADDR_STRING(bp + 12 + (i << 2)));
+ }
+ ND_PRINT(" }");
+ } else
+ ND_PRINT(", %u source(s)", nsrcs);
+ }
+ ND_PRINT("]");
+}
+
+void
+igmp_print(netdissect_options *ndo,
+ const u_char *bp, u_int len)
+{
+ struct cksum_vec vec[1];
+
+ ndo->ndo_protocol = "igmp";
+ if (ndo->ndo_qflag) {
+ ND_PRINT("igmp");
+ return;
+ }
+
+ switch (GET_U_1(bp)) {
+ case 0x11:
+ ND_PRINT("igmp query");
+ if (len >= 12)
+ print_igmpv3_query(ndo, bp, len);
+ else {
+ if (GET_U_1(bp + 1)) {
+ ND_PRINT(" v2");
+ if (GET_U_1(bp + 1) != 100)
+ ND_PRINT(" [max resp time %u]", GET_U_1(bp + 1));
+ } else
+ ND_PRINT(" v1");
+ if (GET_BE_U_4(bp + 4))
+ ND_PRINT(" [gaddr %s]", GET_IPADDR_STRING(bp + 4));
+ if (len != 8)
+ ND_PRINT(" [len %u]", len);
+ }
+ break;
+ case 0x12:
+ ND_PRINT("igmp v1 report %s", GET_IPADDR_STRING(bp + 4));
+ if (len != 8)
+ ND_PRINT(" [len %u]", len);
+ break;
+ case 0x16:
+ ND_PRINT("igmp v2 report %s", GET_IPADDR_STRING(bp + 4));
+ break;
+ case 0x22:
+ ND_PRINT("igmp v3 report");
+ print_igmpv3_report(ndo, bp, len);
+ break;
+ case 0x17:
+ ND_PRINT("igmp leave %s", GET_IPADDR_STRING(bp + 4));
+ break;
+ case 0x13:
+ ND_PRINT("igmp dvmrp");
+ if (len < 8)
+ ND_PRINT(" [len %u]", len);
+ else
+ dvmrp_print(ndo, bp, len);
+ break;
+ case 0x14:
+ ND_PRINT("igmp pimv1");
+ pimv1_print(ndo, bp, len);
+ break;
+ case 0x1e:
+ print_mtrace(ndo, "mresp", bp, len);
+ break;
+ case 0x1f:
+ print_mtrace(ndo, "mtrace", bp, len);
+ break;
+ default:
+ ND_PRINT("igmp-%u", GET_U_1(bp));
+ break;
+ }
+
+ if (ndo->ndo_vflag && len >= 4 && ND_TTEST_LEN(bp, len)) {
+ /* Check the IGMP checksum */
+ vec[0].ptr = bp;
+ vec[0].len = len;
+ if (in_cksum(vec, 1))
+ ND_PRINT(" bad igmp cksum %x!", GET_BE_U_2(bp + 2));
+ }
+}
diff --git a/print-igrp.c b/print-igrp.c
new file mode 100644
index 0000000..0efc78a
--- /dev/null
+++ b/print-igrp.c
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Initial contribution from Francis Dupont (francis.dupont@inria.fr)
+ */
+
+/* \summary: Interior Gateway Routing Protocol (IGRP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+/* Cisco IGRP definitions */
+
+/* IGRP Header */
+
+struct igrphdr {
+ nd_uint8_t ig_vop; /* protocol version number / opcode */
+#define IGRP_V(x) (((x) & 0xf0) >> 4)
+#define IGRP_OP(x) ((x) & 0x0f)
+ nd_uint8_t ig_ed; /* edition number */
+ nd_uint16_t ig_as; /* autonomous system number */
+ nd_uint16_t ig_ni; /* number of subnet in local net */
+ nd_uint16_t ig_ns; /* number of networks in AS */
+ nd_uint16_t ig_nx; /* number of networks ouside AS */
+ nd_uint16_t ig_sum; /* checksum of IGRP header & data */
+};
+
+#define IGRP_UPDATE 1
+#define IGRP_REQUEST 2
+
+/* IGRP routing entry */
+
+struct igrprte {
+ nd_byte igr_net[3]; /* 3 significant octets of IP address */
+ nd_uint24_t igr_dly; /* delay in tens of microseconds */
+ nd_uint24_t igr_bw; /* bandwidth in units of 1 kb/s */
+ nd_uint16_t igr_mtu; /* MTU in octets */
+ nd_uint8_t igr_rel; /* percent packets successfully tx/rx */
+ nd_uint8_t igr_ld; /* percent of channel occupied */
+ nd_uint8_t igr_hct; /* hop count */
+};
+
+#define IGRP_RTE_SIZE 14 /* sizeof() is accurate now */
+
+static void
+igrp_entry_print(netdissect_options *ndo, const struct igrprte *igr)
+{
+ u_int delay, bandwidth;
+ u_int metric, mtu;
+
+ delay = GET_BE_U_3(igr->igr_dly);
+ bandwidth = GET_BE_U_3(igr->igr_bw);
+ metric = ND_MIN(bandwidth + delay, 0xffffff);
+ mtu = GET_BE_U_2(igr->igr_mtu);
+
+ ND_PRINT(" d=%u b=%u r=%u l=%u M=%u mtu=%u in %u hops",
+ 10 * delay, bandwidth == 0 ? 0 : 10000000 / bandwidth,
+ GET_U_1(igr->igr_rel), GET_U_1(igr->igr_ld), metric,
+ mtu, GET_U_1(igr->igr_hct));
+}
+
+static const struct tok op2str[] = {
+ { IGRP_UPDATE, "update" },
+ { IGRP_REQUEST, "request" },
+ { 0, NULL }
+};
+
+void
+igrp_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const struct igrphdr *hdr;
+ const u_char *cp;
+ u_int nint, nsys, next;
+ uint16_t cksum;
+
+ ndo->ndo_protocol = "igrp";
+ hdr = (const struct igrphdr *)bp;
+ cp = (const u_char *)(hdr + 1);
+ ND_PRINT("igrp:");
+
+ /* Header */
+ nint = GET_BE_U_2(hdr->ig_ni);
+ nsys = GET_BE_U_2(hdr->ig_ns);
+ next = GET_BE_U_2(hdr->ig_nx);
+
+ ND_PRINT(" %s V%u edit=%u AS=%u (%u/%u/%u)",
+ tok2str(op2str, "op-#%u", IGRP_OP(GET_U_1(hdr->ig_vop))),
+ IGRP_V(GET_U_1(hdr->ig_vop)),
+ GET_U_1(hdr->ig_ed),
+ GET_BE_U_2(hdr->ig_as),
+ nint,
+ nsys,
+ next);
+ cksum = GET_BE_U_2(hdr->ig_sum);
+ if (ndo->ndo_vflag)
+ ND_PRINT(" checksum=0x%04x", cksum);
+
+ length -= sizeof(*hdr);
+ while (length >= IGRP_RTE_SIZE) {
+ const struct igrprte *igr = (const struct igrprte *)cp;
+ uint8_t net0 = GET_U_1(&igr->igr_net[0]);
+ uint8_t net1 = GET_U_1(&igr->igr_net[1]);
+ uint8_t net2 = GET_U_1(&igr->igr_net[2]);
+
+ if (nint > 0) {
+ ND_PRINT(" *.%u.%u.%u", net0, net1, net2);
+ igrp_entry_print(ndo, igr);
+ --nint;
+ } else if (nsys > 0) {
+ ND_PRINT(" %u.%u.%u.0", net0, net1, net2);
+ igrp_entry_print(ndo, igr);
+ --nsys;
+ } else if (next > 0) {
+ ND_PRINT(" X%u.%u.%u.0", net0, net1, net2);
+ igrp_entry_print(ndo, igr);
+ --next;
+ } else {
+ ND_PRINT(" [extra bytes %u]", length);
+ break;
+ }
+ cp += IGRP_RTE_SIZE;
+ length -= IGRP_RTE_SIZE;
+ }
+ if (nint || nsys || next || length)
+ nd_print_invalid(ndo);
+}
diff --git a/print-ip-demux.c b/print-ip-demux.c
new file mode 100644
index 0000000..b111c6d
--- /dev/null
+++ b/print-ip-demux.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IPv4/IPv6 payload printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip.h"
+#include "ipproto.h"
+
+void
+ip_demux_print(netdissect_options *ndo,
+ const u_char *bp,
+ u_int length, u_int ver, int fragmented, u_int ttl_hl,
+ uint8_t nh, const u_char *iph)
+{
+ int advance;
+ const char *p_name;
+
+ advance = 0;
+
+again:
+ switch (nh) {
+
+ case IPPROTO_AH:
+ if (!ND_TTEST_1(bp)) {
+ ndo->ndo_protocol = "ah";
+ nd_print_trunc(ndo);
+ break;
+ }
+ nh = GET_U_1(bp);
+ advance = ah_print(ndo, bp);
+ if (advance <= 0)
+ break;
+ bp += advance;
+ length -= advance;
+ goto again;
+
+ case IPPROTO_ESP:
+ {
+ esp_print(ndo, bp, length, iph, ver, fragmented, ttl_hl);
+ /*
+ * Either this has decrypted the payload and
+ * printed it, in which case there's nothing more
+ * to do, or it hasn't, in which case there's
+ * nothing more to do.
+ */
+ break;
+ }
+
+ case IPPROTO_IPCOMP:
+ {
+ ipcomp_print(ndo, bp);
+ /*
+ * Either this has decompressed the payload and
+ * printed it, in which case there's nothing more
+ * to do, or it hasn't, in which case there's
+ * nothing more to do.
+ */
+ break;
+ }
+
+ case IPPROTO_SCTP:
+ sctp_print(ndo, bp, iph, length);
+ break;
+
+ case IPPROTO_DCCP:
+ dccp_print(ndo, bp, iph, length);
+ break;
+
+ case IPPROTO_TCP:
+ tcp_print(ndo, bp, length, iph, fragmented);
+ break;
+
+ case IPPROTO_UDP:
+ udp_print(ndo, bp, length, iph, fragmented, ttl_hl);
+ break;
+
+ case IPPROTO_ICMP:
+ if (ver == 4)
+ icmp_print(ndo, bp, length, iph, fragmented);
+ else {
+ ND_PRINT("[%s requires IPv4]",
+ tok2str(ipproto_values,"unknown",nh));
+ nd_print_invalid(ndo);
+ }
+ break;
+
+ case IPPROTO_ICMPV6:
+ if (ver == 6)
+ icmp6_print(ndo, bp, length, iph, fragmented);
+ else {
+ ND_PRINT("[%s requires IPv6]",
+ tok2str(ipproto_values,"unknown",nh));
+ nd_print_invalid(ndo);
+ }
+ break;
+
+ case IPPROTO_PIGP:
+ /*
+ * XXX - the current IANA protocol number assignments
+ * page lists 9 as "any private interior gateway
+ * (used by Cisco for their IGRP)" and 88 as
+ * "EIGRP" from Cisco.
+ *
+ * Recent BSD <netinet/in.h> headers define
+ * IP_PROTO_PIGP as 9 and IP_PROTO_IGRP as 88.
+ * We define IP_PROTO_PIGP as 9 and
+ * IP_PROTO_EIGRP as 88; those names better
+ * match was the current protocol number
+ * assignments say.
+ */
+ igrp_print(ndo, bp, length);
+ break;
+
+ case IPPROTO_EIGRP:
+ eigrp_print(ndo, bp, length);
+ break;
+
+ case IPPROTO_ND:
+ ND_PRINT(" nd %u", length);
+ break;
+
+ case IPPROTO_EGP:
+ egp_print(ndo, bp, length);
+ break;
+
+ case IPPROTO_OSPF:
+ if (ver == 6)
+ ospf6_print(ndo, bp, length);
+ else
+ ospf_print(ndo, bp, length, iph);
+ break;
+
+ case IPPROTO_IGMP:
+ if (ver == 4)
+ igmp_print(ndo, bp, length);
+ else {
+ ND_PRINT("[%s requires IPv4]",
+ tok2str(ipproto_values,"unknown",nh));
+ nd_print_invalid(ndo);
+ }
+ break;
+
+ case IPPROTO_IPV4:
+ /* ipv4-in-ip encapsulation */
+ ip_print(ndo, bp, length);
+ break;
+
+ case IPPROTO_IPV6:
+ /* ip6-in-ip encapsulation */
+ ip6_print(ndo, bp, length);
+ break;
+
+ case IPPROTO_RSVP:
+ rsvp_print(ndo, bp, length);
+ break;
+
+ case IPPROTO_GRE:
+ gre_print(ndo, bp, length);
+ break;
+
+ case IPPROTO_MOBILE:
+ mobile_print(ndo, bp, length);
+ break;
+
+ case IPPROTO_PIM:
+ pim_print(ndo, bp, length, iph);
+ break;
+
+ case IPPROTO_VRRP:
+ if (ndo->ndo_packettype == PT_CARP) {
+ carp_print(ndo, bp, length, ttl_hl);
+ } else {
+ vrrp_print(ndo, bp, length, iph, ttl_hl);
+ }
+ break;
+
+ case IPPROTO_PGM:
+ pgm_print(ndo, bp, length, iph);
+ break;
+
+ case IPPROTO_ETHERNET:
+ if (ver == 6)
+ ether_print(ndo, bp, length, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+ else {
+ ND_PRINT("[%s requires IPv6]",
+ tok2str(ipproto_values,"unknown",nh));
+ nd_print_invalid(ndo);
+ }
+ break;
+
+ case IPPROTO_NONE:
+ ND_PRINT("no next header");
+ break;
+
+ default:
+ if (ndo->ndo_nflag==0 && (p_name = netdb_protoname(nh)) != NULL)
+ ND_PRINT(" %s", p_name);
+ else
+ ND_PRINT(" ip-proto-%u", nh);
+ ND_PRINT(" %u", length);
+ break;
+ }
+}
diff --git a/print-ip.c b/print-ip.c
new file mode 100644
index 0000000..7cec640
--- /dev/null
+++ b/print-ip.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IP printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip.h"
+#include "ipproto.h"
+
+
+static const struct tok ip_option_values[] = {
+ { IPOPT_EOL, "EOL" },
+ { IPOPT_NOP, "NOP" },
+ { IPOPT_TS, "timestamp" },
+ { IPOPT_SECURITY, "security" },
+ { IPOPT_RR, "RR" },
+ { IPOPT_SSRR, "SSRR" },
+ { IPOPT_LSRR, "LSRR" },
+ { IPOPT_RA, "RA" },
+ { IPOPT_RFC1393, "traceroute" },
+ { 0, NULL }
+};
+
+/*
+ * print the recorded route in an IP RR, LSRR or SSRR option.
+ */
+static int
+ip_printroute(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ u_int ptr;
+ u_int len;
+
+ if (length < 3) {
+ ND_PRINT(" [bad length %u]", length);
+ return (0);
+ }
+ if ((length + 1) & 3)
+ ND_PRINT(" [bad length %u]", length);
+ ptr = GET_U_1(cp + 2) - 1;
+ if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1)
+ ND_PRINT(" [bad ptr %u]", GET_U_1(cp + 2));
+
+ for (len = 3; len < length; len += 4) {
+ ND_TCHECK_4(cp + len); /* Needed to print the IP addresses */
+ ND_PRINT(" %s", GET_IPADDR_STRING(cp + len));
+ if (ptr > len)
+ ND_PRINT(",");
+ }
+ return (0);
+
+trunc:
+ return (-1);
+}
+
+/*
+ * If source-routing is present and valid, return the final destination.
+ * Otherwise, return IP destination.
+ *
+ * This is used for UDP and TCP pseudo-header in the checksum
+ * calculation.
+ */
+static uint32_t
+ip_finddst(netdissect_options *ndo,
+ const struct ip *ip)
+{
+ u_int length;
+ u_int len;
+ const u_char *cp;
+
+ cp = (const u_char *)(ip + 1);
+ length = IP_HL(ip) * 4;
+ if (length < sizeof(struct ip))
+ goto trunc;
+ length -= sizeof(struct ip);
+
+ for (; length != 0; cp += len, length -= len) {
+ int tt;
+
+ tt = GET_U_1(cp);
+ if (tt == IPOPT_EOL)
+ break;
+ else if (tt == IPOPT_NOP)
+ len = 1;
+ else {
+ len = GET_U_1(cp + 1);
+ if (len < 2)
+ break;
+ }
+ if (length < len)
+ goto trunc;
+ ND_TCHECK_LEN(cp, len);
+ switch (tt) {
+
+ case IPOPT_SSRR:
+ case IPOPT_LSRR:
+ if (len < 7)
+ break;
+ return (GET_IPV4_TO_NETWORK_ORDER(cp + len - 4));
+ }
+ }
+trunc:
+ return (GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst));
+}
+
+/*
+ * Compute a V4-style checksum by building a pseudoheader.
+ */
+uint16_t
+nextproto4_cksum(netdissect_options *ndo,
+ const struct ip *ip, const uint8_t *data,
+ u_int len, u_int covlen, uint8_t next_proto)
+{
+ struct phdr {
+ uint32_t src;
+ uint32_t dst;
+ uint8_t mbz;
+ uint8_t proto;
+ uint16_t len;
+ } ph;
+ struct cksum_vec vec[2];
+
+ /* pseudo-header.. */
+ ph.len = htons((uint16_t)len);
+ ph.mbz = 0;
+ ph.proto = next_proto;
+ ph.src = GET_IPV4_TO_NETWORK_ORDER(ip->ip_src);
+ if (IP_HL(ip) == 5)
+ ph.dst = GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst);
+ else
+ ph.dst = ip_finddst(ndo, ip);
+
+ vec[0].ptr = (const uint8_t *)(void *)&ph;
+ vec[0].len = sizeof(ph);
+ vec[1].ptr = data;
+ vec[1].len = covlen;
+ return (in_cksum(vec, 2));
+}
+
+static int
+ip_printts(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ u_int ptr;
+ u_int len;
+ u_int hoplen;
+ const char *type;
+
+ if (length < 4) {
+ ND_PRINT("[bad length %u]", length);
+ return (0);
+ }
+ ND_PRINT(" TS{");
+ hoplen = ((GET_U_1(cp + 3) & 0xF) != IPOPT_TS_TSONLY) ? 8 : 4;
+ if ((length - 4) & (hoplen-1))
+ ND_PRINT("[bad length %u]", length);
+ ptr = GET_U_1(cp + 2) - 1;
+ len = 0;
+ if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1)
+ ND_PRINT("[bad ptr %u]", GET_U_1(cp + 2));
+ switch (GET_U_1(cp + 3)&0xF) {
+ case IPOPT_TS_TSONLY:
+ ND_PRINT("TSONLY");
+ break;
+ case IPOPT_TS_TSANDADDR:
+ ND_PRINT("TS+ADDR");
+ break;
+ case IPOPT_TS_PRESPEC:
+ ND_PRINT("PRESPEC");
+ break;
+ default:
+ ND_PRINT("[bad ts type %u]", GET_U_1(cp + 3)&0xF);
+ goto done;
+ }
+
+ type = " ";
+ for (len = 4; len < length; len += hoplen) {
+ if (ptr == len)
+ type = " ^ ";
+ ND_TCHECK_LEN(cp + len, hoplen);
+ ND_PRINT("%s%u@%s", type, GET_BE_U_4(cp + len + hoplen - 4),
+ hoplen!=8 ? "" : GET_IPADDR_STRING(cp + len));
+ type = " ";
+ }
+
+done:
+ ND_PRINT("%s", ptr == len ? " ^ " : "");
+
+ if (GET_U_1(cp + 3) >> 4)
+ ND_PRINT(" [%u hops not recorded]} ", GET_U_1(cp + 3)>>4);
+ else
+ ND_PRINT("}");
+ return (0);
+
+trunc:
+ return (-1);
+}
+
+/*
+ * print IP options.
+ If truncated return -1, else 0.
+ */
+static int
+ip_optprint(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ u_int option_len;
+ const char *sep = "";
+
+ for (; length > 0; cp += option_len, length -= option_len) {
+ u_int option_code;
+
+ ND_PRINT("%s", sep);
+ sep = ",";
+
+ option_code = GET_U_1(cp);
+
+ ND_PRINT("%s",
+ tok2str(ip_option_values,"unknown %u",option_code));
+
+ if (option_code == IPOPT_NOP ||
+ option_code == IPOPT_EOL)
+ option_len = 1;
+
+ else {
+ option_len = GET_U_1(cp + 1);
+ if (option_len < 2) {
+ ND_PRINT(" [bad length %u]", option_len);
+ return 0;
+ }
+ }
+
+ if (option_len > length) {
+ ND_PRINT(" [bad length %u]", option_len);
+ return 0;
+ }
+
+ ND_TCHECK_LEN(cp, option_len);
+
+ switch (option_code) {
+ case IPOPT_EOL:
+ return 0;
+
+ case IPOPT_TS:
+ if (ip_printts(ndo, cp, option_len) == -1)
+ goto trunc;
+ break;
+
+ case IPOPT_RR: /* fall through */
+ case IPOPT_SSRR:
+ case IPOPT_LSRR:
+ if (ip_printroute(ndo, cp, option_len) == -1)
+ goto trunc;
+ break;
+
+ case IPOPT_RA:
+ if (option_len < 4) {
+ ND_PRINT(" [bad length %u]", option_len);
+ break;
+ }
+ ND_TCHECK_1(cp + 3);
+ if (GET_BE_U_2(cp + 2) != 0)
+ ND_PRINT(" value %u", GET_BE_U_2(cp + 2));
+ break;
+
+ case IPOPT_NOP: /* nothing to print - fall through */
+ case IPOPT_SECURITY:
+ default:
+ break;
+ }
+ }
+ return 0;
+
+trunc:
+ return -1;
+}
+
+#define IP_RES 0x8000
+
+static const struct tok ip_frag_values[] = {
+ { IP_MF, "+" },
+ { IP_DF, "DF" },
+ { IP_RES, "rsvd" }, /* The RFC3514 evil ;-) bit */
+ { 0, NULL }
+};
+
+
+/*
+ * print an IP datagram.
+ */
+void
+ip_print(netdissect_options *ndo,
+ const u_char *bp,
+ u_int length)
+{
+ const struct ip *ip;
+ u_int off;
+ u_int hlen;
+ u_int len;
+ struct cksum_vec vec[1];
+ uint8_t ip_tos, ip_ttl, ip_proto;
+ uint16_t sum, ip_sum;
+ const char *p_name;
+ int truncated = 0;
+
+ ndo->ndo_protocol = "ip";
+ ip = (const struct ip *)bp;
+ if (IP_V(ip) != 4) { /* print version and fail if != 4 */
+ if (IP_V(ip) == 6)
+ ND_PRINT("IP6, wrong link-layer encapsulation");
+ else
+ ND_PRINT("IP%u", IP_V(ip));
+ nd_print_invalid(ndo);
+ return;
+ }
+ if (!ndo->ndo_eflag)
+ ND_PRINT("IP ");
+
+ ND_TCHECK_SIZE(ip);
+ if (length < sizeof (struct ip)) {
+ ND_PRINT("truncated-ip %u", length);
+ return;
+ }
+ hlen = IP_HL(ip) * 4;
+ if (hlen < sizeof (struct ip)) {
+ ND_PRINT("bad-hlen %u", hlen);
+ return;
+ }
+
+ len = GET_BE_U_2(ip->ip_len);
+ if (length < len)
+ ND_PRINT("truncated-ip - %u bytes missing! ",
+ len - length);
+ if (len < hlen) {
+#ifdef GUESS_TSO
+ if (len) {
+ ND_PRINT("bad-len %u", len);
+ return;
+ }
+ else {
+ /* we guess that it is a TSO send */
+ len = length;
+ }
+#else
+ ND_PRINT("bad-len %u", len);
+ return;
+#endif /* GUESS_TSO */
+ }
+
+ /*
+ * Cut off the snapshot length to the end of the IP payload.
+ */
+ nd_push_snapend(ndo, bp + len);
+
+ len -= hlen;
+
+ off = GET_BE_U_2(ip->ip_off);
+
+ ip_proto = GET_U_1(ip->ip_p);
+
+ if (ndo->ndo_vflag) {
+ ip_tos = GET_U_1(ip->ip_tos);
+ ND_PRINT("(tos 0x%x", ip_tos);
+ /* ECN bits */
+ switch (ip_tos & 0x03) {
+
+ case 0:
+ break;
+
+ case 1:
+ ND_PRINT(",ECT(1)");
+ break;
+
+ case 2:
+ ND_PRINT(",ECT(0)");
+ break;
+
+ case 3:
+ ND_PRINT(",CE");
+ break;
+ }
+
+ ip_ttl = GET_U_1(ip->ip_ttl);
+ if (ip_ttl >= 1)
+ ND_PRINT(", ttl %u", ip_ttl);
+
+ /*
+ * for the firewall guys, print id, offset.
+ * On all but the last stick a "+" in the flags portion.
+ * For unfragmented datagrams, note the don't fragment flag.
+ */
+ ND_PRINT(", id %u, offset %u, flags [%s], proto %s (%u)",
+ GET_BE_U_2(ip->ip_id),
+ (off & IP_OFFMASK) * 8,
+ bittok2str(ip_frag_values, "none", off & (IP_RES|IP_DF|IP_MF)),
+ tok2str(ipproto_values, "unknown", ip_proto),
+ ip_proto);
+
+ ND_PRINT(", length %u", GET_BE_U_2(ip->ip_len));
+
+ if ((hlen - sizeof(struct ip)) > 0) {
+ ND_PRINT(", options (");
+ if (ip_optprint(ndo, (const u_char *)(ip + 1),
+ hlen - sizeof(struct ip)) == -1) {
+ ND_PRINT(" [truncated-option]");
+ truncated = 1;
+ }
+ ND_PRINT(")");
+ }
+
+ if (!ndo->ndo_Kflag && (const u_char *)ip + hlen <= ndo->ndo_snapend) {
+ vec[0].ptr = (const uint8_t *)(const void *)ip;
+ vec[0].len = hlen;
+ sum = in_cksum(vec, 1);
+ if (sum != 0) {
+ ip_sum = GET_BE_U_2(ip->ip_sum);
+ ND_PRINT(", bad cksum %x (->%x)!", ip_sum,
+ in_cksum_shouldbe(ip_sum, sum));
+ }
+ }
+
+ ND_PRINT(")\n ");
+ if (truncated) {
+ ND_PRINT("%s > %s: ",
+ GET_IPADDR_STRING(ip->ip_src),
+ GET_IPADDR_STRING(ip->ip_dst));
+ nd_print_trunc(ndo);
+ nd_pop_packet_info(ndo);
+ return;
+ }
+ }
+
+ /*
+ * If this is fragment zero, hand it to the next higher
+ * level protocol. Let them know whether there are more
+ * fragments.
+ */
+ if ((off & IP_OFFMASK) == 0) {
+ uint8_t nh = GET_U_1(ip->ip_p);
+
+ if (nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
+ nh != IPPROTO_SCTP && nh != IPPROTO_DCCP) {
+ ND_PRINT("%s > %s: ",
+ GET_IPADDR_STRING(ip->ip_src),
+ GET_IPADDR_STRING(ip->ip_dst));
+ }
+ ip_demux_print(ndo, (const u_char *)ip + hlen, len, 4,
+ off & IP_MF, GET_U_1(ip->ip_ttl), nh, bp);
+ } else {
+ /*
+ * Ultra quiet now means that all this stuff should be
+ * suppressed.
+ */
+ if (ndo->ndo_qflag > 1) {
+ nd_pop_packet_info(ndo);
+ return;
+ }
+
+ /*
+ * This isn't the first frag, so we're missing the
+ * next level protocol header. print the ip addr
+ * and the protocol.
+ */
+ ND_PRINT("%s > %s:", GET_IPADDR_STRING(ip->ip_src),
+ GET_IPADDR_STRING(ip->ip_dst));
+ if (!ndo->ndo_nflag && (p_name = netdb_protoname(ip_proto)) != NULL)
+ ND_PRINT(" %s", p_name);
+ else
+ ND_PRINT(" ip-proto-%u", ip_proto);
+ }
+ nd_pop_packet_info(ndo);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+ipN_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ ndo->ndo_protocol = "ipn";
+ if (length < 1) {
+ ND_PRINT("truncated-ip %u", length);
+ return;
+ }
+
+ switch (GET_U_1(bp) & 0xF0) {
+ case 0x40:
+ ip_print(ndo, bp, length);
+ break;
+ case 0x60:
+ ip6_print(ndo, bp, length);
+ break;
+ default:
+ ND_PRINT("unknown ip %u", (GET_U_1(bp) & 0xF0) >> 4);
+ break;
+ }
+}
diff --git a/print-ip6.c b/print-ip6.c
new file mode 100644
index 0000000..525a284
--- /dev/null
+++ b/print-ip6.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IPv6 printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip6.h"
+#include "ipproto.h"
+
+/*
+ * If routing headers are presend and valid, set dst to the final destination.
+ * Otherwise, set it to the IPv6 destination.
+ *
+ * This is used for UDP and TCP pseudo-header in the checksum
+ * calculation.
+ */
+static void
+ip6_finddst(netdissect_options *ndo, nd_ipv6 *dst,
+ const struct ip6_hdr *ip6)
+{
+ const u_char *cp;
+ u_int advance;
+ u_int nh;
+ const void *dst_addr;
+ const struct ip6_rthdr *dp;
+ const struct ip6_rthdr0 *dp0;
+ const struct ip6_srh *srh;
+ const u_char *p;
+ int i, len;
+
+ cp = (const u_char *)ip6;
+ advance = sizeof(struct ip6_hdr);
+ nh = GET_U_1(ip6->ip6_nxt);
+ dst_addr = (const void *)ip6->ip6_dst;
+
+ while (cp < ndo->ndo_snapend) {
+ cp += advance;
+
+ switch (nh) {
+
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_MOBILITY_OLD:
+ case IPPROTO_MOBILITY:
+ /*
+ * These have a header length byte, following
+ * the next header byte, giving the length of
+ * the header, in units of 8 octets, excluding
+ * the first 8 octets.
+ */
+ advance = (GET_U_1(cp + 1) + 1) << 3;
+ nh = GET_U_1(cp);
+ break;
+
+ case IPPROTO_FRAGMENT:
+ /*
+ * The byte following the next header byte is
+ * marked as reserved, and the header is always
+ * the same size.
+ */
+ advance = sizeof(struct ip6_frag);
+ nh = GET_U_1(cp);
+ break;
+
+ case IPPROTO_ROUTING:
+ /*
+ * OK, we found it.
+ */
+ dp = (const struct ip6_rthdr *)cp;
+ ND_TCHECK_SIZE(dp);
+ len = GET_U_1(dp->ip6r_len);
+ switch (GET_U_1(dp->ip6r_type)) {
+
+ case IPV6_RTHDR_TYPE_0:
+ case IPV6_RTHDR_TYPE_2: /* Mobile IPv6 ID-20 */
+ dp0 = (const struct ip6_rthdr0 *)dp;
+ if (len % 2 == 1)
+ goto trunc;
+ len >>= 1;
+ p = (const u_char *) dp0->ip6r0_addr;
+ for (i = 0; i < len; i++) {
+ ND_TCHECK_16(p);
+ dst_addr = (const void *)p;
+ p += 16;
+ }
+ break;
+ case IPV6_RTHDR_TYPE_4:
+ /* IPv6 Segment Routing Header (SRH) */
+ srh = (const struct ip6_srh *)dp;
+ if (len % 2 == 1)
+ goto trunc;
+ p = (const u_char *) srh->srh_segments;
+ /*
+ * The list of segments are encoded in the reverse order.
+ * Accordingly, the final DA is encoded in srh_segments[0]
+ */
+ ND_TCHECK_16(p);
+ dst_addr = (const void *)p;
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * Only one routing header to a customer.
+ */
+ goto done;
+
+ case IPPROTO_AH:
+ case IPPROTO_ESP:
+ case IPPROTO_IPCOMP:
+ default:
+ /*
+ * AH and ESP are, in the RFCs that describe them,
+ * described as being "viewed as an end-to-end
+ * payload" "in the IPv6 context, so that they
+ * "should appear after hop-by-hop, routing, and
+ * fragmentation extension headers". We assume
+ * that's the case, and stop as soon as we see
+ * one. (We can't handle an ESP header in
+ * the general case anyway, as its length depends
+ * on the encryption algorithm.)
+ *
+ * IPComp is also "viewed as an end-to-end
+ * payload" "in the IPv6 context".
+ *
+ * All other protocols are assumed to be the final
+ * protocol.
+ */
+ goto done;
+ }
+ }
+
+done:
+trunc:
+ GET_CPY_BYTES(dst, dst_addr, sizeof(nd_ipv6));
+}
+
+/*
+ * Compute a V6-style checksum by building a pseudoheader.
+ */
+uint16_t
+nextproto6_cksum(netdissect_options *ndo,
+ const struct ip6_hdr *ip6, const uint8_t *data,
+ u_int len, u_int covlen, uint8_t next_proto)
+{
+ struct {
+ nd_ipv6 ph_src;
+ nd_ipv6 ph_dst;
+ uint32_t ph_len;
+ uint8_t ph_zero[3];
+ uint8_t ph_nxt;
+ } ph;
+ struct cksum_vec vec[2];
+ u_int nh;
+
+ /* pseudo-header */
+ memset(&ph, 0, sizeof(ph));
+ GET_CPY_BYTES(&ph.ph_src, ip6->ip6_src, sizeof(nd_ipv6));
+ nh = GET_U_1(ip6->ip6_nxt);
+ switch (nh) {
+
+ case IPPROTO_HOPOPTS:
+ case IPPROTO_DSTOPTS:
+ case IPPROTO_MOBILITY_OLD:
+ case IPPROTO_MOBILITY:
+ case IPPROTO_FRAGMENT:
+ case IPPROTO_ROUTING:
+ /*
+ * The next header is either a routing header or a header
+ * after which there might be a routing header, so scan
+ * for a routing header.
+ */
+ ip6_finddst(ndo, &ph.ph_dst, ip6);
+ break;
+
+ default:
+ GET_CPY_BYTES(&ph.ph_dst, ip6->ip6_dst, sizeof(nd_ipv6));
+ break;
+ }
+ ph.ph_len = htonl(len);
+ ph.ph_nxt = next_proto;
+
+ vec[0].ptr = (const uint8_t *)(void *)&ph;
+ vec[0].len = sizeof(ph);
+ vec[1].ptr = data;
+ vec[1].len = covlen;
+
+ return in_cksum(vec, 2);
+}
+
+/*
+ * print an IP6 datagram.
+ */
+void
+ip6_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const struct ip6_hdr *ip6;
+ int advance;
+ u_int len;
+ u_int total_advance;
+ const u_char *cp;
+ uint32_t payload_len;
+ uint8_t nh;
+ int fragmented = 0;
+ u_int flow;
+ int found_extension_header;
+ int found_jumbo;
+
+ ndo->ndo_protocol = "ip6";
+ ip6 = (const struct ip6_hdr *)bp;
+
+ ND_TCHECK_SIZE(ip6);
+ if (length < sizeof (struct ip6_hdr)) {
+ ND_PRINT("truncated-ip6 %u", length);
+ return;
+ }
+
+ if (!ndo->ndo_eflag)
+ ND_PRINT("IP6 ");
+
+ if (IP6_VERSION(ip6) != 6) {
+ ND_PRINT("version error: %u != 6", IP6_VERSION(ip6));
+ return;
+ }
+
+ payload_len = GET_BE_U_2(ip6->ip6_plen);
+ /*
+ * RFC 1883 says:
+ *
+ * The Payload Length field in the IPv6 header must be set to zero
+ * in every packet that carries the Jumbo Payload option. If a
+ * packet is received with a valid Jumbo Payload option present and
+ * a non-zero IPv6 Payload Length field, an ICMP Parameter Problem
+ * message, Code 0, should be sent to the packet's source, pointing
+ * to the Option Type field of the Jumbo Payload option.
+ *
+ * Later versions of the IPv6 spec don't discuss the Jumbo Payload
+ * option.
+ *
+ * If the payload length is 0, we temporarily just set the total
+ * length to the remaining data in the packet (which, for Ethernet,
+ * could include frame padding, but if it's a Jumbo Payload frame,
+ * it shouldn't even be sendable over Ethernet, so we don't worry
+ * about that), so we can process the extension headers in order
+ * to *find* a Jumbo Payload hop-by-hop option and, when we've
+ * processed all the extension headers, check whether we found
+ * a Jumbo Payload option, and fail if we haven't.
+ */
+ if (payload_len != 0) {
+ len = payload_len + sizeof(struct ip6_hdr);
+ if (length < len)
+ ND_PRINT("truncated-ip6 - %u bytes missing!",
+ len - length);
+ } else
+ len = length + sizeof(struct ip6_hdr);
+
+ nh = GET_U_1(ip6->ip6_nxt);
+ if (ndo->ndo_vflag) {
+ flow = GET_BE_U_4(ip6->ip6_flow);
+ ND_PRINT("(");
+#if 0
+ /* rfc1883 */
+ if (flow & 0x0f000000)
+ ND_PRINT("pri 0x%02x, ", (flow & 0x0f000000) >> 24);
+ if (flow & 0x00ffffff)
+ ND_PRINT("flowlabel 0x%06x, ", flow & 0x00ffffff);
+#else
+ /* RFC 2460 */
+ if (flow & 0x0ff00000)
+ ND_PRINT("class 0x%02x, ", (flow & 0x0ff00000) >> 20);
+ if (flow & 0x000fffff)
+ ND_PRINT("flowlabel 0x%05x, ", flow & 0x000fffff);
+#endif
+
+ ND_PRINT("hlim %u, next-header %s (%u) payload length: %u) ",
+ GET_U_1(ip6->ip6_hlim),
+ tok2str(ipproto_values,"unknown",nh),
+ nh,
+ payload_len);
+ }
+
+ /*
+ * Cut off the snapshot length to the end of the IP payload.
+ */
+ nd_push_snapend(ndo, bp + len);
+
+ cp = (const u_char *)ip6;
+ advance = sizeof(struct ip6_hdr);
+ total_advance = 0;
+ /* Process extension headers */
+ found_extension_header = 0;
+ found_jumbo = 0;
+ while (cp < ndo->ndo_snapend && advance > 0) {
+ if (len < (u_int)advance)
+ goto trunc;
+ cp += advance;
+ len -= advance;
+ total_advance += advance;
+
+ if (cp == (const u_char *)(ip6 + 1) &&
+ nh != IPPROTO_TCP && nh != IPPROTO_UDP &&
+ nh != IPPROTO_DCCP && nh != IPPROTO_SCTP) {
+ ND_PRINT("%s > %s: ", GET_IP6ADDR_STRING(ip6->ip6_src),
+ GET_IP6ADDR_STRING(ip6->ip6_dst));
+ }
+
+ switch (nh) {
+
+ case IPPROTO_HOPOPTS:
+ advance = hbhopt_process(ndo, cp, &found_jumbo, &payload_len);
+ if (advance < 0) {
+ nd_pop_packet_info(ndo);
+ return;
+ }
+ found_extension_header = 1;
+ nh = GET_U_1(cp);
+ break;
+
+ case IPPROTO_DSTOPTS:
+ advance = dstopt_process(ndo, cp);
+ if (advance < 0) {
+ nd_pop_packet_info(ndo);
+ return;
+ }
+ found_extension_header = 1;
+ nh = GET_U_1(cp);
+ break;
+
+ case IPPROTO_FRAGMENT:
+ advance = frag6_print(ndo, cp, (const u_char *)ip6);
+ if (advance < 0 || ndo->ndo_snapend <= cp + advance) {
+ nd_pop_packet_info(ndo);
+ return;
+ }
+ found_extension_header = 1;
+ nh = GET_U_1(cp);
+ fragmented = 1;
+ break;
+
+ case IPPROTO_MOBILITY_OLD:
+ case IPPROTO_MOBILITY:
+ /*
+ * XXX - we don't use "advance"; RFC 3775 says that
+ * the next header field in a mobility header
+ * should be IPPROTO_NONE, but speaks of
+ * the possibility of a future extension in
+ * which payload can be piggybacked atop a
+ * mobility header.
+ */
+ advance = mobility_print(ndo, cp, (const u_char *)ip6);
+ if (advance < 0) {
+ nd_pop_packet_info(ndo);
+ return;
+ }
+ found_extension_header = 1;
+ nh = GET_U_1(cp);
+ nd_pop_packet_info(ndo);
+ return;
+
+ case IPPROTO_ROUTING:
+ ND_TCHECK_1(cp);
+ advance = rt6_print(ndo, cp, (const u_char *)ip6);
+ if (advance < 0) {
+ nd_pop_packet_info(ndo);
+ return;
+ }
+ found_extension_header = 1;
+ nh = GET_U_1(cp);
+ break;
+
+ default:
+ /*
+ * Not an extension header; hand off to the
+ * IP protocol demuxer.
+ */
+ if (found_jumbo) {
+ /*
+ * We saw a Jumbo Payload option.
+ * Set the length to the payload length
+ * plus the IPv6 header length, and
+ * change the snapshot length accordingly.
+ *
+ * But make sure it's not shorter than
+ * the total number of bytes we've
+ * processed so far.
+ */
+ len = payload_len + sizeof(struct ip6_hdr);
+ if (len < total_advance)
+ goto trunc;
+ if (length < len)
+ ND_PRINT("truncated-ip6 - %u bytes missing!",
+ len - length);
+ nd_change_snapend(ndo, bp + len);
+
+ /*
+ * Now subtract the length of the IPv6
+ * header plus extension headers to get
+ * the payload length.
+ */
+ len -= total_advance;
+ } else {
+ /*
+ * We didn't see a Jumbo Payload option;
+ * was the payload length zero?
+ */
+ if (payload_len == 0) {
+ /*
+ * Yes. If we found an extension
+ * header, treat that as a truncated
+ * packet header, as there was
+ * no payload to contain an
+ * extension header.
+ */
+ if (found_extension_header)
+ goto trunc;
+
+ /*
+ * OK, we didn't see any extnesion
+ * header, but that means we have
+ * no payload, so set the length
+ * to the IPv6 header length,
+ * and change the snapshot length
+ * accordingly.
+ */
+ len = sizeof(struct ip6_hdr);
+ nd_change_snapend(ndo, bp + len);
+
+ /*
+ * Now subtract the length of
+ * the IPv6 header plus extension
+ * headers (there weren't any, so
+ * that's just the IPv6 header
+ * length) to get the payload length.
+ */
+ len -= total_advance;
+ }
+ }
+ ip_demux_print(ndo, cp, len, 6, fragmented,
+ GET_U_1(ip6->ip6_hlim), nh, bp);
+ nd_pop_packet_info(ndo);
+ return;
+ }
+
+ /* ndo_protocol reassignment after xxx_print() calls */
+ ndo->ndo_protocol = "ip6";
+ }
+
+ nd_pop_packet_info(ndo);
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-ip6opts.c b/print-ip6opts.c
new file mode 100644
index 0000000..a78c76d
--- /dev/null
+++ b/print-ip6opts.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: IPv6 header option printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip6.h"
+
+static int
+ip6_sopt_print(netdissect_options *ndo, const u_char *bp, int len)
+{
+ int i;
+ int optlen;
+
+ for (i = 0; i < len; i += optlen) {
+ if (GET_U_1(bp + i) == IP6OPT_PAD1)
+ optlen = 1;
+ else {
+ if (i + 1 < len)
+ optlen = GET_U_1(bp + i + 1) + 2;
+ else
+ goto trunc;
+ }
+ if (i + optlen > len)
+ goto trunc;
+
+ switch (GET_U_1(bp + i)) {
+ case IP6OPT_PAD1:
+ ND_PRINT(", pad1");
+ break;
+ case IP6OPT_PADN:
+ if (len - i < IP6OPT_MINLEN) {
+ ND_PRINT(", padn: trunc");
+ goto trunc;
+ }
+ ND_PRINT(", padn");
+ break;
+ default:
+ if (len - i < IP6OPT_MINLEN) {
+ ND_PRINT(", sopt_type %u: trunc)", GET_U_1(bp + i));
+ goto trunc;
+ }
+ ND_PRINT(", sopt_type 0x%02x: len=%u", GET_U_1(bp + i),
+ GET_U_1(bp + i + 1));
+ break;
+ }
+ }
+ return 0;
+
+trunc:
+ return -1;
+}
+
+static int
+ip6_opt_process(netdissect_options *ndo, const u_char *bp, int len,
+ int *found_jumbop, uint32_t *payload_len)
+{
+ int i;
+ int optlen = 0;
+ int found_jumbo = 0;
+ uint32_t jumbolen = 0;
+
+ if (len == 0)
+ return 0;
+ for (i = 0; i < len; i += optlen) {
+ if (GET_U_1(bp + i) == IP6OPT_PAD1)
+ optlen = 1;
+ else {
+ if (i + 1 < len)
+ optlen = GET_U_1(bp + i + 1) + 2;
+ else
+ goto trunc;
+ }
+ if (i + optlen > len)
+ goto trunc;
+
+ switch (GET_U_1(bp + i)) {
+ case IP6OPT_PAD1:
+ if (ndo->ndo_vflag)
+ ND_PRINT("(pad1)");
+ break;
+ case IP6OPT_PADN:
+ if (len - i < IP6OPT_MINLEN) {
+ ND_PRINT("(padn: trunc)");
+ goto trunc;
+ }
+ if (ndo->ndo_vflag)
+ ND_PRINT("(padn)");
+ break;
+ case IP6OPT_ROUTER_ALERT:
+ if (len - i < IP6OPT_RTALERT_LEN) {
+ ND_PRINT("(rtalert: trunc)");
+ goto trunc;
+ }
+ if (GET_U_1(bp + i + 1) != IP6OPT_RTALERT_LEN - 2) {
+ ND_PRINT("(rtalert: invalid len %u)", GET_U_1(bp + i + 1));
+ goto trunc;
+ }
+ if (ndo->ndo_vflag)
+ ND_PRINT("(rtalert: 0x%04x) ", GET_BE_U_2(bp + i + 2));
+ break;
+ case IP6OPT_JUMBO:
+ if (len - i < IP6OPT_JUMBO_LEN) {
+ ND_PRINT("(jumbo: trunc)");
+ goto trunc;
+ }
+ if (GET_U_1(bp + i + 1) != IP6OPT_JUMBO_LEN - 2) {
+ ND_PRINT("(jumbo: invalid len %u)", GET_U_1(bp + i + 1));
+ goto trunc;
+ }
+ jumbolen = GET_BE_U_4(bp + i + 2);
+ if (found_jumbo) {
+ /* More than one Jumbo Payload option */
+ if (ndo->ndo_vflag)
+ ND_PRINT("(jumbo: %u - already seen) ", jumbolen);
+ } else {
+ found_jumbo = 1;
+ if (payload_len == NULL) {
+ /* Not a hop-by-hop option - not valid */
+ if (ndo->ndo_vflag)
+ ND_PRINT("(jumbo: %u - not a hop-by-hop option) ", jumbolen);
+ } else if (*payload_len != 0) {
+ /* Payload length was non-zero - not valid */
+ if (ndo->ndo_vflag)
+ ND_PRINT("(jumbo: %u - payload len != 0) ", jumbolen);
+ } else {
+ /*
+ * This is a hop-by-hop option, and Payload length
+ * was zero in the IPv6 header.
+ */
+ if (jumbolen < 65536) {
+ /* Too short */
+ if (ndo->ndo_vflag)
+ ND_PRINT("(jumbo: %u - < 65536) ", jumbolen);
+ } else {
+ /* OK, this is valid */
+ *found_jumbop = 1;
+ *payload_len = jumbolen;
+ if (ndo->ndo_vflag)
+ ND_PRINT("(jumbo: %u) ", jumbolen);
+ }
+ }
+ }
+ break;
+ case IP6OPT_HOME_ADDRESS:
+ if (len - i < IP6OPT_HOMEADDR_MINLEN) {
+ ND_PRINT("(homeaddr: trunc)");
+ goto trunc;
+ }
+ if (GET_U_1(bp + i + 1) < IP6OPT_HOMEADDR_MINLEN - 2) {
+ ND_PRINT("(homeaddr: invalid len %u)", GET_U_1(bp + i + 1));
+ goto trunc;
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT("(homeaddr: %s", GET_IP6ADDR_STRING(bp + i + 2));
+ if (GET_U_1(bp + i + 1) > IP6OPT_HOMEADDR_MINLEN - 2) {
+ if (ip6_sopt_print(ndo, bp + i + IP6OPT_HOMEADDR_MINLEN,
+ (optlen - IP6OPT_HOMEADDR_MINLEN)) == -1)
+ goto trunc;
+ }
+ ND_PRINT(")");
+ }
+ break;
+ default:
+ if (len - i < IP6OPT_MINLEN) {
+ ND_PRINT("(type %u: trunc)", GET_U_1(bp + i));
+ goto trunc;
+ }
+ if (ndo->ndo_vflag)
+ ND_PRINT("(opt_type 0x%02x: len=%u)", GET_U_1(bp + i),
+ GET_U_1(bp + i + 1));
+ break;
+ }
+ }
+ if (ndo->ndo_vflag)
+ ND_PRINT(" ");
+ return 0;
+
+trunc:
+ return -1;
+}
+
+int
+hbhopt_process(netdissect_options *ndo, const u_char *bp, int *found_jumbo,
+ uint32_t *jumbolen)
+{
+ const struct ip6_hbh *dp = (const struct ip6_hbh *)bp;
+ u_int hbhlen = 0;
+
+ ndo->ndo_protocol = "hbhopt";
+ hbhlen = (GET_U_1(dp->ip6h_len) + 1) << 3;
+ ND_TCHECK_LEN(dp, hbhlen);
+ ND_PRINT("HBH ");
+ if (ip6_opt_process(ndo, (const u_char *)dp + sizeof(*dp),
+ hbhlen - sizeof(*dp), found_jumbo, jumbolen) == -1)
+ goto trunc;
+ return hbhlen;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+int
+dstopt_process(netdissect_options *ndo, const u_char *bp)
+{
+ const struct ip6_dest *dp = (const struct ip6_dest *)bp;
+ u_int dstoptlen = 0;
+
+ ndo->ndo_protocol = "dstopt";
+ dstoptlen = (GET_U_1(dp->ip6d_len) + 1) << 3;
+ ND_TCHECK_LEN(dp, dstoptlen);
+ ND_PRINT("DSTOPT ");
+ if (ndo->ndo_vflag) {
+ /*
+ * The Jumbo Payload option is a hop-by-hop option; we don't
+ * honor Jumbo Payload destination options, reporting them
+ * as invalid.
+ */
+ if (ip6_opt_process(ndo, (const u_char *)dp + sizeof(*dp),
+ dstoptlen - sizeof(*dp), NULL, NULL) == -1)
+ goto trunc;
+ }
+
+ return dstoptlen;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
diff --git a/print-ipcomp.c b/print-ipcomp.c
new file mode 100644
index 0000000..c0c184d
--- /dev/null
+++ b/print-ipcomp.c
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IP Payload Compression Protocol (IPComp) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+struct ipcomp {
+ nd_uint8_t comp_nxt; /* Next Header */
+ nd_uint8_t comp_flags; /* Length of data, in 32bit */
+ nd_uint16_t comp_cpi; /* Compression parameter index */
+};
+
+void
+ipcomp_print(netdissect_options *ndo, const u_char *bp)
+{
+ const struct ipcomp *ipcomp;
+ uint16_t cpi;
+
+ ndo->ndo_protocol = "ipcomp";
+ ipcomp = (const struct ipcomp *)bp;
+ cpi = GET_BE_U_2(ipcomp->comp_cpi);
+
+ ND_PRINT("IPComp(cpi=0x%04x)", cpi);
+
+ /*
+ * XXX - based on the CPI, we could decompress the packet here.
+ * Packet buffer management is a headache (if we decompress,
+ * packet will become larger).
+ *
+ * We would decompress the packet and then call a routine that,
+ * based on ipcomp->comp_nxt, dissects the decompressed data.
+ *
+ * Until we do that, however, we just return -1, so that
+ * the loop that processes "protocol"/"next header" types
+ * stops - there's nothing more it can do with a compressed
+ * payload.
+ */
+}
diff --git a/print-ipfc.c b/print-ipfc.c
new file mode 100644
index 0000000..ab5a813
--- /dev/null
+++ b/print-ipfc.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IP over Fibre Channel printer */
+
+/* specification: RFC 2625 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+
+
+struct ipfc_header {
+ nd_byte ipfc_dhost[2+MAC_ADDR_LEN];
+ nd_byte ipfc_shost[2+MAC_ADDR_LEN];
+};
+
+#define IPFC_HDRLEN 16
+
+/* Extract src, dst addresses */
+static void
+extract_ipfc_addrs(const struct ipfc_header *ipfcp, char *ipfcsrc,
+ char *ipfcdst)
+{
+ /*
+ * We assume that, as per RFC 2625, the lower 48 bits of the
+ * source and destination addresses are MAC addresses.
+ */
+ memcpy(ipfcdst, (const char *)&ipfcp->ipfc_dhost[2], MAC_ADDR_LEN);
+ memcpy(ipfcsrc, (const char *)&ipfcp->ipfc_shost[2], MAC_ADDR_LEN);
+}
+
+/*
+ * Print the Network_Header
+ */
+static void
+ipfc_hdr_print(netdissect_options *ndo,
+ const struct ipfc_header *ipfcp _U_, u_int length,
+ const u_char *ipfcsrc, const u_char *ipfcdst)
+{
+ const char *srcname, *dstname;
+
+ srcname = etheraddr_string(ndo, ipfcsrc);
+ dstname = etheraddr_string(ndo, ipfcdst);
+
+ /*
+ * XXX - should we show the upper 16 bits of the addresses?
+ * Do so only if "vflag" is set?
+ * Section 3.3 "FC Port and Node Network Addresses" says that
+ *
+ * In this specification, both the Source and Destination
+ * 4-bit NAA identifiers SHALL be set to binary '0001'
+ * indicating that an IEEE 48-bit MAC address is contained
+ * in the lower 48 bits of the network address fields. The
+ * high order 12 bits in the network address fields SHALL
+ * be set to 0x0000.
+ *
+ * so, for captures following this specification, the upper 16
+ * bits should be 0x1000, followed by a MAC address.
+ */
+ ND_PRINT("%s > %s, length %u: ", srcname, dstname, length);
+}
+
+static u_int
+ipfc_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen)
+{
+ const struct ipfc_header *ipfcp = (const struct ipfc_header *)p;
+ nd_mac_addr srcmac, dstmac;
+ struct lladdr_info src, dst;
+ int llc_hdrlen;
+
+ ndo->ndo_protocol = "ipfc";
+ ND_TCHECK_LEN(p, IPFC_HDRLEN);
+ /*
+ * Get the network addresses into a canonical form
+ */
+ extract_ipfc_addrs(ipfcp, (char *)srcmac, (char *)dstmac);
+
+ if (ndo->ndo_eflag)
+ ipfc_hdr_print(ndo, ipfcp, length, srcmac, dstmac);
+
+ src.addr = srcmac;
+ src.addr_string = etheraddr_string;
+ dst.addr = dstmac;
+ dst.addr_string = etheraddr_string;
+
+ /* Skip over Network_Header */
+ length -= IPFC_HDRLEN;
+ p += IPFC_HDRLEN;
+ caplen -= IPFC_HDRLEN;
+
+ /* Try to print the LLC-layer header & higher layers */
+ llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst);
+ if (llc_hdrlen < 0) {
+ /*
+ * Some kinds of LLC packet we cannot
+ * handle intelligently
+ */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = -llc_hdrlen;
+ }
+ return (IPFC_HDRLEN + llc_hdrlen);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the Network_Header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+ipfc_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "ipfc";
+ ndo->ndo_ll_hdr_len += ipfc_print(ndo, p, h->len, h->caplen);
+}
diff --git a/print-ipnet.c b/print-ipnet.c
new file mode 100644
index 0000000..eddcc1d
--- /dev/null
+++ b/print-ipnet.c
@@ -0,0 +1,110 @@
+/* \summary: Solaris DLT_IPNET printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+
+typedef struct ipnet_hdr {
+ nd_uint8_t iph_version;
+ nd_uint8_t iph_family;
+ nd_uint16_t iph_htype;
+ nd_uint32_t iph_pktlen;
+ nd_uint32_t iph_ifindex;
+ nd_uint32_t iph_grifindex;
+ nd_uint32_t iph_zsrc;
+ nd_uint32_t iph_zdst;
+} ipnet_hdr_t;
+
+#define IPH_AF_INET 2 /* Matches Solaris's AF_INET */
+#define IPH_AF_INET6 26 /* Matches Solaris's AF_INET6 */
+
+#ifdef DLT_IPNET
+
+static const struct tok ipnet_values[] = {
+ { IPH_AF_INET, "IPv4" },
+ { IPH_AF_INET6, "IPv6" },
+ { 0, NULL }
+};
+
+static void
+ipnet_hdr_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const ipnet_hdr_t *hdr;
+ hdr = (const ipnet_hdr_t *)bp;
+
+ ND_PRINT("%u > %u", GET_BE_U_4(hdr->iph_zsrc),
+ GET_BE_U_4(hdr->iph_zdst));
+
+ if (!ndo->ndo_qflag) {
+ ND_PRINT(", family %s (%u)",
+ tok2str(ipnet_values, "Unknown",
+ GET_U_1(hdr->iph_family)),
+ GET_U_1(hdr->iph_family));
+ } else {
+ ND_PRINT(", %s",
+ tok2str(ipnet_values,
+ "Unknown Ethertype (0x%04x)",
+ GET_U_1(hdr->iph_family)));
+ }
+
+ ND_PRINT(", length %u: ", length);
+}
+
+static void
+ipnet_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen)
+{
+ const ipnet_hdr_t *hdr;
+
+ ND_TCHECK_LEN(p, sizeof(ipnet_hdr_t));
+ ndo->ndo_ll_hdr_len += sizeof(ipnet_hdr_t);
+
+ if (ndo->ndo_eflag)
+ ipnet_hdr_print(ndo, p, length);
+
+ length -= sizeof(ipnet_hdr_t);
+ caplen -= sizeof(ipnet_hdr_t);
+ hdr = (const ipnet_hdr_t *)p;
+ p += sizeof(ipnet_hdr_t);
+
+ switch (GET_U_1(hdr->iph_family)) {
+
+ case IPH_AF_INET:
+ ip_print(ndo, p, length);
+ break;
+
+ case IPH_AF_INET6:
+ ip6_print(ndo, p, length);
+ break;
+
+ default:
+ if (!ndo->ndo_eflag)
+ ipnet_hdr_print(ndo, (const u_char *)hdr,
+ length + sizeof(ipnet_hdr_t));
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ break;
+ }
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+ipnet_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "ipnet";
+ ipnet_print(ndo, p, h->len, h->caplen);
+}
+#endif /* DLT_IPNET */
diff --git a/print-ipoib.c b/print-ipoib.c
new file mode 100644
index 0000000..3d43123
--- /dev/null
+++ b/print-ipoib.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996,
+ * 1997, 2000, 2011, 2012
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+/*
+ * Copyright (c) 2011, 2016, Oracle and/or its affiliates. All rights reserved.
+ */
+
+/* \summary: IP-over-InfiniBand (IPoIB) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+extern const struct tok ethertype_values[];
+
+#define IPOIB_HDRLEN 44
+
+static inline void
+ipoib_hdr_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ uint16_t ether_type;
+
+ ether_type = GET_BE_U_2(bp + 40);
+ if (!ndo->ndo_qflag) {
+ ND_PRINT(", ethertype %s (0x%04x)",
+ tok2str(ethertype_values,"Unknown", ether_type),
+ ether_type);
+ } else {
+ ND_PRINT(", ethertype %s",
+ tok2str(ethertype_values,"Unknown", ether_type));
+ }
+
+ ND_PRINT(", length %u: ", length);
+}
+
+/*
+ * Print an InfiniBand frame.
+ * This might be encapsulated within another frame; we might be passed
+ * a pointer to a function that can print header information for that
+ * frame's protocol, and an argument to pass to that function.
+ */
+static void
+ipoib_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen,
+ void (*print_encap_header)(const u_char *), const u_char *encap_header_arg)
+{
+ const u_char *orig_hdr = p;
+ u_int orig_length;
+ u_short ether_type;
+
+ if (caplen < IPOIB_HDRLEN) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ if (length < IPOIB_HDRLEN) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += length;
+ return;
+ }
+
+ if (ndo->ndo_eflag) {
+ nd_print_protocol_caps(ndo);
+ if (print_encap_header != NULL)
+ (*print_encap_header)(encap_header_arg);
+ ipoib_hdr_print(ndo, p, length);
+ }
+ orig_length = length;
+
+ ndo->ndo_ll_hdr_len += IPOIB_HDRLEN;
+ length -= IPOIB_HDRLEN;
+ caplen -= IPOIB_HDRLEN;
+ ether_type = GET_BE_U_2(p + 40);
+ p += IPOIB_HDRLEN;
+
+ if (ethertype_print(ndo, ether_type, p, length, caplen, NULL, NULL) == 0) {
+ /* ether_type not known, print raw packet */
+ if (!ndo->ndo_eflag) {
+ if (print_encap_header != NULL)
+ (*print_encap_header)(encap_header_arg);
+ ipoib_hdr_print(ndo, orig_hdr , orig_length);
+ }
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+ipoib_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "ipoib";
+ ipoib_print(ndo, p, h->len, h->caplen, NULL, NULL);
+}
diff --git a/print-ipx.c b/print-ipx.c
new file mode 100644
index 0000000..c16a867
--- /dev/null
+++ b/print-ipx.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Contributed by Brad Parker (brad@fcr.com).
+ */
+
+/* \summary: Novell IPX printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+/* well-known sockets */
+#define IPX_SKT_NCP 0x0451
+#define IPX_SKT_SAP 0x0452
+#define IPX_SKT_RIP 0x0453
+#define IPX_SKT_NETBIOS 0x0455
+#define IPX_SKT_DIAGNOSTICS 0x0456
+#define IPX_SKT_NWLINK_DGM 0x0553 /* NWLink datagram, may contain SMB */
+#define IPX_SKT_EIGRP 0x85be /* Cisco EIGRP over IPX */
+
+/* IPX transport header */
+struct ipxHdr {
+ nd_uint16_t cksum; /* Checksum */
+ nd_uint16_t length; /* Length, in bytes, including header */
+ nd_uint8_t tCtl; /* Transport Control (i.e. hop count) */
+ nd_uint8_t pType; /* Packet Type (i.e. level 2 protocol) */
+ nd_uint32_t dstNet; /* destination net */
+ nd_mac_addr dstNode; /* destination node */
+ nd_uint16_t dstSkt; /* destination socket */
+ nd_uint32_t srcNet; /* source net */
+ nd_mac_addr srcNode; /* source node */
+ nd_uint16_t srcSkt; /* source socket */
+};
+
+#define ipxSize 30
+
+static const char *ipxaddr_string(netdissect_options *, uint32_t, const u_char *);
+static void ipx_decode(netdissect_options *, const struct ipxHdr *, const u_char *, u_int);
+static void ipx_sap_print(netdissect_options *, const u_char *, u_int);
+static void ipx_rip_print(netdissect_options *, const u_char *, u_int);
+
+/*
+ * Print IPX datagram packets.
+ */
+void
+ipx_print(netdissect_options *ndo, const u_char *p, u_int length)
+{
+ const struct ipxHdr *ipx = (const struct ipxHdr *)p;
+
+ ndo->ndo_protocol = "ipx";
+ if (!ndo->ndo_eflag)
+ ND_PRINT("IPX ");
+
+ ND_PRINT("%s.%04x > ",
+ ipxaddr_string(ndo, GET_BE_U_4(ipx->srcNet), ipx->srcNode),
+ GET_BE_U_2(ipx->srcSkt));
+
+ ND_PRINT("%s.%04x: ",
+ ipxaddr_string(ndo, GET_BE_U_4(ipx->dstNet), ipx->dstNode),
+ GET_BE_U_2(ipx->dstSkt));
+
+ /* take length from ipx header */
+ length = GET_BE_U_2(ipx->length);
+
+ if (length < ipxSize) {
+ ND_PRINT("[length %u < %u]", length, ipxSize);
+ nd_print_invalid(ndo);
+ return;
+ }
+ ipx_decode(ndo, ipx, p + ipxSize, length - ipxSize);
+}
+
+static const char *
+ipxaddr_string(netdissect_options *ndo, uint32_t net, const u_char *node)
+{
+ static char line[256];
+
+ snprintf(line, sizeof(line), "%08x.%02x:%02x:%02x:%02x:%02x:%02x",
+ net, GET_U_1(node), GET_U_1(node + 1),
+ GET_U_1(node + 2), GET_U_1(node + 3),
+ GET_U_1(node + 4), GET_U_1(node + 5));
+
+ return line;
+}
+
+static void
+ipx_decode(netdissect_options *ndo, const struct ipxHdr *ipx, const u_char *datap, u_int length)
+{
+ u_short dstSkt;
+
+ dstSkt = GET_BE_U_2(ipx->dstSkt);
+ switch (dstSkt) {
+ case IPX_SKT_NCP:
+ ND_PRINT("ipx-ncp %u", length);
+ break;
+ case IPX_SKT_SAP:
+ ipx_sap_print(ndo, datap, length);
+ break;
+ case IPX_SKT_RIP:
+ ipx_rip_print(ndo, datap, length);
+ break;
+ case IPX_SKT_NETBIOS:
+ ND_PRINT("ipx-netbios %u", length);
+#ifdef ENABLE_SMB
+ ipx_netbios_print(ndo, datap, length);
+#endif
+ break;
+ case IPX_SKT_DIAGNOSTICS:
+ ND_PRINT("ipx-diags %u", length);
+ break;
+ case IPX_SKT_NWLINK_DGM:
+ ND_PRINT("ipx-nwlink-dgm %u", length);
+#ifdef ENABLE_SMB
+ ipx_netbios_print(ndo, datap, length);
+#endif
+ break;
+ case IPX_SKT_EIGRP:
+ eigrp_print(ndo, datap, length);
+ break;
+ default:
+ ND_PRINT("ipx-#%x %u", dstSkt, length);
+ break;
+ }
+}
+
+static void
+ipx_sap_print(netdissect_options *ndo, const u_char *ipx, u_int length)
+{
+ int command, i;
+
+ command = GET_BE_U_2(ipx);
+ ipx += 2;
+ length -= 2;
+
+ switch (command) {
+ case 1:
+ case 3:
+ if (command == 1)
+ ND_PRINT("ipx-sap-req");
+ else
+ ND_PRINT("ipx-sap-nearest-req");
+
+ ND_PRINT(" %s", ipxsap_string(ndo, htons(GET_BE_U_2(ipx))));
+ break;
+
+ case 2:
+ case 4:
+ if (command == 2)
+ ND_PRINT("ipx-sap-resp");
+ else
+ ND_PRINT("ipx-sap-nearest-resp");
+
+ for (i = 0; i < 8 && length != 0; i++) {
+ ND_TCHECK_2(ipx);
+ if (length < 2)
+ goto invalid;
+ ND_PRINT(" %s '", ipxsap_string(ndo, htons(GET_BE_U_2(ipx))));
+ ipx += 2;
+ length -= 2;
+ if (length < 48) {
+ ND_PRINT("'");
+ goto invalid;
+ }
+ nd_printjnp(ndo, ipx, 48);
+ ND_PRINT("'");
+ ipx += 48;
+ length -= 48;
+ /*
+ * 10 bytes of IPX address.
+ */
+ ND_TCHECK_LEN(ipx, 10);
+ if (length < 10)
+ goto invalid;
+ ND_PRINT(" addr %s",
+ ipxaddr_string(ndo, GET_BE_U_4(ipx), ipx + 4));
+ ipx += 10;
+ length -= 10;
+ /*
+ * 2 bytes of socket and 2 bytes of number of intermediate
+ * networks.
+ */
+ ND_TCHECK_4(ipx);
+ if (length < 4)
+ goto invalid;
+ ipx += 4;
+ length -= 4;
+ }
+ break;
+ default:
+ ND_PRINT("ipx-sap-?%x", command);
+ break;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+
+static void
+ipx_rip_print(netdissect_options *ndo, const u_char *ipx, u_int length)
+{
+ int command, i;
+
+ command = GET_BE_U_2(ipx);
+ ipx += 2;
+ length -= 2;
+
+ switch (command) {
+ case 1:
+ ND_PRINT("ipx-rip-req");
+ if (length != 0) {
+ if (length < 8)
+ goto invalid;
+ ND_PRINT(" %08x/%u.%u", GET_BE_U_4(ipx),
+ GET_BE_U_2(ipx + 4), GET_BE_U_2(ipx + 6));
+ }
+ break;
+ case 2:
+ ND_PRINT("ipx-rip-resp");
+ for (i = 0; i < 50 && length != 0; i++) {
+ if (length < 8)
+ goto invalid;
+ ND_PRINT(" %08x/%u.%u", GET_BE_U_4(ipx),
+ GET_BE_U_2(ipx + 4), GET_BE_U_2(ipx + 6));
+
+ ipx += 8;
+ length -= 8;
+ }
+ break;
+ default:
+ ND_PRINT("ipx-rip-?%x", command);
+ break;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-isakmp.c b/print-isakmp.c
new file mode 100644
index 0000000..52bf1fd
--- /dev/null
+++ b/print-isakmp.c
@@ -0,0 +1,3141 @@
+/*
+ * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* \summary: Internet Security Association and Key Management Protocol (ISAKMP) printer */
+
+/* specification: RFC 2407, RFC 2408, RFC 5996 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/* The functions from print-esp.c used in this file are only defined when both
+ * OpenSSL and evp.h are detected. Employ the same preprocessor device here.
+ */
+#ifndef HAVE_OPENSSL_EVP_H
+#undef HAVE_LIBCRYPTO
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip.h"
+#include "ip6.h"
+#include "ipproto.h"
+
+typedef nd_byte cookie_t[8];
+typedef nd_byte msgid_t[4];
+
+#define PORT_ISAKMP 500
+
+/* 3.1 ISAKMP Header Format (IKEv1 and IKEv2)
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! Initiator !
+ ! Cookie !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! Responder !
+ ! Cookie !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! Next Payload ! MjVer ! MnVer ! Exchange Type ! Flags !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! Message ID !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! Length !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+struct isakmp {
+ cookie_t i_ck; /* Initiator Cookie */
+ cookie_t r_ck; /* Responder Cookie */
+ nd_uint8_t np; /* Next Payload Type */
+ nd_uint8_t vers;
+#define ISAKMP_VERS_MAJOR 0xf0
+#define ISAKMP_VERS_MAJOR_SHIFT 4
+#define ISAKMP_VERS_MINOR 0x0f
+#define ISAKMP_VERS_MINOR_SHIFT 0
+ nd_uint8_t etype; /* Exchange Type */
+ nd_uint8_t flags; /* Flags */
+ msgid_t msgid;
+ nd_uint32_t len; /* Length */
+};
+
+/* Next Payload Type */
+#define ISAKMP_NPTYPE_NONE 0 /* NONE*/
+#define ISAKMP_NPTYPE_SA 1 /* Security Association */
+#define ISAKMP_NPTYPE_P 2 /* Proposal */
+#define ISAKMP_NPTYPE_T 3 /* Transform */
+#define ISAKMP_NPTYPE_KE 4 /* Key Exchange */
+#define ISAKMP_NPTYPE_ID 5 /* Identification */
+#define ISAKMP_NPTYPE_CERT 6 /* Certificate */
+#define ISAKMP_NPTYPE_CR 7 /* Certificate Request */
+#define ISAKMP_NPTYPE_HASH 8 /* Hash */
+#define ISAKMP_NPTYPE_SIG 9 /* Signature */
+#define ISAKMP_NPTYPE_NONCE 10 /* Nonce */
+#define ISAKMP_NPTYPE_N 11 /* Notification */
+#define ISAKMP_NPTYPE_D 12 /* Delete */
+#define ISAKMP_NPTYPE_VID 13 /* Vendor ID */
+#define ISAKMP_NPTYPE_v2E 46 /* v2 Encrypted payload */
+
+#define IKEv1_MAJOR_VERSION 1
+#define IKEv1_MINOR_VERSION 0
+
+#define IKEv2_MAJOR_VERSION 2
+#define IKEv2_MINOR_VERSION 0
+
+/* Flags */
+#define ISAKMP_FLAG_E 0x01 /* Encryption Bit */
+#define ISAKMP_FLAG_C 0x02 /* Commit Bit */
+#define ISAKMP_FLAG_extra 0x04
+
+/* IKEv2 */
+#define ISAKMP_FLAG_I (1 << 3) /* (I)nitiator */
+#define ISAKMP_FLAG_V (1 << 4) /* (V)ersion */
+#define ISAKMP_FLAG_R (1 << 5) /* (R)esponse */
+
+
+/* 3.2 Payload Generic Header
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ ! Next Payload ! RESERVED ! Payload Length !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+struct isakmp_gen {
+ nd_uint8_t np; /* Next Payload */
+ nd_uint8_t critical; /* bit 7 - critical, rest is RESERVED */
+ nd_uint16_t len; /* Payload Length */
+};
+
+/* 3.3 Data Attributes
+ 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ !A! Attribute Type ! AF=0 Attribute Length !
+ !F! ! AF=1 Attribute Value !
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ . AF=0 Attribute Value .
+ . AF=1 Not Transmitted .
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+struct isakmp_data {
+ nd_uint16_t type; /* defined by DOI-spec, and Attribute Format */
+ nd_uint16_t lorv; /* if f equal 1, Attribute Length */
+ /* if f equal 0, Attribute Value */
+ /* if f equal 1, Attribute Value */
+};
+
+/* 3.4 Security Association Payload */
+ /* MAY NOT be used, because of being defined in ipsec-doi. */
+ /*
+ If the current payload is the last in the message,
+ then the value of the next payload field will be 0.
+ This field MUST NOT contain the
+ values for the Proposal or Transform payloads as they are considered
+ part of the security association negotiation. For example, this
+ field would contain the value "10" (Nonce payload) in the first
+ message of a Base Exchange (see Section 4.4) and the value "0" in the
+ first message of an Identity Protect Exchange (see Section 4.5).
+ */
+struct ikev1_pl_sa {
+ struct isakmp_gen h;
+ nd_uint32_t doi; /* Domain of Interpretation */
+ nd_uint32_t sit; /* Situation */
+};
+
+/* 3.5 Proposal Payload */
+ /*
+ The value of the next payload field MUST only contain the value "2"
+ or "0". If there are additional Proposal payloads in the message,
+ then this field will be 2. If the current Proposal payload is the
+ last within the security association proposal, then this field will
+ be 0.
+ */
+struct ikev1_pl_p {
+ struct isakmp_gen h;
+ nd_uint8_t p_no; /* Proposal # */
+ nd_uint8_t prot_id; /* Protocol */
+ nd_uint8_t spi_size; /* SPI Size */
+ nd_uint8_t num_t; /* Number of Transforms */
+ /* SPI */
+};
+
+/* 3.6 Transform Payload */
+ /*
+ The value of the next payload field MUST only contain the value "3"
+ or "0". If there are additional Transform payloads in the proposal,
+ then this field will be 3. If the current Transform payload is the
+ last within the proposal, then this field will be 0.
+ */
+struct ikev1_pl_t {
+ struct isakmp_gen h;
+ nd_uint8_t t_no; /* Transform # */
+ nd_uint8_t t_id; /* Transform-Id */
+ nd_byte reserved[2]; /* RESERVED2 */
+ /* SA Attributes */
+};
+
+/* 3.7 Key Exchange Payload */
+struct ikev1_pl_ke {
+ struct isakmp_gen h;
+ /* Key Exchange Data */
+};
+
+/* 3.8 Identification Payload */
+ /* MUST NOT to be used, because of being defined in ipsec-doi. */
+struct ikev1_pl_id {
+ struct isakmp_gen h;
+ union {
+ nd_uint8_t id_type; /* ID Type */
+ nd_uint32_t doi_data; /* DOI Specific ID Data */
+ } d;
+ /* Identification Data */
+};
+
+/* 3.9 Certificate Payload */
+struct ikev1_pl_cert {
+ struct isakmp_gen h;
+ nd_uint8_t encode; /* Cert Encoding */
+ nd_uint8_t cert; /* Certificate Data */
+ /*
+ This field indicates the type of
+ certificate or certificate-related information contained in the
+ Certificate Data field.
+ */
+};
+
+/* 3.10 Certificate Request Payload */
+struct ikev1_pl_cr {
+ struct isakmp_gen h;
+ nd_uint8_t num_cert; /* # Cert. Types */
+ /*
+ Certificate Types (variable length)
+ -- Contains a list of the types of certificates requested,
+ sorted in order of preference. Each individual certificate
+ type is 1 octet. This field is NOT requiredo
+ */
+ /* # Certificate Authorities (1 octet) */
+ /* Certificate Authorities (variable length) */
+};
+
+/* 3.11 Hash Payload */
+ /* may not be used, because of having only data. */
+struct ikev1_pl_hash {
+ struct isakmp_gen h;
+ /* Hash Data */
+};
+
+/* 3.12 Signature Payload */
+ /* may not be used, because of having only data. */
+struct ikev1_pl_sig {
+ struct isakmp_gen h;
+ /* Signature Data */
+};
+
+/* 3.13 Nonce Payload */
+ /* may not be used, because of having only data. */
+struct ikev1_pl_nonce {
+ struct isakmp_gen h;
+ /* Nonce Data */
+};
+
+/* 3.14 Notification Payload */
+struct ikev1_pl_n {
+ struct isakmp_gen h;
+ nd_uint32_t doi; /* Domain of Interpretation */
+ nd_uint8_t prot_id; /* Protocol-ID */
+ nd_uint8_t spi_size; /* SPI Size */
+ nd_uint16_t type; /* Notify Message Type */
+ /* SPI */
+ /* Notification Data */
+};
+
+/* 3.14.1 Notify Message Types */
+/* NOTIFY MESSAGES - ERROR TYPES */
+#define ISAKMP_NTYPE_INVALID_PAYLOAD_TYPE 1
+#define ISAKMP_NTYPE_DOI_NOT_SUPPORTED 2
+#define ISAKMP_NTYPE_SITUATION_NOT_SUPPORTED 3
+#define ISAKMP_NTYPE_INVALID_COOKIE 4
+#define ISAKMP_NTYPE_INVALID_MAJOR_VERSION 5
+#define ISAKMP_NTYPE_INVALID_MINOR_VERSION 6
+#define ISAKMP_NTYPE_INVALID_EXCHANGE_TYPE 7
+#define ISAKMP_NTYPE_INVALID_FLAGS 8
+#define ISAKMP_NTYPE_INVALID_MESSAGE_ID 9
+#define ISAKMP_NTYPE_INVALID_PROTOCOL_ID 10
+#define ISAKMP_NTYPE_INVALID_SPI 11
+#define ISAKMP_NTYPE_INVALID_TRANSFORM_ID 12
+#define ISAKMP_NTYPE_ATTRIBUTES_NOT_SUPPORTED 13
+#define ISAKMP_NTYPE_NO_PROPOSAL_CHOSEN 14
+#define ISAKMP_NTYPE_BAD_PROPOSAL_SYNTAX 15
+#define ISAKMP_NTYPE_PAYLOAD_MALFORMED 16
+#define ISAKMP_NTYPE_INVALID_KEY_INFORMATION 17
+#define ISAKMP_NTYPE_INVALID_ID_INFORMATION 18
+#define ISAKMP_NTYPE_INVALID_CERT_ENCODING 19
+#define ISAKMP_NTYPE_INVALID_CERTIFICATE 20
+#define ISAKMP_NTYPE_BAD_CERT_REQUEST_SYNTAX 21
+#define ISAKMP_NTYPE_INVALID_CERT_AUTHORITY 22
+#define ISAKMP_NTYPE_INVALID_HASH_INFORMATION 23
+#define ISAKMP_NTYPE_AUTHENTICATION_FAILED 24
+#define ISAKMP_NTYPE_INVALID_SIGNATURE 25
+#define ISAKMP_NTYPE_ADDRESS_NOTIFICATION 26
+
+/* 3.15 Delete Payload */
+struct ikev1_pl_d {
+ struct isakmp_gen h;
+ nd_uint32_t doi; /* Domain of Interpretation */
+ nd_uint8_t prot_id; /* Protocol-Id */
+ nd_uint8_t spi_size; /* SPI Size */
+ nd_uint16_t num_spi; /* # of SPIs */
+ /* SPI(es) */
+};
+
+/* IKEv2 (RFC4306) */
+
+/* 3.3 Security Association Payload -- generic header */
+/* 3.3.1. Proposal Substructure */
+struct ikev2_p {
+ struct isakmp_gen h;
+ nd_uint8_t p_no; /* Proposal # */
+ nd_uint8_t prot_id; /* Protocol */
+ nd_uint8_t spi_size; /* SPI Size */
+ nd_uint8_t num_t; /* Number of Transforms */
+};
+
+/* 3.3.2. Transform Substructure */
+struct ikev2_t {
+ struct isakmp_gen h;
+ nd_uint8_t t_type; /* Transform Type (ENCR,PRF,INTEG,etc.*/
+ nd_byte res2; /* reserved byte */
+ nd_uint16_t t_id; /* Transform ID */
+};
+
+enum ikev2_t_type {
+ IV2_T_ENCR = 1,
+ IV2_T_PRF = 2,
+ IV2_T_INTEG= 3,
+ IV2_T_DH = 4,
+ IV2_T_ESN = 5
+};
+
+/* 3.4. Key Exchange Payload */
+struct ikev2_ke {
+ struct isakmp_gen h;
+ nd_uint16_t ke_group;
+ nd_uint16_t ke_res1;
+ /* KE data */
+};
+
+
+/* 3.5. Identification Payloads */
+enum ikev2_id_type {
+ ID_IPV4_ADDR=1,
+ ID_FQDN=2,
+ ID_RFC822_ADDR=3,
+ ID_IPV6_ADDR=5,
+ ID_DER_ASN1_DN=9,
+ ID_DER_ASN1_GN=10,
+ ID_KEY_ID=11
+};
+struct ikev2_id {
+ struct isakmp_gen h;
+ nd_uint8_t type; /* ID type */
+ nd_byte res1;
+ nd_byte res2[2];
+ /* SPI */
+ /* Notification Data */
+};
+
+/* 3.10 Notification Payload */
+struct ikev2_n {
+ struct isakmp_gen h;
+ nd_uint8_t prot_id; /* Protocol-ID */
+ nd_uint8_t spi_size; /* SPI Size */
+ nd_uint16_t type; /* Notify Message Type */
+};
+
+enum ikev2_n_type {
+ IV2_NOTIFY_UNSUPPORTED_CRITICAL_PAYLOAD = 1,
+ IV2_NOTIFY_INVALID_IKE_SPI = 4,
+ IV2_NOTIFY_INVALID_MAJOR_VERSION = 5,
+ IV2_NOTIFY_INVALID_SYNTAX = 7,
+ IV2_NOTIFY_INVALID_MESSAGE_ID = 9,
+ IV2_NOTIFY_INVALID_SPI =11,
+ IV2_NOTIFY_NO_PROPOSAL_CHOSEN =14,
+ IV2_NOTIFY_INVALID_KE_PAYLOAD =17,
+ IV2_NOTIFY_AUTHENTICATION_FAILED =24,
+ IV2_NOTIFY_SINGLE_PAIR_REQUIRED =34,
+ IV2_NOTIFY_NO_ADDITIONAL_SAS =35,
+ IV2_NOTIFY_INTERNAL_ADDRESS_FAILURE =36,
+ IV2_NOTIFY_FAILED_CP_REQUIRED =37,
+ IV2_NOTIFY_INVALID_SELECTORS =39,
+ IV2_NOTIFY_INITIAL_CONTACT =16384,
+ IV2_NOTIFY_SET_WINDOW_SIZE =16385,
+ IV2_NOTIFY_ADDITIONAL_TS_POSSIBLE =16386,
+ IV2_NOTIFY_IPCOMP_SUPPORTED =16387,
+ IV2_NOTIFY_NAT_DETECTION_SOURCE_IP =16388,
+ IV2_NOTIFY_NAT_DETECTION_DESTINATION_IP =16389,
+ IV2_NOTIFY_COOKIE =16390,
+ IV2_NOTIFY_USE_TRANSPORT_MODE =16391,
+ IV2_NOTIFY_HTTP_CERT_LOOKUP_SUPPORTED =16392,
+ IV2_NOTIFY_REKEY_SA =16393,
+ IV2_NOTIFY_ESP_TFC_PADDING_NOT_SUPPORTED =16394,
+ IV2_NOTIFY_NON_FIRST_FRAGMENTS_ALSO =16395
+};
+
+struct notify_messages {
+ uint16_t type;
+ char *msg;
+};
+
+/* 3.8 Authentication Payload */
+struct ikev2_auth {
+ struct isakmp_gen h;
+ nd_uint8_t auth_method; /* Protocol-ID */
+ nd_byte reserved[3];
+ /* authentication data */
+};
+
+enum ikev2_auth_type {
+ IV2_RSA_SIG = 1,
+ IV2_SHARED = 2,
+ IV2_DSS_SIG = 3
+};
+
+/* refer to RFC 2409 */
+
+#if 0
+/* isakmp sa structure */
+struct oakley_sa {
+ uint8_t proto_id; /* OAKLEY */
+ vchar_t *spi; /* spi */
+ uint8_t dhgrp; /* DH; group */
+ uint8_t auth_t; /* method of authentication */
+ uint8_t prf_t; /* type of prf */
+ uint8_t hash_t; /* type of hash */
+ uint8_t enc_t; /* type of cipher */
+ uint8_t life_t; /* type of duration of lifetime */
+ uint32_t ldur; /* life duration */
+};
+#endif
+
+/* refer to RFC 2407 */
+
+#define IPSEC_DOI 1
+
+/* 4.2 IPSEC Situation Definition */
+#define IPSECDOI_SIT_IDENTITY_ONLY 0x00000001
+#define IPSECDOI_SIT_SECRECY 0x00000002
+#define IPSECDOI_SIT_INTEGRITY 0x00000004
+
+/* 4.4.1 IPSEC Security Protocol Identifiers */
+ /* 4.4.2 IPSEC ISAKMP Transform Values */
+#define IPSECDOI_PROTO_ISAKMP 1
+#define IPSECDOI_KEY_IKE 1
+
+/* 4.4.1 IPSEC Security Protocol Identifiers */
+#define IPSECDOI_PROTO_IPSEC_AH 2
+ /* 4.4.3 IPSEC AH Transform Values */
+#define IPSECDOI_AH_MD5 2
+#define IPSECDOI_AH_SHA 3
+#define IPSECDOI_AH_DES 4
+#define IPSECDOI_AH_SHA2_256 5
+#define IPSECDOI_AH_SHA2_384 6
+#define IPSECDOI_AH_SHA2_512 7
+
+/* 4.4.1 IPSEC Security Protocol Identifiers */
+#define IPSECDOI_PROTO_IPSEC_ESP 3
+ /* 4.4.4 IPSEC ESP Transform Identifiers */
+#define IPSECDOI_ESP_DES_IV64 1
+#define IPSECDOI_ESP_DES 2
+#define IPSECDOI_ESP_3DES 3
+#define IPSECDOI_ESP_RC5 4
+#define IPSECDOI_ESP_IDEA 5
+#define IPSECDOI_ESP_CAST 6
+#define IPSECDOI_ESP_BLOWFISH 7
+#define IPSECDOI_ESP_3IDEA 8
+#define IPSECDOI_ESP_DES_IV32 9
+#define IPSECDOI_ESP_RC4 10
+#define IPSECDOI_ESP_NULL 11
+#define IPSECDOI_ESP_RIJNDAEL 12
+#define IPSECDOI_ESP_AES 12
+
+/* 4.4.1 IPSEC Security Protocol Identifiers */
+#define IPSECDOI_PROTO_IPCOMP 4
+ /* 4.4.5 IPSEC IPCOMP Transform Identifiers */
+#define IPSECDOI_IPCOMP_OUI 1
+#define IPSECDOI_IPCOMP_DEFLATE 2
+#define IPSECDOI_IPCOMP_LZS 3
+
+/* 4.5 IPSEC Security Association Attributes */
+#define IPSECDOI_ATTR_SA_LTYPE 1 /* B */
+#define IPSECDOI_ATTR_SA_LTYPE_DEFAULT 1
+#define IPSECDOI_ATTR_SA_LTYPE_SEC 1
+#define IPSECDOI_ATTR_SA_LTYPE_KB 2
+#define IPSECDOI_ATTR_SA_LDUR 2 /* V */
+#define IPSECDOI_ATTR_SA_LDUR_DEFAULT 28800 /* 8 hours */
+#define IPSECDOI_ATTR_GRP_DESC 3 /* B */
+#define IPSECDOI_ATTR_ENC_MODE 4 /* B */
+ /* default value: host dependent */
+#define IPSECDOI_ATTR_ENC_MODE_TUNNEL 1
+#define IPSECDOI_ATTR_ENC_MODE_TRNS 2
+#define IPSECDOI_ATTR_AUTH 5 /* B */
+ /* 0 means not to use authentication. */
+#define IPSECDOI_ATTR_AUTH_HMAC_MD5 1
+#define IPSECDOI_ATTR_AUTH_HMAC_SHA1 2
+#define IPSECDOI_ATTR_AUTH_DES_MAC 3
+#define IPSECDOI_ATTR_AUTH_KPDK 4 /*RFC-1826(Key/Pad/Data/Key)*/
+ /*
+ * When negotiating ESP without authentication, the Auth
+ * Algorithm attribute MUST NOT be included in the proposal.
+ * When negotiating ESP without confidentiality, the Auth
+ * Algorithm attribute MUST be included in the proposal and
+ * the ESP transform ID must be ESP_NULL.
+ */
+#define IPSECDOI_ATTR_KEY_LENGTH 6 /* B */
+#define IPSECDOI_ATTR_KEY_ROUNDS 7 /* B */
+#define IPSECDOI_ATTR_COMP_DICT_SIZE 8 /* B */
+#define IPSECDOI_ATTR_COMP_PRIVALG 9 /* V */
+
+/* 4.6.1 Security Association Payload */
+struct ipsecdoi_sa {
+ struct isakmp_gen h;
+ nd_uint32_t doi; /* Domain of Interpretation */
+ nd_uint32_t sit; /* Situation */
+};
+
+struct ipsecdoi_secrecy_h {
+ nd_uint16_t len;
+ nd_uint16_t reserved;
+};
+
+/* 4.6.2.1 Identification Type Values */
+struct ipsecdoi_id {
+ struct isakmp_gen h;
+ nd_uint8_t type; /* ID Type */
+ nd_uint8_t proto_id; /* Protocol ID */
+ nd_uint16_t port; /* Port */
+ /* Identification Data */
+};
+
+#define IPSECDOI_ID_IPV4_ADDR 1
+#define IPSECDOI_ID_FQDN 2
+#define IPSECDOI_ID_USER_FQDN 3
+#define IPSECDOI_ID_IPV4_ADDR_SUBNET 4
+#define IPSECDOI_ID_IPV6_ADDR 5
+#define IPSECDOI_ID_IPV6_ADDR_SUBNET 6
+#define IPSECDOI_ID_IPV4_ADDR_RANGE 7
+#define IPSECDOI_ID_IPV6_ADDR_RANGE 8
+#define IPSECDOI_ID_DER_ASN1_DN 9
+#define IPSECDOI_ID_DER_ASN1_GN 10
+#define IPSECDOI_ID_KEY_ID 11
+
+/* 4.6.3 IPSEC DOI Notify Message Types */
+/* Notify Messages - Status Types */
+#define IPSECDOI_NTYPE_RESPONDER_LIFETIME 24576
+#define IPSECDOI_NTYPE_REPLAY_STATUS 24577
+#define IPSECDOI_NTYPE_INITIAL_CONTACT 24578
+
+#define DECLARE_PRINTER(func) static const u_char *ike##func##_print( \
+ netdissect_options *ndo, u_char tpay, \
+ const struct isakmp_gen *ext, \
+ u_int item_len, \
+ const u_char *end_pointer, \
+ uint32_t phase,\
+ uint32_t doi0, \
+ uint32_t proto0, int depth)
+
+DECLARE_PRINTER(v1_sa);
+DECLARE_PRINTER(v1_p);
+DECLARE_PRINTER(v1_t);
+DECLARE_PRINTER(v1_ke);
+DECLARE_PRINTER(v1_id);
+DECLARE_PRINTER(v1_cert);
+DECLARE_PRINTER(v1_cr);
+DECLARE_PRINTER(v1_sig);
+DECLARE_PRINTER(v1_hash);
+DECLARE_PRINTER(v1_nonce);
+DECLARE_PRINTER(v1_n);
+DECLARE_PRINTER(v1_d);
+DECLARE_PRINTER(v1_vid);
+
+DECLARE_PRINTER(v2_sa);
+DECLARE_PRINTER(v2_ke);
+DECLARE_PRINTER(v2_ID);
+DECLARE_PRINTER(v2_cert);
+DECLARE_PRINTER(v2_cr);
+DECLARE_PRINTER(v2_auth);
+DECLARE_PRINTER(v2_nonce);
+DECLARE_PRINTER(v2_n);
+DECLARE_PRINTER(v2_d);
+DECLARE_PRINTER(v2_vid);
+DECLARE_PRINTER(v2_TS);
+DECLARE_PRINTER(v2_cp);
+DECLARE_PRINTER(v2_eap);
+
+static const u_char *ikev2_e_print(netdissect_options *ndo,
+ const struct isakmp *base,
+ u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len,
+ const u_char *end_pointer,
+ uint32_t phase,
+ uint32_t doi0,
+ uint32_t proto0, int depth);
+
+
+static const u_char *ike_sub0_print(netdissect_options *ndo,u_char, const struct isakmp_gen *,
+ const u_char *, uint32_t, uint32_t, uint32_t, int);
+static const u_char *ikev1_sub_print(netdissect_options *ndo,u_char, const struct isakmp_gen *,
+ const u_char *, uint32_t, uint32_t, uint32_t, int);
+
+static const u_char *ikev2_sub_print(netdissect_options *ndo,
+ const struct isakmp *base,
+ u_char np, const struct isakmp_gen *ext,
+ const u_char *ep, uint32_t phase,
+ uint32_t doi, uint32_t proto,
+ int depth);
+
+
+static char *numstr(u_int);
+
+static void
+ikev1_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2, const struct isakmp *base);
+
+#define MAXINITIATORS 20
+static int ninitiator = 0;
+union inaddr_u {
+ nd_ipv4 in4;
+ nd_ipv6 in6;
+};
+static struct {
+ cookie_t initiator;
+ u_int version;
+ union inaddr_u iaddr;
+ union inaddr_u raddr;
+} cookiecache[MAXINITIATORS];
+
+/* protocol id */
+static const char *protoidstr[] = {
+ NULL, "isakmp", "ipsec-ah", "ipsec-esp", "ipcomp",
+};
+
+/* isakmp->np */
+static const char *npstr[] = {
+ "none", "sa", "p", "t", "ke", "id", "cert", "cr", "hash", /* 0 - 8 */
+ "sig", "nonce", "n", "d", "vid", /* 9 - 13 */
+ "pay14", "pay15", "pay16", "pay17", "pay18", /* 14- 18 */
+ "pay19", "pay20", "pay21", "pay22", "pay23", /* 19- 23 */
+ "pay24", "pay25", "pay26", "pay27", "pay28", /* 24- 28 */
+ "pay29", "pay30", "pay31", "pay32", /* 29- 32 */
+ "v2sa", "v2ke", "v2IDi", "v2IDr", "v2cert",/* 33- 37 */
+ "v2cr", "v2auth","v2nonce", "v2n", "v2d", /* 38- 42 */
+ "v2vid", "v2TSi", "v2TSr", "v2e", "v2cp", /* 43- 47 */
+ "v2eap", /* 48 */
+
+};
+
+/* isakmp->np */
+static const u_char *(*npfunc[])(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len,
+ const u_char *end_pointer,
+ uint32_t phase,
+ uint32_t doi0,
+ uint32_t proto0, int depth) = {
+ NULL,
+ ikev1_sa_print,
+ ikev1_p_print,
+ ikev1_t_print,
+ ikev1_ke_print,
+ ikev1_id_print,
+ ikev1_cert_print,
+ ikev1_cr_print,
+ ikev1_hash_print,
+ ikev1_sig_print,
+ ikev1_nonce_print,
+ ikev1_n_print,
+ ikev1_d_print,
+ ikev1_vid_print, /* 13 */
+ NULL, NULL, NULL, NULL, NULL, /* 14- 18 */
+ NULL, NULL, NULL, NULL, NULL, /* 19- 23 */
+ NULL, NULL, NULL, NULL, NULL, /* 24- 28 */
+ NULL, NULL, NULL, NULL, /* 29- 32 */
+ ikev2_sa_print, /* 33 */
+ ikev2_ke_print, /* 34 */
+ ikev2_ID_print, /* 35 */
+ ikev2_ID_print, /* 36 */
+ ikev2_cert_print, /* 37 */
+ ikev2_cr_print, /* 38 */
+ ikev2_auth_print, /* 39 */
+ ikev2_nonce_print, /* 40 */
+ ikev2_n_print, /* 41 */
+ ikev2_d_print, /* 42 */
+ ikev2_vid_print, /* 43 */
+ ikev2_TS_print, /* 44 */
+ ikev2_TS_print, /* 45 */
+ NULL, /* ikev2_e_print,*/ /* 46 - special */
+ ikev2_cp_print, /* 47 */
+ ikev2_eap_print, /* 48 */
+};
+
+/* isakmp->etype */
+static const char *etypestr[] = {
+/* IKEv1 exchange types */
+ "none", "base", "ident", "auth", "agg", "inf", NULL, NULL, /* 0-7 */
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 8-15 */
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 16-23 */
+ NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 24-31 */
+ "oakley-quick", "oakley-newgroup", /* 32-33 */
+/* IKEv2 exchange types */
+ "ikev2_init", "ikev2_auth", "child_sa", "inf2" /* 34-37 */
+};
+
+#define STR_OR_ID(x, tab) \
+ (((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)]) ? tab[(x)] : numstr(x))
+#define PROTOIDSTR(x) STR_OR_ID(x, protoidstr)
+#define NPSTR(x) STR_OR_ID(x, npstr)
+#define ETYPESTR(x) STR_OR_ID(x, etypestr)
+
+#define CHECKLEN(p, np) \
+ if (ep < (const u_char *)(p)) { \
+ ND_PRINT(" [|%s]", NPSTR(np)); \
+ goto done; \
+ }
+
+
+#define NPFUNC(x) \
+ (((x) < sizeof(npfunc)/sizeof(npfunc[0]) && npfunc[(x)]) \
+ ? npfunc[(x)] : NULL)
+
+static int
+iszero(const u_char *p, size_t l)
+{
+ while (l != 0) {
+ if (*p)
+ return 0;
+ p++;
+ l--;
+ }
+ return 1;
+}
+
+/* find cookie from initiator cache */
+static int
+cookie_find(const cookie_t *in)
+{
+ int i;
+
+ for (i = 0; i < MAXINITIATORS; i++) {
+ if (memcmp(in, &cookiecache[i].initiator, sizeof(*in)) == 0)
+ return i;
+ }
+
+ return -1;
+}
+
+/* record initiator */
+static void
+cookie_record(netdissect_options *ndo, const cookie_t *in, const u_char *bp2)
+{
+ int i;
+ const struct ip *ip;
+ const struct ip6_hdr *ip6;
+
+ i = cookie_find(in);
+ if (0 <= i) {
+ ninitiator = (i + 1) % MAXINITIATORS;
+ return;
+ }
+
+ ip = (const struct ip *)bp2;
+ switch (IP_V(ip)) {
+ case 4:
+ cookiecache[ninitiator].version = 4;
+ UNALIGNED_MEMCPY(&cookiecache[ninitiator].iaddr.in4,
+ ip->ip_src, sizeof(nd_ipv4));
+ UNALIGNED_MEMCPY(&cookiecache[ninitiator].raddr.in4,
+ ip->ip_dst, sizeof(nd_ipv4));
+ break;
+ case 6:
+ ip6 = (const struct ip6_hdr *)bp2;
+ cookiecache[ninitiator].version = 6;
+ UNALIGNED_MEMCPY(&cookiecache[ninitiator].iaddr.in6,
+ ip6->ip6_src, sizeof(nd_ipv6));
+ UNALIGNED_MEMCPY(&cookiecache[ninitiator].raddr.in6,
+ ip6->ip6_dst, sizeof(nd_ipv6));
+ break;
+ default:
+ return;
+ }
+ UNALIGNED_MEMCPY(&cookiecache[ninitiator].initiator, in, sizeof(*in));
+ ninitiator = (ninitiator + 1) % MAXINITIATORS;
+}
+
+#define cookie_isinitiator(ndo, x, y) cookie_sidecheck(ndo, (x), (y), 1)
+#define cookie_isresponder(ndo, x, y) cookie_sidecheck(ndo, (x), (y), 0)
+static int
+cookie_sidecheck(netdissect_options *ndo, int i, const u_char *bp2, int initiator)
+{
+ const struct ip *ip;
+ const struct ip6_hdr *ip6;
+
+ ip = (const struct ip *)bp2;
+ switch (IP_V(ip)) {
+ case 4:
+ if (cookiecache[i].version != 4)
+ return 0;
+ if (initiator) {
+ if (UNALIGNED_MEMCMP(ip->ip_src, &cookiecache[i].iaddr.in4, sizeof(nd_ipv4)) == 0)
+ return 1;
+ } else {
+ if (UNALIGNED_MEMCMP(ip->ip_src, &cookiecache[i].raddr.in4, sizeof(nd_ipv4)) == 0)
+ return 1;
+ }
+ break;
+ case 6:
+ if (cookiecache[i].version != 6)
+ return 0;
+ ip6 = (const struct ip6_hdr *)bp2;
+ if (initiator) {
+ if (UNALIGNED_MEMCMP(ip6->ip6_src, &cookiecache[i].iaddr.in6, sizeof(nd_ipv6)) == 0)
+ return 1;
+ } else {
+ if (UNALIGNED_MEMCMP(ip6->ip6_src, &cookiecache[i].raddr.in6, sizeof(nd_ipv6)) == 0)
+ return 1;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void
+hexprint(netdissect_options *ndo, const uint8_t *loc, size_t len)
+{
+ const uint8_t *p;
+ size_t i;
+
+ p = loc;
+ for (i = 0; i < len; i++)
+ ND_PRINT("%02x", p[i] & 0xff);
+}
+
+static int
+rawprint(netdissect_options *ndo, const uint8_t *loc, size_t len)
+{
+ ND_TCHECK_LEN(loc, len);
+
+ hexprint(ndo, loc, len);
+ return 1;
+trunc:
+ return 0;
+}
+
+
+/*
+ * returns false if we run out of data buffer
+ */
+static int ike_show_somedata(netdissect_options *ndo,
+ const u_char *cp, const u_char *ep)
+{
+ /* there is too much data, just show some of it */
+ const u_char *end = ep - 20;
+ size_t elen = 20;
+ size_t len = ep - cp;
+ if(len > 10) {
+ len = 10;
+ }
+
+ /* really shouldn't happen because of above */
+ if(end < cp + len) {
+ end = cp+len;
+ elen = ep - end;
+ }
+
+ ND_PRINT(" data=(");
+ if(!rawprint(ndo, (const uint8_t *)(cp), len)) goto trunc;
+ ND_PRINT("...");
+ if(elen) {
+ if(!rawprint(ndo, (const uint8_t *)(end), elen)) goto trunc;
+ }
+ ND_PRINT(")");
+ return 1;
+
+trunc:
+ return 0;
+}
+
+struct attrmap {
+ const char *type;
+ u_int nvalue;
+ const char *value[30]; /*XXX*/
+};
+
+static const u_char *
+ikev1_attrmap_print(netdissect_options *ndo,
+ const u_char *p, const u_char *ep2,
+ const struct attrmap *map, size_t nmap)
+{
+ u_int totlen;
+ uint32_t t, v;
+
+ if (GET_U_1(p) & 0x80)
+ totlen = 4;
+ else {
+ totlen = 4 + GET_BE_U_2(p + 2);
+ }
+ if (ep2 < p + totlen) {
+ ND_PRINT("[|attr]");
+ return ep2 + 1;
+ }
+
+ ND_PRINT("(");
+ t = GET_BE_U_2(p) & 0x7fff;
+ if (map && t < nmap && map[t].type)
+ ND_PRINT("type=%s ", map[t].type);
+ else
+ ND_PRINT("type=#%u ", t);
+ if (GET_U_1(p) & 0x80) {
+ ND_PRINT("value=");
+ v = GET_BE_U_2(p + 2);
+ if (map && t < nmap && v < map[t].nvalue && map[t].value[v])
+ ND_PRINT("%s", map[t].value[v]);
+ else {
+ if (!rawprint(ndo, (const uint8_t *)(p + 2), 2)) {
+ ND_PRINT(")");
+ goto trunc;
+ }
+ }
+ } else {
+ ND_PRINT("len=%u value=", totlen - 4);
+ if (!rawprint(ndo, (const uint8_t *)(p + 4), totlen - 4)) {
+ ND_PRINT(")");
+ goto trunc;
+ }
+ }
+ ND_PRINT(")");
+ return p + totlen;
+
+trunc:
+ return NULL;
+}
+
+static const u_char *
+ikev1_attr_print(netdissect_options *ndo, const u_char *p, const u_char *ep2)
+{
+ u_int totlen;
+ uint32_t t;
+
+ if (GET_U_1(p) & 0x80)
+ totlen = 4;
+ else {
+ totlen = 4 + GET_BE_U_2(p + 2);
+ }
+ if (ep2 < p + totlen) {
+ ND_PRINT("[|attr]");
+ return ep2 + 1;
+ }
+
+ ND_PRINT("(");
+ t = GET_BE_U_2(p) & 0x7fff;
+ ND_PRINT("type=#%u ", t);
+ if (GET_U_1(p) & 0x80) {
+ ND_PRINT("value=");
+ t = GET_U_1(p + 2);
+ if (!rawprint(ndo, (const uint8_t *)(p + 2), 2)) {
+ ND_PRINT(")");
+ goto trunc;
+ }
+ } else {
+ ND_PRINT("len=%u value=", totlen - 4);
+ if (!rawprint(ndo, (const uint8_t *)(p + 4), totlen - 4)) {
+ ND_PRINT(")");
+ goto trunc;
+ }
+ }
+ ND_PRINT(")");
+ return p + totlen;
+
+trunc:
+ return NULL;
+}
+
+static const u_char *
+ikev1_sa_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext,
+ u_int item_len _U_,
+ const u_char *ep, uint32_t phase, uint32_t doi0 _U_,
+ uint32_t proto0, int depth)
+{
+ const struct ikev1_pl_sa *p;
+ uint32_t doi, sit, ident;
+ const u_char *cp, *np;
+ int t;
+
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_SA));
+
+ p = (const struct ikev1_pl_sa *)ext;
+ ND_TCHECK_SIZE(p);
+ doi = GET_BE_U_4(p->doi);
+ sit = GET_BE_U_4(p->sit);
+ if (doi != 1) {
+ ND_PRINT(" doi=%u", doi);
+ ND_PRINT(" situation=%u", sit);
+ return (const u_char *)(p + 1);
+ }
+
+ ND_PRINT(" doi=ipsec");
+ ND_PRINT(" situation=");
+ t = 0;
+ if (sit & 0x01) {
+ ND_PRINT("identity");
+ t++;
+ }
+ if (sit & 0x02) {
+ ND_PRINT("%ssecrecy", t ? "+" : "");
+ t++;
+ }
+ if (sit & 0x04)
+ ND_PRINT("%sintegrity", t ? "+" : "");
+
+ np = (const u_char *)ext + sizeof(struct ikev1_pl_sa);
+ if (sit != 0x01) {
+ ident = GET_BE_U_4(ext + 1);
+ ND_PRINT(" ident=%u", ident);
+ np += sizeof(ident);
+ }
+
+ ext = (const struct isakmp_gen *)np;
+ ND_TCHECK_SIZE(ext);
+
+ cp = ikev1_sub_print(ndo, ISAKMP_NPTYPE_P, ext, ep, phase, doi, proto0,
+ depth);
+
+ return cp;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_SA));
+ return NULL;
+}
+
+static const u_char *
+ikev1_p_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len _U_,
+ const u_char *ep, uint32_t phase, uint32_t doi0,
+ uint32_t proto0 _U_, int depth)
+{
+ const struct ikev1_pl_p *p;
+ const u_char *cp;
+ uint8_t spi_size;
+
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_P));
+
+ p = (const struct ikev1_pl_p *)ext;
+ ND_TCHECK_SIZE(p);
+ ND_PRINT(" #%u protoid=%s transform=%u",
+ GET_U_1(p->p_no), PROTOIDSTR(GET_U_1(p->prot_id)),
+ GET_U_1(p->num_t));
+ spi_size = GET_U_1(p->spi_size);
+ if (spi_size) {
+ ND_PRINT(" spi=");
+ if (!rawprint(ndo, (const uint8_t *)(p + 1), spi_size))
+ goto trunc;
+ }
+
+ ext = (const struct isakmp_gen *)((const u_char *)(p + 1) + spi_size);
+ ND_TCHECK_SIZE(ext);
+
+ cp = ikev1_sub_print(ndo, ISAKMP_NPTYPE_T, ext, ep, phase, doi0,
+ GET_U_1(p->prot_id), depth);
+
+ return cp;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_P));
+ return NULL;
+}
+
+static const char *ikev1_p_map[] = {
+ NULL, "ike",
+};
+
+static const char *ikev2_t_type_map[]={
+ NULL, "encr", "prf", "integ", "dh", "esn"
+};
+
+static const char *ah_p_map[] = {
+ NULL, "(reserved)", "md5", "sha", "1des",
+ "sha2-256", "sha2-384", "sha2-512",
+};
+
+static const char *prf_p_map[] = {
+ NULL, "hmac-md5", "hmac-sha", "hmac-tiger",
+ "aes128_xcbc"
+};
+
+static const char *integ_p_map[] = {
+ NULL, "hmac-md5", "hmac-sha", "dec-mac",
+ "kpdk-md5", "aes-xcbc"
+};
+
+static const char *esn_p_map[] = {
+ "no-esn", "esn"
+};
+
+static const char *dh_p_map[] = {
+ NULL, "modp768",
+ "modp1024", /* group 2 */
+ "EC2N 2^155", /* group 3 */
+ "EC2N 2^185", /* group 4 */
+ "modp1536", /* group 5 */
+ "iana-grp06", "iana-grp07", /* reserved */
+ "iana-grp08", "iana-grp09",
+ "iana-grp10", "iana-grp11",
+ "iana-grp12", "iana-grp13",
+ "modp2048", /* group 14 */
+ "modp3072", /* group 15 */
+ "modp4096", /* group 16 */
+ "modp6144", /* group 17 */
+ "modp8192", /* group 18 */
+};
+
+static const char *esp_p_map[] = {
+ NULL, "1des-iv64", "1des", "3des", "rc5", "idea", "cast",
+ "blowfish", "3idea", "1des-iv32", "rc4", "null", "aes"
+};
+
+static const char *ipcomp_p_map[] = {
+ NULL, "oui", "deflate", "lzs",
+};
+
+static const struct attrmap ipsec_t_map[] = {
+ { NULL, 0, { NULL } },
+ { "lifetype", 3, { NULL, "sec", "kb", }, },
+ { "life", 0, { NULL } },
+ { "group desc", 18, { NULL, "modp768",
+ "modp1024", /* group 2 */
+ "EC2N 2^155", /* group 3 */
+ "EC2N 2^185", /* group 4 */
+ "modp1536", /* group 5 */
+ "iana-grp06", "iana-grp07", /* reserved */
+ "iana-grp08", "iana-grp09",
+ "iana-grp10", "iana-grp11",
+ "iana-grp12", "iana-grp13",
+ "modp2048", /* group 14 */
+ "modp3072", /* group 15 */
+ "modp4096", /* group 16 */
+ "modp6144", /* group 17 */
+ "modp8192", /* group 18 */
+ }, },
+ { "enc mode", 3, { NULL, "tunnel", "transport", }, },
+ { "auth", 5, { NULL, "hmac-md5", "hmac-sha1", "1des-mac", "keyed", }, },
+ { "keylen", 0, { NULL } },
+ { "rounds", 0, { NULL } },
+ { "dictsize", 0, { NULL } },
+ { "privalg", 0, { NULL } },
+};
+
+static const struct attrmap encr_t_map[] = {
+ { NULL, 0, { NULL } }, { NULL, 0, { NULL } }, /* 0, 1 */
+ { NULL, 0, { NULL } }, { NULL, 0, { NULL } }, /* 2, 3 */
+ { NULL, 0, { NULL } }, { NULL, 0, { NULL } }, /* 4, 5 */
+ { NULL, 0, { NULL } }, { NULL, 0, { NULL } }, /* 6, 7 */
+ { NULL, 0, { NULL } }, { NULL, 0, { NULL } }, /* 8, 9 */
+ { NULL, 0, { NULL } }, { NULL, 0, { NULL } }, /* 10,11*/
+ { NULL, 0, { NULL } }, { NULL, 0, { NULL } }, /* 12,13*/
+ { "keylen", 14, { NULL }},
+};
+
+static const struct attrmap oakley_t_map[] = {
+ { NULL, 0, { NULL } },
+ { "enc", 8, { NULL, "1des", "idea", "blowfish", "rc5",
+ "3des", "cast", "aes", }, },
+ { "hash", 7, { NULL, "md5", "sha1", "tiger",
+ "sha2-256", "sha2-384", "sha2-512", }, },
+ { "auth", 6, { NULL, "preshared", "dss", "rsa sig", "rsa enc",
+ "rsa enc revised", }, },
+ { "group desc", 18, { NULL, "modp768",
+ "modp1024", /* group 2 */
+ "EC2N 2^155", /* group 3 */
+ "EC2N 2^185", /* group 4 */
+ "modp1536", /* group 5 */
+ "iana-grp06", "iana-grp07", /* reserved */
+ "iana-grp08", "iana-grp09",
+ "iana-grp10", "iana-grp11",
+ "iana-grp12", "iana-grp13",
+ "modp2048", /* group 14 */
+ "modp3072", /* group 15 */
+ "modp4096", /* group 16 */
+ "modp6144", /* group 17 */
+ "modp8192", /* group 18 */
+ }, },
+ { "group type", 4, { NULL, "MODP", "ECP", "EC2N", }, },
+ { "group prime", 0, { NULL } },
+ { "group gen1", 0, { NULL } },
+ { "group gen2", 0, { NULL } },
+ { "group curve A", 0, { NULL } },
+ { "group curve B", 0, { NULL } },
+ { "lifetype", 3, { NULL, "sec", "kb", }, },
+ { "lifeduration", 0, { NULL } },
+ { "prf", 0, { NULL } },
+ { "keylen", 0, { NULL } },
+ { "field", 0, { NULL } },
+ { "order", 0, { NULL } },
+};
+
+static const u_char *
+ikev1_t_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep, uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto, int depth _U_)
+{
+ const struct ikev1_pl_t *p;
+ const u_char *cp;
+ const char *idstr;
+ const struct attrmap *map;
+ size_t nmap;
+ const u_char *ep2;
+
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_T));
+
+ p = (const struct ikev1_pl_t *)ext;
+ ND_TCHECK_SIZE(p);
+
+ switch (proto) {
+ case 1:
+ idstr = STR_OR_ID(GET_U_1(p->t_id), ikev1_p_map);
+ map = oakley_t_map;
+ nmap = sizeof(oakley_t_map)/sizeof(oakley_t_map[0]);
+ break;
+ case 2:
+ idstr = STR_OR_ID(GET_U_1(p->t_id), ah_p_map);
+ map = ipsec_t_map;
+ nmap = sizeof(ipsec_t_map)/sizeof(ipsec_t_map[0]);
+ break;
+ case 3:
+ idstr = STR_OR_ID(GET_U_1(p->t_id), esp_p_map);
+ map = ipsec_t_map;
+ nmap = sizeof(ipsec_t_map)/sizeof(ipsec_t_map[0]);
+ break;
+ case 4:
+ idstr = STR_OR_ID(GET_U_1(p->t_id), ipcomp_p_map);
+ map = ipsec_t_map;
+ nmap = sizeof(ipsec_t_map)/sizeof(ipsec_t_map[0]);
+ break;
+ default:
+ idstr = NULL;
+ map = NULL;
+ nmap = 0;
+ break;
+ }
+
+ if (idstr)
+ ND_PRINT(" #%u id=%s ", GET_U_1(p->t_no), idstr);
+ else
+ ND_PRINT(" #%u id=%u ", GET_U_1(p->t_no), GET_U_1(p->t_id));
+ cp = (const u_char *)(p + 1);
+ ep2 = (const u_char *)p + item_len;
+ while (cp < ep && cp < ep2) {
+ if (map && nmap)
+ cp = ikev1_attrmap_print(ndo, cp, ep2, map, nmap);
+ else
+ cp = ikev1_attr_print(ndo, cp, ep2);
+ if (cp == NULL)
+ goto trunc;
+ }
+ if (ep < ep2)
+ ND_PRINT("...");
+ return cp;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_T));
+ return NULL;
+}
+
+static const u_char *
+ikev1_ke_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep _U_, uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_KE));
+
+ ND_TCHECK_SIZE(ext);
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" key len=%u", item_len - 4);
+ if (2 < ndo->ndo_vflag && item_len > 4) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_KE));
+ return NULL;
+}
+
+static const u_char *
+ikev1_id_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep _U_, uint32_t phase, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+#define USE_IPSECDOI_IN_PHASE1 1
+ const struct ikev1_pl_id *p;
+ static const char *idtypestr[] = {
+ "IPv4", "IPv4net", "IPv6", "IPv6net",
+ };
+ static const char *ipsecidtypestr[] = {
+ NULL, "IPv4", "FQDN", "user FQDN", "IPv4net", "IPv6",
+ "IPv6net", "IPv4range", "IPv6range", "ASN1 DN", "ASN1 GN",
+ "keyid",
+ };
+ u_int len;
+ const u_char *data;
+
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_ID));
+
+ p = (const struct ikev1_pl_id *)ext;
+ ND_TCHECK_SIZE(p);
+ if (sizeof(*p) < item_len) {
+ data = (const u_char *)(p + 1);
+ len = item_len - sizeof(*p);
+ } else {
+ data = NULL;
+ len = 0;
+ }
+
+#if 0 /*debug*/
+ ND_PRINT(" [phase=%u doi=%u proto=%u]", phase, doi, proto);
+#endif
+ switch (phase) {
+#ifndef USE_IPSECDOI_IN_PHASE1
+ case 1:
+#endif
+ default:
+ ND_PRINT(" idtype=%s",
+ STR_OR_ID(GET_U_1(p->d.id_type), idtypestr));
+ ND_PRINT(" doi_data=%u",
+ GET_BE_U_4(p->d.doi_data) & 0xffffff);
+ break;
+
+#ifdef USE_IPSECDOI_IN_PHASE1
+ case 1:
+#endif
+ case 2:
+ {
+ const struct ipsecdoi_id *doi_p;
+ const char *p_name;
+ uint8_t type, proto_id;
+
+ doi_p = (const struct ipsecdoi_id *)ext;
+ ND_TCHECK_SIZE(doi_p);
+ type = GET_U_1(doi_p->type);
+ ND_PRINT(" idtype=%s", STR_OR_ID(type, ipsecidtypestr));
+ /* A protocol ID of 0 DOES NOT mean IPPROTO_IP! */
+ proto_id = GET_U_1(doi_p->proto_id);
+ if (!ndo->ndo_nflag && proto_id && (p_name = netdb_protoname(proto_id)) != NULL)
+ ND_PRINT(" protoid=%s", p_name);
+ else
+ ND_PRINT(" protoid=%u", proto_id);
+ ND_PRINT(" port=%u", GET_BE_U_2(doi_p->port));
+ if (!len)
+ break;
+ if (data == NULL)
+ goto trunc;
+ ND_TCHECK_LEN(data, len);
+ switch (type) {
+ case IPSECDOI_ID_IPV4_ADDR:
+ if (len < 4)
+ ND_PRINT(" len=%u [bad: < 4]", len);
+ else
+ ND_PRINT(" len=%u %s", len, GET_IPADDR_STRING(data));
+ len = 0;
+ break;
+ case IPSECDOI_ID_FQDN:
+ case IPSECDOI_ID_USER_FQDN:
+ {
+ u_int i;
+ ND_PRINT(" len=%u ", len);
+ for (i = 0; i < len; i++)
+ fn_print_char(ndo, GET_U_1(data + i));
+ len = 0;
+ break;
+ }
+ case IPSECDOI_ID_IPV4_ADDR_SUBNET:
+ {
+ const u_char *mask;
+ if (len < 8)
+ ND_PRINT(" len=%u [bad: < 8]", len);
+ else {
+ mask = data + sizeof(nd_ipv4);
+ ND_PRINT(" len=%u %s/%u.%u.%u.%u", len,
+ GET_IPADDR_STRING(data),
+ GET_U_1(mask), GET_U_1(mask + 1),
+ GET_U_1(mask + 2),
+ GET_U_1(mask + 3));
+ }
+ len = 0;
+ break;
+ }
+ case IPSECDOI_ID_IPV6_ADDR:
+ if (len < 16)
+ ND_PRINT(" len=%u [bad: < 16]", len);
+ else
+ ND_PRINT(" len=%u %s", len, GET_IP6ADDR_STRING(data));
+ len = 0;
+ break;
+ case IPSECDOI_ID_IPV6_ADDR_SUBNET:
+ {
+ const u_char *mask;
+ if (len < 32)
+ ND_PRINT(" len=%u [bad: < 32]", len);
+ else {
+ mask = (const u_char *)(data + sizeof(nd_ipv6));
+ /*XXX*/
+ ND_PRINT(" len=%u %s/0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", len,
+ GET_IP6ADDR_STRING(data),
+ GET_U_1(mask), GET_U_1(mask + 1),
+ GET_U_1(mask + 2),
+ GET_U_1(mask + 3),
+ GET_U_1(mask + 4),
+ GET_U_1(mask + 5),
+ GET_U_1(mask + 6),
+ GET_U_1(mask + 7),
+ GET_U_1(mask + 8),
+ GET_U_1(mask + 9),
+ GET_U_1(mask + 10),
+ GET_U_1(mask + 11),
+ GET_U_1(mask + 12),
+ GET_U_1(mask + 13),
+ GET_U_1(mask + 14),
+ GET_U_1(mask + 15));
+ }
+ len = 0;
+ break;
+ }
+ case IPSECDOI_ID_IPV4_ADDR_RANGE:
+ if (len < 8)
+ ND_PRINT(" len=%u [bad: < 8]", len);
+ else {
+ ND_PRINT(" len=%u %s-%s", len,
+ GET_IPADDR_STRING(data),
+ GET_IPADDR_STRING(data + sizeof(nd_ipv4)));
+ }
+ len = 0;
+ break;
+ case IPSECDOI_ID_IPV6_ADDR_RANGE:
+ if (len < 32)
+ ND_PRINT(" len=%u [bad: < 32]", len);
+ else {
+ ND_PRINT(" len=%u %s-%s", len,
+ GET_IP6ADDR_STRING(data),
+ GET_IP6ADDR_STRING(data + sizeof(nd_ipv6)));
+ }
+ len = 0;
+ break;
+ case IPSECDOI_ID_DER_ASN1_DN:
+ case IPSECDOI_ID_DER_ASN1_GN:
+ case IPSECDOI_ID_KEY_ID:
+ break;
+ }
+ break;
+ }
+ }
+ if (data && len) {
+ ND_PRINT(" len=%u", len);
+ if (2 < ndo->ndo_vflag) {
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)data, len))
+ goto trunc;
+ }
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_ID));
+ return NULL;
+}
+
+static const u_char *
+ikev1_cert_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep _U_, uint32_t phase _U_,
+ uint32_t doi0 _U_,
+ uint32_t proto0 _U_, int depth _U_)
+{
+ const struct ikev1_pl_cert *p;
+ static const char *certstr[] = {
+ "none", "pkcs7", "pgp", "dns",
+ "x509sign", "x509ke", "kerberos", "crl",
+ "arl", "spki", "x509attr",
+ };
+
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_CERT));
+
+ p = (const struct ikev1_pl_cert *)ext;
+ ND_TCHECK_SIZE(p);
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u", item_len - 4);
+ ND_PRINT(" type=%s", STR_OR_ID(GET_U_1(p->encode), certstr));
+ if (2 < ndo->ndo_vflag && 4 < item_len) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_CERT));
+ return NULL;
+}
+
+static const u_char *
+ikev1_cr_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep _U_, uint32_t phase _U_, uint32_t doi0 _U_,
+ uint32_t proto0 _U_, int depth _U_)
+{
+ const struct ikev1_pl_cert *p;
+ static const char *certstr[] = {
+ "none", "pkcs7", "pgp", "dns",
+ "x509sign", "x509ke", "kerberos", "crl",
+ "arl", "spki", "x509attr",
+ };
+
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_CR));
+
+ p = (const struct ikev1_pl_cert *)ext;
+ ND_TCHECK_SIZE(p);
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u", item_len - 4);
+ ND_PRINT(" type=%s", STR_OR_ID(GET_U_1(p->encode), certstr));
+ if (2 < ndo->ndo_vflag && 4 < item_len) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_CR));
+ return NULL;
+}
+
+static const u_char *
+ikev1_hash_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep _U_, uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_HASH));
+
+ ND_TCHECK_SIZE(ext);
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u", item_len - 4);
+ if (2 < ndo->ndo_vflag && 4 < item_len) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_HASH));
+ return NULL;
+}
+
+static const u_char *
+ikev1_sig_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep _U_, uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_SIG));
+
+ ND_TCHECK_SIZE(ext);
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u", item_len - 4);
+ if (2 < ndo->ndo_vflag && 4 < item_len) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_SIG));
+ return NULL;
+}
+
+static const u_char *
+ikev1_nonce_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext,
+ u_int item_len,
+ const u_char *ep,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_NONCE));
+
+ ND_TCHECK_SIZE(ext);
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" n len=%u", item_len - 4);
+ if (item_len > 4) {
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ } else if (ndo->ndo_vflag > 1) {
+ ND_PRINT(" ");
+ if (!ike_show_somedata(ndo, (const u_char *)(ext + 1), ep))
+ goto trunc;
+ }
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_NONCE));
+ return NULL;
+}
+
+static const u_char *
+ikev1_n_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep, uint32_t phase _U_, uint32_t doi0 _U_,
+ uint32_t proto0 _U_, int depth _U_)
+{
+ const struct ikev1_pl_n *p;
+ const u_char *cp;
+ const u_char *ep2;
+ uint32_t doi;
+ uint32_t proto;
+ uint16_t type;
+ uint8_t spi_size;
+ static const char *notify_error_str[] = {
+ NULL, "INVALID-PAYLOAD-TYPE",
+ "DOI-NOT-SUPPORTED", "SITUATION-NOT-SUPPORTED",
+ "INVALID-COOKIE", "INVALID-MAJOR-VERSION",
+ "INVALID-MINOR-VERSION", "INVALID-EXCHANGE-TYPE",
+ "INVALID-FLAGS", "INVALID-MESSAGE-ID",
+ "INVALID-PROTOCOL-ID", "INVALID-SPI",
+ "INVALID-TRANSFORM-ID", "ATTRIBUTES-NOT-SUPPORTED",
+ "NO-PROPOSAL-CHOSEN", "BAD-PROPOSAL-SYNTAX",
+ "PAYLOAD-MALFORMED", "INVALID-KEY-INFORMATION",
+ "INVALID-ID-INFORMATION", "INVALID-CERT-ENCODING",
+ "INVALID-CERTIFICATE", "CERT-TYPE-UNSUPPORTED",
+ "INVALID-CERT-AUTHORITY", "INVALID-HASH-INFORMATION",
+ "AUTHENTICATION-FAILED", "INVALID-SIGNATURE",
+ "ADDRESS-NOTIFICATION", "NOTIFY-SA-LIFETIME",
+ "CERTIFICATE-UNAVAILABLE", "UNSUPPORTED-EXCHANGE-TYPE",
+ "UNEQUAL-PAYLOAD-LENGTHS",
+ };
+ static const char *ipsec_notify_error_str[] = {
+ "RESERVED",
+ };
+ static const char *notify_status_str[] = {
+ "CONNECTED",
+ };
+ static const char *ipsec_notify_status_str[] = {
+ "RESPONDER-LIFETIME", "REPLAY-STATUS",
+ "INITIAL-CONTACT",
+ };
+/* NOTE: these macro must be called with x in proper range */
+
+/* 0 - 8191 */
+#define NOTIFY_ERROR_STR(x) \
+ STR_OR_ID((x), notify_error_str)
+
+/* 8192 - 16383 */
+#define IPSEC_NOTIFY_ERROR_STR(x) \
+ STR_OR_ID((u_int)((x) - 8192), ipsec_notify_error_str)
+
+/* 16384 - 24575 */
+#define NOTIFY_STATUS_STR(x) \
+ STR_OR_ID((u_int)((x) - 16384), notify_status_str)
+
+/* 24576 - 32767 */
+#define IPSEC_NOTIFY_STATUS_STR(x) \
+ STR_OR_ID((u_int)((x) - 24576), ipsec_notify_status_str)
+
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_N));
+
+ p = (const struct ikev1_pl_n *)ext;
+ ND_TCHECK_SIZE(p);
+ doi = GET_BE_U_4(p->doi);
+ proto = GET_U_1(p->prot_id);
+ if (doi != 1) {
+ ND_PRINT(" doi=%u", doi);
+ ND_PRINT(" proto=%u", proto);
+ type = GET_BE_U_2(p->type);
+ if (type < 8192)
+ ND_PRINT(" type=%s", NOTIFY_ERROR_STR(type));
+ else if (type < 16384)
+ ND_PRINT(" type=%s", numstr(type));
+ else if (type < 24576)
+ ND_PRINT(" type=%s", NOTIFY_STATUS_STR(type));
+ else
+ ND_PRINT(" type=%s", numstr(type));
+ spi_size = GET_U_1(p->spi_size);
+ if (spi_size) {
+ ND_PRINT(" spi=");
+ if (!rawprint(ndo, (const uint8_t *)(p + 1), spi_size))
+ goto trunc;
+ }
+ return (const u_char *)(p + 1) + spi_size;
+ }
+
+ ND_PRINT(" doi=ipsec");
+ ND_PRINT(" proto=%s", PROTOIDSTR(proto));
+ type = GET_BE_U_2(p->type);
+ if (type < 8192)
+ ND_PRINT(" type=%s", NOTIFY_ERROR_STR(type));
+ else if (type < 16384)
+ ND_PRINT(" type=%s", IPSEC_NOTIFY_ERROR_STR(type));
+ else if (type < 24576)
+ ND_PRINT(" type=%s", NOTIFY_STATUS_STR(type));
+ else if (type < 32768)
+ ND_PRINT(" type=%s", IPSEC_NOTIFY_STATUS_STR(type));
+ else
+ ND_PRINT(" type=%s", numstr(type));
+ spi_size = GET_U_1(p->spi_size);
+ if (spi_size) {
+ ND_PRINT(" spi=");
+ if (!rawprint(ndo, (const uint8_t *)(p + 1), spi_size))
+ goto trunc;
+ }
+
+ cp = (const u_char *)(p + 1) + spi_size;
+ ep2 = (const u_char *)p + item_len;
+
+ if (cp < ep) {
+ switch (type) {
+ case IPSECDOI_NTYPE_RESPONDER_LIFETIME:
+ {
+ const struct attrmap *map = oakley_t_map;
+ size_t nmap = sizeof(oakley_t_map)/sizeof(oakley_t_map[0]);
+ ND_PRINT(" attrs=(");
+ while (cp < ep && cp < ep2) {
+ cp = ikev1_attrmap_print(ndo, cp, ep2, map, nmap);
+ if (cp == NULL) {
+ ND_PRINT(")");
+ goto trunc;
+ }
+ }
+ ND_PRINT(")");
+ break;
+ }
+ case IPSECDOI_NTYPE_REPLAY_STATUS:
+ ND_PRINT(" status=(");
+ ND_PRINT("replay detection %sabled",
+ GET_BE_U_4(cp) ? "en" : "dis");
+ ND_PRINT(")");
+ break;
+ default:
+ /*
+ * XXX - fill in more types here; see, for example,
+ * draft-ietf-ipsec-notifymsg-04.
+ */
+ if (ndo->ndo_vflag > 3) {
+ ND_PRINT(" data=(");
+ if (!rawprint(ndo, (const uint8_t *)(cp), ep - cp))
+ goto trunc;
+ ND_PRINT(")");
+ } else {
+ if (!ike_show_somedata(ndo, cp, ep))
+ goto trunc;
+ }
+ break;
+ }
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_N));
+ return NULL;
+}
+
+static const u_char *
+ikev1_d_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext, u_int item_len _U_,
+ const u_char *ep _U_, uint32_t phase _U_, uint32_t doi0 _U_,
+ uint32_t proto0 _U_, int depth _U_)
+{
+ const struct ikev1_pl_d *p;
+ const uint8_t *q;
+ uint32_t doi;
+ uint32_t proto;
+ uint8_t spi_size;
+ uint16_t num_spi;
+ u_int i;
+
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_D));
+
+ p = (const struct ikev1_pl_d *)ext;
+ ND_TCHECK_SIZE(p);
+ doi = GET_BE_U_4(p->doi);
+ proto = GET_U_1(p->prot_id);
+ if (doi != 1) {
+ ND_PRINT(" doi=%u", doi);
+ ND_PRINT(" proto=%u", proto);
+ } else {
+ ND_PRINT(" doi=ipsec");
+ ND_PRINT(" proto=%s", PROTOIDSTR(proto));
+ }
+ spi_size = GET_U_1(p->spi_size);
+ ND_PRINT(" spilen=%u", spi_size);
+ num_spi = GET_BE_U_2(p->num_spi);
+ ND_PRINT(" nspi=%u", num_spi);
+ ND_PRINT(" spi=");
+ q = (const uint8_t *)(p + 1);
+ for (i = 0; i < num_spi; i++) {
+ if (i != 0)
+ ND_PRINT(",");
+ if (!rawprint(ndo, (const uint8_t *)q, spi_size))
+ goto trunc;
+ q += spi_size;
+ }
+ return q;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_D));
+ return NULL;
+}
+
+static const u_char *
+ikev1_vid_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ ND_PRINT("%s:", NPSTR(ISAKMP_NPTYPE_VID));
+
+ ND_TCHECK_SIZE(ext);
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u", item_len - 4);
+ if (2 < ndo->ndo_vflag && 4 < item_len) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_VID));
+ return NULL;
+}
+
+/************************************************************/
+/* */
+/* IKE v2 - rfc4306 - dissector */
+/* */
+/************************************************************/
+
+static void
+ikev2_pay_print(netdissect_options *ndo, const char *payname, uint8_t critical)
+{
+ ND_PRINT("%s%s:", payname, critical&0x80 ? "[C]" : "");
+}
+
+static const u_char *
+ikev2_gen_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext, u_int item_len)
+{
+ const struct isakmp_gen *p = (const struct isakmp_gen *)ext;
+
+ ND_TCHECK_SIZE(ext);
+ ikev2_pay_print(ndo, NPSTR(tpay), GET_U_1(p->critical));
+
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u", item_len - 4);
+ if (2 < ndo->ndo_vflag && 4 < item_len) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return NULL;
+}
+
+static const u_char *
+ikev2_t_print(netdissect_options *ndo, int tcount,
+ const struct isakmp_gen *ext, u_int item_len,
+ const u_char *ep)
+{
+ const struct ikev2_t *p;
+ uint16_t t_id;
+ uint8_t t_type;
+ const u_char *cp;
+ const char *idstr;
+ const struct attrmap *map;
+ size_t nmap;
+ const u_char *ep2;
+
+ p = (const struct ikev2_t *)ext;
+ ND_TCHECK_SIZE(p);
+ ikev2_pay_print(ndo, NPSTR(ISAKMP_NPTYPE_T), GET_U_1(p->h.critical));
+
+ t_id = GET_BE_U_2(p->t_id);
+
+ map = NULL;
+ nmap = 0;
+
+ t_type = GET_U_1(p->t_type);
+ switch (t_type) {
+ case IV2_T_ENCR:
+ idstr = STR_OR_ID(t_id, esp_p_map);
+ map = encr_t_map;
+ nmap = sizeof(encr_t_map)/sizeof(encr_t_map[0]);
+ break;
+
+ case IV2_T_PRF:
+ idstr = STR_OR_ID(t_id, prf_p_map);
+ break;
+
+ case IV2_T_INTEG:
+ idstr = STR_OR_ID(t_id, integ_p_map);
+ break;
+
+ case IV2_T_DH:
+ idstr = STR_OR_ID(t_id, dh_p_map);
+ break;
+
+ case IV2_T_ESN:
+ idstr = STR_OR_ID(t_id, esn_p_map);
+ break;
+
+ default:
+ idstr = NULL;
+ break;
+ }
+
+ if (idstr)
+ ND_PRINT(" #%u type=%s id=%s ", tcount,
+ STR_OR_ID(t_type, ikev2_t_type_map),
+ idstr);
+ else
+ ND_PRINT(" #%u type=%s id=%u ", tcount,
+ STR_OR_ID(t_type, ikev2_t_type_map),
+ t_id);
+ cp = (const u_char *)(p + 1);
+ ep2 = (const u_char *)p + item_len;
+ while (cp < ep && cp < ep2) {
+ if (map && nmap) {
+ cp = ikev1_attrmap_print(ndo, cp, ep2, map, nmap);
+ } else
+ cp = ikev1_attr_print(ndo, cp, ep2);
+ if (cp == NULL)
+ goto trunc;
+ }
+ if (ep < ep2)
+ ND_PRINT("...");
+ return cp;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_T));
+ return NULL;
+}
+
+static const u_char *
+ikev2_p_print(netdissect_options *ndo, u_char tpay _U_, int pcount _U_,
+ const struct isakmp_gen *ext, u_int oprop_length,
+ const u_char *ep, int depth)
+{
+ const struct ikev2_p *p;
+ u_int prop_length;
+ uint8_t spi_size;
+ const u_char *cp;
+ int i;
+ int tcount;
+ u_char np;
+ u_int item_len;
+
+ p = (const struct ikev2_p *)ext;
+ ND_TCHECK_SIZE(p);
+
+ ikev2_pay_print(ndo, NPSTR(ISAKMP_NPTYPE_P), GET_U_1(p->h.critical));
+
+ /*
+ * ikev2_sa_print() guarantees that this is >= 4.
+ */
+ prop_length = oprop_length - 4;
+ ND_PRINT(" #%u protoid=%s transform=%u len=%u",
+ GET_U_1(p->p_no), PROTOIDSTR(GET_U_1(p->prot_id)),
+ GET_U_1(p->num_t), oprop_length);
+ cp = (const u_char *)(p + 1);
+
+ spi_size = GET_U_1(p->spi_size);
+ if (spi_size) {
+ if (prop_length < spi_size)
+ goto toolong;
+ ND_PRINT(" spi=");
+ if (!rawprint(ndo, (const uint8_t *)cp, spi_size))
+ goto trunc;
+ cp += spi_size;
+ prop_length -= spi_size;
+ }
+
+ /*
+ * Print the transforms.
+ */
+ tcount = 0;
+ for (np = ISAKMP_NPTYPE_T; np != 0; np = GET_U_1(ext->np)) {
+ tcount++;
+ ext = (const struct isakmp_gen *)cp;
+ if (prop_length < sizeof(*ext))
+ goto toolong;
+ ND_TCHECK_SIZE(ext);
+
+ /*
+ * Since we can't have a payload length of less than 4 bytes,
+ * we need to bail out here if the generic header is nonsensical
+ * or truncated, otherwise we could loop forever processing
+ * zero-length items or otherwise misdissect the packet.
+ */
+ item_len = GET_BE_U_2(ext->len);
+ if (item_len <= 4)
+ goto trunc;
+
+ if (prop_length < item_len)
+ goto toolong;
+ ND_TCHECK_LEN(cp, item_len);
+
+ depth++;
+ ND_PRINT("\n");
+ for (i = 0; i < depth; i++)
+ ND_PRINT(" ");
+ ND_PRINT("(");
+ if (np == ISAKMP_NPTYPE_T) {
+ cp = ikev2_t_print(ndo, tcount, ext, item_len, ep);
+ if (cp == NULL) {
+ /* error, already reported */
+ return NULL;
+ }
+ } else {
+ ND_PRINT("%s", NPSTR(np));
+ cp += item_len;
+ }
+ ND_PRINT(")");
+ depth--;
+ prop_length -= item_len;
+ }
+ return cp;
+toolong:
+ /*
+ * Skip the rest of the proposal.
+ */
+ cp += prop_length;
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_P));
+ return cp;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_P));
+ return NULL;
+}
+
+static const u_char *
+ikev2_sa_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext1,
+ u_int osa_length, const u_char *ep,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth)
+{
+ const struct isakmp_gen *ext;
+ u_int sa_length;
+ const u_char *cp;
+ int i;
+ int pcount;
+ u_char np;
+ u_int item_len;
+
+ ND_TCHECK_SIZE(ext1);
+ ikev2_pay_print(ndo, "sa", GET_U_1(ext1->critical));
+
+ /*
+ * ikev2_sub0_print() guarantees that this is >= 4.
+ */
+ osa_length= GET_BE_U_2(ext1->len);
+ sa_length = osa_length - 4;
+ ND_PRINT(" len=%u", sa_length);
+
+ /*
+ * Print the payloads.
+ */
+ cp = (const u_char *)(ext1 + 1);
+ pcount = 0;
+ for (np = ISAKMP_NPTYPE_P; np != 0; np = GET_U_1(ext->np)) {
+ pcount++;
+ ext = (const struct isakmp_gen *)cp;
+ if (sa_length < sizeof(*ext))
+ goto toolong;
+ ND_TCHECK_SIZE(ext);
+
+ /*
+ * Since we can't have a payload length of less than 4 bytes,
+ * we need to bail out here if the generic header is nonsensical
+ * or truncated, otherwise we could loop forever processing
+ * zero-length items or otherwise misdissect the packet.
+ */
+ item_len = GET_BE_U_2(ext->len);
+ if (item_len <= 4)
+ goto trunc;
+
+ if (sa_length < item_len)
+ goto toolong;
+ ND_TCHECK_LEN(cp, item_len);
+
+ depth++;
+ ND_PRINT("\n");
+ for (i = 0; i < depth; i++)
+ ND_PRINT(" ");
+ ND_PRINT("(");
+ if (np == ISAKMP_NPTYPE_P) {
+ cp = ikev2_p_print(ndo, np, pcount, ext, item_len,
+ ep, depth);
+ if (cp == NULL) {
+ /* error, already reported */
+ return NULL;
+ }
+ } else {
+ ND_PRINT("%s", NPSTR(np));
+ cp += item_len;
+ }
+ ND_PRINT(")");
+ depth--;
+ sa_length -= item_len;
+ }
+ return cp;
+toolong:
+ /*
+ * Skip the rest of the SA.
+ */
+ cp += sa_length;
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return cp;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return NULL;
+}
+
+static const u_char *
+ikev2_ke_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ const struct ikev2_ke *k;
+
+ k = (const struct ikev2_ke *)ext;
+ ND_TCHECK_SIZE(k);
+ ikev2_pay_print(ndo, NPSTR(tpay), GET_U_1(k->h.critical));
+
+ if (item_len < 8) {
+ ND_PRINT(" len=%u < 8", item_len);
+ return (const u_char *)ext + item_len;
+ }
+ ND_PRINT(" len=%u group=%s", item_len - 8,
+ STR_OR_ID(GET_BE_U_2(k->ke_group), dh_p_map));
+
+ if (2 < ndo->ndo_vflag && 8 < item_len) {
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(k + 1), item_len - 8))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return NULL;
+}
+
+static const u_char *
+ikev2_ID_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ const struct ikev2_id *idp;
+ u_int idtype_len, i;
+ unsigned int dumpascii, dumphex;
+ const unsigned char *typedata;
+
+ idp = (const struct ikev2_id *)ext;
+ ND_TCHECK_SIZE(idp);
+ ikev2_pay_print(ndo, NPSTR(tpay), GET_U_1(idp->h.critical));
+
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u", item_len - 4);
+ if (2 < ndo->ndo_vflag && 4 < item_len) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+
+ idtype_len =item_len - sizeof(struct ikev2_id);
+ dumpascii = 0;
+ dumphex = 0;
+ typedata = (const unsigned char *)(ext)+sizeof(struct ikev2_id);
+
+ switch(GET_U_1(idp->type)) {
+ case ID_IPV4_ADDR:
+ ND_PRINT(" ipv4:");
+ dumphex=1;
+ break;
+ case ID_FQDN:
+ ND_PRINT(" fqdn:");
+ dumpascii=1;
+ break;
+ case ID_RFC822_ADDR:
+ ND_PRINT(" rfc822:");
+ dumpascii=1;
+ break;
+ case ID_IPV6_ADDR:
+ ND_PRINT(" ipv6:");
+ dumphex=1;
+ break;
+ case ID_DER_ASN1_DN:
+ ND_PRINT(" dn:");
+ dumphex=1;
+ break;
+ case ID_DER_ASN1_GN:
+ ND_PRINT(" gn:");
+ dumphex=1;
+ break;
+ case ID_KEY_ID:
+ ND_PRINT(" keyid:");
+ dumphex=1;
+ break;
+ }
+
+ if(dumpascii) {
+ ND_TCHECK_LEN(typedata, idtype_len);
+ for(i=0; i<idtype_len; i++) {
+ if(ND_ASCII_ISPRINT(GET_U_1(typedata + i))) {
+ ND_PRINT("%c", GET_U_1(typedata + i));
+ } else {
+ ND_PRINT(".");
+ }
+ }
+ }
+ if(dumphex) {
+ if (!rawprint(ndo, (const uint8_t *)typedata, idtype_len))
+ goto trunc;
+ }
+
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return NULL;
+}
+
+static const u_char *
+ikev2_cert_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ return ikev2_gen_print(ndo, tpay, ext, item_len);
+}
+
+static const u_char *
+ikev2_cr_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ return ikev2_gen_print(ndo, tpay, ext, item_len);
+}
+
+static const u_char *
+ikev2_auth_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ const struct ikev2_auth *p;
+ const char *v2_auth[]={ "invalid", "rsasig",
+ "shared-secret", "dsssig" };
+ const u_char *authdata = (const u_char*)ext + sizeof(struct ikev2_auth);
+
+ ND_TCHECK_LEN(ext, sizeof(struct ikev2_auth));
+ p = (const struct ikev2_auth *)ext;
+ ikev2_pay_print(ndo, NPSTR(tpay), GET_U_1(p->h.critical));
+
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u method=%s", item_len-4,
+ STR_OR_ID(GET_U_1(p->auth_method), v2_auth));
+ if (item_len > 4) {
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT(" authdata=(");
+ if (!rawprint(ndo, (const uint8_t *)authdata, item_len - sizeof(struct ikev2_auth)))
+ goto trunc;
+ ND_PRINT(") ");
+ } else if (ndo->ndo_vflag) {
+ if (!ike_show_somedata(ndo, authdata, ep))
+ goto trunc;
+ }
+ }
+
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return NULL;
+}
+
+static const u_char *
+ikev2_nonce_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ ND_TCHECK_SIZE(ext);
+ ikev2_pay_print(ndo, "nonce", GET_U_1(ext->critical));
+
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u", item_len - 4);
+ if (1 < ndo->ndo_vflag && 4 < item_len) {
+ ND_PRINT(" nonce=(");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ ND_PRINT(") ");
+ } else if(ndo->ndo_vflag && 4 < item_len) {
+ if(!ike_show_somedata(ndo, (const u_char *)(ext+1), ep)) goto trunc;
+ }
+
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return NULL;
+}
+
+/* notify payloads */
+static const u_char *
+ikev2_n_print(netdissect_options *ndo, u_char tpay _U_,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ const struct ikev2_n *p;
+ uint16_t type;
+ uint8_t spi_size;
+ const u_char *cp;
+ u_char showspi, showsomedata;
+ const char *notify_name;
+
+ p = (const struct ikev2_n *)ext;
+ ND_TCHECK_SIZE(p);
+ ikev2_pay_print(ndo, NPSTR(ISAKMP_NPTYPE_N), GET_U_1(p->h.critical));
+
+ showspi = 1;
+ showsomedata=0;
+ notify_name=NULL;
+
+ ND_PRINT(" prot_id=%s", PROTOIDSTR(GET_U_1(p->prot_id)));
+
+ type = GET_BE_U_2(p->type);
+
+ /* notify space is annoying sparse */
+ switch(type) {
+ case IV2_NOTIFY_UNSUPPORTED_CRITICAL_PAYLOAD:
+ notify_name = "unsupported_critical_payload";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_INVALID_IKE_SPI:
+ notify_name = "invalid_ike_spi";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_INVALID_MAJOR_VERSION:
+ notify_name = "invalid_major_version";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_INVALID_SYNTAX:
+ notify_name = "invalid_syntax";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_INVALID_MESSAGE_ID:
+ notify_name = "invalid_message_id";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_INVALID_SPI:
+ notify_name = "invalid_spi";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_NO_PROPOSAL_CHOSEN:
+ notify_name = "no_protocol_chosen";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_INVALID_KE_PAYLOAD:
+ notify_name = "invalid_ke_payload";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_AUTHENTICATION_FAILED:
+ notify_name = "authentication_failed";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_SINGLE_PAIR_REQUIRED:
+ notify_name = "single_pair_required";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_NO_ADDITIONAL_SAS:
+ notify_name = "no_additional_sas";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_INTERNAL_ADDRESS_FAILURE:
+ notify_name = "internal_address_failure";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_FAILED_CP_REQUIRED:
+ notify_name = "failed:cp_required";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_INVALID_SELECTORS:
+ notify_name = "invalid_selectors";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_INITIAL_CONTACT:
+ notify_name = "initial_contact";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_SET_WINDOW_SIZE:
+ notify_name = "set_window_size";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_ADDITIONAL_TS_POSSIBLE:
+ notify_name = "additional_ts_possible";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_IPCOMP_SUPPORTED:
+ notify_name = "ipcomp_supported";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_NAT_DETECTION_SOURCE_IP:
+ notify_name = "nat_detection_source_ip";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_NAT_DETECTION_DESTINATION_IP:
+ notify_name = "nat_detection_destination_ip";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_COOKIE:
+ notify_name = "cookie";
+ showspi = 1;
+ showsomedata= 1;
+ break;
+
+ case IV2_NOTIFY_USE_TRANSPORT_MODE:
+ notify_name = "use_transport_mode";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_HTTP_CERT_LOOKUP_SUPPORTED:
+ notify_name = "http_cert_lookup_supported";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_REKEY_SA:
+ notify_name = "rekey_sa";
+ showspi = 1;
+ break;
+
+ case IV2_NOTIFY_ESP_TFC_PADDING_NOT_SUPPORTED:
+ notify_name = "tfc_padding_not_supported";
+ showspi = 0;
+ break;
+
+ case IV2_NOTIFY_NON_FIRST_FRAGMENTS_ALSO:
+ notify_name = "non_first_fragment_also";
+ showspi = 0;
+ break;
+
+ default:
+ if (type < 8192) {
+ notify_name="error";
+ } else if(type < 16384) {
+ notify_name="private-error";
+ } else if(type < 40960) {
+ notify_name="status";
+ } else {
+ notify_name="private-status";
+ }
+ }
+
+ if(notify_name) {
+ ND_PRINT(" type=%u(%s)", type, notify_name);
+ }
+
+
+ spi_size = GET_U_1(p->spi_size);
+ if (showspi && spi_size) {
+ ND_PRINT(" spi=");
+ if (!rawprint(ndo, (const uint8_t *)(p + 1), spi_size))
+ goto trunc;
+ }
+
+ cp = (const u_char *)(p + 1) + spi_size;
+
+ if (cp < ep) {
+ if (ndo->ndo_vflag > 3 || (showsomedata && ep-cp < 30)) {
+ ND_PRINT(" data=(");
+ if (!rawprint(ndo, (const uint8_t *)(cp), ep - cp))
+ goto trunc;
+
+ ND_PRINT(")");
+ } else if (showsomedata) {
+ if (!ike_show_somedata(ndo, cp, ep))
+ goto trunc;
+ }
+ }
+
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(ISAKMP_NPTYPE_N));
+ return NULL;
+}
+
+static const u_char *
+ikev2_d_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ return ikev2_gen_print(ndo, tpay, ext, item_len);
+}
+
+static const u_char *
+ikev2_vid_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ const u_char *vid;
+ u_int i, len;
+
+ ND_TCHECK_SIZE(ext);
+ ikev2_pay_print(ndo, NPSTR(tpay), GET_U_1(ext->critical));
+
+ /*
+ * Our caller has ensured that the length is >= 4.
+ */
+ ND_PRINT(" len=%u vid=", item_len - 4);
+
+ vid = (const u_char *)(ext+1);
+ len = item_len - 4;
+ ND_TCHECK_LEN(vid, len);
+ for(i=0; i<len; i++) {
+ if(ND_ASCII_ISPRINT(GET_U_1(vid + i)))
+ ND_PRINT("%c", GET_U_1(vid + i));
+ else ND_PRINT(".");
+ }
+ if (2 < ndo->ndo_vflag && 4 < len) {
+ /* Print the entire payload in hex */
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), item_len - 4))
+ goto trunc;
+ }
+ return (const u_char *)ext + item_len;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return NULL;
+}
+
+static const u_char *
+ikev2_TS_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ return ikev2_gen_print(ndo, tpay, ext, item_len);
+}
+
+static const u_char *
+ikev2_e_print(netdissect_options *ndo,
+#ifndef HAVE_LIBCRYPTO
+ _U_
+#endif
+ const struct isakmp *base,
+ u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+#ifndef HAVE_LIBCRYPTO
+ _U_
+#endif
+ uint32_t phase,
+#ifndef HAVE_LIBCRYPTO
+ _U_
+#endif
+ uint32_t doi,
+#ifndef HAVE_LIBCRYPTO
+ _U_
+#endif
+ uint32_t proto,
+#ifndef HAVE_LIBCRYPTO
+ _U_
+#endif
+ int depth)
+{
+ const u_char *dat;
+ u_int dlen;
+#ifdef HAVE_LIBCRYPTO
+ uint8_t np;
+#endif
+
+ ND_TCHECK_SIZE(ext);
+ ikev2_pay_print(ndo, NPSTR(tpay), GET_U_1(ext->critical));
+
+ dlen = item_len-4;
+
+ ND_PRINT(" len=%u", dlen);
+ if (2 < ndo->ndo_vflag && 4 < dlen) {
+ ND_PRINT(" ");
+ if (!rawprint(ndo, (const uint8_t *)(ext + 1), dlen))
+ goto trunc;
+ }
+
+ dat = (const u_char *)(ext+1);
+ ND_TCHECK_LEN(dat, dlen);
+
+#ifdef HAVE_LIBCRYPTO
+ np = GET_U_1(ext->np);
+
+ /* try to decrypt it! */
+ if(esp_decrypt_buffer_by_ikev2_print(ndo,
+ GET_U_1(base->flags) & ISAKMP_FLAG_I,
+ base->i_ck, base->r_ck,
+ dat, dat+dlen)) {
+
+ ext = (const struct isakmp_gen *)ndo->ndo_packetp;
+
+ /* got it decrypted, print stuff inside. */
+ ikev2_sub_print(ndo, base, np, ext,
+ ndo->ndo_snapend, phase, doi, proto, depth+1);
+
+ /*
+ * esp_decrypt_buffer_by_ikev2_print pushed information
+ * on the buffer stack; we're done with the buffer, so
+ * pop it (which frees the buffer)
+ */
+ nd_pop_packet_info(ndo);
+ }
+#endif
+
+
+ /* always return NULL, because E must be at end, and NP refers
+ * to what was inside.
+ */
+ return NULL;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(tpay));
+ return NULL;
+}
+
+static const u_char *
+ikev2_cp_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ return ikev2_gen_print(ndo, tpay, ext, item_len);
+}
+
+static const u_char *
+ikev2_eap_print(netdissect_options *ndo, u_char tpay,
+ const struct isakmp_gen *ext,
+ u_int item_len, const u_char *ep _U_,
+ uint32_t phase _U_, uint32_t doi _U_,
+ uint32_t proto _U_, int depth _U_)
+{
+ return ikev2_gen_print(ndo, tpay, ext, item_len);
+}
+
+static const u_char *
+ike_sub0_print(netdissect_options *ndo,
+ u_char np, const struct isakmp_gen *ext, const u_char *ep,
+
+ uint32_t phase, uint32_t doi, uint32_t proto, int depth)
+{
+ const u_char *cp;
+ u_int item_len;
+
+ cp = (const u_char *)ext;
+ ND_TCHECK_SIZE(ext);
+
+ /*
+ * Since we can't have a payload length of less than 4 bytes,
+ * we need to bail out here if the generic header is nonsensical
+ * or truncated, otherwise we could loop forever processing
+ * zero-length items or otherwise misdissect the packet.
+ */
+ item_len = GET_BE_U_2(ext->len);
+ if (item_len <= 4)
+ return NULL;
+
+ if (NPFUNC(np)) {
+ /*
+ * XXX - what if item_len is too short, or too long,
+ * for this payload type?
+ */
+ cp = (*npfunc[np])(ndo, np, ext, item_len, ep, phase, doi, proto, depth);
+ } else {
+ ND_PRINT("%s", NPSTR(np));
+ cp += item_len;
+ }
+
+ return cp;
+trunc:
+ nd_print_trunc(ndo);
+ return NULL;
+}
+
+static const u_char *
+ikev1_sub_print(netdissect_options *ndo,
+ u_char np, const struct isakmp_gen *ext, const u_char *ep,
+ uint32_t phase, uint32_t doi, uint32_t proto, int depth)
+{
+ const u_char *cp;
+ int i;
+ u_int item_len;
+
+ cp = (const u_char *)ext;
+
+ while (np) {
+ ND_TCHECK_SIZE(ext);
+
+ item_len = GET_BE_U_2(ext->len);
+ ND_TCHECK_LEN(ext, item_len);
+
+ depth++;
+ ND_PRINT("\n");
+ for (i = 0; i < depth; i++)
+ ND_PRINT(" ");
+ ND_PRINT("(");
+ cp = ike_sub0_print(ndo, np, ext, ep, phase, doi, proto, depth);
+ ND_PRINT(")");
+ depth--;
+
+ if (cp == NULL) {
+ /* Zero-length subitem */
+ return NULL;
+ }
+
+ np = GET_U_1(ext->np);
+ ext = (const struct isakmp_gen *)cp;
+ }
+ return cp;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(np));
+ return NULL;
+}
+
+static char *
+numstr(u_int x)
+{
+ static char buf[20];
+ snprintf(buf, sizeof(buf), "#%u", x);
+ return buf;
+}
+
+static void
+ikev1_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2, const struct isakmp *base)
+{
+ const struct isakmp *p;
+ const u_char *ep;
+ u_int flags;
+ u_char np;
+ int i;
+ u_int phase;
+
+ p = (const struct isakmp *)bp;
+ ep = ndo->ndo_snapend;
+
+ phase = (GET_BE_U_4(base->msgid) == 0) ? 1 : 2;
+ if (phase == 1)
+ ND_PRINT(" phase %u", phase);
+ else
+ ND_PRINT(" phase %u/others", phase);
+
+ i = cookie_find(&base->i_ck);
+ if (i < 0) {
+ if (iszero((const u_char *)&base->r_ck, sizeof(base->r_ck))) {
+ /* the first packet */
+ ND_PRINT(" I");
+ if (bp2)
+ cookie_record(ndo, &base->i_ck, bp2);
+ } else
+ ND_PRINT(" ?");
+ } else {
+ if (bp2 && cookie_isinitiator(ndo, i, bp2))
+ ND_PRINT(" I");
+ else if (bp2 && cookie_isresponder(ndo, i, bp2))
+ ND_PRINT(" R");
+ else
+ ND_PRINT(" ?");
+ }
+
+ ND_PRINT(" %s", ETYPESTR(GET_U_1(base->etype)));
+ flags = GET_U_1(base->flags);
+ if (flags) {
+ ND_PRINT("[%s%s]", flags & ISAKMP_FLAG_E ? "E" : "",
+ flags & ISAKMP_FLAG_C ? "C" : "");
+ }
+
+ if (ndo->ndo_vflag) {
+ const struct isakmp_gen *ext;
+
+ ND_PRINT(":");
+
+ np = GET_U_1(base->np);
+
+ /* regardless of phase... */
+ if (flags & ISAKMP_FLAG_E) {
+ /*
+ * encrypted, nothing we can do right now.
+ * we hope to decrypt the packet in the future...
+ */
+ ND_PRINT(" [encrypted %s]", NPSTR(np));
+ goto done;
+ }
+
+ CHECKLEN(p + 1, np);
+ ext = (const struct isakmp_gen *)(p + 1);
+ ikev1_sub_print(ndo, np, ext, ep, phase, 0, 0, 0);
+ }
+
+done:
+ if (ndo->ndo_vflag) {
+ if (GET_BE_U_4(base->len) != length) {
+ ND_PRINT(" (len mismatch: isakmp %u/ip %u)",
+ GET_BE_U_4(base->len), length);
+ }
+ }
+}
+
+static const u_char *
+ikev2_sub0_print(netdissect_options *ndo, const struct isakmp *base,
+ u_char np,
+ const struct isakmp_gen *ext, const u_char *ep,
+ uint32_t phase, uint32_t doi, uint32_t proto, int depth)
+{
+ const u_char *cp;
+ u_int item_len;
+
+ cp = (const u_char *)ext;
+ ND_TCHECK_SIZE(ext);
+
+ /*
+ * Since we can't have a payload length of less than 4 bytes,
+ * we need to bail out here if the generic header is nonsensical
+ * or truncated, otherwise we could loop forever processing
+ * zero-length items or otherwise misdissect the packet.
+ */
+ item_len = GET_BE_U_2(ext->len);
+ if (item_len <= 4)
+ return NULL;
+
+ if (np == ISAKMP_NPTYPE_v2E) {
+ cp = ikev2_e_print(ndo, base, np, ext, item_len,
+ ep, phase, doi, proto, depth);
+ } else if (NPFUNC(np)) {
+ /*
+ * XXX - what if item_len is too short, or too long,
+ * for this payload type?
+ */
+ cp = (*npfunc[np])(ndo, np, ext, item_len,
+ ep, phase, doi, proto, depth);
+ } else {
+ ND_PRINT("%s", NPSTR(np));
+ cp += item_len;
+ }
+
+ return cp;
+trunc:
+ nd_print_trunc(ndo);
+ return NULL;
+}
+
+static const u_char *
+ikev2_sub_print(netdissect_options *ndo,
+ const struct isakmp *base,
+ u_char np, const struct isakmp_gen *ext, const u_char *ep,
+ uint32_t phase, uint32_t doi, uint32_t proto, int depth)
+{
+ const u_char *cp;
+ int i;
+
+ cp = (const u_char *)ext;
+ while (np) {
+ ND_TCHECK_SIZE(ext);
+
+ ND_TCHECK_LEN(ext, GET_BE_U_2(ext->len));
+
+ depth++;
+ ND_PRINT("\n");
+ for (i = 0; i < depth; i++)
+ ND_PRINT(" ");
+ ND_PRINT("(");
+ cp = ikev2_sub0_print(ndo, base, np,
+ ext, ep, phase, doi, proto, depth);
+ ND_PRINT(")");
+ depth--;
+
+ if (cp == NULL) {
+ /* Zero-length subitem */
+ return NULL;
+ }
+
+ np = GET_U_1(ext->np);
+ ext = (const struct isakmp_gen *)cp;
+ }
+ return cp;
+trunc:
+ ND_PRINT(" [|%s]", NPSTR(np));
+ return NULL;
+}
+
+static void
+ikev2_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2 _U_, const struct isakmp *base)
+{
+ const struct isakmp *p;
+ const u_char *ep;
+ uint8_t flags;
+ u_char np;
+ u_int phase;
+
+ p = (const struct isakmp *)bp;
+ ep = ndo->ndo_snapend;
+
+ phase = (GET_BE_U_4(base->msgid) == 0) ? 1 : 2;
+ if (phase == 1)
+ ND_PRINT(" parent_sa");
+ else
+ ND_PRINT(" child_sa ");
+
+ ND_PRINT(" %s", ETYPESTR(GET_U_1(base->etype)));
+ flags = GET_U_1(base->flags);
+ if (flags) {
+ ND_PRINT("[%s%s%s]",
+ flags & ISAKMP_FLAG_I ? "I" : "",
+ flags & ISAKMP_FLAG_V ? "V" : "",
+ flags & ISAKMP_FLAG_R ? "R" : "");
+ }
+
+ if (ndo->ndo_vflag) {
+ const struct isakmp_gen *ext;
+
+ ND_PRINT(":");
+
+ np = GET_U_1(base->np);
+
+ /* regardless of phase... */
+ if (flags & ISAKMP_FLAG_E) {
+ /*
+ * encrypted, nothing we can do right now.
+ * we hope to decrypt the packet in the future...
+ */
+ ND_PRINT(" [encrypted %s]", NPSTR(np));
+ goto done;
+ }
+
+ CHECKLEN(p + 1, np)
+ ext = (const struct isakmp_gen *)(p + 1);
+ ikev2_sub_print(ndo, base, np, ext, ep, phase, 0, 0, 0);
+ }
+
+done:
+ if (ndo->ndo_vflag) {
+ if (GET_BE_U_4(base->len) != length) {
+ ND_PRINT(" (len mismatch: isakmp %u/ip %u)",
+ GET_BE_U_4(base->len), length);
+ }
+ }
+}
+
+void
+isakmp_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2)
+{
+ const struct isakmp *p;
+ const u_char *ep;
+ u_int major, minor;
+
+ ndo->ndo_protocol = "isakmp";
+#ifdef HAVE_LIBCRYPTO
+ /* initialize SAs */
+ if (ndo->ndo_sa_list_head == NULL) {
+ if (ndo->ndo_espsecret)
+ esp_decodesecret_print(ndo);
+ }
+#endif
+
+ p = (const struct isakmp *)bp;
+ ep = ndo->ndo_snapend;
+
+ if ((const struct isakmp *)ep < p + 1) {
+ nd_print_trunc(ndo);
+ return;
+ }
+
+ ND_PRINT("isakmp");
+ major = (GET_U_1(p->vers) & ISAKMP_VERS_MAJOR)
+ >> ISAKMP_VERS_MAJOR_SHIFT;
+ minor = (GET_U_1(p->vers) & ISAKMP_VERS_MINOR)
+ >> ISAKMP_VERS_MINOR_SHIFT;
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" %u.%u", major, minor);
+ }
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" msgid ");
+ hexprint(ndo, p->msgid, sizeof(p->msgid));
+ }
+
+ if (1 < ndo->ndo_vflag) {
+ ND_PRINT(" cookie ");
+ hexprint(ndo, p->i_ck, sizeof(p->i_ck));
+ ND_PRINT("->");
+ hexprint(ndo, p->r_ck, sizeof(p->r_ck));
+ }
+ ND_PRINT(":");
+
+ switch(major) {
+ case IKEv1_MAJOR_VERSION:
+ ikev1_print(ndo, bp, length, bp2, p);
+ break;
+
+ case IKEv2_MAJOR_VERSION:
+ ikev2_print(ndo, bp, length, bp2, p);
+ break;
+ }
+}
+
+void
+isakmp_rfc3948_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2, int ver, int fragmented, u_int ttl_hl)
+{
+ ndo->ndo_protocol = "isakmp_rfc3948";
+ if(length == 1 && GET_U_1(bp)==0xff) {
+ ND_PRINT("isakmp-nat-keep-alive");
+ return;
+ }
+
+ if(length < 4) {
+ goto trunc;
+ }
+
+ /*
+ * see if this is an IKE packet
+ */
+ if (GET_BE_U_4(bp) == 0) {
+ ND_PRINT("NONESP-encap: ");
+ isakmp_print(ndo, bp+4, length-4, bp2);
+ return;
+ }
+
+ /* must be an ESP packet */
+ {
+ ND_PRINT("UDP-encap: ");
+
+ esp_print(ndo, bp, length, bp2, ver, fragmented, ttl_hl);
+
+ /*
+ * Either this has decrypted the payload and
+ * printed it, in which case there's nothing more
+ * to do, or it hasn't, in which case there's
+ * nothing more to do.
+ */
+ return;
+ }
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-isoclns.c b/print-isoclns.c
new file mode 100644
index 0000000..3b4a150
--- /dev/null
+++ b/print-isoclns.c
@@ -0,0 +1,3564 @@
+/*
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Matt Thomas, Digital Equipment Corporation
+ *
+ * Extensively modified by Hannes Gredler (hannes@gredler.at) for more
+ * complete IS-IS & CLNP support.
+ */
+
+/* \summary: ISO CLNS, ESIS, and ISIS printer */
+
+/*
+ * specification:
+ *
+ * CLNP: ISO 8473 (respective ITU version is at https://www.itu.int/rec/T-REC-X.233/en/)
+ * ES-IS: ISO 9542
+ * IS-IS: ISO 10589
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "nlpid.h"
+#include "extract.h"
+#include "gmpls.h"
+#include "oui.h"
+#include "signature.h"
+
+
+/*
+ * IS-IS is defined in ISO 10589. Look there for protocol definitions.
+ */
+
+#define SYSTEM_ID_LEN MAC_ADDR_LEN
+#define NODE_ID_LEN (SYSTEM_ID_LEN+1)
+#define LSP_ID_LEN (SYSTEM_ID_LEN+2)
+
+#define ISIS_VERSION 1
+#define ESIS_VERSION 1
+#define CLNP_VERSION 1
+
+#define ISIS_PDU_TYPE_MASK 0x1F
+#define ESIS_PDU_TYPE_MASK 0x1F
+#define CLNP_PDU_TYPE_MASK 0x1F
+#define CLNP_FLAG_MASK 0xE0
+#define ISIS_LAN_PRIORITY_MASK 0x7F
+
+#define ISIS_PDU_L1_LAN_IIH 15
+#define ISIS_PDU_L2_LAN_IIH 16
+#define ISIS_PDU_PTP_IIH 17
+#define ISIS_PDU_L1_LSP 18
+#define ISIS_PDU_L2_LSP 20
+#define ISIS_PDU_L1_CSNP 24
+#define ISIS_PDU_L2_CSNP 25
+#define ISIS_PDU_L1_PSNP 26
+#define ISIS_PDU_L2_PSNP 27
+
+static const struct tok isis_pdu_values[] = {
+ { ISIS_PDU_L1_LAN_IIH, "L1 Lan IIH"},
+ { ISIS_PDU_L2_LAN_IIH, "L2 Lan IIH"},
+ { ISIS_PDU_PTP_IIH, "p2p IIH"},
+ { ISIS_PDU_L1_LSP, "L1 LSP"},
+ { ISIS_PDU_L2_LSP, "L2 LSP"},
+ { ISIS_PDU_L1_CSNP, "L1 CSNP"},
+ { ISIS_PDU_L2_CSNP, "L2 CSNP"},
+ { ISIS_PDU_L1_PSNP, "L1 PSNP"},
+ { ISIS_PDU_L2_PSNP, "L2 PSNP"},
+ { 0, NULL}
+};
+
+/*
+ * A TLV is a tuple of a type, length and a value and is normally used for
+ * encoding information in all sorts of places. This is an enumeration of
+ * the well known types.
+ *
+ * list taken from rfc3359 plus some memory from veterans ;-)
+ */
+
+#define ISIS_TLV_AREA_ADDR 1 /* iso10589 */
+#define ISIS_TLV_IS_REACH 2 /* iso10589 */
+#define ISIS_TLV_ESNEIGH 3 /* iso10589 */
+#define ISIS_TLV_PART_DIS 4 /* iso10589 */
+#define ISIS_TLV_PREFIX_NEIGH 5 /* iso10589 */
+#define ISIS_TLV_ISNEIGH 6 /* iso10589 */
+#define ISIS_TLV_INSTANCE_ID 7 /* rfc8202 */
+#define ISIS_TLV_PADDING 8 /* iso10589 */
+#define ISIS_TLV_LSP 9 /* iso10589 */
+#define ISIS_TLV_AUTH 10 /* iso10589, rfc3567 */
+#define ISIS_TLV_CHECKSUM 12 /* rfc3358 */
+#define ISIS_TLV_CHECKSUM_MINLEN 2
+#define ISIS_TLV_POI 13 /* rfc6232 */
+#define ISIS_TLV_LSP_BUFFERSIZE 14 /* iso10589 rev2 */
+#define ISIS_TLV_EXT_IS_REACH 22 /* rfc5305 */
+#define ISIS_TLV_IS_ALIAS_ID 24 /* rfc5311 */
+#define ISIS_TLV_DECNET_PHASE4 42
+#define ISIS_TLV_LUCENT_PRIVATE 66
+#define ISIS_TLV_INT_IP_REACH 128 /* rfc1195, rfc2966 */
+#define ISIS_TLV_PROTOCOLS 129 /* rfc1195 */
+#define ISIS_TLV_EXT_IP_REACH 130 /* rfc1195, rfc2966 */
+#define ISIS_TLV_IDRP_INFO 131 /* rfc1195 */
+#define ISIS_TLV_IPADDR 132 /* rfc1195 */
+#define ISIS_TLV_IPAUTH 133 /* rfc1195 */
+#define ISIS_TLV_TE_ROUTER_ID 134 /* rfc5305 */
+#define ISIS_TLV_EXTD_IP_REACH 135 /* rfc5305 */
+#define ISIS_TLV_HOSTNAME 137 /* rfc2763 */
+#define ISIS_TLV_SHARED_RISK_GROUP 138 /* draft-ietf-isis-gmpls-extensions */
+#define ISIS_TLV_MT_PORT_CAP 143 /* rfc6165 */
+#define ISIS_TLV_MT_CAPABILITY 144 /* rfc6329 */
+#define ISIS_TLV_NORTEL_PRIVATE1 176
+#define ISIS_TLV_NORTEL_PRIVATE2 177
+#define ISIS_TLV_RESTART_SIGNALING 211 /* rfc3847 */
+#define ISIS_TLV_RESTART_SIGNALING_FLAGLEN 1
+#define ISIS_TLV_RESTART_SIGNALING_HOLDTIMELEN 2
+#define ISIS_TLV_MT_IS_REACH 222 /* draft-ietf-isis-wg-multi-topology-05 */
+#define ISIS_TLV_MT_SUPPORTED 229 /* draft-ietf-isis-wg-multi-topology-05 */
+#define ISIS_TLV_IP6ADDR 232 /* draft-ietf-isis-ipv6-02 */
+#define ISIS_TLV_MT_IP_REACH 235 /* draft-ietf-isis-wg-multi-topology-05 */
+#define ISIS_TLV_IP6_REACH 236 /* draft-ietf-isis-ipv6-02 */
+#define ISIS_TLV_MT_IP6_REACH 237 /* draft-ietf-isis-wg-multi-topology-05 */
+#define ISIS_TLV_PTP_ADJ 240 /* rfc3373 */
+#define ISIS_TLV_IIH_SEQNR 241 /* draft-shen-isis-iih-sequence-00 */
+#define ISIS_TLV_ROUTER_CAPABILITY 242 /* rfc7981 */
+#define ISIS_TLV_VENDOR_PRIVATE 250 /* draft-ietf-isis-experimental-tlv-01 */
+#define ISIS_TLV_VENDOR_PRIVATE_MINLEN 3
+
+static const struct tok isis_tlv_values[] = {
+ { ISIS_TLV_AREA_ADDR, "Area address(es)"},
+ { ISIS_TLV_IS_REACH, "IS Reachability"},
+ { ISIS_TLV_ESNEIGH, "ES Neighbor(s)"},
+ { ISIS_TLV_PART_DIS, "Partition DIS"},
+ { ISIS_TLV_PREFIX_NEIGH, "Prefix Neighbors"},
+ { ISIS_TLV_ISNEIGH, "IS Neighbor(s)"},
+ { ISIS_TLV_INSTANCE_ID, "Instance Identifier"},
+ { ISIS_TLV_PADDING, "Padding"},
+ { ISIS_TLV_LSP, "LSP entries"},
+ { ISIS_TLV_AUTH, "Authentication"},
+ { ISIS_TLV_CHECKSUM, "Checksum"},
+ { ISIS_TLV_POI, "Purge Originator Identifier"},
+ { ISIS_TLV_LSP_BUFFERSIZE, "LSP Buffersize"},
+ { ISIS_TLV_EXT_IS_REACH, "Extended IS Reachability"},
+ { ISIS_TLV_IS_ALIAS_ID, "IS Alias ID"},
+ { ISIS_TLV_DECNET_PHASE4, "DECnet Phase IV"},
+ { ISIS_TLV_LUCENT_PRIVATE, "Lucent Proprietary"},
+ { ISIS_TLV_INT_IP_REACH, "IPv4 Internal Reachability"},
+ { ISIS_TLV_PROTOCOLS, "Protocols supported"},
+ { ISIS_TLV_EXT_IP_REACH, "IPv4 External Reachability"},
+ { ISIS_TLV_IDRP_INFO, "Inter-Domain Information Type"},
+ { ISIS_TLV_IPADDR, "IPv4 Interface address(es)"},
+ { ISIS_TLV_IPAUTH, "IPv4 authentication (deprecated)"},
+ { ISIS_TLV_TE_ROUTER_ID, "Traffic Engineering Router ID"},
+ { ISIS_TLV_EXTD_IP_REACH, "Extended IPv4 Reachability"},
+ { ISIS_TLV_SHARED_RISK_GROUP, "Shared Risk Link Group"},
+ { ISIS_TLV_MT_PORT_CAP, "Multi-Topology-Aware Port Capability"},
+ { ISIS_TLV_MT_CAPABILITY, "Multi-Topology Capability"},
+ { ISIS_TLV_NORTEL_PRIVATE1, "Nortel Proprietary"},
+ { ISIS_TLV_NORTEL_PRIVATE2, "Nortel Proprietary"},
+ { ISIS_TLV_HOSTNAME, "Hostname"},
+ { ISIS_TLV_RESTART_SIGNALING, "Restart Signaling"},
+ { ISIS_TLV_MT_IS_REACH, "Multi Topology IS Reachability"},
+ { ISIS_TLV_MT_SUPPORTED, "Multi Topology"},
+ { ISIS_TLV_IP6ADDR, "IPv6 Interface address(es)"},
+ { ISIS_TLV_MT_IP_REACH, "Multi-Topology IPv4 Reachability"},
+ { ISIS_TLV_IP6_REACH, "IPv6 reachability"},
+ { ISIS_TLV_MT_IP6_REACH, "Multi-Topology IP6 Reachability"},
+ { ISIS_TLV_PTP_ADJ, "Point-to-point Adjacency State"},
+ { ISIS_TLV_IIH_SEQNR, "Hello PDU Sequence Number"},
+ { ISIS_TLV_ROUTER_CAPABILITY, "IS-IS Router Capability"},
+ { ISIS_TLV_VENDOR_PRIVATE, "Vendor Private"},
+ { 0, NULL }
+};
+
+#define ESIS_OPTION_PROTOCOLS 129
+#define ESIS_OPTION_QOS_MAINTENANCE 195 /* iso9542 */
+#define ESIS_OPTION_SECURITY 197 /* iso9542 */
+#define ESIS_OPTION_ES_CONF_TIME 198 /* iso9542 */
+#define ESIS_OPTION_PRIORITY 205 /* iso9542 */
+#define ESIS_OPTION_ADDRESS_MASK 225 /* iso9542 */
+#define ESIS_OPTION_SNPA_MASK 226 /* iso9542 */
+
+static const struct tok esis_option_values[] = {
+ { ESIS_OPTION_PROTOCOLS, "Protocols supported"},
+ { ESIS_OPTION_QOS_MAINTENANCE, "QoS Maintenance" },
+ { ESIS_OPTION_SECURITY, "Security" },
+ { ESIS_OPTION_ES_CONF_TIME, "ES Configuration Time" },
+ { ESIS_OPTION_PRIORITY, "Priority" },
+ { ESIS_OPTION_ADDRESS_MASK, "Addressk Mask" },
+ { ESIS_OPTION_SNPA_MASK, "SNPA Mask" },
+ { 0, NULL }
+};
+
+#define CLNP_OPTION_DISCARD_REASON 193
+#define CLNP_OPTION_QOS_MAINTENANCE 195 /* iso8473 */
+#define CLNP_OPTION_SECURITY 197 /* iso8473 */
+#define CLNP_OPTION_SOURCE_ROUTING 200 /* iso8473 */
+#define CLNP_OPTION_ROUTE_RECORDING 203 /* iso8473 */
+#define CLNP_OPTION_PADDING 204 /* iso8473 */
+#define CLNP_OPTION_PRIORITY 205 /* iso8473 */
+
+static const struct tok clnp_option_values[] = {
+ { CLNP_OPTION_DISCARD_REASON, "Discard Reason"},
+ { CLNP_OPTION_PRIORITY, "Priority"},
+ { CLNP_OPTION_QOS_MAINTENANCE, "QoS Maintenance"},
+ { CLNP_OPTION_SECURITY, "Security"},
+ { CLNP_OPTION_SOURCE_ROUTING, "Source Routing"},
+ { CLNP_OPTION_ROUTE_RECORDING, "Route Recording"},
+ { CLNP_OPTION_PADDING, "Padding"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_rfd_class_values[] = {
+ { 0x0, "General"},
+ { 0x8, "Address"},
+ { 0x9, "Source Routeing"},
+ { 0xa, "Lifetime"},
+ { 0xb, "PDU Discarded"},
+ { 0xc, "Reassembly"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_rfd_general_values[] = {
+ { 0x0, "Reason not specified"},
+ { 0x1, "Protocol procedure error"},
+ { 0x2, "Incorrect checksum"},
+ { 0x3, "PDU discarded due to congestion"},
+ { 0x4, "Header syntax error (cannot be parsed)"},
+ { 0x5, "Segmentation needed but not permitted"},
+ { 0x6, "Incomplete PDU received"},
+ { 0x7, "Duplicate option"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_rfd_address_values[] = {
+ { 0x0, "Destination address unreachable"},
+ { 0x1, "Destination address unknown"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_rfd_source_routeing_values[] = {
+ { 0x0, "Unspecified source routeing error"},
+ { 0x1, "Syntax error in source routeing field"},
+ { 0x2, "Unknown address in source routeing field"},
+ { 0x3, "Path not acceptable"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_rfd_lifetime_values[] = {
+ { 0x0, "Lifetime expired while data unit in transit"},
+ { 0x1, "Lifetime expired during reassembly"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_rfd_pdu_discard_values[] = {
+ { 0x0, "Unsupported option not specified"},
+ { 0x1, "Unsupported protocol version"},
+ { 0x2, "Unsupported security option"},
+ { 0x3, "Unsupported source routeing option"},
+ { 0x4, "Unsupported recording of route option"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_rfd_reassembly_values[] = {
+ { 0x0, "Reassembly interference"},
+ { 0, NULL }
+};
+
+/* array of 16 error-classes */
+static const struct tok *clnp_option_rfd_error_class[] = {
+ clnp_option_rfd_general_values,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ clnp_option_rfd_address_values,
+ clnp_option_rfd_source_routeing_values,
+ clnp_option_rfd_lifetime_values,
+ clnp_option_rfd_pdu_discard_values,
+ clnp_option_rfd_reassembly_values,
+ NULL,
+ NULL,
+ NULL
+};
+
+#define CLNP_OPTION_OPTION_QOS_MASK 0x3f
+#define CLNP_OPTION_SCOPE_MASK 0xc0
+#define CLNP_OPTION_SCOPE_SA_SPEC 0x40
+#define CLNP_OPTION_SCOPE_DA_SPEC 0x80
+#define CLNP_OPTION_SCOPE_GLOBAL 0xc0
+
+static const struct tok clnp_option_scope_values[] = {
+ { CLNP_OPTION_SCOPE_SA_SPEC, "Source Address Specific"},
+ { CLNP_OPTION_SCOPE_DA_SPEC, "Destination Address Specific"},
+ { CLNP_OPTION_SCOPE_GLOBAL, "Globally unique"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_sr_rr_values[] = {
+ { 0x0, "partial"},
+ { 0x1, "complete"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_sr_rr_string_values[] = {
+ { CLNP_OPTION_SOURCE_ROUTING, "source routing"},
+ { CLNP_OPTION_ROUTE_RECORDING, "recording of route in progress"},
+ { 0, NULL }
+};
+
+static const struct tok clnp_option_qos_global_values[] = {
+ { 0x20, "reserved"},
+ { 0x10, "sequencing vs. delay"},
+ { 0x08, "congested"},
+ { 0x04, "delay vs. cost"},
+ { 0x02, "error vs. delay"},
+ { 0x01, "error vs. cost"},
+ { 0, NULL }
+};
+
+static const struct tok isis_tlv_router_capability_flags[] = {
+ { 0x01, "S bit"},
+ { 0x02, "D bit"},
+ { 0, NULL }
+};
+
+#define ISIS_SUBTLV_ROUTER_CAP_SR 2 /* rfc 8667 */
+
+static const struct tok isis_router_capability_subtlv_values[] = {
+ { ISIS_SUBTLV_ROUTER_CAP_SR, "SR-Capabilities"},
+ { 0, NULL }
+};
+
+static const struct tok isis_router_capability_sr_flags[] = {
+ { 0x80, "ipv4"},
+ { 0x40, "ipv6"},
+ { 0, NULL }
+};
+
+#define ISIS_SUBTLV_EXT_IS_REACH_ADMIN_GROUP 3 /* rfc5305 */
+#define ISIS_SUBTLV_EXT_IS_REACH_LINK_LOCAL_REMOTE_ID 4 /* rfc4205 */
+#define ISIS_SUBTLV_EXT_IS_REACH_LINK_REMOTE_ID 5 /* rfc5305 */
+#define ISIS_SUBTLV_EXT_IS_REACH_IPV4_INTF_ADDR 6 /* rfc5305 */
+#define ISIS_SUBTLV_EXT_IS_REACH_IPV4_NEIGHBOR_ADDR 8 /* rfc5305 */
+#define ISIS_SUBTLV_EXT_IS_REACH_MAX_LINK_BW 9 /* rfc5305 */
+#define ISIS_SUBTLV_EXT_IS_REACH_RESERVABLE_BW 10 /* rfc5305 */
+#define ISIS_SUBTLV_EXT_IS_REACH_UNRESERVED_BW 11 /* rfc4124 */
+#define ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS_OLD 12 /* draft-ietf-tewg-diff-te-proto-06 */
+#define ISIS_SUBTLV_EXT_IS_REACH_TE_METRIC 18 /* rfc5305 */
+#define ISIS_SUBTLV_EXT_IS_REACH_LINK_ATTRIBUTE 19 /* draft-ietf-isis-link-attr-01 */
+#define ISIS_SUBTLV_EXT_IS_REACH_LINK_PROTECTION_TYPE 20 /* rfc4205 */
+#define ISIS_SUBTLV_EXT_IS_REACH_INTF_SW_CAP_DESCR 21 /* rfc4205 */
+#define ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS 22 /* rfc4124 */
+#define ISIS_SUBTLV_EXT_IS_REACH_LAN_ADJ_SEGMENT_ID 32 /* rfc8667 */
+
+#define ISIS_SUBTLV_SPB_METRIC 29 /* rfc6329 */
+
+static const struct tok isis_ext_is_reach_subtlv_values[] = {
+ { ISIS_SUBTLV_EXT_IS_REACH_ADMIN_GROUP, "Administrative groups" },
+ { ISIS_SUBTLV_EXT_IS_REACH_LINK_LOCAL_REMOTE_ID, "Link Local/Remote Identifier" },
+ { ISIS_SUBTLV_EXT_IS_REACH_LINK_REMOTE_ID, "Link Remote Identifier" },
+ { ISIS_SUBTLV_EXT_IS_REACH_IPV4_INTF_ADDR, "IPv4 interface address" },
+ { ISIS_SUBTLV_EXT_IS_REACH_IPV4_NEIGHBOR_ADDR, "IPv4 neighbor address" },
+ { ISIS_SUBTLV_EXT_IS_REACH_MAX_LINK_BW, "Maximum link bandwidth" },
+ { ISIS_SUBTLV_EXT_IS_REACH_RESERVABLE_BW, "Reservable link bandwidth" },
+ { ISIS_SUBTLV_EXT_IS_REACH_UNRESERVED_BW, "Unreserved bandwidth" },
+ { ISIS_SUBTLV_EXT_IS_REACH_TE_METRIC, "Traffic Engineering Metric" },
+ { ISIS_SUBTLV_EXT_IS_REACH_LINK_ATTRIBUTE, "Link Attribute" },
+ { ISIS_SUBTLV_EXT_IS_REACH_LINK_PROTECTION_TYPE, "Link Protection Type" },
+ { ISIS_SUBTLV_EXT_IS_REACH_INTF_SW_CAP_DESCR, "Interface Switching Capability" },
+ { ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS_OLD, "Bandwidth Constraints (old)" },
+ { ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS, "Bandwidth Constraints" },
+ { ISIS_SUBTLV_EXT_IS_REACH_LAN_ADJ_SEGMENT_ID, "LAN Adjacency Segment Identifier" },
+ { ISIS_SUBTLV_SPB_METRIC, "SPB Metric" },
+ { 250, "Reserved for cisco specific extensions" },
+ { 251, "Reserved for cisco specific extensions" },
+ { 252, "Reserved for cisco specific extensions" },
+ { 253, "Reserved for cisco specific extensions" },
+ { 254, "Reserved for cisco specific extensions" },
+ { 255, "Reserved for future expansion" },
+ { 0, NULL }
+};
+
+#define ISIS_SUBTLV_EXTD_IP_REACH_ADMIN_TAG32 1 /* draft-ietf-isis-admin-tags-01 */
+#define ISIS_SUBTLV_EXTD_IP_REACH_ADMIN_TAG64 2 /* draft-ietf-isis-admin-tags-01 */
+#define ISIS_SUBTLV_EXTD_IP_REACH_PREFIX_SID 3 /* rfc8667 */
+#define ISIS_SUBTLV_EXTD_IP_REACH_MGMT_PREFIX_COLOR 117 /* draft-ietf-isis-wg-multi-topology-05 */
+
+static const struct tok isis_ext_ip_reach_subtlv_values[] = {
+ { ISIS_SUBTLV_EXTD_IP_REACH_ADMIN_TAG32, "32-Bit Administrative tag" },
+ { ISIS_SUBTLV_EXTD_IP_REACH_ADMIN_TAG64, "64-Bit Administrative tag" },
+ { ISIS_SUBTLV_EXTD_IP_REACH_PREFIX_SID, "Prefix SID" },
+ { ISIS_SUBTLV_EXTD_IP_REACH_MGMT_PREFIX_COLOR, "Management Prefix Color" },
+ { 0, NULL }
+};
+
+#define ISIS_PREFIX_SID_FLAG_R 0x80 /* rfc 8667 */
+#define ISIS_PREFIX_SID_FLAG_N 0x40 /* rfc 8667 */
+#define ISIS_PREFIX_SID_FLAG_P 0x20 /* rfc 8667 */
+#define ISIS_PREFIX_SID_FLAG_E 0x10 /* rfc 8667 */
+#define ISIS_PREFIX_SID_FLAG_V 0x08 /* rfc 8667 */
+#define ISIS_PREFIX_SID_FLAG_L 0x04 /* rfc 8667 */
+
+static const struct tok prefix_sid_flag_values[] = {
+ { ISIS_PREFIX_SID_FLAG_R, "Readvertisement"},
+ { ISIS_PREFIX_SID_FLAG_N, "Node"},
+ { ISIS_PREFIX_SID_FLAG_P, "No-PHP"},
+ { ISIS_PREFIX_SID_FLAG_E, "Explicit NULL"},
+ { ISIS_PREFIX_SID_FLAG_V, "Value"},
+ { ISIS_PREFIX_SID_FLAG_L, "Local"},
+ { 0, NULL}
+};
+
+
+/* rfc 8667 */
+static const struct tok prefix_sid_algo_values[] = {
+ { 0, "SPF"},
+ { 1, "strict-SPF"},
+ { 0, NULL}
+};
+
+static const struct tok isis_subtlv_link_attribute_values[] = {
+ { 0x01, "Local Protection Available" },
+ { 0x02, "Link excluded from local protection path" },
+ { 0x04, "Local maintenance required"},
+ { 0, NULL }
+};
+
+static const struct tok isis_lan_adj_sid_flag_values[] = {
+ { 0x80, "Address family IPv6" },
+ { 0x40, "Backup" },
+ { 0x20, "Value" },
+ { 0x10, "Local significance" },
+ { 0x08, "Set of adjacencies" },
+ { 0x04, "Persistent" },
+ { 0, NULL }
+};
+
+#define ISIS_SUBTLV_AUTH_SIMPLE 1
+#define ISIS_SUBTLV_AUTH_GENERIC 3 /* rfc 5310 */
+#define ISIS_SUBTLV_AUTH_MD5 54
+#define ISIS_SUBTLV_AUTH_MD5_LEN 16
+#define ISIS_SUBTLV_AUTH_PRIVATE 255
+
+static const struct tok isis_subtlv_auth_values[] = {
+ { ISIS_SUBTLV_AUTH_SIMPLE, "simple text password"},
+ { ISIS_SUBTLV_AUTH_GENERIC, "Generic Crypto key-id"},
+ { ISIS_SUBTLV_AUTH_MD5, "HMAC-MD5 password"},
+ { ISIS_SUBTLV_AUTH_PRIVATE, "Routing Domain private password"},
+ { 0, NULL }
+};
+
+#define ISIS_SUBTLV_IDRP_RES 0
+#define ISIS_SUBTLV_IDRP_LOCAL 1
+#define ISIS_SUBTLV_IDRP_ASN 2
+
+static const struct tok isis_subtlv_idrp_values[] = {
+ { ISIS_SUBTLV_IDRP_RES, "Reserved"},
+ { ISIS_SUBTLV_IDRP_LOCAL, "Routing-Domain Specific"},
+ { ISIS_SUBTLV_IDRP_ASN, "AS Number Tag"},
+ { 0, NULL}
+};
+
+#define ISIS_SUBTLV_SPB_MCID 4
+#define ISIS_SUBTLV_SPB_DIGEST 5
+#define ISIS_SUBTLV_SPB_BVID 6
+
+#define ISIS_SUBTLV_SPB_INSTANCE 1
+#define ISIS_SUBTLV_SPBM_SI 3
+
+#define ISIS_SPB_MCID_LEN 51
+#define ISIS_SUBTLV_SPB_MCID_MIN_LEN 102
+#define ISIS_SUBTLV_SPB_DIGEST_MIN_LEN 33
+#define ISIS_SUBTLV_SPB_BVID_MIN_LEN 6
+#define ISIS_SUBTLV_SPB_INSTANCE_MIN_LEN 19
+#define ISIS_SUBTLV_SPB_INSTANCE_VLAN_TUPLE_LEN 8
+
+static const struct tok isis_mt_port_cap_subtlv_values[] = {
+ { ISIS_SUBTLV_SPB_MCID, "SPB MCID" },
+ { ISIS_SUBTLV_SPB_DIGEST, "SPB Digest" },
+ { ISIS_SUBTLV_SPB_BVID, "SPB BVID" },
+ { 0, NULL }
+};
+
+static const struct tok isis_mt_capability_subtlv_values[] = {
+ { ISIS_SUBTLV_SPB_INSTANCE, "SPB Instance" },
+ { ISIS_SUBTLV_SPBM_SI, "SPBM Service Identifier and Unicast Address" },
+ { 0, NULL }
+};
+
+struct isis_spb_mcid {
+ nd_uint8_t format_id;
+ nd_byte name[32];
+ nd_uint16_t revision_lvl;
+ nd_byte digest[16];
+};
+
+struct isis_subtlv_spb_mcid {
+ struct isis_spb_mcid mcid;
+ struct isis_spb_mcid aux_mcid;
+};
+
+struct isis_subtlv_spb_instance {
+ nd_byte cist_root_id[8];
+ nd_uint32_t cist_external_root_path_cost;
+ nd_uint16_t bridge_priority;
+ nd_uint32_t spsourceid;
+ nd_uint8_t no_of_trees;
+};
+
+#define CLNP_SEGMENT_PART 0x80
+#define CLNP_MORE_SEGMENTS 0x40
+#define CLNP_REQUEST_ER 0x20
+
+static const struct tok clnp_flag_values[] = {
+ { CLNP_SEGMENT_PART, "Segmentation permitted"},
+ { CLNP_MORE_SEGMENTS, "more Segments"},
+ { CLNP_REQUEST_ER, "request Error Report"},
+ { 0, NULL}
+};
+
+#define ISIS_MASK_LSP_OL_BIT(x) (GET_U_1(x)&0x4)
+#define ISIS_MASK_LSP_ISTYPE_BITS(x) (GET_U_1(x)&0x3)
+#define ISIS_MASK_LSP_PARTITION_BIT(x) (GET_U_1(x)&0x80)
+#define ISIS_MASK_LSP_ATT_BITS(x) (GET_U_1(x)&0x78)
+#define ISIS_MASK_LSP_ATT_ERROR_BIT(x) (GET_U_1(x)&0x40)
+#define ISIS_MASK_LSP_ATT_EXPENSE_BIT(x) (GET_U_1(x)&0x20)
+#define ISIS_MASK_LSP_ATT_DELAY_BIT(x) (GET_U_1(x)&0x10)
+#define ISIS_MASK_LSP_ATT_DEFAULT_BIT(x) (GET_U_1(x)&0x8)
+
+#define ISIS_MASK_MTID(x) ((x)&0x0fff)
+#define ISIS_MASK_MTFLAGS(x) ((x)&0xf000)
+
+static const struct tok isis_mt_flag_values[] = {
+ { 0x4000, "ATT bit set"},
+ { 0x8000, "Overload bit set"},
+ { 0, NULL}
+};
+
+#define ISIS_MASK_TLV_EXTD_IP_UPDOWN(x) ((x)&0x80)
+#define ISIS_MASK_TLV_EXTD_IP_SUBTLV(x) ((x)&0x40)
+
+#define ISIS_MASK_TLV_EXTD_IP6_IE(x) ((x)&0x40)
+#define ISIS_MASK_TLV_EXTD_IP6_SUBTLV(x) ((x)&0x20)
+
+#define ISIS_LSP_TLV_METRIC_SUPPORTED(x) (GET_U_1(x)&0x80)
+#define ISIS_LSP_TLV_METRIC_IE(x) (GET_U_1(x)&0x40)
+#define ISIS_LSP_TLV_METRIC_UPDOWN(x) (GET_U_1(x)&0x80)
+#define ISIS_LSP_TLV_METRIC_VALUE(x) (GET_U_1(x)&0x3f)
+
+#define ISIS_MASK_TLV_SHARED_RISK_GROUP(x) ((x)&0x1)
+
+static const struct tok isis_mt_values[] = {
+ { 0, "IPv4 unicast"},
+ { 1, "In-Band Management"},
+ { 2, "IPv6 unicast"},
+ { 3, "Multicast"},
+ { 4095, "Development, Experimental or Proprietary"},
+ { 0, NULL }
+};
+
+static const struct tok isis_iih_circuit_type_values[] = {
+ { 1, "Level 1 only"},
+ { 2, "Level 2 only"},
+ { 3, "Level 1, Level 2"},
+ { 0, NULL}
+};
+
+#define ISIS_LSP_TYPE_UNUSED0 0
+#define ISIS_LSP_TYPE_LEVEL_1 1
+#define ISIS_LSP_TYPE_UNUSED2 2
+#define ISIS_LSP_TYPE_LEVEL_2 3
+
+static const struct tok isis_lsp_istype_values[] = {
+ { ISIS_LSP_TYPE_UNUSED0, "Unused 0x0 (invalid)"},
+ { ISIS_LSP_TYPE_LEVEL_1, "L1 IS"},
+ { ISIS_LSP_TYPE_UNUSED2, "Unused 0x2 (invalid)"},
+ { ISIS_LSP_TYPE_LEVEL_2, "L2 IS"},
+ { 0, NULL }
+};
+
+/*
+ * Katz's point to point adjacency TLV uses codes to tell us the state of
+ * the remote adjacency. Enumerate them.
+ */
+
+#define ISIS_PTP_ADJ_UP 0
+#define ISIS_PTP_ADJ_INIT 1
+#define ISIS_PTP_ADJ_DOWN 2
+
+static const struct tok isis_ptp_adjancey_values[] = {
+ { ISIS_PTP_ADJ_UP, "Up" },
+ { ISIS_PTP_ADJ_INIT, "Initializing" },
+ { ISIS_PTP_ADJ_DOWN, "Down" },
+ { 0, NULL}
+};
+
+struct isis_tlv_ptp_adj {
+ nd_uint8_t adjacency_state;
+ nd_uint32_t extd_local_circuit_id;
+ nd_byte neighbor_sysid[SYSTEM_ID_LEN];
+ nd_uint32_t neighbor_extd_local_circuit_id;
+};
+
+static void osi_print_cksum(netdissect_options *, const uint8_t *pptr,
+ uint16_t checksum, int checksum_offset, u_int length);
+static int clnp_print(netdissect_options *, const uint8_t *, u_int);
+static void esis_print(netdissect_options *, const uint8_t *, u_int);
+static int isis_print(netdissect_options *, const uint8_t *, u_int);
+
+struct isis_metric_block {
+ nd_uint8_t metric_default;
+ nd_uint8_t metric_delay;
+ nd_uint8_t metric_expense;
+ nd_uint8_t metric_error;
+};
+
+struct isis_tlv_is_reach {
+ struct isis_metric_block isis_metric_block;
+ nd_byte neighbor_nodeid[NODE_ID_LEN];
+};
+
+struct isis_tlv_es_reach {
+ struct isis_metric_block isis_metric_block;
+ nd_byte neighbor_sysid[SYSTEM_ID_LEN];
+};
+
+struct isis_tlv_ip_reach {
+ struct isis_metric_block isis_metric_block;
+ nd_ipv4 prefix;
+ nd_ipv4 mask;
+};
+
+static const struct tok isis_is_reach_virtual_values[] = {
+ { 0, "IsNotVirtual"},
+ { 1, "IsVirtual"},
+ { 0, NULL }
+};
+
+static const struct tok isis_restart_flag_values[] = {
+ { 0x1, "Restart Request"},
+ { 0x2, "Restart Acknowledgement"},
+ { 0x4, "Suppress adjacency advertisement"},
+ { 0, NULL }
+};
+
+struct isis_common_header {
+ nd_uint8_t nlpid;
+ nd_uint8_t fixed_len;
+ nd_uint8_t version; /* Protocol version */
+ nd_uint8_t id_length;
+ nd_uint8_t pdu_type; /* 3 MSbits are reserved */
+ nd_uint8_t pdu_version; /* Packet format version */
+ nd_byte reserved;
+ nd_uint8_t max_area;
+};
+
+struct isis_iih_lan_header {
+ nd_uint8_t circuit_type;
+ nd_byte source_id[SYSTEM_ID_LEN];
+ nd_uint16_t holding_time;
+ nd_uint16_t pdu_len;
+ nd_uint8_t priority;
+ nd_byte lan_id[NODE_ID_LEN];
+};
+
+struct isis_iih_ptp_header {
+ nd_uint8_t circuit_type;
+ nd_byte source_id[SYSTEM_ID_LEN];
+ nd_uint16_t holding_time;
+ nd_uint16_t pdu_len;
+ nd_uint8_t circuit_id;
+};
+
+struct isis_lsp_header {
+ nd_uint16_t pdu_len;
+ nd_uint16_t remaining_lifetime;
+ nd_byte lsp_id[LSP_ID_LEN];
+ nd_uint32_t sequence_number;
+ nd_uint16_t checksum;
+ nd_uint8_t typeblock;
+};
+
+struct isis_csnp_header {
+ nd_uint16_t pdu_len;
+ nd_byte source_id[NODE_ID_LEN];
+ nd_byte start_lsp_id[LSP_ID_LEN];
+ nd_byte end_lsp_id[LSP_ID_LEN];
+};
+
+struct isis_psnp_header {
+ nd_uint16_t pdu_len;
+ nd_byte source_id[NODE_ID_LEN];
+};
+
+struct isis_tlv_lsp {
+ nd_uint16_t remaining_lifetime;
+ nd_byte lsp_id[LSP_ID_LEN];
+ nd_uint32_t sequence_number;
+ nd_uint16_t checksum;
+};
+
+#define ISIS_COMMON_HEADER_SIZE (sizeof(struct isis_common_header))
+#define ISIS_IIH_LAN_HEADER_SIZE (sizeof(struct isis_iih_lan_header))
+#define ISIS_IIH_PTP_HEADER_SIZE (sizeof(struct isis_iih_ptp_header))
+#define ISIS_LSP_HEADER_SIZE (sizeof(struct isis_lsp_header))
+#define ISIS_CSNP_HEADER_SIZE (sizeof(struct isis_csnp_header))
+#define ISIS_PSNP_HEADER_SIZE (sizeof(struct isis_psnp_header))
+
+void
+isoclns_print(netdissect_options *ndo, const u_char *p, u_int length)
+{
+ ndo->ndo_protocol = "isoclns";
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("OSI NLPID %s (0x%02x): ",
+ tok2str(nlpid_values, "Unknown", GET_U_1(p)),
+ GET_U_1(p));
+
+ switch (GET_U_1(p)) {
+
+ case NLPID_CLNP:
+ if (!clnp_print(ndo, p, length))
+ print_unknown_data(ndo, p, "\n\t", length);
+ break;
+
+ case NLPID_ESIS:
+ esis_print(ndo, p, length);
+ return;
+
+ case NLPID_ISIS:
+ if (!isis_print(ndo, p, length))
+ print_unknown_data(ndo, p, "\n\t", length);
+ break;
+
+ case NLPID_NULLNS:
+ ND_PRINT("%slength: %u", ndo->ndo_eflag ? "" : ", ", length);
+ break;
+
+ case NLPID_Q933:
+ q933_print(ndo, p + 1, length - 1);
+ break;
+
+ case NLPID_IP:
+ ip_print(ndo, p + 1, length - 1);
+ break;
+
+ case NLPID_IP6:
+ ip6_print(ndo, p + 1, length - 1);
+ break;
+
+ case NLPID_PPP:
+ ppp_print(ndo, p + 1, length - 1);
+ break;
+
+ default:
+ if (!ndo->ndo_eflag)
+ ND_PRINT("OSI NLPID 0x%02x unknown", GET_U_1(p));
+ ND_PRINT("%slength: %u", ndo->ndo_eflag ? "" : ", ", length);
+ if (length > 1)
+ print_unknown_data(ndo, p, "\n\t", length);
+ break;
+ }
+}
+
+#define CLNP_PDU_ER 1
+#define CLNP_PDU_DT 28
+#define CLNP_PDU_MD 29
+#define CLNP_PDU_ERQ 30
+#define CLNP_PDU_ERP 31
+
+static const struct tok clnp_pdu_values[] = {
+ { CLNP_PDU_ER, "Error Report"},
+ { CLNP_PDU_MD, "MD"},
+ { CLNP_PDU_DT, "Data"},
+ { CLNP_PDU_ERQ, "Echo Request"},
+ { CLNP_PDU_ERP, "Echo Response"},
+ { 0, NULL }
+};
+
+struct clnp_header_t {
+ nd_uint8_t nlpid;
+ nd_uint8_t length_indicator;
+ nd_uint8_t version;
+ nd_uint8_t lifetime; /* units of 500ms */
+ nd_uint8_t type;
+ nd_uint16_t segment_length;
+ nd_uint16_t cksum;
+};
+
+struct clnp_segment_header_t {
+ nd_uint16_t data_unit_id;
+ nd_uint16_t segment_offset;
+ nd_uint16_t total_length;
+};
+
+/*
+ * clnp_print
+ * Decode CLNP packets. Return 0 on error.
+ */
+
+static int
+clnp_print(netdissect_options *ndo,
+ const uint8_t *pptr, u_int length)
+{
+ const uint8_t *optr,*source_address,*dest_address;
+ u_int li,li_remaining,tlen,nsap_offset,source_address_length,dest_address_length, clnp_pdu_type, clnp_flags;
+ const struct clnp_header_t *clnp_header;
+ const struct clnp_segment_header_t *clnp_segment_header;
+ uint8_t rfd_error,rfd_error_major,rfd_error_minor;
+
+ ndo->ndo_protocol = "clnp";
+ clnp_header = (const struct clnp_header_t *) pptr;
+ ND_TCHECK_SIZE(clnp_header);
+
+ li = GET_U_1(clnp_header->length_indicator);
+ li_remaining = li;
+ optr = pptr;
+
+ if (!ndo->ndo_eflag)
+ nd_print_protocol_caps(ndo);
+
+ /*
+ * Sanity checking of the header.
+ */
+
+ if (GET_U_1(clnp_header->version) != CLNP_VERSION) {
+ ND_PRINT("version %u packet not supported",
+ GET_U_1(clnp_header->version));
+ return (0);
+ }
+
+ if (li > length) {
+ ND_PRINT(" length indicator(%u) > PDU size (%u)!", li, length);
+ return (0);
+ }
+
+ if (li < sizeof(struct clnp_header_t)) {
+ ND_PRINT(" length indicator %u < min PDU size:", li);
+ while (pptr < ndo->ndo_snapend) {
+ ND_PRINT("%02X", GET_U_1(pptr));
+ pptr++;
+ }
+ return (0);
+ }
+
+ /* FIXME further header sanity checking */
+
+ clnp_pdu_type = GET_U_1(clnp_header->type) & CLNP_PDU_TYPE_MASK;
+ clnp_flags = GET_U_1(clnp_header->type) & CLNP_FLAG_MASK;
+
+ pptr += sizeof(struct clnp_header_t);
+ li_remaining -= sizeof(struct clnp_header_t);
+
+ if (li_remaining < 1) {
+ ND_PRINT("li < size of fixed part of CLNP header and addresses");
+ return (0);
+ }
+ dest_address_length = GET_U_1(pptr);
+ pptr += 1;
+ li_remaining -= 1;
+ if (li_remaining < dest_address_length) {
+ ND_PRINT("li < size of fixed part of CLNP header and addresses");
+ return (0);
+ }
+ ND_TCHECK_LEN(pptr, dest_address_length);
+ dest_address = pptr;
+ pptr += dest_address_length;
+ li_remaining -= dest_address_length;
+
+ if (li_remaining < 1) {
+ ND_PRINT("li < size of fixed part of CLNP header and addresses");
+ return (0);
+ }
+ source_address_length = GET_U_1(pptr);
+ pptr += 1;
+ li_remaining -= 1;
+ if (li_remaining < source_address_length) {
+ ND_PRINT("li < size of fixed part of CLNP header and addresses");
+ return (0);
+ }
+ ND_TCHECK_LEN(pptr, source_address_length);
+ source_address = pptr;
+ pptr += source_address_length;
+ li_remaining -= source_address_length;
+
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("%s%s > %s, %s, length %u",
+ ndo->ndo_eflag ? "" : ", ",
+ GET_ISONSAP_STRING(source_address, source_address_length),
+ GET_ISONSAP_STRING(dest_address, dest_address_length),
+ tok2str(clnp_pdu_values,"unknown (%u)",clnp_pdu_type),
+ length);
+ return (1);
+ }
+ ND_PRINT("%slength %u", ndo->ndo_eflag ? "" : ", ", length);
+
+ ND_PRINT("\n\t%s PDU, hlen: %u, v: %u, lifetime: %u.%us, Segment PDU length: %u, checksum: 0x%04x",
+ tok2str(clnp_pdu_values, "unknown (%u)",clnp_pdu_type),
+ GET_U_1(clnp_header->length_indicator),
+ GET_U_1(clnp_header->version),
+ GET_U_1(clnp_header->lifetime)/2,
+ (GET_U_1(clnp_header->lifetime)%2)*5,
+ GET_BE_U_2(clnp_header->segment_length),
+ GET_BE_U_2(clnp_header->cksum));
+
+ osi_print_cksum(ndo, optr, GET_BE_U_2(clnp_header->cksum), 7,
+ GET_U_1(clnp_header->length_indicator));
+
+ ND_PRINT("\n\tFlags [%s]",
+ bittok2str(clnp_flag_values, "none", clnp_flags));
+
+ ND_PRINT("\n\tsource address (length %u): %s\n\tdest address (length %u): %s",
+ source_address_length,
+ GET_ISONSAP_STRING(source_address, source_address_length),
+ dest_address_length,
+ GET_ISONSAP_STRING(dest_address, dest_address_length));
+
+ if (clnp_flags & CLNP_SEGMENT_PART) {
+ if (li_remaining < sizeof(struct clnp_segment_header_t)) {
+ ND_PRINT("li < size of fixed part of CLNP header, addresses, and segment part");
+ return (0);
+ }
+ clnp_segment_header = (const struct clnp_segment_header_t *) pptr;
+ ND_TCHECK_SIZE(clnp_segment_header);
+ ND_PRINT("\n\tData Unit ID: 0x%04x, Segment Offset: %u, Total PDU Length: %u",
+ GET_BE_U_2(clnp_segment_header->data_unit_id),
+ GET_BE_U_2(clnp_segment_header->segment_offset),
+ GET_BE_U_2(clnp_segment_header->total_length));
+ pptr+=sizeof(struct clnp_segment_header_t);
+ li_remaining-=sizeof(struct clnp_segment_header_t);
+ }
+
+ /* now walk the options */
+ while (li_remaining != 0) {
+ u_int op, opli;
+ const uint8_t *tptr;
+
+ if (li_remaining < 2) {
+ ND_PRINT(", bad opts/li");
+ return (0);
+ }
+ op = GET_U_1(pptr);
+ opli = GET_U_1(pptr + 1);
+ pptr += 2;
+ li_remaining -= 2;
+ if (opli > li_remaining) {
+ ND_PRINT(", opt (%u) too long", op);
+ return (0);
+ }
+ ND_TCHECK_LEN(pptr, opli);
+ li_remaining -= opli;
+ tptr = pptr;
+ tlen = opli;
+
+ ND_PRINT("\n\t %s Option #%u, length %u, value: ",
+ tok2str(clnp_option_values,"Unknown",op),
+ op,
+ opli);
+
+ /*
+ * We've already checked that the entire option is present
+ * in the captured packet with the ND_TCHECK_LEN() call.
+ * Therefore, we don't need to do ND_TCHECK()/ND_TCHECK_LEN()
+ * checks.
+ * We do, however, need to check tlen, to make sure we
+ * don't run past the end of the option.
+ */
+ switch (op) {
+
+
+ case CLNP_OPTION_ROUTE_RECORDING: /* those two options share the format */
+ case CLNP_OPTION_SOURCE_ROUTING:
+ if (tlen < 2) {
+ ND_PRINT(", bad opt len");
+ return (0);
+ }
+ ND_PRINT("%s %s",
+ tok2str(clnp_option_sr_rr_values,"Unknown",GET_U_1(tptr)),
+ tok2str(clnp_option_sr_rr_string_values, "Unknown Option %u", op));
+ nsap_offset=GET_U_1(tptr + 1);
+ if (nsap_offset == 0) {
+ ND_PRINT(" Bad NSAP offset (0)");
+ break;
+ }
+ nsap_offset-=1; /* offset to nsap list */
+ if (nsap_offset > tlen) {
+ ND_PRINT(" Bad NSAP offset (past end of option)");
+ break;
+ }
+ tptr+=nsap_offset;
+ tlen-=nsap_offset;
+ while (tlen > 0) {
+ source_address_length=GET_U_1(tptr);
+ if (tlen < source_address_length+1) {
+ ND_PRINT("\n\t NSAP address goes past end of option");
+ break;
+ }
+ if (source_address_length > 0) {
+ source_address=(tptr+1);
+ ND_PRINT("\n\t NSAP address (length %u): %s",
+ source_address_length,
+ GET_ISONSAP_STRING(source_address, source_address_length));
+ }
+ tlen-=source_address_length+1;
+ }
+ break;
+
+ case CLNP_OPTION_PRIORITY:
+ if (tlen < 1) {
+ ND_PRINT(", bad opt len");
+ return (0);
+ }
+ ND_PRINT("0x%1x", GET_U_1(tptr)&0x0f);
+ break;
+
+ case CLNP_OPTION_QOS_MAINTENANCE:
+ if (tlen < 1) {
+ ND_PRINT(", bad opt len");
+ return (0);
+ }
+ ND_PRINT("\n\t Format Code: %s",
+ tok2str(clnp_option_scope_values, "Reserved", GET_U_1(tptr) & CLNP_OPTION_SCOPE_MASK));
+
+ if ((GET_U_1(tptr)&CLNP_OPTION_SCOPE_MASK) == CLNP_OPTION_SCOPE_GLOBAL)
+ ND_PRINT("\n\t QoS Flags [%s]",
+ bittok2str(clnp_option_qos_global_values,
+ "none",
+ GET_U_1(tptr)&CLNP_OPTION_OPTION_QOS_MASK));
+ break;
+
+ case CLNP_OPTION_SECURITY:
+ if (tlen < 2) {
+ ND_PRINT(", bad opt len");
+ return (0);
+ }
+ ND_PRINT("\n\t Format Code: %s, Security-Level %u",
+ tok2str(clnp_option_scope_values,"Reserved",GET_U_1(tptr)&CLNP_OPTION_SCOPE_MASK),
+ GET_U_1(tptr + 1));
+ break;
+
+ case CLNP_OPTION_DISCARD_REASON:
+ if (tlen < 1) {
+ ND_PRINT(", bad opt len");
+ return (0);
+ }
+ rfd_error = GET_U_1(tptr);
+ rfd_error_major = (rfd_error&0xf0) >> 4;
+ rfd_error_minor = rfd_error&0x0f;
+ ND_PRINT("\n\t Class: %s Error (0x%01x), %s (0x%01x)",
+ tok2str(clnp_option_rfd_class_values,"Unknown",rfd_error_major),
+ rfd_error_major,
+ tok2str(clnp_option_rfd_error_class[rfd_error_major],"Unknown",rfd_error_minor),
+ rfd_error_minor);
+ break;
+
+ case CLNP_OPTION_PADDING:
+ ND_PRINT("padding data");
+ break;
+
+ /*
+ * FIXME those are the defined Options that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ default:
+ print_unknown_data(ndo, tptr, "\n\t ", opli);
+ break;
+ }
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, pptr, "\n\t ", opli);
+ pptr += opli;
+ }
+
+ switch (clnp_pdu_type) {
+
+ case CLNP_PDU_ER: /* fall through */
+ case CLNP_PDU_ERP:
+ if (GET_U_1(pptr) == NLPID_CLNP) {
+ ND_PRINT("\n\t-----original packet-----\n\t");
+ /* FIXME recursion protection */
+ clnp_print(ndo, pptr, length - li);
+ break;
+ }
+
+ /* The cases above break from the switch block if they see and print
+ * a CLNP header in the Data part. For an Error Report PDU this is
+ * described in Section 7.9.6 of ITU X.233 (1997 E), also known as
+ * ISO/IEC 8473-1:1998(E). It is not clear why in this code the same
+ * applies to an Echo Response PDU, as the standard does not specify
+ * the contents -- could be a proprietary extension or a bug. In either
+ * case, if the Data part does not contain a CLNP header, its structure
+ * is considered unknown and the decoding falls through to print the
+ * contents as-is.
+ */
+ ND_FALL_THROUGH;
+
+ case CLNP_PDU_DT:
+ case CLNP_PDU_MD:
+ case CLNP_PDU_ERQ:
+
+ default:
+ /* dump the PDU specific data */
+ if (length > ND_BYTES_BETWEEN(pptr, optr)) {
+ ND_PRINT("\n\t undecoded non-header data, length %u", length-li);
+ print_unknown_data(ndo, pptr, "\n\t ", length - ND_BYTES_BETWEEN(pptr, optr));
+ }
+ }
+
+ return (1);
+
+ trunc:
+ nd_print_trunc(ndo);
+ return (1);
+
+}
+
+
+#define ESIS_PDU_REDIRECT 6
+#define ESIS_PDU_ESH 2
+#define ESIS_PDU_ISH 4
+
+static const struct tok esis_pdu_values[] = {
+ { ESIS_PDU_REDIRECT, "redirect"},
+ { ESIS_PDU_ESH, "ESH"},
+ { ESIS_PDU_ISH, "ISH"},
+ { 0, NULL }
+};
+
+struct esis_header_t {
+ nd_uint8_t nlpid;
+ nd_uint8_t length_indicator;
+ nd_uint8_t version;
+ nd_byte reserved;
+ nd_uint8_t type;
+ nd_uint16_t holdtime;
+ nd_uint16_t cksum;
+};
+
+static void
+esis_print(netdissect_options *ndo,
+ const uint8_t *pptr, u_int length)
+{
+ const uint8_t *optr;
+ u_int li, version, esis_pdu_type, source_address_length, source_address_number;
+ const struct esis_header_t *esis_header;
+
+ ndo->ndo_protocol = "esis";
+ if (!ndo->ndo_eflag)
+ ND_PRINT("ES-IS");
+
+ if (length <= 2) {
+ ND_PRINT(ndo->ndo_qflag ? "bad pkt!" : "no header at all!");
+ return;
+ }
+
+ esis_header = (const struct esis_header_t *) pptr;
+ ND_TCHECK_SIZE(esis_header);
+ li = GET_U_1(esis_header->length_indicator);
+ optr = pptr;
+
+ /*
+ * Sanity checking of the header.
+ */
+
+ if (GET_U_1(esis_header->nlpid) != NLPID_ESIS) {
+ ND_PRINT(" nlpid 0x%02x packet not supported",
+ GET_U_1(esis_header->nlpid));
+ return;
+ }
+
+ version = GET_U_1(esis_header->version);
+ if (version != ESIS_VERSION) {
+ ND_PRINT(" version %u packet not supported", version);
+ return;
+ }
+
+ if (li > length) {
+ ND_PRINT(" length indicator(%u) > PDU size (%u)!", li, length);
+ return;
+ }
+
+ if (li < sizeof(struct esis_header_t) + 2) {
+ ND_PRINT(" length indicator %u < min PDU size:", li);
+ while (pptr < ndo->ndo_snapend) {
+ ND_PRINT("%02X", GET_U_1(pptr));
+ pptr++;
+ }
+ return;
+ }
+
+ esis_pdu_type = GET_U_1(esis_header->type) & ESIS_PDU_TYPE_MASK;
+
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("%s%s, length %u",
+ ndo->ndo_eflag ? "" : ", ",
+ tok2str(esis_pdu_values,"unknown type (%u)",esis_pdu_type),
+ length);
+ return;
+ } else
+ ND_PRINT("%slength %u\n\t%s (%u)",
+ ndo->ndo_eflag ? "" : ", ",
+ length,
+ tok2str(esis_pdu_values,"unknown type: %u", esis_pdu_type),
+ esis_pdu_type);
+
+ ND_PRINT(", v: %u%s", version, version == ESIS_VERSION ? "" : "unsupported" );
+ ND_PRINT(", checksum: 0x%04x", GET_BE_U_2(esis_header->cksum));
+
+ osi_print_cksum(ndo, pptr, GET_BE_U_2(esis_header->cksum), 7,
+ li);
+
+ ND_PRINT(", holding time: %us, length indicator: %u",
+ GET_BE_U_2(esis_header->holdtime), li);
+
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, optr, "\n\t", sizeof(struct esis_header_t));
+
+ pptr += sizeof(struct esis_header_t);
+ li -= sizeof(struct esis_header_t);
+
+ switch (esis_pdu_type) {
+ case ESIS_PDU_REDIRECT: {
+ const uint8_t *dst, *snpa, *neta;
+ u_int dstl, snpal, netal;
+
+ ND_TCHECK_1(pptr);
+ if (li < 1) {
+ ND_PRINT(", bad redirect/li");
+ return;
+ }
+ dstl = GET_U_1(pptr);
+ pptr++;
+ li--;
+ ND_TCHECK_LEN(pptr, dstl);
+ if (li < dstl) {
+ ND_PRINT(", bad redirect/li");
+ return;
+ }
+ dst = pptr;
+ pptr += dstl;
+ li -= dstl;
+ ND_PRINT("\n\t %s", GET_ISONSAP_STRING(dst, dstl));
+
+ ND_TCHECK_1(pptr);
+ if (li < 1) {
+ ND_PRINT(", bad redirect/li");
+ return;
+ }
+ snpal = GET_U_1(pptr);
+ pptr++;
+ li--;
+ ND_TCHECK_LEN(pptr, snpal);
+ if (li < snpal) {
+ ND_PRINT(", bad redirect/li");
+ return;
+ }
+ snpa = pptr;
+ pptr += snpal;
+ li -= snpal;
+ ND_TCHECK_1(pptr);
+ if (li < 1) {
+ ND_PRINT(", bad redirect/li");
+ return;
+ }
+ netal = GET_U_1(pptr);
+ pptr++;
+ ND_TCHECK_LEN(pptr, netal);
+ if (li < netal) {
+ ND_PRINT(", bad redirect/li");
+ return;
+ }
+ neta = pptr;
+ pptr += netal;
+ li -= netal;
+
+ if (snpal == MAC_ADDR_LEN)
+ ND_PRINT("\n\t SNPA (length: %u): %s",
+ snpal,
+ GET_ETHERADDR_STRING(snpa));
+ else
+ ND_PRINT("\n\t SNPA (length: %u): %s",
+ snpal,
+ GET_LINKADDR_STRING(snpa, LINKADDR_OTHER, snpal));
+ if (netal != 0)
+ ND_PRINT("\n\t NET (length: %u) %s",
+ netal,
+ GET_ISONSAP_STRING(neta, netal));
+ break;
+ }
+
+ case ESIS_PDU_ESH:
+ ND_TCHECK_1(pptr);
+ if (li < 1) {
+ ND_PRINT(", bad esh/li");
+ return;
+ }
+ source_address_number = GET_U_1(pptr);
+ pptr++;
+ li--;
+
+ ND_PRINT("\n\t Number of Source Addresses: %u", source_address_number);
+
+ while (source_address_number > 0) {
+ ND_TCHECK_1(pptr);
+ if (li < 1) {
+ ND_PRINT(", bad esh/li");
+ return;
+ }
+ source_address_length = GET_U_1(pptr);
+ pptr++;
+ li--;
+
+ ND_TCHECK_LEN(pptr, source_address_length);
+ if (li < source_address_length) {
+ ND_PRINT(", bad esh/li");
+ return;
+ }
+ ND_PRINT("\n\t NET (length: %u): %s",
+ source_address_length,
+ GET_ISONSAP_STRING(pptr, source_address_length));
+ pptr += source_address_length;
+ li -= source_address_length;
+ source_address_number--;
+ }
+
+ break;
+
+ case ESIS_PDU_ISH: {
+ ND_TCHECK_1(pptr);
+ if (li < 1) {
+ ND_PRINT(", bad ish/li");
+ return;
+ }
+ source_address_length = GET_U_1(pptr);
+ pptr++;
+ li--;
+ ND_TCHECK_LEN(pptr, source_address_length);
+ if (li < source_address_length) {
+ ND_PRINT(", bad ish/li");
+ return;
+ }
+ ND_PRINT("\n\t NET (length: %u): %s", source_address_length, GET_ISONSAP_STRING(pptr, source_address_length));
+ pptr += source_address_length;
+ li -= source_address_length;
+ break;
+ }
+
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ /*
+ * If there's at least one byte to print, print
+ * it/them.
+ */
+ if (ND_TTEST_LEN(pptr, 1))
+ print_unknown_data(ndo, pptr, "\n\t ", ND_BYTES_AVAILABLE_AFTER(pptr));
+ }
+ return;
+ }
+
+ /* now walk the options */
+ while (li != 0) {
+ u_int op, opli;
+ const uint8_t *tptr;
+
+ if (li < 2) {
+ ND_PRINT(", bad opts/li");
+ return;
+ }
+ op = GET_U_1(pptr);
+ opli = GET_U_1(pptr + 1);
+ pptr += 2;
+ li -= 2;
+ if (opli > li) {
+ ND_PRINT(", opt (%u) too long", op);
+ return;
+ }
+ li -= opli;
+ tptr = pptr;
+
+ ND_PRINT("\n\t %s Option #%u, length %u, value: ",
+ tok2str(esis_option_values,"Unknown",op),
+ op,
+ opli);
+
+ switch (op) {
+
+ case ESIS_OPTION_ES_CONF_TIME:
+ if (opli == 2) {
+ ND_TCHECK_2(pptr);
+ ND_PRINT("%us", GET_BE_U_2(tptr));
+ } else
+ ND_PRINT("(bad length)");
+ break;
+
+ case ESIS_OPTION_PROTOCOLS:
+ while (opli>0) {
+ ND_PRINT("%s (0x%02x)",
+ tok2str(nlpid_values,
+ "unknown",
+ GET_U_1(tptr)),
+ GET_U_1(tptr));
+ if (opli>1) /* further NPLIDs ? - put comma */
+ ND_PRINT(", ");
+ tptr++;
+ opli--;
+ }
+ break;
+
+ /*
+ * FIXME those are the defined Options that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case ESIS_OPTION_QOS_MAINTENANCE:
+ case ESIS_OPTION_SECURITY:
+ case ESIS_OPTION_PRIORITY:
+ case ESIS_OPTION_ADDRESS_MASK:
+ case ESIS_OPTION_SNPA_MASK:
+
+ default:
+ print_unknown_data(ndo, tptr, "\n\t ", opli);
+ break;
+ }
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, pptr, "\n\t ", opli);
+ pptr += opli;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+isis_print_mcid(netdissect_options *ndo,
+ const struct isis_spb_mcid *mcid)
+{
+ int i;
+
+ ND_TCHECK_SIZE(mcid);
+ ND_PRINT("ID: %u, Name: ", GET_U_1(mcid->format_id));
+
+ nd_printjnp(ndo, mcid->name, sizeof(mcid->name));
+
+ ND_PRINT("\n\t Lvl: %u", GET_BE_U_2(mcid->revision_lvl));
+
+ ND_PRINT(", Digest: ");
+
+ for(i=0;i<16;i++)
+ ND_PRINT("%.2x ", mcid->digest[i]);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static int
+isis_print_mt_port_cap_subtlv(netdissect_options *ndo,
+ const uint8_t *tptr, u_int len)
+{
+ u_int stlv_type, stlv_len;
+ const struct isis_subtlv_spb_mcid *subtlv_spb_mcid;
+ int i;
+
+ while (len > 2)
+ {
+ stlv_type = GET_U_1(tptr);
+ stlv_len = GET_U_1(tptr + 1);
+
+ /* first lets see if we know the subTLVs name*/
+ ND_PRINT("\n\t %s subTLV #%u, length: %u",
+ tok2str(isis_mt_port_cap_subtlv_values, "unknown", stlv_type),
+ stlv_type,
+ stlv_len);
+
+ tptr += 2;
+ /*len -= TLV_TYPE_LEN_OFFSET;*/
+ len -= 2;
+
+ /* Make sure the subTLV fits within the space left */
+ if (len < stlv_len)
+ goto subtlv_too_long;
+ /* Make sure the entire subTLV is in the captured data */
+ ND_TCHECK_LEN(tptr, stlv_len);
+
+ switch (stlv_type)
+ {
+ case ISIS_SUBTLV_SPB_MCID:
+ {
+ if (stlv_len < ISIS_SUBTLV_SPB_MCID_MIN_LEN)
+ goto subtlv_too_short;
+
+ subtlv_spb_mcid = (const struct isis_subtlv_spb_mcid *)tptr;
+
+ ND_PRINT("\n\t MCID: ");
+ isis_print_mcid(ndo, &(subtlv_spb_mcid->mcid));
+
+ /*tptr += SPB_MCID_MIN_LEN;
+ len -= SPB_MCID_MIN_LEN; */
+
+ ND_PRINT("\n\t AUX-MCID: ");
+ isis_print_mcid(ndo, &(subtlv_spb_mcid->aux_mcid));
+
+ /*tptr += SPB_MCID_MIN_LEN;
+ len -= SPB_MCID_MIN_LEN; */
+ tptr += ISIS_SUBTLV_SPB_MCID_MIN_LEN;
+ len -= ISIS_SUBTLV_SPB_MCID_MIN_LEN;
+ stlv_len -= ISIS_SUBTLV_SPB_MCID_MIN_LEN;
+
+ break;
+ }
+
+ case ISIS_SUBTLV_SPB_DIGEST:
+ {
+ if (stlv_len < ISIS_SUBTLV_SPB_DIGEST_MIN_LEN)
+ goto subtlv_too_short;
+
+ ND_PRINT("\n\t RES: %u V: %u A: %u D: %u",
+ (GET_U_1(tptr) >> 5),
+ ((GET_U_1(tptr) >> 4) & 0x01),
+ ((GET_U_1(tptr) >> 2) & 0x03),
+ (GET_U_1(tptr) & 0x03));
+
+ tptr++;
+
+ ND_PRINT("\n\t Digest: ");
+
+ for(i=1;i<=8; i++)
+ {
+ ND_PRINT("%08x ", GET_BE_U_4(tptr));
+ if (i%4 == 0 && i != 8)
+ ND_PRINT("\n\t ");
+ tptr += 4;
+ }
+
+ len -= ISIS_SUBTLV_SPB_DIGEST_MIN_LEN;
+ stlv_len -= ISIS_SUBTLV_SPB_DIGEST_MIN_LEN;
+
+ break;
+ }
+
+ case ISIS_SUBTLV_SPB_BVID:
+ {
+ while (stlv_len != 0)
+ {
+ if (stlv_len < 4)
+ goto subtlv_too_short;
+ ND_PRINT("\n\t ECT: %08x",
+ GET_BE_U_4(tptr));
+
+ tptr += 4;
+ len -= 4;
+ stlv_len -= 4;
+
+ if (stlv_len < 2)
+ goto subtlv_too_short;
+ ND_PRINT(" BVID: %u, U:%01x M:%01x ",
+ (GET_BE_U_2(tptr) >> 4) ,
+ (GET_BE_U_2(tptr) >> 3) & 0x01,
+ (GET_BE_U_2(tptr) >> 2) & 0x01);
+
+ tptr += 2;
+ len -= 2;
+ stlv_len -= 2;
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ tptr += stlv_len;
+ len -= stlv_len;
+ }
+ return (0);
+
+trunc:
+ nd_print_trunc(ndo);
+ return (1);
+
+subtlv_too_long:
+ ND_PRINT(" (> containing TLV length)");
+ return (1);
+
+subtlv_too_short:
+ ND_PRINT(" (too short)");
+ return (1);
+}
+
+static int
+isis_print_mt_capability_subtlv(netdissect_options *ndo,
+ const uint8_t *tptr, u_int len)
+{
+ u_int stlv_type, stlv_len, treecount;
+
+ while (len > 2)
+ {
+ stlv_type = GET_U_1(tptr);
+ stlv_len = GET_U_1(tptr + 1);
+ tptr += 2;
+ len -= 2;
+
+ /* first lets see if we know the subTLVs name*/
+ ND_PRINT("\n\t %s subTLV #%u, length: %u",
+ tok2str(isis_mt_capability_subtlv_values, "unknown", stlv_type),
+ stlv_type,
+ stlv_len);
+
+ /* Make sure the subTLV fits within the space left */
+ if (len < stlv_len)
+ goto subtlv_too_long;
+ /* Make sure the entire subTLV is in the captured data */
+ ND_TCHECK_LEN(tptr, stlv_len);
+
+ switch (stlv_type)
+ {
+ case ISIS_SUBTLV_SPB_INSTANCE:
+ if (stlv_len < ISIS_SUBTLV_SPB_INSTANCE_MIN_LEN)
+ goto subtlv_too_short;
+
+ ND_PRINT("\n\t CIST Root-ID: %08x", GET_BE_U_4(tptr));
+ tptr += 4;
+ ND_PRINT(" %08x", GET_BE_U_4(tptr));
+ tptr += 4;
+ ND_PRINT(", Path Cost: %08x", GET_BE_U_4(tptr));
+ tptr += 4;
+ ND_PRINT(", Prio: %u", GET_BE_U_2(tptr));
+ tptr += 2;
+ ND_PRINT("\n\t RES: %u",
+ GET_BE_U_2(tptr) >> 5);
+ ND_PRINT(", V: %u",
+ (GET_BE_U_2(tptr) >> 4) & 0x0001);
+ ND_PRINT(", SPSource-ID: %u",
+ (GET_BE_U_4(tptr) & 0x000fffff));
+ tptr += 4;
+ ND_PRINT(", No of Trees: %x", GET_U_1(tptr));
+
+ treecount = GET_U_1(tptr);
+ tptr++;
+
+ len -= ISIS_SUBTLV_SPB_INSTANCE_MIN_LEN;
+ stlv_len -= ISIS_SUBTLV_SPB_INSTANCE_MIN_LEN;
+
+ while (treecount)
+ {
+ if (stlv_len < ISIS_SUBTLV_SPB_INSTANCE_VLAN_TUPLE_LEN)
+ goto trunc;
+
+ ND_PRINT("\n\t U:%u, M:%u, A:%u, RES:%u",
+ GET_U_1(tptr) >> 7,
+ (GET_U_1(tptr) >> 6) & 0x01,
+ (GET_U_1(tptr) >> 5) & 0x01,
+ (GET_U_1(tptr) & 0x1f));
+
+ tptr++;
+
+ ND_PRINT(", ECT: %08x", GET_BE_U_4(tptr));
+
+ tptr += 4;
+
+ ND_PRINT(", BVID: %u, SPVID: %u",
+ (GET_BE_U_3(tptr) >> 12) & 0x000fff,
+ GET_BE_U_3(tptr) & 0x000fff);
+
+ tptr += 3;
+ len -= ISIS_SUBTLV_SPB_INSTANCE_VLAN_TUPLE_LEN;
+ stlv_len -= ISIS_SUBTLV_SPB_INSTANCE_VLAN_TUPLE_LEN;
+ treecount--;
+ }
+
+ break;
+
+ case ISIS_SUBTLV_SPBM_SI:
+ if (stlv_len < 8)
+ goto trunc;
+
+ ND_PRINT("\n\t BMAC: %08x", GET_BE_U_4(tptr));
+ tptr += 4;
+ ND_PRINT("%04x", GET_BE_U_2(tptr));
+ tptr += 2;
+
+ ND_PRINT(", RES: %u, VID: %u", GET_BE_U_2(tptr) >> 12,
+ (GET_BE_U_2(tptr)) & 0x0fff);
+
+ tptr += 2;
+ len -= 8;
+ stlv_len -= 8;
+
+ while (stlv_len >= 4) {
+ ND_PRINT("\n\t T: %u, R: %u, RES: %u, ISID: %u",
+ (GET_BE_U_4(tptr) >> 31),
+ (GET_BE_U_4(tptr) >> 30) & 0x01,
+ (GET_BE_U_4(tptr) >> 24) & 0x03f,
+ (GET_BE_U_4(tptr)) & 0x0ffffff);
+
+ tptr += 4;
+ len -= 4;
+ stlv_len -= 4;
+ }
+
+ break;
+
+ default:
+ break;
+ }
+ tptr += stlv_len;
+ len -= stlv_len;
+ }
+ return (0);
+
+trunc:
+ nd_print_trunc(ndo);
+ return (1);
+
+subtlv_too_long:
+ ND_PRINT(" (> containing TLV length)");
+ return (1);
+
+subtlv_too_short:
+ ND_PRINT(" (too short)");
+ return (1);
+}
+
+/* shared routine for printing system, node and lsp-ids */
+static char *
+isis_print_id(netdissect_options *ndo, const uint8_t *cp, u_int id_len)
+{
+ u_int i;
+ static char id[sizeof("xxxx.xxxx.xxxx.yy-zz")];
+ char *pos = id;
+ u_int sysid_len;
+
+ sysid_len = SYSTEM_ID_LEN;
+ if (sysid_len > id_len)
+ sysid_len = id_len;
+ for (i = 1; i <= sysid_len; i++) {
+ snprintf(pos, sizeof(id) - (pos - id), "%02x", GET_U_1(cp));
+ cp++;
+ pos += strlen(pos);
+ if (i == 2 || i == 4)
+ *pos++ = '.';
+ }
+ if (id_len >= NODE_ID_LEN) {
+ snprintf(pos, sizeof(id) - (pos - id), ".%02x", GET_U_1(cp));
+ cp++;
+ pos += strlen(pos);
+ }
+ if (id_len == LSP_ID_LEN)
+ snprintf(pos, sizeof(id) - (pos - id), "-%02x", GET_U_1(cp));
+ return (id);
+}
+
+/* print the 4-byte metric block which is common found in the old-style TLVs */
+static int
+isis_print_metric_block(netdissect_options *ndo,
+ const struct isis_metric_block *isis_metric_block)
+{
+ ND_PRINT(", Default Metric: %u, %s",
+ ISIS_LSP_TLV_METRIC_VALUE(isis_metric_block->metric_default),
+ ISIS_LSP_TLV_METRIC_IE(isis_metric_block->metric_default) ? "External" : "Internal");
+ if (!ISIS_LSP_TLV_METRIC_SUPPORTED(isis_metric_block->metric_delay))
+ ND_PRINT("\n\t\t Delay Metric: %u, %s",
+ ISIS_LSP_TLV_METRIC_VALUE(isis_metric_block->metric_delay),
+ ISIS_LSP_TLV_METRIC_IE(isis_metric_block->metric_delay) ? "External" : "Internal");
+ if (!ISIS_LSP_TLV_METRIC_SUPPORTED(isis_metric_block->metric_expense))
+ ND_PRINT("\n\t\t Expense Metric: %u, %s",
+ ISIS_LSP_TLV_METRIC_VALUE(isis_metric_block->metric_expense),
+ ISIS_LSP_TLV_METRIC_IE(isis_metric_block->metric_expense) ? "External" : "Internal");
+ if (!ISIS_LSP_TLV_METRIC_SUPPORTED(isis_metric_block->metric_error))
+ ND_PRINT("\n\t\t Error Metric: %u, %s",
+ ISIS_LSP_TLV_METRIC_VALUE(isis_metric_block->metric_error),
+ ISIS_LSP_TLV_METRIC_IE(isis_metric_block->metric_error) ? "External" : "Internal");
+
+ return(1); /* everything is ok */
+}
+
+static int
+isis_print_tlv_ip_reach(netdissect_options *ndo,
+ const uint8_t *cp, const char *ident, u_int length)
+{
+ int prefix_len;
+ const struct isis_tlv_ip_reach *tlv_ip_reach;
+
+ tlv_ip_reach = (const struct isis_tlv_ip_reach *)cp;
+
+ while (length > 0) {
+ if ((size_t)length < sizeof(*tlv_ip_reach)) {
+ ND_PRINT("short IPv4 Reachability (%u vs %zu)",
+ length,
+ sizeof(*tlv_ip_reach));
+ return (0);
+ }
+
+ ND_TCHECK_SIZE(tlv_ip_reach);
+
+ prefix_len = mask2plen(GET_IPV4_TO_HOST_ORDER(tlv_ip_reach->mask));
+
+ if (prefix_len == -1)
+ ND_PRINT("%sIPv4 prefix: %s mask %s",
+ ident,
+ GET_IPADDR_STRING(tlv_ip_reach->prefix),
+ GET_IPADDR_STRING(tlv_ip_reach->mask));
+ else
+ ND_PRINT("%sIPv4 prefix: %15s/%u",
+ ident,
+ GET_IPADDR_STRING(tlv_ip_reach->prefix),
+ prefix_len);
+
+ ND_PRINT(", Distribution: %s, Metric: %u, %s",
+ ISIS_LSP_TLV_METRIC_UPDOWN(tlv_ip_reach->isis_metric_block.metric_default) ? "down" : "up",
+ ISIS_LSP_TLV_METRIC_VALUE(tlv_ip_reach->isis_metric_block.metric_default),
+ ISIS_LSP_TLV_METRIC_IE(tlv_ip_reach->isis_metric_block.metric_default) ? "External" : "Internal");
+
+ if (!ISIS_LSP_TLV_METRIC_SUPPORTED(tlv_ip_reach->isis_metric_block.metric_delay))
+ ND_PRINT("%s Delay Metric: %u, %s",
+ ident,
+ ISIS_LSP_TLV_METRIC_VALUE(tlv_ip_reach->isis_metric_block.metric_delay),
+ ISIS_LSP_TLV_METRIC_IE(tlv_ip_reach->isis_metric_block.metric_delay) ? "External" : "Internal");
+
+ if (!ISIS_LSP_TLV_METRIC_SUPPORTED(tlv_ip_reach->isis_metric_block.metric_expense))
+ ND_PRINT("%s Expense Metric: %u, %s",
+ ident,
+ ISIS_LSP_TLV_METRIC_VALUE(tlv_ip_reach->isis_metric_block.metric_expense),
+ ISIS_LSP_TLV_METRIC_IE(tlv_ip_reach->isis_metric_block.metric_expense) ? "External" : "Internal");
+
+ if (!ISIS_LSP_TLV_METRIC_SUPPORTED(tlv_ip_reach->isis_metric_block.metric_error))
+ ND_PRINT("%s Error Metric: %u, %s",
+ ident,
+ ISIS_LSP_TLV_METRIC_VALUE(tlv_ip_reach->isis_metric_block.metric_error),
+ ISIS_LSP_TLV_METRIC_IE(tlv_ip_reach->isis_metric_block.metric_error) ? "External" : "Internal");
+
+ length -= sizeof(struct isis_tlv_ip_reach);
+ tlv_ip_reach++;
+ }
+ return (1);
+trunc:
+ return 0;
+}
+
+/*
+ * this is the common IP-REACH subTLV decoder it is called
+ * from various EXTD-IP REACH TLVs (135,235,236,237)
+ */
+
+static int
+isis_print_ip_reach_subtlv(netdissect_options *ndo,
+ const uint8_t *tptr, u_int subt, u_int subl,
+ const char *ident)
+{
+ /* first lets see if we know the subTLVs name*/
+ ND_PRINT("%s%s subTLV #%u, length: %u",
+ ident, tok2str(isis_ext_ip_reach_subtlv_values, "unknown", subt),
+ subt, subl);
+
+ ND_TCHECK_LEN(tptr, subl);
+
+ switch(subt) {
+ case ISIS_SUBTLV_EXTD_IP_REACH_MGMT_PREFIX_COLOR: /* fall through */
+ case ISIS_SUBTLV_EXTD_IP_REACH_ADMIN_TAG32:
+ while (subl >= 4) {
+ ND_PRINT(", 0x%08x (=%u)",
+ GET_BE_U_4(tptr),
+ GET_BE_U_4(tptr));
+ tptr+=4;
+ subl-=4;
+ }
+ break;
+ case ISIS_SUBTLV_EXTD_IP_REACH_ADMIN_TAG64:
+ while (subl >= 8) {
+ ND_PRINT(", 0x%08x%08x",
+ GET_BE_U_4(tptr),
+ GET_BE_U_4(tptr + 4));
+ tptr+=8;
+ subl-=8;
+ }
+ break;
+ case ISIS_SUBTLV_EXTD_IP_REACH_PREFIX_SID:
+ {
+ uint8_t algo, flags;
+ uint32_t sid;
+
+ flags = GET_U_1(tptr);
+ algo = GET_U_1(tptr+1);
+
+ if (flags & ISIS_PREFIX_SID_FLAG_V) {
+ if (subl < 5)
+ goto trunc;
+ sid = GET_BE_U_3(tptr+2);
+ tptr+=5;
+ subl-=5;
+ } else {
+ if (subl < 6)
+ goto trunc;
+ sid = GET_BE_U_4(tptr+2);
+ tptr+=6;
+ subl-=6;
+ }
+
+ ND_PRINT(", Flags [%s], Algo %s (%u), %s %u",
+ bittok2str(prefix_sid_flag_values, "None", flags),
+ tok2str(prefix_sid_algo_values, "Unknown", algo), algo,
+ flags & ISIS_PREFIX_SID_FLAG_V ? "label" : "index",
+ sid);
+ }
+ break;
+ default:
+ if (!print_unknown_data(ndo, tptr, "\n\t\t ", subl))
+ return(0);
+ break;
+ }
+ return(1);
+
+trunc:
+ nd_print_trunc(ndo);
+ return(0);
+}
+
+/*
+ * this is the common IS-REACH decoder it is called
+ * from various EXTD-IS REACH style TLVs (22,24,222)
+ */
+
+static int
+isis_print_ext_is_reach(netdissect_options *ndo,
+ const uint8_t *tptr, const char *ident, u_int tlv_type,
+ u_int tlv_remaining)
+{
+ char ident_buffer[20];
+ u_int subtlv_type,subtlv_len,subtlv_sum_len;
+ int proc_bytes = 0; /* how many bytes did we process ? */
+ u_int te_class,priority_level,gmpls_switch_cap;
+ union { /* int to float conversion buffer for several subTLVs */
+ float f;
+ uint32_t i;
+ } bw;
+
+ ND_TCHECK_LEN(tptr, NODE_ID_LEN);
+ if (tlv_remaining < NODE_ID_LEN)
+ return(0);
+
+ ND_PRINT("%sIS Neighbor: %s", ident, isis_print_id(ndo, tptr, NODE_ID_LEN));
+ tptr+=NODE_ID_LEN;
+ tlv_remaining-=NODE_ID_LEN;
+ proc_bytes+=NODE_ID_LEN;
+
+ if (tlv_type != ISIS_TLV_IS_ALIAS_ID) { /* the Alias TLV Metric field is implicit 0 */
+ ND_TCHECK_3(tptr);
+ if (tlv_remaining < 3)
+ return(0);
+ ND_PRINT(", Metric: %u", GET_BE_U_3(tptr));
+ tptr+=3;
+ tlv_remaining-=3;
+ proc_bytes+=3;
+ }
+
+ ND_TCHECK_1(tptr);
+ if (tlv_remaining < 1)
+ return(0);
+ subtlv_sum_len=GET_U_1(tptr); /* read out subTLV length */
+ tptr++;
+ tlv_remaining--;
+ proc_bytes++;
+ ND_PRINT(", %ssub-TLVs present",subtlv_sum_len ? "" : "no ");
+ if (subtlv_sum_len) {
+ ND_PRINT(" (%u)", subtlv_sum_len);
+ /* prepend the indent string */
+ snprintf(ident_buffer, sizeof(ident_buffer), "%s ",ident);
+ ident = ident_buffer;
+ while (subtlv_sum_len != 0) {
+ ND_TCHECK_2(tptr);
+ if (tlv_remaining < 2) {
+ ND_PRINT("%sRemaining data in TLV shorter than a subTLV header",ident);
+ proc_bytes += tlv_remaining;
+ break;
+ }
+ if (subtlv_sum_len < 2) {
+ ND_PRINT("%sRemaining data in subTLVs shorter than a subTLV header",ident);
+ proc_bytes += subtlv_sum_len;
+ break;
+ }
+ subtlv_type=GET_U_1(tptr);
+ subtlv_len=GET_U_1(tptr + 1);
+ tptr += 2;
+ tlv_remaining -= 2;
+ subtlv_sum_len -= 2;
+ proc_bytes += 2;
+ ND_PRINT("%s%s subTLV #%u, length: %u",
+ ident, tok2str(isis_ext_is_reach_subtlv_values, "unknown", subtlv_type),
+ subtlv_type, subtlv_len);
+
+ if (subtlv_sum_len < subtlv_len) {
+ ND_PRINT(" (remaining data in subTLVs shorter than the current subTLV)");
+ proc_bytes += subtlv_sum_len;
+ break;
+ }
+
+ if (tlv_remaining < subtlv_len) {
+ ND_PRINT(" (> remaining tlv length)");
+ proc_bytes += tlv_remaining;
+ break;
+ }
+
+ ND_TCHECK_LEN(tptr, subtlv_len);
+
+ switch(subtlv_type) {
+ case ISIS_SUBTLV_EXT_IS_REACH_ADMIN_GROUP:
+ case ISIS_SUBTLV_EXT_IS_REACH_LINK_LOCAL_REMOTE_ID:
+ case ISIS_SUBTLV_EXT_IS_REACH_LINK_REMOTE_ID:
+ if (subtlv_len >= 4) {
+ ND_PRINT(", 0x%08x", GET_BE_U_4(tptr));
+ if (subtlv_len == 8) /* rfc4205 */
+ ND_PRINT(", 0x%08x", GET_BE_U_4(tptr + 4));
+ }
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_IPV4_INTF_ADDR:
+ case ISIS_SUBTLV_EXT_IS_REACH_IPV4_NEIGHBOR_ADDR:
+ if (subtlv_len >= sizeof(nd_ipv4))
+ ND_PRINT(", %s", GET_IPADDR_STRING(tptr));
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_MAX_LINK_BW :
+ case ISIS_SUBTLV_EXT_IS_REACH_RESERVABLE_BW:
+ if (subtlv_len >= 4) {
+ bw.i = GET_BE_U_4(tptr);
+ ND_PRINT(", %.3f Mbps", bw.f * 8 / 1000000);
+ }
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_UNRESERVED_BW :
+ if (subtlv_len >= 32) {
+ for (te_class = 0; te_class < 8; te_class++) {
+ bw.i = GET_BE_U_4(tptr);
+ ND_PRINT("%s TE-Class %u: %.3f Mbps",
+ ident,
+ te_class,
+ bw.f * 8 / 1000000);
+ tptr += 4;
+ subtlv_len -= 4;
+ subtlv_sum_len -= 4;
+ proc_bytes += 4;
+ }
+ }
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS: /* fall through */
+ case ISIS_SUBTLV_EXT_IS_REACH_BW_CONSTRAINTS_OLD:
+ if (subtlv_len == 0)
+ break;
+ ND_PRINT("%sBandwidth Constraints Model ID: %s (%u)",
+ ident,
+ tok2str(diffserv_te_bc_values, "unknown", GET_U_1(tptr)),
+ GET_U_1(tptr));
+ tptr++;
+ subtlv_len--;
+ subtlv_sum_len--;
+ proc_bytes++;
+ /* decode BCs until the subTLV ends */
+ for (te_class = 0; subtlv_len != 0; te_class++) {
+ if (subtlv_len < 4)
+ break;
+ bw.i = GET_BE_U_4(tptr);
+ ND_PRINT("%s Bandwidth constraint CT%u: %.3f Mbps",
+ ident,
+ te_class,
+ bw.f * 8 / 1000000);
+ tptr += 4;
+ subtlv_len -= 4;
+ subtlv_sum_len -= 4;
+ proc_bytes += 4;
+ }
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_TE_METRIC:
+ if (subtlv_len >= 3)
+ ND_PRINT(", %u", GET_BE_U_3(tptr));
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_LINK_ATTRIBUTE:
+ if (subtlv_len == 2) {
+ ND_PRINT(", [ %s ] (0x%04x)",
+ bittok2str(isis_subtlv_link_attribute_values,
+ "Unknown",
+ GET_BE_U_2(tptr)),
+ GET_BE_U_2(tptr));
+ }
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_LINK_PROTECTION_TYPE:
+ if (subtlv_len >= 2) {
+ ND_PRINT(", %s, Priority %u",
+ bittok2str(gmpls_link_prot_values, "none", GET_U_1(tptr)),
+ GET_U_1(tptr + 1));
+ }
+ break;
+ case ISIS_SUBTLV_SPB_METRIC:
+ if (subtlv_len >= 6) {
+ ND_PRINT(", LM: %u", GET_BE_U_3(tptr));
+ tptr += 3;
+ subtlv_len -= 3;
+ subtlv_sum_len -= 3;
+ proc_bytes += 3;
+ ND_PRINT(", P: %u", GET_U_1(tptr));
+ tptr++;
+ subtlv_len--;
+ subtlv_sum_len--;
+ proc_bytes++;
+ ND_PRINT(", P-ID: %u", GET_BE_U_2(tptr));
+ }
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_INTF_SW_CAP_DESCR:
+ if (subtlv_len >= 36) {
+ gmpls_switch_cap = GET_U_1(tptr);
+ ND_PRINT("%s Interface Switching Capability:%s",
+ ident,
+ tok2str(gmpls_switch_cap_values, "Unknown", gmpls_switch_cap));
+ ND_PRINT(", LSP Encoding: %s",
+ tok2str(gmpls_encoding_values, "Unknown", GET_U_1((tptr + 1))));
+ tptr += 4;
+ subtlv_len -= 4;
+ subtlv_sum_len -= 4;
+ proc_bytes += 4;
+ ND_PRINT("%s Max LSP Bandwidth:", ident);
+ for (priority_level = 0; priority_level < 8; priority_level++) {
+ bw.i = GET_BE_U_4(tptr);
+ ND_PRINT("%s priority level %u: %.3f Mbps",
+ ident,
+ priority_level,
+ bw.f * 8 / 1000000);
+ tptr += 4;
+ subtlv_len -= 4;
+ subtlv_sum_len -= 4;
+ proc_bytes += 4;
+ }
+ switch (gmpls_switch_cap) {
+ case GMPLS_PSC1:
+ case GMPLS_PSC2:
+ case GMPLS_PSC3:
+ case GMPLS_PSC4:
+ if (subtlv_len < 6)
+ break;
+ bw.i = GET_BE_U_4(tptr);
+ ND_PRINT("%s Min LSP Bandwidth: %.3f Mbps", ident, bw.f * 8 / 1000000);
+ ND_PRINT("%s Interface MTU: %u", ident,
+ GET_BE_U_2(tptr + 4));
+ break;
+ case GMPLS_TSC:
+ if (subtlv_len < 8)
+ break;
+ bw.i = GET_BE_U_4(tptr);
+ ND_PRINT("%s Min LSP Bandwidth: %.3f Mbps", ident, bw.f * 8 / 1000000);
+ ND_PRINT("%s Indication %s", ident,
+ tok2str(gmpls_switch_cap_tsc_indication_values, "Unknown (%u)", GET_U_1((tptr + 4))));
+ break;
+ default:
+ /* there is some optional stuff left to decode but this is as of yet
+ not specified so just lets hexdump what is left */
+ if (subtlv_len != 0) {
+ if (!print_unknown_data(ndo, tptr, "\n\t\t ", subtlv_len))
+ return(0);
+ }
+ }
+ }
+ break;
+ case ISIS_SUBTLV_EXT_IS_REACH_LAN_ADJ_SEGMENT_ID:
+ if (subtlv_len >= 8) {
+ ND_PRINT("%s Flags: [%s]", ident,
+ bittok2str(isis_lan_adj_sid_flag_values,
+ "none",
+ GET_U_1(tptr)));
+ int vflag = (GET_U_1(tptr) & 0x20) ? 1:0;
+ int lflag = (GET_U_1(tptr) & 0x10) ? 1:0;
+ tptr++;
+ subtlv_len--;
+ subtlv_sum_len--;
+ proc_bytes++;
+ ND_PRINT("%s Weight: %u", ident, GET_U_1(tptr));
+ tptr++;
+ subtlv_len--;
+ subtlv_sum_len--;
+ proc_bytes++;
+ if(subtlv_len>=SYSTEM_ID_LEN) {
+ ND_TCHECK_LEN(tptr, SYSTEM_ID_LEN);
+ ND_PRINT("%s Neighbor System-ID: %s", ident,
+ isis_print_id(ndo, tptr, SYSTEM_ID_LEN));
+ }
+ /* RFC 8667 section 2.2.2 */
+ /* if V-flag is set to 1 and L-flag is set to 1 ==> 3 octet label */
+ /* if V-flag is set to 0 and L-flag is set to 0 ==> 4 octet index */
+ if (vflag && lflag) {
+ ND_PRINT("%s Label: %u",
+ ident, GET_BE_U_3(tptr+SYSTEM_ID_LEN));
+ } else if ((!vflag) && (!lflag)) {
+ ND_PRINT("%s Index: %u",
+ ident, GET_BE_U_4(tptr+SYSTEM_ID_LEN));
+ } else
+ nd_print_invalid(ndo);
+ }
+ break;
+ default:
+ if (!print_unknown_data(ndo, tptr, "\n\t\t ", subtlv_len))
+ return(0);
+ break;
+ }
+
+ tptr += subtlv_len;
+ tlv_remaining -= subtlv_len;
+ subtlv_sum_len -= subtlv_len;
+ proc_bytes += subtlv_len;
+ }
+ }
+ return(proc_bytes);
+
+trunc:
+ return(0);
+}
+
+/*
+ * this is the common Multi Topology ID decoder
+ * it is called from various MT-TLVs (222,229,235,237)
+ */
+
+static uint8_t
+isis_print_mtid(netdissect_options *ndo,
+ const uint8_t *tptr, const char *ident, u_int tlv_remaining)
+{
+ if (tlv_remaining < 2)
+ goto trunc;
+
+ ND_PRINT("%s%s",
+ ident,
+ tok2str(isis_mt_values,
+ "Reserved for IETF Consensus",
+ ISIS_MASK_MTID(GET_BE_U_2(tptr))));
+
+ ND_PRINT(" Topology (0x%03x), Flags: [%s]",
+ ISIS_MASK_MTID(GET_BE_U_2(tptr)),
+ bittok2str(isis_mt_flag_values, "none",ISIS_MASK_MTFLAGS(GET_BE_U_2(tptr))));
+
+ return(2);
+trunc:
+ return 0;
+}
+
+/*
+ * this is the common extended IP reach decoder
+ * it is called from TLVs (135,235,236,237)
+ * we process the TLV and optional subTLVs and return
+ * the amount of processed bytes
+ */
+
+static u_int
+isis_print_extd_ip_reach(netdissect_options *ndo,
+ const uint8_t *tptr, const char *ident, uint16_t afi)
+{
+ char ident_buffer[20];
+ uint8_t prefix[sizeof(nd_ipv6)]; /* shared copy buffer for IPv4 and IPv6 prefixes */
+ u_int metric, status_byte, bit_length, byte_length, sublen, processed, subtlvtype, subtlvlen;
+
+ metric = GET_BE_U_4(tptr);
+ processed=4;
+ tptr+=4;
+
+ if (afi == AF_INET) {
+ status_byte=GET_U_1(tptr);
+ tptr++;
+ bit_length = status_byte&0x3f;
+ if (bit_length > 32) {
+ ND_PRINT("%sIPv4 prefix: bad bit length %u",
+ ident,
+ bit_length);
+ return (0);
+ }
+ processed++;
+ } else if (afi == AF_INET6) {
+ status_byte=GET_U_1(tptr);
+ bit_length=GET_U_1(tptr + 1);
+ if (bit_length > 128) {
+ ND_PRINT("%sIPv6 prefix: bad bit length %u",
+ ident,
+ bit_length);
+ return (0);
+ }
+ tptr+=2;
+ processed+=2;
+ } else
+ return (0); /* somebody is fooling us */
+
+ byte_length = (bit_length + 7) / 8; /* prefix has variable length encoding */
+
+ ND_TCHECK_LEN(tptr, byte_length);
+ memset(prefix, 0, sizeof(prefix)); /* clear the copy buffer */
+ memcpy(prefix,tptr,byte_length); /* copy as much as is stored in the TLV */
+ tptr+=byte_length;
+ processed+=byte_length;
+
+ if (afi == AF_INET)
+ ND_PRINT("%sIPv4 prefix: %15s/%u",
+ ident,
+ ipaddr_string(ndo, prefix), /* local buffer, not packet data; don't use GET_IPADDR_STRING() */
+ bit_length);
+ else if (afi == AF_INET6)
+ ND_PRINT("%sIPv6 prefix: %s/%u",
+ ident,
+ ip6addr_string(ndo, prefix), /* local buffer, not packet data; don't use GET_IP6ADDR_STRING() */
+ bit_length);
+
+ ND_PRINT(", Distribution: %s, Metric: %u",
+ ISIS_MASK_TLV_EXTD_IP_UPDOWN(status_byte) ? "down" : "up",
+ metric);
+
+ if (afi == AF_INET && ISIS_MASK_TLV_EXTD_IP_SUBTLV(status_byte))
+ ND_PRINT(", sub-TLVs present");
+ else if (afi == AF_INET6)
+ ND_PRINT(", %s%s",
+ ISIS_MASK_TLV_EXTD_IP6_IE(status_byte) ? "External" : "Internal",
+ ISIS_MASK_TLV_EXTD_IP6_SUBTLV(status_byte) ? ", sub-TLVs present" : "");
+
+ if ((afi == AF_INET && ISIS_MASK_TLV_EXTD_IP_SUBTLV(status_byte))
+ || (afi == AF_INET6 && ISIS_MASK_TLV_EXTD_IP6_SUBTLV(status_byte))
+ ) {
+ /* assume that one prefix can hold more
+ than one subTLV - therefore the first byte must reflect
+ the aggregate bytecount of the subTLVs for this prefix
+ */
+ sublen=GET_U_1(tptr);
+ tptr++;
+ processed+=sublen+1;
+ ND_PRINT(" (%u)", sublen); /* print out subTLV length */
+
+ while (sublen>0) {
+ subtlvtype=GET_U_1(tptr);
+ subtlvlen=GET_U_1(tptr + 1);
+ tptr+=2;
+ /* prepend the indent string */
+ snprintf(ident_buffer, sizeof(ident_buffer), "%s ",ident);
+ if (!isis_print_ip_reach_subtlv(ndo, tptr, subtlvtype, subtlvlen, ident_buffer))
+ return(0);
+ tptr+=subtlvlen;
+ sublen-=(subtlvlen+2);
+ }
+ }
+ return (processed);
+trunc:
+ return 0;
+}
+
+static void
+isis_print_router_cap_subtlv(netdissect_options *ndo, const uint8_t *tptr, uint8_t tlen)
+{
+ uint8_t subt, subl;
+
+ while (tlen >= 2) {
+ subt = GET_U_1(tptr);
+ subl = GET_U_1(tptr+1);
+ tlen -= 2;
+ tptr += 2;
+
+ /* first lets see if we know the subTLVs name*/
+ ND_PRINT("\n\t\t%s subTLV #%u, length: %u",
+ tok2str(isis_router_capability_subtlv_values, "unknown", subt),
+ subt, subl);
+
+ /*
+ * Boundary check.
+ */
+ if (subl > tlen) {
+ break;
+ }
+ ND_TCHECK_LEN(tptr, subl);
+
+ switch (subt) {
+ case ISIS_SUBTLV_ROUTER_CAP_SR:
+ {
+ uint8_t flags, sid_tlen, sid_type, sid_len;
+ uint32_t range;
+ const uint8_t *sid_ptr;
+
+ flags = GET_U_1(tptr);
+ range = GET_BE_U_3(tptr+1);
+ ND_PRINT(", Flags [%s], Range %u",
+ bittok2str(isis_router_capability_sr_flags, "None", flags),
+ range);
+ sid_ptr = tptr + 4;
+ sid_tlen = subl - 4;
+
+ while (sid_tlen >= 5) {
+ sid_type = GET_U_1(sid_ptr);
+ sid_len = GET_U_1(sid_ptr+1);
+ sid_tlen -= 2;
+ sid_ptr += 2;
+
+ /*
+ * Boundary check.
+ */
+ if (sid_len > sid_tlen) {
+ break;
+ }
+
+ switch (sid_type) {
+ case 1:
+ if (sid_len == 3) {
+ ND_PRINT(", SID value %u", GET_BE_U_3(sid_ptr));
+ } else if (sid_len == 4) {
+ ND_PRINT(", SID value %u", GET_BE_U_4(sid_ptr));
+ } else {
+ ND_PRINT(", Unknown SID length%u", sid_len);
+ }
+ break;
+ default:
+ print_unknown_data(ndo, sid_ptr, "\n\t\t ", sid_len);
+ }
+
+ sid_ptr += sid_len;
+ sid_tlen -= sid_len;
+ }
+ }
+ break;
+ default:
+ print_unknown_data(ndo, tptr, "\n\t\t", subl);
+ break;
+ }
+
+ tlen -= subl;
+ tptr += subl;
+ }
+ trunc:
+ return;
+}
+
+/*
+ * Clear checksum and lifetime prior to signature verification.
+ */
+static void
+isis_clear_checksum_lifetime(void *header)
+{
+ struct isis_lsp_header *header_lsp = (struct isis_lsp_header *) header;
+
+ header_lsp->checksum[0] = 0;
+ header_lsp->checksum[1] = 0;
+ header_lsp->remaining_lifetime[0] = 0;
+ header_lsp->remaining_lifetime[1] = 0;
+}
+
+/*
+ * isis_print
+ * Decode IS-IS packets. Return 0 on error.
+ */
+
+#define INVALID_OR_DECREMENT(length,decr) \
+ if ((length) < (decr)) { \
+ ND_PRINT(" [packet length %u < %zu]", (length), (decr)); \
+ nd_print_invalid(ndo); \
+ return 1; \
+ } \
+ length -= (decr);
+
+static int
+isis_print(netdissect_options *ndo,
+ const uint8_t *p, u_int length)
+{
+ const struct isis_common_header *isis_header;
+
+ const struct isis_iih_lan_header *header_iih_lan;
+ const struct isis_iih_ptp_header *header_iih_ptp;
+ const struct isis_lsp_header *header_lsp;
+ const struct isis_csnp_header *header_csnp;
+ const struct isis_psnp_header *header_psnp;
+
+ const struct isis_tlv_lsp *tlv_lsp;
+ const struct isis_tlv_ptp_adj *tlv_ptp_adj;
+ const struct isis_tlv_is_reach *tlv_is_reach;
+ const struct isis_tlv_es_reach *tlv_es_reach;
+
+ uint8_t version, pdu_version, fixed_len;
+ uint8_t pdu_type, pdu_max_area, max_area, pdu_id_length, id_length, tlv_type, tlv_len, tlen, alen, prefix_len;
+ u_int ext_is_len, ext_ip_len;
+ uint8_t mt_len;
+ uint8_t isis_subtlv_idrp;
+ const uint8_t *optr, *pptr, *tptr;
+ u_int packet_len;
+ u_short pdu_len, key_id;
+ u_int i,vendor_id, num_vals;
+ uint8_t auth_type;
+ uint8_t num_system_ids;
+ int sigcheck;
+
+ ndo->ndo_protocol = "isis";
+ packet_len=length;
+ optr = p; /* initialize the _o_riginal pointer to the packet start -
+ need it for parsing the checksum TLV and authentication
+ TLV verification */
+ isis_header = (const struct isis_common_header *)p;
+ ND_TCHECK_SIZE(isis_header);
+ if (length < ISIS_COMMON_HEADER_SIZE)
+ goto trunc;
+ pptr = p+(ISIS_COMMON_HEADER_SIZE);
+ header_iih_lan = (const struct isis_iih_lan_header *)pptr;
+ header_iih_ptp = (const struct isis_iih_ptp_header *)pptr;
+ header_lsp = (const struct isis_lsp_header *)pptr;
+ header_csnp = (const struct isis_csnp_header *)pptr;
+ header_psnp = (const struct isis_psnp_header *)pptr;
+
+ if (!ndo->ndo_eflag)
+ ND_PRINT("IS-IS");
+
+ /*
+ * Sanity checking of the header.
+ */
+
+ version = GET_U_1(isis_header->version);
+ if (version != ISIS_VERSION) {
+ ND_PRINT("version %u packet not supported", version);
+ return (0);
+ }
+
+ pdu_id_length = GET_U_1(isis_header->id_length);
+ if ((pdu_id_length != SYSTEM_ID_LEN) && (pdu_id_length != 0)) {
+ ND_PRINT("system ID length of %u is not supported",
+ pdu_id_length);
+ return (0);
+ }
+
+ pdu_version = GET_U_1(isis_header->pdu_version);
+ if (pdu_version != ISIS_VERSION) {
+ ND_PRINT("version %u packet not supported", pdu_version);
+ return (0);
+ }
+
+ fixed_len = GET_U_1(isis_header->fixed_len);
+ if (length < fixed_len) {
+ ND_PRINT("fixed header length %u > packet length %u", fixed_len, length);
+ return (0);
+ }
+
+ if (fixed_len < ISIS_COMMON_HEADER_SIZE) {
+ ND_PRINT("fixed header length %u < minimum header size %u", fixed_len, (u_int)ISIS_COMMON_HEADER_SIZE);
+ return (0);
+ }
+
+ pdu_max_area = GET_U_1(isis_header->max_area);
+ switch(pdu_max_area) {
+ case 0:
+ max_area = 3; /* silly shit */
+ break;
+ case 255:
+ ND_PRINT("bad packet -- 255 areas");
+ return (0);
+ default:
+ max_area = pdu_max_area;
+ break;
+ }
+
+ switch(pdu_id_length) {
+ case 0:
+ id_length = 6; /* silly shit again */
+ break;
+ case 1: /* 1-8 are valid sys-ID lengths */
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ id_length = pdu_id_length;
+ break;
+ case 255:
+ id_length = 0; /* entirely useless */
+ break;
+ default:
+ id_length = pdu_id_length;
+ break;
+ }
+
+ /* toss any non 6-byte sys-ID len PDUs */
+ if (id_length != 6 ) {
+ ND_PRINT("bad packet -- illegal sys-ID length (%u)", id_length);
+ return (0);
+ }
+
+ pdu_type = GET_U_1(isis_header->pdu_type);
+
+ /* in non-verbose mode print the basic PDU Type plus PDU specific brief information*/
+ if (ndo->ndo_vflag == 0) {
+ ND_PRINT("%s%s",
+ ndo->ndo_eflag ? "" : ", ",
+ tok2str(isis_pdu_values, "unknown PDU-Type %u", pdu_type));
+ } else {
+ /* ok they seem to want to know everything - lets fully decode it */
+ ND_PRINT("%slength %u", ndo->ndo_eflag ? "" : ", ", length);
+
+ ND_PRINT("\n\t%s, hlen: %u, v: %u, pdu-v: %u, sys-id-len: %u (%u), max-area: %u (%u)",
+ tok2str(isis_pdu_values,
+ "unknown, type %u",
+ pdu_type),
+ fixed_len,
+ version,
+ pdu_version,
+ id_length,
+ pdu_id_length,
+ max_area,
+ pdu_max_area);
+
+ if (ndo->ndo_vflag > 1) {
+ if (!print_unknown_data(ndo, optr, "\n\t", 8)) /* provide the _o_riginal pointer */
+ return (0); /* for optionally debugging the common header */
+ }
+ }
+
+ switch (pdu_type) {
+
+ case ISIS_PDU_L1_LAN_IIH:
+ case ISIS_PDU_L2_LAN_IIH:
+ if (fixed_len != (ISIS_COMMON_HEADER_SIZE+ISIS_IIH_LAN_HEADER_SIZE)) {
+ ND_PRINT(", bogus fixed header length %u should be %zu",
+ fixed_len, ISIS_COMMON_HEADER_SIZE+ISIS_IIH_LAN_HEADER_SIZE);
+ return (0);
+ }
+ ND_TCHECK_SIZE(header_iih_lan);
+ if (length < ISIS_COMMON_HEADER_SIZE+ISIS_IIH_LAN_HEADER_SIZE)
+ goto trunc;
+ if (ndo->ndo_vflag == 0) {
+ ND_PRINT(", src-id %s",
+ isis_print_id(ndo, header_iih_lan->source_id, SYSTEM_ID_LEN));
+ ND_PRINT(", lan-id %s, prio %u",
+ isis_print_id(ndo, header_iih_lan->lan_id,NODE_ID_LEN),
+ GET_U_1(header_iih_lan->priority));
+ ND_PRINT(", length %u", length);
+ return (1);
+ }
+ pdu_len=GET_BE_U_2(header_iih_lan->pdu_len);
+ if (packet_len>pdu_len) {
+ packet_len=pdu_len; /* do TLV decoding as long as it makes sense */
+ length=pdu_len;
+ }
+
+ ND_PRINT("\n\t source-id: %s, holding time: %us, Flags: [%s]",
+ isis_print_id(ndo, header_iih_lan->source_id,SYSTEM_ID_LEN),
+ GET_BE_U_2(header_iih_lan->holding_time),
+ tok2str(isis_iih_circuit_type_values,
+ "unknown circuit type 0x%02x",
+ GET_U_1(header_iih_lan->circuit_type)));
+
+ ND_PRINT("\n\t lan-id: %s, Priority: %u, PDU length: %u",
+ isis_print_id(ndo, header_iih_lan->lan_id, NODE_ID_LEN),
+ GET_U_1(header_iih_lan->priority) & ISIS_LAN_PRIORITY_MASK,
+ pdu_len);
+
+ if (ndo->ndo_vflag > 1) {
+ if (!print_unknown_data(ndo, pptr, "\n\t ", ISIS_IIH_LAN_HEADER_SIZE))
+ return (0);
+ }
+
+ INVALID_OR_DECREMENT(packet_len,ISIS_COMMON_HEADER_SIZE+ISIS_IIH_LAN_HEADER_SIZE);
+ pptr = p + (ISIS_COMMON_HEADER_SIZE+ISIS_IIH_LAN_HEADER_SIZE);
+ break;
+
+ case ISIS_PDU_PTP_IIH:
+ if (fixed_len != (ISIS_COMMON_HEADER_SIZE+ISIS_IIH_PTP_HEADER_SIZE)) {
+ ND_PRINT(", bogus fixed header length %u should be %zu",
+ fixed_len, ISIS_COMMON_HEADER_SIZE+ISIS_IIH_PTP_HEADER_SIZE);
+ return (0);
+ }
+ ND_TCHECK_SIZE(header_iih_ptp);
+ if (length < ISIS_COMMON_HEADER_SIZE+ISIS_IIH_PTP_HEADER_SIZE)
+ goto trunc;
+ if (ndo->ndo_vflag == 0) {
+ ND_PRINT(", src-id %s", isis_print_id(ndo, header_iih_ptp->source_id, SYSTEM_ID_LEN));
+ ND_PRINT(", length %u", length);
+ return (1);
+ }
+ pdu_len=GET_BE_U_2(header_iih_ptp->pdu_len);
+ if (packet_len>pdu_len) {
+ packet_len=pdu_len; /* do TLV decoding as long as it makes sense */
+ length=pdu_len;
+ }
+
+ ND_PRINT("\n\t source-id: %s, holding time: %us, Flags: [%s]",
+ isis_print_id(ndo, header_iih_ptp->source_id,SYSTEM_ID_LEN),
+ GET_BE_U_2(header_iih_ptp->holding_time),
+ tok2str(isis_iih_circuit_type_values,
+ "unknown circuit type 0x%02x",
+ GET_U_1(header_iih_ptp->circuit_type)));
+
+ ND_PRINT("\n\t circuit-id: 0x%02x, PDU length: %u",
+ GET_U_1(header_iih_ptp->circuit_id),
+ pdu_len);
+
+ if (ndo->ndo_vflag > 1) {
+ if (!print_unknown_data(ndo, pptr, "\n\t ", ISIS_IIH_PTP_HEADER_SIZE))
+ return (0);
+ }
+ INVALID_OR_DECREMENT(packet_len,ISIS_COMMON_HEADER_SIZE+ISIS_IIH_PTP_HEADER_SIZE);
+ pptr = p + (ISIS_COMMON_HEADER_SIZE+ISIS_IIH_PTP_HEADER_SIZE);
+ break;
+
+ case ISIS_PDU_L1_LSP:
+ case ISIS_PDU_L2_LSP:
+ if (fixed_len != (ISIS_COMMON_HEADER_SIZE+ISIS_LSP_HEADER_SIZE)) {
+ ND_PRINT(", bogus fixed header length %u should be %zu",
+ fixed_len, ISIS_LSP_HEADER_SIZE);
+ return (0);
+ }
+ ND_TCHECK_SIZE(header_lsp);
+ if (length < ISIS_COMMON_HEADER_SIZE+ISIS_LSP_HEADER_SIZE)
+ goto trunc;
+ if (ndo->ndo_vflag == 0) {
+ ND_PRINT(", lsp-id %s, seq 0x%08x, lifetime %5us",
+ isis_print_id(ndo, header_lsp->lsp_id, LSP_ID_LEN),
+ GET_BE_U_4(header_lsp->sequence_number),
+ GET_BE_U_2(header_lsp->remaining_lifetime));
+ ND_PRINT(", length %u", length);
+ return (1);
+ }
+ pdu_len=GET_BE_U_2(header_lsp->pdu_len);
+ if (packet_len>pdu_len) {
+ packet_len=pdu_len; /* do TLV decoding as long as it makes sense */
+ length=pdu_len;
+ }
+
+ ND_PRINT("\n\t lsp-id: %s, seq: 0x%08x, lifetime: %5us\n\t chksum: 0x%04x",
+ isis_print_id(ndo, header_lsp->lsp_id, LSP_ID_LEN),
+ GET_BE_U_4(header_lsp->sequence_number),
+ GET_BE_U_2(header_lsp->remaining_lifetime),
+ GET_BE_U_2(header_lsp->checksum));
+
+ osi_print_cksum(ndo, (const uint8_t *)header_lsp->lsp_id,
+ GET_BE_U_2(header_lsp->checksum),
+ 12, length-12);
+
+ ND_PRINT(", PDU length: %u, Flags: [ %s",
+ pdu_len,
+ ISIS_MASK_LSP_OL_BIT(header_lsp->typeblock) ? "Overload bit set, " : "");
+
+ if (ISIS_MASK_LSP_ATT_BITS(header_lsp->typeblock)) {
+ ND_PRINT("%s", ISIS_MASK_LSP_ATT_DEFAULT_BIT(header_lsp->typeblock) ? "default " : "");
+ ND_PRINT("%s", ISIS_MASK_LSP_ATT_DELAY_BIT(header_lsp->typeblock) ? "delay " : "");
+ ND_PRINT("%s", ISIS_MASK_LSP_ATT_EXPENSE_BIT(header_lsp->typeblock) ? "expense " : "");
+ ND_PRINT("%s", ISIS_MASK_LSP_ATT_ERROR_BIT(header_lsp->typeblock) ? "error " : "");
+ ND_PRINT("ATT bit set, ");
+ }
+ ND_PRINT("%s", ISIS_MASK_LSP_PARTITION_BIT(header_lsp->typeblock) ? "P bit set, " : "");
+ ND_PRINT("%s ]", tok2str(isis_lsp_istype_values, "Unknown(0x%x)",
+ ISIS_MASK_LSP_ISTYPE_BITS(header_lsp->typeblock)));
+
+ if (ndo->ndo_vflag > 1) {
+ if (!print_unknown_data(ndo, pptr, "\n\t ", ISIS_LSP_HEADER_SIZE))
+ return (0);
+ }
+
+ INVALID_OR_DECREMENT(packet_len,ISIS_COMMON_HEADER_SIZE+ISIS_LSP_HEADER_SIZE);
+ pptr = p + (ISIS_COMMON_HEADER_SIZE+ISIS_LSP_HEADER_SIZE);
+ break;
+
+ case ISIS_PDU_L1_CSNP:
+ case ISIS_PDU_L2_CSNP:
+ if (fixed_len != (ISIS_COMMON_HEADER_SIZE+ISIS_CSNP_HEADER_SIZE)) {
+ ND_PRINT(", bogus fixed header length %u should be %zu",
+ fixed_len, ISIS_COMMON_HEADER_SIZE+ISIS_CSNP_HEADER_SIZE);
+ return (0);
+ }
+ ND_TCHECK_SIZE(header_csnp);
+ if (length < ISIS_COMMON_HEADER_SIZE+ISIS_CSNP_HEADER_SIZE)
+ goto trunc;
+ if (ndo->ndo_vflag == 0) {
+ ND_PRINT(", src-id %s", isis_print_id(ndo, header_csnp->source_id, NODE_ID_LEN));
+ ND_PRINT(", length %u", length);
+ return (1);
+ }
+ pdu_len=GET_BE_U_2(header_csnp->pdu_len);
+ if (packet_len>pdu_len) {
+ packet_len=pdu_len; /* do TLV decoding as long as it makes sense */
+ length=pdu_len;
+ }
+
+ ND_PRINT("\n\t source-id: %s, PDU length: %u",
+ isis_print_id(ndo, header_csnp->source_id, NODE_ID_LEN),
+ pdu_len);
+ ND_PRINT("\n\t start lsp-id: %s",
+ isis_print_id(ndo, header_csnp->start_lsp_id, LSP_ID_LEN));
+ ND_PRINT("\n\t end lsp-id: %s",
+ isis_print_id(ndo, header_csnp->end_lsp_id, LSP_ID_LEN));
+
+ if (ndo->ndo_vflag > 1) {
+ if (!print_unknown_data(ndo, pptr, "\n\t ", ISIS_CSNP_HEADER_SIZE))
+ return (0);
+ }
+
+ INVALID_OR_DECREMENT(packet_len,ISIS_COMMON_HEADER_SIZE+ISIS_CSNP_HEADER_SIZE);
+ pptr = p + (ISIS_COMMON_HEADER_SIZE+ISIS_CSNP_HEADER_SIZE);
+ break;
+
+ case ISIS_PDU_L1_PSNP:
+ case ISIS_PDU_L2_PSNP:
+ if (fixed_len != (ISIS_COMMON_HEADER_SIZE+ISIS_PSNP_HEADER_SIZE)) {
+ ND_PRINT("- bogus fixed header length %u should be %zu",
+ fixed_len, ISIS_COMMON_HEADER_SIZE+ISIS_PSNP_HEADER_SIZE);
+ return (0);
+ }
+ ND_TCHECK_SIZE(header_psnp);
+ if (length < ISIS_COMMON_HEADER_SIZE+ISIS_PSNP_HEADER_SIZE)
+ goto trunc;
+ if (ndo->ndo_vflag == 0) {
+ ND_PRINT(", src-id %s", isis_print_id(ndo, header_psnp->source_id, NODE_ID_LEN));
+ ND_PRINT(", length %u", length);
+ return (1);
+ }
+ pdu_len=GET_BE_U_2(header_psnp->pdu_len);
+ if (packet_len>pdu_len) {
+ packet_len=pdu_len; /* do TLV decoding as long as it makes sense */
+ length=pdu_len;
+ }
+
+ ND_PRINT("\n\t source-id: %s, PDU length: %u",
+ isis_print_id(ndo, header_psnp->source_id, NODE_ID_LEN),
+ pdu_len);
+
+ if (ndo->ndo_vflag > 1) {
+ if (!print_unknown_data(ndo, pptr, "\n\t ", ISIS_PSNP_HEADER_SIZE))
+ return (0);
+ }
+
+ INVALID_OR_DECREMENT(packet_len,ISIS_COMMON_HEADER_SIZE+ISIS_PSNP_HEADER_SIZE);
+ pptr = p + (ISIS_COMMON_HEADER_SIZE+ISIS_PSNP_HEADER_SIZE);
+ break;
+
+ default:
+ if (ndo->ndo_vflag == 0) {
+ ND_PRINT(", length %u", length);
+ return (1);
+ }
+ (void)print_unknown_data(ndo, pptr, "\n\t ", length);
+ return (0);
+ }
+
+ /*
+ * Now print the TLV's.
+ */
+
+ while (packet_len > 0) {
+ ND_TCHECK_2(pptr);
+ if (packet_len < 2)
+ goto trunc;
+ tlv_type = GET_U_1(pptr);
+ tlv_len = GET_U_1(pptr + 1);
+ pptr += 2;
+ packet_len -= 2;
+ tlen = tlv_len; /* copy temporary len & pointer to packet data */
+ tptr = pptr;
+
+ /* first lets see if we know the TLVs name*/
+ ND_PRINT("\n\t %s TLV #%u, length: %u",
+ tok2str(isis_tlv_values,
+ "unknown",
+ tlv_type),
+ tlv_type,
+ tlv_len);
+
+ if (packet_len < tlv_len)
+ goto trunc;
+
+ /* now check if we have a decoder otherwise do a hexdump at the end*/
+ switch (tlv_type) {
+ case ISIS_TLV_AREA_ADDR:
+ while (tlen != 0) {
+ alen = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+ if (tlen < alen)
+ goto tlv_trunc;
+ ND_PRINT("\n\t Area address (length: %u): %s",
+ alen,
+ GET_ISONSAP_STRING(tptr, alen));
+ tptr += alen;
+ tlen -= alen;
+ }
+ break;
+ case ISIS_TLV_ISNEIGH:
+ while (tlen != 0) {
+ if (tlen < MAC_ADDR_LEN)
+ goto tlv_trunc;
+ ND_TCHECK_LEN(tptr, MAC_ADDR_LEN);
+ ND_PRINT("\n\t SNPA: %s", isis_print_id(ndo, tptr, MAC_ADDR_LEN));
+ tlen -= MAC_ADDR_LEN;
+ tptr += MAC_ADDR_LEN;
+ }
+ break;
+
+ case ISIS_TLV_INSTANCE_ID:
+ if (tlen < 4)
+ goto tlv_trunc;
+ num_vals = (tlen-2)/2;
+ ND_PRINT("\n\t Instance ID: %u, ITIDs(%u)%s ",
+ GET_BE_U_2(tptr), num_vals,
+ num_vals ? ":" : "");
+ tptr += 2;
+ tlen -= 2;
+ for (i=0; i < num_vals; i++) {
+ ND_PRINT("%u", GET_BE_U_2(tptr));
+ if (i < (num_vals - 1)) {
+ ND_PRINT(", ");
+ }
+ tptr += 2;
+ tlen -= 2;
+ }
+ break;
+
+ case ISIS_TLV_PADDING:
+ break;
+
+ case ISIS_TLV_MT_IS_REACH:
+ mt_len = isis_print_mtid(ndo, tptr, "\n\t ", tlen);
+ if (mt_len == 0) /* did something go wrong ? */
+ goto trunc;
+ tptr+=mt_len;
+ tlen-=mt_len;
+ while (tlen != 0) {
+ ext_is_len = isis_print_ext_is_reach(ndo, tptr, "\n\t ", tlv_type, tlen);
+ if (ext_is_len == 0) /* did something go wrong ? */
+ goto trunc;
+ if (tlen < ext_is_len) {
+ ND_PRINT(" [remaining tlv length %u < %u]", tlen, ext_is_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tlen-=(uint8_t)ext_is_len;
+ tptr+=(uint8_t)ext_is_len;
+ }
+ break;
+
+ case ISIS_TLV_IS_ALIAS_ID:
+ while (tlen != 0) {
+ ext_is_len = isis_print_ext_is_reach(ndo, tptr, "\n\t ", tlv_type, tlen);
+ if (ext_is_len == 0) /* did something go wrong ? */
+ goto trunc;
+ if (tlen < ext_is_len) {
+ ND_PRINT(" [remaining tlv length %u < %u]", tlen, ext_is_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tlen-=(uint8_t)ext_is_len;
+ tptr+=(uint8_t)ext_is_len;
+ }
+ break;
+
+ case ISIS_TLV_EXT_IS_REACH:
+ while (tlen != 0) {
+ ext_is_len = isis_print_ext_is_reach(ndo, tptr, "\n\t ", tlv_type, tlen);
+ if (ext_is_len == 0) /* did something go wrong ? */
+ goto trunc;
+ if (tlen < ext_is_len) {
+ ND_PRINT(" [remaining tlv length %u < %u]", tlen, ext_is_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tlen-=(uint8_t)ext_is_len;
+ tptr+=(uint8_t)ext_is_len;
+ }
+ break;
+ case ISIS_TLV_IS_REACH:
+ if (tlen < 1)
+ goto tlv_trunc;
+ ND_PRINT("\n\t %s",
+ tok2str(isis_is_reach_virtual_values,
+ "bogus virtual flag 0x%02x",
+ GET_U_1(tptr)));
+ tptr++;
+ tlen--;
+ tlv_is_reach = (const struct isis_tlv_is_reach *)tptr;
+ while (tlen != 0) {
+ if (tlen < sizeof(struct isis_tlv_is_reach))
+ goto tlv_trunc;
+ ND_TCHECK_SIZE(tlv_is_reach);
+ ND_PRINT("\n\t IS Neighbor: %s",
+ isis_print_id(ndo, tlv_is_reach->neighbor_nodeid, NODE_ID_LEN));
+ isis_print_metric_block(ndo, &tlv_is_reach->isis_metric_block);
+ tlen -= sizeof(struct isis_tlv_is_reach);
+ tlv_is_reach++;
+ }
+ break;
+
+ case ISIS_TLV_ESNEIGH:
+ tlv_es_reach = (const struct isis_tlv_es_reach *)tptr;
+ while (tlen != 0) {
+ if (tlen < sizeof(struct isis_tlv_es_reach))
+ goto tlv_trunc;
+ ND_TCHECK_SIZE(tlv_es_reach);
+ ND_PRINT("\n\t ES Neighbor: %s",
+ isis_print_id(ndo, tlv_es_reach->neighbor_sysid, SYSTEM_ID_LEN));
+ isis_print_metric_block(ndo, &tlv_es_reach->isis_metric_block);
+ tlen -= sizeof(struct isis_tlv_es_reach);
+ tlv_es_reach++;
+ }
+ break;
+
+ /* those two TLVs share the same format */
+ case ISIS_TLV_INT_IP_REACH:
+ case ISIS_TLV_EXT_IP_REACH:
+ if (!isis_print_tlv_ip_reach(ndo, pptr, "\n\t ", tlv_len))
+ return (1);
+ break;
+
+ case ISIS_TLV_EXTD_IP_REACH:
+ while (tlen != 0) {
+ ext_ip_len = isis_print_extd_ip_reach(ndo, tptr, "\n\t ", AF_INET);
+ if (ext_ip_len == 0) /* did something go wrong ? */
+ goto trunc;
+ if (tlen < ext_ip_len) {
+ ND_PRINT(" [remaining tlv length %u < %u]", tlen, ext_ip_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tlen-=(uint8_t)ext_ip_len;
+ tptr+=(uint8_t)ext_ip_len;
+ }
+ break;
+
+ case ISIS_TLV_MT_IP_REACH:
+ mt_len = isis_print_mtid(ndo, tptr, "\n\t ", tlen);
+ if (mt_len == 0) { /* did something go wrong ? */
+ goto trunc;
+ }
+ tptr+=mt_len;
+ tlen-=mt_len;
+
+ while (tlen != 0) {
+ ext_ip_len = isis_print_extd_ip_reach(ndo, tptr, "\n\t ", AF_INET);
+ if (ext_ip_len == 0) /* did something go wrong ? */
+ goto trunc;
+ if (tlen < ext_ip_len) {
+ ND_PRINT(" [remaining tlv length %u < %u]", tlen, ext_ip_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tlen-=(uint8_t)ext_ip_len;
+ tptr+=(uint8_t)ext_ip_len;
+ }
+ break;
+
+ case ISIS_TLV_IP6_REACH:
+ while (tlen != 0) {
+ ext_ip_len = isis_print_extd_ip_reach(ndo, tptr, "\n\t ", AF_INET6);
+ if (ext_ip_len == 0) /* did something go wrong ? */
+ goto trunc;
+ if (tlen < ext_ip_len) {
+ ND_PRINT(" [remaining tlv length %u < %u]", tlen, ext_ip_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tlen-=(uint8_t)ext_ip_len;
+ tptr+=(uint8_t)ext_ip_len;
+ }
+ break;
+
+ case ISIS_TLV_MT_IP6_REACH:
+ mt_len = isis_print_mtid(ndo, tptr, "\n\t ", tlen);
+ if (mt_len == 0) { /* did something go wrong ? */
+ goto trunc;
+ }
+ tptr+=mt_len;
+ tlen-=mt_len;
+
+ while (tlen != 0) {
+ ext_ip_len = isis_print_extd_ip_reach(ndo, tptr, "\n\t ", AF_INET6);
+ if (ext_ip_len == 0) /* did something go wrong ? */
+ goto trunc;
+ if (tlen < ext_ip_len) {
+ ND_PRINT(" [remaining tlv length %u < %u]", tlen, ext_ip_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tlen-=(uint8_t)ext_ip_len;
+ tptr+=(uint8_t)ext_ip_len;
+ }
+ break;
+
+ case ISIS_TLV_IP6ADDR:
+ while (tlen != 0) {
+ if (tlen < sizeof(nd_ipv6))
+ goto tlv_trunc;
+ ND_PRINT("\n\t IPv6 interface address: %s",
+ GET_IP6ADDR_STRING(tptr));
+
+ tptr += sizeof(nd_ipv6);
+ tlen -= sizeof(nd_ipv6);
+ }
+ break;
+ case ISIS_TLV_AUTH:
+ if (tlen < 1)
+ goto tlv_trunc;
+ auth_type = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+
+ ND_PRINT("\n\t %s: ",
+ tok2str(isis_subtlv_auth_values,
+ "unknown Authentication type 0x%02x",
+ auth_type));
+
+ switch (auth_type) {
+ case ISIS_SUBTLV_AUTH_SIMPLE:
+ nd_printjnp(ndo, tptr, tlen);
+ break;
+ case ISIS_SUBTLV_AUTH_MD5:
+ for(i=0;i<tlen;i++) {
+ ND_PRINT("%02x", GET_U_1(tptr + i));
+ }
+ if (tlen != ISIS_SUBTLV_AUTH_MD5_LEN)
+ ND_PRINT(", (invalid subTLV) ");
+
+ sigcheck = signature_verify(ndo, optr, length, tptr,
+ isis_clear_checksum_lifetime,
+ header_lsp);
+ ND_PRINT(" (%s)", tok2str(signature_check_values, "Unknown", sigcheck));
+
+ break;
+ case ISIS_SUBTLV_AUTH_GENERIC:
+ if (tlen < 2)
+ goto tlv_trunc;
+ key_id = GET_BE_U_2(tptr);
+ ND_PRINT("%u, password: ", key_id);
+ tptr += 2;
+ tlen -= 2;
+ for(i=0;i<tlen;i++) {
+ ND_PRINT("%02x", GET_U_1(tptr + i));
+ }
+ break;
+ case ISIS_SUBTLV_AUTH_PRIVATE:
+ default:
+ if (!print_unknown_data(ndo, tptr, "\n\t\t ", tlen))
+ return(0);
+ break;
+ }
+ break;
+
+ case ISIS_TLV_PTP_ADJ:
+ tlv_ptp_adj = (const struct isis_tlv_ptp_adj *)tptr;
+ if(tlen>=1) {
+ ND_PRINT("\n\t Adjacency State: %s (%u)",
+ tok2str(isis_ptp_adjancey_values, "unknown", GET_U_1(tptr)),
+ GET_U_1(tptr));
+ tlen--;
+ }
+ if(tlen>sizeof(tlv_ptp_adj->extd_local_circuit_id)) {
+ ND_PRINT("\n\t Extended Local circuit-ID: 0x%08x",
+ GET_BE_U_4(tlv_ptp_adj->extd_local_circuit_id));
+ tlen-=sizeof(tlv_ptp_adj->extd_local_circuit_id);
+ }
+ if(tlen>=SYSTEM_ID_LEN) {
+ ND_TCHECK_LEN(tlv_ptp_adj->neighbor_sysid, SYSTEM_ID_LEN);
+ ND_PRINT("\n\t Neighbor System-ID: %s",
+ isis_print_id(ndo, tlv_ptp_adj->neighbor_sysid, SYSTEM_ID_LEN));
+ tlen-=SYSTEM_ID_LEN;
+ }
+ if(tlen>=sizeof(tlv_ptp_adj->neighbor_extd_local_circuit_id)) {
+ ND_PRINT("\n\t Neighbor Extended Local circuit-ID: 0x%08x",
+ GET_BE_U_4(tlv_ptp_adj->neighbor_extd_local_circuit_id));
+ }
+ break;
+
+ case ISIS_TLV_PROTOCOLS:
+ ND_PRINT("\n\t NLPID(s): ");
+ while (tlen != 0) {
+ ND_PRINT("%s (0x%02x)",
+ tok2str(nlpid_values,
+ "unknown",
+ GET_U_1(tptr)),
+ GET_U_1(tptr));
+ if (tlen>1) /* further NPLIDs ? - put comma */
+ ND_PRINT(", ");
+ tptr++;
+ tlen--;
+ }
+ break;
+
+ case ISIS_TLV_MT_PORT_CAP:
+ {
+ if (tlen < 2)
+ goto tlv_trunc;
+
+ ND_PRINT("\n\t RES: %u, MTID(s): %u",
+ (GET_BE_U_2(tptr) >> 12),
+ (GET_BE_U_2(tptr) & 0x0fff));
+
+ tptr += 2;
+ tlen -= 2;
+
+ if (tlen)
+ isis_print_mt_port_cap_subtlv(ndo, tptr, tlen);
+
+ break;
+ }
+
+ case ISIS_TLV_MT_CAPABILITY:
+ if (tlen < 2)
+ goto tlv_trunc;
+
+ ND_PRINT("\n\t O: %u, RES: %u, MTID(s): %u",
+ (GET_BE_U_2(tptr) >> 15) & 0x01,
+ (GET_BE_U_2(tptr) >> 12) & 0x07,
+ GET_BE_U_2(tptr) & 0x0fff);
+
+ tptr += 2;
+ tlen -= 2;
+
+ if (tlen)
+ isis_print_mt_capability_subtlv(ndo, tptr, tlen);
+
+ break;
+
+ case ISIS_TLV_TE_ROUTER_ID:
+ if (tlen < sizeof(nd_ipv4))
+ goto tlv_trunc;
+ ND_PRINT("\n\t Traffic Engineering Router ID: %s", GET_IPADDR_STRING(pptr));
+ break;
+
+ case ISIS_TLV_IPADDR:
+ while (tlen != 0) {
+ if (tlen < sizeof(nd_ipv4))
+ goto tlv_trunc;
+ ND_PRINT("\n\t IPv4 interface address: %s", GET_IPADDR_STRING(tptr));
+ tptr += sizeof(nd_ipv4);
+ tlen -= sizeof(nd_ipv4);
+ }
+ break;
+
+ case ISIS_TLV_HOSTNAME:
+ ND_PRINT("\n\t Hostname: ");
+ nd_printjnp(ndo, tptr, tlen);
+ break;
+
+ case ISIS_TLV_SHARED_RISK_GROUP:
+ if (tlen < NODE_ID_LEN)
+ break;
+ ND_TCHECK_LEN(tptr, NODE_ID_LEN);
+ ND_PRINT("\n\t IS Neighbor: %s", isis_print_id(ndo, tptr, NODE_ID_LEN));
+ tptr+=NODE_ID_LEN;
+ tlen-=NODE_ID_LEN;
+
+ if (tlen < 1)
+ break;
+ ND_PRINT(", Flags: [%s]",
+ ISIS_MASK_TLV_SHARED_RISK_GROUP(GET_U_1(tptr)) ? "numbered" : "unnumbered");
+ tptr++;
+ tlen--;
+
+ if (tlen < sizeof(nd_ipv4))
+ break;
+ ND_PRINT("\n\t IPv4 interface address: %s", GET_IPADDR_STRING(tptr));
+ tptr+=sizeof(nd_ipv4);
+ tlen-=sizeof(nd_ipv4);
+
+ if (tlen < sizeof(nd_ipv4))
+ break;
+ ND_PRINT("\n\t IPv4 neighbor address: %s", GET_IPADDR_STRING(tptr));
+ tptr+=sizeof(nd_ipv4);
+ tlen-=sizeof(nd_ipv4);
+
+ while (tlen != 0) {
+ if (tlen < 4)
+ goto tlv_trunc;
+ ND_PRINT("\n\t Link-ID: 0x%08x", GET_BE_U_4(tptr));
+ tptr+=4;
+ tlen-=4;
+ }
+ break;
+
+ case ISIS_TLV_LSP:
+ tlv_lsp = (const struct isis_tlv_lsp *)tptr;
+ while (tlen != 0) {
+ if (tlen < sizeof(struct isis_tlv_lsp))
+ goto tlv_trunc;
+ ND_TCHECK_1(tlv_lsp->lsp_id + LSP_ID_LEN - 1);
+ ND_PRINT("\n\t lsp-id: %s",
+ isis_print_id(ndo, tlv_lsp->lsp_id, LSP_ID_LEN));
+ ND_PRINT(", seq: 0x%08x",
+ GET_BE_U_4(tlv_lsp->sequence_number));
+ ND_PRINT(", lifetime: %5ds",
+ GET_BE_U_2(tlv_lsp->remaining_lifetime));
+ ND_PRINT(", chksum: 0x%04x", GET_BE_U_2(tlv_lsp->checksum));
+ tlen-=sizeof(struct isis_tlv_lsp);
+ tlv_lsp++;
+ }
+ break;
+
+ case ISIS_TLV_CHECKSUM:
+ if (tlen < ISIS_TLV_CHECKSUM_MINLEN)
+ break;
+ ND_TCHECK_LEN(tptr, ISIS_TLV_CHECKSUM_MINLEN);
+ ND_PRINT("\n\t checksum: 0x%04x ", GET_BE_U_2(tptr));
+ /* do not attempt to verify the checksum if it is zero
+ * most likely a HMAC-MD5 TLV is also present and
+ * to avoid conflicts the checksum TLV is zeroed.
+ * see rfc3358 for details
+ */
+ osi_print_cksum(ndo, optr, GET_BE_U_2(tptr), (int)(tptr-optr),
+ length);
+ break;
+
+ case ISIS_TLV_POI:
+ if (tlen < 1)
+ goto tlv_trunc;
+ num_system_ids = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+ if (num_system_ids == 0) {
+ /* Not valid */
+ ND_PRINT(" No system IDs supplied");
+ } else {
+ if (tlen < SYSTEM_ID_LEN)
+ goto tlv_trunc;
+ ND_TCHECK_LEN(tptr, SYSTEM_ID_LEN);
+ ND_PRINT("\n\t Purge Originator System-ID: %s",
+ isis_print_id(ndo, tptr, SYSTEM_ID_LEN));
+ tptr += SYSTEM_ID_LEN;
+ tlen -= SYSTEM_ID_LEN;
+
+ if (num_system_ids > 1) {
+ if (tlen < SYSTEM_ID_LEN)
+ goto tlv_trunc;
+ ND_TCHECK_LEN(tptr, SYSTEM_ID_LEN);
+ ND_TCHECK_LEN(tptr, 2 * SYSTEM_ID_LEN + 1);
+ ND_PRINT("\n\t Received from System-ID: %s",
+ isis_print_id(ndo, tptr, SYSTEM_ID_LEN));
+ }
+ }
+ break;
+
+ case ISIS_TLV_MT_SUPPORTED:
+ while (tlen != 0) {
+ /* length can only be a multiple of 2, otherwise there is
+ something broken -> so decode down until length is 1 */
+ if (tlen!=1) {
+ mt_len = isis_print_mtid(ndo, tptr, "\n\t ", tlen);
+ if (mt_len == 0) /* did something go wrong ? */
+ goto trunc;
+ tptr+=mt_len;
+ tlen-=mt_len;
+ } else {
+ ND_PRINT("\n\t invalid MT-ID");
+ break;
+ }
+ }
+ break;
+
+ case ISIS_TLV_RESTART_SIGNALING:
+ /* first attempt to decode the flags */
+ if (tlen < ISIS_TLV_RESTART_SIGNALING_FLAGLEN)
+ break;
+ ND_TCHECK_LEN(tptr, ISIS_TLV_RESTART_SIGNALING_FLAGLEN);
+ ND_PRINT("\n\t Flags [%s]",
+ bittok2str(isis_restart_flag_values, "none", GET_U_1(tptr)));
+ tptr+=ISIS_TLV_RESTART_SIGNALING_FLAGLEN;
+ tlen-=ISIS_TLV_RESTART_SIGNALING_FLAGLEN;
+
+ /* is there anything other than the flags field? */
+ if (tlen == 0)
+ break;
+
+ if (tlen < ISIS_TLV_RESTART_SIGNALING_HOLDTIMELEN)
+ break;
+ ND_TCHECK_LEN(tptr, ISIS_TLV_RESTART_SIGNALING_HOLDTIMELEN);
+
+ ND_PRINT(", Remaining holding time %us", GET_BE_U_2(tptr));
+ tptr+=ISIS_TLV_RESTART_SIGNALING_HOLDTIMELEN;
+ tlen-=ISIS_TLV_RESTART_SIGNALING_HOLDTIMELEN;
+
+ /* is there an additional sysid field present ?*/
+ if (tlen == SYSTEM_ID_LEN) {
+ ND_TCHECK_LEN(tptr, SYSTEM_ID_LEN);
+ ND_PRINT(", for %s", isis_print_id(ndo, tptr,SYSTEM_ID_LEN));
+ }
+ break;
+
+ case ISIS_TLV_IDRP_INFO:
+ if (tlen < 1)
+ break;
+ isis_subtlv_idrp = GET_U_1(tptr);
+ ND_PRINT("\n\t Inter-Domain Information Type: %s",
+ tok2str(isis_subtlv_idrp_values,
+ "Unknown (0x%02x)",
+ isis_subtlv_idrp));
+ tptr++;
+ tlen--;
+ switch (isis_subtlv_idrp) {
+ case ISIS_SUBTLV_IDRP_ASN:
+ if (tlen < 2)
+ goto tlv_trunc;
+ ND_PRINT("AS Number: %u", GET_BE_U_2(tptr));
+ break;
+ case ISIS_SUBTLV_IDRP_LOCAL:
+ case ISIS_SUBTLV_IDRP_RES:
+ default:
+ if (!print_unknown_data(ndo, tptr, "\n\t ", tlen))
+ return(0);
+ break;
+ }
+ break;
+
+ case ISIS_TLV_LSP_BUFFERSIZE:
+ if (tlen < 2)
+ break;
+ ND_PRINT("\n\t LSP Buffersize: %u", GET_BE_U_2(tptr));
+ break;
+
+ case ISIS_TLV_PART_DIS:
+ while (tlen != 0) {
+ if (tlen < SYSTEM_ID_LEN)
+ goto tlv_trunc;
+ ND_TCHECK_LEN(tptr, SYSTEM_ID_LEN);
+ ND_PRINT("\n\t %s", isis_print_id(ndo, tptr, SYSTEM_ID_LEN));
+ tptr+=SYSTEM_ID_LEN;
+ tlen-=SYSTEM_ID_LEN;
+ }
+ break;
+
+ case ISIS_TLV_PREFIX_NEIGH:
+ if (tlen < sizeof(struct isis_metric_block))
+ break;
+ ND_TCHECK_LEN(tptr, sizeof(struct isis_metric_block));
+ ND_PRINT("\n\t Metric Block");
+ isis_print_metric_block(ndo, (const struct isis_metric_block *)tptr);
+ tptr+=sizeof(struct isis_metric_block);
+ tlen-=sizeof(struct isis_metric_block);
+
+ while (tlen != 0) {
+ prefix_len=GET_U_1(tptr); /* read out prefix length in semioctets*/
+ tptr++;
+ tlen--;
+ if (prefix_len < 2) {
+ ND_PRINT("\n\t\tAddress: prefix length %u < 2", prefix_len);
+ break;
+ }
+ if (tlen < prefix_len/2)
+ break;
+ ND_PRINT("\n\t\tAddress: %s/%u",
+ GET_ISONSAP_STRING(tptr, prefix_len / 2), prefix_len * 4);
+ tptr+=prefix_len/2;
+ tlen-=prefix_len/2;
+ }
+ break;
+
+ case ISIS_TLV_IIH_SEQNR:
+ if (tlen < 4)
+ break;
+ ND_PRINT("\n\t Sequence number: %u", GET_BE_U_4(tptr));
+ break;
+
+ case ISIS_TLV_ROUTER_CAPABILITY:
+ if (tlen < 5) {
+ ND_PRINT(" [object length %u < 5]", tlen);
+ nd_print_invalid(ndo);
+ break;
+ }
+ ND_PRINT("\n\t Router-ID %s", GET_IPADDR_STRING(tptr));
+ ND_PRINT(", Flags [%s]",
+ bittok2str(isis_tlv_router_capability_flags, "none", GET_U_1(tptr+4)));
+
+ /* Optional set of sub-TLV */
+ if (tlen > 5) {
+ isis_print_router_cap_subtlv(ndo, tptr+5, tlen-5);
+ }
+ break;
+
+ case ISIS_TLV_VENDOR_PRIVATE:
+ if (tlen < 3)
+ break;
+ vendor_id = GET_BE_U_3(tptr);
+ ND_PRINT("\n\t Vendor: %s (%u)",
+ tok2str(oui_values, "Unknown", vendor_id),
+ vendor_id);
+ tptr+=3;
+ tlen-=3;
+ if (tlen != 0) /* hexdump the rest */
+ if (!print_unknown_data(ndo, tptr, "\n\t\t", tlen))
+ return(0);
+ break;
+ /*
+ * FIXME those are the defined TLVs that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case ISIS_TLV_DECNET_PHASE4:
+ case ISIS_TLV_LUCENT_PRIVATE:
+ case ISIS_TLV_IPAUTH:
+ case ISIS_TLV_NORTEL_PRIVATE1:
+ case ISIS_TLV_NORTEL_PRIVATE2:
+
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ if (!print_unknown_data(ndo, pptr, "\n\t\t", tlv_len))
+ return(0);
+ }
+ break;
+ }
+tlv_trunc:
+ /* do we want to see an additionally hexdump ? */
+ if (ndo->ndo_vflag> 1) {
+ if (!print_unknown_data(ndo, pptr, "\n\t ", tlv_len))
+ return(0);
+ }
+
+ pptr += tlv_len;
+ packet_len -= tlv_len;
+ }
+
+ if (packet_len != 0) {
+ ND_PRINT("\n\t %u straggler bytes", packet_len);
+ }
+ return (1);
+
+trunc:
+ nd_print_trunc(ndo);
+ return (1);
+}
+
+static void
+osi_print_cksum(netdissect_options *ndo, const uint8_t *pptr,
+ uint16_t checksum, int checksum_offset, u_int length)
+{
+ uint16_t calculated_checksum;
+
+ /* do not attempt to verify the checksum if it is zero,
+ * if the offset is nonsense,
+ * or the base pointer is not sane
+ */
+ if (!checksum
+ || checksum_offset < 0
+ || !ND_TTEST_2(pptr + checksum_offset)
+ || (u_int)checksum_offset > length
+ || !ND_TTEST_LEN(pptr, length)) {
+ ND_PRINT(" (unverified)");
+ } else {
+#if 0
+ ND_PRINT("\nosi_print_cksum: %p %d %u\n", pptr, checksum_offset, length);
+#endif
+ calculated_checksum = create_osi_cksum(pptr, checksum_offset, length);
+ if (checksum == calculated_checksum) {
+ ND_PRINT(" (correct)");
+ } else {
+ ND_PRINT(" (incorrect should be 0x%04x)", calculated_checksum);
+ }
+ }
+}
diff --git a/print-juniper.c b/print-juniper.c
new file mode 100644
index 0000000..7c3df49
--- /dev/null
+++ b/print-juniper.c
@@ -0,0 +1,1616 @@
+/* NetBSD: print-juniper.c,v 1.2 2007/07/24 11:53:45 drochner Exp */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* \summary: DLT_JUNIPER_* printers */
+
+#ifndef lint
+#else
+__RCSID("NetBSD: print-juniper.c,v 1.3 2007/07/25 06:31:32 dogcow Exp ");
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+#include "ppp.h"
+#include "llc.h"
+#include "nlpid.h"
+#include "ethertype.h"
+#include "atm.h"
+
+/*
+ * If none of the Juniper DLT_s are defined, there's nothing to do.
+ */
+#if defined(DLT_JUNIPER_GGSN) || defined(DLT_JUNIPER_ES) || \
+ defined(DLT_JUNIPER_MONITOR) || defined(DLT_JUNIPER_SERVICES) || \
+ defined(DLT_JUNIPER_PPPOE) || defined(DLT_JUNIPER_ETHER) || \
+ defined(DLT_JUNIPER_PPP) || defined(DLT_JUNIPER_FRELAY) || \
+ defined(DLT_JUNIPER_CHDLC) || defined(DLT_JUNIPER_PPPOE_ATM) || \
+ defined(DLT_JUNIPER_MLPPP) || defined(DLT_JUNIPER_MFR) || \
+ defined(DLT_JUNIPER_MLFR) || defined(DLT_JUNIPER_ATM1) || \
+ defined(DLT_JUNIPER_ATM2)
+#define JUNIPER_BPF_OUT 0 /* Outgoing packet */
+#define JUNIPER_BPF_IN 1 /* Incoming packet */
+#define JUNIPER_BPF_PKT_IN 0x1 /* Incoming packet */
+#define JUNIPER_BPF_NO_L2 0x2 /* L2 header stripped */
+#define JUNIPER_BPF_IIF 0x4 /* IIF is valid */
+#define JUNIPER_BPF_FILTER 0x40 /* BPF filtering is supported */
+#define JUNIPER_BPF_EXT 0x80 /* extensions present */
+#define JUNIPER_MGC_NUMBER 0x4d4743 /* = "MGC" */
+
+#define JUNIPER_LSQ_COOKIE_RE (1 << 3)
+#define JUNIPER_LSQ_COOKIE_DIR (1 << 2)
+#define JUNIPER_LSQ_L3_PROTO_SHIFT 4
+#define JUNIPER_LSQ_L3_PROTO_MASK (0x17 << JUNIPER_LSQ_L3_PROTO_SHIFT)
+#define JUNIPER_LSQ_L3_PROTO_IPV4 (0 << JUNIPER_LSQ_L3_PROTO_SHIFT)
+#define JUNIPER_LSQ_L3_PROTO_IPV6 (1 << JUNIPER_LSQ_L3_PROTO_SHIFT)
+#define JUNIPER_LSQ_L3_PROTO_MPLS (2 << JUNIPER_LSQ_L3_PROTO_SHIFT)
+#define JUNIPER_LSQ_L3_PROTO_ISO (3 << JUNIPER_LSQ_L3_PROTO_SHIFT)
+#define AS_PIC_COOKIE_LEN 8
+
+#define JUNIPER_IPSEC_O_ESP_ENCRYPT_ESP_AUTHEN_TYPE 1
+#define JUNIPER_IPSEC_O_ESP_ENCRYPT_AH_AUTHEN_TYPE 2
+#define JUNIPER_IPSEC_O_ESP_AUTHENTICATION_TYPE 3
+#define JUNIPER_IPSEC_O_AH_AUTHENTICATION_TYPE 4
+#define JUNIPER_IPSEC_O_ESP_ENCRYPTION_TYPE 5
+
+#ifdef DLT_JUNIPER_ES
+static const struct tok juniper_ipsec_type_values[] = {
+ { JUNIPER_IPSEC_O_ESP_ENCRYPT_ESP_AUTHEN_TYPE, "ESP ENCR-AUTH" },
+ { JUNIPER_IPSEC_O_ESP_ENCRYPT_AH_AUTHEN_TYPE, "ESP ENCR-AH AUTH" },
+ { JUNIPER_IPSEC_O_ESP_AUTHENTICATION_TYPE, "ESP AUTH" },
+ { JUNIPER_IPSEC_O_AH_AUTHENTICATION_TYPE, "AH AUTH" },
+ { JUNIPER_IPSEC_O_ESP_ENCRYPTION_TYPE, "ESP ENCR" },
+ { 0, NULL}
+};
+#endif
+
+static const struct tok juniper_direction_values[] = {
+ { JUNIPER_BPF_IN, "In"},
+ { JUNIPER_BPF_OUT, "Out"},
+ { 0, NULL}
+};
+
+/* codepoints for encoding extensions to a .pcap file */
+enum {
+ JUNIPER_EXT_TLV_IFD_IDX = 1,
+ JUNIPER_EXT_TLV_IFD_NAME = 2,
+ JUNIPER_EXT_TLV_IFD_MEDIATYPE = 3,
+ JUNIPER_EXT_TLV_IFL_IDX = 4,
+ JUNIPER_EXT_TLV_IFL_UNIT = 5,
+ JUNIPER_EXT_TLV_IFL_ENCAPS = 6,
+ JUNIPER_EXT_TLV_TTP_IFD_MEDIATYPE = 7,
+ JUNIPER_EXT_TLV_TTP_IFL_ENCAPS = 8
+};
+
+/* 1 byte type and 1-byte length */
+#define JUNIPER_EXT_TLV_OVERHEAD 2U
+
+static const struct tok jnx_ext_tlv_values[] = {
+ { JUNIPER_EXT_TLV_IFD_IDX, "Device Interface Index" },
+ { JUNIPER_EXT_TLV_IFD_NAME,"Device Interface Name" },
+ { JUNIPER_EXT_TLV_IFD_MEDIATYPE, "Device Media Type" },
+ { JUNIPER_EXT_TLV_IFL_IDX, "Logical Interface Index" },
+ { JUNIPER_EXT_TLV_IFL_UNIT,"Logical Unit Number" },
+ { JUNIPER_EXT_TLV_IFL_ENCAPS, "Logical Interface Encapsulation" },
+ { JUNIPER_EXT_TLV_TTP_IFD_MEDIATYPE, "TTP derived Device Media Type" },
+ { JUNIPER_EXT_TLV_TTP_IFL_ENCAPS, "TTP derived Logical Interface Encapsulation" },
+ { 0, NULL }
+};
+
+static const struct tok jnx_flag_values[] = {
+ { JUNIPER_BPF_EXT, "Ext" },
+ { JUNIPER_BPF_FILTER, "Filter" },
+ { JUNIPER_BPF_IIF, "IIF" },
+ { JUNIPER_BPF_NO_L2, "no-L2" },
+ { JUNIPER_BPF_PKT_IN, "In" },
+ { 0, NULL }
+};
+
+#define JUNIPER_IFML_ETHER 1
+#define JUNIPER_IFML_FDDI 2
+#define JUNIPER_IFML_TOKENRING 3
+#define JUNIPER_IFML_PPP 4
+#define JUNIPER_IFML_FRAMERELAY 5
+#define JUNIPER_IFML_CISCOHDLC 6
+#define JUNIPER_IFML_SMDSDXI 7
+#define JUNIPER_IFML_ATMPVC 8
+#define JUNIPER_IFML_PPP_CCC 9
+#define JUNIPER_IFML_FRAMERELAY_CCC 10
+#define JUNIPER_IFML_IPIP 11
+#define JUNIPER_IFML_GRE 12
+#define JUNIPER_IFML_PIM 13
+#define JUNIPER_IFML_PIMD 14
+#define JUNIPER_IFML_CISCOHDLC_CCC 15
+#define JUNIPER_IFML_VLAN_CCC 16
+#define JUNIPER_IFML_MLPPP 17
+#define JUNIPER_IFML_MLFR 18
+#define JUNIPER_IFML_ML 19
+#define JUNIPER_IFML_LSI 20
+#define JUNIPER_IFML_DFE 21
+#define JUNIPER_IFML_ATM_CELLRELAY_CCC 22
+#define JUNIPER_IFML_CRYPTO 23
+#define JUNIPER_IFML_GGSN 24
+#define JUNIPER_IFML_LSI_PPP 25
+#define JUNIPER_IFML_LSI_CISCOHDLC 26
+#define JUNIPER_IFML_PPP_TCC 27
+#define JUNIPER_IFML_FRAMERELAY_TCC 28
+#define JUNIPER_IFML_CISCOHDLC_TCC 29
+#define JUNIPER_IFML_ETHERNET_CCC 30
+#define JUNIPER_IFML_VT 31
+#define JUNIPER_IFML_EXTENDED_VLAN_CCC 32
+#define JUNIPER_IFML_ETHER_OVER_ATM 33
+#define JUNIPER_IFML_MONITOR 34
+#define JUNIPER_IFML_ETHERNET_TCC 35
+#define JUNIPER_IFML_VLAN_TCC 36
+#define JUNIPER_IFML_EXTENDED_VLAN_TCC 37
+#define JUNIPER_IFML_CONTROLLER 38
+#define JUNIPER_IFML_MFR 39
+#define JUNIPER_IFML_LS 40
+#define JUNIPER_IFML_ETHERNET_VPLS 41
+#define JUNIPER_IFML_ETHERNET_VLAN_VPLS 42
+#define JUNIPER_IFML_ETHERNET_EXTENDED_VLAN_VPLS 43
+#define JUNIPER_IFML_LT 44
+#define JUNIPER_IFML_SERVICES 45
+#define JUNIPER_IFML_ETHER_VPLS_OVER_ATM 46
+#define JUNIPER_IFML_FR_PORT_CCC 47
+#define JUNIPER_IFML_FRAMERELAY_EXT_CCC 48
+#define JUNIPER_IFML_FRAMERELAY_EXT_TCC 49
+#define JUNIPER_IFML_FRAMERELAY_FLEX 50
+#define JUNIPER_IFML_GGSNI 51
+#define JUNIPER_IFML_ETHERNET_FLEX 52
+#define JUNIPER_IFML_COLLECTOR 53
+#define JUNIPER_IFML_AGGREGATOR 54
+#define JUNIPER_IFML_LAPD 55
+#define JUNIPER_IFML_PPPOE 56
+#define JUNIPER_IFML_PPP_SUBORDINATE 57
+#define JUNIPER_IFML_CISCOHDLC_SUBORDINATE 58
+#define JUNIPER_IFML_DFC 59
+#define JUNIPER_IFML_PICPEER 60
+
+static const struct tok juniper_ifmt_values[] = {
+ { JUNIPER_IFML_ETHER, "Ethernet" },
+ { JUNIPER_IFML_FDDI, "FDDI" },
+ { JUNIPER_IFML_TOKENRING, "Token-Ring" },
+ { JUNIPER_IFML_PPP, "PPP" },
+ { JUNIPER_IFML_PPP_SUBORDINATE, "PPP-Subordinate" },
+ { JUNIPER_IFML_FRAMERELAY, "Frame-Relay" },
+ { JUNIPER_IFML_CISCOHDLC, "Cisco-HDLC" },
+ { JUNIPER_IFML_SMDSDXI, "SMDS-DXI" },
+ { JUNIPER_IFML_ATMPVC, "ATM-PVC" },
+ { JUNIPER_IFML_PPP_CCC, "PPP-CCC" },
+ { JUNIPER_IFML_FRAMERELAY_CCC, "Frame-Relay-CCC" },
+ { JUNIPER_IFML_FRAMERELAY_EXT_CCC, "Extended FR-CCC" },
+ { JUNIPER_IFML_IPIP, "IP-over-IP" },
+ { JUNIPER_IFML_GRE, "GRE" },
+ { JUNIPER_IFML_PIM, "PIM-Encapsulator" },
+ { JUNIPER_IFML_PIMD, "PIM-Decapsulator" },
+ { JUNIPER_IFML_CISCOHDLC_CCC, "Cisco-HDLC-CCC" },
+ { JUNIPER_IFML_VLAN_CCC, "VLAN-CCC" },
+ { JUNIPER_IFML_EXTENDED_VLAN_CCC, "Extended-VLAN-CCC" },
+ { JUNIPER_IFML_MLPPP, "Multilink-PPP" },
+ { JUNIPER_IFML_MLFR, "Multilink-FR" },
+ { JUNIPER_IFML_MFR, "Multilink-FR-UNI-NNI" },
+ { JUNIPER_IFML_ML, "Multilink" },
+ { JUNIPER_IFML_LS, "LinkService" },
+ { JUNIPER_IFML_LSI, "LSI" },
+ { JUNIPER_IFML_ATM_CELLRELAY_CCC, "ATM-CCC-Cell-Relay" },
+ { JUNIPER_IFML_CRYPTO, "IPSEC-over-IP" },
+ { JUNIPER_IFML_GGSN, "GGSN" },
+ { JUNIPER_IFML_PPP_TCC, "PPP-TCC" },
+ { JUNIPER_IFML_FRAMERELAY_TCC, "Frame-Relay-TCC" },
+ { JUNIPER_IFML_FRAMERELAY_EXT_TCC, "Extended FR-TCC" },
+ { JUNIPER_IFML_CISCOHDLC_TCC, "Cisco-HDLC-TCC" },
+ { JUNIPER_IFML_ETHERNET_CCC, "Ethernet-CCC" },
+ { JUNIPER_IFML_VT, "VPN-Loopback-tunnel" },
+ { JUNIPER_IFML_ETHER_OVER_ATM, "Ethernet-over-ATM" },
+ { JUNIPER_IFML_ETHER_VPLS_OVER_ATM, "Ethernet-VPLS-over-ATM" },
+ { JUNIPER_IFML_MONITOR, "Monitor" },
+ { JUNIPER_IFML_ETHERNET_TCC, "Ethernet-TCC" },
+ { JUNIPER_IFML_VLAN_TCC, "VLAN-TCC" },
+ { JUNIPER_IFML_EXTENDED_VLAN_TCC, "Extended-VLAN-TCC" },
+ { JUNIPER_IFML_CONTROLLER, "Controller" },
+ { JUNIPER_IFML_ETHERNET_VPLS, "VPLS" },
+ { JUNIPER_IFML_ETHERNET_VLAN_VPLS, "VLAN-VPLS" },
+ { JUNIPER_IFML_ETHERNET_EXTENDED_VLAN_VPLS, "Extended-VLAN-VPLS" },
+ { JUNIPER_IFML_LT, "Logical-tunnel" },
+ { JUNIPER_IFML_SERVICES, "General-Services" },
+ { JUNIPER_IFML_PPPOE, "PPPoE" },
+ { JUNIPER_IFML_ETHERNET_FLEX, "Flexible-Ethernet-Services" },
+ { JUNIPER_IFML_FRAMERELAY_FLEX, "Flexible-FrameRelay" },
+ { JUNIPER_IFML_COLLECTOR, "Flow-collection" },
+ { JUNIPER_IFML_PICPEER, "PIC Peer" },
+ { JUNIPER_IFML_DFC, "Dynamic-Flow-Capture" },
+ {0, NULL}
+};
+
+#define JUNIPER_IFLE_ATM_SNAP 2
+#define JUNIPER_IFLE_ATM_NLPID 3
+#define JUNIPER_IFLE_ATM_VCMUX 4
+#define JUNIPER_IFLE_ATM_LLC 5
+#define JUNIPER_IFLE_ATM_PPP_VCMUX 6
+#define JUNIPER_IFLE_ATM_PPP_LLC 7
+#define JUNIPER_IFLE_ATM_PPP_FUNI 8
+#define JUNIPER_IFLE_ATM_CCC 9
+#define JUNIPER_IFLE_FR_NLPID 10
+#define JUNIPER_IFLE_FR_SNAP 11
+#define JUNIPER_IFLE_FR_PPP 12
+#define JUNIPER_IFLE_FR_CCC 13
+#define JUNIPER_IFLE_ENET2 14
+#define JUNIPER_IFLE_IEEE8023_SNAP 15
+#define JUNIPER_IFLE_IEEE8023_LLC 16
+#define JUNIPER_IFLE_PPP 17
+#define JUNIPER_IFLE_CISCOHDLC 18
+#define JUNIPER_IFLE_PPP_CCC 19
+#define JUNIPER_IFLE_IPIP_NULL 20
+#define JUNIPER_IFLE_PIM_NULL 21
+#define JUNIPER_IFLE_GRE_NULL 22
+#define JUNIPER_IFLE_GRE_PPP 23
+#define JUNIPER_IFLE_PIMD_DECAPS 24
+#define JUNIPER_IFLE_CISCOHDLC_CCC 25
+#define JUNIPER_IFLE_ATM_CISCO_NLPID 26
+#define JUNIPER_IFLE_VLAN_CCC 27
+#define JUNIPER_IFLE_MLPPP 28
+#define JUNIPER_IFLE_MLFR 29
+#define JUNIPER_IFLE_LSI_NULL 30
+#define JUNIPER_IFLE_AGGREGATE_UNUSED 31
+#define JUNIPER_IFLE_ATM_CELLRELAY_CCC 32
+#define JUNIPER_IFLE_CRYPTO 33
+#define JUNIPER_IFLE_GGSN 34
+#define JUNIPER_IFLE_ATM_TCC 35
+#define JUNIPER_IFLE_FR_TCC 36
+#define JUNIPER_IFLE_PPP_TCC 37
+#define JUNIPER_IFLE_CISCOHDLC_TCC 38
+#define JUNIPER_IFLE_ETHERNET_CCC 39
+#define JUNIPER_IFLE_VT 40
+#define JUNIPER_IFLE_ATM_EOA_LLC 41
+#define JUNIPER_IFLE_EXTENDED_VLAN_CCC 42
+#define JUNIPER_IFLE_ATM_SNAP_TCC 43
+#define JUNIPER_IFLE_MONITOR 44
+#define JUNIPER_IFLE_ETHERNET_TCC 45
+#define JUNIPER_IFLE_VLAN_TCC 46
+#define JUNIPER_IFLE_EXTENDED_VLAN_TCC 47
+#define JUNIPER_IFLE_MFR 48
+#define JUNIPER_IFLE_ETHERNET_VPLS 49
+#define JUNIPER_IFLE_ETHERNET_VLAN_VPLS 50
+#define JUNIPER_IFLE_ETHERNET_EXTENDED_VLAN_VPLS 51
+#define JUNIPER_IFLE_SERVICES 52
+#define JUNIPER_IFLE_ATM_ETHER_VPLS_ATM_LLC 53
+#define JUNIPER_IFLE_FR_PORT_CCC 54
+#define JUNIPER_IFLE_ATM_MLPPP_LLC 55
+#define JUNIPER_IFLE_ATM_EOA_CCC 56
+#define JUNIPER_IFLE_LT_VLAN 57
+#define JUNIPER_IFLE_COLLECTOR 58
+#define JUNIPER_IFLE_AGGREGATOR 59
+#define JUNIPER_IFLE_LAPD 60
+#define JUNIPER_IFLE_ATM_PPPOE_LLC 61
+#define JUNIPER_IFLE_ETHERNET_PPPOE 62
+#define JUNIPER_IFLE_PPPOE 63
+#define JUNIPER_IFLE_PPP_SUBORDINATE 64
+#define JUNIPER_IFLE_CISCOHDLC_SUBORDINATE 65
+#define JUNIPER_IFLE_DFC 66
+#define JUNIPER_IFLE_PICPEER 67
+
+static const struct tok juniper_ifle_values[] = {
+ { JUNIPER_IFLE_AGGREGATOR, "Aggregator" },
+ { JUNIPER_IFLE_ATM_CCC, "CCC over ATM" },
+ { JUNIPER_IFLE_ATM_CELLRELAY_CCC, "ATM CCC Cell Relay" },
+ { JUNIPER_IFLE_ATM_CISCO_NLPID, "CISCO compatible NLPID" },
+ { JUNIPER_IFLE_ATM_EOA_CCC, "Ethernet over ATM CCC" },
+ { JUNIPER_IFLE_ATM_EOA_LLC, "Ethernet over ATM LLC" },
+ { JUNIPER_IFLE_ATM_ETHER_VPLS_ATM_LLC, "Ethernet VPLS over ATM LLC" },
+ { JUNIPER_IFLE_ATM_LLC, "ATM LLC" },
+ { JUNIPER_IFLE_ATM_MLPPP_LLC, "MLPPP over ATM LLC" },
+ { JUNIPER_IFLE_ATM_NLPID, "ATM NLPID" },
+ { JUNIPER_IFLE_ATM_PPPOE_LLC, "PPPoE over ATM LLC" },
+ { JUNIPER_IFLE_ATM_PPP_FUNI, "PPP over FUNI" },
+ { JUNIPER_IFLE_ATM_PPP_LLC, "PPP over ATM LLC" },
+ { JUNIPER_IFLE_ATM_PPP_VCMUX, "PPP over ATM VCMUX" },
+ { JUNIPER_IFLE_ATM_SNAP, "ATM SNAP" },
+ { JUNIPER_IFLE_ATM_SNAP_TCC, "ATM SNAP TCC" },
+ { JUNIPER_IFLE_ATM_TCC, "ATM VCMUX TCC" },
+ { JUNIPER_IFLE_ATM_VCMUX, "ATM VCMUX" },
+ { JUNIPER_IFLE_CISCOHDLC, "C-HDLC" },
+ { JUNIPER_IFLE_CISCOHDLC_CCC, "C-HDLC CCC" },
+ { JUNIPER_IFLE_CISCOHDLC_SUBORDINATE, "C-HDLC via dialer" },
+ { JUNIPER_IFLE_CISCOHDLC_TCC, "C-HDLC TCC" },
+ { JUNIPER_IFLE_COLLECTOR, "Collector" },
+ { JUNIPER_IFLE_CRYPTO, "Crypto" },
+ { JUNIPER_IFLE_ENET2, "Ethernet" },
+ { JUNIPER_IFLE_ETHERNET_CCC, "Ethernet CCC" },
+ { JUNIPER_IFLE_ETHERNET_EXTENDED_VLAN_VPLS, "Extended VLAN VPLS" },
+ { JUNIPER_IFLE_ETHERNET_PPPOE, "PPPoE over Ethernet" },
+ { JUNIPER_IFLE_ETHERNET_TCC, "Ethernet TCC" },
+ { JUNIPER_IFLE_ETHERNET_VLAN_VPLS, "VLAN VPLS" },
+ { JUNIPER_IFLE_ETHERNET_VPLS, "VPLS" },
+ { JUNIPER_IFLE_EXTENDED_VLAN_CCC, "Extended VLAN CCC" },
+ { JUNIPER_IFLE_EXTENDED_VLAN_TCC, "Extended VLAN TCC" },
+ { JUNIPER_IFLE_FR_CCC, "FR CCC" },
+ { JUNIPER_IFLE_FR_NLPID, "FR NLPID" },
+ { JUNIPER_IFLE_FR_PORT_CCC, "FR CCC" },
+ { JUNIPER_IFLE_FR_PPP, "FR PPP" },
+ { JUNIPER_IFLE_FR_SNAP, "FR SNAP" },
+ { JUNIPER_IFLE_FR_TCC, "FR TCC" },
+ { JUNIPER_IFLE_GGSN, "GGSN" },
+ { JUNIPER_IFLE_GRE_NULL, "GRE NULL" },
+ { JUNIPER_IFLE_GRE_PPP, "PPP over GRE" },
+ { JUNIPER_IFLE_IPIP_NULL, "IPIP" },
+ { JUNIPER_IFLE_LAPD, "LAPD" },
+ { JUNIPER_IFLE_LSI_NULL, "LSI Null" },
+ { JUNIPER_IFLE_LT_VLAN, "LT VLAN" },
+ { JUNIPER_IFLE_MFR, "MFR" },
+ { JUNIPER_IFLE_MLFR, "MLFR" },
+ { JUNIPER_IFLE_MLPPP, "MLPPP" },
+ { JUNIPER_IFLE_MONITOR, "Monitor" },
+ { JUNIPER_IFLE_PIMD_DECAPS, "PIMd" },
+ { JUNIPER_IFLE_PIM_NULL, "PIM Null" },
+ { JUNIPER_IFLE_PPP, "PPP" },
+ { JUNIPER_IFLE_PPPOE, "PPPoE" },
+ { JUNIPER_IFLE_PPP_CCC, "PPP CCC" },
+ { JUNIPER_IFLE_PPP_SUBORDINATE, "" },
+ { JUNIPER_IFLE_PPP_TCC, "PPP TCC" },
+ { JUNIPER_IFLE_SERVICES, "General Services" },
+ { JUNIPER_IFLE_VLAN_CCC, "VLAN CCC" },
+ { JUNIPER_IFLE_VLAN_TCC, "VLAN TCC" },
+ { JUNIPER_IFLE_VT, "VT" },
+ {0, NULL}
+};
+
+struct juniper_cookie_table_t {
+ uint32_t pictype; /* pic type */
+ uint8_t cookie_len; /* cookie len */
+ const char *s; /* pic name */
+};
+
+static const struct juniper_cookie_table_t juniper_cookie_table[] = {
+#ifdef DLT_JUNIPER_ATM1
+ { DLT_JUNIPER_ATM1, 4, "ATM1"},
+#endif
+#ifdef DLT_JUNIPER_ATM2
+ { DLT_JUNIPER_ATM2, 8, "ATM2"},
+#endif
+#ifdef DLT_JUNIPER_MLPPP
+ { DLT_JUNIPER_MLPPP, 2, "MLPPP"},
+#endif
+#ifdef DLT_JUNIPER_MLFR
+ { DLT_JUNIPER_MLFR, 2, "MLFR"},
+#endif
+#ifdef DLT_JUNIPER_MFR
+ { DLT_JUNIPER_MFR, 4, "MFR"},
+#endif
+#ifdef DLT_JUNIPER_PPPOE
+ { DLT_JUNIPER_PPPOE, 0, "PPPoE"},
+#endif
+#ifdef DLT_JUNIPER_PPPOE_ATM
+ { DLT_JUNIPER_PPPOE_ATM, 0, "PPPoE ATM"},
+#endif
+#ifdef DLT_JUNIPER_GGSN
+ { DLT_JUNIPER_GGSN, 8, "GGSN"},
+#endif
+#ifdef DLT_JUNIPER_MONITOR
+ { DLT_JUNIPER_MONITOR, 8, "MONITOR"},
+#endif
+#ifdef DLT_JUNIPER_SERVICES
+ { DLT_JUNIPER_SERVICES, 8, "AS"},
+#endif
+#ifdef DLT_JUNIPER_ES
+ { DLT_JUNIPER_ES, 0, "ES"},
+#endif
+ { 0, 0, NULL }
+};
+
+struct juniper_l2info_t {
+ uint32_t length;
+ uint32_t caplen;
+ uint32_t pictype;
+ uint8_t direction;
+ u_int header_len;
+ uint8_t cookie_len;
+ uint8_t cookie_type;
+ uint8_t cookie[8];
+ u_int bundle;
+ uint16_t proto;
+ uint8_t flags;
+};
+
+#define LS_COOKIE_ID 0x54
+#define AS_COOKIE_ID 0x47
+#define LS_MLFR_COOKIE_LEN 4
+#define ML_MLFR_COOKIE_LEN 2
+#define LS_MFR_COOKIE_LEN 6
+#define ATM1_COOKIE_LEN 4
+#define ATM2_COOKIE_LEN 8
+
+#define ATM2_PKT_TYPE_MASK 0x70
+#define ATM2_GAP_COUNT_MASK 0x3F
+
+#define JUNIPER_PROTO_NULL 1
+#define JUNIPER_PROTO_IPV4 2
+#define JUNIPER_PROTO_IPV6 6
+
+#define MFR_BE_MASK 0xc0
+
+#ifdef DLT_JUNIPER_GGSN
+static const struct tok juniper_protocol_values[] = {
+ { JUNIPER_PROTO_NULL, "Null" },
+ { JUNIPER_PROTO_IPV4, "IPv4" },
+ { JUNIPER_PROTO_IPV6, "IPv6" },
+ { 0, NULL}
+};
+#endif
+
+static int ip_heuristic_guess(netdissect_options *, const u_char *, u_int);
+#ifdef DLT_JUNIPER_ATM2
+static int juniper_ppp_heuristic_guess(netdissect_options *, const u_char *, u_int);
+#endif
+static int juniper_parse_header(netdissect_options *, const u_char *, const struct pcap_pkthdr *, struct juniper_l2info_t *);
+
+#ifdef DLT_JUNIPER_GGSN
+void
+juniper_ggsn_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+ struct juniper_ggsn_header {
+ nd_uint8_t svc_id;
+ nd_uint8_t flags_len;
+ nd_uint8_t proto;
+ nd_uint8_t flags;
+ nd_uint16_t vlan_id;
+ nd_byte res[2];
+ };
+ const struct juniper_ggsn_header *gh;
+ uint8_t proto;
+
+ ndo->ndo_protocol = "juniper_ggsn";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_GGSN;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ gh = (struct juniper_ggsn_header *)&l2info.cookie;
+
+ ND_TCHECK_SIZE(gh);
+ proto = GET_U_1(gh->proto);
+ if (ndo->ndo_eflag) {
+ ND_PRINT("proto %s (%u), vlan %u: ",
+ tok2str(juniper_protocol_values,"Unknown",proto),
+ proto,
+ GET_BE_U_2(gh->vlan_id));
+ }
+
+ switch (proto) {
+ case JUNIPER_PROTO_IPV4:
+ ip_print(ndo, p, l2info.length);
+ break;
+ case JUNIPER_PROTO_IPV6:
+ ip6_print(ndo, p, l2info.length);
+ break;
+ default:
+ if (!ndo->ndo_eflag)
+ ND_PRINT("unknown GGSN proto (%u)", proto);
+ }
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_ES
+void
+juniper_es_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+ struct juniper_ipsec_header {
+ nd_uint16_t sa_index;
+ nd_uint8_t ttl;
+ nd_uint8_t type;
+ nd_uint32_t spi;
+ nd_ipv4 src_ip;
+ nd_ipv4 dst_ip;
+ };
+ u_int rewrite_len,es_type_bundle;
+ const struct juniper_ipsec_header *ih;
+
+ ndo->ndo_protocol = "juniper_es";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_ES;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ ih = (const struct juniper_ipsec_header *)p;
+
+ ND_TCHECK_SIZE(ih);
+ switch (GET_U_1(ih->type)) {
+ case JUNIPER_IPSEC_O_ESP_ENCRYPT_ESP_AUTHEN_TYPE:
+ case JUNIPER_IPSEC_O_ESP_ENCRYPT_AH_AUTHEN_TYPE:
+ rewrite_len = 0;
+ es_type_bundle = 1;
+ break;
+ case JUNIPER_IPSEC_O_ESP_AUTHENTICATION_TYPE:
+ case JUNIPER_IPSEC_O_AH_AUTHENTICATION_TYPE:
+ case JUNIPER_IPSEC_O_ESP_ENCRYPTION_TYPE:
+ rewrite_len = 16;
+ es_type_bundle = 0;
+ break;
+ default:
+ ND_PRINT("ES Invalid type %u, length %u",
+ GET_U_1(ih->type),
+ l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ l2info.length-=rewrite_len;
+ p+=rewrite_len;
+
+ if (ndo->ndo_eflag) {
+ if (!es_type_bundle) {
+ ND_PRINT("ES SA, index %u, ttl %u type %s (%u), spi %u, Tunnel %s > %s, length %u\n",
+ GET_BE_U_2(ih->sa_index),
+ GET_U_1(ih->ttl),
+ tok2str(juniper_ipsec_type_values,"Unknown",GET_U_1(ih->type)),
+ GET_U_1(ih->type),
+ GET_BE_U_4(ih->spi),
+ GET_IPADDR_STRING(ih->src_ip),
+ GET_IPADDR_STRING(ih->dst_ip),
+ l2info.length);
+ } else {
+ ND_PRINT("ES SA, index %u, ttl %u type %s (%u), length %u\n",
+ GET_BE_U_2(ih->sa_index),
+ GET_U_1(ih->ttl),
+ tok2str(juniper_ipsec_type_values,"Unknown",GET_U_1(ih->type)),
+ GET_U_1(ih->type),
+ l2info.length);
+ }
+ }
+
+ ip_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_MONITOR
+void
+juniper_monitor_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+ struct juniper_monitor_header {
+ nd_uint8_t pkt_type;
+ nd_byte padding;
+ nd_uint16_t iif;
+ nd_uint32_t service_id;
+ };
+ const struct juniper_monitor_header *mh;
+
+ ndo->ndo_protocol = "juniper_monitor";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_MONITOR;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ mh = (const struct juniper_monitor_header *)p;
+
+ ND_TCHECK_SIZE(mh);
+ if (ndo->ndo_eflag)
+ ND_PRINT("service-id %u, iif %u, pkt-type %u: ",
+ GET_BE_U_4(mh->service_id),
+ GET_BE_U_2(mh->iif),
+ GET_U_1(mh->pkt_type));
+
+ /* no proto field - lets guess by first byte of IP header*/
+ ip_heuristic_guess (ndo, p, l2info.length);
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_SERVICES
+void
+juniper_services_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+ struct juniper_services_header {
+ nd_uint8_t svc_id;
+ nd_uint8_t flags_len;
+ nd_uint16_t svc_set_id;
+ nd_byte pad;
+ nd_uint24_t dir_iif;
+ };
+ const struct juniper_services_header *sh;
+
+ ndo->ndo_protocol = "juniper_services";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_SERVICES;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ sh = (const struct juniper_services_header *)p;
+
+ ND_TCHECK_SIZE(sh);
+ if (ndo->ndo_eflag)
+ ND_PRINT("service-id %u flags 0x%02x service-set-id 0x%04x iif %u: ",
+ GET_U_1(sh->svc_id),
+ GET_U_1(sh->flags_len),
+ GET_BE_U_2(sh->svc_set_id),
+ GET_BE_U_3(sh->dir_iif));
+
+ /* no proto field - lets guess by first byte of IP header*/
+ ip_heuristic_guess (ndo, p, l2info.length);
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_PPPOE
+void
+juniper_pppoe_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_pppoe";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_PPPOE;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ /* this DLT contains nothing but raw ethernet frames */
+ ether_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_ETHER
+void
+juniper_ether_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_ether";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_ETHER;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ /* this DLT contains nothing but raw Ethernet frames */
+ ndo->ndo_ll_hdr_len +=
+ l2info.header_len +
+ ether_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
+}
+#endif
+
+#ifdef DLT_JUNIPER_PPP
+void
+juniper_ppp_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_ppp";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_PPP;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ /* this DLT contains nothing but raw ppp frames */
+ ppp_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_FRELAY
+void
+juniper_frelay_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_frelay";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_FRELAY;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ /* this DLT contains nothing but raw frame-relay frames */
+ fr_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_CHDLC
+void
+juniper_chdlc_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_chdlc";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_CHDLC;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+ /* this DLT contains nothing but raw c-hdlc frames */
+ chdlc_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_PPPOE_ATM
+void
+juniper_pppoe_atm_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+ uint16_t extracted_ethertype;
+
+ ndo->ndo_protocol = "juniper_pppoe_atm";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_PPPOE_ATM;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+
+ extracted_ethertype = GET_BE_U_2(p);
+ /* this DLT contains nothing but raw PPPoE frames,
+ * prepended with a type field*/
+ if (ethertype_print(ndo, extracted_ethertype,
+ p+ETHERTYPE_LEN,
+ l2info.length-ETHERTYPE_LEN,
+ l2info.caplen-ETHERTYPE_LEN,
+ NULL, NULL) == 0)
+ /* ether_type not known, probably it wasn't one */
+ ND_PRINT("unknown ethertype 0x%04x", extracted_ethertype);
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_MLPPP
+void
+juniper_mlppp_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_mlppp";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_MLPPP;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ /* suppress Bundle-ID if frame was captured on a child-link
+ * best indicator if the cookie looks like a proto */
+ if (ndo->ndo_eflag &&
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ EXTRACT_BE_U_2(&l2info.cookie) != PPP_OSI &&
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ EXTRACT_BE_U_2(&l2info.cookie) != (PPP_ADDRESS << 8 | PPP_CONTROL))
+ ND_PRINT("Bundle-ID %u: ", l2info.bundle);
+
+ p+=l2info.header_len;
+
+ /* first try the LSQ protos */
+ switch(l2info.proto) {
+ case JUNIPER_LSQ_L3_PROTO_IPV4:
+ /* IP traffic going to the RE would not have a cookie
+ * -> this must be incoming IS-IS over PPP
+ */
+ if (l2info.cookie[4] == (JUNIPER_LSQ_COOKIE_RE|JUNIPER_LSQ_COOKIE_DIR))
+ ppp_print(ndo, p, l2info.length);
+ else
+ ip_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ case JUNIPER_LSQ_L3_PROTO_IPV6:
+ ip6_print(ndo, p,l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ case JUNIPER_LSQ_L3_PROTO_MPLS:
+ mpls_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ case JUNIPER_LSQ_L3_PROTO_ISO:
+ isoclns_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ default:
+ break;
+ }
+
+ /* zero length cookie ? */
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ switch (EXTRACT_BE_U_2(&l2info.cookie)) {
+ case PPP_OSI:
+ ppp_print(ndo, p - 2, l2info.length + 2);
+ break;
+ case (PPP_ADDRESS << 8 | PPP_CONTROL): /* fall through */
+ default:
+ ppp_print(ndo, p, l2info.length);
+ break;
+ }
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+
+#ifdef DLT_JUNIPER_MFR
+void
+juniper_mfr_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_mfr";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_MFR;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+
+ /* child-link ? */
+ if (l2info.cookie_len == 0) {
+ mfr_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ /* first try the LSQ protos */
+ if (l2info.cookie_len == AS_PIC_COOKIE_LEN) {
+ switch(l2info.proto) {
+ case JUNIPER_LSQ_L3_PROTO_IPV4:
+ ip_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ case JUNIPER_LSQ_L3_PROTO_IPV6:
+ ip6_print(ndo, p,l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ case JUNIPER_LSQ_L3_PROTO_MPLS:
+ mpls_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ case JUNIPER_LSQ_L3_PROTO_ISO:
+ isoclns_print(ndo, p, l2info.length);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ default:
+ break;
+ }
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ /* suppress Bundle-ID if frame was captured on a child-link */
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ if (ndo->ndo_eflag && EXTRACT_BE_U_4(l2info.cookie) != 1)
+ ND_PRINT("Bundle-ID %u, ", l2info.bundle);
+ switch (l2info.proto) {
+ case (LLCSAP_ISONS<<8 | LLCSAP_ISONS):
+ isoclns_print(ndo, p + 1, l2info.length - 1);
+ break;
+ case (LLC_UI<<8 | NLPID_Q933):
+ case (LLC_UI<<8 | NLPID_IP):
+ case (LLC_UI<<8 | NLPID_IP6):
+ /* pass IP{4,6} to the OSI layer for proper link-layer printing */
+ isoclns_print(ndo, p - 1, l2info.length + 1);
+ break;
+ default:
+ ND_PRINT("unknown protocol 0x%04x, length %u", l2info.proto, l2info.length);
+ }
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+#ifdef DLT_JUNIPER_MLFR
+void
+juniper_mlfr_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_mlfr";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_MLFR;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+
+ /* suppress Bundle-ID if frame was captured on a child-link */
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ if (ndo->ndo_eflag && EXTRACT_BE_U_4(l2info.cookie) != 1)
+ ND_PRINT("Bundle-ID %u, ", l2info.bundle);
+ switch (l2info.proto) {
+ case (LLC_UI):
+ case (LLC_UI<<8):
+ isoclns_print(ndo, p, l2info.length);
+ break;
+ case (LLC_UI<<8 | NLPID_Q933):
+ case (LLC_UI<<8 | NLPID_IP):
+ case (LLC_UI<<8 | NLPID_IP6):
+ /* pass IP{4,6} to the OSI layer for proper link-layer printing */
+ isoclns_print(ndo, p - 1, l2info.length + 1);
+ break;
+ default:
+ ND_PRINT("unknown protocol 0x%04x, length %u", l2info.proto, l2info.length);
+ }
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+/*
+ * ATM1 PIC cookie format
+ *
+ * +-----+-------------------------+-------------------------------+
+ * |fmtid| vc index | channel ID |
+ * +-----+-------------------------+-------------------------------+
+ */
+
+#ifdef DLT_JUNIPER_ATM1
+void
+juniper_atm1_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ int llc_hdrlen;
+
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_atm1";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_ATM1;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+
+ if (l2info.cookie[0] == 0x80) { /* OAM cell ? */
+ oam_print(ndo, p, l2info.length, ATM_OAM_NOHEC);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ if (GET_BE_U_3(p) == 0xfefe03 || /* NLPID encaps ? */
+ GET_BE_U_3(p) == 0xaaaa03) { /* SNAP encaps ? */
+
+ llc_hdrlen = llc_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
+ if (llc_hdrlen > 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+ }
+
+ if (GET_U_1(p) == 0x03) { /* Cisco style NLPID encaps ? */
+ isoclns_print(ndo, p + 1, l2info.length - 1);
+ /* FIXME check if frame was recognized */
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ if (ip_heuristic_guess(ndo, p, l2info.length) != 0) { /* last try - vcmux encaps ? */
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+#endif
+
+/*
+ * ATM2 PIC cookie format
+ *
+ * +-------------------------------+---------+---+-----+-----------+
+ * | channel ID | reserv |AAL| CCRQ| gap cnt |
+ * +-------------------------------+---------+---+-----+-----------+
+ */
+
+#ifdef DLT_JUNIPER_ATM2
+void
+juniper_atm2_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ int llc_hdrlen;
+
+ struct juniper_l2info_t l2info;
+
+ ndo->ndo_protocol = "juniper_atm2";
+ memset(&l2info, 0, sizeof(l2info));
+ l2info.pictype = DLT_JUNIPER_ATM2;
+ if (juniper_parse_header(ndo, p, h, &l2info) == 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ p+=l2info.header_len;
+
+ if (l2info.cookie[7] & ATM2_PKT_TYPE_MASK) { /* OAM cell ? */
+ oam_print(ndo, p, l2info.length, ATM_OAM_NOHEC);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ if (GET_BE_U_3(p) == 0xfefe03 || /* NLPID encaps ? */
+ GET_BE_U_3(p) == 0xaaaa03) { /* SNAP encaps ? */
+
+ llc_hdrlen = llc_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
+ if (llc_hdrlen > 0) {
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+ }
+
+ if (l2info.direction != JUNIPER_BPF_PKT_IN && /* ether-over-1483 encaps ? */
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ (EXTRACT_BE_U_4(l2info.cookie) & ATM2_GAP_COUNT_MASK)) {
+ ether_print(ndo, p, l2info.length, l2info.caplen, NULL, NULL);
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ if (GET_U_1(p) == 0x03) { /* Cisco style NLPID encaps ? */
+ isoclns_print(ndo, p + 1, l2info.length - 1);
+ /* FIXME check if frame was recognized */
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ if(juniper_ppp_heuristic_guess(ndo, p, l2info.length) != 0) { /* PPPoA vcmux encaps ? */
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ if (ip_heuristic_guess(ndo, p, l2info.length) != 0) { /* last try - vcmux encaps ? */
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+ return;
+ }
+
+ ndo->ndo_ll_hdr_len += l2info.header_len;
+}
+
+/* try to guess, based on all PPP protos that are supported in
+ * a juniper router if the payload data is encapsulated using PPP */
+static int
+juniper_ppp_heuristic_guess(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ switch(GET_BE_U_2(p)) {
+ case PPP_IP :
+ case PPP_OSI :
+ case PPP_MPLS_UCAST :
+ case PPP_MPLS_MCAST :
+ case PPP_IPCP :
+ case PPP_OSICP :
+ case PPP_MPLSCP :
+ case PPP_LCP :
+ case PPP_PAP :
+ case PPP_CHAP :
+ case PPP_ML :
+ case PPP_IPV6 :
+ case PPP_IPV6CP :
+ ppp_print(ndo, p, length);
+ break;
+
+ default:
+ return 0; /* did not find a ppp header */
+ break;
+ }
+ return 1; /* we printed a ppp packet */
+}
+#endif
+
+static int
+ip_heuristic_guess(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ switch(GET_U_1(p)) {
+ case 0x45:
+ case 0x46:
+ case 0x47:
+ case 0x48:
+ case 0x49:
+ case 0x4a:
+ case 0x4b:
+ case 0x4c:
+ case 0x4d:
+ case 0x4e:
+ case 0x4f:
+ ip_print(ndo, p, length);
+ break;
+ case 0x60:
+ case 0x61:
+ case 0x62:
+ case 0x63:
+ case 0x64:
+ case 0x65:
+ case 0x66:
+ case 0x67:
+ case 0x68:
+ case 0x69:
+ case 0x6a:
+ case 0x6b:
+ case 0x6c:
+ case 0x6d:
+ case 0x6e:
+ case 0x6f:
+ ip6_print(ndo, p, length);
+ break;
+ default:
+ return 0; /* did not find a ip header */
+ break;
+ }
+ return 1; /* we printed an v4/v6 packet */
+}
+
+static int
+juniper_read_tlv_value(netdissect_options *ndo,
+ const u_char *p, u_int tlv_type, u_int tlv_len)
+{
+ int tlv_value;
+
+ /* TLVs < 128 are little endian encoded */
+ if (tlv_type < 128) {
+ switch (tlv_len) {
+ case 1:
+ tlv_value = GET_U_1(p);
+ break;
+ case 2:
+ tlv_value = GET_LE_U_2(p);
+ break;
+ case 3:
+ tlv_value = GET_LE_U_3(p);
+ break;
+ case 4:
+ tlv_value = GET_LE_U_4(p);
+ break;
+ default:
+ tlv_value = -1;
+ break;
+ }
+ } else {
+ /* TLVs >= 128 are big endian encoded */
+ switch (tlv_len) {
+ case 1:
+ tlv_value = GET_U_1(p);
+ break;
+ case 2:
+ tlv_value = GET_BE_U_2(p);
+ break;
+ case 3:
+ tlv_value = GET_BE_U_3(p);
+ break;
+ case 4:
+ tlv_value = GET_BE_U_4(p);
+ break;
+ default:
+ tlv_value = -1;
+ break;
+ }
+ }
+ return tlv_value;
+}
+
+static int
+juniper_parse_header(netdissect_options *ndo,
+ const u_char *p, const struct pcap_pkthdr *h, struct juniper_l2info_t *l2info)
+{
+ const struct juniper_cookie_table_t *lp = juniper_cookie_table;
+ u_int idx, jnx_ext_len, jnx_header_len = 0;
+ uint8_t tlv_type,tlv_len;
+#ifdef DLT_JUNIPER_ATM2
+ uint32_t control_word;
+#endif
+ int tlv_value;
+ const u_char *tptr;
+
+
+ l2info->header_len = 0;
+ l2info->cookie_len = 0;
+ l2info->proto = 0;
+
+
+ l2info->length = h->len;
+ l2info->caplen = h->caplen;
+ l2info->flags = GET_U_1(p + 3);
+ l2info->direction = GET_U_1(p + 3) & JUNIPER_BPF_PKT_IN;
+
+ if (GET_BE_U_3(p) != JUNIPER_MGC_NUMBER) { /* magic number found ? */
+ ND_PRINT("no magic-number found!");
+ return 0;
+ }
+
+ if (ndo->ndo_eflag) /* print direction */
+ ND_PRINT("%3s ", tok2str(juniper_direction_values, "---", l2info->direction));
+
+ /* magic number + flags */
+ jnx_header_len = 4;
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n\tJuniper PCAP Flags [%s]",
+ bittok2str(jnx_flag_values, "none", l2info->flags));
+
+ /* extensions present ? - calculate how much bytes to skip */
+ if ((l2info->flags & JUNIPER_BPF_EXT ) == JUNIPER_BPF_EXT ) {
+
+ tptr = p+jnx_header_len;
+
+ /* ok to read extension length ? */
+ jnx_ext_len = GET_BE_U_2(tptr);
+ jnx_header_len += 2;
+ tptr +=2;
+
+ /* nail up the total length -
+ * just in case something goes wrong
+ * with TLV parsing */
+ jnx_header_len += jnx_ext_len;
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT(", PCAP Extension(s) total length %u", jnx_ext_len);
+
+ ND_TCHECK_LEN(tptr, jnx_ext_len);
+ while (jnx_ext_len > JUNIPER_EXT_TLV_OVERHEAD) {
+ tlv_type = GET_U_1(tptr);
+ tptr++;
+ tlv_len = GET_U_1(tptr);
+ tptr++;
+ tlv_value = 0;
+
+ /* sanity checks */
+ if (tlv_type == 0 || tlv_len == 0)
+ break;
+ if (tlv_len+JUNIPER_EXT_TLV_OVERHEAD > jnx_ext_len)
+ goto trunc;
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n\t %s Extension TLV #%u, length %u, value ",
+ tok2str(jnx_ext_tlv_values,"Unknown",tlv_type),
+ tlv_type,
+ tlv_len);
+
+ tlv_value = juniper_read_tlv_value(ndo, tptr, tlv_type, tlv_len);
+ switch (tlv_type) {
+ case JUNIPER_EXT_TLV_IFD_NAME:
+ /* FIXME */
+ break;
+ case JUNIPER_EXT_TLV_IFD_MEDIATYPE:
+ case JUNIPER_EXT_TLV_TTP_IFD_MEDIATYPE:
+ if (tlv_value != -1) {
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("%s (%u)",
+ tok2str(juniper_ifmt_values, "Unknown", tlv_value),
+ tlv_value);
+ }
+ break;
+ case JUNIPER_EXT_TLV_IFL_ENCAPS:
+ case JUNIPER_EXT_TLV_TTP_IFL_ENCAPS:
+ if (tlv_value != -1) {
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("%s (%u)",
+ tok2str(juniper_ifle_values, "Unknown", tlv_value),
+ tlv_value);
+ }
+ break;
+ case JUNIPER_EXT_TLV_IFL_IDX: /* fall through */
+ case JUNIPER_EXT_TLV_IFL_UNIT:
+ case JUNIPER_EXT_TLV_IFD_IDX:
+ default:
+ if (tlv_value != -1) {
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("%u", tlv_value);
+ }
+ break;
+ }
+
+ tptr+=tlv_len;
+ jnx_ext_len -= tlv_len+JUNIPER_EXT_TLV_OVERHEAD;
+ }
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n\t-----original packet-----\n\t");
+ }
+
+ if ((l2info->flags & JUNIPER_BPF_NO_L2 ) == JUNIPER_BPF_NO_L2 ) {
+ if (ndo->ndo_eflag)
+ ND_PRINT("no-L2-hdr, ");
+
+ /* there is no link-layer present -
+ * perform the v4/v6 heuristics
+ * to figure out what it is
+ */
+ ND_TCHECK_1(p + (jnx_header_len + 4));
+ if (ip_heuristic_guess(ndo, p + jnx_header_len + 4,
+ l2info->length - (jnx_header_len + 4)) == 0)
+ ND_PRINT("no IP-hdr found!");
+
+ l2info->header_len=jnx_header_len+4;
+ return 0; /* stop parsing the output further */
+
+ }
+ l2info->header_len = jnx_header_len;
+ p+=l2info->header_len;
+ l2info->length -= l2info->header_len;
+ l2info->caplen -= l2info->header_len;
+
+ /* search through the cookie table and copy values matching for our PIC type */
+ ND_TCHECK_1(p);
+ while (lp->s != NULL) {
+ if (lp->pictype == l2info->pictype) {
+
+ l2info->cookie_len += lp->cookie_len;
+
+ switch (GET_U_1(p)) {
+ case LS_COOKIE_ID:
+ l2info->cookie_type = LS_COOKIE_ID;
+ l2info->cookie_len += 2;
+ break;
+ case AS_COOKIE_ID:
+ l2info->cookie_type = AS_COOKIE_ID;
+ l2info->cookie_len = 8;
+ break;
+
+ default:
+ l2info->bundle = l2info->cookie[0];
+ break;
+ }
+
+
+#ifdef DLT_JUNIPER_MFR
+ /* MFR child links don't carry cookies */
+ if (l2info->pictype == DLT_JUNIPER_MFR &&
+ (GET_U_1(p) & MFR_BE_MASK) == MFR_BE_MASK) {
+ l2info->cookie_len = 0;
+ }
+#endif
+
+ l2info->header_len += l2info->cookie_len;
+ l2info->length -= l2info->cookie_len;
+ l2info->caplen -= l2info->cookie_len;
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("%s-PIC, cookie-len %u",
+ lp->s,
+ l2info->cookie_len);
+
+ if (l2info->cookie_len > 8) {
+ nd_print_invalid(ndo);
+ return 0;
+ }
+
+ if (l2info->cookie_len > 0) {
+ ND_TCHECK_LEN(p, l2info->cookie_len);
+ if (ndo->ndo_eflag)
+ ND_PRINT(", cookie 0x");
+ for (idx = 0; idx < l2info->cookie_len; idx++) {
+ l2info->cookie[idx] = GET_U_1(p + idx); /* copy cookie data */
+ if (ndo->ndo_eflag) ND_PRINT("%02x", GET_U_1(p + idx));
+ }
+ }
+
+ if (ndo->ndo_eflag) ND_PRINT(": "); /* print demarc b/w L2/L3*/
+
+
+ l2info->proto = GET_BE_U_2(p + l2info->cookie_len);
+ break;
+ }
+ ++lp;
+ }
+ p+=l2info->cookie_len;
+
+ /* DLT_ specific parsing */
+ switch(l2info->pictype) {
+#ifdef DLT_JUNIPER_MLPPP
+ case DLT_JUNIPER_MLPPP:
+ switch (l2info->cookie_type) {
+ case LS_COOKIE_ID:
+ l2info->bundle = l2info->cookie[1];
+ break;
+ case AS_COOKIE_ID:
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ l2info->bundle = (EXTRACT_BE_U_2(&l2info->cookie[6])>>3)&0xfff;
+ l2info->proto = (l2info->cookie[5])&JUNIPER_LSQ_L3_PROTO_MASK;
+ break;
+ default:
+ l2info->bundle = l2info->cookie[0];
+ break;
+ }
+ break;
+#endif
+#ifdef DLT_JUNIPER_MLFR
+ case DLT_JUNIPER_MLFR:
+ switch (l2info->cookie_type) {
+ case LS_COOKIE_ID:
+ l2info->bundle = l2info->cookie[1];
+ l2info->proto = GET_BE_U_2(p);
+ l2info->header_len += 2;
+ l2info->length -= 2;
+ l2info->caplen -= 2;
+ break;
+ case AS_COOKIE_ID:
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ l2info->bundle = (EXTRACT_BE_U_2(&l2info->cookie[6])>>3)&0xfff;
+ l2info->proto = (l2info->cookie[5])&JUNIPER_LSQ_L3_PROTO_MASK;
+ break;
+ default:
+ l2info->bundle = l2info->cookie[0];
+ l2info->header_len += 2;
+ l2info->length -= 2;
+ l2info->caplen -= 2;
+ break;
+ }
+ break;
+#endif
+#ifdef DLT_JUNIPER_MFR
+ case DLT_JUNIPER_MFR:
+ switch (l2info->cookie_type) {
+ case LS_COOKIE_ID:
+ l2info->bundle = l2info->cookie[1];
+ l2info->proto = GET_BE_U_2(p);
+ l2info->header_len += 2;
+ l2info->length -= 2;
+ l2info->caplen -= 2;
+ break;
+ case AS_COOKIE_ID:
+ /* use EXTRACT_, not GET_ (not packet buffer pointer) */
+ l2info->bundle = (EXTRACT_BE_U_2(&l2info->cookie[6])>>3)&0xfff;
+ l2info->proto = (l2info->cookie[5])&JUNIPER_LSQ_L3_PROTO_MASK;
+ break;
+ default:
+ l2info->bundle = l2info->cookie[0];
+ break;
+ }
+ break;
+#endif
+#ifdef DLT_JUNIPER_ATM2
+ case DLT_JUNIPER_ATM2:
+ ND_TCHECK_4(p);
+ /* ATM cell relay control word present ? */
+ if (l2info->cookie[7] & ATM2_PKT_TYPE_MASK) {
+ control_word = GET_BE_U_4(p);
+ /* some control word heuristics */
+ switch(control_word) {
+ case 0: /* zero control word */
+ case 0x08000000: /* < JUNOS 7.4 control-word */
+ case 0x08380000: /* cntl word plus cell length (56) >= JUNOS 7.4*/
+ l2info->header_len += 4;
+ break;
+ default:
+ break;
+ }
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("control-word 0x%08x ", control_word);
+ }
+ break;
+#endif
+#ifdef DLT_JUNIPER_GGSN
+ case DLT_JUNIPER_GGSN:
+ break;
+#endif
+#ifdef DLT_JUNIPER_ATM1
+ case DLT_JUNIPER_ATM1:
+ break;
+#endif
+#ifdef DLT_JUNIPER_PPP
+ case DLT_JUNIPER_PPP:
+ break;
+#endif
+#ifdef DLT_JUNIPER_CHDLC
+ case DLT_JUNIPER_CHDLC:
+ break;
+#endif
+#ifdef DLT_JUNIPER_ETHER
+ case DLT_JUNIPER_ETHER:
+ break;
+#endif
+#ifdef DLT_JUNIPER_FRELAY
+ case DLT_JUNIPER_FRELAY:
+ break;
+#endif
+
+ default:
+ ND_PRINT("Unknown Juniper DLT_ type %u: ", l2info->pictype);
+ break;
+ }
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("hlen %u, proto 0x%04x, ", l2info->header_len, l2info->proto);
+
+ return 1; /* everything went ok so far. continue parsing */
+trunc:
+ nd_print_trunc(ndo);
+ return 0;
+}
+#endif /* defined(DLT_JUNIPER_GGSN) || defined(DLT_JUNIPER_ES) || \
+ defined(DLT_JUNIPER_MONITOR) || defined(DLT_JUNIPER_SERVICES) || \
+ defined(DLT_JUNIPER_PPPOE) || defined(DLT_JUNIPER_ETHER) || \
+ defined(DLT_JUNIPER_PPP) || defined(DLT_JUNIPER_FRELAY) || \
+ defined(DLT_JUNIPER_CHDLC) || defined(DLT_JUNIPER_PPPOE_ATM) || \
+ defined(DLT_JUNIPER_MLPPP) || defined(DLT_JUNIPER_MFR) || \
+ defined(DLT_JUNIPER_MLFR) || defined(DLT_JUNIPER_ATM1) || \
+ defined(DLT_JUNIPER_ATM2) */
diff --git a/print-krb.c b/print-krb.c
new file mode 100644
index 0000000..b4c0fad
--- /dev/null
+++ b/print-krb.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Initial contribution from John Hawkinson (jhawk@mit.edu).
+ */
+
+/* \summary: Kerberos printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+/*
+ * Kerberos 4:
+ *
+ * Athena Technical Plan
+ * Section E.2.1
+ * Kerberos Authentication and Authorization System
+ * by S. P. Miller, B. C. Neuman, J. I. Schiller, and J. H. Saltzer
+ *
+ * https://web.mit.edu/Saltzer/www/publications/athenaplan/e.2.1.pdf
+ *
+ * 7. Appendix I Design Specifications
+ *
+ * Kerberos 5:
+ *
+ * RFC 1510, RFC 2630, etc.
+ */
+
+
+static const u_char *c_print(netdissect_options *, const u_char *, const u_char *);
+static const u_char *krb4_print_hdr(netdissect_options *, const u_char *);
+static void krb4_print(netdissect_options *, const u_char *);
+
+#define AUTH_MSG_KDC_REQUEST 1<<1
+#define AUTH_MSG_KDC_REPLY 2<<1
+#define AUTH_MSG_APPL_REQUEST 3<<1
+#define AUTH_MSG_APPL_REQUEST_MUTUAL 4<<1
+#define AUTH_MSG_ERR_REPLY 5<<1
+#define AUTH_MSG_PRIVATE 6<<1
+#define AUTH_MSG_SAFE 7<<1
+#define AUTH_MSG_APPL_ERR 8<<1
+#define AUTH_MSG_DIE 63<<1
+
+#define KERB_ERR_OK 0
+#define KERB_ERR_NAME_EXP 1
+#define KERB_ERR_SERVICE_EXP 2
+#define KERB_ERR_AUTH_EXP 3
+#define KERB_ERR_PKT_VER 4
+#define KERB_ERR_NAME_MAST_KEY_VER 5
+#define KERB_ERR_SERV_MAST_KEY_VER 6
+#define KERB_ERR_BYTE_ORDER 7
+#define KERB_ERR_PRINCIPAL_UNKNOWN 8
+#define KERB_ERR_PRINCIPAL_NOT_UNIQUE 9
+#define KERB_ERR_NULL_KEY 10
+
+struct krb {
+ nd_uint8_t pvno; /* Protocol Version */
+ nd_uint8_t type; /* Type+B */
+};
+
+static const struct tok type2str[] = {
+ { AUTH_MSG_KDC_REQUEST, "KDC_REQUEST" },
+ { AUTH_MSG_KDC_REPLY, "KDC_REPLY" },
+ { AUTH_MSG_APPL_REQUEST, "APPL_REQUEST" },
+ { AUTH_MSG_APPL_REQUEST_MUTUAL, "APPL_REQUEST_MUTUAL" },
+ { AUTH_MSG_ERR_REPLY, "ERR_REPLY" },
+ { AUTH_MSG_PRIVATE, "PRIVATE" },
+ { AUTH_MSG_SAFE, "SAFE" },
+ { AUTH_MSG_APPL_ERR, "APPL_ERR" },
+ { AUTH_MSG_DIE, "DIE" },
+ { 0, NULL }
+};
+
+static const struct tok kerr2str[] = {
+ { KERB_ERR_OK, "OK" },
+ { KERB_ERR_NAME_EXP, "NAME_EXP" },
+ { KERB_ERR_SERVICE_EXP, "SERVICE_EXP" },
+ { KERB_ERR_AUTH_EXP, "AUTH_EXP" },
+ { KERB_ERR_PKT_VER, "PKT_VER" },
+ { KERB_ERR_NAME_MAST_KEY_VER, "NAME_MAST_KEY_VER" },
+ { KERB_ERR_SERV_MAST_KEY_VER, "SERV_MAST_KEY_VER" },
+ { KERB_ERR_BYTE_ORDER, "BYTE_ORDER" },
+ { KERB_ERR_PRINCIPAL_UNKNOWN, "PRINCIPAL_UNKNOWN" },
+ { KERB_ERR_PRINCIPAL_NOT_UNIQUE,"PRINCIPAL_NOT_UNIQUE" },
+ { KERB_ERR_NULL_KEY, "NULL_KEY"},
+ { 0, NULL}
+};
+
+static const u_char *
+c_print(netdissect_options *ndo,
+ const u_char *s, const u_char *ep)
+{
+ u_char c;
+ int flag;
+
+ flag = 1;
+ while (s < ep) {
+ c = GET_U_1(s);
+ s++;
+ if (c == '\0') {
+ flag = 0;
+ break;
+ }
+ fn_print_char(ndo, c);
+ }
+ if (flag)
+ return NULL;
+ return (s);
+}
+
+static const u_char *
+krb4_print_hdr(netdissect_options *ndo,
+ const u_char *cp)
+{
+ cp += 2;
+
+#define PRINT if ((cp = c_print(ndo, cp, ndo->ndo_snapend)) == NULL) goto trunc
+
+ PRINT;
+ ND_PRINT(".");
+ PRINT;
+ ND_PRINT("@");
+ PRINT;
+ return (cp);
+
+trunc:
+ nd_print_trunc(ndo);
+ return (NULL);
+
+#undef PRINT
+}
+
+static void
+krb4_print(netdissect_options *ndo,
+ const u_char *cp)
+{
+ const struct krb *kp;
+ u_char type;
+ u_short len;
+
+#define PRINT if ((cp = c_print(ndo, cp, ndo->ndo_snapend)) == NULL) goto trunc
+/* True if struct krb is little endian */
+#define IS_LENDIAN(kp) ((GET_U_1((kp)->type) & 0x01) != 0)
+#define KTOHSP(kp, cp) (IS_LENDIAN(kp) ? GET_LE_U_2(cp) : GET_BE_U_2(cp))
+
+ kp = (const struct krb *)cp;
+
+ type = GET_U_1(kp->type) & (0xFF << 1);
+
+ ND_PRINT(" %s %s: ",
+ IS_LENDIAN(kp) ? "le" : "be", tok2str(type2str, NULL, type));
+
+ switch (type) {
+
+ case AUTH_MSG_KDC_REQUEST:
+ if ((cp = krb4_print_hdr(ndo, cp)) == NULL)
+ return;
+ cp += 4; /* ctime */
+ ND_PRINT(" %umin ", GET_U_1(cp) * 5);
+ cp++;
+ PRINT;
+ ND_PRINT(".");
+ PRINT;
+ break;
+
+ case AUTH_MSG_APPL_REQUEST:
+ cp += 2;
+ ND_PRINT("v%u ", GET_U_1(cp));
+ cp++;
+ PRINT;
+ ND_PRINT(" (%u)", GET_U_1(cp));
+ cp++;
+ ND_PRINT(" (%u)", GET_U_1(cp));
+ break;
+
+ case AUTH_MSG_KDC_REPLY:
+ if ((cp = krb4_print_hdr(ndo, cp)) == NULL)
+ return;
+ cp += 10; /* timestamp + n + exp + kvno */
+ len = KTOHSP(kp, cp);
+ ND_PRINT(" (%u)", len);
+ break;
+
+ case AUTH_MSG_ERR_REPLY:
+ if ((cp = krb4_print_hdr(ndo, cp)) == NULL)
+ return;
+ cp += 4; /* timestamp */
+ ND_PRINT(" %s ", tok2str(kerr2str, NULL, KTOHSP(kp, cp)));
+ cp += 4;
+ PRINT;
+ break;
+
+ default:
+ ND_PRINT("(unknown)");
+ break;
+ }
+
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+krb_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct krb *kp;
+
+ ndo->ndo_protocol = "krb";
+ kp = (const struct krb *)dat;
+
+ if (dat >= ndo->ndo_snapend) {
+ nd_print_trunc(ndo);
+ return;
+ }
+
+ switch (GET_U_1(kp->pvno)) {
+
+ case 1:
+ case 2:
+ case 3:
+ ND_PRINT(" v%u", GET_U_1(kp->pvno));
+ break;
+
+ case 4:
+ ND_PRINT(" v%u", GET_U_1(kp->pvno));
+ krb4_print(ndo, (const u_char *)kp);
+ break;
+
+ case 106:
+ case 107:
+ ND_PRINT(" v5");
+ /* Decode ASN.1 here "someday" */
+ break;
+ }
+}
diff --git a/print-l2tp.c b/print-l2tp.c
new file mode 100644
index 0000000..8377d3a
--- /dev/null
+++ b/print-l2tp.c
@@ -0,0 +1,854 @@
+/*
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * L2TP support contributed by Motonori Shindo (mshindo@mshindo.net)
+ */
+
+/* \summary: Layer Two Tunneling Protocol (L2TP) printer */
+
+/* specification: RFC 2661 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+#define L2TP_FLAG_TYPE 0x8000 /* Type (0=Data, 1=Control) */
+#define L2TP_FLAG_LENGTH 0x4000 /* Length */
+#define L2TP_FLAG_SEQUENCE 0x0800 /* Sequence */
+#define L2TP_FLAG_OFFSET 0x0200 /* Offset */
+#define L2TP_FLAG_PRIORITY 0x0100 /* Priority */
+
+#define L2TP_VERSION_MASK 0x000f /* Version Mask */
+#define L2TP_VERSION_L2F 0x0001 /* L2F */
+#define L2TP_VERSION_L2TP 0x0002 /* L2TP */
+
+#define L2TP_AVP_HDR_FLAG_MANDATORY 0x8000 /* Mandatory Flag */
+#define L2TP_AVP_HDR_FLAG_HIDDEN 0x4000 /* Hidden Flag */
+#define L2TP_AVP_HDR_LEN_MASK 0x03ff /* Length Mask */
+
+#define L2TP_FRAMING_CAP_SYNC_MASK 0x00000001 /* Synchronous */
+#define L2TP_FRAMING_CAP_ASYNC_MASK 0x00000002 /* Asynchronous */
+
+#define L2TP_FRAMING_TYPE_SYNC_MASK 0x00000001 /* Synchronous */
+#define L2TP_FRAMING_TYPE_ASYNC_MASK 0x00000002 /* Asynchronous */
+
+#define L2TP_BEARER_CAP_DIGITAL_MASK 0x00000001 /* Digital */
+#define L2TP_BEARER_CAP_ANALOG_MASK 0x00000002 /* Analog */
+
+#define L2TP_BEARER_TYPE_DIGITAL_MASK 0x00000001 /* Digital */
+#define L2TP_BEARER_TYPE_ANALOG_MASK 0x00000002 /* Analog */
+
+/* Authen Type */
+#define L2TP_AUTHEN_TYPE_RESERVED 0x0000 /* Reserved */
+#define L2TP_AUTHEN_TYPE_TEXTUAL 0x0001 /* Textual username/password exchange */
+#define L2TP_AUTHEN_TYPE_CHAP 0x0002 /* PPP CHAP */
+#define L2TP_AUTHEN_TYPE_PAP 0x0003 /* PPP PAP */
+#define L2TP_AUTHEN_TYPE_NO_AUTH 0x0004 /* No Authentication */
+#define L2TP_AUTHEN_TYPE_MSCHAPv1 0x0005 /* MSCHAPv1 */
+
+#define L2TP_PROXY_AUTH_ID_MASK 0x00ff
+
+
+#define L2TP_MSGTYPE_SCCRQ 1 /* Start-Control-Connection-Request */
+#define L2TP_MSGTYPE_SCCRP 2 /* Start-Control-Connection-Reply */
+#define L2TP_MSGTYPE_SCCCN 3 /* Start-Control-Connection-Connected */
+#define L2TP_MSGTYPE_STOPCCN 4 /* Stop-Control-Connection-Notification */
+#define L2TP_MSGTYPE_HELLO 6 /* Hello */
+#define L2TP_MSGTYPE_OCRQ 7 /* Outgoing-Call-Request */
+#define L2TP_MSGTYPE_OCRP 8 /* Outgoing-Call-Reply */
+#define L2TP_MSGTYPE_OCCN 9 /* Outgoing-Call-Connected */
+#define L2TP_MSGTYPE_ICRQ 10 /* Incoming-Call-Request */
+#define L2TP_MSGTYPE_ICRP 11 /* Incoming-Call-Reply */
+#define L2TP_MSGTYPE_ICCN 12 /* Incoming-Call-Connected */
+#define L2TP_MSGTYPE_CDN 14 /* Call-Disconnect-Notify */
+#define L2TP_MSGTYPE_WEN 15 /* WAN-Error-Notify */
+#define L2TP_MSGTYPE_SLI 16 /* Set-Link-Info */
+
+static const struct tok l2tp_msgtype2str[] = {
+ { L2TP_MSGTYPE_SCCRQ, "SCCRQ" },
+ { L2TP_MSGTYPE_SCCRP, "SCCRP" },
+ { L2TP_MSGTYPE_SCCCN, "SCCCN" },
+ { L2TP_MSGTYPE_STOPCCN, "StopCCN" },
+ { L2TP_MSGTYPE_HELLO, "HELLO" },
+ { L2TP_MSGTYPE_OCRQ, "OCRQ" },
+ { L2TP_MSGTYPE_OCRP, "OCRP" },
+ { L2TP_MSGTYPE_OCCN, "OCCN" },
+ { L2TP_MSGTYPE_ICRQ, "ICRQ" },
+ { L2TP_MSGTYPE_ICRP, "ICRP" },
+ { L2TP_MSGTYPE_ICCN, "ICCN" },
+ { L2TP_MSGTYPE_CDN, "CDN" },
+ { L2TP_MSGTYPE_WEN, "WEN" },
+ { L2TP_MSGTYPE_SLI, "SLI" },
+ { 0, NULL }
+};
+
+#define L2TP_AVP_MSGTYPE 0 /* Message Type */
+#define L2TP_AVP_RESULT_CODE 1 /* Result Code */
+#define L2TP_AVP_PROTO_VER 2 /* Protocol Version */
+#define L2TP_AVP_FRAMING_CAP 3 /* Framing Capabilities */
+#define L2TP_AVP_BEARER_CAP 4 /* Bearer Capabilities */
+#define L2TP_AVP_TIE_BREAKER 5 /* Tie Breaker */
+#define L2TP_AVP_FIRM_VER 6 /* Firmware Revision */
+#define L2TP_AVP_HOST_NAME 7 /* Host Name */
+#define L2TP_AVP_VENDOR_NAME 8 /* Vendor Name */
+#define L2TP_AVP_ASSND_TUN_ID 9 /* Assigned Tunnel ID */
+#define L2TP_AVP_RECV_WIN_SIZE 10 /* Receive Window Size */
+#define L2TP_AVP_CHALLENGE 11 /* Challenge */
+#define L2TP_AVP_Q931_CC 12 /* Q.931 Cause Code */
+#define L2TP_AVP_CHALLENGE_RESP 13 /* Challenge Response */
+#define L2TP_AVP_ASSND_SESS_ID 14 /* Assigned Session ID */
+#define L2TP_AVP_CALL_SER_NUM 15 /* Call Serial Number */
+#define L2TP_AVP_MINIMUM_BPS 16 /* Minimum BPS */
+#define L2TP_AVP_MAXIMUM_BPS 17 /* Maximum BPS */
+#define L2TP_AVP_BEARER_TYPE 18 /* Bearer Type */
+#define L2TP_AVP_FRAMING_TYPE 19 /* Framing Type */
+#define L2TP_AVP_PACKET_PROC_DELAY 20 /* Packet Processing Delay (OBSOLETE) */
+#define L2TP_AVP_CALLED_NUMBER 21 /* Called Number */
+#define L2TP_AVP_CALLING_NUMBER 22 /* Calling Number */
+#define L2TP_AVP_SUB_ADDRESS 23 /* Sub-Address */
+#define L2TP_AVP_TX_CONN_SPEED 24 /* (Tx) Connect Speed */
+#define L2TP_AVP_PHY_CHANNEL_ID 25 /* Physical Channel ID */
+#define L2TP_AVP_INI_RECV_LCP 26 /* Initial Received LCP CONFREQ */
+#define L2TP_AVP_LAST_SENT_LCP 27 /* Last Sent LCP CONFREQ */
+#define L2TP_AVP_LAST_RECV_LCP 28 /* Last Received LCP CONFREQ */
+#define L2TP_AVP_PROXY_AUTH_TYPE 29 /* Proxy Authen Type */
+#define L2TP_AVP_PROXY_AUTH_NAME 30 /* Proxy Authen Name */
+#define L2TP_AVP_PROXY_AUTH_CHAL 31 /* Proxy Authen Challenge */
+#define L2TP_AVP_PROXY_AUTH_ID 32 /* Proxy Authen ID */
+#define L2TP_AVP_PROXY_AUTH_RESP 33 /* Proxy Authen Response */
+#define L2TP_AVP_CALL_ERRORS 34 /* Call Errors */
+#define L2TP_AVP_ACCM 35 /* ACCM */
+#define L2TP_AVP_RANDOM_VECTOR 36 /* Random Vector */
+#define L2TP_AVP_PRIVATE_GRP_ID 37 /* Private Group ID */
+#define L2TP_AVP_RX_CONN_SPEED 38 /* (Rx) Connect Speed */
+#define L2TP_AVP_SEQ_REQUIRED 39 /* Sequencing Required */
+#define L2TP_AVP_PPP_DISCON_CC 46 /* PPP Disconnect Cause Code - RFC 3145 */
+
+static const struct tok l2tp_avp2str[] = {
+ { L2TP_AVP_MSGTYPE, "MSGTYPE" },
+ { L2TP_AVP_RESULT_CODE, "RESULT_CODE" },
+ { L2TP_AVP_PROTO_VER, "PROTO_VER" },
+ { L2TP_AVP_FRAMING_CAP, "FRAMING_CAP" },
+ { L2TP_AVP_BEARER_CAP, "BEARER_CAP" },
+ { L2TP_AVP_TIE_BREAKER, "TIE_BREAKER" },
+ { L2TP_AVP_FIRM_VER, "FIRM_VER" },
+ { L2TP_AVP_HOST_NAME, "HOST_NAME" },
+ { L2TP_AVP_VENDOR_NAME, "VENDOR_NAME" },
+ { L2TP_AVP_ASSND_TUN_ID, "ASSND_TUN_ID" },
+ { L2TP_AVP_RECV_WIN_SIZE, "RECV_WIN_SIZE" },
+ { L2TP_AVP_CHALLENGE, "CHALLENGE" },
+ { L2TP_AVP_Q931_CC, "Q931_CC", },
+ { L2TP_AVP_CHALLENGE_RESP, "CHALLENGE_RESP" },
+ { L2TP_AVP_ASSND_SESS_ID, "ASSND_SESS_ID" },
+ { L2TP_AVP_CALL_SER_NUM, "CALL_SER_NUM" },
+ { L2TP_AVP_MINIMUM_BPS, "MINIMUM_BPS" },
+ { L2TP_AVP_MAXIMUM_BPS, "MAXIMUM_BPS" },
+ { L2TP_AVP_BEARER_TYPE, "BEARER_TYPE" },
+ { L2TP_AVP_FRAMING_TYPE, "FRAMING_TYPE" },
+ { L2TP_AVP_PACKET_PROC_DELAY, "PACKET_PROC_DELAY" },
+ { L2TP_AVP_CALLED_NUMBER, "CALLED_NUMBER" },
+ { L2TP_AVP_CALLING_NUMBER, "CALLING_NUMBER" },
+ { L2TP_AVP_SUB_ADDRESS, "SUB_ADDRESS" },
+ { L2TP_AVP_TX_CONN_SPEED, "TX_CONN_SPEED" },
+ { L2TP_AVP_PHY_CHANNEL_ID, "PHY_CHANNEL_ID" },
+ { L2TP_AVP_INI_RECV_LCP, "INI_RECV_LCP" },
+ { L2TP_AVP_LAST_SENT_LCP, "LAST_SENT_LCP" },
+ { L2TP_AVP_LAST_RECV_LCP, "LAST_RECV_LCP" },
+ { L2TP_AVP_PROXY_AUTH_TYPE, "PROXY_AUTH_TYPE" },
+ { L2TP_AVP_PROXY_AUTH_NAME, "PROXY_AUTH_NAME" },
+ { L2TP_AVP_PROXY_AUTH_CHAL, "PROXY_AUTH_CHAL" },
+ { L2TP_AVP_PROXY_AUTH_ID, "PROXY_AUTH_ID" },
+ { L2TP_AVP_PROXY_AUTH_RESP, "PROXY_AUTH_RESP" },
+ { L2TP_AVP_CALL_ERRORS, "CALL_ERRORS" },
+ { L2TP_AVP_ACCM, "ACCM" },
+ { L2TP_AVP_RANDOM_VECTOR, "RANDOM_VECTOR" },
+ { L2TP_AVP_PRIVATE_GRP_ID, "PRIVATE_GRP_ID" },
+ { L2TP_AVP_RX_CONN_SPEED, "RX_CONN_SPEED" },
+ { L2TP_AVP_SEQ_REQUIRED, "SEQ_REQUIRED" },
+ { L2TP_AVP_PPP_DISCON_CC, "PPP_DISCON_CC" },
+ { 0, NULL }
+};
+
+static const struct tok l2tp_authentype2str[] = {
+ { L2TP_AUTHEN_TYPE_RESERVED, "Reserved" },
+ { L2TP_AUTHEN_TYPE_TEXTUAL, "Textual" },
+ { L2TP_AUTHEN_TYPE_CHAP, "CHAP" },
+ { L2TP_AUTHEN_TYPE_PAP, "PAP" },
+ { L2TP_AUTHEN_TYPE_NO_AUTH, "No Auth" },
+ { L2TP_AUTHEN_TYPE_MSCHAPv1, "MS-CHAPv1" },
+ { 0, NULL }
+};
+
+#define L2TP_PPP_DISCON_CC_DIRECTION_GLOBAL 0
+#define L2TP_PPP_DISCON_CC_DIRECTION_AT_PEER 1
+#define L2TP_PPP_DISCON_CC_DIRECTION_AT_LOCAL 2
+
+static const struct tok l2tp_cc_direction2str[] = {
+ { L2TP_PPP_DISCON_CC_DIRECTION_GLOBAL, "global error" },
+ { L2TP_PPP_DISCON_CC_DIRECTION_AT_PEER, "at peer" },
+ { L2TP_PPP_DISCON_CC_DIRECTION_AT_LOCAL,"at local" },
+ { 0, NULL }
+};
+
+#if 0
+static char *l2tp_result_code_StopCCN[] = {
+ "Reserved",
+ "General request to clear control connection",
+ "General error--Error Code indicates the problem",
+ "Control channel already exists",
+ "Requester is not authorized to establish a control channel",
+ "The protocol version of the requester is not supported",
+ "Requester is being shut down",
+ "Finite State Machine error"
+#define L2TP_MAX_RESULT_CODE_STOPCC_INDEX 8
+};
+#endif
+
+#if 0
+static char *l2tp_result_code_CDN[] = {
+ "Reserved",
+ "Call disconnected due to loss of carrier",
+ "Call disconnected for the reason indicated in error code",
+ "Call disconnected for administrative reasons",
+ "Call failed due to lack of appropriate facilities being "
+ "available (temporary condition)",
+ "Call failed due to lack of appropriate facilities being "
+ "available (permanent condition)",
+ "Invalid destination",
+ "Call failed due to no carrier detected",
+ "Call failed due to detection of a busy signal",
+ "Call failed due to lack of a dial tone",
+ "Call was not established within time allotted by LAC",
+ "Call was connected but no appropriate framing was detected"
+#define L2TP_MAX_RESULT_CODE_CDN_INDEX 12
+};
+#endif
+
+#if 0
+static char *l2tp_error_code_general[] = {
+ "No general error",
+ "No control connection exists yet for this LAC-LNS pair",
+ "Length is wrong",
+ "One of the field values was out of range or "
+ "reserved field was non-zero"
+ "Insufficient resources to handle this operation now",
+ "The Session ID is invalid in this context",
+ "A generic vendor-specific error occurred in the LAC",
+ "Try another"
+#define L2TP_MAX_ERROR_CODE_GENERAL_INDEX 8
+};
+#endif
+
+/******************************/
+/* generic print out routines */
+/******************************/
+static void
+print_string(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i;
+ for (i=0; i<length; i++) {
+ fn_print_char(ndo, GET_U_1(dat));
+ dat++;
+ }
+}
+
+static void
+print_octets(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int i;
+ for (i=0; i<length; i++) {
+ ND_PRINT("%02x", GET_U_1(dat));
+ dat++;
+ }
+}
+
+static void
+print_16bits_val(netdissect_options *ndo, const uint8_t *dat)
+{
+ ND_PRINT("%u", GET_BE_U_2(dat));
+}
+
+static void
+print_32bits_val(netdissect_options *ndo, const uint8_t *dat)
+{
+ ND_PRINT("%u", GET_BE_U_4(dat));
+}
+
+/***********************************/
+/* AVP-specific print out routines */
+/***********************************/
+static void
+l2tp_msgtype_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 2) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ ND_PRINT("%s", tok2str(l2tp_msgtype2str, "MSGTYPE-#%u",
+ GET_BE_U_2(dat)));
+}
+
+static void
+l2tp_result_code_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ /* Result Code */
+ if (length < 2) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ ND_PRINT("%u", GET_BE_U_2(dat));
+ dat += 2;
+ length -= 2;
+
+ /* Error Code (opt) */
+ if (length == 0)
+ return;
+ if (length < 2) {
+ ND_PRINT(" AVP too short");
+ return;
+ }
+ ND_PRINT("/%u", GET_BE_U_2(dat));
+ dat += 2;
+ length -= 2;
+
+ /* Error Message (opt) */
+ if (length == 0)
+ return;
+ ND_PRINT(" ");
+ print_string(ndo, dat, length);
+}
+
+static void
+l2tp_proto_ver_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 2) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ ND_PRINT("%u.%u", (GET_BE_U_2(dat) >> 8),
+ (GET_BE_U_2(dat) & 0xff));
+}
+
+static void
+l2tp_framing_cap_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ if (GET_BE_U_4(dat) & L2TP_FRAMING_CAP_ASYNC_MASK) {
+ ND_PRINT("A");
+ }
+ if (GET_BE_U_4(dat) & L2TP_FRAMING_CAP_SYNC_MASK) {
+ ND_PRINT("S");
+ }
+}
+
+static void
+l2tp_bearer_cap_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ if (GET_BE_U_4(dat) & L2TP_BEARER_CAP_ANALOG_MASK) {
+ ND_PRINT("A");
+ }
+ if (GET_BE_U_4(dat) & L2TP_BEARER_CAP_DIGITAL_MASK) {
+ ND_PRINT("D");
+ }
+}
+
+static void
+l2tp_q931_cc_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 3) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ print_16bits_val(ndo, dat);
+ ND_PRINT(", %02x", GET_U_1(dat + 2));
+ dat += 3;
+ length -= 3;
+ if (length != 0) {
+ ND_PRINT(" ");
+ print_string(ndo, dat, length);
+ }
+}
+
+static void
+l2tp_bearer_type_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ if (GET_BE_U_4(dat) & L2TP_BEARER_TYPE_ANALOG_MASK) {
+ ND_PRINT("A");
+ }
+ if (GET_BE_U_4(dat) & L2TP_BEARER_TYPE_DIGITAL_MASK) {
+ ND_PRINT("D");
+ }
+}
+
+static void
+l2tp_framing_type_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ if (GET_BE_U_4(dat) & L2TP_FRAMING_TYPE_ASYNC_MASK) {
+ ND_PRINT("A");
+ }
+ if (GET_BE_U_4(dat) & L2TP_FRAMING_TYPE_SYNC_MASK) {
+ ND_PRINT("S");
+ }
+}
+
+static void
+l2tp_packet_proc_delay_print(netdissect_options *ndo)
+{
+ ND_PRINT("obsolete");
+}
+
+static void
+l2tp_proxy_auth_type_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 2) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ ND_PRINT("%s", tok2str(l2tp_authentype2str,
+ "AuthType-#%u", GET_BE_U_2(dat)));
+}
+
+static void
+l2tp_proxy_auth_id_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 2) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ ND_PRINT("%u", GET_BE_U_2(dat) & L2TP_PROXY_AUTH_ID_MASK);
+}
+
+static void
+l2tp_call_errors_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ uint32_t val;
+
+ if (length < 2) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ dat += 2; /* skip "Reserved" */
+ length -= 2;
+
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ val = GET_BE_U_4(dat); dat += 4; length -= 4;
+ ND_PRINT("CRCErr=%u ", val);
+
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ val = GET_BE_U_4(dat); dat += 4; length -= 4;
+ ND_PRINT("FrameErr=%u ", val);
+
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ val = GET_BE_U_4(dat); dat += 4; length -= 4;
+ ND_PRINT("HardOver=%u ", val);
+
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ val = GET_BE_U_4(dat); dat += 4; length -= 4;
+ ND_PRINT("BufOver=%u ", val);
+
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ val = GET_BE_U_4(dat); dat += 4; length -= 4;
+ ND_PRINT("Timeout=%u ", val);
+
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ val = GET_BE_U_4(dat); dat += 4; length -= 4;
+ ND_PRINT("AlignErr=%u ", val);
+}
+
+static void
+l2tp_accm_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ uint32_t val;
+
+ if (length < 2) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ dat += 2; /* skip "Reserved" */
+ length -= 2;
+
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ val = GET_BE_U_4(dat); dat += 4; length -= 4;
+ ND_PRINT("send=%08x ", val);
+
+ if (length < 4) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ val = GET_BE_U_4(dat); dat += 4; length -= 4;
+ ND_PRINT("recv=%08x ", val);
+}
+
+static void
+l2tp_ppp_discon_cc_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ if (length < 5) {
+ ND_PRINT("AVP too short");
+ return;
+ }
+ /* Disconnect Code */
+ ND_PRINT("%04x, ", GET_BE_U_2(dat));
+ dat += 2;
+ length -= 2;
+ /* Control Protocol Number */
+ ND_PRINT("%04x ", GET_BE_U_2(dat));
+ dat += 2;
+ length -= 2;
+ /* Direction */
+ ND_PRINT("%s", tok2str(l2tp_cc_direction2str,
+ "Direction-#%u", GET_U_1(dat)));
+ dat++;
+ length--;
+
+ if (length != 0) {
+ ND_PRINT(" ");
+ print_string(ndo, (const u_char *)dat, length);
+ }
+}
+
+static u_int
+l2tp_avp_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ u_int len;
+ uint16_t attr_type;
+ int hidden = FALSE;
+
+ ND_PRINT(" ");
+ /* Flags & Length */
+ len = GET_BE_U_2(dat) & L2TP_AVP_HDR_LEN_MASK;
+
+ /* If it is not long enough to contain the header, we'll give up. */
+ if (len < 6)
+ goto trunc;
+
+ /* If it goes past the end of the remaining length of the packet,
+ we'll give up. */
+ if (len > (u_int)length)
+ goto trunc;
+
+ /* If it goes past the end of the remaining length of the captured
+ data, we'll give up. */
+ ND_TCHECK_LEN(dat, len);
+
+ /*
+ * After this point, we don't need to check whether we go past
+ * the length of the captured data; however, we *do* need to
+ * check whether we go past the end of the AVP.
+ */
+
+ if (GET_BE_U_2(dat) & L2TP_AVP_HDR_FLAG_MANDATORY) {
+ ND_PRINT("*");
+ }
+ if (GET_BE_U_2(dat) & L2TP_AVP_HDR_FLAG_HIDDEN) {
+ hidden = TRUE;
+ ND_PRINT("?");
+ }
+ dat += 2;
+
+ if (GET_BE_U_2(dat)) {
+ /* Vendor Specific Attribute */
+ ND_PRINT("VENDOR%04x:", GET_BE_U_2(dat)); dat += 2;
+ ND_PRINT("ATTR%04x", GET_BE_U_2(dat)); dat += 2;
+ ND_PRINT("(");
+ print_octets(ndo, dat, len-6);
+ ND_PRINT(")");
+ } else {
+ /* IETF-defined Attributes */
+ dat += 2;
+ attr_type = GET_BE_U_2(dat); dat += 2;
+ ND_PRINT("%s", tok2str(l2tp_avp2str, "AVP-#%u", attr_type));
+ ND_PRINT("(");
+ if (hidden) {
+ ND_PRINT("???");
+ } else {
+ switch (attr_type) {
+ case L2TP_AVP_MSGTYPE:
+ l2tp_msgtype_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_RESULT_CODE:
+ l2tp_result_code_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_PROTO_VER:
+ l2tp_proto_ver_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_FRAMING_CAP:
+ l2tp_framing_cap_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_BEARER_CAP:
+ l2tp_bearer_cap_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_TIE_BREAKER:
+ if (len-6 < 8) {
+ ND_PRINT("AVP too short");
+ break;
+ }
+ print_octets(ndo, dat, 8);
+ break;
+ case L2TP_AVP_FIRM_VER:
+ case L2TP_AVP_ASSND_TUN_ID:
+ case L2TP_AVP_RECV_WIN_SIZE:
+ case L2TP_AVP_ASSND_SESS_ID:
+ if (len-6 < 2) {
+ ND_PRINT("AVP too short");
+ break;
+ }
+ print_16bits_val(ndo, dat);
+ break;
+ case L2TP_AVP_HOST_NAME:
+ case L2TP_AVP_VENDOR_NAME:
+ case L2TP_AVP_CALLING_NUMBER:
+ case L2TP_AVP_CALLED_NUMBER:
+ case L2TP_AVP_SUB_ADDRESS:
+ case L2TP_AVP_PROXY_AUTH_NAME:
+ case L2TP_AVP_PRIVATE_GRP_ID:
+ print_string(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_CHALLENGE:
+ case L2TP_AVP_INI_RECV_LCP:
+ case L2TP_AVP_LAST_SENT_LCP:
+ case L2TP_AVP_LAST_RECV_LCP:
+ case L2TP_AVP_PROXY_AUTH_CHAL:
+ case L2TP_AVP_PROXY_AUTH_RESP:
+ case L2TP_AVP_RANDOM_VECTOR:
+ print_octets(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_Q931_CC:
+ l2tp_q931_cc_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_CHALLENGE_RESP:
+ if (len-6 < 16) {
+ ND_PRINT("AVP too short");
+ break;
+ }
+ print_octets(ndo, dat, 16);
+ break;
+ case L2TP_AVP_CALL_SER_NUM:
+ case L2TP_AVP_MINIMUM_BPS:
+ case L2TP_AVP_MAXIMUM_BPS:
+ case L2TP_AVP_TX_CONN_SPEED:
+ case L2TP_AVP_PHY_CHANNEL_ID:
+ case L2TP_AVP_RX_CONN_SPEED:
+ if (len-6 < 4) {
+ ND_PRINT("AVP too short");
+ break;
+ }
+ print_32bits_val(ndo, dat);
+ break;
+ case L2TP_AVP_BEARER_TYPE:
+ l2tp_bearer_type_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_FRAMING_TYPE:
+ l2tp_framing_type_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_PACKET_PROC_DELAY:
+ l2tp_packet_proc_delay_print(ndo);
+ break;
+ case L2TP_AVP_PROXY_AUTH_TYPE:
+ l2tp_proxy_auth_type_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_PROXY_AUTH_ID:
+ l2tp_proxy_auth_id_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_CALL_ERRORS:
+ l2tp_call_errors_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_ACCM:
+ l2tp_accm_print(ndo, dat, len-6);
+ break;
+ case L2TP_AVP_SEQ_REQUIRED:
+ break; /* No Attribute Value */
+ case L2TP_AVP_PPP_DISCON_CC:
+ l2tp_ppp_discon_cc_print(ndo, dat, len-6);
+ break;
+ default:
+ break;
+ }
+ }
+ ND_PRINT(")");
+ }
+
+ return (len);
+
+ trunc:
+ nd_print_trunc(ndo);
+ return (0);
+}
+
+
+void
+l2tp_print(netdissect_options *ndo, const u_char *dat, u_int length)
+{
+ const u_char *ptr = dat;
+ u_int cnt = 0; /* total octets consumed */
+ uint16_t pad;
+ int flag_t, flag_l, flag_s, flag_o;
+ uint16_t l2tp_len;
+
+ ndo->ndo_protocol = "l2tp";
+ flag_t = flag_l = flag_s = flag_o = FALSE;
+
+ if ((GET_BE_U_2(ptr) & L2TP_VERSION_MASK) == L2TP_VERSION_L2TP) {
+ ND_PRINT(" l2tp:");
+ } else if ((GET_BE_U_2(ptr) & L2TP_VERSION_MASK) == L2TP_VERSION_L2F) {
+ ND_PRINT(" l2f:");
+ return; /* nothing to do */
+ } else {
+ ND_PRINT(" Unknown Version, neither L2F(1) nor L2TP(2)");
+ return; /* nothing we can do */
+ }
+
+ ND_PRINT("[");
+ if (GET_BE_U_2(ptr) & L2TP_FLAG_TYPE) {
+ flag_t = TRUE;
+ ND_PRINT("T");
+ }
+ if (GET_BE_U_2(ptr) & L2TP_FLAG_LENGTH) {
+ flag_l = TRUE;
+ ND_PRINT("L");
+ }
+ if (GET_BE_U_2(ptr) & L2TP_FLAG_SEQUENCE) {
+ flag_s = TRUE;
+ ND_PRINT("S");
+ }
+ if (GET_BE_U_2(ptr) & L2TP_FLAG_OFFSET) {
+ flag_o = TRUE;
+ ND_PRINT("O");
+ }
+ if (GET_BE_U_2(ptr) & L2TP_FLAG_PRIORITY)
+ ND_PRINT("P");
+ ND_PRINT("]");
+
+ ptr += 2;
+ cnt += 2;
+
+ if (flag_l) {
+ l2tp_len = GET_BE_U_2(ptr);
+ ptr += 2;
+ cnt += 2;
+ } else {
+ l2tp_len = 0;
+ }
+ /* Tunnel ID */
+ ND_PRINT("(%u/", GET_BE_U_2(ptr));
+ ptr += 2;
+ cnt += 2;
+ /* Session ID */
+ ND_PRINT("%u)", GET_BE_U_2(ptr));
+ ptr += 2;
+ cnt += 2;
+
+ if (flag_s) {
+ ND_PRINT("Ns=%u,", GET_BE_U_2(ptr));
+ ptr += 2;
+ cnt += 2;
+ ND_PRINT("Nr=%u", GET_BE_U_2(ptr));
+ ptr += 2;
+ cnt += 2;
+ }
+
+ if (flag_o) { /* Offset Size */
+ pad = GET_BE_U_2(ptr);
+ ptr += (2 + pad);
+ cnt += (2 + pad);
+ }
+
+ if (flag_l) {
+ if (length < l2tp_len) {
+ ND_PRINT(" Length %u larger than packet", l2tp_len);
+ return;
+ }
+ length = l2tp_len;
+ }
+ if (length < cnt) {
+ ND_PRINT(" Length %u smaller than header length", length);
+ return;
+ }
+ if (flag_t) {
+ if (!flag_l) {
+ ND_PRINT(" No length");
+ return;
+ }
+ if (length - cnt == 0) {
+ ND_PRINT(" ZLB");
+ } else {
+ /*
+ * Print AVPs.
+ */
+ while (length - cnt != 0) {
+ u_int avp_length;
+
+ avp_length = l2tp_avp_print(ndo, ptr, length - cnt);
+ if (avp_length == 0) {
+ /*
+ * Truncated.
+ */
+ break;
+ }
+ cnt += avp_length;
+ ptr += avp_length;
+ }
+ }
+ } else {
+ ND_PRINT(" {");
+ ppp_print(ndo, ptr, length - cnt);
+ ND_PRINT("}");
+ }
+}
diff --git a/print-lane.c b/print-lane.c
new file mode 100644
index 0000000..c5fa33b
--- /dev/null
+++ b/print-lane.c
@@ -0,0 +1,110 @@
+/*
+ * Marko Kiiskila carnil@cs.tut.fi
+ *
+ * Tampere University of Technology - Telecommunications Laboratory
+ *
+ * Permission to use, copy, modify and distribute this
+ * software and its documentation is hereby granted,
+ * provided that both the copyright notice and this
+ * permission notice appear in all copies of the software,
+ * derivative works or modified versions, and any portions
+ * thereof, that both notices appear in supporting
+ * documentation, and that the use of this software is
+ * acknowledged in any publications resulting from using
+ * the software.
+ *
+ * TUT ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION AND DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS
+ * SOFTWARE.
+ *
+ */
+
+/* \summary: ATM LANE printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+struct lecdatahdr_8023 {
+ nd_uint16_t le_header;
+ nd_mac_addr h_dest;
+ nd_mac_addr h_source;
+ nd_uint16_t h_type;
+};
+
+struct lane_controlhdr {
+ nd_uint16_t lec_header;
+ nd_uint8_t lec_proto;
+ nd_uint8_t lec_vers;
+ nd_uint16_t lec_opcode;
+};
+
+static const struct tok lecop2str[] = {
+ { 0x0001, "configure request" },
+ { 0x0101, "configure response" },
+ { 0x0002, "join request" },
+ { 0x0102, "join response" },
+ { 0x0003, "ready query" },
+ { 0x0103, "ready indication" },
+ { 0x0004, "register request" },
+ { 0x0104, "register response" },
+ { 0x0005, "unregister request" },
+ { 0x0105, "unregister response" },
+ { 0x0006, "ARP request" },
+ { 0x0106, "ARP response" },
+ { 0x0007, "flush request" },
+ { 0x0107, "flush response" },
+ { 0x0008, "NARP request" },
+ { 0x0009, "topology request" },
+ { 0, NULL }
+};
+
+static void
+lane_hdr_print(netdissect_options *ndo, const u_char *bp)
+{
+ ND_PRINT("lecid:%x ", GET_BE_U_2(bp));
+}
+
+/*
+ * This assumes 802.3, not 802.5, LAN emulation.
+ */
+void
+lane_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen)
+{
+ const struct lane_controlhdr *lec;
+
+ ndo->ndo_protocol = "lane";
+
+ lec = (const struct lane_controlhdr *)p;
+ if (GET_BE_U_2(lec->lec_header) == 0xff00) {
+ /*
+ * LE Control.
+ */
+ ND_PRINT("lec: proto %x vers %x %s",
+ GET_U_1(lec->lec_proto),
+ GET_U_1(lec->lec_vers),
+ tok2str(lecop2str, "opcode-#%u", GET_BE_U_2(lec->lec_opcode)));
+ return;
+ }
+
+ /*
+ * Go past the LE header.
+ */
+ ND_TCHECK_2(p); /* Needed */
+ length -= 2;
+ caplen -= 2;
+ p += 2;
+
+ /*
+ * Now print the encapsulated frame, under the assumption
+ * that it's an Ethernet frame.
+ */
+ ether_print(ndo, p, length, caplen, lane_hdr_print, p - 2);
+}
diff --git a/print-ldp.c b/print-ldp.c
new file mode 100644
index 0000000..896bc40
--- /dev/null
+++ b/print-ldp.c
@@ -0,0 +1,700 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ * and Steinar Haug (sthaug@nethelp.no)
+ */
+
+/* \summary: Label Distribution Protocol (LDP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+#include "l2vpn.h"
+#include "af.h"
+
+
+/*
+ * ldp common header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version | PDU Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | LDP Identifier |
+ * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+struct ldp_common_header {
+ nd_uint16_t version;
+ nd_uint16_t pdu_length;
+ nd_ipv4 lsr_id;
+ nd_uint16_t label_space;
+};
+
+#define LDP_VERSION 1
+
+/*
+ * ldp message header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |U| Message Type | Message Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Message ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | Mandatory Parameters |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | Optional Parameters |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct ldp_msg_header {
+ nd_uint16_t type;
+ nd_uint16_t length;
+ nd_uint32_t id;
+};
+
+#define LDP_MASK_MSG_TYPE(x) ((x)&0x7fff)
+#define LDP_MASK_U_BIT(x) ((x)&0x8000)
+
+#define LDP_MSG_NOTIF 0x0001
+#define LDP_MSG_HELLO 0x0100
+#define LDP_MSG_INIT 0x0200
+#define LDP_MSG_KEEPALIVE 0x0201
+#define LDP_MSG_ADDRESS 0x0300
+#define LDP_MSG_ADDRESS_WITHDRAW 0x0301
+#define LDP_MSG_LABEL_MAPPING 0x0400
+#define LDP_MSG_LABEL_REQUEST 0x0401
+#define LDP_MSG_LABEL_WITHDRAW 0x0402
+#define LDP_MSG_LABEL_RELEASE 0x0403
+#define LDP_MSG_LABEL_ABORT_REQUEST 0x0404
+
+#define LDP_VENDOR_PRIVATE_MIN 0x3e00
+#define LDP_VENDOR_PRIVATE_MAX 0x3eff
+#define LDP_EXPERIMENTAL_MIN 0x3f00
+#define LDP_EXPERIMENTAL_MAX 0x3fff
+
+static const struct tok ldp_msg_values[] = {
+ { LDP_MSG_NOTIF, "Notification" },
+ { LDP_MSG_HELLO, "Hello" },
+ { LDP_MSG_INIT, "Initialization" },
+ { LDP_MSG_KEEPALIVE, "Keepalive" },
+ { LDP_MSG_ADDRESS, "Address" },
+ { LDP_MSG_ADDRESS_WITHDRAW, "Address Withdraw" },
+ { LDP_MSG_LABEL_MAPPING, "Label Mapping" },
+ { LDP_MSG_LABEL_REQUEST, "Label Request" },
+ { LDP_MSG_LABEL_WITHDRAW, "Label Withdraw" },
+ { LDP_MSG_LABEL_RELEASE, "Label Release" },
+ { LDP_MSG_LABEL_ABORT_REQUEST, "Label Abort Request" },
+ { 0, NULL}
+};
+
+#define LDP_MASK_TLV_TYPE(x) ((x)&0x3fff)
+#define LDP_MASK_F_BIT(x) ((x)&0x4000)
+
+#define LDP_TLV_FEC 0x0100
+#define LDP_TLV_ADDRESS_LIST 0x0101
+#define LDP_TLV_ADDRESS_LIST_AFNUM_LEN 2
+#define LDP_TLV_HOP_COUNT 0x0103
+#define LDP_TLV_PATH_VECTOR 0x0104
+#define LDP_TLV_GENERIC_LABEL 0x0200
+#define LDP_TLV_ATM_LABEL 0x0201
+#define LDP_TLV_FR_LABEL 0x0202
+#define LDP_TLV_STATUS 0x0300
+#define LDP_TLV_EXTD_STATUS 0x0301
+#define LDP_TLV_RETURNED_PDU 0x0302
+#define LDP_TLV_RETURNED_MSG 0x0303
+#define LDP_TLV_COMMON_HELLO 0x0400
+#define LDP_TLV_IPV4_TRANSPORT_ADDR 0x0401
+#define LDP_TLV_CONFIG_SEQ_NUMBER 0x0402
+#define LDP_TLV_IPV6_TRANSPORT_ADDR 0x0403
+#define LDP_TLV_COMMON_SESSION 0x0500
+#define LDP_TLV_ATM_SESSION_PARM 0x0501
+#define LDP_TLV_FR_SESSION_PARM 0x0502
+#define LDP_TLV_FT_SESSION 0x0503
+#define LDP_TLV_LABEL_REQUEST_MSG_ID 0x0600
+#define LDP_TLV_MTU 0x0601 /* rfc 3988 */
+
+static const struct tok ldp_tlv_values[] = {
+ { LDP_TLV_FEC, "FEC" },
+ { LDP_TLV_ADDRESS_LIST, "Address List" },
+ { LDP_TLV_HOP_COUNT, "Hop Count" },
+ { LDP_TLV_PATH_VECTOR, "Path Vector" },
+ { LDP_TLV_GENERIC_LABEL, "Generic Label" },
+ { LDP_TLV_ATM_LABEL, "ATM Label" },
+ { LDP_TLV_FR_LABEL, "Frame-Relay Label" },
+ { LDP_TLV_STATUS, "Status" },
+ { LDP_TLV_EXTD_STATUS, "Extended Status" },
+ { LDP_TLV_RETURNED_PDU, "Returned PDU" },
+ { LDP_TLV_RETURNED_MSG, "Returned Message" },
+ { LDP_TLV_COMMON_HELLO, "Common Hello Parameters" },
+ { LDP_TLV_IPV4_TRANSPORT_ADDR, "IPv4 Transport Address" },
+ { LDP_TLV_CONFIG_SEQ_NUMBER, "Configuration Sequence Number" },
+ { LDP_TLV_IPV6_TRANSPORT_ADDR, "IPv6 Transport Address" },
+ { LDP_TLV_COMMON_SESSION, "Common Session Parameters" },
+ { LDP_TLV_ATM_SESSION_PARM, "ATM Session Parameters" },
+ { LDP_TLV_FR_SESSION_PARM, "Frame-Relay Session Parameters" },
+ { LDP_TLV_FT_SESSION, "Fault-Tolerant Session Parameters" },
+ { LDP_TLV_LABEL_REQUEST_MSG_ID, "Label Request Message ID" },
+ { LDP_TLV_MTU, "MTU" },
+ { 0, NULL}
+};
+
+#define LDP_FEC_WILDCARD 0x01
+#define LDP_FEC_PREFIX 0x02
+#define LDP_FEC_HOSTADDRESS 0x03
+/* From RFC 4906; should probably be updated to RFC 4447 (e.g., VC -> PW) */
+#define LDP_FEC_MARTINI_VC 0x80
+
+static const struct tok ldp_fec_values[] = {
+ { LDP_FEC_WILDCARD, "Wildcard" },
+ { LDP_FEC_PREFIX, "Prefix" },
+ { LDP_FEC_HOSTADDRESS, "Host address" },
+ { LDP_FEC_MARTINI_VC, "Martini VC" },
+ { 0, NULL}
+};
+
+#define LDP_FEC_MARTINI_IFPARM_MTU 0x01
+#define LDP_FEC_MARTINI_IFPARM_DESC 0x03
+#define LDP_FEC_MARTINI_IFPARM_VCCV 0x0c
+
+static const struct tok ldp_fec_martini_ifparm_values[] = {
+ { LDP_FEC_MARTINI_IFPARM_MTU, "MTU" },
+ { LDP_FEC_MARTINI_IFPARM_DESC, "Description" },
+ { LDP_FEC_MARTINI_IFPARM_VCCV, "VCCV" },
+ { 0, NULL}
+};
+
+/* draft-ietf-pwe3-vccv-04.txt */
+static const struct tok ldp_fec_martini_ifparm_vccv_cc_values[] = {
+ { 0x01, "PWE3 control word" },
+ { 0x02, "MPLS Router Alert Label" },
+ { 0x04, "MPLS inner label TTL = 1" },
+ { 0, NULL}
+};
+
+/* draft-ietf-pwe3-vccv-04.txt */
+static const struct tok ldp_fec_martini_ifparm_vccv_cv_values[] = {
+ { 0x01, "ICMP Ping" },
+ { 0x02, "LSP Ping" },
+ { 0x04, "BFD" },
+ { 0, NULL}
+};
+
+static u_int ldp_pdu_print(netdissect_options *, const u_char *);
+
+/*
+ * ldp tlv header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |U|F| Type | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Value |
+ * ~ ~
+ * | |
+ * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+#define TLV_TCHECK(minlen) \
+ if (tlv_tlen < minlen) { \
+ ND_PRINT(" [tlv length %u < %u]", tlv_tlen, minlen); \
+ nd_print_invalid(ndo); \
+ goto invalid; \
+ }
+
+static u_int
+ldp_tlv_print(netdissect_options *ndo,
+ const u_char *tptr,
+ u_int msg_tlen)
+{
+ struct ldp_tlv_header {
+ nd_uint16_t type;
+ nd_uint16_t length;
+ };
+
+ const struct ldp_tlv_header *ldp_tlv_header;
+ u_short tlv_type,tlv_len,tlv_tlen,af,ft_flags;
+ u_char fec_type;
+ u_int ui,vc_info_len, vc_info_tlv_type, vc_info_tlv_len,idx;
+ char buf[100];
+ int i;
+
+ ldp_tlv_header = (const struct ldp_tlv_header *)tptr;
+ ND_TCHECK_SIZE(ldp_tlv_header);
+ tlv_len=GET_BE_U_2(ldp_tlv_header->length);
+ if (tlv_len + 4U > msg_tlen) {
+ ND_PRINT("\n\t\t TLV contents go past end of message");
+ return 0;
+ }
+ tlv_tlen=tlv_len;
+ tlv_type=LDP_MASK_TLV_TYPE(GET_BE_U_2(ldp_tlv_header->type));
+
+ /* FIXME vendor private / experimental check */
+ ND_PRINT("\n\t %s TLV (0x%04x), length: %u, Flags: [%s and %s forward if unknown]",
+ tok2str(ldp_tlv_values,
+ "Unknown",
+ tlv_type),
+ tlv_type,
+ tlv_len,
+ LDP_MASK_U_BIT(GET_BE_U_2(ldp_tlv_header->type)) ? "continue processing" : "ignore",
+ LDP_MASK_F_BIT(GET_BE_U_2(ldp_tlv_header->type)) ? "do" : "don't");
+
+ tptr+=sizeof(struct ldp_tlv_header);
+
+ switch(tlv_type) {
+
+ case LDP_TLV_COMMON_HELLO:
+ TLV_TCHECK(4);
+ ND_PRINT("\n\t Hold Time: %us, Flags: [%s Hello%s]",
+ GET_BE_U_2(tptr),
+ (GET_BE_U_2(tptr + 2)&0x8000) ? "Targeted" : "Link",
+ (GET_BE_U_2(tptr + 2)&0x4000) ? ", Request for targeted Hellos" : "");
+ break;
+
+ case LDP_TLV_IPV4_TRANSPORT_ADDR:
+ TLV_TCHECK(4);
+ ND_PRINT("\n\t IPv4 Transport Address: %s", GET_IPADDR_STRING(tptr));
+ break;
+ case LDP_TLV_IPV6_TRANSPORT_ADDR:
+ TLV_TCHECK(16);
+ ND_PRINT("\n\t IPv6 Transport Address: %s", GET_IP6ADDR_STRING(tptr));
+ break;
+ case LDP_TLV_CONFIG_SEQ_NUMBER:
+ TLV_TCHECK(4);
+ ND_PRINT("\n\t Sequence Number: %u", GET_BE_U_4(tptr));
+ break;
+
+ case LDP_TLV_ADDRESS_LIST:
+ TLV_TCHECK(LDP_TLV_ADDRESS_LIST_AFNUM_LEN);
+ af = GET_BE_U_2(tptr);
+ tptr+=LDP_TLV_ADDRESS_LIST_AFNUM_LEN;
+ tlv_tlen -= LDP_TLV_ADDRESS_LIST_AFNUM_LEN;
+ ND_PRINT("\n\t Address Family: %s, addresses",
+ tok2str(af_values, "Unknown (%u)", af));
+ switch (af) {
+ case AFNUM_INET:
+ while(tlv_tlen >= sizeof(nd_ipv4)) {
+ ND_PRINT(" %s", GET_IPADDR_STRING(tptr));
+ tlv_tlen-=sizeof(nd_ipv4);
+ tptr+=sizeof(nd_ipv4);
+ }
+ break;
+ case AFNUM_INET6:
+ while(tlv_tlen >= sizeof(nd_ipv6)) {
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(tptr));
+ tlv_tlen-=sizeof(nd_ipv6);
+ tptr+=sizeof(nd_ipv6);
+ }
+ break;
+ default:
+ /* unknown AF */
+ break;
+ }
+ break;
+
+ case LDP_TLV_COMMON_SESSION:
+ TLV_TCHECK(8);
+ ND_PRINT("\n\t Version: %u, Keepalive: %us, Flags: [Downstream %s, Loop Detection %s]",
+ GET_BE_U_2(tptr), GET_BE_U_2(tptr + 2),
+ (GET_BE_U_2(tptr + 6)&0x8000) ? "On Demand" : "Unsolicited",
+ (GET_BE_U_2(tptr + 6)&0x4000) ? "Enabled" : "Disabled"
+ );
+ break;
+
+ case LDP_TLV_FEC:
+ TLV_TCHECK(1);
+ fec_type = GET_U_1(tptr);
+ ND_PRINT("\n\t %s FEC (0x%02x)",
+ tok2str(ldp_fec_values, "Unknown", fec_type),
+ fec_type);
+
+ tptr+=1;
+ tlv_tlen-=1;
+ switch(fec_type) {
+
+ case LDP_FEC_WILDCARD:
+ break;
+ case LDP_FEC_PREFIX:
+ TLV_TCHECK(2);
+ af = GET_BE_U_2(tptr);
+ tptr+=2;
+ tlv_tlen-=2;
+ if (af == AFNUM_INET) {
+ i=decode_prefix4(ndo, tptr, tlv_tlen, buf, sizeof(buf));
+ if (i == -2)
+ goto trunc;
+ if (i == -3)
+ ND_PRINT(": IPv4 prefix (goes past end of TLV)");
+ else if (i == -1)
+ ND_PRINT(": IPv4 prefix (invalid length)");
+ else
+ ND_PRINT(": IPv4 prefix %s", buf);
+ }
+ else if (af == AFNUM_INET6) {
+ i=decode_prefix6(ndo, tptr, tlv_tlen, buf, sizeof(buf));
+ if (i == -2)
+ goto trunc;
+ if (i == -3)
+ ND_PRINT(": IPv4 prefix (goes past end of TLV)");
+ else if (i == -1)
+ ND_PRINT(": IPv6 prefix (invalid length)");
+ else
+ ND_PRINT(": IPv6 prefix %s", buf);
+ }
+ else
+ ND_PRINT(": Address family %u prefix", af);
+ break;
+ case LDP_FEC_HOSTADDRESS:
+ break;
+ case LDP_FEC_MARTINI_VC:
+ /*
+ * We assume the type was supposed to be one of the MPLS
+ * Pseudowire Types.
+ */
+ TLV_TCHECK(7);
+ vc_info_len = GET_U_1(tptr + 2);
+
+ /*
+ * According to RFC 4908, the VC info Length field can be zero,
+ * in which case not only are there no interface parameters,
+ * there's no VC ID.
+ */
+ if (vc_info_len == 0) {
+ ND_PRINT(": %s, %scontrol word, group-ID %u, VC-info-length: %u",
+ tok2str(mpls_pw_types_values, "Unknown", GET_BE_U_2(tptr)&0x7fff),
+ GET_BE_U_2(tptr)&0x8000 ? "" : "no ",
+ GET_BE_U_4(tptr + 3),
+ vc_info_len);
+ break;
+ }
+
+ /* Make sure we have the VC ID as well */
+ TLV_TCHECK(11);
+ ND_PRINT(": %s, %scontrol word, group-ID %u, VC-ID %u, VC-info-length: %u",
+ tok2str(mpls_pw_types_values, "Unknown", GET_BE_U_2(tptr)&0x7fff),
+ GET_BE_U_2(tptr)&0x8000 ? "" : "no ",
+ GET_BE_U_4(tptr + 3),
+ GET_BE_U_4(tptr + 7),
+ vc_info_len);
+ if (vc_info_len < 4) {
+ /* minimum 4, for the VC ID */
+ ND_PRINT(" (invalid, < 4");
+ return(tlv_len+4); /* Type & Length fields not included */
+ }
+ vc_info_len -= 4; /* subtract out the VC ID, giving the length of the interface parameters */
+
+ /* Skip past the fixed information and the VC ID */
+ tptr+=11;
+ tlv_tlen-=11;
+ TLV_TCHECK(vc_info_len);
+
+ while (vc_info_len > 2) {
+ vc_info_tlv_type = GET_U_1(tptr);
+ vc_info_tlv_len = GET_U_1(tptr + 1);
+ if (vc_info_tlv_len < 2)
+ break;
+ if (vc_info_len < vc_info_tlv_len)
+ break;
+
+ ND_PRINT("\n\t\tInterface Parameter: %s (0x%02x), len %u",
+ tok2str(ldp_fec_martini_ifparm_values,"Unknown",vc_info_tlv_type),
+ vc_info_tlv_type,
+ vc_info_tlv_len);
+
+ switch(vc_info_tlv_type) {
+ case LDP_FEC_MARTINI_IFPARM_MTU:
+ ND_PRINT(": %u", GET_BE_U_2(tptr + 2));
+ break;
+
+ case LDP_FEC_MARTINI_IFPARM_DESC:
+ ND_PRINT(": ");
+ for (idx = 2; idx < vc_info_tlv_len; idx++)
+ fn_print_char(ndo, GET_U_1(tptr + idx));
+ break;
+
+ case LDP_FEC_MARTINI_IFPARM_VCCV:
+ ND_PRINT("\n\t\t Control Channels (0x%02x) = [%s]",
+ GET_U_1((tptr + 2)),
+ bittok2str(ldp_fec_martini_ifparm_vccv_cc_values, "none", GET_U_1((tptr + 2))));
+ ND_PRINT("\n\t\t CV Types (0x%02x) = [%s]",
+ GET_U_1((tptr + 3)),
+ bittok2str(ldp_fec_martini_ifparm_vccv_cv_values, "none", GET_U_1((tptr + 3))));
+ break;
+
+ default:
+ print_unknown_data(ndo, tptr+2, "\n\t\t ", vc_info_tlv_len-2);
+ break;
+ }
+
+ vc_info_len -= vc_info_tlv_len;
+ tptr += vc_info_tlv_len;
+ }
+ break;
+ }
+
+ break;
+
+ case LDP_TLV_GENERIC_LABEL:
+ TLV_TCHECK(4);
+ ND_PRINT("\n\t Label: %u", GET_BE_U_4(tptr) & 0xfffff);
+ break;
+
+ case LDP_TLV_STATUS:
+ TLV_TCHECK(8);
+ ui = GET_BE_U_4(tptr);
+ tptr+=4;
+ ND_PRINT("\n\t Status: 0x%02x, Flags: [%s and %s forward]",
+ ui&0x3fffffff,
+ ui&0x80000000 ? "Fatal error" : "Advisory Notification",
+ ui&0x40000000 ? "do" : "don't");
+ ui = GET_BE_U_4(tptr);
+ tptr+=4;
+ if (ui)
+ ND_PRINT(", causing Message ID: 0x%08x", ui);
+ break;
+
+ case LDP_TLV_FT_SESSION:
+ TLV_TCHECK(12);
+ ft_flags = GET_BE_U_2(tptr);
+ ND_PRINT("\n\t Flags: [%sReconnect, %sSave State, %sAll-Label Protection, %s Checkpoint, %sRe-Learn State]",
+ ft_flags&0x8000 ? "" : "No ",
+ ft_flags&0x8 ? "" : "Don't ",
+ ft_flags&0x4 ? "" : "No ",
+ ft_flags&0x2 ? "Sequence Numbered Label" : "All Labels",
+ ft_flags&0x1 ? "" : "Don't ");
+ /* 16 bits (FT Flags) + 16 bits (Reserved) */
+ tptr+=4;
+ ui = GET_BE_U_4(tptr);
+ if (ui)
+ ND_PRINT(", Reconnect Timeout: %ums", ui);
+ tptr+=4;
+ ui = GET_BE_U_4(tptr);
+ if (ui)
+ ND_PRINT(", Recovery Time: %ums", ui);
+ break;
+
+ case LDP_TLV_MTU:
+ TLV_TCHECK(2);
+ ND_PRINT("\n\t MTU: %u", GET_BE_U_2(tptr));
+ break;
+
+
+ /*
+ * FIXME those are the defined TLVs that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case LDP_TLV_HOP_COUNT:
+ case LDP_TLV_PATH_VECTOR:
+ case LDP_TLV_ATM_LABEL:
+ case LDP_TLV_FR_LABEL:
+ case LDP_TLV_EXTD_STATUS:
+ case LDP_TLV_RETURNED_PDU:
+ case LDP_TLV_RETURNED_MSG:
+ case LDP_TLV_ATM_SESSION_PARM:
+ case LDP_TLV_FR_SESSION_PARM:
+ case LDP_TLV_LABEL_REQUEST_MSG_ID:
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr, "\n\t ", tlv_tlen);
+ break;
+ }
+ return(tlv_len+4); /* Type & Length fields not included */
+
+trunc:
+ nd_trunc_longjmp(ndo);
+
+invalid:
+ return(tlv_len+4); /* Type & Length fields not included */
+}
+
+void
+ldp_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ u_int processed;
+
+ ndo->ndo_protocol = "ldp";
+ while (len > (sizeof(struct ldp_common_header) + sizeof(struct ldp_msg_header))) {
+ processed = ldp_pdu_print(ndo, pptr);
+ if (processed == 0)
+ return;
+ if (len < processed) {
+ ND_PRINT(" [remaining length %u < %u]", len, processed);
+ nd_print_invalid(ndo);
+ break;
+ }
+ len -= processed;
+ pptr += processed;
+ }
+}
+
+static u_int
+ldp_pdu_print(netdissect_options *ndo,
+ const u_char *pptr)
+{
+ const struct ldp_common_header *ldp_com_header;
+ const struct ldp_msg_header *ldp_msg_header;
+ const u_char *tptr,*msg_tptr;
+ u_short tlen;
+ u_short pdu_len,msg_len,msg_type;
+ u_int msg_tlen;
+ int hexdump,processed;
+
+ ldp_com_header = (const struct ldp_common_header *)pptr;
+ ND_TCHECK_SIZE(ldp_com_header);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (GET_BE_U_2(ldp_com_header->version) != LDP_VERSION) {
+ ND_PRINT("%sLDP version %u packet not supported",
+ (ndo->ndo_vflag < 1) ? "" : "\n\t",
+ GET_BE_U_2(ldp_com_header->version));
+ return 0;
+ }
+
+ pdu_len = GET_BE_U_2(ldp_com_header->pdu_length);
+ if (pdu_len < sizeof(struct ldp_common_header)-4) {
+ /* length too short */
+ ND_PRINT("%sLDP, pdu-length: %u (too short, < %zu)",
+ (ndo->ndo_vflag < 1) ? "" : "\n\t",
+ pdu_len,
+ sizeof(struct ldp_common_header)-4);
+ return 0;
+ }
+
+ /* print the LSR-ID, label-space & length */
+ ND_PRINT("%sLDP, Label-Space-ID: %s:%u, pdu-length: %u",
+ (ndo->ndo_vflag < 1) ? "" : "\n\t",
+ GET_IPADDR_STRING(ldp_com_header->lsr_id),
+ GET_BE_U_2(ldp_com_header->label_space),
+ pdu_len);
+
+ /* bail out if non-verbose */
+ if (ndo->ndo_vflag < 1)
+ return 0;
+
+ /* ok they seem to want to know everything - lets fully decode it */
+ tptr = pptr + sizeof(struct ldp_common_header);
+ tlen = pdu_len - (sizeof(struct ldp_common_header)-4); /* Type & Length fields not included */
+
+ while(tlen>0) {
+ /* did we capture enough for fully decoding the msg header ? */
+ ND_TCHECK_LEN(tptr, sizeof(struct ldp_msg_header));
+
+ ldp_msg_header = (const struct ldp_msg_header *)tptr;
+ msg_len=GET_BE_U_2(ldp_msg_header->length);
+ msg_type=LDP_MASK_MSG_TYPE(GET_BE_U_2(ldp_msg_header->type));
+
+ if (msg_len < sizeof(struct ldp_msg_header)-4) {
+ /* length too short */
+ /* FIXME vendor private / experimental check */
+ ND_PRINT("\n\t %s Message (0x%04x), length: %u (too short, < %zu)",
+ tok2str(ldp_msg_values,
+ "Unknown",
+ msg_type),
+ msg_type,
+ msg_len,
+ sizeof(struct ldp_msg_header)-4);
+ return 0;
+ }
+
+ /* FIXME vendor private / experimental check */
+ ND_PRINT("\n\t %s Message (0x%04x), length: %u, Message ID: 0x%08x, Flags: [%s if unknown]",
+ tok2str(ldp_msg_values,
+ "Unknown",
+ msg_type),
+ msg_type,
+ msg_len,
+ GET_BE_U_4(ldp_msg_header->id),
+ LDP_MASK_U_BIT(GET_BE_U_2(ldp_msg_header->type)) ? "continue processing" : "ignore");
+
+ msg_tptr=tptr+sizeof(struct ldp_msg_header);
+ msg_tlen=msg_len-(sizeof(struct ldp_msg_header)-4); /* Type & Length fields not included */
+
+ /* did we capture enough for fully decoding the message ? */
+ ND_TCHECK_LEN(tptr, msg_len);
+ hexdump=FALSE;
+
+ switch(msg_type) {
+
+ case LDP_MSG_NOTIF:
+ case LDP_MSG_HELLO:
+ case LDP_MSG_INIT:
+ case LDP_MSG_KEEPALIVE:
+ case LDP_MSG_ADDRESS:
+ case LDP_MSG_LABEL_MAPPING:
+ case LDP_MSG_ADDRESS_WITHDRAW:
+ case LDP_MSG_LABEL_WITHDRAW:
+ while(msg_tlen >= 4) {
+ processed = ldp_tlv_print(ndo, msg_tptr, msg_tlen);
+ if (processed == 0)
+ break;
+ msg_tlen-=processed;
+ msg_tptr+=processed;
+ }
+ break;
+
+ /*
+ * FIXME those are the defined messages that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case LDP_MSG_LABEL_REQUEST:
+ case LDP_MSG_LABEL_RELEASE:
+ case LDP_MSG_LABEL_ABORT_REQUEST:
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, msg_tptr, "\n\t ", msg_tlen);
+ break;
+ }
+ /* do we want to see an additionally hexdump ? */
+ if (ndo->ndo_vflag > 1 || hexdump==TRUE)
+ print_unknown_data(ndo, tptr+sizeof(struct ldp_msg_header), "\n\t ",
+ msg_len);
+
+ tptr += msg_len+4;
+ tlen -= msg_len+4;
+ }
+ return pdu_len+4;
+trunc:
+ nd_trunc_longjmp(ndo);
+}
diff --git a/print-lisp.c b/print-lisp.c
new file mode 100644
index 0000000..0012e06
--- /dev/null
+++ b/print-lisp.c
@@ -0,0 +1,452 @@
+/*
+ * Copyright (c) 2015 Ritesh Ranjan (r.ranjan789@gmail.com)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: - Locator/Identifier Separation Protocol (LISP) printer */
+
+/*
+ * specification: RFC 6830
+ *
+ *
+ * The Map-Register message format is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Type=3 |P|S|I|R| Reserved |M| Record Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nonce . . . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . . . Nonce |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key ID | Authentication Data Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ~ Authentication Data ~
+ * +-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | Record TTL |
+ * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * R | Locator Count | EID mask-len | ACT |A| Reserved |
+ * e +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * c | Rsvd | Map-Version Number | EID-Prefix-AFI |
+ * o +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * r | EID-Prefix |
+ * d +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | /| Priority | Weight | M Priority | M Weight |
+ * | L +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | o | Unused Flags |L|p|R| Loc-AFI |
+ * | c +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | \| Locator |
+ * +-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * The Map-Notify message format is:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Type=4 |I|R| Reserved | Record Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nonce . . . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . . . Nonce |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key ID | Authentication Data Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * ~ Authentication Data ~
+ * +-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | Record TTL |
+ * | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * R | Locator Count | EID mask-len | ACT |A| Reserved |
+ * e +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * c | Rsvd | Map-Version Number | EID-Prefix-AFI |
+ * o +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * r | EID-Prefix |
+ * d +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | /| Priority | Weight | M Priority | M Weight |
+ * | L +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | o | Unused Flags |L|p|R| Loc-AFI |
+ * | c +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | \| Locator |
+ * +-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+
+#include "ip.h"
+#include "ip6.h"
+
+#include "extract.h"
+#include "addrtoname.h"
+
+
+#define IPv4_AFI 1
+#define IPv6_AFI 2
+#define TYPE_INDEX 4
+#define LISP_MAP_NOTIFY_IBIT_MASK 8
+#define LISP_MAP_REGISTER_IBIT_MASK 2
+
+enum {
+ LISP_MAP_REQUEST = 1,
+ LISP_MAP_REPLY,
+ LISP_MAP_REGISTER,
+ LISP_MAP_NOTIFY,
+ LISP_ENCAPSULATED_CONTROL_MESSAGE = 8
+};
+
+enum {
+ LISP_AUTH_NONE,
+ LISP_AUTH_SHA1,
+ LISP_AUTH_SHA256
+};
+
+static const struct tok lisp_type [] = {
+ { 0, "LISP-Reserved" },
+ { 1, "LISP-Map-Request" },
+ { 2, "LISP-Map-Reply" },
+ { 3, "LISP-Map-Register" },
+ { 4, "LISP-Map-Notify" },
+ { 8, "LISP-Encapsulated-Contol-Message" },
+ { 0, NULL }
+};
+
+/*
+ * P-Bit : Request for Proxy Map-Reply from the MS/MR
+ * S-Bit : Security Enhancement. ETR is LISP-SEC enabled. draft-ietf-lisp-sec
+ * I-Bit : 128 bit xTR-ID and 64 bit Site-ID present.
+ * xTR-ID and Site-ID help in differentiation of xTRs in multi xTR
+ * and multi Site deployment scenarios.
+ * R-Bit : Built for a Reencapsulating-Tunnel-Router. Used in Traffic
+ * Engineering and Service Chaining
+ */
+static const struct tok map_register_hdr_flag[] = {
+ { 0x08000000, "P-Proxy-Map-Reply" },
+ { 0x04000000, "S-LISP-SEC-Capable" },
+ { 0x02000000, "I-xTR-ID-Present" },
+ { 0x01000000, "R-Build-For-RTR" },
+ { 0x00000100, "M-Want-Map-Notify" },
+ { 0, NULL }
+};
+
+static const struct tok map_notify_hdr_flag[] = {
+ { 0x08000000, "I-xTR-ID-Present" },
+ { 0x04000000, "R-Build-For-RTR" },
+ { 0, NULL }
+};
+
+static const struct tok auth_type[] = {
+ { LISP_AUTH_NONE, "None" },
+ { LISP_AUTH_SHA1, "SHA1" },
+ { LISP_AUTH_SHA256, "SHA256" },
+ { 0, NULL}
+};
+
+static const struct tok lisp_eid_action[] = {
+ { 0, "No-Action" },
+ { 1, "Natively-Forward" },
+ { 2, "Send-Map-Request" },
+ { 3, "Drop" },
+ { 0, NULL}
+};
+
+static const struct tok lisp_loc_flag[] = {
+ { 0x0004, "Local-Locator" },
+ { 0x0002, "RLoc-Probed" },
+ { 0x0001, "Reachable" },
+ { 0, NULL }
+};
+
+typedef struct map_register_hdr {
+ nd_uint8_t type_and_flag;
+ nd_uint8_t reserved;
+ nd_uint8_t reserved_and_flag2;
+ nd_uint8_t record_count;
+ nd_uint64_t nonce;
+ nd_uint16_t key_id;
+ nd_uint16_t auth_data_len;
+} lisp_map_register_hdr;
+
+#define MAP_REGISTER_HDR_LEN sizeof(lisp_map_register_hdr)
+
+typedef struct map_register_eid {
+ nd_uint32_t ttl;
+ nd_uint8_t locator_count;
+ nd_uint8_t eid_prefix_mask_length;
+ nd_uint8_t act_auth_inc_res;
+ nd_uint8_t reserved;
+ nd_uint16_t reserved_and_version;
+ nd_uint16_t eid_prefix_afi;
+} lisp_map_register_eid;
+
+#define MAP_REGISTER_EID_LEN sizeof(lisp_map_register_eid)
+
+typedef struct map_register_loc {
+ nd_uint8_t priority;
+ nd_uint8_t weight;
+ nd_uint8_t m_priority;
+ nd_uint8_t m_weight;
+ nd_uint16_t unused_and_flag;
+ nd_uint16_t locator_afi;
+} lisp_map_register_loc;
+
+#define MAP_REGISTER_LOC_LEN sizeof(lisp_map_register_loc)
+
+static uint8_t extract_lisp_type(uint8_t);
+static uint8_t is_xtr_data_present(uint8_t, uint8_t);
+static void lisp_hdr_flag(netdissect_options *, const lisp_map_register_hdr *);
+static void action_flag(netdissect_options *, uint8_t);
+static void loc_hdr_flag(netdissect_options *, uint16_t);
+
+void
+lisp_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ uint8_t type_and_flag;
+ uint8_t type;
+ uint8_t mask_len;
+ uint8_t loc_count;
+ uint8_t xtr_present;
+ uint8_t record_count;
+ uint16_t key_id;
+ uint16_t eid_afi;
+ uint16_t loc_afi;
+ uint16_t map_version;
+ uint16_t packet_offset;
+ uint16_t auth_data_len;
+ uint32_t ttl;
+ const u_char *packet_iterator;
+ const u_char *loc_ip_pointer;
+ const lisp_map_register_hdr *lisp_hdr;
+ const lisp_map_register_eid *lisp_eid;
+ const lisp_map_register_loc *lisp_loc;
+
+ ndo->ndo_protocol = "lisp";
+ /* Check if enough bytes for header are available */
+ ND_TCHECK_LEN(bp, MAP_REGISTER_HDR_LEN);
+ lisp_hdr = (const lisp_map_register_hdr *) bp;
+ lisp_hdr_flag(ndo, lisp_hdr);
+ /* Supporting only MAP NOTIFY and MAP REGISTER LISP packets */
+ type_and_flag = GET_U_1(lisp_hdr->type_and_flag);
+ type = extract_lisp_type(type_and_flag);
+ if ((type != LISP_MAP_REGISTER) && (type != LISP_MAP_NOTIFY))
+ return;
+
+ /* Find if the packet contains xTR and Site-ID data */
+ xtr_present = is_xtr_data_present(type, type_and_flag);
+
+ /* Extract the number of EID records present */
+ auth_data_len = GET_BE_U_2(lisp_hdr->auth_data_len);
+ packet_iterator = (const u_char *)(lisp_hdr);
+ packet_offset = MAP_REGISTER_HDR_LEN;
+ record_count = GET_U_1(lisp_hdr->record_count);
+
+ if (ndo->ndo_vflag) {
+ key_id = GET_BE_U_2(lisp_hdr->key_id);
+ ND_PRINT("\n %u record(s), ", record_count);
+ ND_PRINT("Authentication %s,",
+ tok2str(auth_type, "unknown-type", key_id));
+ hex_print(ndo, "\n Authentication-Data: ", packet_iterator +
+ packet_offset, auth_data_len);
+ } else {
+ ND_PRINT(" %u record(s),", record_count);
+ }
+ packet_offset += auth_data_len;
+
+ if (record_count == 0)
+ goto invalid;
+
+ /* Print all the EID records */
+ while ((length > packet_offset) && (record_count--)) {
+
+ ND_TCHECK_LEN(packet_iterator + packet_offset,
+ MAP_REGISTER_EID_LEN);
+ ND_PRINT("\n");
+ lisp_eid = (const lisp_map_register_eid *)
+ ((const u_char *)lisp_hdr + packet_offset);
+ packet_offset += MAP_REGISTER_EID_LEN;
+ mask_len = GET_U_1(lisp_eid->eid_prefix_mask_length);
+ eid_afi = GET_BE_U_2(lisp_eid->eid_prefix_afi);
+ loc_count = GET_U_1(lisp_eid->locator_count);
+
+ if (ndo->ndo_vflag) {
+ ttl = GET_BE_U_4(lisp_eid->ttl);
+ ND_PRINT(" Record TTL %u,", ttl);
+ action_flag(ndo, GET_U_1(lisp_eid->act_auth_inc_res));
+ map_version = GET_BE_U_2(lisp_eid->reserved_and_version) & 0x0FFF;
+ ND_PRINT(" Map Version: %u,", map_version);
+ }
+
+ switch (eid_afi) {
+ case IPv4_AFI:
+ ND_PRINT(" EID %s/%u,",
+ GET_IPADDR_STRING(packet_iterator + packet_offset),
+ mask_len);
+ packet_offset += 4;
+ break;
+ case IPv6_AFI:
+ ND_PRINT(" EID %s/%u,",
+ GET_IP6ADDR_STRING(packet_iterator + packet_offset),
+ mask_len);
+ packet_offset += 16;
+ break;
+ default:
+ /*
+ * No support for LCAF right now.
+ */
+ return;
+ break;
+ }
+
+ ND_PRINT(" %u locator(s)", loc_count);
+
+ while (loc_count--) {
+ ND_TCHECK_LEN(packet_iterator + packet_offset,
+ MAP_REGISTER_LOC_LEN);
+ lisp_loc = (const lisp_map_register_loc *) (packet_iterator + packet_offset);
+ loc_ip_pointer = (const u_char *) (lisp_loc + 1);
+ packet_offset += MAP_REGISTER_LOC_LEN;
+ loc_afi = GET_BE_U_2(lisp_loc->locator_afi);
+
+ if (ndo->ndo_vflag)
+ ND_PRINT("\n ");
+
+ switch (loc_afi) {
+ case IPv4_AFI:
+ ND_TCHECK_4(packet_iterator + packet_offset);
+ ND_PRINT(" LOC %s", GET_IPADDR_STRING(loc_ip_pointer));
+ packet_offset += 4;
+ break;
+ case IPv6_AFI:
+ ND_TCHECK_16(packet_iterator + packet_offset);
+ ND_PRINT(" LOC %s", GET_IP6ADDR_STRING(loc_ip_pointer));
+ packet_offset += 16;
+ break;
+ default:
+ break;
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT("\n Priority/Weight %u/%u,"
+ " Multicast Priority/Weight %u/%u,",
+ GET_U_1(lisp_loc->priority),
+ GET_U_1(lisp_loc->weight),
+ GET_U_1(lisp_loc->m_priority),
+ GET_U_1(lisp_loc->m_weight));
+ loc_hdr_flag(ndo,
+ GET_BE_U_2(lisp_loc->unused_and_flag));
+ }
+ }
+ }
+
+ /*
+ * Print xTR and Site ID. Handle the fact that the packet could be invalid.
+ * If the xTR_ID_Present bit is not set, and we still have data to display,
+ * show it as hex data.
+ */
+ if (xtr_present) {
+ if (!ND_TTEST_LEN(packet_iterator + packet_offset, 24))
+ goto invalid;
+ hex_print(ndo, "\n xTR-ID: ", packet_iterator + packet_offset, 16);
+ ND_PRINT("\n SITE-ID: %" PRIu64,
+ GET_BE_U_8(packet_iterator + packet_offset + 16));
+ } else {
+ /* Check if packet isn't over yet */
+ if (packet_iterator + packet_offset < ndo->ndo_snapend) {
+ hex_print(ndo, "\n Data: ", packet_iterator + packet_offset,
+ ND_BYTES_AVAILABLE_AFTER(packet_iterator + packet_offset));
+ }
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+ return;
+invalid:
+ nd_print_invalid(ndo);
+}
+
+static uint8_t
+extract_lisp_type(uint8_t lisp_hdr_flags)
+{
+ return (lisp_hdr_flags) >> TYPE_INDEX;
+}
+
+static uint8_t
+is_xtr_data_present(uint8_t type, uint8_t lisp_hdr_flags)
+{
+ uint8_t xtr_present = 0;
+
+ if (type == LISP_MAP_REGISTER)
+ xtr_present = (lisp_hdr_flags) & LISP_MAP_REGISTER_IBIT_MASK;
+ else if (type == LISP_MAP_NOTIFY)
+ xtr_present = (lisp_hdr_flags) & LISP_MAP_NOTIFY_IBIT_MASK;
+
+ return xtr_present;
+}
+
+static void lisp_hdr_flag(netdissect_options *ndo, const lisp_map_register_hdr *lisp_hdr)
+{
+ uint8_t type = extract_lisp_type(GET_U_1(lisp_hdr->type_and_flag));
+
+ if (!ndo->ndo_vflag) {
+ ND_PRINT("%s,", tok2str(lisp_type, "unknown-type-%u", type));
+ return;
+ } else {
+ ND_PRINT("%s,", tok2str(lisp_type, "unknown-type-%u", type));
+ }
+
+ if (type == LISP_MAP_REGISTER) {
+ ND_PRINT(" flags [%s],", bittok2str(map_register_hdr_flag,
+ "none", GET_BE_U_4(lisp_hdr)));
+ } else if (type == LISP_MAP_NOTIFY) {
+ ND_PRINT(" flags [%s],", bittok2str(map_notify_hdr_flag,
+ "none", GET_BE_U_4(lisp_hdr)));
+ }
+}
+
+static void action_flag(netdissect_options *ndo, uint8_t act_auth_inc_res)
+{
+ uint8_t action;
+ uint8_t authoritative;
+
+ authoritative = ((act_auth_inc_res >> 4) & 1);
+
+ if (authoritative)
+ ND_PRINT(" Authoritative,");
+ else
+ ND_PRINT(" Non-Authoritative,");
+
+ action = act_auth_inc_res >> 5;
+ ND_PRINT(" %s,", tok2str(lisp_eid_action, "unknown", action));
+}
+
+static void loc_hdr_flag(netdissect_options *ndo, uint16_t flag)
+{
+ ND_PRINT(" flags [%s],", bittok2str(lisp_loc_flag, "none", flag));
+}
+
diff --git a/print-llc.c b/print-llc.c
new file mode 100644
index 0000000..ae3ae4c
--- /dev/null
+++ b/print-llc.c
@@ -0,0 +1,610 @@
+/*
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Code by Matt Thomas, Digital Equipment Corporation
+ * with an awful lot of hacking by Jeffrey Mogul, DECWRL
+ */
+
+/* \summary: IEEE 802.2 LLC printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "llc.h"
+#include "ethertype.h"
+#include "oui.h"
+
+static const struct tok llc_values[] = {
+ { LLCSAP_NULL, "Null" },
+ { LLCSAP_GLOBAL, "Global" },
+ { LLCSAP_8021B_I, "802.1B I" },
+ { LLCSAP_8021B_G, "802.1B G" },
+ { LLCSAP_IP, "IP" },
+ { LLCSAP_SNA, "SNA" },
+ { LLCSAP_PROWAYNM, "ProWay NM" },
+ { LLCSAP_8021D, "STP" },
+ { LLCSAP_RS511, "RS511" },
+ { LLCSAP_ISO8208, "ISO8208" },
+ { LLCSAP_PROWAY, "ProWay" },
+ { LLCSAP_SNAP, "SNAP" },
+ { LLCSAP_IPX, "IPX" },
+ { LLCSAP_NETBEUI, "NetBeui" },
+ { LLCSAP_ISONS, "OSI" },
+ { 0, NULL },
+};
+
+static const struct tok llc_cmd_values[] = {
+ { LLC_UI, "ui" },
+ { LLC_TEST, "test" },
+ { LLC_XID, "xid" },
+ { LLC_UA, "ua" },
+ { LLC_DISC, "disc" },
+ { LLC_DM, "dm" },
+ { LLC_SABME, "sabme" },
+ { LLC_FRMR, "frmr" },
+ { 0, NULL }
+};
+
+static const struct tok llc_flag_values[] = {
+ { 0, "Command" },
+ { LLC_GSAP, "Response" },
+ { LLC_U_POLL, "Poll" },
+ { LLC_GSAP|LLC_U_POLL, "Final" },
+ { LLC_IS_POLL, "Poll" },
+ { LLC_GSAP|LLC_IS_POLL, "Final" },
+ { 0, NULL }
+};
+
+
+static const struct tok llc_ig_flag_values[] = {
+ { 0, "Individual" },
+ { LLC_IG, "Group" },
+ { 0, NULL }
+};
+
+
+static const struct tok llc_supervisory_values[] = {
+ { 0, "Receiver Ready" },
+ { 1, "Receiver not Ready" },
+ { 2, "Reject" },
+ { 0, NULL }
+};
+
+
+static const struct tok cisco_values[] = {
+ { PID_CISCO_CDP, "CDP" },
+ { PID_CISCO_VTP, "VTP" },
+ { PID_CISCO_DTP, "DTP" },
+ { PID_CISCO_UDLD, "UDLD" },
+ { PID_CISCO_PVST, "PVST" },
+ { PID_CISCO_VLANBRIDGE, "VLAN Bridge" },
+ { 0, NULL }
+};
+
+static const struct tok bridged_values[] = {
+ { PID_RFC2684_ETH_FCS, "Ethernet + FCS" },
+ { PID_RFC2684_ETH_NOFCS, "Ethernet w/o FCS" },
+ { PID_RFC2684_802_4_FCS, "802.4 + FCS" },
+ { PID_RFC2684_802_4_NOFCS, "802.4 w/o FCS" },
+ { PID_RFC2684_802_5_FCS, "Token Ring + FCS" },
+ { PID_RFC2684_802_5_NOFCS, "Token Ring w/o FCS" },
+ { PID_RFC2684_FDDI_FCS, "FDDI + FCS" },
+ { PID_RFC2684_FDDI_NOFCS, "FDDI w/o FCS" },
+ { PID_RFC2684_802_6_FCS, "802.6 + FCS" },
+ { PID_RFC2684_802_6_NOFCS, "802.6 w/o FCS" },
+ { PID_RFC2684_BPDU, "BPDU" },
+ { 0, NULL },
+};
+
+static const struct tok null_values[] = {
+ { 0, NULL }
+};
+
+struct oui_tok {
+ uint32_t oui;
+ const struct tok *tok;
+};
+
+static const struct oui_tok oui_to_tok[] = {
+ { OUI_ENCAP_ETHER, ethertype_values },
+ { OUI_CISCO_90, ethertype_values }, /* uses some Ethertype values */
+ { OUI_APPLETALK, ethertype_values }, /* uses some Ethertype values */
+ { OUI_CISCO, cisco_values },
+ { OUI_RFC2684, bridged_values }, /* bridged, RFC 2427 FR or RFC 2864 ATM */
+ { 0, NULL }
+};
+
+/*
+ * If we printed information about the payload, returns the length of the LLC
+ * header, plus the length of any SNAP header following it.
+ *
+ * Otherwise (for example, if the packet has unknown SAPs or has a SNAP
+ * header with an unknown OUI/PID combination), returns the *negative*
+ * of that value.
+ */
+int
+llc_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen,
+ const struct lladdr_info *src, const struct lladdr_info *dst)
+{
+ uint8_t dsap_field, dsap, ssap_field, ssap;
+ uint16_t control;
+ int hdrlen;
+ int is_u;
+
+ ndo->ndo_protocol = "llc";
+ if (caplen < 3) {
+ nd_print_trunc(ndo);
+ ND_DEFAULTPRINT((const u_char *)p, caplen);
+ return (caplen);
+ }
+ if (length < 3) {
+ nd_print_trunc(ndo);
+ ND_DEFAULTPRINT((const u_char *)p, caplen);
+ return (length);
+ }
+
+ dsap_field = GET_U_1(p);
+ ssap_field = GET_U_1(p + 1);
+
+ /*
+ * OK, what type of LLC frame is this? The length
+ * of the control field depends on that - I frames
+ * have a two-byte control field, and U frames have
+ * a one-byte control field.
+ */
+ control = GET_U_1(p + 2);
+ if ((control & LLC_U_FMT) == LLC_U_FMT) {
+ /*
+ * U frame.
+ */
+ is_u = 1;
+ hdrlen = 3; /* DSAP, SSAP, 1-byte control field */
+ } else {
+ /*
+ * The control field in I and S frames is
+ * 2 bytes...
+ */
+ if (caplen < 4) {
+ nd_print_trunc(ndo);
+ ND_DEFAULTPRINT((const u_char *)p, caplen);
+ return (caplen);
+ }
+ if (length < 4) {
+ nd_print_trunc(ndo);
+ ND_DEFAULTPRINT((const u_char *)p, caplen);
+ return (length);
+ }
+
+ /*
+ * ...and is little-endian.
+ */
+ control = GET_LE_U_2(p + 2);
+ is_u = 0;
+ hdrlen = 4; /* DSAP, SSAP, 2-byte control field */
+ }
+
+ if (ssap_field == LLCSAP_GLOBAL && dsap_field == LLCSAP_GLOBAL) {
+ /*
+ * This is an Ethernet_802.3 IPX frame; it has an
+ * 802.3 header (i.e., an Ethernet header where the
+ * type/length field is <= MAX_ETHERNET_LENGTH_VAL,
+ * i.e. it's a length field, not a type field), but
+ * has no 802.2 header - the IPX packet starts right
+ * after the Ethernet header, with a signature of two
+ * bytes of 0xFF (which is LLCSAP_GLOBAL).
+ *
+ * (It might also have been an Ethernet_802.3 IPX at
+ * one time, but got bridged onto another network,
+ * such as an 802.11 network; this has appeared in at
+ * least one capture file.)
+ */
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("IPX 802.3: ");
+
+ ipx_print(ndo, p, length);
+ return (0); /* no LLC header */
+ }
+
+ dsap = dsap_field & ~LLC_IG;
+ ssap = ssap_field & ~LLC_GSAP;
+
+ if (ndo->ndo_eflag) {
+ ND_PRINT("LLC, dsap %s (0x%02x) %s, ssap %s (0x%02x) %s",
+ tok2str(llc_values, "Unknown", dsap),
+ dsap,
+ tok2str(llc_ig_flag_values, "Unknown", dsap_field & LLC_IG),
+ tok2str(llc_values, "Unknown", ssap),
+ ssap,
+ tok2str(llc_flag_values, "Unknown", ssap_field & LLC_GSAP));
+
+ if (is_u) {
+ ND_PRINT(", ctrl 0x%02x: ", control);
+ } else {
+ ND_PRINT(", ctrl 0x%04x: ", control);
+ }
+ }
+
+ /*
+ * Skip LLC header.
+ */
+ p += hdrlen;
+ length -= hdrlen;
+ caplen -= hdrlen;
+
+ if (ssap == LLCSAP_SNAP && dsap == LLCSAP_SNAP
+ && control == LLC_UI) {
+ /*
+ * XXX - what *is* the right bridge pad value here?
+ * Does anybody ever bridge one form of LAN traffic
+ * over a networking type that uses 802.2 LLC?
+ */
+ if (!snap_print(ndo, p, length, caplen, src, dst, 2)) {
+ /*
+ * Unknown packet type; tell our caller, by
+ * returning a negative value, so they
+ * can print the raw packet.
+ */
+ return (-(hdrlen + 5)); /* include LLC and SNAP header */
+ } else
+ return (hdrlen + 5); /* include LLC and SNAP header */
+ }
+
+ if (ssap == LLCSAP_8021D && dsap == LLCSAP_8021D &&
+ control == LLC_UI) {
+ stp_print(ndo, p, length);
+ return (hdrlen);
+ }
+
+ if (ssap == LLCSAP_IP && dsap == LLCSAP_IP &&
+ control == LLC_UI) {
+ /*
+ * This is an RFC 948-style IP packet, with
+ * an 802.3 header and an 802.2 LLC header
+ * with the source and destination SAPs being
+ * the IP SAP.
+ */
+ ip_print(ndo, p, length);
+ return (hdrlen);
+ }
+
+ if (ssap == LLCSAP_IPX && dsap == LLCSAP_IPX &&
+ control == LLC_UI) {
+ /*
+ * This is an Ethernet_802.2 IPX frame, with an 802.3
+ * header and an 802.2 LLC header with the source and
+ * destination SAPs being the IPX SAP.
+ */
+ if (ndo->ndo_eflag)
+ ND_PRINT("IPX 802.2: ");
+
+ ipx_print(ndo, p, length);
+ return (hdrlen);
+ }
+
+#ifdef ENABLE_SMB
+ if (ssap == LLCSAP_NETBEUI && dsap == LLCSAP_NETBEUI
+ && (!(control & LLC_S_FMT) || control == LLC_U_FMT)) {
+ /*
+ * we don't actually have a full netbeui parser yet, but the
+ * smb parser can handle many smb-in-netbeui packets, which
+ * is very useful, so we call that
+ *
+ * We don't call it for S frames, however, just I frames
+ * (which are frames that don't have the low-order bit,
+ * LLC_S_FMT, set in the first byte of the control field)
+ * and UI frames (whose control field is just 3, LLC_U_FMT).
+ */
+ netbeui_print(ndo, control, p, length);
+ return (hdrlen);
+ }
+#endif
+ if (ssap == LLCSAP_ISONS && dsap == LLCSAP_ISONS
+ && control == LLC_UI) {
+ isoclns_print(ndo, p, length);
+ return (hdrlen);
+ }
+
+ if (!ndo->ndo_eflag) {
+ if (ssap == dsap) {
+ if (src == NULL || dst == NULL)
+ ND_PRINT("%s ", tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
+ else
+ ND_PRINT("%s > %s %s ",
+ (src->addr_string)(ndo, src->addr),
+ (dst->addr_string)(ndo, dst->addr),
+ tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
+ } else {
+ if (src == NULL || dst == NULL)
+ ND_PRINT("%s > %s ",
+ tok2str(llc_values, "Unknown SSAP 0x%02x", ssap),
+ tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
+ else
+ ND_PRINT("%s %s > %s %s ",
+ (src->addr_string)(ndo, src->addr),
+ tok2str(llc_values, "Unknown SSAP 0x%02x", ssap),
+ (dst->addr_string)(ndo, dst->addr),
+ tok2str(llc_values, "Unknown DSAP 0x%02x", dsap));
+ }
+ }
+
+ if (is_u) {
+ ND_PRINT("Unnumbered, %s, Flags [%s], length %u",
+ tok2str(llc_cmd_values, "%02x", LLC_U_CMD(control)),
+ tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_U_POLL)),
+ length + hdrlen);
+
+ if ((control & ~LLC_U_POLL) == LLC_XID) {
+ if (length == 0) {
+ /*
+ * XID with no payload.
+ * This could, for example, be an SNA
+ * "short form" XID.
+ */
+ return (hdrlen);
+ }
+ if (caplen < 1) {
+ nd_print_trunc(ndo);
+ if (caplen > 0)
+ ND_DEFAULTPRINT((const u_char *)p, caplen);
+ return (hdrlen);
+ }
+ if (GET_U_1(p) == LLC_XID_FI) {
+ if (caplen < 3 || length < 3) {
+ nd_print_trunc(ndo);
+ if (caplen > 0)
+ ND_DEFAULTPRINT((const u_char *)p, caplen);
+ } else
+ ND_PRINT(": %02x %02x",
+ GET_U_1(p + 1),
+ GET_U_1(p + 2));
+ return (hdrlen);
+ }
+ }
+ } else {
+ if ((control & LLC_S_FMT) == LLC_S_FMT) {
+ ND_PRINT("Supervisory, %s, rcv seq %u, Flags [%s], length %u",
+ tok2str(llc_supervisory_values,"?",LLC_S_CMD(control)),
+ LLC_IS_NR(control),
+ tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_IS_POLL)),
+ length + hdrlen);
+ return (hdrlen); /* no payload to print */
+ } else {
+ ND_PRINT("Information, send seq %u, rcv seq %u, Flags [%s], length %u",
+ LLC_I_NS(control),
+ LLC_IS_NR(control),
+ tok2str(llc_flag_values,"?",(ssap_field & LLC_GSAP) | (control & LLC_IS_POLL)),
+ length + hdrlen);
+ }
+ }
+ return (-hdrlen);
+}
+
+static const struct tok *
+oui_to_struct_tok(uint32_t orgcode)
+{
+ const struct tok *tok = null_values;
+ const struct oui_tok *otp;
+
+ for (otp = &oui_to_tok[0]; otp->tok != NULL; otp++) {
+ if (otp->oui == orgcode) {
+ tok = otp->tok;
+ break;
+ }
+ }
+ return (tok);
+}
+
+int
+snap_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen,
+ const struct lladdr_info *src, const struct lladdr_info *dst,
+ u_int bridge_pad)
+{
+ uint32_t orgcode;
+ u_short et;
+ int ret;
+
+ ndo->ndo_protocol = "snap";
+ ND_TCHECK_5(p);
+ if (caplen < 5 || length < 5)
+ goto trunc;
+ orgcode = GET_BE_U_3(p);
+ et = GET_BE_U_2(p + 3);
+
+ if (ndo->ndo_eflag) {
+ /*
+ * Somebody's already printed the MAC addresses, if there
+ * are any, so just print the SNAP header, not the MAC
+ * addresses.
+ */
+ ND_PRINT("oui %s (0x%06x), %s %s (0x%04x), length %u: ",
+ tok2str(oui_values, "Unknown", orgcode),
+ orgcode,
+ (orgcode == 0x000000 ? "ethertype" : "pid"),
+ tok2str(oui_to_struct_tok(orgcode), "Unknown", et),
+ et, length - 5);
+ }
+ p += 5;
+ length -= 5;
+ caplen -= 5;
+
+ switch (orgcode) {
+ case OUI_ENCAP_ETHER:
+ case OUI_CISCO_90:
+ /*
+ * This is an encapsulated Ethernet packet,
+ * or a packet bridged by some piece of
+ * Cisco hardware; the protocol ID is
+ * an Ethernet protocol type.
+ */
+ ret = ethertype_print(ndo, et, p, length, caplen, src, dst);
+ if (ret)
+ return (ret);
+ break;
+
+ case OUI_APPLETALK:
+ if (et == ETHERTYPE_ATALK) {
+ /*
+ * No, I have no idea why Apple used one
+ * of their own OUIs, rather than
+ * 0x000000, and an Ethernet packet
+ * type, for Appletalk data packets,
+ * but used 0x000000 and an Ethernet
+ * packet type for AARP packets.
+ */
+ ret = ethertype_print(ndo, et, p, length, caplen, src, dst);
+ if (ret)
+ return (ret);
+ }
+ break;
+
+ case OUI_CISCO:
+ switch (et) {
+ case PID_CISCO_CDP:
+ cdp_print(ndo, p, length);
+ return (1);
+ case PID_CISCO_DTP:
+ dtp_print(ndo, p, length);
+ return (1);
+ case PID_CISCO_UDLD:
+ udld_print(ndo, p, length);
+ return (1);
+ case PID_CISCO_VTP:
+ vtp_print(ndo, p, length);
+ return (1);
+ case PID_CISCO_PVST:
+ case PID_CISCO_VLANBRIDGE:
+ stp_print(ndo, p, length);
+ return (1);
+ default:
+ break;
+ }
+ break;
+
+ case OUI_RFC2684:
+ switch (et) {
+
+ case PID_RFC2684_ETH_FCS:
+ case PID_RFC2684_ETH_NOFCS:
+ /*
+ * XXX - remove the last two bytes for
+ * PID_RFC2684_ETH_FCS?
+ */
+ /*
+ * Skip the padding.
+ */
+ ND_TCHECK_LEN(p, bridge_pad);
+ caplen -= bridge_pad;
+ length -= bridge_pad;
+ p += bridge_pad;
+
+ /*
+ * What remains is an Ethernet packet.
+ */
+ ether_print(ndo, p, length, caplen, NULL, NULL);
+ return (1);
+
+ case PID_RFC2684_802_5_FCS:
+ case PID_RFC2684_802_5_NOFCS:
+ /*
+ * XXX - remove the last two bytes for
+ * PID_RFC2684_ETH_FCS?
+ */
+ /*
+ * Skip the padding, but not the Access
+ * Control field.
+ */
+ ND_TCHECK_LEN(p, bridge_pad);
+ caplen -= bridge_pad;
+ length -= bridge_pad;
+ p += bridge_pad;
+
+ /*
+ * What remains is an 802.5 Token Ring
+ * packet.
+ */
+ token_print(ndo, p, length, caplen);
+ return (1);
+
+ case PID_RFC2684_FDDI_FCS:
+ case PID_RFC2684_FDDI_NOFCS:
+ /*
+ * XXX - remove the last two bytes for
+ * PID_RFC2684_ETH_FCS?
+ */
+ /*
+ * Skip the padding.
+ */
+ ND_TCHECK_LEN(p, bridge_pad + 1);
+ caplen -= bridge_pad + 1;
+ length -= bridge_pad + 1;
+ p += bridge_pad + 1;
+
+ /*
+ * What remains is an FDDI packet.
+ */
+ fddi_print(ndo, p, length, caplen);
+ return (1);
+
+ case PID_RFC2684_BPDU:
+ stp_print(ndo, p, length);
+ return (1);
+ }
+ }
+ if (!ndo->ndo_eflag) {
+ /*
+ * Nobody printed the link-layer addresses, so print them, if
+ * we have any.
+ */
+ if (src != NULL && dst != NULL) {
+ ND_PRINT("%s > %s ",
+ (src->addr_string)(ndo, src->addr),
+ (dst->addr_string)(ndo, dst->addr));
+ }
+ /*
+ * Print the SNAP header, but if the OUI is 000000, don't
+ * bother printing it, and report the PID as being an
+ * ethertype.
+ */
+ if (orgcode == 0x000000) {
+ ND_PRINT("SNAP, ethertype %s (0x%04x), length %u: ",
+ tok2str(ethertype_values, "Unknown", et),
+ et, length);
+ } else {
+ ND_PRINT("SNAP, oui %s (0x%06x), pid %s (0x%04x), length %u: ",
+ tok2str(oui_values, "Unknown", orgcode),
+ orgcode,
+ tok2str(oui_to_struct_tok(orgcode), "Unknown", et),
+ et, length);
+ }
+ }
+ return (0);
+
+trunc:
+ nd_print_trunc(ndo);
+ return (1);
+}
diff --git a/print-lldp.c b/print-lldp.c
new file mode 100644
index 0000000..a0cd9ba
--- /dev/null
+++ b/print-lldp.c
@@ -0,0 +1,1682 @@
+/*
+ * Copyright (c) 1998-2007 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ * IEEE and TIA extensions by Carles Kishimoto <carles.kishimoto@gmail.com>
+ * DCBX extensions by Kaladhar Musunuru <kaladharm@sourceforge.net>
+ */
+
+/* \summary: IEEE 802.1ab Link Layer Discovery Protocol (LLDP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "af.h"
+#include "oui.h"
+
+#define LLDP_EXTRACT_TYPE(x) (((x)&0xfe00)>>9)
+#define LLDP_EXTRACT_LEN(x) ((x)&0x01ff)
+
+/*
+ * TLV type codes
+ */
+#define LLDP_END_TLV 0
+#define LLDP_CHASSIS_ID_TLV 1
+#define LLDP_PORT_ID_TLV 2
+#define LLDP_TTL_TLV 3
+#define LLDP_PORT_DESCR_TLV 4
+#define LLDP_SYSTEM_NAME_TLV 5
+#define LLDP_SYSTEM_DESCR_TLV 6
+#define LLDP_SYSTEM_CAP_TLV 7
+#define LLDP_MGMT_ADDR_TLV 8
+#define LLDP_PRIVATE_TLV 127
+
+static const struct tok lldp_tlv_values[] = {
+ { LLDP_END_TLV, "End" },
+ { LLDP_CHASSIS_ID_TLV, "Chassis ID" },
+ { LLDP_PORT_ID_TLV, "Port ID" },
+ { LLDP_TTL_TLV, "Time to Live" },
+ { LLDP_PORT_DESCR_TLV, "Port Description" },
+ { LLDP_SYSTEM_NAME_TLV, "System Name" },
+ { LLDP_SYSTEM_DESCR_TLV, "System Description" },
+ { LLDP_SYSTEM_CAP_TLV, "System Capabilities" },
+ { LLDP_MGMT_ADDR_TLV, "Management Address" },
+ { LLDP_PRIVATE_TLV, "Organization specific" },
+ { 0, NULL}
+};
+
+/*
+ * Chassis ID subtypes
+ */
+#define LLDP_CHASSIS_CHASSIS_COMP_SUBTYPE 1
+#define LLDP_CHASSIS_INTF_ALIAS_SUBTYPE 2
+#define LLDP_CHASSIS_PORT_COMP_SUBTYPE 3
+#define LLDP_CHASSIS_MAC_ADDR_SUBTYPE 4
+#define LLDP_CHASSIS_NETWORK_ADDR_SUBTYPE 5
+#define LLDP_CHASSIS_INTF_NAME_SUBTYPE 6
+#define LLDP_CHASSIS_LOCAL_SUBTYPE 7
+
+static const struct tok lldp_chassis_subtype_values[] = {
+ { LLDP_CHASSIS_CHASSIS_COMP_SUBTYPE, "Chassis component"},
+ { LLDP_CHASSIS_INTF_ALIAS_SUBTYPE, "Interface alias"},
+ { LLDP_CHASSIS_PORT_COMP_SUBTYPE, "Port component"},
+ { LLDP_CHASSIS_MAC_ADDR_SUBTYPE, "MAC address"},
+ { LLDP_CHASSIS_NETWORK_ADDR_SUBTYPE, "Network address"},
+ { LLDP_CHASSIS_INTF_NAME_SUBTYPE, "Interface name"},
+ { LLDP_CHASSIS_LOCAL_SUBTYPE, "Local"},
+ { 0, NULL}
+};
+
+/*
+ * Port ID subtypes
+ */
+#define LLDP_PORT_INTF_ALIAS_SUBTYPE 1
+#define LLDP_PORT_PORT_COMP_SUBTYPE 2
+#define LLDP_PORT_MAC_ADDR_SUBTYPE 3
+#define LLDP_PORT_NETWORK_ADDR_SUBTYPE 4
+#define LLDP_PORT_INTF_NAME_SUBTYPE 5
+#define LLDP_PORT_AGENT_CIRC_ID_SUBTYPE 6
+#define LLDP_PORT_LOCAL_SUBTYPE 7
+
+static const struct tok lldp_port_subtype_values[] = {
+ { LLDP_PORT_INTF_ALIAS_SUBTYPE, "Interface alias"},
+ { LLDP_PORT_PORT_COMP_SUBTYPE, "Port component"},
+ { LLDP_PORT_MAC_ADDR_SUBTYPE, "MAC address"},
+ { LLDP_PORT_NETWORK_ADDR_SUBTYPE, "Network Address"},
+ { LLDP_PORT_INTF_NAME_SUBTYPE, "Interface Name"},
+ { LLDP_PORT_AGENT_CIRC_ID_SUBTYPE, "Agent circuit ID"},
+ { LLDP_PORT_LOCAL_SUBTYPE, "Local"},
+ { 0, NULL}
+};
+
+/*
+ * System Capabilities
+ */
+#define LLDP_CAP_OTHER (1 << 0)
+#define LLDP_CAP_REPEATER (1 << 1)
+#define LLDP_CAP_BRIDGE (1 << 2)
+#define LLDP_CAP_WLAN_AP (1 << 3)
+#define LLDP_CAP_ROUTER (1 << 4)
+#define LLDP_CAP_PHONE (1 << 5)
+#define LLDP_CAP_DOCSIS (1 << 6)
+#define LLDP_CAP_STATION_ONLY (1 << 7)
+
+static const struct tok lldp_cap_values[] = {
+ { LLDP_CAP_OTHER, "Other"},
+ { LLDP_CAP_REPEATER, "Repeater"},
+ { LLDP_CAP_BRIDGE, "Bridge"},
+ { LLDP_CAP_WLAN_AP, "WLAN AP"},
+ { LLDP_CAP_ROUTER, "Router"},
+ { LLDP_CAP_PHONE, "Telephone"},
+ { LLDP_CAP_DOCSIS, "Docsis"},
+ { LLDP_CAP_STATION_ONLY, "Station Only"},
+ { 0, NULL}
+};
+
+#define LLDP_PRIVATE_8021_SUBTYPE_PORT_VLAN_ID 1
+#define LLDP_PRIVATE_8021_SUBTYPE_PROTOCOL_VLAN_ID 2
+#define LLDP_PRIVATE_8021_SUBTYPE_VLAN_NAME 3
+#define LLDP_PRIVATE_8021_SUBTYPE_PROTOCOL_IDENTITY 4
+#define LLDP_PRIVATE_8021_SUBTYPE_LINKAGGR 7
+#define LLDP_PRIVATE_8021_SUBTYPE_CONGESTION_NOTIFICATION 8
+#define LLDP_PRIVATE_8021_SUBTYPE_ETS_CONFIGURATION 9
+#define LLDP_PRIVATE_8021_SUBTYPE_ETS_RECOMMENDATION 10
+#define LLDP_PRIVATE_8021_SUBTYPE_PFC_CONFIGURATION 11
+#define LLDP_PRIVATE_8021_SUBTYPE_APPLICATION_PRIORITY 12
+#define LLDP_PRIVATE_8021_SUBTYPE_EVB 13
+#define LLDP_PRIVATE_8021_SUBTYPE_CDCP 14
+
+static const struct tok lldp_8021_subtype_values[] = {
+ { LLDP_PRIVATE_8021_SUBTYPE_PORT_VLAN_ID, "Port VLAN Id"},
+ { LLDP_PRIVATE_8021_SUBTYPE_PROTOCOL_VLAN_ID, "Port and Protocol VLAN ID"},
+ { LLDP_PRIVATE_8021_SUBTYPE_VLAN_NAME, "VLAN name"},
+ { LLDP_PRIVATE_8021_SUBTYPE_PROTOCOL_IDENTITY, "Protocol Identity"},
+ { LLDP_PRIVATE_8021_SUBTYPE_LINKAGGR, "Link aggregation"},
+ { LLDP_PRIVATE_8021_SUBTYPE_CONGESTION_NOTIFICATION, "Congestion Notification"},
+ { LLDP_PRIVATE_8021_SUBTYPE_ETS_CONFIGURATION, "ETS Configuration"},
+ { LLDP_PRIVATE_8021_SUBTYPE_ETS_RECOMMENDATION, "ETS Recommendation"},
+ { LLDP_PRIVATE_8021_SUBTYPE_PFC_CONFIGURATION, "Priority Flow Control Configuration"},
+ { LLDP_PRIVATE_8021_SUBTYPE_APPLICATION_PRIORITY, "Application Priority"},
+ { LLDP_PRIVATE_8021_SUBTYPE_EVB, "EVB"},
+ { LLDP_PRIVATE_8021_SUBTYPE_CDCP,"CDCP"},
+ { 0, NULL}
+};
+
+#define LLDP_8021_PORT_PROTOCOL_VLAN_SUPPORT (1 << 1)
+#define LLDP_8021_PORT_PROTOCOL_VLAN_STATUS (1 << 2)
+
+static const struct tok lldp_8021_port_protocol_id_values[] = {
+ { LLDP_8021_PORT_PROTOCOL_VLAN_SUPPORT, "supported"},
+ { LLDP_8021_PORT_PROTOCOL_VLAN_STATUS, "enabled"},
+ { 0, NULL}
+};
+
+#define LLDP_PRIVATE_8023_SUBTYPE_MACPHY 1
+#define LLDP_PRIVATE_8023_SUBTYPE_MDIPOWER 2
+#define LLDP_PRIVATE_8023_SUBTYPE_LINKAGGR 3
+#define LLDP_PRIVATE_8023_SUBTYPE_MTU 4
+
+static const struct tok lldp_8023_subtype_values[] = {
+ { LLDP_PRIVATE_8023_SUBTYPE_MACPHY, "MAC/PHY configuration/status"},
+ { LLDP_PRIVATE_8023_SUBTYPE_MDIPOWER, "Power via MDI"},
+ { LLDP_PRIVATE_8023_SUBTYPE_LINKAGGR, "Link aggregation"},
+ { LLDP_PRIVATE_8023_SUBTYPE_MTU, "Max frame size"},
+ { 0, NULL}
+};
+
+#define LLDP_PRIVATE_TIA_SUBTYPE_CAPABILITIES 1
+#define LLDP_PRIVATE_TIA_SUBTYPE_NETWORK_POLICY 2
+#define LLDP_PRIVATE_TIA_SUBTYPE_LOCAL_ID 3
+#define LLDP_PRIVATE_TIA_SUBTYPE_EXTENDED_POWER_MDI 4
+#define LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_HARDWARE_REV 5
+#define LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_FIRMWARE_REV 6
+#define LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_SOFTWARE_REV 7
+#define LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_SERIAL_NUMBER 8
+#define LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_MANUFACTURER_NAME 9
+#define LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_MODEL_NAME 10
+#define LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_ASSET_ID 11
+
+static const struct tok lldp_tia_subtype_values[] = {
+ { LLDP_PRIVATE_TIA_SUBTYPE_CAPABILITIES, "LLDP-MED Capabilities" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_NETWORK_POLICY, "Network policy" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_LOCAL_ID, "Location identification" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_EXTENDED_POWER_MDI, "Extended power-via-MDI" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_HARDWARE_REV, "Inventory - hardware revision" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_FIRMWARE_REV, "Inventory - firmware revision" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_SOFTWARE_REV, "Inventory - software revision" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_SERIAL_NUMBER, "Inventory - serial number" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_MANUFACTURER_NAME, "Inventory - manufacturer name" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_MODEL_NAME, "Inventory - model name" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_ASSET_ID, "Inventory - asset ID" },
+ { 0, NULL}
+};
+
+#define LLDP_PRIVATE_TIA_LOCATION_ALTITUDE_METERS 1
+#define LLDP_PRIVATE_TIA_LOCATION_ALTITUDE_FLOORS 2
+
+static const struct tok lldp_tia_location_altitude_type_values[] = {
+ { LLDP_PRIVATE_TIA_LOCATION_ALTITUDE_METERS, "meters"},
+ { LLDP_PRIVATE_TIA_LOCATION_ALTITUDE_FLOORS, "floors"},
+ { 0, NULL}
+};
+
+/* ANSI/TIA-1057 - Annex B */
+#define LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A1 1
+#define LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A2 2
+#define LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A3 3
+#define LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A4 4
+#define LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A5 5
+#define LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A6 6
+
+static const struct tok lldp_tia_location_lci_catype_values[] = {
+ { LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A1, "national subdivisions (state,canton,region,province,prefecture)"},
+ { LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A2, "county, parish, gun, district"},
+ { LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A3, "city, township, shi"},
+ { LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A4, "city division, borough, city district, ward chou"},
+ { LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A5, "neighborhood, block"},
+ { LLDP_PRIVATE_TIA_LOCATION_LCI_CATYPE_A6, "street"},
+ { 0, NULL}
+};
+
+static const struct tok lldp_tia_location_lci_what_values[] = {
+ { 0, "location of DHCP server"},
+ { 1, "location of the network element believed to be closest to the client"},
+ { 2, "location of the client"},
+ { 0, NULL}
+};
+
+/*
+ * From RFC 3636 - dot3MauType
+ */
+#define LLDP_MAU_TYPE_UNKNOWN 0
+#define LLDP_MAU_TYPE_AUI 1
+#define LLDP_MAU_TYPE_10BASE_5 2
+#define LLDP_MAU_TYPE_FOIRL 3
+#define LLDP_MAU_TYPE_10BASE_2 4
+#define LLDP_MAU_TYPE_10BASE_T 5
+#define LLDP_MAU_TYPE_10BASE_FP 6
+#define LLDP_MAU_TYPE_10BASE_FB 7
+#define LLDP_MAU_TYPE_10BASE_FL 8
+#define LLDP_MAU_TYPE_10BROAD36 9
+#define LLDP_MAU_TYPE_10BASE_T_HD 10
+#define LLDP_MAU_TYPE_10BASE_T_FD 11
+#define LLDP_MAU_TYPE_10BASE_FL_HD 12
+#define LLDP_MAU_TYPE_10BASE_FL_FD 13
+#define LLDP_MAU_TYPE_100BASE_T4 14
+#define LLDP_MAU_TYPE_100BASE_TX_HD 15
+#define LLDP_MAU_TYPE_100BASE_TX_FD 16
+#define LLDP_MAU_TYPE_100BASE_FX_HD 17
+#define LLDP_MAU_TYPE_100BASE_FX_FD 18
+#define LLDP_MAU_TYPE_100BASE_T2_HD 19
+#define LLDP_MAU_TYPE_100BASE_T2_FD 20
+#define LLDP_MAU_TYPE_1000BASE_X_HD 21
+#define LLDP_MAU_TYPE_1000BASE_X_FD 22
+#define LLDP_MAU_TYPE_1000BASE_LX_HD 23
+#define LLDP_MAU_TYPE_1000BASE_LX_FD 24
+#define LLDP_MAU_TYPE_1000BASE_SX_HD 25
+#define LLDP_MAU_TYPE_1000BASE_SX_FD 26
+#define LLDP_MAU_TYPE_1000BASE_CX_HD 27
+#define LLDP_MAU_TYPE_1000BASE_CX_FD 28
+#define LLDP_MAU_TYPE_1000BASE_T_HD 29
+#define LLDP_MAU_TYPE_1000BASE_T_FD 30
+#define LLDP_MAU_TYPE_10GBASE_X 31
+#define LLDP_MAU_TYPE_10GBASE_LX4 32
+#define LLDP_MAU_TYPE_10GBASE_R 33
+#define LLDP_MAU_TYPE_10GBASE_ER 34
+#define LLDP_MAU_TYPE_10GBASE_LR 35
+#define LLDP_MAU_TYPE_10GBASE_SR 36
+#define LLDP_MAU_TYPE_10GBASE_W 37
+#define LLDP_MAU_TYPE_10GBASE_EW 38
+#define LLDP_MAU_TYPE_10GBASE_LW 39
+#define LLDP_MAU_TYPE_10GBASE_SW 40
+
+static const struct tok lldp_mau_types_values[] = {
+ { LLDP_MAU_TYPE_UNKNOWN, "Unknown"},
+ { LLDP_MAU_TYPE_AUI, "AUI"},
+ { LLDP_MAU_TYPE_10BASE_5, "10BASE_5"},
+ { LLDP_MAU_TYPE_FOIRL, "FOIRL"},
+ { LLDP_MAU_TYPE_10BASE_2, "10BASE2"},
+ { LLDP_MAU_TYPE_10BASE_T, "10BASET duplex mode unknown"},
+ { LLDP_MAU_TYPE_10BASE_FP, "10BASEFP"},
+ { LLDP_MAU_TYPE_10BASE_FB, "10BASEFB"},
+ { LLDP_MAU_TYPE_10BASE_FL, "10BASEFL duplex mode unknown"},
+ { LLDP_MAU_TYPE_10BROAD36, "10BROAD36"},
+ { LLDP_MAU_TYPE_10BASE_T_HD, "10BASET hdx"},
+ { LLDP_MAU_TYPE_10BASE_T_FD, "10BASET fdx"},
+ { LLDP_MAU_TYPE_10BASE_FL_HD, "10BASEFL hdx"},
+ { LLDP_MAU_TYPE_10BASE_FL_FD, "10BASEFL fdx"},
+ { LLDP_MAU_TYPE_100BASE_T4, "100BASET4"},
+ { LLDP_MAU_TYPE_100BASE_TX_HD, "100BASETX hdx"},
+ { LLDP_MAU_TYPE_100BASE_TX_FD, "100BASETX fdx"},
+ { LLDP_MAU_TYPE_100BASE_FX_HD, "100BASEFX hdx"},
+ { LLDP_MAU_TYPE_100BASE_FX_FD, "100BASEFX fdx"},
+ { LLDP_MAU_TYPE_100BASE_T2_HD, "100BASET2 hdx"},
+ { LLDP_MAU_TYPE_100BASE_T2_FD, "100BASET2 fdx"},
+ { LLDP_MAU_TYPE_1000BASE_X_HD, "1000BASEX hdx"},
+ { LLDP_MAU_TYPE_1000BASE_X_FD, "1000BASEX fdx"},
+ { LLDP_MAU_TYPE_1000BASE_LX_HD, "1000BASELX hdx"},
+ { LLDP_MAU_TYPE_1000BASE_LX_FD, "1000BASELX fdx"},
+ { LLDP_MAU_TYPE_1000BASE_SX_HD, "1000BASESX hdx"},
+ { LLDP_MAU_TYPE_1000BASE_SX_FD, "1000BASESX fdx"},
+ { LLDP_MAU_TYPE_1000BASE_CX_HD, "1000BASECX hdx"},
+ { LLDP_MAU_TYPE_1000BASE_CX_FD, "1000BASECX fdx"},
+ { LLDP_MAU_TYPE_1000BASE_T_HD, "1000BASET hdx"},
+ { LLDP_MAU_TYPE_1000BASE_T_FD, "1000BASET fdx"},
+ { LLDP_MAU_TYPE_10GBASE_X, "10GBASEX"},
+ { LLDP_MAU_TYPE_10GBASE_LX4, "10GBASELX4"},
+ { LLDP_MAU_TYPE_10GBASE_R, "10GBASER"},
+ { LLDP_MAU_TYPE_10GBASE_ER, "10GBASEER"},
+ { LLDP_MAU_TYPE_10GBASE_LR, "10GBASELR"},
+ { LLDP_MAU_TYPE_10GBASE_SR, "10GBASESR"},
+ { LLDP_MAU_TYPE_10GBASE_W, "10GBASEW"},
+ { LLDP_MAU_TYPE_10GBASE_EW, "10GBASEEW"},
+ { LLDP_MAU_TYPE_10GBASE_LW, "10GBASELW"},
+ { LLDP_MAU_TYPE_10GBASE_SW, "10GBASESW"},
+ { 0, NULL}
+};
+
+#define LLDP_8023_AUTONEGOTIATION_SUPPORT (1 << 0)
+#define LLDP_8023_AUTONEGOTIATION_STATUS (1 << 1)
+
+static const struct tok lldp_8023_autonegotiation_values[] = {
+ { LLDP_8023_AUTONEGOTIATION_SUPPORT, "supported"},
+ { LLDP_8023_AUTONEGOTIATION_STATUS, "enabled"},
+ { 0, NULL}
+};
+
+#define LLDP_TIA_CAPABILITY_MED (1 << 0)
+#define LLDP_TIA_CAPABILITY_NETWORK_POLICY (1 << 1)
+#define LLDP_TIA_CAPABILITY_LOCATION_IDENTIFICATION (1 << 2)
+#define LLDP_TIA_CAPABILITY_EXTENDED_POWER_MDI_PSE (1 << 3)
+#define LLDP_TIA_CAPABILITY_EXTENDED_POWER_MDI_PD (1 << 4)
+#define LLDP_TIA_CAPABILITY_INVENTORY (1 << 5)
+
+static const struct tok lldp_tia_capabilities_values[] = {
+ { LLDP_TIA_CAPABILITY_MED, "LLDP-MED capabilities"},
+ { LLDP_TIA_CAPABILITY_NETWORK_POLICY, "network policy"},
+ { LLDP_TIA_CAPABILITY_LOCATION_IDENTIFICATION, "location identification"},
+ { LLDP_TIA_CAPABILITY_EXTENDED_POWER_MDI_PSE, "extended power via MDI-PSE"},
+ { LLDP_TIA_CAPABILITY_EXTENDED_POWER_MDI_PD, "extended power via MDI-PD"},
+ { LLDP_TIA_CAPABILITY_INVENTORY, "Inventory"},
+ { 0, NULL}
+};
+
+#define LLDP_TIA_DEVICE_TYPE_ENDPOINT_CLASS_1 1
+#define LLDP_TIA_DEVICE_TYPE_ENDPOINT_CLASS_2 2
+#define LLDP_TIA_DEVICE_TYPE_ENDPOINT_CLASS_3 3
+#define LLDP_TIA_DEVICE_TYPE_NETWORK_CONNECTIVITY 4
+
+static const struct tok lldp_tia_device_type_values[] = {
+ { LLDP_TIA_DEVICE_TYPE_ENDPOINT_CLASS_1, "endpoint class 1"},
+ { LLDP_TIA_DEVICE_TYPE_ENDPOINT_CLASS_2, "endpoint class 2"},
+ { LLDP_TIA_DEVICE_TYPE_ENDPOINT_CLASS_3, "endpoint class 3"},
+ { LLDP_TIA_DEVICE_TYPE_NETWORK_CONNECTIVITY, "network connectivity"},
+ { 0, NULL}
+};
+
+#define LLDP_TIA_APPLICATION_TYPE_VOICE 1
+#define LLDP_TIA_APPLICATION_TYPE_VOICE_SIGNALING 2
+#define LLDP_TIA_APPLICATION_TYPE_GUEST_VOICE 3
+#define LLDP_TIA_APPLICATION_TYPE_GUEST_VOICE_SIGNALING 4
+#define LLDP_TIA_APPLICATION_TYPE_SOFTPHONE_VOICE 5
+#define LLDP_TIA_APPLICATION_TYPE_VIDEO_CONFERENCING 6
+#define LLDP_TIA_APPLICATION_TYPE_STREAMING_VIDEO 7
+#define LLDP_TIA_APPLICATION_TYPE_VIDEO_SIGNALING 8
+
+static const struct tok lldp_tia_application_type_values[] = {
+ { LLDP_TIA_APPLICATION_TYPE_VOICE, "voice"},
+ { LLDP_TIA_APPLICATION_TYPE_VOICE_SIGNALING, "voice signaling"},
+ { LLDP_TIA_APPLICATION_TYPE_GUEST_VOICE, "guest voice"},
+ { LLDP_TIA_APPLICATION_TYPE_GUEST_VOICE_SIGNALING, "guest voice signaling"},
+ { LLDP_TIA_APPLICATION_TYPE_SOFTPHONE_VOICE, "softphone voice"},
+ { LLDP_TIA_APPLICATION_TYPE_VIDEO_CONFERENCING, "video conferencing"},
+ { LLDP_TIA_APPLICATION_TYPE_STREAMING_VIDEO, "streaming video"},
+ { LLDP_TIA_APPLICATION_TYPE_VIDEO_SIGNALING, "video signaling"},
+ { 0, NULL}
+};
+
+#define LLDP_TIA_NETWORK_POLICY_X_BIT (1 << 5)
+#define LLDP_TIA_NETWORK_POLICY_T_BIT (1 << 6)
+#define LLDP_TIA_NETWORK_POLICY_U_BIT (1 << 7)
+
+static const struct tok lldp_tia_network_policy_bits_values[] = {
+ { LLDP_TIA_NETWORK_POLICY_U_BIT, "Unknown"},
+ { LLDP_TIA_NETWORK_POLICY_T_BIT, "Tagged"},
+ { LLDP_TIA_NETWORK_POLICY_X_BIT, "reserved"},
+ { 0, NULL}
+};
+
+#define LLDP_EXTRACT_NETWORK_POLICY_VLAN(x) (((x)&0x1ffe)>>1)
+#define LLDP_EXTRACT_NETWORK_POLICY_L2_PRIORITY(x) (((x)&0x01ff)>>6)
+#define LLDP_EXTRACT_NETWORK_POLICY_DSCP(x) ((x)&0x003f)
+
+#define LLDP_TIA_LOCATION_DATA_FORMAT_COORDINATE_BASED 1
+#define LLDP_TIA_LOCATION_DATA_FORMAT_CIVIC_ADDRESS 2
+#define LLDP_TIA_LOCATION_DATA_FORMAT_ECS_ELIN 3
+
+static const struct tok lldp_tia_location_data_format_values[] = {
+ { LLDP_TIA_LOCATION_DATA_FORMAT_COORDINATE_BASED, "coordinate-based LCI"},
+ { LLDP_TIA_LOCATION_DATA_FORMAT_CIVIC_ADDRESS, "civic address LCI"},
+ { LLDP_TIA_LOCATION_DATA_FORMAT_ECS_ELIN, "ECS ELIN"},
+ { 0, NULL}
+};
+
+#define LLDP_TIA_LOCATION_DATUM_WGS_84 1
+#define LLDP_TIA_LOCATION_DATUM_NAD_83_NAVD_88 2
+#define LLDP_TIA_LOCATION_DATUM_NAD_83_MLLW 3
+
+static const struct tok lldp_tia_location_datum_type_values[] = {
+ { LLDP_TIA_LOCATION_DATUM_WGS_84, "World Geodesic System 1984"},
+ { LLDP_TIA_LOCATION_DATUM_NAD_83_NAVD_88, "North American Datum 1983 (NAVD88)"},
+ { LLDP_TIA_LOCATION_DATUM_NAD_83_MLLW, "North American Datum 1983 (MLLW)"},
+ { 0, NULL}
+};
+
+#define LLDP_TIA_POWER_SOURCE_PSE 1
+#define LLDP_TIA_POWER_SOURCE_LOCAL 2
+#define LLDP_TIA_POWER_SOURCE_PSE_AND_LOCAL 3
+
+static const struct tok lldp_tia_power_source_values[] = {
+ { LLDP_TIA_POWER_SOURCE_PSE, "PSE - primary power source"},
+ { LLDP_TIA_POWER_SOURCE_LOCAL, "local - backup power source"},
+ { LLDP_TIA_POWER_SOURCE_PSE_AND_LOCAL, "PSE+local - reserved"},
+ { 0, NULL}
+};
+
+#define LLDP_TIA_POWER_PRIORITY_CRITICAL 1
+#define LLDP_TIA_POWER_PRIORITY_HIGH 2
+#define LLDP_TIA_POWER_PRIORITY_LOW 3
+
+static const struct tok lldp_tia_power_priority_values[] = {
+ { LLDP_TIA_POWER_PRIORITY_CRITICAL, "critical"},
+ { LLDP_TIA_POWER_PRIORITY_HIGH, "high"},
+ { LLDP_TIA_POWER_PRIORITY_LOW, "low"},
+ { 0, NULL}
+};
+
+#define LLDP_TIA_POWER_VAL_MAX 1024
+
+static const struct tok lldp_tia_inventory_values[] = {
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_HARDWARE_REV, "Hardware revision" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_FIRMWARE_REV, "Firmware revision" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_SOFTWARE_REV, "Software revision" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_SERIAL_NUMBER, "Serial number" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_MANUFACTURER_NAME, "Manufacturer name" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_MODEL_NAME, "Model name" },
+ { LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_ASSET_ID, "Asset ID" },
+ { 0, NULL}
+};
+
+/*
+ * From RFC 3636 - ifMauAutoNegCapAdvertisedBits
+ */
+#define LLDP_MAU_PMD_OTHER (1 << 15)
+#define LLDP_MAU_PMD_10BASE_T (1 << 14)
+#define LLDP_MAU_PMD_10BASE_T_FD (1 << 13)
+#define LLDP_MAU_PMD_100BASE_T4 (1 << 12)
+#define LLDP_MAU_PMD_100BASE_TX (1 << 11)
+#define LLDP_MAU_PMD_100BASE_TX_FD (1 << 10)
+#define LLDP_MAU_PMD_100BASE_T2 (1 << 9)
+#define LLDP_MAU_PMD_100BASE_T2_FD (1 << 8)
+#define LLDP_MAU_PMD_FDXPAUSE (1 << 7)
+#define LLDP_MAU_PMD_FDXAPAUSE (1 << 6)
+#define LLDP_MAU_PMD_FDXSPAUSE (1 << 5)
+#define LLDP_MAU_PMD_FDXBPAUSE (1 << 4)
+#define LLDP_MAU_PMD_1000BASE_X (1 << 3)
+#define LLDP_MAU_PMD_1000BASE_X_FD (1 << 2)
+#define LLDP_MAU_PMD_1000BASE_T (1 << 1)
+#define LLDP_MAU_PMD_1000BASE_T_FD (1 << 0)
+
+static const struct tok lldp_pmd_capability_values[] = {
+ { LLDP_MAU_PMD_10BASE_T, "10BASE-T hdx"},
+ { LLDP_MAU_PMD_10BASE_T_FD, "10BASE-T fdx"},
+ { LLDP_MAU_PMD_100BASE_T4, "100BASE-T4"},
+ { LLDP_MAU_PMD_100BASE_TX, "100BASE-TX hdx"},
+ { LLDP_MAU_PMD_100BASE_TX_FD, "100BASE-TX fdx"},
+ { LLDP_MAU_PMD_100BASE_T2, "100BASE-T2 hdx"},
+ { LLDP_MAU_PMD_100BASE_T2_FD, "100BASE-T2 fdx"},
+ { LLDP_MAU_PMD_FDXPAUSE, "Pause for fdx links"},
+ { LLDP_MAU_PMD_FDXAPAUSE, "Asym PAUSE for fdx"},
+ { LLDP_MAU_PMD_FDXSPAUSE, "Sym PAUSE for fdx"},
+ { LLDP_MAU_PMD_FDXBPAUSE, "Asym and Sym PAUSE for fdx"},
+ { LLDP_MAU_PMD_1000BASE_X, "1000BASE-{X LX SX CX} hdx"},
+ { LLDP_MAU_PMD_1000BASE_X_FD, "1000BASE-{X LX SX CX} fdx"},
+ { LLDP_MAU_PMD_1000BASE_T, "1000BASE-T hdx"},
+ { LLDP_MAU_PMD_1000BASE_T_FD, "1000BASE-T fdx"},
+ { 0, NULL}
+};
+
+#define LLDP_MDI_PORT_CLASS (1 << 0)
+#define LLDP_MDI_POWER_SUPPORT (1 << 1)
+#define LLDP_MDI_POWER_STATE (1 << 2)
+#define LLDP_MDI_PAIR_CONTROL_ABILITY (1 << 3)
+
+static const struct tok lldp_mdi_values[] = {
+ { LLDP_MDI_PORT_CLASS, "PSE"},
+ { LLDP_MDI_POWER_SUPPORT, "supported"},
+ { LLDP_MDI_POWER_STATE, "enabled"},
+ { LLDP_MDI_PAIR_CONTROL_ABILITY, "can be controlled"},
+ { 0, NULL}
+};
+
+#define LLDP_MDI_PSE_PORT_POWER_PAIRS_SIGNAL 1
+#define LLDP_MDI_PSE_PORT_POWER_PAIRS_SPARE 2
+
+static const struct tok lldp_mdi_power_pairs_values[] = {
+ { LLDP_MDI_PSE_PORT_POWER_PAIRS_SIGNAL, "signal"},
+ { LLDP_MDI_PSE_PORT_POWER_PAIRS_SPARE, "spare"},
+ { 0, NULL}
+};
+
+#define LLDP_MDI_POWER_CLASS0 1
+#define LLDP_MDI_POWER_CLASS1 2
+#define LLDP_MDI_POWER_CLASS2 3
+#define LLDP_MDI_POWER_CLASS3 4
+#define LLDP_MDI_POWER_CLASS4 5
+
+static const struct tok lldp_mdi_power_class_values[] = {
+ { LLDP_MDI_POWER_CLASS0, "class0"},
+ { LLDP_MDI_POWER_CLASS1, "class1"},
+ { LLDP_MDI_POWER_CLASS2, "class2"},
+ { LLDP_MDI_POWER_CLASS3, "class3"},
+ { LLDP_MDI_POWER_CLASS4, "class4"},
+ { 0, NULL}
+};
+
+#define LLDP_AGGREGATION_CAPABILITY (1 << 0)
+#define LLDP_AGGREGATION_STATUS (1 << 1)
+
+static const struct tok lldp_aggregation_values[] = {
+ { LLDP_AGGREGATION_CAPABILITY, "supported"},
+ { LLDP_AGGREGATION_STATUS, "enabled"},
+ { 0, NULL}
+};
+
+/*
+ * DCBX protocol subtypes.
+ */
+#define LLDP_DCBX_SUBTYPE_1 1
+#define LLDP_DCBX_SUBTYPE_2 2
+
+static const struct tok lldp_dcbx_subtype_values[] = {
+ { LLDP_DCBX_SUBTYPE_1, "DCB Capability Exchange Protocol Rev 1" },
+ { LLDP_DCBX_SUBTYPE_2, "DCB Capability Exchange Protocol Rev 1.01" },
+ { 0, NULL}
+};
+
+#define LLDP_DCBX_CONTROL_TLV 1
+#define LLDP_DCBX_PRIORITY_GROUPS_TLV 2
+#define LLDP_DCBX_PRIORITY_FLOW_CONTROL_TLV 3
+#define LLDP_DCBX_APPLICATION_TLV 4
+
+/*
+ * Interface numbering subtypes.
+ */
+#define LLDP_INTF_NUMB_IFX_SUBTYPE 2
+#define LLDP_INTF_NUMB_SYSPORT_SUBTYPE 3
+
+static const struct tok lldp_intf_numb_subtype_values[] = {
+ { LLDP_INTF_NUMB_IFX_SUBTYPE, "Interface Index" },
+ { LLDP_INTF_NUMB_SYSPORT_SUBTYPE, "System Port Number" },
+ { 0, NULL}
+};
+
+#define LLDP_INTF_NUM_LEN 5
+
+#define LLDP_EVB_MODE_NOT_SUPPORTED 0
+#define LLDP_EVB_MODE_EVB_BRIDGE 1
+#define LLDP_EVB_MODE_EVB_STATION 2
+#define LLDP_EVB_MODE_RESERVED 3
+
+static const struct tok lldp_evb_mode_values[]={
+ { LLDP_EVB_MODE_NOT_SUPPORTED, "Not Supported"},
+ { LLDP_EVB_MODE_EVB_BRIDGE, "EVB Bridge"},
+ { LLDP_EVB_MODE_EVB_STATION, "EVB Station"},
+ { LLDP_EVB_MODE_RESERVED, "Reserved for future Standardization"},
+ { 0, NULL},
+};
+
+#define NO_OF_BITS 8
+#define LLDP_PRIVATE_8021_SUBTYPE_CONGESTION_NOTIFICATION_LENGTH 6
+#define LLDP_PRIVATE_8021_SUBTYPE_ETS_CONFIGURATION_LENGTH 25
+#define LLDP_PRIVATE_8021_SUBTYPE_ETS_RECOMMENDATION_LENGTH 25
+#define LLDP_PRIVATE_8021_SUBTYPE_PFC_CONFIGURATION_LENGTH 6
+#define LLDP_PRIVATE_8021_SUBTYPE_APPLICATION_PRIORITY_MIN_LENGTH 5
+#define LLDP_PRIVATE_8021_SUBTYPE_EVB_LENGTH 9
+#define LLDP_PRIVATE_8021_SUBTYPE_CDCP_MIN_LENGTH 8
+
+#define LLDP_IANA_SUBTYPE_MUDURL 1
+
+static const struct tok lldp_iana_subtype_values[] = {
+ { LLDP_IANA_SUBTYPE_MUDURL, "MUD-URL" },
+ { 0, NULL }
+};
+
+
+static void
+print_ets_priority_assignment_table(netdissect_options *ndo,
+ const u_char *ptr)
+{
+ ND_PRINT("\n\t Priority Assignment Table");
+ ND_PRINT("\n\t Priority : 0 1 2 3 4 5 6 7");
+ ND_PRINT("\n\t Value : %-3d %-3d %-3d %-3d %-3d %-3d %-3d %-3d",
+ GET_U_1(ptr) >> 4, GET_U_1(ptr) & 0x0f,
+ GET_U_1(ptr + 1) >> 4, GET_U_1(ptr + 1) & 0x0f,
+ GET_U_1(ptr + 2) >> 4, GET_U_1(ptr + 2) & 0x0f,
+ GET_U_1(ptr + 3) >> 4, GET_U_1(ptr + 3) & 0x0f);
+}
+
+static void
+print_tc_bandwidth_table(netdissect_options *ndo,
+ const u_char *ptr)
+{
+ ND_PRINT("\n\t TC Bandwidth Table");
+ ND_PRINT("\n\t TC%% : 0 1 2 3 4 5 6 7");
+ ND_PRINT("\n\t Value : %-3d %-3d %-3d %-3d %-3d %-3d %-3d %-3d",
+ GET_U_1(ptr), GET_U_1(ptr + 1), GET_U_1(ptr + 2),
+ GET_U_1(ptr + 3), GET_U_1(ptr + 4), GET_U_1(ptr + 5),
+ GET_U_1(ptr + 6), GET_U_1(ptr + 7));
+}
+
+static void
+print_tsa_assignment_table(netdissect_options *ndo,
+ const u_char *ptr)
+{
+ ND_PRINT("\n\t TSA Assignment Table");
+ ND_PRINT("\n\t Traffic Class: 0 1 2 3 4 5 6 7");
+ ND_PRINT("\n\t Value : %-3d %-3d %-3d %-3d %-3d %-3d %-3d %-3d",
+ GET_U_1(ptr), GET_U_1(ptr + 1), GET_U_1(ptr + 2),
+ GET_U_1(ptr + 3), GET_U_1(ptr + 4), GET_U_1(ptr + 5),
+ GET_U_1(ptr + 6), GET_U_1(ptr + 7));
+}
+
+/*
+ * Print IEEE 802.1 private extensions. (802.1AB annex E)
+ */
+static int
+lldp_private_8021_print(netdissect_options *ndo,
+ const u_char *tptr, u_int tlv_len)
+{
+ int hexdump = FALSE;
+ u_int subtype;
+ u_int sublen;
+ u_int tval;
+ u_int i;
+
+ if (tlv_len < 4) {
+ return hexdump;
+ }
+ subtype = GET_U_1(tptr + 3);
+
+ ND_PRINT("\n\t %s Subtype (%u)",
+ tok2str(lldp_8021_subtype_values, "unknown", subtype),
+ subtype);
+
+ switch (subtype) {
+ case LLDP_PRIVATE_8021_SUBTYPE_PORT_VLAN_ID:
+ if (tlv_len < 6) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t port vlan id (PVID): %u",
+ GET_BE_U_2(tptr + 4));
+ break;
+ case LLDP_PRIVATE_8021_SUBTYPE_PROTOCOL_VLAN_ID:
+ if (tlv_len < 7) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t port and protocol vlan id (PPVID): %u, flags [%s] (0x%02x)",
+ GET_BE_U_2(tptr + 5),
+ bittok2str(lldp_8021_port_protocol_id_values, "none", GET_U_1(tptr + 4)),
+ GET_U_1(tptr + 4));
+ break;
+ case LLDP_PRIVATE_8021_SUBTYPE_VLAN_NAME:
+ if (tlv_len < 6) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t vlan id (VID): %u", GET_BE_U_2(tptr + 4));
+ if (tlv_len < 7) {
+ return hexdump;
+ }
+ sublen = GET_U_1(tptr + 6);
+ if (tlv_len < 7+sublen) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t vlan name: ");
+ nd_printjnp(ndo, tptr + 7, sublen);
+ break;
+ case LLDP_PRIVATE_8021_SUBTYPE_PROTOCOL_IDENTITY:
+ if (tlv_len < 5) {
+ return hexdump;
+ }
+ sublen = GET_U_1(tptr + 4);
+ if (tlv_len < 5+sublen) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t protocol identity: ");
+ nd_printjnp(ndo, tptr + 5, sublen);
+ break;
+
+ case LLDP_PRIVATE_8021_SUBTYPE_LINKAGGR:
+ if (tlv_len < 9) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t aggregation status [%s], aggregation port ID %u",
+ bittok2str(lldp_aggregation_values, "none", GET_U_1((tptr + 4))),
+ GET_BE_U_4(tptr + 5));
+ break;
+
+ case LLDP_PRIVATE_8021_SUBTYPE_CONGESTION_NOTIFICATION:
+ if(tlv_len<LLDP_PRIVATE_8021_SUBTYPE_CONGESTION_NOTIFICATION_LENGTH){
+ return hexdump;
+ }
+ tval=GET_U_1(tptr + 4);
+ ND_PRINT("\n\t Pre-Priority CNPV Indicator");
+ ND_PRINT("\n\t Priority : 0 1 2 3 4 5 6 7");
+ ND_PRINT("\n\t Value : ");
+ for(i=0;i<NO_OF_BITS;i++)
+ ND_PRINT("%-2d ", (tval >> i) & 0x01);
+ tval=GET_U_1(tptr + 5);
+ ND_PRINT("\n\t Pre-Priority Ready Indicator");
+ ND_PRINT("\n\t Priority : 0 1 2 3 4 5 6 7");
+ ND_PRINT("\n\t Value : ");
+ for(i=0;i<NO_OF_BITS;i++)
+ ND_PRINT("%-2d ", (tval >> i) & 0x01);
+ break;
+
+ case LLDP_PRIVATE_8021_SUBTYPE_ETS_CONFIGURATION:
+ if(tlv_len<LLDP_PRIVATE_8021_SUBTYPE_ETS_CONFIGURATION_LENGTH) {
+ return hexdump;
+ }
+ tval=GET_U_1(tptr + 4);
+ ND_PRINT("\n\t Willing:%u, CBS:%u, RES:%u, Max TCs:%u",
+ tval >> 7, (tval >> 6) & 0x02, (tval >> 3) & 0x07, tval & 0x07);
+
+ /*Print Priority Assignment Table*/
+ print_ets_priority_assignment_table(ndo, tptr + 5);
+
+ /*Print TC Bandwidth Table*/
+ print_tc_bandwidth_table(ndo, tptr + 9);
+
+ /* Print TSA Assignment Table */
+ print_tsa_assignment_table(ndo, tptr + 17);
+
+ break;
+
+ case LLDP_PRIVATE_8021_SUBTYPE_ETS_RECOMMENDATION:
+ if(tlv_len<LLDP_PRIVATE_8021_SUBTYPE_ETS_RECOMMENDATION_LENGTH) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t RES: %u", GET_U_1(tptr + 4));
+ /*Print Priority Assignment Table */
+ print_ets_priority_assignment_table(ndo, tptr + 5);
+ /*Print TC Bandwidth Table */
+ print_tc_bandwidth_table(ndo, tptr + 9);
+ /* Print TSA Assignment Table */
+ print_tsa_assignment_table(ndo, tptr + 17);
+ break;
+
+ case LLDP_PRIVATE_8021_SUBTYPE_PFC_CONFIGURATION:
+ if(tlv_len<LLDP_PRIVATE_8021_SUBTYPE_PFC_CONFIGURATION_LENGTH) {
+ return hexdump;
+ }
+ tval=GET_U_1(tptr + 4);
+ ND_PRINT("\n\t Willing: %u, MBC: %u, RES: %u, PFC cap:%u ",
+ tval >> 7, (tval >> 6) & 0x01, (tval >> 4) & 0x03, (tval & 0x0f));
+ ND_PRINT("\n\t PFC Enable");
+ tval=GET_U_1(tptr + 5);
+ ND_PRINT("\n\t Priority : 0 1 2 3 4 5 6 7");
+ ND_PRINT("\n\t Value : ");
+ for(i=0;i<NO_OF_BITS;i++)
+ ND_PRINT("%-2d ", (tval >> i) & 0x01);
+ break;
+
+ case LLDP_PRIVATE_8021_SUBTYPE_APPLICATION_PRIORITY:
+ if(tlv_len<LLDP_PRIVATE_8021_SUBTYPE_APPLICATION_PRIORITY_MIN_LENGTH) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t RES: %u", GET_U_1(tptr + 4));
+ if(tlv_len<=LLDP_PRIVATE_8021_SUBTYPE_APPLICATION_PRIORITY_MIN_LENGTH){
+ return hexdump;
+ }
+ /* Length of Application Priority Table */
+ sublen=tlv_len-5;
+ if(sublen%3!=0){
+ return hexdump;
+ }
+ i=0;
+ ND_PRINT("\n\t Application Priority Table");
+ while(i<sublen) {
+ tval=GET_U_1(tptr + i + 5);
+ ND_PRINT("\n\t Priority: %u, RES: %u, Sel: %u, Protocol ID: %u",
+ tval >> 5, (tval >> 3) & 0x03, (tval & 0x07),
+ GET_BE_U_2(tptr + i + 6));
+ i=i+3;
+ }
+ break;
+ case LLDP_PRIVATE_8021_SUBTYPE_EVB:
+ if(tlv_len<LLDP_PRIVATE_8021_SUBTYPE_EVB_LENGTH){
+ return hexdump;
+ }
+ ND_PRINT("\n\t EVB Bridge Status");
+ tval=GET_U_1(tptr + 4);
+ ND_PRINT("\n\t RES: %u, BGID: %u, RRCAP: %u, RRCTR: %u",
+ tval >> 3, (tval >> 2) & 0x01, (tval >> 1) & 0x01, tval & 0x01);
+ ND_PRINT("\n\t EVB Station Status");
+ tval=GET_U_1(tptr + 5);
+ ND_PRINT("\n\t RES: %u, SGID: %u, RRREQ: %u,RRSTAT: %u",
+ tval >> 4, (tval >> 3) & 0x01, (tval >> 2) & 0x01, tval & 0x03);
+ tval=GET_U_1(tptr + 6);
+ ND_PRINT("\n\t R: %u, RTE: %u, ",tval >> 5, tval & 0x1f);
+ tval=GET_U_1(tptr + 7);
+ ND_PRINT("EVB Mode: %s [%u]",
+ tok2str(lldp_evb_mode_values, "unknown", tval >> 6), tval >> 6);
+ ND_PRINT("\n\t ROL: %u, RWD: %u, ", (tval >> 5) & 0x01, tval & 0x1f);
+ tval=GET_U_1(tptr + 8);
+ ND_PRINT("RES: %u, ROL: %u, RKA: %u", tval >> 6, (tval >> 5) & 0x01, tval & 0x1f);
+ break;
+
+ case LLDP_PRIVATE_8021_SUBTYPE_CDCP:
+ if(tlv_len<LLDP_PRIVATE_8021_SUBTYPE_CDCP_MIN_LENGTH){
+ return hexdump;
+ }
+ tval=GET_U_1(tptr + 4);
+ ND_PRINT("\n\t Role: %u, RES: %u, Scomp: %u ",
+ tval >> 7, (tval >> 4) & 0x07, (tval >> 3) & 0x01);
+ ND_PRINT("ChnCap: %u", GET_BE_U_2(tptr + 6) & 0x0fff);
+ sublen=tlv_len-8;
+ if(sublen%3!=0) {
+ return hexdump;
+ }
+ i=0;
+ while(i<sublen) {
+ tval=GET_BE_U_3(tptr + i + 8);
+ ND_PRINT("\n\t SCID: %u, SVID: %u",
+ tval >> 12, tval & 0x000fff);
+ i=i+3;
+ }
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ return hexdump;
+}
+
+/*
+ * Print IEEE 802.3 private extensions. (802.3bc)
+ */
+static int
+lldp_private_8023_print(netdissect_options *ndo,
+ const u_char *tptr, u_int tlv_len)
+{
+ int hexdump = FALSE;
+ u_int subtype;
+
+ if (tlv_len < 4) {
+ return hexdump;
+ }
+ subtype = GET_U_1(tptr + 3);
+
+ ND_PRINT("\n\t %s Subtype (%u)",
+ tok2str(lldp_8023_subtype_values, "unknown", subtype),
+ subtype);
+
+ switch (subtype) {
+ case LLDP_PRIVATE_8023_SUBTYPE_MACPHY:
+ if (tlv_len < 9) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t autonegotiation [%s] (0x%02x)",
+ bittok2str(lldp_8023_autonegotiation_values, "none", GET_U_1(tptr + 4)),
+ GET_U_1(tptr + 4));
+ ND_PRINT("\n\t PMD autoneg capability [%s] (0x%04x)",
+ bittok2str(lldp_pmd_capability_values,"unknown", GET_BE_U_2(tptr + 5)),
+ GET_BE_U_2(tptr + 5));
+ ND_PRINT("\n\t MAU type %s (0x%04x)",
+ tok2str(lldp_mau_types_values, "unknown", GET_BE_U_2(tptr + 7)),
+ GET_BE_U_2(tptr + 7));
+ break;
+
+ case LLDP_PRIVATE_8023_SUBTYPE_MDIPOWER:
+ if (tlv_len < 7) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t MDI power support [%s], power pair %s, power class %s",
+ bittok2str(lldp_mdi_values, "none", GET_U_1((tptr + 4))),
+ tok2str(lldp_mdi_power_pairs_values, "unknown", GET_U_1((tptr + 5))),
+ tok2str(lldp_mdi_power_class_values, "unknown", GET_U_1((tptr + 6))));
+ break;
+
+ case LLDP_PRIVATE_8023_SUBTYPE_LINKAGGR:
+ if (tlv_len < 9) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t aggregation status [%s], aggregation port ID %u",
+ bittok2str(lldp_aggregation_values, "none", GET_U_1((tptr + 4))),
+ GET_BE_U_4(tptr + 5));
+ break;
+
+ case LLDP_PRIVATE_8023_SUBTYPE_MTU:
+ if (tlv_len < 6) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t MTU size %u", GET_BE_U_2(tptr + 4));
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ return hexdump;
+}
+
+/*
+ * Extract 34bits of latitude/longitude coordinates.
+ */
+static uint64_t
+lldp_extract_latlon(netdissect_options *ndo, const u_char *tptr)
+{
+ uint64_t latlon;
+
+ latlon = GET_U_1(tptr) & 0x3;
+ latlon = (latlon << 32) | GET_BE_U_4(tptr + 1);
+
+ return latlon;
+}
+
+/* objects defined in IANA subtype 00 00 5e
+ * (right now there is only one)
+ */
+
+
+static int
+lldp_private_iana_print(netdissect_options *ndo,
+ const u_char *tptr, u_int tlv_len)
+{
+ int hexdump = FALSE;
+ u_int subtype;
+
+ if (tlv_len < 8) {
+ return hexdump;
+ }
+ subtype = GET_U_1(tptr + 3);
+
+ ND_PRINT("\n\t %s Subtype (%u)",
+ tok2str(lldp_iana_subtype_values, "unknown", subtype),
+ subtype);
+
+ switch (subtype) {
+ case LLDP_IANA_SUBTYPE_MUDURL:
+ ND_PRINT("\n\t MUD-URL=");
+ (void)nd_printn(ndo, tptr+4, tlv_len-4, NULL);
+ break;
+ default:
+ hexdump=TRUE;
+ }
+
+ return hexdump;
+}
+
+
+
+/*
+ * Print private TIA extensions.
+ */
+static int
+lldp_private_tia_print(netdissect_options *ndo,
+ const u_char *tptr, u_int tlv_len)
+{
+ int hexdump = FALSE;
+ u_int subtype;
+ uint8_t location_format;
+ uint16_t power_val;
+ u_int lci_len;
+ uint8_t ca_type, ca_len;
+
+ if (tlv_len < 4) {
+ return hexdump;
+ }
+ subtype = GET_U_1(tptr + 3);
+
+ ND_PRINT("\n\t %s Subtype (%u)",
+ tok2str(lldp_tia_subtype_values, "unknown", subtype),
+ subtype);
+
+ switch (subtype) {
+ case LLDP_PRIVATE_TIA_SUBTYPE_CAPABILITIES:
+ if (tlv_len < 7) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t Media capabilities [%s] (0x%04x)",
+ bittok2str(lldp_tia_capabilities_values, "none",
+ GET_BE_U_2(tptr + 4)), GET_BE_U_2(tptr + 4));
+ ND_PRINT("\n\t Device type [%s] (0x%02x)",
+ tok2str(lldp_tia_device_type_values, "unknown", GET_U_1(tptr + 6)),
+ GET_U_1(tptr + 6));
+ break;
+
+ case LLDP_PRIVATE_TIA_SUBTYPE_NETWORK_POLICY:
+ if (tlv_len < 8) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t Application type [%s] (0x%02x)",
+ tok2str(lldp_tia_application_type_values, "none", GET_U_1(tptr + 4)),
+ GET_U_1(tptr + 4));
+ ND_PRINT(", Flags [%s]", bittok2str(
+ lldp_tia_network_policy_bits_values, "none", GET_U_1((tptr + 5))));
+ ND_PRINT("\n\t Vlan id %u",
+ LLDP_EXTRACT_NETWORK_POLICY_VLAN(GET_BE_U_2(tptr + 5)));
+ ND_PRINT(", L2 priority %u",
+ LLDP_EXTRACT_NETWORK_POLICY_L2_PRIORITY(GET_BE_U_2(tptr + 6)));
+ ND_PRINT(", DSCP value %u",
+ LLDP_EXTRACT_NETWORK_POLICY_DSCP(GET_BE_U_2(tptr + 6)));
+ break;
+
+ case LLDP_PRIVATE_TIA_SUBTYPE_LOCAL_ID:
+ if (tlv_len < 5) {
+ return hexdump;
+ }
+ location_format = GET_U_1(tptr + 4);
+ ND_PRINT("\n\t Location data format %s (0x%02x)",
+ tok2str(lldp_tia_location_data_format_values, "unknown", location_format),
+ location_format);
+
+ switch (location_format) {
+ case LLDP_TIA_LOCATION_DATA_FORMAT_COORDINATE_BASED:
+ if (tlv_len < 21) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t Latitude resolution %u, latitude value %" PRIu64,
+ (GET_U_1(tptr + 5) >> 2),
+ lldp_extract_latlon(ndo, tptr + 5));
+ ND_PRINT("\n\t Longitude resolution %u, longitude value %" PRIu64,
+ (GET_U_1(tptr + 10) >> 2),
+ lldp_extract_latlon(ndo, tptr + 10));
+ ND_PRINT("\n\t Altitude type %s (%u)",
+ tok2str(lldp_tia_location_altitude_type_values, "unknown",GET_U_1(tptr + 15) >> 4),
+ (GET_U_1(tptr + 15) >> 4));
+ ND_PRINT("\n\t Altitude resolution %u, altitude value 0x%x",
+ (GET_BE_U_2(tptr + 15)>>6)&0x3f,
+ (GET_BE_U_4(tptr + 16) & 0x3fffffff));
+ ND_PRINT("\n\t Datum %s (0x%02x)",
+ tok2str(lldp_tia_location_datum_type_values, "unknown", GET_U_1(tptr + 20)),
+ GET_U_1(tptr + 20));
+ break;
+
+ case LLDP_TIA_LOCATION_DATA_FORMAT_CIVIC_ADDRESS:
+ if (tlv_len < 6) {
+ return hexdump;
+ }
+ lci_len = GET_U_1(tptr + 5);
+ if (lci_len < 3) {
+ return hexdump;
+ }
+ if (tlv_len < 7+lci_len) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t LCI length %u, LCI what %s (0x%02x), Country-code ",
+ lci_len,
+ tok2str(lldp_tia_location_lci_what_values, "unknown", GET_U_1(tptr + 6)),
+ GET_U_1(tptr + 6));
+
+ /* Country code */
+ nd_printjnp(ndo, tptr + 7, 2);
+
+ lci_len = lci_len-3;
+ tptr = tptr + 9;
+
+ /* Decode each civic address element */
+ while (lci_len > 0) {
+ if (lci_len < 2) {
+ return hexdump;
+ }
+ ca_type = GET_U_1(tptr);
+ ca_len = GET_U_1(tptr + 1);
+
+ tptr += 2;
+ lci_len -= 2;
+
+ ND_PRINT("\n\t CA type \'%s\' (%u), length %u: ",
+ tok2str(lldp_tia_location_lci_catype_values, "unknown", ca_type),
+ ca_type, ca_len);
+
+ /* basic sanity check */
+ if ( ca_type == 0 || ca_len == 0) {
+ return hexdump;
+ }
+ if (lci_len < ca_len) {
+ return hexdump;
+ }
+
+ nd_printjnp(ndo, tptr, ca_len);
+ tptr += ca_len;
+ lci_len -= ca_len;
+ }
+ break;
+
+ case LLDP_TIA_LOCATION_DATA_FORMAT_ECS_ELIN:
+ ND_PRINT("\n\t ECS ELIN id ");
+ nd_printjnp(ndo, tptr + 5, tlv_len - 5);
+ break;
+
+ default:
+ ND_PRINT("\n\t Location ID ");
+ print_unknown_data(ndo, tptr + 5, "\n\t ", tlv_len - 5);
+ }
+ break;
+
+ case LLDP_PRIVATE_TIA_SUBTYPE_EXTENDED_POWER_MDI:
+ if (tlv_len < 7) {
+ return hexdump;
+ }
+ ND_PRINT("\n\t Power type [%s]",
+ (GET_U_1(tptr + 4) & 0xC0 >> 6) ? "PD device" : "PSE device");
+ ND_PRINT(", Power source [%s]",
+ tok2str(lldp_tia_power_source_values, "none", (GET_U_1((tptr + 4)) & 0x30) >> 4));
+ ND_PRINT("\n\t Power priority [%s] (0x%02x)",
+ tok2str(lldp_tia_power_priority_values, "none", GET_U_1(tptr + 4) & 0x0f),
+ GET_U_1(tptr + 4) & 0x0f);
+ power_val = GET_BE_U_2(tptr + 5);
+ if (power_val < LLDP_TIA_POWER_VAL_MAX) {
+ ND_PRINT(", Power %.1f Watts", ((float)power_val) / 10);
+ } else {
+ ND_PRINT(", Power %u (Reserved)", power_val);
+ }
+ break;
+
+ case LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_HARDWARE_REV:
+ case LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_FIRMWARE_REV:
+ case LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_SOFTWARE_REV:
+ case LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_SERIAL_NUMBER:
+ case LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_MANUFACTURER_NAME:
+ case LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_MODEL_NAME:
+ case LLDP_PRIVATE_TIA_SUBTYPE_INVENTORY_ASSET_ID:
+ ND_PRINT("\n\t %s ",
+ tok2str(lldp_tia_inventory_values, "unknown", subtype));
+ nd_printjnp(ndo, tptr + 4, tlv_len - 4);
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ return hexdump;
+}
+
+/*
+ * Print DCBX Protocol fields (V 1.01).
+ */
+static int
+lldp_private_dcbx_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ int hexdump = FALSE;
+ u_int subtype;
+ uint16_t tval;
+ uint16_t tlv;
+ uint32_t i, pgval, uval;
+ u_int tlen, tlv_type;
+ uint16_t tlv_len;
+ const u_char *tptr, *mptr;
+
+ if (len < 4) {
+ return hexdump;
+ }
+ subtype = GET_U_1(pptr + 3);
+
+ ND_PRINT("\n\t %s Subtype (%u)",
+ tok2str(lldp_dcbx_subtype_values, "unknown", subtype),
+ subtype);
+
+ /* by passing old version */
+ if (subtype == LLDP_DCBX_SUBTYPE_1)
+ return TRUE;
+
+ tptr = pptr + 4;
+ tlen = len - 4;
+
+ while (tlen >= sizeof(tlv)) {
+
+ ND_TCHECK_LEN(tptr, sizeof(tlv));
+
+ tlv = GET_BE_U_2(tptr);
+
+ tlv_type = LLDP_EXTRACT_TYPE(tlv);
+ tlv_len = LLDP_EXTRACT_LEN(tlv);
+ hexdump = FALSE;
+
+ tlen -= sizeof(tlv);
+ tptr += sizeof(tlv);
+
+ /* loop check */
+ if (!tlv_type || !tlv_len) {
+ break;
+ }
+
+ ND_TCHECK_LEN(tptr, tlv_len);
+ if (tlen < tlv_len) {
+ goto trunc;
+ }
+
+ /* decode every tlv */
+ switch (tlv_type) {
+ case LLDP_DCBX_CONTROL_TLV:
+ if (tlv_len < 10) {
+ goto trunc;
+ }
+ ND_PRINT("\n\t Control - Protocol Control (type 0x%x, length %u)",
+ LLDP_DCBX_CONTROL_TLV, tlv_len);
+ ND_PRINT("\n\t Oper_Version: %u", GET_U_1(tptr));
+ ND_PRINT("\n\t Max_Version: %u", GET_U_1(tptr + 1));
+ ND_PRINT("\n\t Sequence Number: %u", GET_BE_U_4(tptr + 2));
+ ND_PRINT("\n\t Acknowledgement Number: %u",
+ GET_BE_U_4(tptr + 6));
+ break;
+ case LLDP_DCBX_PRIORITY_GROUPS_TLV:
+ if (tlv_len < 17) {
+ goto trunc;
+ }
+ ND_PRINT("\n\t Feature - Priority Group (type 0x%x, length %u)",
+ LLDP_DCBX_PRIORITY_GROUPS_TLV, tlv_len);
+ ND_PRINT("\n\t Oper_Version: %u", GET_U_1(tptr));
+ ND_PRINT("\n\t Max_Version: %u", GET_U_1(tptr + 1));
+ ND_PRINT("\n\t Info block(0x%02X): ", GET_U_1(tptr + 2));
+ tval = GET_U_1(tptr + 2);
+ ND_PRINT("Enable bit: %u, Willing bit: %u, Error Bit: %u",
+ (tval & 0x80) ? 1 : 0, (tval & 0x40) ? 1 : 0,
+ (tval & 0x20) ? 1 : 0);
+ ND_PRINT("\n\t SubType: %u", GET_U_1(tptr + 3));
+ ND_PRINT("\n\t Priority Allocation");
+
+ /*
+ * Array of 8 4-bit priority group ID values; we fetch all
+ * 32 bits and extract each nibble.
+ */
+ pgval = GET_BE_U_4(tptr + 4);
+ for (i = 0; i <= 7; i++) {
+ ND_PRINT("\n\t PgId_%u: %u",
+ i, (pgval >> (28 - 4 * i)) & 0xF);
+ }
+ ND_PRINT("\n\t Priority Group Allocation");
+ for (i = 0; i <= 7; i++)
+ ND_PRINT("\n\t Pg percentage[%u]: %u", i,
+ GET_U_1(tptr + 8 + i));
+ ND_PRINT("\n\t NumTCsSupported: %u", GET_U_1(tptr + 8 + 8));
+ break;
+ case LLDP_DCBX_PRIORITY_FLOW_CONTROL_TLV:
+ if (tlv_len < 6) {
+ goto trunc;
+ }
+ ND_PRINT("\n\t Feature - Priority Flow Control");
+ ND_PRINT(" (type 0x%x, length %u)",
+ LLDP_DCBX_PRIORITY_FLOW_CONTROL_TLV, tlv_len);
+ ND_PRINT("\n\t Oper_Version: %u", GET_U_1(tptr));
+ ND_PRINT("\n\t Max_Version: %u", GET_U_1(tptr + 1));
+ ND_PRINT("\n\t Info block(0x%02X): ", GET_U_1(tptr + 2));
+ tval = GET_U_1(tptr + 2);
+ ND_PRINT("Enable bit: %u, Willing bit: %u, Error Bit: %u",
+ (tval & 0x80) ? 1 : 0, (tval & 0x40) ? 1 : 0,
+ (tval & 0x20) ? 1 : 0);
+ ND_PRINT("\n\t SubType: %u", GET_U_1(tptr + 3));
+ tval = GET_U_1(tptr + 4);
+ ND_PRINT("\n\t PFC Config (0x%02X)", GET_U_1(tptr + 4));
+ for (i = 0; i <= 7; i++)
+ ND_PRINT("\n\t Priority Bit %u: %s",
+ i, (tval & (1 << i)) ? "Enabled" : "Disabled");
+ ND_PRINT("\n\t NumTCPFCSupported: %u", GET_U_1(tptr + 5));
+ break;
+ case LLDP_DCBX_APPLICATION_TLV:
+ if (tlv_len < 4) {
+ goto trunc;
+ }
+ ND_PRINT("\n\t Feature - Application (type 0x%x, length %u)",
+ LLDP_DCBX_APPLICATION_TLV, tlv_len);
+ ND_PRINT("\n\t Oper_Version: %u", GET_U_1(tptr));
+ ND_PRINT("\n\t Max_Version: %u", GET_U_1(tptr + 1));
+ ND_PRINT("\n\t Info block(0x%02X): ", GET_U_1(tptr + 2));
+ tval = GET_U_1(tptr + 2);
+ ND_PRINT("Enable bit: %u, Willing bit: %u, Error Bit: %u",
+ (tval & 0x80) ? 1 : 0, (tval & 0x40) ? 1 : 0,
+ (tval & 0x20) ? 1 : 0);
+ ND_PRINT("\n\t SubType: %u", GET_U_1(tptr + 3));
+ tval = tlv_len - 4;
+ mptr = tptr + 4;
+ while (tval >= 6) {
+ ND_PRINT("\n\t Application Value");
+ ND_PRINT("\n\t Application Protocol ID: 0x%04x",
+ GET_BE_U_2(mptr));
+ uval = GET_BE_U_3(mptr + 2);
+ ND_PRINT("\n\t SF (0x%x) Application Protocol ID is %s",
+ (uval >> 22),
+ (uval >> 22) ? "Socket Number" : "L2 EtherType");
+ ND_PRINT("\n\t OUI: 0x%06x", uval & 0x3fffff);
+ ND_PRINT("\n\t User Priority Map: 0x%02x",
+ GET_U_1(mptr + 5));
+ tval = tval - 6;
+ mptr = mptr + 6;
+ }
+ break;
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) {
+ print_unknown_data(ndo, tptr, "\n\t ", tlv_len);
+ }
+
+ tlen -= tlv_len;
+ tptr += tlv_len;
+ }
+
+ trunc:
+ return hexdump;
+}
+
+static char *
+lldp_network_addr_print(netdissect_options *ndo, const u_char *tptr, u_int len)
+{
+ uint8_t af;
+ static char buf[BUFSIZE];
+ const char * (*pfunc)(netdissect_options *, const u_char *);
+
+ if (len < 1)
+ return NULL;
+ len--;
+ af = GET_U_1(tptr);
+ switch (af) {
+ case AFNUM_INET:
+ if (len < sizeof(nd_ipv4))
+ return NULL;
+ pfunc = ipaddr_string;
+ break;
+ case AFNUM_INET6:
+ if (len < sizeof(nd_ipv6))
+ return NULL;
+ pfunc = ip6addr_string;
+ break;
+ case AFNUM_802:
+ if (len < MAC_ADDR_LEN)
+ return NULL;
+ pfunc = etheraddr_string;
+ break;
+ default:
+ pfunc = NULL;
+ break;
+ }
+
+ if (!pfunc) {
+ snprintf(buf, sizeof(buf), "AFI %s (%u), no AF printer !",
+ tok2str(af_values, "Unknown", af), af);
+ } else {
+ snprintf(buf, sizeof(buf), "AFI %s (%u): %s",
+ tok2str(af_values, "Unknown", af), af, (*pfunc)(ndo, tptr+1));
+ }
+
+ return buf;
+}
+
+static int
+lldp_mgmt_addr_tlv_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ uint8_t mgmt_addr_len, intf_num_subtype, oid_len;
+ const u_char *tptr;
+ u_int tlen;
+ char *mgmt_addr;
+
+ tlen = len;
+ tptr = pptr;
+
+ if (tlen < 1) {
+ return 0;
+ }
+ mgmt_addr_len = GET_U_1(tptr);
+ tptr++;
+ tlen--;
+
+ if (tlen < mgmt_addr_len) {
+ return 0;
+ }
+
+ mgmt_addr = lldp_network_addr_print(ndo, tptr, mgmt_addr_len);
+ if (mgmt_addr == NULL) {
+ return 0;
+ }
+ ND_PRINT("\n\t Management Address length %u, %s",
+ mgmt_addr_len, mgmt_addr);
+ tptr += mgmt_addr_len;
+ tlen -= mgmt_addr_len;
+
+ if (tlen < LLDP_INTF_NUM_LEN) {
+ return 0;
+ }
+
+ intf_num_subtype = GET_U_1(tptr);
+ ND_PRINT("\n\t %s Interface Numbering (%u): %u",
+ tok2str(lldp_intf_numb_subtype_values, "Unknown", intf_num_subtype),
+ intf_num_subtype,
+ GET_BE_U_4(tptr + 1));
+
+ tptr += LLDP_INTF_NUM_LEN;
+ tlen -= LLDP_INTF_NUM_LEN;
+
+ /*
+ * The OID is optional.
+ */
+ if (tlen) {
+ oid_len = GET_U_1(tptr);
+
+ if (tlen < 1U + oid_len) {
+ return 0;
+ }
+ if (oid_len) {
+ ND_PRINT("\n\t OID length %u", oid_len);
+ nd_printjnp(ndo, tptr + 1, oid_len);
+ }
+ }
+
+ return 1;
+}
+
+void
+lldp_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ uint8_t subtype;
+ uint16_t tlv, cap, ena_cap;
+ u_int oui, tlen, hexdump, tlv_type, tlv_len;
+ const u_char *tptr;
+ char *network_addr;
+
+ ndo->ndo_protocol = "lldp";
+ tptr = pptr;
+ tlen = len;
+
+ ND_PRINT("LLDP, length %u", len);
+
+ while (tlen >= sizeof(tlv)) {
+
+ ND_TCHECK_LEN(tptr, sizeof(tlv));
+
+ tlv = GET_BE_U_2(tptr);
+
+ tlv_type = LLDP_EXTRACT_TYPE(tlv);
+ tlv_len = LLDP_EXTRACT_LEN(tlv);
+ hexdump = FALSE;
+
+ tlen -= sizeof(tlv);
+ tptr += sizeof(tlv);
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("\n\t%s TLV (%u), length %u",
+ tok2str(lldp_tlv_values, "Unknown", tlv_type),
+ tlv_type, tlv_len);
+ }
+
+ /* infinite loop check */
+ if (!tlv_type || !tlv_len) {
+ break;
+ }
+
+ ND_TCHECK_LEN(tptr, tlv_len);
+ if (tlen < tlv_len) {
+ goto trunc;
+ }
+
+ switch (tlv_type) {
+
+ case LLDP_CHASSIS_ID_TLV:
+ if (ndo->ndo_vflag) {
+ if (tlv_len < 2) {
+ goto trunc;
+ }
+ subtype = GET_U_1(tptr);
+ ND_PRINT("\n\t Subtype %s (%u): ",
+ tok2str(lldp_chassis_subtype_values, "Unknown", subtype),
+ subtype);
+
+ switch (subtype) {
+ case LLDP_CHASSIS_MAC_ADDR_SUBTYPE:
+ if (tlv_len < 1+6) {
+ goto trunc;
+ }
+ ND_PRINT("%s", GET_ETHERADDR_STRING(tptr + 1));
+ break;
+
+ case LLDP_CHASSIS_INTF_NAME_SUBTYPE: /* fall through */
+ case LLDP_CHASSIS_LOCAL_SUBTYPE:
+ case LLDP_CHASSIS_CHASSIS_COMP_SUBTYPE:
+ case LLDP_CHASSIS_INTF_ALIAS_SUBTYPE:
+ case LLDP_CHASSIS_PORT_COMP_SUBTYPE:
+ nd_printjnp(ndo, tptr + 1, tlv_len - 1);
+ break;
+
+ case LLDP_CHASSIS_NETWORK_ADDR_SUBTYPE:
+ network_addr = lldp_network_addr_print(ndo, tptr+1, tlv_len-1);
+ if (network_addr == NULL) {
+ goto trunc;
+ }
+ ND_PRINT("%s", network_addr);
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+ }
+ break;
+
+ case LLDP_PORT_ID_TLV:
+ if (ndo->ndo_vflag) {
+ if (tlv_len < 2) {
+ goto trunc;
+ }
+ subtype = GET_U_1(tptr);
+ ND_PRINT("\n\t Subtype %s (%u): ",
+ tok2str(lldp_port_subtype_values, "Unknown", subtype),
+ subtype);
+
+ switch (subtype) {
+ case LLDP_PORT_MAC_ADDR_SUBTYPE:
+ if (tlv_len < 1+6) {
+ goto trunc;
+ }
+ ND_PRINT("%s", GET_ETHERADDR_STRING(tptr + 1));
+ break;
+
+ case LLDP_PORT_INTF_NAME_SUBTYPE: /* fall through */
+ case LLDP_PORT_LOCAL_SUBTYPE:
+ case LLDP_PORT_AGENT_CIRC_ID_SUBTYPE:
+ case LLDP_PORT_INTF_ALIAS_SUBTYPE:
+ case LLDP_PORT_PORT_COMP_SUBTYPE:
+ nd_printjnp(ndo, tptr + 1, tlv_len - 1);
+ break;
+
+ case LLDP_PORT_NETWORK_ADDR_SUBTYPE:
+ network_addr = lldp_network_addr_print(ndo, tptr+1, tlv_len-1);
+ if (network_addr == NULL) {
+ goto trunc;
+ }
+ ND_PRINT("%s", network_addr);
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+ }
+ break;
+
+ case LLDP_TTL_TLV:
+ if (ndo->ndo_vflag) {
+ if (tlv_len < 2) {
+ goto trunc;
+ }
+ ND_PRINT(": TTL %us", GET_BE_U_2(tptr));
+ }
+ break;
+
+ case LLDP_PORT_DESCR_TLV:
+ if (ndo->ndo_vflag) {
+ ND_PRINT(": ");
+ nd_printjnp(ndo, tptr, tlv_len);
+ }
+ break;
+
+ case LLDP_SYSTEM_NAME_TLV:
+ /*
+ * The system name is also print in non-verbose mode
+ * similar to the CDP printer.
+ */
+ ND_PRINT(": ");
+ nd_printjnp(ndo, tptr, tlv_len);
+ break;
+
+ case LLDP_SYSTEM_DESCR_TLV:
+ if (ndo->ndo_vflag) {
+ ND_PRINT("\n\t ");
+ nd_printjnp(ndo, tptr, tlv_len);
+ }
+ break;
+
+ case LLDP_SYSTEM_CAP_TLV:
+ if (ndo->ndo_vflag) {
+ /*
+ * XXX - IEEE Std 802.1AB-2009 says the first octet
+ * if a chassis ID subtype, with the system
+ * capabilities and enabled capabilities following
+ * it.
+ */
+ if (tlv_len < 4) {
+ goto trunc;
+ }
+ cap = GET_BE_U_2(tptr);
+ ena_cap = GET_BE_U_2(tptr + 2);
+ ND_PRINT("\n\t System Capabilities [%s] (0x%04x)",
+ bittok2str(lldp_cap_values, "none", cap), cap);
+ ND_PRINT("\n\t Enabled Capabilities [%s] (0x%04x)",
+ bittok2str(lldp_cap_values, "none", ena_cap), ena_cap);
+ }
+ break;
+
+ case LLDP_MGMT_ADDR_TLV:
+ if (ndo->ndo_vflag) {
+ if (!lldp_mgmt_addr_tlv_print(ndo, tptr, tlv_len)) {
+ goto trunc;
+ }
+ }
+ break;
+
+ case LLDP_PRIVATE_TLV:
+ if (ndo->ndo_vflag) {
+ if (tlv_len < 3) {
+ goto trunc;
+ }
+ oui = GET_BE_U_3(tptr);
+ ND_PRINT(": OUI %s (0x%06x)", tok2str(oui_values, "Unknown", oui), oui);
+
+ switch (oui) {
+ case OUI_IEEE_8021_PRIVATE:
+ hexdump = lldp_private_8021_print(ndo, tptr, tlv_len);
+ break;
+ case OUI_IEEE_8023_PRIVATE:
+ hexdump = lldp_private_8023_print(ndo, tptr, tlv_len);
+ break;
+ case OUI_IANA:
+ hexdump = lldp_private_iana_print(ndo, tptr, tlv_len);
+ break;
+ case OUI_TIA:
+ hexdump = lldp_private_tia_print(ndo, tptr, tlv_len);
+ break;
+ case OUI_DCBX:
+ hexdump = lldp_private_dcbx_print(ndo, tptr, tlv_len);
+ break;
+ default:
+ hexdump = TRUE;
+ break;
+ }
+ }
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) {
+ print_unknown_data(ndo, tptr, "\n\t ", tlv_len);
+ }
+
+ tlen -= tlv_len;
+ tptr += tlv_len;
+ }
+ return;
+ trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-lmp.c b/print-lmp.c
new file mode 100644
index 0000000..925f3b0
--- /dev/null
+++ b/print-lmp.c
@@ -0,0 +1,1137 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ * Support for LMP service discovery extensions (defined by OIF UNI 1.0)
+ * added by Manu Pathak (mapathak@cisco.com), May 2005
+ */
+
+/* \summary: Link Management Protocol (LMP) printer */
+
+/* specification: RFC 4204 */
+/* OIF UNI 1.0: https://web.archive.org/web/20160401194747/http://www.oiforum.com/public/documents/OIF-UNI-01.0.pdf */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "gmpls.h"
+
+
+/*
+ * LMP common header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Vers | (Reserved) | Flags | Msg Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | LMP Length | (Reserved) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct lmp_common_header {
+ nd_uint16_t version_res;
+ nd_uint8_t flags;
+ nd_uint8_t msg_type;
+ nd_uint16_t length;
+ nd_byte reserved[2];
+};
+
+#define LMP_VERSION 1
+#define LMP_EXTRACT_VERSION(x) (((x)&0xf000)>>12)
+
+static const struct tok lmp_header_flag_values[] = {
+ { 0x01, "Control Channel Down"},
+ { 0x02, "LMP restart"},
+ { 0, NULL}
+};
+
+static const struct tok lmp_obj_te_link_flag_values[] = {
+ { 0x01, "Fault Management Supported"},
+ { 0x02, "Link Verification Supported"},
+ { 0, NULL}
+};
+
+static const struct tok lmp_obj_data_link_flag_values[] = {
+ { 0x01, "Data Link Port"},
+ { 0x02, "Allocated for user traffic"},
+ { 0x04, "Failed link"},
+ { 0, NULL}
+};
+
+static const struct tok lmp_obj_channel_status_values[] = {
+ { 1, "Signal Okay"},
+ { 2, "Signal Degraded"},
+ { 3, "Signal Fail"},
+ { 0, NULL}
+};
+
+static const struct tok lmp_obj_begin_verify_flag_values[] = {
+ { 0x0001, "Verify all links"},
+ { 0x0002, "Data link type"},
+ { 0, NULL}
+};
+
+static const struct tok lmp_obj_begin_verify_error_values[] = {
+ { 0x01, "Link Verification Procedure Not supported"},
+ { 0x02, "Unwilling to verify"},
+ { 0x04, "Unsupported verification transport mechanism"},
+ { 0x08, "Link-Id configuration error"},
+ { 0x10, "Unknown object c-type"},
+ { 0, NULL}
+};
+
+static const struct tok lmp_obj_link_summary_error_values[] = {
+ { 0x01, "Unacceptable non-negotiable LINK-SUMMARY parameters"},
+ { 0x02, "Renegotiate LINK-SUMMARY parameters"},
+ { 0x04, "Invalid TE-LINK Object"},
+ { 0x08, "Invalid DATA-LINK Object"},
+ { 0x10, "Unknown TE-LINK Object c-type"},
+ { 0x20, "Unknown DATA-LINK Object c-type"},
+ { 0, NULL}
+};
+
+/* Service Config Supported Protocols Flags */
+static const struct tok lmp_obj_service_config_sp_flag_values[] = {
+ { 0x01, "RSVP Supported"},
+ { 0x02, "LDP Supported"},
+ { 0, NULL}
+};
+
+/* Service Config Client Port Service Attribute Transparency Flags */
+static const struct tok lmp_obj_service_config_cpsa_tp_flag_values[] = {
+ { 0x01, "Path/VC Overhead Transparency Supported"},
+ { 0x02, "Line/MS Overhead Transparency Supported"},
+ { 0x04, "Section/RS Overhead Transparency Supported"},
+ { 0, NULL}
+};
+
+/* Service Config Client Port Service Attribute Contiguous Concatenation Types Flags */
+static const struct tok lmp_obj_service_config_cpsa_cct_flag_values[] = {
+ { 0x01, "Contiguous Concatenation Types Supported"},
+ { 0, NULL}
+};
+
+/* Service Config Network Service Attributes Transparency Flags */
+static const struct tok lmp_obj_service_config_nsa_transparency_flag_values[] = {
+ { 0x01, "Standard SOH/RSOH Transparency Supported"},
+ { 0x02, "Standard LOH/MSOH Transparency Supported"},
+ { 0, NULL}
+};
+
+/* Service Config Network Service Attributes TCM Monitoring Flags */
+static const struct tok lmp_obj_service_config_nsa_tcm_flag_values[] = {
+ { 0x01, "Transparent Tandem Connection Monitoring Supported"},
+ { 0, NULL}
+};
+
+/* Network Service Attributes Network Diversity Flags */
+static const struct tok lmp_obj_service_config_nsa_network_diversity_flag_values[] = {
+ { 0x01, "Node Diversity Supported"},
+ { 0x02, "Link Diversity Supported"},
+ { 0x04, "SRLG Diversity Supported"},
+ { 0, NULL}
+};
+
+#define LMP_MSGTYPE_CONFIG 1
+#define LMP_MSGTYPE_CONFIG_ACK 2
+#define LMP_MSGTYPE_CONFIG_NACK 3
+#define LMP_MSGTYPE_HELLO 4
+#define LMP_MSGTYPE_VERIFY_BEGIN 5
+#define LMP_MSGTYPE_VERIFY_BEGIN_ACK 6
+#define LMP_MSGTYPE_VERIFY_BEGIN_NACK 7
+#define LMP_MSGTYPE_VERIFY_END 8
+#define LMP_MSGTYPE_VERIFY_END_ACK 9
+#define LMP_MSGTYPE_TEST 10
+#define LMP_MSGTYPE_TEST_STATUS_SUCCESS 11
+#define LMP_MSGTYPE_TEST_STATUS_FAILURE 12
+#define LMP_MSGTYPE_TEST_STATUS_ACK 13
+#define LMP_MSGTYPE_LINK_SUMMARY 14
+#define LMP_MSGTYPE_LINK_SUMMARY_ACK 15
+#define LMP_MSGTYPE_LINK_SUMMARY_NACK 16
+#define LMP_MSGTYPE_CHANNEL_STATUS 17
+#define LMP_MSGTYPE_CHANNEL_STATUS_ACK 18
+#define LMP_MSGTYPE_CHANNEL_STATUS_REQ 19
+#define LMP_MSGTYPE_CHANNEL_STATUS_RESP 20
+/* LMP Service Discovery message types defined by UNI 1.0 */
+#define LMP_MSGTYPE_SERVICE_CONFIG 50
+#define LMP_MSGTYPE_SERVICE_CONFIG_ACK 51
+#define LMP_MSGTYPE_SERVICE_CONFIG_NACK 52
+
+static const struct tok lmp_msg_type_values[] = {
+ { LMP_MSGTYPE_CONFIG, "Config"},
+ { LMP_MSGTYPE_CONFIG_ACK, "Config ACK"},
+ { LMP_MSGTYPE_CONFIG_NACK, "Config NACK"},
+ { LMP_MSGTYPE_HELLO, "Hello"},
+ { LMP_MSGTYPE_VERIFY_BEGIN, "Begin Verify"},
+ { LMP_MSGTYPE_VERIFY_BEGIN_ACK, "Begin Verify ACK"},
+ { LMP_MSGTYPE_VERIFY_BEGIN_NACK, "Begin Verify NACK"},
+ { LMP_MSGTYPE_VERIFY_END, "End Verify"},
+ { LMP_MSGTYPE_VERIFY_END_ACK, "End Verify ACK"},
+ { LMP_MSGTYPE_TEST, "Test"},
+ { LMP_MSGTYPE_TEST_STATUS_SUCCESS, "Test Status Success"},
+ { LMP_MSGTYPE_TEST_STATUS_FAILURE, "Test Status Failure"},
+ { LMP_MSGTYPE_TEST_STATUS_ACK, "Test Status ACK"},
+ { LMP_MSGTYPE_LINK_SUMMARY, "Link Summary"},
+ { LMP_MSGTYPE_LINK_SUMMARY_ACK, "Link Summary ACK"},
+ { LMP_MSGTYPE_LINK_SUMMARY_NACK, "Link Summary NACK"},
+ { LMP_MSGTYPE_CHANNEL_STATUS, "Channel Status"},
+ { LMP_MSGTYPE_CHANNEL_STATUS_ACK, "Channel Status ACK"},
+ { LMP_MSGTYPE_CHANNEL_STATUS_REQ, "Channel Status Request"},
+ { LMP_MSGTYPE_CHANNEL_STATUS_RESP, "Channel Status Response"},
+ { LMP_MSGTYPE_SERVICE_CONFIG, "Service Config"},
+ { LMP_MSGTYPE_SERVICE_CONFIG_ACK, "Service Config ACK"},
+ { LMP_MSGTYPE_SERVICE_CONFIG_NACK, "Service Config NACK"},
+ { 0, NULL}
+};
+
+/*
+ * LMP object header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |N| C-Type | Class | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * // (object contents) //
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct lmp_object_header {
+ nd_uint8_t ctype;
+ nd_uint8_t class_num;
+ nd_uint16_t length;
+};
+
+#define LMP_OBJ_CC_ID 1
+#define LMP_OBJ_NODE_ID 2
+#define LMP_OBJ_LINK_ID 3
+#define LMP_OBJ_INTERFACE_ID 4
+#define LMP_OBJ_MESSAGE_ID 5
+#define LMP_OBJ_CONFIG 6
+#define LMP_OBJ_HELLO 7
+#define LMP_OBJ_VERIFY_BEGIN 8
+#define LMP_OBJ_VERIFY_BEGIN_ACK 9
+#define LMP_OBJ_VERIFY_ID 10
+#define LMP_OBJ_TE_LINK 11
+#define LMP_OBJ_DATA_LINK 12
+#define LMP_OBJ_CHANNEL_STATUS 13
+#define LMP_OBJ_CHANNEL_STATUS_REQ 14
+#define LMP_OBJ_ERROR_CODE 20
+
+#define LMP_OBJ_SERVICE_CONFIG 51 /* defined in UNI 1.0 */
+
+static const struct tok lmp_obj_values[] = {
+ { LMP_OBJ_CC_ID, "Control Channel ID" },
+ { LMP_OBJ_NODE_ID, "Node ID" },
+ { LMP_OBJ_LINK_ID, "Link ID" },
+ { LMP_OBJ_INTERFACE_ID, "Interface ID" },
+ { LMP_OBJ_MESSAGE_ID, "Message ID" },
+ { LMP_OBJ_CONFIG, "Configuration" },
+ { LMP_OBJ_HELLO, "Hello" },
+ { LMP_OBJ_VERIFY_BEGIN, "Verify Begin" },
+ { LMP_OBJ_VERIFY_BEGIN_ACK, "Verify Begin ACK" },
+ { LMP_OBJ_VERIFY_ID, "Verify ID" },
+ { LMP_OBJ_TE_LINK, "TE Link" },
+ { LMP_OBJ_DATA_LINK, "Data Link" },
+ { LMP_OBJ_CHANNEL_STATUS, "Channel Status" },
+ { LMP_OBJ_CHANNEL_STATUS_REQ, "Channel Status Request" },
+ { LMP_OBJ_ERROR_CODE, "Error Code" },
+ { LMP_OBJ_SERVICE_CONFIG, "Service Config" },
+
+ { 0, NULL}
+};
+
+#define INT_SWITCHING_TYPE_SUBOBJ 1
+#define WAVELENGTH_SUBOBJ 2
+
+static const struct tok lmp_data_link_subobj[] = {
+ { INT_SWITCHING_TYPE_SUBOBJ, "Interface Switching Type" },
+ { WAVELENGTH_SUBOBJ , "Wavelength" },
+ { 0, NULL}
+};
+
+#define LMP_CTYPE_IPV4 1
+#define LMP_CTYPE_IPV6 2
+
+#define LMP_CTYPE_LOC 1
+#define LMP_CTYPE_RMT 2
+#define LMP_CTYPE_UNMD 3
+
+#define LMP_CTYPE_IPV4_LOC 1
+#define LMP_CTYPE_IPV4_RMT 2
+#define LMP_CTYPE_IPV6_LOC 3
+#define LMP_CTYPE_IPV6_RMT 4
+#define LMP_CTYPE_UNMD_LOC 5
+#define LMP_CTYPE_UNMD_RMT 6
+
+#define LMP_CTYPE_1 1
+#define LMP_CTYPE_2 2
+
+#define LMP_CTYPE_HELLO_CONFIG 1
+#define LMP_CTYPE_HELLO 1
+
+#define LMP_CTYPE_BEGIN_VERIFY_ERROR 1
+#define LMP_CTYPE_LINK_SUMMARY_ERROR 2
+
+/* C-Types for Service Config Object */
+#define LMP_CTYPE_SERVICE_CONFIG_SP 1
+#define LMP_CTYPE_SERVICE_CONFIG_CPSA 2
+#define LMP_CTYPE_SERVICE_CONFIG_TRANSPARENCY_TCM 3
+#define LMP_CTYPE_SERVICE_CONFIG_NETWORK_DIVERSITY 4
+
+/*
+ * Different link types allowed in the Client Port Service Attributes
+ * subobject defined for LMP Service Discovery in the UNI 1.0 spec
+ */
+#define LMP_SD_SERVICE_CONFIG_CPSA_LINK_TYPE_SDH 5 /* UNI 1.0 Sec 9.4.2 */
+#define LMP_SD_SERVICE_CONFIG_CPSA_LINK_TYPE_SONET 6 /* UNI 1.0 Sec 9.4.2 */
+
+/*
+ * the ctypes are not globally unique so for
+ * translating it to strings we build a table based
+ * on objects offsetted by the ctype
+ */
+
+static const struct tok lmp_ctype_values[] = {
+ { 256*LMP_OBJ_CC_ID+LMP_CTYPE_LOC, "Local" },
+ { 256*LMP_OBJ_CC_ID+LMP_CTYPE_RMT, "Remote" },
+ { 256*LMP_OBJ_NODE_ID+LMP_CTYPE_LOC, "Local" },
+ { 256*LMP_OBJ_NODE_ID+LMP_CTYPE_RMT, "Remote" },
+ { 256*LMP_OBJ_LINK_ID+LMP_CTYPE_IPV4_LOC, "IPv4 Local" },
+ { 256*LMP_OBJ_LINK_ID+LMP_CTYPE_IPV4_RMT, "IPv4 Remote" },
+ { 256*LMP_OBJ_LINK_ID+LMP_CTYPE_IPV6_LOC, "IPv6 Local" },
+ { 256*LMP_OBJ_LINK_ID+LMP_CTYPE_IPV6_RMT, "IPv6 Remote" },
+ { 256*LMP_OBJ_LINK_ID+LMP_CTYPE_UNMD_LOC, "Unnumbered Local" },
+ { 256*LMP_OBJ_LINK_ID+LMP_CTYPE_UNMD_RMT, "Unnumbered Remote" },
+ { 256*LMP_OBJ_INTERFACE_ID+LMP_CTYPE_IPV4_LOC, "IPv4 Local" },
+ { 256*LMP_OBJ_INTERFACE_ID+LMP_CTYPE_IPV4_RMT, "IPv4 Remote" },
+ { 256*LMP_OBJ_INTERFACE_ID+LMP_CTYPE_IPV6_LOC, "IPv6 Local" },
+ { 256*LMP_OBJ_INTERFACE_ID+LMP_CTYPE_IPV6_RMT, "IPv6 Remote" },
+ { 256*LMP_OBJ_INTERFACE_ID+LMP_CTYPE_UNMD_LOC, "Unnumbered Local" },
+ { 256*LMP_OBJ_INTERFACE_ID+LMP_CTYPE_UNMD_RMT, "Unnumbered Remote" },
+ { 256*LMP_OBJ_MESSAGE_ID+LMP_CTYPE_1, "1" },
+ { 256*LMP_OBJ_MESSAGE_ID+LMP_CTYPE_2, "2" },
+ { 256*LMP_OBJ_CONFIG+LMP_CTYPE_1, "1" },
+ { 256*LMP_OBJ_HELLO+LMP_CTYPE_1, "1" },
+ { 256*LMP_OBJ_VERIFY_BEGIN+LMP_CTYPE_1, "1" },
+ { 256*LMP_OBJ_VERIFY_BEGIN_ACK+LMP_CTYPE_1, "1" },
+ { 256*LMP_OBJ_VERIFY_ID+LMP_CTYPE_1, "1" },
+ { 256*LMP_OBJ_TE_LINK+LMP_CTYPE_IPV4, "IPv4" },
+ { 256*LMP_OBJ_TE_LINK+LMP_CTYPE_IPV6, "IPv6" },
+ { 256*LMP_OBJ_TE_LINK+LMP_CTYPE_UNMD, "Unnumbered" },
+ { 256*LMP_OBJ_DATA_LINK+LMP_CTYPE_IPV4, "IPv4" },
+ { 256*LMP_OBJ_DATA_LINK+LMP_CTYPE_IPV6, "IPv6" },
+ { 256*LMP_OBJ_DATA_LINK+LMP_CTYPE_UNMD, "Unnumbered" },
+ { 256*LMP_OBJ_CHANNEL_STATUS+LMP_CTYPE_IPV4, "IPv4" },
+ { 256*LMP_OBJ_CHANNEL_STATUS+LMP_CTYPE_IPV6, "IPv6" },
+ { 256*LMP_OBJ_CHANNEL_STATUS+LMP_CTYPE_UNMD, "Unnumbered" },
+ { 256*LMP_OBJ_CHANNEL_STATUS_REQ+LMP_CTYPE_IPV4, "IPv4" },
+ { 256*LMP_OBJ_CHANNEL_STATUS_REQ+LMP_CTYPE_IPV6, "IPv6" },
+ { 256*LMP_OBJ_CHANNEL_STATUS_REQ+LMP_CTYPE_UNMD, "Unnumbered" },
+ { 256*LMP_OBJ_ERROR_CODE+LMP_CTYPE_1, "1" },
+ { 256*LMP_OBJ_ERROR_CODE+LMP_CTYPE_2, "2" },
+ { 256*LMP_OBJ_SERVICE_CONFIG+LMP_CTYPE_SERVICE_CONFIG_SP, "1" },
+ { 256*LMP_OBJ_SERVICE_CONFIG+LMP_CTYPE_SERVICE_CONFIG_CPSA, "2" },
+ { 256*LMP_OBJ_SERVICE_CONFIG+LMP_CTYPE_SERVICE_CONFIG_TRANSPARENCY_TCM, "3" },
+ { 256*LMP_OBJ_SERVICE_CONFIG+LMP_CTYPE_SERVICE_CONFIG_NETWORK_DIVERSITY, "4" },
+ { 0, NULL}
+};
+
+static int
+lmp_print_data_link_subobjs(netdissect_options *ndo, const u_char *obj_tptr,
+ int total_subobj_len, int offset)
+{
+ int hexdump = FALSE;
+ int subobj_type, subobj_len;
+
+ union { /* int to float conversion buffer */
+ float f;
+ uint32_t i;
+ } bw;
+
+ while (total_subobj_len > 0 && hexdump == FALSE ) {
+ subobj_type = GET_U_1(obj_tptr + offset);
+ subobj_len = GET_U_1(obj_tptr + offset + 1);
+ ND_PRINT("\n\t Subobject, Type: %s (%u), Length: %u",
+ tok2str(lmp_data_link_subobj,
+ "Unknown",
+ subobj_type),
+ subobj_type,
+ subobj_len);
+ if (subobj_len < 4) {
+ ND_PRINT(" (too short)");
+ break;
+ }
+ if ((subobj_len % 4) != 0) {
+ ND_PRINT(" (not a multiple of 4)");
+ break;
+ }
+ if (total_subobj_len < subobj_len) {
+ ND_PRINT(" (goes past the end of the object)");
+ break;
+ }
+ switch(subobj_type) {
+ case INT_SWITCHING_TYPE_SUBOBJ:
+ ND_PRINT("\n\t Switching Type: %s (%u)",
+ tok2str(gmpls_switch_cap_values,
+ "Unknown",
+ GET_U_1(obj_tptr + offset + 2)),
+ GET_U_1(obj_tptr + offset + 2));
+ ND_PRINT("\n\t Encoding Type: %s (%u)",
+ tok2str(gmpls_encoding_values,
+ "Unknown",
+ GET_U_1(obj_tptr + offset + 3)),
+ GET_U_1(obj_tptr + offset + 3));
+ bw.i = GET_BE_U_4(obj_tptr + offset + 4);
+ ND_PRINT("\n\t Min Reservable Bandwidth: %.3f Mbps",
+ bw.f*8/1000000);
+ bw.i = GET_BE_U_4(obj_tptr + offset + 8);
+ ND_PRINT("\n\t Max Reservable Bandwidth: %.3f Mbps",
+ bw.f*8/1000000);
+ break;
+ case WAVELENGTH_SUBOBJ:
+ ND_PRINT("\n\t Wavelength: %u",
+ GET_BE_U_4(obj_tptr + offset + 4));
+ break;
+ default:
+ /* Any Unknown Subobject ==> Exit loop */
+ hexdump=TRUE;
+ break;
+ }
+ total_subobj_len-=subobj_len;
+ offset+=subobj_len;
+ }
+ return (hexdump);
+}
+
+void
+lmp_print(netdissect_options *ndo,
+ const u_char *pptr, u_int length)
+{
+ const struct lmp_common_header *lmp_com_header;
+ const u_char *tptr,*obj_tptr;
+ u_int version_res, tlen, lmp_obj_len, lmp_obj_ctype, obj_tlen;
+ int hexdump;
+ u_int offset;
+ u_int link_type;
+
+ union { /* int to float conversion buffer */
+ float f;
+ uint32_t i;
+ } bw;
+
+ ndo->ndo_protocol = "lmp";
+ tptr=pptr;
+ lmp_com_header = (const struct lmp_common_header *)pptr;
+ ND_TCHECK_SIZE(lmp_com_header);
+
+ version_res = GET_BE_U_2(lmp_com_header->version_res);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (LMP_EXTRACT_VERSION(version_res) != LMP_VERSION) {
+ ND_PRINT("LMP version %u packet not supported",
+ LMP_EXTRACT_VERSION(version_res));
+ return;
+ }
+
+ /* in non-verbose mode just lets print the basic Message Type*/
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("LMPv%u %s Message, length: %u",
+ LMP_EXTRACT_VERSION(version_res),
+ tok2str(lmp_msg_type_values, "unknown (%u)",GET_U_1(lmp_com_header->msg_type)),
+ length);
+ return;
+ }
+
+ /* ok they seem to want to know everything - lets fully decode it */
+
+ tlen=GET_BE_U_2(lmp_com_header->length);
+
+ ND_PRINT("\n\tLMPv%u, msg-type: %s, Flags: [%s], length: %u",
+ LMP_EXTRACT_VERSION(version_res),
+ tok2str(lmp_msg_type_values, "unknown, type: %u",GET_U_1(lmp_com_header->msg_type)),
+ bittok2str(lmp_header_flag_values,"none",GET_U_1(lmp_com_header->flags)),
+ tlen);
+ if (tlen < sizeof(struct lmp_common_header)) {
+ ND_PRINT(" (too short)");
+ return;
+ }
+ if (tlen > length) {
+ ND_PRINT(" (too long)");
+ tlen = length;
+ }
+
+ tptr+=sizeof(struct lmp_common_header);
+ tlen-=sizeof(struct lmp_common_header);
+
+ while(tlen>0) {
+ const struct lmp_object_header *lmp_obj_header =
+ (const struct lmp_object_header *)tptr;
+ lmp_obj_len=GET_BE_U_2(lmp_obj_header->length);
+ lmp_obj_ctype=GET_U_1(lmp_obj_header->ctype)&0x7f;
+
+ ND_PRINT("\n\t %s Object (%u), Class-Type: %s (%u) Flags: [%snegotiable], length: %u",
+ tok2str(lmp_obj_values,
+ "Unknown",
+ GET_U_1(lmp_obj_header->class_num)),
+ GET_U_1(lmp_obj_header->class_num),
+ tok2str(lmp_ctype_values,
+ "Unknown",
+ (GET_U_1(lmp_obj_header->class_num)<<8)+lmp_obj_ctype),
+ lmp_obj_ctype,
+ GET_U_1(lmp_obj_header->ctype)&0x80 ? "" : "non-",
+ lmp_obj_len);
+
+ if (lmp_obj_len < 4) {
+ ND_PRINT(" (too short)");
+ return;
+ }
+ if ((lmp_obj_len % 4) != 0) {
+ ND_PRINT(" (not a multiple of 4)");
+ return;
+ }
+
+ obj_tptr=tptr+sizeof(struct lmp_object_header);
+ obj_tlen=lmp_obj_len-sizeof(struct lmp_object_header);
+
+ /* did we capture enough for fully decoding the object ? */
+ ND_TCHECK_LEN(tptr, lmp_obj_len);
+ hexdump=FALSE;
+
+ switch(GET_U_1(lmp_obj_header->class_num)) {
+
+ case LMP_OBJ_CC_ID:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_LOC:
+ case LMP_CTYPE_RMT:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Control Channel ID: %u (0x%08x)",
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_4(obj_tptr));
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_LINK_ID:
+ case LMP_OBJ_INTERFACE_ID:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_IPV4_LOC:
+ case LMP_CTYPE_IPV4_RMT:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t IPv4 Link ID: %s (0x%08x)",
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_4(obj_tptr));
+ break;
+ case LMP_CTYPE_IPV6_LOC:
+ case LMP_CTYPE_IPV6_RMT:
+ if (obj_tlen != 16) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t IPv6 Link ID: %s (0x%08x)",
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_BE_U_4(obj_tptr));
+ break;
+ case LMP_CTYPE_UNMD_LOC:
+ case LMP_CTYPE_UNMD_RMT:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Link ID: %u (0x%08x)",
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_4(obj_tptr));
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_MESSAGE_ID:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_1:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Message ID: %u (0x%08x)",
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_4(obj_tptr));
+ break;
+ case LMP_CTYPE_2:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Message ID Ack: %u (0x%08x)",
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_4(obj_tptr));
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_NODE_ID:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_LOC:
+ case LMP_CTYPE_RMT:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Node ID: %s (0x%08x)",
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_4(obj_tptr));
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_CONFIG:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_HELLO_CONFIG:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Hello Interval: %u\n\t Hello Dead Interval: %u",
+ GET_BE_U_2(obj_tptr),
+ GET_BE_U_2(obj_tptr + 2));
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_HELLO:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_HELLO:
+ if (obj_tlen != 8) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Tx Seq: %u, Rx Seq: %u",
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_4(obj_tptr + 4));
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_TE_LINK:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_IPV4:
+ if (obj_tlen != 12) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Flags: [%s]",
+ bittok2str(lmp_obj_te_link_flag_values,
+ "none",
+ GET_U_1(obj_tptr)));
+
+ ND_PRINT("\n\t Local Link-ID: %s (0x%08x)"
+ "\n\t Remote Link-ID: %s (0x%08x)",
+ GET_IPADDR_STRING(obj_tptr+4),
+ GET_BE_U_4(obj_tptr + 4),
+ GET_IPADDR_STRING(obj_tptr+8),
+ GET_BE_U_4(obj_tptr + 8));
+ break;
+
+ case LMP_CTYPE_IPV6:
+ if (obj_tlen != 36) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Flags: [%s]",
+ bittok2str(lmp_obj_te_link_flag_values,
+ "none",
+ GET_U_1(obj_tptr)));
+
+ ND_PRINT("\n\t Local Link-ID: %s (0x%08x)"
+ "\n\t Remote Link-ID: %s (0x%08x)",
+ GET_IP6ADDR_STRING(obj_tptr+4),
+ GET_BE_U_4(obj_tptr + 4),
+ GET_IP6ADDR_STRING(obj_tptr+20),
+ GET_BE_U_4(obj_tptr + 20));
+ break;
+
+ case LMP_CTYPE_UNMD:
+ if (obj_tlen != 12) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Flags: [%s]",
+ bittok2str(lmp_obj_te_link_flag_values,
+ "none",
+ GET_U_1(obj_tptr)));
+
+ ND_PRINT("\n\t Local Link-ID: %u (0x%08x)"
+ "\n\t Remote Link-ID: %u (0x%08x)",
+ GET_BE_U_4(obj_tptr + 4),
+ GET_BE_U_4(obj_tptr + 4),
+ GET_BE_U_4(obj_tptr + 8),
+ GET_BE_U_4(obj_tptr + 8));
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_DATA_LINK:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_IPV4:
+ if (obj_tlen < 12) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Flags: [%s]",
+ bittok2str(lmp_obj_data_link_flag_values,
+ "none",
+ GET_U_1(obj_tptr)));
+ ND_PRINT("\n\t Local Interface ID: %s (0x%08x)"
+ "\n\t Remote Interface ID: %s (0x%08x)",
+ GET_IPADDR_STRING(obj_tptr+4),
+ GET_BE_U_4(obj_tptr + 4),
+ GET_IPADDR_STRING(obj_tptr+8),
+ GET_BE_U_4(obj_tptr + 8));
+
+ if (lmp_print_data_link_subobjs(ndo, obj_tptr, obj_tlen - 12, 12))
+ hexdump=TRUE;
+ break;
+
+ case LMP_CTYPE_IPV6:
+ if (obj_tlen < 36) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Flags: [%s]",
+ bittok2str(lmp_obj_data_link_flag_values,
+ "none",
+ GET_U_1(obj_tptr)));
+ ND_PRINT("\n\t Local Interface ID: %s (0x%08x)"
+ "\n\t Remote Interface ID: %s (0x%08x)",
+ GET_IP6ADDR_STRING(obj_tptr+4),
+ GET_BE_U_4(obj_tptr + 4),
+ GET_IP6ADDR_STRING(obj_tptr+20),
+ GET_BE_U_4(obj_tptr + 20));
+
+ if (lmp_print_data_link_subobjs(ndo, obj_tptr, obj_tlen - 36, 36))
+ hexdump=TRUE;
+ break;
+
+ case LMP_CTYPE_UNMD:
+ if (obj_tlen < 12) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Flags: [%s]",
+ bittok2str(lmp_obj_data_link_flag_values,
+ "none",
+ GET_U_1(obj_tptr)));
+ ND_PRINT("\n\t Local Interface ID: %u (0x%08x)"
+ "\n\t Remote Interface ID: %u (0x%08x)",
+ GET_BE_U_4(obj_tptr + 4),
+ GET_BE_U_4(obj_tptr + 4),
+ GET_BE_U_4(obj_tptr + 8),
+ GET_BE_U_4(obj_tptr + 8));
+
+ if (lmp_print_data_link_subobjs(ndo, obj_tptr, obj_tlen - 12, 12))
+ hexdump=TRUE;
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_VERIFY_BEGIN:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_1:
+ if (obj_tlen != 20) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Flags: %s",
+ bittok2str(lmp_obj_begin_verify_flag_values,
+ "none",
+ GET_BE_U_2(obj_tptr)));
+ ND_PRINT("\n\t Verify Interval: %u",
+ GET_BE_U_2(obj_tptr + 2));
+ ND_PRINT("\n\t Data links: %u",
+ GET_BE_U_4(obj_tptr + 4));
+ ND_PRINT("\n\t Encoding type: %s",
+ tok2str(gmpls_encoding_values, "Unknown", GET_U_1((obj_tptr + 8))));
+ ND_PRINT("\n\t Verify Transport Mechanism: %u (0x%x)%s",
+ GET_BE_U_2(obj_tptr + 10),
+ GET_BE_U_2(obj_tptr + 10),
+ GET_BE_U_2(obj_tptr + 10)&8000 ? " (Payload test messages capable)" : "");
+ bw.i = GET_BE_U_4(obj_tptr + 12);
+ ND_PRINT("\n\t Transmission Rate: %.3f Mbps",bw.f*8/1000000);
+ ND_PRINT("\n\t Wavelength: %u",
+ GET_BE_U_4(obj_tptr + 16));
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_VERIFY_BEGIN_ACK:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_1:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Verify Dead Interval: %u"
+ "\n\t Verify Transport Response: %u",
+ GET_BE_U_2(obj_tptr),
+ GET_BE_U_2(obj_tptr + 2));
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_VERIFY_ID:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_1:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Verify ID: %u",
+ GET_BE_U_4(obj_tptr));
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_CHANNEL_STATUS:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_IPV4:
+ offset = 0;
+ /* Decode pairs: <Interface_ID (4 bytes), Channel_status (4 bytes)> */
+ while (offset+8 <= obj_tlen) {
+ ND_PRINT("\n\t Interface ID: %s (0x%08x)",
+ GET_IPADDR_STRING(obj_tptr+offset),
+ GET_BE_U_4(obj_tptr + offset));
+
+ ND_PRINT("\n\t\t Active: %s (%u)",
+ (GET_BE_U_4(obj_tptr + offset + 4)>>31) ?
+ "Allocated" : "Non-allocated",
+ (GET_BE_U_4(obj_tptr + offset + 4)>>31));
+
+ ND_PRINT("\n\t\t Direction: %s (%u)",
+ (GET_BE_U_4(obj_tptr + offset + 4)>>30)&0x1 ?
+ "Transmit" : "Receive",
+ (GET_BE_U_4(obj_tptr + offset + 4)>>30)&0x1);
+
+ ND_PRINT("\n\t\t Channel Status: %s (%u)",
+ tok2str(lmp_obj_channel_status_values,
+ "Unknown",
+ GET_BE_U_4(obj_tptr + offset + 4)&0x3FFFFFF),
+ GET_BE_U_4(obj_tptr + offset + 4)&0x3FFFFFF);
+ offset+=8;
+ }
+ break;
+
+ case LMP_CTYPE_IPV6:
+ offset = 0;
+ /* Decode pairs: <Interface_ID (16 bytes), Channel_status (4 bytes)> */
+ while (offset+20 <= obj_tlen) {
+ ND_PRINT("\n\t Interface ID: %s (0x%08x)",
+ GET_IP6ADDR_STRING(obj_tptr+offset),
+ GET_BE_U_4(obj_tptr + offset));
+
+ ND_PRINT("\n\t\t Active: %s (%u)",
+ (GET_BE_U_4(obj_tptr + offset + 16)>>31) ?
+ "Allocated" : "Non-allocated",
+ (GET_BE_U_4(obj_tptr + offset + 16)>>31));
+
+ ND_PRINT("\n\t\t Direction: %s (%u)",
+ (GET_BE_U_4(obj_tptr + offset + 16)>>30)&0x1 ?
+ "Transmit" : "Receive",
+ (GET_BE_U_4(obj_tptr + offset + 16)>>30)&0x1);
+
+ ND_PRINT("\n\t\t Channel Status: %s (%u)",
+ tok2str(lmp_obj_channel_status_values,
+ "Unknown",
+ GET_BE_U_4(obj_tptr + offset + 16)&0x3FFFFFF),
+ GET_BE_U_4(obj_tptr + offset + 16)&0x3FFFFFF);
+ offset+=20;
+ }
+ break;
+
+ case LMP_CTYPE_UNMD:
+ offset = 0;
+ /* Decode pairs: <Interface_ID (4 bytes), Channel_status (4 bytes)> */
+ while (offset+8 <= obj_tlen) {
+ ND_PRINT("\n\t Interface ID: %u (0x%08x)",
+ GET_BE_U_4(obj_tptr + offset),
+ GET_BE_U_4(obj_tptr + offset));
+
+ ND_PRINT("\n\t\t Active: %s (%u)",
+ (GET_BE_U_4(obj_tptr + offset + 4)>>31) ?
+ "Allocated" : "Non-allocated",
+ (GET_BE_U_4(obj_tptr + offset + 4)>>31));
+
+ ND_PRINT("\n\t\t Direction: %s (%u)",
+ (GET_BE_U_4(obj_tptr + offset + 4)>>30)&0x1 ?
+ "Transmit" : "Receive",
+ (GET_BE_U_4(obj_tptr + offset + 4)>>30)&0x1);
+
+ ND_PRINT("\n\t\t Channel Status: %s (%u)",
+ tok2str(lmp_obj_channel_status_values,
+ "Unknown",
+ GET_BE_U_4(obj_tptr + offset + 4)&0x3FFFFFF),
+ GET_BE_U_4(obj_tptr + offset + 4)&0x3FFFFFF);
+ offset+=8;
+ }
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_CHANNEL_STATUS_REQ:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_IPV4:
+ offset = 0;
+ while (offset+4 <= obj_tlen) {
+ ND_PRINT("\n\t Interface ID: %s (0x%08x)",
+ GET_IPADDR_STRING(obj_tptr+offset),
+ GET_BE_U_4(obj_tptr + offset));
+ offset+=4;
+ }
+ break;
+
+ case LMP_CTYPE_IPV6:
+ offset = 0;
+ while (offset+16 <= obj_tlen) {
+ ND_PRINT("\n\t Interface ID: %s (0x%08x)",
+ GET_IP6ADDR_STRING(obj_tptr+offset),
+ GET_BE_U_4(obj_tptr + offset));
+ offset+=16;
+ }
+ break;
+
+ case LMP_CTYPE_UNMD:
+ offset = 0;
+ while (offset+4 <= obj_tlen) {
+ ND_PRINT("\n\t Interface ID: %u (0x%08x)",
+ GET_BE_U_4(obj_tptr + offset),
+ GET_BE_U_4(obj_tptr + offset));
+ offset+=4;
+ }
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_ERROR_CODE:
+ switch(lmp_obj_ctype) {
+ case LMP_CTYPE_BEGIN_VERIFY_ERROR:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Error Code: %s",
+ bittok2str(lmp_obj_begin_verify_error_values,
+ "none",
+ GET_BE_U_4(obj_tptr)));
+ break;
+
+ case LMP_CTYPE_LINK_SUMMARY_ERROR:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Error Code: %s",
+ bittok2str(lmp_obj_link_summary_error_values,
+ "none",
+ GET_BE_U_4(obj_tptr)));
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case LMP_OBJ_SERVICE_CONFIG:
+ switch (lmp_obj_ctype) {
+ case LMP_CTYPE_SERVICE_CONFIG_SP:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+ ND_PRINT("\n\t Flags: %s",
+ bittok2str(lmp_obj_service_config_sp_flag_values,
+ "none",
+ GET_U_1(obj_tptr)));
+
+ ND_PRINT("\n\t UNI Version: %u",
+ GET_U_1(obj_tptr + 1));
+
+ break;
+
+ case LMP_CTYPE_SERVICE_CONFIG_CPSA:
+ if (obj_tlen != 16) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+
+ link_type = GET_U_1(obj_tptr);
+
+ ND_PRINT("\n\t Link Type: %s (%u)",
+ tok2str(lmp_sd_service_config_cpsa_link_type_values,
+ "Unknown", link_type),
+ link_type);
+
+ switch (link_type) {
+ case LMP_SD_SERVICE_CONFIG_CPSA_LINK_TYPE_SDH:
+ ND_PRINT("\n\t Signal Type: %s (%u)",
+ tok2str(lmp_sd_service_config_cpsa_signal_type_sdh_values,
+ "Unknown",
+ GET_U_1(obj_tptr + 1)),
+ GET_U_1(obj_tptr + 1));
+ break;
+
+ case LMP_SD_SERVICE_CONFIG_CPSA_LINK_TYPE_SONET:
+ ND_PRINT("\n\t Signal Type: %s (%u)",
+ tok2str(lmp_sd_service_config_cpsa_signal_type_sonet_values,
+ "Unknown",
+ GET_U_1(obj_tptr + 1)),
+ GET_U_1(obj_tptr + 1));
+ break;
+ }
+
+ ND_PRINT("\n\t Transparency: %s",
+ bittok2str(lmp_obj_service_config_cpsa_tp_flag_values,
+ "none",
+ GET_U_1(obj_tptr + 2)));
+
+ ND_PRINT("\n\t Contiguous Concatenation Types: %s",
+ bittok2str(lmp_obj_service_config_cpsa_cct_flag_values,
+ "none",
+ GET_U_1(obj_tptr + 3)));
+
+ ND_PRINT("\n\t Minimum NCC: %u",
+ GET_BE_U_2(obj_tptr + 4));
+
+ ND_PRINT("\n\t Maximum NCC: %u",
+ GET_BE_U_2(obj_tptr + 6));
+
+ ND_PRINT("\n\t Minimum NVC:%u",
+ GET_BE_U_2(obj_tptr + 8));
+
+ ND_PRINT("\n\t Maximum NVC:%u",
+ GET_BE_U_2(obj_tptr + 10));
+
+ ND_PRINT("\n\t Local Interface ID: %s (0x%08x)",
+ GET_IPADDR_STRING(obj_tptr+12),
+ GET_BE_U_4(obj_tptr + 12));
+
+ break;
+
+ case LMP_CTYPE_SERVICE_CONFIG_TRANSPARENCY_TCM:
+ if (obj_tlen != 8) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+
+ ND_PRINT("\n\t Transparency Flags: %s",
+ bittok2str(
+ lmp_obj_service_config_nsa_transparency_flag_values,
+ "none",
+ GET_BE_U_4(obj_tptr)));
+
+ ND_PRINT("\n\t TCM Monitoring Flags: %s",
+ bittok2str(
+ lmp_obj_service_config_nsa_tcm_flag_values,
+ "none",
+ GET_U_1(obj_tptr + 7)));
+
+ break;
+
+ case LMP_CTYPE_SERVICE_CONFIG_NETWORK_DIVERSITY:
+ if (obj_tlen != 4) {
+ ND_PRINT(" (not correct for object)");
+ break;
+ }
+
+ ND_PRINT("\n\t Diversity: Flags: %s",
+ bittok2str(
+ lmp_obj_service_config_nsa_network_diversity_flag_values,
+ "none",
+ GET_U_1(obj_tptr + 3)));
+ break;
+
+ default:
+ hexdump = TRUE;
+ }
+
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo,obj_tptr,"\n\t ",obj_tlen);
+ break;
+ }
+ /* do we want to see an additionally hexdump ? */
+ if (ndo->ndo_vflag > 1 || hexdump==TRUE)
+ print_unknown_data(ndo,tptr+sizeof(struct lmp_object_header),"\n\t ",
+ lmp_obj_len-sizeof(struct lmp_object_header));
+
+ if (tlen < lmp_obj_len) {
+ ND_PRINT(" [remaining objects length %u < %u]", tlen, lmp_obj_len);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tptr+=lmp_obj_len;
+ tlen-=lmp_obj_len;
+ }
+}
diff --git a/print-loopback.c b/print-loopback.c
new file mode 100644
index 0000000..ee0caf3
--- /dev/null
+++ b/print-loopback.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2014 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: Loopback Protocol printer */
+
+/*
+ * originally defined as the Ethernet Configuration Testing Protocol.
+ * specification: https://www.mit.edu/people/jhawk/ctp.pdf
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+#define LOOPBACK_REPLY 1
+#define LOOPBACK_FWDDATA 2
+
+static const struct tok fcode_str[] = {
+ { LOOPBACK_REPLY, "Reply" },
+ { LOOPBACK_FWDDATA, "Forward Data" },
+ { 0, NULL }
+};
+
+static void
+loopback_message_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t function;
+
+ if (len < 2)
+ goto invalid;
+ /* function */
+ function = GET_LE_U_2(cp);
+ cp += 2;
+ len -= 2;
+ ND_PRINT(", %s", tok2str(fcode_str, " invalid (%u)", function));
+
+ switch (function) {
+ case LOOPBACK_REPLY:
+ if (len < 2)
+ goto invalid;
+ /* receipt number */
+ ND_PRINT(", receipt number %u", GET_LE_U_2(cp));
+ cp += 2;
+ len -= 2;
+ /* data */
+ ND_PRINT(", data (%u octets)", len);
+ ND_TCHECK_LEN(cp, len);
+ break;
+ case LOOPBACK_FWDDATA:
+ if (len < MAC_ADDR_LEN)
+ goto invalid;
+ /* forwarding address */
+ ND_PRINT(", forwarding address %s", GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ len -= MAC_ADDR_LEN;
+ /* data */
+ ND_PRINT(", data (%u octets)", len);
+ ND_TCHECK_LEN(cp, len);
+ break;
+ default:
+ ND_TCHECK_LEN(cp, len);
+ break;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+void
+loopback_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t skipCount;
+
+ ndo->ndo_protocol = "loopback";
+ ND_PRINT("Loopback");
+ if (len < 2)
+ goto invalid;
+ /* skipCount */
+ skipCount = GET_LE_U_2(cp);
+ cp += 2;
+ len -= 2;
+ ND_PRINT(", skipCount %u", skipCount);
+ if (skipCount % 8)
+ ND_PRINT(" (bogus)");
+ if (skipCount > len)
+ goto invalid;
+ /* the octets to skip */
+ ND_TCHECK_LEN(cp, skipCount);
+ cp += skipCount;
+ len -= skipCount;
+ /* the first message to decode */
+ loopback_message_print(ndo, cp, len);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
diff --git a/print-lspping.c b/print-lspping.c
new file mode 100644
index 0000000..4c5fc4e
--- /dev/null
+++ b/print-lspping.c
@@ -0,0 +1,1080 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* \summary: MPLS LSP PING printer */
+
+/* specification: RFC 4379 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "ntp.h"
+
+#include "l2vpn.h"
+#include "oui.h"
+
+
+/*
+ * LSPPING common header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version Number | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Message Type | Reply mode | Return Code | Return Subcode|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sender's Handle |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TimeStamp Sent (seconds) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TimeStamp Sent (microseconds) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TimeStamp Received (seconds) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TimeStamp Received (microseconds) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TLVs ... |
+ * . .
+ * . .
+ * . .
+ */
+
+struct lspping_common_header {
+ nd_uint16_t version;
+ nd_uint16_t global_flags;
+ nd_uint8_t msg_type;
+ nd_uint8_t reply_mode;
+ nd_uint8_t return_code;
+ nd_uint8_t return_subcode;
+ nd_uint32_t sender_handle;
+ nd_uint32_t seq_number;
+ struct l_fixedpt ts_sent;
+ struct l_fixedpt ts_rcvd;
+};
+
+#define LSPPING_VERSION 1
+
+static const struct tok lspping_msg_type_values[] = {
+ { 1, "MPLS Echo Request"},
+ { 2, "MPLS Echo Reply"},
+ { 0, NULL}
+};
+
+static const struct tok lspping_reply_mode_values[] = {
+ { 1, "Do not reply"},
+ { 2, "Reply via an IPv4/IPv6 UDP packet"},
+ { 3, "Reply via an IPv4/IPv6 UDP packet with Router Alert"},
+ { 4, "Reply via application level control channel"},
+ { 0, NULL}
+};
+
+static const struct tok lspping_return_code_values[] = {
+ { 0, "No return code or return code contained in the Error Code TLV"},
+ { 1, "Malformed echo request received"},
+ { 2, "One or more of the TLVs was not understood"},
+ { 3, "Replying router is an egress for the FEC at stack depth"},
+ { 4, "Replying router has no mapping for the FEC at stack depth"},
+ { 5, "Reserved"},
+ { 6, "Reserved"},
+ { 7, "Reserved"},
+ { 8, "Label switched at stack-depth"},
+ { 9, "Label switched but no MPLS forwarding at stack-depth"},
+ { 10, "Mapping for this FEC is not the given label at stack depth"},
+ { 11, "No label entry at stack-depth"},
+ { 12, "Protocol not associated with interface at FEC stack depth"},
+ { 13, "Premature termination of ping due to label stack shrinking to a single label"},
+ { 0, NULL},
+};
+
+
+/*
+ * LSPPING TLV header
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Value |
+ * . .
+ * . .
+ * . .
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct lspping_tlv_header {
+ nd_uint16_t type;
+ nd_uint16_t length;
+};
+
+#define LSPPING_TLV_TARGET_FEC_STACK 1
+#define LSPPING_TLV_DOWNSTREAM_MAPPING 2
+#define LSPPING_TLV_PAD 3
+/* not assigned 4 */
+#define LSPPING_TLV_VENDOR_ENTERPRISE 5
+#define LSPPING_TLV_VENDOR_ENTERPRISE_LEN 4
+/* not assigned 6 */
+#define LSPPING_TLV_INTERFACE_LABEL_STACK 7
+/* not assigned 8 */
+#define LSPPING_TLV_ERROR_CODE 9
+#define LSPPING_TLV_REPLY_TOS_BYTE 10
+#define LSPPING_TLV_BFD_DISCRIMINATOR 15 /* draft-ietf-bfd-mpls-02 */
+#define LSPPING_TLV_BFD_DISCRIMINATOR_LEN 4
+#define LSPPING_TLV_VENDOR_PRIVATE 0xfc00
+
+static const struct tok lspping_tlv_values[] = {
+ { LSPPING_TLV_TARGET_FEC_STACK, "Target FEC Stack" },
+ { LSPPING_TLV_DOWNSTREAM_MAPPING, "Downstream Mapping" },
+ { LSPPING_TLV_PAD, "Pad" },
+ { LSPPING_TLV_ERROR_CODE, "Error Code" },
+ { LSPPING_TLV_VENDOR_ENTERPRISE, "Vendor Enterprise Code" },
+ { LSPPING_TLV_INTERFACE_LABEL_STACK, "Interface Label Stack" },
+ { LSPPING_TLV_REPLY_TOS_BYTE, "Reply TOS Byte" },
+ { LSPPING_TLV_BFD_DISCRIMINATOR, "BFD Discriminator" },
+ { LSPPING_TLV_VENDOR_PRIVATE, "Vendor Private Code" },
+ { 0, NULL}
+};
+
+#define LSPPING_TLV_TARGETFEC_SUBTLV_LDP_IPV4 1
+#define LSPPING_TLV_TARGETFEC_SUBTLV_LDP_IPV6 2
+#define LSPPING_TLV_TARGETFEC_SUBTLV_RSVP_IPV4 3
+#define LSPPING_TLV_TARGETFEC_SUBTLV_RSVP_IPV6 4
+/* not assigned 5 */
+#define LSPPING_TLV_TARGETFEC_SUBTLV_L3VPN_IPV4 6
+#define LSPPING_TLV_TARGETFEC_SUBTLV_L3VPN_IPV6 7
+#define LSPPING_TLV_TARGETFEC_SUBTLV_L2VPN_ENDPT 8
+#define LSPPING_TLV_TARGETFEC_SUBTLV_FEC_128_PW_OLD 9
+#define LSPPING_TLV_TARGETFEC_SUBTLV_FEC_128_PW 10
+#define LSPPING_TLV_TARGETFEC_SUBTLV_FEC_129_PW 11
+#define LSPPING_TLV_TARGETFEC_SUBTLV_BGP_IPV4 12
+#define LSPPING_TLV_TARGETFEC_SUBTLV_BGP_IPV6 13
+#define LSPPING_TLV_TARGETFEC_SUBTLV_GENERIC_IPV4 14
+#define LSPPING_TLV_TARGETFEC_SUBTLV_GENERIC_IPV6 15
+#define LSPPING_TLV_TARGETFEC_SUBTLV_NIL_FEC 16
+
+static const struct tok lspping_tlvtargetfec_subtlv_values[] = {
+ { LSPPING_TLV_TARGETFEC_SUBTLV_LDP_IPV4, "LDP IPv4 prefix"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_LDP_IPV6, "LDP IPv6 prefix"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_RSVP_IPV4, "RSVP IPv4 Session Query"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_RSVP_IPV6, "RSVP IPv6 Session Query"},
+ { 5, "Reserved"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_L3VPN_IPV4, "VPN IPv4 prefix"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_L3VPN_IPV6, "VPN IPv6 prefix"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_L2VPN_ENDPT, "L2 VPN endpoint"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_FEC_128_PW_OLD, "FEC 128 pseudowire (old)"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_FEC_128_PW, "FEC 128 pseudowire"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_BGP_IPV4, "BGP labeled IPv4 prefix"},
+ { LSPPING_TLV_TARGETFEC_SUBTLV_BGP_IPV6, "BGP labeled IPv6 prefix"},
+ { 0, NULL}
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv4 prefix |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Prefix Length | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_ldp_ipv4_t {
+ nd_ipv4 prefix;
+ nd_uint8_t prefix_len;
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv6 prefix |
+ * | (16 octets) |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Prefix Length | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_ldp_ipv6_t {
+ nd_ipv6 prefix;
+ nd_uint8_t prefix_len;
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv4 tunnel end point address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Must Be Zero | Tunnel ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Extended Tunnel ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv4 tunnel sender address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Must Be Zero | LSP ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_rsvp_ipv4_t {
+ nd_ipv4 tunnel_endpoint;
+ nd_byte res[2];
+ nd_uint16_t tunnel_id;
+ nd_ipv4 extended_tunnel_id;
+ nd_ipv4 tunnel_sender;
+ nd_byte res2[2];
+ nd_uint16_t lsp_id;
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv6 tunnel end point address |
+ * | |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Must Be Zero | Tunnel ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Extended Tunnel ID |
+ * | |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv6 tunnel sender address |
+ * | |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Must Be Zero | LSP ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_rsvp_ipv6_t {
+ nd_ipv6 tunnel_endpoint;
+ nd_byte res[2];
+ nd_uint16_t tunnel_id;
+ nd_ipv6 extended_tunnel_id;
+ nd_ipv6 tunnel_sender;
+ nd_byte res2[2];
+ nd_uint16_t lsp_id;
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Route Distinguisher |
+ * | (8 octets) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv4 prefix |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Prefix Length | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_l3vpn_ipv4_t {
+ nd_byte rd[8];
+ nd_ipv4 prefix;
+ nd_uint8_t prefix_len;
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Route Distinguisher |
+ * | (8 octets) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv6 prefix |
+ * | (16 octets) |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Prefix Length | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_l3vpn_ipv6_t {
+ nd_byte rd[8];
+ nd_ipv6 prefix;
+ nd_uint8_t prefix_len;
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Route Distinguisher |
+ * | (8 octets) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sender's VE ID | Receiver's VE ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encapsulation Type | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 0 1 2 3
+ */
+struct lspping_tlv_targetfec_subtlv_l2vpn_endpt_t {
+ nd_byte rd[8];
+ nd_uint16_t sender_ve_id;
+ nd_uint16_t receiver_ve_id;
+ nd_uint16_t encapsulation;
+};
+
+/*
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Remote PE Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | PW ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | PW Type | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_fec_128_pw_old {
+ nd_ipv4 remote_pe_address;
+ nd_uint32_t pw_id;
+ nd_uint16_t pw_type;
+};
+
+/*
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sender's PE Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Remote PE Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | PW ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | PW Type | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_fec_128_pw {
+ nd_ipv4 sender_pe_address;
+ nd_ipv4 remote_pe_address;
+ nd_uint32_t pw_id;
+ nd_uint16_t pw_type;
+};
+
+/*
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv4 prefix |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Prefix Length | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_bgp_ipv4_t {
+ nd_ipv4 prefix;
+ nd_uint8_t prefix_len;
+};
+
+/*
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv6 prefix |
+ * | (16 octets) |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Prefix Length | Must Be Zero |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lspping_tlv_targetfec_subtlv_bgp_ipv6_t {
+ nd_ipv6 prefix;
+ nd_uint8_t prefix_len;
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MTU | Address Type | Resvd (SBZ) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Downstream IP Address (4 or 16 octets) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Downstream Interface Address (4 or 16 octets) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Multipath Type| Depth Limit | Multipath Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * . .
+ * . (Multipath Information) .
+ * . .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Downstream Label | Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * . .
+ * . .
+ * . .
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Downstream Label | Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+/* Enough to get the address type */
+struct lspping_tlv_downstream_map_t {
+ nd_uint16_t mtu;
+ nd_uint8_t address_type;
+ nd_uint8_t ds_flags;
+};
+
+struct lspping_tlv_downstream_map_ipv4_t {
+ nd_uint16_t mtu;
+ nd_uint8_t address_type;
+ nd_uint8_t ds_flags;
+ nd_ipv4 downstream_ip;
+ nd_ipv4 downstream_interface;
+};
+
+struct lspping_tlv_downstream_map_ipv4_unmb_t {
+ nd_uint16_t mtu;
+ nd_uint8_t address_type;
+ nd_uint8_t ds_flags;
+ nd_ipv4 downstream_ip;
+ nd_uint32_t downstream_interface;
+};
+
+struct lspping_tlv_downstream_map_ipv6_t {
+ nd_uint16_t mtu;
+ nd_uint8_t address_type;
+ nd_uint8_t ds_flags;
+ nd_ipv6 downstream_ip;
+ nd_ipv6 downstream_interface;
+};
+
+struct lspping_tlv_downstream_map_ipv6_unmb_t {
+ nd_uint16_t mtu;
+ nd_uint8_t address_type;
+ nd_uint8_t ds_flags;
+ nd_ipv6 downstream_ip;
+ nd_uint32_t downstream_interface;
+};
+
+struct lspping_tlv_downstream_map_info_t {
+ nd_uint8_t multipath_type;
+ nd_uint8_t depth_limit;
+ nd_uint16_t multipath_length;
+};
+
+#define LSPPING_AFI_IPV4 1
+#define LSPPING_AFI_IPV4_UNMB 2
+#define LSPPING_AFI_IPV6 3
+#define LSPPING_AFI_IPV6_UNMB 4
+
+static const struct tok lspping_tlv_downstream_addr_values[] = {
+ { LSPPING_AFI_IPV4, "IPv4"},
+ { LSPPING_AFI_IPV4_UNMB, "Unnumbered IPv4"},
+ { LSPPING_AFI_IPV6, "IPv6"},
+ { LSPPING_AFI_IPV6_UNMB, "IPv6"},
+ { 0, NULL}
+};
+
+void
+lspping_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ const struct lspping_common_header *lspping_com_header;
+ const struct lspping_tlv_header *lspping_tlv_header;
+ const struct lspping_tlv_header *lspping_subtlv_header;
+ const u_char *tptr,*tlv_tptr,*subtlv_tptr;
+ u_int return_code, return_subcode;
+ u_int tlen,lspping_tlv_len,lspping_tlv_type,tlv_tlen;
+ int tlv_hexdump,subtlv_hexdump;
+ u_int lspping_subtlv_len,lspping_subtlv_type;
+ uint32_t int_part, fraction;
+ u_int address_type;
+
+ union {
+ const struct lspping_tlv_downstream_map_t *lspping_tlv_downstream_map;
+ const struct lspping_tlv_downstream_map_ipv4_t *lspping_tlv_downstream_map_ipv4;
+ const struct lspping_tlv_downstream_map_ipv4_unmb_t *lspping_tlv_downstream_map_ipv4_unmb;
+ const struct lspping_tlv_downstream_map_ipv6_t *lspping_tlv_downstream_map_ipv6;
+ const struct lspping_tlv_downstream_map_ipv6_unmb_t *lspping_tlv_downstream_map_ipv6_unmb;
+ const struct lspping_tlv_downstream_map_info_t *lspping_tlv_downstream_map_info;
+ } tlv_ptr;
+
+ union {
+ const struct lspping_tlv_targetfec_subtlv_ldp_ipv4_t *lspping_tlv_targetfec_subtlv_ldp_ipv4;
+ const struct lspping_tlv_targetfec_subtlv_ldp_ipv6_t *lspping_tlv_targetfec_subtlv_ldp_ipv6;
+ const struct lspping_tlv_targetfec_subtlv_rsvp_ipv4_t *lspping_tlv_targetfec_subtlv_rsvp_ipv4;
+ const struct lspping_tlv_targetfec_subtlv_rsvp_ipv6_t *lspping_tlv_targetfec_subtlv_rsvp_ipv6;
+ const struct lspping_tlv_targetfec_subtlv_l3vpn_ipv4_t *lspping_tlv_targetfec_subtlv_l3vpn_ipv4;
+ const struct lspping_tlv_targetfec_subtlv_l3vpn_ipv6_t *lspping_tlv_targetfec_subtlv_l3vpn_ipv6;
+ const struct lspping_tlv_targetfec_subtlv_l2vpn_endpt_t *lspping_tlv_targetfec_subtlv_l2vpn_endpt;
+ const struct lspping_tlv_targetfec_subtlv_fec_128_pw_old *lspping_tlv_targetfec_subtlv_l2vpn_vcid_old;
+ const struct lspping_tlv_targetfec_subtlv_fec_128_pw *lspping_tlv_targetfec_subtlv_l2vpn_vcid;
+ const struct lspping_tlv_targetfec_subtlv_bgp_ipv4_t *lspping_tlv_targetfec_subtlv_bgp_ipv4;
+ const struct lspping_tlv_targetfec_subtlv_bgp_ipv6_t *lspping_tlv_targetfec_subtlv_bgp_ipv6;
+ } subtlv_ptr;
+
+ ndo->ndo_protocol = "lspping";
+ tptr=pptr;
+ lspping_com_header = (const struct lspping_common_header *)pptr;
+ if (len < sizeof(struct lspping_common_header))
+ goto tooshort;
+ ND_TCHECK_SIZE(lspping_com_header);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (GET_BE_U_2(lspping_com_header->version) != LSPPING_VERSION) {
+ ND_PRINT("LSP-PING version %u packet not supported",
+ GET_BE_U_2(lspping_com_header->version));
+ return;
+ }
+
+ /* in non-verbose mode just lets print the basic Message Type*/
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("LSP-PINGv%u, %s, seq %u, length: %u",
+ GET_BE_U_2(lspping_com_header->version),
+ tok2str(lspping_msg_type_values, "unknown (%u)",GET_U_1(lspping_com_header->msg_type)),
+ GET_BE_U_4(lspping_com_header->seq_number),
+ len);
+ return;
+ }
+
+ /* ok they seem to want to know everything - lets fully decode it */
+
+ tlen=len;
+
+ ND_PRINT("\n\tLSP-PINGv%u, msg-type: %s (%u), length: %u\n\t reply-mode: %s (%u)",
+ GET_BE_U_2(lspping_com_header->version),
+ tok2str(lspping_msg_type_values, "unknown",GET_U_1(lspping_com_header->msg_type)),
+ GET_U_1(lspping_com_header->msg_type),
+ len,
+ tok2str(lspping_reply_mode_values, "unknown",GET_U_1(lspping_com_header->reply_mode)),
+ GET_U_1(lspping_com_header->reply_mode));
+
+ /*
+ * the following return codes require that the subcode is attached
+ * at the end of the translated token output
+ */
+ return_code = GET_U_1(lspping_com_header->return_code);
+ return_subcode = GET_U_1(lspping_com_header->return_subcode);
+ if (return_code == 3 ||
+ return_code == 4 ||
+ return_code == 8 ||
+ return_code == 10 ||
+ return_code == 11 ||
+ return_code == 12 )
+ ND_PRINT("\n\t Return Code: %s %u (%u)\n\t Return Subcode: (%u)",
+ tok2str(lspping_return_code_values, "unknown",return_code),
+ return_subcode,
+ return_code,
+ return_subcode);
+ else
+ ND_PRINT("\n\t Return Code: %s (%u)\n\t Return Subcode: (%u)",
+ tok2str(lspping_return_code_values, "unknown",return_code),
+ return_code,
+ return_subcode);
+
+ ND_PRINT("\n\t Sender Handle: 0x%08x, Sequence: %u",
+ GET_BE_U_4(lspping_com_header->sender_handle),
+ GET_BE_U_4(lspping_com_header->seq_number));
+
+ ND_PRINT("\n\t Sender Timestamp: ");
+ p_ntp_time(ndo, &lspping_com_header->ts_sent);
+ ND_PRINT(" ");
+
+ int_part=GET_BE_U_4(lspping_com_header->ts_rcvd.int_part);
+ fraction=GET_BE_U_4(lspping_com_header->ts_rcvd.fraction);
+ ND_PRINT("Receiver Timestamp: ");
+ if (! (int_part == 0 && fraction == 0))
+ p_ntp_time(ndo, &lspping_com_header->ts_rcvd);
+ else
+ ND_PRINT("no timestamp");
+
+ tptr+=sizeof(struct lspping_common_header);
+ tlen-=sizeof(struct lspping_common_header);
+
+ while (tlen != 0) {
+ /* Does the TLV go past the end of the packet? */
+ if (tlen < sizeof(struct lspping_tlv_header))
+ goto tooshort;
+
+ lspping_tlv_header = (const struct lspping_tlv_header *)tptr;
+ lspping_tlv_type=GET_BE_U_2(lspping_tlv_header->type);
+ lspping_tlv_len=GET_BE_U_2(lspping_tlv_header->length);
+
+ ND_PRINT("\n\t %s TLV (%u), length: %u",
+ tok2str(lspping_tlv_values,
+ "Unknown",
+ lspping_tlv_type),
+ lspping_tlv_type,
+ lspping_tlv_len);
+
+ /* some little sanity checking */
+ if (lspping_tlv_len == 0) {
+ tptr+=sizeof(struct lspping_tlv_header);
+ tlen-=sizeof(struct lspping_tlv_header);
+ continue; /* no value to dissect */
+ }
+
+ tlv_tptr=tptr+sizeof(struct lspping_tlv_header);
+ tlv_tlen=lspping_tlv_len; /* header not included -> no adjustment */
+
+ /* Does the TLV go past the end of the packet? */
+ if (tlen < lspping_tlv_len+sizeof(struct lspping_tlv_header))
+ goto tooshort;
+ /* did we capture enough for fully decoding the tlv ? */
+ ND_TCHECK_LEN(tlv_tptr, lspping_tlv_len);
+ tlv_hexdump=FALSE;
+
+ switch(lspping_tlv_type) {
+ case LSPPING_TLV_TARGET_FEC_STACK:
+ while (tlv_tlen != 0) {
+ /* Does the subTLV header go past the end of the TLV? */
+ if (tlv_tlen < sizeof(struct lspping_tlv_header)) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ }
+ subtlv_hexdump=FALSE;
+
+ lspping_subtlv_header = (const struct lspping_tlv_header *)tlv_tptr;
+ lspping_subtlv_type=GET_BE_U_2(lspping_subtlv_header->type);
+ lspping_subtlv_len=GET_BE_U_2(lspping_subtlv_header->length);
+ subtlv_tptr=tlv_tptr+sizeof(struct lspping_tlv_header);
+
+ /* Does the subTLV go past the end of the TLV? */
+ if (tlv_tlen < lspping_subtlv_len+sizeof(struct lspping_tlv_header)) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ }
+
+ /* Did we capture enough for fully decoding the subTLV? */
+ ND_TCHECK_LEN(subtlv_tptr, lspping_subtlv_len);
+
+ ND_PRINT("\n\t %s subTLV (%u), length: %u",
+ tok2str(lspping_tlvtargetfec_subtlv_values,
+ "Unknown",
+ lspping_subtlv_type),
+ lspping_subtlv_type,
+ lspping_subtlv_len);
+
+ switch(lspping_subtlv_type) {
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_LDP_IPV4:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 5) {
+ ND_PRINT("\n\t invalid subTLV length, should be 5");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_ldp_ipv4 =
+ (const struct lspping_tlv_targetfec_subtlv_ldp_ipv4_t *)subtlv_tptr;
+ ND_PRINT("\n\t %s/%u",
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_ldp_ipv4->prefix),
+ GET_U_1(subtlv_ptr.lspping_tlv_targetfec_subtlv_ldp_ipv4->prefix_len));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_LDP_IPV6:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 17) {
+ ND_PRINT("\n\t invalid subTLV length, should be 17");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_ldp_ipv6 =
+ (const struct lspping_tlv_targetfec_subtlv_ldp_ipv6_t *)subtlv_tptr;
+ ND_PRINT("\n\t %s/%u",
+ GET_IP6ADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_ldp_ipv6->prefix),
+ GET_U_1(subtlv_ptr.lspping_tlv_targetfec_subtlv_ldp_ipv6->prefix_len));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_BGP_IPV4:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 5) {
+ ND_PRINT("\n\t invalid subTLV length, should be 5");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_bgp_ipv4 =
+ (const struct lspping_tlv_targetfec_subtlv_bgp_ipv4_t *)subtlv_tptr;
+ ND_PRINT("\n\t %s/%u",
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_bgp_ipv4->prefix),
+ GET_U_1(subtlv_ptr.lspping_tlv_targetfec_subtlv_bgp_ipv4->prefix_len));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_BGP_IPV6:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 17) {
+ ND_PRINT("\n\t invalid subTLV length, should be 17");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_bgp_ipv6 =
+ (const struct lspping_tlv_targetfec_subtlv_bgp_ipv6_t *)subtlv_tptr;
+ ND_PRINT("\n\t %s/%u",
+ GET_IP6ADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_bgp_ipv6->prefix),
+ GET_U_1(subtlv_ptr.lspping_tlv_targetfec_subtlv_bgp_ipv6->prefix_len));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_RSVP_IPV4:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 20) {
+ ND_PRINT("\n\t invalid subTLV length, should be 20");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv4 =
+ (const struct lspping_tlv_targetfec_subtlv_rsvp_ipv4_t *)subtlv_tptr;
+ ND_PRINT("\n\t tunnel end-point %s, tunnel sender %s, lsp-id 0x%04x"
+ "\n\t tunnel-id 0x%04x, extended tunnel-id %s",
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv4->tunnel_endpoint),
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv4->tunnel_sender),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv4->lsp_id),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv4->tunnel_id),
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv4->extended_tunnel_id));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_RSVP_IPV6:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 56) {
+ ND_PRINT("\n\t invalid subTLV length, should be 56");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv6 =
+ (const struct lspping_tlv_targetfec_subtlv_rsvp_ipv6_t *)subtlv_tptr;
+ ND_PRINT("\n\t tunnel end-point %s, tunnel sender %s, lsp-id 0x%04x"
+ "\n\t tunnel-id 0x%04x, extended tunnel-id %s",
+ GET_IP6ADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv6->tunnel_endpoint),
+ GET_IP6ADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv6->tunnel_sender),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv6->lsp_id),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv6->tunnel_id),
+ GET_IP6ADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_rsvp_ipv6->extended_tunnel_id));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_L3VPN_IPV4:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 13) {
+ ND_PRINT("\n\t invalid subTLV length, should be 13");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_l3vpn_ipv4 =
+ (const struct lspping_tlv_targetfec_subtlv_l3vpn_ipv4_t *)subtlv_tptr;
+ ND_PRINT("\n\t RD: %s, %s/%u",
+ bgp_vpn_rd_print(ndo, subtlv_ptr.lspping_tlv_targetfec_subtlv_l3vpn_ipv4->rd),
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_l3vpn_ipv4->prefix),
+ GET_U_1(subtlv_ptr.lspping_tlv_targetfec_subtlv_l3vpn_ipv4->prefix_len));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_L3VPN_IPV6:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 25) {
+ ND_PRINT("\n\t invalid subTLV length, should be 25");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_l3vpn_ipv6 =
+ (const struct lspping_tlv_targetfec_subtlv_l3vpn_ipv6_t *)subtlv_tptr;
+ ND_PRINT("\n\t RD: %s, %s/%u",
+ bgp_vpn_rd_print(ndo, subtlv_ptr.lspping_tlv_targetfec_subtlv_l3vpn_ipv6->rd),
+ GET_IP6ADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_l3vpn_ipv6->prefix),
+ GET_U_1(subtlv_ptr.lspping_tlv_targetfec_subtlv_l3vpn_ipv6->prefix_len));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_L2VPN_ENDPT:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 14) {
+ ND_PRINT("\n\t invalid subTLV length, should be 14");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_endpt =
+ (const struct lspping_tlv_targetfec_subtlv_l2vpn_endpt_t *)subtlv_tptr;
+ ND_PRINT("\n\t RD: %s, Sender VE ID: %u, Receiver VE ID: %u"
+ "\n\t Encapsulation Type: %s (%u)",
+ bgp_vpn_rd_print(ndo, subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_endpt->rd),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_endpt->sender_ve_id),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_endpt->receiver_ve_id),
+ tok2str(mpls_pw_types_values,
+ "unknown",
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_endpt->encapsulation)),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_endpt->encapsulation));
+ }
+ break;
+
+ /* the old L2VPN VCID subTLV does not have support for the sender field */
+ case LSPPING_TLV_TARGETFEC_SUBTLV_FEC_128_PW_OLD:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 10) {
+ ND_PRINT("\n\t invalid subTLV length, should be 10");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid_old =
+ (const struct lspping_tlv_targetfec_subtlv_fec_128_pw_old *)subtlv_tptr;
+ ND_PRINT("\n\t Remote PE: %s"
+ "\n\t PW ID: 0x%08x, PW Type: %s (%u)",
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid_old->remote_pe_address),
+ GET_BE_U_4(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid_old->pw_id),
+ tok2str(mpls_pw_types_values,
+ "unknown",
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid_old->pw_type)),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid_old->pw_type));
+ }
+ break;
+
+ case LSPPING_TLV_TARGETFEC_SUBTLV_FEC_128_PW:
+ /* Is the subTLV length correct? */
+ if (lspping_subtlv_len != 14) {
+ ND_PRINT("\n\t invalid subTLV length, should be 14");
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ } else {
+ subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid =
+ (const struct lspping_tlv_targetfec_subtlv_fec_128_pw *)subtlv_tptr;
+ ND_PRINT("\n\t Sender PE: %s, Remote PE: %s"
+ "\n\t PW ID: 0x%08x, PW Type: %s (%u)",
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid->sender_pe_address),
+ GET_IPADDR_STRING(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid->remote_pe_address),
+ GET_BE_U_4(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid->pw_id),
+ tok2str(mpls_pw_types_values,
+ "unknown",
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid->pw_type)),
+ GET_BE_U_2(subtlv_ptr.lspping_tlv_targetfec_subtlv_l2vpn_vcid->pw_type));
+ }
+ break;
+
+ default:
+ subtlv_hexdump=TRUE; /* unknown subTLV just hexdump it */
+ break;
+ }
+ /* do we want to see an additionally subtlv hexdump ? */
+ if (ndo->ndo_vflag > 1 || subtlv_hexdump==TRUE)
+ print_unknown_data(ndo, tlv_tptr+sizeof(struct lspping_tlv_header),
+ "\n\t ",
+ lspping_subtlv_len);
+
+ /* All subTLVs are aligned to four octet boundary */
+ if (lspping_subtlv_len % 4) {
+ lspping_subtlv_len += 4 - (lspping_subtlv_len % 4);
+ /* Does the subTLV, including padding, go past the end of the TLV? */
+ if (tlv_tlen < lspping_subtlv_len+sizeof(struct lspping_tlv_header)) {
+ ND_PRINT("\n\t\t TLV is too short");
+ return;
+ }
+ }
+ tlv_tptr+=lspping_subtlv_len;
+ tlv_tlen-=lspping_subtlv_len+sizeof(struct lspping_tlv_header);
+ }
+ break;
+
+ case LSPPING_TLV_DOWNSTREAM_MAPPING:
+ /* Does the header go past the end of the TLV? */
+ if (tlv_tlen < sizeof(struct lspping_tlv_downstream_map_t)) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ }
+ /* Did we capture enough to get the address family? */
+ ND_TCHECK_LEN(tlv_tptr,
+ sizeof(struct lspping_tlv_downstream_map_t));
+
+ tlv_ptr.lspping_tlv_downstream_map=
+ (const struct lspping_tlv_downstream_map_t *)tlv_tptr;
+
+ /* that strange thing with the downstream map TLV is that until now
+ * we do not know if its IPv4 or IPv6 or is unnumbered; after
+ * we find the address-type, we recast the tlv_tptr and move on. */
+
+ address_type = GET_U_1(tlv_ptr.lspping_tlv_downstream_map->address_type);
+ ND_PRINT("\n\t MTU: %u, Address-Type: %s (%u)",
+ GET_BE_U_2(tlv_ptr.lspping_tlv_downstream_map->mtu),
+ tok2str(lspping_tlv_downstream_addr_values,
+ "unknown",
+ address_type),
+ address_type);
+
+ switch(address_type) {
+
+ case LSPPING_AFI_IPV4:
+ /* Does the data go past the end of the TLV? */
+ if (tlv_tlen < sizeof(struct lspping_tlv_downstream_map_ipv4_t)) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ }
+ /* Did we capture enough for this part of the TLV? */
+ ND_TCHECK_LEN(tlv_tptr,
+ sizeof(struct lspping_tlv_downstream_map_ipv4_t));
+
+ tlv_ptr.lspping_tlv_downstream_map_ipv4=
+ (const struct lspping_tlv_downstream_map_ipv4_t *)tlv_tptr;
+ ND_PRINT("\n\t Downstream IP: %s"
+ "\n\t Downstream Interface IP: %s",
+ GET_IPADDR_STRING(tlv_ptr.lspping_tlv_downstream_map_ipv4->downstream_ip),
+ GET_IPADDR_STRING(tlv_ptr.lspping_tlv_downstream_map_ipv4->downstream_interface));
+ tlv_tptr+=sizeof(struct lspping_tlv_downstream_map_ipv4_t);
+ tlv_tlen-=sizeof(struct lspping_tlv_downstream_map_ipv4_t);
+ break;
+ case LSPPING_AFI_IPV4_UNMB:
+ /* Does the data go past the end of the TLV? */
+ if (tlv_tlen < sizeof(struct lspping_tlv_downstream_map_ipv4_unmb_t)) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ }
+ /* Did we capture enough for this part of the TLV? */
+ ND_TCHECK_LEN(tlv_tptr,
+ sizeof(struct lspping_tlv_downstream_map_ipv4_unmb_t));
+
+ tlv_ptr.lspping_tlv_downstream_map_ipv4_unmb=
+ (const struct lspping_tlv_downstream_map_ipv4_unmb_t *)tlv_tptr;
+ ND_PRINT("\n\t Downstream IP: %s"
+ "\n\t Downstream Interface Index: 0x%08x",
+ GET_IPADDR_STRING(tlv_ptr.lspping_tlv_downstream_map_ipv4_unmb->downstream_ip),
+ GET_BE_U_4(tlv_ptr.lspping_tlv_downstream_map_ipv4_unmb->downstream_interface));
+ tlv_tptr+=sizeof(struct lspping_tlv_downstream_map_ipv4_unmb_t);
+ tlv_tlen-=sizeof(struct lspping_tlv_downstream_map_ipv4_unmb_t);
+ break;
+ case LSPPING_AFI_IPV6:
+ /* Does the data go past the end of the TLV? */
+ if (tlv_tlen < sizeof(struct lspping_tlv_downstream_map_ipv6_t)) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ }
+ /* Did we capture enough for this part of the TLV? */
+ ND_TCHECK_LEN(tlv_tptr,
+ sizeof(struct lspping_tlv_downstream_map_ipv6_t));
+
+ tlv_ptr.lspping_tlv_downstream_map_ipv6=
+ (const struct lspping_tlv_downstream_map_ipv6_t *)tlv_tptr;
+ ND_PRINT("\n\t Downstream IP: %s"
+ "\n\t Downstream Interface IP: %s",
+ GET_IP6ADDR_STRING(tlv_ptr.lspping_tlv_downstream_map_ipv6->downstream_ip),
+ GET_IP6ADDR_STRING(tlv_ptr.lspping_tlv_downstream_map_ipv6->downstream_interface));
+ tlv_tptr+=sizeof(struct lspping_tlv_downstream_map_ipv6_t);
+ tlv_tlen-=sizeof(struct lspping_tlv_downstream_map_ipv6_t);
+ break;
+ case LSPPING_AFI_IPV6_UNMB:
+ /* Does the data go past the end of the TLV? */
+ if (tlv_tlen < sizeof(struct lspping_tlv_downstream_map_ipv6_unmb_t)) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ }
+ /* Did we capture enough for this part of the TLV? */
+ ND_TCHECK_LEN(tlv_tptr,
+ sizeof(struct lspping_tlv_downstream_map_ipv6_unmb_t));
+
+ tlv_ptr.lspping_tlv_downstream_map_ipv6_unmb=
+ (const struct lspping_tlv_downstream_map_ipv6_unmb_t *)tlv_tptr;
+ ND_PRINT("\n\t Downstream IP: %s"
+ "\n\t Downstream Interface Index: 0x%08x",
+ GET_IP6ADDR_STRING(tlv_ptr.lspping_tlv_downstream_map_ipv6_unmb->downstream_ip),
+ GET_BE_U_4(tlv_ptr.lspping_tlv_downstream_map_ipv6_unmb->downstream_interface));
+ tlv_tptr+=sizeof(struct lspping_tlv_downstream_map_ipv6_unmb_t);
+ tlv_tlen-=sizeof(struct lspping_tlv_downstream_map_ipv6_unmb_t);
+ break;
+
+ default:
+ /* should not happen ! - no error message - tok2str() has barked already */
+ break;
+ }
+
+ /* Does the data go past the end of the TLV? */
+ if (tlv_tlen < sizeof(struct lspping_tlv_downstream_map_info_t)) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ }
+ /* Did we capture enough for this part of the TLV? */
+ ND_TCHECK_LEN(tlv_tptr,
+ sizeof(struct lspping_tlv_downstream_map_info_t));
+
+ tlv_ptr.lspping_tlv_downstream_map_info=
+ (const struct lspping_tlv_downstream_map_info_t *)tlv_tptr;
+
+ /* FIXME add hash-key type, depth limit, multipath processing */
+
+ tlv_tptr+=sizeof(struct lspping_tlv_downstream_map_info_t);
+ tlv_tlen-=sizeof(struct lspping_tlv_downstream_map_info_t);
+
+ /* FIXME print downstream labels */
+
+ tlv_hexdump=TRUE; /* dump the TLV until code complete */
+
+ break;
+
+ case LSPPING_TLV_BFD_DISCRIMINATOR:
+ if (tlv_tlen < LSPPING_TLV_BFD_DISCRIMINATOR_LEN) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ } else {
+ ND_PRINT("\n\t BFD Discriminator 0x%08x", GET_BE_U_4(tlv_tptr));
+ }
+ break;
+
+ case LSPPING_TLV_VENDOR_ENTERPRISE:
+ {
+ uint32_t vendor_id;
+
+ if (tlv_tlen < LSPPING_TLV_VENDOR_ENTERPRISE_LEN) {
+ ND_PRINT("\n\t TLV is too short");
+ tlv_hexdump = TRUE;
+ goto tlv_tooshort;
+ } else {
+ vendor_id = GET_BE_U_4(tlv_tptr);
+ ND_PRINT("\n\t Vendor: %s (0x%04x)",
+ tok2str(smi_values, "Unknown", vendor_id),
+ vendor_id);
+ }
+ }
+ break;
+
+ /*
+ * FIXME those are the defined TLVs that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+ case LSPPING_TLV_PAD:
+ case LSPPING_TLV_ERROR_CODE:
+ case LSPPING_TLV_VENDOR_PRIVATE:
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tlv_tptr, "\n\t ", tlv_tlen);
+ break;
+ }
+ /* do we want to see an additionally tlv hexdump ? */
+ tlv_tooshort:
+ if (ndo->ndo_vflag > 1 || tlv_hexdump==TRUE)
+ print_unknown_data(ndo, tptr+sizeof(struct lspping_tlv_header), "\n\t ",
+ lspping_tlv_len);
+
+
+ /* All TLVs are aligned to four octet boundary */
+ if (lspping_tlv_len % 4) {
+ lspping_tlv_len += (4 - lspping_tlv_len % 4);
+ /* Does the TLV, including padding, go past the end of the packet? */
+ if (tlen < lspping_tlv_len+sizeof(struct lspping_tlv_header))
+ goto tooshort;
+ }
+
+ tptr+=lspping_tlv_len+sizeof(struct lspping_tlv_header);
+ tlen-=lspping_tlv_len+sizeof(struct lspping_tlv_header);
+ }
+ return;
+tooshort:
+ ND_PRINT("\n\t\t packet is too short");
+}
diff --git a/print-lwapp.c b/print-lwapp.c
new file mode 100644
index 0000000..10a2e0b
--- /dev/null
+++ b/print-lwapp.c
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 1998-2007 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Carles Kishimoto <carles.kishimoto@gmail.com>
+ */
+
+/* \summary: Light Weight Access Point Protocol (LWAPP) printer */
+
+/* specification: RFC 5412 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+/*
+ * LWAPP transport (common) header
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |VER| RID |C|F|L| Frag ID | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Status/WLANs | Payload... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+struct lwapp_transport_header {
+ nd_uint8_t version;
+ nd_uint8_t frag_id;
+ nd_uint16_t length;
+ nd_uint16_t status;
+};
+
+/*
+ * LWAPP control header
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Message Type | Seq Num | Msg Element Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Session ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Msg Element [0..N] |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct lwapp_control_header {
+ nd_uint8_t msg_type;
+ nd_uint8_t seq_num;
+ nd_uint16_t len;
+ nd_uint32_t session_id;
+};
+
+#define LWAPP_VERSION 0
+#define LWAPP_EXTRACT_VERSION(x) (((x)&0xC0)>>6)
+#define LWAPP_EXTRACT_RID(x) (((x)&0x38)>>3)
+#define LWAPP_EXTRACT_CONTROL_BIT(x) (((x)&0x04)>>2)
+
+static const struct tok lwapp_header_bits_values[] = {
+ { 0x01, "Last Fragment Bit"},
+ { 0x02, "Fragment Bit"},
+ { 0x04, "Control Bit"},
+ { 0, NULL}
+};
+
+#define LWAPP_MSGTYPE_DISCOVERY_REQUEST 1
+#define LWAPP_MSGTYPE_DISCOVERY_RESPONSE 2
+#define LWAPP_MSGTYPE_JOIN_REQUEST 3
+#define LWAPP_MSGTYPE_JOIN_RESPONSE 4
+#define LWAPP_MSGTYPE_JOIN_ACK 5
+#define LWAPP_MSGTYPE_JOIN_CONFIRM 6
+#define LWAPP_MSGTYPE_CONFIGURE_REQUEST 10
+#define LWAPP_MSGTYPE_CONFIGURE_RESPONSE 11
+#define LWAPP_MSGTYPE_CONF_UPDATE_REQUEST 12
+#define LWAPP_MSGTYPE_CONF_UPDATE_RESPONSE 13
+#define LWAPP_MSGTYPE_WTP_EVENT_REQUEST 14
+#define LWAPP_MSGTYPE_WTP_EVENT_RESPONSE 15
+#define LWAPP_MSGTYPE_CHANGE_STATE_EVENT_REQUEST 16
+#define LWAPP_MSGTYPE_CHANGE_STATE_EVENT_RESPONSE 17
+#define LWAPP_MSGTYPE_ECHO_REQUEST 22
+#define LWAPP_MSGTYPE_ECHO_RESPONSE 23
+#define LWAPP_MSGTYPE_IMAGE_DATA_REQUEST 24
+#define LWAPP_MSGTYPE_IMAGE_DATA_RESPONSE 25
+#define LWAPP_MSGTYPE_RESET_REQUEST 26
+#define LWAPP_MSGTYPE_RESET_RESPONSE 27
+#define LWAPP_MSGTYPE_KEY_UPDATE_REQUEST 30
+#define LWAPP_MSGTYPE_KEY_UPDATE_RESPONSE 31
+#define LWAPP_MSGTYPE_PRIMARY_DISCOVERY_REQUEST 32
+#define LWAPP_MSGTYPE_PRIMARY_DISCOVERY_RESPONSE 33
+#define LWAPP_MSGTYPE_DATA_TRANSFER_REQUEST 34
+#define LWAPP_MSGTYPE_DATA_TRANSFER_RESPONSE 35
+#define LWAPP_MSGTYPE_CLEAR_CONFIG_INDICATION 36
+#define LWAPP_MSGTYPE_WLAN_CONFIG_REQUEST 37
+#define LWAPP_MSGTYPE_WLAN_CONFIG_RESPONSE 38
+#define LWAPP_MSGTYPE_MOBILE_CONFIG_REQUEST 39
+#define LWAPP_MSGTYPE_MOBILE_CONFIG_RESPONSE 40
+
+static const struct tok lwapp_msg_type_values[] = {
+ { LWAPP_MSGTYPE_DISCOVERY_REQUEST, "Discovery req"},
+ { LWAPP_MSGTYPE_DISCOVERY_RESPONSE, "Discovery resp"},
+ { LWAPP_MSGTYPE_JOIN_REQUEST, "Join req"},
+ { LWAPP_MSGTYPE_JOIN_RESPONSE, "Join resp"},
+ { LWAPP_MSGTYPE_JOIN_ACK, "Join ack"},
+ { LWAPP_MSGTYPE_JOIN_CONFIRM, "Join confirm"},
+ { LWAPP_MSGTYPE_CONFIGURE_REQUEST, "Configure req"},
+ { LWAPP_MSGTYPE_CONFIGURE_RESPONSE, "Configure resp"},
+ { LWAPP_MSGTYPE_CONF_UPDATE_REQUEST, "Update req"},
+ { LWAPP_MSGTYPE_CONF_UPDATE_RESPONSE, "Update resp"},
+ { LWAPP_MSGTYPE_WTP_EVENT_REQUEST, "WTP event req"},
+ { LWAPP_MSGTYPE_WTP_EVENT_RESPONSE, "WTP event resp"},
+ { LWAPP_MSGTYPE_CHANGE_STATE_EVENT_REQUEST, "Change state event req"},
+ { LWAPP_MSGTYPE_CHANGE_STATE_EVENT_RESPONSE, "Change state event resp"},
+ { LWAPP_MSGTYPE_ECHO_REQUEST, "Echo req"},
+ { LWAPP_MSGTYPE_ECHO_RESPONSE, "Echo resp"},
+ { LWAPP_MSGTYPE_IMAGE_DATA_REQUEST, "Image data req"},
+ { LWAPP_MSGTYPE_IMAGE_DATA_RESPONSE, "Image data resp"},
+ { LWAPP_MSGTYPE_RESET_REQUEST, "Channel status req"},
+ { LWAPP_MSGTYPE_RESET_RESPONSE, "Channel status resp"},
+ { LWAPP_MSGTYPE_KEY_UPDATE_REQUEST, "Key update req"},
+ { LWAPP_MSGTYPE_KEY_UPDATE_RESPONSE, "Key update resp"},
+ { LWAPP_MSGTYPE_PRIMARY_DISCOVERY_REQUEST, "Primary discovery req"},
+ { LWAPP_MSGTYPE_PRIMARY_DISCOVERY_RESPONSE, "Primary discovery resp"},
+ { LWAPP_MSGTYPE_DATA_TRANSFER_REQUEST, "Data transfer req"},
+ { LWAPP_MSGTYPE_DATA_TRANSFER_RESPONSE, "Data transfer resp"},
+ { LWAPP_MSGTYPE_CLEAR_CONFIG_INDICATION, "Clear config ind"},
+ { LWAPP_MSGTYPE_WLAN_CONFIG_REQUEST, "Wlan config req"},
+ { LWAPP_MSGTYPE_WLAN_CONFIG_RESPONSE, "Wlan config resp"},
+ { LWAPP_MSGTYPE_MOBILE_CONFIG_REQUEST, "Mobile config req"},
+ { LWAPP_MSGTYPE_MOBILE_CONFIG_RESPONSE, "Mobile config resp"},
+ { 0, NULL}
+};
+
+/*
+ * LWAPP message elements
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Type | Length | Value ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+struct lwapp_message_header {
+ nd_uint8_t type;
+ nd_uint16_t length;
+};
+
+void
+lwapp_control_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len, int has_ap_ident)
+{
+ const struct lwapp_transport_header *lwapp_trans_header;
+ const struct lwapp_control_header *lwapp_control_header;
+ const u_char *tptr;
+ uint8_t version;
+ u_int tlen;
+ u_int msg_type, msg_tlen;
+
+ ndo->ndo_protocol = "lwapp_control";
+ tptr=pptr;
+
+ if (has_ap_ident) {
+ /* check if enough bytes for AP identity */
+ ND_TCHECK_6(tptr);
+ lwapp_trans_header = (const struct lwapp_transport_header *)(pptr+6);
+ } else {
+ lwapp_trans_header = (const struct lwapp_transport_header *)pptr;
+ }
+ ND_TCHECK_SIZE(lwapp_trans_header);
+ version = GET_U_1(lwapp_trans_header->version);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (LWAPP_EXTRACT_VERSION(version) != LWAPP_VERSION) {
+ ND_PRINT("LWAPP version %u packet not supported",
+ LWAPP_EXTRACT_VERSION(version));
+ return;
+ }
+
+ /* non-verbose */
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("LWAPPv%u, %s frame, Flags [%s], length %u",
+ LWAPP_EXTRACT_VERSION(version),
+ LWAPP_EXTRACT_CONTROL_BIT(version) ? "Control" : "Data",
+ bittok2str(lwapp_header_bits_values,"none",version&0x07),
+ len);
+ return;
+ }
+
+ /* ok they seem to want to know everything - lets fully decode it */
+ tlen=GET_BE_U_2(lwapp_trans_header->length);
+
+ ND_PRINT("LWAPPv%u, %s frame, Radio-id %u, Flags [%s], Frag-id %u, length %u",
+ LWAPP_EXTRACT_VERSION(version),
+ LWAPP_EXTRACT_CONTROL_BIT(version) ? "Control" : "Data",
+ LWAPP_EXTRACT_RID(version),
+ bittok2str(lwapp_header_bits_values,"none",version&0x07),
+ GET_U_1(lwapp_trans_header->frag_id),
+ tlen);
+
+ if (has_ap_ident) {
+ ND_PRINT("\n\tAP identity: %s", GET_ETHERADDR_STRING(tptr));
+ tptr+=sizeof(struct lwapp_transport_header)+6;
+ } else {
+ tptr+=sizeof(struct lwapp_transport_header);
+ }
+
+ while(tlen!=0) {
+
+ /* did we capture enough for fully decoding the object header ? */
+ ND_TCHECK_LEN(tptr, sizeof(struct lwapp_control_header));
+ if (tlen < sizeof(struct lwapp_control_header)) {
+ ND_PRINT("\n\t Msg goes past end of PDU");
+ break;
+ }
+
+ lwapp_control_header = (const struct lwapp_control_header *)tptr;
+ msg_tlen = GET_BE_U_2(lwapp_control_header->len);
+ if (tlen < sizeof(struct lwapp_control_header) + msg_tlen) {
+ ND_PRINT("\n\t Msg goes past end of PDU");
+ break;
+ }
+
+ /* print message header */
+ msg_type = GET_U_1(lwapp_control_header->msg_type);
+ ND_PRINT("\n\t Msg type: %s (%u), Seqnum: %u, Msg len: %u, Session: 0x%08x",
+ tok2str(lwapp_msg_type_values,"Unknown",msg_type),
+ msg_type,
+ GET_U_1(lwapp_control_header->seq_num),
+ msg_tlen,
+ GET_BE_U_4(lwapp_control_header->session_id));
+
+ /* did we capture enough for fully decoding the message */
+ ND_TCHECK_LEN(tptr, msg_tlen);
+
+ /* XXX - Decode sub messages for each message */
+ switch(msg_type) {
+ case LWAPP_MSGTYPE_DISCOVERY_REQUEST:
+ case LWAPP_MSGTYPE_DISCOVERY_RESPONSE:
+ case LWAPP_MSGTYPE_JOIN_REQUEST:
+ case LWAPP_MSGTYPE_JOIN_RESPONSE:
+ case LWAPP_MSGTYPE_JOIN_ACK:
+ case LWAPP_MSGTYPE_JOIN_CONFIRM:
+ case LWAPP_MSGTYPE_CONFIGURE_REQUEST:
+ case LWAPP_MSGTYPE_CONFIGURE_RESPONSE:
+ case LWAPP_MSGTYPE_CONF_UPDATE_REQUEST:
+ case LWAPP_MSGTYPE_CONF_UPDATE_RESPONSE:
+ case LWAPP_MSGTYPE_WTP_EVENT_REQUEST:
+ case LWAPP_MSGTYPE_WTP_EVENT_RESPONSE:
+ case LWAPP_MSGTYPE_CHANGE_STATE_EVENT_REQUEST:
+ case LWAPP_MSGTYPE_CHANGE_STATE_EVENT_RESPONSE:
+ case LWAPP_MSGTYPE_ECHO_REQUEST:
+ case LWAPP_MSGTYPE_ECHO_RESPONSE:
+ case LWAPP_MSGTYPE_IMAGE_DATA_REQUEST:
+ case LWAPP_MSGTYPE_IMAGE_DATA_RESPONSE:
+ case LWAPP_MSGTYPE_RESET_REQUEST:
+ case LWAPP_MSGTYPE_RESET_RESPONSE:
+ case LWAPP_MSGTYPE_KEY_UPDATE_REQUEST:
+ case LWAPP_MSGTYPE_KEY_UPDATE_RESPONSE:
+ case LWAPP_MSGTYPE_PRIMARY_DISCOVERY_REQUEST:
+ case LWAPP_MSGTYPE_PRIMARY_DISCOVERY_RESPONSE:
+ case LWAPP_MSGTYPE_DATA_TRANSFER_REQUEST:
+ case LWAPP_MSGTYPE_DATA_TRANSFER_RESPONSE:
+ case LWAPP_MSGTYPE_CLEAR_CONFIG_INDICATION:
+ case LWAPP_MSGTYPE_WLAN_CONFIG_REQUEST:
+ case LWAPP_MSGTYPE_WLAN_CONFIG_RESPONSE:
+ case LWAPP_MSGTYPE_MOBILE_CONFIG_REQUEST:
+ case LWAPP_MSGTYPE_MOBILE_CONFIG_RESPONSE:
+ default:
+ break;
+ }
+
+ tptr += sizeof(struct lwapp_control_header) + msg_tlen;
+ tlen -= sizeof(struct lwapp_control_header) + msg_tlen;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+lwapp_data_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ const struct lwapp_transport_header *lwapp_trans_header;
+ const u_char *tptr;
+ u_int tlen;
+ u_int version;
+
+ ndo->ndo_protocol = "lwapp_data";
+ tptr=pptr;
+
+ /* check if enough bytes for AP identity */
+ ND_TCHECK_6(tptr);
+ lwapp_trans_header = (const struct lwapp_transport_header *)pptr;
+ ND_TCHECK_SIZE(lwapp_trans_header);
+ version = GET_U_1(lwapp_trans_header->version);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (LWAPP_EXTRACT_VERSION(version) != LWAPP_VERSION) {
+ ND_PRINT("LWAPP version %u packet not supported",
+ LWAPP_EXTRACT_VERSION(version));
+ return;
+ }
+
+ /* non-verbose */
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("LWAPPv%u, %s frame, Flags [%s], length %u",
+ LWAPP_EXTRACT_VERSION(version),
+ LWAPP_EXTRACT_CONTROL_BIT(version) ? "Control" : "Data",
+ bittok2str(lwapp_header_bits_values,"none",version&0x07),
+ len);
+ return;
+ }
+
+ /* ok they seem to want to know everything - lets fully decode it */
+ tlen=GET_BE_U_2(lwapp_trans_header->length);
+ if (tlen < sizeof(struct lwapp_transport_header)) {
+ ND_PRINT("LWAPPv%u, %s frame, Radio-id %u, Flags [%s], length %u < transport header length",
+ LWAPP_EXTRACT_VERSION(version),
+ LWAPP_EXTRACT_CONTROL_BIT(version) ? "Control" : "Data",
+ LWAPP_EXTRACT_RID(version),
+ bittok2str(lwapp_header_bits_values,"none",version&0x07),
+ tlen);
+ return;
+ }
+
+ ND_PRINT("LWAPPv%u, %s frame, Radio-id %u, Flags [%s], Frag-id %u, length %u",
+ LWAPP_EXTRACT_VERSION(version),
+ LWAPP_EXTRACT_CONTROL_BIT(version) ? "Control" : "Data",
+ LWAPP_EXTRACT_RID(version),
+ bittok2str(lwapp_header_bits_values,"none",version&0x07),
+ GET_U_1(lwapp_trans_header->frag_id),
+ tlen);
+
+ tptr+=sizeof(struct lwapp_transport_header);
+ tlen-=sizeof(struct lwapp_transport_header);
+
+ /* FIX - An IEEE 802.11 frame follows - hexdump for now */
+ print_unknown_data(ndo, tptr, "\n\t", tlen);
+
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-lwres.c b/print-lwres.c
new file mode 100644
index 0000000..c237e48
--- /dev/null
+++ b/print-lwres.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright (C) 2001 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: BIND9 Lightweight Resolver protocol printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "nameser.h"
+
+/* BIND9 lib/lwres/include/lwres */
+/*
+ * Use nd_uint16_t for lwres_uint16_t
+ * Use nd_uint32_t for lwres_uint32_t
+*/
+
+struct lwres_lwpacket {
+ nd_uint32_t length;
+ nd_uint16_t version;
+ nd_uint16_t pktflags;
+ nd_uint32_t serial;
+ nd_uint32_t opcode;
+ nd_uint32_t result;
+ nd_uint32_t recvlength;
+ nd_uint16_t authtype;
+ nd_uint16_t authlength;
+};
+
+#define LWRES_LWPACKETFLAG_RESPONSE 0x0001U /* if set, pkt is a response */
+
+#define LWRES_LWPACKETVERSION_0 0
+
+#define LWRES_FLAG_TRUSTNOTREQUIRED 0x00000001U
+#define LWRES_FLAG_SECUREDATA 0x00000002U
+
+/*
+ * no-op
+ */
+#define LWRES_OPCODE_NOOP 0x00000000U
+
+typedef struct {
+ /* public */
+ nd_uint16_t datalength;
+ /* data follows */
+} lwres_nooprequest_t;
+
+typedef struct {
+ /* public */
+ nd_uint16_t datalength;
+ /* data follows */
+} lwres_noopresponse_t;
+
+/*
+ * get addresses by name
+ */
+#define LWRES_OPCODE_GETADDRSBYNAME 0x00010001U
+
+typedef struct lwres_addr lwres_addr_t;
+
+struct lwres_addr {
+ nd_uint32_t family;
+ nd_uint16_t length;
+ /* address follows */
+};
+#define LWRES_ADDR_LEN 6
+
+typedef struct {
+ /* public */
+ nd_uint32_t flags;
+ nd_uint32_t addrtypes;
+ nd_uint16_t namelen;
+ /* name follows */
+} lwres_gabnrequest_t;
+#define LWRES_GABNREQUEST_LEN 10
+
+typedef struct {
+ /* public */
+ nd_uint32_t flags;
+ nd_uint16_t naliases;
+ nd_uint16_t naddrs;
+ nd_uint16_t realnamelen;
+ /* aliases follows */
+ /* addrs follows */
+ /* realname follows */
+} lwres_gabnresponse_t;
+#define LWRES_GABNRESPONSE_LEN 10
+
+/*
+ * get name by address
+ */
+#define LWRES_OPCODE_GETNAMEBYADDR 0x00010002U
+typedef struct {
+ /* public */
+ nd_uint32_t flags;
+ /* addr follows */
+} lwres_gnbarequest_t;
+#define LWRES_GNBAREQUEST_LEN 4
+
+typedef struct {
+ /* public */
+ nd_uint32_t flags;
+ nd_uint16_t naliases;
+ nd_uint16_t realnamelen;
+ /* aliases follows */
+ /* realname follows */
+} lwres_gnbaresponse_t;
+#define LWRES_GNBARESPONSE_LEN 8
+
+/*
+ * get rdata by name
+ */
+#define LWRES_OPCODE_GETRDATABYNAME 0x00010003U
+
+typedef struct {
+ /* public */
+ nd_uint32_t flags;
+ nd_uint16_t rdclass;
+ nd_uint16_t rdtype;
+ nd_uint16_t namelen;
+ /* name follows */
+} lwres_grbnrequest_t;
+#define LWRES_GRBNREQUEST_LEN 10
+
+typedef struct {
+ /* public */
+ nd_uint32_t flags;
+ nd_uint16_t rdclass;
+ nd_uint16_t rdtype;
+ nd_uint32_t ttl;
+ nd_uint16_t nrdatas;
+ nd_uint16_t nsigs;
+ /* realname here (len + name) */
+ /* rdata here (len + name) */
+ /* signatures here (len + name) */
+} lwres_grbnresponse_t;
+#define LWRES_GRBNRESPONSE_LEN 16
+
+#define LWRDATA_VALIDATED 0x00000001
+
+#define LWRES_ADDRTYPE_V4 0x00000001U /* ipv4 */
+#define LWRES_ADDRTYPE_V6 0x00000002U /* ipv6 */
+
+#define LWRES_MAX_ALIASES 16 /* max # of aliases */
+#define LWRES_MAX_ADDRS 64 /* max # of addrs */
+
+static const struct tok opcode[] = {
+ { LWRES_OPCODE_NOOP, "noop", },
+ { LWRES_OPCODE_GETADDRSBYNAME, "getaddrsbyname", },
+ { LWRES_OPCODE_GETNAMEBYADDR, "getnamebyaddr", },
+ { LWRES_OPCODE_GETRDATABYNAME, "getrdatabyname", },
+ { 0, NULL, },
+};
+
+/* print-domain.c */
+extern const struct tok ns_type2str[];
+extern const struct tok ns_class2str[];
+
+static unsigned
+lwres_printname(netdissect_options *ndo,
+ size_t l, const u_char *p0)
+{
+ ND_PRINT(" ");
+ (void)nd_printn(ndo, p0, l, NULL);
+ p0 += l;
+ if (GET_U_1(p0))
+ ND_PRINT(" (not NUL-terminated!)");
+ return l + 1;
+}
+
+static unsigned
+lwres_printnamelen(netdissect_options *ndo,
+ const u_char *p)
+{
+ uint16_t l;
+ int advance;
+
+ l = GET_BE_U_2(p);
+ advance = lwres_printname(ndo, l, p + 2);
+ return 2 + advance;
+}
+
+static unsigned
+lwres_printbinlen(netdissect_options *ndo,
+ const u_char *p0)
+{
+ const u_char *p;
+ uint16_t l;
+ int i;
+
+ p = p0;
+ l = GET_BE_U_2(p);
+ p += 2;
+ for (i = 0; i < l; i++) {
+ ND_PRINT("%02x", GET_U_1(p));
+ p++;
+ }
+ return 2 + l;
+}
+
+static int
+lwres_printaddr(netdissect_options *ndo,
+ const u_char *p0)
+{
+ const u_char *p;
+ const lwres_addr_t *ap;
+ uint16_t l;
+ int i;
+
+ p = p0;
+ ap = (const lwres_addr_t *)p;
+ l = GET_BE_U_2(ap->length);
+ p += LWRES_ADDR_LEN;
+ ND_TCHECK_LEN(p, l);
+
+ switch (GET_BE_U_4(ap->family)) {
+ case 1: /* IPv4 */
+ if (l < 4)
+ return -1;
+ ND_PRINT(" %s", GET_IPADDR_STRING(p));
+ p += sizeof(nd_ipv4);
+ break;
+ case 2: /* IPv6 */
+ if (l < 16)
+ return -1;
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(p));
+ p += sizeof(nd_ipv6);
+ break;
+ default:
+ ND_PRINT(" %u/", GET_BE_U_4(ap->family));
+ for (i = 0; i < l; i++) {
+ ND_PRINT("%02x", GET_U_1(p));
+ p++;
+ }
+ }
+
+ return ND_BYTES_BETWEEN(p, p0);
+}
+
+void
+lwres_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const u_char *p;
+ const struct lwres_lwpacket *np;
+ uint32_t v;
+ const u_char *s;
+ int response;
+ int advance;
+ int unsupported = 0;
+
+ ndo->ndo_protocol = "lwres";
+ np = (const struct lwres_lwpacket *)bp;
+ ND_TCHECK_2(np->authlength);
+
+ ND_PRINT(" lwres");
+ v = GET_BE_U_2(np->version);
+ if (ndo->ndo_vflag || v != LWRES_LWPACKETVERSION_0)
+ ND_PRINT(" v%u", v);
+ if (v != LWRES_LWPACKETVERSION_0) {
+ s = bp + GET_BE_U_4(np->length);
+ goto tail;
+ }
+
+ response = GET_BE_U_2(np->pktflags) & LWRES_LWPACKETFLAG_RESPONSE;
+
+ /* opcode and pktflags */
+ v = GET_BE_U_4(np->opcode);
+ ND_PRINT(" %s%s", tok2str(opcode, "#0x%x", v), response ? "" : "?");
+
+ /* pktflags */
+ v = GET_BE_U_2(np->pktflags);
+ if (v & ~LWRES_LWPACKETFLAG_RESPONSE)
+ ND_PRINT("[0x%x]", v);
+
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT(" ("); /*)*/
+ ND_PRINT("serial:0x%x", GET_BE_U_4(np->serial));
+ ND_PRINT(" result:0x%x", GET_BE_U_4(np->result));
+ ND_PRINT(" recvlen:%u", GET_BE_U_4(np->recvlength));
+ /* BIND910: not used */
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT(" authtype:0x%x", GET_BE_U_2(np->authtype));
+ ND_PRINT(" authlen:%u", GET_BE_U_2(np->authlength));
+ }
+ /*(*/
+ ND_PRINT(")");
+ }
+
+ /* per-opcode content */
+ if (!response) {
+ /*
+ * queries
+ */
+ const lwres_gabnrequest_t *gabn;
+ const lwres_gnbarequest_t *gnba;
+ const lwres_grbnrequest_t *grbn;
+ uint32_t l;
+
+ gabn = NULL;
+ gnba = NULL;
+ grbn = NULL;
+
+ p = (const u_char *)(np + 1);
+ switch (GET_BE_U_4(np->opcode)) {
+ case LWRES_OPCODE_NOOP:
+ s = p;
+ break;
+ case LWRES_OPCODE_GETADDRSBYNAME:
+ gabn = (const lwres_gabnrequest_t *)p;
+ ND_TCHECK_2(gabn->namelen);
+
+ /* BIND910: not used */
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT(" flags:0x%x",
+ GET_BE_U_4(gabn->flags));
+ }
+
+ v = GET_BE_U_4(gabn->addrtypes);
+ switch (v & (LWRES_ADDRTYPE_V4 | LWRES_ADDRTYPE_V6)) {
+ case LWRES_ADDRTYPE_V4:
+ ND_PRINT(" IPv4");
+ break;
+ case LWRES_ADDRTYPE_V6:
+ ND_PRINT(" IPv6");
+ break;
+ case LWRES_ADDRTYPE_V4 | LWRES_ADDRTYPE_V6:
+ ND_PRINT(" IPv4/6");
+ break;
+ }
+ if (v & ~(LWRES_ADDRTYPE_V4 | LWRES_ADDRTYPE_V6))
+ ND_PRINT("[0x%x]", v);
+
+ s = p + LWRES_GABNREQUEST_LEN;
+ l = GET_BE_U_2(gabn->namelen);
+ advance = lwres_printname(ndo, l, s);
+ s += advance;
+ break;
+ case LWRES_OPCODE_GETNAMEBYADDR:
+ gnba = (const lwres_gnbarequest_t *)p;
+ ND_TCHECK_4(gnba->flags);
+
+ /* BIND910: not used */
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT(" flags:0x%x",
+ GET_BE_U_4(gnba->flags));
+ }
+
+ s = p + LWRES_GNBAREQUEST_LEN;
+ advance = lwres_printaddr(ndo, s);
+ if (advance < 0)
+ goto invalid;
+ s += advance;
+ break;
+ case LWRES_OPCODE_GETRDATABYNAME:
+ /* XXX no trace, not tested */
+ grbn = (const lwres_grbnrequest_t *)p;
+ ND_TCHECK_2(grbn->namelen);
+
+ /* BIND910: not used */
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT(" flags:0x%x",
+ GET_BE_U_4(grbn->flags));
+ }
+
+ ND_PRINT(" %s", tok2str(ns_type2str, "Type%u",
+ GET_BE_U_2(grbn->rdtype)));
+ if (GET_BE_U_2(grbn->rdclass) != C_IN) {
+ ND_PRINT(" %s", tok2str(ns_class2str, "Class%u",
+ GET_BE_U_2(grbn->rdclass)));
+ }
+
+ s = p + LWRES_GRBNREQUEST_LEN;
+ l = GET_BE_U_2(grbn->namelen);
+ advance = lwres_printname(ndo, l, s);
+ s += advance;
+ break;
+ default:
+ s = p;
+ unsupported++;
+ break;
+ }
+ } else {
+ /*
+ * responses
+ */
+ const lwres_gabnresponse_t *gabn;
+ const lwres_gnbaresponse_t *gnba;
+ const lwres_grbnresponse_t *grbn;
+ uint32_t l, na;
+ uint32_t i;
+
+ gabn = NULL;
+ gnba = NULL;
+ grbn = NULL;
+
+ p = (const u_char *)(np + 1);
+ switch (GET_BE_U_4(np->opcode)) {
+ case LWRES_OPCODE_NOOP:
+ s = p;
+ break;
+ case LWRES_OPCODE_GETADDRSBYNAME:
+ gabn = (const lwres_gabnresponse_t *)p;
+ ND_TCHECK_2(gabn->realnamelen);
+
+ /* BIND910: not used */
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT(" flags:0x%x",
+ GET_BE_U_4(gabn->flags));
+ }
+
+ ND_PRINT(" %u/%u", GET_BE_U_2(gabn->naliases),
+ GET_BE_U_2(gabn->naddrs));
+
+ s = p + LWRES_GABNRESPONSE_LEN;
+ l = GET_BE_U_2(gabn->realnamelen);
+ advance = lwres_printname(ndo, l, s);
+ s += advance;
+
+ /* aliases */
+ na = GET_BE_U_2(gabn->naliases);
+ for (i = 0; i < na; i++) {
+ advance = lwres_printnamelen(ndo, s);
+ s += advance;
+ }
+
+ /* addrs */
+ na = GET_BE_U_2(gabn->naddrs);
+ for (i = 0; i < na; i++) {
+ advance = lwres_printaddr(ndo, s);
+ if (advance < 0)
+ goto invalid;
+ s += advance;
+ }
+ break;
+ case LWRES_OPCODE_GETNAMEBYADDR:
+ gnba = (const lwres_gnbaresponse_t *)p;
+ ND_TCHECK_2(gnba->realnamelen);
+
+ /* BIND910: not used */
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT(" flags:0x%x",
+ GET_BE_U_4(gnba->flags));
+ }
+
+ ND_PRINT(" %u", GET_BE_U_2(gnba->naliases));
+
+ s = p + LWRES_GNBARESPONSE_LEN;
+ l = GET_BE_U_2(gnba->realnamelen);
+ advance = lwres_printname(ndo, l, s);
+ s += advance;
+
+ /* aliases */
+ na = GET_BE_U_2(gnba->naliases);
+ for (i = 0; i < na; i++) {
+ advance = lwres_printnamelen(ndo, s);
+ s += advance;
+ }
+ break;
+ case LWRES_OPCODE_GETRDATABYNAME:
+ /* XXX no trace, not tested */
+ grbn = (const lwres_grbnresponse_t *)p;
+ ND_TCHECK_2(grbn->nsigs);
+
+ /* BIND910: not used */
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT(" flags:0x%x",
+ GET_BE_U_4(grbn->flags));
+ }
+
+ ND_PRINT(" %s", tok2str(ns_type2str, "Type%u",
+ GET_BE_U_2(grbn->rdtype)));
+ if (GET_BE_U_2(grbn->rdclass) != C_IN) {
+ ND_PRINT(" %s", tok2str(ns_class2str, "Class%u",
+ GET_BE_U_2(grbn->rdclass)));
+ }
+ ND_PRINT(" TTL ");
+ unsigned_relts_print(ndo,
+ GET_BE_U_4(grbn->ttl));
+ ND_PRINT(" %u/%u", GET_BE_U_2(grbn->nrdatas),
+ GET_BE_U_2(grbn->nsigs));
+
+ s = p + LWRES_GRBNRESPONSE_LEN;
+ advance = lwres_printnamelen(ndo, s);
+ s += advance;
+
+ /* rdatas */
+ na = GET_BE_U_2(grbn->nrdatas);
+ for (i = 0; i < na; i++) {
+ /* XXX should decode resource data */
+ advance = lwres_printbinlen(ndo, s);
+ s += advance;
+ }
+
+ /* sigs */
+ na = GET_BE_U_2(grbn->nsigs);
+ for (i = 0; i < na; i++) {
+ /* XXX how should we print it? */
+ advance = lwres_printbinlen(ndo, s);
+ s += advance;
+ }
+ break;
+ default:
+ s = p;
+ unsupported++;
+ break;
+ }
+ }
+
+ tail:
+ /* length mismatch */
+ if (GET_BE_U_4(np->length) != length) {
+ ND_PRINT(" [len: %u != %u]", GET_BE_U_4(np->length),
+ length);
+ }
+ if (!unsupported && s < bp + GET_BE_U_4(np->length))
+ ND_PRINT("[extra]");
+ return;
+
+ invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-m3ua.c b/print-m3ua.c
new file mode 100644
index 0000000..ecf7db8
--- /dev/null
+++ b/print-m3ua.c
@@ -0,0 +1,337 @@
+/* Copyright (c) 2013, The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: Message Transfer Part 3 (MTP3) User Adaptation Layer (M3UA) printer */
+
+/* RFC 4666 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+
+#define M3UA_REL_1_0 1
+
+struct m3ua_common_header {
+ nd_uint8_t v;
+ nd_uint8_t reserved;
+ nd_uint8_t msg_class;
+ nd_uint8_t msg_type;
+ nd_uint32_t len;
+};
+
+struct m3ua_param_header {
+ nd_uint16_t tag;
+ nd_uint16_t len;
+};
+
+/* message classes */
+#define M3UA_MSGC_MGMT 0
+#define M3UA_MSGC_TRANSFER 1
+#define M3UA_MSGC_SSNM 2
+#define M3UA_MSGC_ASPSM 3
+#define M3UA_MSGC_ASPTM 4
+/* reserved values */
+#define M3UA_MSGC_RKM 9
+
+static const struct tok MessageClasses[] = {
+ { M3UA_MSGC_MGMT, "Management" },
+ { M3UA_MSGC_TRANSFER, "Transfer" },
+ { M3UA_MSGC_SSNM, "SS7" },
+ { M3UA_MSGC_ASPSM, "ASP" },
+ { M3UA_MSGC_ASPTM, "ASP" },
+ { M3UA_MSGC_RKM, "Routing Key Management"},
+ { 0, NULL }
+};
+
+/* management messages */
+#define M3UA_MGMT_ERROR 0
+#define M3UA_MGMT_NOTIFY 1
+
+static const struct tok MgmtMessages[] = {
+ { M3UA_MGMT_ERROR, "Error" },
+ { M3UA_MGMT_NOTIFY, "Notify" },
+ { 0, NULL }
+};
+
+/* transfer messages */
+#define M3UA_TRANSFER_DATA 1
+
+static const struct tok TransferMessages[] = {
+ { M3UA_TRANSFER_DATA, "Data" },
+ { 0, NULL }
+};
+
+/* SS7 Signaling Network Management messages */
+#define M3UA_SSNM_DUNA 1
+#define M3UA_SSNM_DAVA 2
+#define M3UA_SSNM_DAUD 3
+#define M3UA_SSNM_SCON 4
+#define M3UA_SSNM_DUPU 5
+#define M3UA_SSNM_DRST 6
+
+static const struct tok SS7Messages[] = {
+ { M3UA_SSNM_DUNA, "Destination Unavailable" },
+ { M3UA_SSNM_DAVA, "Destination Available" },
+ { M3UA_SSNM_DAUD, "Destination State Audit" },
+ { M3UA_SSNM_SCON, "Signalling Congestion" },
+ { M3UA_SSNM_DUPU, "Destination User Part Unavailable" },
+ { M3UA_SSNM_DRST, "Destination Restricted" },
+ { 0, NULL }
+};
+
+/* ASP State Maintenance messages */
+#define M3UA_ASP_UP 1
+#define M3UA_ASP_DN 2
+#define M3UA_ASP_BEAT 3
+#define M3UA_ASP_UP_ACK 4
+#define M3UA_ASP_DN_ACK 5
+#define M3UA_ASP_BEAT_ACK 6
+
+static const struct tok ASPStateMessages[] = {
+ { M3UA_ASP_UP, "Up" },
+ { M3UA_ASP_DN, "Down" },
+ { M3UA_ASP_BEAT, "Heartbeat" },
+ { M3UA_ASP_UP_ACK, "Up Acknowledgement" },
+ { M3UA_ASP_DN_ACK, "Down Acknowledgement" },
+ { M3UA_ASP_BEAT_ACK, "Heartbeat Acknowledgement" },
+ { 0, NULL }
+};
+
+/* ASP Traffic Maintenance messages */
+#define M3UA_ASP_AC 1
+#define M3UA_ASP_IA 2
+#define M3UA_ASP_AC_ACK 3
+#define M3UA_ASP_IA_ACK 4
+
+static const struct tok ASPTrafficMessages[] = {
+ { M3UA_ASP_AC, "Active" },
+ { M3UA_ASP_IA, "Inactive" },
+ { M3UA_ASP_AC_ACK, "Active Acknowledgement" },
+ { M3UA_ASP_IA_ACK, "Inactive Acknowledgement" },
+ { 0, NULL }
+};
+
+/* Routing Key Management messages */
+#define M3UA_RKM_REQ 1
+#define M3UA_RKM_RSP 2
+#define M3UA_RKM_DEREQ 3
+#define M3UA_RKM_DERSP 4
+
+static const struct tok RoutingKeyMgmtMessages[] = {
+ { M3UA_RKM_REQ, "Registration Request" },
+ { M3UA_RKM_RSP, "Registration Response" },
+ { M3UA_RKM_DEREQ, "Deregistration Request" },
+ { M3UA_RKM_DERSP, "Deregistration Response" },
+ { 0, NULL }
+};
+
+static const struct uint_tokary m3ua_msgc2tokary[] = {
+ { M3UA_MSGC_MGMT, MgmtMessages },
+ { M3UA_MSGC_TRANSFER, TransferMessages },
+ { M3UA_MSGC_SSNM, SS7Messages },
+ { M3UA_MSGC_ASPSM, ASPStateMessages },
+ { M3UA_MSGC_ASPTM, ASPTrafficMessages },
+ { M3UA_MSGC_RKM, RoutingKeyMgmtMessages },
+ /* uint2tokary() does not use array termination. */
+};
+
+/* M3UA Parameters */
+#define M3UA_PARAM_INFO 0x0004
+#define M3UA_PARAM_ROUTING_CTX 0x0006
+#define M3UA_PARAM_DIAGNOSTIC 0x0007
+#define M3UA_PARAM_HB_DATA 0x0009
+#define M3UA_PARAM_TRAFFIC_MODE_TYPE 0x000b
+#define M3UA_PARAM_ERROR_CODE 0x000c
+#define M3UA_PARAM_STATUS 0x000d
+#define M3UA_PARAM_ASP_ID 0x0011
+#define M3UA_PARAM_AFFECTED_POINT_CODE 0x0012
+#define M3UA_PARAM_CORR_ID 0x0013
+
+#define M3UA_PARAM_NETWORK_APPEARANCE 0x0200
+#define M3UA_PARAM_USER 0x0204
+#define M3UA_PARAM_CONGESTION_INDICATION 0x0205
+#define M3UA_PARAM_CONCERNED_DST 0x0206
+#define M3UA_PARAM_ROUTING_KEY 0x0207
+#define M3UA_PARAM_REG_RESULT 0x0208
+#define M3UA_PARAM_DEREG_RESULT 0x0209
+#define M3UA_PARAM_LOCAL_ROUTING_KEY_ID 0x020a
+#define M3UA_PARAM_DST_POINT_CODE 0x020b
+#define M3UA_PARAM_SI 0x020c
+#define M3UA_PARAM_ORIGIN_POINT_CODE_LIST 0x020e
+#define M3UA_PARAM_PROTO_DATA 0x0210
+#define M3UA_PARAM_REG_STATUS 0x0212
+#define M3UA_PARAM_DEREG_STATUS 0x0213
+
+static const struct tok ParamName[] = {
+ { M3UA_PARAM_INFO, "INFO String" },
+ { M3UA_PARAM_ROUTING_CTX, "Routing Context" },
+ { M3UA_PARAM_DIAGNOSTIC, "Diagnostic Info" },
+ { M3UA_PARAM_HB_DATA, "Heartbeat Data" },
+ { M3UA_PARAM_TRAFFIC_MODE_TYPE, "Traffic Mode Type" },
+ { M3UA_PARAM_ERROR_CODE, "Error Code" },
+ { M3UA_PARAM_STATUS, "Status" },
+ { M3UA_PARAM_ASP_ID, "ASP Identifier" },
+ { M3UA_PARAM_AFFECTED_POINT_CODE, "Affected Point Code" },
+ { M3UA_PARAM_CORR_ID, "Correlation ID" },
+ { M3UA_PARAM_NETWORK_APPEARANCE, "Network Appearance" },
+ { M3UA_PARAM_USER, "User/Cause" },
+ { M3UA_PARAM_CONGESTION_INDICATION, "Congestion Indications" },
+ { M3UA_PARAM_CONCERNED_DST, "Concerned Destination" },
+ { M3UA_PARAM_ROUTING_KEY, "Routing Key" },
+ { M3UA_PARAM_REG_RESULT, "Registration Result" },
+ { M3UA_PARAM_DEREG_RESULT, "Deregistration Result" },
+ { M3UA_PARAM_LOCAL_ROUTING_KEY_ID, "Local Routing Key Identifier" },
+ { M3UA_PARAM_DST_POINT_CODE, "Destination Point Code" },
+ { M3UA_PARAM_SI, "Service Indicators" },
+ { M3UA_PARAM_ORIGIN_POINT_CODE_LIST, "Originating Point Code List" },
+ { M3UA_PARAM_PROTO_DATA, "Protocol Data" },
+ { M3UA_PARAM_REG_STATUS, "Registration Status" },
+ { M3UA_PARAM_DEREG_STATUS, "Deregistration Status" },
+ { 0, NULL }
+};
+
+static void
+tag_value_print(netdissect_options *ndo,
+ const u_char *buf, const uint16_t tag, const uint16_t size)
+{
+ switch (tag) {
+ case M3UA_PARAM_NETWORK_APPEARANCE:
+ case M3UA_PARAM_ROUTING_CTX:
+ case M3UA_PARAM_CORR_ID:
+ /* buf and size don't include the header */
+ if (size < 4)
+ goto invalid;
+ ND_PRINT("0x%08x", GET_BE_U_4(buf));
+ break;
+ /* ... */
+ default:
+ ND_PRINT("(length %zu)", size + sizeof(struct m3ua_param_header));
+ }
+ ND_TCHECK_LEN(buf, size);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(buf, size);
+}
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Parameter Tag | Parameter Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * \ \
+ * / Parameter Value /
+ * \ \
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static void
+m3ua_tags_print(netdissect_options *ndo,
+ const u_char *buf, const u_int size)
+{
+ const u_char *p = buf;
+ int align;
+ uint16_t hdr_tag;
+ uint16_t hdr_len;
+
+ while (p < buf + size) {
+ if (p + sizeof(struct m3ua_param_header) > buf + size)
+ goto invalid;
+ /* Parameter Tag */
+ hdr_tag = GET_BE_U_2(p);
+ ND_PRINT("\n\t\t\t%s: ", tok2str(ParamName, "Unknown Parameter (0x%04x)", hdr_tag));
+ /* Parameter Length */
+ hdr_len = GET_BE_U_2(p + 2);
+ if (hdr_len < sizeof(struct m3ua_param_header))
+ goto invalid;
+ /* Parameter Value */
+ align = (p + hdr_len - buf) % 4;
+ align = align ? 4 - align : 0;
+ ND_TCHECK_LEN(p, hdr_len + align);
+ tag_value_print(ndo, p, hdr_tag, hdr_len - sizeof(struct m3ua_param_header));
+ p += hdr_len + align;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(buf, size);
+}
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version | Reserved | Message Class | Message Type |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Message Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * \ \
+ * / /
+ */
+void
+m3ua_print(netdissect_options *ndo,
+ const u_char *buf, const u_int size)
+{
+ const struct m3ua_common_header *hdr = (const struct m3ua_common_header *) buf;
+ const struct tok *dict;
+ uint8_t msg_class;
+
+ ndo->ndo_protocol = "m3ua";
+ /* size includes the header */
+ if (size < sizeof(struct m3ua_common_header))
+ goto invalid;
+ ND_TCHECK_SIZE(hdr);
+ if (GET_U_1(hdr->v) != M3UA_REL_1_0)
+ return;
+
+ msg_class = GET_U_1(hdr->msg_class);
+ dict = uint2tokary(m3ua_msgc2tokary, msg_class);
+
+ ND_PRINT("\n\t\t%s", tok2str(MessageClasses, "Unknown message class %i", msg_class));
+ if (dict != NULL)
+ ND_PRINT(" %s Message",
+ tok2str(dict, "Unknown (0x%02x)", GET_U_1(hdr->msg_type)));
+
+ if (size != GET_BE_U_4(hdr->len))
+ ND_PRINT("\n\t\t\t@@@@@@ Corrupted length %u of message @@@@@@",
+ GET_BE_U_4(hdr->len));
+ else
+ m3ua_tags_print(ndo, buf + sizeof(struct m3ua_common_header),
+ GET_BE_U_4(hdr->len) - sizeof(struct m3ua_common_header));
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(buf, size);
+}
+
diff --git a/print-macsec.c b/print-macsec.c
new file mode 100644
index 0000000..0cf8cd6
--- /dev/null
+++ b/print-macsec.c
@@ -0,0 +1,249 @@
+/* Copyright (c) 2017, Sabrina Dubroca <sd@queasysnail.net>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: MACsec printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <netdissect-stdinc.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+#include "extract.h"
+
+#define MACSEC_DEFAULT_ICV_LEN 16
+
+/* Header format (SecTAG), following an Ethernet header
+ * IEEE 802.1AE-2006 9.3
+ *
+ * +---------------------------------+----------------+----------------+
+ * | (MACsec ethertype) | TCI_AN | SL |
+ * +---------------------------------+----------------+----------------+
+ * | Packet Number |
+ * +-------------------------------------------------------------------+
+ * | Secure Channel Identifier |
+ * | (optional) |
+ * +-------------------------------------------------------------------+
+ *
+ * MACsec ethertype = 0x88e5
+ * TCI: Tag Control Information, set of flags
+ * AN: association number, 2 bits
+ * SL (short length): 6-bit length of the protected payload, if < 48
+ * Packet Number: 32-bits packet identifier
+ * Secure Channel Identifier: 64-bit unique identifier, usually
+ * composed of a MAC address + 16-bit port number
+ */
+struct macsec_sectag {
+ nd_uint8_t tci_an;
+ nd_uint8_t short_length;
+ nd_uint32_t packet_number;
+ nd_uint8_t secure_channel_id[8]; /* optional */
+};
+
+/* IEEE 802.1AE-2006 9.5 */
+#define MACSEC_TCI_VERSION 0x80
+#define MACSEC_TCI_ES 0x40 /* end station */
+#define MACSEC_TCI_SC 0x20 /* SCI present */
+#define MACSEC_TCI_SCB 0x10 /* epon */
+#define MACSEC_TCI_E 0x08 /* encryption */
+#define MACSEC_TCI_C 0x04 /* changed text */
+#define MACSEC_AN_MASK 0x03 /* association number */
+#define MACSEC_TCI_FLAGS (MACSEC_TCI_ES | MACSEC_TCI_SC | MACSEC_TCI_SCB | MACSEC_TCI_E | MACSEC_TCI_C)
+#define MACSEC_TCI_CONFID (MACSEC_TCI_E | MACSEC_TCI_C)
+#define MACSEC_SL_MASK 0x3F /* short length */
+
+#define MACSEC_SECTAG_LEN_NOSCI 6 /* length of MACsec header without SCI */
+#define MACSEC_SECTAG_LEN_SCI 14 /* length of MACsec header with SCI */
+
+#define SCI_FMT "%016" PRIx64
+
+static const struct tok macsec_flag_values[] = {
+ { MACSEC_TCI_E, "E" },
+ { MACSEC_TCI_C, "C" },
+ { MACSEC_TCI_ES, "S" },
+ { MACSEC_TCI_SCB, "B" },
+ { MACSEC_TCI_SC, "I" },
+ { 0, NULL }
+};
+
+static void macsec_print_header(netdissect_options *ndo,
+ const struct macsec_sectag *sectag,
+ u_int short_length)
+{
+ ND_PRINT("an %u, pn %u, flags %s",
+ GET_U_1(sectag->tci_an) & MACSEC_AN_MASK,
+ GET_BE_U_4(sectag->packet_number),
+ bittok2str_nosep(macsec_flag_values, "none",
+ GET_U_1(sectag->tci_an) & MACSEC_TCI_FLAGS));
+
+ if (short_length != 0)
+ ND_PRINT(", sl %u", short_length);
+
+ if (GET_U_1(sectag->tci_an) & MACSEC_TCI_SC)
+ ND_PRINT(", sci " SCI_FMT, GET_BE_U_8(sectag->secure_channel_id));
+
+ ND_PRINT(", ");
+}
+
+/* returns < 0 iff the packet can be decoded completely */
+int macsec_print(netdissect_options *ndo, const u_char **bp,
+ u_int *lengthp, u_int *caplenp, u_int *hdrlenp,
+ const struct lladdr_info *src, const struct lladdr_info *dst)
+{
+ const char *save_protocol;
+ const u_char *p = *bp;
+ u_int length = *lengthp;
+ u_int caplen = *caplenp;
+ u_int hdrlen = *hdrlenp;
+ const struct macsec_sectag *sectag = (const struct macsec_sectag *)p;
+ u_int sectag_len;
+ u_int short_length;
+
+ save_protocol = ndo->ndo_protocol;
+ ndo->ndo_protocol = "macsec";
+
+ /* we need the full MACsec header in the capture */
+ if (caplen < MACSEC_SECTAG_LEN_NOSCI) {
+ nd_print_trunc(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+ if (length < MACSEC_SECTAG_LEN_NOSCI) {
+ nd_print_trunc(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+
+ if (GET_U_1(sectag->tci_an) & MACSEC_TCI_SC) {
+ sectag_len = MACSEC_SECTAG_LEN_SCI;
+ if (caplen < MACSEC_SECTAG_LEN_SCI) {
+ nd_print_trunc(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+ if (length < MACSEC_SECTAG_LEN_SCI) {
+ nd_print_trunc(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+ } else
+ sectag_len = MACSEC_SECTAG_LEN_NOSCI;
+
+ if ((GET_U_1(sectag->short_length) & ~MACSEC_SL_MASK) != 0 ||
+ GET_U_1(sectag->tci_an) & MACSEC_TCI_VERSION) {
+ nd_print_invalid(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+
+ short_length = GET_U_1(sectag->short_length) & MACSEC_SL_MASK;
+ if (ndo->ndo_eflag)
+ macsec_print_header(ndo, sectag, short_length);
+
+ /* Skip the MACsec header. */
+ *bp += sectag_len;
+ *hdrlenp += sectag_len;
+
+ /* Remove it from the lengths, as it's been processed. */
+ *lengthp -= sectag_len;
+ *caplenp -= sectag_len;
+
+ if ((GET_U_1(sectag->tci_an) & MACSEC_TCI_CONFID)) {
+ /*
+ * The payload is encrypted. Print link-layer
+ * information, if it hasn't already been printed.
+ */
+ if (!ndo->ndo_eflag) {
+ /*
+ * Nobody printed the link-layer addresses,
+ * so print them, if we have any.
+ */
+ if (src != NULL && dst != NULL) {
+ ND_PRINT("%s > %s ",
+ (src->addr_string)(ndo, src->addr),
+ (dst->addr_string)(ndo, dst->addr));
+ }
+
+ ND_PRINT("802.1AE MACsec, ");
+
+ /*
+ * Print the MACsec header.
+ */
+ macsec_print_header(ndo, sectag, short_length);
+ }
+
+ /*
+ * Tell our caller it can't be dissected.
+ */
+ ndo->ndo_protocol = save_protocol;
+ return 0;
+ }
+
+ /*
+ * The payload isn't encrypted; remove the
+ * ICV length from the lengths, so our caller
+ * doesn't treat it as payload.
+ */
+ if (*lengthp < MACSEC_DEFAULT_ICV_LEN) {
+ nd_print_trunc(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+ if (*caplenp < MACSEC_DEFAULT_ICV_LEN) {
+ nd_print_trunc(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+ *lengthp -= MACSEC_DEFAULT_ICV_LEN;
+ *caplenp -= MACSEC_DEFAULT_ICV_LEN;
+
+ /*
+ * If the SL field is non-zero, then it's the length of the
+ * Secure Data; otherwise, the Secure Data is what's left
+ * ver after the MACsec header and ICV are removed.
+ */
+ if (short_length != 0) {
+ /*
+ * If the short length is more than we *have*,
+ * that's an error.
+ */
+ if (short_length > *lengthp) {
+ nd_print_trunc(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+ if (short_length > *caplenp) {
+ nd_print_trunc(ndo);
+ ndo->ndo_protocol = save_protocol;
+ return hdrlen + caplen;
+ }
+ if (*lengthp > short_length)
+ *lengthp = short_length;
+ if (*caplenp > short_length)
+ *caplenp = short_length;
+ }
+
+ ndo->ndo_protocol = save_protocol;
+ return -1;
+}
diff --git a/print-mobile.c b/print-mobile.c
new file mode 100644
index 0000000..528da79
--- /dev/null
+++ b/print-mobile.c
@@ -0,0 +1,104 @@
+/* $NetBSD: print-mobile.c,v 1.2 1998/09/30 08:57:01 hwr Exp $ */
+
+/*
+ * (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Heiko W.Rupp <hwr@pilhuhn.de>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: IPv4 mobility printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#define MOBILE_SIZE (8)
+
+struct mobile_ip {
+ nd_uint16_t proto;
+ nd_uint16_t hcheck;
+ nd_uint32_t odst;
+ nd_uint32_t osrc;
+};
+
+#define OSRC_PRES 0x0080 /* old source is present */
+
+/*
+ * Deencapsulate and print a mobile-tunneled IP datagram
+ */
+void
+mobile_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const struct mobile_ip *mob;
+ struct cksum_vec vec[1];
+ u_short proto,crc;
+ u_char osp =0; /* old source address present */
+
+ ndo->ndo_protocol = "mobile";
+ mob = (const struct mobile_ip *)bp;
+
+ if (length < MOBILE_SIZE || !ND_TTEST_SIZE(mob)) {
+ nd_print_trunc(ndo);
+ return;
+ }
+ ND_PRINT("mobile: ");
+
+ proto = GET_BE_U_2(mob->proto);
+ crc = GET_BE_U_2(mob->hcheck);
+ if (proto & OSRC_PRES) {
+ osp=1;
+ }
+
+ if (osp) {
+ ND_PRINT("[S] ");
+ if (ndo->ndo_vflag)
+ ND_PRINT("%s ", GET_IPADDR_STRING(mob->osrc));
+ } else {
+ ND_PRINT("[] ");
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT("> %s ", GET_IPADDR_STRING(mob->odst));
+ ND_PRINT("(oproto=%u)", proto>>8);
+ }
+ vec[0].ptr = (const uint8_t *)(const void *)mob;
+ vec[0].len = osp ? 12 : 8;
+ if (in_cksum(vec, 1)!=0) {
+ ND_PRINT(" (bad checksum %u)", crc);
+ }
+}
diff --git a/print-mobility.c b/print-mobility.c
new file mode 100644
index 0000000..55340ca
--- /dev/null
+++ b/print-mobility.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (C) 2002 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: IPv6 mobility printer */
+/* RFC 3775 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip6.h"
+
+
+/* Mobility header */
+struct ip6_mobility {
+ nd_uint8_t ip6m_pproto; /* following payload protocol (for PG) */
+ nd_uint8_t ip6m_len; /* length in units of 8 octets */
+ nd_uint8_t ip6m_type; /* message type */
+ nd_uint8_t reserved; /* reserved */
+ nd_uint16_t ip6m_cksum; /* sum of IPv6 pseudo-header and MH */
+ union {
+ nd_uint16_t ip6m_un_data16[1]; /* type-specific field */
+ nd_uint8_t ip6m_un_data8[2]; /* type-specific field */
+ } ip6m_dataun;
+};
+
+#define ip6m_data16 ip6m_dataun.ip6m_un_data16
+#define ip6m_data8 ip6m_dataun.ip6m_un_data8
+
+#define IP6M_MINLEN 8
+
+/* https://www.iana.org/assignments/mobility-parameters/mobility-parameters.xhtml */
+
+/* message type */
+#define IP6M_BINDING_REQUEST 0 /* Binding Refresh Request */
+#define IP6M_HOME_TEST_INIT 1 /* Home Test Init */
+#define IP6M_CAREOF_TEST_INIT 2 /* Care-of Test Init */
+#define IP6M_HOME_TEST 3 /* Home Test */
+#define IP6M_CAREOF_TEST 4 /* Care-of Test */
+#define IP6M_BINDING_UPDATE 5 /* Binding Update */
+#define IP6M_BINDING_ACK 6 /* Binding Acknowledgement */
+#define IP6M_BINDING_ERROR 7 /* Binding Error */
+#define IP6M_MAX 7
+
+static const struct tok ip6m_str[] = {
+ { IP6M_BINDING_REQUEST, "BRR" },
+ { IP6M_HOME_TEST_INIT, "HoTI" },
+ { IP6M_CAREOF_TEST_INIT, "CoTI" },
+ { IP6M_HOME_TEST, "HoT" },
+ { IP6M_CAREOF_TEST, "CoT" },
+ { IP6M_BINDING_UPDATE, "BU" },
+ { IP6M_BINDING_ACK, "BA" },
+ { IP6M_BINDING_ERROR, "BE" },
+ { 0, NULL }
+};
+
+static const unsigned ip6m_hdrlen[IP6M_MAX + 1] = {
+ IP6M_MINLEN, /* IP6M_BINDING_REQUEST */
+ IP6M_MINLEN + 8, /* IP6M_HOME_TEST_INIT */
+ IP6M_MINLEN + 8, /* IP6M_CAREOF_TEST_INIT */
+ IP6M_MINLEN + 16, /* IP6M_HOME_TEST */
+ IP6M_MINLEN + 16, /* IP6M_CAREOF_TEST */
+ IP6M_MINLEN + 4, /* IP6M_BINDING_UPDATE */
+ IP6M_MINLEN + 4, /* IP6M_BINDING_ACK */
+ IP6M_MINLEN + 16, /* IP6M_BINDING_ERROR */
+};
+
+/* Mobility Header Options */
+#define IP6MOPT_MINLEN 2
+#define IP6MOPT_PAD1 0x0 /* Pad1 */
+#define IP6MOPT_PADN 0x1 /* PadN */
+#define IP6MOPT_REFRESH 0x2 /* Binding Refresh Advice */
+#define IP6MOPT_REFRESH_MINLEN 4
+#define IP6MOPT_ALTCOA 0x3 /* Alternate Care-of Address */
+#define IP6MOPT_ALTCOA_MINLEN 18
+#define IP6MOPT_NONCEID 0x4 /* Nonce Indices */
+#define IP6MOPT_NONCEID_MINLEN 6
+#define IP6MOPT_AUTH 0x5 /* Binding Authorization Data */
+#define IP6MOPT_AUTH_MINLEN 12
+
+static const struct tok ip6m_binding_update_bits [] = {
+ { 0x08, "A" },
+ { 0x04, "H" },
+ { 0x02, "L" },
+ { 0x01, "K" },
+ { 0, NULL }
+};
+
+static int
+mobility_opt_print(netdissect_options *ndo,
+ const u_char *bp, const unsigned len)
+{
+ unsigned i, optlen;
+
+ for (i = 0; i < len; i += optlen) {
+ if (GET_U_1(bp + i) == IP6MOPT_PAD1)
+ optlen = 1;
+ else {
+ if (i + 1 < len) {
+ optlen = GET_U_1(bp + i + 1) + 2;
+ }
+ else
+ goto trunc;
+ }
+ if (i + optlen > len)
+ goto trunc;
+ ND_TCHECK_1(bp + i + optlen);
+
+ switch (GET_U_1(bp + i)) {
+ case IP6MOPT_PAD1:
+ ND_PRINT("(pad1)");
+ break;
+ case IP6MOPT_PADN:
+ if (len - i < IP6MOPT_MINLEN) {
+ ND_PRINT("(padn: trunc)");
+ goto trunc;
+ }
+ ND_PRINT("(padn)");
+ break;
+ case IP6MOPT_REFRESH:
+ if (len - i < IP6MOPT_REFRESH_MINLEN) {
+ ND_PRINT("(refresh: trunc)");
+ goto trunc;
+ }
+ /* units of 4 secs */
+ ND_PRINT("(refresh: %u)",
+ GET_BE_U_2(bp + i + 2) << 2);
+ break;
+ case IP6MOPT_ALTCOA:
+ if (len - i < IP6MOPT_ALTCOA_MINLEN) {
+ ND_PRINT("(altcoa: trunc)");
+ goto trunc;
+ }
+ ND_PRINT("(alt-CoA: %s)", GET_IP6ADDR_STRING(bp + i + 2));
+ break;
+ case IP6MOPT_NONCEID:
+ if (len - i < IP6MOPT_NONCEID_MINLEN) {
+ ND_PRINT("(ni: trunc)");
+ goto trunc;
+ }
+ ND_PRINT("(ni: ho=0x%04x co=0x%04x)",
+ GET_BE_U_2(bp + i + 2),
+ GET_BE_U_2(bp + i + 4));
+ break;
+ case IP6MOPT_AUTH:
+ if (len - i < IP6MOPT_AUTH_MINLEN) {
+ ND_PRINT("(auth: trunc)");
+ goto trunc;
+ }
+ ND_PRINT("(auth)");
+ break;
+ default:
+ if (len - i < IP6MOPT_MINLEN) {
+ ND_PRINT("(sopt_type %u: trunc)",
+ GET_U_1(bp + i));
+ goto trunc;
+ }
+ ND_PRINT("(type-0x%02x: len=%u)", GET_U_1(bp + i),
+ GET_U_1(bp + i + 1));
+ break;
+ }
+ }
+ return 0;
+
+trunc:
+ return 1;
+}
+
+/*
+ * Mobility Header
+ */
+int
+mobility_print(netdissect_options *ndo,
+ const u_char *bp, const u_char *bp2 _U_)
+{
+ const struct ip6_mobility *mh;
+ const u_char *ep;
+ unsigned mhlen, hlen;
+ uint8_t type;
+
+ ndo->ndo_protocol = "mobility";
+ mh = (const struct ip6_mobility *)bp;
+
+ /* 'ep' points to the end of available data. */
+ ep = ndo->ndo_snapend;
+
+ if (!ND_TTEST_1(mh->ip6m_len)) {
+ /*
+ * There's not enough captured data to include the
+ * mobility header length.
+ *
+ * Our caller expects us to return the length, however,
+ * so return a value that will run to the end of the
+ * captured data.
+ *
+ * XXX - "ip6_print()" doesn't do anything with the
+ * returned length, however, as it breaks out of the
+ * header-processing loop.
+ */
+ mhlen = (unsigned)(ep - bp);
+ goto trunc;
+ }
+ mhlen = (GET_U_1(mh->ip6m_len) + 1) << 3;
+
+ /* XXX ip6m_cksum */
+
+ type = GET_U_1(mh->ip6m_type);
+ if (type <= IP6M_MAX && mhlen < ip6m_hdrlen[type]) {
+ ND_PRINT("(header length %u is too small for type %u)", mhlen, type);
+ goto trunc;
+ }
+ ND_PRINT("mobility: %s", tok2str(ip6m_str, "type-#%u", type));
+ switch (type) {
+ case IP6M_BINDING_REQUEST:
+ hlen = IP6M_MINLEN;
+ break;
+ case IP6M_HOME_TEST_INIT:
+ case IP6M_CAREOF_TEST_INIT:
+ hlen = IP6M_MINLEN;
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" %s Init Cookie=%08x:%08x",
+ type == IP6M_HOME_TEST_INIT ? "Home" : "Care-of",
+ GET_BE_U_4(bp + hlen),
+ GET_BE_U_4(bp + hlen + 4));
+ }
+ hlen += 8;
+ break;
+ case IP6M_HOME_TEST:
+ case IP6M_CAREOF_TEST:
+ ND_PRINT(" nonce id=0x%x", GET_BE_U_2(mh->ip6m_data16[0]));
+ hlen = IP6M_MINLEN;
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" %s Init Cookie=%08x:%08x",
+ type == IP6M_HOME_TEST ? "Home" : "Care-of",
+ GET_BE_U_4(bp + hlen),
+ GET_BE_U_4(bp + hlen + 4));
+ }
+ hlen += 8;
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" %s Keygen Token=%08x:%08x",
+ type == IP6M_HOME_TEST ? "Home" : "Care-of",
+ GET_BE_U_4(bp + hlen),
+ GET_BE_U_4(bp + hlen + 4));
+ }
+ hlen += 8;
+ break;
+ case IP6M_BINDING_UPDATE:
+ {
+ int bits;
+ ND_PRINT(" seq#=%u", GET_BE_U_2(mh->ip6m_data16[0]));
+ hlen = IP6M_MINLEN;
+ ND_TCHECK_2(bp + hlen);
+ bits = (GET_U_1(bp + hlen) & 0xf0) >> 4;
+ if (bits) {
+ ND_PRINT(" ");
+ ND_PRINT("%s",
+ bittok2str_nosep(ip6m_binding_update_bits,
+ "bits-#0x%x", bits));
+ }
+ /* Reserved (4bits) */
+ hlen += 1;
+ /* Reserved (8bits) */
+ hlen += 1;
+ /* units of 4 secs */
+ ND_PRINT(" lifetime=%u", GET_BE_U_2(bp + hlen) << 2);
+ hlen += 2;
+ break;
+ }
+ case IP6M_BINDING_ACK:
+ ND_PRINT(" status=%u", GET_U_1(mh->ip6m_data8[0]));
+ if (GET_U_1(mh->ip6m_data8[1]) & 0x80)
+ ND_PRINT(" K");
+ /* Reserved (7bits) */
+ hlen = IP6M_MINLEN;
+ ND_PRINT(" seq#=%u", GET_BE_U_2(bp + hlen));
+ hlen += 2;
+ /* units of 4 secs */
+ ND_PRINT(" lifetime=%u", GET_BE_U_2(bp + hlen) << 2);
+ hlen += 2;
+ break;
+ case IP6M_BINDING_ERROR:
+ ND_PRINT(" status=%u", GET_U_1(mh->ip6m_data8[0]));
+ /* Reserved */
+ hlen = IP6M_MINLEN;
+ ND_PRINT(" homeaddr %s", GET_IP6ADDR_STRING(bp + hlen));
+ hlen += 16;
+ break;
+ default:
+ ND_PRINT(" len=%u", GET_U_1(mh->ip6m_len));
+ return(mhlen);
+ break;
+ }
+ if (ndo->ndo_vflag)
+ if (mobility_opt_print(ndo, bp + hlen, mhlen - hlen))
+ goto trunc;
+
+ return(mhlen);
+
+ trunc:
+ nd_print_trunc(ndo);
+ return(-1);
+}
diff --git a/print-mpcp.c b/print-mpcp.c
new file mode 100644
index 0000000..4ba873b
--- /dev/null
+++ b/print-mpcp.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1998-2006 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* \summary: IEEE 802.3ah Multi-Point Control Protocol (MPCP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+struct mpcp_common_header_t {
+ nd_uint16_t opcode;
+ nd_uint32_t timestamp;
+};
+
+#define MPCP_OPCODE_PAUSE 0x0001
+#define MPCP_OPCODE_GATE 0x0002
+#define MPCP_OPCODE_REPORT 0x0003
+#define MPCP_OPCODE_REG_REQ 0x0004
+#define MPCP_OPCODE_REG 0x0005
+#define MPCP_OPCODE_REG_ACK 0x0006
+
+static const struct tok mpcp_opcode_values[] = {
+ { MPCP_OPCODE_PAUSE, "Pause" },
+ { MPCP_OPCODE_GATE, "Gate" },
+ { MPCP_OPCODE_REPORT, "Report" },
+ { MPCP_OPCODE_REG_REQ, "Register Request" },
+ { MPCP_OPCODE_REG, "Register" },
+ { MPCP_OPCODE_REG_ACK, "Register ACK" },
+ { 0, NULL}
+};
+
+#define MPCP_GRANT_NUMBER_LEN 1
+#define MPCP_GRANT_NUMBER_MASK 0x7
+static const struct tok mpcp_grant_flag_values[] = {
+ { 0x08, "Discovery" },
+ { 0x10, "Force Grant #1" },
+ { 0x20, "Force Grant #2" },
+ { 0x40, "Force Grant #3" },
+ { 0x80, "Force Grant #4" },
+ { 0, NULL}
+};
+
+struct mpcp_grant_t {
+ nd_uint32_t starttime;
+ nd_uint16_t duration;
+};
+
+struct mpcp_reg_req_t {
+ nd_uint8_t flags;
+ nd_uint8_t pending_grants;
+};
+
+
+static const struct tok mpcp_reg_req_flag_values[] = {
+ { 1, "Register" },
+ { 3, "De-Register" },
+ { 0, NULL}
+};
+
+struct mpcp_reg_t {
+ nd_uint16_t assigned_port;
+ nd_uint8_t flags;
+ nd_uint16_t sync_time;
+ nd_uint8_t echoed_pending_grants;
+};
+
+static const struct tok mpcp_reg_flag_values[] = {
+ { 1, "Re-Register" },
+ { 2, "De-Register" },
+ { 3, "ACK" },
+ { 4, "NACK" },
+ { 0, NULL}
+};
+
+#define MPCP_REPORT_QUEUESETS_LEN 1
+#define MPCP_REPORT_REPORTBITMAP_LEN 1
+static const struct tok mpcp_report_bitmap_values[] = {
+ { 0x01, "Q0" },
+ { 0x02, "Q1" },
+ { 0x04, "Q2" },
+ { 0x08, "Q3" },
+ { 0x10, "Q4" },
+ { 0x20, "Q5" },
+ { 0x40, "Q6" },
+ { 0x80, "Q7" },
+ { 0, NULL}
+};
+
+struct mpcp_reg_ack_t {
+ nd_uint8_t flags;
+ nd_uint16_t echoed_assigned_port;
+ nd_uint16_t echoed_sync_time;
+};
+
+static const struct tok mpcp_reg_ack_flag_values[] = {
+ { 0, "NACK" },
+ { 1, "ACK" },
+ { 0, NULL}
+};
+
+void
+mpcp_print(netdissect_options *ndo, const u_char *pptr, u_int length)
+{
+ const struct mpcp_common_header_t *mpcp_common_header;
+ const struct mpcp_reg_req_t *mpcp_reg_req;
+ const struct mpcp_reg_t *mpcp_reg;
+ const struct mpcp_reg_ack_t *mpcp_reg_ack;
+
+
+ const u_char *tptr;
+ uint16_t opcode;
+ uint32_t timestamp;
+ uint8_t grant_numbers, grant;
+ uint8_t queue_sets, queue_set, report_bitmap, report;
+
+ ndo->ndo_protocol = "mpcp";
+ tptr=pptr;
+ mpcp_common_header = (const struct mpcp_common_header_t *)pptr;
+
+ opcode = GET_BE_U_2(mpcp_common_header->opcode);
+ timestamp = GET_BE_U_4(mpcp_common_header->timestamp);
+ ND_PRINT("MPCP, Opcode %s", tok2str(mpcp_opcode_values, "Unknown (%u)", opcode));
+ if (opcode != MPCP_OPCODE_PAUSE) {
+ ND_PRINT(", Timestamp %u ticks", timestamp);
+ }
+ ND_PRINT(", length %u", length);
+
+ if (!ndo->ndo_vflag)
+ return;
+
+ tptr += sizeof(struct mpcp_common_header_t);
+
+ switch (opcode) {
+ case MPCP_OPCODE_PAUSE:
+ break;
+
+ case MPCP_OPCODE_GATE:
+ grant_numbers = GET_U_1(tptr) & MPCP_GRANT_NUMBER_MASK;
+ ND_PRINT("\n\tGrant Numbers %u, Flags [ %s ]",
+ grant_numbers,
+ bittok2str(mpcp_grant_flag_values,
+ "?",
+ GET_U_1(tptr) & ~MPCP_GRANT_NUMBER_MASK));
+ tptr++;
+
+ for (grant = 1; grant <= grant_numbers; grant++) {
+ const struct mpcp_grant_t *mpcp_grant = (const struct mpcp_grant_t *)tptr;
+ ND_PRINT("\n\tGrant #%u, Start-Time %u ticks, duration %u ticks",
+ grant,
+ GET_BE_U_4(mpcp_grant->starttime),
+ GET_BE_U_2(mpcp_grant->duration));
+ tptr += sizeof(struct mpcp_grant_t);
+ }
+
+ ND_PRINT("\n\tSync-Time %u ticks", GET_BE_U_2(tptr));
+ break;
+
+
+ case MPCP_OPCODE_REPORT:
+ queue_sets = GET_U_1(tptr);
+ tptr+=MPCP_REPORT_QUEUESETS_LEN;
+ ND_PRINT("\n\tTotal Queue-Sets %u", queue_sets);
+
+ for (queue_set = 1; queue_set < queue_sets; queue_set++) {
+ report_bitmap = GET_U_1(tptr);
+ ND_PRINT("\n\t Queue-Set #%u, Report-Bitmap [ %s ]",
+ queue_sets,
+ bittok2str(mpcp_report_bitmap_values, "Unknown", report_bitmap));
+ tptr++;
+
+ report=1;
+ while (report_bitmap != 0) {
+ if (report_bitmap & 1) {
+ ND_PRINT("\n\t Q%u Report, Duration %u ticks",
+ report,
+ GET_BE_U_2(tptr));
+ tptr += 2;
+ }
+ report++;
+ report_bitmap = report_bitmap >> 1;
+ }
+ }
+ break;
+
+ case MPCP_OPCODE_REG_REQ:
+ mpcp_reg_req = (const struct mpcp_reg_req_t *)tptr;
+ ND_PRINT("\n\tFlags [ %s ], Pending-Grants %u",
+ bittok2str(mpcp_reg_req_flag_values, "Reserved", GET_U_1(mpcp_reg_req->flags)),
+ GET_U_1(mpcp_reg_req->pending_grants));
+ break;
+
+ case MPCP_OPCODE_REG:
+ mpcp_reg = (const struct mpcp_reg_t *)tptr;
+ ND_PRINT("\n\tAssigned-Port %u, Flags [ %s ]"
+ "\n\tSync-Time %u ticks, Echoed-Pending-Grants %u",
+ GET_BE_U_2(mpcp_reg->assigned_port),
+ bittok2str(mpcp_reg_flag_values, "Reserved", GET_U_1(mpcp_reg->flags)),
+ GET_BE_U_2(mpcp_reg->sync_time),
+ GET_U_1(mpcp_reg->echoed_pending_grants));
+ break;
+
+ case MPCP_OPCODE_REG_ACK:
+ mpcp_reg_ack = (const struct mpcp_reg_ack_t *)tptr;
+ ND_PRINT("\n\tEchoed-Assigned-Port %u, Flags [ %s ]"
+ "\n\tEchoed-Sync-Time %u ticks",
+ GET_BE_U_2(mpcp_reg_ack->echoed_assigned_port),
+ bittok2str(mpcp_reg_ack_flag_values, "Reserved", GET_U_1(mpcp_reg_ack->flags)),
+ GET_BE_U_2(mpcp_reg_ack->echoed_sync_time));
+ break;
+
+ default:
+ /* unknown opcode - hexdump for now */
+ print_unknown_data(ndo,pptr, "\n\t", length);
+ break;
+ }
+}
diff --git a/print-mpls.c b/print-mpls.c
new file mode 100644
index 0000000..b8820aa
--- /dev/null
+++ b/print-mpls.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2001 WIDE Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: Multi-Protocol Label Switching (MPLS) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "mpls.h"
+
+static const char *mpls_labelname[] = {
+/*0*/ "IPv4 explicit NULL", "router alert", "IPv6 explicit NULL",
+ "implicit NULL", "rsvd",
+/*5*/ "rsvd", "rsvd", "rsvd", "rsvd", "rsvd",
+/*10*/ "rsvd", "rsvd", "rsvd", "rsvd", "rsvd",
+/*15*/ "rsvd",
+};
+
+enum mpls_packet_type {
+ PT_UNKNOWN,
+ PT_IPV4,
+ PT_IPV6,
+ PT_OSI
+};
+
+/*
+ * RFC3032: MPLS label stack encoding
+ */
+void
+mpls_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const u_char *p;
+ uint32_t label_entry;
+ uint16_t label_stack_depth = 0;
+ uint8_t first;
+ enum mpls_packet_type pt = PT_UNKNOWN;
+
+ ndo->ndo_protocol = "mpls";
+ p = bp;
+ nd_print_protocol_caps(ndo);
+ do {
+ if (length < sizeof(label_entry))
+ goto invalid;
+ label_entry = GET_BE_U_4(p);
+ ND_PRINT("%s(label %u",
+ (label_stack_depth && ndo->ndo_vflag) ? "\n\t" : " ",
+ MPLS_LABEL(label_entry));
+ label_stack_depth++;
+ if (ndo->ndo_vflag &&
+ MPLS_LABEL(label_entry) < sizeof(mpls_labelname) / sizeof(mpls_labelname[0]))
+ ND_PRINT(" (%s)", mpls_labelname[MPLS_LABEL(label_entry)]);
+ ND_PRINT(", exp %u", MPLS_EXP(label_entry));
+ if (MPLS_STACK(label_entry))
+ ND_PRINT(", [S]");
+ ND_PRINT(", ttl %u)", MPLS_TTL(label_entry));
+
+ p += sizeof(label_entry);
+ length -= sizeof(label_entry);
+ } while (!MPLS_STACK(label_entry));
+
+ /*
+ * Try to figure out the packet type.
+ */
+ switch (MPLS_LABEL(label_entry)) {
+
+ case 0: /* IPv4 explicit NULL label */
+ case 3: /* IPv4 implicit NULL label */
+ pt = PT_IPV4;
+ break;
+
+ case 2: /* IPv6 explicit NULL label */
+ pt = PT_IPV6;
+ break;
+
+ default:
+ /*
+ * Generally there's no indication of protocol in MPLS label
+ * encoding.
+ *
+ * However, draft-hsmit-isis-aal5mux-00.txt describes a
+ * technique for encapsulating IS-IS and IP traffic on the
+ * same ATM virtual circuit; you look at the first payload
+ * byte to determine the network layer protocol, based on
+ * the fact that
+ *
+ * 1) the first byte of an IP header is 0x45-0x4f
+ * for IPv4 and 0x60-0x6f for IPv6;
+ *
+ * 2) the first byte of an OSI CLNP packet is 0x81,
+ * the first byte of an OSI ES-IS packet is 0x82,
+ * and the first byte of an OSI IS-IS packet is
+ * 0x83;
+ *
+ * so the network layer protocol can be inferred from the
+ * first byte of the packet, if the protocol is one of the
+ * ones listed above.
+ *
+ * Cisco sends control-plane traffic MPLS-encapsulated in
+ * this fashion.
+ */
+ if (length < 1) {
+ /* nothing to print */
+ return;
+ }
+ first = GET_U_1(p);
+ pt =
+ (first >= 0x45 && first <= 0x4f) ? PT_IPV4 :
+ (first >= 0x60 && first <= 0x6f) ? PT_IPV6 :
+ (first >= 0x81 && first <= 0x83) ? PT_OSI :
+ /* ok bail out - we did not figure out what it is*/
+ PT_UNKNOWN;
+ }
+
+ /*
+ * Print the payload.
+ */
+ switch (pt) {
+ case PT_UNKNOWN:
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, length);
+ break;
+
+ case PT_IPV4:
+ ND_PRINT(ndo->ndo_vflag ? "\n\t" : " ");
+ ip_print(ndo, p, length);
+ break;
+
+ case PT_IPV6:
+ ND_PRINT(ndo->ndo_vflag ? "\n\t" : " ");
+ ip6_print(ndo, p, length);
+ break;
+
+ case PT_OSI:
+ ND_PRINT(ndo->ndo_vflag ? "\n\t" : " ");
+ isoclns_print(ndo, p, length);
+ break;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(p, length);
+}
diff --git a/print-mptcp.c b/print-mptcp.c
new file mode 100644
index 0000000..aae78df
--- /dev/null
+++ b/print-mptcp.c
@@ -0,0 +1,483 @@
+/**
+ * Copyright (c) 2012
+ *
+ * Gregory Detal <gregory.detal@uclouvain.be>
+ * Christoph Paasch <christoph.paasch@uclouvain.be>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: Multipath TCP (MPTCP) printer */
+
+/* specification: RFC 6824 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+#include "tcp.h"
+
+#define MPTCP_SUB_CAPABLE 0x0
+#define MPTCP_SUB_JOIN 0x1
+#define MPTCP_SUB_DSS 0x2
+#define MPTCP_SUB_ADD_ADDR 0x3
+#define MPTCP_SUB_REMOVE_ADDR 0x4
+#define MPTCP_SUB_PRIO 0x5
+#define MPTCP_SUB_FAIL 0x6
+#define MPTCP_SUB_FCLOSE 0x7
+
+struct mptcp_option {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub_etc; /* subtype upper 4 bits, other stuff lower 4 bits */
+};
+
+#define MPTCP_OPT_SUBTYPE(sub_etc) ((GET_U_1(sub_etc) >> 4) & 0xF)
+
+struct mp_capable {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub_ver;
+ nd_uint8_t flags;
+ nd_uint64_t sender_key;
+ nd_uint64_t receiver_key;
+};
+
+#define MP_CAPABLE_OPT_VERSION(sub_ver) ((GET_U_1(sub_ver) >> 0) & 0xF)
+#define MP_CAPABLE_C 0x80
+#define MP_CAPABLE_S 0x01
+
+struct mp_join {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub_b;
+ nd_uint8_t addr_id;
+ union {
+ struct {
+ nd_uint32_t token;
+ nd_uint32_t nonce;
+ } syn;
+ struct {
+ nd_uint64_t mac;
+ nd_uint32_t nonce;
+ } synack;
+ struct {
+ nd_byte mac[20];
+ } ack;
+ } u;
+};
+
+#define MP_JOIN_B 0x01
+
+struct mp_dss {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub;
+ nd_uint8_t flags;
+};
+
+#define MP_DSS_F 0x10
+#define MP_DSS_m 0x08
+#define MP_DSS_M 0x04
+#define MP_DSS_a 0x02
+#define MP_DSS_A 0x01
+
+static const struct tok mptcp_addr_subecho_bits[] = {
+ { 0x6, "v0-ip6" },
+ { 0x4, "v0-ip4" },
+ { 0x1, "v1-echo" },
+ { 0x0, "v1" },
+ { 0, NULL }
+};
+
+struct mp_add_addr {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub_echo;
+ nd_uint8_t addr_id;
+ union {
+ struct {
+ nd_ipv4 addr;
+ nd_uint16_t port;
+ nd_uint64_t mac;
+ } v4;
+ struct {
+ nd_ipv4 addr;
+ nd_uint64_t mac;
+ } v4np;
+ struct {
+ nd_ipv6 addr;
+ nd_uint16_t port;
+ nd_uint64_t mac;
+ } v6;
+ struct {
+ nd_ipv6 addr;
+ nd_uint64_t mac;
+ } v6np;
+ } u;
+};
+
+struct mp_remove_addr {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub;
+ /* list of addr_id */
+ nd_uint8_t addrs_id[1];
+};
+
+struct mp_fail {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub;
+ nd_uint8_t resv;
+ nd_uint64_t data_seq;
+};
+
+struct mp_close {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub;
+ nd_uint8_t rsv;
+ nd_byte key[8];
+};
+
+struct mp_prio {
+ nd_uint8_t kind;
+ nd_uint8_t len;
+ nd_uint8_t sub_b;
+ nd_uint8_t addr_id;
+};
+
+#define MP_PRIO_B 0x01
+
+static int
+dummy_print(netdissect_options *ndo _U_,
+ const u_char *opt _U_, u_int opt_len _U_, u_char flags _U_)
+{
+ return 1;
+}
+
+static int
+mp_capable_print(netdissect_options *ndo,
+ const u_char *opt, u_int opt_len, u_char flags)
+{
+ const struct mp_capable *mpc = (const struct mp_capable *) opt;
+ uint8_t version;
+
+ if (!((opt_len == 12 || opt_len == 4) && flags & TH_SYN) &&
+ !((opt_len == 20 || opt_len == 22) && (flags & (TH_SYN | TH_ACK)) ==
+ TH_ACK))
+ return 0;
+
+ version = MP_CAPABLE_OPT_VERSION(mpc->sub_ver); /* uses GET_U_1() */
+ switch (version) {
+ case 0: /* fall through */
+ case 1:
+ ND_PRINT(" v%u", version);
+ break;
+ default:
+ ND_PRINT(" Unknown Version (%u)", version);
+ return 1;
+ }
+
+ if (GET_U_1(mpc->flags) & MP_CAPABLE_C)
+ ND_PRINT(" csum");
+ if (opt_len == 12 || opt_len >= 20) {
+ ND_PRINT(" {0x%" PRIx64, GET_BE_U_8(mpc->sender_key));
+ if (opt_len >= 20)
+ ND_PRINT(",0x%" PRIx64, GET_BE_U_8(mpc->receiver_key));
+ ND_PRINT("}");
+ }
+ return 1;
+}
+
+static int
+mp_join_print(netdissect_options *ndo,
+ const u_char *opt, u_int opt_len, u_char flags)
+{
+ const struct mp_join *mpj = (const struct mp_join *) opt;
+
+ if (!(opt_len == 12 && (flags & TH_SYN)) &&
+ !(opt_len == 16 && (flags & (TH_SYN | TH_ACK)) == (TH_SYN | TH_ACK)) &&
+ !(opt_len == 24 && (flags & TH_ACK)))
+ return 0;
+
+ if (opt_len != 24) {
+ if (GET_U_1(mpj->sub_b) & MP_JOIN_B)
+ ND_PRINT(" backup");
+ ND_PRINT(" id %u", GET_U_1(mpj->addr_id));
+ }
+
+ switch (opt_len) {
+ case 12: /* SYN */
+ ND_PRINT(" token 0x%x" " nonce 0x%x",
+ GET_BE_U_4(mpj->u.syn.token),
+ GET_BE_U_4(mpj->u.syn.nonce));
+ break;
+ case 16: /* SYN/ACK */
+ ND_PRINT(" hmac 0x%" PRIx64 " nonce 0x%x",
+ GET_BE_U_8(mpj->u.synack.mac),
+ GET_BE_U_4(mpj->u.synack.nonce));
+ break;
+ case 24: {/* ACK */
+ size_t i;
+ ND_PRINT(" hmac 0x");
+ for (i = 0; i < sizeof(mpj->u.ack.mac); ++i)
+ ND_PRINT("%02x", mpj->u.ack.mac[i]);
+ }
+ default:
+ break;
+ }
+ return 1;
+}
+
+static int
+mp_dss_print(netdissect_options *ndo,
+ const u_char *opt, u_int opt_len, u_char flags)
+{
+ const struct mp_dss *mdss = (const struct mp_dss *) opt;
+ uint8_t mdss_flags;
+
+ /* We need the flags, at a minimum. */
+ if (opt_len < 4)
+ return 0;
+
+ if (flags & TH_SYN)
+ return 0;
+
+ mdss_flags = GET_U_1(mdss->flags);
+ if (mdss_flags & MP_DSS_F)
+ ND_PRINT(" fin");
+
+ opt += 4;
+ opt_len -= 4;
+ if (mdss_flags & MP_DSS_A) {
+ /* Ack present */
+ ND_PRINT(" ack ");
+ /*
+ * If the a flag is set, we have an 8-byte ack; if it's
+ * clear, we have a 4-byte ack.
+ */
+ if (mdss_flags & MP_DSS_a) {
+ if (opt_len < 8)
+ return 0;
+ ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
+ opt += 8;
+ opt_len -= 8;
+ } else {
+ if (opt_len < 4)
+ return 0;
+ ND_PRINT("%u", GET_BE_U_4(opt));
+ opt += 4;
+ opt_len -= 4;
+ }
+ }
+
+ if (mdss_flags & MP_DSS_M) {
+ /*
+ * Data Sequence Number (DSN), Subflow Sequence Number (SSN),
+ * Data-Level Length present, and Checksum possibly present.
+ */
+ ND_PRINT(" seq ");
+ /*
+ * If the m flag is set, we have an 8-byte NDS; if it's clear,
+ * we have a 4-byte DSN.
+ */
+ if (mdss_flags & MP_DSS_m) {
+ if (opt_len < 8)
+ return 0;
+ ND_PRINT("%" PRIu64, GET_BE_U_8(opt));
+ opt += 8;
+ opt_len -= 8;
+ } else {
+ if (opt_len < 4)
+ return 0;
+ ND_PRINT("%u", GET_BE_U_4(opt));
+ opt += 4;
+ opt_len -= 4;
+ }
+ if (opt_len < 4)
+ return 0;
+ ND_PRINT(" subseq %u", GET_BE_U_4(opt));
+ opt += 4;
+ opt_len -= 4;
+ if (opt_len < 2)
+ return 0;
+ ND_PRINT(" len %u", GET_BE_U_2(opt));
+ opt += 2;
+ opt_len -= 2;
+
+ /*
+ * The Checksum is present only if negotiated.
+ * If there are at least 2 bytes left, process the next 2
+ * bytes as the Checksum.
+ */
+ if (opt_len >= 2) {
+ ND_PRINT(" csum 0x%x", GET_BE_U_2(opt));
+ opt_len -= 2;
+ }
+ }
+ if (opt_len != 0)
+ return 0;
+ return 1;
+}
+
+static int
+add_addr_print(netdissect_options *ndo,
+ const u_char *opt, u_int opt_len, u_char flags _U_)
+{
+ const struct mp_add_addr *add_addr = (const struct mp_add_addr *) opt;
+
+ if (!(opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18 ||
+ opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30))
+ return 0;
+
+ ND_PRINT(" %s",
+ tok2str(mptcp_addr_subecho_bits, "[bad version/echo]",
+ GET_U_1(add_addr->sub_echo) & 0xF));
+ ND_PRINT(" id %u", GET_U_1(add_addr->addr_id));
+ if (opt_len == 8 || opt_len == 10 || opt_len == 16 || opt_len == 18) {
+ ND_PRINT(" %s", GET_IPADDR_STRING(add_addr->u.v4.addr));
+ if (opt_len == 10 || opt_len == 18)
+ ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v4.port));
+ if (opt_len == 16)
+ ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4np.mac));
+ if (opt_len == 18)
+ ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v4.mac));
+ }
+
+ if (opt_len == 20 || opt_len == 22 || opt_len == 28 || opt_len == 30) {
+ ND_PRINT(" %s", GET_IP6ADDR_STRING(add_addr->u.v6.addr));
+ if (opt_len == 22 || opt_len == 30)
+ ND_PRINT(":%u", GET_BE_U_2(add_addr->u.v6.port));
+ if (opt_len == 28)
+ ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6np.mac));
+ if (opt_len == 30)
+ ND_PRINT(" hmac 0x%" PRIx64, GET_BE_U_8(add_addr->u.v6.mac));
+ }
+
+ return 1;
+}
+
+static int
+remove_addr_print(netdissect_options *ndo,
+ const u_char *opt, u_int opt_len, u_char flags _U_)
+{
+ const struct mp_remove_addr *remove_addr = (const struct mp_remove_addr *) opt;
+ u_int i;
+
+ if (opt_len < 4)
+ return 0;
+
+ opt_len -= 3;
+ ND_PRINT(" id");
+ for (i = 0; i < opt_len; i++)
+ ND_PRINT(" %u", GET_U_1(remove_addr->addrs_id[i]));
+ return 1;
+}
+
+static int
+mp_prio_print(netdissect_options *ndo,
+ const u_char *opt, u_int opt_len, u_char flags _U_)
+{
+ const struct mp_prio *mpp = (const struct mp_prio *) opt;
+
+ if (opt_len != 3 && opt_len != 4)
+ return 0;
+
+ if (GET_U_1(mpp->sub_b) & MP_PRIO_B)
+ ND_PRINT(" backup");
+ else
+ ND_PRINT(" non-backup");
+ if (opt_len == 4)
+ ND_PRINT(" id %u", GET_U_1(mpp->addr_id));
+
+ return 1;
+}
+
+static int
+mp_fail_print(netdissect_options *ndo,
+ const u_char *opt, u_int opt_len, u_char flags _U_)
+{
+ if (opt_len != 12)
+ return 0;
+
+ ND_PRINT(" seq %" PRIu64, GET_BE_U_8(opt + 4));
+ return 1;
+}
+
+static int
+mp_fast_close_print(netdissect_options *ndo,
+ const u_char *opt, u_int opt_len, u_char flags _U_)
+{
+ if (opt_len != 12)
+ return 0;
+
+ ND_PRINT(" key 0x%" PRIx64, GET_BE_U_8(opt + 4));
+ return 1;
+}
+
+static const struct {
+ const char *name;
+ int (*print)(netdissect_options *, const u_char *, u_int, u_char);
+} mptcp_options[] = {
+ { "capable", mp_capable_print},
+ { "join", mp_join_print },
+ { "dss", mp_dss_print },
+ { "add-addr", add_addr_print },
+ { "rem-addr", remove_addr_print },
+ { "prio", mp_prio_print },
+ { "fail", mp_fail_print },
+ { "fast-close", mp_fast_close_print },
+ { "unknown", dummy_print },
+};
+
+int
+mptcp_print(netdissect_options *ndo,
+ const u_char *cp, u_int len, u_char flags)
+{
+ const struct mptcp_option *opt;
+ u_int subtype;
+
+ ndo->ndo_protocol = "mptcp";
+ if (len < 3)
+ return 0;
+
+ opt = (const struct mptcp_option *) cp;
+ subtype = MPTCP_OPT_SUBTYPE(opt->sub_etc); /* uses GET_U_1() */
+ subtype = ND_MIN(subtype, MPTCP_SUB_FCLOSE + 1);
+
+ ND_PRINT(" %s", mptcp_options[subtype].name);
+ return mptcp_options[subtype].print(ndo, cp, len, flags);
+}
diff --git a/print-msdp.c b/print-msdp.c
new file mode 100644
index 0000000..545f452
--- /dev/null
+++ b/print-msdp.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2001 William C. Fenner.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * The name of William C. Fenner may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Multicast Source Discovery Protocol (MSDP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#define MSDP_TYPE_MAX 7
+
+void
+msdp_print(netdissect_options *ndo, const u_char *sp, u_int length)
+{
+ unsigned int type, len;
+
+ ndo->ndo_protocol = "msdp";
+ /* See if we think we're at the beginning of a compound packet */
+ type = GET_U_1(sp);
+ len = GET_BE_U_2(sp + 1);
+ if (len > 1500 || len < 3 || type == 0 || type > MSDP_TYPE_MAX)
+ goto trunc; /* not really truncated, but still not decodable */
+ ND_PRINT(" msdp:");
+ while (length != 0) {
+ type = GET_U_1(sp);
+ len = GET_BE_U_2(sp + 1);
+ if (len > 1400 || ndo->ndo_vflag)
+ ND_PRINT(" [len %u]", len);
+ if (len < 3)
+ goto trunc;
+ if (length < len)
+ goto trunc;
+ sp += 3;
+ length -= 3;
+ switch (type) {
+ case 1: /* IPv4 Source-Active */
+ case 3: /* IPv4 Source-Active Response */
+ if (type == 1)
+ ND_PRINT(" SA");
+ else
+ ND_PRINT(" SA-Response");
+ ND_PRINT(" %u entries", GET_U_1(sp));
+ if ((u_int)((GET_U_1(sp) * 12) + 8) < len) {
+ ND_PRINT(" [w/data]");
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT(" ");
+ ip_print(ndo, sp +
+ GET_U_1(sp) * 12 + 8 - 3,
+ len - (GET_U_1(sp) * 12 + 8));
+ }
+ }
+ break;
+ case 2:
+ ND_PRINT(" SA-Request");
+ ND_PRINT(" for %s", GET_IPADDR_STRING(sp + 1));
+ break;
+ case 4:
+ ND_PRINT(" Keepalive");
+ if (len != 3)
+ ND_PRINT("[len=%u] ", len);
+ break;
+ case 5:
+ ND_PRINT(" Notification");
+ break;
+ default:
+ ND_PRINT(" [type=%u len=%u]", type, len);
+ break;
+ }
+ sp += (len - 3);
+ length -= (len - 3);
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-msnlb.c b/print-msnlb.c
new file mode 100644
index 0000000..8afaa7f
--- /dev/null
+++ b/print-msnlb.c
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013 Romain Francoise <romain@orebokech.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: MS Network Load Balancing's (NLB) heartbeat printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+struct msnlb_heartbeat_pkt {
+ nd_byte unknown1[4];
+ nd_byte unknown2[4];
+ nd_uint32_t host_prio; /* little-endian */
+ nd_ipv4 virtual_ip;
+ nd_ipv4 host_ip;
+ /* the protocol is undocumented so we ignore the rest */
+};
+
+void
+msnlb_print(netdissect_options *ndo, const u_char *bp)
+{
+ const struct msnlb_heartbeat_pkt *hb;
+
+ ndo->ndo_protocol = "msnlb";
+ hb = (const struct msnlb_heartbeat_pkt *)bp;
+
+ ND_PRINT("MS NLB heartbeat");
+ ND_PRINT(", host priority: %u", GET_LE_U_4((hb->host_prio)));
+ ND_PRINT(", cluster IP: %s", GET_IPADDR_STRING(hb->virtual_ip));
+ ND_PRINT(", host IP: %s", GET_IPADDR_STRING(hb->host_ip));
+}
diff --git a/print-nflog.c b/print-nflog.c
new file mode 100644
index 0000000..1e75561
--- /dev/null
+++ b/print-nflog.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 2013, Petar Alilovic,
+ * Faculty of Electrical Engineering and Computing, University of Zagreb
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+/* \summary: DLT_NFLOG printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+#ifdef DLT_NFLOG
+
+/*
+ * Structure of an NFLOG header and TLV parts, as described at
+ * https://www.tcpdump.org/linktypes/LINKTYPE_NFLOG.html
+ *
+ * The NFLOG header is big-endian.
+ *
+ * The TLV length and type are in host byte order. The value is either
+ * big-endian or is an array of bytes in some externally-specified byte
+ * order (text string, link-layer address, link-layer header, packet
+ * data, etc.).
+ */
+typedef struct nflog_hdr {
+ nd_uint8_t nflog_family; /* address family */
+ nd_uint8_t nflog_version; /* version */
+ nd_uint16_t nflog_rid; /* resource ID */
+} nflog_hdr_t;
+
+#define NFLOG_HDR_LEN sizeof(nflog_hdr_t)
+
+typedef struct nflog_tlv {
+ nd_uint16_t tlv_length; /* tlv length */
+ nd_uint16_t tlv_type; /* tlv type */
+ /* value follows this */
+} nflog_tlv_t;
+
+#define NFLOG_TLV_LEN sizeof(nflog_tlv_t)
+
+typedef struct nflog_packet_hdr {
+ nd_uint16_t hw_protocol; /* hw protocol */
+ nd_uint8_t hook; /* netfilter hook */
+ nd_byte pad[1]; /* padding to 32 bits */
+} nflog_packet_hdr_t;
+
+typedef struct nflog_hwaddr {
+ nd_uint16_t hw_addrlen; /* address length */
+ nd_byte pad[2]; /* padding to 32-bit boundary */
+ nd_byte hw_addr[8]; /* address, up to 8 bytes */
+} nflog_hwaddr_t;
+
+typedef struct nflog_timestamp {
+ nd_uint64_t sec;
+ nd_uint64_t usec;
+} nflog_timestamp_t;
+
+/*
+ * TLV types.
+ */
+#define NFULA_PACKET_HDR 1 /* nflog_packet_hdr_t */
+#define NFULA_MARK 2 /* packet mark from skbuff */
+#define NFULA_TIMESTAMP 3 /* nflog_timestamp_t for skbuff's time stamp */
+#define NFULA_IFINDEX_INDEV 4 /* ifindex of device on which packet received (possibly bridge group) */
+#define NFULA_IFINDEX_OUTDEV 5 /* ifindex of device on which packet transmitted (possibly bridge group) */
+#define NFULA_IFINDEX_PHYSINDEV 6 /* ifindex of physical device on which packet received (not bridge group) */
+#define NFULA_IFINDEX_PHYSOUTDEV 7 /* ifindex of physical device on which packet transmitted (not bridge group) */
+#define NFULA_HWADDR 8 /* nflog_hwaddr_t for hardware address */
+#define NFULA_PAYLOAD 9 /* packet payload */
+#define NFULA_PREFIX 10 /* text string - null-terminated, count includes NUL */
+#define NFULA_UID 11 /* UID owning socket on which packet was sent/received */
+#define NFULA_SEQ 12 /* sequence number of packets on this NFLOG socket */
+#define NFULA_SEQ_GLOBAL 13 /* sequence number of pakets on all NFLOG sockets */
+#define NFULA_GID 14 /* GID owning socket on which packet was sent/received */
+#define NFULA_HWTYPE 15 /* ARPHRD_ type of skbuff's device */
+#define NFULA_HWHEADER 16 /* skbuff's MAC-layer header */
+#define NFULA_HWLEN 17 /* length of skbuff's MAC-layer header */
+
+static const struct tok nflog_values[] = {
+ { AF_INET, "IPv4" },
+ { AF_INET6, "IPv6" },
+ { 0, NULL }
+};
+
+static void
+nflog_hdr_print(netdissect_options *ndo, const nflog_hdr_t *hdr, u_int length)
+{
+ ND_PRINT("version %u, resource ID %u",
+ GET_U_1(hdr->nflog_version), GET_BE_U_2(hdr->nflog_rid));
+
+ if (!ndo->ndo_qflag) {
+ ND_PRINT(", family %s (%u)",
+ tok2str(nflog_values, "Unknown",
+ GET_U_1(hdr->nflog_family)),
+ GET_U_1(hdr->nflog_family));
+ } else {
+ ND_PRINT(", %s",
+ tok2str(nflog_values,
+ "Unknown NFLOG (0x%02x)",
+ GET_U_1(hdr->nflog_family)));
+ }
+
+ ND_PRINT(", length %u: ", length);
+}
+
+void
+nflog_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ const nflog_hdr_t *hdr = (const nflog_hdr_t *)p;
+ uint16_t size;
+ uint16_t h_size = NFLOG_HDR_LEN;
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+
+ ndo->ndo_protocol = "nflog";
+ if (caplen < NFLOG_HDR_LEN) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+ ndo->ndo_ll_hdr_len += NFLOG_HDR_LEN;
+
+ ND_TCHECK_SIZE(hdr);
+ if (GET_U_1(hdr->nflog_version) != 0) {
+ ND_PRINT("version %u (unknown)", GET_U_1(hdr->nflog_version));
+ return;
+ }
+
+ if (ndo->ndo_eflag)
+ nflog_hdr_print(ndo, hdr, length);
+
+ p += NFLOG_HDR_LEN;
+ length -= NFLOG_HDR_LEN;
+ caplen -= NFLOG_HDR_LEN;
+
+ while (length > 0) {
+ const nflog_tlv_t *tlv;
+
+ /* We have some data. Do we have enough for the TLV header? */
+ if (caplen < NFLOG_TLV_LEN)
+ goto trunc; /* No. */
+
+ tlv = (const nflog_tlv_t *) p;
+ ND_TCHECK_SIZE(tlv);
+ size = GET_HE_U_2(tlv->tlv_length);
+ if (size % 4 != 0)
+ size += 4 - size % 4;
+
+ /* Is the TLV's length less than the minimum? */
+ if (size < NFLOG_TLV_LEN)
+ goto trunc; /* Yes. Give up now. */
+
+ /* Do we have enough data for the full TLV? */
+ if (caplen < size)
+ goto trunc; /* No. */
+
+ if (GET_HE_U_2(tlv->tlv_type) == NFULA_PAYLOAD) {
+ /*
+ * This TLV's data is the packet payload.
+ * Skip past the TLV header, and break out
+ * of the loop so we print the packet data.
+ */
+ p += NFLOG_TLV_LEN;
+ h_size += NFLOG_TLV_LEN;
+ length -= NFLOG_TLV_LEN;
+ caplen -= NFLOG_TLV_LEN;
+ break;
+ }
+
+ p += size;
+ h_size += size;
+ length -= size;
+ caplen -= size;
+ }
+
+ switch (GET_U_1(hdr->nflog_family)) {
+
+ case AF_INET:
+ ip_print(ndo, p, length);
+ break;
+
+ case AF_INET6:
+ ip6_print(ndo, p, length);
+ break;
+
+ default:
+ if (!ndo->ndo_eflag)
+ nflog_hdr_print(ndo, hdr,
+ length + NFLOG_HDR_LEN);
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ break;
+ }
+
+ ndo->ndo_ll_hdr_len += h_size - NFLOG_HDR_LEN;
+ return;
+trunc:
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += h_size - NFLOG_HDR_LEN;
+}
+
+#endif /* DLT_NFLOG */
diff --git a/print-nsh.c b/print-nsh.c
new file mode 100644
index 0000000..12a63cd
--- /dev/null
+++ b/print-nsh.c
@@ -0,0 +1,264 @@
+/* Copyright (c) 2015, bugyo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: Network Service Header (NSH) printer */
+
+/* specification: RFC 8300 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+static const struct tok nsh_flags [] = {
+ { 0x2, "O" },
+ { 0, NULL }
+};
+
+/*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Ver|O|U| TTL | Length |U|U|U|U|MD Type| Next Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+#define NSH_BASE_HDR_LEN 4
+#define NSH_VER(x) (((x) & 0xc0000000) >> 30)
+#define NSH_FLAGS(x) (((x) & 0x30000000) >> 28)
+#define NSH_TTL(x) (((x) & 0x0fc00000) >> 22)
+#define NSH_LENGTH(x) (((x) & 0x003f0000) >> 16)
+#define NSH_MD_TYPE(x) (((x) & 0x00000f00) >> 8)
+#define NSH_NEXT_PROT(x) (((x) & 0x000000ff) >> 0)
+
+#define NSH_SERVICE_PATH_HDR_LEN 4
+#define NSH_HDR_WORD_SIZE 4U
+
+#define MD_RSV 0x00
+#define MD_TYPE1 0x01
+#define MD_TYPE2 0x02
+#define MD_EXP 0x0F
+static const struct tok md_str[] = {
+ { MD_RSV, "reserved" },
+ { MD_TYPE1, "1" },
+ { MD_TYPE2, "2" },
+ { MD_EXP, "experimental" },
+ { 0, NULL }
+};
+
+#define NP_IPV4 0x01
+#define NP_IPV6 0x02
+#define NP_ETH 0x03
+#define NP_NSH 0x04
+#define NP_MPLS 0x05
+#define NP_EXP1 0xFE
+#define NP_EXP2 0xFF
+static const struct tok np_str[] = {
+ { NP_IPV4, "IPv4" },
+ { NP_IPV6, "IPv6" },
+ { NP_ETH, "Ethernet" },
+ { NP_NSH, "NSH" },
+ { NP_MPLS, "MPLS" },
+ { NP_EXP1, "Experiment 1" },
+ { NP_EXP2, "Experiment 2" },
+ { 0, NULL }
+};
+
+void
+nsh_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ uint32_t basehdr;
+ u_int ver, length, md_type;
+ uint8_t next_protocol;
+ u_char past_headers = 0;
+ u_int next_len;
+
+ ndo->ndo_protocol = "nsh";
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Base Header |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Service Path Header |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * ~ Context Header(s) ~
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ /* print Base Header and Service Path Header */
+ if (len < NSH_BASE_HDR_LEN + NSH_SERVICE_PATH_HDR_LEN) {
+ ND_PRINT(" (packet length %u < %u)",
+ len, NSH_BASE_HDR_LEN + NSH_SERVICE_PATH_HDR_LEN);
+ goto invalid;
+ }
+
+ basehdr = GET_BE_U_4(bp);
+ bp += 4;
+ ver = NSH_VER(basehdr);
+ length = NSH_LENGTH(basehdr);
+ md_type = NSH_MD_TYPE(basehdr);
+ next_protocol = NSH_NEXT_PROT(basehdr);
+
+ ND_PRINT("NSH, ");
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT("ver %u, ", ver);
+ }
+ if (ver != 0)
+ return;
+ ND_PRINT("flags [%s], ",
+ bittok2str_nosep(nsh_flags, "none", NSH_FLAGS(basehdr)));
+ if (ndo->ndo_vflag > 2) {
+ ND_PRINT("TTL %u, ", NSH_TTL(basehdr));
+ ND_PRINT("length %u, ", length);
+ ND_PRINT("md type %s, ", tok2str(md_str, "unknown (0x%02x)", md_type));
+ }
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT("next-protocol %s, ",
+ tok2str(np_str, "unknown (0x%02x)", next_protocol));
+ }
+
+ /* Make sure we have all the headers */
+ if (len < length * NSH_HDR_WORD_SIZE) {
+ ND_PRINT(" (too many headers for packet length %u)", len);
+ goto invalid;
+ }
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Service Path Identifier (SPI) | Service Index |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+ ND_PRINT("service-path-id 0x%06x, ", GET_BE_U_3(bp));
+ bp += 3;
+ ND_PRINT("service-index 0x%x", GET_U_1(bp));
+ bp += 1;
+
+ /*
+ * length includes the lengths of the Base and Service Path headers.
+ * That means it must be at least 2.
+ */
+ if (length < 2) {
+ ND_PRINT(" (less than two headers)");
+ goto invalid;
+ }
+
+ /*
+ * Print, or skip, the Context Headers.
+ * (length - 2) is the length of those headers.
+ */
+ if (ndo->ndo_vflag > 2) {
+ u_int n;
+
+ if (md_type == MD_TYPE1) {
+ if (length != 6) {
+ ND_PRINT(" (invalid length for the MD type)");
+ goto invalid;
+ }
+ for (n = 0; n < length - 2; n++) {
+ ND_PRINT("\n Context[%02u]: 0x%08x", n, GET_BE_U_4(bp));
+ bp += NSH_HDR_WORD_SIZE;
+ }
+ past_headers = 1;
+ }
+ else if (md_type == MD_TYPE2) {
+ n = 0;
+ while (n < length - 2) {
+ uint16_t tlv_class;
+ uint8_t tlv_type, tlv_len, tlv_len_padded;
+
+ tlv_class = GET_BE_U_2(bp);
+ bp += 2;
+ tlv_type = GET_U_1(bp);
+ bp += 1;
+ tlv_len = GET_U_1(bp) & 0x7f;
+ bp += 1;
+ tlv_len_padded = roundup2(tlv_len, NSH_HDR_WORD_SIZE);
+
+ ND_PRINT("\n TLV Class %u, Type %u, Len %u",
+ tlv_class, tlv_type, tlv_len);
+
+ n += 1;
+
+ if (length - 2 < n + tlv_len_padded / NSH_HDR_WORD_SIZE) {
+ ND_PRINT(" (length too big)");
+ goto invalid;
+ }
+
+ if (tlv_len) {
+ const char *sep = "0x";
+ u_int vn;
+
+ ND_PRINT("\n Value: ");
+ for (vn = 0; vn < tlv_len; vn++) {
+ ND_PRINT("%s%02x", sep, GET_U_1(bp));
+ bp += 1;
+ sep = ":";
+ }
+ /* Cover any TLV padding. */
+ ND_TCHECK_LEN(bp, tlv_len_padded - tlv_len);
+ bp += tlv_len_padded - tlv_len;
+ n += tlv_len_padded / NSH_HDR_WORD_SIZE;
+ }
+ }
+ past_headers = 1;
+ }
+ }
+ if (! past_headers) {
+ ND_TCHECK_LEN(bp, (length - 2) * NSH_HDR_WORD_SIZE);
+ bp += (length - 2) * NSH_HDR_WORD_SIZE;
+ }
+ ND_PRINT(ndo->ndo_vflag ? "\n " : ": ");
+
+ /* print Next Protocol */
+ next_len = len - length * NSH_HDR_WORD_SIZE;
+ switch (next_protocol) {
+ case NP_IPV4:
+ ip_print(ndo, bp, next_len);
+ break;
+ case NP_IPV6:
+ ip6_print(ndo, bp, next_len);
+ break;
+ case NP_ETH:
+ ether_print(ndo, bp, next_len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+ break;
+ default:
+ ND_PRINT("ERROR: unknown-next-protocol");
+ return;
+ }
+
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+
diff --git a/print-ntp.c b/print-ntp.c
new file mode 100644
index 0000000..99c0896
--- /dev/null
+++ b/print-ntp.c
@@ -0,0 +1,538 @@
+/*
+ * Copyright (c) 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * By Jeffrey Mogul/DECWRL
+ * loosely based on print-bootp.c
+ */
+
+/* \summary: Network Time Protocol (NTP) printer */
+
+/*
+ * specification:
+ *
+ * RFC 1119 - NTPv2
+ * RFC 1305 - NTPv3
+ * RFC 5905 - NTPv4
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#ifdef HAVE_STRFTIME
+#include <time.h>
+#endif
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ntp.h"
+
+/*
+ * Based on ntp.h from the U of MD implementation
+ * This file is based on Version 2 of the NTP spec (RFC1119).
+ */
+
+/* rfc2030
+ * 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |LI | VN |Mode | Stratum | Poll | Precision |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Root Delay |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Root Dispersion |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reference Identifier |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Reference Timestamp (64) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Originate Timestamp (64) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Receive Timestamp (64) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Transmit Timestamp (64) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Key Identifier (optional) (32) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | |
+ * | Message Digest (optional) (128) |
+ * | |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/* Length of the NTP data message with the mandatory fields ("the header")
+ * and without any optional fields (extension, Key Identifier,
+ * Message Digest).
+ */
+#define NTP_TIMEMSG_MINLEN 48U
+
+struct ntp_time_data {
+ nd_uint8_t status; /* status of local clock and leap info */
+ nd_uint8_t stratum; /* Stratum level */
+ nd_int8_t ppoll; /* poll value */
+ nd_int8_t precision;
+ struct s_fixedpt root_delay;
+ struct s_fixedpt root_dispersion;
+ nd_uint32_t refid;
+ struct l_fixedpt ref_timestamp;
+ struct l_fixedpt org_timestamp;
+ struct l_fixedpt rec_timestamp;
+ struct l_fixedpt xmt_timestamp;
+ nd_uint32_t key_id;
+ nd_uint8_t message_digest[20];
+};
+/*
+ * Leap Second Codes (high order two bits)
+ */
+#define NO_WARNING 0x00 /* no warning */
+#define PLUS_SEC 0x40 /* add a second (61 seconds) */
+#define MINUS_SEC 0x80 /* minus a second (59 seconds) */
+#define ALARM 0xc0 /* alarm condition (clock unsynchronized) */
+
+/*
+ * Clock Status Bits that Encode Version
+ */
+#define NTPVERSION_1 0x08
+#define VERSIONMASK 0x38
+#define VERSIONSHIFT 3
+#define LEAPMASK 0xc0
+#define LEAPSHIFT 6
+#ifdef MODEMASK
+#undef MODEMASK /* Solaris sucks */
+#endif
+#define MODEMASK 0x07
+#define MODESHIFT 0
+
+/*
+ * Code values
+ */
+#define MODE_UNSPEC 0 /* unspecified */
+#define MODE_SYM_ACT 1 /* symmetric active */
+#define MODE_SYM_PAS 2 /* symmetric passive */
+#define MODE_CLIENT 3 /* client */
+#define MODE_SERVER 4 /* server */
+#define MODE_BROADCAST 5 /* broadcast */
+#define MODE_CONTROL 6 /* control message */
+#define MODE_RES2 7 /* reserved */
+
+/*
+ * Stratum Definitions
+ */
+#define UNSPECIFIED 0
+#define PRIM_REF 1 /* radio clock */
+#define INFO_QUERY 62 /* **** THIS implementation dependent **** */
+#define INFO_REPLY 63 /* **** THIS implementation dependent **** */
+
+static void p_sfix(netdissect_options *ndo, const struct s_fixedpt *);
+static void p_ntp_delta(netdissect_options *, const struct l_fixedpt *, const struct l_fixedpt *);
+static void p_poll(netdissect_options *, const int);
+
+static const struct tok ntp_mode_values[] = {
+ { MODE_UNSPEC, "unspecified" },
+ { MODE_SYM_ACT, "symmetric active" },
+ { MODE_SYM_PAS, "symmetric passive" },
+ { MODE_CLIENT, "Client" },
+ { MODE_SERVER, "Server" },
+ { MODE_BROADCAST, "Broadcast" },
+ { MODE_CONTROL, "Control Message" },
+ { MODE_RES2, "Reserved" },
+ { 0, NULL }
+};
+
+static const struct tok ntp_leapind_values[] = {
+ { NO_WARNING, "" },
+ { PLUS_SEC, "+1s" },
+ { MINUS_SEC, "-1s" },
+ { ALARM, "clock unsynchronized" },
+ { 0, NULL }
+};
+
+static const struct tok ntp_stratum_values[] = {
+ { UNSPECIFIED, "unspecified" },
+ { PRIM_REF, "primary reference" },
+ { 0, NULL }
+};
+
+/* draft-ietf-ntp-mode-6-cmds-02
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |LI | VN |Mode |R|E|M| OpCode | Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Status | Association ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Offset | Count |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / Data (up to 468 bytes) /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Padding (optional) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * / Authenticator (optional, 96 bytes) /
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Figure 1: NTP Control Message Header
+ */
+
+/* Length of the NTP control message with the mandatory fields ("the header")
+ * and without any optional fields (Data, Padding, Authenticator).
+ */
+#define NTP_CTRLMSG_MINLEN 12U
+
+struct ntp_control_data {
+ nd_uint8_t magic; /* LI, VN, Mode */
+ nd_uint8_t control; /* R, E, M, OpCode */
+ nd_uint16_t sequence; /* Sequence Number */
+ nd_uint16_t status; /* Status */
+ nd_uint16_t assoc; /* Association ID */
+ nd_uint16_t offset; /* Offset */
+ nd_uint16_t count; /* Count */
+ nd_uint8_t data[564]; /* Data, [Padding, [Authenticator]] */
+};
+
+/*
+ * Print NTP time requests and responses
+ */
+static void
+ntp_time_print(netdissect_options *ndo,
+ const struct ntp_time_data *bp, u_int length)
+{
+ uint8_t stratum;
+
+ if (length < NTP_TIMEMSG_MINLEN)
+ goto invalid;
+
+ stratum = GET_U_1(bp->stratum);
+ ND_PRINT(", Stratum %u (%s)",
+ stratum,
+ tok2str(ntp_stratum_values, (stratum >=2 && stratum<=15) ? "secondary reference" : "reserved", stratum));
+
+ ND_PRINT(", poll %d", GET_S_1(bp->ppoll));
+ p_poll(ndo, GET_S_1(bp->ppoll));
+
+ ND_PRINT(", precision %d", GET_S_1(bp->precision));
+
+ ND_TCHECK_SIZE(&bp->root_delay);
+ ND_PRINT("\n\tRoot Delay: ");
+ p_sfix(ndo, &bp->root_delay);
+
+ ND_TCHECK_SIZE(&bp->root_dispersion);
+ ND_PRINT(", Root dispersion: ");
+ p_sfix(ndo, &bp->root_dispersion);
+
+ ND_TCHECK_4(bp->refid);
+ ND_PRINT(", Reference-ID: ");
+ /* Interpretation depends on stratum */
+ switch (stratum) {
+
+ case UNSPECIFIED:
+ ND_PRINT("(unspec)");
+ break;
+
+ case PRIM_REF:
+ if (nd_printn(ndo, (const u_char *)&(bp->refid), 4, ndo->ndo_snapend))
+ goto trunc;
+ break;
+
+ case INFO_QUERY:
+ ND_PRINT("%s INFO_QUERY", GET_IPADDR_STRING(bp->refid));
+ /* this doesn't have more content */
+ return;
+
+ case INFO_REPLY:
+ ND_PRINT("%s INFO_REPLY", GET_IPADDR_STRING(bp->refid));
+ /* this is too complex to be worth printing */
+ return;
+
+ default:
+ /* In NTPv4 (RFC 5905) refid is an IPv4 address or first 32 bits of
+ MD5 sum of IPv6 address */
+ ND_PRINT("0x%08x", GET_BE_U_4(bp->refid));
+ break;
+ }
+
+ ND_TCHECK_SIZE(&bp->ref_timestamp);
+ ND_PRINT("\n\t Reference Timestamp: ");
+ p_ntp_time(ndo, &(bp->ref_timestamp));
+
+ ND_TCHECK_SIZE(&bp->org_timestamp);
+ ND_PRINT("\n\t Originator Timestamp: ");
+ p_ntp_time(ndo, &(bp->org_timestamp));
+
+ ND_TCHECK_SIZE(&bp->rec_timestamp);
+ ND_PRINT("\n\t Receive Timestamp: ");
+ p_ntp_time(ndo, &(bp->rec_timestamp));
+
+ ND_TCHECK_SIZE(&bp->xmt_timestamp);
+ ND_PRINT("\n\t Transmit Timestamp: ");
+ p_ntp_time(ndo, &(bp->xmt_timestamp));
+
+ ND_PRINT("\n\t Originator - Receive Timestamp: ");
+ p_ntp_delta(ndo, &(bp->org_timestamp), &(bp->rec_timestamp));
+
+ ND_PRINT("\n\t Originator - Transmit Timestamp: ");
+ p_ntp_delta(ndo, &(bp->org_timestamp), &(bp->xmt_timestamp));
+
+ /* FIXME: this code is not aware of any extension fields */
+ if (length == NTP_TIMEMSG_MINLEN + 4) { /* Optional: key-id (crypto-NAK) */
+ ND_PRINT("\n\tKey id: %u", GET_BE_U_4(bp->key_id));
+ } else if (length == NTP_TIMEMSG_MINLEN + 4 + 16) { /* Optional: key-id + 128-bit digest */
+ ND_PRINT("\n\tKey id: %u", GET_BE_U_4(bp->key_id));
+ ND_TCHECK_LEN(bp->message_digest, 16);
+ ND_PRINT("\n\tAuthentication: %08x%08x%08x%08x",
+ GET_BE_U_4(bp->message_digest),
+ GET_BE_U_4(bp->message_digest + 4),
+ GET_BE_U_4(bp->message_digest + 8),
+ GET_BE_U_4(bp->message_digest + 12));
+ } else if (length == NTP_TIMEMSG_MINLEN + 4 + 20) { /* Optional: key-id + 160-bit digest */
+ ND_PRINT("\n\tKey id: %u", GET_BE_U_4(bp->key_id));
+ ND_TCHECK_LEN(bp->message_digest, 20);
+ ND_PRINT("\n\tAuthentication: %08x%08x%08x%08x%08x",
+ GET_BE_U_4(bp->message_digest),
+ GET_BE_U_4(bp->message_digest + 4),
+ GET_BE_U_4(bp->message_digest + 8),
+ GET_BE_U_4(bp->message_digest + 12),
+ GET_BE_U_4(bp->message_digest + 16));
+ } else if (length > NTP_TIMEMSG_MINLEN) {
+ ND_PRINT("\n\t(%u more bytes after the header)", length - NTP_TIMEMSG_MINLEN);
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(bp, length);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+/*
+ * Print NTP control message requests and responses
+ */
+static void
+ntp_control_print(netdissect_options *ndo,
+ const struct ntp_control_data *cd, u_int length)
+{
+ uint8_t control, R, E, M, opcode;
+ uint16_t sequence, status, assoc, offset, count;
+
+ if (length < NTP_CTRLMSG_MINLEN)
+ goto invalid;
+
+ control = GET_U_1(cd->control);
+ R = (control & 0x80) != 0;
+ E = (control & 0x40) != 0;
+ M = (control & 0x20) != 0;
+ opcode = control & 0x1f;
+ ND_PRINT(", %s, %s, %s, OpCode=%u\n",
+ R ? "Response" : "Request", E ? "Error" : "OK",
+ M ? "More" : "Last", opcode);
+
+ sequence = GET_BE_U_2(cd->sequence);
+ ND_PRINT("\tSequence=%hu", sequence);
+
+ status = GET_BE_U_2(cd->status);
+ ND_PRINT(", Status=%#hx", status);
+
+ assoc = GET_BE_U_2(cd->assoc);
+ ND_PRINT(", Assoc.=%hu", assoc);
+
+ offset = GET_BE_U_2(cd->offset);
+ ND_PRINT(", Offset=%hu", offset);
+
+ count = GET_BE_U_2(cd->count);
+ ND_PRINT(", Count=%hu", count);
+
+ if (NTP_CTRLMSG_MINLEN + count > length)
+ goto invalid;
+ if (count != 0) {
+ ND_TCHECK_LEN(cd->data, count);
+ ND_PRINT("\n\tTO-BE-DONE: data not interpreted");
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cd, length);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+union ntpdata {
+ struct ntp_time_data td;
+ struct ntp_control_data cd;
+};
+
+/*
+ * Print NTP requests, handling the common VN, LI, and Mode
+ */
+void
+ntp_print(netdissect_options *ndo,
+ const u_char *cp, u_int length)
+{
+ const union ntpdata *bp = (const union ntpdata *)cp;
+ u_int mode, version, leapind;
+ uint8_t status;
+
+ ndo->ndo_protocol = "ntp";
+ status = GET_U_1(bp->td.status);
+
+ version = (status & VERSIONMASK) >> VERSIONSHIFT;
+ ND_PRINT("NTPv%u", version);
+
+ mode = (status & MODEMASK) >> MODESHIFT;
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", %s, length %u",
+ tok2str(ntp_mode_values, "Unknown mode", mode),
+ length);
+ return;
+ }
+
+ ND_PRINT(", %s, length %u\n",
+ tok2str(ntp_mode_values, "Unknown mode", mode), length);
+
+ /* leapind = (status & LEAPMASK) >> LEAPSHIFT; */
+ leapind = (status & LEAPMASK);
+ ND_PRINT("\tLeap indicator: %s (%u)",
+ tok2str(ntp_leapind_values, "Unknown", leapind),
+ leapind);
+
+ switch (mode) {
+
+ case MODE_UNSPEC:
+ case MODE_SYM_ACT:
+ case MODE_SYM_PAS:
+ case MODE_CLIENT:
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ ntp_time_print(ndo, &bp->td, length);
+ break;
+
+ case MODE_CONTROL:
+ ntp_control_print(ndo, &bp->cd, length);
+ break;
+
+ default:
+ break; /* XXX: not implemented! */
+ }
+}
+
+static void
+p_sfix(netdissect_options *ndo,
+ const struct s_fixedpt *sfp)
+{
+ int i;
+ int f;
+ double ff;
+
+ i = GET_BE_U_2(sfp->int_part);
+ f = GET_BE_U_2(sfp->fraction);
+ ff = f / 65536.0; /* shift radix point by 16 bits */
+ f = (int)(ff * 1000000.0); /* Treat fraction as parts per million */
+ ND_PRINT("%d.%06d", i, f);
+}
+
+/* Prints time difference between *lfp and *olfp */
+static void
+p_ntp_delta(netdissect_options *ndo,
+ const struct l_fixedpt *olfp,
+ const struct l_fixedpt *lfp)
+{
+ uint32_t u, uf;
+ uint32_t ou, ouf;
+ uint32_t i;
+ uint32_t f;
+ double ff;
+ int signbit;
+
+ u = GET_BE_U_4(lfp->int_part);
+ ou = GET_BE_U_4(olfp->int_part);
+ uf = GET_BE_U_4(lfp->fraction);
+ ouf = GET_BE_U_4(olfp->fraction);
+ if (ou == 0 && ouf == 0) {
+ p_ntp_time(ndo, lfp);
+ return;
+ }
+
+ if (u > ou) { /* new is definitely greater than old */
+ signbit = 0;
+ i = u - ou;
+ f = uf - ouf;
+ if (ouf > uf) /* must borrow from high-order bits */
+ i -= 1;
+ } else if (u < ou) { /* new is definitely less than old */
+ signbit = 1;
+ i = ou - u;
+ f = ouf - uf;
+ if (uf > ouf) /* must borrow from the high-order bits */
+ i -= 1;
+ } else { /* int_part is zero */
+ i = 0;
+ if (uf > ouf) {
+ signbit = 0;
+ f = uf - ouf;
+ } else {
+ signbit = 1;
+ f = ouf - uf;
+ }
+ }
+
+ ff = f;
+ if (ff < 0.0) /* some compilers are buggy */
+ ff += FMAXINT;
+ ff = ff / FMAXINT; /* shift radix point by 32 bits */
+ f = (uint32_t)(ff * 1000000000.0); /* treat fraction as parts per billion */
+ ND_PRINT("%s%u.%09u", signbit ? "-" : "+", i, f);
+}
+
+/* Prints polling interval in log2 as seconds or fraction of second */
+static void
+p_poll(netdissect_options *ndo,
+ const int poll_interval)
+{
+ if (poll_interval <= -32 || poll_interval >= 32)
+ return;
+
+ if (poll_interval >= 0)
+ ND_PRINT(" (%us)", 1U << poll_interval);
+ else
+ ND_PRINT(" (1/%us)", 1U << -poll_interval);
+}
+
diff --git a/print-null.c b/print-null.c
new file mode 100644
index 0000000..a1b03f8
--- /dev/null
+++ b/print-null.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: BSD loopback device printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "af.h"
+
+
+/*
+ * The DLT_NULL packet header is 4 bytes long. It contains a host-byte-order
+ * 32-bit integer that specifies the family, e.g. AF_INET.
+ *
+ * Note here that "host" refers to the host on which the packets were
+ * captured; that isn't necessarily *this* host.
+ *
+ * The OpenBSD DLT_LOOP packet header is the same, except that the integer
+ * is in network byte order.
+ */
+#define NULL_HDRLEN 4
+
+/*
+ * Byte-swap a 32-bit number.
+ * ("htonl()" or "ntohl()" won't work - we want to byte-swap even on
+ * big-endian platforms.)
+ */
+#define SWAPLONG(y) \
+((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
+
+static void
+null_hdr_print(netdissect_options *ndo, uint32_t family, u_int length)
+{
+ if (!ndo->ndo_qflag) {
+ ND_PRINT("AF %s (%u)",
+ tok2str(bsd_af_values,"Unknown",family),family);
+ } else {
+ ND_PRINT("%s",
+ tok2str(bsd_af_values,"Unknown AF %u",family));
+ }
+
+ ND_PRINT(", length %u: ", length);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+null_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+ uint32_t family;
+
+ ndo->ndo_protocol = "null";
+ ND_TCHECK_LEN(p, NULL_HDRLEN);
+ ndo->ndo_ll_hdr_len += NULL_HDRLEN;
+
+ family = GET_HE_U_4(p);
+
+ /*
+ * This isn't necessarily in our host byte order; if this is
+ * a DLT_LOOP capture, it's in network byte order, and if
+ * this is a DLT_NULL capture from a machine with the opposite
+ * byte-order, it's in the opposite byte order from ours.
+ *
+ * If the upper 16 bits aren't all zero, assume it's byte-swapped.
+ */
+ if ((family & 0xFFFF0000) != 0)
+ family = SWAPLONG(family);
+
+ if (ndo->ndo_eflag)
+ null_hdr_print(ndo, family, length);
+
+ length -= NULL_HDRLEN;
+ caplen -= NULL_HDRLEN;
+ p += NULL_HDRLEN;
+
+ switch (family) {
+
+ case BSD_AFNUM_INET:
+ ip_print(ndo, p, length);
+ break;
+
+ case BSD_AFNUM_INET6_BSD:
+ case BSD_AFNUM_INET6_FREEBSD:
+ case BSD_AFNUM_INET6_DARWIN:
+ ip6_print(ndo, p, length);
+ break;
+
+ case BSD_AFNUM_ISO:
+ isoclns_print(ndo, p, length);
+ break;
+
+ case BSD_AFNUM_APPLETALK:
+ atalk_print(ndo, p, length);
+ break;
+
+ case BSD_AFNUM_IPX:
+ ipx_print(ndo, p, length);
+ break;
+
+ default:
+ /* unknown AF_ value */
+ if (!ndo->ndo_eflag)
+ null_hdr_print(ndo, family, length + NULL_HDRLEN);
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+}
diff --git a/print-olsr.c b/print-olsr.c
new file mode 100644
index 0000000..2b85d67
--- /dev/null
+++ b/print-olsr.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright (c) 1998-2007 The TCPDUMP project
+ * Copyright (c) 2009 Florian Forster
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler <hannes@gredler.at>
+ * IPv6 additions by Florian Forster <octo at verplant.org>
+ */
+
+/* \summary: Optimized Link State Routing Protocol (OLSR) printer */
+
+/* specification: RFC 3626 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+/*
+ * RFC 3626 common header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Packet Length | Packet Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Message Type | Vtime | Message Size |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Originator Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Time To Live | Hop Count | Message Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * : MESSAGE :
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Message Type | Vtime | Message Size |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Originator Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Time To Live | Hop Count | Message Sequence Number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * : MESSAGE :
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * : :
+ */
+
+struct olsr_common {
+ nd_uint16_t packet_len;
+ nd_uint16_t packet_seq;
+};
+
+#define OLSR_HELLO_MSG 1 /* rfc3626 */
+#define OLSR_TC_MSG 2 /* rfc3626 */
+#define OLSR_MID_MSG 3 /* rfc3626 */
+#define OLSR_HNA_MSG 4 /* rfc3626 */
+#define OLSR_POWERINFO_MSG 128
+#define OLSR_NAMESERVICE_MSG 130
+#define OLSR_HELLO_LQ_MSG 201 /* LQ extensions olsr.org */
+#define OLSR_TC_LQ_MSG 202 /* LQ extensions olsr.org */
+
+static const struct tok olsr_msg_values[] = {
+ { OLSR_HELLO_MSG, "Hello" },
+ { OLSR_TC_MSG, "TC" },
+ { OLSR_MID_MSG, "MID" },
+ { OLSR_HNA_MSG, "HNA" },
+ { OLSR_POWERINFO_MSG, "Powerinfo" },
+ { OLSR_NAMESERVICE_MSG, "Nameservice" },
+ { OLSR_HELLO_LQ_MSG, "Hello-LQ" },
+ { OLSR_TC_LQ_MSG, "TC-LQ" },
+ { 0, NULL}
+};
+
+struct olsr_msg4 {
+ nd_uint8_t msg_type;
+ nd_uint8_t vtime;
+ nd_uint16_t msg_len;
+ nd_ipv4 originator;
+ nd_uint8_t ttl;
+ nd_uint8_t hopcount;
+ nd_uint16_t msg_seq;
+};
+
+struct olsr_msg6 {
+ nd_uint8_t msg_type;
+ nd_uint8_t vtime;
+ nd_uint16_t msg_len;
+ nd_ipv6 originator;
+ nd_uint8_t ttl;
+ nd_uint8_t hopcount;
+ nd_uint16_t msg_seq;
+};
+
+struct olsr_hello {
+ nd_byte res[2];
+ nd_uint8_t htime;
+ nd_uint8_t will;
+};
+
+struct olsr_hello_link {
+ nd_uint8_t link_code;
+ nd_byte res;
+ nd_uint16_t len;
+};
+
+struct olsr_tc {
+ nd_uint16_t ans_seq;
+ nd_byte res[2];
+};
+
+struct olsr_hna4 {
+ nd_ipv4 network;
+ nd_ipv4 mask;
+};
+
+struct olsr_hna6 {
+ nd_ipv6 network;
+ nd_ipv6 mask;
+};
+
+
+/** gateway HNA flags */
+enum gateway_hna_flags {
+ GW_HNA_FLAG_LINKSPEED = 1 << 0,
+ GW_HNA_FLAG_IPV4 = 1 << 1,
+ GW_HNA_FLAG_IPV4_NAT = 1 << 2,
+ GW_HNA_FLAG_IPV6 = 1 << 3,
+ GW_HNA_FLAG_IPV6PREFIX = 1 << 4
+};
+
+/** gateway HNA field byte offsets in the netmask field of the HNA */
+enum gateway_hna_fields {
+ GW_HNA_PAD = 0,
+ GW_HNA_FLAGS = 1,
+ GW_HNA_UPLINK = 2,
+ GW_HNA_DOWNLINK = 3,
+ GW_HNA_V6PREFIXLEN = 4,
+ GW_HNA_V6PREFIX = 5
+};
+
+
+#define OLSR_EXTRACT_LINK_TYPE(link_code) (link_code & 0x3)
+#define OLSR_EXTRACT_NEIGHBOR_TYPE(link_code) (link_code >> 2)
+
+static const struct tok olsr_link_type_values[] = {
+ { 0, "Unspecified" },
+ { 1, "Asymmetric" },
+ { 2, "Symmetric" },
+ { 3, "Lost" },
+ { 0, NULL}
+};
+
+static const struct tok olsr_neighbor_type_values[] = {
+ { 0, "Not-Neighbor" },
+ { 1, "Symmetric" },
+ { 2, "Symmetric-MPR" },
+ { 0, NULL}
+};
+
+struct olsr_lq_neighbor4 {
+ nd_ipv4 neighbor;
+ nd_uint8_t link_quality;
+ nd_uint8_t neighbor_link_quality;
+ nd_byte res[2];
+};
+
+struct olsr_lq_neighbor6 {
+ nd_ipv6 neighbor;
+ nd_uint8_t link_quality;
+ nd_uint8_t neighbor_link_quality;
+ nd_byte res[2];
+};
+
+#define MAX_SMARTGW_SPEED 320000000
+
+/**
+ * Convert an encoded 1 byte transport value (5 bits mantissa, 3 bits exponent)
+ * to an uplink/downlink speed value
+ *
+ * @param value the encoded 1 byte transport value
+ * @return the uplink/downlink speed value (in kbit/s)
+ */
+static uint32_t deserialize_gw_speed(uint8_t value) {
+ uint32_t speed;
+ uint32_t exp;
+
+ if (!value) {
+ return 0;
+ }
+
+ if (value == UINT8_MAX) {
+ /* maximum value: also return maximum value */
+ return MAX_SMARTGW_SPEED;
+ }
+
+ speed = (value >> 3) + 1;
+ exp = value & 7;
+
+ while (exp != 0) {
+ speed *= 10;
+ exp--;
+ }
+ return speed;
+}
+
+/*
+ * macro to convert the 8-bit mantissa/exponent to a double float
+ * taken from olsr.org.
+ */
+#define VTIME_SCALE_FACTOR 0.0625
+#define ME_TO_DOUBLE(me) \
+ (double)(VTIME_SCALE_FACTOR*(1+(double)(me>>4)/16)*(double)(1<<(me&0x0F)))
+
+/*
+ * print a neighbor list with LQ extensions.
+ */
+static int
+olsr_print_lq_neighbor4(netdissect_options *ndo,
+ const u_char *msg_data, u_int hello_len)
+{
+ const struct olsr_lq_neighbor4 *lq_neighbor;
+
+ while (hello_len >= sizeof(struct olsr_lq_neighbor4)) {
+
+ lq_neighbor = (const struct olsr_lq_neighbor4 *)msg_data;
+ ND_TCHECK_SIZE(lq_neighbor);
+
+ ND_PRINT("\n\t neighbor %s, link-quality %.2f%%"
+ ", neighbor-link-quality %.2f%%",
+ GET_IPADDR_STRING(lq_neighbor->neighbor),
+ ((double) GET_U_1(lq_neighbor->link_quality)/2.55),
+ ((double) GET_U_1(lq_neighbor->neighbor_link_quality)/2.55));
+
+ msg_data += sizeof(struct olsr_lq_neighbor4);
+ hello_len -= sizeof(struct olsr_lq_neighbor4);
+ }
+ return (0);
+trunc:
+ return -1;
+}
+
+static int
+olsr_print_lq_neighbor6(netdissect_options *ndo,
+ const u_char *msg_data, u_int hello_len)
+{
+ const struct olsr_lq_neighbor6 *lq_neighbor;
+
+ while (hello_len >= sizeof(struct olsr_lq_neighbor6)) {
+
+ lq_neighbor = (const struct olsr_lq_neighbor6 *)msg_data;
+ ND_TCHECK_SIZE(lq_neighbor);
+
+ ND_PRINT("\n\t neighbor %s, link-quality %.2f%%"
+ ", neighbor-link-quality %.2f%%",
+ GET_IP6ADDR_STRING(lq_neighbor->neighbor),
+ ((double) GET_U_1(lq_neighbor->link_quality)/2.55),
+ ((double) GET_U_1(lq_neighbor->neighbor_link_quality)/2.55));
+
+ msg_data += sizeof(struct olsr_lq_neighbor6);
+ hello_len -= sizeof(struct olsr_lq_neighbor6);
+ }
+ return (0);
+trunc:
+ return -1;
+}
+
+/*
+ * print a neighbor list.
+ */
+static int
+olsr_print_neighbor(netdissect_options *ndo,
+ const u_char *msg_data, u_int hello_len)
+{
+ int neighbor;
+
+ ND_PRINT("\n\t neighbor\n\t\t");
+ neighbor = 1;
+
+ while (hello_len >= sizeof(nd_ipv4)) {
+ /* print 4 neighbors per line */
+ ND_PRINT("%s%s", GET_IPADDR_STRING(msg_data),
+ neighbor % 4 == 0 ? "\n\t\t" : " ");
+
+ msg_data += sizeof(nd_ipv4);
+ hello_len -= sizeof(nd_ipv4);
+ }
+ return (0);
+}
+
+
+void
+olsr_print(netdissect_options *ndo,
+ const u_char *pptr, u_int length, int is_ipv6)
+{
+ union {
+ const struct olsr_common *common;
+ const struct olsr_msg4 *msg4;
+ const struct olsr_msg6 *msg6;
+ const struct olsr_hello *hello;
+ const struct olsr_hello_link *hello_link;
+ const struct olsr_tc *tc;
+ const struct olsr_hna4 *hna;
+ } ptr;
+
+ u_int msg_type, msg_len, msg_tlen, hello_len;
+ uint16_t name_entry_type, name_entry_len;
+ u_int name_entry_padding;
+ uint8_t link_type, neighbor_type;
+ const u_char *tptr, *msg_data;
+
+ ndo->ndo_protocol = "olsr";
+ tptr = pptr;
+
+ if (length < sizeof(struct olsr_common)) {
+ goto trunc;
+ }
+
+ ND_TCHECK_LEN(tptr, sizeof(struct olsr_common));
+
+ ptr.common = (const struct olsr_common *)tptr;
+ length = ND_MIN(length, GET_BE_U_2(ptr.common->packet_len));
+
+ ND_PRINT("OLSRv%i, seq 0x%04x, length %u",
+ (is_ipv6 == 0) ? 4 : 6,
+ GET_BE_U_2(ptr.common->packet_seq),
+ length);
+
+ tptr += sizeof(struct olsr_common);
+
+ /*
+ * In non-verbose mode, just print version.
+ */
+ if (ndo->ndo_vflag < 1) {
+ return;
+ }
+
+ while (tptr < (pptr+length)) {
+ union
+ {
+ const struct olsr_msg4 *v4;
+ const struct olsr_msg6 *v6;
+ } msgptr;
+ int msg_len_valid = 0;
+
+ if (is_ipv6)
+ {
+ ND_TCHECK_LEN(tptr, sizeof(struct olsr_msg6));
+ msgptr.v6 = (const struct olsr_msg6 *) tptr;
+ msg_type = GET_U_1(msgptr.v6->msg_type);
+ msg_len = GET_BE_U_2(msgptr.v6->msg_len);
+ if ((msg_len >= sizeof (struct olsr_msg6))
+ && (msg_len <= length))
+ msg_len_valid = 1;
+
+ /* infinite loop check */
+ if (msg_type == 0 || msg_len == 0) {
+ return;
+ }
+
+ ND_PRINT("\n\t%s Message (%#04x), originator %s, ttl %u, hop %u"
+ "\n\t vtime %.3fs, msg-seq 0x%04x, length %u%s",
+ tok2str(olsr_msg_values, "Unknown", msg_type),
+ msg_type, GET_IP6ADDR_STRING(msgptr.v6->originator),
+ GET_U_1(msgptr.v6->ttl),
+ GET_U_1(msgptr.v6->hopcount),
+ ME_TO_DOUBLE(GET_U_1(msgptr.v6->vtime)),
+ GET_BE_U_2(msgptr.v6->msg_seq),
+ msg_len, (msg_len_valid == 0) ? " (invalid)" : "");
+ if (!msg_len_valid) {
+ return;
+ }
+
+ msg_tlen = msg_len - sizeof(struct olsr_msg6);
+ msg_data = tptr + sizeof(struct olsr_msg6);
+ }
+ else /* (!is_ipv6) */
+ {
+ ND_TCHECK_LEN(tptr, sizeof(struct olsr_msg4));
+ msgptr.v4 = (const struct olsr_msg4 *) tptr;
+ msg_type = GET_U_1(msgptr.v4->msg_type);
+ msg_len = GET_BE_U_2(msgptr.v4->msg_len);
+ if ((msg_len >= sizeof (struct olsr_msg4))
+ && (msg_len <= length))
+ msg_len_valid = 1;
+
+ /* infinite loop check */
+ if (msg_type == 0 || msg_len == 0) {
+ return;
+ }
+
+ ND_PRINT("\n\t%s Message (%#04x), originator %s, ttl %u, hop %u"
+ "\n\t vtime %.3fs, msg-seq 0x%04x, length %u%s",
+ tok2str(olsr_msg_values, "Unknown", msg_type),
+ msg_type, GET_IPADDR_STRING(msgptr.v4->originator),
+ GET_U_1(msgptr.v4->ttl),
+ GET_U_1(msgptr.v4->hopcount),
+ ME_TO_DOUBLE(GET_U_1(msgptr.v4->vtime)),
+ GET_BE_U_2(msgptr.v4->msg_seq),
+ msg_len, (msg_len_valid == 0) ? " (invalid)" : "");
+ if (!msg_len_valid) {
+ return;
+ }
+
+ msg_tlen = msg_len - sizeof(struct olsr_msg4);
+ msg_data = tptr + sizeof(struct olsr_msg4);
+ }
+
+ switch (msg_type) {
+ case OLSR_HELLO_MSG:
+ case OLSR_HELLO_LQ_MSG:
+ if (msg_tlen < sizeof(struct olsr_hello))
+ goto trunc;
+ ND_TCHECK_LEN(msg_data, sizeof(struct olsr_hello));
+
+ ptr.hello = (const struct olsr_hello *)msg_data;
+ ND_PRINT("\n\t hello-time %.3fs, MPR willingness %u",
+ ME_TO_DOUBLE(GET_U_1(ptr.hello->htime)),
+ GET_U_1(ptr.hello->will));
+ msg_data += sizeof(struct olsr_hello);
+ msg_tlen -= sizeof(struct olsr_hello);
+
+ while (msg_tlen >= sizeof(struct olsr_hello_link)) {
+ int hello_len_valid = 0;
+
+ /*
+ * link-type.
+ */
+ ND_TCHECK_LEN(msg_data, sizeof(struct olsr_hello_link));
+
+ ptr.hello_link = (const struct olsr_hello_link *)msg_data;
+
+ hello_len = GET_BE_U_2(ptr.hello_link->len);
+ link_type = OLSR_EXTRACT_LINK_TYPE(GET_U_1(ptr.hello_link->link_code));
+ neighbor_type = OLSR_EXTRACT_NEIGHBOR_TYPE(GET_U_1(ptr.hello_link->link_code));
+
+ if ((hello_len <= msg_tlen)
+ && (hello_len >= sizeof(struct olsr_hello_link)))
+ hello_len_valid = 1;
+
+ ND_PRINT("\n\t link-type %s, neighbor-type %s, len %u%s",
+ tok2str(olsr_link_type_values, "Unknown", link_type),
+ tok2str(olsr_neighbor_type_values, "Unknown", neighbor_type),
+ hello_len,
+ (hello_len_valid == 0) ? " (invalid)" : "");
+
+ if (hello_len_valid == 0)
+ break;
+
+ msg_data += sizeof(struct olsr_hello_link);
+ msg_tlen -= sizeof(struct olsr_hello_link);
+ hello_len -= sizeof(struct olsr_hello_link);
+
+ ND_TCHECK_LEN(msg_data, hello_len);
+ if (msg_type == OLSR_HELLO_MSG) {
+ if (olsr_print_neighbor(ndo, msg_data, hello_len) == -1)
+ goto trunc;
+ } else {
+ if (is_ipv6) {
+ if (olsr_print_lq_neighbor6(ndo, msg_data, hello_len) == -1)
+ goto trunc;
+ } else {
+ if (olsr_print_lq_neighbor4(ndo, msg_data, hello_len) == -1)
+ goto trunc;
+ }
+ }
+
+ msg_data += hello_len;
+ msg_tlen -= hello_len;
+ }
+ break;
+
+ case OLSR_TC_MSG:
+ case OLSR_TC_LQ_MSG:
+ if (msg_tlen < sizeof(struct olsr_tc))
+ goto trunc;
+ ND_TCHECK_LEN(msg_data, sizeof(struct olsr_tc));
+
+ ptr.tc = (const struct olsr_tc *)msg_data;
+ ND_PRINT("\n\t advertised neighbor seq 0x%04x",
+ GET_BE_U_2(ptr.tc->ans_seq));
+ msg_data += sizeof(struct olsr_tc);
+ msg_tlen -= sizeof(struct olsr_tc);
+
+ if (msg_type == OLSR_TC_MSG) {
+ if (olsr_print_neighbor(ndo, msg_data, msg_tlen) == -1)
+ goto trunc;
+ } else {
+ if (is_ipv6) {
+ if (olsr_print_lq_neighbor6(ndo, msg_data, msg_tlen) == -1)
+ goto trunc;
+ } else {
+ if (olsr_print_lq_neighbor4(ndo, msg_data, msg_tlen) == -1)
+ goto trunc;
+ }
+ }
+ break;
+
+ case OLSR_MID_MSG:
+ {
+ u_int addr_size = (u_int)sizeof(nd_ipv4);
+
+ if (is_ipv6)
+ addr_size = (u_int)sizeof(nd_ipv6);
+
+ while (msg_tlen >= addr_size) {
+ ND_TCHECK_LEN(msg_data, addr_size);
+ ND_PRINT("\n\t interface address %s",
+ is_ipv6 ? GET_IP6ADDR_STRING(msg_data) :
+ GET_IPADDR_STRING(msg_data));
+
+ msg_data += addr_size;
+ msg_tlen -= addr_size;
+ }
+ break;
+ }
+
+ case OLSR_HNA_MSG:
+ if (is_ipv6)
+ {
+ int i = 0;
+
+ ND_PRINT("\n\t Advertised networks (total %u)",
+ (unsigned int) (msg_tlen / sizeof(struct olsr_hna6)));
+
+ while (msg_tlen >= sizeof(struct olsr_hna6)) {
+ const struct olsr_hna6 *hna6;
+
+ ND_TCHECK_LEN(msg_data, sizeof(struct olsr_hna6));
+
+ hna6 = (const struct olsr_hna6 *)msg_data;
+
+ ND_PRINT("\n\t #%i: %s/%u",
+ i, GET_IP6ADDR_STRING(hna6->network),
+ mask62plen (hna6->mask));
+
+ msg_data += sizeof(struct olsr_hna6);
+ msg_tlen -= sizeof(struct olsr_hna6);
+ }
+ }
+ else
+ {
+ int col = 0;
+
+ ND_PRINT("\n\t Advertised networks (total %u)",
+ (unsigned int) (msg_tlen / sizeof(struct olsr_hna4)));
+
+ while (msg_tlen >= sizeof(struct olsr_hna4)) {
+ ND_TCHECK_LEN(msg_data, sizeof(struct olsr_hna4));
+
+ ptr.hna = (const struct olsr_hna4 *)msg_data;
+
+ /* print 4 prefixes per line */
+ if (!ptr.hna->network[0] && !ptr.hna->network[1] &&
+ !ptr.hna->network[2] && !ptr.hna->network[3] &&
+ !ptr.hna->mask[GW_HNA_PAD] &&
+ ptr.hna->mask[GW_HNA_FLAGS]) {
+ /* smart gateway */
+ ND_PRINT("%sSmart-Gateway:%s%s%s%s%s %u/%u",
+ col == 0 ? "\n\t " : ", ", /* indent */
+ /* sgw */
+ /* LINKSPEED */
+ (ptr.hna->mask[GW_HNA_FLAGS] &
+ GW_HNA_FLAG_LINKSPEED) ? " LINKSPEED" : "",
+ /* IPV4 */
+ (ptr.hna->mask[GW_HNA_FLAGS] &
+ GW_HNA_FLAG_IPV4) ? " IPV4" : "",
+ /* IPV4-NAT */
+ (ptr.hna->mask[GW_HNA_FLAGS] &
+ GW_HNA_FLAG_IPV4_NAT) ? " IPV4-NAT" : "",
+ /* IPV6 */
+ (ptr.hna->mask[GW_HNA_FLAGS] &
+ GW_HNA_FLAG_IPV6) ? " IPV6" : "",
+ /* IPv6PREFIX */
+ (ptr.hna->mask[GW_HNA_FLAGS] &
+ GW_HNA_FLAG_IPV6PREFIX) ? " IPv6-PREFIX" : "",
+ /* uplink */
+ (ptr.hna->mask[GW_HNA_FLAGS] &
+ GW_HNA_FLAG_LINKSPEED) ?
+ deserialize_gw_speed(ptr.hna->mask[GW_HNA_UPLINK]) : 0,
+ /* downlink */
+ (ptr.hna->mask[GW_HNA_FLAGS] &
+ GW_HNA_FLAG_LINKSPEED) ?
+ deserialize_gw_speed(ptr.hna->mask[GW_HNA_DOWNLINK]) : 0
+ );
+ } else {
+ /* normal route */
+ ND_PRINT("%s%s/%u",
+ col == 0 ? "\n\t " : ", ",
+ GET_IPADDR_STRING(ptr.hna->network),
+ mask2plen(GET_BE_U_4(ptr.hna->mask)));
+ }
+
+ msg_data += sizeof(struct olsr_hna4);
+ msg_tlen -= sizeof(struct olsr_hna4);
+
+ col = (col + 1) % 4;
+ }
+ }
+ break;
+
+ case OLSR_NAMESERVICE_MSG:
+ {
+ u_int name_entries;
+ u_int addr_size;
+ int name_entries_valid;
+ u_int i;
+
+ if (msg_tlen < 4)
+ goto trunc;
+
+ name_entries = GET_BE_U_2(msg_data + 2);
+ addr_size = 4;
+ if (is_ipv6)
+ addr_size = 16;
+
+ name_entries_valid = 0;
+ if ((name_entries > 0)
+ && ((name_entries * (4 + addr_size)) <= msg_tlen))
+ name_entries_valid = 1;
+
+ ND_PRINT("\n\t Version %u, Entries %u%s",
+ GET_BE_U_2(msg_data),
+ name_entries, (name_entries_valid == 0) ? " (invalid)" : "");
+
+ if (name_entries_valid == 0)
+ break;
+
+ msg_data += 4;
+ msg_tlen -= 4;
+
+ for (i = 0; i < name_entries; i++) {
+ int name_entry_len_valid = 0;
+
+ if (msg_tlen < 4)
+ break;
+
+ name_entry_type = GET_BE_U_2(msg_data);
+ name_entry_len = GET_BE_U_2(msg_data + 2);
+
+ msg_data += 4;
+ msg_tlen -= 4;
+
+ if ((name_entry_len > 0) && ((addr_size + name_entry_len) <= msg_tlen))
+ name_entry_len_valid = 1;
+
+ ND_PRINT("\n\t #%u: type %#06x, length %u%s",
+ (unsigned int) i, name_entry_type,
+ name_entry_len, (name_entry_len_valid == 0) ? " (invalid)" : "");
+
+ if (name_entry_len_valid == 0)
+ break;
+
+ /* 32-bit alignment */
+ name_entry_padding = 0;
+ if (name_entry_len%4 != 0)
+ name_entry_padding = 4-(name_entry_len%4);
+
+ if (msg_tlen < addr_size + name_entry_len + name_entry_padding)
+ goto trunc;
+
+ ND_TCHECK_LEN(msg_data,
+ addr_size + name_entry_len + name_entry_padding);
+
+ if (is_ipv6)
+ ND_PRINT(", address %s, name \"",
+ GET_IP6ADDR_STRING(msg_data));
+ else
+ ND_PRINT(", address %s, name \"",
+ GET_IPADDR_STRING(msg_data));
+ (void)nd_printn(ndo, msg_data + addr_size, name_entry_len, NULL);
+ ND_PRINT("\"");
+
+ msg_data += addr_size + name_entry_len + name_entry_padding;
+ msg_tlen -= addr_size + name_entry_len + name_entry_padding;
+ } /* for (i = 0; i < name_entries; i++) */
+ break;
+ } /* case OLSR_NAMESERVICE_MSG */
+
+ /*
+ * FIXME those are the defined messages that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+ case OLSR_POWERINFO_MSG:
+ default:
+ print_unknown_data(ndo, msg_data, "\n\t ", msg_tlen);
+ break;
+ } /* switch (msg_type) */
+ tptr += msg_len;
+ } /* while (tptr < (pptr+length)) */
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-openflow-1.0.c b/print-openflow-1.0.c
new file mode 100644
index 0000000..d2cc5b9
--- /dev/null
+++ b/print-openflow-1.0.c
@@ -0,0 +1,2287 @@
+/*
+ * This module implements decoding of OpenFlow protocol version 1.0 (wire
+ * protocol 0x01). The decoder implements terse (default), detailed (-v) and
+ * full (-vv) output formats and, as much as each format implies, detects and
+ * tries to work around sizing anomalies inside the messages. The decoder marks
+ * up bogus values of selected message fields and decodes partially captured
+ * messages up to the snapshot end. It is based on the specification below:
+ *
+ * [OF10] https://www.opennetworking.org/wp-content/uploads/2013/04/openflow-spec-v1.0.0.pdf
+ *
+ * Most functions in this file take the following arguments:
+ * * cp -- the pointer to the first octet to decode
+ * * len -- the declared length of the structure to decode
+ * The convention is that a printer function returns iff the given structure is
+ * completely within the packet buffer; otherwise it processes the part that is
+ * within the buffer, sooner of later takes the "truncated packet" shortcut via
+ * longjmp() and never returns. With that in mind, the function may return
+ * without printing the structure completely if it is invalid or the ndo_vflag
+ * value is not high enough. This way the calling function can try to decode
+ * the next data item.
+ *
+ * Decoding of Ethernet frames nested in OFPT_PACKET_IN and OFPT_PACKET_OUT
+ * messages is done only when the verbosity level set by command-line argument
+ * is "-vvv" or higher. In that case the verbosity level is temporarily
+ * decremented by 3 during the nested frame decoding. For example, running
+ * tcpdump with "-vvvv" will do full decoding of OpenFlow and "-v" decoding of
+ * the nested frames.
+ *
+ * Partial decoding of Big Switch Networks vendor extensions is done after the
+ * oftest (OpenFlow Testing Framework) and Loxigen (library generator) source
+ * code.
+ *
+ *
+ * Copyright (c) 2013 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: OpenFlow protocol version 1.0 printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+#include "ipproto.h"
+#include "oui.h"
+#include "openflow.h"
+
+
+#define OFPT_HELLO 0x00
+#define OFPT_ERROR 0x01
+#define OFPT_ECHO_REQUEST 0x02
+#define OFPT_ECHO_REPLY 0x03
+#define OFPT_VENDOR 0x04
+#define OFPT_FEATURES_REQUEST 0x05
+#define OFPT_FEATURES_REPLY 0x06
+#define OFPT_GET_CONFIG_REQUEST 0x07
+#define OFPT_GET_CONFIG_REPLY 0x08
+#define OFPT_SET_CONFIG 0x09
+#define OFPT_PACKET_IN 0x0a
+#define OFPT_FLOW_REMOVED 0x0b
+#define OFPT_PORT_STATUS 0x0c
+#define OFPT_PACKET_OUT 0x0d
+#define OFPT_FLOW_MOD 0x0e
+#define OFPT_PORT_MOD 0x0f
+#define OFPT_STATS_REQUEST 0x10
+#define OFPT_STATS_REPLY 0x11
+#define OFPT_BARRIER_REQUEST 0x12
+#define OFPT_BARRIER_REPLY 0x13
+#define OFPT_QUEUE_GET_CONFIG_REQUEST 0x14
+#define OFPT_QUEUE_GET_CONFIG_REPLY 0x15
+#define OFPT_MAX OFPT_QUEUE_GET_CONFIG_REPLY
+
+#define OFPPC_PORT_DOWN (1U <<0)
+#define OFPPC_NO_STP (1U <<1)
+#define OFPPC_NO_RECV (1U <<2)
+#define OFPPC_NO_RECV_STP (1U <<3)
+#define OFPPC_NO_FLOOD (1U <<4)
+#define OFPPC_NO_FWD (1U <<5)
+#define OFPPC_NO_PACKET_IN (1U <<6)
+static const struct tok ofppc_bm[] = {
+ { OFPPC_PORT_DOWN, "PORT_DOWN" },
+ { OFPPC_NO_STP, "NO_STP" },
+ { OFPPC_NO_RECV, "NO_RECV" },
+ { OFPPC_NO_RECV_STP, "NO_RECV_STP" },
+ { OFPPC_NO_FLOOD, "NO_FLOOD" },
+ { OFPPC_NO_FWD, "NO_FWD" },
+ { OFPPC_NO_PACKET_IN, "NO_PACKET_IN" },
+ { 0, NULL }
+};
+#define OFPPC_U (~(OFPPC_PORT_DOWN | OFPPC_NO_STP | OFPPC_NO_RECV | \
+ OFPPC_NO_RECV_STP | OFPPC_NO_FLOOD | OFPPC_NO_FWD | \
+ OFPPC_NO_PACKET_IN))
+
+/*
+ * [OF10] lists all FPPS_ constants in one enum, but they mean a 1-bit bitmap
+ * in the least significant octet and a 2-bit code point in the next octet.
+ * Remember to mix or to separate these two parts as the context requires.
+ */
+#define OFPPS_LINK_DOWN (1U << 0) /* bitmap */
+#define OFPPS_STP_LISTEN (0U << 8) /* code point */
+#define OFPPS_STP_LEARN (1U << 8) /* code point */
+#define OFPPS_STP_FORWARD (2U << 8) /* code point */
+#define OFPPS_STP_BLOCK (3U << 8) /* code point */
+#define OFPPS_STP_MASK (3U << 8) /* code point bitmask */
+static const struct tok ofpps_stp_str[] = {
+ { OFPPS_STP_LISTEN, "STP_LISTEN" },
+ { OFPPS_STP_LEARN, "STP_LEARN" },
+ { OFPPS_STP_FORWARD, "STP_FORWARD" },
+ { OFPPS_STP_BLOCK, "STP_BLOCK" },
+ { 0, NULL }
+};
+#define OFPPS_U (~(OFPPS_LINK_DOWN | OFPPS_STP_LISTEN | OFPPS_STP_LEARN | \
+ OFPPS_STP_FORWARD | OFPPS_STP_BLOCK))
+
+#define OFPP_MAX 0xff00U
+#define OFPP_IN_PORT 0xfff8U
+#define OFPP_TABLE 0xfff9U
+#define OFPP_NORMAL 0xfffaU
+#define OFPP_FLOOD 0xfffbU
+#define OFPP_ALL 0xfffcU
+#define OFPP_CONTROLLER 0xfffdU
+#define OFPP_LOCAL 0xfffeU
+#define OFPP_NONE 0xffffU
+static const struct tok ofpp_str[] = {
+ { OFPP_MAX, "MAX" },
+ { OFPP_IN_PORT, "IN_PORT" },
+ { OFPP_TABLE, "TABLE" },
+ { OFPP_NORMAL, "NORMAL" },
+ { OFPP_FLOOD, "FLOOD" },
+ { OFPP_ALL, "ALL" },
+ { OFPP_CONTROLLER, "CONTROLLER" },
+ { OFPP_LOCAL, "LOCAL" },
+ { OFPP_NONE, "NONE" },
+ { 0, NULL }
+};
+
+#define OFPPF_10MB_HD (1U << 0)
+#define OFPPF_10MB_FD (1U << 1)
+#define OFPPF_100MB_HD (1U << 2)
+#define OFPPF_100MB_FD (1U << 3)
+#define OFPPF_1GB_HD (1U << 4)
+#define OFPPF_1GB_FD (1U << 5)
+#define OFPPF_10GB_FD (1U << 6)
+#define OFPPF_COPPER (1U << 7)
+#define OFPPF_FIBER (1U << 8)
+#define OFPPF_AUTONEG (1U << 9)
+#define OFPPF_PAUSE (1U <<10)
+#define OFPPF_PAUSE_ASYM (1U <<11)
+static const struct tok ofppf_bm[] = {
+ { OFPPF_10MB_HD, "10MB_HD" },
+ { OFPPF_10MB_FD, "10MB_FD" },
+ { OFPPF_100MB_HD, "100MB_HD" },
+ { OFPPF_100MB_FD, "100MB_FD" },
+ { OFPPF_1GB_HD, "1GB_HD" },
+ { OFPPF_1GB_FD, "1GB_FD" },
+ { OFPPF_10GB_FD, "10GB_FD" },
+ { OFPPF_COPPER, "COPPER" },
+ { OFPPF_FIBER, "FIBER" },
+ { OFPPF_AUTONEG, "AUTONEG" },
+ { OFPPF_PAUSE, "PAUSE" },
+ { OFPPF_PAUSE_ASYM, "PAUSE_ASYM" },
+ { 0, NULL }
+};
+#define OFPPF_U (~(OFPPF_10MB_HD | OFPPF_10MB_FD | OFPPF_100MB_HD | \
+ OFPPF_100MB_FD | OFPPF_1GB_HD | OFPPF_1GB_FD | \
+ OFPPF_10GB_FD | OFPPF_COPPER | OFPPF_FIBER | \
+ OFPPF_AUTONEG | OFPPF_PAUSE | OFPPF_PAUSE_ASYM))
+
+#define OFPQT_NONE 0x0000
+#define OFPQT_MIN_RATE 0x0001
+static const struct tok ofpqt_str[] = {
+ { OFPQT_NONE, "NONE" },
+ { OFPQT_MIN_RATE, "MIN_RATE" },
+ { 0, NULL }
+};
+
+#define OFPFW_IN_PORT (1U <<0)
+#define OFPFW_DL_VLAN (1U <<1)
+#define OFPFW_DL_SRC (1U <<2)
+#define OFPFW_DL_DST (1U <<3)
+#define OFPFW_DL_TYPE (1U <<4)
+#define OFPFW_NW_PROTO (1U <<5)
+#define OFPFW_TP_SRC (1U <<6)
+#define OFPFW_TP_DST (1U <<7)
+#define OFPFW_NW_SRC_SHIFT 8
+#define OFPFW_NW_SRC_BITS 6
+#define OFPFW_NW_SRC_MASK (((1U <<OFPFW_NW_SRC_BITS) - 1) << OFPFW_NW_SRC_SHIFT)
+#define OFPFW_NW_DST_SHIFT 14
+#define OFPFW_NW_DST_BITS 6
+#define OFPFW_NW_DST_MASK (((1U <<OFPFW_NW_DST_BITS) - 1) << OFPFW_NW_DST_SHIFT)
+#define OFPFW_DL_VLAN_PCP (1U <<20)
+#define OFPFW_NW_TOS (1U <<21)
+#define OFPFW_ALL ((1U <<22) - 1)
+static const struct tok ofpfw_bm[] = {
+ { OFPFW_IN_PORT, "IN_PORT" },
+ { OFPFW_DL_VLAN, "DL_VLAN" },
+ { OFPFW_DL_SRC, "DL_SRC" },
+ { OFPFW_DL_DST, "DL_DST" },
+ { OFPFW_DL_TYPE, "DL_TYPE" },
+ { OFPFW_NW_PROTO, "NW_PROTO" },
+ { OFPFW_TP_SRC, "TP_SRC" },
+ { OFPFW_TP_DST, "TP_DST" },
+ { OFPFW_DL_VLAN_PCP, "DL_VLAN_PCP" },
+ { OFPFW_NW_TOS, "NW_TOS" },
+ { 0, NULL }
+};
+/* The above array does not include bits 8~13 (OFPFW_NW_SRC_*) and 14~19
+ * (OFPFW_NW_DST_*), which are not a part of the bitmap and require decoding
+ * other than that of tok2str(). The macro below includes these bits such that
+ * they are not reported as bogus in the decoding. */
+#define OFPFW_U (~(OFPFW_ALL))
+
+#define OFPAT_OUTPUT 0x0000U
+#define OFPAT_SET_VLAN_VID 0x0001U
+#define OFPAT_SET_VLAN_PCP 0x0002U
+#define OFPAT_STRIP_VLAN 0x0003U
+#define OFPAT_SET_DL_SRC 0x0004U
+#define OFPAT_SET_DL_DST 0x0005U
+#define OFPAT_SET_NW_SRC 0x0006U
+#define OFPAT_SET_NW_DST 0x0007U
+#define OFPAT_SET_NW_TOS 0x0008U
+#define OFPAT_SET_TP_SRC 0x0009U
+#define OFPAT_SET_TP_DST 0x000aU
+#define OFPAT_ENQUEUE 0x000bU
+#define OFPAT_VENDOR 0xffffU
+static const struct tok ofpat_str[] = {
+ { OFPAT_OUTPUT, "OUTPUT" },
+ { OFPAT_SET_VLAN_VID, "SET_VLAN_VID" },
+ { OFPAT_SET_VLAN_PCP, "SET_VLAN_PCP" },
+ { OFPAT_STRIP_VLAN, "STRIP_VLAN" },
+ { OFPAT_SET_DL_SRC, "SET_DL_SRC" },
+ { OFPAT_SET_DL_DST, "SET_DL_DST" },
+ { OFPAT_SET_NW_SRC, "SET_NW_SRC" },
+ { OFPAT_SET_NW_DST, "SET_NW_DST" },
+ { OFPAT_SET_NW_TOS, "SET_NW_TOS" },
+ { OFPAT_SET_TP_SRC, "SET_TP_SRC" },
+ { OFPAT_SET_TP_DST, "SET_TP_DST" },
+ { OFPAT_ENQUEUE, "ENQUEUE" },
+ { OFPAT_VENDOR, "VENDOR" },
+ { 0, NULL }
+};
+
+/* bit-shifted, w/o vendor action */
+static const struct tok ofpat_bm[] = {
+ { 1U <<OFPAT_OUTPUT, "OUTPUT" },
+ { 1U <<OFPAT_SET_VLAN_VID, "SET_VLAN_VID" },
+ { 1U <<OFPAT_SET_VLAN_PCP, "SET_VLAN_PCP" },
+ { 1U <<OFPAT_STRIP_VLAN, "STRIP_VLAN" },
+ { 1U <<OFPAT_SET_DL_SRC, "SET_DL_SRC" },
+ { 1U <<OFPAT_SET_DL_DST, "SET_DL_DST" },
+ { 1U <<OFPAT_SET_NW_SRC, "SET_NW_SRC" },
+ { 1U <<OFPAT_SET_NW_DST, "SET_NW_DST" },
+ { 1U <<OFPAT_SET_NW_TOS, "SET_NW_TOS" },
+ { 1U <<OFPAT_SET_TP_SRC, "SET_TP_SRC" },
+ { 1U <<OFPAT_SET_TP_DST, "SET_TP_DST" },
+ { 1U <<OFPAT_ENQUEUE, "ENQUEUE" },
+ { 0, NULL }
+};
+#define OFPAT_U (~(1U <<OFPAT_OUTPUT | 1U <<OFPAT_SET_VLAN_VID | \
+ 1U <<OFPAT_SET_VLAN_PCP | 1U <<OFPAT_STRIP_VLAN | \
+ 1U <<OFPAT_SET_DL_SRC | 1U <<OFPAT_SET_DL_DST | \
+ 1U <<OFPAT_SET_NW_SRC | 1U <<OFPAT_SET_NW_DST | \
+ 1U <<OFPAT_SET_NW_TOS | 1U <<OFPAT_SET_TP_SRC | \
+ 1U <<OFPAT_SET_TP_DST | 1U <<OFPAT_ENQUEUE))
+
+#define OFPC_FLOW_STATS (1U <<0)
+#define OFPC_TABLE_STATS (1U <<1)
+#define OFPC_PORT_STATS (1U <<2)
+#define OFPC_STP (1U <<3)
+#define OFPC_RESERVED (1U <<4)
+#define OFPC_IP_REASM (1U <<5)
+#define OFPC_QUEUE_STATS (1U <<6)
+#define OFPC_ARP_MATCH_IP (1U <<7)
+static const struct tok ofp_capabilities_bm[] = {
+ { OFPC_FLOW_STATS, "FLOW_STATS" },
+ { OFPC_TABLE_STATS, "TABLE_STATS" },
+ { OFPC_PORT_STATS, "PORT_STATS" },
+ { OFPC_STP, "STP" },
+ { OFPC_RESERVED, "RESERVED" }, /* not in the mask below */
+ { OFPC_IP_REASM, "IP_REASM" },
+ { OFPC_QUEUE_STATS, "QUEUE_STATS" },
+ { OFPC_ARP_MATCH_IP, "ARP_MATCH_IP" },
+ { 0, NULL }
+};
+#define OFPCAP_U (~(OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
+ OFPC_STP | OFPC_IP_REASM | OFPC_QUEUE_STATS | \
+ OFPC_ARP_MATCH_IP))
+
+#define OFPC_FRAG_NORMAL 0x0000U
+#define OFPC_FRAG_DROP 0x0001U
+#define OFPC_FRAG_REASM 0x0002U
+#define OFPC_FRAG_MASK 0x0003U
+static const struct tok ofp_config_str[] = {
+ { OFPC_FRAG_NORMAL, "FRAG_NORMAL" },
+ { OFPC_FRAG_DROP, "FRAG_DROP" },
+ { OFPC_FRAG_REASM, "FRAG_REASM" },
+ { 0, NULL }
+};
+
+#define OFPFC_ADD 0x0000U
+#define OFPFC_MODIFY 0x0001U
+#define OFPFC_MODIFY_STRICT 0x0002U
+#define OFPFC_DELETE 0x0003U
+#define OFPFC_DELETE_STRICT 0x0004U
+static const struct tok ofpfc_str[] = {
+ { OFPFC_ADD, "ADD" },
+ { OFPFC_MODIFY, "MODIFY" },
+ { OFPFC_MODIFY_STRICT, "MODIFY_STRICT" },
+ { OFPFC_DELETE, "DELETE" },
+ { OFPFC_DELETE_STRICT, "DELETE_STRICT" },
+ { 0, NULL }
+};
+
+static const struct tok bufferid_str[] = {
+ { 0xffffffff, "NONE" },
+ { 0, NULL }
+};
+
+#define OFPFF_SEND_FLOW_REM (1U <<0)
+#define OFPFF_CHECK_OVERLAP (1U <<1)
+#define OFPFF_EMERG (1U <<2)
+static const struct tok ofpff_bm[] = {
+ { OFPFF_SEND_FLOW_REM, "SEND_FLOW_REM" },
+ { OFPFF_CHECK_OVERLAP, "CHECK_OVERLAP" },
+ { OFPFF_EMERG, "EMERG" },
+ { 0, NULL }
+};
+#define OFPFF_U (~(OFPFF_SEND_FLOW_REM | OFPFF_CHECK_OVERLAP | OFPFF_EMERG))
+
+#define OFPST_DESC 0x0000U
+#define OFPST_FLOW 0x0001U
+#define OFPST_AGGREGATE 0x0002U
+#define OFPST_TABLE 0x0003U
+#define OFPST_PORT 0x0004U
+#define OFPST_QUEUE 0x0005U
+#define OFPST_VENDOR 0xffffU
+static const struct tok ofpst_str[] = {
+ { OFPST_DESC, "DESC" },
+ { OFPST_FLOW, "FLOW" },
+ { OFPST_AGGREGATE, "AGGREGATE" },
+ { OFPST_TABLE, "TABLE" },
+ { OFPST_PORT, "PORT" },
+ { OFPST_QUEUE, "QUEUE" },
+ { OFPST_VENDOR, "VENDOR" },
+ { 0, NULL }
+};
+
+static const struct tok tableid_str[] = {
+ { 0xfeU, "EMERG" },
+ { 0xffU, "ALL" },
+ { 0, NULL }
+};
+
+#define OFPQ_ALL 0xffffffffU
+static const struct tok ofpq_str[] = {
+ { OFPQ_ALL, "ALL" },
+ { 0, NULL }
+};
+
+#define OFPSF_REPLY_MORE 0x0001U
+static const struct tok ofpsf_reply_bm[] = {
+ { OFPSF_REPLY_MORE, "MORE" },
+ { 0, NULL }
+};
+#define OFPSF_REPLY_U (~(OFPSF_REPLY_MORE))
+
+#define OFPR_NO_MATCH 0x00U
+#define OFPR_ACTION 0x01U
+static const struct tok ofpr_str[] = {
+ { OFPR_NO_MATCH, "NO_MATCH" },
+ { OFPR_ACTION, "ACTION" },
+ { 0, NULL }
+};
+
+#define OFPRR_IDLE_TIMEOUT 0x00U
+#define OFPRR_HARD_TIMEOUT 0x01U
+#define OFPRR_DELETE 0x02U
+static const struct tok ofprr_str[] = {
+ { OFPRR_IDLE_TIMEOUT, "IDLE_TIMEOUT" },
+ { OFPRR_HARD_TIMEOUT, "HARD_TIMEOUT" },
+ { OFPRR_DELETE, "DELETE" },
+ { 0, NULL }
+};
+
+#define OFPPR_ADD 0x00U
+#define OFPPR_DELETE 0x01U
+#define OFPPR_MODIFY 0x02U
+static const struct tok ofppr_str[] = {
+ { OFPPR_ADD, "ADD" },
+ { OFPPR_DELETE, "DELETE" },
+ { OFPPR_MODIFY, "MODIFY" },
+ { 0, NULL }
+};
+
+#define OFPET_HELLO_FAILED 0x0000U
+#define OFPET_BAD_REQUEST 0x0001U
+#define OFPET_BAD_ACTION 0x0002U
+#define OFPET_FLOW_MOD_FAILED 0x0003U
+#define OFPET_PORT_MOD_FAILED 0x0004U
+#define OFPET_QUEUE_OP_FAILED 0x0005U
+static const struct tok ofpet_str[] = {
+ { OFPET_HELLO_FAILED, "HELLO_FAILED" },
+ { OFPET_BAD_REQUEST, "BAD_REQUEST" },
+ { OFPET_BAD_ACTION, "BAD_ACTION" },
+ { OFPET_FLOW_MOD_FAILED, "FLOW_MOD_FAILED" },
+ { OFPET_PORT_MOD_FAILED, "PORT_MOD_FAILED" },
+ { OFPET_QUEUE_OP_FAILED, "QUEUE_OP_FAILED" },
+ { 0, NULL }
+};
+
+#define OFPHFC_INCOMPATIBLE 0x0000U
+#define OFPHFC_EPERM 0x0001U
+static const struct tok ofphfc_str[] = {
+ { OFPHFC_INCOMPATIBLE, "INCOMPATIBLE" },
+ { OFPHFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPBRC_BAD_VERSION 0x0000U
+#define OFPBRC_BAD_TYPE 0x0001U
+#define OFPBRC_BAD_STAT 0x0002U
+#define OFPBRC_BAD_VENDOR 0x0003U
+#define OFPBRC_BAD_SUBTYPE 0x0004U
+#define OFPBRC_EPERM 0x0005U
+#define OFPBRC_BAD_LEN 0x0006U
+#define OFPBRC_BUFFER_EMPTY 0x0007U
+#define OFPBRC_BUFFER_UNKNOWN 0x0008U
+static const struct tok ofpbrc_str[] = {
+ { OFPBRC_BAD_VERSION, "BAD_VERSION" },
+ { OFPBRC_BAD_TYPE, "BAD_TYPE" },
+ { OFPBRC_BAD_STAT, "BAD_STAT" },
+ { OFPBRC_BAD_VENDOR, "BAD_VENDOR" },
+ { OFPBRC_BAD_SUBTYPE, "BAD_SUBTYPE" },
+ { OFPBRC_EPERM, "EPERM" },
+ { OFPBRC_BAD_LEN, "BAD_LEN" },
+ { OFPBRC_BUFFER_EMPTY, "BUFFER_EMPTY" },
+ { OFPBRC_BUFFER_UNKNOWN, "BUFFER_UNKNOWN" },
+ { 0, NULL }
+};
+
+#define OFPBAC_BAD_TYPE 0x0000U
+#define OFPBAC_BAD_LEN 0x0001U
+#define OFPBAC_BAD_VENDOR 0x0002U
+#define OFPBAC_BAD_VENDOR_TYPE 0x0003U
+#define OFPBAC_BAD_OUT_PORT 0x0004U
+#define OFPBAC_BAD_ARGUMENT 0x0005U
+#define OFPBAC_EPERM 0x0006U
+#define OFPBAC_TOO_MANY 0x0007U
+#define OFPBAC_BAD_QUEUE 0x0008U
+static const struct tok ofpbac_str[] = {
+ { OFPBAC_BAD_TYPE, "BAD_TYPE" },
+ { OFPBAC_BAD_LEN, "BAD_LEN" },
+ { OFPBAC_BAD_VENDOR, "BAD_VENDOR" },
+ { OFPBAC_BAD_VENDOR_TYPE, "BAD_VENDOR_TYPE" },
+ { OFPBAC_BAD_OUT_PORT, "BAD_OUT_PORT" },
+ { OFPBAC_BAD_ARGUMENT, "BAD_ARGUMENT" },
+ { OFPBAC_EPERM, "EPERM" },
+ { OFPBAC_TOO_MANY, "TOO_MANY" },
+ { OFPBAC_BAD_QUEUE, "BAD_QUEUE" },
+ { 0, NULL }
+};
+
+#define OFPFMFC_ALL_TABLES_FULL 0x0000U
+#define OFPFMFC_OVERLAP 0x0001U
+#define OFPFMFC_EPERM 0x0002U
+#define OFPFMFC_BAD_EMERG_TIMEOUT 0x0003U
+#define OFPFMFC_BAD_COMMAND 0x0004U
+#define OFPFMFC_UNSUPPORTED 0x0005U
+static const struct tok ofpfmfc_str[] = {
+ { OFPFMFC_ALL_TABLES_FULL, "ALL_TABLES_FULL" },
+ { OFPFMFC_OVERLAP, "OVERLAP" },
+ { OFPFMFC_EPERM, "EPERM" },
+ { OFPFMFC_BAD_EMERG_TIMEOUT, "BAD_EMERG_TIMEOUT" },
+ { OFPFMFC_BAD_COMMAND, "BAD_COMMAND" },
+ { OFPFMFC_UNSUPPORTED, "UNSUPPORTED" },
+ { 0, NULL }
+};
+
+#define OFPPMFC_BAD_PORT 0x0000U
+#define OFPPMFC_BAD_HW_ADDR 0x0001U
+static const struct tok ofppmfc_str[] = {
+ { OFPPMFC_BAD_PORT, "BAD_PORT" },
+ { OFPPMFC_BAD_HW_ADDR, "BAD_HW_ADDR" },
+ { 0, NULL }
+};
+
+#define OFPQOFC_BAD_PORT 0x0000U
+#define OFPQOFC_BAD_QUEUE 0x0001U
+#define OFPQOFC_EPERM 0x0002U
+static const struct tok ofpqofc_str[] = {
+ { OFPQOFC_BAD_PORT, "BAD_PORT" },
+ { OFPQOFC_BAD_QUEUE, "BAD_QUEUE" },
+ { OFPQOFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+static const struct uint_tokary of10_ofpet2tokary[] = {
+ { OFPET_HELLO_FAILED, ofphfc_str },
+ { OFPET_BAD_REQUEST, ofpbrc_str },
+ { OFPET_BAD_ACTION, ofpbac_str },
+ { OFPET_FLOW_MOD_FAILED, ofpfmfc_str },
+ { OFPET_PORT_MOD_FAILED, ofppmfc_str },
+ { OFPET_QUEUE_OP_FAILED, ofpqofc_str },
+ /* uint2tokary() does not use array termination. */
+};
+
+/* lengths (fixed or minimal) of particular message types, where not 0 */
+#define OF_SWITCH_CONFIG_FIXLEN (12U - OF_HEADER_FIXLEN)
+#define OF_FEATURES_REPLY_MINLEN (32U - OF_HEADER_FIXLEN)
+#define OF_PORT_STATUS_FIXLEN (64U - OF_HEADER_FIXLEN)
+#define OF_PORT_MOD_FIXLEN (32U - OF_HEADER_FIXLEN)
+#define OF_PACKET_IN_MINLEN (20U - OF_HEADER_FIXLEN) /* with 2 mock octets */
+#define OF_PACKET_OUT_MINLEN (16U - OF_HEADER_FIXLEN)
+#define OF_FLOW_MOD_MINLEN (72U - OF_HEADER_FIXLEN)
+#define OF_FLOW_REMOVED_FIXLEN (88U - OF_HEADER_FIXLEN)
+#define OF_ERROR_MSG_MINLEN (12U - OF_HEADER_FIXLEN)
+#define OF_STATS_REQUEST_MINLEN (12U - OF_HEADER_FIXLEN)
+#define OF_STATS_REPLY_MINLEN (12U - OF_HEADER_FIXLEN)
+#define OF_VENDOR_MINLEN (12U - OF_HEADER_FIXLEN)
+#define OF_QUEUE_GET_CONFIG_REQUEST_FIXLEN (12U - OF_HEADER_FIXLEN)
+#define OF_QUEUE_GET_CONFIG_REPLY_MINLEN (16U - OF_HEADER_FIXLEN)
+
+/* lengths (fixed or minimal) of particular protocol structures */
+#define OF_PHY_PORT_FIXLEN 48
+#define OF_ACTION_MINLEN 8
+#define OF_MATCH_FIXLEN 40
+#define OF_DESC_STATS_REPLY_FIXLEN 1056
+#define OF_FLOW_STATS_REQUEST_FIXLEN 44
+#define OF_FLOW_STATS_REPLY_MINLEN 88
+#define OF_AGGREGATE_STATS_REPLY_FIXLEN 24
+#define OF_TABLE_STATS_REPLY_FIXLEN 64
+#define OF_PORT_STATS_REQUEST_FIXLEN 8
+#define OF_PORT_STATS_REPLY_FIXLEN 104
+#define OF_QUEUE_PROP_MINLEN 8
+#define OF_QUEUE_PROP_MIN_RATE_FIXLEN 16
+#define OF_PACKET_QUEUE_MINLEN 8
+#define OF_QUEUE_STATS_REQUEST_FIXLEN 8
+#define OF_QUEUE_STATS_REPLY_FIXLEN 32
+
+/* miscellaneous constants from [OF10] */
+#define OFP_MAX_TABLE_NAME_LEN 32
+#define OFP_MAX_PORT_NAME_LEN 16
+#define DESC_STR_LEN 256
+#define SERIAL_NUM_LEN 32
+#define OFP_VLAN_NONE 0xffffU
+
+/* vendor extensions */
+#define BSN_SET_IP_MASK 0
+#define BSN_GET_IP_MASK_REQUEST 1
+#define BSN_GET_IP_MASK_REPLY 2
+#define BSN_SET_MIRRORING 3
+#define BSN_GET_MIRRORING_REQUEST 4
+#define BSN_GET_MIRRORING_REPLY 5
+#define BSN_SHELL_COMMAND 6
+#define BSN_SHELL_OUTPUT 7
+#define BSN_SHELL_STATUS 8
+#define BSN_GET_INTERFACES_REQUEST 9
+#define BSN_GET_INTERFACES_REPLY 10
+#define BSN_SET_PKTIN_SUPPRESSION_REQUEST 11
+#define BSN_SET_L2_TABLE_REQUEST 12
+#define BSN_GET_L2_TABLE_REQUEST 13
+#define BSN_GET_L2_TABLE_REPLY 14
+#define BSN_VIRTUAL_PORT_CREATE_REQUEST 15
+#define BSN_VIRTUAL_PORT_CREATE_REPLY 16
+#define BSN_VIRTUAL_PORT_REMOVE_REQUEST 17
+#define BSN_BW_ENABLE_SET_REQUEST 18
+#define BSN_BW_ENABLE_GET_REQUEST 19
+#define BSN_BW_ENABLE_GET_REPLY 20
+#define BSN_BW_CLEAR_DATA_REQUEST 21
+#define BSN_BW_CLEAR_DATA_REPLY 22
+#define BSN_BW_ENABLE_SET_REPLY 23
+#define BSN_SET_L2_TABLE_REPLY 24
+#define BSN_SET_PKTIN_SUPPRESSION_REPLY 25
+#define BSN_VIRTUAL_PORT_REMOVE_REPLY 26
+#define BSN_HYBRID_GET_REQUEST 27
+#define BSN_HYBRID_GET_REPLY 28
+ /* 29 */
+ /* 30 */
+#define BSN_PDU_TX_REQUEST 31
+#define BSN_PDU_TX_REPLY 32
+#define BSN_PDU_RX_REQUEST 33
+#define BSN_PDU_RX_REPLY 34
+#define BSN_PDU_RX_TIMEOUT 35
+
+static const struct tok bsn_subtype_str[] = {
+ { BSN_SET_IP_MASK, "SET_IP_MASK" },
+ { BSN_GET_IP_MASK_REQUEST, "GET_IP_MASK_REQUEST" },
+ { BSN_GET_IP_MASK_REPLY, "GET_IP_MASK_REPLY" },
+ { BSN_SET_MIRRORING, "SET_MIRRORING" },
+ { BSN_GET_MIRRORING_REQUEST, "GET_MIRRORING_REQUEST" },
+ { BSN_GET_MIRRORING_REPLY, "GET_MIRRORING_REPLY" },
+ { BSN_SHELL_COMMAND, "SHELL_COMMAND" },
+ { BSN_SHELL_OUTPUT, "SHELL_OUTPUT" },
+ { BSN_SHELL_STATUS, "SHELL_STATUS" },
+ { BSN_GET_INTERFACES_REQUEST, "GET_INTERFACES_REQUEST" },
+ { BSN_GET_INTERFACES_REPLY, "GET_INTERFACES_REPLY" },
+ { BSN_SET_PKTIN_SUPPRESSION_REQUEST, "SET_PKTIN_SUPPRESSION_REQUEST" },
+ { BSN_SET_L2_TABLE_REQUEST, "SET_L2_TABLE_REQUEST" },
+ { BSN_GET_L2_TABLE_REQUEST, "GET_L2_TABLE_REQUEST" },
+ { BSN_GET_L2_TABLE_REPLY, "GET_L2_TABLE_REPLY" },
+ { BSN_VIRTUAL_PORT_CREATE_REQUEST, "VIRTUAL_PORT_CREATE_REQUEST" },
+ { BSN_VIRTUAL_PORT_CREATE_REPLY, "VIRTUAL_PORT_CREATE_REPLY" },
+ { BSN_VIRTUAL_PORT_REMOVE_REQUEST, "VIRTUAL_PORT_REMOVE_REQUEST" },
+ { BSN_BW_ENABLE_SET_REQUEST, "BW_ENABLE_SET_REQUEST" },
+ { BSN_BW_ENABLE_GET_REQUEST, "BW_ENABLE_GET_REQUEST" },
+ { BSN_BW_ENABLE_GET_REPLY, "BW_ENABLE_GET_REPLY" },
+ { BSN_BW_CLEAR_DATA_REQUEST, "BW_CLEAR_DATA_REQUEST" },
+ { BSN_BW_CLEAR_DATA_REPLY, "BW_CLEAR_DATA_REPLY" },
+ { BSN_BW_ENABLE_SET_REPLY, "BW_ENABLE_SET_REPLY" },
+ { BSN_SET_L2_TABLE_REPLY, "SET_L2_TABLE_REPLY" },
+ { BSN_SET_PKTIN_SUPPRESSION_REPLY, "SET_PKTIN_SUPPRESSION_REPLY" },
+ { BSN_VIRTUAL_PORT_REMOVE_REPLY, "VIRTUAL_PORT_REMOVE_REPLY" },
+ { BSN_HYBRID_GET_REQUEST, "HYBRID_GET_REQUEST" },
+ { BSN_HYBRID_GET_REPLY, "HYBRID_GET_REPLY" },
+ { BSN_PDU_TX_REQUEST, "PDU_TX_REQUEST" },
+ { BSN_PDU_TX_REPLY, "PDU_TX_REPLY" },
+ { BSN_PDU_RX_REQUEST, "PDU_RX_REQUEST" },
+ { BSN_PDU_RX_REPLY, "PDU_RX_REPLY" },
+ { BSN_PDU_RX_TIMEOUT, "PDU_RX_TIMEOUT" },
+ { 0, NULL }
+};
+
+#define BSN_ACTION_MIRROR 1
+#define BSN_ACTION_SET_TUNNEL_DST 2
+ /* 3 */
+#define BSN_ACTION_CHECKSUM 4
+
+static const struct tok bsn_action_subtype_str[] = {
+ { BSN_ACTION_MIRROR, "MIRROR" },
+ { BSN_ACTION_SET_TUNNEL_DST, "SET_TUNNEL_DST" },
+ { BSN_ACTION_CHECKSUM, "CHECKSUM" },
+ { 0, NULL }
+};
+
+static const struct tok bsn_mirror_copy_stage_str[] = {
+ { 0, "INGRESS" },
+ { 1, "EGRESS" },
+ { 0, NULL },
+};
+
+static const struct tok bsn_onoff_str[] = {
+ { 0, "OFF" },
+ { 1, "ON" },
+ { 0, NULL },
+};
+
+static const char *
+vlan_str(const uint16_t vid)
+{
+ static char buf[sizeof("65535 (bogus)")];
+
+ if (vid == OFP_VLAN_NONE)
+ return "NONE";
+ snprintf(buf, sizeof(buf), "%u%s", vid,
+ (vid > 0 && vid < 0x0fff) ? "" : " (bogus)");
+ return buf;
+}
+
+static const char *
+pcp_str(const uint8_t pcp)
+{
+ static char buf[sizeof("255 (bogus)")];
+ snprintf(buf, sizeof(buf), "%u%s", pcp,
+ pcp <= 7 ? "" : " (bogus)");
+ return buf;
+}
+
+static void
+of10_bsn_message_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint32_t subtype;
+
+ if (len < 4)
+ goto invalid;
+ /* subtype */
+ subtype = GET_BE_U_4(cp);
+ OF_FWD(4);
+ ND_PRINT("\n\t subtype %s", tok2str(bsn_subtype_str, "unknown (0x%08x)", subtype));
+ switch (subtype) {
+ case BSN_GET_IP_MASK_REQUEST:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ * | index | pad |
+ * +---------------+---------------+---------------+---------------+
+ * | pad |
+ * +---------------+---------------+---------------+---------------+
+ *
+ */
+ if (len != 8)
+ goto invalid;
+ /* index */
+ ND_PRINT(", index %u", GET_U_1(cp));
+ OF_FWD(1);
+ /* pad */
+ /* Always the last field, check bounds. */
+ ND_TCHECK_7(cp);
+ break;
+ case BSN_SET_IP_MASK:
+ case BSN_GET_IP_MASK_REPLY:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ * | index | pad |
+ * +---------------+---------------+---------------+---------------+
+ * | mask |
+ * +---------------+---------------+---------------+---------------+
+ *
+ */
+ if (len != 8)
+ goto invalid;
+ /* index */
+ ND_PRINT(", index %u", GET_U_1(cp));
+ OF_FWD(1);
+ /* pad */
+ OF_FWD(3);
+ /* mask */
+ ND_PRINT(", mask %s", GET_IPADDR_STRING(cp));
+ break;
+ case BSN_SET_MIRRORING:
+ case BSN_GET_MIRRORING_REQUEST:
+ case BSN_GET_MIRRORING_REPLY:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ * | report m. p. | pad |
+ * +---------------+---------------+---------------+---------------+
+ *
+ */
+ if (len != 4)
+ goto invalid;
+ /* report_mirror_ports */
+ ND_PRINT(", report_mirror_ports %s",
+ tok2str(bsn_onoff_str, "bogus (%u)", GET_U_1(cp)));
+ OF_FWD(1);
+ /* pad */
+ /* Always the last field, check bounds. */
+ ND_TCHECK_3(cp);
+ break;
+ case BSN_GET_INTERFACES_REQUEST:
+ case BSN_GET_L2_TABLE_REQUEST:
+ case BSN_BW_ENABLE_GET_REQUEST:
+ case BSN_BW_CLEAR_DATA_REQUEST:
+ case BSN_HYBRID_GET_REQUEST:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ *
+ */
+ if (len)
+ goto invalid;
+ break;
+ case BSN_VIRTUAL_PORT_REMOVE_REQUEST:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ * | vport_no |
+ * +---------------+---------------+---------------+---------------+
+ *
+ */
+ if (len != 4)
+ goto invalid;
+ /* vport_no */
+ ND_PRINT(", vport_no %u", GET_BE_U_4(cp));
+ break;
+ case BSN_SHELL_COMMAND:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ * | service |
+ * +---------------+---------------+---------------+---------------+
+ * | data ...
+ * +---------------+---------------+--------
+ *
+ */
+ if (len < 4)
+ goto invalid;
+ /* service */
+ ND_PRINT(", service %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* data */
+ ND_PRINT(", data '");
+ (void)nd_printn(ndo, cp, len, NULL);
+ ND_PRINT("'");
+ break;
+ case BSN_SHELL_OUTPUT:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ * | data ...
+ * +---------------+---------------+--------
+ *
+ */
+ /* already checked that len >= 4 */
+ /* data */
+ ND_PRINT(", data '");
+ (void)nd_printn(ndo, cp, len, NULL);
+ ND_PRINT("'");
+ break;
+ case BSN_SHELL_STATUS:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ * | status |
+ * +---------------+---------------+---------------+---------------+
+ *
+ */
+ if (len != 4)
+ goto invalid;
+ /* status */
+ ND_PRINT(", status 0x%08x", GET_BE_U_4(cp));
+ break;
+ default:
+ ND_TCHECK_LEN(cp, len);
+ }
+ return;
+
+invalid: /* skip the undersized data */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+of10_bsn_actions_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint32_t subtype, vlan_tag;
+
+ if (len < 4)
+ goto invalid;
+ /* subtype */
+ subtype = GET_BE_U_4(cp);
+ OF_FWD(4);
+ ND_PRINT("\n\t subtype %s", tok2str(bsn_action_subtype_str, "unknown (0x%08x)", subtype));
+ switch (subtype) {
+ case BSN_ACTION_MIRROR:
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +---------------+---------------+---------------+---------------+
+ * | subtype |
+ * +---------------+---------------+---------------+---------------+
+ * | dest_port |
+ * +---------------+---------------+---------------+---------------+
+ * | vlan_tag |
+ * +---------------+---------------+---------------+---------------+
+ * | copy_stage | pad |
+ * +---------------+---------------+---------------+---------------+
+ *
+ */
+ if (len != 12)
+ goto invalid;
+ /* dest_port */
+ ND_PRINT(", dest_port %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* vlan_tag */
+ vlan_tag = GET_BE_U_4(cp);
+ OF_FWD(4);
+ switch (vlan_tag >> 16) {
+ case 0:
+ ND_PRINT(", vlan_tag none");
+ break;
+ case ETHERTYPE_8021Q:
+ ND_PRINT(", vlan_tag 802.1Q (%s)", ieee8021q_tci_string(vlan_tag & 0xffff));
+ break;
+ default:
+ ND_PRINT(", vlan_tag unknown (0x%04x)", vlan_tag >> 16);
+ }
+ /* copy_stage */
+ ND_PRINT(", copy_stage %s",
+ tok2str(bsn_mirror_copy_stage_str, "unknown (%u)", GET_U_1(cp)));
+ OF_FWD(1);
+ /* pad */
+ /* Always the last field, check bounds. */
+ ND_TCHECK_3(cp);
+ break;
+ default:
+ ND_TCHECK_LEN(cp, len);
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+of10_vendor_action_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint32_t vendor;
+ void (*decoder)(netdissect_options *, const u_char *, u_int);
+
+ if (len < 4)
+ goto invalid;
+ /* vendor */
+ vendor = GET_BE_U_4(cp);
+ OF_FWD(4);
+ ND_PRINT(", vendor 0x%08x (%s)", vendor, of_vendor_name(vendor));
+ /* data */
+ decoder =
+ vendor == OUI_BSN ? of10_bsn_actions_print :
+ of_data_print;
+ decoder(ndo, cp, len);
+ return;
+
+invalid: /* skip the undersized data */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* [OF10] Section 5.5.4 */
+static void
+of10_vendor_message_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint32_t vendor;
+ void (*decoder)(netdissect_options *, const u_char *, u_int);
+
+ /* vendor */
+ vendor = GET_BE_U_4(cp);
+ OF_FWD(4);
+ ND_PRINT(", vendor 0x%08x (%s)", vendor, of_vendor_name(vendor));
+ /* data */
+ decoder =
+ vendor == OUI_BSN ? of10_bsn_message_print :
+ of_data_print;
+ decoder(ndo, cp, len);
+}
+
+/* Vendor ID is mandatory, data is optional. */
+static void
+of10_vendor_data_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint32_t vendor;
+
+ if (len < 4)
+ goto invalid;
+ /* vendor */
+ vendor = GET_BE_U_4(cp);
+ OF_FWD(4);
+ ND_PRINT(", vendor 0x%08x (%s)", vendor, of_vendor_name(vendor));
+ /* data */
+ of_data_print(ndo, cp, len);
+ return;
+
+invalid: /* skip the undersized data */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+of10_packet_data_print(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ if (len == 0)
+ return;
+ /* data */
+ ND_PRINT("\n\t data (%u octets)", len);
+ if (ndo->ndo_vflag < 3) {
+ ND_TCHECK_LEN(cp, len);
+ return;
+ }
+ ndo->ndo_vflag -= 3;
+ ND_PRINT(", frame decoding below\n");
+ ether_print(ndo, cp, len, ND_BYTES_AVAILABLE_AFTER(cp), NULL, NULL);
+ ndo->ndo_vflag += 3;
+}
+
+/* [OF10] Section 5.2.1 */
+static void
+of10_phy_port_print(netdissect_options *ndo,
+ const u_char *cp)
+{
+ uint32_t state;
+
+ /* port_no */
+ ND_PRINT("\n\t port_no %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ cp += 2;
+ /* hw_addr */
+ ND_PRINT(", hw_addr %s", GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ /* name */
+ ND_PRINT(", name '");
+ nd_printjnp(ndo, cp, OFP_MAX_PORT_NAME_LEN);
+ ND_PRINT("'");
+ cp += OFP_MAX_PORT_NAME_LEN;
+
+ if (ndo->ndo_vflag < 2) {
+ ND_TCHECK_LEN(cp, 24);
+ return;
+ }
+ /* config */
+ ND_PRINT("\n\t config 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppc_bm, GET_BE_U_4(cp), OFPPC_U);
+ cp += 4;
+ /* state */
+ state = GET_BE_U_4(cp);
+ /*
+ * Decode the code point and the single bit separately, but
+ * format the result as a single sequence of comma-separated
+ * strings (see the comments at the OFPPS_ props).
+ */
+ ND_PRINT("\n\t state 0x%08x (%s%s)%s", state,
+ tok2str(ofpps_stp_str, "", state & OFPPS_STP_MASK),
+ state & OFPPS_LINK_DOWN ? ", LINK_DOWN" : "",
+ state & OFPPS_U ? " (bogus)" : "");
+ cp += 4;
+ /* curr */
+ ND_PRINT("\n\t curr 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* advertised */
+ ND_PRINT("\n\t advertised 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* supported */
+ ND_PRINT("\n\t supported 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* peer */
+ ND_PRINT("\n\t peer 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+}
+
+/* [OF10] Section 5.2.2 */
+static void
+of10_queue_props_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ while (len) {
+ uint16_t property, plen;
+ u_char plen_bogus = 0, skip = 0;
+
+ if (len < OF_QUEUE_PROP_MINLEN)
+ goto invalid;
+ /* property */
+ property = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT("\n\t property %s", tok2str(ofpqt_str, "invalid (0x%04x)", property));
+ /* len */
+ plen = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT(", len %u", plen);
+ if (plen < OF_QUEUE_PROP_MINLEN || plen > len + 4)
+ goto invalid;
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(4);
+ /* property-specific constraints and decoding */
+ switch (property) {
+ case OFPQT_NONE:
+ plen_bogus = plen != OF_QUEUE_PROP_MINLEN;
+ break;
+ case OFPQT_MIN_RATE:
+ plen_bogus = plen != OF_QUEUE_PROP_MIN_RATE_FIXLEN;
+ break;
+ default:
+ skip = 1;
+ }
+ if (plen_bogus) {
+ ND_PRINT(" (bogus)");
+ skip = 1;
+ }
+ if (skip) {
+ /*
+ * plen >= OF_QUEUE_PROP_MINLEN
+ * cp is OF_QUEUE_PROP_MINLEN bytes in
+ */
+ OF_CHK_FWD(plen - OF_QUEUE_PROP_MINLEN);
+ continue;
+ }
+ if (property == OFPQT_MIN_RATE) { /* the only case of property decoding */
+ /* rate */
+ uint16_t rate = GET_BE_U_2(cp);
+ OF_FWD(2);
+ if (rate > 1000)
+ ND_PRINT(", rate disabled");
+ else
+ ND_PRINT(", rate %u.%u%%", rate / 10, rate % 10);
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(6);
+ }
+ } /* while */
+ return;
+
+invalid: /* skip the rest of queue properties */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* ibid */
+static void
+of10_queues_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ while (len) {
+ uint16_t desclen;
+
+ if (len < OF_PACKET_QUEUE_MINLEN)
+ goto invalid;
+ /* queue_id */
+ ND_PRINT("\n\t queue_id %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* len */
+ desclen = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT(", len %u", desclen);
+ if (desclen < OF_PACKET_QUEUE_MINLEN || desclen > len + 6)
+ goto invalid;
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(2);
+ /* properties */
+ if (ndo->ndo_vflag >= 2)
+ of10_queue_props_print(ndo, cp, desclen - OF_PACKET_QUEUE_MINLEN);
+ else
+ ND_TCHECK_LEN(cp, desclen - OF_PACKET_QUEUE_MINLEN);
+ OF_FWD(desclen - OF_PACKET_QUEUE_MINLEN);
+ } /* while */
+ return;
+
+invalid: /* skip the rest of queues */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* [OF10] Section 5.2.3 */
+static void
+of10_match_print(netdissect_options *ndo,
+ const char *pfx, const u_char *cp)
+{
+ uint32_t wildcards;
+ uint16_t dl_type;
+ uint8_t nw_proto;
+ u_int nw_bits;
+ const char *field_name;
+
+ /* wildcards */
+ wildcards = GET_BE_U_4(cp);
+ if (wildcards & OFPFW_U)
+ ND_PRINT("%swildcards 0x%08x (bogus)", pfx, wildcards);
+ cp += 4;
+ /* in_port */
+ if (! (wildcards & OFPFW_IN_PORT))
+ ND_PRINT("%smatch in_port %s", pfx,
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ cp += 2;
+ /* dl_src */
+ if (! (wildcards & OFPFW_DL_SRC))
+ ND_PRINT("%smatch dl_src %s", pfx, GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ /* dl_dst */
+ if (! (wildcards & OFPFW_DL_DST))
+ ND_PRINT("%smatch dl_dst %s", pfx, GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ /* dl_vlan */
+ if (! (wildcards & OFPFW_DL_VLAN))
+ ND_PRINT("%smatch dl_vlan %s", pfx, vlan_str(GET_BE_U_2(cp)));
+ cp += 2;
+ /* dl_vlan_pcp */
+ if (! (wildcards & OFPFW_DL_VLAN_PCP))
+ ND_PRINT("%smatch dl_vlan_pcp %s", pfx, pcp_str(GET_U_1(cp)));
+ cp += 1;
+ /* pad1 */
+ cp += 1;
+ /* dl_type */
+ dl_type = GET_BE_U_2(cp);
+ cp += 2;
+ if (! (wildcards & OFPFW_DL_TYPE))
+ ND_PRINT("%smatch dl_type 0x%04x", pfx, dl_type);
+ /* nw_tos */
+ if (! (wildcards & OFPFW_NW_TOS))
+ ND_PRINT("%smatch nw_tos 0x%02x", pfx, GET_U_1(cp));
+ cp += 1;
+ /* nw_proto */
+ nw_proto = GET_U_1(cp);
+ cp += 1;
+ if (! (wildcards & OFPFW_NW_PROTO)) {
+ field_name = ! (wildcards & OFPFW_DL_TYPE) && dl_type == ETHERTYPE_ARP
+ ? "arp_opcode" : "nw_proto";
+ ND_PRINT("%smatch %s %u", pfx, field_name, nw_proto);
+ }
+ /* pad2 */
+ cp += 2;
+ /* nw_src */
+ nw_bits = (wildcards & OFPFW_NW_SRC_MASK) >> OFPFW_NW_SRC_SHIFT;
+ if (nw_bits < 32)
+ ND_PRINT("%smatch nw_src %s/%u", pfx, GET_IPADDR_STRING(cp), 32 - nw_bits);
+ cp += 4;
+ /* nw_dst */
+ nw_bits = (wildcards & OFPFW_NW_DST_MASK) >> OFPFW_NW_DST_SHIFT;
+ if (nw_bits < 32)
+ ND_PRINT("%smatch nw_dst %s/%u", pfx, GET_IPADDR_STRING(cp), 32 - nw_bits);
+ cp += 4;
+ /* tp_src */
+ if (! (wildcards & OFPFW_TP_SRC)) {
+ field_name = ! (wildcards & OFPFW_DL_TYPE) && dl_type == ETHERTYPE_IP
+ && ! (wildcards & OFPFW_NW_PROTO) && nw_proto == IPPROTO_ICMP
+ ? "icmp_type" : "tp_src";
+ ND_PRINT("%smatch %s %u", pfx, field_name, GET_BE_U_2(cp));
+ }
+ cp += 2;
+ /* tp_dst */
+ /* The last unconditional check was at nw_proto, so have an "else" here. */
+ if (! (wildcards & OFPFW_TP_DST)) {
+ field_name = ! (wildcards & OFPFW_DL_TYPE) && dl_type == ETHERTYPE_IP
+ && ! (wildcards & OFPFW_NW_PROTO) && nw_proto == IPPROTO_ICMP
+ ? "icmp_code" : "tp_dst";
+ ND_PRINT("%smatch %s %u", pfx, field_name, GET_BE_U_2(cp));
+ }
+ else
+ ND_TCHECK_2(cp);
+}
+
+/* [OF10] Section 5.2.4 */
+static void
+of10_actions_print(netdissect_options *ndo,
+ const char *pfx, const u_char *cp, u_int len)
+{
+ while (len) {
+ uint16_t type, alen, output_port;
+ u_char alen_bogus = 0, skip = 0;
+
+ if (len < OF_ACTION_MINLEN)
+ goto invalid;
+ /* type */
+ type = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT("%saction type %s", pfx, tok2str(ofpat_str, "invalid (0x%04x)", type));
+ /* length */
+ alen = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT(", len %u", alen);
+ /*
+ * The 4-byte "pad" in the specification is not a field of the
+ * action header, but a placeholder to illustrate the 64-bit
+ * alignment requirement. Action type specific case blocks
+ * below fetch these 4 bytes.
+ */
+
+ /* On action size underrun/overrun skip the rest of the action list. */
+ if (alen < OF_ACTION_MINLEN || alen > len + 4)
+ goto invalid;
+ /*
+ * After validating the basic length constraint it will be safe
+ * to skip the current action if the action size is not valid
+ * for the type or the type is invalid.
+ */
+ switch (type) {
+ case OFPAT_OUTPUT:
+ case OFPAT_SET_VLAN_VID:
+ case OFPAT_SET_VLAN_PCP:
+ case OFPAT_STRIP_VLAN:
+ case OFPAT_SET_NW_SRC:
+ case OFPAT_SET_NW_DST:
+ case OFPAT_SET_NW_TOS:
+ case OFPAT_SET_TP_SRC:
+ case OFPAT_SET_TP_DST:
+ alen_bogus = alen != 8;
+ break;
+ case OFPAT_SET_DL_SRC:
+ case OFPAT_SET_DL_DST:
+ case OFPAT_ENQUEUE:
+ alen_bogus = alen != 16;
+ break;
+ case OFPAT_VENDOR:
+ alen_bogus = alen % 8 != 0; /* already >= 8 so far */
+ break;
+ default:
+ skip = 1;
+ }
+ if (alen_bogus) {
+ ND_PRINT(" (bogus)");
+ skip = 1;
+ }
+ if (skip) {
+ /*
+ * alen >= OF_ACTION_MINLEN
+ * cp is 4 bytes in
+ */
+ OF_CHK_FWD(alen - 4);
+ continue;
+ }
+ /* OK to decode the rest of the action structure */
+ switch (type) {
+ case OFPAT_OUTPUT:
+ /* port */
+ output_port = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT(", port %s", tok2str(ofpp_str, "%u", output_port));
+ /* max_len */
+ if (output_port == OFPP_CONTROLLER)
+ ND_PRINT(", max_len %u", GET_BE_U_2(cp));
+ else
+ ND_TCHECK_2(cp);
+ OF_FWD(2);
+ break;
+ case OFPAT_SET_VLAN_VID:
+ /* vlan_vid */
+ ND_PRINT(", vlan_vid %s", vlan_str(GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(2);
+ break;
+ case OFPAT_SET_VLAN_PCP:
+ /* vlan_pcp */
+ ND_PRINT(", vlan_pcp %s", pcp_str(GET_U_1(cp)));
+ OF_FWD(1);
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(3);
+ break;
+ case OFPAT_SET_DL_SRC:
+ case OFPAT_SET_DL_DST:
+ /* dl_addr */
+ ND_PRINT(", dl_addr %s", GET_ETHERADDR_STRING(cp));
+ OF_FWD(MAC_ADDR_LEN);
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(6);
+ break;
+ case OFPAT_SET_NW_SRC:
+ case OFPAT_SET_NW_DST:
+ /* nw_addr */
+ ND_PRINT(", nw_addr %s", GET_IPADDR_STRING(cp));
+ OF_FWD(4);
+ break;
+ case OFPAT_SET_NW_TOS:
+ /* nw_tos */
+ ND_PRINT(", nw_tos 0x%02x", GET_U_1(cp));
+ OF_FWD(1);
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(3);
+ break;
+ case OFPAT_SET_TP_SRC:
+ case OFPAT_SET_TP_DST:
+ /* nw_tos */
+ ND_PRINT(", tp_port %u", GET_BE_U_2(cp));
+ OF_FWD(2);
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(2);
+ break;
+ case OFPAT_ENQUEUE:
+ /* port */
+ ND_PRINT(", port %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* pad */
+ OF_FWD(6);
+ /* queue_id */
+ ND_PRINT(", queue_id %s",
+ tok2str(ofpq_str, "%u", GET_BE_U_4(cp)));
+ OF_FWD(4);
+ break;
+ case OFPAT_VENDOR:
+ of10_vendor_action_print(ndo, cp, alen - 4);
+ OF_FWD(alen - 4);
+ break;
+ case OFPAT_STRIP_VLAN:
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(4);
+ break;
+ } /* switch */
+ } /* while */
+ return;
+
+invalid: /* skip the rest of actions */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* [OF10] Section 5.3.1 */
+static void
+of10_features_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ /* datapath_id */
+ ND_PRINT("\n\t dpid 0x%016" PRIx64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* n_buffers */
+ ND_PRINT(", n_buffers %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* n_tables */
+ ND_PRINT(", n_tables %u", GET_U_1(cp));
+ OF_FWD(1);
+ /* pad */
+ OF_FWD(3);
+ /* capabilities */
+ ND_PRINT("\n\t capabilities 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofp_capabilities_bm, GET_BE_U_4(cp), OFPCAP_U);
+ OF_FWD(4);
+ /* actions */
+ ND_PRINT("\n\t actions 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofpat_bm, GET_BE_U_4(cp), OFPAT_U);
+ OF_FWD(4);
+ /* ports */
+ while (len) {
+ if (len < OF_PHY_PORT_FIXLEN)
+ goto invalid;
+ of10_phy_port_print(ndo, cp);
+ OF_FWD(OF_PHY_PORT_FIXLEN);
+ }
+ return;
+
+invalid: /* skip the undersized trailing data */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* [OF10] Section 5.3.2 */
+static void
+of10_switch_config_msg_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* flags */
+ ND_PRINT("\n\t flags %s",
+ tok2str(ofp_config_str, "invalid (0x%04x)", GET_BE_U_2(cp)));
+ cp += 2;
+ /* miss_send_len */
+ ND_PRINT(", miss_send_len %u", GET_BE_U_2(cp));
+}
+
+/* [OF10] Section 5.3.3 */
+static void
+of10_flow_mod_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t command;
+
+ /* match */
+ of10_match_print(ndo, "\n\t ", cp);
+ OF_FWD(OF_MATCH_FIXLEN);
+ /* cookie */
+ ND_PRINT("\n\t cookie 0x%016" PRIx64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* command */
+ command = GET_BE_U_2(cp);
+ ND_PRINT(", command %s", tok2str(ofpfc_str, "invalid (0x%04x)", command));
+ OF_FWD(2);
+ /* idle_timeout */
+ if (GET_BE_U_2(cp))
+ ND_PRINT(", idle_timeout %u", GET_BE_U_2(cp));
+ OF_FWD(2);
+ /* hard_timeout */
+ if (GET_BE_U_2(cp))
+ ND_PRINT(", hard_timeout %u", GET_BE_U_2(cp));
+ OF_FWD(2);
+ /* priority */
+ if (GET_BE_U_2(cp))
+ ND_PRINT(", priority %u", GET_BE_U_2(cp));
+ OF_FWD(2);
+ /* buffer_id */
+ if (command == OFPFC_ADD || command == OFPFC_MODIFY ||
+ command == OFPFC_MODIFY_STRICT)
+ ND_PRINT(", buffer_id %s",
+ tok2str(bufferid_str, "0x%08x", GET_BE_U_4(cp)));
+ OF_FWD(4);
+ /* out_port */
+ if (command == OFPFC_DELETE || command == OFPFC_DELETE_STRICT)
+ ND_PRINT(", out_port %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* flags */
+ ND_PRINT(", flags 0x%04x", GET_BE_U_2(cp));
+ of_bitmap_print(ndo, ofpff_bm, GET_BE_U_2(cp), OFPFF_U);
+ OF_FWD(2);
+ /* actions */
+ of10_actions_print(ndo, "\n\t ", cp, len);
+}
+
+/* ibid */
+static void
+of10_port_mod_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* port_no */
+ ND_PRINT("\n\t port_no %s", tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ cp += 2;
+ /* hw_addr */
+ ND_PRINT(", hw_addr %s", GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ /* config */
+ ND_PRINT("\n\t config 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppc_bm, GET_BE_U_4(cp), OFPPC_U);
+ cp += 4;
+ /* mask */
+ ND_PRINT("\n\t mask 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppc_bm, GET_BE_U_4(cp), OFPPC_U);
+ cp += 4;
+ /* advertise */
+ ND_PRINT("\n\t advertise 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* pad */
+ /* Always the last field, check bounds. */
+ ND_TCHECK_4(cp);
+}
+
+/* [OF10] Section 5.3.4 */
+static void
+of10_queue_get_config_request_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* port */
+ ND_PRINT("\n\t port %s", tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ cp += 2;
+ /* pad */
+ /* Always the last field, check bounds. */
+ ND_TCHECK_2(cp);
+}
+
+/* ibid */
+static void
+of10_queue_get_config_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ /* port */
+ ND_PRINT("\n\t port %s", tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(6);
+ /* queues */
+ of10_queues_print(ndo, cp, len);
+}
+
+/* [OF10] Section 5.3.5 */
+static void
+of10_stats_request_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t type;
+
+ /* type */
+ type = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT("\n\t type %s", tok2str(ofpst_str, "invalid (0x%04x)", type));
+ /* flags */
+ ND_PRINT(", flags 0x%04x", GET_BE_U_2(cp));
+ if (GET_BE_U_2(cp))
+ ND_PRINT(" (bogus)");
+ OF_FWD(2);
+ /* type-specific body of one of fixed lengths */
+ switch(type) {
+ case OFPST_DESC:
+ case OFPST_TABLE:
+ if (len)
+ goto invalid;
+ return;
+ case OFPST_FLOW:
+ case OFPST_AGGREGATE:
+ if (len != OF_FLOW_STATS_REQUEST_FIXLEN)
+ goto invalid;
+ /* match */
+ of10_match_print(ndo, "\n\t ", cp);
+ OF_FWD(OF_MATCH_FIXLEN);
+ /* table_id */
+ ND_PRINT("\n\t table_id %s",
+ tok2str(tableid_str, "%u", GET_U_1(cp)));
+ OF_FWD(1);
+ /* pad */
+ OF_FWD(1);
+ /* out_port */
+ ND_PRINT(", out_port %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ return;
+ case OFPST_PORT:
+ if (len != OF_PORT_STATS_REQUEST_FIXLEN)
+ goto invalid;
+ /* port_no */
+ ND_PRINT("\n\t port_no %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* pad */
+ /* Always the last field, check bounds. */
+ OF_CHK_FWD(6);
+ return;
+ case OFPST_QUEUE:
+ if (len != OF_QUEUE_STATS_REQUEST_FIXLEN)
+ goto invalid;
+ /* port_no */
+ ND_PRINT("\n\t port_no %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* pad */
+ OF_FWD(2);
+ /* queue_id */
+ ND_PRINT(", queue_id %s",
+ tok2str(ofpq_str, "%u", GET_BE_U_4(cp)));
+ return;
+ case OFPST_VENDOR:
+ of10_vendor_data_print(ndo, cp, len);
+ return;
+ }
+ return;
+
+invalid: /* skip the message body */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* ibid */
+static void
+of10_desc_stats_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ if (len != OF_DESC_STATS_REPLY_FIXLEN)
+ goto invalid;
+ /* mfr_desc */
+ ND_PRINT("\n\t mfr_desc '");
+ nd_printjnp(ndo, cp, DESC_STR_LEN);
+ ND_PRINT("'");
+ OF_FWD(DESC_STR_LEN);
+ /* hw_desc */
+ ND_PRINT("\n\t hw_desc '");
+ nd_printjnp(ndo, cp, DESC_STR_LEN);
+ ND_PRINT("'");
+ OF_FWD(DESC_STR_LEN);
+ /* sw_desc */
+ ND_PRINT("\n\t sw_desc '");
+ nd_printjnp(ndo, cp, DESC_STR_LEN);
+ ND_PRINT("'");
+ OF_FWD(DESC_STR_LEN);
+ /* serial_num */
+ ND_PRINT("\n\t serial_num '");
+ nd_printjnp(ndo, cp, SERIAL_NUM_LEN);
+ ND_PRINT("'");
+ OF_FWD(SERIAL_NUM_LEN);
+ /* dp_desc */
+ ND_PRINT("\n\t dp_desc '");
+ nd_printjnp(ndo, cp, DESC_STR_LEN);
+ ND_PRINT("'");
+ return;
+
+invalid: /* skip the message body */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* ibid */
+static void
+of10_flow_stats_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ while (len) {
+ uint16_t entry_len;
+
+ if (len < OF_FLOW_STATS_REPLY_MINLEN)
+ goto invalid;
+ /* length */
+ entry_len = GET_BE_U_2(cp);
+ ND_PRINT("\n\t length %u", entry_len);
+ if (entry_len < OF_FLOW_STATS_REPLY_MINLEN || entry_len > len)
+ goto invalid;
+ OF_FWD(2);
+ /* table_id */
+ ND_PRINT(", table_id %s",
+ tok2str(tableid_str, "%u", GET_U_1(cp)));
+ OF_FWD(1);
+ /* pad */
+ OF_FWD(1);
+ /* match */
+ of10_match_print(ndo, "\n\t ", cp);
+ OF_FWD(OF_MATCH_FIXLEN);
+ /* duration_sec */
+ ND_PRINT("\n\t duration_sec %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* duration_nsec */
+ ND_PRINT(", duration_nsec %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* priority */
+ ND_PRINT(", priority %u", GET_BE_U_2(cp));
+ OF_FWD(2);
+ /* idle_timeout */
+ ND_PRINT(", idle_timeout %u", GET_BE_U_2(cp));
+ OF_FWD(2);
+ /* hard_timeout */
+ ND_PRINT(", hard_timeout %u", GET_BE_U_2(cp));
+ OF_FWD(2);
+ /* pad2 */
+ OF_FWD(6);
+ /* cookie */
+ ND_PRINT(", cookie 0x%016" PRIx64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* packet_count */
+ ND_PRINT(", packet_count %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* byte_count */
+ ND_PRINT(", byte_count %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* actions */
+ of10_actions_print(ndo, "\n\t ", cp, entry_len - OF_FLOW_STATS_REPLY_MINLEN);
+ OF_FWD(entry_len - OF_FLOW_STATS_REPLY_MINLEN);
+ } /* while */
+ return;
+
+invalid: /* skip the rest of flow statistics entries */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* ibid */
+static void
+of10_aggregate_stats_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ if (len != OF_AGGREGATE_STATS_REPLY_FIXLEN)
+ goto invalid;
+ /* packet_count */
+ ND_PRINT("\n\t packet_count %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* byte_count */
+ ND_PRINT(", byte_count %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* flow_count */
+ ND_PRINT(", flow_count %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* pad */
+ /* Always the last field, check bounds. */
+ ND_TCHECK_4(cp);
+ return;
+
+invalid: /* skip the message body */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* ibid */
+static void
+of10_table_stats_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ while (len) {
+ if (len < OF_TABLE_STATS_REPLY_FIXLEN)
+ goto invalid;
+ /* table_id */
+ ND_PRINT("\n\t table_id %s",
+ tok2str(tableid_str, "%u", GET_U_1(cp)));
+ OF_FWD(1);
+ /* pad */
+ OF_FWD(3);
+ /* name */
+ ND_PRINT(", name '");
+ nd_printjnp(ndo, cp, OFP_MAX_TABLE_NAME_LEN);
+ ND_PRINT("'");
+ OF_FWD(OFP_MAX_TABLE_NAME_LEN);
+ /* wildcards */
+ ND_PRINT("\n\t wildcards 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofpfw_bm, GET_BE_U_4(cp), OFPFW_U);
+ OF_FWD(4);
+ /* max_entries */
+ ND_PRINT("\n\t max_entries %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* active_count */
+ ND_PRINT(", active_count %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* lookup_count */
+ ND_PRINT(", lookup_count %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* matched_count */
+ ND_PRINT(", matched_count %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ } /* while */
+ return;
+
+invalid: /* skip the undersized trailing data */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* ibid */
+static void
+of10_port_stats_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ while (len) {
+ if (len < OF_PORT_STATS_REPLY_FIXLEN)
+ goto invalid;
+ /* port_no */
+ ND_PRINT("\n\t port_no %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ if (ndo->ndo_vflag < 2) {
+ OF_CHK_FWD(OF_PORT_STATS_REPLY_FIXLEN - 2);
+ continue;
+ }
+ /* pad */
+ OF_FWD(6);
+ /* rx_packets */
+ ND_PRINT(", rx_packets %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* tx_packets */
+ ND_PRINT(", tx_packets %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* rx_bytes */
+ ND_PRINT(", rx_bytes %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* tx_bytes */
+ ND_PRINT(", tx_bytes %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* rx_dropped */
+ ND_PRINT(", rx_dropped %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* tx_dropped */
+ ND_PRINT(", tx_dropped %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* rx_errors */
+ ND_PRINT(", rx_errors %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* tx_errors */
+ ND_PRINT(", tx_errors %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* rx_frame_err */
+ ND_PRINT(", rx_frame_err %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* rx_over_err */
+ ND_PRINT(", rx_over_err %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* rx_crc_err */
+ ND_PRINT(", rx_crc_err %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* collisions */
+ ND_PRINT(", collisions %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ } /* while */
+ return;
+
+invalid: /* skip the undersized trailing data */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* ibid */
+static void
+of10_queue_stats_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ while (len) {
+ if (len < OF_QUEUE_STATS_REPLY_FIXLEN)
+ goto invalid;
+ /* port_no */
+ ND_PRINT("\n\t port_no %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* pad */
+ OF_FWD(2);
+ /* queue_id */
+ ND_PRINT(", queue_id %u", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* tx_bytes */
+ ND_PRINT(", tx_bytes %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* tx_packets */
+ ND_PRINT(", tx_packets %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ /* tx_errors */
+ ND_PRINT(", tx_errors %" PRIu64, GET_BE_U_8(cp));
+ OF_FWD(8);
+ } /* while */
+ return;
+
+invalid: /* skip the undersized trailing data */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* ibid */
+static void
+of10_stats_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t type;
+
+ /* type */
+ type = GET_BE_U_2(cp);
+ ND_PRINT("\n\t type %s", tok2str(ofpst_str, "invalid (0x%04x)", type));
+ OF_FWD(2);
+ /* flags */
+ ND_PRINT(", flags 0x%04x", GET_BE_U_2(cp));
+ of_bitmap_print(ndo, ofpsf_reply_bm, GET_BE_U_2(cp), OFPSF_REPLY_U);
+ OF_FWD(2);
+
+ if (ndo->ndo_vflag > 0) {
+ void (*decoder)(netdissect_options *, const u_char *, u_int) =
+ type == OFPST_DESC ? of10_desc_stats_reply_print :
+ type == OFPST_FLOW ? of10_flow_stats_reply_print :
+ type == OFPST_AGGREGATE ? of10_aggregate_stats_reply_print :
+ type == OFPST_TABLE ? of10_table_stats_reply_print :
+ type == OFPST_PORT ? of10_port_stats_reply_print :
+ type == OFPST_QUEUE ? of10_queue_stats_reply_print :
+ type == OFPST_VENDOR ? of10_vendor_data_print :
+ NULL;
+ if (decoder != NULL) {
+ decoder(ndo, cp, len);
+ return;
+ }
+ }
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* [OF10] Section 5.3.6 */
+static void
+of10_packet_out_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t actions_len;
+
+ /* buffer_id */
+ ND_PRINT("\n\t buffer_id 0x%08x", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* in_port */
+ ND_PRINT(", in_port %s", tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* actions_len */
+ actions_len = GET_BE_U_2(cp);
+ OF_FWD(2);
+ if (actions_len > len)
+ goto invalid;
+ /* actions */
+ of10_actions_print(ndo, "\n\t ", cp, actions_len);
+ OF_FWD(actions_len);
+ /* data */
+ of10_packet_data_print(ndo, cp, len);
+ return;
+
+invalid: /* skip the rest of the message body */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* [OF10] Section 5.4.1 */
+static void
+of10_packet_in_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ /* buffer_id */
+ ND_PRINT("\n\t buffer_id %s",
+ tok2str(bufferid_str, "0x%08x", GET_BE_U_4(cp)));
+ OF_FWD(4);
+ /* total_len */
+ ND_PRINT(", total_len %u", GET_BE_U_2(cp));
+ OF_FWD(2);
+ /* in_port */
+ ND_PRINT(", in_port %s", tok2str(ofpp_str, "%u", GET_BE_U_2(cp)));
+ OF_FWD(2);
+ /* reason */
+ ND_PRINT(", reason %s",
+ tok2str(ofpr_str, "invalid (0x%02x)", GET_U_1(cp)));
+ OF_FWD(1);
+ /* pad */
+ /* Sometimes the last field, check bounds. */
+ OF_CHK_FWD(1);
+ /* data */
+ of10_packet_data_print(ndo, cp, len);
+}
+
+/* [OF10] Section 5.4.2 */
+static void
+of10_flow_removed_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* match */
+ of10_match_print(ndo, "\n\t ", cp);
+ cp += OF_MATCH_FIXLEN;
+ /* cookie */
+ ND_PRINT("\n\t cookie 0x%016" PRIx64, GET_BE_U_8(cp));
+ cp += 8;
+ /* priority */
+ if (GET_BE_U_2(cp))
+ ND_PRINT(", priority %u", GET_BE_U_2(cp));
+ cp += 2;
+ /* reason */
+ ND_PRINT(", reason %s",
+ tok2str(ofprr_str, "unknown (0x%02x)", GET_U_1(cp)));
+ cp += 1;
+ /* pad */
+ cp += 1;
+ /* duration_sec */
+ ND_PRINT(", duration_sec %u", GET_BE_U_4(cp));
+ cp += 4;
+ /* duration_nsec */
+ ND_PRINT(", duration_nsec %u", GET_BE_U_4(cp));
+ cp += 4;
+ /* idle_timeout */
+ if (GET_BE_U_2(cp))
+ ND_PRINT(", idle_timeout %u", GET_BE_U_2(cp));
+ cp += 2;
+ /* pad2 */
+ cp += 2;
+ /* packet_count */
+ ND_PRINT(", packet_count %" PRIu64, GET_BE_U_8(cp));
+ cp += 8;
+ /* byte_count */
+ ND_PRINT(", byte_count %" PRIu64, GET_BE_U_8(cp));
+}
+
+/* [OF10] Section 5.4.3 */
+static void
+of10_port_status_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* reason */
+ ND_PRINT("\n\t reason %s",
+ tok2str(ofppr_str, "invalid (0x%02x)", GET_U_1(cp)));
+ cp += 1;
+ /* pad */
+ /* No need to check bounds, more data follows. */
+ cp += 7;
+ /* desc */
+ of10_phy_port_print(ndo, cp);
+}
+
+/* [OF10] Section 5.4.4 */
+static void
+of10_error_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t type, code;
+ const struct tok *code_str;
+
+ /* type */
+ type = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT("\n\t type %s", tok2str(ofpet_str, "invalid (0x%04x)", type));
+ /* code */
+ code = GET_BE_U_2(cp);
+ OF_FWD(2);
+ code_str = uint2tokary(of10_ofpet2tokary, type);
+ if (code_str != NULL)
+ ND_PRINT(", code %s",
+ tok2str(code_str, "invalid (0x%04x)", code));
+ else
+ ND_PRINT(", code invalid (0x%04x)", code);
+ /* data */
+ of_data_print(ndo, cp, len);
+}
+
+static const struct of_msgtypeinfo of10_msgtypeinfo[OFPT_MAX + 1] = {
+ /*
+ * [OF10] Section 5.5.1
+ * Variable-size data.
+ */
+ {
+ "HELLO", of_data_print,
+ REQ_MINLEN, 0
+ },
+ /*
+ * [OF10] Section 5.4.4
+ * A fixed-size message body and variable-size data.
+ */
+ {
+ "ERROR", of10_error_print,
+ REQ_MINLEN, OF_ERROR_MSG_MINLEN
+ },
+ /*
+ * [OF10] Section 5.5.2
+ * Variable-size data.
+ */
+ {
+ "ECHO_REQUEST", of_data_print,
+ REQ_MINLEN, 0
+ },
+ /*
+ * [OF10] Section 5.5.3
+ * Variable-size data.
+ */
+ {
+ "ECHO_REPLY", of_data_print,
+ REQ_MINLEN, 0
+ },
+ /*
+ * [OF10] Section 5.5.4
+ * A fixed-size message body and variable-size data.
+ */
+ {
+ "VENDOR", of10_vendor_message_print,
+ REQ_MINLEN, OF_VENDOR_MINLEN
+ },
+ /*
+ * [OF10] Section 5.3.1
+ * No message body.
+ */
+ {
+ "FEATURES_REQUEST", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF10] Section 5.3.1
+ * A fixed-size message body and n * fixed-size data units.
+ */
+ {
+ "FEATURES_REPLY", of10_features_reply_print,
+ REQ_MINLEN, OF_FEATURES_REPLY_MINLEN
+ },
+ /*
+ * [OF10] Section 5.3.2
+ * No message body.
+ */
+ {
+ "GET_CONFIG_REQUEST", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF10] Section 5.3.2
+ * A fixed-size message body.
+ */
+ {
+ "GET_CONFIG_REPLY", of10_switch_config_msg_print,
+ REQ_FIXLEN, OF_SWITCH_CONFIG_FIXLEN
+ },
+ /*
+ * [OF10] Section 5.3.2
+ * A fixed-size message body.
+ */
+ {
+ "SET_CONFIG", of10_switch_config_msg_print,
+ REQ_FIXLEN, OF_SWITCH_CONFIG_FIXLEN
+ },
+ /*
+ * [OF10] Section 5.4.1
+ * A fixed-size message body and variable-size data.
+ * (The 2 mock octets count in OF_PACKET_IN_MINLEN only.)
+ */
+ {
+ "PACKET_IN", of10_packet_in_print,
+ REQ_MINLEN, OF_PACKET_IN_MINLEN - 2
+ },
+ /*
+ * [OF10] Section 5.4.2
+ * A fixed-size message body.
+ */
+ {
+ "FLOW_REMOVED", of10_flow_removed_print,
+ REQ_FIXLEN, OF_FLOW_REMOVED_FIXLEN
+ },
+ /*
+ * [OF10] Section 5.4.3
+ * A fixed-size message body.
+ */
+ {
+ "PORT_STATUS", of10_port_status_print,
+ REQ_FIXLEN, OF_PORT_STATUS_FIXLEN
+ },
+ /*
+ * [OF10] Section 5.3.6
+ * A fixed-size message body, n * variable-size data units and
+ * variable-size data.
+ */
+ {
+ "PACKET_OUT", of10_packet_out_print,
+ REQ_MINLEN, OF_PACKET_OUT_MINLEN
+ },
+ /*
+ * [OF10] Section 5.3.3
+ * A fixed-size message body and n * variable-size data units.
+ */
+ {
+ "FLOW_MOD", of10_flow_mod_print,
+ REQ_MINLEN, OF_FLOW_MOD_MINLEN
+ },
+ /*
+ * [OF10] Section 5.3.3
+ * A fixed-size message body.
+ */
+ {
+ "PORT_MOD", of10_port_mod_print,
+ REQ_FIXLEN, OF_PORT_MOD_FIXLEN
+ },
+ /*
+ * [OF10] Section 5.3.5
+ * A fixed-size message body and possibly more data of varying size
+ * and structure.
+ */
+ {
+ "STATS_REQUEST", of10_stats_request_print,
+ REQ_MINLEN, OF_STATS_REQUEST_MINLEN
+ },
+ /*
+ * [OF10] Section 5.3.5
+ * A fixed-size message body and possibly more data of varying size
+ * and structure.
+ */
+ {
+ "STATS_REPLY", of10_stats_reply_print,
+ REQ_MINLEN, OF_STATS_REPLY_MINLEN
+ },
+ /*
+ * [OF10] Section 5.3.7
+ * No message body.
+ */
+ {
+ "BARRIER_REQUEST", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF10] Section 5.3.7
+ * No message body.
+ */
+ {
+ "BARRIER_REPLY", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF10] Section 5.3.4
+ * A fixed-size message body.
+ */
+ {
+ "QUEUE_GET_CONFIG_REQUEST", of10_queue_get_config_request_print,
+ REQ_FIXLEN, OF_QUEUE_GET_CONFIG_REQUEST_FIXLEN
+ },
+ /*
+ * [OF10] Section 5.3.4
+ * A fixed-size message body and n * variable-size data units.
+ */
+ {
+ "QUEUE_GET_CONFIG_REPLY", of10_queue_get_config_reply_print,
+ REQ_MINLEN, OF_QUEUE_GET_CONFIG_REPLY_MINLEN
+ },
+};
+
+const struct of_msgtypeinfo *
+of10_identify_msgtype(const uint8_t type)
+{
+ return type <= OFPT_MAX ? &of10_msgtypeinfo[type] : NULL;
+}
diff --git a/print-openflow-1.3.c b/print-openflow-1.3.c
new file mode 100644
index 0000000..9e76ba1
--- /dev/null
+++ b/print-openflow-1.3.c
@@ -0,0 +1,1209 @@
+/*
+ * This module implements decoding of OpenFlow protocol version 1.3 (wire
+ * protocol 0x04). It is based on the implementation conventions explained in
+ * print-openflow-1.0.c.
+ *
+ * [OF13] https://www.opennetworking.org/wp-content/uploads/2014/10/openflow-switch-v1.3.5.pdf
+ *
+ * Copyright (c) 2020 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: OpenFlow protocol version 1.3 printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "openflow.h"
+
+#define OFPT_HELLO 0U
+#define OFPT_ERROR 1U
+#define OFPT_ECHO_REQUEST 2U
+#define OFPT_ECHO_REPLY 3U
+#define OFPT_EXPERIMENTER 4U
+#define OFPT_FEATURES_REQUEST 5U
+#define OFPT_FEATURES_REPLY 6U
+#define OFPT_GET_CONFIG_REQUEST 7U
+#define OFPT_GET_CONFIG_REPLY 8U
+#define OFPT_SET_CONFIG 9U
+#define OFPT_PACKET_IN 10U
+#define OFPT_FLOW_REMOVED 11U
+#define OFPT_PORT_STATUS 12U
+#define OFPT_PACKET_OUT 13U
+#define OFPT_FLOW_MOD 14U
+#define OFPT_GROUP_MOD 15U
+#define OFPT_PORT_MOD 16U
+#define OFPT_TABLE_MOD 17U
+#define OFPT_MULTIPART_REQUEST 18U
+#define OFPT_MULTIPART_REPLY 19U
+#define OFPT_BARRIER_REQUEST 20U
+#define OFPT_BARRIER_REPLY 21U
+#define OFPT_QUEUE_GET_CONFIG_REQUEST 22U
+#define OFPT_QUEUE_GET_CONFIG_REPLY 23U
+#define OFPT_ROLE_REQUEST 24U
+#define OFPT_ROLE_REPLY 25U
+#define OFPT_GET_ASYNC_REQUEST 26U
+#define OFPT_GET_ASYNC_REPLY 27U
+#define OFPT_SET_ASYNC 28U
+#define OFPT_METER_MOD 29U
+#define OFPT_MAX OFPT_METER_MOD
+
+#define OFPC_FLOW_STATS (1U <<0)
+#define OFPC_TABLE_STATS (1U <<1)
+#define OFPC_PORT_STATS (1U <<2)
+#define OFPC_GROUP_STATS (1U <<3)
+#define OFPC_IP_REASM (1U <<5)
+#define OFPC_QUEUE_STATS (1U <<6)
+#define OFPC_PORT_BLOCKED (1U <<8)
+static const struct tok ofp_capabilities_bm[] = {
+ { OFPC_FLOW_STATS, "FLOW_STATS" },
+ { OFPC_TABLE_STATS, "TABLE_STATS" },
+ { OFPC_PORT_STATS, "PORT_STATS" },
+ { OFPC_GROUP_STATS, "GROUP_STATS" },
+ { OFPC_IP_REASM, "IP_REASM" },
+ { OFPC_QUEUE_STATS, "QUEUE_STATS" },
+ { OFPC_PORT_BLOCKED, "PORT_BLOCKED" },
+ { 0, NULL }
+};
+#define OFPCAP_U (~(OFPC_FLOW_STATS | OFPC_TABLE_STATS | OFPC_PORT_STATS | \
+ OFPC_GROUP_STATS | OFPC_IP_REASM | OFPC_QUEUE_STATS | \
+ OFPC_PORT_BLOCKED))
+
+#define OFPC_FRAG_NORMAL 0U
+#define OFPC_FRAG_DROP 1U
+#define OFPC_FRAG_REASM 2U
+static const struct tok ofp_config_str[] = {
+ { OFPC_FRAG_NORMAL, "FRAG_NORMAL" },
+ { OFPC_FRAG_DROP, "FRAG_DROP" },
+ { OFPC_FRAG_REASM, "FRAG_REASM" },
+ { 0, NULL }
+};
+
+#define OFPTT_MAX 0xfeU
+#define OFPTT_ALL 0xffU
+static const struct tok ofptt_str[] = {
+ { OFPTT_MAX, "MAX" },
+ { OFPTT_ALL, "ALL" },
+ { 0, NULL },
+};
+
+#define OFPCML_MAX 0xffe5U
+#define OFPCML_NO_BUFFER 0xffffU
+static const struct tok ofpcml_str[] = {
+ { OFPCML_MAX, "MAX" },
+ { OFPCML_NO_BUFFER, "NO_BUFFER" },
+ { 0, NULL }
+};
+
+#define OFPPC_PORT_DOWN (1U <<0)
+#define OFPPC_NO_RECV (1U <<2)
+#define OFPPC_NO_FWD (1U <<5)
+#define OFPPC_NO_PACKET_IN (1U <<6)
+static const struct tok ofppc_bm[] = {
+ { OFPPC_PORT_DOWN, "PORT_DOWN" },
+ { OFPPC_NO_RECV, "NO_RECV" },
+ { OFPPC_NO_FWD, "NO_FWD" },
+ { OFPPC_NO_PACKET_IN, "NO_PACKET_IN" },
+ { 0, NULL }
+};
+#define OFPPC_U (~(OFPPC_PORT_DOWN | OFPPC_NO_RECV | OFPPC_NO_FWD | \
+ OFPPC_NO_PACKET_IN))
+
+#define OFPPS_LINK_DOWN (1U << 0)
+#define OFPPS_BLOCKED (1U << 1)
+#define OFPPS_LIVE (1U << 2)
+static const struct tok ofpps_bm[] = {
+ { OFPPS_LINK_DOWN, "LINK_DOWN" },
+ { OFPPS_BLOCKED, "BLOCKED" },
+ { OFPPS_LIVE, "LIVE" },
+ { 0, NULL }
+};
+#define OFPPS_U (~(OFPPS_LINK_DOWN | OFPPS_BLOCKED | OFPPS_LIVE))
+
+#define OFPPF_10MB_HD (1U << 0)
+#define OFPPF_10MB_FD (1U << 1)
+#define OFPPF_100MB_HD (1U << 2)
+#define OFPPF_100MB_FD (1U << 3)
+#define OFPPF_1GB_HD (1U << 4)
+#define OFPPF_1GB_FD (1U << 5)
+#define OFPPF_10GB_FD (1U << 6)
+#define OFPPF_40GB_FD (1U << 7)
+#define OFPPF_100GB_FD (1U << 8)
+#define OFPPF_1TB_FD (1U << 9)
+#define OFPPF_OTHER (1U << 10)
+#define OFPPF_COPPER (1U << 11)
+#define OFPPF_FIBER (1U << 12)
+#define OFPPF_AUTONEG (1U << 13)
+#define OFPPF_PAUSE (1U << 14)
+#define OFPPF_PAUSE_ASYM (1U << 15)
+static const struct tok ofppf_bm[] = {
+ { OFPPF_10MB_HD, "10MB_HD" },
+ { OFPPF_10MB_FD, "10MB_FD" },
+ { OFPPF_100MB_HD, "100MB_HD" },
+ { OFPPF_100MB_FD, "100MB_FD" },
+ { OFPPF_1GB_HD, "1GB_HD" },
+ { OFPPF_1GB_FD, "1GB_FD" },
+ { OFPPF_10GB_FD, "10GB_FD" },
+ { OFPPF_40GB_FD, "40GB_FD" },
+ { OFPPF_100GB_FD, "100GB_FD" },
+ { OFPPF_1TB_FD, "1TB_FD" },
+ { OFPPF_OTHER, "OTHER" },
+ { OFPPF_COPPER, "COPPER" },
+ { OFPPF_FIBER, "FIBER" },
+ { OFPPF_AUTONEG, "AUTONEG" },
+ { OFPPF_PAUSE, "PAUSE" },
+ { OFPPF_PAUSE_ASYM, "PAUSE_ASYM" },
+ { 0, NULL }
+};
+#define OFPPF_U (~(OFPPF_10MB_HD | OFPPF_10MB_FD | OFPPF_100MB_HD | \
+ OFPPF_100MB_FD | OFPPF_1GB_HD | OFPPF_1GB_FD | \
+ OFPPF_10GB_FD | OFPPF_40GB_FD | OFPPF_100GB_FD | \
+ OFPPF_1TB_FD | OFPPF_OTHER | OFPPF_COPPER | OFPPF_FIBER | \
+ OFPPF_AUTONEG | OFPPF_PAUSE | OFPPF_PAUSE_ASYM))
+
+#define OFPHET_VERSIONBITMAP 1U
+static const struct tok ofphet_str[] = {
+ { OFPHET_VERSIONBITMAP, "VERSIONBITMAP" },
+ { 0, NULL }
+};
+
+#define OFPP_MAX 0xffffff00U
+#define OFPP_IN_PORT 0xfffffff8U
+#define OFPP_TABLE 0xfffffff9U
+#define OFPP_NORMAL 0xfffffffaU
+#define OFPP_FLOOD 0xfffffffbU
+#define OFPP_ALL 0xfffffffcU
+#define OFPP_CONTROLLER 0xfffffffdU
+#define OFPP_LOCAL 0xfffffffeU
+#define OFPP_ANY 0xffffffffU
+static const struct tok ofpp_str[] = {
+ { OFPP_MAX, "MAX" },
+ { OFPP_IN_PORT, "IN_PORT" },
+ { OFPP_TABLE, "TABLE" },
+ { OFPP_NORMAL, "NORMAL" },
+ { OFPP_FLOOD, "FLOOD" },
+ { OFPP_ALL, "ALL" },
+ { OFPP_CONTROLLER, "CONTROLLER" },
+ { OFPP_LOCAL, "LOCAL" },
+ { OFPP_ANY, "ANY" },
+ { 0, NULL }
+};
+
+#define OFPCR_ROLE_NOCHANGE 0U
+#define OFPCR_ROLE_EQUAL 1U
+#define OFPCR_ROLE_MASTER 2U
+#define OFPCR_ROLE_SLAVE 3U
+static const struct tok ofpcr_str[] = {
+ { OFPCR_ROLE_NOCHANGE, "NOCHANGE" },
+ { OFPCR_ROLE_EQUAL, "EQUAL" },
+ { OFPCR_ROLE_MASTER, "MASTER" },
+ { OFPCR_ROLE_SLAVE, "SLAVE" },
+ { 0, NULL }
+};
+
+#define OF_BIT_VER_1_0 (1U << (OF_VER_1_0 - 1))
+#define OF_BIT_VER_1_1 (1U << (OF_VER_1_1 - 1))
+#define OF_BIT_VER_1_2 (1U << (OF_VER_1_2 - 1))
+#define OF_BIT_VER_1_3 (1U << (OF_VER_1_3 - 1))
+#define OF_BIT_VER_1_4 (1U << (OF_VER_1_4 - 1))
+#define OF_BIT_VER_1_5 (1U << (OF_VER_1_5 - 1))
+static const struct tok ofverbm_str[] = {
+ { OF_BIT_VER_1_0, "1.0" },
+ { OF_BIT_VER_1_1, "1.1" },
+ { OF_BIT_VER_1_2, "1.2" },
+ { OF_BIT_VER_1_3, "1.3" },
+ { OF_BIT_VER_1_4, "1.4" },
+ { OF_BIT_VER_1_5, "1.5" },
+ { 0, NULL }
+};
+#define OF_BIT_VER_U (~(OF_BIT_VER_1_0 | OF_BIT_VER_1_1 | OF_BIT_VER_1_2 | \
+ OF_BIT_VER_1_3 | OF_BIT_VER_1_4 | OF_BIT_VER_1_5))
+
+#define OFPR_NO_MATCH 0U
+#define OFPR_ACTION 1U
+#define OFPR_INVALID_TTL 2U
+#if 0 /* for OFPT_PACKET_IN */
+static const struct tok ofpr_str[] = {
+ { OFPR_NO_MATCH, "NO_MATCH" },
+ { OFPR_ACTION, "ACTION" },
+ { OFPR_INVALID_TTL, "OFPR_INVALID_TTL" },
+ { 0, NULL }
+};
+#endif
+
+#define ASYNC_OFPR_NO_MATCH (1U << OFPR_NO_MATCH )
+#define ASYNC_OFPR_ACTION (1U << OFPR_ACTION )
+#define ASYNC_OFPR_INVALID_TTL (1U << OFPR_INVALID_TTL)
+static const struct tok async_ofpr_bm[] = {
+ { ASYNC_OFPR_NO_MATCH, "NO_MATCH" },
+ { ASYNC_OFPR_ACTION, "ACTION" },
+ { ASYNC_OFPR_INVALID_TTL, "INVALID_TTL" },
+ { 0, NULL }
+};
+#define ASYNC_OFPR_U (~(ASYNC_OFPR_NO_MATCH | ASYNC_OFPR_ACTION | \
+ ASYNC_OFPR_INVALID_TTL))
+
+#define OFPPR_ADD 0U
+#define OFPPR_DELETE 1U
+#define OFPPR_MODIFY 2U
+static const struct tok ofppr_str[] = {
+ { OFPPR_ADD, "ADD" },
+ { OFPPR_DELETE, "DELETE" },
+ { OFPPR_MODIFY, "MODIFY" },
+ { 0, NULL }
+};
+
+#define ASYNC_OFPPR_ADD (1U << OFPPR_ADD )
+#define ASYNC_OFPPR_DELETE (1U << OFPPR_DELETE)
+#define ASYNC_OFPPR_MODIFY (1U << OFPPR_MODIFY)
+static const struct tok async_ofppr_bm[] = {
+ { ASYNC_OFPPR_ADD, "ADD" },
+ { ASYNC_OFPPR_DELETE, "DELETE" },
+ { ASYNC_OFPPR_MODIFY, "MODIFY" },
+ { 0, NULL }
+};
+#define ASYNC_OFPPR_U (~(ASYNC_OFPPR_ADD | ASYNC_OFPPR_DELETE | \
+ ASYNC_OFPPR_MODIFY))
+
+#define OFPET_HELLO_FAILED 0U
+#define OFPET_BAD_REQUEST 1U
+#define OFPET_BAD_ACTION 2U
+#define OFPET_BAD_INSTRUCTION 3U
+#define OFPET_BAD_MATCH 4U
+#define OFPET_FLOW_MOD_FAILED 5U
+#define OFPET_GROUP_MOD_FAILED 6U
+#define OFPET_PORT_MOD_FAILED 7U
+#define OFPET_TABLE_MOD_FAILED 8U
+#define OFPET_QUEUE_OP_FAILED 9U
+#define OFPET_SWITCH_CONFIG_FAILED 10U
+#define OFPET_ROLE_REQUEST_FAILED 11U
+#define OFPET_METER_MOD_FAILED 12U
+#define OFPET_TABLE_FEATURES_FAILED 13U
+#define OFPET_EXPERIMENTER 0xffffU /* a special case */
+static const struct tok ofpet_str[] = {
+ { OFPET_HELLO_FAILED, "HELLO_FAILED" },
+ { OFPET_BAD_REQUEST, "BAD_REQUEST" },
+ { OFPET_BAD_ACTION, "BAD_ACTION" },
+ { OFPET_BAD_INSTRUCTION, "BAD_INSTRUCTION" },
+ { OFPET_BAD_MATCH, "BAD_MATCH" },
+ { OFPET_FLOW_MOD_FAILED, "FLOW_MOD_FAILED" },
+ { OFPET_GROUP_MOD_FAILED, "GROUP_MOD_FAILED" },
+ { OFPET_PORT_MOD_FAILED, "PORT_MOD_FAILED" },
+ { OFPET_TABLE_MOD_FAILED, "TABLE_MOD_FAILED" },
+ { OFPET_QUEUE_OP_FAILED, "QUEUE_OP_FAILED" },
+ { OFPET_SWITCH_CONFIG_FAILED, "SWITCH_CONFIG_FAILED" },
+ { OFPET_ROLE_REQUEST_FAILED, "ROLE_REQUEST_FAILED" },
+ { OFPET_METER_MOD_FAILED, "METER_MOD_FAILED" },
+ { OFPET_TABLE_FEATURES_FAILED, "TABLE_FEATURES_FAILED" },
+ { OFPET_EXPERIMENTER, "EXPERIMENTER" },
+ { 0, NULL }
+};
+
+#define OFPHFC_INCOMPATIBLE 0U
+#define OFPHFC_EPERM 1U
+static const struct tok ofphfc_str[] = {
+ { OFPHFC_INCOMPATIBLE, "INCOMPATIBLE" },
+ { OFPHFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPBRC_BAD_VERSION 0U
+#define OFPBRC_BAD_TYPE 1U
+#define OFPBRC_BAD_MULTIPART 2U
+#define OFPBRC_BAD_EXPERIMENTER 3U
+#define OFPBRC_BAD_EXP_TYPE 4U
+#define OFPBRC_EPERM 5U
+#define OFPBRC_BAD_LEN 6U
+#define OFPBRC_BUFFER_EMPTY 7U
+#define OFPBRC_BUFFER_UNKNOWN 8U
+#define OFPBRC_BAD_TABLE_ID 9U
+#define OFPBRC_IS_SLAVE 10U
+#define OFPBRC_BAD_PORT 11U
+#define OFPBRC_BAD_PACKET 12U
+#define OFPBRC_MULTIPART_BUFFER_OVERFLOW 13U
+static const struct tok ofpbrc_str[] = {
+ { OFPBRC_BAD_VERSION, "BAD_VERSION" },
+ { OFPBRC_BAD_TYPE, "BAD_TYPE" },
+ { OFPBRC_BAD_MULTIPART, "BAD_MULTIPART" },
+ { OFPBRC_BAD_EXPERIMENTER, "BAD_EXPERIMENTER" },
+ { OFPBRC_BAD_EXP_TYPE, "BAD_EXP_TYPE" },
+ { OFPBRC_EPERM, "EPERM" },
+ { OFPBRC_BAD_LEN, "BAD_LEN" },
+ { OFPBRC_BUFFER_EMPTY, "BUFFER_EMPTY" },
+ { OFPBRC_BUFFER_UNKNOWN, "BUFFER_UNKNOWN" },
+ { OFPBRC_BAD_TABLE_ID, "BAD_TABLE_ID" },
+ { OFPBRC_IS_SLAVE, "IS_SLAVE" },
+ { OFPBRC_BAD_PORT, "BAD_PORT" },
+ { OFPBRC_BAD_PACKET, "BAD_PACKET" },
+ { OFPBRC_MULTIPART_BUFFER_OVERFLOW, "MULTIPART_BUFFER_OVERFLOW" },
+ { 0, NULL }
+};
+
+#define OFPBAC_BAD_TYPE 0U
+#define OFPBAC_BAD_LEN 1U
+#define OFPBAC_BAD_EXPERIMENTER 2U
+#define OFPBAC_BAD_EXP_TYPE 3U
+#define OFPBAC_BAD_OUT_PORT 4U
+#define OFPBAC_BAD_ARGUMENT 5U
+#define OFPBAC_EPERM 6U
+#define OFPBAC_TOO_MANY 7U
+#define OFPBAC_BAD_QUEUE 8U
+#define OFPBAC_BAD_OUT_GROUP 9U
+#define OFPBAC_MATCH_INCONSISTENT 10U
+#define OFPBAC_UNSUPPORTED_ORDER 11U
+#define OFPBAC_BAD_TAG 12U
+#define OFPBAC_BAD_SET_TYPE 13U
+#define OFPBAC_BAD_SET_LEN 14U
+#define OFPBAC_BAD_SET_ARGUMENT 15U
+static const struct tok ofpbac_str[] = {
+ { OFPBAC_BAD_TYPE, "BAD_TYPE" },
+ { OFPBAC_BAD_LEN, "BAD_LEN" },
+ { OFPBAC_BAD_EXPERIMENTER, "BAD_EXPERIMENTER" },
+ { OFPBAC_BAD_EXP_TYPE, "BAD_EXP_TYPE" },
+ { OFPBAC_BAD_OUT_PORT, "BAD_OUT_PORT" },
+ { OFPBAC_BAD_ARGUMENT, "BAD_ARGUMENT" },
+ { OFPBAC_EPERM, "EPERM" },
+ { OFPBAC_TOO_MANY, "TOO_MANY" },
+ { OFPBAC_BAD_QUEUE, "BAD_QUEUE" },
+ { OFPBAC_BAD_OUT_GROUP, "BAD_OUT_GROUP" },
+ { OFPBAC_MATCH_INCONSISTENT, "MATCH_INCONSISTENT" },
+ { OFPBAC_UNSUPPORTED_ORDER, "UNSUPPORTED_ORDER" },
+ { OFPBAC_BAD_TAG, "BAD_TAG" },
+ { OFPBAC_BAD_SET_TYPE, "BAD_SET_TYPE" },
+ { OFPBAC_BAD_SET_LEN, "BAD_SET_LEN" },
+ { OFPBAC_BAD_SET_ARGUMENT, "BAD_SET_ARGUMENT" },
+ { 0, NULL }
+};
+
+#define OFPBIC_UNKNOWN_INST 0U
+#define OFPBIC_UNSUP_INST 1U
+#define OFPBIC_BAD_TABLE_ID 2U
+#define OFPBIC_UNSUP_METADATA 3U
+#define OFPBIC_UNSUP_METADATA_MASK 4U
+#define OFPBIC_BAD_EXPERIMENTER 5U
+#define OFPBIC_BAD_EXP_TYPE 6U
+#define OFPBIC_BAD_LEN 7U
+#define OFPBIC_EPERM 8U
+static const struct tok ofpbic_str[] = {
+ { OFPBIC_UNKNOWN_INST, "UNKNOWN_INST" },
+ { OFPBIC_UNSUP_INST, "UNSUP_INST" },
+ { OFPBIC_BAD_TABLE_ID, "BAD_TABLE_ID" },
+ { OFPBIC_UNSUP_METADATA, "UNSUP_METADATA" },
+ { OFPBIC_UNSUP_METADATA_MASK, "UNSUP_METADATA_MASK" },
+ { OFPBIC_BAD_EXPERIMENTER, "BAD_EXPERIMENTER" },
+ { OFPBIC_BAD_EXP_TYPE, "BAD_EXP_TYPE" },
+ { OFPBIC_BAD_LEN, "BAD_LEN" },
+ { OFPBIC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPBMC_BAD_TYPE 0U
+#define OFPBMC_BAD_LEN 1U
+#define OFPBMC_BAD_TAG 2U
+#define OFPBMC_BAD_DL_ADDR_MASK 3U
+#define OFPBMC_BAD_NW_ADDR_MASK 4U
+#define OFPBMC_BAD_WILDCARDS 5U
+#define OFPBMC_BAD_FIELD 6U
+#define OFPBMC_BAD_VALUE 7U
+#define OFPBMC_BAD_MASK 8U
+#define OFPBMC_BAD_PREREQ 9U
+#define OFPBMC_DUP_FIELD 10U
+#define OFPBMC_EPERM 11U
+static const struct tok ofpbmc_str[] = {
+ { OFPBMC_BAD_TYPE, "BAD_TYPE" },
+ { OFPBMC_BAD_LEN, "BAD_LEN" },
+ { OFPBMC_BAD_TAG, "BAD_TAG" },
+ { OFPBMC_BAD_DL_ADDR_MASK, "BAD_DL_ADDR_MASK" },
+ { OFPBMC_BAD_NW_ADDR_MASK, "BAD_NW_ADDR_MASK" },
+ { OFPBMC_BAD_WILDCARDS, "BAD_WILDCARDS" },
+ { OFPBMC_BAD_FIELD, "BAD_FIELD" },
+ { OFPBMC_BAD_VALUE, "BAD_VALUE" },
+ { OFPBMC_BAD_MASK, "BAD_MASK" },
+ { OFPBMC_BAD_PREREQ, "BAD_PREREQ" },
+ { OFPBMC_DUP_FIELD, "DUP_FIELD" },
+ { OFPBMC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPFMFC_UNKNOWN 0U
+#define OFPFMFC_TABLE_FULL 1U
+#define OFPFMFC_BAD_TABLE_ID 2U
+#define OFPFMFC_OVERLAP 3U
+#define OFPFMFC_EPERM 4U
+#define OFPFMFC_BAD_TIMEOUT 5U
+#define OFPFMFC_BAD_COMMAND 6U
+#define OFPFMFC_BAD_FLAGS 7U
+static const struct tok ofpfmfc_str[] = {
+ { OFPFMFC_UNKNOWN, "UNKNOWN" },
+ { OFPFMFC_TABLE_FULL, "TABLE_FULL" },
+ { OFPFMFC_BAD_TABLE_ID, "BAD_TABLE_ID" },
+ { OFPFMFC_OVERLAP, "OVERLAP" },
+ { OFPFMFC_EPERM, "EPERM" },
+ { OFPFMFC_BAD_TIMEOUT, "BAD_TIMEOUT" },
+ { OFPFMFC_BAD_COMMAND, "BAD_COMMAND" },
+ { OFPFMFC_BAD_FLAGS, "BAD_FLAGS" },
+ { 0, NULL }
+};
+
+#define OFPGMFC_GROUP_EXISTS 0U
+#define OFPGMFC_INVALID_GROUP 1U
+#define OFPGMFC_WEIGHT_UNSUPPORTED 2U
+#define OFPGMFC_OUT_OF_GROUPS 3U
+#define OFPGMFC_OUT_OF_BUCKETS 4U
+#define OFPGMFC_CHAINING_UNSUPPORTED 5U
+#define OFPGMFC_WATCH_UNSUPPORTED 6U
+#define OFPGMFC_LOOP 7U
+#define OFPGMFC_UNKNOWN_GROUP 8U
+#define OFPGMFC_CHAINED_GROUP 9U
+#define OFPGMFC_BAD_TYPE 10U
+#define OFPGMFC_BAD_COMMAND 11U
+#define OFPGMFC_BAD_BUCKET 12U
+#define OFPGMFC_BAD_MATCH 13U
+#define OFPGMFC_EPERM 14U
+static const struct tok ofpgmfc_str[] = {
+ { OFPGMFC_GROUP_EXISTS, "GROUP_EXISTS" },
+ { OFPGMFC_INVALID_GROUP, "INVALID_GROUP" },
+ { OFPGMFC_WEIGHT_UNSUPPORTED, "WEIGHT_UNSUPPORTED" },
+ { OFPGMFC_OUT_OF_GROUPS, "OUT_OF_GROUPS" },
+ { OFPGMFC_OUT_OF_BUCKETS, "OUT_OF_BUCKETS" },
+ { OFPGMFC_CHAINING_UNSUPPORTED, "CHAINING_UNSUPPORTED" },
+ { OFPGMFC_WATCH_UNSUPPORTED, "WATCH_UNSUPPORTED" },
+ { OFPGMFC_LOOP, "LOOP" },
+ { OFPGMFC_UNKNOWN_GROUP, "UNKNOWN_GROUP" },
+ { OFPGMFC_CHAINED_GROUP, "CHAINED_GROUP" },
+ { OFPGMFC_BAD_TYPE, "BAD_TYPE" },
+ { OFPGMFC_BAD_COMMAND, "BAD_COMMAND" },
+ { OFPGMFC_BAD_BUCKET, "BAD_BUCKET" },
+ { OFPGMFC_BAD_MATCH, "BAD_MATCH" },
+ { OFPGMFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPPMFC_BAD_PORT 0U
+#define OFPPMFC_BAD_HW_ADDR 1U
+#define OFPPMFC_BAD_CONFIG 2U
+#define OFPPMFC_BAD_ADVERTISE 3U
+#define OFPPMFC_EPERM 4U
+static const struct tok ofppmfc_str[] = {
+ { OFPPMFC_BAD_PORT, "BAD_PORT" },
+ { OFPPMFC_BAD_HW_ADDR, "BAD_HW_ADDR" },
+ { OFPPMFC_BAD_CONFIG, "BAD_CONFIG" },
+ { OFPPMFC_BAD_ADVERTISE, "BAD_ADVERTISE" },
+ { OFPPMFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPTMFC_BAD_TABLE 0U
+#define OFPTMFC_BAD_CONFIG 1U
+#define OFPTMFC_EPERM 2U
+static const struct tok ofptmfc_str[] = {
+ { OFPTMFC_BAD_TABLE, "BAD_TABLE" },
+ { OFPTMFC_BAD_CONFIG, "BAD_CONFIG" },
+ { OFPTMFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPQOFC_BAD_PORT 0U
+#define OFPQOFC_BAD_QUEUE 1U
+#define OFPQOFC_EPERM 2U
+static const struct tok ofpqofc_str[] = {
+ { OFPQOFC_BAD_PORT, "BAD_PORT" },
+ { OFPQOFC_BAD_QUEUE, "BAD_QUEUE" },
+ { OFPQOFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPSCFC_BAD_FLAGS 0U
+#define OFPSCFC_BAD_LEN 1U
+#define OFPSCFC_EPERM 2U
+static const struct tok ofpscfc_str[] = {
+ { OFPSCFC_BAD_FLAGS, "BAD_FLAGS" },
+ { OFPSCFC_BAD_LEN, "BAD_LEN" },
+ { OFPSCFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+#define OFPRRFC_STALE 0U
+#define OFPRRFC_UNSUP 1U
+#define OFPRRFC_BAD_ROLE 2U
+static const struct tok ofprrfc_str[] = {
+ { OFPRRFC_STALE, "STALE" },
+ { OFPRRFC_UNSUP, "UNSUP" },
+ { OFPRRFC_BAD_ROLE, "BAD_ROLE" },
+ { 0, NULL }
+};
+
+#define OFPMMFC_UNKNOWN 0U
+#define OFPMMFC_METER_EXISTS 1U
+#define OFPMMFC_INVALID_METER 2U
+#define OFPMMFC_UNKNOWN_METER 3U
+#define OFPMMFC_BAD_COMMAND 4U
+#define OFPMMFC_BAD_FLAGS 5U
+#define OFPMMFC_BAD_RATE 6U
+#define OFPMMFC_BAD_BURST 7U
+#define OFPMMFC_BAD_BAND 8U
+#define OFPMMFC_BAD_BAND_VALUE 9U
+#define OFPMMFC_OUT_OF_METERS 10U
+#define OFPMMFC_OUT_OF_BANDS 11U
+static const struct tok ofpmmfc_str[] = {
+ { OFPMMFC_UNKNOWN, "UNKNOWN" },
+ { OFPMMFC_METER_EXISTS, "METER_EXISTS" },
+ { OFPMMFC_INVALID_METER, "INVALID_METER" },
+ { OFPMMFC_UNKNOWN_METER, "UNKNOWN_METER" },
+ { OFPMMFC_BAD_COMMAND, "BAD_COMMAND" },
+ { OFPMMFC_BAD_FLAGS, "BAD_FLAGS" },
+ { OFPMMFC_BAD_RATE, "BAD_RATE" },
+ { OFPMMFC_BAD_BURST, "BAD_BURST" },
+ { OFPMMFC_BAD_BAND, "BAD_BAND" },
+ { OFPMMFC_BAD_BAND_VALUE, "BAD_BAND_VALUE" },
+ { OFPMMFC_OUT_OF_METERS, "OUT_OF_METERS" },
+ { OFPMMFC_OUT_OF_BANDS, "OUT_OF_BANDS" },
+ { 0, NULL }
+};
+
+#define OFPTFFC_BAD_TABLE 0U
+#define OFPTFFC_BAD_METADATA 1U
+#define OFPTFFC_BAD_TYPE 2U
+#define OFPTFFC_BAD_LEN 3U
+#define OFPTFFC_BAD_ARGUMENT 4U
+#define OFPTFFC_EPERM 5U
+static const struct tok ofptffc_str[] = {
+ { OFPTFFC_BAD_TABLE, "BAD_TABLE" },
+ { OFPTFFC_BAD_METADATA, "BAD_METADATA" },
+ { OFPTFFC_BAD_TYPE, "BAD_TYPE" },
+ { OFPTFFC_BAD_LEN, "BAD_LEN" },
+ { OFPTFFC_BAD_ARGUMENT, "BAD_ARGUMENT" },
+ { OFPTFFC_EPERM, "EPERM" },
+ { 0, NULL }
+};
+
+static const struct uint_tokary of13_ofpet2tokary[] = {
+ { OFPET_HELLO_FAILED, ofphfc_str },
+ { OFPET_BAD_REQUEST, ofpbrc_str },
+ { OFPET_BAD_ACTION, ofpbac_str },
+ { OFPET_BAD_INSTRUCTION, ofpbic_str },
+ { OFPET_BAD_MATCH, ofpbmc_str },
+ { OFPET_FLOW_MOD_FAILED, ofpfmfc_str },
+ { OFPET_GROUP_MOD_FAILED, ofpgmfc_str },
+ { OFPET_PORT_MOD_FAILED, ofppmfc_str },
+ { OFPET_TABLE_MOD_FAILED, ofptmfc_str },
+ { OFPET_QUEUE_OP_FAILED, ofpqofc_str },
+ { OFPET_SWITCH_CONFIG_FAILED, ofpscfc_str },
+ { OFPET_ROLE_REQUEST_FAILED, ofprrfc_str },
+ { OFPET_METER_MOD_FAILED, ofpmmfc_str },
+ { OFPET_TABLE_FEATURES_FAILED, ofptffc_str },
+ { OFPET_EXPERIMENTER, NULL }, /* defines no codes */
+ /* uint2tokary() does not use array termination. */
+};
+
+/* lengths (fixed or minimal) of particular message types, where not 0 */
+#define OF_ERROR_MSG_MINLEN (12U - OF_HEADER_FIXLEN)
+#define OF_FEATURES_REPLY_FIXLEN (32U - OF_HEADER_FIXLEN)
+#define OF_PORT_MOD_FIXLEN (40U - OF_HEADER_FIXLEN)
+#define OF_SWITCH_CONFIG_MSG_FIXLEN (12U - OF_HEADER_FIXLEN)
+#define OF_TABLE_MOD_FIXLEN (16U - OF_HEADER_FIXLEN)
+#define OF_QUEUE_GET_CONFIG_REQUEST_FIXLEN (16U - OF_HEADER_FIXLEN)
+#define OF_ROLE_MSG_FIXLEN (24U - OF_HEADER_FIXLEN)
+#define OF_ASYNC_MSG_FIXLEN (32U - OF_HEADER_FIXLEN)
+#define OF_PORT_STATUS_FIXLEN (80U - OF_HEADER_FIXLEN)
+#define OF_EXPERIMENTER_MSG_MINLEN (16U - OF_HEADER_FIXLEN)
+
+/* lengths (fixed or minimal) of particular protocol structures */
+#define OF_HELLO_ELEM_MINSIZE 4U
+
+/* miscellaneous constants from [OF13] */
+#define OFP_MAX_PORT_NAME_LEN 16U
+
+/* [OF13] Section 7.2.1 */
+static void
+of13_port_print(netdissect_options *ndo,
+ const u_char *cp)
+{
+ /* port_no */
+ ND_PRINT("\n\t port_no %s",
+ tok2str(ofpp_str, "%u", GET_BE_U_4(cp)));
+ cp += 4;
+ /* pad */
+ cp += 4;
+ /* hw_addr */
+ ND_PRINT(", hw_addr %s", GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ /* pad2 */
+ cp += 2;
+ /* name */
+ ND_PRINT(", name '");
+ nd_printjnp(ndo, cp, OFP_MAX_PORT_NAME_LEN);
+ ND_PRINT("'");
+ cp += OFP_MAX_PORT_NAME_LEN;
+
+ if (ndo->ndo_vflag < 2) {
+ ND_TCHECK_LEN(cp, 32);
+ return;
+ }
+
+ /* config */
+ ND_PRINT("\n\t config 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppc_bm, GET_BE_U_4(cp), OFPPC_U);
+ cp += 4;
+ /* state */
+ ND_PRINT("\n\t state 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofpps_bm, GET_BE_U_4(cp), OFPPS_U);;
+ cp += 4;
+ /* curr */
+ ND_PRINT("\n\t curr 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* advertised */
+ ND_PRINT("\n\t advertised 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* supported */
+ ND_PRINT("\n\t supported 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* peer */
+ ND_PRINT("\n\t peer 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* curr_speed */
+ ND_PRINT("\n\t curr_speed %ukbps", GET_BE_U_4(cp));
+ cp += 4;
+ /* max_speed */
+ ND_PRINT("\n\t max_speed %ukbps", GET_BE_U_4(cp));
+}
+
+/* [OF13] Section 7.3.1 */
+static void
+of13_features_reply_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* datapath_id */
+ ND_PRINT("\n\t dpid 0x%016" PRIx64, GET_BE_U_8(cp));
+ cp += 8;
+ /* n_buffers */
+ ND_PRINT(", n_buffers %u", GET_BE_U_4(cp));
+ cp += 4;
+ /* n_tables */
+ ND_PRINT(", n_tables %u", GET_U_1(cp));
+ cp += 1;
+ /* auxiliary_id */
+ ND_PRINT(", auxiliary_id %u", GET_U_1(cp));
+ cp += 1;
+ /* pad */
+ cp += 2;
+ /* capabilities */
+ ND_PRINT("\n\t capabilities 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofp_capabilities_bm, GET_BE_U_4(cp), OFPCAP_U);
+ cp += 4;
+ /* reserved */
+ ND_TCHECK_4(cp);
+}
+
+/* [OF13] Section 7.3.2 */
+static void
+of13_switch_config_msg_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* flags */
+ ND_PRINT("\n\t flags %s",
+ tok2str(ofp_config_str, "invalid (0x%04x)", GET_BE_U_2(cp)));
+ cp += 2;
+ /* miss_send_len */
+ ND_PRINT(", miss_send_len %s",
+ tok2str(ofpcml_str, "%u", GET_BE_U_2(cp)));
+}
+
+/* [OF13] Section 7.3.3 */
+static void
+of13_table_mod_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* table_id */
+ ND_PRINT("\n\t table_id %s", tok2str(ofptt_str, "%u", GET_U_1(cp)));
+ cp += 1;
+ /* pad */
+ cp += 3;
+ /* config */
+ ND_PRINT(", config 0x%08x", GET_BE_U_4(cp));
+}
+
+/* [OF13] Section 7.3.9 */
+static void
+of13_role_msg_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* role */
+ ND_PRINT("\n\t role %s",
+ tok2str(ofpcr_str, "invalid (0x%08x)", GET_BE_U_4(cp)));
+ cp += 4;
+ /* pad */
+ cp += 4;
+ /* generation_id */
+ ND_PRINT(", generation_id 0x%016" PRIx64, GET_BE_U_8(cp));
+}
+
+/* [OF13] Section 7.3.10 */
+static void
+of13_async_msg_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* packet_in_mask[0] */
+ ND_PRINT("\n\t packet_in_mask[EM] 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, async_ofpr_bm, GET_BE_U_4(cp), ASYNC_OFPR_U);
+ cp += 4;
+ /* packet_in_mask[1] */
+ ND_PRINT("\n\t packet_in_mask[S] 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, async_ofpr_bm, GET_BE_U_4(cp), ASYNC_OFPR_U);
+ cp += 4;
+ /* port_status_mask[0] */
+ ND_PRINT("\n\t port_status_mask[EM] 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, async_ofppr_bm, GET_BE_U_4(cp), ASYNC_OFPPR_U);
+ cp += 4;
+ /* port_status_mask[1] */
+ ND_PRINT("\n\t port_status_mask[S] 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, async_ofppr_bm, GET_BE_U_4(cp), ASYNC_OFPPR_U);
+ cp += 4;
+ /* flow_removed_mask[0] */
+ ND_PRINT("\n\t flow_removed_mask[EM] 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, async_ofppr_bm, GET_BE_U_4(cp), ASYNC_OFPPR_U);
+ cp += 4;
+ /* flow_removed_mask[1] */
+ ND_PRINT("\n\t flow_removed_mask[S] 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, async_ofppr_bm, GET_BE_U_4(cp), ASYNC_OFPPR_U);
+}
+
+/* [OF13] Section 7.3.4.3 */
+static void
+of13_port_mod_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* port_no */
+ ND_PRINT("\n\t port_no %s", tok2str(ofpp_str, "%u", GET_BE_U_4(cp)));
+ cp += 4;
+ /* pad */
+ cp += 4;
+ /* hw_addr */
+ ND_PRINT(", hw_addr %s", GET_ETHERADDR_STRING(cp));
+ cp += MAC_ADDR_LEN;
+ /* pad2 */
+ cp += 2;
+ /* config */
+ ND_PRINT("\n\t config 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppc_bm, GET_BE_U_4(cp), OFPPC_U);
+ cp += 4;
+ /* mask */
+ ND_PRINT("\n\t mask 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppc_bm, GET_BE_U_4(cp), OFPPC_U);
+ cp += 4;
+ /* advertise */
+ ND_PRINT("\n\t advertise 0x%08x", GET_BE_U_4(cp));
+ of_bitmap_print(ndo, ofppf_bm, GET_BE_U_4(cp), OFPPF_U);
+ cp += 4;
+ /* pad3 */
+ /* Always the last field, check bounds. */
+ ND_TCHECK_4(cp);
+}
+
+/* [OF13] Section 7.4.3 */
+static void
+of13_port_status_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* reason */
+ ND_PRINT("\n\t reason %s",
+ tok2str(ofppr_str, "invalid (0x02x)", GET_U_1(cp)));
+ cp += 1;
+ /* pad */
+ cp += 7;
+ /* desc */
+ of13_port_print(ndo, cp);
+}
+
+/* [OF13] Section 7.5.1 */
+static void
+of13_hello_elements_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ while (len) {
+ uint16_t type, bmlen;
+
+ if (len < OF_HELLO_ELEM_MINSIZE)
+ goto invalid;
+ /* type */
+ type = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT("\n\t type %s",
+ tok2str(ofphet_str, "unknown (0x%04x)", type));
+ /* length */
+ bmlen = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT(", length %u", bmlen);
+ /* cp is OF_HELLO_ELEM_MINSIZE bytes in */
+ if (bmlen < OF_HELLO_ELEM_MINSIZE ||
+ bmlen > OF_HELLO_ELEM_MINSIZE + len)
+ goto invalid;
+ switch (type) {
+ case OFPHET_VERSIONBITMAP:
+ /*
+ * The specification obviously overprovisions the space
+ * for version bitmaps in this element ("ofp versions
+ * 32 to 63 are encoded in the second bitmap and so
+ * on"). Keep this code simple for now and recognize
+ * only a single bitmap with no padding.
+ */
+ if (bmlen == OF_HELLO_ELEM_MINSIZE + 4) {
+ uint32_t bitmap = GET_BE_U_4(cp);
+ ND_PRINT(", bitmap 0x%08x", bitmap);
+ of_bitmap_print(ndo, ofverbm_str, bitmap,
+ OF_BIT_VER_U);
+ } else {
+ ND_PRINT(" (bogus)");
+ ND_TCHECK_LEN(cp, bmlen - OF_HELLO_ELEM_MINSIZE);
+ }
+ break;
+ default:
+ ND_TCHECK_LEN(cp, bmlen - OF_HELLO_ELEM_MINSIZE);
+ }
+ OF_FWD(bmlen - OF_HELLO_ELEM_MINSIZE);
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* [OF13] Section 7.5.4 */
+static void
+of13_experimenter_message_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint32_t experimenter;
+
+ /* experimenter */
+ experimenter = GET_BE_U_4(cp);
+ OF_FWD(4);
+ ND_PRINT("\n\t experimenter 0x%08x (%s)", experimenter,
+ of_vendor_name(experimenter));
+ /* exp_type */
+ ND_PRINT(", exp_type 0x%08x", GET_BE_U_4(cp));
+ OF_FWD(4);
+ /* data */
+ of_data_print(ndo, cp, len);
+}
+
+/* [OF13] Section 7.3.6 */
+static void
+of13_queue_get_config_request_print(netdissect_options *ndo,
+ const u_char *cp, u_int len _U_)
+{
+ /* port */
+ ND_PRINT("\n\t port %s", tok2str(ofpp_str, "%u", GET_BE_U_4(cp)));
+ cp += 4;
+ /* pad */
+ /* Always the last field, check bounds. */
+ ND_TCHECK_4(cp);
+}
+
+/* [OF13] Section 7.4.4 */
+static void
+of13_error_print(netdissect_options *ndo,
+ const u_char *cp, u_int len)
+{
+ uint16_t type, code;
+ const struct tok *code_str;
+
+ /* type */
+ type = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT("\n\t type %s", tok2str(ofpet_str, "invalid (0x%04x)", type));
+ /* code */
+ code = GET_BE_U_2(cp);
+ OF_FWD(2);
+ code_str = uint2tokary(of13_ofpet2tokary, type);
+ if (code_str != NULL)
+ ND_PRINT(", code %s",
+ tok2str(code_str, "invalid (0x%04x)", code));
+ else
+ ND_PRINT(", code invalid (0x%04x)", code);
+ /* data */
+ of_data_print(ndo, cp, len);
+}
+
+static const struct of_msgtypeinfo of13_msgtypeinfo[OFPT_MAX + 1] = {
+ /*
+ * [OF13] Section 7.5.1
+ * n * variable-size data units.
+ */
+ {
+ "HELLO", of13_hello_elements_print,
+ REQ_MINLEN, 0
+ },
+ /*
+ * [OF13] Section 7.4.4
+ * A fixed-size message body and variable-size data.
+ */
+ {
+ "ERROR", of13_error_print,
+ REQ_MINLEN, OF_ERROR_MSG_MINLEN
+ },
+ /*
+ * [OF13] Section 7.5.2
+ * Variable-size data.
+ */
+ {
+ "ECHO_REQUEST", of_data_print,
+ REQ_MINLEN, 0
+ },
+ /*
+ * [OF13] Section 7.5.3
+ * Variable-size data.
+ */
+ {
+ "ECHO_REPLY", of_data_print,
+ REQ_MINLEN, 0
+ },
+ /*
+ * [OF13] Section 7.5.4
+ * A fixed-size message body and variable-size data.
+ */
+ {
+ "EXPERIMENTER", of13_experimenter_message_print,
+ REQ_MINLEN, OF_EXPERIMENTER_MSG_MINLEN
+ },
+ /*
+ * [OF13] Section 7.3.1
+ * No message body.
+ */
+ {
+ "FEATURES_REQUEST", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF13] Section 7.3.1
+ * A fixed-size message body.
+ */
+ {
+ "FEATURES_REPLY", of13_features_reply_print,
+ REQ_FIXLEN, OF_FEATURES_REPLY_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.2
+ * No message body.
+ */
+ {
+ "GET_CONFIG_REQUEST", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF13] Section 7.3.2
+ * A fixed-size message body.
+ */
+ {
+ "GET_CONFIG_REPLY", of13_switch_config_msg_print,
+ REQ_FIXLEN, OF_SWITCH_CONFIG_MSG_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.2
+ * A fixed-size message body.
+ */
+ {
+ "SET_CONFIG", of13_switch_config_msg_print,
+ REQ_FIXLEN, OF_SWITCH_CONFIG_MSG_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.4.1
+ * (to be done)
+ */
+ {
+ "PACKET_IN", NULL,
+ REQ_NONE, 0
+ },
+ /*
+ * [OF13] Section 7.4.2
+ * (to be done)
+ */
+ {
+ "FLOW_REMOVED", NULL,
+ REQ_NONE, 0
+ },
+ /*
+ * [OF13] Section 7.4.3
+ * A fixed-size message body.
+ */
+ {
+ "PORT_STATUS", of13_port_status_print,
+ REQ_FIXLEN, OF_PORT_STATUS_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.7
+ * (to be done)
+ */
+ {
+ "PACKET_OUT", NULL,
+ REQ_NONE, 0
+ },
+ /*
+ * [OF13] Section 7.3.4.1
+ * (to be done)
+ */
+ {
+ "FLOW_MOD", NULL,
+ REQ_NONE, 0
+ },
+ /*
+ * [OF13] Section 7.3.4.2
+ * (to be done)
+ */
+ {
+ "GROUP_MOD", NULL,
+ REQ_NONE, 0
+ },
+ /*
+ * [OF13] Section 7.3.4.3
+ * A fixed-size message body.
+ */
+ {
+ "PORT_MOD", of13_port_mod_print,
+ REQ_FIXLEN, OF_PORT_MOD_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.3
+ * A fixed-size message body.
+ */
+ {
+ "TABLE_MOD", of13_table_mod_print,
+ REQ_FIXLEN, OF_TABLE_MOD_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.5
+ * (to be done)
+ */
+ {
+ "MULTIPART_REQUEST", NULL,
+ REQ_NONE, 0
+ },
+ /*
+ * [OF13] Section 7.3.5
+ * (to be done)
+ */
+ {
+ "MULTIPART_REPLY", NULL,
+ REQ_NONE, 0
+ },
+ /*
+ * [OF13] Section 7.3.8
+ * No message body.
+ */
+ {
+ "BARRIER_REQUEST", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF13] Section 7.3.8
+ * No message body.
+ */
+ {
+ "BARRIER_REPLY", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF13] Section 7.3.6
+ * A fixed-size message body.
+ */
+ {
+ "QUEUE_GET_CONFIG_REQUEST", of13_queue_get_config_request_print,
+ REQ_FIXLEN, OF_QUEUE_GET_CONFIG_REQUEST_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.6
+ * (to be done)
+ */
+ {
+ "QUEUE_GET_CONFIG_REPLY", NULL,
+ REQ_NONE, 0
+ },
+ /*
+ * [OF13] Section 7.3.9
+ * A fixed-size message body.
+ */
+ {
+ "ROLE_REQUEST", of13_role_msg_print,
+ REQ_FIXLEN, OF_ROLE_MSG_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.9
+ * A fixed-size message body.
+ */
+ {
+ "ROLE_REPLY", of13_role_msg_print,
+ REQ_FIXLEN, OF_ROLE_MSG_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.10
+ * No message body.
+ */
+ {
+ "GET_ASYNC_REQUEST", NULL,
+ REQ_FIXLEN, 0
+ },
+ /*
+ * [OF13] Section 7.3.10
+ * A fixed-size message body.
+ */
+ {
+ "GET_ASYNC_REPLY", of13_async_msg_print,
+ REQ_FIXLEN, OF_ASYNC_MSG_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.10
+ * A fixed-size message body.
+ */
+ {
+ "SET_ASYNC", of13_async_msg_print,
+ REQ_FIXLEN, OF_ASYNC_MSG_FIXLEN
+ },
+ /*
+ * [OF13] Section 7.3.4.4
+ * (to be done)
+ */
+ {
+ "METER_MOD", NULL,
+ REQ_NONE, 0
+ },
+};
+
+const struct of_msgtypeinfo *
+of13_identify_msgtype(const uint8_t type)
+{
+ return type <= OFPT_MAX ? &of13_msgtypeinfo[type] : NULL;
+}
diff --git a/print-openflow.c b/print-openflow.c
new file mode 100644
index 0000000..6024a21
--- /dev/null
+++ b/print-openflow.c
@@ -0,0 +1,228 @@
+/*
+ * This module implements printing of the very basic (version-independent)
+ * OpenFlow header and iteration over OpenFlow messages. It is intended for
+ * dispatching of version-specific OpenFlow message decoding.
+ *
+ *
+ * Copyright (c) 2013 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: version-independent OpenFlow printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "openflow.h"
+#include "oui.h"
+
+
+static const struct tok ofver_str[] = {
+ { OF_VER_1_0, "1.0" },
+ { OF_VER_1_1, "1.1" },
+ { OF_VER_1_2, "1.2" },
+ { OF_VER_1_3, "1.3" },
+ { OF_VER_1_4, "1.4" },
+ { OF_VER_1_5, "1.5" },
+ { 0, NULL }
+};
+
+const struct tok onf_exp_str[] = {
+ { ONF_EXP_ONF, "ONF Extensions" },
+ { ONF_EXP_BUTE, "Budapest University of Technology and Economics" },
+ { ONF_EXP_NOVIFLOW, "NoviFlow" },
+ { ONF_EXP_L3, "L3+ Extensions, Vendor Neutral" },
+ { ONF_EXP_L4L7, "L4-L7 Extensions" },
+ { ONF_EXP_WMOB, "Wireless and Mobility Extensions" },
+ { ONF_EXP_FABS, "Forwarding Abstractions Extensions" },
+ { ONF_EXP_OTRANS, "Optical Transport Extensions" },
+ { ONF_EXP_NBLNCTU, "Network Benchmarking Lab, NCTU" },
+ { ONF_EXP_MPCE, "Mobile Packet Core Extensions" },
+ { ONF_EXP_MPLSTPSPTN, "MPLS-TP OpenFlow Extensions for SPTN" },
+ { 0, NULL }
+};
+
+const char *
+of_vendor_name(const uint32_t vendor)
+{
+ const struct tok *table = (vendor & 0xff000000) == 0 ? oui_values : onf_exp_str;
+ return tok2str(table, "unknown", vendor);
+}
+
+void
+of_bitmap_print(netdissect_options *ndo,
+ const struct tok *t, const uint32_t v, const uint32_t u)
+{
+ /* Assigned bits? */
+ if (v & ~u)
+ ND_PRINT(" (%s)", bittok2str(t, "", v));
+ /* Unassigned bits? */
+ if (v & u)
+ ND_PRINT(" (bogus)");
+}
+
+void
+of_data_print(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ if (len == 0)
+ return;
+ /* data */
+ ND_PRINT("\n\t data (%u octets)", len);
+ if (ndo->ndo_vflag >= 2)
+ hex_and_ascii_print(ndo, "\n\t ", cp, len);
+ else
+ ND_TCHECK_LEN(cp, len);
+}
+
+static void
+of_message_print(netdissect_options *ndo,
+ const u_char *cp, uint16_t len,
+ const struct of_msgtypeinfo *mti)
+{
+ /*
+ * Here "cp" and "len" stand for the message part beyond the common
+ * OpenFlow 1.0 header, if any.
+ *
+ * Most message types are longer than just the header, and the length
+ * constraints may be complex. When possible, validate the constraint
+ * completely here (REQ_FIXLEN), otherwise check that the message is
+ * long enough to begin the decoding (REQ_MINLEN) and have the
+ * type-specific function do any remaining validation.
+ */
+
+ if (!mti)
+ goto tcheck_remainder;
+
+ if ((mti->req_what == REQ_FIXLEN && len != mti->req_value) ||
+ (mti->req_what == REQ_MINLEN && len < mti->req_value))
+ goto invalid;
+
+ if (!ndo->ndo_vflag || !mti->decoder)
+ goto tcheck_remainder;
+
+ mti->decoder(ndo, cp, len);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+tcheck_remainder:
+ ND_TCHECK_LEN(cp, len);
+}
+
+/* Print a TCP segment worth of OpenFlow messages presuming the segment begins
+ * on a message boundary. */
+void
+openflow_print(netdissect_options *ndo, const u_char *cp, u_int len)
+{
+ ndo->ndo_protocol = "openflow";
+ ND_PRINT(": OpenFlow");
+ while (len) {
+ /* Print a single OpenFlow message. */
+ uint8_t version, type;
+ uint16_t length;
+ const struct of_msgtypeinfo *mti;
+
+ /* version */
+ version = GET_U_1(cp);
+ OF_FWD(1);
+ ND_PRINT("\n\tversion %s",
+ tok2str(ofver_str, "unknown (0x%02x)", version));
+ /* type */
+ if (len < 1)
+ goto partial_header;
+ type = GET_U_1(cp);
+ OF_FWD(1);
+ mti =
+ version == OF_VER_1_0 ? of10_identify_msgtype(type) :
+ version == OF_VER_1_3 ? of13_identify_msgtype(type) :
+ NULL;
+ if (mti && mti->name)
+ ND_PRINT(", type %s", mti->name);
+ else
+ ND_PRINT(", type unknown (0x%02x)", type);
+ /* length */
+ if (len < 2)
+ goto partial_header;
+ length = GET_BE_U_2(cp);
+ OF_FWD(2);
+ ND_PRINT(", length %u%s", length,
+ length < OF_HEADER_FIXLEN ? " (too short!)" : "");
+ /* xid */
+ if (len < 4)
+ goto partial_header;
+ ND_PRINT(", xid 0x%08x", GET_BE_U_4(cp));
+ OF_FWD(4);
+
+ /*
+ * When a TCP packet can contain several protocol messages,
+ * and at the same time a protocol message can span several
+ * TCP packets, decoding an incomplete message at the end of
+ * a TCP packet requires attention to detail in this loop.
+ *
+ * Message length includes the header length and a message
+ * always includes the basic header. A message length underrun
+ * fails decoding of the rest of the current packet. At the
+ * same time, try decoding as much of the current message as
+ * possible even when it does not end within the current TCP
+ * segment.
+ *
+ * Specifically, to try to process the message body in this
+ * iteration do NOT require the header "length" to be small
+ * enough for the full declared OpenFlow message to fit into
+ * the remainder of the declared TCP segment, same as the full
+ * declared TCP segment is not required to fit into the
+ * captured packet buffer.
+ *
+ * But DO require the same at the end of this iteration to
+ * decrement "len" and to proceed to the next iteration.
+ * (Ideally the declared TCP payload end will be at or after
+ * the captured packet buffer end, but stay safe even when
+ * that's somehow not the case.)
+ */
+ if (length < OF_HEADER_FIXLEN)
+ goto invalid;
+
+ of_message_print(ndo, cp, length - OF_HEADER_FIXLEN, mti);
+ if (length - OF_HEADER_FIXLEN > len)
+ break;
+ OF_FWD(length - OF_HEADER_FIXLEN);
+ } /* while (len) */
+ return;
+
+partial_header:
+ ND_PRINT(" (end of TCP payload)");
+ ND_TCHECK_LEN(cp, len);
+ return;
+invalid: /* fail the current packet */
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(cp, len);
+}
diff --git a/print-ospf.c b/print-ospf.c
new file mode 100644
index 0000000..c370bda
--- /dev/null
+++ b/print-ospf.c
@@ -0,0 +1,1178 @@
+/*
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * OSPF support contributed by Jeffrey Honig (jch@mitchell.cit.cornell.edu)
+ */
+
+/* \summary: Open Shortest Path First (OSPF) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+#include "gmpls.h"
+
+#include "ospf.h"
+
+
+static const struct tok ospf_option_values[] = {
+ { OSPF_OPTION_MT, "MultiTopology" }, /* draft-ietf-ospf-mt-09 */
+ { OSPF_OPTION_E, "External" },
+ { OSPF_OPTION_MC, "Multicast" },
+ { OSPF_OPTION_NP, "NSSA" },
+ { OSPF_OPTION_L, "LLS" },
+ { OSPF_OPTION_DC, "Demand Circuit" },
+ { OSPF_OPTION_O, "Opaque" },
+ { OSPF_OPTION_DN, "Up/Down" },
+ { 0, NULL }
+};
+
+static const struct tok ospf_authtype_values[] = {
+ { OSPF_AUTH_NONE, "none" },
+ { OSPF_AUTH_SIMPLE, "simple" },
+ { OSPF_AUTH_MD5, "MD5" },
+ { 0, NULL }
+};
+
+static const struct tok ospf_rla_flag_values[] = {
+ { RLA_FLAG_B, "ABR" },
+ { RLA_FLAG_E, "ASBR" },
+ { RLA_FLAG_W1, "Virtual" },
+ { RLA_FLAG_W2, "W2" },
+ { 0, NULL }
+};
+
+static const struct tok type2str[] = {
+ { OSPF_TYPE_HELLO, "Hello" },
+ { OSPF_TYPE_DD, "Database Description" },
+ { OSPF_TYPE_LS_REQ, "LS-Request" },
+ { OSPF_TYPE_LS_UPDATE, "LS-Update" },
+ { OSPF_TYPE_LS_ACK, "LS-Ack" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_values[] = {
+ { LS_TYPE_ROUTER, "Router" },
+ { LS_TYPE_NETWORK, "Network" },
+ { LS_TYPE_SUM_IP, "Summary" },
+ { LS_TYPE_SUM_ABR, "ASBR Summary" },
+ { LS_TYPE_ASE, "External" },
+ { LS_TYPE_GROUP, "Multicast Group" },
+ { LS_TYPE_NSSA, "NSSA" },
+ { LS_TYPE_OPAQUE_LL, "Link Local Opaque" },
+ { LS_TYPE_OPAQUE_AL, "Area Local Opaque" },
+ { LS_TYPE_OPAQUE_DW, "Domain Wide Opaque" },
+ { 0, NULL }
+};
+
+static const struct tok ospf_dd_flag_values[] = {
+ { OSPF_DB_INIT, "Init" },
+ { OSPF_DB_MORE, "More" },
+ { OSPF_DB_MASTER, "Master" },
+ { OSPF_DB_RESYNC, "OOBResync" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_opaque_values[] = {
+ { LS_OPAQUE_TYPE_TE, "Traffic Engineering" },
+ { LS_OPAQUE_TYPE_GRACE, "Graceful restart" },
+ { LS_OPAQUE_TYPE_RI, "Router Information" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_opaque_te_tlv_values[] = {
+ { LS_OPAQUE_TE_TLV_ROUTER, "Router Address" },
+ { LS_OPAQUE_TE_TLV_LINK, "Link" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_opaque_te_link_tlv_subtlv_values[] = {
+ { LS_OPAQUE_TE_LINK_SUBTLV_LINK_TYPE, "Link Type" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_LINK_ID, "Link ID" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_LOCAL_IP, "Local Interface IP address" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_REMOTE_IP, "Remote Interface IP address" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_TE_METRIC, "Traffic Engineering Metric" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_MAX_BW, "Maximum Bandwidth" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_MAX_RES_BW, "Maximum Reservable Bandwidth" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_UNRES_BW, "Unreserved Bandwidth" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_ADMIN_GROUP, "Administrative Group" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_LINK_LOCAL_REMOTE_ID, "Link Local/Remote Identifier" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_LINK_PROTECTION_TYPE, "Link Protection Type" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_INTF_SW_CAP_DESCR, "Interface Switching Capability" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_SHARED_RISK_GROUP, "Shared Risk Link Group" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_BW_CONSTRAINTS, "Bandwidth Constraints" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_opaque_grace_tlv_values[] = {
+ { LS_OPAQUE_GRACE_TLV_PERIOD, "Grace Period" },
+ { LS_OPAQUE_GRACE_TLV_REASON, "Graceful restart Reason" },
+ { LS_OPAQUE_GRACE_TLV_INT_ADDRESS, "IPv4 interface address" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_opaque_grace_tlv_reason_values[] = {
+ { LS_OPAQUE_GRACE_TLV_REASON_UNKNOWN, "Unknown" },
+ { LS_OPAQUE_GRACE_TLV_REASON_SW_RESTART, "Software Restart" },
+ { LS_OPAQUE_GRACE_TLV_REASON_SW_UPGRADE, "Software Reload/Upgrade" },
+ { LS_OPAQUE_GRACE_TLV_REASON_CP_SWITCH, "Control Processor Switch" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_opaque_te_tlv_link_type_sub_tlv_values[] = {
+ { LS_OPAQUE_TE_LINK_SUBTLV_LINK_TYPE_PTP, "Point-to-point" },
+ { LS_OPAQUE_TE_LINK_SUBTLV_LINK_TYPE_MA, "Multi-Access" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_opaque_ri_tlv_values[] = {
+ { LS_OPAQUE_RI_TLV_CAP, "Router Capabilities" },
+ { 0, NULL }
+};
+
+static const struct tok lsa_opaque_ri_tlv_cap_values[] = {
+ { 1, "Reserved" },
+ { 2, "Reserved" },
+ { 4, "Reserved" },
+ { 8, "Reserved" },
+ { 16, "graceful restart capable" },
+ { 32, "graceful restart helper" },
+ { 64, "Stub router support" },
+ { 128, "Traffic engineering" },
+ { 256, "p2p over LAN" },
+ { 512, "path computation server" },
+ { 0, NULL }
+};
+
+static const struct tok ospf_lls_tlv_values[] = {
+ { OSPF_LLS_EO, "Extended Options" },
+ { OSPF_LLS_MD5, "MD5 Authentication" },
+ { 0, NULL }
+};
+
+static const struct tok ospf_lls_eo_options[] = {
+ { OSPF_LLS_EO_LR, "LSDB resync" },
+ { OSPF_LLS_EO_RS, "Restart" },
+ { 0, NULL }
+};
+
+int
+ospf_grace_lsa_print(netdissect_options *ndo,
+ const u_char *tptr, u_int ls_length)
+{
+ u_int tlv_type, tlv_length;
+
+
+ while (ls_length > 0) {
+ ND_TCHECK_4(tptr);
+ if (ls_length < 4) {
+ ND_PRINT("\n\t Remaining LS length %u < 4", ls_length);
+ return -1;
+ }
+ tlv_type = GET_BE_U_2(tptr);
+ tlv_length = GET_BE_U_2(tptr + 2);
+ tptr+=4;
+ ls_length-=4;
+
+ ND_PRINT("\n\t %s TLV (%u), length %u, value: ",
+ tok2str(lsa_opaque_grace_tlv_values,"unknown",tlv_type),
+ tlv_type,
+ tlv_length);
+
+ if (tlv_length > ls_length) {
+ ND_PRINT("\n\t Bogus length %u > %u", tlv_length,
+ ls_length);
+ return -1;
+ }
+
+ /* Infinite loop protection. */
+ if (tlv_type == 0 || tlv_length ==0) {
+ return -1;
+ }
+
+ ND_TCHECK_LEN(tptr, tlv_length);
+ switch(tlv_type) {
+
+ case LS_OPAQUE_GRACE_TLV_PERIOD:
+ if (tlv_length != 4) {
+ ND_PRINT("\n\t Bogus length %u != 4", tlv_length);
+ return -1;
+ }
+ ND_PRINT("%us", GET_BE_U_4(tptr));
+ break;
+
+ case LS_OPAQUE_GRACE_TLV_REASON:
+ if (tlv_length != 1) {
+ ND_PRINT("\n\t Bogus length %u != 1", tlv_length);
+ return -1;
+ }
+ ND_PRINT("%s (%u)",
+ tok2str(lsa_opaque_grace_tlv_reason_values, "Unknown", GET_U_1(tptr)),
+ GET_U_1(tptr));
+ break;
+
+ case LS_OPAQUE_GRACE_TLV_INT_ADDRESS:
+ if (tlv_length != 4) {
+ ND_PRINT("\n\t Bogus length %u != 4", tlv_length);
+ return -1;
+ }
+ ND_PRINT("%s", GET_IPADDR_STRING(tptr));
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ if (!print_unknown_data(ndo, tptr, "\n\t ", tlv_length))
+ return -1;
+ }
+ break;
+
+ }
+ /* in OSPF everything has to be 32-bit aligned, including TLVs */
+ if (tlv_length%4 != 0)
+ tlv_length+=4-(tlv_length%4);
+ ls_length-=tlv_length;
+ tptr+=tlv_length;
+ }
+
+ return 0;
+trunc:
+ return -1;
+}
+
+int
+ospf_te_lsa_print(netdissect_options *ndo,
+ const u_char *tptr, u_int ls_length)
+{
+ u_int tlv_type, tlv_length, subtlv_type, subtlv_length;
+ u_int priority_level, te_class, count_srlg;
+ union { /* int to float conversion buffer for several subTLVs */
+ float f;
+ uint32_t i;
+ } bw;
+
+ while (ls_length != 0) {
+ ND_TCHECK_4(tptr);
+ if (ls_length < 4) {
+ ND_PRINT("\n\t Remaining LS length %u < 4", ls_length);
+ return -1;
+ }
+ tlv_type = GET_BE_U_2(tptr);
+ tlv_length = GET_BE_U_2(tptr + 2);
+ tptr+=4;
+ ls_length-=4;
+
+ ND_PRINT("\n\t %s TLV (%u), length: %u",
+ tok2str(lsa_opaque_te_tlv_values,"unknown",tlv_type),
+ tlv_type,
+ tlv_length);
+
+ if (tlv_length > ls_length) {
+ ND_PRINT("\n\t Bogus length %u > %u", tlv_length,
+ ls_length);
+ return -1;
+ }
+
+ /* Infinite loop protection. */
+ if (tlv_type == 0 || tlv_length ==0) {
+ return -1;
+ }
+
+ switch(tlv_type) {
+ case LS_OPAQUE_TE_TLV_LINK:
+ while (tlv_length != 0) {
+ if (tlv_length < 4) {
+ ND_PRINT("\n\t Remaining TLV length %u < 4",
+ tlv_length);
+ return -1;
+ }
+ subtlv_type = GET_BE_U_2(tptr);
+ subtlv_length = GET_BE_U_2(tptr + 2);
+ tptr+=4;
+ tlv_length-=4;
+
+ /* Infinite loop protection */
+ if (subtlv_type == 0 || subtlv_length == 0)
+ goto invalid;
+
+ ND_PRINT("\n\t %s subTLV (%u), length: %u",
+ tok2str(lsa_opaque_te_link_tlv_subtlv_values,"unknown",subtlv_type),
+ subtlv_type,
+ subtlv_length);
+
+ if (tlv_length < subtlv_length) {
+ ND_PRINT("\n\t Remaining TLV length %u < %u",
+ tlv_length + 4, subtlv_length + 4);
+ return -1;
+ }
+ ND_TCHECK_LEN(tptr, subtlv_length);
+ switch(subtlv_type) {
+ case LS_OPAQUE_TE_LINK_SUBTLV_ADMIN_GROUP:
+ if (subtlv_length != 4) {
+ ND_PRINT(" != 4");
+ goto invalid;
+ }
+ ND_PRINT(", 0x%08x", GET_BE_U_4(tptr));
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_LINK_ID:
+ case LS_OPAQUE_TE_LINK_SUBTLV_LINK_LOCAL_REMOTE_ID:
+ if (subtlv_length != 4 && subtlv_length != 8) {
+ ND_PRINT(" != 4 && != 8");
+ goto invalid;
+ }
+ ND_PRINT(", %s (0x%08x)",
+ GET_IPADDR_STRING(tptr),
+ GET_BE_U_4(tptr));
+ if (subtlv_length == 8) /* rfc4203 */
+ ND_PRINT(", %s (0x%08x)",
+ GET_IPADDR_STRING(tptr+4),
+ GET_BE_U_4(tptr + 4));
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_LOCAL_IP:
+ case LS_OPAQUE_TE_LINK_SUBTLV_REMOTE_IP:
+ if (subtlv_length != 4) {
+ ND_PRINT(" != 4");
+ goto invalid;
+ }
+ ND_PRINT(", %s", GET_IPADDR_STRING(tptr));
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_MAX_BW:
+ case LS_OPAQUE_TE_LINK_SUBTLV_MAX_RES_BW:
+ if (subtlv_length != 4) {
+ ND_PRINT(" != 4");
+ goto invalid;
+ }
+ bw.i = GET_BE_U_4(tptr);
+ ND_PRINT(", %.3f Mbps", bw.f * 8 / 1000000);
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_UNRES_BW:
+ if (subtlv_length != 32) {
+ ND_PRINT(" != 32");
+ goto invalid;
+ }
+ for (te_class = 0; te_class < 8; te_class++) {
+ bw.i = GET_BE_U_4(tptr + te_class * 4);
+ ND_PRINT("\n\t\tTE-Class %u: %.3f Mbps",
+ te_class,
+ bw.f * 8 / 1000000);
+ }
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_BW_CONSTRAINTS:
+ if (subtlv_length < 4) {
+ ND_PRINT(" < 4");
+ goto invalid;
+ }
+ /* BC Model Id (1 octet) + Reserved (3 octets) */
+ ND_PRINT("\n\t\tBandwidth Constraints Model ID: %s (%u)",
+ tok2str(diffserv_te_bc_values, "unknown", GET_U_1(tptr)),
+ GET_U_1(tptr));
+ if (subtlv_length % 4 != 0) {
+ ND_PRINT("\n\t\tlength %u != N x 4", subtlv_length);
+ goto invalid;
+ }
+ if (subtlv_length > 36) {
+ ND_PRINT("\n\t\tlength %u > 36", subtlv_length);
+ goto invalid;
+ }
+ /* decode BCs until the subTLV ends */
+ for (te_class = 0; te_class < (subtlv_length-4)/4; te_class++) {
+ bw.i = GET_BE_U_4(tptr + 4 + te_class * 4);
+ ND_PRINT("\n\t\t Bandwidth constraint CT%u: %.3f Mbps",
+ te_class,
+ bw.f * 8 / 1000000);
+ }
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_TE_METRIC:
+ if (subtlv_length != 4) {
+ ND_PRINT(" != 4");
+ goto invalid;
+ }
+ ND_PRINT(", Metric %u", GET_BE_U_4(tptr));
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_LINK_PROTECTION_TYPE:
+ /* Protection Cap (1 octet) + Reserved ((3 octets) */
+ if (subtlv_length != 4) {
+ ND_PRINT(" != 4");
+ goto invalid;
+ }
+ ND_PRINT(", %s",
+ bittok2str(gmpls_link_prot_values, "none", GET_U_1(tptr)));
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_INTF_SW_CAP_DESCR:
+ if (subtlv_length < 36) {
+ ND_PRINT(" < 36");
+ goto invalid;
+ }
+ /* Switching Cap (1 octet) + Encoding (1) + Reserved (2) */
+ ND_PRINT("\n\t\tInterface Switching Capability: %s",
+ tok2str(gmpls_switch_cap_values, "Unknown", GET_U_1((tptr))));
+ ND_PRINT("\n\t\tLSP Encoding: %s\n\t\tMax LSP Bandwidth:",
+ tok2str(gmpls_encoding_values, "Unknown", GET_U_1((tptr + 1))));
+ for (priority_level = 0; priority_level < 8; priority_level++) {
+ bw.i = GET_BE_U_4(tptr + 4 + (priority_level * 4));
+ ND_PRINT("\n\t\t priority level %u: %.3f Mbps",
+ priority_level,
+ bw.f * 8 / 1000000);
+ }
+ break;
+ case LS_OPAQUE_TE_LINK_SUBTLV_LINK_TYPE:
+ if (subtlv_length != 1) {
+ ND_PRINT(" != 1");
+ goto invalid;
+ }
+ ND_PRINT(", %s (%u)",
+ tok2str(lsa_opaque_te_tlv_link_type_sub_tlv_values,"unknown",GET_U_1(tptr)),
+ GET_U_1(tptr));
+ break;
+
+ case LS_OPAQUE_TE_LINK_SUBTLV_SHARED_RISK_GROUP:
+ if (subtlv_length % 4 != 0) {
+ ND_PRINT(" != N x 4");
+ goto invalid;
+ }
+ count_srlg = subtlv_length / 4;
+ if (count_srlg != 0)
+ ND_PRINT("\n\t\t Shared risk group: ");
+ while (count_srlg > 0) {
+ bw.i = GET_BE_U_4(tptr);
+ ND_PRINT("%u", bw.i);
+ tptr+=4;
+ count_srlg--;
+ if (count_srlg > 0)
+ ND_PRINT(", ");
+ }
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ if (!print_unknown_data(ndo, tptr, "\n\t\t", subtlv_length))
+ return -1;
+ }
+ break;
+ }
+ /* in OSPF everything has to be 32-bit aligned, including subTLVs */
+ if (subtlv_length%4 != 0)
+ subtlv_length+=4-(subtlv_length%4);
+
+ if (tlv_length < subtlv_length) {
+ ND_PRINT("\n\t Remaining TLV length %u < %u",
+ tlv_length + 4, subtlv_length + 4);
+ return -1;
+ }
+ tlv_length-=subtlv_length;
+ tptr+=subtlv_length;
+
+ }
+ break;
+
+ case LS_OPAQUE_TE_TLV_ROUTER:
+ if (tlv_length < 4) {
+ ND_PRINT("\n\t TLV length %u < 4", tlv_length);
+ return -1;
+ }
+ ND_PRINT(", %s", GET_IPADDR_STRING(tptr));
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ if (!print_unknown_data(ndo, tptr, "\n\t ", tlv_length))
+ return -1;
+ }
+ break;
+ }
+ /* in OSPF everything has to be 32-bit aligned, including TLVs */
+ if (tlv_length%4 != 0)
+ tlv_length+=4-(tlv_length%4);
+ if (tlv_length > ls_length) {
+ ND_PRINT("\n\t Bogus padded length %u > %u", tlv_length,
+ ls_length);
+ return -1;
+ }
+ ls_length-=tlv_length;
+ tptr+=tlv_length;
+ }
+ return 0;
+trunc:
+ return -1;
+invalid:
+ nd_print_invalid(ndo);
+ return -1;
+}
+
+static int
+ospf_print_lshdr(netdissect_options *ndo,
+ const struct lsa_hdr *lshp)
+{
+ u_int ls_type;
+ u_int ls_length;
+
+ ls_length = GET_BE_U_2(lshp->ls_length);
+ if (ls_length < sizeof(struct lsa_hdr)) {
+ ND_PRINT("\n\t Bogus length %u < header (%zu)", ls_length,
+ sizeof(struct lsa_hdr));
+ return(-1);
+ }
+ ND_PRINT("\n\t Advertising Router %s, seq 0x%08x, age %us, length %zu",
+ GET_IPADDR_STRING(lshp->ls_router),
+ GET_BE_U_4(lshp->ls_seq),
+ GET_BE_U_2(lshp->ls_age),
+ ls_length - sizeof(struct lsa_hdr));
+ ls_type = GET_U_1(lshp->ls_type);
+ switch (ls_type) {
+ /* the LSA header for opaque LSAs was slightly changed */
+ case LS_TYPE_OPAQUE_LL:
+ case LS_TYPE_OPAQUE_AL:
+ case LS_TYPE_OPAQUE_DW:
+ ND_PRINT("\n\t %s LSA (%u), Opaque-Type %s LSA (%u), Opaque-ID %u",
+ tok2str(lsa_values,"unknown",ls_type),
+ ls_type,
+
+ tok2str(lsa_opaque_values,
+ "unknown",
+ GET_U_1(lshp->un_lsa_id.opaque_field.opaque_type)),
+ GET_U_1(lshp->un_lsa_id.opaque_field.opaque_type),
+ GET_BE_U_3(lshp->un_lsa_id.opaque_field.opaque_id)
+
+ );
+ break;
+
+ /* all other LSA types use regular style LSA headers */
+ default:
+ ND_PRINT("\n\t %s LSA (%u), LSA-ID: %s",
+ tok2str(lsa_values,"unknown",ls_type),
+ ls_type,
+ GET_IPADDR_STRING(lshp->un_lsa_id.lsa_id));
+ break;
+ }
+ ND_PRINT("\n\t Options: [%s]",
+ bittok2str(ospf_option_values, "none", GET_U_1(lshp->ls_options)));
+
+ return (ls_length);
+}
+
+/* draft-ietf-ospf-mt-09 */
+static const struct tok ospf_topology_values[] = {
+ { 0, "default" },
+ { 1, "multicast" },
+ { 2, "management" },
+ { 0, NULL }
+};
+
+/*
+ * Print all the per-topology metrics.
+ */
+static void
+ospf_print_tos_metrics(netdissect_options *ndo,
+ const union un_tos *tos)
+{
+ u_int metric_count;
+ u_int toscount;
+ u_int tos_type;
+
+ toscount = GET_U_1(tos->link.link_tos_count)+1;
+ metric_count = 0;
+
+ /*
+ * All but the first metric contain a valid topology id.
+ */
+ while (toscount != 0) {
+ tos_type = GET_U_1(tos->metrics.tos_type);
+ ND_PRINT("\n\t\ttopology %s (%u), metric %u",
+ tok2str(ospf_topology_values, "Unknown",
+ metric_count ? tos_type : 0),
+ metric_count ? tos_type : 0,
+ GET_BE_U_2(tos->metrics.tos_metric));
+ metric_count++;
+ tos++;
+ toscount--;
+ }
+}
+
+/*
+ * Print a single link state advertisement. If truncated or if LSA length
+ * field is less than the length of the LSA header, return NULl, else
+ * return pointer to data past end of LSA.
+ */
+static const uint8_t *
+ospf_print_lsa(netdissect_options *ndo,
+ const struct lsa *lsap)
+{
+ const uint8_t *ls_end;
+ const struct rlalink *rlp;
+ const nd_ipv4 *ap;
+ const struct aslametric *almp;
+ const struct mcla *mcp;
+ const uint8_t *lp;
+ u_int tlv_type, tlv_length, rla_count, topology;
+ int ospf_print_lshdr_ret;
+ u_int ls_length;
+ const uint8_t *tptr;
+
+ tptr = (const uint8_t *)lsap->lsa_un.un_unknown; /* squelch compiler warnings */
+ ospf_print_lshdr_ret = ospf_print_lshdr(ndo, &lsap->ls_hdr);
+ if (ospf_print_lshdr_ret < 0)
+ return(NULL);
+ ls_length = (u_int)ospf_print_lshdr_ret;
+ ls_end = (const uint8_t *)lsap + ls_length;
+ /*
+ * ospf_print_lshdr() returns -1 if the length is too short,
+ * so we know ls_length is >= sizeof(struct lsa_hdr).
+ */
+ ls_length -= sizeof(struct lsa_hdr);
+
+ switch (GET_U_1(lsap->ls_hdr.ls_type)) {
+
+ case LS_TYPE_ROUTER:
+ ND_PRINT("\n\t Router LSA Options: [%s]",
+ bittok2str(ospf_rla_flag_values, "none", GET_U_1(lsap->lsa_un.un_rla.rla_flags)));
+
+ rla_count = GET_BE_U_2(lsap->lsa_un.un_rla.rla_count);
+ ND_TCHECK_SIZE(lsap->lsa_un.un_rla.rla_link);
+ rlp = lsap->lsa_un.un_rla.rla_link;
+ for (u_int i = rla_count; i != 0; i--) {
+ ND_TCHECK_SIZE(rlp);
+ switch (GET_U_1(rlp->un_tos.link.link_type)) {
+
+ case RLA_TYPE_VIRTUAL:
+ ND_PRINT("\n\t Virtual Link: Neighbor Router-ID: %s, Interface Address: %s",
+ GET_IPADDR_STRING(rlp->link_id),
+ GET_IPADDR_STRING(rlp->link_data));
+ break;
+
+ case RLA_TYPE_ROUTER:
+ ND_PRINT("\n\t Neighbor Router-ID: %s, Interface Address: %s",
+ GET_IPADDR_STRING(rlp->link_id),
+ GET_IPADDR_STRING(rlp->link_data));
+ break;
+
+ case RLA_TYPE_TRANSIT:
+ ND_PRINT("\n\t Neighbor Network-ID: %s, Interface Address: %s",
+ GET_IPADDR_STRING(rlp->link_id),
+ GET_IPADDR_STRING(rlp->link_data));
+ break;
+
+ case RLA_TYPE_STUB:
+ ND_PRINT("\n\t Stub Network: %s, Mask: %s",
+ GET_IPADDR_STRING(rlp->link_id),
+ GET_IPADDR_STRING(rlp->link_data));
+ break;
+
+ default:
+ ND_PRINT("\n\t Unknown Router Link Type (%u)",
+ GET_U_1(rlp->un_tos.link.link_type));
+ return (ls_end);
+ }
+
+ ospf_print_tos_metrics(ndo, &rlp->un_tos);
+
+ rlp = (const struct rlalink *)((const u_char *)(rlp + 1) +
+ (GET_U_1(rlp->un_tos.link.link_tos_count) * sizeof(union un_tos)));
+ }
+ break;
+
+ case LS_TYPE_NETWORK:
+ ND_PRINT("\n\t Mask %s\n\t Connected Routers:",
+ GET_IPADDR_STRING(lsap->lsa_un.un_nla.nla_mask));
+ ap = lsap->lsa_un.un_nla.nla_router;
+ while ((const u_char *)ap < ls_end) {
+ ND_TCHECK_SIZE(ap);
+ ND_PRINT("\n\t %s", GET_IPADDR_STRING(*ap));
+ ++ap;
+ }
+ break;
+
+ case LS_TYPE_SUM_IP:
+ ND_TCHECK_4(lsap->lsa_un.un_nla.nla_mask);
+ ND_PRINT("\n\t Mask %s",
+ GET_IPADDR_STRING(lsap->lsa_un.un_sla.sla_mask));
+ ND_TCHECK_SIZE(lsap->lsa_un.un_sla.sla_tosmetric);
+ lp = (const uint8_t *)lsap->lsa_un.un_sla.sla_tosmetric;
+ while (lp < ls_end) {
+ uint32_t ul;
+
+ ul = GET_BE_U_4(lp);
+ topology = (ul & SLA_MASK_TOS) >> SLA_SHIFT_TOS;
+ ND_PRINT("\n\t\ttopology %s (%u) metric %u",
+ tok2str(ospf_topology_values, "Unknown", topology),
+ topology,
+ ul & SLA_MASK_METRIC);
+ lp += 4;
+ }
+ break;
+
+ case LS_TYPE_SUM_ABR:
+ ND_TCHECK_SIZE(lsap->lsa_un.un_sla.sla_tosmetric);
+ lp = (const uint8_t *)lsap->lsa_un.un_sla.sla_tosmetric;
+ while (lp < ls_end) {
+ uint32_t ul;
+
+ ul = GET_BE_U_4(lp);
+ topology = (ul & SLA_MASK_TOS) >> SLA_SHIFT_TOS;
+ ND_PRINT("\n\t\ttopology %s (%u) metric %u",
+ tok2str(ospf_topology_values, "Unknown", topology),
+ topology,
+ ul & SLA_MASK_METRIC);
+ lp += 4;
+ }
+ break;
+
+ case LS_TYPE_ASE:
+ case LS_TYPE_NSSA: /* fall through - those LSAs share the same format */
+ ND_TCHECK_4(lsap->lsa_un.un_nla.nla_mask);
+ ND_PRINT("\n\t Mask %s",
+ GET_IPADDR_STRING(lsap->lsa_un.un_asla.asla_mask));
+
+ ND_TCHECK_SIZE(lsap->lsa_un.un_sla.sla_tosmetric);
+ almp = lsap->lsa_un.un_asla.asla_metric;
+ while ((const u_char *)almp < ls_end) {
+ uint32_t ul;
+
+ ul = GET_BE_U_4(almp->asla_tosmetric);
+ topology = ((ul & ASLA_MASK_TOS) >> ASLA_SHIFT_TOS);
+ ND_PRINT("\n\t\ttopology %s (%u), type %u, metric",
+ tok2str(ospf_topology_values, "Unknown", topology),
+ topology,
+ (ul & ASLA_FLAG_EXTERNAL) ? 2 : 1);
+ if ((ul & ASLA_MASK_METRIC) == 0xffffff)
+ ND_PRINT(" infinite");
+ else
+ ND_PRINT(" %u", (ul & ASLA_MASK_METRIC));
+
+ if (GET_IPV4_TO_NETWORK_ORDER(almp->asla_forward) != 0) {
+ ND_PRINT(", forward %s", GET_IPADDR_STRING(almp->asla_forward));
+ }
+ if (GET_IPV4_TO_NETWORK_ORDER(almp->asla_tag) != 0) {
+ ND_PRINT(", tag %s", GET_IPADDR_STRING(almp->asla_tag));
+ }
+ ++almp;
+ }
+ break;
+
+ case LS_TYPE_GROUP:
+ /* Multicast extensions as of 23 July 1991 */
+ mcp = lsap->lsa_un.un_mcla;
+ while ((const u_char *)mcp < ls_end) {
+ switch (GET_BE_U_4(mcp->mcla_vtype)) {
+
+ case MCLA_VERTEX_ROUTER:
+ ND_PRINT("\n\t Router Router-ID %s",
+ GET_IPADDR_STRING(mcp->mcla_vid));
+ break;
+
+ case MCLA_VERTEX_NETWORK:
+ ND_PRINT("\n\t Network Designated Router %s",
+ GET_IPADDR_STRING(mcp->mcla_vid));
+ break;
+
+ default:
+ ND_PRINT("\n\t unknown VertexType (%u)",
+ GET_BE_U_4(mcp->mcla_vtype));
+ break;
+ }
+ ++mcp;
+ }
+ break;
+
+ case LS_TYPE_OPAQUE_LL: /* fall through */
+ case LS_TYPE_OPAQUE_AL:
+ case LS_TYPE_OPAQUE_DW:
+
+ switch (GET_U_1(lsap->ls_hdr.un_lsa_id.opaque_field.opaque_type)) {
+ case LS_OPAQUE_TYPE_RI:
+ tptr = (const uint8_t *)(lsap->lsa_un.un_ri_tlv);
+
+ u_int ls_length_remaining = ls_length;
+ while (ls_length_remaining != 0) {
+ ND_TCHECK_4(tptr);
+ if (ls_length_remaining < 4) {
+ ND_PRINT("\n\t Remaining LS length %u < 4", ls_length_remaining);
+ return(ls_end);
+ }
+ tlv_type = GET_BE_U_2(tptr);
+ tlv_length = GET_BE_U_2(tptr + 2);
+ tptr+=4;
+ ls_length_remaining-=4;
+
+ ND_PRINT("\n\t %s TLV (%u), length: %u, value: ",
+ tok2str(lsa_opaque_ri_tlv_values,"unknown",tlv_type),
+ tlv_type,
+ tlv_length);
+
+ if (tlv_length > ls_length_remaining) {
+ ND_PRINT("\n\t Bogus length %u > remaining LS length %u", tlv_length,
+ ls_length_remaining);
+ return(ls_end);
+ }
+ ND_TCHECK_LEN(tptr, tlv_length);
+ switch(tlv_type) {
+
+ case LS_OPAQUE_RI_TLV_CAP:
+ if (tlv_length != 4) {
+ ND_PRINT("\n\t Bogus length %u != 4", tlv_length);
+ return(ls_end);
+ }
+ ND_PRINT("Capabilities: %s",
+ bittok2str(lsa_opaque_ri_tlv_cap_values, "Unknown", GET_BE_U_4(tptr)));
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ if (!print_unknown_data(ndo, tptr, "\n\t ", tlv_length))
+ return(ls_end);
+ }
+ break;
+
+ }
+ tptr+=tlv_length;
+ ls_length_remaining-=tlv_length;
+ }
+ break;
+
+ case LS_OPAQUE_TYPE_GRACE:
+ if (ospf_grace_lsa_print(ndo, (const u_char *)(lsap->lsa_un.un_grace_tlv),
+ ls_length) == -1) {
+ return(ls_end);
+ }
+ break;
+
+ case LS_OPAQUE_TYPE_TE:
+ if (ospf_te_lsa_print(ndo, (const u_char *)(lsap->lsa_un.un_te_lsa_tlv),
+ ls_length) == -1) {
+ return(ls_end);
+ }
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ if (!print_unknown_data(ndo, (const uint8_t *)lsap->lsa_un.un_unknown,
+ "\n\t ", ls_length))
+ return(ls_end);
+ }
+ break;
+ }
+ }
+
+ /* do we want to see an additionally hexdump ? */
+ if (ndo->ndo_vflag> 1)
+ if (!print_unknown_data(ndo, (const uint8_t *)lsap->lsa_un.un_unknown,
+ "\n\t ", ls_length)) {
+ return(ls_end);
+ }
+
+ return (ls_end);
+trunc:
+ return (NULL);
+}
+
+static void
+ospf_decode_lls(netdissect_options *ndo,
+ const struct ospfhdr *op, u_int length)
+{
+ const u_char *dptr;
+ const u_char *dataend;
+ u_int length2;
+ uint16_t lls_type, lls_len;
+ uint32_t lls_flags;
+
+ switch (GET_U_1(op->ospf_type)) {
+
+ case OSPF_TYPE_HELLO:
+ if (!(GET_U_1(op->ospf_hello.hello_options) & OSPF_OPTION_L))
+ return;
+ break;
+
+ case OSPF_TYPE_DD:
+ if (!(GET_U_1(op->ospf_db.db_options) & OSPF_OPTION_L))
+ return;
+ break;
+
+ default:
+ return;
+ }
+
+ /* dig deeper if LLS data is available; see RFC4813 */
+ length2 = GET_BE_U_2(op->ospf_len);
+ dptr = (const u_char *)op + length2;
+ dataend = (const u_char *)op + length;
+
+ if (GET_BE_U_2(op->ospf_authtype) == OSPF_AUTH_MD5) {
+ dptr = dptr + GET_U_1(op->ospf_authdata + 3);
+ length2 += GET_U_1(op->ospf_authdata + 3);
+ }
+ if (length2 >= length) {
+ ND_PRINT("\n\t[LLS truncated]");
+ return;
+ }
+ ND_PRINT("\n\t LLS: checksum: 0x%04x", (u_int) GET_BE_U_2(dptr));
+
+ dptr += 2;
+ length2 = GET_BE_U_2(dptr);
+ ND_PRINT(", length: %u", length2);
+
+ dptr += 2;
+ while (dptr < dataend) {
+ lls_type = GET_BE_U_2(dptr);
+ ND_PRINT("\n\t %s (%u)",
+ tok2str(ospf_lls_tlv_values,"Unknown TLV",lls_type),
+ lls_type);
+ dptr += 2;
+ lls_len = GET_BE_U_2(dptr);
+ ND_PRINT(", length: %u", lls_len);
+ dptr += 2;
+ switch (lls_type) {
+
+ case OSPF_LLS_EO:
+ if (lls_len != 4) {
+ ND_PRINT(" [should be 4]");
+ lls_len = 4;
+ }
+ lls_flags = GET_BE_U_4(dptr);
+ ND_PRINT("\n\t Options: 0x%08x [%s]", lls_flags,
+ bittok2str(ospf_lls_eo_options, "?", lls_flags));
+
+ break;
+
+ case OSPF_LLS_MD5:
+ if (lls_len != 20) {
+ ND_PRINT(" [should be 20]");
+ lls_len = 20;
+ }
+ ND_PRINT("\n\t Sequence number: 0x%08x", GET_BE_U_4(dptr));
+ break;
+ }
+
+ dptr += lls_len;
+ }
+}
+
+static int
+ospf_decode_v2(netdissect_options *ndo,
+ const struct ospfhdr *op, const u_char *dataend)
+{
+ const nd_ipv4 *ap;
+ const struct lsr *lsrp;
+ const struct lsa_hdr *lshp;
+ const struct lsa *lsap;
+ uint32_t lsa_count,lsa_count_max;
+
+ switch (GET_U_1(op->ospf_type)) {
+
+ case OSPF_TYPE_HELLO:
+ ND_PRINT("\n\tOptions [%s]",
+ bittok2str(ospf_option_values,"none",GET_U_1(op->ospf_hello.hello_options)));
+
+ ND_PRINT("\n\t Hello Timer %us, Dead Timer %us, Mask %s, Priority %u",
+ GET_BE_U_2(op->ospf_hello.hello_helloint),
+ GET_BE_U_4(op->ospf_hello.hello_deadint),
+ GET_IPADDR_STRING(op->ospf_hello.hello_mask),
+ GET_U_1(op->ospf_hello.hello_priority));
+
+ if (GET_IPV4_TO_NETWORK_ORDER(op->ospf_hello.hello_dr) != 0)
+ ND_PRINT("\n\t Designated Router %s",
+ GET_IPADDR_STRING(op->ospf_hello.hello_dr));
+
+ if (GET_IPV4_TO_NETWORK_ORDER(op->ospf_hello.hello_bdr) != 0)
+ ND_PRINT(", Backup Designated Router %s",
+ GET_IPADDR_STRING(op->ospf_hello.hello_bdr));
+
+ ap = op->ospf_hello.hello_neighbor;
+ if ((const u_char *)ap < dataend)
+ ND_PRINT("\n\t Neighbor List:");
+ while ((const u_char *)ap < dataend) {
+ ND_TCHECK_SIZE(ap);
+ ND_PRINT("\n\t %s", GET_IPADDR_STRING(*ap));
+ ++ap;
+ }
+ break; /* HELLO */
+
+ case OSPF_TYPE_DD:
+ ND_PRINT("\n\tOptions [%s]",
+ bittok2str(ospf_option_values, "none", GET_U_1(op->ospf_db.db_options)));
+ ND_PRINT(", DD Flags [%s]",
+ bittok2str(ospf_dd_flag_values, "none", GET_U_1(op->ospf_db.db_flags)));
+ if (GET_BE_U_2(op->ospf_db.db_ifmtu)) {
+ ND_PRINT(", MTU: %u",
+ GET_BE_U_2(op->ospf_db.db_ifmtu));
+ }
+ ND_PRINT(", Sequence: 0x%08x", GET_BE_U_4(op->ospf_db.db_seq));
+
+ /* Print all the LS adv's */
+ lshp = op->ospf_db.db_lshdr;
+ while (((const u_char *)lshp < dataend) && ospf_print_lshdr(ndo, lshp) != -1) {
+ ++lshp;
+ }
+ break;
+
+ case OSPF_TYPE_LS_REQ:
+ lsrp = op->ospf_lsr;
+ while ((const u_char *)lsrp < dataend) {
+ ND_TCHECK_SIZE(lsrp);
+
+ ND_PRINT("\n\t Advertising Router: %s, %s LSA (%u)",
+ GET_IPADDR_STRING(lsrp->ls_router),
+ tok2str(lsa_values,"unknown",GET_BE_U_4(lsrp->ls_type)),
+ GET_BE_U_4(lsrp->ls_type));
+
+ switch (GET_BE_U_4(lsrp->ls_type)) {
+ /* the LSA header for opaque LSAs was slightly changed */
+ case LS_TYPE_OPAQUE_LL:
+ case LS_TYPE_OPAQUE_AL:
+ case LS_TYPE_OPAQUE_DW:
+ ND_PRINT(", Opaque-Type: %s LSA (%u), Opaque-ID: %u",
+ tok2str(lsa_opaque_values, "unknown",GET_U_1(lsrp->un_ls_stateid.opaque_field.opaque_type)),
+ GET_U_1(lsrp->un_ls_stateid.opaque_field.opaque_type),
+ GET_BE_U_3(lsrp->un_ls_stateid.opaque_field.opaque_id));
+ break;
+ default:
+ ND_PRINT(", LSA-ID: %s",
+ GET_IPADDR_STRING(lsrp->un_ls_stateid.ls_stateid));
+ break;
+ }
+
+ ++lsrp;
+ }
+ break;
+
+ case OSPF_TYPE_LS_UPDATE:
+ lsap = op->ospf_lsu.lsu_lsa;
+ lsa_count_max = GET_BE_U_4(op->ospf_lsu.lsu_count);
+ ND_PRINT(", %u LSA%s", lsa_count_max, PLURAL_SUFFIX(lsa_count_max));
+ for (lsa_count=1;lsa_count <= lsa_count_max;lsa_count++) {
+ ND_PRINT("\n\t LSA #%u", lsa_count);
+ lsap = (const struct lsa *)ospf_print_lsa(ndo, lsap);
+ if (lsap == NULL)
+ goto trunc;
+ }
+ break;
+
+ case OSPF_TYPE_LS_ACK:
+ lshp = op->ospf_lsa.lsa_lshdr;
+ while (ospf_print_lshdr(ndo, lshp) != -1) {
+ ++lshp;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return (0);
+trunc:
+ return (1);
+}
+
+void
+ospf_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2 _U_)
+{
+ const struct ospfhdr *op;
+ const u_char *dataend;
+ const char *cp;
+
+ ndo->ndo_protocol = "ospf2";
+ op = (const struct ospfhdr *)bp;
+
+ /* XXX Before we do anything else, strip off the MD5 trailer */
+ if (GET_BE_U_2(op->ospf_authtype) == OSPF_AUTH_MD5) {
+ length -= OSPF_AUTH_MD5_LEN;
+ ndo->ndo_snapend -= OSPF_AUTH_MD5_LEN;
+ }
+
+ /* If the type is valid translate it, or just print the type */
+ /* value. If it's not valid, say so and return */
+ cp = tok2str(type2str, "unknown LS-type %u", GET_U_1(op->ospf_type));
+ ND_PRINT("OSPFv%u, %s, length %u", GET_U_1(op->ospf_version), cp,
+ length);
+ if (*cp == 'u')
+ return;
+
+ if (!ndo->ndo_vflag) { /* non verbose - so lets bail out here */
+ return;
+ }
+
+ if (length != GET_BE_U_2(op->ospf_len)) {
+ ND_PRINT(" [len %u]", GET_BE_U_2(op->ospf_len));
+ }
+
+ if (length > GET_BE_U_2(op->ospf_len)) {
+ dataend = bp + GET_BE_U_2(op->ospf_len);
+ } else {
+ dataend = bp + length;
+ }
+
+ ND_PRINT("\n\tRouter-ID %s", GET_IPADDR_STRING(op->ospf_routerid));
+
+ if (GET_IPV4_TO_NETWORK_ORDER(op->ospf_areaid) != 0)
+ ND_PRINT(", Area %s", GET_IPADDR_STRING(op->ospf_areaid));
+ else
+ ND_PRINT(", Backbone Area");
+
+ if (ndo->ndo_vflag) {
+ /* Print authentication data (should we really do this?) */
+ ND_TCHECK_LEN(op->ospf_authdata, sizeof(op->ospf_authdata));
+
+ ND_PRINT(", Authentication Type: %s (%u)",
+ tok2str(ospf_authtype_values, "unknown", GET_BE_U_2(op->ospf_authtype)),
+ GET_BE_U_2(op->ospf_authtype));
+
+ switch (GET_BE_U_2(op->ospf_authtype)) {
+
+ case OSPF_AUTH_NONE:
+ break;
+
+ case OSPF_AUTH_SIMPLE:
+ ND_PRINT("\n\tSimple text password: ");
+ nd_printjnp(ndo, op->ospf_authdata, OSPF_AUTH_SIMPLE_LEN);
+ break;
+
+ case OSPF_AUTH_MD5:
+ ND_PRINT("\n\tKey-ID: %u, Auth-Length: %u, Crypto Sequence Number: 0x%08x",
+ GET_U_1(op->ospf_authdata + 2),
+ GET_U_1(op->ospf_authdata + 3),
+ GET_BE_U_4((op->ospf_authdata) + 4));
+ break;
+
+ default:
+ return;
+ }
+ }
+ /* Do rest according to version. */
+ switch (GET_U_1(op->ospf_version)) {
+
+ case 2:
+ /* ospf version 2 */
+ if (ospf_decode_v2(ndo, op, dataend))
+ goto trunc;
+ if (length > GET_BE_U_2(op->ospf_len))
+ ospf_decode_lls(ndo, op, length);
+ break;
+
+ default:
+ ND_PRINT(" ospf [version %u]", GET_U_1(op->ospf_version));
+ break;
+ } /* end switch on version */
+
+ return;
+trunc:
+ nd_trunc_longjmp(ndo);
+}
diff --git a/print-ospf6.c b/print-ospf6.c
new file mode 100644
index 0000000..1b862b3
--- /dev/null
+++ b/print-ospf6.c
@@ -0,0 +1,1005 @@
+/*
+ * Copyright (c) 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * OSPF support contributed by Jeffrey Honig (jch@mitchell.cit.cornell.edu)
+ */
+
+/* \summary: IPv6 Open Shortest Path First (OSPFv3) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ospf.h"
+
+#define OSPF_TYPE_HELLO 1 /* Hello */
+#define OSPF_TYPE_DD 2 /* Database Description */
+#define OSPF_TYPE_LS_REQ 3 /* Link State Request */
+#define OSPF_TYPE_LS_UPDATE 4 /* Link State Update */
+#define OSPF_TYPE_LS_ACK 5 /* Link State Ack */
+
+/* Options *_options */
+#define OSPF6_OPTION_V6 0x01 /* V6 bit: A bit for peeping tom */
+#define OSPF6_OPTION_E 0x02 /* E bit: External routes advertised */
+#define OSPF6_OPTION_MC 0x04 /* MC bit: Multicast capable */
+#define OSPF6_OPTION_N 0x08 /* N bit: For type-7 LSA */
+#define OSPF6_OPTION_R 0x10 /* R bit: Router bit */
+#define OSPF6_OPTION_DC 0x20 /* DC bit: Demand circuits */
+/* The field is actually 24-bit (RFC5340 Section A.2). */
+#define OSPF6_OPTION_AF 0x0100 /* AF bit: Multiple address families */
+#define OSPF6_OPTION_L 0x0200 /* L bit: Link-local signaling (LLS) */
+#define OSPF6_OPTION_AT 0x0400 /* AT bit: Authentication trailer */
+
+
+/* db_flags */
+#define OSPF6_DB_INIT 0x04 /* */
+#define OSPF6_DB_MORE 0x02
+#define OSPF6_DB_MASTER 0x01
+#define OSPF6_DB_M6 0x10 /* IPv6 MTU */
+
+/* ls_type */
+#define LS_TYPE_ROUTER 1 /* router link */
+#define LS_TYPE_NETWORK 2 /* network link */
+#define LS_TYPE_INTER_AP 3 /* Inter-Area-Prefix */
+#define LS_TYPE_INTER_AR 4 /* Inter-Area-Router */
+#define LS_TYPE_ASE 5 /* ASE */
+#define LS_TYPE_GROUP 6 /* Group membership */
+#define LS_TYPE_NSSA 7 /* NSSA */
+#define LS_TYPE_LINK 8 /* Link LSA */
+#define LS_TYPE_INTRA_AP 9 /* Intra-Area-Prefix */
+#define LS_TYPE_INTRA_ATE 10 /* Intra-Area-TE */
+#define LS_TYPE_GRACE 11 /* Grace LSA */
+#define LS_TYPE_RI 12 /* Router information */
+#define LS_TYPE_INTER_ASTE 13 /* Inter-AS-TE */
+#define LS_TYPE_L1VPN 14 /* L1VPN */
+#define LS_TYPE_MASK 0x1fff
+
+#define LS_SCOPE_LINKLOCAL 0x0000
+#define LS_SCOPE_AREA 0x2000
+#define LS_SCOPE_AS 0x4000
+#define LS_SCOPE_MASK 0x6000
+#define LS_SCOPE_U 0x8000
+
+/* rla_link.link_type */
+#define RLA_TYPE_ROUTER 1 /* point-to-point to another router */
+#define RLA_TYPE_TRANSIT 2 /* connection to transit network */
+#define RLA_TYPE_VIRTUAL 4 /* virtual link */
+
+/* rla_flags */
+#define RLA_FLAG_B 0x01
+#define RLA_FLAG_E 0x02
+#define RLA_FLAG_V 0x04
+#define RLA_FLAG_W 0x08
+#define RLA_FLAG_Nt 0x10
+
+/* lsa_prefix options */
+#define LSA_PREFIX_OPT_NU 0x01
+#define LSA_PREFIX_OPT_LA 0x02
+#define LSA_PREFIX_OPT_MC 0x04
+#define LSA_PREFIX_OPT_P 0x08
+#define LSA_PREFIX_OPT_DN 0x10
+#define LSA_PREFIX_OPT_N 0x20
+
+/* sla_tosmetric breakdown */
+#define SLA_MASK_TOS 0x7f000000
+#define SLA_MASK_METRIC 0x00ffffff
+#define SLA_SHIFT_TOS 24
+
+/* asla_metric */
+#define ASLA_FLAG_FWDADDR 0x02000000
+#define ASLA_FLAG_ROUTETAG 0x01000000
+#define ASLA_MASK_METRIC 0x00ffffff
+
+/* RFC6506 Section 4.1 */
+#define OSPF6_AT_HDRLEN 16U
+#define OSPF6_AUTH_TYPE_HMAC 0x0001
+
+typedef nd_uint32_t rtrid_t;
+
+/* link state advertisement header */
+struct lsa6_hdr {
+ nd_uint16_t ls_age;
+ nd_uint16_t ls_type;
+ rtrid_t ls_stateid;
+ rtrid_t ls_router;
+ nd_uint32_t ls_seq;
+ nd_uint16_t ls_chksum;
+ nd_uint16_t ls_length;
+};
+
+/* Length of an IPv6 address, in bytes. */
+#define IPV6_ADDR_LEN_BYTES (128/8)
+
+struct lsa6_prefix {
+ nd_uint8_t lsa_p_len;
+ nd_uint8_t lsa_p_opt;
+ nd_uint16_t lsa_p_metric;
+ nd_byte lsa_p_prefix[IPV6_ADDR_LEN_BYTES]; /* maximum length */
+};
+
+/* link state advertisement */
+struct lsa6 {
+ struct lsa6_hdr ls_hdr;
+
+ /* Link state types */
+ union {
+ /* Router links advertisements */
+ struct {
+ union {
+ nd_uint8_t flg;
+ nd_uint32_t opt;
+ } rla_flgandopt;
+#define rla_flags rla_flgandopt.flg
+#define rla_options rla_flgandopt.opt
+ struct rlalink6 {
+ nd_uint8_t link_type;
+ nd_byte link_zero;
+ nd_uint16_t link_metric;
+ nd_uint32_t link_ifid;
+ nd_uint32_t link_nifid;
+ rtrid_t link_nrtid;
+ } rla_link[1]; /* may repeat */
+ } un_rla;
+
+ /* Network links advertisements */
+ struct {
+ nd_uint32_t nla_options;
+ rtrid_t nla_router[1]; /* may repeat */
+ } un_nla;
+
+ /* Inter Area Prefix LSA */
+ struct {
+ nd_uint32_t inter_ap_metric;
+ struct lsa6_prefix inter_ap_prefix[1];
+ } un_inter_ap;
+
+ /* AS external links advertisements */
+ struct {
+ nd_uint32_t asla_metric;
+ struct lsa6_prefix asla_prefix[1];
+ /* some optional fields follow */
+ } un_asla;
+
+#if 0
+ /* Summary links advertisements */
+ struct {
+ nd_ipv4 sla_mask;
+ nd_uint32_t sla_tosmetric[1]; /* may repeat */
+ } un_sla;
+
+ /* Multicast group membership */
+ struct mcla {
+ nd_uint32_t mcla_vtype;
+ nd_ipv4 mcla_vid;
+ } un_mcla[1];
+#endif
+
+ /* Type 7 LSA */
+
+ /* Link LSA */
+ struct llsa {
+ union {
+ nd_uint8_t pri;
+ nd_uint32_t opt;
+ } llsa_priandopt;
+#define llsa_priority llsa_priandopt.pri
+#define llsa_options llsa_priandopt.opt
+ nd_ipv6 llsa_lladdr;
+ nd_uint32_t llsa_nprefix;
+ struct lsa6_prefix llsa_prefix[1];
+ } un_llsa;
+
+ /* Intra-Area-Prefix */
+ struct {
+ nd_uint16_t intra_ap_nprefix;
+ nd_uint16_t intra_ap_lstype;
+ rtrid_t intra_ap_lsid;
+ rtrid_t intra_ap_rtid;
+ struct lsa6_prefix intra_ap_prefix[1];
+ } un_intra_ap;
+ } lsa_un;
+};
+
+/*
+ * the main header
+ */
+struct ospf6hdr {
+ nd_uint8_t ospf6_version;
+ nd_uint8_t ospf6_type;
+ nd_uint16_t ospf6_len;
+ rtrid_t ospf6_routerid;
+ rtrid_t ospf6_areaid;
+ nd_uint16_t ospf6_chksum;
+ nd_uint8_t ospf6_instanceid;
+ nd_uint8_t ospf6_rsvd;
+};
+
+/*
+ * The OSPF6 header length is 16 bytes, regardless of how your compiler
+ * might choose to pad the above structure.
+ */
+#define OSPF6HDR_LEN 16
+
+/* Hello packet */
+struct hello6 {
+ nd_uint32_t hello_ifid;
+ union {
+ nd_uint8_t pri;
+ nd_uint32_t opt;
+ } hello_priandopt;
+#define hello_priority hello_priandopt.pri
+#define hello_options hello_priandopt.opt
+ nd_uint16_t hello_helloint;
+ nd_uint16_t hello_deadint;
+ rtrid_t hello_dr;
+ rtrid_t hello_bdr;
+ rtrid_t hello_neighbor[1]; /* may repeat */
+};
+
+/* Database Description packet */
+struct dd6 {
+ nd_uint32_t db_options;
+ nd_uint16_t db_mtu;
+ nd_uint8_t db_mbz;
+ nd_uint8_t db_flags;
+ nd_uint32_t db_seq;
+ struct lsa6_hdr db_lshdr[1]; /* may repeat */
+};
+
+/* Link State Request */
+struct lsr6 {
+ nd_uint16_t ls_mbz;
+ nd_uint16_t ls_type;
+ rtrid_t ls_stateid;
+ rtrid_t ls_router;
+};
+
+/* Link State Update */
+struct lsu6 {
+ nd_uint32_t lsu_count;
+ struct lsa6 lsu_lsa[1]; /* may repeat */
+};
+
+
+static const struct tok ospf6_option_values[] = {
+ { OSPF6_OPTION_V6, "V6" },
+ { OSPF6_OPTION_E, "External" },
+ { OSPF6_OPTION_MC, "Deprecated" },
+ { OSPF6_OPTION_N, "NSSA" },
+ { OSPF6_OPTION_R, "Router" },
+ { OSPF6_OPTION_DC, "Demand Circuit" },
+ { OSPF6_OPTION_AF, "AFs Support" },
+ { OSPF6_OPTION_L, "LLS" },
+ { OSPF6_OPTION_AT, "Authentication Trailer" },
+ { 0, NULL }
+};
+
+static const struct tok ospf6_rla_flag_values[] = {
+ { RLA_FLAG_B, "ABR" },
+ { RLA_FLAG_E, "External" },
+ { RLA_FLAG_V, "Virtual-Link Endpoint" },
+ { RLA_FLAG_W, "Deprecated" },
+ { RLA_FLAG_Nt, "NSSA Translator" },
+ { 0, NULL }
+};
+
+static const struct tok ospf6_asla_flag_values[] = {
+ { ASLA_FLAG_EXTERNAL, "External Type 2" },
+ { ASLA_FLAG_FWDADDR, "Forwarding" },
+ { ASLA_FLAG_ROUTETAG, "Tag" },
+ { 0, NULL }
+};
+
+static const struct tok ospf6_type_values[] = {
+ { OSPF_TYPE_HELLO, "Hello" },
+ { OSPF_TYPE_DD, "Database Description" },
+ { OSPF_TYPE_LS_REQ, "LS-Request" },
+ { OSPF_TYPE_LS_UPDATE, "LS-Update" },
+ { OSPF_TYPE_LS_ACK, "LS-Ack" },
+ { 0, NULL }
+};
+
+static const struct tok ospf6_lsa_values[] = {
+ { LS_TYPE_ROUTER, "Router" },
+ { LS_TYPE_NETWORK, "Network" },
+ { LS_TYPE_INTER_AP, "Inter-Area Prefix" },
+ { LS_TYPE_INTER_AR, "Inter-Area Router" },
+ { LS_TYPE_ASE, "External" },
+ { LS_TYPE_GROUP, "Deprecated" },
+ { LS_TYPE_NSSA, "NSSA" },
+ { LS_TYPE_LINK, "Link" },
+ { LS_TYPE_INTRA_AP, "Intra-Area Prefix" },
+ { LS_TYPE_INTRA_ATE, "Intra-Area TE" },
+ { LS_TYPE_GRACE, "Grace" },
+ { LS_TYPE_RI, "Router Information" },
+ { LS_TYPE_INTER_ASTE, "Inter-AS-TE" },
+ { LS_TYPE_L1VPN, "Layer 1 VPN" },
+ { 0, NULL }
+};
+
+static const struct tok ospf6_ls_scope_values[] = {
+ { LS_SCOPE_LINKLOCAL, "Link Local" },
+ { LS_SCOPE_AREA, "Area Local" },
+ { LS_SCOPE_AS, "Domain Wide" },
+ { 0, NULL }
+};
+
+static const struct tok ospf6_dd_flag_values[] = {
+ { OSPF6_DB_INIT, "Init" },
+ { OSPF6_DB_MORE, "More" },
+ { OSPF6_DB_MASTER, "Master" },
+ { OSPF6_DB_M6, "IPv6 MTU" },
+ { 0, NULL }
+};
+
+static const struct tok ospf6_lsa_prefix_option_values[] = {
+ { LSA_PREFIX_OPT_NU, "No Unicast" },
+ { LSA_PREFIX_OPT_LA, "Local address" },
+ { LSA_PREFIX_OPT_MC, "Deprecated" },
+ { LSA_PREFIX_OPT_P, "Propagate" },
+ { LSA_PREFIX_OPT_DN, "Down" },
+ { LSA_PREFIX_OPT_N, "N-bit" },
+ { 0, NULL }
+};
+
+static const struct tok ospf6_auth_type_str[] = {
+ { OSPF6_AUTH_TYPE_HMAC, "HMAC" },
+ { 0, NULL }
+};
+
+static void
+ospf6_print_ls_type(netdissect_options *ndo,
+ u_int ls_type, const rtrid_t *ls_stateid)
+{
+ ND_PRINT("\n\t %s LSA (%u), %s Scope%s, LSA-ID %s",
+ tok2str(ospf6_lsa_values, "Unknown", ls_type & LS_TYPE_MASK),
+ ls_type & LS_TYPE_MASK,
+ tok2str(ospf6_ls_scope_values, "Unknown", ls_type & LS_SCOPE_MASK),
+ ls_type &0x8000 ? ", transitive" : "", /* U-bit */
+ GET_IPADDR_STRING((const u_char *)ls_stateid));
+}
+
+static int
+ospf6_print_lshdr(netdissect_options *ndo,
+ const struct lsa6_hdr *lshp, const u_char *dataend)
+{
+ if ((const u_char *)(lshp + 1) > dataend)
+ goto trunc;
+
+ ND_PRINT("\n\t Advertising Router %s, seq 0x%08x, age %us, length %zu",
+ GET_IPADDR_STRING(lshp->ls_router),
+ GET_BE_U_4(lshp->ls_seq),
+ GET_BE_U_2(lshp->ls_age),
+ GET_BE_U_2(lshp->ls_length)-sizeof(struct lsa6_hdr));
+
+ ospf6_print_ls_type(ndo, GET_BE_U_2(lshp->ls_type),
+ &lshp->ls_stateid);
+
+ return (0);
+trunc:
+ return (1);
+}
+
+static int
+ospf6_print_lsaprefix(netdissect_options *ndo,
+ const uint8_t *tptr, u_int lsa_length)
+{
+ const struct lsa6_prefix *lsapp = (const struct lsa6_prefix *)tptr;
+ u_int wordlen;
+ nd_ipv6 prefix;
+
+ if (lsa_length < sizeof (*lsapp) - IPV6_ADDR_LEN_BYTES)
+ goto trunc;
+ lsa_length -= sizeof (*lsapp) - IPV6_ADDR_LEN_BYTES;
+ ND_TCHECK_LEN(lsapp, sizeof(*lsapp) - IPV6_ADDR_LEN_BYTES);
+ wordlen = (GET_U_1(lsapp->lsa_p_len) + 31) / 32;
+ if (wordlen * 4 > sizeof(nd_ipv6)) {
+ ND_PRINT(" bogus prefixlen /%u", GET_U_1(lsapp->lsa_p_len));
+ goto trunc;
+ }
+ if (lsa_length < wordlen * 4)
+ goto trunc;
+ lsa_length -= wordlen * 4;
+ ND_TCHECK_LEN(lsapp->lsa_p_prefix, wordlen * 4);
+ memset(prefix, 0, sizeof(prefix));
+ memcpy(prefix, lsapp->lsa_p_prefix, wordlen * 4);
+ ND_PRINT("\n\t\t%s/%u", ip6addr_string(ndo, prefix), /* local buffer, not packet data; don't use GET_IP6ADDR_STRING() */
+ GET_U_1(lsapp->lsa_p_len));
+ if (GET_U_1(lsapp->lsa_p_opt)) {
+ ND_PRINT(", Options [%s]",
+ bittok2str(ospf6_lsa_prefix_option_values,
+ "none", GET_U_1(lsapp->lsa_p_opt)));
+ }
+ ND_PRINT(", metric %u", GET_BE_U_2(lsapp->lsa_p_metric));
+ return sizeof(*lsapp) - IPV6_ADDR_LEN_BYTES + wordlen * 4;
+
+trunc:
+ return -1;
+}
+
+
+/*
+ * Print a single link state advertisement. If truncated return 1, else 0.
+ */
+static int
+ospf6_print_lsa(netdissect_options *ndo,
+ const struct lsa6 *lsap, const u_char *dataend)
+{
+ const struct rlalink6 *rlp;
+#if 0
+ const struct tos_metric *tosp;
+#endif
+ const rtrid_t *ap;
+#if 0
+ const struct aslametric *almp;
+ const struct mcla *mcp;
+#endif
+ const struct llsa *llsap;
+ const struct lsa6_prefix *lsapp;
+#if 0
+ const uint32_t *lp;
+#endif
+ u_int prefixes;
+ int bytelen;
+ u_int length, lsa_length;
+ uint32_t flags32;
+ const uint8_t *tptr;
+
+ if (ospf6_print_lshdr(ndo, &lsap->ls_hdr, dataend))
+ return (1);
+ length = GET_BE_U_2(lsap->ls_hdr.ls_length);
+
+ /*
+ * The LSA length includes the length of the header;
+ * it must have a value that's at least that length.
+ * If it does, find the length of what follows the
+ * header.
+ */
+ if (length < sizeof(struct lsa6_hdr) || (const u_char *)lsap + length > dataend)
+ return (1);
+ lsa_length = length - sizeof(struct lsa6_hdr);
+ tptr = (const uint8_t *)lsap+sizeof(struct lsa6_hdr);
+
+ switch (GET_BE_U_2(lsap->ls_hdr.ls_type)) {
+ case LS_TYPE_ROUTER | LS_SCOPE_AREA:
+ if (lsa_length < sizeof (lsap->lsa_un.un_rla.rla_options))
+ return (1);
+ lsa_length -= sizeof (lsap->lsa_un.un_rla.rla_options);
+ ND_PRINT("\n\t Options [%s]",
+ bittok2str(ospf6_option_values, "none",
+ GET_BE_U_4(lsap->lsa_un.un_rla.rla_options)));
+ ND_PRINT(", RLA-Flags [%s]",
+ bittok2str(ospf6_rla_flag_values, "none",
+ GET_U_1(lsap->lsa_un.un_rla.rla_flags)));
+
+ rlp = lsap->lsa_un.un_rla.rla_link;
+ while (lsa_length != 0) {
+ if (lsa_length < sizeof (*rlp))
+ return (1);
+ lsa_length -= sizeof (*rlp);
+ ND_TCHECK_SIZE(rlp);
+ switch (GET_U_1(rlp->link_type)) {
+
+ case RLA_TYPE_VIRTUAL:
+ ND_PRINT("\n\t Virtual Link: Neighbor Router-ID %s"
+ "\n\t Neighbor Interface-ID %s, Interface %s",
+ GET_IPADDR_STRING(rlp->link_nrtid),
+ GET_IPADDR_STRING(rlp->link_nifid),
+ GET_IPADDR_STRING(rlp->link_ifid));
+ break;
+
+ case RLA_TYPE_ROUTER:
+ ND_PRINT("\n\t Neighbor Router-ID %s"
+ "\n\t Neighbor Interface-ID %s, Interface %s",
+ GET_IPADDR_STRING(rlp->link_nrtid),
+ GET_IPADDR_STRING(rlp->link_nifid),
+ GET_IPADDR_STRING(rlp->link_ifid));
+ break;
+
+ case RLA_TYPE_TRANSIT:
+ ND_PRINT("\n\t Neighbor Network-ID %s"
+ "\n\t Neighbor Interface-ID %s, Interface %s",
+ GET_IPADDR_STRING(rlp->link_nrtid),
+ GET_IPADDR_STRING(rlp->link_nifid),
+ GET_IPADDR_STRING(rlp->link_ifid));
+ break;
+
+ default:
+ ND_PRINT("\n\t Unknown Router Links Type 0x%02x",
+ GET_U_1(rlp->link_type));
+ return (0);
+ }
+ ND_PRINT(", metric %u", GET_BE_U_2(rlp->link_metric));
+ rlp++;
+ }
+ break;
+
+ case LS_TYPE_NETWORK | LS_SCOPE_AREA:
+ if (lsa_length < sizeof (lsap->lsa_un.un_nla.nla_options))
+ return (1);
+ lsa_length -= sizeof (lsap->lsa_un.un_nla.nla_options);
+ ND_PRINT("\n\t Options [%s]",
+ bittok2str(ospf6_option_values, "none",
+ GET_BE_U_4(lsap->lsa_un.un_nla.nla_options)));
+
+ ND_PRINT("\n\t Connected Routers:");
+ ap = lsap->lsa_un.un_nla.nla_router;
+ while (lsa_length != 0) {
+ if (lsa_length < sizeof (*ap))
+ return (1);
+ lsa_length -= sizeof (*ap);
+ ND_TCHECK_SIZE(ap);
+ ND_PRINT("\n\t\t%s", GET_IPADDR_STRING(*ap));
+ ++ap;
+ }
+ break;
+
+ case LS_TYPE_INTER_AP | LS_SCOPE_AREA:
+ if (lsa_length < sizeof (lsap->lsa_un.un_inter_ap.inter_ap_metric))
+ return (1);
+ lsa_length -= sizeof (lsap->lsa_un.un_inter_ap.inter_ap_metric);
+ ND_PRINT(", metric %u",
+ GET_BE_U_4(lsap->lsa_un.un_inter_ap.inter_ap_metric) & SLA_MASK_METRIC);
+
+ tptr = (const uint8_t *)lsap->lsa_un.un_inter_ap.inter_ap_prefix;
+ while (lsa_length != 0) {
+ bytelen = ospf6_print_lsaprefix(ndo, tptr, lsa_length);
+ if (bytelen < 0)
+ goto trunc;
+ /*
+ * ospf6_print_lsaprefix() will return -1 if
+ * the length is too high, so this will not
+ * underflow.
+ */
+ lsa_length -= bytelen;
+ tptr += bytelen;
+ }
+ break;
+
+ case LS_TYPE_ASE | LS_SCOPE_AS:
+ if (lsa_length < sizeof (lsap->lsa_un.un_asla.asla_metric))
+ return (1);
+ lsa_length -= sizeof (lsap->lsa_un.un_asla.asla_metric);
+ flags32 = GET_BE_U_4(lsap->lsa_un.un_asla.asla_metric);
+ ND_PRINT("\n\t Flags [%s]",
+ bittok2str(ospf6_asla_flag_values, "none", flags32));
+ ND_PRINT(" metric %u",
+ GET_BE_U_4(lsap->lsa_un.un_asla.asla_metric) &
+ ASLA_MASK_METRIC);
+
+ tptr = (const uint8_t *)lsap->lsa_un.un_asla.asla_prefix;
+ lsapp = (const struct lsa6_prefix *)tptr;
+ bytelen = ospf6_print_lsaprefix(ndo, tptr, lsa_length);
+ if (bytelen < 0)
+ goto trunc;
+ /*
+ * ospf6_print_lsaprefix() will return -1 if
+ * the length is too high, so this will not
+ * underflow.
+ */
+ lsa_length -= bytelen;
+ tptr += bytelen;
+
+ if ((flags32 & ASLA_FLAG_FWDADDR) != 0) {
+ if (lsa_length < sizeof (nd_ipv6))
+ return (1);
+ lsa_length -= sizeof (nd_ipv6);
+ ND_PRINT(" forward %s",
+ GET_IP6ADDR_STRING(tptr));
+ tptr += sizeof(nd_ipv6);
+ }
+
+ if ((flags32 & ASLA_FLAG_ROUTETAG) != 0) {
+ if (lsa_length < sizeof (uint32_t))
+ return (1);
+ lsa_length -= sizeof (uint32_t);
+ ND_PRINT(" tag %s",
+ GET_IPADDR_STRING(tptr));
+ tptr += sizeof(uint32_t);
+ }
+
+ if (GET_U_1(lsapp->lsa_p_metric)) {
+ if (lsa_length < sizeof (uint32_t))
+ return (1);
+ lsa_length -= sizeof (uint32_t);
+ ND_PRINT(" RefLSID: %s",
+ GET_IPADDR_STRING(tptr));
+ tptr += sizeof(uint32_t);
+ }
+ break;
+
+ case LS_TYPE_LINK:
+ /* Link LSA */
+ llsap = &lsap->lsa_un.un_llsa;
+ if (lsa_length < sizeof (llsap->llsa_priandopt))
+ return (1);
+ lsa_length -= sizeof (llsap->llsa_priandopt);
+ ND_TCHECK_SIZE(&llsap->llsa_priandopt);
+ ND_PRINT("\n\t Options [%s]",
+ bittok2str(ospf6_option_values, "none",
+ GET_BE_U_4(llsap->llsa_options)));
+
+ if (lsa_length < sizeof (llsap->llsa_lladdr) + sizeof (llsap->llsa_nprefix))
+ return (1);
+ lsa_length -= sizeof (llsap->llsa_lladdr) + sizeof (llsap->llsa_nprefix);
+ prefixes = GET_BE_U_4(llsap->llsa_nprefix);
+ ND_PRINT("\n\t Priority %u, Link-local address %s, Prefixes %u:",
+ GET_U_1(llsap->llsa_priority),
+ GET_IP6ADDR_STRING(llsap->llsa_lladdr),
+ prefixes);
+
+ tptr = (const uint8_t *)llsap->llsa_prefix;
+ while (prefixes > 0) {
+ bytelen = ospf6_print_lsaprefix(ndo, tptr, lsa_length);
+ if (bytelen < 0)
+ goto trunc;
+ prefixes--;
+ /*
+ * ospf6_print_lsaprefix() will return -1 if
+ * the length is too high, so this will not
+ * underflow.
+ */
+ lsa_length -= bytelen;
+ tptr += bytelen;
+ }
+ break;
+
+ case LS_TYPE_INTRA_AP | LS_SCOPE_AREA:
+ /* Intra-Area-Prefix LSA */
+ if (lsa_length < sizeof (lsap->lsa_un.un_intra_ap.intra_ap_rtid))
+ return (1);
+ lsa_length -= sizeof (lsap->lsa_un.un_intra_ap.intra_ap_rtid);
+ ND_TCHECK_4(lsap->lsa_un.un_intra_ap.intra_ap_rtid);
+ ospf6_print_ls_type(ndo,
+ GET_BE_U_2(lsap->lsa_un.un_intra_ap.intra_ap_lstype),
+ &lsap->lsa_un.un_intra_ap.intra_ap_lsid);
+
+ if (lsa_length < sizeof (lsap->lsa_un.un_intra_ap.intra_ap_nprefix))
+ return (1);
+ lsa_length -= sizeof (lsap->lsa_un.un_intra_ap.intra_ap_nprefix);
+ prefixes = GET_BE_U_2(lsap->lsa_un.un_intra_ap.intra_ap_nprefix);
+ ND_PRINT("\n\t Prefixes %u:", prefixes);
+
+ tptr = (const uint8_t *)lsap->lsa_un.un_intra_ap.intra_ap_prefix;
+ while (prefixes > 0) {
+ bytelen = ospf6_print_lsaprefix(ndo, tptr, lsa_length);
+ if (bytelen < 0)
+ goto trunc;
+ prefixes--;
+ /*
+ * ospf6_print_lsaprefix() will return -1 if
+ * the length is too high, so this will not
+ * underflow.
+ */
+ lsa_length -= bytelen;
+ tptr += bytelen;
+ }
+ break;
+
+ case LS_TYPE_GRACE | LS_SCOPE_LINKLOCAL:
+ if (ospf_grace_lsa_print(ndo, tptr, lsa_length) == -1) {
+ return 1;
+ }
+ break;
+
+ case LS_TYPE_INTRA_ATE | LS_SCOPE_LINKLOCAL:
+ if (ospf_te_lsa_print(ndo, tptr, lsa_length) == -1) {
+ return 1;
+ }
+ break;
+
+ default:
+ if(!print_unknown_data(ndo,tptr,
+ "\n\t ",
+ lsa_length)) {
+ return (1);
+ }
+ break;
+ }
+
+ return (0);
+trunc:
+ return (1);
+}
+
+static int
+ospf6_decode_v3(netdissect_options *ndo,
+ const struct ospf6hdr *op,
+ const u_char *dataend)
+{
+ const rtrid_t *ap;
+ const struct lsr6 *lsrp;
+ const struct lsa6_hdr *lshp;
+ const struct lsa6 *lsap;
+ int i;
+
+ switch (GET_U_1(op->ospf6_type)) {
+
+ case OSPF_TYPE_HELLO: {
+ const struct hello6 *hellop = (const struct hello6 *)((const uint8_t *)op + OSPF6HDR_LEN);
+
+ ND_PRINT("\n\tOptions [%s]",
+ bittok2str(ospf6_option_values, "none",
+ GET_BE_U_4(hellop->hello_options)));
+
+ ND_PRINT("\n\t Hello Timer %us, Dead Timer %us, Interface-ID %s, Priority %u",
+ GET_BE_U_2(hellop->hello_helloint),
+ GET_BE_U_2(hellop->hello_deadint),
+ GET_IPADDR_STRING(hellop->hello_ifid),
+ GET_U_1(hellop->hello_priority));
+
+ if (GET_BE_U_4(hellop->hello_dr) != 0)
+ ND_PRINT("\n\t Designated Router %s",
+ GET_IPADDR_STRING(hellop->hello_dr));
+ if (GET_BE_U_4(hellop->hello_bdr) != 0)
+ ND_PRINT(", Backup Designated Router %s",
+ GET_IPADDR_STRING(hellop->hello_bdr));
+ if (ndo->ndo_vflag > 1) {
+ ND_PRINT("\n\t Neighbor List:");
+ ap = hellop->hello_neighbor;
+ while ((const u_char *)ap < dataend) {
+ ND_TCHECK_SIZE(ap);
+ ND_PRINT("\n\t %s", GET_IPADDR_STRING(*ap));
+ ++ap;
+ }
+ }
+ break; /* HELLO */
+ }
+
+ case OSPF_TYPE_DD: {
+ const struct dd6 *ddp = (const struct dd6 *)((const uint8_t *)op + OSPF6HDR_LEN);
+
+ ND_PRINT("\n\tOptions [%s]",
+ bittok2str(ospf6_option_values, "none",
+ GET_BE_U_4(ddp->db_options)));
+ ND_PRINT(", DD Flags [%s]",
+ bittok2str(ospf6_dd_flag_values,"none",GET_U_1(ddp->db_flags)));
+
+ ND_PRINT(", MTU %u, DD-Sequence 0x%08x",
+ GET_BE_U_2(ddp->db_mtu),
+ GET_BE_U_4(ddp->db_seq));
+ if (ndo->ndo_vflag > 1) {
+ /* Print all the LS adv's */
+ lshp = ddp->db_lshdr;
+ while ((const u_char *)lshp < dataend) {
+ if (ospf6_print_lshdr(ndo, lshp++, dataend))
+ goto trunc;
+ }
+ }
+ break;
+ }
+
+ case OSPF_TYPE_LS_REQ:
+ if (ndo->ndo_vflag > 1) {
+ lsrp = (const struct lsr6 *)((const uint8_t *)op + OSPF6HDR_LEN);
+ while ((const u_char *)lsrp < dataend) {
+ ND_TCHECK_SIZE(lsrp);
+ ND_PRINT("\n\t Advertising Router %s",
+ GET_IPADDR_STRING(lsrp->ls_router));
+ ospf6_print_ls_type(ndo,
+ GET_BE_U_2(lsrp->ls_type),
+ &lsrp->ls_stateid);
+ ++lsrp;
+ }
+ }
+ break;
+
+ case OSPF_TYPE_LS_UPDATE:
+ if (ndo->ndo_vflag > 1) {
+ const struct lsu6 *lsup = (const struct lsu6 *)((const uint8_t *)op + OSPF6HDR_LEN);
+
+ i = GET_BE_U_4(lsup->lsu_count);
+ lsap = lsup->lsu_lsa;
+ while ((const u_char *)lsap < dataend && i--) {
+ if (ospf6_print_lsa(ndo, lsap, dataend))
+ goto trunc;
+ lsap = (const struct lsa6 *)((const u_char *)lsap +
+ GET_BE_U_2(lsap->ls_hdr.ls_length));
+ }
+ }
+ break;
+
+ case OSPF_TYPE_LS_ACK:
+ if (ndo->ndo_vflag > 1) {
+ lshp = (const struct lsa6_hdr *)((const uint8_t *)op + OSPF6HDR_LEN);
+ while ((const u_char *)lshp < dataend) {
+ if (ospf6_print_lshdr(ndo, lshp++, dataend))
+ goto trunc;
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return (0);
+trunc:
+ return (1);
+}
+
+/* RFC5613 Section 2.2 (w/o the TLVs) */
+static int
+ospf6_print_lls(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ uint16_t llsdatalen;
+
+ if (len == 0)
+ return 0;
+ if (len < OSPF_LLS_HDRLEN)
+ goto trunc;
+ /* Checksum */
+ ND_PRINT("\n\tLLS Checksum 0x%04x", GET_BE_U_2(cp));
+ cp += 2;
+ /* LLS Data Length */
+ llsdatalen = GET_BE_U_2(cp);
+ ND_PRINT(", Data Length %u", llsdatalen);
+ if (llsdatalen < OSPF_LLS_HDRLEN || llsdatalen > len)
+ goto trunc;
+ cp += 2;
+ /* LLS TLVs */
+ ND_TCHECK_LEN(cp, llsdatalen - OSPF_LLS_HDRLEN);
+ /* FIXME: code in print-ospf.c can be reused to decode the TLVs */
+
+ return llsdatalen;
+trunc:
+ return -1;
+}
+
+/* RFC6506 Section 4.1 */
+static int
+ospf6_decode_at(netdissect_options *ndo,
+ const u_char *cp, const u_int len)
+{
+ uint16_t authdatalen;
+
+ if (len == 0)
+ return 0;
+ if (len < OSPF6_AT_HDRLEN)
+ goto trunc;
+ /* Authentication Type */
+ ND_PRINT("\n\tAuthentication Type %s",
+ tok2str(ospf6_auth_type_str, "unknown (0x%04x)", GET_BE_U_2(cp)));
+ cp += 2;
+ /* Auth Data Len */
+ authdatalen = GET_BE_U_2(cp);
+ ND_PRINT(", Length %u", authdatalen);
+ if (authdatalen < OSPF6_AT_HDRLEN || authdatalen > len)
+ goto trunc;
+ cp += 2;
+ /* Reserved */
+ cp += 2;
+ /* Security Association ID */
+ ND_PRINT(", SAID %u", GET_BE_U_2(cp));
+ cp += 2;
+ /* Cryptographic Sequence Number (High-Order 32 Bits) */
+ ND_PRINT(", CSN 0x%08x", GET_BE_U_4(cp));
+ cp += 4;
+ /* Cryptographic Sequence Number (Low-Order 32 Bits) */
+ ND_PRINT(":%08x", GET_BE_U_4(cp));
+ cp += 4;
+ /* Authentication Data */
+ ND_TCHECK_LEN(cp, authdatalen - OSPF6_AT_HDRLEN);
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo,cp, "\n\tAuthentication Data ", authdatalen - OSPF6_AT_HDRLEN);
+ return 0;
+
+trunc:
+ return 1;
+}
+
+/* The trailing data may include LLS and/or AT data (in this specific order).
+ * LLS data may be present only in Hello and DBDesc packets with the L-bit set.
+ * AT data may be present in Hello and DBDesc packets with the AT-bit set or in
+ * any other packet type, thus decode the AT data regardless of the AT-bit.
+ */
+static int
+ospf6_decode_v3_trailer(netdissect_options *ndo,
+ const struct ospf6hdr *op, const u_char *cp, const unsigned len)
+{
+ uint8_t type;
+ int llslen = 0;
+ int lls_hello = 0;
+ int lls_dd = 0;
+
+ type = GET_U_1(op->ospf6_type);
+ if (type == OSPF_TYPE_HELLO) {
+ const struct hello6 *hellop = (const struct hello6 *)((const uint8_t *)op + OSPF6HDR_LEN);
+ if (GET_BE_U_4(hellop->hello_options) & OSPF6_OPTION_L)
+ lls_hello = 1;
+ } else if (type == OSPF_TYPE_DD) {
+ const struct dd6 *ddp = (const struct dd6 *)((const uint8_t *)op + OSPF6HDR_LEN);
+ if (GET_BE_U_4(ddp->db_options) & OSPF6_OPTION_L)
+ lls_dd = 1;
+ }
+ if ((lls_hello || lls_dd) && (llslen = ospf6_print_lls(ndo, cp, len)) < 0)
+ goto trunc;
+ return ospf6_decode_at(ndo, cp + llslen, len - llslen);
+
+trunc:
+ return 1;
+}
+
+void
+ospf6_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const struct ospf6hdr *op;
+ const u_char *dataend;
+ const char *cp;
+ uint16_t datalen;
+
+ ndo->ndo_protocol = "ospf3";
+ op = (const struct ospf6hdr *)bp;
+
+ /* If the type is valid translate it, or just print the type */
+ /* value. If it's not valid, say so and return */
+ cp = tok2str(ospf6_type_values, "unknown packet type (%u)",
+ GET_U_1(op->ospf6_type));
+ ND_PRINT("OSPFv%u, %s, length %u", GET_U_1(op->ospf6_version), cp,
+ length);
+ if (*cp == 'u') {
+ return;
+ }
+
+ if(!ndo->ndo_vflag) { /* non verbose - so lets bail out here */
+ return;
+ }
+
+ /* OSPFv3 data always comes first and optional trailing data may follow. */
+ datalen = GET_BE_U_2(op->ospf6_len);
+ if (datalen > length) {
+ ND_PRINT(" [len %u]", datalen);
+ return;
+ }
+ dataend = bp + datalen;
+
+ ND_PRINT("\n\tRouter-ID %s", GET_IPADDR_STRING(op->ospf6_routerid));
+
+ if (GET_BE_U_4(op->ospf6_areaid) != 0)
+ ND_PRINT(", Area %s", GET_IPADDR_STRING(op->ospf6_areaid));
+ else
+ ND_PRINT(", Backbone Area");
+ if (GET_U_1(op->ospf6_instanceid))
+ ND_PRINT(", Instance %u", GET_U_1(op->ospf6_instanceid));
+
+ /* Do rest according to version. */
+ switch (GET_U_1(op->ospf6_version)) {
+
+ case 3:
+ /* ospf version 3 */
+ if (ospf6_decode_v3(ndo, op, dataend) ||
+ ospf6_decode_v3_trailer(ndo, op, dataend, length - datalen))
+ goto trunc;
+ break;
+ } /* end switch on version */
+
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-otv.c b/print-otv.c
new file mode 100644
index 0000000..a0fe9d0
--- /dev/null
+++ b/print-otv.c
@@ -0,0 +1,76 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Francesco Fondelli (francesco dot fondelli, gmail dot com)
+ */
+
+/* \summary: Overlay Transport Virtualization (OTV) printer */
+
+/* specification: draft-hasmit-otv-04 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+#define OTV_HDR_LEN 8
+
+/*
+ * OTV header, draft-hasmit-otv-04
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|R|R|R|I|R|R|R| Overlay ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Instance ID | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+void
+otv_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ uint8_t flags;
+
+ ndo->ndo_protocol = "otv";
+ ND_PRINT("OTV, ");
+ if (len < OTV_HDR_LEN) {
+ ND_PRINT("[length %u < %u]", len, OTV_HDR_LEN);
+ goto invalid;
+ }
+
+ flags = GET_U_1(bp);
+ ND_PRINT("flags [%s] (0x%02x), ", flags & 0x08 ? "I" : ".", flags);
+ bp += 1;
+
+ ND_PRINT("overlay %u, ", GET_BE_U_3(bp));
+ bp += 3;
+
+ ND_PRINT("instance %u\n", GET_BE_U_3(bp));
+ bp += 3;
+
+ /* Reserved */
+ ND_TCHECK_1(bp);
+ bp += 1;
+
+ ether_print(ndo, bp, len - OTV_HDR_LEN, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(bp, len);
+}
diff --git a/print-pgm.c b/print-pgm.c
new file mode 100644
index 0000000..ccb0b46
--- /dev/null
+++ b/print-pgm.c
@@ -0,0 +1,829 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Andy Heffernan (ahh@juniper.net)
+ */
+
+/* \summary: Pragmatic General Multicast (PGM) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "addrtostr.h"
+
+#include "ip.h"
+#include "ip6.h"
+#include "ipproto.h"
+#include "af.h"
+
+/*
+ * PGM header (RFC 3208)
+ */
+struct pgm_header {
+ nd_uint16_t pgm_sport;
+ nd_uint16_t pgm_dport;
+ nd_uint8_t pgm_type;
+ nd_uint8_t pgm_options;
+ nd_uint16_t pgm_sum;
+ nd_byte pgm_gsid[6];
+ nd_uint16_t pgm_length;
+};
+
+struct pgm_spm {
+ nd_uint32_t pgms_seq;
+ nd_uint32_t pgms_trailseq;
+ nd_uint32_t pgms_leadseq;
+ nd_uint16_t pgms_nla_afi;
+ nd_uint16_t pgms_reserved;
+ /* ... uint8_t pgms_nla[0]; */
+ /* ... options */
+};
+
+struct pgm_nak {
+ nd_uint32_t pgmn_seq;
+ nd_uint16_t pgmn_source_afi;
+ nd_uint16_t pgmn_reserved;
+ /* ... uint8_t pgmn_source[0]; */
+ /* ... uint16_t pgmn_group_afi */
+ /* ... uint16_t pgmn_reserved2; */
+ /* ... uint8_t pgmn_group[0]; */
+ /* ... options */
+};
+
+struct pgm_ack {
+ nd_uint32_t pgma_rx_max_seq;
+ nd_uint32_t pgma_bitmap;
+ /* ... options */
+};
+
+struct pgm_poll {
+ nd_uint32_t pgmp_seq;
+ nd_uint16_t pgmp_round;
+ nd_uint16_t pgmp_subtype;
+ nd_uint16_t pgmp_nla_afi;
+ nd_uint16_t pgmp_reserved;
+ /* ... uint8_t pgmp_nla[0]; */
+ /* ... options */
+};
+
+struct pgm_polr {
+ nd_uint32_t pgmp_seq;
+ nd_uint16_t pgmp_round;
+ nd_uint16_t pgmp_reserved;
+ /* ... options */
+};
+
+struct pgm_data {
+ nd_uint32_t pgmd_seq;
+ nd_uint32_t pgmd_trailseq;
+ /* ... options */
+};
+
+typedef enum _pgm_type {
+ PGM_SPM = 0, /* source path message */
+ PGM_POLL = 1, /* POLL Request */
+ PGM_POLR = 2, /* POLL Response */
+ PGM_ODATA = 4, /* original data */
+ PGM_RDATA = 5, /* repair data */
+ PGM_NAK = 8, /* NAK */
+ PGM_NULLNAK = 9, /* Null NAK */
+ PGM_NCF = 10, /* NAK Confirmation */
+ PGM_ACK = 11, /* ACK for congestion control */
+ PGM_SPMR = 12, /* SPM request */
+ PGM_MAX = 255
+} pgm_type;
+
+#define PGM_OPT_BIT_PRESENT 0x01
+#define PGM_OPT_BIT_NETWORK 0x02
+#define PGM_OPT_BIT_VAR_PKTLEN 0x40
+#define PGM_OPT_BIT_PARITY 0x80
+
+#define PGM_OPT_LENGTH 0x00
+#define PGM_OPT_FRAGMENT 0x01
+#define PGM_OPT_NAK_LIST 0x02
+#define PGM_OPT_JOIN 0x03
+#define PGM_OPT_NAK_BO_IVL 0x04
+#define PGM_OPT_NAK_BO_RNG 0x05
+
+#define PGM_OPT_REDIRECT 0x07
+#define PGM_OPT_PARITY_PRM 0x08
+#define PGM_OPT_PARITY_GRP 0x09
+#define PGM_OPT_CURR_TGSIZE 0x0A
+#define PGM_OPT_NBR_UNREACH 0x0B
+#define PGM_OPT_PATH_NLA 0x0C
+
+#define PGM_OPT_SYN 0x0D
+#define PGM_OPT_FIN 0x0E
+#define PGM_OPT_RST 0x0F
+#define PGM_OPT_CR 0x10
+#define PGM_OPT_CRQST 0x11
+
+#define PGM_OPT_PGMCC_DATA 0x12
+#define PGM_OPT_PGMCC_FEEDBACK 0x13
+
+#define PGM_OPT_MASK 0x7f
+
+#define PGM_OPT_END 0x80 /* end of options marker */
+
+#define PGM_MIN_OPT_LEN 4
+
+void
+pgm_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2)
+{
+ const struct pgm_header *pgm;
+ const struct ip *ip;
+ uint8_t pgm_type_val;
+ uint16_t sport, dport;
+ u_int nla_afnum;
+ char nla_buf[INET6_ADDRSTRLEN];
+ const struct ip6_hdr *ip6;
+ uint8_t opt_type, opt_len;
+ uint32_t seq, opts_len, len, offset;
+
+ ndo->ndo_protocol = "pgm";
+ pgm = (const struct pgm_header *)bp;
+ ip = (const struct ip *)bp2;
+ if (IP_V(ip) == 6)
+ ip6 = (const struct ip6_hdr *)bp2;
+ else
+ ip6 = NULL;
+ if (!ND_TTEST_2(pgm->pgm_dport)) {
+ if (ip6) {
+ ND_PRINT("%s > %s:",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ GET_IP6ADDR_STRING(ip6->ip6_dst));
+ } else {
+ ND_PRINT("%s > %s:",
+ GET_IPADDR_STRING(ip->ip_src),
+ GET_IPADDR_STRING(ip->ip_dst));
+ }
+ nd_print_trunc(ndo);
+ return;
+ }
+
+ sport = GET_BE_U_2(pgm->pgm_sport);
+ dport = GET_BE_U_2(pgm->pgm_dport);
+
+ if (ip6) {
+ if (GET_U_1(ip6->ip6_nxt) == IPPROTO_PGM) {
+ ND_PRINT("%s.%s > %s.%s: ",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ tcpport_string(ndo, sport),
+ GET_IP6ADDR_STRING(ip6->ip6_dst),
+ tcpport_string(ndo, dport));
+ } else {
+ ND_PRINT("%s > %s: ",
+ tcpport_string(ndo, sport), tcpport_string(ndo, dport));
+ }
+ } else {
+ if (GET_U_1(ip->ip_p) == IPPROTO_PGM) {
+ ND_PRINT("%s.%s > %s.%s: ",
+ GET_IPADDR_STRING(ip->ip_src),
+ tcpport_string(ndo, sport),
+ GET_IPADDR_STRING(ip->ip_dst),
+ tcpport_string(ndo, dport));
+ } else {
+ ND_PRINT("%s > %s: ",
+ tcpport_string(ndo, sport), tcpport_string(ndo, dport));
+ }
+ }
+
+ ND_TCHECK_SIZE(pgm);
+
+ ND_PRINT("PGM, length %u", GET_BE_U_2(pgm->pgm_length));
+
+ if (!ndo->ndo_vflag)
+ return;
+
+ pgm_type_val = GET_U_1(pgm->pgm_type);
+ ND_PRINT(" 0x%02x%02x%02x%02x%02x%02x ",
+ pgm->pgm_gsid[0],
+ pgm->pgm_gsid[1],
+ pgm->pgm_gsid[2],
+ pgm->pgm_gsid[3],
+ pgm->pgm_gsid[4],
+ pgm->pgm_gsid[5]);
+ switch (pgm_type_val) {
+ case PGM_SPM: {
+ const struct pgm_spm *spm;
+
+ spm = (const struct pgm_spm *)(pgm + 1);
+ ND_TCHECK_SIZE(spm);
+ bp = (const u_char *) (spm + 1);
+
+ switch (GET_BE_U_2(spm->pgms_nla_afi)) {
+ case AFNUM_INET:
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
+ addrtostr(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv4);
+ break;
+ case AFNUM_INET6:
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
+ addrtostr6(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv6);
+ break;
+ default:
+ goto trunc;
+ break;
+ }
+
+ ND_PRINT("SPM seq %u trail %u lead %u nla %s",
+ GET_BE_U_4(spm->pgms_seq),
+ GET_BE_U_4(spm->pgms_trailseq),
+ GET_BE_U_4(spm->pgms_leadseq),
+ nla_buf);
+ break;
+ }
+
+ case PGM_POLL: {
+ const struct pgm_poll *pgm_poll;
+ uint32_t ivl, rnd, mask;
+
+ pgm_poll = (const struct pgm_poll *)(pgm + 1);
+ ND_TCHECK_SIZE(pgm_poll);
+ bp = (const u_char *) (pgm_poll + 1);
+
+ switch (GET_BE_U_2(pgm_poll->pgmp_nla_afi)) {
+ case AFNUM_INET:
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
+ addrtostr(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv4);
+ break;
+ case AFNUM_INET6:
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
+ addrtostr6(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv6);
+ break;
+ default:
+ goto trunc;
+ break;
+ }
+
+ ivl = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+
+ rnd = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+
+ mask = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+
+ ND_PRINT("POLL seq %u round %u nla %s ivl %u rnd 0x%08x "
+ "mask 0x%08x", GET_BE_U_4(pgm_poll->pgmp_seq),
+ GET_BE_U_2(pgm_poll->pgmp_round), nla_buf, ivl, rnd,
+ mask);
+ break;
+ }
+ case PGM_POLR: {
+ const struct pgm_polr *polr_msg;
+
+ polr_msg = (const struct pgm_polr *)(pgm + 1);
+ ND_TCHECK_SIZE(polr_msg);
+ ND_PRINT("POLR seq %u round %u",
+ GET_BE_U_4(polr_msg->pgmp_seq),
+ GET_BE_U_2(polr_msg->pgmp_round));
+ bp = (const u_char *) (polr_msg + 1);
+ break;
+ }
+ case PGM_ODATA: {
+ const struct pgm_data *odata;
+
+ odata = (const struct pgm_data *)(pgm + 1);
+ ND_TCHECK_SIZE(odata);
+ ND_PRINT("ODATA trail %u seq %u",
+ GET_BE_U_4(odata->pgmd_trailseq),
+ GET_BE_U_4(odata->pgmd_seq));
+ bp = (const u_char *) (odata + 1);
+ break;
+ }
+
+ case PGM_RDATA: {
+ const struct pgm_data *rdata;
+
+ rdata = (const struct pgm_data *)(pgm + 1);
+ ND_TCHECK_SIZE(rdata);
+ ND_PRINT("RDATA trail %u seq %u",
+ GET_BE_U_4(rdata->pgmd_trailseq),
+ GET_BE_U_4(rdata->pgmd_seq));
+ bp = (const u_char *) (rdata + 1);
+ break;
+ }
+
+ case PGM_NAK:
+ case PGM_NULLNAK:
+ case PGM_NCF: {
+ const struct pgm_nak *nak;
+ char source_buf[INET6_ADDRSTRLEN], group_buf[INET6_ADDRSTRLEN];
+
+ nak = (const struct pgm_nak *)(pgm + 1);
+ ND_TCHECK_SIZE(nak);
+ bp = (const u_char *) (nak + 1);
+
+ /*
+ * Skip past the source, saving info along the way
+ * and stopping if we don't have enough.
+ */
+ switch (GET_BE_U_2(nak->pgmn_source_afi)) {
+ case AFNUM_INET:
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
+ addrtostr(bp, source_buf, sizeof(source_buf));
+ bp += sizeof(nd_ipv4);
+ break;
+ case AFNUM_INET6:
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
+ addrtostr6(bp, source_buf, sizeof(source_buf));
+ bp += sizeof(nd_ipv6);
+ break;
+ default:
+ goto trunc;
+ break;
+ }
+
+ /*
+ * Skip past the group, saving info along the way
+ * and stopping if we don't have enough.
+ */
+ bp += (2 * sizeof(uint16_t));
+ switch (GET_BE_U_2(bp)) {
+ case AFNUM_INET:
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
+ addrtostr(bp, group_buf, sizeof(group_buf));
+ bp += sizeof(nd_ipv4);
+ break;
+ case AFNUM_INET6:
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
+ addrtostr6(bp, group_buf, sizeof(group_buf));
+ bp += sizeof(nd_ipv6);
+ break;
+ default:
+ goto trunc;
+ break;
+ }
+
+ /*
+ * Options decoding can go here.
+ */
+ switch (pgm_type_val) {
+ case PGM_NAK:
+ ND_PRINT("NAK ");
+ break;
+ case PGM_NULLNAK:
+ ND_PRINT("NNAK ");
+ break;
+ case PGM_NCF:
+ ND_PRINT("NCF ");
+ break;
+ default:
+ break;
+ }
+ ND_PRINT("(%s -> %s), seq %u",
+ source_buf, group_buf, GET_BE_U_4(nak->pgmn_seq));
+ break;
+ }
+
+ case PGM_ACK: {
+ const struct pgm_ack *ack;
+
+ ack = (const struct pgm_ack *)(pgm + 1);
+ ND_TCHECK_SIZE(ack);
+ ND_PRINT("ACK seq %u",
+ GET_BE_U_4(ack->pgma_rx_max_seq));
+ bp = (const u_char *) (ack + 1);
+ break;
+ }
+
+ case PGM_SPMR:
+ ND_PRINT("SPMR");
+ break;
+
+ default:
+ ND_PRINT("UNKNOWN type 0x%02x", pgm_type_val);
+ break;
+
+ }
+ if (GET_U_1(pgm->pgm_options) & PGM_OPT_BIT_PRESENT) {
+
+ /*
+ * make sure there's enough for the first option header
+ */
+ ND_TCHECK_LEN(bp, PGM_MIN_OPT_LEN);
+
+ /*
+ * That option header MUST be an OPT_LENGTH option
+ * (see the first paragraph of section 9.1 in RFC 3208).
+ */
+ opt_type = GET_U_1(bp);
+ bp++;
+ if ((opt_type & PGM_OPT_MASK) != PGM_OPT_LENGTH) {
+ ND_PRINT("[First option bad, should be PGM_OPT_LENGTH, is %u]", opt_type & PGM_OPT_MASK);
+ return;
+ }
+ opt_len = GET_U_1(bp);
+ bp++;
+ if (opt_len != 4) {
+ ND_PRINT("[Bad OPT_LENGTH option, length %u != 4]", opt_len);
+ return;
+ }
+ opts_len = GET_BE_U_2(bp);
+ bp += sizeof(uint16_t);
+ if (opts_len < 4) {
+ ND_PRINT("[Bad total option length %u < 4]", opts_len);
+ return;
+ }
+ ND_PRINT(" OPTS LEN %u", opts_len);
+ opts_len -= 4;
+
+ while (opts_len) {
+ if (opts_len < PGM_MIN_OPT_LEN) {
+ ND_PRINT("[Total option length leaves no room for final option]");
+ return;
+ }
+ opt_type = GET_U_1(bp);
+ bp++;
+ opt_len = GET_U_1(bp);
+ bp++;
+ if (opt_len < PGM_MIN_OPT_LEN) {
+ ND_PRINT("[Bad option, length %u < %u]", opt_len,
+ PGM_MIN_OPT_LEN);
+ break;
+ }
+ if (opts_len < opt_len) {
+ ND_PRINT("[Total option length leaves no room for final option]");
+ return;
+ }
+ ND_TCHECK_LEN(bp, opt_len - 2);
+
+ switch (opt_type & PGM_OPT_MASK) {
+ case PGM_OPT_LENGTH:
+#define PGM_OPT_LENGTH_LEN (2+2)
+ if (opt_len != PGM_OPT_LENGTH_LEN) {
+ ND_PRINT("[Bad OPT_LENGTH option, length %u != %u]",
+ opt_len, PGM_OPT_LENGTH_LEN);
+ return;
+ }
+ ND_PRINT(" OPTS LEN (extra?) %u", GET_BE_U_2(bp));
+ bp += 2;
+ opts_len -= PGM_OPT_LENGTH_LEN;
+ break;
+
+ case PGM_OPT_FRAGMENT:
+#define PGM_OPT_FRAGMENT_LEN (2+2+4+4+4)
+ if (opt_len != PGM_OPT_FRAGMENT_LEN) {
+ ND_PRINT("[Bad OPT_FRAGMENT option, length %u != %u]",
+ opt_len, PGM_OPT_FRAGMENT_LEN);
+ return;
+ }
+ bp += 2;
+ seq = GET_BE_U_4(bp);
+ bp += 4;
+ offset = GET_BE_U_4(bp);
+ bp += 4;
+ len = GET_BE_U_4(bp);
+ bp += 4;
+ ND_PRINT(" FRAG seq %u off %u len %u", seq, offset, len);
+ opts_len -= PGM_OPT_FRAGMENT_LEN;
+ break;
+
+ case PGM_OPT_NAK_LIST:
+ bp += 2;
+ opt_len -= 4; /* option header */
+ ND_PRINT(" NAK LIST");
+ while (opt_len) {
+ if (opt_len < 4) {
+ ND_PRINT("[Option length not a multiple of 4]");
+ return;
+ }
+ ND_PRINT(" %u", GET_BE_U_4(bp));
+ bp += 4;
+ opt_len -= 4;
+ opts_len -= 4;
+ }
+ break;
+
+ case PGM_OPT_JOIN:
+#define PGM_OPT_JOIN_LEN (2+2+4)
+ if (opt_len != PGM_OPT_JOIN_LEN) {
+ ND_PRINT("[Bad OPT_JOIN option, length %u != %u]",
+ opt_len, PGM_OPT_JOIN_LEN);
+ return;
+ }
+ bp += 2;
+ seq = GET_BE_U_4(bp);
+ bp += 4;
+ ND_PRINT(" JOIN %u", seq);
+ opts_len -= PGM_OPT_JOIN_LEN;
+ break;
+
+ case PGM_OPT_NAK_BO_IVL:
+#define PGM_OPT_NAK_BO_IVL_LEN (2+2+4+4)
+ if (opt_len != PGM_OPT_NAK_BO_IVL_LEN) {
+ ND_PRINT("[Bad OPT_NAK_BO_IVL option, length %u != %u]",
+ opt_len, PGM_OPT_NAK_BO_IVL_LEN);
+ return;
+ }
+ bp += 2;
+ offset = GET_BE_U_4(bp);
+ bp += 4;
+ seq = GET_BE_U_4(bp);
+ bp += 4;
+ ND_PRINT(" BACKOFF ivl %u ivlseq %u", offset, seq);
+ opts_len -= PGM_OPT_NAK_BO_IVL_LEN;
+ break;
+
+ case PGM_OPT_NAK_BO_RNG:
+#define PGM_OPT_NAK_BO_RNG_LEN (2+2+4+4)
+ if (opt_len != PGM_OPT_NAK_BO_RNG_LEN) {
+ ND_PRINT("[Bad OPT_NAK_BO_RNG option, length %u != %u]",
+ opt_len, PGM_OPT_NAK_BO_RNG_LEN);
+ return;
+ }
+ bp += 2;
+ offset = GET_BE_U_4(bp);
+ bp += 4;
+ seq = GET_BE_U_4(bp);
+ bp += 4;
+ ND_PRINT(" BACKOFF max %u min %u", offset, seq);
+ opts_len -= PGM_OPT_NAK_BO_RNG_LEN;
+ break;
+
+ case PGM_OPT_REDIRECT:
+#define PGM_OPT_REDIRECT_FIXED_LEN (2+2+2+2)
+ if (opt_len < PGM_OPT_REDIRECT_FIXED_LEN) {
+ ND_PRINT("[Bad OPT_REDIRECT option, length %u < %u]",
+ opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
+ return;
+ }
+ bp += 2;
+ nla_afnum = GET_BE_U_2(bp);
+ bp += 2+2;
+ switch (nla_afnum) {
+ case AFNUM_INET:
+ if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv4)) {
+ ND_PRINT("[Bad OPT_REDIRECT option, length %u != %u + address size]",
+ opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
+ return;
+ }
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
+ addrtostr(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv4);
+ opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv4);
+ break;
+ case AFNUM_INET6:
+ if (opt_len != PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv6)) {
+ ND_PRINT("[Bad OPT_REDIRECT option, length %u != %u + address size]",
+ opt_len, PGM_OPT_REDIRECT_FIXED_LEN);
+ return;
+ }
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
+ addrtostr6(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv6);
+ opts_len -= PGM_OPT_REDIRECT_FIXED_LEN + sizeof(nd_ipv6);
+ break;
+ default:
+ goto trunc;
+ break;
+ }
+
+ ND_PRINT(" REDIRECT %s", nla_buf);
+ break;
+
+ case PGM_OPT_PARITY_PRM:
+#define PGM_OPT_PARITY_PRM_LEN (2+2+4)
+ if (opt_len != PGM_OPT_PARITY_PRM_LEN) {
+ ND_PRINT("[Bad OPT_PARITY_PRM option, length %u != %u]",
+ opt_len, PGM_OPT_PARITY_PRM_LEN);
+ return;
+ }
+ bp += 2;
+ len = GET_BE_U_4(bp);
+ bp += 4;
+ ND_PRINT(" PARITY MAXTGS %u", len);
+ opts_len -= PGM_OPT_PARITY_PRM_LEN;
+ break;
+
+ case PGM_OPT_PARITY_GRP:
+#define PGM_OPT_PARITY_GRP_LEN (2+2+4)
+ if (opt_len != PGM_OPT_PARITY_GRP_LEN) {
+ ND_PRINT("[Bad OPT_PARITY_GRP option, length %u != %u]",
+ opt_len, PGM_OPT_PARITY_GRP_LEN);
+ return;
+ }
+ bp += 2;
+ seq = GET_BE_U_4(bp);
+ bp += 4;
+ ND_PRINT(" PARITY GROUP %u", seq);
+ opts_len -= PGM_OPT_PARITY_GRP_LEN;
+ break;
+
+ case PGM_OPT_CURR_TGSIZE:
+#define PGM_OPT_CURR_TGSIZE_LEN (2+2+4)
+ if (opt_len != PGM_OPT_CURR_TGSIZE_LEN) {
+ ND_PRINT("[Bad OPT_CURR_TGSIZE option, length %u != %u]",
+ opt_len, PGM_OPT_CURR_TGSIZE_LEN);
+ return;
+ }
+ bp += 2;
+ len = GET_BE_U_4(bp);
+ bp += 4;
+ ND_PRINT(" PARITY ATGS %u", len);
+ opts_len -= PGM_OPT_CURR_TGSIZE_LEN;
+ break;
+
+ case PGM_OPT_NBR_UNREACH:
+#define PGM_OPT_NBR_UNREACH_LEN (2+2)
+ if (opt_len != PGM_OPT_NBR_UNREACH_LEN) {
+ ND_PRINT("[Bad OPT_NBR_UNREACH option, length %u != %u]",
+ opt_len, PGM_OPT_NBR_UNREACH_LEN);
+ return;
+ }
+ bp += 2;
+ ND_PRINT(" NBR_UNREACH");
+ opts_len -= PGM_OPT_NBR_UNREACH_LEN;
+ break;
+
+ case PGM_OPT_PATH_NLA:
+ ND_PRINT(" PATH_NLA [%u]", opt_len);
+ bp += opt_len;
+ opts_len -= opt_len;
+ break;
+
+ case PGM_OPT_SYN:
+#define PGM_OPT_SYN_LEN (2+2)
+ if (opt_len != PGM_OPT_SYN_LEN) {
+ ND_PRINT("[Bad OPT_SYN option, length %u != %u]",
+ opt_len, PGM_OPT_SYN_LEN);
+ return;
+ }
+ bp += 2;
+ ND_PRINT(" SYN");
+ opts_len -= PGM_OPT_SYN_LEN;
+ break;
+
+ case PGM_OPT_FIN:
+#define PGM_OPT_FIN_LEN (2+2)
+ if (opt_len != PGM_OPT_FIN_LEN) {
+ ND_PRINT("[Bad OPT_FIN option, length %u != %u]",
+ opt_len, PGM_OPT_FIN_LEN);
+ return;
+ }
+ bp += 2;
+ ND_PRINT(" FIN");
+ opts_len -= PGM_OPT_FIN_LEN;
+ break;
+
+ case PGM_OPT_RST:
+#define PGM_OPT_RST_LEN (2+2)
+ if (opt_len != PGM_OPT_RST_LEN) {
+ ND_PRINT("[Bad OPT_RST option, length %u != %u]",
+ opt_len, PGM_OPT_RST_LEN);
+ return;
+ }
+ bp += 2;
+ ND_PRINT(" RST");
+ opts_len -= PGM_OPT_RST_LEN;
+ break;
+
+ case PGM_OPT_CR:
+ ND_PRINT(" CR");
+ bp += opt_len;
+ opts_len -= opt_len;
+ break;
+
+ case PGM_OPT_CRQST:
+#define PGM_OPT_CRQST_LEN (2+2)
+ if (opt_len != PGM_OPT_CRQST_LEN) {
+ ND_PRINT("[Bad OPT_CRQST option, length %u != %u]",
+ opt_len, PGM_OPT_CRQST_LEN);
+ return;
+ }
+ bp += 2;
+ ND_PRINT(" CRQST");
+ opts_len -= PGM_OPT_CRQST_LEN;
+ break;
+
+ case PGM_OPT_PGMCC_DATA:
+#define PGM_OPT_PGMCC_DATA_FIXED_LEN (2+2+4+2+2)
+ if (opt_len < PGM_OPT_PGMCC_DATA_FIXED_LEN) {
+ ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u < %u]",
+ opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
+ return;
+ }
+ bp += 2;
+ offset = GET_BE_U_4(bp);
+ bp += 4;
+ nla_afnum = GET_BE_U_2(bp);
+ bp += 2+2;
+ switch (nla_afnum) {
+ case AFNUM_INET:
+ if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv4)) {
+ ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
+ opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
+ return;
+ }
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
+ addrtostr(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv4);
+ opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv4);
+ break;
+ case AFNUM_INET6:
+ if (opt_len != PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv6)) {
+ ND_PRINT("[Bad OPT_PGMCC_DATA option, length %u != %u + address size]",
+ opt_len, PGM_OPT_PGMCC_DATA_FIXED_LEN);
+ return;
+ }
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
+ addrtostr6(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv6);
+ opts_len -= PGM_OPT_PGMCC_DATA_FIXED_LEN + sizeof(nd_ipv6);
+ break;
+ default:
+ goto trunc;
+ break;
+ }
+
+ ND_PRINT(" PGMCC DATA %u %s", offset, nla_buf);
+ break;
+
+ case PGM_OPT_PGMCC_FEEDBACK:
+#define PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN (2+2+4+2+2)
+ if (opt_len < PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN) {
+ ND_PRINT("[Bad PGM_OPT_PGMCC_FEEDBACK option, length %u < %u]",
+ opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
+ return;
+ }
+ bp += 2;
+ offset = GET_BE_U_4(bp);
+ bp += 4;
+ nla_afnum = GET_BE_U_2(bp);
+ bp += 2+2;
+ switch (nla_afnum) {
+ case AFNUM_INET:
+ if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv4)) {
+ ND_PRINT("[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
+ opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
+ return;
+ }
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv4));
+ addrtostr(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv4);
+ opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv4);
+ break;
+ case AFNUM_INET6:
+ if (opt_len != PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv6)) {
+ ND_PRINT("[Bad OPT_PGMCC_FEEDBACK option, length %u != %u + address size]",
+ opt_len, PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN);
+ return;
+ }
+ ND_TCHECK_LEN(bp, sizeof(nd_ipv6));
+ addrtostr6(bp, nla_buf, sizeof(nla_buf));
+ bp += sizeof(nd_ipv6);
+ opts_len -= PGM_OPT_PGMCC_FEEDBACK_FIXED_LEN + sizeof(nd_ipv6);
+ break;
+ default:
+ goto trunc;
+ break;
+ }
+
+ ND_PRINT(" PGMCC FEEDBACK %u %s", offset, nla_buf);
+ break;
+
+ default:
+ ND_PRINT(" OPT_%02X [%u] ", opt_type, opt_len);
+ bp += opt_len;
+ opts_len -= opt_len;
+ break;
+ }
+
+ if (opt_type & PGM_OPT_END)
+ break;
+ }
+ }
+
+ ND_PRINT(" [%u]", length);
+ if (ndo->ndo_packettype == PT_PGM_ZMTP1 &&
+ (pgm_type_val == PGM_ODATA || pgm_type_val == PGM_RDATA))
+ zmtp1_datagram_print(ndo, bp,
+ GET_BE_U_2(pgm->pgm_length));
+
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-pim.c b/print-pim.c
new file mode 100644
index 0000000..f2db8c7
--- /dev/null
+++ b/print-pim.c
@@ -0,0 +1,1234 @@
+/*
+ * Copyright (c) 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Protocol Independent Multicast (PIM) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip.h"
+#include "ip6.h"
+#include "ipproto.h"
+
+#define PIMV1_TYPE_QUERY 0
+#define PIMV1_TYPE_REGISTER 1
+#define PIMV1_TYPE_REGISTER_STOP 2
+#define PIMV1_TYPE_JOIN_PRUNE 3
+#define PIMV1_TYPE_RP_REACHABILITY 4
+#define PIMV1_TYPE_ASSERT 5
+#define PIMV1_TYPE_GRAFT 6
+#define PIMV1_TYPE_GRAFT_ACK 7
+
+static const struct tok pimv1_type_str[] = {
+ { PIMV1_TYPE_QUERY, "Query" },
+ { PIMV1_TYPE_REGISTER, "Register" },
+ { PIMV1_TYPE_REGISTER_STOP, "Register-Stop" },
+ { PIMV1_TYPE_JOIN_PRUNE, "Join/Prune" },
+ { PIMV1_TYPE_RP_REACHABILITY, "RP-reachable" },
+ { PIMV1_TYPE_ASSERT, "Assert" },
+ { PIMV1_TYPE_GRAFT, "Graft" },
+ { PIMV1_TYPE_GRAFT_ACK, "Graft-ACK" },
+ { 0, NULL }
+};
+
+#define PIMV2_TYPE_HELLO 0
+#define PIMV2_TYPE_REGISTER 1
+#define PIMV2_TYPE_REGISTER_STOP 2
+#define PIMV2_TYPE_JOIN_PRUNE 3
+#define PIMV2_TYPE_BOOTSTRAP 4
+#define PIMV2_TYPE_ASSERT 5
+#define PIMV2_TYPE_GRAFT 6
+#define PIMV2_TYPE_GRAFT_ACK 7
+#define PIMV2_TYPE_CANDIDATE_RP 8
+#define PIMV2_TYPE_PRUNE_REFRESH 9
+#define PIMV2_TYPE_DF_ELECTION 10
+#define PIMV2_TYPE_ECMP_REDIRECT 11
+
+static const struct tok pimv2_type_values[] = {
+ { PIMV2_TYPE_HELLO, "Hello" },
+ { PIMV2_TYPE_REGISTER, "Register" },
+ { PIMV2_TYPE_REGISTER_STOP, "Register Stop" },
+ { PIMV2_TYPE_JOIN_PRUNE, "Join / Prune" },
+ { PIMV2_TYPE_BOOTSTRAP, "Bootstrap" },
+ { PIMV2_TYPE_ASSERT, "Assert" },
+ { PIMV2_TYPE_GRAFT, "Graft" },
+ { PIMV2_TYPE_GRAFT_ACK, "Graft Acknowledgement" },
+ { PIMV2_TYPE_CANDIDATE_RP, "Candidate RP Advertisement" },
+ { PIMV2_TYPE_PRUNE_REFRESH, "Prune Refresh" },
+ { PIMV2_TYPE_DF_ELECTION, "DF Election" },
+ { PIMV2_TYPE_ECMP_REDIRECT, "ECMP Redirect" },
+ { 0, NULL}
+};
+
+#define PIMV2_HELLO_OPTION_HOLDTIME 1
+#define PIMV2_HELLO_OPTION_LANPRUNEDELAY 2
+#define PIMV2_HELLO_OPTION_DR_PRIORITY_OLD 18
+#define PIMV2_HELLO_OPTION_DR_PRIORITY 19
+#define PIMV2_HELLO_OPTION_GENID 20
+#define PIMV2_HELLO_OPTION_REFRESH_CAP 21
+#define PIMV2_HELLO_OPTION_BIDIR_CAP 22
+#define PIMV2_HELLO_OPTION_ADDRESS_LIST 24
+#define PIMV2_HELLO_OPTION_ADDRESS_LIST_OLD 65001
+
+static const struct tok pimv2_hello_option_values[] = {
+ { PIMV2_HELLO_OPTION_HOLDTIME, "Hold Time" },
+ { PIMV2_HELLO_OPTION_LANPRUNEDELAY, "LAN Prune Delay" },
+ { PIMV2_HELLO_OPTION_DR_PRIORITY_OLD, "DR Priority (Old)" },
+ { PIMV2_HELLO_OPTION_DR_PRIORITY, "DR Priority" },
+ { PIMV2_HELLO_OPTION_GENID, "Generation ID" },
+ { PIMV2_HELLO_OPTION_REFRESH_CAP, "State Refresh Capability" },
+ { PIMV2_HELLO_OPTION_BIDIR_CAP, "Bi-Directional Capability" },
+ { PIMV2_HELLO_OPTION_ADDRESS_LIST, "Address List" },
+ { PIMV2_HELLO_OPTION_ADDRESS_LIST_OLD, "Address List (Old)" },
+ { 0, NULL}
+};
+
+#define PIMV2_REGISTER_FLAG_LEN 4
+#define PIMV2_REGISTER_FLAG_BORDER 0x80000000
+#define PIMV2_REGISTER_FLAG_NULL 0x40000000
+
+static const struct tok pimv2_register_flag_values[] = {
+ { PIMV2_REGISTER_FLAG_BORDER, "Border" },
+ { PIMV2_REGISTER_FLAG_NULL, "Null" },
+ { 0, NULL}
+};
+
+#define PIMV2_DF_ELECTION_OFFER 1
+#define PIMV2_DF_ELECTION_WINNER 2
+#define PIMV2_DF_ELECTION_BACKOFF 3
+#define PIMV2_DF_ELECTION_PASS 4
+
+static const struct tok pimv2_df_election_flag_values[] = {
+ { PIMV2_DF_ELECTION_OFFER, "Offer" },
+ { PIMV2_DF_ELECTION_WINNER, "Winner" },
+ { PIMV2_DF_ELECTION_BACKOFF, "Backoff" },
+ { PIMV2_DF_ELECTION_PASS, "Pass" },
+ { 0, NULL}
+};
+
+#define PIMV2_DF_ELECTION_PASS_BACKOFF_STR(x) ( \
+ x == PIMV2_DF_ELECTION_BACKOFF ? "offer" : "new winner" )
+
+
+/*
+ * XXX: We consider a case where IPv6 is not ready yet for portability,
+ * but PIM dependent definitions should be independent of IPv6...
+ */
+
+struct pim {
+ nd_uint8_t pim_typever;
+ /* upper 4bit: PIM version number; 2 for PIMv2 */
+ /* lower 4bit: the PIM message type, currently they are:
+ * Hello, Register, Register-Stop, Join/Prune,
+ * Bootstrap, Assert, Graft (PIM-DM only),
+ * Graft-Ack (PIM-DM only), C-RP-Adv
+ */
+#define PIM_VER(x) (((x) & 0xf0) >> 4)
+#define PIM_TYPE(x) ((x) & 0x0f)
+ nd_uint8_t pim_rsv; /* Reserved in v1, subtype+address length in v2 */
+#define PIM_SUBTYPE(x) (((x) & 0xf0) >> 4)
+ nd_uint16_t pim_cksum; /* IP style check sum */
+};
+
+static void pimv2_print(netdissect_options *, const u_char *bp, u_int len, const u_char *);
+
+static void
+pimv1_join_prune_print(netdissect_options *ndo,
+ const u_char *bp, u_int len)
+{
+ u_int ngroups, njoin, nprune;
+ u_int njp;
+
+ /* If it's a single group and a single source, use 1-line output. */
+ if (ND_TTEST_LEN(bp, 30) && GET_U_1(bp + 11) == 1 &&
+ ((njoin = GET_BE_U_2(bp + 20)) + GET_BE_U_2(bp + 22)) == 1) {
+ u_int hold;
+
+ ND_PRINT(" RPF %s ", GET_IPADDR_STRING(bp));
+ hold = GET_BE_U_2(bp + 6);
+ if (hold != 180) {
+ ND_PRINT("Hold ");
+ unsigned_relts_print(ndo, hold);
+ }
+ ND_PRINT("%s (%s/%u, %s", njoin ? "Join" : "Prune",
+ GET_IPADDR_STRING(bp + 26), GET_U_1(bp + 25) & 0x3f,
+ GET_IPADDR_STRING(bp + 12));
+ if (GET_BE_U_4(bp + 16) != 0xffffffff)
+ ND_PRINT("/%s", GET_IPADDR_STRING(bp + 16));
+ ND_PRINT(") %s%s %s",
+ (GET_U_1(bp + 24) & 0x01) ? "Sparse" : "Dense",
+ (GET_U_1(bp + 25) & 0x80) ? " WC" : "",
+ (GET_U_1(bp + 25) & 0x40) ? "RP" : "SPT");
+ return;
+ }
+
+ if (len < sizeof(nd_ipv4))
+ goto trunc;
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n");
+ ND_PRINT(" Upstream Nbr: %s", GET_IPADDR_STRING(bp));
+ bp += 4;
+ len -= 4;
+ if (len < 4)
+ goto trunc;
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n");
+ ND_PRINT(" Hold time: ");
+ unsigned_relts_print(ndo, GET_BE_U_2(bp + 2));
+ if (ndo->ndo_vflag < 2)
+ return;
+ bp += 4;
+ len -= 4;
+
+ if (len < 4)
+ goto trunc;
+ ngroups = GET_U_1(bp + 3);
+ bp += 4;
+ len -= 4;
+ while (ngroups != 0) {
+ /*
+ * XXX - does the address have length "addrlen" and the
+ * mask length "maddrlen"?
+ */
+ if (len < 4)
+ goto trunc;
+ ND_PRINT("\n\tGroup: %s", GET_IPADDR_STRING(bp));
+ bp += 4;
+ len -= 4;
+ if (len < 4)
+ goto trunc;
+ if (GET_BE_U_4(bp) != 0xffffffff)
+ ND_PRINT("/%s", GET_IPADDR_STRING(bp));
+ bp += 4;
+ len -= 4;
+ if (len < 4)
+ goto trunc;
+ njoin = GET_BE_U_2(bp);
+ nprune = GET_BE_U_2(bp + 2);
+ ND_PRINT(" joined: %u pruned: %u", njoin, nprune);
+ bp += 4;
+ len -= 4;
+ for (njp = 0; njp < (njoin + nprune); njp++) {
+ const char *type;
+
+ if (njp < njoin)
+ type = "Join ";
+ else
+ type = "Prune";
+ if (len < 6)
+ goto trunc;
+ ND_PRINT("\n\t%s %s%s%s%s/%u", type,
+ (GET_U_1(bp) & 0x01) ? "Sparse " : "Dense ",
+ (GET_U_1(bp + 1) & 0x80) ? "WC " : "",
+ (GET_U_1(bp + 1) & 0x40) ? "RP " : "SPT ",
+ GET_IPADDR_STRING(bp + 2),
+ GET_U_1(bp + 1) & 0x3f);
+ bp += 6;
+ len -= 6;
+ }
+ ngroups--;
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+pimv1_print(netdissect_options *ndo,
+ const u_char *bp, u_int len)
+{
+ u_char type;
+
+ ndo->ndo_protocol = "pimv1";
+ type = GET_U_1(bp + 1);
+
+ ND_PRINT(" %s", tok2str(pimv1_type_str, "[type %u]", type));
+ switch (type) {
+ case PIMV1_TYPE_QUERY:
+ if (ND_TTEST_1(bp + 8)) {
+ switch (GET_U_1(bp + 8) >> 4) {
+ case 0:
+ ND_PRINT(" Dense-mode");
+ break;
+ case 1:
+ ND_PRINT(" Sparse-mode");
+ break;
+ case 2:
+ ND_PRINT(" Sparse-Dense-mode");
+ break;
+ default:
+ ND_PRINT(" mode-%u", GET_U_1(bp + 8) >> 4);
+ break;
+ }
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" (Hold-time ");
+ unsigned_relts_print(ndo, GET_BE_U_2(bp + 10));
+ ND_PRINT(")");
+ }
+ break;
+
+ case PIMV1_TYPE_REGISTER:
+ ND_TCHECK_LEN(bp + 8, 20); /* ip header */
+ ND_PRINT(" for %s > %s", GET_IPADDR_STRING(bp + 20),
+ GET_IPADDR_STRING(bp + 24));
+ break;
+ case PIMV1_TYPE_REGISTER_STOP:
+ ND_PRINT(" for %s > %s", GET_IPADDR_STRING(bp + 8),
+ GET_IPADDR_STRING(bp + 12));
+ break;
+ case PIMV1_TYPE_RP_REACHABILITY:
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" group %s", GET_IPADDR_STRING(bp + 8));
+ if (GET_BE_U_4(bp + 12) != 0xffffffff)
+ ND_PRINT("/%s", GET_IPADDR_STRING(bp + 12));
+ ND_PRINT(" RP %s hold ", GET_IPADDR_STRING(bp + 16));
+ unsigned_relts_print(ndo, GET_BE_U_2(bp + 22));
+ }
+ break;
+ case PIMV1_TYPE_ASSERT:
+ ND_PRINT(" for %s > %s", GET_IPADDR_STRING(bp + 16),
+ GET_IPADDR_STRING(bp + 8));
+ if (GET_BE_U_4(bp + 12) != 0xffffffff)
+ ND_PRINT("/%s", GET_IPADDR_STRING(bp + 12));
+ ND_PRINT(" %s pref %u metric %u",
+ (GET_U_1(bp + 20) & 0x80) ? "RP-tree" : "SPT",
+ GET_BE_U_4(bp + 20) & 0x7fffffff,
+ GET_BE_U_4(bp + 24));
+ break;
+ case PIMV1_TYPE_JOIN_PRUNE:
+ case PIMV1_TYPE_GRAFT:
+ case PIMV1_TYPE_GRAFT_ACK:
+ if (ndo->ndo_vflag) {
+ if (len < 8)
+ goto trunc;
+ pimv1_join_prune_print(ndo, bp + 8, len - 8);
+ }
+ break;
+ }
+ if ((GET_U_1(bp + 4) >> 4) != 1)
+ ND_PRINT(" [v%u]", GET_U_1(bp + 4) >> 4);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+/*
+ * auto-RP is a cisco protocol, documented at
+ * ftp://ftpeng.cisco.com/ipmulticast/specs/pim-autorp-spec01.txt
+ *
+ * This implements version 1+, dated Sept 9, 1998.
+ */
+void
+cisco_autorp_print(netdissect_options *ndo,
+ const u_char *bp, u_int len)
+{
+ u_int type;
+ u_int numrps;
+ u_int hold;
+
+ ndo->ndo_protocol = "cisco_autorp";
+ if (len < 8)
+ goto trunc;
+ ND_PRINT(" auto-rp ");
+ type = GET_U_1(bp);
+ switch (type) {
+ case 0x11:
+ ND_PRINT("candidate-advert");
+ break;
+ case 0x12:
+ ND_PRINT("mapping");
+ break;
+ default:
+ ND_PRINT("type-0x%02x", type);
+ break;
+ }
+
+ numrps = GET_U_1(bp + 1);
+
+ ND_PRINT(" Hold ");
+ hold = GET_BE_U_2(bp + 2);
+ if (hold)
+ unsigned_relts_print(ndo, GET_BE_U_2(bp + 2));
+ else
+ ND_PRINT("FOREVER");
+
+ /* Next 4 bytes are reserved. */
+
+ bp += 8; len -= 8;
+
+ /*XXX skip unless -v? */
+
+ /*
+ * Rest of packet:
+ * numrps entries of the form:
+ * 32 bits: RP
+ * 6 bits: reserved
+ * 2 bits: PIM version supported, bit 0 is "supports v1", 1 is "v2".
+ * 8 bits: # of entries for this RP
+ * each entry: 7 bits: reserved, 1 bit: negative,
+ * 8 bits: mask 32 bits: source
+ * lather, rinse, repeat.
+ */
+ while (numrps != 0) {
+ u_int nentries;
+ char s;
+
+ if (len < 4)
+ goto trunc;
+ ND_PRINT(" RP %s", GET_IPADDR_STRING(bp));
+ bp += 4;
+ len -= 4;
+ if (len < 1)
+ goto trunc;
+ switch (GET_U_1(bp) & 0x3) {
+ case 0: ND_PRINT(" PIMv?");
+ break;
+ case 1: ND_PRINT(" PIMv1");
+ break;
+ case 2: ND_PRINT(" PIMv2");
+ break;
+ case 3: ND_PRINT(" PIMv1+2");
+ break;
+ }
+ if (GET_U_1(bp) & 0xfc)
+ ND_PRINT(" [rsvd=0x%02x]", GET_U_1(bp) & 0xfc);
+ bp += 1;
+ len -= 1;
+ if (len < 1)
+ goto trunc;
+ nentries = GET_U_1(bp);
+ bp += 1;
+ len -= 1;
+ s = ' ';
+ while (nentries != 0) {
+ if (len < 6)
+ goto trunc;
+ ND_PRINT("%c%s%s/%u", s, GET_U_1(bp) & 1 ? "!" : "",
+ GET_IPADDR_STRING(bp + 2), GET_U_1(bp + 1));
+ if (GET_U_1(bp) & 0x02) {
+ ND_PRINT(" bidir");
+ }
+ if (GET_U_1(bp) & 0xfc) {
+ ND_PRINT("[rsvd=0x%02x]", GET_U_1(bp) & 0xfc);
+ }
+ s = ',';
+ bp += 6; len -= 6;
+ nentries--;
+ }
+ numrps--;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+pim_print(netdissect_options *ndo,
+ const u_char *bp, u_int len, const u_char *bp2)
+{
+ const struct pim *pim = (const struct pim *)bp;
+ uint8_t pim_typever;
+
+ ndo->ndo_protocol = "pim";
+
+ pim_typever = GET_U_1(pim->pim_typever);
+ switch (PIM_VER(pim_typever)) {
+ case 2:
+ if (!ndo->ndo_vflag) {
+ ND_PRINT("PIMv%u, %s, length %u",
+ PIM_VER(pim_typever),
+ tok2str(pimv2_type_values,"Unknown Type",PIM_TYPE(pim_typever)),
+ len);
+ return;
+ } else {
+ ND_PRINT("PIMv%u, length %u\n\t%s",
+ PIM_VER(pim_typever),
+ len,
+ tok2str(pimv2_type_values,"Unknown Type",PIM_TYPE(pim_typever)));
+ pimv2_print(ndo, bp, len, bp2);
+ }
+ break;
+ default:
+ ND_PRINT("PIMv%u, length %u",
+ PIM_VER(pim_typever),
+ len);
+ break;
+ }
+}
+
+/*
+ * PIMv2 uses encoded address representations.
+ *
+ * The last PIM-SM I-D before RFC2117 was published specified the
+ * following representation for unicast addresses. However, RFC2117
+ * specified no encoding for unicast addresses with the unicast
+ * address length specified in the header. Therefore, we have to
+ * guess which encoding is being used (Cisco's PIMv2 implementation
+ * uses the non-RFC encoding). RFC2117 turns a previously "Reserved"
+ * field into a 'unicast-address-length-in-bytes' field. We guess
+ * that it's the draft encoding if this reserved field is zero.
+ *
+ * RFC2362 goes back to the encoded format, and calls the addr length
+ * field "reserved" again.
+ *
+ * The first byte is the address family, from:
+ *
+ * 0 Reserved
+ * 1 IP (IP version 4)
+ * 2 IP6 (IP version 6)
+ * 3 NSAP
+ * 4 HDLC (8-bit multidrop)
+ * 5 BBN 1822
+ * 6 802 (includes all 802 media plus Ethernet "canonical format")
+ * 7 E.163
+ * 8 E.164 (SMDS, Frame Relay, ATM)
+ * 9 F.69 (Telex)
+ * 10 X.121 (X.25, Frame Relay)
+ * 11 IPX
+ * 12 Appletalk
+ * 13 Decnet IV
+ * 14 Banyan Vines
+ * 15 E.164 with NSAP format subaddress
+ *
+ * In addition, the second byte is an "Encoding". 0 is the default
+ * encoding for the address family, and no other encodings are currently
+ * specified.
+ *
+ */
+
+enum pimv2_addrtype {
+ pimv2_unicast, pimv2_group, pimv2_source
+};
+
+/* 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Addr Family | Encoding Type | Unicast Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+++++++
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Addr Family | Encoding Type | Reserved | Mask Len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Group multicast Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Addr Family | Encoding Type | Rsrvd |S|W|R| Mask Len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Source Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+static int
+pimv2_addr_print(netdissect_options *ndo,
+ const u_char *bp, u_int len, enum pimv2_addrtype at,
+ u_int addr_len, int silent)
+{
+ u_int af;
+ int hdrlen;
+
+ if (addr_len == 0) {
+ if (len < 2)
+ goto trunc;
+ switch (GET_U_1(bp)) {
+ case 1:
+ af = AF_INET;
+ addr_len = (u_int)sizeof(nd_ipv4);
+ break;
+ case 2:
+ af = AF_INET6;
+ addr_len = (u_int)sizeof(nd_ipv6);
+ break;
+ default:
+ return -1;
+ }
+ if (GET_U_1(bp + 1) != 0)
+ return -1;
+ hdrlen = 2;
+ } else {
+ switch (addr_len) {
+ case sizeof(nd_ipv4):
+ af = AF_INET;
+ break;
+ case sizeof(nd_ipv6):
+ af = AF_INET6;
+ break;
+ default:
+ return -1;
+ break;
+ }
+ hdrlen = 0;
+ }
+
+ bp += hdrlen;
+ len -= hdrlen;
+ switch (at) {
+ case pimv2_unicast:
+ if (len < addr_len)
+ goto trunc;
+ ND_TCHECK_LEN(bp, addr_len);
+ if (af == AF_INET) {
+ if (!silent)
+ ND_PRINT("%s", GET_IPADDR_STRING(bp));
+ }
+ else if (af == AF_INET6) {
+ if (!silent)
+ ND_PRINT("%s", GET_IP6ADDR_STRING(bp));
+ }
+ return hdrlen + addr_len;
+ case pimv2_group:
+ case pimv2_source:
+ if (len < addr_len + 2)
+ goto trunc;
+ ND_TCHECK_LEN(bp, addr_len + 2);
+ if (af == AF_INET) {
+ if (!silent) {
+ ND_PRINT("%s", GET_IPADDR_STRING(bp + 2));
+ if (GET_U_1(bp + 1) != 32)
+ ND_PRINT("/%u", GET_U_1(bp + 1));
+ }
+ }
+ else if (af == AF_INET6) {
+ if (!silent) {
+ ND_PRINT("%s", GET_IP6ADDR_STRING(bp + 2));
+ if (GET_U_1(bp + 1) != 128)
+ ND_PRINT("/%u", GET_U_1(bp + 1));
+ }
+ }
+ if (GET_U_1(bp) && !silent) {
+ if (at == pimv2_group) {
+ ND_PRINT("(0x%02x)", GET_U_1(bp));
+ } else {
+ ND_PRINT("(%s%s%s",
+ GET_U_1(bp) & 0x04 ? "S" : "",
+ GET_U_1(bp) & 0x02 ? "W" : "",
+ GET_U_1(bp) & 0x01 ? "R" : "");
+ if (GET_U_1(bp) & 0xf8) {
+ ND_PRINT("+0x%02x",
+ GET_U_1(bp) & 0xf8);
+ }
+ ND_PRINT(")");
+ }
+ }
+ return hdrlen + 2 + addr_len;
+ default:
+ return -1;
+ }
+trunc:
+ return -1;
+}
+
+enum checksum_status {
+ CORRECT,
+ INCORRECT,
+ UNVERIFIED
+};
+
+static enum checksum_status
+pimv2_check_checksum(netdissect_options *ndo, const u_char *bp,
+ const u_char *bp2, u_int len)
+{
+ const struct ip *ip;
+ u_int cksum;
+
+ if (!ND_TTEST_LEN(bp, len)) {
+ /* We don't have all the data. */
+ return (UNVERIFIED);
+ }
+ ip = (const struct ip *)bp2;
+ if (IP_V(ip) == 4) {
+ struct cksum_vec vec[1];
+
+ vec[0].ptr = bp;
+ vec[0].len = len;
+ cksum = in_cksum(vec, 1);
+ return (cksum ? INCORRECT : CORRECT);
+ } else if (IP_V(ip) == 6) {
+ const struct ip6_hdr *ip6;
+
+ ip6 = (const struct ip6_hdr *)bp2;
+ cksum = nextproto6_cksum(ndo, ip6, bp, len, len, IPPROTO_PIM);
+ return (cksum ? INCORRECT : CORRECT);
+ } else {
+ return (UNVERIFIED);
+ }
+}
+
+static void
+pimv2_print(netdissect_options *ndo,
+ const u_char *bp, u_int len, const u_char *bp2)
+{
+ const struct pim *pim = (const struct pim *)bp;
+ int advance;
+ int subtype;
+ enum checksum_status cksum_status;
+ u_int pim_typever;
+ u_int pimv2_addr_len;
+
+ ndo->ndo_protocol = "pimv2";
+ if (len < 2) {
+ ND_PRINT("[length %u < 2]", len);
+ nd_print_invalid(ndo);
+ return;
+ }
+ pim_typever = GET_U_1(pim->pim_typever);
+ /* RFC5015 allocates the high 4 bits of pim_rsv for "subtype". */
+ pimv2_addr_len = GET_U_1(pim->pim_rsv) & 0x0f;
+ if (pimv2_addr_len != 0)
+ ND_PRINT(", RFC2117-encoding");
+
+ if (len < 4) {
+ ND_PRINT("[length %u < 4]", len);
+ nd_print_invalid(ndo);
+ return;
+ }
+ ND_PRINT(", cksum 0x%04x ", GET_BE_U_2(pim->pim_cksum));
+ if (GET_BE_U_2(pim->pim_cksum) == 0) {
+ ND_PRINT("(unverified)");
+ } else {
+ if (PIM_TYPE(pim_typever) == PIMV2_TYPE_REGISTER) {
+ /*
+ * The checksum only covers the packet header,
+ * not the encapsulated packet.
+ */
+ cksum_status = pimv2_check_checksum(ndo, bp, bp2, 8);
+ if (cksum_status == INCORRECT) {
+ /*
+ * To quote RFC 4601, "For interoperability
+ * reasons, a message carrying a checksum
+ * calculated over the entire PIM Register
+ * message should also be accepted."
+ */
+ cksum_status = pimv2_check_checksum(ndo, bp, bp2, len);
+ }
+ } else {
+ /*
+ * The checksum covers the entire packet.
+ */
+ cksum_status = pimv2_check_checksum(ndo, bp, bp2, len);
+ }
+ switch (cksum_status) {
+
+ case CORRECT:
+ ND_PRINT("(correct)");
+ break;
+
+ case INCORRECT:
+ ND_PRINT("(incorrect)");
+ break;
+
+ case UNVERIFIED:
+ ND_PRINT("(unverified)");
+ break;
+ }
+ }
+ bp += 4;
+ len -= 4;
+
+ switch (PIM_TYPE(pim_typever)) {
+ case PIMV2_TYPE_HELLO:
+ {
+ uint16_t otype, olen;
+ while (len > 0) {
+ if (len < 4)
+ goto trunc;
+ otype = GET_BE_U_2(bp);
+ olen = GET_BE_U_2(bp + 2);
+ ND_PRINT("\n\t %s Option (%u), length %u, Value: ",
+ tok2str(pimv2_hello_option_values, "Unknown", otype),
+ otype,
+ olen);
+ bp += 4;
+ len -= 4;
+
+ if (len < olen)
+ goto trunc;
+ ND_TCHECK_LEN(bp, olen);
+ switch (otype) {
+ case PIMV2_HELLO_OPTION_HOLDTIME:
+ if (olen != 2) {
+ ND_PRINT("[option length %u != 2]", olen);
+ nd_print_invalid(ndo);
+ return;
+ } else {
+ unsigned_relts_print(ndo,
+ GET_BE_U_2(bp));
+ }
+ break;
+
+ case PIMV2_HELLO_OPTION_LANPRUNEDELAY:
+ if (olen != 4) {
+ ND_PRINT("[option length %u != 4]", olen);
+ nd_print_invalid(ndo);
+ return;
+ } else {
+ char t_bit;
+ uint16_t lan_delay, override_interval;
+ lan_delay = GET_BE_U_2(bp);
+ override_interval = GET_BE_U_2(bp + 2);
+ t_bit = (lan_delay & 0x8000)? 1 : 0;
+ lan_delay &= ~0x8000;
+ ND_PRINT("\n\t T-bit=%u, LAN delay %ums, Override interval %ums",
+ t_bit, lan_delay, override_interval);
+ }
+ break;
+
+ case PIMV2_HELLO_OPTION_DR_PRIORITY_OLD:
+ case PIMV2_HELLO_OPTION_DR_PRIORITY:
+ switch (olen) {
+ case 0:
+ ND_PRINT("Bi-Directional Capability (Old)");
+ break;
+ case 4:
+ ND_PRINT("%u", GET_BE_U_4(bp));
+ break;
+ default:
+ ND_PRINT("[option length %u != 4]", olen);
+ nd_print_invalid(ndo);
+ return;
+ break;
+ }
+ break;
+
+ case PIMV2_HELLO_OPTION_GENID:
+ if (olen != 4) {
+ ND_PRINT("[option length %u != 4]", olen);
+ nd_print_invalid(ndo);
+ return;
+ } else {
+ ND_PRINT("0x%08x", GET_BE_U_4(bp));
+ }
+ break;
+
+ case PIMV2_HELLO_OPTION_REFRESH_CAP:
+ if (olen != 4) {
+ ND_PRINT("[option length %u != 4]", olen);
+ nd_print_invalid(ndo);
+ return;
+ } else {
+ ND_PRINT("v%u", GET_U_1(bp));
+ if (GET_U_1(bp + 1) != 0) {
+ ND_PRINT(", interval ");
+ unsigned_relts_print(ndo,
+ GET_U_1(bp + 1));
+ }
+ if (GET_BE_U_2(bp + 2) != 0) {
+ ND_PRINT(" ?0x%04x?",
+ GET_BE_U_2(bp + 2));
+ }
+ }
+ break;
+
+ case PIMV2_HELLO_OPTION_BIDIR_CAP:
+ break;
+
+ case PIMV2_HELLO_OPTION_ADDRESS_LIST_OLD:
+ case PIMV2_HELLO_OPTION_ADDRESS_LIST:
+ if (ndo->ndo_vflag > 1) {
+ const u_char *ptr = bp;
+ u_int plen = len;
+ while (ptr < (bp+olen)) {
+ ND_PRINT("\n\t ");
+ advance = pimv2_addr_print(ndo, ptr, plen, pimv2_unicast, pimv2_addr_len, 0);
+ if (advance < 0)
+ goto trunc;
+ ptr += advance;
+ plen -= advance;
+ }
+ }
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, bp, "\n\t ", olen);
+ break;
+ }
+ /* do we want to see an additionally hexdump ? */
+ if (ndo->ndo_vflag> 1)
+ print_unknown_data(ndo, bp, "\n\t ", olen);
+ bp += olen;
+ len -= olen;
+ }
+ break;
+ }
+
+ case PIMV2_TYPE_REGISTER:
+ {
+ const struct ip *ip;
+
+ if (len < 4)
+ goto trunc;
+ ND_TCHECK_LEN(bp, PIMV2_REGISTER_FLAG_LEN);
+
+ ND_PRINT(", Flags [ %s ]\n\t",
+ tok2str(pimv2_register_flag_values,
+ "none",
+ GET_BE_U_4(bp)));
+
+ bp += 4; len -= 4;
+ /* encapsulated multicast packet */
+ if (len == 0)
+ goto trunc;
+ ip = (const struct ip *)bp;
+ switch (IP_V(ip)) {
+ case 0: /* Null header */
+ ND_PRINT("IP-Null-header %s > %s",
+ GET_IPADDR_STRING(ip->ip_src),
+ GET_IPADDR_STRING(ip->ip_dst));
+ break;
+
+ case 4: /* IPv4 */
+ ip_print(ndo, bp, len);
+ break;
+
+ case 6: /* IPv6 */
+ ip6_print(ndo, bp, len);
+ break;
+
+ default:
+ ND_PRINT("IP ver %u", IP_V(ip));
+ break;
+ }
+ break;
+ }
+
+ case PIMV2_TYPE_REGISTER_STOP:
+ ND_PRINT(" group=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance; len -= advance;
+ ND_PRINT(" source=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance; len -= advance;
+ break;
+
+ case PIMV2_TYPE_JOIN_PRUNE:
+ case PIMV2_TYPE_GRAFT:
+ case PIMV2_TYPE_GRAFT_ACK:
+
+
+ /*
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |PIM Ver| Type | Addr length | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Unicast-Upstream Neighbor Address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved | Num groups | Holdtime |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encoded-Multicast Group Address-1 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Number of Joined Sources | Number of Pruned Sources |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encoded-Joined Source Address-1 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . |
+ * | . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encoded-Joined Source Address-n |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encoded-Pruned Source Address-1 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . |
+ * | . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encoded-Pruned Source Address-n |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . |
+ * | . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encoded-Multicast Group Address-n |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ {
+ uint8_t ngroup;
+ uint16_t holdtime;
+ uint16_t njoin;
+ uint16_t nprune;
+ u_int i, j;
+
+ if (PIM_TYPE(pim_typever) != 7) { /*not for Graft-ACK*/
+ ND_PRINT(", upstream-neighbor: ");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance; len -= advance;
+ }
+ if (len < 4)
+ goto trunc;
+ ND_TCHECK_4(bp);
+ ngroup = GET_U_1(bp + 1);
+ holdtime = GET_BE_U_2(bp + 2);
+ ND_PRINT("\n\t %u group(s)", ngroup);
+ if (PIM_TYPE(pim_typever) != 7) { /*not for Graft-ACK*/
+ ND_PRINT(", holdtime: ");
+ if (holdtime == 0xffff)
+ ND_PRINT("infinite");
+ else
+ unsigned_relts_print(ndo, holdtime);
+ }
+ bp += 4; len -= 4;
+ for (i = 0; i < ngroup; i++) {
+ ND_PRINT("\n\t group #%u: ", i+1);
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance; len -= advance;
+ if (len < 4)
+ goto trunc;
+ ND_TCHECK_4(bp);
+ njoin = GET_BE_U_2(bp);
+ nprune = GET_BE_U_2(bp + 2);
+ ND_PRINT(", joined sources: %u, pruned sources: %u", njoin, nprune);
+ bp += 4; len -= 4;
+ for (j = 0; j < njoin; j++) {
+ ND_PRINT("\n\t joined source #%u: ", j+1);
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_source, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance; len -= advance;
+ }
+ for (j = 0; j < nprune; j++) {
+ ND_PRINT("\n\t pruned source #%u: ", j+1);
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_source, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance; len -= advance;
+ }
+ }
+ break;
+ }
+
+ case PIMV2_TYPE_BOOTSTRAP:
+ {
+ u_int i, j, frpcnt;
+
+ /* Fragment Tag, Hash Mask len, and BSR-priority */
+ if (len < 2)
+ goto trunc;
+ ND_PRINT(" tag=%x", GET_BE_U_2(bp));
+ bp += 2;
+ len -= 2;
+ if (len < 1)
+ goto trunc;
+ ND_PRINT(" hashmlen=%u", GET_U_1(bp));
+ if (len < 2)
+ goto trunc;
+ ND_TCHECK_1(bp + 2);
+ ND_PRINT(" BSRprio=%u", GET_U_1(bp + 1));
+ bp += 2;
+ len -= 2;
+
+ /* Encoded-Unicast-BSR-Address */
+ ND_PRINT(" BSR=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance;
+ len -= advance;
+
+ for (i = 0; len > 0; i++) {
+ /* Encoded-Group Address */
+ ND_PRINT(" (group%u: ", i);
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance;
+ len -= advance;
+
+ /* RP-Count, Frag RP-Cnt, and rsvd */
+ if (len < 1)
+ goto trunc;
+ ND_PRINT(" RPcnt=%u", GET_U_1(bp));
+ if (len < 2)
+ goto trunc;
+ frpcnt = GET_U_1(bp + 1);
+ ND_PRINT(" FRPcnt=%u", frpcnt);
+ if (len < 4)
+ goto trunc;
+ bp += 4;
+ len -= 4;
+
+ for (j = 0; j < frpcnt && len > 0; j++) {
+ /* each RP info */
+ ND_PRINT(" RP%u=", j);
+ if ((advance = pimv2_addr_print(ndo, bp, len,
+ pimv2_unicast,
+ pimv2_addr_len,
+ 0)) < 0)
+ goto trunc;
+ bp += advance;
+ len -= advance;
+
+ if (len < 2)
+ goto trunc;
+ ND_PRINT(",holdtime=");
+ unsigned_relts_print(ndo,
+ GET_BE_U_2(bp));
+ if (len < 3)
+ goto trunc;
+ ND_PRINT(",prio=%u", GET_U_1(bp + 2));
+ if (len < 4)
+ goto trunc;
+ bp += 4;
+ len -= 4;
+ }
+ ND_PRINT(")");
+ }
+ break;
+ }
+ case PIMV2_TYPE_ASSERT:
+ ND_PRINT(" group=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance; len -= advance;
+ ND_PRINT(" src=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance; len -= advance;
+ if (len < 8)
+ goto trunc;
+ ND_TCHECK_8(bp);
+ if (GET_U_1(bp) & 0x80)
+ ND_PRINT(" RPT");
+ ND_PRINT(" pref=%u", GET_BE_U_4(bp) & 0x7fffffff);
+ ND_PRINT(" metric=%u", GET_BE_U_4(bp + 4));
+ break;
+
+ case PIMV2_TYPE_CANDIDATE_RP:
+ {
+ u_int i, pfxcnt;
+
+ /* Prefix-Cnt, Priority, and Holdtime */
+ if (len < 1)
+ goto trunc;
+ ND_PRINT(" prefix-cnt=%u", GET_U_1(bp));
+ pfxcnt = GET_U_1(bp);
+ if (len < 2)
+ goto trunc;
+ ND_PRINT(" prio=%u", GET_U_1(bp + 1));
+ if (len < 4)
+ goto trunc;
+ ND_PRINT(" holdtime=");
+ unsigned_relts_print(ndo, GET_BE_U_2(bp + 2));
+ bp += 4;
+ len -= 4;
+
+ /* Encoded-Unicast-RP-Address */
+ ND_PRINT(" RP=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance;
+ len -= advance;
+
+ /* Encoded-Group Addresses */
+ for (i = 0; i < pfxcnt && len > 0; i++) {
+ ND_PRINT(" Group%u=", i);
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance;
+ len -= advance;
+ }
+ break;
+ }
+
+ case PIMV2_TYPE_PRUNE_REFRESH:
+ ND_PRINT(" src=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance;
+ len -= advance;
+ ND_PRINT(" grp=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_group, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance;
+ len -= advance;
+ ND_PRINT(" forwarder=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0)
+ goto trunc;
+ bp += advance;
+ len -= advance;
+ if (len < 2)
+ goto trunc;
+ ND_PRINT(" TUNR ");
+ unsigned_relts_print(ndo, GET_BE_U_2(bp));
+ break;
+
+ case PIMV2_TYPE_DF_ELECTION:
+ subtype = PIM_SUBTYPE(GET_U_1(pim->pim_rsv));
+ ND_PRINT("\n\t %s,", tok2str( pimv2_df_election_flag_values,
+ "Unknown", subtype) );
+
+ ND_PRINT(" rpa=");
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0) {
+ goto trunc;
+ }
+ bp += advance;
+ len -= advance;
+ ND_PRINT(" sender pref=%u", GET_BE_U_4(bp) );
+ ND_PRINT(" sender metric=%u", GET_BE_U_4(bp + 4));
+
+ bp += 8;
+ len -= 8;
+
+ switch (subtype) {
+ case PIMV2_DF_ELECTION_BACKOFF:
+ case PIMV2_DF_ELECTION_PASS:
+ ND_PRINT("\n\t %s addr=", PIMV2_DF_ELECTION_PASS_BACKOFF_STR(subtype));
+ if ((advance = pimv2_addr_print(ndo, bp, len, pimv2_unicast, pimv2_addr_len, 0)) < 0) {
+ goto trunc;
+ }
+ bp += advance;
+ len -= advance;
+
+ ND_PRINT(" %s pref=%u", PIMV2_DF_ELECTION_PASS_BACKOFF_STR(subtype), GET_BE_U_4(bp) );
+ ND_PRINT(" %s metric=%u", PIMV2_DF_ELECTION_PASS_BACKOFF_STR(subtype), GET_BE_U_4(bp + 4));
+
+ bp += 8;
+ len -= 8;
+
+ if (subtype == PIMV2_DF_ELECTION_BACKOFF) {
+ ND_PRINT(" interval %dms", GET_BE_U_2(bp));
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+
+ default:
+ ND_PRINT(" [type %u]", PIM_TYPE(pim_typever));
+ break;
+ }
+
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-pktap.c b/print-pktap.c
new file mode 100644
index 0000000..05ce5f9
--- /dev/null
+++ b/print-pktap.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Apple's DLT_PKTAP printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+#ifdef DLT_PKTAP
+
+/*
+ * XXX - these are little-endian in the captures I've seen, but Apple
+ * no longer make any big-endian machines (Macs use x86, iOS machines
+ * use ARM and run it little-endian), so that might be by definition
+ * or they might be host-endian.
+ *
+ * If a big-endian PKTAP file ever shows up, and it comes from a
+ * big-endian machine, presumably these are host-endian, and we need
+ * to just fetch the fields directly in tcpdump but byte-swap them
+ * to host byte order in libpcap.
+ */
+typedef struct pktap_header {
+ nd_uint32_t pkt_len; /* length of pktap header */
+ nd_uint32_t pkt_rectype; /* type of record */
+ nd_uint32_t pkt_dlt; /* DLT type of this packet */
+ char pkt_ifname[24]; /* interface name */
+ nd_uint32_t pkt_flags;
+ nd_uint32_t pkt_pfamily; /* "protocol family" */
+ nd_uint32_t pkt_llhdrlen; /* link-layer header length? */
+ nd_uint32_t pkt_lltrlrlen; /* link-layer trailer length? */
+ nd_uint32_t pkt_pid; /* process ID */
+ char pkt_cmdname[20]; /* command name */
+ nd_uint32_t pkt_svc_class; /* "service class" */
+ nd_uint16_t pkt_iftype; /* "interface type" */
+ nd_uint16_t pkt_ifunit; /* unit number of interface? */
+ nd_uint32_t pkt_epid; /* "effective process ID" */
+ char pkt_ecmdname[20]; /* "effective command name" */
+} pktap_header_t;
+
+/*
+ * Record types.
+ */
+#define PKT_REC_NONE 0 /* nothing follows the header */
+#define PKT_REC_PACKET 1 /* a packet follows the header */
+
+static void
+pktap_header_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const pktap_header_t *hdr;
+ uint32_t dlt, hdrlen;
+ const char *dltname;
+
+ hdr = (const pktap_header_t *)bp;
+
+ dlt = GET_LE_U_4(hdr->pkt_dlt);
+ hdrlen = GET_LE_U_4(hdr->pkt_len);
+ dltname = pcap_datalink_val_to_name(dlt);
+ if (!ndo->ndo_qflag) {
+ ND_PRINT("DLT %s (%u) len %u",
+ (dltname != NULL ? dltname : "UNKNOWN"), dlt, hdrlen);
+ } else {
+ ND_PRINT("%s", (dltname != NULL ? dltname : "UNKNOWN"));
+ }
+
+ ND_PRINT(", length %u: ", length);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+pktap_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ uint32_t dlt, hdrlen, rectype;
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ if_printer printer;
+ const pktap_header_t *hdr;
+ struct pcap_pkthdr nhdr;
+
+ ndo->ndo_protocol = "pktap";
+ if (length < sizeof(pktap_header_t)) {
+ ND_PRINT(" (packet too short, %u < %zu)",
+ length, sizeof(pktap_header_t));
+ goto invalid;
+ }
+ hdr = (const pktap_header_t *)p;
+ dlt = GET_LE_U_4(hdr->pkt_dlt);
+ hdrlen = GET_LE_U_4(hdr->pkt_len);
+ if (hdrlen < sizeof(pktap_header_t)) {
+ /*
+ * Claimed header length < structure length.
+ * XXX - does this just mean some fields aren't
+ * being supplied, or is it truly an error (i.e.,
+ * is the length supplied so that the header can
+ * be expanded in the future)?
+ */
+ ND_PRINT(" (pkt_len too small, %u < %zu)",
+ hdrlen, sizeof(pktap_header_t));
+ goto invalid;
+ }
+ if (hdrlen > length) {
+ ND_PRINT(" (pkt_len too big, %u > %u)",
+ hdrlen, length);
+ goto invalid;
+ }
+ ND_TCHECK_LEN(p, hdrlen);
+
+ if (ndo->ndo_eflag)
+ pktap_header_print(ndo, p, length);
+
+ length -= hdrlen;
+ caplen -= hdrlen;
+ p += hdrlen;
+
+ rectype = GET_LE_U_4(hdr->pkt_rectype);
+ switch (rectype) {
+
+ case PKT_REC_NONE:
+ ND_PRINT("no data");
+ break;
+
+ case PKT_REC_PACKET:
+ printer = lookup_printer(dlt);
+ if (printer != NULL) {
+ nhdr = *h;
+ nhdr.caplen = caplen;
+ nhdr.len = length;
+ printer(ndo, &nhdr, p);
+ hdrlen += ndo->ndo_ll_hdr_len;
+ } else {
+ if (!ndo->ndo_eflag)
+ pktap_header_print(ndo, (const u_char *)hdr,
+ length + hdrlen);
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+ break;
+ }
+
+ ndo->ndo_ll_hdr_len += hdrlen;
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+#endif /* DLT_PKTAP */
diff --git a/print-ppi.c b/print-ppi.c
new file mode 100644
index 0000000..774edfb
--- /dev/null
+++ b/print-ppi.c
@@ -0,0 +1,131 @@
+/*
+ * Oracle
+ */
+
+/* \summary: Per-Packet Information (DLT_PPI) printer */
+
+/* Specification:
+ * Per-Packet Information Header Specification - Version 1.0.7
+ * https://web.archive.org/web/20160328114748/http://www.cacetech.com/documents/PPI%20Header%20format%201.0.7.pdf
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+
+typedef struct ppi_header {
+ nd_uint8_t ppi_ver; /* Version. Currently 0 */
+ nd_uint8_t ppi_flags; /* Flags. */
+ nd_uint16_t ppi_len; /* Length of entire message, including
+ * this header and TLV payload. */
+ nd_uint32_t ppi_dlt; /* Data Link Type of the captured
+ * packet data. */
+} ppi_header_t;
+
+#define PPI_HDRLEN 8
+
+#ifdef DLT_PPI
+
+static void
+ppi_header_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const ppi_header_t *hdr;
+ uint16_t len;
+ uint32_t dlt;
+ const char *dltname;
+
+ hdr = (const ppi_header_t *)bp;
+
+ len = GET_LE_U_2(hdr->ppi_len);
+ dlt = GET_LE_U_4(hdr->ppi_dlt);
+ dltname = pcap_datalink_val_to_name(dlt);
+
+ if (!ndo->ndo_qflag) {
+ ND_PRINT("V.%u DLT %s (%u) len %u", GET_U_1(hdr->ppi_ver),
+ (dltname != NULL ? dltname : "UNKNOWN"), dlt,
+ len);
+ } else {
+ ND_PRINT("%s", (dltname != NULL ? dltname : "UNKNOWN"));
+ }
+
+ ND_PRINT(", length %u: ", length);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+ppi_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ if_printer printer;
+ const ppi_header_t *hdr;
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ uint16_t len;
+ uint32_t dlt;
+ uint32_t hdrlen;
+ struct pcap_pkthdr nhdr;
+
+ ndo->ndo_protocol = "ppi";
+ if (caplen < sizeof(ppi_header_t)) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ hdr = (const ppi_header_t *)p;
+ len = GET_LE_U_2(hdr->ppi_len);
+ if (len < sizeof(ppi_header_t) || len > 65532) {
+ /* It MUST be between 8 and 65,532 inclusive (spec 3.1.3) */
+ ND_PRINT(" [length %u < %zu or > 65532]", len,
+ sizeof(ppi_header_t));
+ nd_print_invalid(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+ if (caplen < len) {
+ /*
+ * If we don't have the entire PPI header, don't
+ * bother.
+ */
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+ dlt = GET_LE_U_4(hdr->ppi_dlt);
+
+ if (ndo->ndo_eflag)
+ ppi_header_print(ndo, p, length);
+
+ length -= len;
+ caplen -= len;
+ p += len;
+
+ printer = lookup_printer(dlt);
+ if (printer != NULL) {
+ nhdr = *h;
+ nhdr.caplen = caplen;
+ nhdr.len = length;
+ printer(ndo, &nhdr, p);
+ hdrlen = ndo->ndo_ll_hdr_len;
+ } else {
+ if (!ndo->ndo_eflag)
+ ppi_header_print(ndo, (const u_char *)hdr, length + len);
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ hdrlen = 0;
+ }
+ ndo->ndo_ll_hdr_len += len + hdrlen;
+}
+#endif /* DLT_PPI */
diff --git a/print-ppp.c b/print-ppp.c
new file mode 100644
index 0000000..baeb4f0
--- /dev/null
+++ b/print-ppp.c
@@ -0,0 +1,1879 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Extensively modified by Motonori Shindo (mshindo@mshindo.net) for more
+ * complete PPP support.
+ */
+
+/* \summary: Point to Point Protocol (PPP) printer */
+
+/*
+ * TODO:
+ * o resolve XXX as much as possible
+ * o MP support
+ * o BAP support
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#ifdef __bsdi__
+#include <net/slcompress.h>
+#include <net/if_ppp.h>
+#endif
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "ppp.h"
+#include "chdlc.h"
+#include "ethertype.h"
+#include "oui.h"
+#include "netdissect-alloc.h"
+
+/*
+ * The following constants are defined by IANA. Please refer to
+ * https://www.isi.edu/in-notes/iana/assignments/ppp-numbers
+ * for the up-to-date information.
+ */
+
+/* Protocol Codes defined in ppp.h */
+
+static const struct tok ppptype2str[] = {
+ { PPP_IP, "IP" },
+ { PPP_OSI, "OSI" },
+ { PPP_NS, "NS" },
+ { PPP_DECNET, "DECNET" },
+ { PPP_APPLE, "APPLE" },
+ { PPP_IPX, "IPX" },
+ { PPP_VJC, "VJC IP" },
+ { PPP_VJNC, "VJNC IP" },
+ { PPP_BRPDU, "BRPDU" },
+ { PPP_STII, "STII" },
+ { PPP_VINES, "VINES" },
+ { PPP_MPLS_UCAST, "MPLS" },
+ { PPP_MPLS_MCAST, "MPLS" },
+ { PPP_COMP, "Compressed"},
+ { PPP_ML, "MLPPP"},
+ { PPP_IPV6, "IP6"},
+
+ { PPP_HELLO, "HELLO" },
+ { PPP_LUXCOM, "LUXCOM" },
+ { PPP_SNS, "SNS" },
+ { PPP_IPCP, "IPCP" },
+ { PPP_OSICP, "OSICP" },
+ { PPP_NSCP, "NSCP" },
+ { PPP_DECNETCP, "DECNETCP" },
+ { PPP_APPLECP, "APPLECP" },
+ { PPP_IPXCP, "IPXCP" },
+ { PPP_STIICP, "STIICP" },
+ { PPP_VINESCP, "VINESCP" },
+ { PPP_IPV6CP, "IP6CP" },
+ { PPP_MPLSCP, "MPLSCP" },
+
+ { PPP_LCP, "LCP" },
+ { PPP_PAP, "PAP" },
+ { PPP_LQM, "LQM" },
+ { PPP_CHAP, "CHAP" },
+ { PPP_EAP, "EAP" },
+ { PPP_SPAP, "SPAP" },
+ { PPP_SPAP_OLD, "Old-SPAP" },
+ { PPP_BACP, "BACP" },
+ { PPP_BAP, "BAP" },
+ { PPP_MPCP, "MLPPP-CP" },
+ { PPP_CCP, "CCP" },
+ { 0, NULL }
+};
+
+/* Control Protocols (LCP/IPCP/CCP etc.) Codes defined in RFC 1661 */
+
+#define CPCODES_VEXT 0 /* Vendor-Specific (RFC2153) */
+#define CPCODES_CONF_REQ 1 /* Configure-Request */
+#define CPCODES_CONF_ACK 2 /* Configure-Ack */
+#define CPCODES_CONF_NAK 3 /* Configure-Nak */
+#define CPCODES_CONF_REJ 4 /* Configure-Reject */
+#define CPCODES_TERM_REQ 5 /* Terminate-Request */
+#define CPCODES_TERM_ACK 6 /* Terminate-Ack */
+#define CPCODES_CODE_REJ 7 /* Code-Reject */
+#define CPCODES_PROT_REJ 8 /* Protocol-Reject (LCP only) */
+#define CPCODES_ECHO_REQ 9 /* Echo-Request (LCP only) */
+#define CPCODES_ECHO_RPL 10 /* Echo-Reply (LCP only) */
+#define CPCODES_DISC_REQ 11 /* Discard-Request (LCP only) */
+#define CPCODES_ID 12 /* Identification (LCP only) RFC1570 */
+#define CPCODES_TIME_REM 13 /* Time-Remaining (LCP only) RFC1570 */
+#define CPCODES_RESET_REQ 14 /* Reset-Request (CCP only) RFC1962 */
+#define CPCODES_RESET_REP 15 /* Reset-Reply (CCP only) */
+
+static const struct tok cpcodes[] = {
+ {CPCODES_VEXT, "Vendor-Extension"}, /* RFC2153 */
+ {CPCODES_CONF_REQ, "Conf-Request"},
+ {CPCODES_CONF_ACK, "Conf-Ack"},
+ {CPCODES_CONF_NAK, "Conf-Nack"},
+ {CPCODES_CONF_REJ, "Conf-Reject"},
+ {CPCODES_TERM_REQ, "Term-Request"},
+ {CPCODES_TERM_ACK, "Term-Ack"},
+ {CPCODES_CODE_REJ, "Code-Reject"},
+ {CPCODES_PROT_REJ, "Prot-Reject"},
+ {CPCODES_ECHO_REQ, "Echo-Request"},
+ {CPCODES_ECHO_RPL, "Echo-Reply"},
+ {CPCODES_DISC_REQ, "Disc-Req"},
+ {CPCODES_ID, "Ident"}, /* RFC1570 */
+ {CPCODES_TIME_REM, "Time-Rem"}, /* RFC1570 */
+ {CPCODES_RESET_REQ, "Reset-Req"}, /* RFC1962 */
+ {CPCODES_RESET_REP, "Reset-Ack"}, /* RFC1962 */
+ {0, NULL}
+};
+
+/* LCP Config Options */
+
+#define LCPOPT_VEXT 0
+#define LCPOPT_MRU 1
+#define LCPOPT_ACCM 2
+#define LCPOPT_AP 3
+#define LCPOPT_QP 4
+#define LCPOPT_MN 5
+#define LCPOPT_DEP6 6
+#define LCPOPT_PFC 7
+#define LCPOPT_ACFC 8
+#define LCPOPT_FCSALT 9
+#define LCPOPT_SDP 10
+#define LCPOPT_NUMMODE 11
+#define LCPOPT_DEP12 12
+#define LCPOPT_CBACK 13
+#define LCPOPT_DEP14 14
+#define LCPOPT_DEP15 15
+#define LCPOPT_DEP16 16
+#define LCPOPT_MLMRRU 17
+#define LCPOPT_MLSSNHF 18
+#define LCPOPT_MLED 19
+#define LCPOPT_PROP 20
+#define LCPOPT_DCEID 21
+#define LCPOPT_MPP 22
+#define LCPOPT_LD 23
+#define LCPOPT_LCPAOPT 24
+#define LCPOPT_COBS 25
+#define LCPOPT_PE 26
+#define LCPOPT_MLHF 27
+#define LCPOPT_I18N 28
+#define LCPOPT_SDLOS 29
+#define LCPOPT_PPPMUX 30
+
+static const char *lcpconfopts[] = {
+ "Vend-Ext", /* (0) */
+ "MRU", /* (1) */
+ "ACCM", /* (2) */
+ "Auth-Prot", /* (3) */
+ "Qual-Prot", /* (4) */
+ "Magic-Num", /* (5) */
+ "deprecated(6)", /* used to be a Quality Protocol */
+ "PFC", /* (7) */
+ "ACFC", /* (8) */
+ "FCS-Alt", /* (9) */
+ "SDP", /* (10) */
+ "Num-Mode", /* (11) */
+ "deprecated(12)", /* used to be a Multi-Link-Procedure*/
+ "Call-Back", /* (13) */
+ "deprecated(14)", /* used to be a Connect-Time */
+ "deprecated(15)", /* used to be a Compund-Frames */
+ "deprecated(16)", /* used to be a Nominal-Data-Encap */
+ "MRRU", /* (17) */
+ "12-Bit seq #", /* (18) */
+ "End-Disc", /* (19) */
+ "Proprietary", /* (20) */
+ "DCE-Id", /* (21) */
+ "MP+", /* (22) */
+ "Link-Disc", /* (23) */
+ "LCP-Auth-Opt", /* (24) */
+ "COBS", /* (25) */
+ "Prefix-elision", /* (26) */
+ "Multilink-header-Form",/* (27) */
+ "I18N", /* (28) */
+ "SDL-over-SONET/SDH", /* (29) */
+ "PPP-Muxing", /* (30) */
+};
+
+#define NUM_LCPOPTS (sizeof(lcpconfopts) / sizeof(lcpconfopts[0]))
+
+/* ECP - to be supported */
+
+/* CCP Config Options */
+
+#define CCPOPT_OUI 0 /* RFC1962 */
+#define CCPOPT_PRED1 1 /* RFC1962 */
+#define CCPOPT_PRED2 2 /* RFC1962 */
+#define CCPOPT_PJUMP 3 /* RFC1962 */
+/* 4-15 unassigned */
+#define CCPOPT_HPPPC 16 /* RFC1962 */
+#define CCPOPT_STACLZS 17 /* RFC1974 */
+#define CCPOPT_MPPC 18 /* RFC2118 */
+#define CCPOPT_GFZA 19 /* RFC1962 */
+#define CCPOPT_V42BIS 20 /* RFC1962 */
+#define CCPOPT_BSDCOMP 21 /* RFC1977 */
+/* 22 unassigned */
+#define CCPOPT_LZSDCP 23 /* RFC1967 */
+#define CCPOPT_MVRCA 24 /* RFC1975 */
+#define CCPOPT_DEC 25 /* RFC1976 */
+#define CCPOPT_DEFLATE 26 /* RFC1979 */
+/* 27-254 unassigned */
+#define CCPOPT_RESV 255 /* RFC1962 */
+
+static const struct tok ccpconfopts_values[] = {
+ { CCPOPT_OUI, "OUI" },
+ { CCPOPT_PRED1, "Pred-1" },
+ { CCPOPT_PRED2, "Pred-2" },
+ { CCPOPT_PJUMP, "Puddle" },
+ { CCPOPT_HPPPC, "HP-PPC" },
+ { CCPOPT_STACLZS, "Stac-LZS" },
+ { CCPOPT_MPPC, "MPPC" },
+ { CCPOPT_GFZA, "Gand-FZA" },
+ { CCPOPT_V42BIS, "V.42bis" },
+ { CCPOPT_BSDCOMP, "BSD-Comp" },
+ { CCPOPT_LZSDCP, "LZS-DCP" },
+ { CCPOPT_MVRCA, "MVRCA" },
+ { CCPOPT_DEC, "DEC" },
+ { CCPOPT_DEFLATE, "Deflate" },
+ { CCPOPT_RESV, "Reserved"},
+ {0, NULL}
+};
+
+/* BACP Config Options */
+
+#define BACPOPT_FPEER 1 /* RFC2125 */
+
+static const struct tok bacconfopts_values[] = {
+ { BACPOPT_FPEER, "Favored-Peer" },
+ {0, NULL}
+};
+
+
+/* SDCP - to be supported */
+
+/* IPCP Config Options */
+#define IPCPOPT_2ADDR 1 /* RFC1172, RFC1332 (deprecated) */
+#define IPCPOPT_IPCOMP 2 /* RFC1332 */
+#define IPCPOPT_ADDR 3 /* RFC1332 */
+#define IPCPOPT_MOBILE4 4 /* RFC2290 */
+#define IPCPOPT_PRIDNS 129 /* RFC1877 */
+#define IPCPOPT_PRINBNS 130 /* RFC1877 */
+#define IPCPOPT_SECDNS 131 /* RFC1877 */
+#define IPCPOPT_SECNBNS 132 /* RFC1877 */
+
+static const struct tok ipcpopt_values[] = {
+ { IPCPOPT_2ADDR, "IP-Addrs" },
+ { IPCPOPT_IPCOMP, "IP-Comp" },
+ { IPCPOPT_ADDR, "IP-Addr" },
+ { IPCPOPT_MOBILE4, "Home-Addr" },
+ { IPCPOPT_PRIDNS, "Pri-DNS" },
+ { IPCPOPT_PRINBNS, "Pri-NBNS" },
+ { IPCPOPT_SECDNS, "Sec-DNS" },
+ { IPCPOPT_SECNBNS, "Sec-NBNS" },
+ { 0, NULL }
+};
+
+#define IPCPOPT_IPCOMP_HDRCOMP 0x61 /* rfc3544 */
+#define IPCPOPT_IPCOMP_MINLEN 14
+
+static const struct tok ipcpopt_compproto_values[] = {
+ { PPP_VJC, "VJ-Comp" },
+ { IPCPOPT_IPCOMP_HDRCOMP, "IP Header Compression" },
+ { 0, NULL }
+};
+
+static const struct tok ipcpopt_compproto_subopt_values[] = {
+ { 1, "RTP-Compression" },
+ { 2, "Enhanced RTP-Compression" },
+ { 0, NULL }
+};
+
+/* IP6CP Config Options */
+#define IP6CP_IFID 1
+
+static const struct tok ip6cpopt_values[] = {
+ { IP6CP_IFID, "Interface-ID" },
+ { 0, NULL }
+};
+
+/* ATCP - to be supported */
+/* OSINLCP - to be supported */
+/* BVCP - to be supported */
+/* BCP - to be supported */
+/* IPXCP - to be supported */
+/* MPLSCP - to be supported */
+
+/* Auth Algorithms */
+
+/* 0-4 Reserved (RFC1994) */
+#define AUTHALG_CHAPMD5 5 /* RFC1994 */
+#define AUTHALG_MSCHAP1 128 /* RFC2433 */
+#define AUTHALG_MSCHAP2 129 /* RFC2795 */
+
+static const struct tok authalg_values[] = {
+ { AUTHALG_CHAPMD5, "MD5" },
+ { AUTHALG_MSCHAP1, "MS-CHAPv1" },
+ { AUTHALG_MSCHAP2, "MS-CHAPv2" },
+ { 0, NULL }
+};
+
+/* FCS Alternatives - to be supported */
+
+/* Multilink Endpoint Discriminator (RFC1717) */
+#define MEDCLASS_NULL 0 /* Null Class */
+#define MEDCLASS_LOCAL 1 /* Locally Assigned */
+#define MEDCLASS_IPV4 2 /* Internet Protocol (IPv4) */
+#define MEDCLASS_MAC 3 /* IEEE 802.1 global MAC address */
+#define MEDCLASS_MNB 4 /* PPP Magic Number Block */
+#define MEDCLASS_PSNDN 5 /* Public Switched Network Director Number */
+
+/* PPP LCP Callback */
+#define CALLBACK_AUTH 0 /* Location determined by user auth */
+#define CALLBACK_DSTR 1 /* Dialing string */
+#define CALLBACK_LID 2 /* Location identifier */
+#define CALLBACK_E164 3 /* E.164 number */
+#define CALLBACK_X500 4 /* X.500 distinguished name */
+#define CALLBACK_CBCP 6 /* Location is determined during CBCP nego */
+
+static const struct tok ppp_callback_values[] = {
+ { CALLBACK_AUTH, "UserAuth" },
+ { CALLBACK_DSTR, "DialString" },
+ { CALLBACK_LID, "LocalID" },
+ { CALLBACK_E164, "E.164" },
+ { CALLBACK_X500, "X.500" },
+ { CALLBACK_CBCP, "CBCP" },
+ { 0, NULL }
+};
+
+/* CHAP */
+
+#define CHAP_CHAL 1
+#define CHAP_RESP 2
+#define CHAP_SUCC 3
+#define CHAP_FAIL 4
+
+static const struct tok chapcode_values[] = {
+ { CHAP_CHAL, "Challenge" },
+ { CHAP_RESP, "Response" },
+ { CHAP_SUCC, "Success" },
+ { CHAP_FAIL, "Fail" },
+ { 0, NULL}
+};
+
+/* PAP */
+
+#define PAP_AREQ 1
+#define PAP_AACK 2
+#define PAP_ANAK 3
+
+static const struct tok papcode_values[] = {
+ { PAP_AREQ, "Auth-Req" },
+ { PAP_AACK, "Auth-ACK" },
+ { PAP_ANAK, "Auth-NACK" },
+ { 0, NULL }
+};
+
+/* BAP */
+#define BAP_CALLREQ 1
+#define BAP_CALLRES 2
+#define BAP_CBREQ 3
+#define BAP_CBRES 4
+#define BAP_LDQREQ 5
+#define BAP_LDQRES 6
+#define BAP_CSIND 7
+#define BAP_CSRES 8
+
+static u_int print_lcp_config_options(netdissect_options *, const u_char *p, u_int);
+static u_int print_ipcp_config_options(netdissect_options *, const u_char *p, u_int);
+static u_int print_ip6cp_config_options(netdissect_options *, const u_char *p, u_int);
+static u_int print_ccp_config_options(netdissect_options *, const u_char *p, u_int);
+static u_int print_bacp_config_options(netdissect_options *, const u_char *p, u_int);
+static void handle_ppp(netdissect_options *, u_int proto, const u_char *p, u_int length);
+
+/* generic Control Protocol (e.g. LCP, IPCP, CCP, etc.) handler */
+static void
+handle_ctrl_proto(netdissect_options *ndo,
+ u_int proto, const u_char *pptr, u_int length)
+{
+ const char *typestr;
+ u_int code, len;
+ u_int (*pfunc)(netdissect_options *, const u_char *, u_int);
+ u_int tlen, advance;
+ const u_char *tptr;
+
+ tptr=pptr;
+
+ typestr = tok2str(ppptype2str, "unknown ctrl-proto (0x%04x)", proto);
+ ND_PRINT("%s, ", typestr);
+
+ if (length < 4) /* FIXME weak boundary checking */
+ goto trunc;
+ ND_TCHECK_2(tptr);
+
+ code = GET_U_1(tptr);
+ tptr++;
+
+ ND_PRINT("%s (0x%02x), id %u, length %u",
+ tok2str(cpcodes, "Unknown Opcode",code),
+ code,
+ GET_U_1(tptr), /* ID */
+ length + 2);
+ tptr++;
+
+ if (!ndo->ndo_vflag)
+ return;
+
+ len = GET_BE_U_2(tptr);
+ tptr += 2;
+
+ if (len < 4) {
+ ND_PRINT("\n\tencoded length %u (< 4))", len);
+ return;
+ }
+
+ if (len > length) {
+ ND_PRINT("\n\tencoded length %u (> packet length %u))", len, length);
+ return;
+ }
+ length = len;
+
+ ND_PRINT("\n\tencoded length %u (=Option(s) length %u)", len, len - 4);
+
+ if (length == 4)
+ return; /* there may be a NULL confreq etc. */
+
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, pptr - 2, "\n\t", 6);
+
+
+ switch (code) {
+ case CPCODES_VEXT:
+ if (length < 11)
+ break;
+ ND_PRINT("\n\t Magic-Num 0x%08x", GET_BE_U_4(tptr));
+ tptr += 4;
+ ND_PRINT(" Vendor: %s (%u)",
+ tok2str(oui_values,"Unknown",GET_BE_U_3(tptr)),
+ GET_BE_U_3(tptr));
+ /* XXX: need to decode Kind and Value(s)? */
+ break;
+ case CPCODES_CONF_REQ:
+ case CPCODES_CONF_ACK:
+ case CPCODES_CONF_NAK:
+ case CPCODES_CONF_REJ:
+ tlen = len - 4; /* Code(1), Identifier(1) and Length(2) */
+ do {
+ switch (proto) {
+ case PPP_LCP:
+ pfunc = print_lcp_config_options;
+ break;
+ case PPP_IPCP:
+ pfunc = print_ipcp_config_options;
+ break;
+ case PPP_IPV6CP:
+ pfunc = print_ip6cp_config_options;
+ break;
+ case PPP_CCP:
+ pfunc = print_ccp_config_options;
+ break;
+ case PPP_BACP:
+ pfunc = print_bacp_config_options;
+ break;
+ default:
+ /*
+ * No print routine for the options for
+ * this protocol.
+ */
+ pfunc = NULL;
+ break;
+ }
+
+ if (pfunc == NULL) /* catch the above null pointer if unknown CP */
+ break;
+
+ if ((advance = (*pfunc)(ndo, tptr, len)) == 0)
+ break;
+ if (tlen < advance) {
+ ND_PRINT(" [remaining options length %u < %u]",
+ tlen, advance);
+ nd_print_invalid(ndo);
+ break;
+ }
+ tlen -= advance;
+ tptr += advance;
+ } while (tlen != 0);
+ break;
+
+ case CPCODES_TERM_REQ:
+ case CPCODES_TERM_ACK:
+ /* XXX: need to decode Data? */
+ break;
+ case CPCODES_CODE_REJ:
+ /* XXX: need to decode Rejected-Packet? */
+ break;
+ case CPCODES_PROT_REJ:
+ if (length < 6)
+ break;
+ ND_PRINT("\n\t Rejected %s Protocol (0x%04x)",
+ tok2str(ppptype2str,"unknown", GET_BE_U_2(tptr)),
+ GET_BE_U_2(tptr));
+ /* XXX: need to decode Rejected-Information? - hexdump for now */
+ if (len > 6) {
+ ND_PRINT("\n\t Rejected Packet");
+ print_unknown_data(ndo, tptr + 2, "\n\t ", len - 2);
+ }
+ break;
+ case CPCODES_ECHO_REQ:
+ case CPCODES_ECHO_RPL:
+ case CPCODES_DISC_REQ:
+ if (length < 8)
+ break;
+ ND_PRINT("\n\t Magic-Num 0x%08x", GET_BE_U_4(tptr));
+ /* XXX: need to decode Data? - hexdump for now */
+ if (len > 8) {
+ ND_PRINT("\n\t -----trailing data-----");
+ ND_TCHECK_LEN(tptr + 4, len - 8);
+ print_unknown_data(ndo, tptr + 4, "\n\t ", len - 8);
+ }
+ break;
+ case CPCODES_ID:
+ if (length < 8)
+ break;
+ ND_PRINT("\n\t Magic-Num 0x%08x", GET_BE_U_4(tptr));
+ /* RFC 1661 says this is intended to be human readable */
+ if (len > 8) {
+ ND_PRINT("\n\t Message\n\t ");
+ if (nd_printn(ndo, tptr + 4, len - 4, ndo->ndo_snapend))
+ goto trunc;
+ }
+ break;
+ case CPCODES_TIME_REM:
+ if (length < 12)
+ break;
+ ND_PRINT("\n\t Magic-Num 0x%08x", GET_BE_U_4(tptr));
+ ND_PRINT(", Seconds-Remaining %us", GET_BE_U_4(tptr + 4));
+ /* XXX: need to decode Message? */
+ break;
+ default:
+ /* XXX this is dirty but we do not get the
+ * original pointer passed to the begin
+ * the PPP packet */
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, pptr - 2, "\n\t ", length + 2);
+ break;
+ }
+ return;
+
+trunc:
+ ND_PRINT("[|%s]", typestr);
+}
+
+/* LCP config options */
+static u_int
+print_lcp_config_options(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int opt, len;
+
+ if (length < 2)
+ return 0;
+ ND_TCHECK_2(p);
+ opt = GET_U_1(p);
+ len = GET_U_1(p + 1);
+ if (length < len)
+ return 0;
+ if (len < 2) {
+ if (opt < NUM_LCPOPTS)
+ ND_PRINT("\n\t %s Option (0x%02x), length %u (length bogus, should be >= 2)",
+ lcpconfopts[opt], opt, len);
+ else
+ ND_PRINT("\n\tunknown LCP option 0x%02x", opt);
+ return 0;
+ }
+ if (opt < NUM_LCPOPTS)
+ ND_PRINT("\n\t %s Option (0x%02x), length %u", lcpconfopts[opt], opt, len);
+ else {
+ ND_PRINT("\n\tunknown LCP option 0x%02x", opt);
+ return len;
+ }
+
+ switch (opt) {
+ case LCPOPT_VEXT:
+ if (len < 6) {
+ ND_PRINT(" (length bogus, should be >= 6)");
+ return len;
+ }
+ ND_PRINT(": Vendor: %s (%u)",
+ tok2str(oui_values,"Unknown",GET_BE_U_3(p + 2)),
+ GET_BE_U_3(p + 2));
+#if 0
+ ND_PRINT(", kind: 0x%02x", GET_U_1(p + 5));
+ ND_PRINT(", Value: 0x");
+ for (i = 0; i < len - 6; i++) {
+ ND_PRINT("%02x", GET_U_1(p + 6 + i));
+ }
+#endif
+ break;
+ case LCPOPT_MRU:
+ if (len != 4) {
+ ND_PRINT(" (length bogus, should be = 4)");
+ return len;
+ }
+ ND_PRINT(": %u", GET_BE_U_2(p + 2));
+ break;
+ case LCPOPT_ACCM:
+ if (len != 6) {
+ ND_PRINT(" (length bogus, should be = 6)");
+ return len;
+ }
+ ND_PRINT(": 0x%08x", GET_BE_U_4(p + 2));
+ break;
+ case LCPOPT_AP:
+ if (len < 4) {
+ ND_PRINT(" (length bogus, should be >= 4)");
+ return len;
+ }
+ ND_PRINT(": %s",
+ tok2str(ppptype2str, "Unknown Auth Proto (0x04x)", GET_BE_U_2(p + 2)));
+
+ switch (GET_BE_U_2(p + 2)) {
+ case PPP_CHAP:
+ ND_PRINT(", %s",
+ tok2str(authalg_values, "Unknown Auth Alg %u", GET_U_1(p + 4)));
+ break;
+ case PPP_PAP: /* fall through */
+ case PPP_EAP:
+ case PPP_SPAP:
+ case PPP_SPAP_OLD:
+ break;
+ default:
+ print_unknown_data(ndo, p, "\n\t", len);
+ }
+ break;
+ case LCPOPT_QP:
+ if (len < 4) {
+ ND_PRINT(" (length bogus, should be >= 4)");
+ return 0;
+ }
+ if (GET_BE_U_2(p + 2) == PPP_LQM)
+ ND_PRINT(": LQR");
+ else
+ ND_PRINT(": unknown");
+ break;
+ case LCPOPT_MN:
+ if (len != 6) {
+ ND_PRINT(" (length bogus, should be = 6)");
+ return 0;
+ }
+ ND_PRINT(": 0x%08x", GET_BE_U_4(p + 2));
+ break;
+ case LCPOPT_PFC:
+ break;
+ case LCPOPT_ACFC:
+ break;
+ case LCPOPT_LD:
+ if (len != 4) {
+ ND_PRINT(" (length bogus, should be = 4)");
+ return 0;
+ }
+ ND_PRINT(": 0x%04x", GET_BE_U_2(p + 2));
+ break;
+ case LCPOPT_CBACK:
+ if (len < 3) {
+ ND_PRINT(" (length bogus, should be >= 3)");
+ return 0;
+ }
+ ND_PRINT(": ");
+ ND_PRINT(": Callback Operation %s (%u)",
+ tok2str(ppp_callback_values, "Unknown", GET_U_1(p + 2)),
+ GET_U_1(p + 2));
+ break;
+ case LCPOPT_MLMRRU:
+ if (len != 4) {
+ ND_PRINT(" (length bogus, should be = 4)");
+ return 0;
+ }
+ ND_PRINT(": %u", GET_BE_U_2(p + 2));
+ break;
+ case LCPOPT_MLED:
+ if (len < 3) {
+ ND_PRINT(" (length bogus, should be >= 3)");
+ return 0;
+ }
+ switch (GET_U_1(p + 2)) { /* class */
+ case MEDCLASS_NULL:
+ ND_PRINT(": Null");
+ break;
+ case MEDCLASS_LOCAL:
+ ND_PRINT(": Local"); /* XXX */
+ break;
+ case MEDCLASS_IPV4:
+ if (len != 7) {
+ ND_PRINT(" (length bogus, should be = 7)");
+ return 0;
+ }
+ ND_PRINT(": IPv4 %s", GET_IPADDR_STRING(p + 3));
+ break;
+ case MEDCLASS_MAC:
+ if (len != 9) {
+ ND_PRINT(" (length bogus, should be = 9)");
+ return 0;
+ }
+ ND_PRINT(": MAC %s", GET_ETHERADDR_STRING(p + 3));
+ break;
+ case MEDCLASS_MNB:
+ ND_PRINT(": Magic-Num-Block"); /* XXX */
+ break;
+ case MEDCLASS_PSNDN:
+ ND_PRINT(": PSNDN"); /* XXX */
+ break;
+ default:
+ ND_PRINT(": Unknown class %u", GET_U_1(p + 2));
+ break;
+ }
+ break;
+
+/* XXX: to be supported */
+#if 0
+ case LCPOPT_DEP6:
+ case LCPOPT_FCSALT:
+ case LCPOPT_SDP:
+ case LCPOPT_NUMMODE:
+ case LCPOPT_DEP12:
+ case LCPOPT_DEP14:
+ case LCPOPT_DEP15:
+ case LCPOPT_DEP16:
+ case LCPOPT_MLSSNHF:
+ case LCPOPT_PROP:
+ case LCPOPT_DCEID:
+ case LCPOPT_MPP:
+ case LCPOPT_LCPAOPT:
+ case LCPOPT_COBS:
+ case LCPOPT_PE:
+ case LCPOPT_MLHF:
+ case LCPOPT_I18N:
+ case LCPOPT_SDLOS:
+ case LCPOPT_PPPMUX:
+ break;
+#endif
+ default:
+ /*
+ * Unknown option; dump it as raw bytes now if we're
+ * not going to do so below.
+ */
+ if (ndo->ndo_vflag < 2)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2);
+ break;
+ }
+
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2); /* exclude TLV header */
+
+ return len;
+
+trunc:
+ ND_PRINT("[|lcp]");
+ return 0;
+}
+
+/* ML-PPP*/
+static const struct tok ppp_ml_flag_values[] = {
+ { 0x80, "begin" },
+ { 0x40, "end" },
+ { 0, NULL }
+};
+
+static void
+handle_mlppp(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ if (!ndo->ndo_eflag)
+ ND_PRINT("MLPPP, ");
+
+ if (length < 2) {
+ ND_PRINT("[|mlppp]");
+ return;
+ }
+ if (!ND_TTEST_2(p)) {
+ ND_PRINT("[|mlppp]");
+ return;
+ }
+
+ ND_PRINT("seq 0x%03x, Flags [%s], length %u",
+ (GET_BE_U_2(p))&0x0fff,
+ /* only support 12-Bit sequence space for now */
+ bittok2str(ppp_ml_flag_values, "none", GET_U_1(p) & 0xc0),
+ length);
+}
+
+/* CHAP */
+static void
+handle_chap(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int code, len;
+ u_int val_size, name_size, msg_size;
+ const u_char *p0;
+ u_int i;
+
+ p0 = p;
+ if (length < 1) {
+ ND_PRINT("[|chap]");
+ return;
+ } else if (length < 4) {
+ ND_PRINT("[|chap 0x%02x]", GET_U_1(p));
+ return;
+ }
+
+ code = GET_U_1(p);
+ ND_PRINT("CHAP, %s (0x%02x)",
+ tok2str(chapcode_values,"unknown",code),
+ code);
+ p++;
+
+ ND_PRINT(", id %u", GET_U_1(p)); /* ID */
+ p++;
+
+ len = GET_BE_U_2(p);
+ p += 2;
+
+ /*
+ * Note that this is a generic CHAP decoding routine. Since we
+ * don't know which flavor of CHAP (i.e. CHAP-MD5, MS-CHAPv1,
+ * MS-CHAPv2) is used at this point, we can't decode packet
+ * specifically to each algorithms. Instead, we simply decode
+ * the GCD (Gratest Common Denominator) for all algorithms.
+ */
+ switch (code) {
+ case CHAP_CHAL:
+ case CHAP_RESP:
+ if (length - (p - p0) < 1)
+ return;
+ val_size = GET_U_1(p); /* value size */
+ p++;
+ if (length - (p - p0) < val_size)
+ return;
+ ND_PRINT(", Value ");
+ for (i = 0; i < val_size; i++) {
+ ND_PRINT("%02x", GET_U_1(p));
+ p++;
+ }
+ name_size = len - (u_int)(p - p0);
+ ND_PRINT(", Name ");
+ for (i = 0; i < name_size; i++) {
+ fn_print_char(ndo, GET_U_1(p));
+ p++;
+ }
+ break;
+ case CHAP_SUCC:
+ case CHAP_FAIL:
+ msg_size = len - (u_int)(p - p0);
+ ND_PRINT(", Msg ");
+ for (i = 0; i< msg_size; i++) {
+ fn_print_char(ndo, GET_U_1(p));
+ p++;
+ }
+ break;
+ }
+}
+
+/* PAP (see RFC 1334) */
+static void
+handle_pap(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int code, len;
+ u_int peerid_len, passwd_len, msg_len;
+ const u_char *p0;
+ u_int i;
+
+ p0 = p;
+ if (length < 1) {
+ ND_PRINT("[|pap]");
+ return;
+ } else if (length < 4) {
+ ND_PRINT("[|pap 0x%02x]", GET_U_1(p));
+ return;
+ }
+
+ code = GET_U_1(p);
+ ND_PRINT("PAP, %s (0x%02x)",
+ tok2str(papcode_values, "unknown", code),
+ code);
+ p++;
+
+ ND_PRINT(", id %u", GET_U_1(p)); /* ID */
+ p++;
+
+ len = GET_BE_U_2(p);
+ p += 2;
+
+ if (len > length) {
+ ND_PRINT(", length %u > packet size", len);
+ return;
+ }
+ length = len;
+ if (length < (size_t)(p - p0)) {
+ ND_PRINT(", length %u < PAP header length", length);
+ return;
+ }
+
+ switch (code) {
+ case PAP_AREQ:
+ /* A valid Authenticate-Request is 6 or more octets long. */
+ if (len < 6)
+ goto trunc;
+ if (length - (p - p0) < 1)
+ return;
+ peerid_len = GET_U_1(p); /* Peer-ID Length */
+ p++;
+ if (length - (p - p0) < peerid_len)
+ return;
+ ND_PRINT(", Peer ");
+ for (i = 0; i < peerid_len; i++) {
+ fn_print_char(ndo, GET_U_1(p));
+ p++;
+ }
+
+ if (length - (p - p0) < 1)
+ return;
+ passwd_len = GET_U_1(p); /* Password Length */
+ p++;
+ if (length - (p - p0) < passwd_len)
+ return;
+ ND_PRINT(", Name ");
+ for (i = 0; i < passwd_len; i++) {
+ fn_print_char(ndo, GET_U_1(p));
+ p++;
+ }
+ break;
+ case PAP_AACK:
+ case PAP_ANAK:
+ /* Although some implementations ignore truncation at
+ * this point and at least one generates a truncated
+ * packet, RFC 1334 section 2.2.2 clearly states that
+ * both AACK and ANAK are at least 5 bytes long.
+ */
+ if (len < 5)
+ goto trunc;
+ if (length - (p - p0) < 1)
+ return;
+ msg_len = GET_U_1(p); /* Msg-Length */
+ p++;
+ if (length - (p - p0) < msg_len)
+ return;
+ ND_PRINT(", Msg ");
+ for (i = 0; i< msg_len; i++) {
+ fn_print_char(ndo, GET_U_1(p));
+ p++;
+ }
+ break;
+ }
+ return;
+
+trunc:
+ ND_PRINT("[|pap]");
+}
+
+/* BAP */
+static void
+handle_bap(netdissect_options *ndo _U_,
+ const u_char *p _U_, u_int length _U_)
+{
+ /* XXX: to be supported!! */
+}
+
+
+/* IPCP config options */
+static u_int
+print_ipcp_config_options(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int opt, len;
+ u_int compproto, ipcomp_subopttotallen, ipcomp_subopt, ipcomp_suboptlen;
+
+ if (length < 2)
+ return 0;
+ ND_TCHECK_2(p);
+ opt = GET_U_1(p);
+ len = GET_U_1(p + 1);
+ if (length < len)
+ return 0;
+ if (len < 2) {
+ ND_PRINT("\n\t %s Option (0x%02x), length %u (length bogus, should be >= 2)",
+ tok2str(ipcpopt_values,"unknown",opt),
+ opt,
+ len);
+ return 0;
+ }
+
+ ND_PRINT("\n\t %s Option (0x%02x), length %u",
+ tok2str(ipcpopt_values,"unknown",opt),
+ opt,
+ len);
+
+ switch (opt) {
+ case IPCPOPT_2ADDR: /* deprecated */
+ if (len != 10) {
+ ND_PRINT(" (length bogus, should be = 10)");
+ return len;
+ }
+ ND_PRINT(": src %s, dst %s",
+ GET_IPADDR_STRING(p + 2),
+ GET_IPADDR_STRING(p + 6));
+ break;
+ case IPCPOPT_IPCOMP:
+ if (len < 4) {
+ ND_PRINT(" (length bogus, should be >= 4)");
+ return 0;
+ }
+ compproto = GET_BE_U_2(p + 2);
+
+ ND_PRINT(": %s (0x%02x):",
+ tok2str(ipcpopt_compproto_values, "Unknown", compproto),
+ compproto);
+
+ switch (compproto) {
+ case PPP_VJC:
+ /* XXX: VJ-Comp parameters should be decoded */
+ break;
+ case IPCPOPT_IPCOMP_HDRCOMP:
+ if (len < IPCPOPT_IPCOMP_MINLEN) {
+ ND_PRINT(" (length bogus, should be >= %u)",
+ IPCPOPT_IPCOMP_MINLEN);
+ return 0;
+ }
+
+ ND_TCHECK_LEN(p + 2, IPCPOPT_IPCOMP_MINLEN);
+ ND_PRINT("\n\t TCP Space %u, non-TCP Space %u"
+ ", maxPeriod %u, maxTime %u, maxHdr %u",
+ GET_BE_U_2(p + 4),
+ GET_BE_U_2(p + 6),
+ GET_BE_U_2(p + 8),
+ GET_BE_U_2(p + 10),
+ GET_BE_U_2(p + 12));
+
+ /* suboptions present ? */
+ if (len > IPCPOPT_IPCOMP_MINLEN) {
+ ipcomp_subopttotallen = len - IPCPOPT_IPCOMP_MINLEN;
+ p += IPCPOPT_IPCOMP_MINLEN;
+
+ ND_PRINT("\n\t Suboptions, length %u", ipcomp_subopttotallen);
+
+ while (ipcomp_subopttotallen >= 2) {
+ ND_TCHECK_2(p);
+ ipcomp_subopt = GET_U_1(p);
+ ipcomp_suboptlen = GET_U_1(p + 1);
+
+ /* sanity check */
+ if (ipcomp_subopt == 0 ||
+ ipcomp_suboptlen == 0 )
+ break;
+
+ /* XXX: just display the suboptions for now */
+ ND_PRINT("\n\t\t%s Suboption #%u, length %u",
+ tok2str(ipcpopt_compproto_subopt_values,
+ "Unknown",
+ ipcomp_subopt),
+ ipcomp_subopt,
+ ipcomp_suboptlen);
+ if (ipcomp_subopttotallen < ipcomp_suboptlen) {
+ ND_PRINT(" [remaining suboptions length %u < %u]",
+ ipcomp_subopttotallen, ipcomp_suboptlen);
+ nd_print_invalid(ndo);
+ break;
+ }
+ ipcomp_subopttotallen -= ipcomp_suboptlen;
+ p += ipcomp_suboptlen;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case IPCPOPT_ADDR: /* those options share the same format - fall through */
+ case IPCPOPT_MOBILE4:
+ case IPCPOPT_PRIDNS:
+ case IPCPOPT_PRINBNS:
+ case IPCPOPT_SECDNS:
+ case IPCPOPT_SECNBNS:
+ if (len != 6) {
+ ND_PRINT(" (length bogus, should be = 6)");
+ return 0;
+ }
+ ND_PRINT(": %s", GET_IPADDR_STRING(p + 2));
+ break;
+ default:
+ /*
+ * Unknown option; dump it as raw bytes now if we're
+ * not going to do so below.
+ */
+ if (ndo->ndo_vflag < 2)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2);
+ break;
+ }
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2); /* exclude TLV header */
+ return len;
+
+trunc:
+ ND_PRINT("[|ipcp]");
+ return 0;
+}
+
+/* IP6CP config options */
+static u_int
+print_ip6cp_config_options(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int opt, len;
+
+ if (length < 2)
+ return 0;
+ ND_TCHECK_2(p);
+ opt = GET_U_1(p);
+ len = GET_U_1(p + 1);
+ if (length < len)
+ return 0;
+ if (len < 2) {
+ ND_PRINT("\n\t %s Option (0x%02x), length %u (length bogus, should be >= 2)",
+ tok2str(ip6cpopt_values,"unknown",opt),
+ opt,
+ len);
+ return 0;
+ }
+
+ ND_PRINT("\n\t %s Option (0x%02x), length %u",
+ tok2str(ip6cpopt_values,"unknown",opt),
+ opt,
+ len);
+
+ switch (opt) {
+ case IP6CP_IFID:
+ if (len != 10) {
+ ND_PRINT(" (length bogus, should be = 10)");
+ return len;
+ }
+ ND_TCHECK_8(p + 2);
+ ND_PRINT(": %04x:%04x:%04x:%04x",
+ GET_BE_U_2(p + 2),
+ GET_BE_U_2(p + 4),
+ GET_BE_U_2(p + 6),
+ GET_BE_U_2(p + 8));
+ break;
+ default:
+ /*
+ * Unknown option; dump it as raw bytes now if we're
+ * not going to do so below.
+ */
+ if (ndo->ndo_vflag < 2)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2);
+ break;
+ }
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2); /* exclude TLV header */
+
+ return len;
+
+trunc:
+ ND_PRINT("[|ip6cp]");
+ return 0;
+}
+
+
+/* CCP config options */
+static u_int
+print_ccp_config_options(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int opt, len;
+
+ if (length < 2)
+ return 0;
+ ND_TCHECK_2(p);
+ opt = GET_U_1(p);
+ len = GET_U_1(p + 1);
+ if (length < len)
+ return 0;
+ if (len < 2) {
+ ND_PRINT("\n\t %s Option (0x%02x), length %u (length bogus, should be >= 2)",
+ tok2str(ccpconfopts_values, "Unknown", opt),
+ opt,
+ len);
+ return 0;
+ }
+
+ ND_PRINT("\n\t %s Option (0x%02x), length %u",
+ tok2str(ccpconfopts_values, "Unknown", opt),
+ opt,
+ len);
+
+ switch (opt) {
+ case CCPOPT_BSDCOMP:
+ if (len < 3) {
+ ND_PRINT(" (length bogus, should be >= 3)");
+ return len;
+ }
+ ND_PRINT(": Version: %u, Dictionary Bits: %u",
+ GET_U_1(p + 2) >> 5,
+ GET_U_1(p + 2) & 0x1f);
+ break;
+ case CCPOPT_MVRCA:
+ if (len < 4) {
+ ND_PRINT(" (length bogus, should be >= 4)");
+ return len;
+ }
+ ND_PRINT(": Features: %u, PxP: %s, History: %u, #CTX-ID: %u",
+ (GET_U_1(p + 2) & 0xc0) >> 6,
+ (GET_U_1(p + 2) & 0x20) ? "Enabled" : "Disabled",
+ GET_U_1(p + 2) & 0x1f,
+ GET_U_1(p + 3));
+ break;
+ case CCPOPT_DEFLATE:
+ if (len < 4) {
+ ND_PRINT(" (length bogus, should be >= 4)");
+ return len;
+ }
+ ND_PRINT(": Window: %uK, Method: %s (0x%x), MBZ: %u, CHK: %u",
+ (GET_U_1(p + 2) & 0xf0) >> 4,
+ ((GET_U_1(p + 2) & 0x0f) == 8) ? "zlib" : "unknown",
+ GET_U_1(p + 2) & 0x0f,
+ (GET_U_1(p + 3) & 0xfc) >> 2,
+ GET_U_1(p + 3) & 0x03);
+ break;
+
+/* XXX: to be supported */
+#if 0
+ case CCPOPT_OUI:
+ case CCPOPT_PRED1:
+ case CCPOPT_PRED2:
+ case CCPOPT_PJUMP:
+ case CCPOPT_HPPPC:
+ case CCPOPT_STACLZS:
+ case CCPOPT_MPPC:
+ case CCPOPT_GFZA:
+ case CCPOPT_V42BIS:
+ case CCPOPT_LZSDCP:
+ case CCPOPT_DEC:
+ case CCPOPT_RESV:
+ break;
+#endif
+ default:
+ /*
+ * Unknown option; dump it as raw bytes now if we're
+ * not going to do so below.
+ */
+ if (ndo->ndo_vflag < 2)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2);
+ break;
+ }
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2); /* exclude TLV header */
+
+ return len;
+
+trunc:
+ ND_PRINT("[|ccp]");
+ return 0;
+}
+
+/* BACP config options */
+static u_int
+print_bacp_config_options(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int opt, len;
+
+ if (length < 2)
+ return 0;
+ ND_TCHECK_2(p);
+ opt = GET_U_1(p);
+ len = GET_U_1(p + 1);
+ if (length < len)
+ return 0;
+ if (len < 2) {
+ ND_PRINT("\n\t %s Option (0x%02x), length %u (length bogus, should be >= 2)",
+ tok2str(bacconfopts_values, "Unknown", opt),
+ opt,
+ len);
+ return 0;
+ }
+
+ ND_PRINT("\n\t %s Option (0x%02x), length %u",
+ tok2str(bacconfopts_values, "Unknown", opt),
+ opt,
+ len);
+
+ switch (opt) {
+ case BACPOPT_FPEER:
+ if (len != 6) {
+ ND_PRINT(" (length bogus, should be = 6)");
+ return len;
+ }
+ ND_PRINT(": Magic-Num 0x%08x", GET_BE_U_4(p + 2));
+ break;
+ default:
+ /*
+ * Unknown option; dump it as raw bytes now if we're
+ * not going to do so below.
+ */
+ if (ndo->ndo_vflag < 2)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2);
+ break;
+ }
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, p + 2, "\n\t ", len - 2); /* exclude TLV header */
+
+ return len;
+
+trunc:
+ ND_PRINT("[|bacp]");
+ return 0;
+}
+
+/*
+ * Un-escape RFC 1662 PPP in HDLC-like framing, with octet escapes.
+ * The length argument is the on-the-wire length, not the captured
+ * length; we can only un-escape the captured part.
+ */
+static void
+ppp_hdlc(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int caplen = ND_BYTES_AVAILABLE_AFTER(p);
+ u_char *b, *t, c;
+ const u_char *s;
+ u_int i, proto;
+ const void *se;
+
+ if (caplen == 0)
+ return;
+
+ if (length == 0)
+ return;
+
+ b = (u_char *)nd_malloc(ndo, caplen);
+ if (b == NULL)
+ return;
+
+ /*
+ * Unescape all the data into a temporary, private, buffer.
+ * Do this so that we don't overwrite the original packet
+ * contents.
+ */
+ for (s = p, t = b, i = caplen; i != 0; i--) {
+ c = GET_U_1(s);
+ s++;
+ if (c == 0x7d) {
+ if (i <= 1)
+ break;
+ i--;
+ c = GET_U_1(s) ^ 0x20;
+ s++;
+ }
+ *t++ = c;
+ }
+
+ /*
+ * Change the end pointer, so bounds checks work.
+ */
+ se = ndo->ndo_snapend;
+ ndo->ndo_snapend = t;
+ length = ND_BYTES_AVAILABLE_AFTER(b);
+
+ /* now lets guess about the payload codepoint format */
+ if (length < 1)
+ goto trunc;
+ proto = GET_U_1(b); /* start with a one-octet codepoint guess */
+
+ switch (proto) {
+ case PPP_IP:
+ ip_print(ndo, b + 1, length - 1);
+ goto cleanup;
+ case PPP_IPV6:
+ ip6_print(ndo, b + 1, length - 1);
+ goto cleanup;
+ default: /* no luck - try next guess */
+ break;
+ }
+
+ if (length < 2)
+ goto trunc;
+ proto = GET_BE_U_2(b); /* next guess - load two octets */
+
+ switch (proto) {
+ case (PPP_ADDRESS << 8 | PPP_CONTROL): /* looks like a PPP frame */
+ if (length < 4)
+ goto trunc;
+ proto = GET_BE_U_2(b + 2); /* load the PPP proto-id */
+ handle_ppp(ndo, proto, b + 4, length - 4);
+ break;
+ default: /* last guess - proto must be a PPP proto-id */
+ handle_ppp(ndo, proto, b + 2, length - 2);
+ break;
+ }
+
+cleanup:
+ ndo->ndo_snapend = se;
+ return;
+
+trunc:
+ ndo->ndo_snapend = se;
+ nd_print_trunc(ndo);
+}
+
+
+/* PPP */
+static void
+handle_ppp(netdissect_options *ndo,
+ u_int proto, const u_char *p, u_int length)
+{
+ if ((proto & 0xff00) == 0x7e00) { /* is this an escape code ? */
+ ppp_hdlc(ndo, p - 1, length);
+ return;
+ }
+
+ switch (proto) {
+ case PPP_LCP: /* fall through */
+ case PPP_IPCP:
+ case PPP_OSICP:
+ case PPP_MPLSCP:
+ case PPP_IPV6CP:
+ case PPP_CCP:
+ case PPP_BACP:
+ handle_ctrl_proto(ndo, proto, p, length);
+ break;
+ case PPP_ML:
+ handle_mlppp(ndo, p, length);
+ break;
+ case PPP_CHAP:
+ handle_chap(ndo, p, length);
+ break;
+ case PPP_PAP:
+ handle_pap(ndo, p, length);
+ break;
+ case PPP_BAP: /* XXX: not yet completed */
+ handle_bap(ndo, p, length);
+ break;
+ case ETHERTYPE_IP: /*XXX*/
+ case PPP_VJNC:
+ case PPP_IP:
+ ip_print(ndo, p, length);
+ break;
+ case ETHERTYPE_IPV6: /*XXX*/
+ case PPP_IPV6:
+ ip6_print(ndo, p, length);
+ break;
+ case ETHERTYPE_IPX: /*XXX*/
+ case PPP_IPX:
+ ipx_print(ndo, p, length);
+ break;
+ case PPP_OSI:
+ isoclns_print(ndo, p, length);
+ break;
+ case PPP_MPLS_UCAST:
+ case PPP_MPLS_MCAST:
+ mpls_print(ndo, p, length);
+ break;
+ case PPP_COMP:
+ ND_PRINT("compressed PPP data");
+ break;
+ default:
+ ND_PRINT("%s ", tok2str(ppptype2str, "unknown PPP protocol (0x%04x)", proto));
+ print_unknown_data(ndo, p, "\n\t", length);
+ break;
+ }
+}
+
+/* Standard PPP printer */
+u_int
+ppp_print(netdissect_options *ndo,
+ const u_char *p, u_int length)
+{
+ u_int proto,ppp_header;
+ u_int olen = length; /* _o_riginal length */
+ u_int hdr_len = 0;
+
+ ndo->ndo_protocol = "ppp";
+ /*
+ * Here, we assume that p points to the Address and Control
+ * field (if they present).
+ */
+ if (length < 2)
+ goto trunc;
+ ppp_header = GET_BE_U_2(p);
+
+ switch(ppp_header) {
+ case (PPP_PPPD_IN << 8 | PPP_CONTROL):
+ if (ndo->ndo_eflag) ND_PRINT("In ");
+ p += 2;
+ length -= 2;
+ hdr_len += 2;
+ break;
+ case (PPP_PPPD_OUT << 8 | PPP_CONTROL):
+ if (ndo->ndo_eflag) ND_PRINT("Out ");
+ p += 2;
+ length -= 2;
+ hdr_len += 2;
+ break;
+ case (PPP_ADDRESS << 8 | PPP_CONTROL):
+ p += 2; /* ACFC not used */
+ length -= 2;
+ hdr_len += 2;
+ break;
+
+ default:
+ break;
+ }
+
+ if (length < 2)
+ goto trunc;
+ if (GET_U_1(p) % 2) {
+ proto = GET_U_1(p); /* PFC is used */
+ p++;
+ length--;
+ hdr_len++;
+ } else {
+ proto = GET_BE_U_2(p);
+ p += 2;
+ length -= 2;
+ hdr_len += 2;
+ }
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("%s (0x%04x), length %u: ",
+ tok2str(ppptype2str, "unknown", proto),
+ proto,
+ olen);
+
+ handle_ppp(ndo, proto, p, length);
+ return (hdr_len);
+trunc:
+ nd_print_trunc(ndo);
+ return (0);
+}
+
+
+/* PPP I/F printer */
+void
+ppp_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+
+ ndo->ndo_protocol = "ppp";
+ if (caplen < PPP_HDRLEN) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+ ndo->ndo_ll_hdr_len += PPP_HDRLEN;
+
+#if 0
+ /*
+ * XXX: seems to assume that there are 2 octets prepended to an
+ * actual PPP frame. The 1st octet looks like Input/Output flag
+ * while 2nd octet is unknown, at least to me
+ * (mshindo@mshindo.net).
+ *
+ * That was what the original tcpdump code did.
+ *
+ * FreeBSD's "if_ppp.c" *does* set the first octet to 1 for outbound
+ * packets and 0 for inbound packets - but only if the
+ * protocol field has the 0x8000 bit set (i.e., it's a network
+ * control protocol); it does so before running the packet through
+ * "bpf_filter" to see if it should be discarded, and to see
+ * if we should update the time we sent the most recent packet...
+ *
+ * ...but it puts the original address field back after doing
+ * so.
+ *
+ * NetBSD's "if_ppp.c" doesn't set the first octet in that fashion.
+ *
+ * I don't know if any PPP implementation handed up to a BPF
+ * device packets with the first octet being 1 for outbound and
+ * 0 for inbound packets, so I (guy@alum.mit.edu) don't know
+ * whether that ever needs to be checked or not.
+ *
+ * Note that NetBSD has a DLT_PPP_SERIAL, which it uses for PPP,
+ * and its tcpdump appears to assume that the frame always
+ * begins with an address field and a control field, and that
+ * the address field might be 0x0f or 0x8f, for Cisco
+ * point-to-point with HDLC framing as per section 4.3.1 of RFC
+ * 1547, as well as 0xff, for PPP in HDLC-like framing as per
+ * RFC 1662.
+ *
+ * (Is the Cisco framing in question what DLT_C_HDLC, in
+ * BSD/OS, is?)
+ */
+ if (ndo->ndo_eflag)
+ ND_PRINT("%c %4d %02x ", GET_U_1(p) ? 'O' : 'I',
+ length, GET_U_1(p + 1));
+#endif
+
+ ppp_print(ndo, p, length);
+}
+
+/*
+ * PPP I/F printer to use if we know that RFC 1662-style PPP in HDLC-like
+ * framing, or Cisco PPP with HDLC framing as per section 4.3.1 of RFC 1547,
+ * is being used (i.e., we don't check for PPP_ADDRESS and PPP_CONTROL,
+ * discard them *if* those are the first two octets, and parse the remaining
+ * packet as a PPP packet, as "ppp_print()" does).
+ *
+ * This handles, for example, DLT_PPP_SERIAL in NetBSD.
+ */
+void
+ppp_hdlc_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+ u_int proto;
+ u_int hdrlen = 0;
+
+ ndo->ndo_protocol = "ppp_hdlc";
+ if (caplen < 2) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ switch (GET_U_1(p)) {
+
+ case PPP_ADDRESS:
+ if (caplen < 4) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("%02x %02x %u ", GET_U_1(p),
+ GET_U_1(p + 1), length);
+ p += 2;
+ length -= 2;
+ hdrlen += 2;
+
+ proto = GET_BE_U_2(p);
+ p += 2;
+ length -= 2;
+ hdrlen += 2;
+ ND_PRINT("%s: ", tok2str(ppptype2str, "unknown PPP protocol (0x%04x)", proto));
+
+ handle_ppp(ndo, proto, p, length);
+ break;
+
+ case CHDLC_UNICAST:
+ case CHDLC_BCAST:
+ chdlc_if_print(ndo, h, p);
+ return;
+
+ default:
+ if (caplen < 4) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("%02x %02x %u ", GET_U_1(p),
+ GET_U_1(p + 1), length);
+ p += 2;
+ hdrlen += 2;
+
+ /*
+ * XXX - NetBSD's "ppp_netbsd_serial_if_print()" treats
+ * the next two octets as an Ethernet type; does that
+ * ever happen?
+ */
+ ND_PRINT("unknown addr %02x; ctrl %02x", GET_U_1(p),
+ GET_U_1(p + 1));
+ break;
+ }
+
+ ndo->ndo_ll_hdr_len += hdrlen;
+}
+
+#define PPP_BSDI_HDRLEN 24
+
+/* BSD/OS specific PPP printer */
+void
+ppp_bsdos_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h _U_, const u_char *p _U_)
+{
+ u_int hdrlength;
+#ifdef __bsdi__
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+ uint16_t ptype;
+ uint8_t llhl;
+ const u_char *q;
+ u_int i;
+
+ ndo->ndo_protocol = "ppp_bsdos";
+ if (caplen < PPP_BSDI_HDRLEN) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+
+ hdrlength = 0;
+
+#if 0
+ if (GET_U_1(p) == PPP_ADDRESS &&
+ GET_U_1(p + 1) == PPP_CONTROL) {
+ if (ndo->ndo_eflag)
+ ND_PRINT("%02x %02x ", GET_U_1(p),
+ GET_U_1(p + 1));
+ p += 2;
+ hdrlength = 2;
+ }
+
+ if (ndo->ndo_eflag)
+ ND_PRINT("%u ", length);
+ /* Retrieve the protocol type */
+ if (GET_U_1(p) & 01) {
+ /* Compressed protocol field */
+ ptype = GET_U_1(p);
+ if (ndo->ndo_eflag)
+ ND_PRINT("%02x ", ptype);
+ p++;
+ hdrlength += 1;
+ } else {
+ /* Un-compressed protocol field */
+ ptype = GET_BE_U_2(p);
+ if (ndo->ndo_eflag)
+ ND_PRINT("%04x ", ptype);
+ p += 2;
+ hdrlength += 2;
+ }
+#else
+ ptype = 0; /*XXX*/
+ if (ndo->ndo_eflag)
+ ND_PRINT("%c ", GET_U_1(p + SLC_DIR) ? 'O' : 'I');
+ llhl = GET_U_1(p + SLC_LLHL);
+ if (llhl) {
+ /* link level header */
+ struct ppp_header *ph;
+
+ q = p + SLC_BPFHDRLEN;
+ ph = (struct ppp_header *)q;
+ if (ph->phdr_addr == PPP_ADDRESS
+ && ph->phdr_ctl == PPP_CONTROL) {
+ if (ndo->ndo_eflag)
+ ND_PRINT("%02x %02x ", GET_U_1(q),
+ GET_U_1(q + 1));
+ ptype = GET_BE_U_2(&ph->phdr_type);
+ if (ndo->ndo_eflag && (ptype == PPP_VJC || ptype == PPP_VJNC)) {
+ ND_PRINT("%s ", tok2str(ppptype2str,
+ "proto-#%u", ptype));
+ }
+ } else {
+ if (ndo->ndo_eflag) {
+ ND_PRINT("LLH=[");
+ for (i = 0; i < llhl; i++)
+ ND_PRINT("%02x", GET_U_1(q + i));
+ ND_PRINT("] ");
+ }
+ }
+ }
+ if (ndo->ndo_eflag)
+ ND_PRINT("%u ", length);
+ if (GET_U_1(p + SLC_CHL)) {
+ q = p + SLC_BPFHDRLEN + llhl;
+
+ switch (ptype) {
+ case PPP_VJC:
+ ptype = vjc_print(ndo, q, ptype);
+ hdrlength = PPP_BSDI_HDRLEN;
+ p += hdrlength;
+ switch (ptype) {
+ case PPP_IP:
+ ip_print(ndo, p, length);
+ break;
+ case PPP_IPV6:
+ ip6_print(ndo, p, length);
+ break;
+ case PPP_MPLS_UCAST:
+ case PPP_MPLS_MCAST:
+ mpls_print(ndo, p, length);
+ break;
+ }
+ goto printx;
+ case PPP_VJNC:
+ ptype = vjc_print(ndo, q, ptype);
+ hdrlength = PPP_BSDI_HDRLEN;
+ p += hdrlength;
+ switch (ptype) {
+ case PPP_IP:
+ ip_print(ndo, p, length);
+ break;
+ case PPP_IPV6:
+ ip6_print(ndo, p, length);
+ break;
+ case PPP_MPLS_UCAST:
+ case PPP_MPLS_MCAST:
+ mpls_print(ndo, p, length);
+ break;
+ }
+ goto printx;
+ default:
+ if (ndo->ndo_eflag) {
+ ND_PRINT("CH=[");
+ for (i = 0; i < llhl; i++)
+ ND_PRINT("%02x",
+ GET_U_1(q + i));
+ ND_PRINT("] ");
+ }
+ break;
+ }
+ }
+
+ hdrlength = PPP_BSDI_HDRLEN;
+#endif
+
+ length -= hdrlength;
+ p += hdrlength;
+
+ switch (ptype) {
+ case PPP_IP:
+ ip_print(p, length);
+ break;
+ case PPP_IPV6:
+ ip6_print(ndo, p, length);
+ break;
+ case PPP_MPLS_UCAST:
+ case PPP_MPLS_MCAST:
+ mpls_print(ndo, p, length);
+ break;
+ default:
+ ND_PRINT("%s ", tok2str(ppptype2str, "unknown PPP protocol (0x%04x)", ptype));
+ }
+
+printx:
+#else /* __bsdi */
+ hdrlength = 0;
+#endif /* __bsdi__ */
+ ndo->ndo_ll_hdr_len += hdrlength;
+}
diff --git a/print-pppoe.c b/print-pppoe.c
new file mode 100644
index 0000000..65518df
--- /dev/null
+++ b/print-pppoe.c
@@ -0,0 +1,206 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Greg Stark <gsstark@mit.edu>
+ */
+
+/* \summary: PPP-over-Ethernet (PPPoE) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect-ctype.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+/* Codes */
+enum {
+ PPPOE_PADI = 0x09,
+ PPPOE_PADO = 0x07,
+ PPPOE_PADR = 0x19,
+ PPPOE_PADS = 0x65,
+ PPPOE_PADT = 0xa7
+};
+
+static const struct tok pppoecode2str[] = {
+ { PPPOE_PADI, "PADI" },
+ { PPPOE_PADO, "PADO" },
+ { PPPOE_PADR, "PADR" },
+ { PPPOE_PADS, "PADS" },
+ { PPPOE_PADT, "PADT" },
+ { 0, "" }, /* PPP Data */
+ { 0, NULL }
+};
+
+/* Tags */
+enum {
+ PPPOE_EOL = 0,
+ PPPOE_SERVICE_NAME = 0x0101,
+ PPPOE_AC_NAME = 0x0102,
+ PPPOE_HOST_UNIQ = 0x0103,
+ PPPOE_AC_COOKIE = 0x0104,
+ PPPOE_VENDOR = 0x0105,
+ PPPOE_RELAY_SID = 0x0110,
+ PPPOE_MAX_PAYLOAD = 0x0120,
+ PPPOE_SERVICE_NAME_ERROR = 0x0201,
+ PPPOE_AC_SYSTEM_ERROR = 0x0202,
+ PPPOE_GENERIC_ERROR = 0x0203
+};
+
+static const struct tok pppoetag2str[] = {
+ { PPPOE_EOL, "EOL" },
+ { PPPOE_SERVICE_NAME, "Service-Name" },
+ { PPPOE_AC_NAME, "AC-Name" },
+ { PPPOE_HOST_UNIQ, "Host-Uniq" },
+ { PPPOE_AC_COOKIE, "AC-Cookie" },
+ { PPPOE_VENDOR, "Vendor-Specific" },
+ { PPPOE_RELAY_SID, "Relay-Session-ID" },
+ { PPPOE_MAX_PAYLOAD, "PPP-Max-Payload" },
+ { PPPOE_SERVICE_NAME_ERROR, "Service-Name-Error" },
+ { PPPOE_AC_SYSTEM_ERROR, "AC-System-Error" },
+ { PPPOE_GENERIC_ERROR, "Generic-Error" },
+ { 0, NULL }
+};
+
+#define PPPOE_HDRLEN 6
+#define MAXTAGPRINT 80
+
+void
+pppoe_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "pppoe";
+ ndo->ndo_ll_hdr_len += pppoe_print(ndo, p, h->len);
+}
+
+u_int
+pppoe_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ uint16_t pppoe_ver, pppoe_type, pppoe_code, pppoe_sessionid;
+ u_int pppoe_length;
+ const u_char *pppoe_packet, *pppoe_payload;
+
+ ndo->ndo_protocol = "pppoe";
+ if (length < PPPOE_HDRLEN) {
+ ND_PRINT(" (length %u < %u)", length, PPPOE_HDRLEN);
+ goto invalid;
+ }
+ length -= PPPOE_HDRLEN;
+ pppoe_packet = bp;
+ ND_TCHECK_LEN(pppoe_packet, PPPOE_HDRLEN);
+ pppoe_ver = (GET_U_1(pppoe_packet) & 0xF0) >> 4;
+ pppoe_type = (GET_U_1(pppoe_packet) & 0x0F);
+ pppoe_code = GET_U_1(pppoe_packet + 1);
+ pppoe_sessionid = GET_BE_U_2(pppoe_packet + 2);
+ pppoe_length = GET_BE_U_2(pppoe_packet + 4);
+ pppoe_payload = pppoe_packet + PPPOE_HDRLEN;
+
+ if (pppoe_ver != 1) {
+ ND_PRINT(" [ver %u]",pppoe_ver);
+ }
+ if (pppoe_type != 1) {
+ ND_PRINT(" [type %u]",pppoe_type);
+ }
+
+ ND_PRINT("PPPoE %s", tok2str(pppoecode2str, "PAD-%x", pppoe_code));
+ if (pppoe_code == PPPOE_PADI && pppoe_length > 1484 - PPPOE_HDRLEN) {
+ ND_PRINT(" [len %u!]",pppoe_length);
+ }
+ if (pppoe_length > length) {
+ ND_PRINT(" [len %u > %u!]", pppoe_length, length);
+ pppoe_length = length;
+ }
+ if (pppoe_sessionid) {
+ ND_PRINT(" [ses 0x%x]", pppoe_sessionid);
+ }
+
+ if (pppoe_code) {
+ /* PPP session packets don't contain tags */
+ u_short tag_type = 0xffff, tag_len;
+ const u_char *p = pppoe_payload;
+
+ /*
+ * loop invariant:
+ * p points to current tag,
+ * tag_type is previous tag or 0xffff for first iteration
+ */
+ while (tag_type && p < pppoe_payload + pppoe_length) {
+ tag_type = GET_BE_U_2(p);
+ tag_len = GET_BE_U_2(p + 2);
+ p += 4;
+ /* p points to tag_value */
+
+ if (tag_len) {
+ unsigned ascii_count = 0, garbage_count = 0;
+ const u_char *v;
+ char tag_str[MAXTAGPRINT];
+ unsigned tag_str_len = 0;
+
+ /* TODO print UTF-8 decoded text */
+ ND_TCHECK_LEN(p, tag_len);
+ for (v = p; v < p + tag_len && tag_str_len < MAXTAGPRINT-1; v++)
+ if (ND_ASCII_ISPRINT(GET_U_1(v))) {
+ tag_str[tag_str_len++] = GET_U_1(v);
+ ascii_count++;
+ } else {
+ tag_str[tag_str_len++] = '.';
+ garbage_count++;
+ }
+ tag_str[tag_str_len] = 0;
+
+ if (ascii_count > garbage_count) {
+ ND_PRINT(" [%s \"%*.*s\"]",
+ tok2str(pppoetag2str, "TAG-0x%x", tag_type),
+ (int)tag_str_len,
+ (int)tag_str_len,
+ tag_str);
+ } else {
+ /* Print hex, not fast to abuse printf but this doesn't get used much */
+ ND_PRINT(" [%s 0x", tok2str(pppoetag2str, "TAG-0x%x", tag_type));
+ for (v=p; v<p+tag_len; v++) {
+ ND_PRINT("%02X", GET_U_1(v));
+ }
+ ND_PRINT("]");
+ }
+
+
+ } else
+ ND_PRINT(" [%s]", tok2str(pppoetag2str,
+ "TAG-0x%x", tag_type));
+
+ p += tag_len;
+ /* p points to next tag */
+ }
+ return PPPOE_HDRLEN;
+ } else {
+ /* PPPoE data */
+ ND_PRINT(" ");
+ return (PPPOE_HDRLEN + ppp_print(ndo, pppoe_payload, pppoe_length));
+ }
+ /* NOTREACHED */
+
+invalid:
+ nd_print_invalid(ndo);
+ return 0;
+}
diff --git a/print-pptp.c b/print-pptp.c
new file mode 100644
index 0000000..8e1b303
--- /dev/null
+++ b/print-pptp.c
@@ -0,0 +1,871 @@
+/*
+ * Copyright (c) 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * PPTP support contributed by Motonori Shindo (mshindo@mshindo.net)
+ */
+
+/* \summary: Point-to-Point Tunnelling Protocol (PPTP) printer */
+
+/* specification: RFC 2637 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+
+#define PPTP_MSG_TYPE_CTRL 1 /* Control Message */
+#define PPTP_MSG_TYPE_MGMT 2 /* Management Message (currently not used */
+#define PPTP_MAGIC_COOKIE 0x1a2b3c4d /* for sanity check */
+
+#define PPTP_CTRL_MSG_TYPE_SCCRQ 1
+#define PPTP_CTRL_MSG_TYPE_SCCRP 2
+#define PPTP_CTRL_MSG_TYPE_StopCCRQ 3
+#define PPTP_CTRL_MSG_TYPE_StopCCRP 4
+#define PPTP_CTRL_MSG_TYPE_ECHORQ 5
+#define PPTP_CTRL_MSG_TYPE_ECHORP 6
+#define PPTP_CTRL_MSG_TYPE_OCRQ 7
+#define PPTP_CTRL_MSG_TYPE_OCRP 8
+#define PPTP_CTRL_MSG_TYPE_ICRQ 9
+#define PPTP_CTRL_MSG_TYPE_ICRP 10
+#define PPTP_CTRL_MSG_TYPE_ICCN 11
+#define PPTP_CTRL_MSG_TYPE_CCRQ 12
+#define PPTP_CTRL_MSG_TYPE_CDN 13
+#define PPTP_CTRL_MSG_TYPE_WEN 14
+#define PPTP_CTRL_MSG_TYPE_SLI 15
+
+#define PPTP_FRAMING_CAP_ASYNC_MASK 0x00000001 /* Aynchronous */
+#define PPTP_FRAMING_CAP_SYNC_MASK 0x00000002 /* Synchronous */
+
+#define PPTP_BEARER_CAP_ANALOG_MASK 0x00000001 /* Analog */
+#define PPTP_BEARER_CAP_DIGITAL_MASK 0x00000002 /* Digital */
+
+static const char *pptp_message_type_string[] = {
+ "NOT_DEFINED", /* 0 Not defined in the RFC2637 */
+ "SCCRQ", /* 1 Start-Control-Connection-Request */
+ "SCCRP", /* 2 Start-Control-Connection-Reply */
+ "StopCCRQ", /* 3 Stop-Control-Connection-Request */
+ "StopCCRP", /* 4 Stop-Control-Connection-Reply */
+ "ECHORQ", /* 5 Echo Request */
+ "ECHORP", /* 6 Echo Reply */
+
+ "OCRQ", /* 7 Outgoing-Call-Request */
+ "OCRP", /* 8 Outgoing-Call-Reply */
+ "ICRQ", /* 9 Incoming-Call-Request */
+ "ICRP", /* 10 Incoming-Call-Reply */
+ "ICCN", /* 11 Incoming-Call-Connected */
+ "CCRQ", /* 12 Call-Clear-Request */
+ "CDN", /* 13 Call-Disconnect-Notify */
+
+ "WEN", /* 14 WAN-Error-Notify */
+
+ "SLI" /* 15 Set-Link-Info */
+#define PPTP_MAX_MSGTYPE_INDEX 16
+};
+
+/* common for all PPTP control messages */
+struct pptp_hdr {
+ nd_uint16_t length;
+ nd_uint16_t msg_type;
+ nd_uint32_t magic_cookie;
+ nd_uint16_t ctrl_msg_type;
+ nd_uint16_t reserved0;
+};
+
+struct pptp_msg_sccrq {
+ nd_uint16_t proto_ver;
+ nd_uint16_t reserved1;
+ nd_uint32_t framing_cap;
+ nd_uint32_t bearer_cap;
+ nd_uint16_t max_channel;
+ nd_uint16_t firm_rev;
+ nd_byte hostname[64];
+ nd_byte vendor[64];
+};
+
+struct pptp_msg_sccrp {
+ nd_uint16_t proto_ver;
+ nd_uint8_t result_code;
+ nd_uint8_t err_code;
+ nd_uint32_t framing_cap;
+ nd_uint32_t bearer_cap;
+ nd_uint16_t max_channel;
+ nd_uint16_t firm_rev;
+ nd_byte hostname[64];
+ nd_byte vendor[64];
+};
+
+struct pptp_msg_stopccrq {
+ nd_uint8_t reason;
+ nd_uint8_t reserved1;
+ nd_uint16_t reserved2;
+};
+
+struct pptp_msg_stopccrp {
+ nd_uint8_t result_code;
+ nd_uint8_t err_code;
+ nd_uint16_t reserved1;
+};
+
+struct pptp_msg_echorq {
+ nd_uint32_t id;
+};
+
+struct pptp_msg_echorp {
+ nd_uint32_t id;
+ nd_uint8_t result_code;
+ nd_uint8_t err_code;
+ nd_uint16_t reserved1;
+};
+
+struct pptp_msg_ocrq {
+ nd_uint16_t call_id;
+ nd_uint16_t call_ser;
+ nd_uint32_t min_bps;
+ nd_uint32_t max_bps;
+ nd_uint32_t bearer_type;
+ nd_uint32_t framing_type;
+ nd_uint16_t recv_winsiz;
+ nd_uint16_t pkt_proc_delay;
+ nd_uint16_t phone_no_len;
+ nd_uint16_t reserved1;
+ nd_byte phone_no[64];
+ nd_byte subaddr[64];
+};
+
+struct pptp_msg_ocrp {
+ nd_uint16_t call_id;
+ nd_uint16_t peer_call_id;
+ nd_uint8_t result_code;
+ nd_uint8_t err_code;
+ nd_uint16_t cause_code;
+ nd_uint32_t conn_speed;
+ nd_uint16_t recv_winsiz;
+ nd_uint16_t pkt_proc_delay;
+ nd_uint32_t phy_chan_id;
+};
+
+struct pptp_msg_icrq {
+ nd_uint16_t call_id;
+ nd_uint16_t call_ser;
+ nd_uint32_t bearer_type;
+ nd_uint32_t phy_chan_id;
+ nd_uint16_t dialed_no_len;
+ nd_uint16_t dialing_no_len;
+ nd_byte dialed_no[64]; /* DNIS */
+ nd_byte dialing_no[64]; /* CLID */
+ nd_byte subaddr[64];
+};
+
+struct pptp_msg_icrp {
+ nd_uint16_t call_id;
+ nd_uint16_t peer_call_id;
+ nd_uint8_t result_code;
+ nd_uint8_t err_code;
+ nd_uint16_t recv_winsiz;
+ nd_uint16_t pkt_proc_delay;
+ nd_uint16_t reserved1;
+};
+
+struct pptp_msg_iccn {
+ nd_uint16_t peer_call_id;
+ nd_uint16_t reserved1;
+ nd_uint32_t conn_speed;
+ nd_uint16_t recv_winsiz;
+ nd_uint16_t pkt_proc_delay;
+ nd_uint32_t framing_type;
+};
+
+struct pptp_msg_ccrq {
+ nd_uint16_t call_id;
+ nd_uint16_t reserved1;
+};
+
+struct pptp_msg_cdn {
+ nd_uint16_t call_id;
+ nd_uint8_t result_code;
+ nd_uint8_t err_code;
+ nd_uint16_t cause_code;
+ nd_uint16_t reserved1;
+ nd_byte call_stats[128];
+};
+
+struct pptp_msg_wen {
+ nd_uint16_t peer_call_id;
+ nd_uint16_t reserved1;
+ nd_uint32_t crc_err;
+ nd_uint32_t framing_err;
+ nd_uint32_t hardware_overrun;
+ nd_uint32_t buffer_overrun;
+ nd_uint32_t timeout_err;
+ nd_uint32_t align_err;
+};
+
+struct pptp_msg_sli {
+ nd_uint16_t peer_call_id;
+ nd_uint16_t reserved1;
+ nd_uint32_t send_accm;
+ nd_uint32_t recv_accm;
+};
+
+/* attributes that appear more than once in above messages:
+
+ Number of
+ occurrence attributes
+ --------------------------------------
+ 2 uint32_t bearer_cap;
+ 2 uint32_t bearer_type;
+ 6 uint16_t call_id;
+ 2 uint16_t call_ser;
+ 2 uint16_t cause_code;
+ 2 uint32_t conn_speed;
+ 6 uint8_t err_code;
+ 2 uint16_t firm_rev;
+ 2 uint32_t framing_cap;
+ 2 uint32_t framing_type;
+ 2 u_char hostname[64];
+ 2 uint32_t id;
+ 2 uint16_t max_channel;
+ 5 uint16_t peer_call_id;
+ 2 uint32_t phy_chan_id;
+ 4 uint16_t pkt_proc_delay;
+ 2 uint16_t proto_ver;
+ 4 uint16_t recv_winsiz;
+ 2 uint8_t reserved1;
+ 9 uint16_t reserved1;
+ 6 uint8_t result_code;
+ 2 u_char subaddr[64];
+ 2 u_char vendor[64];
+
+ so I will prepare print out functions for these attributes (except for
+ reserved*).
+*/
+
+#define PRINT_RESERVED_IF_NOT_ZERO_1(reserved) \
+ if (GET_U_1(reserved)) \
+ ND_PRINT(" [ERROR: reserved=%u must be zero]", \
+ GET_U_1(reserved));
+
+#define PRINT_RESERVED_IF_NOT_ZERO_2(reserved) \
+ if (GET_BE_U_2(reserved)) \
+ ND_PRINT(" [ERROR: reserved=%u must be zero]", \
+ GET_BE_U_2(reserved));
+
+/******************************************/
+/* Attribute-specific print out functions */
+/******************************************/
+
+static void
+pptp_bearer_cap_print(netdissect_options *ndo,
+ const nd_uint32_t bearer_cap)
+{
+ ND_PRINT(" BEARER_CAP(%s%s)",
+ GET_BE_U_4(bearer_cap) & PPTP_BEARER_CAP_DIGITAL_MASK ? "D" : "",
+ GET_BE_U_4(bearer_cap) & PPTP_BEARER_CAP_ANALOG_MASK ? "A" : "");
+}
+
+static const struct tok pptp_btype_str[] = {
+ { 1, "A" }, /* Analog */
+ { 2, "D" }, /* Digital */
+ { 3, "Any" },
+ { 0, NULL }
+};
+
+static void
+pptp_bearer_type_print(netdissect_options *ndo,
+ const nd_uint32_t bearer_type)
+{
+ ND_PRINT(" BEARER_TYPE(%s)",
+ tok2str(pptp_btype_str, "?", GET_BE_U_4(bearer_type)));
+}
+
+static void
+pptp_call_id_print(netdissect_options *ndo,
+ const nd_uint16_t call_id)
+{
+ ND_PRINT(" CALL_ID(%u)", GET_BE_U_2(call_id));
+}
+
+static void
+pptp_call_ser_print(netdissect_options *ndo,
+ const nd_uint16_t call_ser)
+{
+ ND_PRINT(" CALL_SER_NUM(%u)", GET_BE_U_2(call_ser));
+}
+
+static void
+pptp_cause_code_print(netdissect_options *ndo,
+ const nd_uint16_t cause_code)
+{
+ ND_PRINT(" CAUSE_CODE(%u)", GET_BE_U_2(cause_code));
+}
+
+static void
+pptp_conn_speed_print(netdissect_options *ndo,
+ const nd_uint32_t conn_speed)
+{
+ ND_PRINT(" CONN_SPEED(%u)", GET_BE_U_4(conn_speed));
+}
+
+static const struct tok pptp_errcode_str[] = {
+ { 0, "None" },
+ { 1, "Not-Connected" },
+ { 2, "Bad-Format" },
+ { 3, "Bad-Value" },
+ { 4, "No-Resource" },
+ { 5, "Bad-Call-ID" },
+ { 6, "PAC-Error" },
+ { 0, NULL }
+};
+
+static void
+pptp_err_code_print(netdissect_options *ndo,
+ const nd_uint8_t err_code)
+{
+ ND_PRINT(" ERR_CODE(%u", GET_U_1(err_code));
+ if (ndo->ndo_vflag) {
+ ND_PRINT(":%s",
+ tok2str(pptp_errcode_str, "?", GET_U_1(err_code)));
+ }
+ ND_PRINT(")");
+}
+
+static void
+pptp_firm_rev_print(netdissect_options *ndo,
+ const nd_uint16_t firm_rev)
+{
+ ND_PRINT(" FIRM_REV(%u)", GET_BE_U_2(firm_rev));
+}
+
+static void
+pptp_framing_cap_print(netdissect_options *ndo,
+ const nd_uint32_t framing_cap)
+{
+ ND_PRINT(" FRAME_CAP(");
+ if (GET_BE_U_4(framing_cap) & PPTP_FRAMING_CAP_ASYNC_MASK) {
+ ND_PRINT("A"); /* Async */
+ }
+ if (GET_BE_U_4(framing_cap) & PPTP_FRAMING_CAP_SYNC_MASK) {
+ ND_PRINT("S"); /* Sync */
+ }
+ ND_PRINT(")");
+}
+
+static const struct tok pptp_ftype_str[] = {
+ { 1, "A" }, /* Async */
+ { 2, "S" }, /* Sync */
+ { 3, "E" }, /* Either */
+ { 0, NULL }
+};
+
+static void
+pptp_framing_type_print(netdissect_options *ndo,
+ const nd_uint32_t framing_type)
+{
+ ND_PRINT(" FRAME_TYPE(%s)",
+ tok2str(pptp_ftype_str, "?", GET_BE_U_4(framing_type)));
+}
+
+static void
+pptp_hostname_print(netdissect_options *ndo,
+ const u_char *hostname)
+{
+ ND_PRINT(" HOSTNAME(");
+ nd_printjnp(ndo, hostname, 64);
+ ND_PRINT(")");
+}
+
+static void
+pptp_id_print(netdissect_options *ndo,
+ const nd_uint32_t id)
+{
+ ND_PRINT(" ID(%u)", GET_BE_U_4(id));
+}
+
+static void
+pptp_max_channel_print(netdissect_options *ndo,
+ const nd_uint16_t max_channel)
+{
+ ND_PRINT(" MAX_CHAN(%u)", GET_BE_U_2(max_channel));
+}
+
+static void
+pptp_peer_call_id_print(netdissect_options *ndo,
+ const nd_uint16_t peer_call_id)
+{
+ ND_PRINT(" PEER_CALL_ID(%u)", GET_BE_U_2(peer_call_id));
+}
+
+static void
+pptp_phy_chan_id_print(netdissect_options *ndo,
+ const nd_uint32_t phy_chan_id)
+{
+ ND_PRINT(" PHY_CHAN_ID(%u)", GET_BE_U_4(phy_chan_id));
+}
+
+static void
+pptp_pkt_proc_delay_print(netdissect_options *ndo,
+ const nd_uint16_t pkt_proc_delay)
+{
+ ND_PRINT(" PROC_DELAY(%u)", GET_BE_U_2(pkt_proc_delay));
+}
+
+static void
+pptp_proto_ver_print(netdissect_options *ndo,
+ const nd_uint16_t proto_ver)
+{
+ ND_PRINT(" PROTO_VER(%u.%u)", /* Version.Revision */
+ GET_BE_U_2(proto_ver) >> 8,
+ GET_BE_U_2(proto_ver) & 0xff);
+}
+
+static void
+pptp_recv_winsiz_print(netdissect_options *ndo,
+ const nd_uint16_t recv_winsiz)
+{
+ ND_PRINT(" RECV_WIN(%u)", GET_BE_U_2(recv_winsiz));
+}
+
+static const struct tok pptp_scrrp_str[] = {
+ { 1, "Successful channel establishment" },
+ { 2, "General error" },
+ { 3, "Command channel already exists" },
+ { 4, "Requester is not authorized to establish a command channel" },
+ { 5, "The protocol version of the requester is not supported" },
+ { 0, NULL }
+};
+
+static const struct tok pptp_echorp_str[] = {
+ { 1, "OK" },
+ { 2, "General Error" },
+ { 0, NULL }
+};
+
+static const struct tok pptp_ocrp_str[] = {
+ { 1, "Connected" },
+ { 2, "General Error" },
+ { 3, "No Carrier" },
+ { 4, "Busy" },
+ { 5, "No Dial Tone" },
+ { 6, "Time-out" },
+ { 7, "Do Not Accept" },
+ { 0, NULL }
+};
+
+static const struct tok pptp_icrp_str[] = {
+ { 1, "Connect" },
+ { 2, "General Error" },
+ { 3, "Do Not Accept" },
+ { 0, NULL }
+};
+
+static const struct tok pptp_cdn_str[] = {
+ { 1, "Lost Carrier" },
+ { 2, "General Error" },
+ { 3, "Admin Shutdown" },
+ { 4, "Request" },
+ { 0, NULL }
+};
+
+static void
+pptp_result_code_print(netdissect_options *ndo,
+ const nd_uint8_t result_code, int ctrl_msg_type)
+{
+ ND_PRINT(" RESULT_CODE(%u", GET_U_1(result_code));
+ if (ndo->ndo_vflag) {
+ const struct tok *dict =
+ ctrl_msg_type == PPTP_CTRL_MSG_TYPE_SCCRP ? pptp_scrrp_str :
+ ctrl_msg_type == PPTP_CTRL_MSG_TYPE_StopCCRP ? pptp_echorp_str :
+ ctrl_msg_type == PPTP_CTRL_MSG_TYPE_ECHORP ? pptp_echorp_str :
+ ctrl_msg_type == PPTP_CTRL_MSG_TYPE_OCRP ? pptp_ocrp_str :
+ ctrl_msg_type == PPTP_CTRL_MSG_TYPE_ICRP ? pptp_icrp_str :
+ ctrl_msg_type == PPTP_CTRL_MSG_TYPE_CDN ? pptp_cdn_str :
+ NULL; /* assertion error */
+ if (dict != NULL)
+ ND_PRINT(":%s",
+ tok2str(dict, "?", GET_U_1(result_code)));
+ }
+ ND_PRINT(")");
+}
+
+static void
+pptp_subaddr_print(netdissect_options *ndo,
+ const u_char *subaddr)
+{
+ ND_PRINT(" SUB_ADDR(");
+ nd_printjnp(ndo, subaddr, 64);
+ ND_PRINT(")");
+}
+
+static void
+pptp_vendor_print(netdissect_options *ndo,
+ const u_char *vendor)
+{
+ ND_PRINT(" VENDOR(");
+ nd_printjnp(ndo, vendor, 64);
+ ND_PRINT(")");
+}
+
+/************************************/
+/* PPTP message print out functions */
+/************************************/
+static void
+pptp_sccrq_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_sccrq *ptr = (const struct pptp_msg_sccrq *)dat;
+
+ pptp_proto_ver_print(ndo, ptr->proto_ver);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+ pptp_framing_cap_print(ndo, ptr->framing_cap);
+ pptp_bearer_cap_print(ndo, ptr->bearer_cap);
+ pptp_max_channel_print(ndo, ptr->max_channel);
+ pptp_firm_rev_print(ndo, ptr->firm_rev);
+ pptp_hostname_print(ndo, ptr->hostname);
+ pptp_vendor_print(ndo, ptr->vendor);
+}
+
+static void
+pptp_sccrp_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_sccrp *ptr = (const struct pptp_msg_sccrp *)dat;
+
+ pptp_proto_ver_print(ndo, ptr->proto_ver);
+ pptp_result_code_print(ndo, ptr->result_code, PPTP_CTRL_MSG_TYPE_SCCRP);
+ pptp_err_code_print(ndo, ptr->err_code);
+ pptp_framing_cap_print(ndo, ptr->framing_cap);
+ pptp_bearer_cap_print(ndo, ptr->bearer_cap);
+ pptp_max_channel_print(ndo, ptr->max_channel);
+ pptp_firm_rev_print(ndo, ptr->firm_rev);
+ pptp_hostname_print(ndo, ptr->hostname);
+ pptp_vendor_print(ndo, ptr->vendor);
+}
+
+static void
+pptp_stopccrq_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_stopccrq *ptr = (const struct pptp_msg_stopccrq *)dat;
+
+ ND_PRINT(" REASON(%u", GET_U_1(ptr->reason));
+ if (ndo->ndo_vflag) {
+ switch (GET_U_1(ptr->reason)) {
+ case 1:
+ ND_PRINT(":None");
+ break;
+ case 2:
+ ND_PRINT(":Stop-Protocol");
+ break;
+ case 3:
+ ND_PRINT(":Stop-Local-Shutdown");
+ break;
+ default:
+ ND_PRINT(":?");
+ break;
+ }
+ }
+ ND_PRINT(")");
+ PRINT_RESERVED_IF_NOT_ZERO_1(ptr->reserved1);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved2);
+}
+
+static void
+pptp_stopccrp_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_stopccrp *ptr = (const struct pptp_msg_stopccrp *)dat;
+
+ pptp_result_code_print(ndo, ptr->result_code, PPTP_CTRL_MSG_TYPE_StopCCRP);
+ pptp_err_code_print(ndo, ptr->err_code);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+}
+
+static void
+pptp_echorq_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_echorq *ptr = (const struct pptp_msg_echorq *)dat;
+
+ pptp_id_print(ndo, ptr->id);
+}
+
+static void
+pptp_echorp_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_echorp *ptr = (const struct pptp_msg_echorp *)dat;
+
+ pptp_id_print(ndo, ptr->id);
+ pptp_result_code_print(ndo, ptr->result_code, PPTP_CTRL_MSG_TYPE_ECHORP);
+ pptp_err_code_print(ndo, ptr->err_code);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+}
+
+static void
+pptp_ocrq_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_ocrq *ptr = (const struct pptp_msg_ocrq *)dat;
+
+ pptp_call_id_print(ndo, ptr->call_id);
+ pptp_call_ser_print(ndo, ptr->call_ser);
+ ND_PRINT(" MIN_BPS(%u)", GET_BE_U_4(ptr->min_bps));
+ ND_PRINT(" MAX_BPS(%u)", GET_BE_U_4(ptr->max_bps));
+ pptp_bearer_type_print(ndo, ptr->bearer_type);
+ pptp_framing_type_print(ndo, ptr->framing_type);
+ pptp_recv_winsiz_print(ndo, ptr->recv_winsiz);
+ pptp_pkt_proc_delay_print(ndo, ptr->pkt_proc_delay);
+ ND_PRINT(" PHONE_NO_LEN(%u)", GET_BE_U_2(ptr->phone_no_len));
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+ ND_PRINT(" PHONE_NO(");
+ nd_printjnp(ndo, ptr->phone_no,
+ ND_MIN(64, GET_BE_U_2(ptr->phone_no_len)));
+ ND_PRINT(")");
+ pptp_subaddr_print(ndo, ptr->subaddr);
+}
+
+static void
+pptp_ocrp_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_ocrp *ptr = (const struct pptp_msg_ocrp *)dat;
+
+ pptp_call_id_print(ndo, ptr->call_id);
+ pptp_peer_call_id_print(ndo, ptr->peer_call_id);
+ pptp_result_code_print(ndo, ptr->result_code, PPTP_CTRL_MSG_TYPE_OCRP);
+ pptp_err_code_print(ndo, ptr->err_code);
+ pptp_cause_code_print(ndo, ptr->cause_code);
+ pptp_conn_speed_print(ndo, ptr->conn_speed);
+ pptp_recv_winsiz_print(ndo, ptr->recv_winsiz);
+ pptp_pkt_proc_delay_print(ndo, ptr->pkt_proc_delay);
+ pptp_phy_chan_id_print(ndo, ptr->phy_chan_id);
+}
+
+static void
+pptp_icrq_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_icrq *ptr = (const struct pptp_msg_icrq *)dat;
+
+ pptp_call_id_print(ndo, ptr->call_id);
+ pptp_call_ser_print(ndo, ptr->call_ser);
+ pptp_bearer_type_print(ndo, ptr->bearer_type);
+ pptp_phy_chan_id_print(ndo, ptr->phy_chan_id);
+ ND_PRINT(" DIALED_NO_LEN(%u)", GET_BE_U_2(ptr->dialed_no_len));
+ ND_PRINT(" DIALING_NO_LEN(%u)", GET_BE_U_2(ptr->dialing_no_len));
+ ND_PRINT(" DIALED_NO(");
+ nd_printjnp(ndo, ptr->dialed_no,
+ ND_MIN(64, GET_BE_U_2(ptr->dialed_no_len)));
+ ND_PRINT(")");
+ ND_PRINT(" DIALING_NO(");
+ nd_printjnp(ndo, ptr->dialing_no,
+ ND_MIN(64, GET_BE_U_2(ptr->dialing_no_len)));
+ ND_PRINT(")");
+ pptp_subaddr_print(ndo, ptr->subaddr);
+}
+
+static void
+pptp_icrp_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_icrp *ptr = (const struct pptp_msg_icrp *)dat;
+
+ pptp_call_id_print(ndo, ptr->call_id);
+ pptp_peer_call_id_print(ndo, ptr->peer_call_id);
+ pptp_result_code_print(ndo, ptr->result_code, PPTP_CTRL_MSG_TYPE_ICRP);
+ pptp_err_code_print(ndo, ptr->err_code);
+ pptp_recv_winsiz_print(ndo, ptr->recv_winsiz);
+ pptp_pkt_proc_delay_print(ndo, ptr->pkt_proc_delay);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+}
+
+static void
+pptp_iccn_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_iccn *ptr = (const struct pptp_msg_iccn *)dat;
+
+ pptp_peer_call_id_print(ndo, ptr->peer_call_id);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+ pptp_conn_speed_print(ndo, ptr->conn_speed);
+ pptp_recv_winsiz_print(ndo, ptr->recv_winsiz);
+ pptp_pkt_proc_delay_print(ndo, ptr->pkt_proc_delay);
+ pptp_framing_type_print(ndo, ptr->framing_type);
+}
+
+static void
+pptp_ccrq_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_ccrq *ptr = (const struct pptp_msg_ccrq *)dat;
+
+ pptp_call_id_print(ndo, ptr->call_id);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+}
+
+static void
+pptp_cdn_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_cdn *ptr = (const struct pptp_msg_cdn *)dat;
+
+ pptp_call_id_print(ndo, ptr->call_id);
+ pptp_result_code_print(ndo, ptr->result_code, PPTP_CTRL_MSG_TYPE_CDN);
+ pptp_err_code_print(ndo, ptr->err_code);
+ pptp_cause_code_print(ndo, ptr->cause_code);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+ ND_PRINT(" CALL_STATS(");
+ nd_printjnp(ndo, ptr->call_stats, 128);
+ ND_PRINT(")");
+}
+
+static void
+pptp_wen_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_wen *ptr = (const struct pptp_msg_wen *)dat;
+
+ pptp_peer_call_id_print(ndo, ptr->peer_call_id);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+ ND_PRINT(" CRC_ERR(%u)", GET_BE_U_4(ptr->crc_err));
+ ND_PRINT(" FRAMING_ERR(%u)", GET_BE_U_4(ptr->framing_err));
+ ND_PRINT(" HARDWARE_OVERRUN(%u)", GET_BE_U_4(ptr->hardware_overrun));
+ ND_PRINT(" BUFFER_OVERRUN(%u)", GET_BE_U_4(ptr->buffer_overrun));
+ ND_PRINT(" TIMEOUT_ERR(%u)", GET_BE_U_4(ptr->timeout_err));
+ ND_PRINT(" ALIGN_ERR(%u)", GET_BE_U_4(ptr->align_err));
+}
+
+static void
+pptp_sli_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_msg_sli *ptr = (const struct pptp_msg_sli *)dat;
+
+ pptp_peer_call_id_print(ndo, ptr->peer_call_id);
+ PRINT_RESERVED_IF_NOT_ZERO_2(ptr->reserved1);
+ ND_PRINT(" SEND_ACCM(0x%08x)", GET_BE_U_4(ptr->send_accm));
+ ND_PRINT(" RECV_ACCM(0x%08x)", GET_BE_U_4(ptr->recv_accm));
+}
+
+void
+pptp_print(netdissect_options *ndo,
+ const u_char *dat)
+{
+ const struct pptp_hdr *hdr;
+ uint32_t mc;
+ uint16_t ctrl_msg_type;
+
+ ndo->ndo_protocol = "pptp";
+ ND_PRINT(": ");
+ nd_print_protocol(ndo);
+
+ hdr = (const struct pptp_hdr *)dat;
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" Length=%u", GET_BE_U_2(hdr->length));
+ }
+ if (ndo->ndo_vflag) {
+ switch(GET_BE_U_2(hdr->msg_type)) {
+ case PPTP_MSG_TYPE_CTRL:
+ ND_PRINT(" CTRL-MSG");
+ break;
+ case PPTP_MSG_TYPE_MGMT:
+ ND_PRINT(" MGMT-MSG");
+ break;
+ default:
+ ND_PRINT(" UNKNOWN-MSG-TYPE");
+ break;
+ }
+ }
+
+ mc = GET_BE_U_4(hdr->magic_cookie);
+ if (mc != PPTP_MAGIC_COOKIE) {
+ ND_PRINT(" UNEXPECTED Magic-Cookie!!(%08x)", mc);
+ }
+ if (ndo->ndo_vflag || mc != PPTP_MAGIC_COOKIE) {
+ ND_PRINT(" Magic-Cookie=%08x", mc);
+ }
+ ctrl_msg_type = GET_BE_U_2(hdr->ctrl_msg_type);
+ if (ctrl_msg_type < PPTP_MAX_MSGTYPE_INDEX) {
+ ND_PRINT(" CTRL_MSGTYPE=%s",
+ pptp_message_type_string[ctrl_msg_type]);
+ } else {
+ ND_PRINT(" UNKNOWN_CTRL_MSGTYPE(%u)", ctrl_msg_type);
+ }
+ PRINT_RESERVED_IF_NOT_ZERO_2(hdr->reserved0);
+
+ dat += 12;
+
+ switch(ctrl_msg_type) {
+ case PPTP_CTRL_MSG_TYPE_SCCRQ:
+ pptp_sccrq_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_SCCRP:
+ pptp_sccrp_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_StopCCRQ:
+ pptp_stopccrq_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_StopCCRP:
+ pptp_stopccrp_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_ECHORQ:
+ pptp_echorq_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_ECHORP:
+ pptp_echorp_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_OCRQ:
+ pptp_ocrq_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_OCRP:
+ pptp_ocrp_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_ICRQ:
+ pptp_icrq_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_ICRP:
+ pptp_icrp_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_ICCN:
+ pptp_iccn_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_CCRQ:
+ pptp_ccrq_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_CDN:
+ pptp_cdn_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_WEN:
+ pptp_wen_print(ndo, dat);
+ break;
+ case PPTP_CTRL_MSG_TYPE_SLI:
+ pptp_sli_print(ndo, dat);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+}
diff --git a/print-ptp.c b/print-ptp.c
new file mode 100644
index 0000000..6f12b90
--- /dev/null
+++ b/print-ptp.c
@@ -0,0 +1,620 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Partha S. Ghosh (psglinux dot gmail dot com)
+ */
+
+/* \summary: Precision Time Protocol (PTP) printer */
+
+/* specification: https://standards.ieee.org/findstds/standard/1588-2008.html*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include "extract.h"
+
+/*
+ * PTP header
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | R | |msgtype| version | Msg Len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | domain No | rsvd1 | flag Field |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Correction NS |
+ * | Correction Sub NS |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Reserved2 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Clock Identity |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Port Identity | Sequence ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | control | log msg int |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * 0 1 2 3
+ *
+ * Announce Message (msg type=0xB)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nano Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Origin Cur UTC Offset | Reserved | GM Prio 1 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |GM Clock Class | GM Clock Accu | GM Clock Variance |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | GM Prio 2 | |
+ * +-+-+-+-+-+-+-+-+ +
+ * | GM Clock Identity |
+ * + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | | Steps Removed | Time Source |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * 0 1 2 3
+ *
+ * Sync Message (msg type=0x0)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nano Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Delay Request Message (msg type=0x1)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | Origin Time Stamp Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nano Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Followup Message (msg type=0x8)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | Precise Origin Time Stamp Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nano Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Delay Resp Message (msg type=0x9)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nano Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Port Identity |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * PDelay Request Message (msg type=0x2)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | Origin Time Stamp Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Origin Time Stamp Nano Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Port Identity |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * PDelay Response Message (msg type=0x3)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | Request receipt Time Stamp Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nano Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Requesting Port Identity |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * PDelay Resp Follow up Message (msg type=0xA)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +
+ * | Response Origin Time Stamp Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Nano Seconds |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Requesting Port Identity |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Signalling Message (msg type=0xC)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Requesting Port Identity |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * Management Message (msg type=0xD)
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Requesting Port Identity |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Start Bndry Hps| Boundary Hops | flags | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+#define M_SYNC 0x0
+#define M_DELAY_REQ 0x1
+#define M_PDELAY_REQ 0x2
+#define M_PDELAY_RESP 0x3
+#define M_OTHER 0x5
+#define M_FOLLOW_UP 0x8
+#define M_DELAY_RESP 0x9
+#define M_PDELAY_RESP_FOLLOW_UP 0xA
+#define M_ANNOUNCE 0xB
+#define M_SIGNALLING 0xC
+#define M_MANAGEMENT 0xD
+
+static const struct tok ptp_msg_type[] = {
+ { M_SYNC ,"sync msg"},
+ { M_DELAY_REQ ,"delay req msg"},
+ { M_PDELAY_REQ ,"peer delay req msg"},
+ { M_PDELAY_RESP ,"peer delay resp msg"},
+ { M_OTHER, "Other"},
+ { M_FOLLOW_UP ,"follow up msg"},
+ { M_DELAY_RESP ,"delay resp msg"},
+ { M_PDELAY_RESP_FOLLOW_UP ,"pdelay resp fup msg"},
+ { M_ANNOUNCE ,"announce msg"},
+ { M_SIGNALLING ,"signalling msg"},
+ { M_MANAGEMENT ,"management msg"},
+ { 0, NULL}
+};
+
+
+#define PTP_TRUE 1
+#define PTP_FALSE !PTP_TRUE
+
+#define PTP_HDR_LEN 0x22
+
+/* mask based on the first byte */
+#define PTP_VERS_MASK 0xFF
+#define PTP_V1_COMPAT 0x10
+#define PTP_MSG_TYPE_MASK 0x0F
+
+/*mask based 2byte */
+#define PTP_DOMAIN_MASK 0xFF00
+#define PTP_RSVD1_MASK 0xFF
+#define PTP_CONTROL_MASK 0xFF
+#define PTP_LOGMSG_MASK 0xFF
+
+/* mask based on the flags 2 bytes */
+
+#define PTP_L161_MASK 0x1
+#define PTP_L1_59_MASK 0x2
+#define PTP_UTC_REASONABLE_MASK 0x4
+#define PTP_TIMESCALE_MASK 0x8
+#define PTP_TIME_TRACABLE_MASK 0x10
+#define PTP_FREQUENCY_TRACABLE_MASK 0x20
+#define PTP_ALTERNATE_MASTER_MASK 0x100
+#define PTP_TWO_STEP_MASK 0x200
+#define PTP_UNICAST_MASK 0x400
+#define PTP_PROFILE_SPEC_1_MASK 0x1000
+#define PTP_PROFILE_SPEC_2_MASK 0x2000
+#define PTP_SECURITY_MASK 0x4000
+#define PTP_FLAGS_UNKNOWN_MASK 0x18C0
+
+
+static const struct tok ptp_flag_values[] = {
+ { PTP_L161_MASK ,"l1 61"},
+ { PTP_L1_59_MASK ,"l1 59"},
+ { PTP_UTC_REASONABLE_MASK ,"utc reasonable"},
+ { PTP_TIMESCALE_MASK ,"timescale"},
+ { PTP_TIME_TRACABLE_MASK ,"time tracable"},
+ { PTP_FREQUENCY_TRACABLE_MASK ,"frequency tracable"},
+ { PTP_ALTERNATE_MASTER_MASK ,"alternate master"},
+ { PTP_TWO_STEP_MASK ,"two step"},
+ { PTP_UNICAST_MASK ,"unicast"},
+ { PTP_PROFILE_SPEC_1_MASK ,"profile specific 1"},
+ { PTP_PROFILE_SPEC_2_MASK ,"profile specific 2"},
+ { PTP_SECURITY_MASK ,"security mask"},
+ { PTP_FLAGS_UNKNOWN_MASK , "unknown"},
+ {0, NULL}
+};
+
+#define PTP_PRINT_MSG_TYPE(e) \
+ { \
+ ND_PRINT("(%s)", tok2str(ptp_msg_type, "unknown", e)); \
+ }
+
+static const char *p_porigin_ts = "preciseOriginTimeStamp";
+static const char *p_origin_ts = "originTimeStamp";
+static const char *p_recv_ts = "receiveTimeStamp";
+
+#define PTP_VER_1 0x1
+#define PTP_VER_2 0x2
+
+#define PTP_UCHAR_LEN sizeof(uint8_t)
+#define PTP_UINT16_LEN sizeof(uint16_t)
+#define PTP_UINT32_LEN sizeof(uint32_t)
+#define PTP_6BYTES_LEN sizeof(uint32_t)+sizeof(uint16_t)
+#define PTP_UINT64_LEN sizeof(uint64_t)
+
+
+
+static void ptp_print_1(netdissect_options *ndo);
+static void ptp_print_2(netdissect_options *ndo, const u_char *bp, u_int len);
+
+static void ptp_print_timestamp(netdissect_options *ndo, const u_char *bp, u_int *len, const char *stype);
+static void ptp_print_timestamp_identity(netdissect_options *ndo, const u_char *bp, u_int *len, const char *ttype);
+static void ptp_print_announce_msg(netdissect_options *ndo, const u_char *bp, u_int *len);
+static void ptp_print_port_id(netdissect_options *ndo, const u_char *bp, u_int *len);
+static void ptp_print_mgmt_msg(netdissect_options *ndo, const u_char *bp, u_int *len);
+
+static void
+print_field(netdissect_options *ndo, const char *st, uint32_t flen,
+ const u_char *bp, u_int *len, uint8_t hex)
+{
+ uint8_t u8_val;
+ uint16_t u16_val;
+ uint32_t u32_val;
+ uint64_t u64_val;
+
+ switch(flen) {
+ case PTP_UCHAR_LEN:
+ u8_val = GET_U_1(bp);
+ ND_PRINT(", %s", st);
+ if (hex)
+ ND_PRINT(" 0x%x", u8_val);
+ else
+ ND_PRINT(" %u", u8_val);
+ *len -= 1; bp += 1;
+ break;
+ case PTP_UINT16_LEN:
+ u16_val = GET_BE_U_2(bp);
+ ND_PRINT(", %s", st);
+ if (hex)
+ ND_PRINT(" 0x%x", u16_val);
+ else
+ ND_PRINT(" %u", u16_val);
+ *len -= 2; bp += 2;
+ break;
+ case PTP_UINT32_LEN:
+ u32_val = GET_BE_U_4(bp);
+ ND_PRINT(", %s", st);
+ if (hex)
+ ND_PRINT(" 0x%x", u32_val);
+ else
+ ND_PRINT(" %u", u32_val);
+ *len -= 4; bp += 4;
+ break;
+ case PTP_UINT64_LEN:
+ u64_val = GET_BE_U_8(bp);
+ ND_PRINT(", %s", st);
+ if (hex)
+ ND_PRINT(" 0x%"PRIx64, u64_val);
+ else
+ ND_PRINT(" 0x%"PRIu64, u64_val);
+ *len -= 8; bp += 8;
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+ptp_print_1(netdissect_options *ndo)
+{
+ ND_PRINT(" (not implemented)");
+}
+
+static void
+ptp_print_2(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ u_int len = length;
+ uint16_t msg_len, flags, port_id, seq_id;
+ uint8_t foct, domain_no, msg_type, v1_compat, rsvd1, lm_int, control;
+ uint32_t ns_corr, sns_corr, rsvd2;
+ uint64_t clk_id;
+
+ foct = GET_U_1(bp);
+ v1_compat = foct & PTP_V1_COMPAT;
+ ND_PRINT(", v1 compat : %s", v1_compat?"yes":"no");
+ msg_type = foct & PTP_MSG_TYPE_MASK;
+ ND_PRINT(", msg type : %s", tok2str(ptp_msg_type, "none", msg_type));
+
+ /* msg length */
+ len -= 2; bp += 2; msg_len = GET_BE_U_2(bp); ND_PRINT(", length : %u", msg_len);
+
+ /* domain */
+ len -= 2; bp += 2; domain_no = (GET_BE_U_2(bp) & PTP_DOMAIN_MASK) >> 8; ND_PRINT(", domain : %u", domain_no);
+
+ /* rsvd 1*/
+ rsvd1 = GET_BE_U_2(bp) & PTP_RSVD1_MASK;
+ ND_PRINT(", reserved1 : %u", rsvd1);
+
+ /* flags */
+ len -= 2; bp += 2; flags = GET_BE_U_2(bp); ND_PRINT(", Flags [%s]", bittok2str(ptp_flag_values, "none", flags));
+
+ /* correction NS */
+ len -= 2; bp += 2; ns_corr = GET_BE_U_4(bp); ND_PRINT(", NS correction : %u", ns_corr);
+
+ /* correction sub NS */
+ len -= 4; bp += 4; sns_corr = GET_BE_U_4(bp); ND_PRINT(", sub NS correction : %u", sns_corr);
+
+ /* Reserved 2 */
+ len -= 4; bp += 4; rsvd2 = GET_BE_U_4(bp); ND_PRINT(", reserved2 : %u", rsvd2);
+
+ /* clock identity */
+ len -= 4; bp += 4; clk_id = GET_BE_U_8(bp); ND_PRINT(", clock identity : 0x%"PRIx64, clk_id);
+
+ /* port identity */
+ len -= 8; bp += 8; port_id = GET_BE_U_2(bp); ND_PRINT(", port id : %u", port_id);
+
+ /* sequence ID */
+ len -= 2; bp += 2; seq_id = GET_BE_U_2(bp); ND_PRINT(", seq id : %u", seq_id);
+
+ /* control */
+ len -= 2; bp += 2; control = GET_U_1(bp) ;
+ ND_PRINT(", control : %u (%s)", control, tok2str(ptp_msg_type, "none", control));
+
+ /* log message interval */
+ lm_int = GET_BE_U_2(bp) & PTP_LOGMSG_MASK; ND_PRINT(", log message interval : %u", lm_int); len -= 2; bp += 2;
+
+ switch(msg_type) {
+ case M_SYNC:
+ ptp_print_timestamp(ndo, bp, &len, p_origin_ts);
+ break;
+ case M_DELAY_REQ:
+ ptp_print_timestamp(ndo, bp, &len, p_origin_ts);
+ break;
+ case M_PDELAY_REQ:
+ ptp_print_timestamp_identity(ndo, bp, &len, p_porigin_ts);
+ break;
+ case M_PDELAY_RESP:
+ ptp_print_timestamp_identity(ndo, bp, &len, p_recv_ts);
+ break;
+ case M_FOLLOW_UP:
+ ptp_print_timestamp(ndo, bp, &len, p_porigin_ts);
+ break;
+ case M_DELAY_RESP:
+ ptp_print_timestamp_identity(ndo, bp, &len, p_recv_ts);
+ break;
+ case M_PDELAY_RESP_FOLLOW_UP:
+ ptp_print_timestamp_identity(ndo, bp, &len, p_porigin_ts);
+ break;
+ case M_ANNOUNCE:
+ ptp_print_announce_msg(ndo, bp, &len);
+ break;
+ case M_SIGNALLING:
+ ptp_print_port_id(ndo, bp, &len);
+ break;
+ case M_MANAGEMENT:
+ ptp_print_mgmt_msg(ndo, bp, &len);
+ break;
+ default:
+ break;
+ }
+}
+/*
+ * PTP general message
+ */
+void
+ptp_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ u_int vers;
+
+ ndo->ndo_protocol = "ptp";
+ if (len < PTP_HDR_LEN) {
+ goto trunc;
+ }
+ vers = GET_BE_U_2(bp) & PTP_VERS_MASK;
+ ND_PRINT("PTPv%u",vers);
+ switch(vers) {
+ case PTP_VER_1:
+ ptp_print_1(ndo);
+ break;
+ case PTP_VER_2:
+ ptp_print_2(ndo, bp, len);
+ break;
+ default:
+ //ND_PRINT("ERROR: unknown-version\n");
+ break;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+ptp_print_timestamp(netdissect_options *ndo, const u_char *bp, u_int *len, const char *stype)
+{
+ uint64_t secs;
+ uint32_t nsecs;
+
+ ND_PRINT(", %s :", stype);
+ /* sec time stamp 6 bytes */
+ secs = GET_BE_U_6(bp);
+ ND_PRINT(" %"PRIu64" seconds,", secs);
+ *len -= 6;
+ bp += 6;
+
+ /* NS time stamp 4 bytes */
+ nsecs = GET_BE_U_4(bp);
+ ND_PRINT(" %u nanoseconds", nsecs);
+ *len -= 4;
+ bp += 4;
+}
+static void
+ptp_print_timestamp_identity(netdissect_options *ndo,
+ const u_char *bp, u_int *len, const char *ttype)
+{
+ uint64_t secs;
+ uint32_t nsecs;
+ uint16_t port_id;
+ uint64_t port_identity;
+
+ ND_PRINT(", %s :", ttype);
+ /* sec time stamp 6 bytes */
+ secs = GET_BE_U_6(bp);
+ ND_PRINT(" %"PRIu64" seconds,", secs);
+ *len -= 6;
+ bp += 6;
+
+ /* NS time stamp 4 bytes */
+ nsecs = GET_BE_U_4(bp);
+ ND_PRINT(" %u nanoseconds", nsecs);
+ *len -= 4;
+ bp += 4;
+
+ /* port identity*/
+ port_identity = GET_BE_U_8(bp);
+ ND_PRINT(", port identity : 0x%"PRIx64, port_identity);
+ *len -= 8;
+ bp += 8;
+
+ /* port id */
+ port_id = GET_BE_U_2(bp);
+ ND_PRINT(", port id : %u", port_id);
+ *len -= 2;
+ bp += 2;
+}
+static void
+ptp_print_announce_msg(netdissect_options *ndo, const u_char *bp, u_int *len)
+{
+ uint8_t rsvd, gm_prio_1, gm_prio_2, gm_clk_cls, gm_clk_acc, time_src;
+ uint16_t origin_cur_utc, gm_clk_var, steps_removed;
+ uint64_t gm_clock_id;
+ uint64_t secs;
+ uint32_t nsecs;
+
+ ND_PRINT(", %s :", p_origin_ts);
+ /* sec time stamp 6 bytes */
+ secs = GET_BE_U_6(bp);
+ ND_PRINT(" %"PRIu64" seconds", secs);
+ *len -= 6;
+ bp += 6;
+
+ /* NS time stamp 4 bytes */
+ nsecs = GET_BE_U_4(bp);
+ ND_PRINT(" %u nanoseconds", nsecs);
+ *len -= 4;
+ bp += 4;
+
+ /* origin cur utc */
+ origin_cur_utc = GET_BE_U_2(bp);
+ ND_PRINT(", origin cur utc :%u", origin_cur_utc);
+ *len -= 2;
+ bp += 2;
+
+ /* rsvd */
+ rsvd = GET_U_1(bp);
+ ND_PRINT(", rsvd : %u", rsvd);
+ *len -= 1;
+ bp += 1;
+
+ /* gm prio */
+ gm_prio_1 = GET_U_1(bp);
+ ND_PRINT(", gm priority_1 : %u", gm_prio_1);
+ *len -= 1;
+ bp += 1;
+
+ /* GM clock class */
+ gm_clk_cls = GET_U_1(bp);
+ ND_PRINT(", gm clock class : %u", gm_clk_cls);
+ *len -= 1;
+ bp += 1;
+ /* GM clock accuracy */
+ gm_clk_acc = GET_U_1(bp);
+ ND_PRINT(", gm clock accuracy : %u", gm_clk_acc);
+ *len -= 1;
+ bp += 1;
+ /* GM clock variance */
+ gm_clk_var = GET_BE_U_2(bp);
+ ND_PRINT(", gm clock variance : %u", gm_clk_var);
+ *len -= 2;
+ bp += 2;
+ /* GM Prio 2 */
+ gm_prio_2 = GET_U_1(bp);
+ ND_PRINT(", gm priority_2 : %u", gm_prio_2);
+ *len -= 1;
+ bp += 1;
+
+ /* GM Clock Identity */
+ gm_clock_id = GET_BE_U_8(bp);
+ ND_PRINT(", gm clock id : 0x%"PRIx64, gm_clock_id);
+ *len -= 8;
+ bp += 8;
+ /* steps removed */
+ steps_removed = GET_BE_U_2(bp);
+ ND_PRINT(", steps removed : %u", steps_removed);
+ *len -= 2;
+ bp += 2;
+ /* Time source */
+ time_src = GET_U_1(bp);
+ ND_PRINT(", time source : 0x%x", time_src);
+ *len -= 1;
+ bp += 1;
+
+}
+static void
+ptp_print_port_id(netdissect_options *ndo, const u_char *bp, u_int *len)
+{
+ uint16_t port_id;
+ uint64_t port_identity;
+
+ /* port identity*/
+ port_identity = GET_BE_U_8(bp);
+ ND_PRINT(", port identity : 0x%"PRIx64, port_identity);
+ *len -= 8;
+ bp += 8;
+
+ /* port id */
+ port_id = GET_BE_U_2(bp);
+ ND_PRINT(", port id : %u", port_id);
+ *len -= 2;
+ bp += 2;
+
+}
+
+static void
+ptp_print_mgmt_msg(netdissect_options *ndo, const u_char *bp, u_int *len)
+{
+ ptp_print_port_id(ndo, bp, len);
+ print_field(ndo, ", start boundary hops ", PTP_UCHAR_LEN, bp, len, PTP_FALSE);
+ print_field(ndo, ", boundary hops ", PTP_UCHAR_LEN, bp, len, PTP_FALSE);
+ print_field(ndo, ", flags ", PTP_UCHAR_LEN, bp, len, PTP_TRUE);
+ print_field(ndo, ", reserved ", PTP_UCHAR_LEN, bp, len, PTP_TRUE);
+}
diff --git a/print-radius.c b/print-radius.c
new file mode 100644
index 0000000..c87fa90
--- /dev/null
+++ b/print-radius.c
@@ -0,0 +1,1513 @@
+/*
+ * Copyright (C) 2000 Alfredo Andres Omella. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Radius protocol printer */
+
+/*
+ * Radius printer routines as specified on:
+ *
+ * RFC 2865:
+ * "Remote Authentication Dial In User Service (RADIUS)"
+ *
+ * RFC 2866:
+ * "RADIUS Accounting"
+ *
+ * RFC 2867:
+ * "RADIUS Accounting Modifications for Tunnel Protocol Support"
+ *
+ * RFC 2868:
+ * "RADIUS Attributes for Tunnel Protocol Support"
+ *
+ * RFC 2869:
+ * "RADIUS Extensions"
+ *
+ * RFC 3162:
+ * "RADIUS and IPv6"
+ *
+ * RFC 3580:
+ * "IEEE 802.1X Remote Authentication Dial In User Service (RADIUS)"
+ * "Usage Guidelines"
+ *
+ * RFC 4072:
+ * "Diameter Extensible Authentication Protocol (EAP) Application"
+ *
+ * RFC 4675:
+ * "RADIUS Attributes for Virtual LAN and Priority Support"
+ *
+ * RFC 4818:
+ * "RADIUS Delegated-IPv6-Prefix Attribute"
+ *
+ * RFC 4849:
+ * "RADIUS Filter Rule Attribute"
+ *
+ * RFC 5090:
+ * "RADIUS Extension for Digest Authentication"
+ *
+ * RFC 5176:
+ * "Dynamic Authorization Extensions to RADIUS"
+ *
+ * RFC 5447:
+ * "Diameter Mobile IPv6"
+ *
+ * RFC 5580:
+ * "Carrying Location Objects in RADIUS and Diameter"
+ *
+ * RFC 6572:
+ * "RADIUS Support for Proxy Mobile IPv6"
+ *
+ * RFC 7155:
+ * "Diameter Network Access Server Application"
+ *
+ * Alfredo Andres Omella (aandres@s21sec.com) v0.1 2000/09/15
+ *
+ * TODO: Among other things to print ok MacIntosh and Vendor values
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+#include "oui.h"
+#include "ntp.h"
+
+
+#define TAM_SIZE(x) (sizeof(x)/sizeof(x[0]) )
+
+#define PRINT_HEX(bytes_len, ptr_data) \
+ while(bytes_len) \
+ { \
+ ND_PRINT("%02X", *ptr_data ); \
+ ptr_data++; \
+ bytes_len--; \
+ }
+
+
+/* Radius packet codes */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-27 */
+#define RADCMD_ACCESS_REQ 1 /* Access-Request */
+#define RADCMD_ACCESS_ACC 2 /* Access-Accept */
+#define RADCMD_ACCESS_REJ 3 /* Access-Reject */
+#define RADCMD_ACCOUN_REQ 4 /* Accounting-Request */
+#define RADCMD_ACCOUN_RES 5 /* Accounting-Response */
+#define RADCMD_ACCESS_CHA 11 /* Access-Challenge */
+#define RADCMD_STATUS_SER 12 /* Status-Server */
+#define RADCMD_STATUS_CLI 13 /* Status-Client */
+#define RADCMD_DISCON_REQ 40 /* Disconnect-Request */
+#define RADCMD_DISCON_ACK 41 /* Disconnect-ACK */
+#define RADCMD_DISCON_NAK 42 /* Disconnect-NAK */
+#define RADCMD_COA_REQ 43 /* CoA-Request */
+#define RADCMD_COA_ACK 44 /* CoA-ACK */
+#define RADCMD_COA_NAK 45 /* CoA-NAK */
+#define RADCMD_RESERVED 255 /* Reserved */
+
+static const struct tok radius_command_values[] = {
+ { RADCMD_ACCESS_REQ, "Access-Request" },
+ { RADCMD_ACCESS_ACC, "Access-Accept" },
+ { RADCMD_ACCESS_REJ, "Access-Reject" },
+ { RADCMD_ACCOUN_REQ, "Accounting-Request" },
+ { RADCMD_ACCOUN_RES, "Accounting-Response" },
+ { RADCMD_ACCESS_CHA, "Access-Challenge" },
+ { RADCMD_STATUS_SER, "Status-Server" },
+ { RADCMD_STATUS_CLI, "Status-Client" },
+ { RADCMD_DISCON_REQ, "Disconnect-Request" },
+ { RADCMD_DISCON_ACK, "Disconnect-ACK" },
+ { RADCMD_DISCON_NAK, "Disconnect-NAK" },
+ { RADCMD_COA_REQ, "CoA-Request" },
+ { RADCMD_COA_ACK, "CoA-ACK" },
+ { RADCMD_COA_NAK, "CoA-NAK" },
+ { RADCMD_RESERVED, "Reserved" },
+ { 0, NULL}
+};
+
+/********************************/
+/* Begin Radius Attribute types */
+/********************************/
+#define SERV_TYPE 6
+#define FRM_IPADDR 8
+#define LOG_IPHOST 14
+#define LOG_SERVICE 15
+#define FRM_IPX 23
+#define SESSION_TIMEOUT 27
+#define IDLE_TIMEOUT 28
+#define FRM_ATALK_LINK 37
+#define FRM_ATALK_NETWORK 38
+
+#define ACCT_DELAY 41
+#define ACCT_SESSION_TIME 46
+
+#define EGRESS_VLAN_ID 56
+#define EGRESS_VLAN_NAME 58
+
+#define TUNNEL_TYPE 64
+#define TUNNEL_MEDIUM 65
+#define TUNNEL_CLIENT_END 66
+#define TUNNEL_SERVER_END 67
+#define TUNNEL_PASS 69
+
+#define ARAP_PASS 70
+#define ARAP_FEATURES 71
+
+#define EAP_MESSAGE 79
+
+#define TUNNEL_PRIV_GROUP 81
+#define TUNNEL_ASSIGN_ID 82
+#define TUNNEL_PREFERENCE 83
+
+#define ARAP_CHALLENGE_RESP 84
+#define ACCT_INT_INTERVAL 85
+
+#define TUNNEL_CLIENT_AUTH 90
+#define TUNNEL_SERVER_AUTH 91
+
+#define ERROR_CAUSE 101
+/********************************/
+/* End Radius Attribute types */
+/********************************/
+
+#define RFC4675_TAGGED 0x31
+#define RFC4675_UNTAGGED 0x32
+
+static const struct tok rfc4675_tagged[] = {
+ { RFC4675_TAGGED, "Tagged" },
+ { RFC4675_UNTAGGED, "Untagged" },
+ { 0, NULL}
+};
+
+
+static void print_attr_string(netdissect_options *, const u_char *, u_int, u_short );
+static void print_attr_num(netdissect_options *, const u_char *, u_int, u_short );
+static void print_vendor_attr(netdissect_options *, const u_char *, u_int, u_short );
+static void print_attr_address(netdissect_options *, const u_char *, u_int, u_short);
+static void print_attr_address6(netdissect_options *, const u_char *, u_int, u_short);
+static void print_attr_netmask6(netdissect_options *, const u_char *, u_int, u_short);
+static void print_attr_mip6_home_link_prefix(netdissect_options *, const u_char *, u_int, u_short);
+static void print_attr_operator_name(netdissect_options *, const u_char *, u_int, u_short);
+static void print_attr_location_information(netdissect_options *, const u_char *, u_int, u_short);
+static void print_attr_location_data(netdissect_options *, const u_char *, u_int, u_short);
+static void print_basic_location_policy_rules(netdissect_options *, const u_char *, u_int, u_short);
+static void print_attr_time(netdissect_options *, const u_char *, u_int, u_short);
+static void print_attr_vector64(netdissect_options *, register const u_char *, u_int, u_short);
+static void print_attr_strange(netdissect_options *, const u_char *, u_int, u_short);
+
+
+struct radius_hdr { nd_uint8_t code; /* Radius packet code */
+ nd_uint8_t id; /* Radius packet id */
+ nd_uint16_t len; /* Radius total length */
+ nd_byte auth[16]; /* Authenticator */
+ };
+
+#define MIN_RADIUS_LEN 20
+
+struct radius_attr { nd_uint8_t type; /* Attribute type */
+ nd_uint8_t len; /* Attribute length */
+ };
+
+
+/* Service-Type Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-4 */
+static const char *serv_type[]={ NULL,
+ "Login",
+ "Framed",
+ "Callback Login",
+ "Callback Framed",
+ "Outbound",
+ "Administrative",
+ "NAS Prompt",
+ "Authenticate Only",
+ "Callback NAS Prompt",
+ /* ^ [0, 9] ^ */
+ "Call Check",
+ "Callback Administrative",
+ "Voice",
+ "Fax",
+ "Modem Relay",
+ "IAPP-Register",
+ "IAPP-AP-Check",
+ "Authorize Only",
+ "Framed-Management",
+ "Additional-Authorization",
+ /* ^ [10, 19] ^ */
+ };
+
+/* Framed-Protocol Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-5 */
+static const char *frm_proto[]={ NULL,
+ "PPP",
+ "SLIP",
+ "ARAP",
+ "Gandalf proprietary",
+ "Xylogics IPX/SLIP",
+ "X.75 Synchronous",
+ "GPRS PDP Context",
+ };
+
+/* Framed-Routing Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-6 */
+static const char *frm_routing[]={ "None",
+ "Send",
+ "Listen",
+ "Send&Listen",
+ };
+
+/* Framed-Compression Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-7 */
+static const char *frm_comp[]={ "None",
+ "VJ TCP/IP",
+ "IPX",
+ "Stac-LZS",
+ };
+
+/* Login-Service Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-8 */
+static const char *login_serv[]={ "Telnet",
+ "Rlogin",
+ "TCP Clear",
+ "PortMaster(proprietary)",
+ "LAT",
+ "X.25-PAD",
+ "X.25-T3POS",
+ "Unassigned",
+ "TCP Clear Quiet",
+ };
+
+
+/* Termination-Action Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-9 */
+static const char *term_action[]={ "Default",
+ "RADIUS-Request",
+ };
+
+/* Ingress-Filters Attribute standard values */
+static const char *ingress_filters[]={ NULL,
+ "Enabled",
+ "Disabled",
+ };
+
+/* NAS-Port-Type Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-13 */
+static const char *nas_port_type[]={ "Async",
+ "Sync",
+ "ISDN Sync",
+ "ISDN Async V.120",
+ "ISDN Async V.110",
+ "Virtual",
+ "PIAFS",
+ "HDLC Clear Channel",
+ "X.25",
+ "X.75",
+ /* ^ [0, 9] ^ */
+ "G.3 Fax",
+ "SDSL",
+ "ADSL-CAP",
+ "ADSL-DMT",
+ "ISDN-DSL",
+ "Ethernet",
+ "xDSL",
+ "Cable",
+ "Wireless - Other",
+ "Wireless - IEEE 802.11",
+ /* ^ [10, 19] ^ */
+ "Token-Ring",
+ "FDDI",
+ "Wireless - CDMA200",
+ "Wireless - UMTS",
+ "Wireless - 1X-EV",
+ "IAPP",
+ "FTTP",
+ "Wireless - IEEE 802.16",
+ "Wireless - IEEE 802.20",
+ "Wireless - IEEE 802.22",
+ /* ^ [20, 29] ^ */
+ "PPPoA",
+ "PPPoEoA",
+ "PPPoEoE",
+ "PPPoEoVLAN",
+ "PPPoEoQinQ",
+ "xPON",
+ "Wireless - XGP",
+ "WiMAX Pre-Release 8 IWK Function",
+ "WIMAX-WIFI-IWK",
+ "WIMAX-SFF",
+ /* ^ [30, 39] ^ */
+ "WIMAX-HA-LMA",
+ "WIMAX-DHCP",
+ "WIMAX-LBS",
+ "WIMAX-WVS",
+ };
+
+/* Acct-Status-Type Accounting Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-10 */
+static const char *acct_status[]={ NULL,
+ "Start",
+ "Stop",
+ "Interim-Update",
+ "Unassigned",
+ "Unassigned",
+ "Unassigned",
+ "Accounting-On",
+ "Accounting-Off",
+ "Tunnel-Start",
+ /* ^ [0, 9] ^ */
+ "Tunnel-Stop",
+ "Tunnel-Reject",
+ "Tunnel-Link-Start",
+ "Tunnel-Link-Stop",
+ "Tunnel-Link-Reject",
+ "Failed",
+ };
+
+/* Acct-Authentic Accounting Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-11 */
+static const char *acct_auth[]={ NULL,
+ "RADIUS",
+ "Local",
+ "Remote",
+ "Diameter",
+ };
+
+/* Acct-Terminate-Cause Accounting Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-12 */
+static const char *acct_term[]={ NULL,
+ "User Request",
+ "Lost Carrier",
+ "Lost Service",
+ "Idle Timeout",
+ "Session Timeout",
+ "Admin Reset",
+ "Admin Reboot",
+ "Port Error",
+ "NAS Error",
+ /* ^ [0, 9] ^ */
+ "NAS Request",
+ "NAS Reboot",
+ "Port Unneeded",
+ "Port Preempted",
+ "Port Suspended",
+ "Service Unavailable",
+ "Callback",
+ "User Error",
+ "Host Request",
+ "Supplicant Restart",
+ /* ^ [10, 19] ^ */
+ "Reauthentication Failure",
+ "Port Reinitialized",
+ "Port Administratively Disabled",
+ "Lost Power",
+ };
+
+/* Tunnel-Type Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-14 */
+static const char *tunnel_type[]={ NULL,
+ "PPTP",
+ "L2F",
+ "L2TP",
+ "ATMP",
+ "VTP",
+ "AH",
+ "IP-IP",
+ "MIN-IP-IP",
+ "ESP",
+ /* ^ [0, 9] ^ */
+ "GRE",
+ "DVS",
+ "IP-in-IP Tunneling",
+ "VLAN",
+ };
+
+/* Tunnel-Medium-Type Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-15 */
+static const char *tunnel_medium[]={ NULL,
+ "IPv4",
+ "IPv6",
+ "NSAP",
+ "HDLC",
+ "BBN 1822",
+ "802",
+ "E.163",
+ "E.164",
+ "F.69",
+ /* ^ [0, 9] ^ */
+ "X.121",
+ "IPX",
+ "Appletalk",
+ "Decnet IV",
+ "Banyan Vines",
+ "E.164 with NSAP subaddress",
+ };
+
+/* ARAP-Zone-Access Attribute standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-16 */
+static const char *arap_zone[]={ NULL,
+ "Only access to dfl zone",
+ "Use zone filter inc.",
+ "Not used",
+ "Use zone filter exc.",
+ };
+
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-17 */
+static const char *prompt[]={ "No Echo",
+ "Echo",
+ };
+
+/* Error-Cause standard values */
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-18 */
+#define ERROR_CAUSE_RESIDUAL_CONTEXT_REMOVED 201
+#define ERROR_CAUSE_INVALID_EAP_PACKET 202
+#define ERROR_CAUSE_UNSUPPORTED_ATTRIBUTE 401
+#define ERROR_CAUSE_MISSING_ATTRIBUTE 402
+#define ERROR_CAUSE_NAS_IDENTIFICATION_MISMATCH 403
+#define ERROR_CAUSE_INVALID_REQUEST 404
+#define ERROR_CAUSE_UNSUPPORTED_SERVICE 405
+#define ERROR_CAUSE_UNSUPPORTED_EXTENSION 406
+#define ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE 407
+#define ERROR_CAUSE_ADMINISTRATIVELY_PROHIBITED 501
+#define ERROR_CAUSE_PROXY_REQUEST_NOT_ROUTABLE 502
+#define ERROR_CAUSE_SESSION_CONTEXT_NOT_FOUND 503
+#define ERROR_CAUSE_SESSION_CONTEXT_NOT_REMOVABLE 504
+#define ERROR_CAUSE_PROXY_PROCESSING_ERROR 505
+#define ERROR_CAUSE_RESOURCES_UNAVAILABLE 506
+#define ERROR_CAUSE_REQUEST_INITIATED 507
+#define ERROR_CAUSE_MULTIPLE_SESSION_SELECTION_UNSUPPORTED 508
+#define ERROR_CAUSE_LOCATION_INFO_REQUIRED 509
+static const struct tok errorcausetype[] = {
+ { ERROR_CAUSE_RESIDUAL_CONTEXT_REMOVED, "Residual Session Context Removed" },
+ { ERROR_CAUSE_INVALID_EAP_PACKET, "Invalid EAP Packet (Ignored)" },
+ { ERROR_CAUSE_UNSUPPORTED_ATTRIBUTE, "Unsupported Attribute" },
+ { ERROR_CAUSE_MISSING_ATTRIBUTE, "Missing Attribute" },
+ { ERROR_CAUSE_NAS_IDENTIFICATION_MISMATCH, "NAS Identification Mismatch" },
+ { ERROR_CAUSE_INVALID_REQUEST, "Invalid Request" },
+ { ERROR_CAUSE_UNSUPPORTED_SERVICE, "Unsupported Service" },
+ { ERROR_CAUSE_UNSUPPORTED_EXTENSION, "Unsupported Extension" },
+ { ERROR_CAUSE_INVALID_ATTRIBUTE_VALUE, "Invalid Attribute Value" },
+ { ERROR_CAUSE_ADMINISTRATIVELY_PROHIBITED, "Administratively Prohibited" },
+ { ERROR_CAUSE_PROXY_REQUEST_NOT_ROUTABLE, "Request Not Routable (Proxy)" },
+ { ERROR_CAUSE_SESSION_CONTEXT_NOT_FOUND, "Session Context Not Found" },
+ { ERROR_CAUSE_SESSION_CONTEXT_NOT_REMOVABLE, "Session Context Not Removable" },
+ { ERROR_CAUSE_PROXY_PROCESSING_ERROR, "Other Proxy Processing Error" },
+ { ERROR_CAUSE_RESOURCES_UNAVAILABLE, "Resources Unavailable" },
+ { ERROR_CAUSE_REQUEST_INITIATED, "Request Initiated" },
+ { ERROR_CAUSE_MULTIPLE_SESSION_SELECTION_UNSUPPORTED, "Multiple Session Selection Unsupported" },
+ { ERROR_CAUSE_LOCATION_INFO_REQUIRED, "Location Info Required" },
+ { 0, NULL }
+ };
+
+/* MIP6-Feature-Vector standard values */
+/* https://www.iana.org/assignments/aaa-parameters/aaa-parameters.xhtml */
+#define MIP6_INTEGRATED 0x0000000000000001
+#define LOCAL_HOME_AGENT_ASSIGNMENT 0x0000000000000002
+#define PMIP6_SUPPORTED 0x0000010000000000
+#define IP4_HOA_SUPPORTED 0x0000020000000000
+#define LOCAL_MAG_ROUTING_SUPPORTED 0x0000040000000000
+#define ASSIGN_LOCAL_IP 0x0000080000000000
+#define MIP4_SUPPORTED 0x0000100000000000
+#define OPTIMIZED_IDLE_MODE_MOBILITY 0x0000200000000000
+#define GTPv2_SUPPORTED 0x0000400000000000
+#define IP4_TRANSPORT_SUPPORTED 0x0000800000000000
+#define IP4_HOA_ONLY_SUPPORTED 0x0001000000000000
+#define INTER_MAG_ROUTING_SUPPORTED 0x0002000000000000
+static const struct mip6_feature_vector {
+ uint64_t v;
+ const char *s;
+ } mip6_feature_vector[] = {
+ { MIP6_INTEGRATED, "MIP6_INTEGRATED" },
+ { LOCAL_HOME_AGENT_ASSIGNMENT, "LOCAL_HOME_AGENT_ASSIGNMENT" },
+ { PMIP6_SUPPORTED, "PMIP6_SUPPORTED" },
+ { IP4_HOA_SUPPORTED, "IP4_HOA_SUPPORTED" },
+ { LOCAL_MAG_ROUTING_SUPPORTED, "LOCAL_MAG_ROUTING_SUPPORTED" },
+ { ASSIGN_LOCAL_IP, "ASSIGN_LOCAL_IP" },
+ { MIP4_SUPPORTED, "MIP4_SUPPORTED" },
+ { OPTIMIZED_IDLE_MODE_MOBILITY, "OPTIMIZED_IDLE_MODE_MOBILITY" },
+ { GTPv2_SUPPORTED, "GTPv2_SUPPORTED" },
+ { IP4_TRANSPORT_SUPPORTED, "IP4_TRANSPORT_SUPPORTED" },
+ { IP4_HOA_ONLY_SUPPORTED, "IP4_HOA_ONLY_SUPPORTED" },
+ { INTER_MAG_ROUTING_SUPPORTED, "INTER_MAG_ROUTING_SUPPORTED" },
+ };
+
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-19 */
+#define OPERATOR_NAME_TADIG 0x30
+#define OPERATOR_NAME_REALM 0x31
+#define OPERATOR_NAME_E212 0x32
+#define OPERATOR_NAME_ICC 0x33
+static const struct tok operator_name_vector[] = {
+ { OPERATOR_NAME_TADIG, "TADIG" },
+ { OPERATOR_NAME_REALM, "REALM" },
+ { OPERATOR_NAME_E212, "E212" },
+ { OPERATOR_NAME_ICC, "ICC" },
+ { 0, NULL }
+ };
+
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-20 */
+#define LOCATION_INFORMATION_CODE_CIVIC 0
+#define LOCATION_INFORMATION_CODE_GEOSPATIAL 1
+static const struct tok location_information_code_vector[] = {
+ { LOCATION_INFORMATION_CODE_CIVIC , "Civic" },
+ { LOCATION_INFORMATION_CODE_GEOSPATIAL, "Geospatial" },
+ { 0, NULL }
+ };
+
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-21 */
+#define LOCATION_INFORMATION_ENTITY_USER 0
+#define LOCATION_INFORMATION_ENTITY_RADIUS 1
+static const struct tok location_information_entity_vector[] = {
+ { LOCATION_INFORMATION_ENTITY_USER, "User" },
+ { LOCATION_INFORMATION_ENTITY_RADIUS, "RADIUS" },
+ { 0, NULL }
+ };
+
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-22 */
+static const struct tok blpr_bm[] = {
+ { 0x0001, "MBZ-15" },
+ { 0x0002, "MBZ-14" },
+ { 0x0004, "MBZ-13" },
+ { 0x0008, "MBZ-12" },
+ { 0x0010, "MBZ-11" },
+ { 0x0020, "MBZ-10" },
+ { 0x0040, "MBZ-9" },
+ { 0x0080, "MBZ-8" },
+ { 0x0100, "MBZ-7" },
+ { 0x0200, "MBZ-6" },
+ { 0x0400, "MBZ-5" },
+ { 0x0800, "MBZ-4" },
+ { 0x1000, "MBZ-3" },
+ { 0x2000, "MBZ-2" },
+ { 0x4000, "MBZ-1" },
+ { 0x8000, "Retransmission Allowed" },
+ { 0, NULL }
+ };
+
+/* https://www.iana.org/assignments/radius-types/radius-types.xhtml#radius-types-2 */
+static const struct attrtype {
+ const char *name; /* Attribute name */
+ const char **subtypes; /* Standard Values (if any) */
+ u_char siz_subtypes; /* Size of total standard values */
+ u_char first_subtype; /* First standard value is 0 or 1 */
+ void (*print_func)(netdissect_options *, const u_char *, u_int, u_short);
+ } attr_type[]=
+ {
+ { NULL, NULL, 0, 0, NULL },
+ { "User-Name", NULL, 0, 0, print_attr_string },
+ { "User-Password", NULL, 0, 0, NULL },
+ { "CHAP-Password", NULL, 0, 0, NULL },
+ { "NAS-IP-Address", NULL, 0, 0, print_attr_address },
+ { "NAS-Port", NULL, 0, 0, print_attr_num },
+ { "Service-Type", serv_type, TAM_SIZE(serv_type)-1, 1, print_attr_num },
+ { "Framed-Protocol", frm_proto, TAM_SIZE(frm_proto)-1, 1, print_attr_num },
+ { "Framed-IP-Address", NULL, 0, 0, print_attr_address },
+ { "Framed-IP-Netmask", NULL, 0, 0, print_attr_address },
+ /* ^ [0, 9] ^ */
+ { "Framed-Routing", frm_routing, TAM_SIZE(frm_routing), 0, print_attr_num },
+ { "Filter-Id", NULL, 0, 0, print_attr_string },
+ { "Framed-MTU", NULL, 0, 0, print_attr_num },
+ { "Framed-Compression", frm_comp, TAM_SIZE(frm_comp), 0, print_attr_num },
+ { "Login-IP-Host", NULL, 0, 0, print_attr_address },
+ { "Login-Service", login_serv, TAM_SIZE(login_serv), 0, print_attr_num },
+ { "Login-TCP-Port", NULL, 0, 0, print_attr_num },
+ { "Unassigned", NULL, 0, 0, NULL }, /*17*/
+ { "Reply-Message", NULL, 0, 0, print_attr_string },
+ { "Callback-Number", NULL, 0, 0, print_attr_string },
+ /* ^ [10, 19] ^ */
+ { "Callback-Id", NULL, 0, 0, print_attr_string },
+ { "Unassigned", NULL, 0, 0, NULL }, /*21*/
+ { "Framed-Route", NULL, 0, 0, print_attr_string },
+ { "Framed-IPX-Network", NULL, 0, 0, print_attr_num },
+ { "State", NULL, 0, 0, print_attr_string },
+ { "Class", NULL, 0, 0, print_attr_string },
+ { "Vendor-Specific", NULL, 0, 0, print_vendor_attr },
+ { "Session-Timeout", NULL, 0, 0, print_attr_num },
+ { "Idle-Timeout", NULL, 0, 0, print_attr_num },
+ { "Termination-Action", term_action, TAM_SIZE(term_action), 0, print_attr_num },
+ /* ^ [20, 29] ^ */
+ { "Called-Station-Id", NULL, 0, 0, print_attr_string },
+ { "Calling-Station-Id", NULL, 0, 0, print_attr_string },
+ { "NAS-Identifier", NULL, 0, 0, print_attr_string },
+ { "Proxy-State", NULL, 0, 0, print_attr_string },
+ { "Login-LAT-Service", NULL, 0, 0, print_attr_string },
+ { "Login-LAT-Node", NULL, 0, 0, print_attr_string },
+ { "Login-LAT-Group", NULL, 0, 0, print_attr_string },
+ { "Framed-AppleTalk-Link", NULL, 0, 0, print_attr_num },
+ { "Framed-AppleTalk-Network", NULL, 0, 0, print_attr_num },
+ { "Framed-AppleTalk-Zone", NULL, 0, 0, print_attr_string },
+ /* ^ [30, 39] ^ */
+ { "Acct-Status-Type", acct_status, TAM_SIZE(acct_status)-1, 1, print_attr_num },
+ { "Acct-Delay-Time", NULL, 0, 0, print_attr_num },
+ { "Acct-Input-Octets", NULL, 0, 0, print_attr_num },
+ { "Acct-Output-Octets", NULL, 0, 0, print_attr_num },
+ { "Acct-Session-Id", NULL, 0, 0, print_attr_string },
+ { "Acct-Authentic", acct_auth, TAM_SIZE(acct_auth)-1, 1, print_attr_num },
+ { "Acct-Session-Time", NULL, 0, 0, print_attr_num },
+ { "Acct-Input-Packets", NULL, 0, 0, print_attr_num },
+ { "Acct-Output-Packets", NULL, 0, 0, print_attr_num },
+ { "Acct-Terminate-Cause", acct_term, TAM_SIZE(acct_term)-1, 1, print_attr_num },
+ /* ^ [40, 49] ^ */
+ { "Acct-Multi-Session-Id", NULL, 0, 0, print_attr_string },
+ { "Acct-Link-Count", NULL, 0, 0, print_attr_num },
+ { "Acct-Input-Gigawords", NULL, 0, 0, print_attr_num },
+ { "Acct-Output-Gigawords", NULL, 0, 0, print_attr_num },
+ { "Unassigned", NULL, 0, 0, NULL }, /*54*/
+ { "Event-Timestamp", NULL, 0, 0, print_attr_time },
+ { "Egress-VLANID", NULL, 0, 0, print_attr_num },
+ { "Ingress-Filters", ingress_filters, TAM_SIZE(ingress_filters)-1, 1, print_attr_num },
+ { "Egress-VLAN-Name", NULL, 0, 0, print_attr_string },
+ { "User-Priority-Table", NULL, 0, 0, NULL },
+ /* ^ [50, 59] ^ */
+ { "CHAP-Challenge", NULL, 0, 0, print_attr_string },
+ { "NAS-Port-Type", nas_port_type, TAM_SIZE(nas_port_type), 0, print_attr_num },
+ { "Port-Limit", NULL, 0, 0, print_attr_num },
+ { "Login-LAT-Port", NULL, 0, 0, print_attr_string }, /*63*/
+ { "Tunnel-Type", tunnel_type, TAM_SIZE(tunnel_type)-1, 1, print_attr_num },
+ { "Tunnel-Medium-Type", tunnel_medium, TAM_SIZE(tunnel_medium)-1, 1, print_attr_num },
+ { "Tunnel-Client-Endpoint", NULL, 0, 0, print_attr_string },
+ { "Tunnel-Server-Endpoint", NULL, 0, 0, print_attr_string },
+ { "Acct-Tunnel-Connection", NULL, 0, 0, print_attr_string },
+ { "Tunnel-Password", NULL, 0, 0, print_attr_string },
+ /* ^ [60, 69] ^ */
+ { "ARAP-Password", NULL, 0, 0, print_attr_strange },
+ { "ARAP-Features", NULL, 0, 0, print_attr_strange },
+ { "ARAP-Zone-Access", arap_zone, TAM_SIZE(arap_zone)-1, 1, print_attr_num }, /*72*/
+ { "ARAP-Security", NULL, 0, 0, print_attr_string },
+ { "ARAP-Security-Data", NULL, 0, 0, print_attr_string },
+ { "Password-Retry", NULL, 0, 0, print_attr_num },
+ { "Prompt", prompt, TAM_SIZE(prompt), 0, print_attr_num },
+ { "Connect-Info", NULL, 0, 0, print_attr_string },
+ { "Configuration-Token", NULL, 0, 0, print_attr_string },
+ { "EAP-Message", NULL, 0, 0, print_attr_string },
+ /* ^ [70, 79] ^ */
+ { "Message-Authenticator", NULL, 0, 0, print_attr_string }, /*80*/
+ { "Tunnel-Private-Group-ID", NULL, 0, 0, print_attr_string },
+ { "Tunnel-Assignment-ID", NULL, 0, 0, print_attr_string },
+ { "Tunnel-Preference", NULL, 0, 0, print_attr_num },
+ { "ARAP-Challenge-Response", NULL, 0, 0, print_attr_strange },
+ { "Acct-Interim-Interval", NULL, 0, 0, print_attr_num },
+ { "Acct-Tunnel-Packets-Lost", NULL, 0, 0, print_attr_num }, /*86*/
+ { "NAS-Port-Id", NULL, 0, 0, print_attr_string },
+ { "Framed-Pool", NULL, 0, 0, print_attr_string },
+ { "CUI", NULL, 0, 0, print_attr_string },
+ /* ^ [80, 89] ^ */
+ { "Tunnel-Client-Auth-ID", NULL, 0, 0, print_attr_string },
+ { "Tunnel-Server-Auth-ID", NULL, 0, 0, print_attr_string },
+ { "NAS-Filter-Rule", NULL, 0, 0, print_attr_string },
+ { "Unassigned", NULL, 0, 0, NULL }, /*93*/
+ { "Originating-Line-Info", NULL, 0, 0, NULL },
+ { "NAS-IPv6-Address", NULL, 0, 0, print_attr_address6 },
+ { "Framed-Interface-ID", NULL, 0, 0, NULL },
+ { "Framed-IPv6-Prefix", NULL, 0, 0, print_attr_netmask6 },
+ { "Login-IPv6-Host", NULL, 0, 0, print_attr_address6 },
+ { "Framed-IPv6-Route", NULL, 0, 0, print_attr_string },
+ /* ^ [90, 99] ^ */
+ { "Framed-IPv6-Pool", NULL, 0, 0, print_attr_string },
+ { "Error-Cause", NULL, 0, 0, print_attr_strange },
+ { "EAP-Key-Name", NULL, 0, 0, NULL },
+ { "Digest-Response", NULL, 0, 0, print_attr_string },
+ { "Digest-Realm", NULL, 0, 0, print_attr_string },
+ { "Digest-Nonce", NULL, 0, 0, print_attr_string },
+ { "Digest-Response-Auth", NULL, 0, 0, print_attr_string },
+ { "Digest-Nextnonce", NULL, 0, 0, print_attr_string },
+ { "Digest-Method", NULL, 0, 0, print_attr_string },
+ { "Digest-URI", NULL, 0, 0, print_attr_string },
+ /* ^ [100, 109] ^ */
+ { "Digest-Qop", NULL, 0, 0, print_attr_string },
+ { "Digest-Algorithm", NULL, 0, 0, print_attr_string },
+ { "Digest-Entity-Body-Hash", NULL, 0, 0, print_attr_string },
+ { "Digest-CNonce", NULL, 0, 0, print_attr_string },
+ { "Digest-Nonce-Count", NULL, 0, 0, print_attr_string },
+ { "Digest-Username", NULL, 0, 0, print_attr_string },
+ { "Digest-Opaque", NULL, 0, 0, print_attr_string },
+ { "Digest-Auth-Param", NULL, 0, 0, print_attr_string },
+ { "Digest-AKA-Auts", NULL, 0, 0, print_attr_string },
+ { "Digest-Domain", NULL, 0, 0, print_attr_string },
+ /* ^ [110, 119] ^ */
+ { "Digest-Stale", NULL, 0, 0, print_attr_string },
+ { "Digest-HA1", NULL, 0, 0, print_attr_string },
+ { "SIP-AOR", NULL, 0, 0, print_attr_string },
+ { "Delegated-IPv6-Prefix", NULL, 0, 0, print_attr_netmask6 },
+ { "MIP6-Feature-Vector", NULL, 0, 0, print_attr_vector64 },
+ { "MIP6-Home-Link-Prefix", NULL, 0, 0, print_attr_mip6_home_link_prefix },
+ { "Operator-Name", NULL, 0, 0, print_attr_operator_name },
+ { "Location-Information", NULL, 0, 0, print_attr_location_information },
+ { "Location-Data", NULL, 0, 0, print_attr_location_data },
+ { "Basic-Location-Policy-Rules", NULL, 0, 0, print_basic_location_policy_rules }
+ /* ^ [120, 129] ^ */
+ };
+
+
+/*****************************/
+/* Print an attribute string */
+/* value pointed by 'data' */
+/* and 'length' size. */
+/*****************************/
+/* Returns nothing. */
+/*****************************/
+static void
+print_attr_string(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code)
+{
+ u_int i;
+
+ ND_TCHECK_LEN(data, length);
+
+ switch(attr_code)
+ {
+ case TUNNEL_PASS:
+ if (length < 3)
+ goto trunc;
+ if (GET_U_1(data) && (GET_U_1(data) <= 0x1F))
+ ND_PRINT("Tag[%u] ", GET_U_1(data));
+ else
+ ND_PRINT("Tag[Unused] ");
+ data++;
+ length--;
+ ND_PRINT("Salt %u ", GET_BE_U_2(data));
+ data+=2;
+ length-=2;
+ break;
+ case TUNNEL_CLIENT_END:
+ case TUNNEL_SERVER_END:
+ case TUNNEL_PRIV_GROUP:
+ case TUNNEL_ASSIGN_ID:
+ case TUNNEL_CLIENT_AUTH:
+ case TUNNEL_SERVER_AUTH:
+ if (GET_U_1(data) <= 0x1F)
+ {
+ if (length < 1)
+ goto trunc;
+ if (GET_U_1(data))
+ ND_PRINT("Tag[%u] ", GET_U_1(data));
+ else
+ ND_PRINT("Tag[Unused] ");
+ data++;
+ length--;
+ }
+ break;
+ case EGRESS_VLAN_NAME:
+ if (length < 1)
+ goto trunc;
+ ND_PRINT("%s (0x%02x) ",
+ tok2str(rfc4675_tagged,"Unknown tag",GET_U_1(data)),
+ GET_U_1(data));
+ data++;
+ length--;
+ break;
+ case EAP_MESSAGE:
+ if (length < 1)
+ goto trunc;
+ eap_print(ndo, data, length);
+ return;
+ }
+
+ for (i=0; i < length && GET_U_1(data); i++, data++)
+ ND_PRINT("%c", ND_ASCII_ISPRINT(GET_U_1(data)) ? GET_U_1(data) : '.');
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+/*
+ * print vendor specific attributes
+ */
+static void
+print_vendor_attr(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ u_int idx;
+ u_int vendor_id;
+ u_int vendor_type;
+ u_int vendor_length;
+
+ if (length < 4)
+ goto trunc;
+ vendor_id = GET_BE_U_4(data);
+ data+=4;
+ length-=4;
+
+ ND_PRINT("Vendor: %s (%u)",
+ tok2str(smi_values,"Unknown",vendor_id),
+ vendor_id);
+
+ while (length >= 2) {
+ vendor_type = GET_U_1(data);
+ vendor_length = GET_U_1(data + 1);
+
+ if (vendor_length < 2)
+ {
+ ND_PRINT("\n\t Vendor Attribute: %u, Length: %u (bogus, must be >= 2)",
+ vendor_type,
+ vendor_length);
+ return;
+ }
+ if (vendor_length > length)
+ {
+ ND_PRINT("\n\t Vendor Attribute: %u, Length: %u (bogus, goes past end of vendor-specific attribute)",
+ vendor_type,
+ vendor_length);
+ return;
+ }
+ data+=2;
+ vendor_length-=2;
+ length-=2;
+ ND_TCHECK_LEN(data, vendor_length);
+
+ ND_PRINT("\n\t Vendor Attribute: %u, Length: %u, Value: ",
+ vendor_type,
+ vendor_length);
+ for (idx = 0; idx < vendor_length ; idx++, data++)
+ ND_PRINT("%c", ND_ASCII_ISPRINT(GET_U_1(data)) ? GET_U_1(data) : '.');
+ length-=vendor_length;
+ }
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+/******************************/
+/* Print an attribute numeric */
+/* value pointed by 'data' */
+/* and 'length' size. */
+/******************************/
+/* Returns nothing. */
+/******************************/
+static void
+print_attr_num(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code)
+{
+ uint32_t timeout;
+
+ if (length != 4)
+ {
+ ND_PRINT("ERROR: length %u != 4", length);
+ return;
+ }
+
+ /* This attribute has standard values */
+ if (attr_type[attr_code].siz_subtypes)
+ {
+ static const char **table;
+ uint32_t data_value;
+ table = attr_type[attr_code].subtypes;
+
+ if ( (attr_code == TUNNEL_TYPE) || (attr_code == TUNNEL_MEDIUM) )
+ {
+ if (!GET_U_1(data))
+ ND_PRINT("Tag[Unused] ");
+ else
+ ND_PRINT("Tag[%u] ", GET_U_1(data));
+ data++;
+ data_value = GET_BE_U_3(data);
+ }
+ else
+ {
+ data_value = GET_BE_U_4(data);
+ }
+ if ( data_value <= (uint32_t)(attr_type[attr_code].siz_subtypes - 1 +
+ attr_type[attr_code].first_subtype) &&
+ data_value >= attr_type[attr_code].first_subtype )
+ ND_PRINT("%s", table[data_value]);
+ else
+ ND_PRINT("#%u", data_value);
+ }
+ else
+ {
+ switch(attr_code) /* Be aware of special cases... */
+ {
+ case FRM_IPX:
+ if (GET_BE_U_4(data) == 0xFFFFFFFE )
+ ND_PRINT("NAS Select");
+ else
+ ND_PRINT("%u", GET_BE_U_4(data));
+ break;
+
+ case SESSION_TIMEOUT:
+ case IDLE_TIMEOUT:
+ case ACCT_DELAY:
+ case ACCT_SESSION_TIME:
+ case ACCT_INT_INTERVAL:
+ timeout = GET_BE_U_4(data);
+ if ( timeout < 60 )
+ ND_PRINT("%02d secs", timeout);
+ else
+ {
+ if ( timeout < 3600 )
+ ND_PRINT("%02d:%02d min",
+ timeout / 60, timeout % 60);
+ else
+ ND_PRINT("%02d:%02d:%02d hours",
+ timeout / 3600, (timeout % 3600) / 60,
+ timeout % 60);
+ }
+ break;
+
+ case FRM_ATALK_LINK:
+ if (GET_BE_U_4(data))
+ ND_PRINT("%u", GET_BE_U_4(data));
+ else
+ ND_PRINT("Unnumbered");
+ break;
+
+ case FRM_ATALK_NETWORK:
+ if (GET_BE_U_4(data))
+ ND_PRINT("%u", GET_BE_U_4(data));
+ else
+ ND_PRINT("NAS assigned");
+ break;
+
+ case TUNNEL_PREFERENCE:
+ if (GET_U_1(data))
+ ND_PRINT("Tag[%u] ", GET_U_1(data));
+ else
+ ND_PRINT("Tag[Unused] ");
+ data++;
+ ND_PRINT("%u", GET_BE_U_3(data));
+ break;
+
+ case EGRESS_VLAN_ID:
+ ND_PRINT("%s (0x%02x) ",
+ tok2str(rfc4675_tagged,"Unknown tag",GET_U_1(data)),
+ GET_U_1(data));
+ data++;
+ ND_PRINT("%u", GET_BE_U_3(data));
+ break;
+
+ default:
+ ND_PRINT("%u", GET_BE_U_4(data));
+ break;
+
+ } /* switch */
+
+ } /* if-else */
+}
+
+/*****************************/
+/* Print an attribute IPv4 */
+/* address value pointed by */
+/* 'data' and 'length' size. */
+/*****************************/
+/* Returns nothing. */
+/*****************************/
+static void
+print_attr_address(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code)
+{
+ if (length != 4)
+ {
+ ND_PRINT("ERROR: length %u != 4", length);
+ return;
+ }
+
+ switch(attr_code)
+ {
+ case FRM_IPADDR:
+ case LOG_IPHOST:
+ if (GET_BE_U_4(data) == 0xFFFFFFFF )
+ ND_PRINT("User Selected");
+ else
+ if (GET_BE_U_4(data) == 0xFFFFFFFE )
+ ND_PRINT("NAS Select");
+ else
+ ND_PRINT("%s",GET_IPADDR_STRING(data));
+ break;
+
+ default:
+ ND_PRINT("%s", GET_IPADDR_STRING(data));
+ break;
+ }
+}
+
+/*****************************/
+/* Print an attribute IPv6 */
+/* address value pointed by */
+/* 'data' and 'length' size. */
+/*****************************/
+/* Returns nothing. */
+/*****************************/
+static void
+print_attr_address6(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ if (length != 16)
+ {
+ ND_PRINT("ERROR: length %u != 16", length);
+ return;
+ }
+
+ ND_PRINT("%s", GET_IP6ADDR_STRING(data));
+}
+
+static void
+print_attr_netmask6(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ u_char data2[16];
+
+ if (length < 2 || length > 18)
+ {
+ ND_PRINT("ERROR: length %u not in range (2..18)", length);
+ return;
+ }
+ ND_TCHECK_LEN(data, length);
+ if (GET_U_1(data + 1) > 128)
+ {
+ ND_PRINT("ERROR: netmask %u not in range (0..128)", GET_U_1(data + 1));
+ return;
+ }
+
+ memset(data2, 0, sizeof(data2));
+ if (length > 2)
+ memcpy(data2, data+2, length-2);
+
+ ND_PRINT("%s/%u", ip6addr_string(ndo, data2), GET_U_1(data + 1));
+
+ if (GET_U_1(data + 1) > 8 * (length - 2))
+ ND_PRINT(" (inconsistent prefix length)");
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+print_attr_mip6_home_link_prefix(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ if (length != 17)
+ {
+ ND_PRINT("ERROR: length %u != 17", length);
+ return;
+ }
+ ND_TCHECK_LEN(data, length);
+ if (GET_U_1(data) > 128)
+ {
+ ND_PRINT("ERROR: netmask %u not in range (0..128)", GET_U_1(data));
+ return;
+ }
+
+ ND_PRINT("%s/%u", GET_IP6ADDR_STRING(data + 1), GET_U_1(data));
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+print_attr_operator_name(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ u_int namespace_value;
+
+ ND_TCHECK_LEN(data, length);
+ if (length < 2)
+ {
+ ND_PRINT("ERROR: length %u < 2", length);
+ return;
+ }
+ namespace_value = GET_U_1(data);
+ data++;
+ ND_PRINT("[%s] ", tok2str(operator_name_vector, "unknown namespace %u", namespace_value));
+
+ (void)nd_printn(ndo, data, length - 1, NULL);
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+print_attr_location_information(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ uint16_t index;
+ uint8_t code, entity;
+
+ ND_TCHECK_LEN(data, length);
+ if (length < 21)
+ {
+ ND_PRINT("ERROR: length %u < 21", length);
+ return;
+ }
+
+ index = GET_BE_U_2(data);
+ data += 2;
+
+ code = GET_U_1(data);
+ data++;
+
+ entity = GET_U_1(data);
+ data++;
+
+ ND_PRINT("index %u, code %s, entity %s, ",
+ index,
+ tok2str(location_information_code_vector, "Unknown (%u)", code),
+ tok2str(location_information_entity_vector, "Unknown (%u)", entity)
+ );
+
+ ND_PRINT("sighting time ");
+ p_ntp_time(ndo, (const struct l_fixedpt *)data);
+ ND_PRINT(", ");
+ data += 8;
+
+ ND_PRINT("time to live ");
+ p_ntp_time(ndo, (const struct l_fixedpt *)data);
+ ND_PRINT(", ");
+ data += 8;
+
+ ND_PRINT("method \"");
+ (void)nd_printn(ndo, data, length - 20, NULL);
+ ND_PRINT("\"");
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+print_attr_location_data(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ uint16_t index;
+
+ ND_TCHECK_LEN(data, length);
+ if (length < 3)
+ {
+ ND_PRINT("ERROR: length %u < 3", length);
+ return;
+ }
+
+ index = GET_BE_U_2(data);
+ data += 2;
+ ND_PRINT("index %u, location", index);
+
+ /* The Location field of the String field of the Location-Data attribute
+ * can have two completely different structures depending on the value of
+ * the Code field of a Location-Info attribute, which supposedly precedes
+ * the current attribute. Unfortunately, this choice of encoding makes it
+ * non-trivial to decode the Location field without preserving some state
+ * between the attributes.
+ */
+ hex_and_ascii_print(ndo, "\n\t ", data, length - 2);
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+print_basic_location_policy_rules(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ uint16_t flags;
+
+ ND_TCHECK_LEN(data, length);
+ if (length < 10)
+ {
+ ND_PRINT("ERROR: length %u < 10", length);
+ return;
+ }
+
+ flags = GET_BE_U_2(data);
+ data += 2;
+ ND_PRINT("flags [%s], ", bittok2str(blpr_bm, "none", flags));
+
+ ND_PRINT("retention expires ");
+ p_ntp_time(ndo, (const struct l_fixedpt *)data);
+ data += 8;
+
+ if (length > 10) {
+ ND_PRINT(", note well \"");
+ (void)nd_printn(ndo, data, length - 10, NULL);
+ ND_PRINT("\"");
+ }
+
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+
+/*************************************/
+/* Print an attribute of 'secs since */
+/* January 1, 1970 00:00 UTC' value */
+/* pointed by 'data' and 'length' */
+/* size. */
+/*************************************/
+/* Returns nothing. */
+/*************************************/
+static void
+print_attr_time(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code _U_)
+{
+ time_t attr_time;
+ char string[26];
+
+ if (length != 4)
+ {
+ ND_PRINT("ERROR: length %u != 4", length);
+ return;
+ }
+
+ attr_time = GET_BE_U_4(data);
+ strlcpy(string, ctime(&attr_time), sizeof(string));
+ /* Get rid of the newline */
+ string[24] = '\0';
+ ND_PRINT("%.24s", string);
+}
+
+static void
+print_attr_vector64(netdissect_options *ndo,
+ register const u_char *data, u_int length, u_short attr_code _U_)
+{
+ uint64_t data_value, i;
+ const char *sep = "";
+
+ if (length != 8)
+ {
+ ND_PRINT("ERROR: length %u != 8", length);
+ return;
+ }
+
+ ND_PRINT("[");
+
+ data_value = GET_BE_U_8(data);
+ /* Print the 64-bit field in a format similar to bittok2str(), less
+ * flagging any unknown bits. This way it should be easier to replace
+ * the custom code with a library function later.
+ */
+ for (i = 0; i < TAM_SIZE(mip6_feature_vector); i++) {
+ if (data_value & mip6_feature_vector[i].v) {
+ ND_PRINT("%s%s", sep, mip6_feature_vector[i].s);
+ sep = ", ";
+ }
+ }
+
+ ND_PRINT("]");
+}
+
+/***********************************/
+/* Print an attribute of 'strange' */
+/* data format pointed by 'data' */
+/* and 'length' size. */
+/***********************************/
+/* Returns nothing. */
+/***********************************/
+static void
+print_attr_strange(netdissect_options *ndo,
+ const u_char *data, u_int length, u_short attr_code)
+{
+ u_short len_data;
+ u_int error_cause_value;
+
+ switch(attr_code)
+ {
+ case ARAP_PASS:
+ if (length != 16)
+ {
+ ND_PRINT("ERROR: length %u != 16", length);
+ return;
+ }
+ ND_PRINT("User_challenge (");
+ ND_TCHECK_8(data);
+ len_data = 8;
+ PRINT_HEX(len_data, data);
+ ND_PRINT(") User_resp(");
+ ND_TCHECK_8(data);
+ len_data = 8;
+ PRINT_HEX(len_data, data);
+ ND_PRINT(")");
+ break;
+
+ case ARAP_FEATURES:
+ if (length != 14)
+ {
+ ND_PRINT("ERROR: length %u != 14", length);
+ return;
+ }
+ if (GET_U_1(data))
+ ND_PRINT("User can change password");
+ else
+ ND_PRINT("User cannot change password");
+ data++;
+ ND_PRINT(", Min password length: %u", GET_U_1(data));
+ data++;
+ ND_PRINT(", created at: ");
+ ND_TCHECK_4(data);
+ len_data = 4;
+ PRINT_HEX(len_data, data);
+ ND_PRINT(", expires in: ");
+ ND_TCHECK_4(data);
+ len_data = 4;
+ PRINT_HEX(len_data, data);
+ ND_PRINT(", Current Time: ");
+ ND_TCHECK_4(data);
+ len_data = 4;
+ PRINT_HEX(len_data, data);
+ break;
+
+ case ARAP_CHALLENGE_RESP:
+ if (length < 8)
+ {
+ ND_PRINT("ERROR: length %u != 8", length);
+ return;
+ }
+ ND_TCHECK_8(data);
+ len_data = 8;
+ PRINT_HEX(len_data, data);
+ break;
+
+ case ERROR_CAUSE:
+ if (length != 4)
+ {
+ ND_PRINT("Error: length %u != 4", length);
+ return;
+ }
+
+ error_cause_value = GET_BE_U_4(data);
+ ND_PRINT("Error cause %u: %s", error_cause_value, tok2str(errorcausetype, "Error-Cause %u not known", error_cause_value));
+ break;
+ }
+ return;
+
+ trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+radius_attrs_print(netdissect_options *ndo,
+ const u_char *attr, u_int length)
+{
+ const struct radius_attr *rad_attr = (const struct radius_attr *)attr;
+ const char *attr_string;
+ uint8_t type, len;
+
+ while (length > 0)
+ {
+ if (length < 2)
+ goto trunc;
+ ND_TCHECK_SIZE(rad_attr);
+
+ type = GET_U_1(rad_attr->type);
+ len = GET_U_1(rad_attr->len);
+ if (type != 0 && type < TAM_SIZE(attr_type))
+ attr_string = attr_type[type].name;
+ else
+ attr_string = "Unknown";
+
+ ND_PRINT("\n\t %s Attribute (%u), length: %u",
+ attr_string,
+ type,
+ len);
+ if (len < 2)
+ {
+ ND_PRINT(" (bogus, must be >= 2)");
+ return;
+ }
+ if (len > length)
+ {
+ ND_PRINT(" (bogus, goes past end of packet)");
+ return;
+ }
+ ND_PRINT(", Value: ");
+
+ if (type < TAM_SIZE(attr_type))
+ {
+ if (len > 2)
+ {
+ if ( attr_type[type].print_func )
+ (*attr_type[type].print_func)(
+ ndo, ((const u_char *)(rad_attr+1)),
+ len - 2, type);
+ }
+ }
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag> 1)
+ print_unknown_data(ndo, (const u_char *)rad_attr+2, "\n\t ", (len)-2);
+
+ length-=(len);
+ rad_attr = (const struct radius_attr *)( ((const char *)(rad_attr))+len);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+radius_print(netdissect_options *ndo,
+ const u_char *dat, u_int length)
+{
+ const struct radius_hdr *rad;
+ u_int len, auth_idx;
+
+ ndo->ndo_protocol = "radius";
+ ND_TCHECK_LEN(dat, MIN_RADIUS_LEN);
+ rad = (const struct radius_hdr *)dat;
+ len = GET_BE_U_2(rad->len);
+
+ if (len < MIN_RADIUS_LEN)
+ {
+ nd_print_trunc(ndo);
+ return;
+ }
+
+ if (len > length)
+ len = length;
+
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("RADIUS, %s (%u), id: 0x%02x length: %u",
+ tok2str(radius_command_values,"Unknown Command",GET_U_1(rad->code)),
+ GET_U_1(rad->code),
+ GET_U_1(rad->id),
+ len);
+ return;
+ }
+ else {
+ ND_PRINT("RADIUS, length: %u\n\t%s (%u), id: 0x%02x, Authenticator: ",
+ len,
+ tok2str(radius_command_values,"Unknown Command",GET_U_1(rad->code)),
+ GET_U_1(rad->code),
+ GET_U_1(rad->id));
+
+ for(auth_idx=0; auth_idx < 16; auth_idx++)
+ ND_PRINT("%02x", rad->auth[auth_idx]);
+ }
+
+ if (len > MIN_RADIUS_LEN)
+ radius_attrs_print(ndo, dat + MIN_RADIUS_LEN, len - MIN_RADIUS_LEN);
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-raw.c b/print-raw.c
new file mode 100644
index 0000000..9c6558f
--- /dev/null
+++ b/print-raw.c
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Raw IP printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+/*
+ * The DLT_RAW packet has no header. It contains a raw IP packet.
+ */
+
+void
+raw_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "raw";
+ ndo->ndo_ll_hdr_len += 0;
+ if (ndo->ndo_eflag)
+ ND_PRINT("ip: ");
+
+ ipN_print(ndo, p, h->len);
+}
diff --git a/print-resp.c b/print-resp.c
new file mode 100644
index 0000000..00a6e25
--- /dev/null
+++ b/print-resp.c
@@ -0,0 +1,536 @@
+/*
+ * Copyright (c) 2015 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Initial contribution by Andrew Darqui (andrew.darqui@gmail.com).
+ */
+
+/* \summary: REdis Serialization Protocol (RESP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include <limits.h>
+
+#include "extract.h"
+
+
+/*
+ * For information regarding RESP, see: https://redis.io/topics/protocol
+ */
+
+#define RESP_SIMPLE_STRING '+'
+#define RESP_ERROR '-'
+#define RESP_INTEGER ':'
+#define RESP_BULK_STRING '$'
+#define RESP_ARRAY '*'
+
+#define resp_print_empty(ndo) ND_PRINT(" empty")
+#define resp_print_null(ndo) ND_PRINT(" null")
+#define resp_print_length_too_large(ndo) ND_PRINT(" length too large")
+#define resp_print_length_negative(ndo) ND_PRINT(" length negative and not -1")
+#define resp_print_invalid(ndo) ND_PRINT(" invalid")
+
+static int resp_parse(netdissect_options *, const u_char *, int);
+static int resp_print_string_error_integer(netdissect_options *, const u_char *, int);
+static int resp_print_simple_string(netdissect_options *, const u_char *, int);
+static int resp_print_integer(netdissect_options *, const u_char *, int);
+static int resp_print_error(netdissect_options *, const u_char *, int);
+static int resp_print_bulk_string(netdissect_options *, const u_char *, int);
+static int resp_print_bulk_array(netdissect_options *, const u_char *, int);
+static int resp_print_inline(netdissect_options *, const u_char *, int);
+static int resp_get_length(netdissect_options *, const u_char *, int, const u_char **);
+
+#define LCHECK2(_tot_len, _len) \
+ { \
+ if (_tot_len < _len) \
+ goto trunc; \
+ }
+
+#define LCHECK(_tot_len) LCHECK2(_tot_len, 1)
+
+/*
+ * FIND_CRLF:
+ * Attempts to move our 'ptr' forward until a \r\n is found,
+ * while also making sure we don't exceed the buffer '_len'
+ * or go past the end of the captured data.
+ * If we exceed or go past the end of the captured data,
+ * jump to trunc.
+ */
+#define FIND_CRLF(_ptr, _len) \
+ for (;;) { \
+ LCHECK2(_len, 2); \
+ ND_TCHECK_2(_ptr); \
+ if (GET_U_1(_ptr) == '\r' && \
+ GET_U_1(_ptr+1) == '\n') \
+ break; \
+ _ptr++; \
+ _len--; \
+ }
+
+/*
+ * CONSUME_CRLF
+ * Consume a CRLF that we've just found.
+ */
+#define CONSUME_CRLF(_ptr, _len) \
+ _ptr += 2; \
+ _len -= 2;
+
+/*
+ * FIND_CR_OR_LF
+ * Attempts to move our '_ptr' forward until a \r or \n is found,
+ * while also making sure we don't exceed the buffer '_len'
+ * or go past the end of the captured data.
+ * If we exceed or go past the end of the captured data,
+ * jump to trunc.
+ */
+#define FIND_CR_OR_LF(_ptr, _len) \
+ for (;;) { \
+ LCHECK(_len); \
+ if (GET_U_1(_ptr) == '\r' || \
+ GET_U_1(_ptr) == '\n') \
+ break; \
+ _ptr++; \
+ _len--; \
+ }
+
+/*
+ * CONSUME_CR_OR_LF
+ * Consume all consecutive \r and \n bytes.
+ * If we exceed '_len' or go past the end of the captured data,
+ * jump to trunc.
+ */
+#define CONSUME_CR_OR_LF(_ptr, _len) \
+ { \
+ int _found_cr_or_lf = 0; \
+ for (;;) { \
+ /* \
+ * Have we hit the end of data? \
+ */ \
+ if (_len == 0 || !ND_TTEST_1(_ptr)) {\
+ /* \
+ * Yes. Have we seen a \r \
+ * or \n? \
+ */ \
+ if (_found_cr_or_lf) { \
+ /* \
+ * Yes. Just stop. \
+ */ \
+ break; \
+ } \
+ /* \
+ * No. We ran out of packet. \
+ */ \
+ goto trunc; \
+ } \
+ if (GET_U_1(_ptr) != '\r' && \
+ GET_U_1(_ptr) != '\n') \
+ break; \
+ _found_cr_or_lf = 1; \
+ _ptr++; \
+ _len--; \
+ } \
+ }
+
+/*
+ * SKIP_OPCODE
+ * Skip over the opcode character.
+ * The opcode has already been fetched, so we know it's there, and don't
+ * need to do any checks.
+ */
+#define SKIP_OPCODE(_ptr, _tot_len) \
+ _ptr++; \
+ _tot_len--;
+
+/*
+ * GET_LENGTH
+ * Get a bulk string or array length.
+ */
+#define GET_LENGTH(_ndo, _tot_len, _ptr, _len) \
+ { \
+ const u_char *_endp; \
+ _len = resp_get_length(_ndo, _ptr, _tot_len, &_endp); \
+ _tot_len -= (_endp - _ptr); \
+ _ptr = _endp; \
+ }
+
+/*
+ * TEST_RET_LEN
+ * If ret_len is < 0, jump to the trunc tag which returns (-1)
+ * and 'bubbles up' to printing tstr. Otherwise, return ret_len.
+ */
+#define TEST_RET_LEN(rl) \
+ if (rl < 0) { goto trunc; } else { return rl; }
+
+/*
+ * TEST_RET_LEN_NORETURN
+ * If ret_len is < 0, jump to the trunc tag which returns (-1)
+ * and 'bubbles up' to printing tstr. Otherwise, continue onward.
+ */
+#define TEST_RET_LEN_NORETURN(rl) \
+ if (rl < 0) { goto trunc; }
+
+/*
+ * RESP_PRINT_SEGMENT
+ * Prints a segment in the form of: ' "<stuff>"\n"
+ * Assumes the data has already been verified as present.
+ */
+#define RESP_PRINT_SEGMENT(_ndo, _bp, _len) \
+ ND_PRINT(" \""); \
+ if (nd_printn(_ndo, _bp, _len, _ndo->ndo_snapend)) \
+ goto trunc; \
+ fn_print_char(_ndo, '"');
+
+void
+resp_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ int ret_len = 0, length_cur = length;
+
+ ndo->ndo_protocol = "resp";
+ if(!bp || length <= 0)
+ return;
+
+ ND_PRINT(": RESP");
+ while (length_cur > 0) {
+ /*
+ * This block supports redis pipelining.
+ * For example, multiple operations can be pipelined within the same string:
+ * "*2\r\n\$4\r\nINCR\r\n\$1\r\nz\r\n*2\r\n\$4\r\nINCR\r\n\$1\r\nz\r\n*2\r\n\$4\r\nINCR\r\n\$1\r\nz\r\n"
+ * or
+ * "PING\r\nPING\r\nPING\r\n"
+ * In order to handle this case, we must try and parse 'bp' until
+ * 'length' bytes have been processed or we reach a trunc condition.
+ */
+ ret_len = resp_parse(ndo, bp, length_cur);
+ TEST_RET_LEN_NORETURN(ret_len);
+ bp += ret_len;
+ length_cur -= ret_len;
+ }
+
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static int
+resp_parse(netdissect_options *ndo, const u_char *bp, int length)
+{
+ u_char op;
+ int ret_len;
+
+ LCHECK2(length, 1);
+ op = GET_U_1(bp);
+
+ /* bp now points to the op, so these routines must skip it */
+ switch(op) {
+ case RESP_SIMPLE_STRING: ret_len = resp_print_simple_string(ndo, bp, length); break;
+ case RESP_INTEGER: ret_len = resp_print_integer(ndo, bp, length); break;
+ case RESP_ERROR: ret_len = resp_print_error(ndo, bp, length); break;
+ case RESP_BULK_STRING: ret_len = resp_print_bulk_string(ndo, bp, length); break;
+ case RESP_ARRAY: ret_len = resp_print_bulk_array(ndo, bp, length); break;
+ default: ret_len = resp_print_inline(ndo, bp, length); break;
+ }
+
+ /*
+ * This gives up with a "truncated" indicator for all errors,
+ * including invalid packet errors; that's what we want, as
+ * we have to give up on further parsing in that case.
+ */
+ TEST_RET_LEN(ret_len);
+
+trunc:
+ return (-1);
+}
+
+static int
+resp_print_simple_string(netdissect_options *ndo, const u_char *bp, int length) {
+ return resp_print_string_error_integer(ndo, bp, length);
+}
+
+static int
+resp_print_integer(netdissect_options *ndo, const u_char *bp, int length) {
+ return resp_print_string_error_integer(ndo, bp, length);
+}
+
+static int
+resp_print_error(netdissect_options *ndo, const u_char *bp, int length) {
+ return resp_print_string_error_integer(ndo, bp, length);
+}
+
+static int
+resp_print_string_error_integer(netdissect_options *ndo, const u_char *bp, int length) {
+ int length_cur = length, len, ret_len;
+ const u_char *bp_ptr;
+
+ /* bp points to the op; skip it */
+ SKIP_OPCODE(bp, length_cur);
+ bp_ptr = bp;
+
+ /*
+ * bp now prints past the (+-;) opcode, so it's pointing to the first
+ * character of the string (which could be numeric).
+ * +OK\r\n
+ * -ERR ...\r\n
+ * :02912309\r\n
+ *
+ * Find the \r\n with FIND_CRLF().
+ */
+ FIND_CRLF(bp_ptr, length_cur);
+
+ /*
+ * bp_ptr points to the \r\n, so bp_ptr - bp is the length of text
+ * preceding the \r\n. That includes the opcode, so don't print
+ * that.
+ */
+ len = ND_BYTES_BETWEEN(bp_ptr, bp);
+ RESP_PRINT_SEGMENT(ndo, bp, len);
+ ret_len = 1 /*<opcode>*/ + len /*<string>*/ + 2 /*<CRLF>*/;
+
+ TEST_RET_LEN(ret_len);
+
+trunc:
+ return (-1);
+}
+
+static int
+resp_print_bulk_string(netdissect_options *ndo, const u_char *bp, int length) {
+ int length_cur = length, string_len;
+
+ /* bp points to the op; skip it */
+ SKIP_OPCODE(bp, length_cur);
+
+ /* <length>\r\n */
+ GET_LENGTH(ndo, length_cur, bp, string_len);
+
+ if (string_len >= 0) {
+ /* Byte string of length string_len, starting at bp */
+ if (string_len == 0)
+ resp_print_empty(ndo);
+ else {
+ LCHECK2(length_cur, string_len);
+ ND_TCHECK_LEN(bp, string_len);
+ RESP_PRINT_SEGMENT(ndo, bp, string_len);
+ bp += string_len;
+ length_cur -= string_len;
+ }
+
+ /*
+ * Find the \r\n at the end of the string and skip past it.
+ * XXX - report an error if the \r\n isn't immediately after
+ * the item?
+ */
+ FIND_CRLF(bp, length_cur);
+ CONSUME_CRLF(bp, length_cur);
+ } else {
+ /* null, truncated, or invalid for some reason */
+ switch(string_len) {
+ case (-1): resp_print_null(ndo); break;
+ case (-2): goto trunc;
+ case (-3): resp_print_length_too_large(ndo); break;
+ case (-4): resp_print_length_negative(ndo); break;
+ default: resp_print_invalid(ndo); break;
+ }
+ }
+
+ return (length - length_cur);
+
+trunc:
+ return (-1);
+}
+
+static int
+resp_print_bulk_array(netdissect_options *ndo, const u_char *bp, int length) {
+ u_int length_cur = length;
+ int array_len, i, ret_len;
+
+ /* bp points to the op; skip it */
+ SKIP_OPCODE(bp, length_cur);
+
+ /* <array_length>\r\n */
+ GET_LENGTH(ndo, length_cur, bp, array_len);
+
+ if (array_len > 0) {
+ /* non empty array */
+ for (i = 0; i < array_len; i++) {
+ ret_len = resp_parse(ndo, bp, length_cur);
+
+ TEST_RET_LEN_NORETURN(ret_len);
+
+ bp += ret_len;
+ length_cur -= ret_len;
+ }
+ } else {
+ /* empty, null, truncated, or invalid */
+ switch(array_len) {
+ case 0: resp_print_empty(ndo); break;
+ case (-1): resp_print_null(ndo); break;
+ case (-2): goto trunc;
+ case (-3): resp_print_length_too_large(ndo); break;
+ case (-4): resp_print_length_negative(ndo); break;
+ default: resp_print_invalid(ndo); break;
+ }
+ }
+
+ return (length - length_cur);
+
+trunc:
+ return (-1);
+}
+
+static int
+resp_print_inline(netdissect_options *ndo, const u_char *bp, int length) {
+ int length_cur = length;
+ int len;
+ const u_char *bp_ptr;
+
+ /*
+ * Inline commands are simply 'strings' followed by \r or \n or both.
+ * Redis will do its best to split/parse these strings.
+ * This feature of redis is implemented to support the ability of
+ * command parsing from telnet/nc sessions etc.
+ *
+ * <string><\r||\n||\r\n...>
+ */
+
+ /*
+ * Skip forward past any leading \r, \n, or \r\n.
+ */
+ CONSUME_CR_OR_LF(bp, length_cur);
+ bp_ptr = bp;
+
+ /*
+ * Scan forward looking for \r or \n.
+ */
+ FIND_CR_OR_LF(bp_ptr, length_cur);
+
+ /*
+ * Found it; bp_ptr points to the \r or \n, so bp_ptr - bp is the
+ * Length of the line text that precedes it. Print it.
+ */
+ len = ND_BYTES_BETWEEN(bp_ptr, bp);
+ RESP_PRINT_SEGMENT(ndo, bp, len);
+
+ /*
+ * Skip forward past the \r, \n, or \r\n.
+ */
+ CONSUME_CR_OR_LF(bp_ptr, length_cur);
+
+ /*
+ * Return the number of bytes we processed.
+ */
+ return (length - length_cur);
+
+trunc:
+ return (-1);
+}
+
+static int
+resp_get_length(netdissect_options *ndo, const u_char *bp, int len, const u_char **endp)
+{
+ int result;
+ u_char c;
+ int saw_digit;
+ int neg;
+ int too_large;
+
+ if (len == 0)
+ goto trunc;
+ too_large = 0;
+ neg = 0;
+ if (GET_U_1(bp) == '-') {
+ neg = 1;
+ bp++;
+ len--;
+ }
+ result = 0;
+ saw_digit = 0;
+
+ for (;;) {
+ if (len == 0)
+ goto trunc;
+ c = GET_U_1(bp);
+ if (!(c >= '0' && c <= '9')) {
+ if (!saw_digit) {
+ bp++;
+ goto invalid;
+ }
+ break;
+ }
+ c -= '0';
+ if (result > (INT_MAX / 10)) {
+ /* This will overflow an int when we multiply it by 10. */
+ too_large = 1;
+ } else {
+ result *= 10;
+ if (result == ((INT_MAX / 10) * 10) && c > (INT_MAX % 10)) {
+ /* This will overflow an int when we add c */
+ too_large = 1;
+ } else
+ result += c;
+ }
+ bp++;
+ len--;
+ saw_digit = 1;
+ }
+
+ /*
+ * OK, we found a non-digit character. It should be a \r, followed
+ * by a \n.
+ */
+ if (GET_U_1(bp) != '\r') {
+ bp++;
+ goto invalid;
+ }
+ bp++;
+ len--;
+ if (len == 0)
+ goto trunc;
+ if (GET_U_1(bp) != '\n') {
+ bp++;
+ goto invalid;
+ }
+ bp++;
+ len--;
+ *endp = bp;
+ if (neg) {
+ /* -1 means "null", anything else is invalid */
+ if (too_large || result != 1)
+ return (-4);
+ result = -1;
+ }
+ return (too_large ? -3 : result);
+
+trunc:
+ *endp = bp;
+ return (-2);
+
+invalid:
+ *endp = bp;
+ return (-5);
+}
diff --git a/print-rip.c b/print-rip.c
new file mode 100644
index 0000000..fca534f
--- /dev/null
+++ b/print-rip.c
@@ -0,0 +1,422 @@
+/*
+ * Copyright (c) 1989, 1990, 1991, 1993, 1994, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Routing Information Protocol (RIP) printer */
+
+/* specification: RFC 1058, RFC 2453, RFC 4822 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "af.h"
+
+
+/*
+ * RFC 1058 and RFC 2453 header of packet.
+ *
+ * 0 1 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Command (1) | Version (1) | unused |
+ * +---------------+---------------+-------------------------------+
+ */
+struct rip {
+ nd_uint8_t rip_cmd; /* request/response */
+ nd_uint8_t rip_vers; /* protocol version # */
+ nd_byte unused[2]; /* unused */
+};
+
+#define RIPCMD_REQUEST 1 /* want info */
+#define RIPCMD_RESPONSE 2 /* responding to request */
+#define RIPCMD_TRACEON 3 /* turn tracing on */
+#define RIPCMD_TRACEOFF 4 /* turn it off */
+#define RIPCMD_POLL 5 /* want info from everybody */
+#define RIPCMD_POLLENTRY 6 /* poll for entry */
+
+static const struct tok rip_cmd_values[] = {
+ { RIPCMD_REQUEST, "Request" },
+ { RIPCMD_RESPONSE, "Response" },
+ { RIPCMD_TRACEON, "Trace on" },
+ { RIPCMD_TRACEOFF, "Trace off" },
+ { RIPCMD_POLL, "Poll" },
+ { RIPCMD_POLLENTRY, "Poll Entry" },
+ { 0, NULL}
+};
+
+#define RIP_AUTHLEN 16
+#define RIP_ROUTELEN 20
+
+/*
+ * First 4 bytes of all RIPv1/RIPv2 entries.
+ */
+struct rip_entry_header {
+ nd_uint16_t rip_family;
+ nd_uint16_t rip_tag;
+};
+
+/*
+ * RFC 1058 entry.
+ *
+ * 0 1 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Address Family Identifier (2) | must be zero (2) |
+ * +-------------------------------+-------------------------------+
+ * | IP Address (4) |
+ * +---------------------------------------------------------------+
+ * | must be zero (4) |
+ * +---------------------------------------------------------------+
+ * | must be zero (4) |
+ * +---------------------------------------------------------------+
+ * | Metric (4) |
+ * +---------------------------------------------------------------+
+ */
+struct rip_netinfo_v1 {
+ nd_uint16_t rip_family;
+ nd_byte rip_mbz1[2];
+ nd_ipv4 rip_dest;
+ nd_byte rip_mbz2[4];
+ nd_byte rip_mbz3[4];
+ nd_uint32_t rip_metric; /* cost of route */
+};
+
+
+/*
+ * RFC 2453 route entry
+ *
+ * 0 1 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Address Family Identifier (2) | Route Tag (2) |
+ * +-------------------------------+-------------------------------+
+ * | IP Address (4) |
+ * +---------------------------------------------------------------+
+ * | Subnet Mask (4) |
+ * +---------------------------------------------------------------+
+ * | Next Hop (4) |
+ * +---------------------------------------------------------------+
+ * | Metric (4) |
+ * +---------------------------------------------------------------+
+ *
+ */
+
+struct rip_netinfo_v2 {
+ nd_uint16_t rip_family;
+ nd_uint16_t rip_tag;
+ nd_ipv4 rip_dest;
+ nd_uint32_t rip_dest_mask;
+ nd_ipv4 rip_router;
+ nd_uint32_t rip_metric; /* cost of route */
+};
+
+/*
+ * RFC 2453 authentication entry
+ *
+ * 0 1 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 0xFFFF | Authentication Type (2) |
+ * +-------------------------------+-------------------------------+
+ * - Authentication (16) -
+ * +---------------------------------------------------------------+
+ */
+
+struct rip_auth_v2 {
+ nd_uint16_t rip_family;
+ nd_uint16_t rip_tag;
+ nd_byte rip_auth[16];
+};
+
+/*
+ * RFC 4822 Cryptographic Authentication entry.
+ *
+ * 0 1 2 3 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | RIPv2 Packet Length | Key ID | Auth Data Len |
+ * +---------------+---------------+---------------+---------------+
+ * | Sequence Number (non-decreasing) |
+ * +---------------+---------------+---------------+---------------+
+ * | reserved must be zero |
+ * +---------------+---------------+---------------+---------------+
+ * | reserved must be zero |
+ * +---------------+---------------+---------------+---------------+
+ */
+struct rip_auth_crypto_v2 {
+ nd_uint16_t rip_packet_len;
+ nd_uint8_t rip_key_id;
+ nd_uint8_t rip_auth_data_len;
+ nd_uint32_t rip_seq_num;
+ nd_byte rip_mbz1[4];
+ nd_byte rip_mbz2[4];
+};
+
+static unsigned
+rip_entry_print_v1(netdissect_options *ndo, const u_char *p,
+ unsigned remaining)
+{
+ const struct rip_entry_header *eh = (const struct rip_entry_header *)p;
+ u_short family;
+ const struct rip_netinfo_v1 *ni = (const struct rip_netinfo_v1 *)p;
+
+ /* RFC 1058 */
+ if (remaining < RIP_ROUTELEN)
+ return (0);
+ ND_TCHECK_SIZE(ni);
+ family = GET_BE_U_2(ni->rip_family);
+ if (family != BSD_AFNUM_INET && family != 0) {
+ ND_PRINT("\n\t AFI %s, ", tok2str(bsd_af_values, "Unknown (%u)", family));
+ print_unknown_data(ndo, p + sizeof(*eh), "\n\t ", RIP_ROUTELEN - sizeof(*eh));
+ return (RIP_ROUTELEN);
+ }
+ if (GET_BE_U_2(ni->rip_mbz1) ||
+ GET_BE_U_4(ni->rip_mbz2) ||
+ GET_BE_U_4(ni->rip_mbz3)) {
+ /* MBZ fields not zero */
+ print_unknown_data(ndo, p, "\n\t ", RIP_ROUTELEN);
+ return (RIP_ROUTELEN);
+ }
+ if (family == 0) {
+ ND_PRINT("\n\t AFI 0, %s, metric: %u",
+ GET_IPADDR_STRING(ni->rip_dest),
+ GET_BE_U_4(ni->rip_metric));
+ return (RIP_ROUTELEN);
+ } /* BSD_AFNUM_INET */
+ ND_PRINT("\n\t %s, metric: %u",
+ GET_IPADDR_STRING(ni->rip_dest),
+ GET_BE_U_4(ni->rip_metric));
+ return (RIP_ROUTELEN);
+trunc:
+ return 0;
+}
+
+static unsigned
+rip_entry_print_v2(netdissect_options *ndo, const u_char *p,
+ unsigned remaining)
+{
+ const struct rip_entry_header *eh = (const struct rip_entry_header *)p;
+ u_short family;
+ const struct rip_netinfo_v2 *ni;
+
+ if (remaining < sizeof(*eh))
+ return (0);
+ ND_TCHECK_SIZE(eh);
+ family = GET_BE_U_2(eh->rip_family);
+ if (family == 0xFFFF) { /* variable-sized authentication structures */
+ uint16_t auth_type = GET_BE_U_2(eh->rip_tag);
+
+ p += sizeof(*eh);
+ remaining -= sizeof(*eh);
+ if (auth_type == 2) {
+ ND_PRINT("\n\t Simple Text Authentication data: ");
+ nd_printjnp(ndo, p, RIP_AUTHLEN);
+ } else if (auth_type == 3) {
+ const struct rip_auth_crypto_v2 *ch;
+
+ ch = (const struct rip_auth_crypto_v2 *)p;
+ ND_TCHECK_SIZE(ch);
+ if (remaining < sizeof(*ch))
+ return (0);
+ ND_PRINT("\n\t Auth header:");
+ ND_PRINT(" Packet Len %u,",
+ GET_BE_U_2(ch->rip_packet_len));
+ ND_PRINT(" Key-ID %u,", GET_U_1(ch->rip_key_id));
+ ND_PRINT(" Auth Data Len %u,",
+ GET_U_1(ch->rip_auth_data_len));
+ ND_PRINT(" SeqNo %u,", GET_BE_U_4(ch->rip_seq_num));
+ ND_PRINT(" MBZ %u,", GET_BE_U_4(ch->rip_mbz1));
+ ND_PRINT(" MBZ %u", GET_BE_U_4(ch->rip_mbz2));
+ } else if (auth_type == 1) {
+ ND_PRINT("\n\t Auth trailer:");
+ print_unknown_data(ndo, p, "\n\t ", remaining);
+ return (sizeof(*eh) + remaining); /* AT spans till the packet end */
+ } else {
+ ND_PRINT("\n\t Unknown (%u) Authentication data:",
+ auth_type);
+ print_unknown_data(ndo, p, "\n\t ", remaining);
+ return (sizeof(*eh) + remaining); /* we don't know how long this is, so we go to the packet end */
+ }
+ } else if (family != BSD_AFNUM_INET && family != 0) {
+ ND_PRINT("\n\t AFI %s", tok2str(bsd_af_values, "Unknown (%u)", family));
+ print_unknown_data(ndo, p + sizeof(*eh), "\n\t ", RIP_ROUTELEN - sizeof(*eh));
+ } else { /* BSD_AFNUM_INET or AFI 0 */
+ ni = (const struct rip_netinfo_v2 *)p;
+ ND_TCHECK_SIZE(ni);
+ if (remaining < sizeof(*ni))
+ return (0);
+ ND_PRINT("\n\t AFI %s, %15s/%-2d, tag 0x%04x, metric: %u, next-hop: ",
+ tok2str(bsd_af_values, "%u", family),
+ GET_IPADDR_STRING(ni->rip_dest),
+ mask2plen(GET_BE_U_4(ni->rip_dest_mask)),
+ GET_BE_U_2(ni->rip_tag),
+ GET_BE_U_4(ni->rip_metric));
+ if (GET_BE_U_4(ni->rip_router))
+ ND_PRINT("%s", GET_IPADDR_STRING(ni->rip_router));
+ else
+ ND_PRINT("self");
+ }
+ return (RIP_ROUTELEN);
+trunc:
+ return 0;
+}
+
+void
+rip_print(netdissect_options *ndo,
+ const u_char *dat, u_int length)
+{
+ const struct rip *rp;
+ uint8_t vers, cmd;
+ const u_char *p;
+ u_int len, routecount;
+ unsigned entry_size;
+
+ ndo->ndo_protocol = "rip";
+ if (ndo->ndo_snapend < dat) {
+ nd_print_trunc(ndo);
+ return;
+ }
+ len = ND_BYTES_AVAILABLE_AFTER(dat);
+ if (len > length)
+ len = length;
+ if (len < sizeof(*rp)) {
+ nd_print_trunc(ndo);
+ return;
+ }
+ len -= sizeof(*rp);
+
+ rp = (const struct rip *)dat;
+
+ ND_TCHECK_SIZE(rp);
+ vers = GET_U_1(rp->rip_vers);
+ ND_PRINT("%sRIPv%u",
+ (ndo->ndo_vflag >= 1) ? "\n\t" : "",
+ vers);
+
+ if (vers == 0) {
+ /*
+ * RFC 1058.
+ *
+ * XXX - RFC 1058 says
+ *
+ * 0 Datagrams whose version number is zero are to be ignored.
+ * These are from a previous version of the protocol, whose
+ * packet format was machine-specific.
+ *
+ * so perhaps we should just dump the packet, in hex.
+ */
+ print_unknown_data(ndo, (const uint8_t *)&rp->rip_cmd, "\n\t", length);
+ return;
+ }
+
+ /* dump version and lets see if we know the commands name*/
+ cmd = GET_U_1(rp->rip_cmd);
+ ND_PRINT(", %s, length: %u",
+ tok2str(rip_cmd_values, "unknown command (%u)", cmd),
+ length);
+
+ if (ndo->ndo_vflag < 1)
+ return;
+
+ switch (cmd) {
+
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ switch (vers) {
+
+ case 1:
+ routecount = length / RIP_ROUTELEN;
+ ND_PRINT(", routes: %u", routecount);
+ p = (const u_char *)(rp + 1);
+ while (len != 0) {
+ entry_size = rip_entry_print_v1(ndo, p, len);
+ if (entry_size == 0) {
+ /* Error */
+ nd_print_trunc(ndo);
+ break;
+ }
+ if (len < entry_size) {
+ ND_PRINT(" [remaining entries length %u < %u]",
+ len, entry_size);
+ nd_print_invalid(ndo);
+ break;
+ }
+ p += entry_size;
+ len -= entry_size;
+ }
+ break;
+
+ case 2:
+ routecount = length / RIP_ROUTELEN;
+ ND_PRINT(", routes: %u or less", routecount);
+ p = (const u_char *)(rp + 1);
+ while (len != 0) {
+ entry_size = rip_entry_print_v2(ndo, p, len);
+ if (entry_size == 0) {
+ /* Error */
+ nd_print_trunc(ndo);
+ break;
+ }
+ if (len < entry_size) {
+ ND_PRINT(" [remaining entries length %u < %u]",
+ len, entry_size);
+ nd_print_invalid(ndo);
+ break;
+ }
+ p += entry_size;
+ len -= entry_size;
+ }
+ break;
+
+ default:
+ ND_PRINT(", unknown version");
+ break;
+ }
+ break;
+
+ case RIPCMD_TRACEOFF:
+ case RIPCMD_POLL:
+ case RIPCMD_POLLENTRY:
+ break;
+
+ case RIPCMD_TRACEON:
+ /* fall through */
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ if (!print_unknown_data(ndo, (const uint8_t *)rp, "\n\t", length))
+ return;
+ }
+ break;
+ }
+ /* do we want to see an additionally hexdump ? */
+ if (ndo->ndo_vflag> 1) {
+ if (!print_unknown_data(ndo, (const uint8_t *)rp, "\n\t", length))
+ return;
+ }
+trunc:
+ return;
+}
diff --git a/print-ripng.c b/print-ripng.c
new file mode 100644
index 0000000..c4f4ea3
--- /dev/null
+++ b/print-ripng.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 1989, 1990, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IPv6 Routing Information Protocol (RIPng) printer */
+
+/* specification: RFC 2080 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+/*
+ * Copyright (C) 1995, 1996, 1997 and 1998 WIDE Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the project nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#define RIP6_VERSION 1
+
+#define RIP6_REQUEST 1
+#define RIP6_RESPONSE 2
+
+struct netinfo6 {
+ nd_ipv6 rip6_dest;
+ nd_uint16_t rip6_tag;
+ nd_uint8_t rip6_plen;
+ nd_uint8_t rip6_metric;
+};
+
+struct rip6 {
+ nd_uint8_t rip6_cmd;
+ nd_uint8_t rip6_vers;
+ nd_byte rip6_res1[2];
+ struct netinfo6 rip6_nets[1];
+};
+
+#define HOPCNT_INFINITY6 16
+
+static int ND_IN6_IS_ADDR_UNSPECIFIED(const nd_ipv6 *addr)
+{
+ static const nd_ipv6 in6addr_any_val = { 0 }; /* :: */
+ return (memcmp(addr, &in6addr_any_val, sizeof(*addr)) == 0);
+}
+
+static void
+rip6_entry_print(netdissect_options *ndo,
+ const struct netinfo6 *ni, const u_int print_metric)
+{
+ uint16_t tag;
+ uint8_t metric;
+
+ ND_PRINT("%s/%u", GET_IP6ADDR_STRING(ni->rip6_dest),
+ GET_U_1(ni->rip6_plen));
+ tag = GET_BE_U_2(ni->rip6_tag);
+ if (tag)
+ ND_PRINT(" [%u]", tag);
+ metric = GET_U_1(ni->rip6_metric);
+ if (metric && print_metric)
+ ND_PRINT(" (%u)", metric);
+}
+
+void
+ripng_print(netdissect_options *ndo, const u_char *dat, unsigned int length)
+{
+ const struct rip6 *rp = (const struct rip6 *)dat;
+ uint8_t cmd, vers;
+ const struct netinfo6 *ni;
+ unsigned int length_left;
+ u_int j;
+
+ ndo->ndo_protocol = "ripng";
+ vers = GET_U_1(rp->rip6_vers);
+ if (vers != RIP6_VERSION) {
+ ND_PRINT(" [vers %u]", vers);
+ goto invalid;
+ }
+ cmd = GET_U_1(rp->rip6_cmd);
+ switch (cmd) {
+
+ case RIP6_REQUEST:
+ length_left = length;
+ if (length_left < (sizeof(struct rip6) - sizeof(struct netinfo6)))
+ goto invalid;
+ length_left -= (sizeof(struct rip6) - sizeof(struct netinfo6));
+ j = length_left / sizeof(*ni);
+ if (j == 1) {
+ if (GET_U_1(rp->rip6_nets->rip6_metric) == HOPCNT_INFINITY6
+ && ND_IN6_IS_ADDR_UNSPECIFIED(&rp->rip6_nets->rip6_dest)) {
+ ND_PRINT(" ripng-req dump");
+ break;
+ }
+ }
+ if (j * sizeof(*ni) != length_left)
+ ND_PRINT(" ripng-req %u[%u]:", j, length);
+ else
+ ND_PRINT(" ripng-req %u:", j);
+ for (ni = rp->rip6_nets; length_left >= sizeof(*ni);
+ length_left -= sizeof(*ni), ++ni) {
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n\t");
+ else
+ ND_PRINT(" ");
+ rip6_entry_print(ndo, ni, FALSE);
+ }
+ if (length_left != 0)
+ goto invalid;
+ break;
+ case RIP6_RESPONSE:
+ length_left = length;
+ if (length_left < (sizeof(struct rip6) - sizeof(struct netinfo6)))
+ goto invalid;
+ length_left -= (sizeof(struct rip6) - sizeof(struct netinfo6));
+ j = length_left / sizeof(*ni);
+ if (j * sizeof(*ni) != length_left)
+ ND_PRINT(" ripng-resp %u[%u]:", j, length);
+ else
+ ND_PRINT(" ripng-resp %u:", j);
+ for (ni = rp->rip6_nets; length_left >= sizeof(*ni);
+ length_left -= sizeof(*ni), ++ni) {
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n\t");
+ else
+ ND_PRINT(" ");
+ rip6_entry_print(ndo, ni, TRUE);
+ }
+ if (length_left != 0)
+ goto invalid;
+ break;
+ default:
+ ND_PRINT(" ripng-%u ?? %u", cmd, length);
+ goto invalid;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-rpki-rtr.c b/print-rpki-rtr.c
new file mode 100644
index 0000000..36be399
--- /dev/null
+++ b/print-rpki-rtr.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 1998-2011 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* \summary: Resource Public Key Infrastructure (RPKI) to Router Protocol printer */
+
+/* specification: RFC 6810 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+
+/*
+ * RPKI/Router PDU header
+ *
+ * Here's what the PDU header looks like.
+ * The length does include the version and length fields.
+ */
+typedef struct rpki_rtr_pdu_ {
+ nd_uint8_t version; /* Version number */
+ nd_uint8_t pdu_type; /* PDU type */
+ union {
+ nd_uint16_t session_id; /* Session id */
+ nd_uint16_t error_code; /* Error code */
+ } u;
+ nd_uint32_t length;
+} rpki_rtr_pdu;
+
+/*
+ * IPv4 Prefix PDU.
+ */
+typedef struct rpki_rtr_pdu_ipv4_prefix_ {
+ rpki_rtr_pdu pdu_header;
+ nd_uint8_t flags;
+ nd_uint8_t prefix_length;
+ nd_uint8_t max_length;
+ nd_uint8_t zero;
+ nd_ipv4 prefix;
+ nd_uint32_t as;
+} rpki_rtr_pdu_ipv4_prefix;
+
+/*
+ * IPv6 Prefix PDU.
+ */
+typedef struct rpki_rtr_pdu_ipv6_prefix_ {
+ rpki_rtr_pdu pdu_header;
+ nd_uint8_t flags;
+ nd_uint8_t prefix_length;
+ nd_uint8_t max_length;
+ nd_uint8_t zero;
+ nd_ipv6 prefix;
+ nd_uint32_t as;
+} rpki_rtr_pdu_ipv6_prefix;
+
+/*
+ * Error report PDU.
+ */
+typedef struct rpki_rtr_pdu_error_report_ {
+ rpki_rtr_pdu pdu_header;
+ nd_uint32_t encapsulated_pdu_length; /* Encapsulated PDU length */
+ /* Copy of Erroneous PDU (variable, optional) */
+ /* Length of Error Text (4 octets in network byte order) */
+ /* Arbitrary Text of Error Diagnostic Message (variable, optional) */
+} rpki_rtr_pdu_error_report;
+
+/*
+ * PDU type codes
+ */
+#define RPKI_RTR_SERIAL_NOTIFY_PDU 0
+#define RPKI_RTR_SERIAL_QUERY_PDU 1
+#define RPKI_RTR_RESET_QUERY_PDU 2
+#define RPKI_RTR_CACHE_RESPONSE_PDU 3
+#define RPKI_RTR_IPV4_PREFIX_PDU 4
+#define RPKI_RTR_IPV6_PREFIX_PDU 6
+#define RPKI_RTR_END_OF_DATA_PDU 7
+#define RPKI_RTR_CACHE_RESET_PDU 8
+#define RPKI_RTR_ERROR_REPORT_PDU 10
+
+static const struct tok rpki_rtr_pdu_values[] = {
+ { RPKI_RTR_SERIAL_NOTIFY_PDU, "Serial Notify" },
+ { RPKI_RTR_SERIAL_QUERY_PDU, "Serial Query" },
+ { RPKI_RTR_RESET_QUERY_PDU, "Reset Query" },
+ { RPKI_RTR_CACHE_RESPONSE_PDU, "Cache Response" },
+ { RPKI_RTR_IPV4_PREFIX_PDU, "IPV4 Prefix" },
+ { RPKI_RTR_IPV6_PREFIX_PDU, "IPV6 Prefix" },
+ { RPKI_RTR_END_OF_DATA_PDU, "End of Data" },
+ { RPKI_RTR_CACHE_RESET_PDU, "Cache Reset" },
+ { RPKI_RTR_ERROR_REPORT_PDU, "Error Report" },
+ { 0, NULL}
+};
+
+static const struct tok rpki_rtr_error_codes[] = {
+ { 0, "Corrupt Data" },
+ { 1, "Internal Error" },
+ { 2, "No Data Available" },
+ { 3, "Invalid Request" },
+ { 4, "Unsupported Protocol Version" },
+ { 5, "Unsupported PDU Type" },
+ { 6, "Withdrawal of Unknown Record" },
+ { 7, "Duplicate Announcement Received" },
+ { 0, NULL}
+};
+
+/*
+ * Build a indentation string for a given indentation level.
+ * XXX this should be really in util.c
+ */
+static char *
+indent_string (u_int indent)
+{
+ static char buf[20];
+ u_int idx;
+
+ idx = 0;
+ buf[idx] = '\0';
+
+ /*
+ * Does the static buffer fit ?
+ */
+ if (sizeof(buf) < ((indent/8) + (indent %8) + 2)) {
+ return buf;
+ }
+
+ /*
+ * Heading newline.
+ */
+ buf[idx] = '\n';
+ idx++;
+
+ while (indent >= 8) {
+ buf[idx] = '\t';
+ idx++;
+ indent -= 8;
+ }
+
+ while (indent > 0) {
+ buf[idx] = ' ';
+ idx++;
+ indent--;
+ }
+
+ /*
+ * Trailing zero.
+ */
+ buf[idx] = '\0';
+
+ return buf;
+}
+
+/*
+ * Print a single PDU.
+ */
+static u_int
+rpki_rtr_pdu_print(netdissect_options *ndo, const u_char *tptr, const u_int len,
+ const u_char recurse, const u_int indent)
+{
+ const rpki_rtr_pdu *pdu_header;
+ u_int pdu_type, pdu_len, hexdump;
+ const u_char *msg;
+
+ /* Protocol Version */
+ if (GET_U_1(tptr) != 0) {
+ /* Skip the rest of the input buffer because even if this is
+ * a well-formed PDU of a future RPKI-Router protocol version
+ * followed by a well-formed PDU of RPKI-Router protocol
+ * version 0, there is no way to know exactly how to skip the
+ * current PDU.
+ */
+ ND_PRINT("%sRPKI-RTRv%u (unknown)", indent_string(8), GET_U_1(tptr));
+ return len;
+ }
+ if (len < sizeof(rpki_rtr_pdu)) {
+ ND_PRINT("(%u bytes is too few to decode)", len);
+ goto invalid;
+ }
+ ND_TCHECK_LEN(tptr, sizeof(rpki_rtr_pdu));
+ pdu_header = (const rpki_rtr_pdu *)tptr;
+ pdu_type = GET_U_1(pdu_header->pdu_type);
+ pdu_len = GET_BE_U_4(pdu_header->length);
+ /* Do not check bounds with pdu_len yet, do it in the case blocks
+ * below to make it possible to decode at least the beginning of
+ * a truncated Error Report PDU or a truncated encapsulated PDU.
+ */
+ hexdump = FALSE;
+
+ ND_PRINT("%sRPKI-RTRv%u, %s PDU (%u), length: %u",
+ indent_string(8),
+ GET_U_1(pdu_header->version),
+ tok2str(rpki_rtr_pdu_values, "Unknown", pdu_type),
+ pdu_type, pdu_len);
+ if (pdu_len < sizeof(rpki_rtr_pdu) || pdu_len > len)
+ goto invalid;
+
+ switch (pdu_type) {
+
+ /*
+ * The following PDUs share the message format.
+ */
+ case RPKI_RTR_SERIAL_NOTIFY_PDU:
+ case RPKI_RTR_SERIAL_QUERY_PDU:
+ case RPKI_RTR_END_OF_DATA_PDU:
+ if (pdu_len != sizeof(rpki_rtr_pdu) + 4)
+ goto invalid;
+ msg = (const u_char *)(pdu_header + 1);
+ ND_PRINT("%sSession ID: 0x%04x, Serial: %u",
+ indent_string(indent+2),
+ GET_BE_U_2(pdu_header->u.session_id),
+ GET_BE_U_4(msg));
+ break;
+
+ /*
+ * The following PDUs share the message format.
+ */
+ case RPKI_RTR_RESET_QUERY_PDU:
+ case RPKI_RTR_CACHE_RESET_PDU:
+ if (pdu_len != sizeof(rpki_rtr_pdu))
+ goto invalid;
+ /* no additional boundary to check */
+
+ /*
+ * Zero payload PDUs.
+ */
+ break;
+
+ case RPKI_RTR_CACHE_RESPONSE_PDU:
+ if (pdu_len != sizeof(rpki_rtr_pdu))
+ goto invalid;
+ /* no additional boundary to check */
+ ND_PRINT("%sSession ID: 0x%04x",
+ indent_string(indent+2),
+ GET_BE_U_2(pdu_header->u.session_id));
+ break;
+
+ case RPKI_RTR_IPV4_PREFIX_PDU:
+ {
+ const rpki_rtr_pdu_ipv4_prefix *pdu;
+
+ if (pdu_len != sizeof(rpki_rtr_pdu_ipv4_prefix))
+ goto invalid;
+ pdu = (const rpki_rtr_pdu_ipv4_prefix *)tptr;
+ ND_PRINT("%sIPv4 Prefix %s/%u-%u, origin-as %u, flags 0x%02x",
+ indent_string(indent+2),
+ GET_IPADDR_STRING(pdu->prefix),
+ GET_U_1(pdu->prefix_length), GET_U_1(pdu->max_length),
+ GET_BE_U_4(pdu->as), GET_U_1(pdu->flags));
+ }
+ break;
+
+ case RPKI_RTR_IPV6_PREFIX_PDU:
+ {
+ const rpki_rtr_pdu_ipv6_prefix *pdu;
+
+ if (pdu_len != sizeof(rpki_rtr_pdu_ipv6_prefix))
+ goto invalid;
+ pdu = (const rpki_rtr_pdu_ipv6_prefix *)tptr;
+ ND_PRINT("%sIPv6 Prefix %s/%u-%u, origin-as %u, flags 0x%02x",
+ indent_string(indent+2),
+ GET_IP6ADDR_STRING(pdu->prefix),
+ GET_U_1(pdu->prefix_length), GET_U_1(pdu->max_length),
+ GET_BE_U_4(pdu->as), GET_U_1(pdu->flags));
+ }
+ break;
+
+ case RPKI_RTR_ERROR_REPORT_PDU:
+ {
+ const rpki_rtr_pdu_error_report *pdu;
+ u_int encapsulated_pdu_length, text_length, tlen, error_code;
+
+ tlen = sizeof(rpki_rtr_pdu);
+ /* Do not test for the "Length of Error Text" data element yet. */
+ if (pdu_len < tlen + 4)
+ goto invalid;
+ ND_TCHECK_LEN(tptr, tlen + 4);
+ /* Safe up to and including the "Length of Encapsulated PDU"
+ * data element, more data elements may be present.
+ */
+ pdu = (const rpki_rtr_pdu_error_report *)tptr;
+ encapsulated_pdu_length = GET_BE_U_4(pdu->encapsulated_pdu_length);
+ tlen += 4;
+
+ error_code = GET_BE_U_2(pdu->pdu_header.u.error_code);
+ ND_PRINT("%sError code: %s (%u), Encapsulated PDU length: %u",
+ indent_string(indent+2),
+ tok2str(rpki_rtr_error_codes, "Unknown", error_code),
+ error_code, encapsulated_pdu_length);
+
+ if (encapsulated_pdu_length) {
+ /* Section 5.10 of RFC 6810 says:
+ * "An Error Report PDU MUST NOT be sent for an Error Report PDU."
+ *
+ * However, as far as the protocol encoding goes Error Report PDUs can
+ * happen to be nested in each other, however many times, in which case
+ * the decoder should still print such semantically incorrect PDUs.
+ *
+ * That said, "the Erroneous PDU field MAY be truncated" (ibid), thus
+ * to keep things simple this implementation decodes only the two
+ * outermost layers of PDUs and makes bounds checks in the outer and
+ * the inner PDU independently.
+ */
+ if (pdu_len < tlen + encapsulated_pdu_length)
+ goto invalid;
+ if (! recurse) {
+ ND_TCHECK_LEN(tptr, tlen + encapsulated_pdu_length);
+ }
+ else {
+ ND_PRINT("%s-----encapsulated PDU-----", indent_string(indent+4));
+ rpki_rtr_pdu_print(ndo, tptr + tlen,
+ encapsulated_pdu_length, 0, indent + 2);
+ }
+ tlen += encapsulated_pdu_length;
+ }
+
+ if (pdu_len < tlen + 4)
+ goto invalid;
+ ND_TCHECK_LEN(tptr, tlen + 4);
+ /* Safe up to and including the "Length of Error Text" data element,
+ * one more data element may be present.
+ */
+
+ /*
+ * Extract, trail-zero and print the Error message.
+ */
+ text_length = GET_BE_U_4(tptr + tlen);
+ tlen += 4;
+
+ if (text_length) {
+ if (pdu_len < tlen + text_length)
+ goto invalid;
+ /* nd_printn() makes the bounds check */
+ ND_PRINT("%sError text: ", indent_string(indent+2));
+ (void)nd_printn(ndo, tptr + tlen, text_length, NULL);
+ }
+ }
+ break;
+
+ default:
+ ND_TCHECK_LEN(tptr, pdu_len);
+
+ /*
+ * Unknown data, please hexdump.
+ */
+ hexdump = TRUE;
+ }
+
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) {
+ print_unknown_data(ndo,tptr,"\n\t ", pdu_len);
+ }
+ return pdu_len;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(tptr, len);
+ return len;
+}
+
+void
+rpki_rtr_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ ndo->ndo_protocol = "rpki_rtr";
+ if (!ndo->ndo_vflag) {
+ ND_PRINT(", RPKI-RTR");
+ return;
+ }
+ while (len) {
+ u_int pdu_len = rpki_rtr_pdu_print(ndo, pptr, len, 1, 8);
+ len -= pdu_len;
+ pptr += pdu_len;
+ }
+}
diff --git a/print-rrcp.c b/print-rrcp.c
new file mode 100644
index 0000000..9803f75
--- /dev/null
+++ b/print-rrcp.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2007 - Andrey "nording" Chernyak <andrew@nording.ru>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Format and print Realtek Remote Control Protocol (RRCP)
+ * and Realtek Echo Protocol (RRCP-REP) packets.
+ */
+
+/* \summary: Realtek Remote Control Protocol (RRCP) printer */
+
+/*
+ * See, for example, section 8.20 "Realtek Remote Control Protocol" of
+ *
+ * http://realtek.info/pdf/rtl8324.pdf
+ *
+ * and section 7.22 "Realtek Remote Control Protocol" of
+ *
+ * http://realtek.info/pdf/rtl8326.pdf
+ *
+ * and this page on the OpenRRCP Wiki:
+ *
+ * http://openrrcp.org.ru/wiki/rrcp_protocol
+ *
+ * NOTE: none of them indicate the byte order of multi-byte fields in any
+ * obvious fashion.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#define RRCP_OPCODE_MASK 0x7F /* 0x00 = hello, 0x01 = get, 0x02 = set */
+#define RRCP_ISREPLY 0x80 /* 0 = request to switch, 0x80 = reply from switch */
+
+#define RRCP_PROTO_OFFSET 0 /* proto - 1 byte, must be 1 */
+#define RRCP_OPCODE_ISREPLY_OFFSET 1 /* opcode and isreply flag - 1 byte */
+#define RRCP_AUTHKEY_OFFSET 2 /* authorization key - 2 bytes, 0x2379 by default */
+
+/* most packets */
+#define RRCP_REG_ADDR_OFFSET 4 /* register address - 2 bytes */
+#define RRCP_REG_DATA_OFFSET 6 /* register data - 4 bytes */
+#define RRCP_COOKIE1_OFFSET 10 /* 4 bytes */
+#define RRCP_COOKIE2_OFFSET 14 /* 4 bytes */
+
+/* hello reply packets */
+#define RRCP_DOWNLINK_PORT_OFFSET 4 /* 1 byte */
+#define RRCP_UPLINK_PORT_OFFSET 5 /* 1 byte */
+#define RRCP_UPLINK_MAC_OFFSET 6 /* 6 byte MAC address */
+#define RRCP_CHIP_ID_OFFSET 12 /* 2 bytes */
+#define RRCP_VENDOR_ID_OFFSET 14 /* 4 bytes */
+
+static const struct tok proto_values[] = {
+ { 1, "RRCP" },
+ { 2, "RRCP-REP" },
+ { 0, NULL }
+};
+
+static const struct tok opcode_values[] = {
+ { 0, "hello" },
+ { 1, "get" },
+ { 2, "set" },
+ { 0, NULL }
+};
+
+/*
+ * Print RRCP requests
+ */
+void
+rrcp_print(netdissect_options *ndo,
+ const u_char *cp,
+ u_int length _U_,
+ const struct lladdr_info *src,
+ const struct lladdr_info *dst)
+{
+ uint8_t rrcp_proto;
+ uint8_t rrcp_opcode;
+
+ ndo->ndo_protocol = "rrcp";
+ rrcp_proto = GET_U_1(cp + RRCP_PROTO_OFFSET);
+ rrcp_opcode = GET_U_1((cp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_OPCODE_MASK;
+ if (src != NULL && dst != NULL) {
+ ND_PRINT("%s > %s, ",
+ (src->addr_string)(ndo, src->addr),
+ (dst->addr_string)(ndo, dst->addr));
+ }
+ ND_PRINT("%s %s",
+ tok2str(proto_values,"RRCP-0x%02x",rrcp_proto),
+ ((GET_U_1(cp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_ISREPLY) ? "reply" : "query");
+ if (rrcp_proto==1){
+ ND_PRINT(": %s",
+ tok2str(opcode_values,"unknown opcode (0x%02x)",rrcp_opcode));
+ }
+ if (rrcp_opcode==1 || rrcp_opcode==2){
+ ND_PRINT(" addr=0x%04x, data=0x%08x",
+ GET_LE_U_2(cp + RRCP_REG_ADDR_OFFSET),
+ GET_LE_U_4(cp + RRCP_REG_DATA_OFFSET));
+ }
+ if (rrcp_proto==1){
+ ND_PRINT(", auth=0x%04x",
+ GET_BE_U_2(cp + RRCP_AUTHKEY_OFFSET));
+ }
+ if (rrcp_proto==1 && rrcp_opcode==0 &&
+ ((GET_U_1(cp + RRCP_OPCODE_ISREPLY_OFFSET)) & RRCP_ISREPLY)){
+ ND_PRINT(" downlink_port=%u, uplink_port=%u, uplink_mac=%s, vendor_id=%08x ,chip_id=%04x ",
+ GET_U_1(cp + RRCP_DOWNLINK_PORT_OFFSET),
+ GET_U_1(cp + RRCP_UPLINK_PORT_OFFSET),
+ GET_ETHERADDR_STRING(cp + RRCP_UPLINK_MAC_OFFSET),
+ GET_BE_U_4(cp + RRCP_VENDOR_ID_OFFSET),
+ GET_BE_U_2(cp + RRCP_CHIP_ID_OFFSET));
+ }else if (rrcp_opcode==1 || rrcp_opcode==2 || rrcp_proto==2){
+ ND_PRINT(", cookie=0x%08x%08x ",
+ GET_BE_U_4(cp + RRCP_COOKIE2_OFFSET),
+ GET_BE_U_4(cp + RRCP_COOKIE1_OFFSET));
+ }
+}
diff --git a/print-rsvp.c b/print-rsvp.c
new file mode 100644
index 0000000..23b6d5a
--- /dev/null
+++ b/print-rsvp.c
@@ -0,0 +1,2069 @@
+/*
+ * Copyright (c) 1998-2007 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* \summary: Resource ReSerVation Protocol (RSVP) printer */
+
+/* specification: RFC 2205 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+#include "gmpls.h"
+#include "af.h"
+#include "signature.h"
+
+
+/*
+ * RFC 2205 common header
+ *
+ * 0 1 2 3
+ * +-------------+-------------+-------------+-------------+
+ * | Vers | Flags| Msg Type | RSVP Checksum |
+ * +-------------+-------------+-------------+-------------+
+ * | Send_TTL | (Reserved) | RSVP Length |
+ * +-------------+-------------+-------------+-------------+
+ *
+ */
+
+struct rsvp_common_header {
+ nd_uint8_t version_flags;
+ nd_uint8_t msg_type;
+ nd_uint16_t checksum;
+ nd_uint8_t ttl;
+ nd_byte reserved[1];
+ nd_uint16_t length;
+};
+
+/*
+ * RFC2205 object header
+ *
+ *
+ * 0 1 2 3
+ * +-------------+-------------+-------------+-------------+
+ * | Length (bytes) | Class-Num | C-Type |
+ * +-------------+-------------+-------------+-------------+
+ * | |
+ * // (Object contents) //
+ * | |
+ * +-------------+-------------+-------------+-------------+
+ */
+
+struct rsvp_object_header {
+ nd_uint16_t length;
+ nd_uint8_t class_num;
+ nd_uint8_t ctype;
+};
+
+#define RSVP_VERSION 1
+#define RSVP_EXTRACT_VERSION(x) (((x)&0xf0)>>4)
+#define RSVP_EXTRACT_FLAGS(x) ((x)&0x0f)
+
+#define RSVP_MSGTYPE_PATH 1
+#define RSVP_MSGTYPE_RESV 2
+#define RSVP_MSGTYPE_PATHERR 3
+#define RSVP_MSGTYPE_RESVERR 4
+#define RSVP_MSGTYPE_PATHTEAR 5
+#define RSVP_MSGTYPE_RESVTEAR 6
+#define RSVP_MSGTYPE_RESVCONF 7
+#define RSVP_MSGTYPE_BUNDLE 12
+#define RSVP_MSGTYPE_ACK 13
+#define RSVP_MSGTYPE_HELLO_OLD 14 /* ancient Hellos */
+#define RSVP_MSGTYPE_SREFRESH 15
+#define RSVP_MSGTYPE_HELLO 20
+
+static const struct tok rsvp_msg_type_values[] = {
+ { RSVP_MSGTYPE_PATH, "Path" },
+ { RSVP_MSGTYPE_RESV, "Resv" },
+ { RSVP_MSGTYPE_PATHERR, "PathErr" },
+ { RSVP_MSGTYPE_RESVERR, "ResvErr" },
+ { RSVP_MSGTYPE_PATHTEAR, "PathTear" },
+ { RSVP_MSGTYPE_RESVTEAR, "ResvTear" },
+ { RSVP_MSGTYPE_RESVCONF, "ResvConf" },
+ { RSVP_MSGTYPE_BUNDLE, "Bundle" },
+ { RSVP_MSGTYPE_ACK, "Acknowledgement" },
+ { RSVP_MSGTYPE_HELLO_OLD, "Hello (Old)" },
+ { RSVP_MSGTYPE_SREFRESH, "Refresh" },
+ { RSVP_MSGTYPE_HELLO, "Hello" },
+ { 0, NULL}
+};
+
+static const struct tok rsvp_header_flag_values[] = {
+ { 0x01, "Refresh reduction capable" }, /* rfc2961 */
+ { 0, NULL}
+};
+
+static const struct tok rsvp_obj_capability_flag_values[] = {
+ { 0x0004, "RecoveryPath Transmit Enabled" },
+ { 0x0002, "RecoveryPath Desired" },
+ { 0x0001, "RecoveryPath Srefresh Capable" },
+ { 0, NULL}
+};
+
+#define RSVP_OBJ_SESSION 1 /* rfc2205 */
+#define RSVP_OBJ_RSVP_HOP 3 /* rfc2205, rfc3473 */
+#define RSVP_OBJ_INTEGRITY 4 /* rfc2747 */
+#define RSVP_OBJ_TIME_VALUES 5 /* rfc2205 */
+#define RSVP_OBJ_ERROR_SPEC 6
+#define RSVP_OBJ_SCOPE 7
+#define RSVP_OBJ_STYLE 8 /* rfc2205 */
+#define RSVP_OBJ_FLOWSPEC 9 /* rfc2215 */
+#define RSVP_OBJ_FILTERSPEC 10 /* rfc2215 */
+#define RSVP_OBJ_SENDER_TEMPLATE 11
+#define RSVP_OBJ_SENDER_TSPEC 12 /* rfc2215 */
+#define RSVP_OBJ_ADSPEC 13 /* rfc2215 */
+#define RSVP_OBJ_POLICY_DATA 14
+#define RSVP_OBJ_CONFIRM 15 /* rfc2205 */
+#define RSVP_OBJ_LABEL 16 /* rfc3209 */
+#define RSVP_OBJ_LABEL_REQ 19 /* rfc3209 */
+#define RSVP_OBJ_ERO 20 /* rfc3209 */
+#define RSVP_OBJ_RRO 21 /* rfc3209 */
+#define RSVP_OBJ_HELLO 22 /* rfc3209 */
+#define RSVP_OBJ_MESSAGE_ID 23 /* rfc2961 */
+#define RSVP_OBJ_MESSAGE_ID_ACK 24 /* rfc2961 */
+#define RSVP_OBJ_MESSAGE_ID_LIST 25 /* rfc2961 */
+#define RSVP_OBJ_RECOVERY_LABEL 34 /* rfc3473 */
+#define RSVP_OBJ_UPSTREAM_LABEL 35 /* rfc3473 */
+#define RSVP_OBJ_LABEL_SET 36 /* rfc3473 */
+#define RSVP_OBJ_PROTECTION 37 /* rfc3473 */
+#define RSVP_OBJ_S2L 50 /* rfc4875 */
+#define RSVP_OBJ_DETOUR 63 /* rfc4090 */
+#define RSVP_OBJ_CLASSTYPE 66 /* rfc4124 */
+#define RSVP_OBJ_CLASSTYPE_OLD 125 /* draft-ietf-tewg-diff-te-proto-07 */
+#define RSVP_OBJ_SUGGESTED_LABEL 129 /* rfc3473 */
+#define RSVP_OBJ_ACCEPT_LABEL_SET 130 /* rfc3473 */
+#define RSVP_OBJ_RESTART_CAPABILITY 131 /* rfc3473 */
+#define RSVP_OBJ_CAPABILITY 134 /* rfc5063 */
+#define RSVP_OBJ_NOTIFY_REQ 195 /* rfc3473 */
+#define RSVP_OBJ_ADMIN_STATUS 196 /* rfc3473 */
+#define RSVP_OBJ_PROPERTIES 204 /* juniper proprietary */
+#define RSVP_OBJ_FASTREROUTE 205 /* rfc4090 */
+#define RSVP_OBJ_SESSION_ATTRIBUTE 207 /* rfc3209 */
+#define RSVP_OBJ_GENERALIZED_UNI 229 /* OIF RSVP extensions UNI 1.0 Signaling, Rel. 2 */
+#define RSVP_OBJ_CALL_ID 230 /* rfc3474 */
+#define RSVP_OBJ_CALL_OPS 236 /* rfc3474 */
+
+static const struct tok rsvp_obj_values[] = {
+ { RSVP_OBJ_SESSION, "Session" },
+ { RSVP_OBJ_RSVP_HOP, "RSVP Hop" },
+ { RSVP_OBJ_INTEGRITY, "Integrity" },
+ { RSVP_OBJ_TIME_VALUES, "Time Values" },
+ { RSVP_OBJ_ERROR_SPEC, "Error Spec" },
+ { RSVP_OBJ_SCOPE, "Scope" },
+ { RSVP_OBJ_STYLE, "Style" },
+ { RSVP_OBJ_FLOWSPEC, "Flowspec" },
+ { RSVP_OBJ_FILTERSPEC, "FilterSpec" },
+ { RSVP_OBJ_SENDER_TEMPLATE, "Sender Template" },
+ { RSVP_OBJ_SENDER_TSPEC, "Sender TSpec" },
+ { RSVP_OBJ_ADSPEC, "Adspec" },
+ { RSVP_OBJ_POLICY_DATA, "Policy Data" },
+ { RSVP_OBJ_CONFIRM, "Confirm" },
+ { RSVP_OBJ_LABEL, "Label" },
+ { RSVP_OBJ_LABEL_REQ, "Label Request" },
+ { RSVP_OBJ_ERO, "ERO" },
+ { RSVP_OBJ_RRO, "RRO" },
+ { RSVP_OBJ_HELLO, "Hello" },
+ { RSVP_OBJ_MESSAGE_ID, "Message ID" },
+ { RSVP_OBJ_MESSAGE_ID_ACK, "Message ID Ack" },
+ { RSVP_OBJ_MESSAGE_ID_LIST, "Message ID List" },
+ { RSVP_OBJ_RECOVERY_LABEL, "Recovery Label" },
+ { RSVP_OBJ_UPSTREAM_LABEL, "Upstream Label" },
+ { RSVP_OBJ_LABEL_SET, "Label Set" },
+ { RSVP_OBJ_ACCEPT_LABEL_SET, "Acceptable Label Set" },
+ { RSVP_OBJ_DETOUR, "Detour" },
+ { RSVP_OBJ_CLASSTYPE, "Class Type" },
+ { RSVP_OBJ_CLASSTYPE_OLD, "Class Type (old)" },
+ { RSVP_OBJ_SUGGESTED_LABEL, "Suggested Label" },
+ { RSVP_OBJ_PROPERTIES, "Properties" },
+ { RSVP_OBJ_FASTREROUTE, "Fast Re-Route" },
+ { RSVP_OBJ_SESSION_ATTRIBUTE, "Session Attribute" },
+ { RSVP_OBJ_GENERALIZED_UNI, "Generalized UNI" },
+ { RSVP_OBJ_CALL_ID, "Call-ID" },
+ { RSVP_OBJ_CALL_OPS, "Call Capability" },
+ { RSVP_OBJ_RESTART_CAPABILITY, "Restart Capability" },
+ { RSVP_OBJ_CAPABILITY, "Capability" },
+ { RSVP_OBJ_NOTIFY_REQ, "Notify Request" },
+ { RSVP_OBJ_PROTECTION, "Protection" },
+ { RSVP_OBJ_ADMIN_STATUS, "Administrative Status" },
+ { RSVP_OBJ_S2L, "Sub-LSP to LSP" },
+ { 0, NULL}
+};
+
+#define RSVP_CTYPE_IPV4 1
+#define RSVP_CTYPE_IPV6 2
+#define RSVP_CTYPE_TUNNEL_IPV4 7
+#define RSVP_CTYPE_TUNNEL_IPV6 8
+#define RSVP_CTYPE_UNI_IPV4 11 /* OIF RSVP extensions UNI 1.0 Signaling Rel. 2 */
+#define RSVP_CTYPE_1 1
+#define RSVP_CTYPE_2 2
+#define RSVP_CTYPE_3 3
+#define RSVP_CTYPE_4 4
+#define RSVP_CTYPE_12 12
+#define RSVP_CTYPE_13 13
+#define RSVP_CTYPE_14 14
+
+/*
+ * the ctypes are not globally unique so for
+ * translating it to strings we build a table based
+ * on objects offsetted by the ctype
+ */
+
+static const struct tok rsvp_ctype_values[] = {
+ { 256*RSVP_OBJ_RSVP_HOP+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_RSVP_HOP+RSVP_CTYPE_IPV6, "IPv6" },
+ { 256*RSVP_OBJ_RSVP_HOP+RSVP_CTYPE_3, "IPv4 plus opt. TLVs" },
+ { 256*RSVP_OBJ_RSVP_HOP+RSVP_CTYPE_4, "IPv6 plus opt. TLVs" },
+ { 256*RSVP_OBJ_NOTIFY_REQ+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_NOTIFY_REQ+RSVP_CTYPE_IPV6, "IPv6" },
+ { 256*RSVP_OBJ_CONFIRM+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_CONFIRM+RSVP_CTYPE_IPV6, "IPv6" },
+ { 256*RSVP_OBJ_TIME_VALUES+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_FLOWSPEC+RSVP_CTYPE_1, "obsolete" },
+ { 256*RSVP_OBJ_FLOWSPEC+RSVP_CTYPE_2, "IntServ" },
+ { 256*RSVP_OBJ_SENDER_TSPEC+RSVP_CTYPE_2, "IntServ" },
+ { 256*RSVP_OBJ_ADSPEC+RSVP_CTYPE_2, "IntServ" },
+ { 256*RSVP_OBJ_FILTERSPEC+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_FILTERSPEC+RSVP_CTYPE_IPV6, "IPv6" },
+ { 256*RSVP_OBJ_FILTERSPEC+RSVP_CTYPE_3, "IPv6 Flow-label" },
+ { 256*RSVP_OBJ_FILTERSPEC+RSVP_CTYPE_TUNNEL_IPV4, "Tunnel IPv4" },
+ { 256*RSVP_OBJ_FILTERSPEC+RSVP_CTYPE_12, "IPv4 P2MP LSP Tunnel" },
+ { 256*RSVP_OBJ_FILTERSPEC+RSVP_CTYPE_13, "IPv6 P2MP LSP Tunnel" },
+ { 256*RSVP_OBJ_SESSION+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_SESSION+RSVP_CTYPE_IPV6, "IPv6" },
+ { 256*RSVP_OBJ_SESSION+RSVP_CTYPE_TUNNEL_IPV4, "Tunnel IPv4" },
+ { 256*RSVP_OBJ_SESSION+RSVP_CTYPE_UNI_IPV4, "UNI IPv4" },
+ { 256*RSVP_OBJ_SESSION+RSVP_CTYPE_13, "IPv4 P2MP LSP Tunnel" },
+ { 256*RSVP_OBJ_SESSION+RSVP_CTYPE_14, "IPv6 P2MP LSP Tunnel" },
+ { 256*RSVP_OBJ_SENDER_TEMPLATE+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_SENDER_TEMPLATE+RSVP_CTYPE_IPV6, "IPv6" },
+ { 256*RSVP_OBJ_SENDER_TEMPLATE+RSVP_CTYPE_TUNNEL_IPV4, "Tunnel IPv4" },
+ { 256*RSVP_OBJ_SENDER_TEMPLATE+RSVP_CTYPE_12, "IPv4 P2MP LSP Tunnel" },
+ { 256*RSVP_OBJ_SENDER_TEMPLATE+RSVP_CTYPE_13, "IPv6 P2MP LSP Tunnel" },
+ { 256*RSVP_OBJ_MESSAGE_ID+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_MESSAGE_ID_ACK+RSVP_CTYPE_1, "Message id ack" },
+ { 256*RSVP_OBJ_MESSAGE_ID_ACK+RSVP_CTYPE_2, "Message id nack" },
+ { 256*RSVP_OBJ_MESSAGE_ID_LIST+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_STYLE+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_HELLO+RSVP_CTYPE_1, "Hello Request" },
+ { 256*RSVP_OBJ_HELLO+RSVP_CTYPE_2, "Hello Ack" },
+ { 256*RSVP_OBJ_LABEL_REQ+RSVP_CTYPE_1, "without label range" },
+ { 256*RSVP_OBJ_LABEL_REQ+RSVP_CTYPE_2, "with ATM label range" },
+ { 256*RSVP_OBJ_LABEL_REQ+RSVP_CTYPE_3, "with FR label range" },
+ { 256*RSVP_OBJ_LABEL_REQ+RSVP_CTYPE_4, "Generalized Label" },
+ { 256*RSVP_OBJ_LABEL+RSVP_CTYPE_1, "Label" },
+ { 256*RSVP_OBJ_LABEL+RSVP_CTYPE_2, "Generalized Label" },
+ { 256*RSVP_OBJ_LABEL+RSVP_CTYPE_3, "Waveband Switching" },
+ { 256*RSVP_OBJ_SUGGESTED_LABEL+RSVP_CTYPE_1, "Label" },
+ { 256*RSVP_OBJ_SUGGESTED_LABEL+RSVP_CTYPE_2, "Generalized Label" },
+ { 256*RSVP_OBJ_SUGGESTED_LABEL+RSVP_CTYPE_3, "Waveband Switching" },
+ { 256*RSVP_OBJ_UPSTREAM_LABEL+RSVP_CTYPE_1, "Label" },
+ { 256*RSVP_OBJ_UPSTREAM_LABEL+RSVP_CTYPE_2, "Generalized Label" },
+ { 256*RSVP_OBJ_UPSTREAM_LABEL+RSVP_CTYPE_3, "Waveband Switching" },
+ { 256*RSVP_OBJ_RECOVERY_LABEL+RSVP_CTYPE_1, "Label" },
+ { 256*RSVP_OBJ_RECOVERY_LABEL+RSVP_CTYPE_2, "Generalized Label" },
+ { 256*RSVP_OBJ_RECOVERY_LABEL+RSVP_CTYPE_3, "Waveband Switching" },
+ { 256*RSVP_OBJ_ERO+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_RRO+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_ERROR_SPEC+RSVP_CTYPE_IPV4, "IPv4" },
+ { 256*RSVP_OBJ_ERROR_SPEC+RSVP_CTYPE_IPV6, "IPv6" },
+ { 256*RSVP_OBJ_ERROR_SPEC+RSVP_CTYPE_3, "IPv4 plus opt. TLVs" },
+ { 256*RSVP_OBJ_ERROR_SPEC+RSVP_CTYPE_4, "IPv6 plus opt. TLVs" },
+ { 256*RSVP_OBJ_RESTART_CAPABILITY+RSVP_CTYPE_1, "IPv4" },
+ { 256*RSVP_OBJ_CAPABILITY+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_SESSION_ATTRIBUTE+RSVP_CTYPE_TUNNEL_IPV4, "Tunnel IPv4" },
+ { 256*RSVP_OBJ_FASTREROUTE+RSVP_CTYPE_TUNNEL_IPV4, "Tunnel IPv4" }, /* old style*/
+ { 256*RSVP_OBJ_FASTREROUTE+RSVP_CTYPE_1, "1" }, /* new style */
+ { 256*RSVP_OBJ_DETOUR+RSVP_CTYPE_TUNNEL_IPV4, "Tunnel IPv4" },
+ { 256*RSVP_OBJ_PROPERTIES+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_ADMIN_STATUS+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_CLASSTYPE+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_CLASSTYPE_OLD+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_LABEL_SET+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_GENERALIZED_UNI+RSVP_CTYPE_1, "1" },
+ { 256*RSVP_OBJ_S2L+RSVP_CTYPE_IPV4, "IPv4 sub-LSP" },
+ { 256*RSVP_OBJ_S2L+RSVP_CTYPE_IPV6, "IPv6 sub-LSP" },
+ { 0, NULL}
+};
+
+/*
+ * XXX - this assumes a 16-byte digest, which is true for HMAC-MD5, but
+ * isn't necessarily the case for other hash algorithms.
+ *
+ * Unless I've missed something, there's nothing in RFC 2747 to indicate
+ * the hash algorithm being used, so it's presumably something set up
+ * out-of-band, or negotiated by other RSVP objects.
+ */
+struct rsvp_obj_integrity_t {
+ uint8_t flags;
+ uint8_t res;
+ uint8_t key_id[6];
+ uint8_t sequence[8];
+ uint8_t digest[16];
+};
+
+static const struct tok rsvp_obj_integrity_flag_values[] = {
+ { 0x80, "Handshake" },
+ { 0, NULL}
+};
+
+struct rsvp_obj_frr_t {
+ uint8_t setup_prio;
+ uint8_t hold_prio;
+ uint8_t hop_limit;
+ uint8_t flags;
+ uint8_t bandwidth[4];
+ uint8_t include_any[4];
+ uint8_t exclude_any[4];
+ uint8_t include_all[4];
+};
+
+
+#define RSVP_OBJ_XRO_MASK_SUBOBJ(x) ((x)&0x7f)
+#define RSVP_OBJ_XRO_MASK_LOOSE(x) ((x)&0x80)
+
+#define RSVP_OBJ_CAPABILITY_FLAGS_MASK 0x7U
+
+#define RSVP_OBJ_XRO_RES 0
+#define RSVP_OBJ_XRO_IPV4 1
+#define RSVP_OBJ_XRO_IPV6 2
+#define RSVP_OBJ_XRO_LABEL 3
+#define RSVP_OBJ_XRO_ASN 32
+#define RSVP_OBJ_XRO_MPLS 64
+
+static const struct tok rsvp_obj_xro_values[] = {
+ { RSVP_OBJ_XRO_RES, "Reserved" },
+ { RSVP_OBJ_XRO_IPV4, "IPv4 prefix" },
+ { RSVP_OBJ_XRO_IPV6, "IPv6 prefix" },
+ { RSVP_OBJ_XRO_LABEL, "Label" },
+ { RSVP_OBJ_XRO_ASN, "Autonomous system number" },
+ { RSVP_OBJ_XRO_MPLS, "MPLS label switched path termination" },
+ { 0, NULL}
+};
+
+/* RFC4090 */
+static const struct tok rsvp_obj_rro_flag_values[] = {
+ { 0x01, "Local protection available" },
+ { 0x02, "Local protection in use" },
+ { 0x04, "Bandwidth protection" },
+ { 0x08, "Node protection" },
+ { 0, NULL}
+};
+
+/* RFC3209 */
+static const struct tok rsvp_obj_rro_label_flag_values[] = {
+ { 0x01, "Global" },
+ { 0, NULL}
+};
+
+static const struct tok rsvp_resstyle_values[] = {
+ { 17, "Wildcard Filter" },
+ { 10, "Fixed Filter" },
+ { 18, "Shared Explicit" },
+ { 0, NULL}
+};
+
+#define RSVP_OBJ_INTSERV_GUARANTEED_SERV 2
+#define RSVP_OBJ_INTSERV_CONTROLLED_LOAD 5
+
+static const struct tok rsvp_intserv_service_type_values[] = {
+ { 1, "Default/Global Information" },
+ { RSVP_OBJ_INTSERV_GUARANTEED_SERV, "Guaranteed Service" },
+ { RSVP_OBJ_INTSERV_CONTROLLED_LOAD, "Controlled Load" },
+ { 0, NULL}
+};
+
+static const struct tok rsvp_intserv_parameter_id_values[] = {
+ { 4, "IS hop cnt" },
+ { 6, "Path b/w estimate" },
+ { 8, "Minimum path latency" },
+ { 10, "Composed MTU" },
+ { 127, "Token Bucket TSpec" },
+ { 130, "Guaranteed Service RSpec" },
+ { 133, "End-to-end composed value for C" },
+ { 134, "End-to-end composed value for D" },
+ { 135, "Since-last-reshaping point composed C" },
+ { 136, "Since-last-reshaping point composed D" },
+ { 0, NULL}
+};
+
+static const struct tok rsvp_session_attribute_flag_values[] = {
+ { 0x01, "Local Protection" },
+ { 0x02, "Label Recording" },
+ { 0x04, "SE Style" },
+ { 0x08, "Bandwidth protection" }, /* RFC4090 */
+ { 0x10, "Node protection" }, /* RFC4090 */
+ { 0, NULL}
+};
+
+static const struct tok rsvp_obj_prop_tlv_values[] = {
+ { 0x01, "Cos" },
+ { 0x02, "Metric 1" },
+ { 0x04, "Metric 2" },
+ { 0x08, "CCC Status" },
+ { 0x10, "Path Type" },
+ { 0, NULL}
+};
+
+#define RSVP_OBJ_ERROR_SPEC_CODE_ROUTING 24
+#define RSVP_OBJ_ERROR_SPEC_CODE_NOTIFY 25
+#define RSVP_OBJ_ERROR_SPEC_CODE_DIFFSERV_TE 28
+#define RSVP_OBJ_ERROR_SPEC_CODE_DIFFSERV_TE_OLD 125
+
+static const struct tok rsvp_obj_error_code_values[] = {
+ { RSVP_OBJ_ERROR_SPEC_CODE_ROUTING, "Routing Problem" },
+ { RSVP_OBJ_ERROR_SPEC_CODE_NOTIFY, "Notify Error" },
+ { RSVP_OBJ_ERROR_SPEC_CODE_DIFFSERV_TE, "Diffserv TE Error" },
+ { RSVP_OBJ_ERROR_SPEC_CODE_DIFFSERV_TE_OLD, "Diffserv TE Error (Old)" },
+ { 0, NULL}
+};
+
+static const struct tok rsvp_obj_error_code_routing_values[] = {
+ { 1, "Bad EXPLICIT_ROUTE object" },
+ { 2, "Bad strict node" },
+ { 3, "Bad loose node" },
+ { 4, "Bad initial subobject" },
+ { 5, "No route available toward destination" },
+ { 6, "Unacceptable label value" },
+ { 7, "RRO indicated routing loops" },
+ { 8, "non-RSVP-capable router in the path" },
+ { 9, "MPLS label allocation failure" },
+ { 10, "Unsupported L3PID" },
+ { 0, NULL}
+};
+
+static const struct tok rsvp_obj_error_code_diffserv_te_values[] = {
+ { 1, "Unexpected CT object" },
+ { 2, "Unsupported CT" },
+ { 3, "Invalid CT value" },
+ { 4, "CT/setup priority do not form a configured TE-Class" },
+ { 5, "CT/holding priority do not form a configured TE-Class" },
+ { 6, "CT/setup priority and CT/holding priority do not form a configured TE-Class" },
+ { 7, "Inconsistency between signaled PSC and signaled CT" },
+ { 8, "Inconsistency between signaled PHBs and signaled CT" },
+ { 0, NULL}
+};
+
+/* rfc3473 / rfc 3471 */
+static const struct tok rsvp_obj_admin_status_flag_values[] = {
+ { 0x80000000, "Reflect" },
+ { 0x00000004, "Testing" },
+ { 0x00000002, "Admin-down" },
+ { 0x00000001, "Delete-in-progress" },
+ { 0, NULL}
+};
+
+/* label set actions - rfc3471 */
+#define LABEL_SET_INCLUSIVE_LIST 0
+#define LABEL_SET_EXCLUSIVE_LIST 1
+#define LABEL_SET_INCLUSIVE_RANGE 2
+#define LABEL_SET_EXCLUSIVE_RANGE 3
+
+static const struct tok rsvp_obj_label_set_action_values[] = {
+ { LABEL_SET_INCLUSIVE_LIST, "Inclusive list" },
+ { LABEL_SET_EXCLUSIVE_LIST, "Exclusive list" },
+ { LABEL_SET_INCLUSIVE_RANGE, "Inclusive range" },
+ { LABEL_SET_EXCLUSIVE_RANGE, "Exclusive range" },
+ { 0, NULL}
+};
+
+/* OIF RSVP extensions UNI 1.0 Signaling, release 2 */
+#define RSVP_GEN_UNI_SUBOBJ_SOURCE_TNA_ADDRESS 1
+#define RSVP_GEN_UNI_SUBOBJ_DESTINATION_TNA_ADDRESS 2
+#define RSVP_GEN_UNI_SUBOBJ_DIVERSITY 3
+#define RSVP_GEN_UNI_SUBOBJ_EGRESS_LABEL 4
+#define RSVP_GEN_UNI_SUBOBJ_SERVICE_LEVEL 5
+
+static const struct tok rsvp_obj_generalized_uni_values[] = {
+ { RSVP_GEN_UNI_SUBOBJ_SOURCE_TNA_ADDRESS, "Source TNA address" },
+ { RSVP_GEN_UNI_SUBOBJ_DESTINATION_TNA_ADDRESS, "Destination TNA address" },
+ { RSVP_GEN_UNI_SUBOBJ_DIVERSITY, "Diversity" },
+ { RSVP_GEN_UNI_SUBOBJ_EGRESS_LABEL, "Egress label" },
+ { RSVP_GEN_UNI_SUBOBJ_SERVICE_LEVEL, "Service level" },
+ { 0, NULL}
+};
+
+/*
+ * this is a dissector for all the intserv defined
+ * specs as defined per rfc2215
+ * it is called from various rsvp objects;
+ * returns the amount of bytes being processed
+ */
+static u_int
+rsvp_intserv_print(netdissect_options *ndo,
+ const u_char *tptr, u_int obj_tlen)
+{
+ u_int parameter_id,parameter_length;
+ union {
+ float f;
+ uint32_t i;
+ } bw;
+
+ if (obj_tlen < 4)
+ return 0;
+ parameter_id = GET_U_1(tptr);
+ parameter_length = GET_BE_U_2(tptr + 2)<<2; /* convert wordcount to bytecount */
+
+ ND_PRINT("\n\t Parameter ID: %s (%u), length: %u, Flags: [0x%02x]",
+ tok2str(rsvp_intserv_parameter_id_values,"unknown",parameter_id),
+ parameter_id,
+ parameter_length,
+ GET_U_1(tptr + 1));
+
+ if (obj_tlen < parameter_length+4)
+ return 0;
+ switch(parameter_id) { /* parameter_id */
+
+ case 4:
+ /*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 4 (e) | (f) | 1 (g) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IS hop cnt (32-bit unsigned integer) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ if (parameter_length == 4) {
+ ND_PRINT("\n\t\tIS hop count: %u", GET_BE_U_4(tptr + 4));
+ }
+ break;
+
+ case 6:
+ /*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 6 (h) | (i) | 1 (j) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Path b/w estimate (32-bit IEEE floating point number) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ if (parameter_length == 4) {
+ bw.i = GET_BE_U_4(tptr + 4);
+ ND_PRINT("\n\t\tPath b/w estimate: %.10g Mbps", bw.f / 125000);
+ }
+ break;
+
+ case 8:
+ /*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 8 (k) | (l) | 1 (m) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Minimum path latency (32-bit integer) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ if (parameter_length == 4) {
+ ND_PRINT("\n\t\tMinimum path latency: ");
+ if (GET_BE_U_4(tptr + 4) == 0xffffffff)
+ ND_PRINT("don't care");
+ else
+ ND_PRINT("%u", GET_BE_U_4(tptr + 4));
+ }
+ break;
+
+ case 10:
+
+ /*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 10 (n) | (o) | 1 (p) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Composed MTU (32-bit unsigned integer) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+ if (parameter_length == 4) {
+ ND_PRINT("\n\t\tComposed MTU: %u bytes", GET_BE_U_4(tptr + 4));
+ }
+ break;
+ case 127:
+ /*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 127 (e) | 0 (f) | 5 (g) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Token Bucket Rate [r] (32-bit IEEE floating point number) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Token Bucket Size [b] (32-bit IEEE floating point number) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Peak Data Rate [p] (32-bit IEEE floating point number) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Minimum Policed Unit [m] (32-bit integer) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Maximum Packet Size [M] (32-bit integer) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ if (parameter_length == 20) {
+ ND_TCHECK_LEN(tptr + 4, 20);
+ bw.i = GET_BE_U_4(tptr + 4);
+ ND_PRINT("\n\t\tToken Bucket Rate: %.10g Mbps", bw.f / 125000);
+ bw.i = GET_BE_U_4(tptr + 8);
+ ND_PRINT("\n\t\tToken Bucket Size: %.10g bytes", bw.f);
+ bw.i = GET_BE_U_4(tptr + 12);
+ ND_PRINT("\n\t\tPeak Data Rate: %.10g Mbps", bw.f / 125000);
+ ND_PRINT("\n\t\tMinimum Policed Unit: %u bytes",
+ GET_BE_U_4(tptr + 16));
+ ND_PRINT("\n\t\tMaximum Packet Size: %u bytes",
+ GET_BE_U_4(tptr + 20));
+ }
+ break;
+
+ case 130:
+ /*
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 130 (h) | 0 (i) | 2 (j) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Rate [R] (32-bit IEEE floating point number) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Slack Term [S] (32-bit integer) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+ if (parameter_length == 8) {
+ ND_TCHECK_8(tptr + 4);
+ bw.i = GET_BE_U_4(tptr + 4);
+ ND_PRINT("\n\t\tRate: %.10g Mbps", bw.f / 125000);
+ ND_PRINT("\n\t\tSlack Term: %u", GET_BE_U_4(tptr + 8));
+ }
+ break;
+
+ case 133:
+ case 134:
+ case 135:
+ case 136:
+ if (parameter_length == 4) {
+ ND_PRINT("\n\t\tValue: %u", GET_BE_U_4(tptr + 4));
+ }
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr + 4, "\n\t\t", parameter_length);
+ }
+ return (parameter_length+4); /* header length 4 bytes */
+
+trunc:
+ nd_print_trunc(ndo);
+ return 0;
+}
+
+/*
+ * Clear checksum prior to signature verification.
+ */
+static void
+rsvp_clear_checksum(void *header)
+{
+ struct rsvp_common_header *rsvp_com_header = (struct rsvp_common_header *) header;
+
+ rsvp_com_header->checksum[0] = 0;
+ rsvp_com_header->checksum[1] = 0;
+}
+
+static int
+rsvp_obj_print(netdissect_options *ndo,
+ const u_char *pptr, u_int plen, const u_char *tptr,
+ const char *indent, u_int tlen,
+ const struct rsvp_common_header *rsvp_com_header)
+{
+ const struct rsvp_object_header *rsvp_obj_header;
+ const u_char *obj_tptr;
+ union {
+ const struct rsvp_obj_integrity_t *rsvp_obj_integrity;
+ const struct rsvp_obj_frr_t *rsvp_obj_frr;
+ } obj_ptr;
+
+ u_short rsvp_obj_len,rsvp_obj_ctype,rsvp_obj_class_num;
+ u_int obj_tlen,intserv_serv_tlen;
+ int hexdump;
+ u_int processed,padbytes,error_code,error_value,i,sigcheck;
+ union {
+ float f;
+ uint32_t i;
+ } bw;
+ u_int namelen;
+
+ u_int action, subchannel;
+
+ while(tlen>=sizeof(struct rsvp_object_header)) {
+ /* did we capture enough for fully decoding the object header ? */
+ ND_TCHECK_LEN(tptr, sizeof(struct rsvp_object_header));
+
+ rsvp_obj_header = (const struct rsvp_object_header *)tptr;
+ rsvp_obj_len=GET_BE_U_2(rsvp_obj_header->length);
+ rsvp_obj_ctype=GET_U_1(rsvp_obj_header->ctype);
+
+ if(rsvp_obj_len % 4) {
+ ND_PRINT("%sERROR: object header size %u not a multiple of 4", indent, rsvp_obj_len);
+ return -1;
+ }
+ if(rsvp_obj_len < sizeof(struct rsvp_object_header)) {
+ ND_PRINT("%sERROR: object header too short %u < %zu", indent, rsvp_obj_len,
+ sizeof(struct rsvp_object_header));
+ return -1;
+ }
+
+ rsvp_obj_class_num = GET_U_1(rsvp_obj_header->class_num);
+ ND_PRINT("%s%s Object (%u) Flags: [%s",
+ indent,
+ tok2str(rsvp_obj_values,
+ "Unknown",
+ rsvp_obj_class_num),
+ rsvp_obj_class_num,
+ (rsvp_obj_class_num & 0x80) ?
+ ((rsvp_obj_class_num & 0x40) ? "ignore and forward" :
+ "ignore silently") :
+ "reject");
+
+ ND_PRINT(" if unknown], Class-Type: %s (%u), length: %u",
+ tok2str(rsvp_ctype_values,
+ "Unknown",
+ (rsvp_obj_class_num<<8)+rsvp_obj_ctype),
+ rsvp_obj_ctype,
+ rsvp_obj_len);
+
+ if(tlen < rsvp_obj_len) {
+ ND_PRINT("%sERROR: object goes past end of objects TLV", indent);
+ return -1;
+ }
+
+ obj_tptr=tptr+sizeof(struct rsvp_object_header);
+ obj_tlen=rsvp_obj_len-sizeof(struct rsvp_object_header);
+
+ /* did we capture enough for fully decoding the object ? */
+ ND_TCHECK_LEN(tptr, rsvp_obj_len);
+ hexdump=FALSE;
+
+ switch(rsvp_obj_class_num) {
+ case RSVP_OBJ_SESSION:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_IPV4:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv4 DestAddress: %s, Protocol ID: 0x%02x",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_U_1(obj_tptr + sizeof(nd_ipv4)));
+ ND_PRINT("%s Flags: [0x%02x], DestPort %u",
+ indent,
+ GET_U_1((obj_tptr + 5)),
+ GET_BE_U_2(obj_tptr + 6));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ break;
+ case RSVP_CTYPE_IPV6:
+ if (obj_tlen < 20)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv6 DestAddress: %s, Protocol ID: 0x%02x",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_U_1(obj_tptr + sizeof(nd_ipv6)));
+ ND_PRINT("%s Flags: [0x%02x], DestPort %u",
+ indent,
+ GET_U_1((obj_tptr + sizeof(nd_ipv6) + 1)),
+ GET_BE_U_2(obj_tptr + sizeof(nd_ipv6) + 2));
+ obj_tlen-=20;
+ obj_tptr+=20;
+ break;
+
+ case RSVP_CTYPE_TUNNEL_IPV6:
+ if (obj_tlen < 36)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv6 Tunnel EndPoint: %s, Tunnel ID: 0x%04x, Extended Tunnel ID: %s",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 18),
+ GET_IP6ADDR_STRING(obj_tptr + 20));
+ obj_tlen-=36;
+ obj_tptr+=36;
+ break;
+
+ case RSVP_CTYPE_14: /* IPv6 p2mp LSP Tunnel */
+ if (obj_tlen < 26)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv6 P2MP LSP ID: 0x%08x, Tunnel ID: 0x%04x, Extended Tunnel ID: %s",
+ indent,
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6),
+ GET_IP6ADDR_STRING(obj_tptr + 8));
+ obj_tlen-=26;
+ obj_tptr+=26;
+ break;
+ case RSVP_CTYPE_13: /* IPv4 p2mp LSP Tunnel */
+ if (obj_tlen < 12)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv4 P2MP LSP ID: %s, Tunnel ID: 0x%04x, Extended Tunnel ID: %s",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6),
+ GET_IPADDR_STRING(obj_tptr + 8));
+ obj_tlen-=12;
+ obj_tptr+=12;
+ break;
+ case RSVP_CTYPE_TUNNEL_IPV4:
+ case RSVP_CTYPE_UNI_IPV4:
+ if (obj_tlen < 12)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv4 Tunnel EndPoint: %s, Tunnel ID: 0x%04x, Extended Tunnel ID: %s",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6),
+ GET_IPADDR_STRING(obj_tptr + 8));
+ obj_tlen-=12;
+ obj_tptr+=12;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_CONFIRM:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_IPV4:
+ if (obj_tlen < sizeof(nd_ipv4))
+ goto obj_tooshort;
+ ND_PRINT("%s IPv4 Receiver Address: %s",
+ indent,
+ GET_IPADDR_STRING(obj_tptr));
+ obj_tlen-=sizeof(nd_ipv4);
+ obj_tptr+=sizeof(nd_ipv4);
+ break;
+ case RSVP_CTYPE_IPV6:
+ if (obj_tlen < sizeof(nd_ipv6))
+ goto obj_tooshort;
+ ND_PRINT("%s IPv6 Receiver Address: %s",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr));
+ obj_tlen-=sizeof(nd_ipv6);
+ obj_tptr+=sizeof(nd_ipv6);
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_NOTIFY_REQ:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_IPV4:
+ if (obj_tlen < sizeof(nd_ipv4))
+ goto obj_tooshort;
+ ND_PRINT("%s IPv4 Notify Node Address: %s",
+ indent,
+ GET_IPADDR_STRING(obj_tptr));
+ obj_tlen-=sizeof(nd_ipv4);
+ obj_tptr+=sizeof(nd_ipv4);
+ break;
+ case RSVP_CTYPE_IPV6:
+ if (obj_tlen < sizeof(nd_ipv6))
+ goto obj_tooshort;
+ ND_PRINT("%s IPv6 Notify Node Address: %s",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr));
+ obj_tlen-=sizeof(nd_ipv6);
+ obj_tptr+=sizeof(nd_ipv6);
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_SUGGESTED_LABEL: /* fall through */
+ case RSVP_OBJ_UPSTREAM_LABEL: /* fall through */
+ case RSVP_OBJ_RECOVERY_LABEL: /* fall through */
+ case RSVP_OBJ_LABEL:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ while(obj_tlen >= 4 ) {
+ ND_PRINT("%s Label: %u", indent, GET_BE_U_4(obj_tptr));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ }
+ break;
+ case RSVP_CTYPE_2:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s Generalized Label: %u",
+ indent,
+ GET_BE_U_4(obj_tptr));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ break;
+ case RSVP_CTYPE_3:
+ if (obj_tlen < 12)
+ goto obj_tooshort;
+ ND_PRINT("%s Waveband ID: %u%s Start Label: %u, Stop Label: %u",
+ indent,
+ GET_BE_U_4(obj_tptr),
+ indent,
+ GET_BE_U_4(obj_tptr + 4),
+ GET_BE_U_4(obj_tptr + 8));
+ obj_tlen-=12;
+ obj_tptr+=12;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_STYLE:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s Reservation Style: %s, Flags: [0x%02x]",
+ indent,
+ tok2str(rsvp_resstyle_values,
+ "Unknown",
+ GET_BE_U_3(obj_tptr + 1)),
+ GET_U_1(obj_tptr));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_SENDER_TEMPLATE:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_IPV4:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ ND_PRINT("%s Source Address: %s, Source Port: %u",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ break;
+ case RSVP_CTYPE_IPV6:
+ if (obj_tlen < 20)
+ goto obj_tooshort;
+ ND_PRINT("%s Source Address: %s, Source Port: %u",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 18));
+ obj_tlen-=20;
+ obj_tptr+=20;
+ break;
+ case RSVP_CTYPE_13: /* IPv6 p2mp LSP tunnel */
+ if (obj_tlen < 40)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv6 Tunnel Sender Address: %s, LSP ID: 0x%04x"
+ "%s Sub-Group Originator ID: %s, Sub-Group ID: 0x%04x",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 18),
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr+20),
+ GET_BE_U_2(obj_tptr + 38));
+ obj_tlen-=40;
+ obj_tptr+=40;
+ break;
+ case RSVP_CTYPE_TUNNEL_IPV4:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv4 Tunnel Sender Address: %s, LSP-ID: 0x%04x",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ break;
+ case RSVP_CTYPE_12: /* IPv4 p2mp LSP tunnel */
+ if (obj_tlen < 16)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv4 Tunnel Sender Address: %s, LSP ID: 0x%04x"
+ "%s Sub-Group Originator ID: %s, Sub-Group ID: 0x%04x",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6),
+ indent,
+ GET_IPADDR_STRING(obj_tptr+8),
+ GET_BE_U_2(obj_tptr + 12));
+ obj_tlen-=16;
+ obj_tptr+=16;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_LABEL_REQ:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ while(obj_tlen >= 4 ) {
+ ND_PRINT("%s L3 Protocol ID: %s",
+ indent,
+ tok2str(ethertype_values,
+ "Unknown Protocol (0x%04x)",
+ GET_BE_U_2(obj_tptr + 2)));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ }
+ break;
+ case RSVP_CTYPE_2:
+ if (obj_tlen < 12)
+ goto obj_tooshort;
+ ND_PRINT("%s L3 Protocol ID: %s",
+ indent,
+ tok2str(ethertype_values,
+ "Unknown Protocol (0x%04x)",
+ GET_BE_U_2(obj_tptr + 2)));
+ ND_PRINT(",%s merge capability",
+ ((GET_U_1(obj_tptr + 4)) & 0x80) ? "no" : "" );
+ ND_PRINT("%s Minimum VPI/VCI: %u/%u",
+ indent,
+ (GET_BE_U_2(obj_tptr + 4))&0xfff,
+ (GET_BE_U_2(obj_tptr + 6)) & 0xfff);
+ ND_PRINT("%s Maximum VPI/VCI: %u/%u",
+ indent,
+ (GET_BE_U_2(obj_tptr + 8))&0xfff,
+ (GET_BE_U_2(obj_tptr + 10)) & 0xfff);
+ obj_tlen-=12;
+ obj_tptr+=12;
+ break;
+ case RSVP_CTYPE_3:
+ if (obj_tlen < 12)
+ goto obj_tooshort;
+ ND_PRINT("%s L3 Protocol ID: %s",
+ indent,
+ tok2str(ethertype_values,
+ "Unknown Protocol (0x%04x)",
+ GET_BE_U_2(obj_tptr + 2)));
+ ND_PRINT("%s Minimum/Maximum DLCI: %u/%u, %s%s bit DLCI",
+ indent,
+ (GET_BE_U_4(obj_tptr + 4))&0x7fffff,
+ (GET_BE_U_4(obj_tptr + 8))&0x7fffff,
+ (((GET_BE_U_2(obj_tptr + 4)>>7)&3) == 0 ) ? "10" : "",
+ (((GET_BE_U_2(obj_tptr + 4) >> 7) & 3) == 2 ) ? "23" : "");
+ obj_tlen-=12;
+ obj_tptr+=12;
+ break;
+ case RSVP_CTYPE_4:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s LSP Encoding Type: %s (%u)",
+ indent,
+ tok2str(gmpls_encoding_values,
+ "Unknown",
+ GET_U_1(obj_tptr)),
+ GET_U_1(obj_tptr));
+ ND_PRINT("%s Switching Type: %s (%u), Payload ID: %s (0x%04x)",
+ indent,
+ tok2str(gmpls_switch_cap_values,
+ "Unknown",
+ GET_U_1((obj_tptr + 1))),
+ GET_U_1(obj_tptr + 1),
+ tok2str(gmpls_payload_values,
+ "Unknown",
+ GET_BE_U_2(obj_tptr + 2)),
+ GET_BE_U_2(obj_tptr + 2));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_RRO:
+ case RSVP_OBJ_ERO:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_IPV4:
+ while(obj_tlen >= 4 ) {
+ u_char length;
+
+ ND_TCHECK_4(obj_tptr);
+ length = GET_U_1(obj_tptr + 1);
+ ND_PRINT("%s Subobject Type: %s, length %u",
+ indent,
+ tok2str(rsvp_obj_xro_values,
+ "Unknown %u",
+ RSVP_OBJ_XRO_MASK_SUBOBJ(GET_U_1(obj_tptr))),
+ length);
+ if (obj_tlen < length) {
+ ND_PRINT("%s ERROR: ERO subobject length > object length", indent);
+ break;
+ }
+
+ if (length == 0) { /* prevent infinite loops */
+ ND_PRINT("%s ERROR: zero length ERO subtype", indent);
+ break;
+ }
+
+ switch(RSVP_OBJ_XRO_MASK_SUBOBJ(GET_U_1(obj_tptr))) {
+ u_char prefix_length;
+
+ case RSVP_OBJ_XRO_IPV4:
+ if (length != 8) {
+ ND_PRINT(" ERROR: length != 8");
+ goto invalid;
+ }
+ ND_TCHECK_8(obj_tptr);
+ prefix_length = GET_U_1(obj_tptr + 6);
+ if (prefix_length != 32) {
+ ND_PRINT(" ERROR: Prefix length %u != 32",
+ prefix_length);
+ goto invalid;
+ }
+ ND_PRINT(", %s, %s/%u, Flags: [%s]",
+ RSVP_OBJ_XRO_MASK_LOOSE(GET_U_1(obj_tptr)) ? "Loose" : "Strict",
+ GET_IPADDR_STRING(obj_tptr+2),
+ GET_U_1((obj_tptr + 6)),
+ bittok2str(rsvp_obj_rro_flag_values,
+ "none",
+ GET_U_1((obj_tptr + 7)))); /* rfc3209 says that this field is rsvd. */
+ break;
+ case RSVP_OBJ_XRO_LABEL:
+ if (length != 8) {
+ ND_PRINT(" ERROR: length != 8");
+ goto invalid;
+ }
+ ND_TCHECK_8(obj_tptr);
+ ND_PRINT(", Flags: [%s] (%#x), Class-Type: %s (%u), %u",
+ bittok2str(rsvp_obj_rro_label_flag_values,
+ "none",
+ GET_U_1((obj_tptr + 2))),
+ GET_U_1(obj_tptr + 2),
+ tok2str(rsvp_ctype_values,
+ "Unknown",
+ GET_U_1((obj_tptr + 3)) + (256 * RSVP_OBJ_RRO)),
+ GET_U_1((obj_tptr + 3)),
+ GET_BE_U_4(obj_tptr + 4));
+ }
+ obj_tlen-=length;
+ obj_tptr+=length;
+ }
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_HELLO:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ case RSVP_CTYPE_2:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ ND_PRINT("%s Source Instance: 0x%08x, Destination Instance: 0x%08x",
+ indent,
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_4(obj_tptr + 4));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_RESTART_CAPABILITY:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ ND_PRINT("%s Restart Time: %ums, Recovery Time: %ums",
+ indent,
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_4(obj_tptr + 4));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_CAPABILITY:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ uint32_t unused_and_flags = GET_BE_U_4(obj_tptr);
+ if (unused_and_flags & ~RSVP_OBJ_CAPABILITY_FLAGS_MASK)
+ ND_PRINT("%s [reserved=0x%08x must be zero]", indent,
+ unused_and_flags & ~RSVP_OBJ_CAPABILITY_FLAGS_MASK);
+ ND_PRINT("%s Flags: [%s]",
+ indent,
+ bittok2str(rsvp_obj_capability_flag_values,
+ "none",
+ (unused_and_flags & RSVP_OBJ_CAPABILITY_FLAGS_MASK)));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_SESSION_ATTRIBUTE:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_TUNNEL_IPV4:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ namelen = GET_U_1(obj_tptr + 3);
+ if (obj_tlen < 4+namelen)
+ goto obj_tooshort;
+ ND_PRINT("%s Session Name: ", indent);
+ for (i = 0; i < namelen; i++)
+ fn_print_char(ndo, GET_U_1(obj_tptr + 4 + i));
+ ND_PRINT("%s Setup Priority: %u, Holding Priority: %u, Flags: [%s] (%#x)",
+ indent,
+ GET_U_1(obj_tptr),
+ GET_U_1(obj_tptr + 1),
+ bittok2str(rsvp_session_attribute_flag_values,
+ "none",
+ GET_U_1((obj_tptr + 2))),
+ GET_U_1(obj_tptr + 2));
+ obj_tlen-=4+namelen;
+ obj_tptr+=4+namelen;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_GENERALIZED_UNI:
+ switch(rsvp_obj_ctype) {
+ u_int subobj_type,af,subobj_len,total_subobj_len;
+
+ case RSVP_CTYPE_1:
+
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+
+ /* read variable length subobjects */
+ total_subobj_len = obj_tlen;
+ while(total_subobj_len > 0) {
+ /* If RFC 3476 Section 3.1 defined that a sub-object of the
+ * GENERALIZED_UNI RSVP object must have the Length field as
+ * a multiple of 4, instead of the check below it would be
+ * better to test total_subobj_len only once before the loop.
+ * So long as it does not define it and this while loop does
+ * not implement such a requirement, let's accept that within
+ * each iteration subobj_len may happen to be a multiple of 1
+ * and test it and total_subobj_len respectively.
+ */
+ if (total_subobj_len < 4)
+ goto invalid;
+ subobj_len = GET_BE_U_2(obj_tptr);
+ subobj_type = (GET_BE_U_2(obj_tptr + 2))>>8;
+ af = (GET_BE_U_2(obj_tptr + 2))&0x00FF;
+
+ ND_PRINT("%s Subobject Type: %s (%u), AF: %s (%u), length: %u",
+ indent,
+ tok2str(rsvp_obj_generalized_uni_values, "Unknown", subobj_type),
+ subobj_type,
+ tok2str(af_values, "Unknown", af), af,
+ subobj_len);
+
+ /* In addition to what is explained above, the same spec does not
+ * explicitly say that the same Length field includes the 4-octet
+ * sub-object header, but as long as this while loop implements it
+ * as it does include, let's keep the check below consistent with
+ * the rest of the code.
+ *
+ * XXX - RFC 3476 Section 3.1 says "The contents of these
+ * sub-objects are described in [8]", where [8] is
+ * UNI 1.0 Signaling Specification, The Optical
+ * Internetworking Forum. The URL they give for that
+ * document is
+ *
+ * http://www.oiforum.com/public/UNI_1.0_ia.html
+ *
+ * but that doesn't work; the new URL appears to be
+ *
+ * https://web.archive.org/web/20160401194747/http://www.oiforum.com/public/documents/OIF-UNI-01.0.pdf
+ *
+ * and *that* document, in section 12.5.2.3
+ * "GENERALIZED_UNI Object (Class-Num=11bbbbbb (TBA))",
+ * says nothing about the length field in general, but
+ * some of the examples it gives in subsections have
+ * length field values that clearly includes the length
+ * of the sub-object header as well as the length of the
+ * value.
+ */
+ if(subobj_len < 4 || subobj_len > total_subobj_len ||
+ obj_tlen < subobj_len)
+ goto invalid;
+
+ switch(subobj_type) {
+ case RSVP_GEN_UNI_SUBOBJ_SOURCE_TNA_ADDRESS:
+ case RSVP_GEN_UNI_SUBOBJ_DESTINATION_TNA_ADDRESS:
+
+ switch(af) {
+ case AFNUM_INET:
+ if (subobj_len < 8)
+ goto subobj_tooshort;
+ ND_PRINT("%s UNI IPv4 TNA address: %s",
+ indent, GET_IPADDR_STRING(obj_tptr + 4));
+ break;
+ case AFNUM_INET6:
+ if (subobj_len < 20)
+ goto subobj_tooshort;
+ ND_PRINT("%s UNI IPv6 TNA address: %s",
+ indent, GET_IP6ADDR_STRING(obj_tptr + 4));
+ break;
+ case AFNUM_NSAP:
+ if (subobj_len) {
+ /* unless we have a TLV parser lets just hexdump */
+ hexdump=TRUE;
+ }
+ break;
+ }
+ break;
+
+ case RSVP_GEN_UNI_SUBOBJ_DIVERSITY:
+ if (subobj_len > 4) {
+ /* unless we have a TLV parser lets just hexdump */
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_GEN_UNI_SUBOBJ_EGRESS_LABEL:
+ if (subobj_len < 16) {
+ goto subobj_tooshort;
+ }
+
+ ND_PRINT("%s U-bit: %x, Label type: %u, Logical port id: %u, Label: %u",
+ indent,
+ ((GET_BE_U_4(obj_tptr + 4))>>31),
+ ((GET_BE_U_4(obj_tptr + 4))&0xFF),
+ GET_BE_U_4(obj_tptr + 8),
+ GET_BE_U_4(obj_tptr + 12));
+ break;
+
+ case RSVP_GEN_UNI_SUBOBJ_SERVICE_LEVEL:
+ if (subobj_len < 8) {
+ goto subobj_tooshort;
+ }
+
+ ND_PRINT("%s Service level: %u",
+ indent, (GET_BE_U_4(obj_tptr + 4)) >> 24);
+ break;
+
+ default:
+ hexdump=TRUE;
+ break;
+ }
+ total_subobj_len-=subobj_len;
+ obj_tptr+=subobj_len;
+ obj_tlen+=subobj_len;
+ }
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_RSVP_HOP:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_3: /* fall through - FIXME add TLV parser */
+ case RSVP_CTYPE_IPV4:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ ND_PRINT("%s Previous/Next Interface: %s, Logical Interface Handle: 0x%08x",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_4(obj_tptr + 4));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ if (obj_tlen)
+ hexdump=TRUE; /* unless we have a TLV parser lets just hexdump */
+ break;
+ case RSVP_CTYPE_4: /* fall through - FIXME add TLV parser */
+ case RSVP_CTYPE_IPV6:
+ if (obj_tlen < 20)
+ goto obj_tooshort;
+ ND_PRINT("%s Previous/Next Interface: %s, Logical Interface Handle: 0x%08x",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_BE_U_4(obj_tptr + 16));
+ obj_tlen-=20;
+ obj_tptr+=20;
+ hexdump=TRUE; /* unless we have a TLV parser lets just hexdump */
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_TIME_VALUES:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s Refresh Period: %ums",
+ indent,
+ GET_BE_U_4(obj_tptr));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ /* those three objects do share the same semantics */
+ case RSVP_OBJ_SENDER_TSPEC:
+ case RSVP_OBJ_ADSPEC:
+ case RSVP_OBJ_FLOWSPEC:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_2:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s Msg-Version: %u, length: %u",
+ indent,
+ (GET_U_1(obj_tptr) & 0xf0) >> 4,
+ GET_BE_U_2(obj_tptr + 2) << 2);
+ obj_tptr+=4; /* get to the start of the service header */
+ obj_tlen-=4;
+
+ while (obj_tlen >= 4) {
+ intserv_serv_tlen=GET_BE_U_2(obj_tptr + 2)<<2;
+ ND_PRINT("%s Service Type: %s (%u), break bit %sset, Service length: %u",
+ indent,
+ tok2str(rsvp_intserv_service_type_values,"unknown",GET_U_1((obj_tptr))),
+ GET_U_1(obj_tptr),
+ (GET_U_1(obj_tptr + 1)&0x80) ? "" : "not ",
+ intserv_serv_tlen);
+
+ obj_tptr+=4; /* get to the start of the parameter list */
+ obj_tlen-=4;
+
+ while (intserv_serv_tlen>=4) {
+ processed = rsvp_intserv_print(ndo, obj_tptr, obj_tlen);
+ if (processed == 0)
+ break;
+ obj_tlen-=processed;
+ intserv_serv_tlen-=processed;
+ obj_tptr+=processed;
+ }
+ }
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_FILTERSPEC:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_IPV4:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ ND_PRINT("%s Source Address: %s, Source Port: %u",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ break;
+ case RSVP_CTYPE_IPV6:
+ if (obj_tlen < 20)
+ goto obj_tooshort;
+ ND_PRINT("%s Source Address: %s, Source Port: %u",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 18));
+ obj_tlen-=20;
+ obj_tptr+=20;
+ break;
+ case RSVP_CTYPE_3:
+ if (obj_tlen < 20)
+ goto obj_tooshort;
+ ND_PRINT("%s Source Address: %s, Flow Label: %u",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_BE_U_3(obj_tptr + 17));
+ obj_tlen-=20;
+ obj_tptr+=20;
+ break;
+ case RSVP_CTYPE_TUNNEL_IPV6:
+ if (obj_tlen < 20)
+ goto obj_tooshort;
+ ND_PRINT("%s Source Address: %s, LSP-ID: 0x%04x",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 18));
+ obj_tlen-=20;
+ obj_tptr+=20;
+ break;
+ case RSVP_CTYPE_13: /* IPv6 p2mp LSP tunnel */
+ if (obj_tlen < 40)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv6 Tunnel Sender Address: %s, LSP ID: 0x%04x"
+ "%s Sub-Group Originator ID: %s, Sub-Group ID: 0x%04x",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 18),
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr+20),
+ GET_BE_U_2(obj_tptr + 38));
+ obj_tlen-=40;
+ obj_tptr+=40;
+ break;
+ case RSVP_CTYPE_TUNNEL_IPV4:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ ND_PRINT("%s Source Address: %s, LSP-ID: 0x%04x",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ break;
+ case RSVP_CTYPE_12: /* IPv4 p2mp LSP tunnel */
+ if (obj_tlen < 16)
+ goto obj_tooshort;
+ ND_PRINT("%s IPv4 Tunnel Sender Address: %s, LSP ID: 0x%04x"
+ "%s Sub-Group Originator ID: %s, Sub-Group ID: 0x%04x",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_BE_U_2(obj_tptr + 6),
+ indent,
+ GET_IPADDR_STRING(obj_tptr+8),
+ GET_BE_U_2(obj_tptr + 12));
+ obj_tlen-=16;
+ obj_tptr+=16;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_FASTREROUTE:
+ /* the differences between c-type 1 and 7 are minor */
+ obj_ptr.rsvp_obj_frr = (const struct rsvp_obj_frr_t *)obj_tptr;
+
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1: /* new style */
+ if (obj_tlen < sizeof(struct rsvp_obj_frr_t))
+ goto obj_tooshort;
+ bw.i = GET_BE_U_4(obj_ptr.rsvp_obj_frr->bandwidth);
+ ND_PRINT("%s Setup Priority: %u, Holding Priority: %u, Hop-limit: %u, Bandwidth: %.10g Mbps",
+ indent,
+ obj_ptr.rsvp_obj_frr->setup_prio,
+ obj_ptr.rsvp_obj_frr->hold_prio,
+ obj_ptr.rsvp_obj_frr->hop_limit,
+ bw.f * 8 / 1000000);
+ ND_PRINT("%s Include-any: 0x%08x, Exclude-any: 0x%08x, Include-all: 0x%08x",
+ indent,
+ GET_BE_U_4(obj_ptr.rsvp_obj_frr->include_any),
+ GET_BE_U_4(obj_ptr.rsvp_obj_frr->exclude_any),
+ GET_BE_U_4(obj_ptr.rsvp_obj_frr->include_all));
+ obj_tlen-=sizeof(struct rsvp_obj_frr_t);
+ obj_tptr+=sizeof(struct rsvp_obj_frr_t);
+ break;
+
+ case RSVP_CTYPE_TUNNEL_IPV4: /* old style */
+ if (obj_tlen < 16)
+ goto obj_tooshort;
+ bw.i = GET_BE_U_4(obj_ptr.rsvp_obj_frr->bandwidth);
+ ND_PRINT("%s Setup Priority: %u, Holding Priority: %u, Hop-limit: %u, Bandwidth: %.10g Mbps",
+ indent,
+ obj_ptr.rsvp_obj_frr->setup_prio,
+ obj_ptr.rsvp_obj_frr->hold_prio,
+ obj_ptr.rsvp_obj_frr->hop_limit,
+ bw.f * 8 / 1000000);
+ ND_PRINT("%s Include Colors: 0x%08x, Exclude Colors: 0x%08x",
+ indent,
+ GET_BE_U_4(obj_ptr.rsvp_obj_frr->include_any),
+ GET_BE_U_4(obj_ptr.rsvp_obj_frr->exclude_any));
+ obj_tlen-=16;
+ obj_tptr+=16;
+ break;
+
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_DETOUR:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_TUNNEL_IPV4:
+ while(obj_tlen >= 8) {
+ ND_PRINT("%s PLR-ID: %s, Avoid-Node-ID: %s",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_IPADDR_STRING(obj_tptr + 4));
+ obj_tlen-=8;
+ obj_tptr+=8;
+ }
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_CLASSTYPE:
+ case RSVP_OBJ_CLASSTYPE_OLD: /* fall through */
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s CT: %u",
+ indent,
+ GET_BE_U_4(obj_tptr) & 0x7);
+ obj_tlen-=4;
+ obj_tptr+=4;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_ERROR_SPEC:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_3: /* fall through - FIXME add TLV parser */
+ case RSVP_CTYPE_IPV4:
+ if (obj_tlen < 8)
+ goto obj_tooshort;
+ error_code=GET_U_1(obj_tptr + 5);
+ error_value=GET_BE_U_2(obj_tptr + 6);
+ ND_PRINT("%s Error Node Address: %s, Flags: [0x%02x]%s Error Code: %s (%u)",
+ indent,
+ GET_IPADDR_STRING(obj_tptr),
+ GET_U_1(obj_tptr + 4),
+ indent,
+ tok2str(rsvp_obj_error_code_values,"unknown",error_code),
+ error_code);
+ switch (error_code) {
+ case RSVP_OBJ_ERROR_SPEC_CODE_ROUTING:
+ ND_PRINT(", Error Value: %s (%u)",
+ tok2str(rsvp_obj_error_code_routing_values,"unknown",error_value),
+ error_value);
+ break;
+ case RSVP_OBJ_ERROR_SPEC_CODE_DIFFSERV_TE: /* fall through */
+ case RSVP_OBJ_ERROR_SPEC_CODE_DIFFSERV_TE_OLD:
+ ND_PRINT(", Error Value: %s (%u)",
+ tok2str(rsvp_obj_error_code_diffserv_te_values,"unknown",error_value),
+ error_value);
+ break;
+ default:
+ ND_PRINT(", Unknown Error Value (%u)", error_value);
+ break;
+ }
+ obj_tlen-=8;
+ obj_tptr+=8;
+ break;
+ case RSVP_CTYPE_4: /* fall through - FIXME add TLV parser */
+ case RSVP_CTYPE_IPV6:
+ if (obj_tlen < 20)
+ goto obj_tooshort;
+ error_code=GET_U_1(obj_tptr + 17);
+ error_value=GET_BE_U_2(obj_tptr + 18);
+ ND_PRINT("%s Error Node Address: %s, Flags: [0x%02x]%s Error Code: %s (%u)",
+ indent,
+ GET_IP6ADDR_STRING(obj_tptr),
+ GET_U_1(obj_tptr + 16),
+ indent,
+ tok2str(rsvp_obj_error_code_values,"unknown",error_code),
+ error_code);
+
+ switch (error_code) {
+ case RSVP_OBJ_ERROR_SPEC_CODE_ROUTING:
+ ND_PRINT(", Error Value: %s (%u)",
+ tok2str(rsvp_obj_error_code_routing_values,"unknown",error_value),
+ error_value);
+ break;
+ default:
+ break;
+ }
+ obj_tlen-=20;
+ obj_tptr+=20;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_PROPERTIES:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ padbytes = GET_BE_U_2(obj_tptr + 2);
+ ND_PRINT("%s TLV count: %u, padding bytes: %u",
+ indent,
+ GET_BE_U_2(obj_tptr),
+ padbytes);
+ obj_tlen-=4;
+ obj_tptr+=4;
+ /* loop through as long there is anything longer than the TLV header (2) */
+ while(obj_tlen >= 2 + padbytes) {
+ ND_PRINT("%s %s TLV (0x%02x), length: %u", /* length includes header */
+ indent,
+ tok2str(rsvp_obj_prop_tlv_values,"unknown",GET_U_1(obj_tptr)),
+ GET_U_1(obj_tptr),
+ GET_U_1(obj_tptr + 1));
+ if (obj_tlen < GET_U_1(obj_tptr + 1))
+ goto obj_tooshort;
+ if (GET_U_1(obj_tptr + 1) < 2) {
+ ND_PRINT("%sERROR: property TLV is too short", indent);
+ return -1;
+ }
+ print_unknown_data(ndo, obj_tptr + 2, "\n\t\t",
+ GET_U_1(obj_tptr + 1) - 2);
+ obj_tlen-=GET_U_1(obj_tptr + 1);
+ obj_tptr+=GET_U_1(obj_tptr + 1);
+ }
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_MESSAGE_ID: /* fall through */
+ case RSVP_OBJ_MESSAGE_ID_ACK: /* fall through */
+ case RSVP_OBJ_MESSAGE_ID_LIST:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ case RSVP_CTYPE_2:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s Flags [0x%02x], epoch: %u",
+ indent,
+ GET_U_1(obj_tptr),
+ GET_BE_U_3(obj_tptr + 1));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ /* loop through as long there are no messages left */
+ while(obj_tlen >= 4) {
+ ND_PRINT("%s Message-ID 0x%08x (%u)",
+ indent,
+ GET_BE_U_4(obj_tptr),
+ GET_BE_U_4(obj_tptr));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ }
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_INTEGRITY:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < sizeof(struct rsvp_obj_integrity_t))
+ goto obj_tooshort;
+ obj_ptr.rsvp_obj_integrity = (const struct rsvp_obj_integrity_t *)obj_tptr;
+ ND_PRINT("%s Key-ID 0x%04x%08x, Sequence 0x%08x%08x, Flags [%s]",
+ indent,
+ GET_BE_U_2(obj_ptr.rsvp_obj_integrity->key_id),
+ GET_BE_U_4(obj_ptr.rsvp_obj_integrity->key_id + 2),
+ GET_BE_U_4(obj_ptr.rsvp_obj_integrity->sequence),
+ GET_BE_U_4(obj_ptr.rsvp_obj_integrity->sequence + 4),
+ bittok2str(rsvp_obj_integrity_flag_values,
+ "none",
+ obj_ptr.rsvp_obj_integrity->flags));
+ ND_PRINT("%s MD5-sum 0x%08x%08x%08x%08x ",
+ indent,
+ GET_BE_U_4(obj_ptr.rsvp_obj_integrity->digest),
+ GET_BE_U_4(obj_ptr.rsvp_obj_integrity->digest + 4),
+ GET_BE_U_4(obj_ptr.rsvp_obj_integrity->digest + 8),
+ GET_BE_U_4(obj_ptr.rsvp_obj_integrity->digest + 12));
+
+ sigcheck = signature_verify(ndo, pptr, plen,
+ obj_ptr.rsvp_obj_integrity->digest,
+ rsvp_clear_checksum,
+ rsvp_com_header);
+ ND_PRINT(" (%s)", tok2str(signature_check_values, "Unknown", sigcheck));
+
+ obj_tlen+=sizeof(struct rsvp_obj_integrity_t);
+ obj_tptr+=sizeof(struct rsvp_obj_integrity_t);
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_ADMIN_STATUS:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s Flags [%s]", indent,
+ bittok2str(rsvp_obj_admin_status_flag_values, "none",
+ GET_BE_U_4(obj_tptr)));
+ obj_tlen-=4;
+ obj_tptr+=4;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_LABEL_SET:
+ switch(rsvp_obj_ctype) {
+ case RSVP_CTYPE_1:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ action = (GET_BE_U_2(obj_tptr)>>8);
+
+ ND_PRINT("%s Action: %s (%u), Label type: %u", indent,
+ tok2str(rsvp_obj_label_set_action_values, "Unknown", action),
+ action, (GET_BE_U_4(obj_tptr) & 0x7F));
+
+ switch (action) {
+ case LABEL_SET_INCLUSIVE_RANGE:
+ case LABEL_SET_EXCLUSIVE_RANGE: /* fall through */
+
+ /* only a couple of subchannels are expected */
+ if (obj_tlen < 12)
+ goto obj_tooshort;
+ ND_PRINT("%s Start range: %u, End range: %u", indent,
+ GET_BE_U_4(obj_tptr + 4),
+ GET_BE_U_4(obj_tptr + 8));
+ obj_tlen-=12;
+ obj_tptr+=12;
+ break;
+
+ default:
+ obj_tlen-=4;
+ obj_tptr+=4;
+ subchannel = 1;
+ while(obj_tlen >= 4 ) {
+ ND_PRINT("%s Subchannel #%u: %u", indent, subchannel,
+ GET_BE_U_4(obj_tptr));
+ obj_tptr+=4;
+ obj_tlen-=4;
+ subchannel++;
+ }
+ break;
+ }
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ case RSVP_OBJ_S2L:
+ switch (rsvp_obj_ctype) {
+ case RSVP_CTYPE_IPV4:
+ if (obj_tlen < 4)
+ goto obj_tooshort;
+ ND_PRINT("%s Sub-LSP destination address: %s",
+ indent, GET_IPADDR_STRING(obj_tptr));
+
+ obj_tlen-=4;
+ obj_tptr+=4;
+ break;
+ case RSVP_CTYPE_IPV6:
+ if (obj_tlen < 16)
+ goto obj_tooshort;
+ ND_PRINT("%s Sub-LSP destination address: %s",
+ indent, GET_IP6ADDR_STRING(obj_tptr));
+
+ obj_tlen-=16;
+ obj_tptr+=16;
+ break;
+ default:
+ hexdump=TRUE;
+ }
+ break;
+
+ /*
+ * FIXME those are the defined objects that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case RSVP_OBJ_SCOPE:
+ case RSVP_OBJ_POLICY_DATA:
+ case RSVP_OBJ_ACCEPT_LABEL_SET:
+ case RSVP_OBJ_PROTECTION:
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, obj_tptr, "\n\t ", obj_tlen); /* FIXME indentation */
+ break;
+ }
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag > 1 || hexdump == TRUE)
+ print_unknown_data(ndo, tptr + sizeof(struct rsvp_object_header), "\n\t ", /* FIXME indentation */
+ rsvp_obj_len - sizeof(struct rsvp_object_header));
+
+ tptr+=rsvp_obj_len;
+ tlen-=rsvp_obj_len;
+ }
+ return 0;
+subobj_tooshort:
+ ND_PRINT("%sERROR: sub-object is too short", indent);
+ return -1;
+obj_tooshort:
+ ND_PRINT("%sERROR: object is too short", indent);
+ return -1;
+invalid:
+ nd_print_invalid(ndo);
+ return -1;
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+void
+rsvp_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ const struct rsvp_common_header *rsvp_com_header;
+ uint8_t version_flags, msg_type;
+ const u_char *tptr;
+ u_short plen, tlen;
+
+ ndo->ndo_protocol = "rsvp";
+ tptr=pptr;
+
+ rsvp_com_header = (const struct rsvp_common_header *)pptr;
+ ND_TCHECK_SIZE(rsvp_com_header);
+ version_flags = GET_U_1(rsvp_com_header->version_flags);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (RSVP_EXTRACT_VERSION(version_flags) != RSVP_VERSION) {
+ ND_PRINT("ERROR: RSVP version %u packet not supported",
+ RSVP_EXTRACT_VERSION(version_flags));
+ return;
+ }
+
+ msg_type = GET_U_1(rsvp_com_header->msg_type);
+
+ /* in non-verbose mode just lets print the basic Message Type*/
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("RSVPv%u %s Message, length: %u",
+ RSVP_EXTRACT_VERSION(version_flags),
+ tok2str(rsvp_msg_type_values, "unknown (%u)",msg_type),
+ len);
+ return;
+ }
+
+ /* ok they seem to want to know everything - lets fully decode it */
+
+ plen = tlen = GET_BE_U_2(rsvp_com_header->length);
+
+ ND_PRINT("\n\tRSVPv%u %s Message (%u), Flags: [%s], length: %u, ttl: %u, checksum: 0x%04x",
+ RSVP_EXTRACT_VERSION(version_flags),
+ tok2str(rsvp_msg_type_values, "unknown, type: %u",msg_type),
+ msg_type,
+ bittok2str(rsvp_header_flag_values,"none",RSVP_EXTRACT_FLAGS(version_flags)),
+ tlen,
+ GET_U_1(rsvp_com_header->ttl),
+ GET_BE_U_2(rsvp_com_header->checksum));
+
+ if (tlen < sizeof(struct rsvp_common_header)) {
+ ND_PRINT("ERROR: common header too short %u < %zu", tlen,
+ sizeof(struct rsvp_common_header));
+ return;
+ }
+
+ tptr+=sizeof(struct rsvp_common_header);
+ tlen-=sizeof(struct rsvp_common_header);
+
+ switch(msg_type) {
+
+ case RSVP_MSGTYPE_BUNDLE:
+ /*
+ * Process each submessage in the bundle message.
+ * Bundle messages may not contain bundle submessages, so we don't
+ * need to handle bundle submessages specially.
+ */
+ while(tlen > 0) {
+ const u_char *subpptr=tptr, *subtptr;
+ u_short subplen, subtlen;
+
+ subtptr=subpptr;
+
+ rsvp_com_header = (const struct rsvp_common_header *)subpptr;
+ ND_TCHECK_SIZE(rsvp_com_header);
+ version_flags = GET_U_1(rsvp_com_header->version_flags);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (RSVP_EXTRACT_VERSION(version_flags) != RSVP_VERSION) {
+ ND_PRINT("ERROR: RSVP version %u packet not supported",
+ RSVP_EXTRACT_VERSION(version_flags));
+ return;
+ }
+
+ subplen = subtlen = GET_BE_U_2(rsvp_com_header->length);
+
+ msg_type = GET_U_1(rsvp_com_header->msg_type);
+ ND_PRINT("\n\t RSVPv%u %s Message (%u), Flags: [%s], length: %u, ttl: %u, checksum: 0x%04x",
+ RSVP_EXTRACT_VERSION(version_flags),
+ tok2str(rsvp_msg_type_values, "unknown, type: %u",msg_type),
+ msg_type,
+ bittok2str(rsvp_header_flag_values,"none",RSVP_EXTRACT_FLAGS(version_flags)),
+ subtlen,
+ GET_U_1(rsvp_com_header->ttl),
+ GET_BE_U_2(rsvp_com_header->checksum));
+
+ if (subtlen < sizeof(struct rsvp_common_header)) {
+ ND_PRINT("ERROR: common header too short %u < %zu", subtlen,
+ sizeof(struct rsvp_common_header));
+ return;
+ }
+
+ if (tlen < subtlen) {
+ ND_PRINT("ERROR: common header too large %u > %u", subtlen,
+ tlen);
+ return;
+ }
+
+ subtptr+=sizeof(struct rsvp_common_header);
+ subtlen-=sizeof(struct rsvp_common_header);
+
+ /*
+ * Print all objects in the submessage.
+ */
+ if (rsvp_obj_print(ndo, subpptr, subplen, subtptr, "\n\t ", subtlen, rsvp_com_header) == -1)
+ return;
+
+ tptr+=subtlen+sizeof(struct rsvp_common_header);
+ tlen-=subtlen+sizeof(struct rsvp_common_header);
+ }
+
+ break;
+
+ case RSVP_MSGTYPE_PATH:
+ case RSVP_MSGTYPE_RESV:
+ case RSVP_MSGTYPE_PATHERR:
+ case RSVP_MSGTYPE_RESVERR:
+ case RSVP_MSGTYPE_PATHTEAR:
+ case RSVP_MSGTYPE_RESVTEAR:
+ case RSVP_MSGTYPE_RESVCONF:
+ case RSVP_MSGTYPE_HELLO_OLD:
+ case RSVP_MSGTYPE_HELLO:
+ case RSVP_MSGTYPE_ACK:
+ case RSVP_MSGTYPE_SREFRESH:
+ /*
+ * Print all objects in the message.
+ */
+ if (rsvp_obj_print(ndo, pptr, plen, tptr, "\n\t ", tlen, rsvp_com_header) == -1)
+ return;
+ break;
+
+ default:
+ print_unknown_data(ndo, tptr, "\n\t ", tlen);
+ break;
+ }
+
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-rt6.c b/print-rt6.c
new file mode 100644
index 0000000..096a962
--- /dev/null
+++ b/print-rt6.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: IPv6 routing header printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip6.h"
+
+int
+rt6_print(netdissect_options *ndo, const u_char *bp, const u_char *bp2 _U_)
+{
+ const struct ip6_rthdr *dp;
+ const struct ip6_rthdr0 *dp0;
+ const struct ip6_srh *srh;
+ u_int i, len, type;
+ const u_char *p;
+
+ ndo->ndo_protocol = "rt6";
+
+ nd_print_protocol_caps(ndo);
+ dp = (const struct ip6_rthdr *)bp;
+
+ len = GET_U_1(dp->ip6r_len);
+ ND_PRINT(" (len=%u", len); /*)*/
+ type = GET_U_1(dp->ip6r_type);
+ ND_PRINT(", type=%u", type);
+ if (type == IPV6_RTHDR_TYPE_0)
+ ND_PRINT(" [Deprecated]");
+ ND_PRINT(", segleft=%u", GET_U_1(dp->ip6r_segleft));
+
+ switch (type) {
+ case IPV6_RTHDR_TYPE_0:
+ case IPV6_RTHDR_TYPE_2: /* Mobile IPv6 ID-20 */
+ dp0 = (const struct ip6_rthdr0 *)dp;
+
+ if (GET_BE_U_4(dp0->ip6r0_reserved) || ndo->ndo_vflag) {
+ ND_PRINT(", rsv=0x%0x",
+ GET_BE_U_4(dp0->ip6r0_reserved));
+ }
+
+ if (len % 2 == 1) {
+ ND_PRINT(" (invalid length %u)", len);
+ goto invalid;
+ }
+ len >>= 1;
+ p = (const u_char *) dp0->ip6r0_addr;
+ for (i = 0; i < len; i++) {
+ ND_PRINT(", [%u]%s", i, GET_IP6ADDR_STRING(p));
+ p += 16;
+ }
+ /*(*/
+ ND_PRINT(") ");
+ return((GET_U_1(dp0->ip6r0_len) + 1) << 3);
+ break;
+ case IPV6_RTHDR_TYPE_4:
+ srh = (const struct ip6_srh *)dp;
+ ND_PRINT(", last-entry=%u", GET_U_1(srh->srh_last_ent));
+
+ if (GET_U_1(srh->srh_flags) || ndo->ndo_vflag) {
+ ND_PRINT(", flags=0x%0x",
+ GET_U_1(srh->srh_flags));
+ }
+
+ ND_PRINT(", tag=%x", GET_BE_U_2(srh->srh_tag));
+
+ if (len % 2 == 1) {
+ ND_PRINT(" (invalid length %u)", len);
+ goto invalid;
+ }
+ len >>= 1;
+ p = (const u_char *) srh->srh_segments;
+ for (i = 0; i < len; i++) {
+ ND_PRINT(", [%u]%s", i, GET_IP6ADDR_STRING(p));
+ p += 16;
+ }
+ /*(*/
+ ND_PRINT(") ");
+ return((GET_U_1(srh->srh_len) + 1) << 3);
+ break;
+ default:
+ ND_PRINT(" (unknown type)");
+ goto invalid;
+ }
+
+invalid:
+ nd_print_invalid(ndo);
+ return -1;
+}
diff --git a/print-rtsp.c b/print-rtsp.c
new file mode 100644
index 0000000..626a5ed
--- /dev/null
+++ b/print-rtsp.c
@@ -0,0 +1,44 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Real Time Streaming Protocol (RTSP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+static const char *rtspcmds[] = {
+ "DESCRIBE",
+ "ANNOUNCE",
+ "GET_PARAMETER",
+ "OPTIONS",
+ "PAUSE",
+ "PLAY",
+ "RECORD",
+ "REDIRECT",
+ "SETUP",
+ "SET_PARAMETER",
+ "TEARDOWN",
+ NULL
+};
+
+void
+rtsp_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ ndo->ndo_protocol = "rtsp";
+ txtproto_print(ndo, pptr, len, rtspcmds, RESP_CODE_SECOND_TOKEN);
+}
diff --git a/print-rx.c b/print-rx.c
new file mode 100644
index 0000000..b8ee5a8
--- /dev/null
+++ b/print-rx.c
@@ -0,0 +1,2841 @@
+/*
+ * Copyright: (c) 2000 United States Government as represented by the
+ * Secretary of the Navy. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: AFS RX printer */
+
+/*
+ * This code unmangles RX packets. RX is the mutant form of RPC that AFS
+ * uses to communicate between clients and servers.
+ *
+ * In this code, I mainly concern myself with decoding the AFS calls, not
+ * with the guts of RX, per se.
+ *
+ * Bah. If I never look at rx_packet.h again, it will be too soon.
+ *
+ * Ken Hornstein <kenh@cmf.nrl.navy.mil>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "ip.h"
+
+#define FS_RX_PORT 7000
+#define CB_RX_PORT 7001
+#define PROT_RX_PORT 7002
+#define VLDB_RX_PORT 7003
+#define KAUTH_RX_PORT 7004
+#define VOL_RX_PORT 7005
+#define ERROR_RX_PORT 7006 /* Doesn't seem to be used */
+#define BOS_RX_PORT 7007
+
+#define AFSOPAQUEMAX 1024
+#define AFSNAMEMAX 256 /* Must be >= PRNAMEMAX + 1, VLNAMEMAX + 1, and 32 + 1 */
+#define PRNAMEMAX 64
+#define VLNAMEMAX 65
+#define KANAMEMAX 64
+#define BOSNAMEMAX 256
+#define USERNAMEMAX 1024 /* AFSOPAQUEMAX was used for this; does it need to be this big? */
+
+#define PRSFS_READ 1 /* Read files */
+#define PRSFS_WRITE 2 /* Write files */
+#define PRSFS_INSERT 4 /* Insert files into a directory */
+#define PRSFS_LOOKUP 8 /* Lookup files into a directory */
+#define PRSFS_DELETE 16 /* Delete files */
+#define PRSFS_LOCK 32 /* Lock files */
+#define PRSFS_ADMINISTER 64 /* Change ACL's */
+
+struct rx_header {
+ nd_uint32_t epoch;
+ nd_uint32_t cid;
+ nd_uint32_t callNumber;
+ nd_uint32_t seq;
+ nd_uint32_t serial;
+ nd_uint8_t type;
+#define RX_PACKET_TYPE_DATA 1
+#define RX_PACKET_TYPE_ACK 2
+#define RX_PACKET_TYPE_BUSY 3
+#define RX_PACKET_TYPE_ABORT 4
+#define RX_PACKET_TYPE_ACKALL 5
+#define RX_PACKET_TYPE_CHALLENGE 6
+#define RX_PACKET_TYPE_RESPONSE 7
+#define RX_PACKET_TYPE_DEBUG 8
+#define RX_PACKET_TYPE_PARAMS 9
+#define RX_PACKET_TYPE_VERSION 13
+ nd_uint8_t flags;
+#define RX_CLIENT_INITIATED 1
+#define RX_REQUEST_ACK 2
+#define RX_LAST_PACKET 4
+#define RX_MORE_PACKETS 8
+#define RX_FREE_PACKET 16
+#define RX_SLOW_START_OK 32
+#define RX_JUMBO_PACKET 32
+ nd_uint8_t userStatus;
+ nd_uint8_t securityIndex;
+ nd_uint16_t spare; /* How clever: even though the AFS */
+ nd_uint16_t serviceId; /* header files indicate that the */
+}; /* serviceId is first, it's really */
+ /* encoded _after_ the spare field */
+ /* I wasted a day figuring that out! */
+
+#define NUM_RX_FLAGS 7
+
+#define RX_MAXACKS 255
+
+struct rx_ackPacket {
+ nd_uint16_t bufferSpace; /* Number of packet buffers available */
+ nd_uint16_t maxSkew; /* Max diff between ack'd packet and */
+ /* highest packet received */
+ nd_uint32_t firstPacket; /* The first packet in ack list */
+ nd_uint32_t previousPacket; /* Previous packet recv'd (obsolete) */
+ nd_uint32_t serial; /* # of packet that prompted the ack */
+ nd_uint8_t reason; /* Reason for acknowledgement */
+ nd_uint8_t nAcks; /* Number of acknowledgements */
+ /* Followed by nAcks acknowledgments */
+#if 0
+ uint8_t acks[RX_MAXACKS]; /* Up to RX_MAXACKS acknowledgements */
+#endif
+};
+
+/*
+ * Values for the acks array
+ */
+
+#define RX_ACK_TYPE_NACK 0 /* Don't have this packet */
+#define RX_ACK_TYPE_ACK 1 /* I have this packet */
+
+static const struct tok rx_types[] = {
+ { RX_PACKET_TYPE_DATA, "data" },
+ { RX_PACKET_TYPE_ACK, "ack" },
+ { RX_PACKET_TYPE_BUSY, "busy" },
+ { RX_PACKET_TYPE_ABORT, "abort" },
+ { RX_PACKET_TYPE_ACKALL, "ackall" },
+ { RX_PACKET_TYPE_CHALLENGE, "challenge" },
+ { RX_PACKET_TYPE_RESPONSE, "response" },
+ { RX_PACKET_TYPE_DEBUG, "debug" },
+ { RX_PACKET_TYPE_PARAMS, "params" },
+ { RX_PACKET_TYPE_VERSION, "version" },
+ { 0, NULL },
+};
+
+static const struct double_tok {
+ uint32_t flag; /* Rx flag */
+ uint32_t packetType; /* Packet type */
+ const char *s; /* Flag string */
+} rx_flags[] = {
+ { RX_CLIENT_INITIATED, 0, "client-init" },
+ { RX_REQUEST_ACK, 0, "req-ack" },
+ { RX_LAST_PACKET, 0, "last-pckt" },
+ { RX_MORE_PACKETS, 0, "more-pckts" },
+ { RX_FREE_PACKET, 0, "free-pckt" },
+ { RX_SLOW_START_OK, RX_PACKET_TYPE_ACK, "slow-start" },
+ { RX_JUMBO_PACKET, RX_PACKET_TYPE_DATA, "jumbogram" }
+};
+
+static const struct tok fs_req[] = {
+ { 130, "fetch-data" },
+ { 131, "fetch-acl" },
+ { 132, "fetch-status" },
+ { 133, "store-data" },
+ { 134, "store-acl" },
+ { 135, "store-status" },
+ { 136, "remove-file" },
+ { 137, "create-file" },
+ { 138, "rename" },
+ { 139, "symlink" },
+ { 140, "link" },
+ { 141, "makedir" },
+ { 142, "rmdir" },
+ { 143, "oldsetlock" },
+ { 144, "oldextlock" },
+ { 145, "oldrellock" },
+ { 146, "get-stats" },
+ { 147, "give-cbs" },
+ { 148, "get-vlinfo" },
+ { 149, "get-vlstats" },
+ { 150, "set-vlstats" },
+ { 151, "get-rootvl" },
+ { 152, "check-token" },
+ { 153, "get-time" },
+ { 154, "nget-vlinfo" },
+ { 155, "bulk-stat" },
+ { 156, "setlock" },
+ { 157, "extlock" },
+ { 158, "rellock" },
+ { 159, "xstat-ver" },
+ { 160, "get-xstat" },
+ { 161, "dfs-lookup" },
+ { 162, "dfs-flushcps" },
+ { 163, "dfs-symlink" },
+ { 220, "residency" },
+ { 65536, "inline-bulk-status" },
+ { 65537, "fetch-data-64" },
+ { 65538, "store-data-64" },
+ { 65539, "give-up-all-cbs" },
+ { 65540, "get-caps" },
+ { 65541, "cb-rx-conn-addr" },
+ { 0, NULL },
+};
+
+static const struct tok cb_req[] = {
+ { 204, "callback" },
+ { 205, "initcb" },
+ { 206, "probe" },
+ { 207, "getlock" },
+ { 208, "getce" },
+ { 209, "xstatver" },
+ { 210, "getxstat" },
+ { 211, "initcb2" },
+ { 212, "whoareyou" },
+ { 213, "initcb3" },
+ { 214, "probeuuid" },
+ { 215, "getsrvprefs" },
+ { 216, "getcellservdb" },
+ { 217, "getlocalcell" },
+ { 218, "getcacheconf" },
+ { 65536, "getce64" },
+ { 65537, "getcellbynum" },
+ { 65538, "tellmeaboutyourself" },
+ { 0, NULL },
+};
+
+static const struct tok pt_req[] = {
+ { 500, "new-user" },
+ { 501, "where-is-it" },
+ { 502, "dump-entry" },
+ { 503, "add-to-group" },
+ { 504, "name-to-id" },
+ { 505, "id-to-name" },
+ { 506, "delete" },
+ { 507, "remove-from-group" },
+ { 508, "get-cps" },
+ { 509, "new-entry" },
+ { 510, "list-max" },
+ { 511, "set-max" },
+ { 512, "list-entry" },
+ { 513, "change-entry" },
+ { 514, "list-elements" },
+ { 515, "same-mbr-of" },
+ { 516, "set-fld-sentry" },
+ { 517, "list-owned" },
+ { 518, "get-cps2" },
+ { 519, "get-host-cps" },
+ { 520, "update-entry" },
+ { 521, "list-entries" },
+ { 530, "list-super-groups" },
+ { 0, NULL },
+};
+
+static const struct tok vldb_req[] = {
+ { 501, "create-entry" },
+ { 502, "delete-entry" },
+ { 503, "get-entry-by-id" },
+ { 504, "get-entry-by-name" },
+ { 505, "get-new-volume-id" },
+ { 506, "replace-entry" },
+ { 507, "update-entry" },
+ { 508, "setlock" },
+ { 509, "releaselock" },
+ { 510, "list-entry" },
+ { 511, "list-attrib" },
+ { 512, "linked-list" },
+ { 513, "get-stats" },
+ { 514, "probe" },
+ { 515, "get-addrs" },
+ { 516, "change-addr" },
+ { 517, "create-entry-n" },
+ { 518, "get-entry-by-id-n" },
+ { 519, "get-entry-by-name-n" },
+ { 520, "replace-entry-n" },
+ { 521, "list-entry-n" },
+ { 522, "list-attrib-n" },
+ { 523, "linked-list-n" },
+ { 524, "update-entry-by-name" },
+ { 525, "create-entry-u" },
+ { 526, "get-entry-by-id-u" },
+ { 527, "get-entry-by-name-u" },
+ { 528, "replace-entry-u" },
+ { 529, "list-entry-u" },
+ { 530, "list-attrib-u" },
+ { 531, "linked-list-u" },
+ { 532, "regaddr" },
+ { 533, "get-addrs-u" },
+ { 534, "list-attrib-n2" },
+ { 0, NULL },
+};
+
+static const struct tok kauth_req[] = {
+ { 1, "auth-old" },
+ { 21, "authenticate" },
+ { 22, "authenticate-v2" },
+ { 2, "change-pw" },
+ { 3, "get-ticket-old" },
+ { 23, "get-ticket" },
+ { 4, "set-pw" },
+ { 5, "set-fields" },
+ { 6, "create-user" },
+ { 7, "delete-user" },
+ { 8, "get-entry" },
+ { 9, "list-entry" },
+ { 10, "get-stats" },
+ { 11, "debug" },
+ { 12, "get-pw" },
+ { 13, "get-random-key" },
+ { 14, "unlock" },
+ { 15, "lock-status" },
+ { 0, NULL },
+};
+
+static const struct tok vol_req[] = {
+ { 100, "create-volume" },
+ { 101, "delete-volume" },
+ { 102, "restore" },
+ { 103, "forward" },
+ { 104, "end-trans" },
+ { 105, "clone" },
+ { 106, "set-flags" },
+ { 107, "get-flags" },
+ { 108, "trans-create" },
+ { 109, "dump" },
+ { 110, "get-nth-volume" },
+ { 111, "set-forwarding" },
+ { 112, "get-name" },
+ { 113, "get-status" },
+ { 114, "sig-restore" },
+ { 115, "list-partitions" },
+ { 116, "list-volumes" },
+ { 117, "set-id-types" },
+ { 118, "monitor" },
+ { 119, "partition-info" },
+ { 120, "reclone" },
+ { 121, "list-one-volume" },
+ { 122, "nuke" },
+ { 123, "set-date" },
+ { 124, "x-list-volumes" },
+ { 125, "x-list-one-volume" },
+ { 126, "set-info" },
+ { 127, "x-list-partitions" },
+ { 128, "forward-multiple" },
+ { 65536, "convert-ro" },
+ { 65537, "get-size" },
+ { 65538, "dump-v2" },
+ { 0, NULL },
+};
+
+static const struct tok bos_req[] = {
+ { 80, "create-bnode" },
+ { 81, "delete-bnode" },
+ { 82, "set-status" },
+ { 83, "get-status" },
+ { 84, "enumerate-instance" },
+ { 85, "get-instance-info" },
+ { 86, "get-instance-parm" },
+ { 87, "add-superuser" },
+ { 88, "delete-superuser" },
+ { 89, "list-superusers" },
+ { 90, "list-keys" },
+ { 91, "add-key" },
+ { 92, "delete-key" },
+ { 93, "set-cell-name" },
+ { 94, "get-cell-name" },
+ { 95, "get-cell-host" },
+ { 96, "add-cell-host" },
+ { 97, "delete-cell-host" },
+ { 98, "set-t-status" },
+ { 99, "shutdown-all" },
+ { 100, "restart-all" },
+ { 101, "startup-all" },
+ { 102, "set-noauth-flag" },
+ { 103, "re-bozo" },
+ { 104, "restart" },
+ { 105, "start-bozo-install" },
+ { 106, "uninstall" },
+ { 107, "get-dates" },
+ { 108, "exec" },
+ { 109, "prune" },
+ { 110, "set-restart-time" },
+ { 111, "get-restart-time" },
+ { 112, "start-bozo-log" },
+ { 113, "wait-all" },
+ { 114, "get-instance-strings" },
+ { 115, "get-restricted" },
+ { 116, "set-restricted" },
+ { 0, NULL },
+};
+
+static const struct tok ubik_req[] = {
+ { 10000, "vote-beacon" },
+ { 10001, "vote-debug-old" },
+ { 10002, "vote-sdebug-old" },
+ { 10003, "vote-getsyncsite" },
+ { 10004, "vote-debug" },
+ { 10005, "vote-sdebug" },
+ { 10006, "vote-xdebug" },
+ { 10007, "vote-xsdebug" },
+ { 20000, "disk-begin" },
+ { 20001, "disk-commit" },
+ { 20002, "disk-lock" },
+ { 20003, "disk-write" },
+ { 20004, "disk-getversion" },
+ { 20005, "disk-getfile" },
+ { 20006, "disk-sendfile" },
+ { 20007, "disk-abort" },
+ { 20008, "disk-releaselocks" },
+ { 20009, "disk-truncate" },
+ { 20010, "disk-probe" },
+ { 20011, "disk-writev" },
+ { 20012, "disk-interfaceaddr" },
+ { 20013, "disk-setversion" },
+ { 0, NULL },
+};
+
+#define VOTE_LOW 10000
+#define VOTE_HIGH 10007
+#define DISK_LOW 20000
+#define DISK_HIGH 20013
+
+static const struct tok cb_types[] = {
+ { 1, "exclusive" },
+ { 2, "shared" },
+ { 3, "dropped" },
+ { 0, NULL },
+};
+
+static const struct tok ubik_lock_types[] = {
+ { 1, "read" },
+ { 2, "write" },
+ { 3, "wait" },
+ { 0, NULL },
+};
+
+static const char *voltype[] = { "read-write", "read-only", "backup" };
+
+static const struct tok afs_fs_errors[] = {
+ { 101, "salvage volume" },
+ { 102, "no such vnode" },
+ { 103, "no such volume" },
+ { 104, "volume exist" },
+ { 105, "no service" },
+ { 106, "volume offline" },
+ { 107, "voline online" },
+ { 108, "diskfull" },
+ { 109, "diskquota exceeded" },
+ { 110, "volume busy" },
+ { 111, "volume moved" },
+ { 112, "AFS IO error" },
+ { 0xffffff9c, "restarting fileserver" }, /* -100, sic! */
+ { 0, NULL }
+};
+
+/*
+ * Reasons for acknowledging a packet
+ */
+
+static const struct tok rx_ack_reasons[] = {
+ { 1, "ack requested" },
+ { 2, "duplicate packet" },
+ { 3, "out of sequence" },
+ { 4, "exceeds window" },
+ { 5, "no buffer space" },
+ { 6, "ping" },
+ { 7, "ping response" },
+ { 8, "delay" },
+ { 9, "idle" },
+ { 0, NULL },
+};
+
+/*
+ * Cache entries we keep around so we can figure out the RX opcode
+ * numbers for replies. This allows us to make sense of RX reply packets.
+ */
+
+struct rx_cache_entry {
+ uint32_t callnum; /* Call number (net order) */
+ uint32_t client; /* client IP address (net order) */
+ uint32_t server; /* server IP address (net order) */
+ uint16_t dport; /* server UDP port (host order) */
+ uint16_t serviceId; /* Service identifier (net order) */
+ uint32_t opcode; /* RX opcode (host order) */
+};
+
+#define RX_CACHE_SIZE 64
+
+static struct rx_cache_entry rx_cache[RX_CACHE_SIZE];
+
+static uint32_t rx_cache_next = 0;
+static uint32_t rx_cache_hint = 0;
+static void rx_cache_insert(netdissect_options *, const u_char *, const struct ip *, uint16_t);
+static int rx_cache_find(netdissect_options *, const struct rx_header *,
+ const struct ip *, uint16_t, uint32_t *);
+
+static void fs_print(netdissect_options *, const u_char *, u_int);
+static void fs_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+static void acl_print(netdissect_options *, u_char *, u_char *);
+static void cb_print(netdissect_options *, const u_char *, u_int);
+static void cb_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+static void prot_print(netdissect_options *, const u_char *, u_int);
+static void prot_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+static void vldb_print(netdissect_options *, const u_char *, u_int);
+static void vldb_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+static void kauth_print(netdissect_options *, const u_char *, u_int);
+static void kauth_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+static void vol_print(netdissect_options *, const u_char *, u_int);
+static void vol_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+static void bos_print(netdissect_options *, const u_char *, u_int);
+static void bos_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+static void ubik_print(netdissect_options *, const u_char *);
+static void ubik_reply_print(netdissect_options *, const u_char *, u_int, uint32_t);
+
+static void rx_ack_print(netdissect_options *, const u_char *, u_int);
+
+static int is_ubik(uint32_t);
+
+/*
+ * Handle the rx-level packet. See if we know what port it's going to so
+ * we can peek at the afs call inside
+ */
+
+void
+rx_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint16_t sport, uint16_t dport,
+ const u_char *bp2)
+{
+ const struct rx_header *rxh;
+ uint32_t i;
+ uint8_t type, flags;
+ uint32_t opcode;
+
+ ndo->ndo_protocol = "rx";
+ if (!ND_TTEST_LEN(bp, sizeof(struct rx_header))) {
+ ND_PRINT(" [|rx] (%u)", length);
+ return;
+ }
+
+ rxh = (const struct rx_header *) bp;
+
+ type = GET_U_1(rxh->type);
+ ND_PRINT(" rx %s", tok2str(rx_types, "type %u", type));
+
+ flags = GET_U_1(rxh->flags);
+ if (ndo->ndo_vflag) {
+ int firstflag = 0;
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT(" cid %08x call# %u",
+ GET_BE_U_4(rxh->cid),
+ GET_BE_U_4(rxh->callNumber));
+
+ ND_PRINT(" seq %u ser %u",
+ GET_BE_U_4(rxh->seq),
+ GET_BE_U_4(rxh->serial));
+
+ if (ndo->ndo_vflag > 2)
+ ND_PRINT(" secindex %u serviceid %hu",
+ GET_U_1(rxh->securityIndex),
+ GET_BE_U_2(rxh->serviceId));
+
+ if (ndo->ndo_vflag > 1)
+ for (i = 0; i < NUM_RX_FLAGS; i++) {
+ if (flags & rx_flags[i].flag &&
+ (!rx_flags[i].packetType ||
+ type == rx_flags[i].packetType)) {
+ if (!firstflag) {
+ firstflag = 1;
+ ND_PRINT(" ");
+ } else {
+ ND_PRINT(",");
+ }
+ ND_PRINT("<%s>", rx_flags[i].s);
+ }
+ }
+ }
+
+ /*
+ * Try to handle AFS calls that we know about. Check the destination
+ * port and make sure it's a data packet. Also, make sure the
+ * seq number is 1 (because otherwise it's a continuation packet,
+ * and we can't interpret that). Also, seems that reply packets
+ * do not have the client-init flag set, so we check for that
+ * as well.
+ */
+
+ if (type == RX_PACKET_TYPE_DATA &&
+ GET_BE_U_4(rxh->seq) == 1 &&
+ flags & RX_CLIENT_INITIATED) {
+
+ /*
+ * Insert this call into the call cache table, so we
+ * have a chance to print out replies
+ */
+
+ rx_cache_insert(ndo, bp, (const struct ip *) bp2, dport);
+
+ switch (dport) {
+ case FS_RX_PORT: /* AFS file service */
+ fs_print(ndo, bp, length);
+ break;
+ case CB_RX_PORT: /* AFS callback service */
+ cb_print(ndo, bp, length);
+ break;
+ case PROT_RX_PORT: /* AFS protection service */
+ prot_print(ndo, bp, length);
+ break;
+ case VLDB_RX_PORT: /* AFS VLDB service */
+ vldb_print(ndo, bp, length);
+ break;
+ case KAUTH_RX_PORT: /* AFS Kerberos auth service */
+ kauth_print(ndo, bp, length);
+ break;
+ case VOL_RX_PORT: /* AFS Volume service */
+ vol_print(ndo, bp, length);
+ break;
+ case BOS_RX_PORT: /* AFS BOS service */
+ bos_print(ndo, bp, length);
+ break;
+ default:
+ ;
+ }
+
+ /*
+ * If it's a reply (client-init is _not_ set, but seq is one)
+ * then look it up in the cache. If we find it, call the reply
+ * printing functions Note that we handle abort packets here,
+ * because printing out the return code can be useful at times.
+ */
+
+ } else if (((type == RX_PACKET_TYPE_DATA &&
+ GET_BE_U_4(rxh->seq) == 1) ||
+ type == RX_PACKET_TYPE_ABORT) &&
+ (flags & RX_CLIENT_INITIATED) == 0 &&
+ rx_cache_find(ndo, rxh, (const struct ip *) bp2,
+ sport, &opcode)) {
+
+ switch (sport) {
+ case FS_RX_PORT: /* AFS file service */
+ fs_reply_print(ndo, bp, length, opcode);
+ break;
+ case CB_RX_PORT: /* AFS callback service */
+ cb_reply_print(ndo, bp, length, opcode);
+ break;
+ case PROT_RX_PORT: /* AFS PT service */
+ prot_reply_print(ndo, bp, length, opcode);
+ break;
+ case VLDB_RX_PORT: /* AFS VLDB service */
+ vldb_reply_print(ndo, bp, length, opcode);
+ break;
+ case KAUTH_RX_PORT: /* AFS Kerberos auth service */
+ kauth_reply_print(ndo, bp, length, opcode);
+ break;
+ case VOL_RX_PORT: /* AFS Volume service */
+ vol_reply_print(ndo, bp, length, opcode);
+ break;
+ case BOS_RX_PORT: /* AFS BOS service */
+ bos_reply_print(ndo, bp, length, opcode);
+ break;
+ default:
+ ;
+ }
+
+ /*
+ * If it's an RX ack packet, then use the appropriate ack decoding
+ * function (there isn't any service-specific information in the
+ * ack packet, so we can use one for all AFS services)
+ */
+
+ } else if (type == RX_PACKET_TYPE_ACK)
+ rx_ack_print(ndo, bp, length);
+
+
+ ND_PRINT(" (%u)", length);
+}
+
+/*
+ * Insert an entry into the cache. Taken from print-nfs.c
+ */
+
+static void
+rx_cache_insert(netdissect_options *ndo,
+ const u_char *bp, const struct ip *ip, uint16_t dport)
+{
+ struct rx_cache_entry *rxent;
+ const struct rx_header *rxh = (const struct rx_header *) bp;
+
+ if (!ND_TTEST_4(bp + sizeof(struct rx_header)))
+ return;
+
+ rxent = &rx_cache[rx_cache_next];
+
+ if (++rx_cache_next >= RX_CACHE_SIZE)
+ rx_cache_next = 0;
+
+ rxent->callnum = GET_BE_U_4(rxh->callNumber);
+ rxent->client = GET_IPV4_TO_NETWORK_ORDER(ip->ip_src);
+ rxent->server = GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst);
+ rxent->dport = dport;
+ rxent->serviceId = GET_BE_U_2(rxh->serviceId);
+ rxent->opcode = GET_BE_U_4(bp + sizeof(struct rx_header));
+}
+
+/*
+ * Lookup an entry in the cache. Also taken from print-nfs.c
+ *
+ * Note that because this is a reply, we're looking at the _source_
+ * port.
+ */
+
+static int
+rx_cache_find(netdissect_options *ndo, const struct rx_header *rxh,
+ const struct ip *ip, uint16_t sport, uint32_t *opcode)
+{
+ uint32_t i;
+ struct rx_cache_entry *rxent;
+ uint32_t clip;
+ uint32_t sip;
+
+ clip = GET_IPV4_TO_NETWORK_ORDER(ip->ip_dst);
+ sip = GET_IPV4_TO_NETWORK_ORDER(ip->ip_src);
+
+ /* Start the search where we last left off */
+
+ i = rx_cache_hint;
+ do {
+ rxent = &rx_cache[i];
+ if (rxent->callnum == GET_BE_U_4(rxh->callNumber) &&
+ rxent->client == clip &&
+ rxent->server == sip &&
+ rxent->serviceId == GET_BE_U_2(rxh->serviceId) &&
+ rxent->dport == sport) {
+
+ /* We got a match! */
+
+ rx_cache_hint = i;
+ *opcode = rxent->opcode;
+ return(1);
+ }
+ if (++i >= RX_CACHE_SIZE)
+ i = 0;
+ } while (i != rx_cache_hint);
+
+ /* Our search failed */
+ return(0);
+}
+
+/*
+ * These extremely grody macros handle the printing of various AFS stuff.
+ */
+
+#define FIDOUT() { uint32_t n1, n2, n3; \
+ ND_TCHECK_LEN(bp, sizeof(uint32_t) * 3); \
+ n1 = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ n2 = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ n3 = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ ND_PRINT(" fid %u/%u/%u", n1, n2, n3); \
+ }
+
+#define STROUT(MAX) { uint32_t _i; \
+ _i = GET_BE_U_4(bp); \
+ if (_i > (MAX)) \
+ goto trunc; \
+ bp += sizeof(uint32_t); \
+ ND_PRINT(" \""); \
+ if (nd_printn(ndo, bp, _i, ndo->ndo_snapend)) \
+ goto trunc; \
+ ND_PRINT("\""); \
+ bp += ((_i + sizeof(uint32_t) - 1) / sizeof(uint32_t)) * sizeof(uint32_t); \
+ }
+
+#define INTOUT() { int32_t _i; \
+ _i = GET_BE_S_4(bp); \
+ bp += sizeof(int32_t); \
+ ND_PRINT(" %d", _i); \
+ }
+
+#define UINTOUT() { uint32_t _i; \
+ _i = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ ND_PRINT(" %u", _i); \
+ }
+
+#define UINT64OUT() { uint64_t _i; \
+ _i = GET_BE_U_8(bp); \
+ bp += sizeof(uint64_t); \
+ ND_PRINT(" %" PRIu64, _i); \
+ }
+
+#define DATEOUT() { time_t _t; struct tm *tm; char str[256]; \
+ _t = (time_t) GET_BE_S_4(bp); \
+ bp += sizeof(int32_t); \
+ tm = localtime(&_t); \
+ strftime(str, 256, "%Y/%m/%d %H:%M:%S", tm); \
+ ND_PRINT(" %s", str); \
+ }
+
+#define STOREATTROUT() { uint32_t mask, _i; \
+ ND_TCHECK_LEN(bp, (sizeof(uint32_t) * 6)); \
+ mask = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
+ if (mask) ND_PRINT(" StoreStatus"); \
+ if (mask & 1) { ND_PRINT(" date"); DATEOUT(); } \
+ else bp += sizeof(uint32_t); \
+ _i = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
+ if (mask & 2) ND_PRINT(" owner %u", _i); \
+ _i = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
+ if (mask & 4) ND_PRINT(" group %u", _i); \
+ _i = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
+ if (mask & 8) ND_PRINT(" mode %o", _i & 07777); \
+ _i = GET_BE_U_4(bp); bp += sizeof(uint32_t); \
+ if (mask & 16) ND_PRINT(" segsize %u", _i); \
+ /* undocumented in 3.3 docu */ \
+ if (mask & 1024) ND_PRINT(" fsync"); \
+ }
+
+#define UBIK_VERSIONOUT() {uint32_t epoch; uint32_t counter; \
+ ND_TCHECK_LEN(bp, sizeof(uint32_t) * 2); \
+ epoch = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ counter = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ ND_PRINT(" %u.%u", epoch, counter); \
+ }
+
+#define AFSUUIDOUT() {uint32_t temp; int _i; \
+ ND_TCHECK_LEN(bp, 11 * sizeof(uint32_t)); \
+ temp = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ ND_PRINT(" %08x", temp); \
+ temp = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ ND_PRINT("%04x", temp); \
+ temp = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ ND_PRINT("%04x", temp); \
+ for (_i = 0; _i < 8; _i++) { \
+ temp = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ ND_PRINT("%02x", (unsigned char) temp); \
+ } \
+ }
+
+/*
+ * This is the sickest one of all
+ * MAX is expected to be a constant here
+ */
+
+#define VECOUT(MAX) { u_char *sp; \
+ u_char s[(MAX) + 1]; \
+ uint32_t k; \
+ ND_TCHECK_LEN(bp, (MAX) * sizeof(uint32_t)); \
+ sp = s; \
+ for (k = 0; k < (MAX); k++) { \
+ *sp++ = (u_char) GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ } \
+ s[(MAX)] = '\0'; \
+ ND_PRINT(" \""); \
+ fn_print_str(ndo, s); \
+ ND_PRINT("\""); \
+ }
+
+#define DESTSERVEROUT() { uint32_t n1, n2, n3; \
+ ND_TCHECK_LEN(bp, sizeof(uint32_t) * 3); \
+ n1 = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ n2 = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ n3 = GET_BE_U_4(bp); \
+ bp += sizeof(uint32_t); \
+ ND_PRINT(" server %u:%u:%u", n1, n2, n3); \
+ }
+
+/*
+ * Handle calls to the AFS file service (fs)
+ */
+
+static void
+fs_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ uint32_t fs_op;
+ uint32_t i;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from fsint/afsint.xg
+ */
+
+ fs_op = GET_BE_U_4(bp + sizeof(struct rx_header));
+
+ ND_PRINT(" fs call %s", tok2str(fs_req, "op#%u", fs_op));
+
+ /*
+ * Print out arguments to some of the AFS calls. This stuff is
+ * all from afsint.xg
+ */
+
+ bp += sizeof(struct rx_header) + 4;
+
+ /*
+ * Sigh. This is gross. Ritchie forgive me.
+ */
+
+ switch (fs_op) {
+ case 130: /* Fetch data */
+ FIDOUT();
+ ND_PRINT(" offset");
+ UINTOUT();
+ ND_PRINT(" length");
+ UINTOUT();
+ break;
+ case 131: /* Fetch ACL */
+ case 132: /* Fetch Status */
+ case 143: /* Old set lock */
+ case 144: /* Old extend lock */
+ case 145: /* Old release lock */
+ case 156: /* Set lock */
+ case 157: /* Extend lock */
+ case 158: /* Release lock */
+ FIDOUT();
+ break;
+ case 135: /* Store status */
+ FIDOUT();
+ STOREATTROUT();
+ break;
+ case 133: /* Store data */
+ FIDOUT();
+ STOREATTROUT();
+ ND_PRINT(" offset");
+ UINTOUT();
+ ND_PRINT(" length");
+ UINTOUT();
+ ND_PRINT(" flen");
+ UINTOUT();
+ break;
+ case 134: /* Store ACL */
+ {
+ char a[AFSOPAQUEMAX+1];
+ FIDOUT();
+ i = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ ND_TCHECK_LEN(bp, i);
+ i = ND_MIN(AFSOPAQUEMAX, i);
+ strncpy(a, (const char *) bp, i);
+ a[i] = '\0';
+ acl_print(ndo, (u_char *) a, (u_char *) a + i);
+ break;
+ }
+ case 137: /* Create file */
+ case 141: /* MakeDir */
+ FIDOUT();
+ STROUT(AFSNAMEMAX);
+ STOREATTROUT();
+ break;
+ case 136: /* Remove file */
+ case 142: /* Remove directory */
+ FIDOUT();
+ STROUT(AFSNAMEMAX);
+ break;
+ case 138: /* Rename file */
+ ND_PRINT(" old");
+ FIDOUT();
+ STROUT(AFSNAMEMAX);
+ ND_PRINT(" new");
+ FIDOUT();
+ STROUT(AFSNAMEMAX);
+ break;
+ case 139: /* Symlink */
+ FIDOUT();
+ STROUT(AFSNAMEMAX);
+ ND_PRINT(" link to");
+ STROUT(AFSNAMEMAX);
+ break;
+ case 140: /* Link */
+ FIDOUT();
+ STROUT(AFSNAMEMAX);
+ ND_PRINT(" link to");
+ FIDOUT();
+ break;
+ case 148: /* Get volume info */
+ STROUT(AFSNAMEMAX);
+ break;
+ case 149: /* Get volume stats */
+ case 150: /* Set volume stats */
+ ND_PRINT(" volid");
+ UINTOUT();
+ break;
+ case 154: /* New get volume info */
+ ND_PRINT(" volname");
+ STROUT(AFSNAMEMAX);
+ break;
+ case 155: /* Bulk stat */
+ case 65536: /* Inline bulk stat */
+ {
+ uint32_t j;
+ j = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+
+ for (i = 0; i < j; i++) {
+ FIDOUT();
+ if (i != j - 1)
+ ND_PRINT(",");
+ }
+ if (j == 0)
+ ND_PRINT(" <none!>");
+ break;
+ }
+ case 65537: /* Fetch data 64 */
+ FIDOUT();
+ ND_PRINT(" offset");
+ UINT64OUT();
+ ND_PRINT(" length");
+ UINT64OUT();
+ break;
+ case 65538: /* Store data 64 */
+ FIDOUT();
+ STOREATTROUT();
+ ND_PRINT(" offset");
+ UINT64OUT();
+ ND_PRINT(" length");
+ UINT64OUT();
+ ND_PRINT(" flen");
+ UINT64OUT();
+ break;
+ case 65541: /* CallBack rx conn address */
+ ND_PRINT(" addr");
+ UINTOUT();
+ default:
+ ;
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|fs]");
+}
+
+/*
+ * Handle replies to the AFS file service
+ */
+
+static void
+fs_reply_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint32_t opcode)
+{
+ uint32_t i;
+ const struct rx_header *rxh;
+ uint8_t type;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ rxh = (const struct rx_header *) bp;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from fsint/afsint.xg
+ */
+
+ ND_PRINT(" fs reply %s", tok2str(fs_req, "op#%u", opcode));
+
+ type = GET_U_1(rxh->type);
+ bp += sizeof(struct rx_header);
+
+ /*
+ * If it was a data packet, interpret the response
+ */
+
+ if (type == RX_PACKET_TYPE_DATA) {
+ switch (opcode) {
+ case 131: /* Fetch ACL */
+ {
+ char a[AFSOPAQUEMAX+1];
+ i = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ ND_TCHECK_LEN(bp, i);
+ i = ND_MIN(AFSOPAQUEMAX, i);
+ strncpy(a, (const char *) bp, i);
+ a[i] = '\0';
+ acl_print(ndo, (u_char *) a, (u_char *) a + i);
+ break;
+ }
+ case 137: /* Create file */
+ case 141: /* MakeDir */
+ ND_PRINT(" new");
+ FIDOUT();
+ break;
+ case 151: /* Get root volume */
+ ND_PRINT(" root volume");
+ STROUT(AFSNAMEMAX);
+ break;
+ case 153: /* Get time */
+ DATEOUT();
+ break;
+ default:
+ ;
+ }
+ } else if (type == RX_PACKET_TYPE_ABORT) {
+ /*
+ * Otherwise, just print out the return code
+ */
+ int32_t errcode;
+
+ errcode = GET_BE_S_4(bp);
+ bp += sizeof(int32_t);
+
+ ND_PRINT(" error %s", tok2str(afs_fs_errors, "#%d", errcode));
+ } else {
+ ND_PRINT(" strange fs reply of type %u", type);
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|fs]");
+}
+
+/*
+ * Print out an AFS ACL string. An AFS ACL is a string that has the
+ * following format:
+ *
+ * <positive> <negative>
+ * <uid1> <aclbits1>
+ * ....
+ *
+ * "positive" and "negative" are integers which contain the number of
+ * positive and negative ACL's in the string. The uid/aclbits pair are
+ * ASCII strings containing the UID/PTS record and an ASCII number
+ * representing a logical OR of all the ACL permission bits
+ */
+
+#define NUMSTRINGIFY(x) XSTRINGIFY(x)
+
+static void
+acl_print(netdissect_options *ndo,
+ u_char *s, u_char *end)
+{
+ int pos, neg, acl;
+ int n, i;
+ char user[USERNAMEMAX+1];
+
+ if (sscanf((char *) s, "%d %d\n%n", &pos, &neg, &n) != 2)
+ return;
+
+ s += n;
+
+ if (s > end)
+ return;
+
+ /*
+ * This wacky order preserves the order used by the "fs" command
+ */
+
+#define ACLOUT(acl) \
+ ND_PRINT("%s%s%s%s%s%s%s", \
+ acl & PRSFS_READ ? "r" : "", \
+ acl & PRSFS_LOOKUP ? "l" : "", \
+ acl & PRSFS_INSERT ? "i" : "", \
+ acl & PRSFS_DELETE ? "d" : "", \
+ acl & PRSFS_WRITE ? "w" : "", \
+ acl & PRSFS_LOCK ? "k" : "", \
+ acl & PRSFS_ADMINISTER ? "a" : "");
+
+ for (i = 0; i < pos; i++) {
+ if (sscanf((char *) s, "%" NUMSTRINGIFY(USERNAMEMAX) "s %d\n%n", user, &acl, &n) != 2)
+ return;
+ s += n;
+ ND_PRINT(" +{");
+ fn_print_str(ndo, (u_char *)user);
+ ND_PRINT(" ");
+ ACLOUT(acl);
+ ND_PRINT("}");
+ if (s > end)
+ return;
+ }
+
+ for (i = 0; i < neg; i++) {
+ if (sscanf((char *) s, "%" NUMSTRINGIFY(USERNAMEMAX) "s %d\n%n", user, &acl, &n) != 2)
+ return;
+ s += n;
+ ND_PRINT(" -{");
+ fn_print_str(ndo, (u_char *)user);
+ ND_PRINT(" ");
+ ACLOUT(acl);
+ ND_PRINT("}");
+ if (s > end)
+ return;
+ }
+}
+
+#undef ACLOUT
+
+/*
+ * Handle calls to the AFS callback service
+ */
+
+static void
+cb_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ uint32_t cb_op;
+ uint32_t i;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from fsint/afscbint.xg
+ */
+
+ cb_op = GET_BE_U_4(bp + sizeof(struct rx_header));
+
+ ND_PRINT(" cb call %s", tok2str(cb_req, "op#%u", cb_op));
+
+ bp += sizeof(struct rx_header) + 4;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from fsint/afscbint.xg
+ */
+
+ switch (cb_op) {
+ case 204: /* Callback */
+ {
+ uint32_t j, t;
+ j = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+
+ for (i = 0; i < j; i++) {
+ FIDOUT();
+ if (i != j - 1)
+ ND_PRINT(",");
+ }
+
+ if (j == 0)
+ ND_PRINT(" <none!>");
+
+ j = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+
+ if (j != 0)
+ ND_PRINT(";");
+
+ for (i = 0; i < j; i++) {
+ ND_PRINT(" ver");
+ INTOUT();
+ ND_PRINT(" expires");
+ DATEOUT();
+ t = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ tok2str(cb_types, "type %u", t);
+ }
+ break;
+ }
+ case 214: {
+ ND_PRINT(" afsuuid");
+ AFSUUIDOUT();
+ break;
+ }
+ default:
+ ;
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|cb]");
+}
+
+/*
+ * Handle replies to the AFS Callback Service
+ */
+
+static void
+cb_reply_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint32_t opcode)
+{
+ const struct rx_header *rxh;
+ uint8_t type;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ rxh = (const struct rx_header *) bp;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from fsint/afscbint.xg
+ */
+
+ ND_PRINT(" cb reply %s", tok2str(cb_req, "op#%u", opcode));
+
+ type = GET_U_1(rxh->type);
+ bp += sizeof(struct rx_header);
+
+ /*
+ * If it was a data packet, interpret the response.
+ */
+
+ if (type == RX_PACKET_TYPE_DATA)
+ switch (opcode) {
+ case 213: /* InitCallBackState3 */
+ AFSUUIDOUT();
+ break;
+ default:
+ ;
+ }
+ else {
+ /*
+ * Otherwise, just print out the return code
+ */
+ ND_PRINT(" errcode");
+ INTOUT();
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|cb]");
+}
+
+/*
+ * Handle calls to the AFS protection database server
+ */
+
+static void
+prot_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ uint32_t i;
+ uint32_t pt_op;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from ptserver/ptint.xg
+ */
+
+ pt_op = GET_BE_U_4(bp + sizeof(struct rx_header));
+
+ ND_PRINT(" pt");
+
+ if (is_ubik(pt_op)) {
+ ubik_print(ndo, bp);
+ return;
+ }
+
+ ND_PRINT(" call %s", tok2str(pt_req, "op#%u", pt_op));
+
+ /*
+ * Decode some of the arguments to the PT calls
+ */
+
+ bp += sizeof(struct rx_header) + 4;
+
+ switch (pt_op) {
+ case 500: /* I New User */
+ STROUT(PRNAMEMAX);
+ ND_PRINT(" id");
+ INTOUT();
+ ND_PRINT(" oldid");
+ INTOUT();
+ break;
+ case 501: /* Where is it */
+ case 506: /* Delete */
+ case 508: /* Get CPS */
+ case 512: /* List entry */
+ case 514: /* List elements */
+ case 517: /* List owned */
+ case 518: /* Get CPS2 */
+ case 519: /* Get host CPS */
+ case 530: /* List super groups */
+ ND_PRINT(" id");
+ INTOUT();
+ break;
+ case 502: /* Dump entry */
+ ND_PRINT(" pos");
+ INTOUT();
+ break;
+ case 503: /* Add to group */
+ case 507: /* Remove from group */
+ case 515: /* Is a member of? */
+ ND_PRINT(" uid");
+ INTOUT();
+ ND_PRINT(" gid");
+ INTOUT();
+ break;
+ case 504: /* Name to ID */
+ {
+ uint32_t j;
+ j = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+
+ /*
+ * Who designed this chicken-shit protocol?
+ *
+ * Each character is stored as a 32-bit
+ * integer!
+ */
+
+ for (i = 0; i < j; i++) {
+ VECOUT(PRNAMEMAX);
+ }
+ if (j == 0)
+ ND_PRINT(" <none!>");
+ }
+ break;
+ case 505: /* Id to name */
+ {
+ uint32_t j;
+ ND_PRINT(" ids:");
+ i = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ for (j = 0; j < i; j++)
+ INTOUT();
+ if (j == 0)
+ ND_PRINT(" <none!>");
+ }
+ break;
+ case 509: /* New entry */
+ STROUT(PRNAMEMAX);
+ ND_PRINT(" flag");
+ INTOUT();
+ ND_PRINT(" oid");
+ INTOUT();
+ break;
+ case 511: /* Set max */
+ ND_PRINT(" id");
+ INTOUT();
+ ND_PRINT(" gflag");
+ INTOUT();
+ break;
+ case 513: /* Change entry */
+ ND_PRINT(" id");
+ INTOUT();
+ STROUT(PRNAMEMAX);
+ ND_PRINT(" oldid");
+ INTOUT();
+ ND_PRINT(" newid");
+ INTOUT();
+ break;
+ case 520: /* Update entry */
+ ND_PRINT(" id");
+ INTOUT();
+ STROUT(PRNAMEMAX);
+ break;
+ default:
+ ;
+ }
+
+
+ return;
+
+trunc:
+ ND_PRINT(" [|pt]");
+}
+
+/*
+ * Handle replies to the AFS protection service
+ */
+
+static void
+prot_reply_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint32_t opcode)
+{
+ const struct rx_header *rxh;
+ uint8_t type;
+ uint32_t i;
+
+ if (length < sizeof(struct rx_header))
+ return;
+
+ rxh = (const struct rx_header *) bp;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from ptserver/ptint.xg. Check to see if it's a
+ * Ubik call, however.
+ */
+
+ ND_PRINT(" pt");
+
+ if (is_ubik(opcode)) {
+ ubik_reply_print(ndo, bp, length, opcode);
+ return;
+ }
+
+ ND_PRINT(" reply %s", tok2str(pt_req, "op#%u", opcode));
+
+ type = GET_U_1(rxh->type);
+ bp += sizeof(struct rx_header);
+
+ /*
+ * If it was a data packet, interpret the response
+ */
+
+ if (type == RX_PACKET_TYPE_DATA)
+ switch (opcode) {
+ case 504: /* Name to ID */
+ {
+ uint32_t j;
+ ND_PRINT(" ids:");
+ i = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ for (j = 0; j < i; j++)
+ INTOUT();
+ if (j == 0)
+ ND_PRINT(" <none!>");
+ }
+ break;
+ case 505: /* ID to name */
+ {
+ uint32_t j;
+ j = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+
+ /*
+ * Who designed this chicken-shit protocol?
+ *
+ * Each character is stored as a 32-bit
+ * integer!
+ */
+
+ for (i = 0; i < j; i++) {
+ VECOUT(PRNAMEMAX);
+ }
+ if (j == 0)
+ ND_PRINT(" <none!>");
+ }
+ break;
+ case 508: /* Get CPS */
+ case 514: /* List elements */
+ case 517: /* List owned */
+ case 518: /* Get CPS2 */
+ case 519: /* Get host CPS */
+ {
+ uint32_t j;
+ j = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ for (i = 0; i < j; i++) {
+ INTOUT();
+ }
+ if (j == 0)
+ ND_PRINT(" <none!>");
+ }
+ break;
+ case 510: /* List max */
+ ND_PRINT(" maxuid");
+ INTOUT();
+ ND_PRINT(" maxgid");
+ INTOUT();
+ break;
+ default:
+ ;
+ }
+ else {
+ /*
+ * Otherwise, just print out the return code
+ */
+ ND_PRINT(" errcode");
+ INTOUT();
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|pt]");
+}
+
+/*
+ * Handle calls to the AFS volume location database service
+ */
+
+static void
+vldb_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ uint32_t vldb_op;
+ uint32_t i;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from vlserver/vldbint.xg
+ */
+
+ vldb_op = GET_BE_U_4(bp + sizeof(struct rx_header));
+
+ ND_PRINT(" vldb");
+
+ if (is_ubik(vldb_op)) {
+ ubik_print(ndo, bp);
+ return;
+ }
+ ND_PRINT(" call %s", tok2str(vldb_req, "op#%u", vldb_op));
+
+ /*
+ * Decode some of the arguments to the VLDB calls
+ */
+
+ bp += sizeof(struct rx_header) + 4;
+
+ switch (vldb_op) {
+ case 501: /* Create new volume */
+ case 517: /* Create entry N */
+ VECOUT(VLNAMEMAX);
+ break;
+ case 502: /* Delete entry */
+ case 503: /* Get entry by ID */
+ case 507: /* Update entry */
+ case 508: /* Set lock */
+ case 509: /* Release lock */
+ case 518: /* Get entry by ID N */
+ ND_PRINT(" volid");
+ INTOUT();
+ i = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ if (i <= 2)
+ ND_PRINT(" type %s", voltype[i]);
+ break;
+ case 504: /* Get entry by name */
+ case 519: /* Get entry by name N */
+ case 524: /* Update entry by name */
+ case 527: /* Get entry by name U */
+ STROUT(VLNAMEMAX);
+ break;
+ case 505: /* Get new vol id */
+ ND_PRINT(" bump");
+ INTOUT();
+ break;
+ case 506: /* Replace entry */
+ case 520: /* Replace entry N */
+ ND_PRINT(" volid");
+ INTOUT();
+ i = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ if (i <= 2)
+ ND_PRINT(" type %s", voltype[i]);
+ VECOUT(VLNAMEMAX);
+ break;
+ case 510: /* List entry */
+ case 521: /* List entry N */
+ ND_PRINT(" index");
+ INTOUT();
+ break;
+ default:
+ ;
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|vldb]");
+}
+
+/*
+ * Handle replies to the AFS volume location database service
+ */
+
+static void
+vldb_reply_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint32_t opcode)
+{
+ const struct rx_header *rxh;
+ uint8_t type;
+ uint32_t i;
+
+ if (length < sizeof(struct rx_header))
+ return;
+
+ rxh = (const struct rx_header *) bp;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from vlserver/vldbint.xg. Check to see if it's a
+ * Ubik call, however.
+ */
+
+ ND_PRINT(" vldb");
+
+ if (is_ubik(opcode)) {
+ ubik_reply_print(ndo, bp, length, opcode);
+ return;
+ }
+
+ ND_PRINT(" reply %s", tok2str(vldb_req, "op#%u", opcode));
+
+ type = GET_U_1(rxh->type);
+ bp += sizeof(struct rx_header);
+
+ /*
+ * If it was a data packet, interpret the response
+ */
+
+ if (type == RX_PACKET_TYPE_DATA)
+ switch (opcode) {
+ case 510: /* List entry */
+ ND_PRINT(" count");
+ INTOUT();
+ ND_PRINT(" nextindex");
+ INTOUT();
+ ND_FALL_THROUGH;
+ case 503: /* Get entry by id */
+ case 504: /* Get entry by name */
+ { uint32_t nservers, j;
+ VECOUT(VLNAMEMAX);
+ ND_TCHECK_4(bp);
+ bp += sizeof(uint32_t);
+ ND_PRINT(" numservers");
+ nservers = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ ND_PRINT(" %u", nservers);
+ ND_PRINT(" servers");
+ for (i = 0; i < 8; i++) {
+ ND_TCHECK_4(bp);
+ if (i < nservers)
+ ND_PRINT(" %s",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(bp)));
+ bp += sizeof(nd_ipv4);
+ }
+ ND_PRINT(" partitions");
+ for (i = 0; i < 8; i++) {
+ j = GET_BE_U_4(bp);
+ if (i < nservers && j <= 26)
+ ND_PRINT(" %c", 'a' + j);
+ else if (i < nservers)
+ ND_PRINT(" %u", j);
+ bp += sizeof(uint32_t);
+ }
+ ND_TCHECK_LEN(bp, 8 * sizeof(uint32_t));
+ bp += 8 * sizeof(uint32_t);
+ ND_PRINT(" rwvol");
+ UINTOUT();
+ ND_PRINT(" rovol");
+ UINTOUT();
+ ND_PRINT(" backup");
+ UINTOUT();
+ }
+ break;
+ case 505: /* Get new volume ID */
+ ND_PRINT(" newvol");
+ UINTOUT();
+ break;
+ case 521: /* List entry */
+ case 529: /* List entry U */
+ ND_PRINT(" count");
+ INTOUT();
+ ND_PRINT(" nextindex");
+ INTOUT();
+ ND_FALL_THROUGH;
+ case 518: /* Get entry by ID N */
+ case 519: /* Get entry by name N */
+ { uint32_t nservers, j;
+ VECOUT(VLNAMEMAX);
+ ND_PRINT(" numservers");
+ nservers = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ ND_PRINT(" %u", nservers);
+ ND_PRINT(" servers");
+ for (i = 0; i < 13; i++) {
+ ND_TCHECK_4(bp);
+ if (i < nservers)
+ ND_PRINT(" %s",
+ intoa(GET_IPV4_TO_NETWORK_ORDER(bp)));
+ bp += sizeof(nd_ipv4);
+ }
+ ND_PRINT(" partitions");
+ for (i = 0; i < 13; i++) {
+ j = GET_BE_U_4(bp);
+ if (i < nservers && j <= 26)
+ ND_PRINT(" %c", 'a' + j);
+ else if (i < nservers)
+ ND_PRINT(" %u", j);
+ bp += sizeof(uint32_t);
+ }
+ ND_TCHECK_LEN(bp, 13 * sizeof(uint32_t));
+ bp += 13 * sizeof(uint32_t);
+ ND_PRINT(" rwvol");
+ UINTOUT();
+ ND_PRINT(" rovol");
+ UINTOUT();
+ ND_PRINT(" backup");
+ UINTOUT();
+ }
+ break;
+ case 526: /* Get entry by ID U */
+ case 527: /* Get entry by name U */
+ { uint32_t nservers, j;
+ VECOUT(VLNAMEMAX);
+ ND_PRINT(" numservers");
+ nservers = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ ND_PRINT(" %u", nservers);
+ ND_PRINT(" servers");
+ for (i = 0; i < 13; i++) {
+ if (i < nservers) {
+ ND_PRINT(" afsuuid");
+ AFSUUIDOUT();
+ } else {
+ ND_TCHECK_LEN(bp, 44);
+ bp += 44;
+ }
+ }
+ ND_TCHECK_LEN(bp, 4 * 13);
+ bp += 4 * 13;
+ ND_PRINT(" partitions");
+ for (i = 0; i < 13; i++) {
+ j = GET_BE_U_4(bp);
+ if (i < nservers && j <= 26)
+ ND_PRINT(" %c", 'a' + j);
+ else if (i < nservers)
+ ND_PRINT(" %u", j);
+ bp += sizeof(uint32_t);
+ }
+ ND_TCHECK_LEN(bp, 13 * sizeof(uint32_t));
+ bp += 13 * sizeof(uint32_t);
+ ND_PRINT(" rwvol");
+ UINTOUT();
+ ND_PRINT(" rovol");
+ UINTOUT();
+ ND_PRINT(" backup");
+ UINTOUT();
+ }
+ default:
+ ;
+ }
+
+ else {
+ /*
+ * Otherwise, just print out the return code
+ */
+ ND_PRINT(" errcode");
+ INTOUT();
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|vldb]");
+}
+
+/*
+ * Handle calls to the AFS Kerberos Authentication service
+ */
+
+static void
+kauth_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ uint32_t kauth_op;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from kauth/kauth.rg
+ */
+
+ kauth_op = GET_BE_U_4(bp + sizeof(struct rx_header));
+
+ ND_PRINT(" kauth");
+
+ if (is_ubik(kauth_op)) {
+ ubik_print(ndo, bp);
+ return;
+ }
+
+
+ ND_PRINT(" call %s", tok2str(kauth_req, "op#%u", kauth_op));
+
+ /*
+ * Decode some of the arguments to the KA calls
+ */
+
+ bp += sizeof(struct rx_header) + 4;
+
+ switch (kauth_op) {
+ case 1: /* Authenticate old */
+ case 21: /* Authenticate */
+ case 22: /* Authenticate-V2 */
+ case 2: /* Change PW */
+ case 5: /* Set fields */
+ case 6: /* Create user */
+ case 7: /* Delete user */
+ case 8: /* Get entry */
+ case 14: /* Unlock */
+ case 15: /* Lock status */
+ ND_PRINT(" principal");
+ STROUT(KANAMEMAX);
+ STROUT(KANAMEMAX);
+ break;
+ case 3: /* GetTicket-old */
+ case 23: /* GetTicket */
+ {
+ uint32_t i;
+ ND_PRINT(" kvno");
+ INTOUT();
+ ND_PRINT(" domain");
+ STROUT(KANAMEMAX);
+ i = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ ND_TCHECK_LEN(bp, i);
+ bp += i;
+ ND_PRINT(" principal");
+ STROUT(KANAMEMAX);
+ STROUT(KANAMEMAX);
+ break;
+ }
+ case 4: /* Set Password */
+ ND_PRINT(" principal");
+ STROUT(KANAMEMAX);
+ STROUT(KANAMEMAX);
+ ND_PRINT(" kvno");
+ INTOUT();
+ break;
+ case 12: /* Get password */
+ ND_PRINT(" name");
+ STROUT(KANAMEMAX);
+ break;
+ default:
+ ;
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|kauth]");
+}
+
+/*
+ * Handle replies to the AFS Kerberos Authentication Service
+ */
+
+static void
+kauth_reply_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint32_t opcode)
+{
+ const struct rx_header *rxh;
+ uint8_t type;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ rxh = (const struct rx_header *) bp;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from kauth/kauth.rg
+ */
+
+ ND_PRINT(" kauth");
+
+ if (is_ubik(opcode)) {
+ ubik_reply_print(ndo, bp, length, opcode);
+ return;
+ }
+
+ ND_PRINT(" reply %s", tok2str(kauth_req, "op#%u", opcode));
+
+ type = GET_U_1(rxh->type);
+ bp += sizeof(struct rx_header);
+
+ /*
+ * If it was a data packet, interpret the response.
+ */
+
+ if (type == RX_PACKET_TYPE_DATA)
+ /* Well, no, not really. Leave this for later */
+ ;
+ else {
+ /*
+ * Otherwise, just print out the return code
+ */
+ ND_PRINT(" errcode");
+ INTOUT();
+ }
+}
+
+/*
+ * Handle calls to the AFS Volume location service
+ */
+
+static void
+vol_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ uint32_t vol_op;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from volser/volint.xg
+ */
+
+ vol_op = GET_BE_U_4(bp + sizeof(struct rx_header));
+
+ ND_PRINT(" vol call %s", tok2str(vol_req, "op#%u", vol_op));
+
+ bp += sizeof(struct rx_header) + 4;
+
+ switch (vol_op) {
+ case 100: /* Create volume */
+ ND_PRINT(" partition");
+ UINTOUT();
+ ND_PRINT(" name");
+ STROUT(AFSNAMEMAX);
+ ND_PRINT(" type");
+ UINTOUT();
+ ND_PRINT(" parent");
+ UINTOUT();
+ break;
+ case 101: /* Delete volume */
+ case 107: /* Get flags */
+ ND_PRINT(" trans");
+ UINTOUT();
+ break;
+ case 102: /* Restore */
+ ND_PRINT(" totrans");
+ UINTOUT();
+ ND_PRINT(" flags");
+ UINTOUT();
+ break;
+ case 103: /* Forward */
+ ND_PRINT(" fromtrans");
+ UINTOUT();
+ ND_PRINT(" fromdate");
+ DATEOUT();
+ DESTSERVEROUT();
+ ND_PRINT(" desttrans");
+ INTOUT();
+ break;
+ case 104: /* End trans */
+ ND_PRINT(" trans");
+ UINTOUT();
+ break;
+ case 105: /* Clone */
+ ND_PRINT(" trans");
+ UINTOUT();
+ ND_PRINT(" purgevol");
+ UINTOUT();
+ ND_PRINT(" newtype");
+ UINTOUT();
+ ND_PRINT(" newname");
+ STROUT(AFSNAMEMAX);
+ break;
+ case 106: /* Set flags */
+ ND_PRINT(" trans");
+ UINTOUT();
+ ND_PRINT(" flags");
+ UINTOUT();
+ break;
+ case 108: /* Trans create */
+ ND_PRINT(" vol");
+ UINTOUT();
+ ND_PRINT(" partition");
+ UINTOUT();
+ ND_PRINT(" flags");
+ UINTOUT();
+ break;
+ case 109: /* Dump */
+ case 655537: /* Get size */
+ ND_PRINT(" fromtrans");
+ UINTOUT();
+ ND_PRINT(" fromdate");
+ DATEOUT();
+ break;
+ case 110: /* Get n-th volume */
+ ND_PRINT(" index");
+ UINTOUT();
+ break;
+ case 111: /* Set forwarding */
+ ND_PRINT(" tid");
+ UINTOUT();
+ ND_PRINT(" newsite");
+ UINTOUT();
+ break;
+ case 112: /* Get name */
+ case 113: /* Get status */
+ ND_PRINT(" tid");
+ break;
+ case 114: /* Signal restore */
+ ND_PRINT(" name");
+ STROUT(AFSNAMEMAX);
+ ND_PRINT(" type");
+ UINTOUT();
+ ND_PRINT(" pid");
+ UINTOUT();
+ ND_PRINT(" cloneid");
+ UINTOUT();
+ break;
+ case 116: /* List volumes */
+ ND_PRINT(" partition");
+ UINTOUT();
+ ND_PRINT(" flags");
+ UINTOUT();
+ break;
+ case 117: /* Set id types */
+ ND_PRINT(" tid");
+ UINTOUT();
+ ND_PRINT(" name");
+ STROUT(AFSNAMEMAX);
+ ND_PRINT(" type");
+ UINTOUT();
+ ND_PRINT(" pid");
+ UINTOUT();
+ ND_PRINT(" clone");
+ UINTOUT();
+ ND_PRINT(" backup");
+ UINTOUT();
+ break;
+ case 119: /* Partition info */
+ ND_PRINT(" name");
+ STROUT(AFSNAMEMAX);
+ break;
+ case 120: /* Reclone */
+ ND_PRINT(" tid");
+ UINTOUT();
+ break;
+ case 121: /* List one volume */
+ case 122: /* Nuke volume */
+ case 124: /* Extended List volumes */
+ case 125: /* Extended List one volume */
+ case 65536: /* Convert RO to RW volume */
+ ND_PRINT(" partid");
+ UINTOUT();
+ ND_PRINT(" volid");
+ UINTOUT();
+ break;
+ case 123: /* Set date */
+ ND_PRINT(" tid");
+ UINTOUT();
+ ND_PRINT(" date");
+ DATEOUT();
+ break;
+ case 126: /* Set info */
+ ND_PRINT(" tid");
+ UINTOUT();
+ break;
+ case 128: /* Forward multiple */
+ ND_PRINT(" fromtrans");
+ UINTOUT();
+ ND_PRINT(" fromdate");
+ DATEOUT();
+ {
+ uint32_t i, j;
+ j = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ for (i = 0; i < j; i++) {
+ DESTSERVEROUT();
+ if (i != j - 1)
+ ND_PRINT(",");
+ }
+ if (j == 0)
+ ND_PRINT(" <none!>");
+ }
+ break;
+ case 65538: /* Dump version 2 */
+ ND_PRINT(" fromtrans");
+ UINTOUT();
+ ND_PRINT(" fromdate");
+ DATEOUT();
+ ND_PRINT(" flags");
+ UINTOUT();
+ break;
+ default:
+ ;
+ }
+ return;
+
+trunc:
+ ND_PRINT(" [|vol]");
+}
+
+/*
+ * Handle replies to the AFS Volume Service
+ */
+
+static void
+vol_reply_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint32_t opcode)
+{
+ const struct rx_header *rxh;
+ uint8_t type;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ rxh = (const struct rx_header *) bp;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from volser/volint.xg
+ */
+
+ ND_PRINT(" vol reply %s", tok2str(vol_req, "op#%u", opcode));
+
+ type = GET_U_1(rxh->type);
+ bp += sizeof(struct rx_header);
+
+ /*
+ * If it was a data packet, interpret the response.
+ */
+
+ if (type == RX_PACKET_TYPE_DATA) {
+ switch (opcode) {
+ case 100: /* Create volume */
+ ND_PRINT(" volid");
+ UINTOUT();
+ ND_PRINT(" trans");
+ UINTOUT();
+ break;
+ case 104: /* End transaction */
+ UINTOUT();
+ break;
+ case 105: /* Clone */
+ ND_PRINT(" newvol");
+ UINTOUT();
+ break;
+ case 107: /* Get flags */
+ UINTOUT();
+ break;
+ case 108: /* Transaction create */
+ ND_PRINT(" trans");
+ UINTOUT();
+ break;
+ case 110: /* Get n-th volume */
+ ND_PRINT(" volume");
+ UINTOUT();
+ ND_PRINT(" partition");
+ UINTOUT();
+ break;
+ case 112: /* Get name */
+ STROUT(AFSNAMEMAX);
+ break;
+ case 113: /* Get status */
+ ND_PRINT(" volid");
+ UINTOUT();
+ ND_PRINT(" nextuniq");
+ UINTOUT();
+ ND_PRINT(" type");
+ UINTOUT();
+ ND_PRINT(" parentid");
+ UINTOUT();
+ ND_PRINT(" clone");
+ UINTOUT();
+ ND_PRINT(" backup");
+ UINTOUT();
+ ND_PRINT(" restore");
+ UINTOUT();
+ ND_PRINT(" maxquota");
+ UINTOUT();
+ ND_PRINT(" minquota");
+ UINTOUT();
+ ND_PRINT(" owner");
+ UINTOUT();
+ ND_PRINT(" create");
+ DATEOUT();
+ ND_PRINT(" access");
+ DATEOUT();
+ ND_PRINT(" update");
+ DATEOUT();
+ ND_PRINT(" expire");
+ DATEOUT();
+ ND_PRINT(" backup");
+ DATEOUT();
+ ND_PRINT(" copy");
+ DATEOUT();
+ break;
+ case 115: /* Old list partitions */
+ break;
+ case 116: /* List volumes */
+ case 121: /* List one volume */
+ {
+ uint32_t i, j;
+ j = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ for (i = 0; i < j; i++) {
+ ND_PRINT(" name");
+ VECOUT(32);
+ ND_PRINT(" volid");
+ UINTOUT();
+ ND_PRINT(" type");
+ bp += sizeof(uint32_t) * 21;
+ if (i != j - 1)
+ ND_PRINT(",");
+ }
+ if (j == 0)
+ ND_PRINT(" <none!>");
+ }
+ break;
+
+
+ default:
+ ;
+ }
+ } else {
+ /*
+ * Otherwise, just print out the return code
+ */
+ ND_PRINT(" errcode");
+ INTOUT();
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|vol]");
+}
+
+/*
+ * Handle calls to the AFS BOS service
+ */
+
+static void
+bos_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ uint32_t bos_op;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from bozo/bosint.xg
+ */
+
+ bos_op = GET_BE_U_4(bp + sizeof(struct rx_header));
+
+ ND_PRINT(" bos call %s", tok2str(bos_req, "op#%u", bos_op));
+
+ /*
+ * Decode some of the arguments to the BOS calls
+ */
+
+ bp += sizeof(struct rx_header) + 4;
+
+ switch (bos_op) {
+ case 80: /* Create B node */
+ ND_PRINT(" type");
+ STROUT(BOSNAMEMAX);
+ ND_PRINT(" instance");
+ STROUT(BOSNAMEMAX);
+ break;
+ case 81: /* Delete B node */
+ case 83: /* Get status */
+ case 85: /* Get instance info */
+ case 87: /* Add super user */
+ case 88: /* Delete super user */
+ case 93: /* Set cell name */
+ case 96: /* Add cell host */
+ case 97: /* Delete cell host */
+ case 104: /* Restart */
+ case 106: /* Uninstall */
+ case 108: /* Exec */
+ case 112: /* Getlog */
+ case 114: /* Get instance strings */
+ STROUT(BOSNAMEMAX);
+ break;
+ case 82: /* Set status */
+ case 98: /* Set T status */
+ STROUT(BOSNAMEMAX);
+ ND_PRINT(" status");
+ INTOUT();
+ break;
+ case 86: /* Get instance parm */
+ STROUT(BOSNAMEMAX);
+ ND_PRINT(" num");
+ INTOUT();
+ break;
+ case 84: /* Enumerate instance */
+ case 89: /* List super users */
+ case 90: /* List keys */
+ case 91: /* Add key */
+ case 92: /* Delete key */
+ case 95: /* Get cell host */
+ INTOUT();
+ break;
+ case 105: /* Install */
+ STROUT(BOSNAMEMAX);
+ ND_PRINT(" size");
+ INTOUT();
+ ND_PRINT(" flags");
+ INTOUT();
+ ND_PRINT(" date");
+ INTOUT();
+ break;
+ default:
+ ;
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|bos]");
+}
+
+/*
+ * Handle replies to the AFS BOS Service
+ */
+
+static void
+bos_reply_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint32_t opcode)
+{
+ const struct rx_header *rxh;
+ uint8_t type;
+
+ if (length <= sizeof(struct rx_header))
+ return;
+
+ rxh = (const struct rx_header *) bp;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from volser/volint.xg
+ */
+
+ ND_PRINT(" bos reply %s", tok2str(bos_req, "op#%u", opcode));
+
+ type = GET_U_1(rxh->type);
+ bp += sizeof(struct rx_header);
+
+ /*
+ * If it was a data packet, interpret the response.
+ */
+
+ if (type == RX_PACKET_TYPE_DATA)
+ /* Well, no, not really. Leave this for later */
+ ;
+ else {
+ /*
+ * Otherwise, just print out the return code
+ */
+ ND_PRINT(" errcode");
+ INTOUT();
+ }
+}
+
+/*
+ * Check to see if this is a Ubik opcode.
+ */
+
+static int
+is_ubik(uint32_t opcode)
+{
+ if ((opcode >= VOTE_LOW && opcode <= VOTE_HIGH) ||
+ (opcode >= DISK_LOW && opcode <= DISK_HIGH))
+ return(1);
+ else
+ return(0);
+}
+
+/*
+ * Handle Ubik opcodes to any one of the replicated database services
+ */
+
+static void
+ubik_print(netdissect_options *ndo,
+ const u_char *bp)
+{
+ uint32_t ubik_op;
+ uint32_t temp;
+
+ /*
+ * Print out the afs call we're invoking. The table used here was
+ * gleaned from ubik/ubik_int.xg
+ */
+
+ /* Every function that calls this function first makes a bounds check
+ * for (sizeof(rx_header) + 4) bytes, so long as it remains this way
+ * the line below will not over-read.
+ */
+ ubik_op = GET_BE_U_4(bp + sizeof(struct rx_header));
+
+ ND_PRINT(" ubik call %s", tok2str(ubik_req, "op#%u", ubik_op));
+
+ /*
+ * Decode some of the arguments to the Ubik calls
+ */
+
+ bp += sizeof(struct rx_header) + 4;
+
+ switch (ubik_op) {
+ case 10000: /* Beacon */
+ temp = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ ND_PRINT(" syncsite %s", temp ? "yes" : "no");
+ ND_PRINT(" votestart");
+ DATEOUT();
+ ND_PRINT(" dbversion");
+ UBIK_VERSIONOUT();
+ ND_PRINT(" tid");
+ UBIK_VERSIONOUT();
+ break;
+ case 10003: /* Get sync site */
+ ND_PRINT(" site");
+ UINTOUT();
+ break;
+ case 20000: /* Begin */
+ case 20001: /* Commit */
+ case 20007: /* Abort */
+ case 20008: /* Release locks */
+ case 20010: /* Writev */
+ ND_PRINT(" tid");
+ UBIK_VERSIONOUT();
+ break;
+ case 20002: /* Lock */
+ ND_PRINT(" tid");
+ UBIK_VERSIONOUT();
+ ND_PRINT(" file");
+ INTOUT();
+ ND_PRINT(" pos");
+ INTOUT();
+ ND_PRINT(" length");
+ INTOUT();
+ temp = GET_BE_U_4(bp);
+ bp += sizeof(uint32_t);
+ tok2str(ubik_lock_types, "type %u", temp);
+ break;
+ case 20003: /* Write */
+ ND_PRINT(" tid");
+ UBIK_VERSIONOUT();
+ ND_PRINT(" file");
+ INTOUT();
+ ND_PRINT(" pos");
+ INTOUT();
+ break;
+ case 20005: /* Get file */
+ ND_PRINT(" file");
+ INTOUT();
+ break;
+ case 20006: /* Send file */
+ ND_PRINT(" file");
+ INTOUT();
+ ND_PRINT(" length");
+ INTOUT();
+ ND_PRINT(" dbversion");
+ UBIK_VERSIONOUT();
+ break;
+ case 20009: /* Truncate */
+ ND_PRINT(" tid");
+ UBIK_VERSIONOUT();
+ ND_PRINT(" file");
+ INTOUT();
+ ND_PRINT(" length");
+ INTOUT();
+ break;
+ case 20012: /* Set version */
+ ND_PRINT(" tid");
+ UBIK_VERSIONOUT();
+ ND_PRINT(" oldversion");
+ UBIK_VERSIONOUT();
+ ND_PRINT(" newversion");
+ UBIK_VERSIONOUT();
+ break;
+ default:
+ ;
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|ubik]");
+}
+
+/*
+ * Handle Ubik replies to any one of the replicated database services
+ */
+
+static void
+ubik_reply_print(netdissect_options *ndo,
+ const u_char *bp, u_int length, uint32_t opcode)
+{
+ const struct rx_header *rxh;
+ uint8_t type;
+
+ if (length < sizeof(struct rx_header))
+ return;
+
+ rxh = (const struct rx_header *) bp;
+
+ /*
+ * Print out the ubik call we're invoking. This table was gleaned
+ * from ubik/ubik_int.xg
+ */
+
+ ND_PRINT(" ubik reply %s", tok2str(ubik_req, "op#%u", opcode));
+
+ type = GET_U_1(rxh->type);
+ bp += sizeof(struct rx_header);
+
+ /*
+ * If it was a data packet, print out the arguments to the Ubik calls
+ */
+
+ if (type == RX_PACKET_TYPE_DATA)
+ switch (opcode) {
+ case 10000: /* Beacon */
+ ND_PRINT(" vote no");
+ break;
+ case 20004: /* Get version */
+ ND_PRINT(" dbversion");
+ UBIK_VERSIONOUT();
+ break;
+ default:
+ ;
+ }
+
+ /*
+ * Otherwise, print out "yes" if it was a beacon packet (because
+ * that's how yes votes are returned, go figure), otherwise
+ * just print out the error code.
+ */
+
+ else
+ switch (opcode) {
+ case 10000: /* Beacon */
+ ND_PRINT(" vote yes until");
+ DATEOUT();
+ break;
+ default:
+ ND_PRINT(" errcode");
+ INTOUT();
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|ubik]");
+}
+
+/*
+ * Handle RX ACK packets.
+ */
+
+static void
+rx_ack_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const struct rx_ackPacket *rxa;
+ uint8_t nAcks;
+ int i, start, last;
+ uint32_t firstPacket;
+
+ if (length < sizeof(struct rx_header))
+ return;
+
+ bp += sizeof(struct rx_header);
+
+ ND_TCHECK_LEN(bp, sizeof(struct rx_ackPacket));
+
+ rxa = (const struct rx_ackPacket *) bp;
+ bp += sizeof(struct rx_ackPacket);
+
+ /*
+ * Print out a few useful things from the ack packet structure
+ */
+
+ if (ndo->ndo_vflag > 2)
+ ND_PRINT(" bufspace %u maxskew %u",
+ GET_BE_U_2(rxa->bufferSpace),
+ GET_BE_U_2(rxa->maxSkew));
+
+ firstPacket = GET_BE_U_4(rxa->firstPacket);
+ ND_PRINT(" first %u serial %u reason %s",
+ firstPacket, GET_BE_U_4(rxa->serial),
+ tok2str(rx_ack_reasons, "#%u", GET_U_1(rxa->reason)));
+
+ /*
+ * Okay, now we print out the ack array. The way _this_ works
+ * is that we start at "first", and step through the ack array.
+ * If we have a contiguous range of acks/nacks, try to
+ * collapse them into a range.
+ *
+ * If you're really clever, you might have noticed that this
+ * doesn't seem quite correct. Specifically, due to structure
+ * padding, sizeof(struct rx_ackPacket) - RX_MAXACKS won't actually
+ * yield the start of the ack array (because RX_MAXACKS is 255
+ * and the structure will likely get padded to a 2 or 4 byte
+ * boundary). However, this is the way it's implemented inside
+ * of AFS - the start of the extra fields are at
+ * sizeof(struct rx_ackPacket) - RX_MAXACKS + nAcks, which _isn't_
+ * the exact start of the ack array. Sigh. That's why we aren't
+ * using bp, but instead use rxa->acks[]. But nAcks gets added
+ * to bp after this, so bp ends up at the right spot. Go figure.
+ */
+
+ nAcks = GET_U_1(rxa->nAcks);
+ if (nAcks != 0) {
+
+ ND_TCHECK_LEN(bp, nAcks);
+
+ /*
+ * Sigh, this is gross, but it seems to work to collapse
+ * ranges correctly.
+ */
+
+ for (i = 0, start = last = -2; i < nAcks; i++)
+ if (GET_U_1(bp + i) == RX_ACK_TYPE_ACK) {
+
+ /*
+ * I figured this deserved _some_ explanation.
+ * First, print "acked" and the packet seq
+ * number if this is the first time we've
+ * seen an acked packet.
+ */
+
+ if (last == -2) {
+ ND_PRINT(" acked %u", firstPacket + i);
+ start = i;
+ }
+
+ /*
+ * Otherwise, if there is a skip in
+ * the range (such as an nacked packet in
+ * the middle of some acked packets),
+ * then print the current packet number
+ * separated from the last number by
+ * a comma.
+ */
+
+ else if (last != i - 1) {
+ ND_PRINT(",%u", firstPacket + i);
+ start = i;
+ }
+
+ /*
+ * We always set last to the value of
+ * the last ack we saw. Conversely, start
+ * is set to the value of the first ack
+ * we saw in a range.
+ */
+
+ last = i;
+
+ /*
+ * Okay, this bit a code gets executed when
+ * we hit a nack ... in _this_ case we
+ * want to print out the range of packets
+ * that were acked, so we need to print
+ * the _previous_ packet number separated
+ * from the first by a dash (-). Since we
+ * already printed the first packet above,
+ * just print the final packet. Don't
+ * do this if there will be a single-length
+ * range.
+ */
+ } else if (last == i - 1 && start != last)
+ ND_PRINT("-%u", firstPacket + i - 1);
+
+ /*
+ * So, what's going on here? We ran off the end of the
+ * ack list, and if we got a range we need to finish it up.
+ * So we need to determine if the last packet in the list
+ * was an ack (if so, then last will be set to it) and
+ * we need to see if the last range didn't start with the
+ * last packet (because if it _did_, then that would mean
+ * that the packet number has already been printed and
+ * we don't need to print it again).
+ */
+
+ if (last == i - 1 && start != last)
+ ND_PRINT("-%u", firstPacket + i - 1);
+
+ /*
+ * Same as above, just without comments
+ */
+
+ for (i = 0, start = last = -2; i < nAcks; i++)
+ if (GET_U_1(bp + i) == RX_ACK_TYPE_NACK) {
+ if (last == -2) {
+ ND_PRINT(" nacked %u", firstPacket + i);
+ start = i;
+ } else if (last != i - 1) {
+ ND_PRINT(",%u", firstPacket + i);
+ start = i;
+ }
+ last = i;
+ } else if (last == i - 1 && start != last)
+ ND_PRINT("-%u", firstPacket + i - 1);
+
+ if (last == i - 1 && start != last)
+ ND_PRINT("-%u", firstPacket + i - 1);
+
+ bp += nAcks;
+ }
+
+ /* Padding. */
+ bp += 3;
+
+ /*
+ * These are optional fields; depending on your version of AFS,
+ * you may or may not see them
+ */
+
+#define TRUNCRET(n) if (ndo->ndo_snapend - bp + 1 <= n) return;
+
+ if (ndo->ndo_vflag > 1) {
+ TRUNCRET(4);
+ ND_PRINT(" ifmtu");
+ UINTOUT();
+
+ TRUNCRET(4);
+ ND_PRINT(" maxmtu");
+ UINTOUT();
+
+ TRUNCRET(4);
+ ND_PRINT(" rwind");
+ UINTOUT();
+
+ TRUNCRET(4);
+ ND_PRINT(" maxpackets");
+ UINTOUT();
+ }
+
+ return;
+
+trunc:
+ ND_PRINT(" [|ack]");
+}
+#undef TRUNCRET
diff --git a/print-sctp.c b/print-sctp.c
new file mode 100644
index 0000000..ad0f785
--- /dev/null
+++ b/print-sctp.c
@@ -0,0 +1,775 @@
+/* Copyright (c) 2001 NETLAB, Temple University
+ * Copyright (c) 2001 Protocol Engineering Lab, University of Delaware
+ *
+ * Jerry Heinz <gheinz@astro.temple.edu>
+ * John Fiore <jfiore@joda.cis.temple.edu>
+ * Armando L. Caro Jr. <acaro@cis.udel.edu>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* \summary: Stream Control Transmission Protocol (SCTP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+#include "ip.h"
+#include "ip6.h"
+
+/* Definitions from:
+ *
+ * SCTP reference Implementation Copyright (C) 1999 Cisco And Motorola
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of Cisco nor of Motorola may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is part of the SCTP reference Implementation
+ *
+ *
+ * Please send any bug reports or fixes you make to one of the following email
+ * addresses:
+ *
+ * rstewar1@email.mot.com
+ * kmorneau@cisco.com
+ * qxie1@email.mot.com
+ *
+ * Any bugs reported given to us we will try to fix... any fixes shared will
+ * be incorporated into the next SCTP release.
+ */
+
+/* The valid defines for all message
+ * types know to SCTP. 0 is reserved
+ */
+#define SCTP_DATA 0x00
+#define SCTP_INITIATION 0x01
+#define SCTP_INITIATION_ACK 0x02
+#define SCTP_SELECTIVE_ACK 0x03
+#define SCTP_HEARTBEAT_REQUEST 0x04
+#define SCTP_HEARTBEAT_ACK 0x05
+#define SCTP_ABORT_ASSOCIATION 0x06
+#define SCTP_SHUTDOWN 0x07
+#define SCTP_SHUTDOWN_ACK 0x08
+#define SCTP_OPERATION_ERR 0x09
+#define SCTP_COOKIE_ECHO 0x0a
+#define SCTP_COOKIE_ACK 0x0b
+#define SCTP_ECN_ECHO 0x0c
+#define SCTP_ECN_CWR 0x0d
+#define SCTP_SHUTDOWN_COMPLETE 0x0e
+#define SCTP_FORWARD_CUM_TSN 0xc0
+#define SCTP_RELIABLE_CNTL 0xc1
+#define SCTP_RELIABLE_CNTL_ACK 0xc2
+
+static const struct tok sctp_chunkid_str[] = {
+ { SCTP_DATA, "DATA" },
+ { SCTP_INITIATION, "INIT" },
+ { SCTP_INITIATION_ACK, "INIT ACK" },
+ { SCTP_SELECTIVE_ACK, "SACK" },
+ { SCTP_HEARTBEAT_REQUEST, "HB REQ" },
+ { SCTP_HEARTBEAT_ACK, "HB ACK" },
+ { SCTP_ABORT_ASSOCIATION, "ABORT" },
+ { SCTP_SHUTDOWN, "SHUTDOWN" },
+ { SCTP_SHUTDOWN_ACK, "SHUTDOWN ACK" },
+ { SCTP_OPERATION_ERR, "OP ERR" },
+ { SCTP_COOKIE_ECHO, "COOKIE ECHO" },
+ { SCTP_COOKIE_ACK, "COOKIE ACK" },
+ { SCTP_ECN_ECHO, "ECN ECHO" },
+ { SCTP_ECN_CWR, "ECN CWR" },
+ { SCTP_SHUTDOWN_COMPLETE, "SHUTDOWN COMPLETE" },
+ { SCTP_FORWARD_CUM_TSN, "FOR CUM TSN" },
+ { SCTP_RELIABLE_CNTL, "REL CTRL" },
+ { SCTP_RELIABLE_CNTL_ACK, "REL CTRL ACK" },
+ { 0, NULL }
+};
+
+/* Data Chuck Specific Flags */
+#define SCTP_DATA_FRAG_MASK 0x03
+#define SCTP_DATA_MIDDLE_FRAG 0x00
+#define SCTP_DATA_LAST_FRAG 0x01
+#define SCTP_DATA_FIRST_FRAG 0x02
+#define SCTP_DATA_NOT_FRAG 0x03
+#define SCTP_DATA_UNORDERED 0x04
+
+#define SCTP_ADDRMAX 60
+
+#define CHAN_HP 6704
+#define CHAN_MP 6705
+#define CHAN_LP 6706
+
+/* the sctp common header */
+
+struct sctpHeader{
+ nd_uint16_t source;
+ nd_uint16_t destination;
+ nd_uint32_t verificationTag;
+ nd_uint32_t adler32;
+};
+
+/* various descriptor parsers */
+
+struct sctpChunkDesc{
+ nd_uint8_t chunkID;
+ nd_uint8_t chunkFlg;
+ nd_uint16_t chunkLength;
+};
+
+struct sctpParamDesc{
+ nd_uint16_t paramType;
+ nd_uint16_t paramLength;
+};
+
+
+struct sctpRelChunkDesc{
+ struct sctpChunkDesc chk;
+ nd_uint32_t serialNumber;
+};
+
+struct sctpVendorSpecificParam {
+ struct sctpParamDesc p; /* type must be 0xfffe */
+ nd_uint32_t vendorId; /* vendor ID from RFC 1700 */
+ nd_uint16_t vendorSpecificType;
+ nd_uint16_t vendorSpecificLen;
+};
+
+
+/* Structures for the control parts */
+
+
+
+/* Sctp association init request/ack */
+
+/* this is used for init ack, too */
+struct sctpInitiation{
+ nd_uint32_t initTag; /* tag of mine */
+ nd_uint32_t rcvWindowCredit; /* rwnd */
+ nd_uint16_t NumPreopenStreams; /* OS */
+ nd_uint16_t MaxInboundStreams; /* MIS */
+ nd_uint32_t initialTSN;
+ /* optional param's follow in sctpParamDesc form */
+};
+
+struct sctpV4IpAddress{
+ struct sctpParamDesc p; /* type is set to SCTP_IPV4_PARAM_TYPE, len=10 */
+ nd_ipv4 ipAddress;
+};
+
+
+struct sctpV6IpAddress{
+ struct sctpParamDesc p; /* type is set to SCTP_IPV6_PARAM_TYPE, len=22 */
+ nd_ipv6 ipAddress;
+};
+
+struct sctpDNSName{
+ struct sctpParamDesc param;
+ nd_byte name[1];
+};
+
+
+struct sctpCookiePreserve{
+ struct sctpParamDesc p; /* type is set to SCTP_COOKIE_PRESERVE, len=8 */
+ nd_uint32_t extraTime;
+};
+
+
+struct sctpTimeStamp{
+ nd_uint32_t ts_sec;
+ nd_uint32_t ts_usec;
+};
+
+
+/* this guy is for use when
+ * I have a initiate message gloming the
+ * things together.
+
+ */
+struct sctpUnifiedInit{
+ struct sctpChunkDesc uh;
+ struct sctpInitiation initm;
+};
+
+struct sctpSendableInit{
+ struct sctpHeader mh;
+ struct sctpUnifiedInit msg;
+};
+
+
+/* Selective Acknowledgement
+ * has the following structure with
+ * a optional amount of trailing int's
+ * on the last part (based on the numberOfDesc
+ * field).
+ */
+
+struct sctpSelectiveAck{
+ nd_uint32_t highestConseqTSN;
+ nd_uint32_t updatedRwnd;
+ nd_uint16_t numberOfdesc;
+ nd_uint16_t numDupTsns;
+};
+
+struct sctpSelectiveFrag{
+ nd_uint16_t fragmentStart;
+ nd_uint16_t fragmentEnd;
+};
+
+
+struct sctpUnifiedSack{
+ struct sctpChunkDesc uh;
+ struct sctpSelectiveAck sack;
+};
+
+/* for the abort and shutdown ACK
+ * we must carry the init tag in the common header. Just the
+ * common header is all that is needed with a chunk descriptor.
+ */
+struct sctpUnifiedAbort{
+ struct sctpChunkDesc uh;
+};
+
+struct sctpUnifiedAbortLight{
+ struct sctpHeader mh;
+ struct sctpChunkDesc uh;
+};
+
+struct sctpUnifiedAbortHeavy{
+ struct sctpHeader mh;
+ struct sctpChunkDesc uh;
+ nd_uint16_t causeCode;
+ nd_uint16_t causeLen;
+};
+
+/* For the graceful shutdown we must carry
+ * the tag (in common header) and the highest consequitive acking value
+ */
+struct sctpShutdown {
+ nd_uint32_t TSN_Seen;
+};
+
+struct sctpUnifiedShutdown{
+ struct sctpChunkDesc uh;
+ struct sctpShutdown shut;
+};
+
+/* in the unified message we add the trailing
+ * stream id since it is the only message
+ * that is defined as a operation error.
+ */
+struct sctpOpErrorCause{
+ nd_uint16_t cause;
+ nd_uint16_t causeLen;
+};
+
+struct sctpUnifiedOpError{
+ struct sctpChunkDesc uh;
+ struct sctpOpErrorCause c;
+};
+
+struct sctpUnifiedStreamError{
+ struct sctpHeader mh;
+ struct sctpChunkDesc uh;
+ struct sctpOpErrorCause c;
+ nd_uint16_t strmNum;
+ nd_uint16_t reserved;
+};
+
+struct staleCookieMsg{
+ struct sctpHeader mh;
+ struct sctpChunkDesc uh;
+ struct sctpOpErrorCause c;
+ nd_uint32_t moretime;
+};
+
+/* the following is used in all sends
+ * where nothing is needed except the
+ * chunk/type i.e. shutdownAck Abort */
+
+struct sctpUnifiedSingleMsg{
+ struct sctpHeader mh;
+ struct sctpChunkDesc uh;
+};
+
+struct sctpDataPart{
+ nd_uint32_t TSN;
+ nd_uint16_t streamId;
+ nd_uint16_t sequence;
+ nd_uint32_t payloadtype;
+};
+
+struct sctpUnifiedDatagram{
+ struct sctpChunkDesc uh;
+ struct sctpDataPart dp;
+};
+
+struct sctpECN_echo{
+ struct sctpChunkDesc uh;
+ nd_uint32_t Lowest_TSN;
+};
+
+
+struct sctpCWR{
+ struct sctpChunkDesc uh;
+ nd_uint32_t TSN_reduced_at;
+};
+
+static const struct tok ForCES_channels[] = {
+ { CHAN_HP, "ForCES HP" },
+ { CHAN_MP, "ForCES MP" },
+ { CHAN_LP, "ForCES LP" },
+ { 0, NULL }
+};
+
+/* data chunk's payload protocol identifiers */
+
+#define SCTP_PPID_IUA 1
+#define SCTP_PPID_M2UA 2
+#define SCTP_PPID_M3UA 3
+#define SCTP_PPID_SUA 4
+#define SCTP_PPID_M2PA 5
+#define SCTP_PPID_V5UA 6
+#define SCTP_PPID_H248 7
+#define SCTP_PPID_BICC 8
+#define SCTP_PPID_TALI 9
+#define SCTP_PPID_DUA 10
+#define SCTP_PPID_ASAP 11
+#define SCTP_PPID_ENRP 12
+#define SCTP_PPID_H323 13
+#define SCTP_PPID_QIPC 14
+#define SCTP_PPID_SIMCO 15
+#define SCTP_PPID_DDPSC 16
+#define SCTP_PPID_DDPSSC 17
+#define SCTP_PPID_S1AP 18
+#define SCTP_PPID_RUA 19
+#define SCTP_PPID_HNBAP 20
+#define SCTP_PPID_FORCES_HP 21
+#define SCTP_PPID_FORCES_MP 22
+#define SCTP_PPID_FORCES_LP 23
+#define SCTP_PPID_SBC_AP 24
+#define SCTP_PPID_NBAP 25
+/* 26 */
+#define SCTP_PPID_X2AP 27
+
+static const struct tok PayloadProto_idents[] = {
+ { SCTP_PPID_IUA, "ISDN Q.921" },
+ { SCTP_PPID_M2UA, "M2UA" },
+ { SCTP_PPID_M3UA, "M3UA" },
+ { SCTP_PPID_SUA, "SUA" },
+ { SCTP_PPID_M2PA, "M2PA" },
+ { SCTP_PPID_V5UA, "V5.2" },
+ { SCTP_PPID_H248, "H.248" },
+ { SCTP_PPID_BICC, "BICC" },
+ { SCTP_PPID_TALI, "TALI" },
+ { SCTP_PPID_DUA, "DUA" },
+ { SCTP_PPID_ASAP, "ASAP" },
+ { SCTP_PPID_ENRP, "ENRP" },
+ { SCTP_PPID_H323, "H.323" },
+ { SCTP_PPID_QIPC, "Q.IPC" },
+ { SCTP_PPID_SIMCO, "SIMCO" },
+ { SCTP_PPID_DDPSC, "DDPSC" },
+ { SCTP_PPID_DDPSSC, "DDPSSC" },
+ { SCTP_PPID_S1AP, "S1AP" },
+ { SCTP_PPID_RUA, "RUA" },
+ { SCTP_PPID_HNBAP, "HNBAP" },
+ { SCTP_PPID_FORCES_HP, "ForCES HP" },
+ { SCTP_PPID_FORCES_MP, "ForCES MP" },
+ { SCTP_PPID_FORCES_LP, "ForCES LP" },
+ { SCTP_PPID_SBC_AP, "SBc-AP" },
+ { SCTP_PPID_NBAP, "NBAP" },
+ /* 26 */
+ { SCTP_PPID_X2AP, "X2AP" },
+ { 0, NULL }
+};
+
+
+static int
+isForCES_port(u_short Port)
+{
+ if (Port == CHAN_HP)
+ return 1;
+ if (Port == CHAN_MP)
+ return 1;
+ if (Port == CHAN_LP)
+ return 1;
+
+ return 0;
+}
+
+void
+sctp_print(netdissect_options *ndo,
+ const u_char *bp, /* beginning of sctp packet */
+ const u_char *bp2, /* beginning of enclosing */
+ u_int sctpPacketLength) /* ip packet */
+{
+ u_int sctpPacketLengthRemaining;
+ const struct sctpHeader *sctpPktHdr;
+ const struct ip *ip;
+ const struct ip6_hdr *ip6;
+ uint8_t chunkID;
+ u_short sourcePort, destPort;
+ u_int chunkCount;
+ const struct sctpChunkDesc *chunkDescPtr;
+ const char *sep;
+ int isforces = 0;
+
+ ndo->ndo_protocol = "sctp";
+ if (sctpPacketLength < sizeof(struct sctpHeader))
+ {
+ ND_PRINT("truncated-sctp - %zu bytes missing!",
+ sizeof(struct sctpHeader) - sctpPacketLength);
+ return;
+ }
+ sctpPktHdr = (const struct sctpHeader*) bp;
+ ND_TCHECK_SIZE(sctpPktHdr);
+ sctpPacketLengthRemaining = sctpPacketLength;
+
+ sourcePort = GET_BE_U_2(sctpPktHdr->source);
+ destPort = GET_BE_U_2(sctpPktHdr->destination);
+
+ ip = (const struct ip *)bp2;
+ if (IP_V(ip) == 6)
+ ip6 = (const struct ip6_hdr *)bp2;
+ else
+ ip6 = NULL;
+
+ if (ip6) {
+ ND_PRINT("%s.%u > %s.%u: sctp",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ sourcePort,
+ GET_IP6ADDR_STRING(ip6->ip6_dst),
+ destPort);
+ } else {
+ ND_PRINT("%s.%u > %s.%u: sctp",
+ GET_IPADDR_STRING(ip->ip_src),
+ sourcePort,
+ GET_IPADDR_STRING(ip->ip_dst),
+ destPort);
+ }
+
+ if (isForCES_port(sourcePort)) {
+ ND_PRINT("[%s]", tok2str(ForCES_channels, NULL, sourcePort));
+ isforces = 1;
+ }
+ if (isForCES_port(destPort)) {
+ ND_PRINT("[%s]", tok2str(ForCES_channels, NULL, destPort));
+ isforces = 1;
+ }
+
+ bp += sizeof(struct sctpHeader);
+ sctpPacketLengthRemaining -= sizeof(struct sctpHeader);
+
+ if (ndo->ndo_vflag >= 2)
+ sep = "\n\t";
+ else
+ sep = " (";
+ /* cycle through all chunks, printing information on each one */
+ for (chunkCount = 0, chunkDescPtr = (const struct sctpChunkDesc *)bp;
+ sctpPacketLengthRemaining != 0;
+ chunkCount++)
+ {
+ uint16_t chunkLength, chunkLengthRemaining;
+ uint16_t align;
+
+ chunkDescPtr = (const struct sctpChunkDesc *)bp;
+ if (sctpPacketLengthRemaining < sizeof(*chunkDescPtr)) {
+ ND_PRINT("%s%u) [chunk descriptor cut off at end of packet]", sep, chunkCount+1);
+ break;
+ }
+ ND_TCHECK_SIZE(chunkDescPtr);
+ chunkLength = GET_BE_U_2(chunkDescPtr->chunkLength);
+ if (chunkLength < sizeof(*chunkDescPtr)) {
+ ND_PRINT("%s%u) [Bad chunk length %u, < size of chunk descriptor]", sep, chunkCount+1, chunkLength);
+ break;
+ }
+ chunkLengthRemaining = chunkLength;
+
+ align = chunkLength % 4;
+ if (align != 0)
+ align = 4 - align;
+
+ if (sctpPacketLengthRemaining < align) {
+ ND_PRINT("%s%u) [Bad chunk length %u, > remaining data in packet]", sep, chunkCount+1, chunkLength);
+ break;
+ }
+
+ ND_TCHECK_LEN(bp, chunkLength);
+
+ bp += sizeof(*chunkDescPtr);
+ sctpPacketLengthRemaining -= sizeof(*chunkDescPtr);
+ chunkLengthRemaining -= sizeof(*chunkDescPtr);
+
+ ND_PRINT("%s%u) ", sep, chunkCount+1);
+ chunkID = GET_U_1(chunkDescPtr->chunkID);
+ ND_PRINT("[%s] ", tok2str(sctp_chunkid_str, "Unknown chunk type: 0x%x",
+ chunkID));
+ switch (chunkID)
+ {
+ case SCTP_DATA :
+ {
+ const struct sctpDataPart *dataHdrPtr;
+ uint8_t chunkFlg;
+ uint32_t ppid;
+ uint16_t payload_size;
+
+ chunkFlg = GET_U_1(chunkDescPtr->chunkFlg);
+ if ((chunkFlg & SCTP_DATA_UNORDERED) == SCTP_DATA_UNORDERED)
+ ND_PRINT("(U)");
+
+ if ((chunkFlg & SCTP_DATA_FIRST_FRAG) == SCTP_DATA_FIRST_FRAG)
+ ND_PRINT("(B)");
+
+ if ((chunkFlg & SCTP_DATA_LAST_FRAG) == SCTP_DATA_LAST_FRAG)
+ ND_PRINT("(E)");
+
+ if( ((chunkFlg & SCTP_DATA_UNORDERED) == SCTP_DATA_UNORDERED) ||
+ ((chunkFlg & SCTP_DATA_FIRST_FRAG) == SCTP_DATA_FIRST_FRAG) ||
+ ((chunkFlg & SCTP_DATA_LAST_FRAG) == SCTP_DATA_LAST_FRAG) )
+ ND_PRINT(" ");
+
+ if (chunkLengthRemaining < sizeof(*dataHdrPtr)) {
+ ND_PRINT("bogus chunk length %u]", chunkLength);
+ return;
+ }
+ dataHdrPtr=(const struct sctpDataPart*)bp;
+
+ ppid = GET_BE_U_4(dataHdrPtr->payloadtype);
+ ND_PRINT("[TSN: %u] ", GET_BE_U_4(dataHdrPtr->TSN));
+ ND_PRINT("[SID: %u] ", GET_BE_U_2(dataHdrPtr->streamId));
+ ND_PRINT("[SSEQ %u] ", GET_BE_U_2(dataHdrPtr->sequence));
+ ND_PRINT("[PPID %s] ",
+ tok2str(PayloadProto_idents, "0x%x", ppid));
+
+ if (!isforces) {
+ isforces = (ppid == SCTP_PPID_FORCES_HP) ||
+ (ppid == SCTP_PPID_FORCES_MP) ||
+ (ppid == SCTP_PPID_FORCES_LP);
+ }
+
+ bp += sizeof(*dataHdrPtr);
+ sctpPacketLengthRemaining -= sizeof(*dataHdrPtr);
+ chunkLengthRemaining -= sizeof(*dataHdrPtr);
+ payload_size = chunkLengthRemaining;
+ if (payload_size == 0) {
+ ND_PRINT("bogus chunk length %u]", chunkLength);
+ return;
+ }
+
+ if (isforces) {
+ forces_print(ndo, bp, payload_size);
+ /* ndo_protocol reassignment after forces_print() call */
+ ndo->ndo_protocol = "sctp";
+ } else if (ndo->ndo_vflag >= 2) { /* if verbose output is specified */
+ /* at the command line */
+ switch (ppid) {
+ case SCTP_PPID_M3UA :
+ m3ua_print(ndo, bp, payload_size);
+ /* ndo_protocol reassignment after m3ua_print() call */
+ ndo->ndo_protocol = "sctp";
+ break;
+ default:
+ ND_PRINT("[Payload");
+ if (!ndo->ndo_suppress_default_print) {
+ ND_PRINT(":");
+ ND_DEFAULTPRINT(bp, payload_size);
+ }
+ ND_PRINT("]");
+ break;
+ }
+ }
+ bp += payload_size;
+ sctpPacketLengthRemaining -= payload_size;
+ chunkLengthRemaining -= payload_size;
+ break;
+ }
+ case SCTP_INITIATION :
+ {
+ const struct sctpInitiation *init;
+
+ if (chunkLengthRemaining < sizeof(*init)) {
+ ND_PRINT("bogus chunk length %u]", chunkLength);
+ return;
+ }
+ init=(const struct sctpInitiation*)bp;
+ ND_PRINT("[init tag: %u] ", GET_BE_U_4(init->initTag));
+ ND_PRINT("[rwnd: %u] ", GET_BE_U_4(init->rcvWindowCredit));
+ ND_PRINT("[OS: %u] ", GET_BE_U_2(init->NumPreopenStreams));
+ ND_PRINT("[MIS: %u] ", GET_BE_U_2(init->MaxInboundStreams));
+ ND_PRINT("[init TSN: %u] ", GET_BE_U_4(init->initialTSN));
+ bp += sizeof(*init);
+ sctpPacketLengthRemaining -= sizeof(*init);
+ chunkLengthRemaining -= sizeof(*init);
+
+#if 0 /* ALC you can add code for optional params here */
+ if( chunkLengthRemaining != 0 )
+ ND_PRINT(" @@@@@ UNFINISHED @@@@@@%s\n",
+ "Optional params present, but not printed.");
+#endif
+ bp += chunkLengthRemaining;
+ sctpPacketLengthRemaining -= chunkLengthRemaining;
+ chunkLengthRemaining = 0;
+ break;
+ }
+ case SCTP_INITIATION_ACK :
+ {
+ const struct sctpInitiation *init;
+
+ if (chunkLengthRemaining < sizeof(*init)) {
+ ND_PRINT("bogus chunk length %u]", chunkLength);
+ return;
+ }
+ init=(const struct sctpInitiation*)bp;
+ ND_PRINT("[init tag: %u] ", GET_BE_U_4(init->initTag));
+ ND_PRINT("[rwnd: %u] ", GET_BE_U_4(init->rcvWindowCredit));
+ ND_PRINT("[OS: %u] ", GET_BE_U_2(init->NumPreopenStreams));
+ ND_PRINT("[MIS: %u] ", GET_BE_U_2(init->MaxInboundStreams));
+ ND_PRINT("[init TSN: %u] ", GET_BE_U_4(init->initialTSN));
+ bp += sizeof(*init);
+ sctpPacketLengthRemaining -= sizeof(*init);
+ chunkLengthRemaining -= sizeof(*init);
+
+#if 0 /* ALC you can add code for optional params here */
+ if( chunkLengthRemaining != 0 )
+ ND_PRINT(" @@@@@ UNFINISHED @@@@@@%s\n",
+ "Optional params present, but not printed.");
+#endif
+ bp += chunkLengthRemaining;
+ sctpPacketLengthRemaining -= chunkLengthRemaining;
+ chunkLengthRemaining = 0;
+ break;
+ }
+ case SCTP_SELECTIVE_ACK:
+ {
+ const struct sctpSelectiveAck *sack;
+ const struct sctpSelectiveFrag *frag;
+ u_int fragNo, tsnNo;
+ const u_char *dupTSN;
+
+ if (chunkLengthRemaining < sizeof(*sack)) {
+ ND_PRINT("bogus chunk length %u]", chunkLength);
+ return;
+ }
+ sack=(const struct sctpSelectiveAck*)bp;
+ ND_PRINT("[cum ack %u] ", GET_BE_U_4(sack->highestConseqTSN));
+ ND_PRINT("[a_rwnd %u] ", GET_BE_U_4(sack->updatedRwnd));
+ ND_PRINT("[#gap acks %u] ", GET_BE_U_2(sack->numberOfdesc));
+ ND_PRINT("[#dup tsns %u] ", GET_BE_U_2(sack->numDupTsns));
+ bp += sizeof(*sack);
+ sctpPacketLengthRemaining -= sizeof(*sack);
+ chunkLengthRemaining -= sizeof(*sack);
+
+
+ /* print gaps */
+ for (fragNo=0;
+ chunkLengthRemaining != 0 && fragNo < GET_BE_U_2(sack->numberOfdesc);
+ bp += sizeof(*frag), sctpPacketLengthRemaining -= sizeof(*frag), chunkLengthRemaining -= sizeof(*frag), fragNo++) {
+ if (chunkLengthRemaining < sizeof(*frag)) {
+ ND_PRINT("bogus chunk length %u]", chunkLength);
+ return;
+ }
+ frag = (const struct sctpSelectiveFrag *)bp;
+ ND_PRINT("\n\t\t[gap ack block #%u: start = %u, end = %u] ",
+ fragNo+1,
+ GET_BE_U_4(sack->highestConseqTSN) + GET_BE_U_2(frag->fragmentStart),
+ GET_BE_U_4(sack->highestConseqTSN) + GET_BE_U_2(frag->fragmentEnd));
+ }
+
+ /* print duplicate TSNs */
+ for (tsnNo=0;
+ chunkLengthRemaining != 0 && tsnNo<GET_BE_U_2(sack->numDupTsns);
+ bp += 4, sctpPacketLengthRemaining -= 4, chunkLengthRemaining -= 4, tsnNo++) {
+ if (chunkLengthRemaining < 4) {
+ ND_PRINT("bogus chunk length %u]", chunkLength);
+ return;
+ }
+ dupTSN = (const u_char *)bp;
+ ND_PRINT("\n\t\t[dup TSN #%u: %u] ", tsnNo+1,
+ GET_BE_U_4(dupTSN));
+ }
+ break;
+ }
+ default :
+ {
+ bp += chunkLengthRemaining;
+ sctpPacketLengthRemaining -= chunkLengthRemaining;
+ chunkLengthRemaining = 0;
+ break;
+ }
+ }
+
+ /*
+ * Any extra stuff at the end of the chunk?
+ * XXX - report this?
+ */
+ bp += chunkLengthRemaining;
+ sctpPacketLengthRemaining -= chunkLengthRemaining;
+
+ if (ndo->ndo_vflag < 2)
+ sep = ", (";
+
+ if (align != 0) {
+ /*
+ * Fail if the alignment padding isn't in the captured data.
+ * Otherwise, skip it.
+ */
+ ND_TCHECK_LEN(bp, align);
+ bp += align;
+ sctpPacketLengthRemaining -= align;
+ }
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-sflow.c b/print-sflow.c
new file mode 100644
index 0000000..51325e2
--- /dev/null
+++ b/print-sflow.c
@@ -0,0 +1,926 @@
+/*
+ * Copyright (c) 1998-2007 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Carles Kishimoto <carles.kishimoto@gmail.com>
+ *
+ * Expansion and refactoring by Rick Jones <rick.jones2@hp.com>
+ */
+
+/* \summary: sFlow protocol printer */
+
+/* specification: https://sflow.org/developers/specifications.php */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+/*
+ * sFlow datagram
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sflow version (2,4,5) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IP version (1 for IPv4 | 2 for IPv6) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IP Address AGENT (4 or 16 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Sub agent ID |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Datagram sequence number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Switch uptime in ms |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | num samples in datagram |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+struct sflow_datagram_t {
+ nd_uint32_t version;
+ nd_uint32_t ip_version;
+ nd_ipv4 agent;
+ nd_uint32_t agent_id;
+ nd_uint32_t seqnum;
+ nd_uint32_t uptime;
+ nd_uint32_t samples;
+};
+
+struct sflow_sample_header {
+ nd_uint32_t format;
+ nd_uint32_t len;
+};
+
+#define SFLOW_FLOW_SAMPLE 1
+#define SFLOW_COUNTER_SAMPLE 2
+#define SFLOW_EXPANDED_FLOW_SAMPLE 3
+#define SFLOW_EXPANDED_COUNTER_SAMPLE 4
+
+static const struct tok sflow_format_values[] = {
+ { SFLOW_FLOW_SAMPLE, "flow sample" },
+ { SFLOW_COUNTER_SAMPLE, "counter sample" },
+ { SFLOW_EXPANDED_FLOW_SAMPLE, "expanded flow sample" },
+ { SFLOW_EXPANDED_COUNTER_SAMPLE, "expanded counter sample" },
+ { 0, NULL}
+};
+
+struct sflow_flow_sample_t {
+ nd_uint32_t seqnum;
+ nd_uint8_t type;
+ nd_uint24_t index;
+ nd_uint32_t rate;
+ nd_uint32_t pool;
+ nd_uint32_t drops;
+ nd_uint32_t in_interface;
+ nd_uint32_t out_interface;
+ nd_uint32_t records;
+
+};
+
+struct sflow_expanded_flow_sample_t {
+ nd_uint32_t seqnum;
+ nd_uint32_t type;
+ nd_uint32_t index;
+ nd_uint32_t rate;
+ nd_uint32_t pool;
+ nd_uint32_t drops;
+ nd_uint32_t in_interface_format;
+ nd_uint32_t in_interface_value;
+ nd_uint32_t out_interface_format;
+ nd_uint32_t out_interface_value;
+ nd_uint32_t records;
+};
+
+#define SFLOW_FLOW_RAW_PACKET 1
+#define SFLOW_FLOW_ETHERNET_FRAME 2
+#define SFLOW_FLOW_IPV4_DATA 3
+#define SFLOW_FLOW_IPV6_DATA 4
+#define SFLOW_FLOW_EXTENDED_SWITCH_DATA 1001
+#define SFLOW_FLOW_EXTENDED_ROUTER_DATA 1002
+#define SFLOW_FLOW_EXTENDED_GATEWAY_DATA 1003
+#define SFLOW_FLOW_EXTENDED_USER_DATA 1004
+#define SFLOW_FLOW_EXTENDED_URL_DATA 1005
+#define SFLOW_FLOW_EXTENDED_MPLS_DATA 1006
+#define SFLOW_FLOW_EXTENDED_NAT_DATA 1007
+#define SFLOW_FLOW_EXTENDED_MPLS_TUNNEL 1008
+#define SFLOW_FLOW_EXTENDED_MPLS_VC 1009
+#define SFLOW_FLOW_EXTENDED_MPLS_FEC 1010
+#define SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC 1011
+#define SFLOW_FLOW_EXTENDED_VLAN_TUNNEL 1012
+
+static const struct tok sflow_flow_type_values[] = {
+ { SFLOW_FLOW_RAW_PACKET, "Raw packet"},
+ { SFLOW_FLOW_ETHERNET_FRAME, "Ethernet frame"},
+ { SFLOW_FLOW_IPV4_DATA, "IPv4 Data"},
+ { SFLOW_FLOW_IPV6_DATA, "IPv6 Data"},
+ { SFLOW_FLOW_EXTENDED_SWITCH_DATA, "Extended Switch data"},
+ { SFLOW_FLOW_EXTENDED_ROUTER_DATA, "Extended Router data"},
+ { SFLOW_FLOW_EXTENDED_GATEWAY_DATA, "Extended Gateway data"},
+ { SFLOW_FLOW_EXTENDED_USER_DATA, "Extended User data"},
+ { SFLOW_FLOW_EXTENDED_URL_DATA, "Extended URL data"},
+ { SFLOW_FLOW_EXTENDED_MPLS_DATA, "Extended MPLS data"},
+ { SFLOW_FLOW_EXTENDED_NAT_DATA, "Extended NAT data"},
+ { SFLOW_FLOW_EXTENDED_MPLS_TUNNEL, "Extended MPLS tunnel"},
+ { SFLOW_FLOW_EXTENDED_MPLS_VC, "Extended MPLS VC"},
+ { SFLOW_FLOW_EXTENDED_MPLS_FEC, "Extended MPLS FEC"},
+ { SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC, "Extended MPLS LVP FEC"},
+ { SFLOW_FLOW_EXTENDED_VLAN_TUNNEL, "Extended VLAN Tunnel"},
+ { 0, NULL}
+};
+
+#define SFLOW_HEADER_PROTOCOL_ETHERNET 1
+#define SFLOW_HEADER_PROTOCOL_IPV4 11
+#define SFLOW_HEADER_PROTOCOL_IPV6 12
+
+static const struct tok sflow_flow_raw_protocol_values[] = {
+ { SFLOW_HEADER_PROTOCOL_ETHERNET, "Ethernet"},
+ { SFLOW_HEADER_PROTOCOL_IPV4, "IPv4"},
+ { SFLOW_HEADER_PROTOCOL_IPV6, "IPv6"},
+ { 0, NULL}
+};
+
+struct sflow_expanded_flow_raw_t {
+ nd_uint32_t protocol;
+ nd_uint32_t length;
+ nd_uint32_t stripped_bytes;
+ nd_uint32_t header_size;
+};
+
+struct sflow_ethernet_frame_t {
+ nd_uint32_t length;
+ nd_byte src_mac[8];
+ nd_byte dst_mac[8];
+ nd_uint32_t type;
+};
+
+struct sflow_extended_switch_data_t {
+ nd_uint32_t src_vlan;
+ nd_uint32_t src_pri;
+ nd_uint32_t dst_vlan;
+ nd_uint32_t dst_pri;
+};
+
+struct sflow_counter_record_t {
+ nd_uint32_t format;
+ nd_uint32_t length;
+};
+
+struct sflow_flow_record_t {
+ nd_uint32_t format;
+ nd_uint32_t length;
+};
+
+struct sflow_counter_sample_t {
+ nd_uint32_t seqnum;
+ nd_uint8_t type;
+ nd_uint24_t index;
+ nd_uint32_t records;
+};
+
+struct sflow_expanded_counter_sample_t {
+ nd_uint32_t seqnum;
+ nd_uint32_t type;
+ nd_uint32_t index;
+ nd_uint32_t records;
+};
+
+#define SFLOW_COUNTER_GENERIC 1
+#define SFLOW_COUNTER_ETHERNET 2
+#define SFLOW_COUNTER_TOKEN_RING 3
+#define SFLOW_COUNTER_BASEVG 4
+#define SFLOW_COUNTER_VLAN 5
+#define SFLOW_COUNTER_PROCESSOR 1001
+
+static const struct tok sflow_counter_type_values[] = {
+ { SFLOW_COUNTER_GENERIC, "Generic counter"},
+ { SFLOW_COUNTER_ETHERNET, "Ethernet counter"},
+ { SFLOW_COUNTER_TOKEN_RING, "Token ring counter"},
+ { SFLOW_COUNTER_BASEVG, "100 BaseVG counter"},
+ { SFLOW_COUNTER_VLAN, "Vlan counter"},
+ { SFLOW_COUNTER_PROCESSOR, "Processor counter"},
+ { 0, NULL}
+};
+
+#define SFLOW_IFACE_DIRECTION_UNKNOWN 0
+#define SFLOW_IFACE_DIRECTION_FULLDUPLEX 1
+#define SFLOW_IFACE_DIRECTION_HALFDUPLEX 2
+#define SFLOW_IFACE_DIRECTION_IN 3
+#define SFLOW_IFACE_DIRECTION_OUT 4
+
+static const struct tok sflow_iface_direction_values[] = {
+ { SFLOW_IFACE_DIRECTION_UNKNOWN, "unknown"},
+ { SFLOW_IFACE_DIRECTION_FULLDUPLEX, "full-duplex"},
+ { SFLOW_IFACE_DIRECTION_HALFDUPLEX, "half-duplex"},
+ { SFLOW_IFACE_DIRECTION_IN, "in"},
+ { SFLOW_IFACE_DIRECTION_OUT, "out"},
+ { 0, NULL}
+};
+
+struct sflow_generic_counter_t {
+ nd_uint32_t ifindex;
+ nd_uint32_t iftype;
+ nd_uint64_t ifspeed;
+ nd_uint32_t ifdirection;
+ nd_uint32_t ifstatus;
+ nd_uint64_t ifinoctets;
+ nd_uint32_t ifinunicastpkts;
+ nd_uint32_t ifinmulticastpkts;
+ nd_uint32_t ifinbroadcastpkts;
+ nd_uint32_t ifindiscards;
+ nd_uint32_t ifinerrors;
+ nd_uint32_t ifinunkownprotos;
+ nd_uint64_t ifoutoctets;
+ nd_uint32_t ifoutunicastpkts;
+ nd_uint32_t ifoutmulticastpkts;
+ nd_uint32_t ifoutbroadcastpkts;
+ nd_uint32_t ifoutdiscards;
+ nd_uint32_t ifouterrors;
+ nd_uint32_t ifpromiscmode;
+};
+
+struct sflow_ethernet_counter_t {
+ nd_uint32_t alignerrors;
+ nd_uint32_t fcserrors;
+ nd_uint32_t single_collision_frames;
+ nd_uint32_t multiple_collision_frames;
+ nd_uint32_t test_errors;
+ nd_uint32_t deferred_transmissions;
+ nd_uint32_t late_collisions;
+ nd_uint32_t excessive_collisions;
+ nd_uint32_t mac_transmit_errors;
+ nd_uint32_t carrier_sense_errors;
+ nd_uint32_t frame_too_longs;
+ nd_uint32_t mac_receive_errors;
+ nd_uint32_t symbol_errors;
+};
+
+struct sflow_100basevg_counter_t {
+ nd_uint32_t in_highpriority_frames;
+ nd_uint64_t in_highpriority_octets;
+ nd_uint32_t in_normpriority_frames;
+ nd_uint64_t in_normpriority_octets;
+ nd_uint32_t in_ipmerrors;
+ nd_uint32_t in_oversized;
+ nd_uint32_t in_data_errors;
+ nd_uint32_t in_null_addressed_frames;
+ nd_uint32_t out_highpriority_frames;
+ nd_uint64_t out_highpriority_octets;
+ nd_uint32_t transitioninto_frames;
+ nd_uint64_t hc_in_highpriority_octets;
+ nd_uint64_t hc_in_normpriority_octets;
+ nd_uint64_t hc_out_highpriority_octets;
+};
+
+struct sflow_vlan_counter_t {
+ nd_uint32_t vlan_id;
+ nd_uint64_t octets;
+ nd_uint32_t unicast_pkt;
+ nd_uint32_t multicast_pkt;
+ nd_uint32_t broadcast_pkt;
+ nd_uint32_t discards;
+};
+
+static int
+print_sflow_counter_generic(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_generic_counter_t *sflow_gen_counter;
+
+ if (len < sizeof(struct sflow_generic_counter_t))
+ return 1;
+
+ sflow_gen_counter = (const struct sflow_generic_counter_t *)pointer;
+ ND_PRINT("\n\t ifindex %u, iftype %u, ifspeed %" PRIu64 ", ifdirection %u (%s)",
+ GET_BE_U_4(sflow_gen_counter->ifindex),
+ GET_BE_U_4(sflow_gen_counter->iftype),
+ GET_BE_U_8(sflow_gen_counter->ifspeed),
+ GET_BE_U_4(sflow_gen_counter->ifdirection),
+ tok2str(sflow_iface_direction_values, "Unknown",
+ GET_BE_U_4(sflow_gen_counter->ifdirection)));
+ ND_PRINT("\n\t ifstatus %u, adminstatus: %s, operstatus: %s",
+ GET_BE_U_4(sflow_gen_counter->ifstatus),
+ GET_BE_U_4(sflow_gen_counter->ifstatus)&1 ? "up" : "down",
+ (GET_BE_U_4(sflow_gen_counter->ifstatus)>>1)&1 ? "up" : "down");
+ ND_PRINT("\n\t In octets %" PRIu64
+ ", unicast pkts %u, multicast pkts %u, broadcast pkts %u, discards %u",
+ GET_BE_U_8(sflow_gen_counter->ifinoctets),
+ GET_BE_U_4(sflow_gen_counter->ifinunicastpkts),
+ GET_BE_U_4(sflow_gen_counter->ifinmulticastpkts),
+ GET_BE_U_4(sflow_gen_counter->ifinbroadcastpkts),
+ GET_BE_U_4(sflow_gen_counter->ifindiscards));
+ ND_PRINT("\n\t In errors %u, unknown protos %u",
+ GET_BE_U_4(sflow_gen_counter->ifinerrors),
+ GET_BE_U_4(sflow_gen_counter->ifinunkownprotos));
+ ND_PRINT("\n\t Out octets %" PRIu64
+ ", unicast pkts %u, multicast pkts %u, broadcast pkts %u, discards %u",
+ GET_BE_U_8(sflow_gen_counter->ifoutoctets),
+ GET_BE_U_4(sflow_gen_counter->ifoutunicastpkts),
+ GET_BE_U_4(sflow_gen_counter->ifoutmulticastpkts),
+ GET_BE_U_4(sflow_gen_counter->ifoutbroadcastpkts),
+ GET_BE_U_4(sflow_gen_counter->ifoutdiscards));
+ ND_PRINT("\n\t Out errors %u, promisc mode %u",
+ GET_BE_U_4(sflow_gen_counter->ifouterrors),
+ GET_BE_U_4(sflow_gen_counter->ifpromiscmode));
+
+ return 0;
+}
+
+static int
+print_sflow_counter_ethernet(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_ethernet_counter_t *sflow_eth_counter;
+
+ if (len < sizeof(struct sflow_ethernet_counter_t))
+ return 1;
+
+ sflow_eth_counter = (const struct sflow_ethernet_counter_t *)pointer;
+ ND_PRINT("\n\t align errors %u, fcs errors %u, single collision %u, multiple collision %u, test error %u",
+ GET_BE_U_4(sflow_eth_counter->alignerrors),
+ GET_BE_U_4(sflow_eth_counter->fcserrors),
+ GET_BE_U_4(sflow_eth_counter->single_collision_frames),
+ GET_BE_U_4(sflow_eth_counter->multiple_collision_frames),
+ GET_BE_U_4(sflow_eth_counter->test_errors));
+ ND_PRINT("\n\t deferred %u, late collision %u, excessive collision %u, mac trans error %u",
+ GET_BE_U_4(sflow_eth_counter->deferred_transmissions),
+ GET_BE_U_4(sflow_eth_counter->late_collisions),
+ GET_BE_U_4(sflow_eth_counter->excessive_collisions),
+ GET_BE_U_4(sflow_eth_counter->mac_transmit_errors));
+ ND_PRINT("\n\t carrier error %u, frames too long %u, mac receive errors %u, symbol errors %u",
+ GET_BE_U_4(sflow_eth_counter->carrier_sense_errors),
+ GET_BE_U_4(sflow_eth_counter->frame_too_longs),
+ GET_BE_U_4(sflow_eth_counter->mac_receive_errors),
+ GET_BE_U_4(sflow_eth_counter->symbol_errors));
+
+ return 0;
+}
+
+static int
+print_sflow_counter_token_ring(netdissect_options *ndo _U_,
+ const u_char *pointer _U_, u_int len _U_)
+{
+ return 0;
+}
+
+static int
+print_sflow_counter_basevg(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_100basevg_counter_t *sflow_100basevg_counter;
+
+ if (len < sizeof(struct sflow_100basevg_counter_t))
+ return 1;
+
+ sflow_100basevg_counter = (const struct sflow_100basevg_counter_t *)pointer;
+ ND_PRINT("\n\t in high prio frames %u, in high prio octets %" PRIu64,
+ GET_BE_U_4(sflow_100basevg_counter->in_highpriority_frames),
+ GET_BE_U_8(sflow_100basevg_counter->in_highpriority_octets));
+ ND_PRINT("\n\t in norm prio frames %u, in norm prio octets %" PRIu64,
+ GET_BE_U_4(sflow_100basevg_counter->in_normpriority_frames),
+ GET_BE_U_8(sflow_100basevg_counter->in_normpriority_octets));
+ ND_PRINT("\n\t in ipm errors %u, oversized %u, in data errors %u, null addressed frames %u",
+ GET_BE_U_4(sflow_100basevg_counter->in_ipmerrors),
+ GET_BE_U_4(sflow_100basevg_counter->in_oversized),
+ GET_BE_U_4(sflow_100basevg_counter->in_data_errors),
+ GET_BE_U_4(sflow_100basevg_counter->in_null_addressed_frames));
+ ND_PRINT("\n\t out high prio frames %u, out high prio octets %" PRIu64
+ ", trans into frames %u",
+ GET_BE_U_4(sflow_100basevg_counter->out_highpriority_frames),
+ GET_BE_U_8(sflow_100basevg_counter->out_highpriority_octets),
+ GET_BE_U_4(sflow_100basevg_counter->transitioninto_frames));
+ ND_PRINT("\n\t in hc high prio octets %" PRIu64
+ ", in hc norm prio octets %" PRIu64
+ ", out hc high prio octets %" PRIu64,
+ GET_BE_U_8(sflow_100basevg_counter->hc_in_highpriority_octets),
+ GET_BE_U_8(sflow_100basevg_counter->hc_in_normpriority_octets),
+ GET_BE_U_8(sflow_100basevg_counter->hc_out_highpriority_octets));
+
+ return 0;
+}
+
+static int
+print_sflow_counter_vlan(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_vlan_counter_t *sflow_vlan_counter;
+
+ if (len < sizeof(struct sflow_vlan_counter_t))
+ return 1;
+
+ sflow_vlan_counter = (const struct sflow_vlan_counter_t *)pointer;
+ ND_PRINT("\n\t vlan_id %u, octets %" PRIu64
+ ", unicast_pkt %u, multicast_pkt %u, broadcast_pkt %u, discards %u",
+ GET_BE_U_4(sflow_vlan_counter->vlan_id),
+ GET_BE_U_8(sflow_vlan_counter->octets),
+ GET_BE_U_4(sflow_vlan_counter->unicast_pkt),
+ GET_BE_U_4(sflow_vlan_counter->multicast_pkt),
+ GET_BE_U_4(sflow_vlan_counter->broadcast_pkt),
+ GET_BE_U_4(sflow_vlan_counter->discards));
+
+ return 0;
+}
+
+struct sflow_processor_counter_t {
+ nd_uint32_t five_sec_util;
+ nd_uint32_t one_min_util;
+ nd_uint32_t five_min_util;
+ nd_uint64_t total_memory;
+ nd_uint64_t free_memory;
+};
+
+static int
+print_sflow_counter_processor(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_processor_counter_t *sflow_processor_counter;
+
+ if (len < sizeof(struct sflow_processor_counter_t))
+ return 1;
+
+ sflow_processor_counter = (const struct sflow_processor_counter_t *)pointer;
+ ND_PRINT("\n\t 5sec %u, 1min %u, 5min %u, total_mem %" PRIu64
+ ", total_mem %" PRIu64,
+ GET_BE_U_4(sflow_processor_counter->five_sec_util),
+ GET_BE_U_4(sflow_processor_counter->one_min_util),
+ GET_BE_U_4(sflow_processor_counter->five_min_util),
+ GET_BE_U_8(sflow_processor_counter->total_memory),
+ GET_BE_U_8(sflow_processor_counter->free_memory));
+
+ return 0;
+}
+
+static int
+sflow_print_counter_records(netdissect_options *ndo,
+ const u_char *pointer, u_int len, u_int records)
+{
+ u_int nrecords;
+ const u_char *tptr;
+ u_int tlen;
+ u_int counter_type;
+ u_int counter_len;
+ u_int enterprise;
+ const struct sflow_counter_record_t *sflow_counter_record;
+
+ nrecords = records;
+ tptr = pointer;
+ tlen = len;
+
+ while (nrecords > 0) {
+ /* do we have the "header?" */
+ if (tlen < sizeof(struct sflow_counter_record_t))
+ return 1;
+ sflow_counter_record = (const struct sflow_counter_record_t *)tptr;
+
+ enterprise = GET_BE_U_4(sflow_counter_record->format);
+ counter_type = enterprise & 0x0FFF;
+ enterprise = enterprise >> 20;
+ counter_len = GET_BE_U_4(sflow_counter_record->length);
+ ND_PRINT("\n\t enterprise %u, %s (%u) length %u",
+ enterprise,
+ (enterprise == 0) ? tok2str(sflow_counter_type_values,"Unknown",counter_type) : "Unknown",
+ counter_type,
+ counter_len);
+
+ tptr += sizeof(struct sflow_counter_record_t);
+ tlen -= sizeof(struct sflow_counter_record_t);
+
+ if (tlen < counter_len)
+ return 1;
+ if (enterprise == 0) {
+ switch (counter_type) {
+ case SFLOW_COUNTER_GENERIC:
+ if (print_sflow_counter_generic(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_ETHERNET:
+ if (print_sflow_counter_ethernet(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_TOKEN_RING:
+ if (print_sflow_counter_token_ring(ndo, tptr,tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_BASEVG:
+ if (print_sflow_counter_basevg(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_VLAN:
+ if (print_sflow_counter_vlan(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_COUNTER_PROCESSOR:
+ if (print_sflow_counter_processor(ndo, tptr, tlen))
+ return 1;
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr, "\n\t\t", counter_len);
+ break;
+ }
+ }
+ tptr += counter_len;
+ tlen -= counter_len;
+ nrecords--;
+
+ }
+
+ return 0;
+}
+
+static int
+sflow_print_counter_sample(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_counter_sample_t *sflow_counter_sample;
+ u_int nrecords;
+
+ if (len < sizeof(struct sflow_counter_sample_t))
+ return 1;
+
+ sflow_counter_sample = (const struct sflow_counter_sample_t *)pointer;
+
+ nrecords = GET_BE_U_4(sflow_counter_sample->records);
+
+ ND_PRINT(" seqnum %u, type %u, idx %u, records %u",
+ GET_BE_U_4(sflow_counter_sample->seqnum),
+ GET_U_1(sflow_counter_sample->type),
+ GET_BE_U_3(sflow_counter_sample->index),
+ nrecords);
+
+ return sflow_print_counter_records(ndo, pointer + sizeof(struct sflow_counter_sample_t),
+ len - sizeof(struct sflow_counter_sample_t),
+ nrecords);
+}
+
+static int
+sflow_print_expanded_counter_sample(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_expanded_counter_sample_t *sflow_expanded_counter_sample;
+ u_int nrecords;
+
+
+ if (len < sizeof(struct sflow_expanded_counter_sample_t))
+ return 1;
+
+ sflow_expanded_counter_sample = (const struct sflow_expanded_counter_sample_t *)pointer;
+
+ nrecords = GET_BE_U_4(sflow_expanded_counter_sample->records);
+
+ ND_PRINT(" seqnum %u, type %u, idx %u, records %u",
+ GET_BE_U_4(sflow_expanded_counter_sample->seqnum),
+ GET_BE_U_4(sflow_expanded_counter_sample->type),
+ GET_BE_U_4(sflow_expanded_counter_sample->index),
+ nrecords);
+
+ return sflow_print_counter_records(ndo, pointer + sizeof(struct sflow_expanded_counter_sample_t),
+ len - sizeof(struct sflow_expanded_counter_sample_t),
+ nrecords);
+}
+
+static int
+print_sflow_raw_packet(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_expanded_flow_raw_t *sflow_flow_raw;
+
+ if (len < sizeof(struct sflow_expanded_flow_raw_t))
+ return 1;
+
+ sflow_flow_raw = (const struct sflow_expanded_flow_raw_t *)pointer;
+ ND_PRINT("\n\t protocol %s (%u), length %u, stripped bytes %u, header_size %u",
+ tok2str(sflow_flow_raw_protocol_values,"Unknown",GET_BE_U_4(sflow_flow_raw->protocol)),
+ GET_BE_U_4(sflow_flow_raw->protocol),
+ GET_BE_U_4(sflow_flow_raw->length),
+ GET_BE_U_4(sflow_flow_raw->stripped_bytes),
+ GET_BE_U_4(sflow_flow_raw->header_size));
+
+ /* QUESTION - should we attempt to print the raw header itself?
+ assuming of course there is enough data present to do so... */
+
+ return 0;
+}
+
+static int
+print_sflow_ethernet_frame(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_ethernet_frame_t *sflow_ethernet_frame;
+
+ if (len < sizeof(struct sflow_ethernet_frame_t))
+ return 1;
+
+ sflow_ethernet_frame = (const struct sflow_ethernet_frame_t *)pointer;
+
+ ND_PRINT("\n\t frame len %u, type %u",
+ GET_BE_U_4(sflow_ethernet_frame->length),
+ GET_BE_U_4(sflow_ethernet_frame->type));
+
+ return 0;
+}
+
+static int
+print_sflow_extended_switch_data(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_extended_switch_data_t *sflow_extended_sw_data;
+
+ if (len < sizeof(struct sflow_extended_switch_data_t))
+ return 1;
+
+ sflow_extended_sw_data = (const struct sflow_extended_switch_data_t *)pointer;
+ ND_PRINT("\n\t src vlan %u, src pri %u, dst vlan %u, dst pri %u",
+ GET_BE_U_4(sflow_extended_sw_data->src_vlan),
+ GET_BE_U_4(sflow_extended_sw_data->src_pri),
+ GET_BE_U_4(sflow_extended_sw_data->dst_vlan),
+ GET_BE_U_4(sflow_extended_sw_data->dst_pri));
+
+ return 0;
+}
+
+static int
+sflow_print_flow_records(netdissect_options *ndo,
+ const u_char *pointer, u_int len, u_int records)
+{
+ u_int nrecords;
+ const u_char *tptr;
+ u_int tlen;
+ u_int flow_type;
+ u_int enterprise;
+ u_int flow_len;
+ const struct sflow_flow_record_t *sflow_flow_record;
+
+ nrecords = records;
+ tptr = pointer;
+ tlen = len;
+
+ while (nrecords > 0) {
+ /* do we have the "header?" */
+ if (tlen < sizeof(struct sflow_flow_record_t))
+ return 1;
+
+ sflow_flow_record = (const struct sflow_flow_record_t *)tptr;
+
+ /* so, the funky encoding means we cannot blythly mask-off
+ bits, we must also check the enterprise. */
+
+ enterprise = GET_BE_U_4(sflow_flow_record->format);
+ flow_type = enterprise & 0x0FFF;
+ enterprise = enterprise >> 12;
+ flow_len = GET_BE_U_4(sflow_flow_record->length);
+ ND_PRINT("\n\t enterprise %u %s (%u) length %u",
+ enterprise,
+ (enterprise == 0) ? tok2str(sflow_flow_type_values,"Unknown",flow_type) : "Unknown",
+ flow_type,
+ flow_len);
+
+ tptr += sizeof(struct sflow_flow_record_t);
+ tlen -= sizeof(struct sflow_flow_record_t);
+
+ if (tlen < flow_len)
+ return 1;
+
+ if (enterprise == 0) {
+ switch (flow_type) {
+ case SFLOW_FLOW_RAW_PACKET:
+ if (print_sflow_raw_packet(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_FLOW_EXTENDED_SWITCH_DATA:
+ if (print_sflow_extended_switch_data(ndo, tptr, tlen))
+ return 1;
+ break;
+ case SFLOW_FLOW_ETHERNET_FRAME:
+ if (print_sflow_ethernet_frame(ndo, tptr, tlen))
+ return 1;
+ break;
+ /* FIXME these need a decoder */
+ case SFLOW_FLOW_IPV4_DATA:
+ case SFLOW_FLOW_IPV6_DATA:
+ case SFLOW_FLOW_EXTENDED_ROUTER_DATA:
+ case SFLOW_FLOW_EXTENDED_GATEWAY_DATA:
+ case SFLOW_FLOW_EXTENDED_USER_DATA:
+ case SFLOW_FLOW_EXTENDED_URL_DATA:
+ case SFLOW_FLOW_EXTENDED_MPLS_DATA:
+ case SFLOW_FLOW_EXTENDED_NAT_DATA:
+ case SFLOW_FLOW_EXTENDED_MPLS_TUNNEL:
+ case SFLOW_FLOW_EXTENDED_MPLS_VC:
+ case SFLOW_FLOW_EXTENDED_MPLS_FEC:
+ case SFLOW_FLOW_EXTENDED_MPLS_LVP_FEC:
+ case SFLOW_FLOW_EXTENDED_VLAN_TUNNEL:
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr, "\n\t\t", flow_len);
+ break;
+ }
+ }
+ tptr += flow_len;
+ tlen -= flow_len;
+ nrecords--;
+
+ }
+
+ return 0;
+}
+
+static int
+sflow_print_flow_sample(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_flow_sample_t *sflow_flow_sample;
+ u_int nrecords;
+
+ if (len < sizeof(struct sflow_flow_sample_t))
+ return 1;
+
+ sflow_flow_sample = (const struct sflow_flow_sample_t *)pointer;
+
+ nrecords = GET_BE_U_4(sflow_flow_sample->records);
+
+ ND_PRINT(" seqnum %u, type %u, idx %u, rate %u, pool %u, drops %u, input %u output %u records %u",
+ GET_BE_U_4(sflow_flow_sample->seqnum),
+ GET_U_1(sflow_flow_sample->type),
+ GET_BE_U_3(sflow_flow_sample->index),
+ GET_BE_U_4(sflow_flow_sample->rate),
+ GET_BE_U_4(sflow_flow_sample->pool),
+ GET_BE_U_4(sflow_flow_sample->drops),
+ GET_BE_U_4(sflow_flow_sample->in_interface),
+ GET_BE_U_4(sflow_flow_sample->out_interface),
+ nrecords);
+
+ return sflow_print_flow_records(ndo, pointer + sizeof(struct sflow_flow_sample_t),
+ len - sizeof(struct sflow_flow_sample_t),
+ nrecords);
+}
+
+static int
+sflow_print_expanded_flow_sample(netdissect_options *ndo,
+ const u_char *pointer, u_int len)
+{
+ const struct sflow_expanded_flow_sample_t *sflow_expanded_flow_sample;
+ u_int nrecords;
+
+ if (len < sizeof(struct sflow_expanded_flow_sample_t))
+ return 1;
+
+ sflow_expanded_flow_sample = (const struct sflow_expanded_flow_sample_t *)pointer;
+
+ nrecords = GET_BE_U_4(sflow_expanded_flow_sample->records);
+
+ ND_PRINT(" seqnum %u, type %u, idx %u, rate %u, pool %u, drops %u, records %u",
+ GET_BE_U_4(sflow_expanded_flow_sample->seqnum),
+ GET_BE_U_4(sflow_expanded_flow_sample->type),
+ GET_BE_U_4(sflow_expanded_flow_sample->index),
+ GET_BE_U_4(sflow_expanded_flow_sample->rate),
+ GET_BE_U_4(sflow_expanded_flow_sample->pool),
+ GET_BE_U_4(sflow_expanded_flow_sample->drops),
+ nrecords);
+
+ return sflow_print_flow_records(ndo, pointer + sizeof(struct sflow_expanded_flow_sample_t),
+ len - sizeof(struct sflow_expanded_flow_sample_t),
+ nrecords);
+}
+
+void
+sflow_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ const struct sflow_datagram_t *sflow_datagram;
+ const struct sflow_sample_header *sflow_sample;
+
+ const u_char *tptr;
+ u_int tlen;
+ uint32_t sflow_sample_type, sflow_sample_len;
+ uint32_t nsamples;
+
+ ndo->ndo_protocol = "sflow";
+ tptr = pptr;
+ tlen = len;
+ sflow_datagram = (const struct sflow_datagram_t *)pptr;
+ if (len < sizeof(struct sflow_datagram_t)) {
+ ND_PRINT("sFlowv%u", GET_BE_U_4(sflow_datagram->version));
+ ND_PRINT(" [length %u < %zu]", len, sizeof(struct sflow_datagram_t));
+ nd_print_invalid(ndo);
+ return;
+ }
+ ND_TCHECK_SIZE(sflow_datagram);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (GET_BE_U_4(sflow_datagram->version) != 5) {
+ ND_PRINT("sFlow version %u packet not supported",
+ GET_BE_U_4(sflow_datagram->version));
+ return;
+ }
+
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("sFlowv%u, %s agent %s, agent-id %u, length %u",
+ GET_BE_U_4(sflow_datagram->version),
+ GET_BE_U_4(sflow_datagram->ip_version) == 1 ? "IPv4" : "IPv6",
+ GET_IPADDR_STRING(sflow_datagram->agent),
+ GET_BE_U_4(sflow_datagram->agent_id),
+ len);
+ return;
+ }
+
+ /* ok they seem to want to know everything - lets fully decode it */
+ nsamples=GET_BE_U_4(sflow_datagram->samples);
+ ND_PRINT("sFlowv%u, %s agent %s, agent-id %u, seqnum %u, uptime %u, samples %u, length %u",
+ GET_BE_U_4(sflow_datagram->version),
+ GET_BE_U_4(sflow_datagram->ip_version) == 1 ? "IPv4" : "IPv6",
+ GET_IPADDR_STRING(sflow_datagram->agent),
+ GET_BE_U_4(sflow_datagram->agent_id),
+ GET_BE_U_4(sflow_datagram->seqnum),
+ GET_BE_U_4(sflow_datagram->uptime),
+ nsamples,
+ len);
+
+ /* skip Common header */
+ tptr += sizeof(struct sflow_datagram_t);
+ tlen -= sizeof(struct sflow_datagram_t);
+
+ while (nsamples > 0 && tlen > 0) {
+ sflow_sample = (const struct sflow_sample_header *)tptr;
+
+ sflow_sample_type = (GET_BE_U_4(sflow_sample->format)&0x0FFF);
+ sflow_sample_len = GET_BE_U_4(sflow_sample->len);
+
+ if (tlen < sizeof(struct sflow_sample_header))
+ goto invalid;
+
+ tptr += sizeof(struct sflow_sample_header);
+ tlen -= sizeof(struct sflow_sample_header);
+
+ ND_PRINT("\n\t%s (%u), length %u,",
+ tok2str(sflow_format_values, "Unknown", sflow_sample_type),
+ sflow_sample_type,
+ sflow_sample_len);
+
+ /* basic sanity check */
+ if (sflow_sample_type == 0 || sflow_sample_len ==0) {
+ return;
+ }
+
+ if (tlen < sflow_sample_len)
+ goto invalid;
+
+ /* did we capture enough for fully decoding the sample ? */
+ ND_TCHECK_LEN(tptr, sflow_sample_len);
+
+ switch(sflow_sample_type) {
+ case SFLOW_FLOW_SAMPLE:
+ if (sflow_print_flow_sample(ndo, tptr, tlen))
+ goto invalid;
+ break;
+
+ case SFLOW_COUNTER_SAMPLE:
+ if (sflow_print_counter_sample(ndo, tptr,tlen))
+ goto invalid;
+ break;
+
+ case SFLOW_EXPANDED_FLOW_SAMPLE:
+ if (sflow_print_expanded_flow_sample(ndo, tptr, tlen))
+ goto invalid;
+ break;
+
+ case SFLOW_EXPANDED_COUNTER_SAMPLE:
+ if (sflow_print_expanded_counter_sample(ndo, tptr,tlen))
+ goto invalid;
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tptr, "\n\t ", sflow_sample_len);
+ break;
+ }
+ tptr += sflow_sample_len;
+ tlen -= sflow_sample_len;
+ nsamples--;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+ ND_TCHECK_LEN(tptr, tlen);
+}
diff --git a/print-sip.c b/print-sip.c
new file mode 100644
index 0000000..bfbfddf
--- /dev/null
+++ b/print-sip.c
@@ -0,0 +1,54 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ * Turned into common "text protocol" code, which this uses, by
+ * Guy Harris.
+ */
+
+/* \summary: Session Initiation Protocol (SIP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+static const char *sipcmds[] = {
+ "ACK",
+ "BYE",
+ "CANCEL",
+ "DO",
+ "INFO",
+ "INVITE",
+ "MESSAGE",
+ "NOTIFY",
+ "OPTIONS",
+ "PRACK",
+ "QAUTH",
+ "REFER",
+ "REGISTER",
+ "SPRACK",
+ "SUBSCRIBE",
+ "UPDATE",
+ "PUBLISH",
+ NULL
+};
+
+void
+sip_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ ndo->ndo_protocol = "sip";
+ txtproto_print(ndo, pptr, len, sipcmds, RESP_CODE_SECOND_TOKEN);
+}
diff --git a/print-sl.c b/print-sl.c
new file mode 100644
index 0000000..85b7624
--- /dev/null
+++ b/print-sl.c
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Compressed Serial Line Internet Protocol printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+#include "ip.h"
+#include "tcp.h"
+#include "slcompress.h"
+
+/*
+ * definitions of the pseudo- link-level header attached to slip
+ * packets grabbed by the packet filter (bpf) traffic monitor.
+ */
+#define SLIP_HDRLEN 16
+
+#define SLX_DIR 0
+#define SLX_CHDR 1
+
+#define SLIPDIR_IN 0
+#define SLIPDIR_OUT 1
+
+
+static u_int lastlen[2][256];
+static u_int lastconn = 255;
+
+static void sliplink_print(netdissect_options *, const u_char *, const struct ip *, u_int);
+static void compressed_sl_print(netdissect_options *, const u_char *, const struct ip *, u_int, int);
+
+void
+sl_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ const struct ip *ip;
+
+ ndo->ndo_protocol = "slip";
+ ND_TCHECK_LEN(p, SLIP_HDRLEN);
+ ndo->ndo_ll_hdr_len += SLIP_HDRLEN;
+
+ length -= SLIP_HDRLEN;
+
+ ip = (const struct ip *)(p + SLIP_HDRLEN);
+
+ if (ndo->ndo_eflag)
+ sliplink_print(ndo, p, ip, length);
+
+ switch (IP_V(ip)) {
+ case 4:
+ ip_print(ndo, (const u_char *)ip, length);
+ break;
+ case 6:
+ ip6_print(ndo, (const u_char *)ip, length);
+ break;
+ default:
+ ND_PRINT("ip v%u", IP_V(ip));
+ }
+}
+
+void
+sl_bsdos_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ const struct ip *ip;
+
+ ndo->ndo_protocol = "slip_bsdos";
+ ND_TCHECK_LEN(p, SLIP_HDRLEN);
+ ndo->ndo_ll_hdr_len += SLIP_HDRLEN;
+
+ length -= SLIP_HDRLEN;
+
+ ip = (const struct ip *)(p + SLIP_HDRLEN);
+
+#ifdef notdef
+ if (ndo->ndo_eflag)
+ sliplink_print(ndo, p, ip, length);
+#endif
+
+ ip_print(ndo, (const u_char *)ip, length);
+}
+
+static void
+sliplink_print(netdissect_options *ndo,
+ const u_char *p, const struct ip *ip,
+ u_int length)
+{
+ int dir;
+ u_int hlen;
+
+ dir = GET_U_1(p + SLX_DIR);
+ switch (dir) {
+
+ case SLIPDIR_IN:
+ ND_PRINT("I ");
+ break;
+
+ case SLIPDIR_OUT:
+ ND_PRINT("O ");
+ break;
+
+ default:
+ ND_PRINT("Invalid direction %d ", dir);
+ dir = -1;
+ break;
+ }
+ switch (GET_U_1(p + SLX_CHDR) & 0xf0) {
+
+ case TYPE_IP:
+ ND_PRINT("ip %u: ", length + SLIP_HDRLEN);
+ break;
+
+ case TYPE_UNCOMPRESSED_TCP:
+ /*
+ * The connection id is stored in the IP protocol field.
+ * Get it from the link layer since sl_uncompress_tcp()
+ * has restored the IP header copy to IPPROTO_TCP.
+ */
+ lastconn = GET_U_1(((const struct ip *)(p + SLX_CHDR))->ip_p);
+ ND_PRINT("utcp %u: ", lastconn);
+ if (dir == -1) {
+ /* Direction is bogus, don't use it */
+ return;
+ }
+ ND_TCHECK_SIZE(ip);
+ hlen = IP_HL(ip);
+ ND_TCHECK_SIZE((const struct tcphdr *)&((const int *)ip)[hlen]);
+ hlen += TH_OFF((const struct tcphdr *)&((const int *)ip)[hlen]);
+ lastlen[dir][lastconn] = length - (hlen << 2);
+ break;
+
+ default:
+ if (dir == -1) {
+ /* Direction is bogus, don't use it */
+ return;
+ }
+ if (GET_U_1(p + SLX_CHDR) & TYPE_COMPRESSED_TCP) {
+ compressed_sl_print(ndo, p + SLX_CHDR, ip, length, dir);
+ ND_PRINT(": ");
+ } else
+ ND_PRINT("slip-%u!: ", GET_U_1(p + SLX_CHDR));
+ }
+}
+
+static const u_char *
+print_sl_change(netdissect_options *ndo,
+ const char *str, const u_char *cp)
+{
+ u_int i;
+
+ if ((i = GET_U_1(cp)) == 0) {
+ cp++;
+ i = GET_BE_U_2(cp);
+ cp += 2;
+ }
+ ND_PRINT(" %s%u", str, i);
+ return (cp);
+}
+
+static const u_char *
+print_sl_winchange(netdissect_options *ndo,
+ const u_char *cp)
+{
+ int16_t i;
+
+ if ((i = GET_U_1(cp)) == 0) {
+ cp++;
+ i = GET_BE_S_2(cp);
+ cp += 2;
+ }
+ if (i >= 0)
+ ND_PRINT(" W+%d", i);
+ else
+ ND_PRINT(" W%d", i);
+ return (cp);
+}
+
+static void
+compressed_sl_print(netdissect_options *ndo,
+ const u_char *chdr, const struct ip *ip,
+ u_int length, int dir)
+{
+ const u_char *cp = chdr;
+ u_int flags, hlen;
+
+ flags = GET_U_1(cp);
+ cp++;
+ if (flags & NEW_C) {
+ lastconn = GET_U_1(cp);
+ cp++;
+ ND_PRINT("ctcp %u", lastconn);
+ } else
+ ND_PRINT("ctcp *");
+
+ /* skip tcp checksum */
+ cp += 2;
+
+ switch (flags & SPECIALS_MASK) {
+ case SPECIAL_I:
+ ND_PRINT(" *SA+%u", lastlen[dir][lastconn]);
+ break;
+
+ case SPECIAL_D:
+ ND_PRINT(" *S+%u", lastlen[dir][lastconn]);
+ break;
+
+ default:
+ if (flags & NEW_U)
+ cp = print_sl_change(ndo, "U=", cp);
+ if (flags & NEW_W)
+ cp = print_sl_winchange(ndo, cp);
+ if (flags & NEW_A)
+ cp = print_sl_change(ndo, "A+", cp);
+ if (flags & NEW_S)
+ cp = print_sl_change(ndo, "S+", cp);
+ break;
+ }
+ if (flags & NEW_I)
+ cp = print_sl_change(ndo, "I+", cp);
+
+ /*
+ * 'hlen' is the length of the uncompressed TCP/IP header (in words).
+ * 'cp - chdr' is the length of the compressed header.
+ * 'length - hlen' is the amount of data in the packet.
+ */
+ ND_TCHECK_SIZE(ip);
+ hlen = IP_HL(ip);
+ ND_TCHECK_SIZE((const struct tcphdr *)&((const int32_t *)ip)[hlen]);
+ hlen += TH_OFF((const struct tcphdr *)&((const int32_t *)ip)[hlen]);
+ lastlen[dir][lastconn] = length - (hlen << 2);
+ ND_PRINT(" %u (%ld)", lastlen[dir][lastconn], (long)(cp - chdr));
+}
diff --git a/print-sll.c b/print-sll.c
new file mode 100644
index 0000000..19d2973
--- /dev/null
+++ b/print-sll.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Linux cooked sockets capture printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "ethertype.h"
+#include "extract.h"
+
+/*
+ * For captures on Linux cooked sockets, we construct a fake header
+ * that includes:
+ *
+ * a 2-byte "packet type" which is one of:
+ *
+ * LINUX_SLL_HOST packet was sent to us
+ * LINUX_SLL_BROADCAST packet was broadcast
+ * LINUX_SLL_MULTICAST packet was multicast
+ * LINUX_SLL_OTHERHOST packet was sent to somebody else
+ * LINUX_SLL_OUTGOING packet was sent *by* us;
+ *
+ * a 2-byte Ethernet protocol field;
+ *
+ * a 2-byte link-layer type;
+ *
+ * a 2-byte link-layer address length;
+ *
+ * an 8-byte source link-layer address, whose actual length is
+ * specified by the previous value.
+ *
+ * All fields except for the link-layer address are in network byte order.
+ *
+ * DO NOT change the layout of this structure, or change any of the
+ * LINUX_SLL_ values below. If you must change the link-layer header
+ * for a "cooked" Linux capture, introduce a new DLT_ type (ask
+ * "tcpdump-workers@lists.tcpdump.org" for one, so that you don't give it
+ * a value that collides with a value already being used), and use the
+ * new header in captures of that type, so that programs that can
+ * handle DLT_LINUX_SLL captures will continue to handle them correctly
+ * without any change, and so that capture files with different headers
+ * can be told apart and programs that read them can dissect the
+ * packets in them.
+ *
+ * This structure, and the #defines below, must be the same in the
+ * libpcap and tcpdump versions of "sll.h".
+ */
+
+/*
+ * A DLT_LINUX_SLL fake link-layer header.
+ */
+#define SLL_HDR_LEN 16 /* total header length */
+#define SLL_ADDRLEN 8 /* length of address field */
+
+struct sll_header {
+ nd_uint16_t sll_pkttype; /* packet type */
+ nd_uint16_t sll_hatype; /* link-layer address type */
+ nd_uint16_t sll_halen; /* link-layer address length */
+ nd_byte sll_addr[SLL_ADDRLEN]; /* link-layer address */
+ nd_uint16_t sll_protocol; /* protocol */
+};
+
+/*
+ * A DLT_LINUX_SLL2 fake link-layer header.
+ */
+#define SLL2_HDR_LEN 20 /* total header length */
+
+struct sll2_header {
+ nd_uint16_t sll2_protocol; /* protocol */
+ nd_uint16_t sll2_reserved_mbz; /* reserved - must be zero */
+ nd_uint32_t sll2_if_index; /* 1-based interface index */
+ nd_uint16_t sll2_hatype; /* link-layer address type */
+ nd_uint8_t sll2_pkttype; /* packet type */
+ nd_uint8_t sll2_halen; /* link-layer address length */
+ nd_byte sll2_addr[SLL_ADDRLEN]; /* link-layer address */
+};
+
+/*
+ * The LINUX_SLL_ values for "sll_pkttype"; these correspond to the
+ * PACKET_ values on Linux, but are defined here so that they're
+ * available even on systems other than Linux, and so that they
+ * don't change even if the PACKET_ values change.
+ */
+#define LINUX_SLL_HOST 0
+#define LINUX_SLL_BROADCAST 1
+#define LINUX_SLL_MULTICAST 2
+#define LINUX_SLL_OTHERHOST 3
+#define LINUX_SLL_OUTGOING 4
+
+/*
+ * The LINUX_SLL_ values for "sll_protocol"; these correspond to the
+ * ETH_P_ values on Linux, but are defined here so that they're
+ * available even on systems other than Linux. We assume, for now,
+ * that the ETH_P_ values won't change in Linux; if they do, then:
+ *
+ * if we don't translate them in "pcap-linux.c", capture files
+ * won't necessarily be readable if captured on a system that
+ * defines ETH_P_ values that don't match these values;
+ *
+ * if we do translate them in "pcap-linux.c", that makes life
+ * unpleasant for the BPF code generator, as the values you test
+ * for in the kernel aren't the values that you test for when
+ * reading a capture file, so the fixup code run on BPF programs
+ * handed to the kernel ends up having to do more work.
+ *
+ * Add other values here as necessary, for handling packet types that
+ * might show up on non-Ethernet, non-802.x networks. (Not all the ones
+ * in the Linux "if_ether.h" will, I suspect, actually show up in
+ * captures.)
+ */
+#define LINUX_SLL_P_802_3 0x0001 /* Novell 802.3 frames without 802.2 LLC header */
+#define LINUX_SLL_P_802_2 0x0004 /* 802.2 frames (not D/I/X Ethernet) */
+
+static const struct tok sll_pkttype_values[] = {
+ { LINUX_SLL_HOST, "In" },
+ { LINUX_SLL_BROADCAST, "B" },
+ { LINUX_SLL_MULTICAST, "M" },
+ { LINUX_SLL_OTHERHOST, "P" },
+ { LINUX_SLL_OUTGOING, "Out" },
+ { 0, NULL}
+};
+
+static void
+sll_print(netdissect_options *ndo, const struct sll_header *sllp, u_int length)
+{
+ u_short ether_type;
+
+ ndo->ndo_protocol = "sll";
+ ND_PRINT("%3s ",
+ tok2str(sll_pkttype_values,"?",GET_BE_U_2(sllp->sll_pkttype)));
+
+ /*
+ * XXX - check the link-layer address type value?
+ * For now, we just assume 6 means Ethernet.
+ * XXX - print others as strings of hex?
+ */
+ if (GET_BE_U_2(sllp->sll_halen) == MAC_ADDR_LEN)
+ ND_PRINT("%s ", GET_ETHERADDR_STRING(sllp->sll_addr));
+
+ if (!ndo->ndo_qflag) {
+ ether_type = GET_BE_U_2(sllp->sll_protocol);
+
+ if (ether_type <= MAX_ETHERNET_LENGTH_VAL) {
+ /*
+ * Not an Ethernet type; what type is it?
+ */
+ switch (ether_type) {
+
+ case LINUX_SLL_P_802_3:
+ /*
+ * Ethernet_802.3 IPX frame.
+ */
+ ND_PRINT("802.3");
+ break;
+
+ case LINUX_SLL_P_802_2:
+ /*
+ * 802.2.
+ */
+ ND_PRINT("802.2");
+ break;
+
+ default:
+ /*
+ * What is it?
+ */
+ ND_PRINT("ethertype Unknown (0x%04x)",
+ ether_type);
+ break;
+ }
+ } else {
+ ND_PRINT("ethertype %s (0x%04x)",
+ tok2str(ethertype_values, "Unknown", ether_type),
+ ether_type);
+ }
+ ND_PRINT(", length %u: ", length);
+ }
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points to the
+ * Linux "cooked capture" header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+sll_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ const struct sll_header *sllp;
+ u_short hatype;
+ u_short ether_type;
+ int llc_hdrlen;
+ u_int hdrlen;
+
+ ndo->ndo_protocol = "sll";
+ ND_TCHECK_LEN(p, SLL_HDR_LEN);
+
+ sllp = (const struct sll_header *)p;
+
+ if (ndo->ndo_eflag)
+ sll_print(ndo, sllp, length);
+
+ /*
+ * Go past the cooked-mode header.
+ */
+ length -= SLL_HDR_LEN;
+ caplen -= SLL_HDR_LEN;
+ p += SLL_HDR_LEN;
+ hdrlen = SLL_HDR_LEN;
+
+ hatype = GET_BE_U_2(sllp->sll_hatype);
+ switch (hatype) {
+
+ case 803:
+ /*
+ * This is an packet with a radiotap header;
+ * just dissect the payload as such.
+ */
+ ndo->ndo_ll_hdr_len += SLL_HDR_LEN;
+ ndo->ndo_ll_hdr_len += ieee802_11_radio_print(ndo, p, length, caplen);
+ return;
+ }
+ ether_type = GET_BE_U_2(sllp->sll_protocol);
+
+recurse:
+ /*
+ * Is it (gag) an 802.3 encapsulation, or some non-Ethernet
+ * packet type?
+ */
+ if (ether_type <= MAX_ETHERNET_LENGTH_VAL) {
+ /*
+ * Yes - what type is it?
+ */
+ switch (ether_type) {
+
+ case LINUX_SLL_P_802_3:
+ /*
+ * Ethernet_802.3 IPX frame.
+ */
+ ipx_print(ndo, p, length);
+ break;
+
+ case LINUX_SLL_P_802_2:
+ /*
+ * 802.2.
+ * Try to print the LLC-layer header & higher layers.
+ */
+ llc_hdrlen = llc_print(ndo, p, length, caplen, NULL, NULL);
+ if (llc_hdrlen < 0)
+ goto unknown; /* unknown LLC type */
+ hdrlen += llc_hdrlen;
+ break;
+
+ default:
+ /*FALLTHROUGH*/
+
+ unknown:
+ /* packet type not known, print raw packet */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ break;
+ }
+ } else if (ether_type == ETHERTYPE_8021Q) {
+ /*
+ * Print VLAN information, and then go back and process
+ * the enclosed type field.
+ */
+ if (caplen < 4) {
+ ndo->ndo_protocol = "vlan";
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += hdrlen + caplen;
+ return;
+ }
+ if (ndo->ndo_eflag) {
+ uint16_t tag = GET_BE_U_2(p);
+
+ ND_PRINT("%s, ", ieee8021q_tci_string(tag));
+ }
+
+ ether_type = GET_BE_U_2(p + 2);
+ if (ether_type <= MAX_ETHERNET_LENGTH_VAL)
+ ether_type = LINUX_SLL_P_802_2;
+ if (!ndo->ndo_qflag) {
+ ND_PRINT("ethertype %s, ",
+ tok2str(ethertype_values, "Unknown", ether_type));
+ }
+ p += 4;
+ length -= 4;
+ caplen -= 4;
+ hdrlen += 4;
+ goto recurse;
+ } else {
+ if (ethertype_print(ndo, ether_type, p, length, caplen, NULL, NULL) == 0) {
+ /* ether_type not known, print raw packet */
+ if (!ndo->ndo_eflag)
+ sll_print(ndo, sllp, length + SLL_HDR_LEN);
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+ }
+
+ ndo->ndo_ll_hdr_len += hdrlen;
+}
+
+static void
+sll2_print(netdissect_options *ndo, const struct sll2_header *sllp, u_int length)
+{
+ u_short ether_type;
+
+ ndo->ndo_protocol = "sll2";
+ ND_PRINT("ifindex %u ", GET_BE_U_4(sllp->sll2_if_index));
+
+ /*
+ * XXX - check the link-layer address type value?
+ * For now, we just assume 6 means Ethernet.
+ * XXX - print others as strings of hex?
+ */
+ if (GET_U_1(sllp->sll2_halen) == MAC_ADDR_LEN)
+ ND_PRINT("%s ", GET_ETHERADDR_STRING(sllp->sll2_addr));
+
+ if (!ndo->ndo_qflag) {
+ ether_type = GET_BE_U_2(sllp->sll2_protocol);
+
+ if (ether_type <= MAX_ETHERNET_LENGTH_VAL) {
+ /*
+ * Not an Ethernet type; what type is it?
+ */
+ switch (ether_type) {
+
+ case LINUX_SLL_P_802_3:
+ /*
+ * Ethernet_802.3 IPX frame.
+ */
+ ND_PRINT("802.3");
+ break;
+
+ case LINUX_SLL_P_802_2:
+ /*
+ * 802.2.
+ */
+ ND_PRINT("802.2");
+ break;
+
+ default:
+ /*
+ * What is it?
+ */
+ ND_PRINT("ethertype Unknown (0x%04x)",
+ ether_type);
+ break;
+ }
+ } else {
+ ND_PRINT("ethertype %s (0x%04x)",
+ tok2str(ethertype_values, "Unknown", ether_type),
+ ether_type);
+ }
+ ND_PRINT(", length %u: ", length);
+ }
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points to the
+ * Linux "cooked capture" header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+sll2_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ const struct sll2_header *sllp;
+ u_short hatype;
+ u_short ether_type;
+ int llc_hdrlen;
+ u_int hdrlen;
+#ifdef HAVE_NET_IF_H
+ uint32_t if_index;
+ char ifname[IF_NAMESIZE];
+#endif
+
+ ndo->ndo_protocol = "sll2";
+ ND_TCHECK_LEN(p, SLL2_HDR_LEN);
+
+ sllp = (const struct sll2_header *)p;
+#ifdef HAVE_NET_IF_H
+ if_index = GET_BE_U_4(sllp->sll2_if_index);
+ if (!if_indextoname(if_index, ifname))
+ strncpy(ifname, "?", 2);
+ ND_PRINT("%-5s ", ifname);
+#endif
+
+ ND_PRINT("%-3s ",
+ tok2str(sll_pkttype_values, "?", GET_U_1(sllp->sll2_pkttype)));
+
+ if (ndo->ndo_eflag)
+ sll2_print(ndo, sllp, length);
+
+ /*
+ * Go past the cooked-mode header.
+ */
+ length -= SLL2_HDR_LEN;
+ caplen -= SLL2_HDR_LEN;
+ p += SLL2_HDR_LEN;
+ hdrlen = SLL2_HDR_LEN;
+
+ hatype = GET_BE_U_2(sllp->sll2_hatype);
+ switch (hatype) {
+
+ case 803:
+ /*
+ * This is an packet with a radiotap header;
+ * just dissect the payload as such.
+ */
+ ndo->ndo_ll_hdr_len += SLL2_HDR_LEN;
+ ndo->ndo_ll_hdr_len += ieee802_11_radio_print(ndo, p, length, caplen);
+ return;
+ }
+ ether_type = GET_BE_U_2(sllp->sll2_protocol);
+
+recurse:
+ /*
+ * Is it (gag) an 802.3 encapsulation, or some non-Ethernet
+ * packet type?
+ */
+ if (ether_type <= MAX_ETHERNET_LENGTH_VAL) {
+ /*
+ * Yes - what type is it?
+ */
+ switch (ether_type) {
+
+ case LINUX_SLL_P_802_3:
+ /*
+ * Ethernet_802.3 IPX frame.
+ */
+ ipx_print(ndo, p, length);
+ break;
+
+ case LINUX_SLL_P_802_2:
+ /*
+ * 802.2.
+ * Try to print the LLC-layer header & higher layers.
+ */
+ llc_hdrlen = llc_print(ndo, p, length, caplen, NULL, NULL);
+ if (llc_hdrlen < 0)
+ goto unknown; /* unknown LLC type */
+ hdrlen += llc_hdrlen;
+ break;
+
+ default:
+ /*FALLTHROUGH*/
+
+ unknown:
+ /* packet type not known, print raw packet */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ break;
+ }
+ } else if (ether_type == ETHERTYPE_8021Q) {
+ /*
+ * Print VLAN information, and then go back and process
+ * the enclosed type field.
+ */
+ if (caplen < 4) {
+ ndo->ndo_protocol = "vlan";
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += hdrlen + caplen;
+ return;
+ }
+ if (ndo->ndo_eflag) {
+ uint16_t tag = GET_BE_U_2(p);
+
+ ND_PRINT("%s, ", ieee8021q_tci_string(tag));
+ }
+
+ ether_type = GET_BE_U_2(p + 2);
+ if (ether_type <= MAX_ETHERNET_LENGTH_VAL)
+ ether_type = LINUX_SLL_P_802_2;
+ if (!ndo->ndo_qflag) {
+ ND_PRINT("ethertype %s, ",
+ tok2str(ethertype_values, "Unknown", ether_type));
+ }
+ p += 4;
+ length -= 4;
+ caplen -= 4;
+ hdrlen += 4;
+ goto recurse;
+ } else {
+ if (ethertype_print(ndo, ether_type, p, length, caplen, NULL, NULL) == 0) {
+ /* ether_type not known, print raw packet */
+ if (!ndo->ndo_eflag)
+ sll2_print(ndo, sllp, length + SLL2_HDR_LEN);
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+ }
+
+ ndo->ndo_ll_hdr_len += hdrlen;
+}
diff --git a/print-slow.c b/print-slow.c
new file mode 100644
index 0000000..1183818
--- /dev/null
+++ b/print-slow.c
@@ -0,0 +1,737 @@
+/*
+ * Copyright (c) 1998-2006 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * support for the IEEE "slow protocols" LACP, MARKER as per 802.3ad
+ * OAM as per 802.3ah
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* \summary: IEEE "slow protocols" (802.3ad/802.3ah) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+#include "oui.h"
+
+
+#define SLOW_PROTO_LACP 1
+#define SLOW_PROTO_MARKER 2
+#define SLOW_PROTO_OAM 3
+
+#define LACP_VERSION 1
+#define MARKER_VERSION 1
+
+static const struct tok slow_proto_values[] = {
+ { SLOW_PROTO_LACP, "LACP" },
+ { SLOW_PROTO_MARKER, "MARKER" },
+ { SLOW_PROTO_OAM, "OAM" },
+ { 0, NULL}
+};
+
+static const struct tok slow_oam_flag_values[] = {
+ { 0x0001, "Link Fault" },
+ { 0x0002, "Dying Gasp" },
+ { 0x0004, "Critical Event" },
+ { 0x0008, "Local Evaluating" },
+ { 0x0010, "Local Stable" },
+ { 0x0020, "Remote Evaluating" },
+ { 0x0040, "Remote Stable" },
+ { 0, NULL}
+};
+
+#define SLOW_OAM_CODE_INFO 0x00
+#define SLOW_OAM_CODE_EVENT_NOTIF 0x01
+#define SLOW_OAM_CODE_VAR_REQUEST 0x02
+#define SLOW_OAM_CODE_VAR_RESPONSE 0x03
+#define SLOW_OAM_CODE_LOOPBACK_CTRL 0x04
+#define SLOW_OAM_CODE_PRIVATE 0xfe
+
+static const struct tok slow_oam_code_values[] = {
+ { SLOW_OAM_CODE_INFO, "Information" },
+ { SLOW_OAM_CODE_EVENT_NOTIF, "Event Notification" },
+ { SLOW_OAM_CODE_VAR_REQUEST, "Variable Request" },
+ { SLOW_OAM_CODE_VAR_RESPONSE, "Variable Response" },
+ { SLOW_OAM_CODE_LOOPBACK_CTRL, "Loopback Control" },
+ { SLOW_OAM_CODE_PRIVATE, "Vendor Private" },
+ { 0, NULL}
+};
+
+struct slow_oam_info_t {
+ nd_uint8_t info_type;
+ nd_uint8_t info_length;
+ nd_uint8_t oam_version;
+ nd_uint16_t revision;
+ nd_uint8_t state;
+ nd_uint8_t oam_config;
+ nd_uint16_t oam_pdu_config;
+ nd_uint24_t oui;
+ nd_uint32_t vendor_private;
+};
+
+#define SLOW_OAM_INFO_TYPE_END_OF_TLV 0x00
+#define SLOW_OAM_INFO_TYPE_LOCAL 0x01
+#define SLOW_OAM_INFO_TYPE_REMOTE 0x02
+#define SLOW_OAM_INFO_TYPE_ORG_SPECIFIC 0xfe
+
+static const struct tok slow_oam_info_type_values[] = {
+ { SLOW_OAM_INFO_TYPE_END_OF_TLV, "End of TLV marker" },
+ { SLOW_OAM_INFO_TYPE_LOCAL, "Local" },
+ { SLOW_OAM_INFO_TYPE_REMOTE, "Remote" },
+ { SLOW_OAM_INFO_TYPE_ORG_SPECIFIC, "Organization specific" },
+ { 0, NULL}
+};
+
+#define OAM_INFO_TYPE_PARSER_MASK 0x3
+static const struct tok slow_oam_info_type_state_parser_values[] = {
+ { 0x00, "forwarding" },
+ { 0x01, "looping back" },
+ { 0x02, "discarding" },
+ { 0x03, "reserved" },
+ { 0, NULL}
+};
+
+#define OAM_INFO_TYPE_MUX_MASK 0x4
+static const struct tok slow_oam_info_type_state_mux_values[] = {
+ { 0x00, "forwarding" },
+ { 0x04, "discarding" },
+ { 0, NULL}
+};
+
+static const struct tok slow_oam_info_type_oam_config_values[] = {
+ { 0x01, "Active" },
+ { 0x02, "Unidirectional" },
+ { 0x04, "Remote-Loopback" },
+ { 0x08, "Link-Events" },
+ { 0x10, "Variable-Retrieval" },
+ { 0, NULL}
+};
+
+/* 11 Bits */
+#define OAM_INFO_TYPE_PDU_SIZE_MASK 0x7ff
+
+#define SLOW_OAM_LINK_EVENT_END_OF_TLV 0x00
+#define SLOW_OAM_LINK_EVENT_ERR_SYM_PER 0x01
+#define SLOW_OAM_LINK_EVENT_ERR_FRM 0x02
+#define SLOW_OAM_LINK_EVENT_ERR_FRM_PER 0x03
+#define SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM 0x04
+#define SLOW_OAM_LINK_EVENT_ORG_SPECIFIC 0xfe
+
+static const struct tok slow_oam_link_event_values[] = {
+ { SLOW_OAM_LINK_EVENT_END_OF_TLV, "End of TLV marker" },
+ { SLOW_OAM_LINK_EVENT_ERR_SYM_PER, "Errored Symbol Period Event" },
+ { SLOW_OAM_LINK_EVENT_ERR_FRM, "Errored Frame Event" },
+ { SLOW_OAM_LINK_EVENT_ERR_FRM_PER, "Errored Frame Period Event" },
+ { SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM, "Errored Frame Seconds Summary Event" },
+ { SLOW_OAM_LINK_EVENT_ORG_SPECIFIC, "Organization specific" },
+ { 0, NULL}
+};
+
+struct slow_oam_link_event_t {
+ nd_uint8_t event_type;
+ nd_uint8_t event_length;
+ nd_uint16_t time_stamp;
+ nd_uint64_t window;
+ nd_uint64_t threshold;
+ nd_uint64_t errors;
+ nd_uint64_t errors_running_total;
+ nd_uint32_t event_running_total;
+};
+
+struct slow_oam_variablerequest_t {
+ nd_uint8_t branch;
+ nd_uint16_t leaf;
+};
+
+struct slow_oam_variableresponse_t {
+ nd_uint8_t branch;
+ nd_uint16_t leaf;
+ nd_uint8_t length;
+};
+
+struct slow_oam_loopbackctrl_t {
+ nd_uint8_t command;
+};
+
+static const struct tok slow_oam_loopbackctrl_cmd_values[] = {
+ { 0x01, "Enable OAM Remote Loopback" },
+ { 0x02, "Disable OAM Remote Loopback" },
+ { 0, NULL}
+};
+
+struct tlv_header_t {
+ nd_uint8_t type;
+ nd_uint8_t length;
+};
+
+#define LACP_MARKER_TLV_TERMINATOR 0x00 /* same code for LACP and Marker */
+
+#define LACP_TLV_ACTOR_INFO 0x01
+#define LACP_TLV_PARTNER_INFO 0x02
+#define LACP_TLV_COLLECTOR_INFO 0x03
+
+#define MARKER_TLV_MARKER_INFO 0x01
+
+static const struct tok slow_tlv_values[] = {
+ { (SLOW_PROTO_LACP << 8) + LACP_MARKER_TLV_TERMINATOR, "Terminator"},
+ { (SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO, "Actor Information"},
+ { (SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO, "Partner Information"},
+ { (SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO, "Collector Information"},
+
+ { (SLOW_PROTO_MARKER << 8) + LACP_MARKER_TLV_TERMINATOR, "Terminator"},
+ { (SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO, "Marker Information"},
+ { 0, NULL}
+};
+
+struct lacp_tlv_actor_partner_info_t {
+ nd_uint16_t sys_pri;
+ nd_mac_addr sys;
+ nd_uint16_t key;
+ nd_uint16_t port_pri;
+ nd_uint16_t port;
+ nd_uint8_t state;
+ nd_byte pad[3];
+};
+
+static const struct tok lacp_tlv_actor_partner_info_state_values[] = {
+ { 0x01, "Activity"},
+ { 0x02, "Timeout"},
+ { 0x04, "Aggregation"},
+ { 0x08, "Synchronization"},
+ { 0x10, "Collecting"},
+ { 0x20, "Distributing"},
+ { 0x40, "Default"},
+ { 0x80, "Expired"},
+ { 0, NULL}
+};
+
+struct lacp_tlv_collector_info_t {
+ nd_uint16_t max_delay;
+ nd_byte pad[12];
+};
+
+struct marker_tlv_marker_info_t {
+ nd_uint16_t req_port;
+ nd_mac_addr req_sys;
+ nd_uint32_t req_trans_id;
+ nd_byte pad[2];
+};
+
+struct lacp_marker_tlv_terminator_t {
+ nd_byte pad[50];
+};
+
+static void slow_marker_lacp_print(netdissect_options *, const u_char *, u_int, u_int);
+static void slow_oam_print(netdissect_options *, const u_char *, u_int);
+
+void
+slow_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ int print_version;
+ u_int subtype;
+
+ ndo->ndo_protocol = "slow";
+ if (len < 1)
+ goto tooshort;
+ subtype = GET_U_1(pptr);
+
+ /*
+ * Sanity checking of the header.
+ */
+ switch (subtype) {
+ case SLOW_PROTO_LACP:
+ if (len < 2)
+ goto tooshort;
+ if (GET_U_1(pptr + 1) != LACP_VERSION) {
+ ND_PRINT("LACP version %u packet not supported",
+ GET_U_1(pptr + 1));
+ return;
+ }
+ print_version = 1;
+ break;
+
+ case SLOW_PROTO_MARKER:
+ if (len < 2)
+ goto tooshort;
+ if (GET_U_1(pptr + 1) != MARKER_VERSION) {
+ ND_PRINT("MARKER version %u packet not supported",
+ GET_U_1(pptr + 1));
+ return;
+ }
+ print_version = 1;
+ break;
+
+ case SLOW_PROTO_OAM: /* fall through */
+ print_version = 0;
+ break;
+
+ default:
+ /* print basic information and exit */
+ print_version = -1;
+ break;
+ }
+
+ if (print_version == 1) {
+ ND_PRINT("%sv%u, length %u",
+ tok2str(slow_proto_values, "unknown (%u)", subtype),
+ GET_U_1((pptr + 1)),
+ len);
+ } else {
+ /* some slow protos don't have a version number in the header */
+ ND_PRINT("%s, length %u",
+ tok2str(slow_proto_values, "unknown (%u)", subtype),
+ len);
+ }
+
+ /* unrecognized subtype */
+ if (print_version == -1) {
+ print_unknown_data(ndo, pptr, "\n\t", len);
+ return;
+ }
+
+ if (!ndo->ndo_vflag)
+ return;
+
+ switch (subtype) {
+ default: /* should not happen */
+ break;
+
+ case SLOW_PROTO_OAM:
+ /* skip subtype */
+ len -= 1;
+ pptr += 1;
+ slow_oam_print(ndo, pptr, len);
+ break;
+
+ case SLOW_PROTO_LACP: /* LACP and MARKER share the same semantics */
+ case SLOW_PROTO_MARKER:
+ /* skip subtype and version */
+ len -= 2;
+ pptr += 2;
+ slow_marker_lacp_print(ndo, pptr, len, subtype);
+ break;
+ }
+ return;
+
+tooshort:
+ if (!ndo->ndo_vflag)
+ ND_PRINT(" (packet is too short)");
+ else
+ ND_PRINT("\n\t\t packet is too short");
+}
+
+static void
+slow_marker_lacp_print(netdissect_options *ndo,
+ const u_char *tptr, u_int tlen,
+ u_int proto_subtype)
+{
+ const struct tlv_header_t *tlv_header;
+ const u_char *tlv_tptr;
+ u_int tlv_type, tlv_len, tlv_tlen;
+
+ union {
+ const struct lacp_marker_tlv_terminator_t *lacp_marker_tlv_terminator;
+ const struct lacp_tlv_actor_partner_info_t *lacp_tlv_actor_partner_info;
+ const struct lacp_tlv_collector_info_t *lacp_tlv_collector_info;
+ const struct marker_tlv_marker_info_t *marker_tlv_marker_info;
+ } tlv_ptr;
+
+ while(tlen>0) {
+ /* is the packet big enough to include the tlv header ? */
+ if (tlen < sizeof(struct tlv_header_t))
+ goto tooshort;
+ /* did we capture enough for fully decoding the tlv header ? */
+ tlv_header = (const struct tlv_header_t *)tptr;
+ tlv_type = GET_U_1(tlv_header->type);
+ tlv_len = GET_U_1(tlv_header->length);
+
+ ND_PRINT("\n\t%s TLV (0x%02x), length %u",
+ tok2str(slow_tlv_values,
+ "Unknown",
+ (proto_subtype << 8) + tlv_type),
+ tlv_type,
+ tlv_len);
+
+ if (tlv_type == LACP_MARKER_TLV_TERMINATOR) {
+ /*
+ * This TLV has a length of zero, and means there are no
+ * more TLVs to process.
+ */
+ return;
+ }
+
+ /* length includes the type and length fields */
+ if (tlv_len < sizeof(struct tlv_header_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be >= %zu",
+ sizeof(struct tlv_header_t));
+ return;
+ }
+
+ /* is the packet big enough to include the tlv ? */
+ if (tlen < tlv_len)
+ goto tooshort;
+ /* did we capture enough for fully decoding the tlv ? */
+ ND_TCHECK_LEN(tptr, tlv_len);
+
+ tlv_tptr=tptr+sizeof(struct tlv_header_t);
+ tlv_tlen=tlv_len-sizeof(struct tlv_header_t);
+
+ switch((proto_subtype << 8) + tlv_type) {
+
+ /* those two TLVs have the same structure -> fall through */
+ case ((SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO):
+ case ((SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO):
+ if (tlv_tlen !=
+ sizeof(struct lacp_tlv_actor_partner_info_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be %zu",
+ sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_actor_partner_info_t));
+ goto badlength;
+ }
+
+ tlv_ptr.lacp_tlv_actor_partner_info = (const struct lacp_tlv_actor_partner_info_t *)tlv_tptr;
+
+ ND_PRINT("\n\t System %s, System Priority %u, Key %u"
+ ", Port %u, Port Priority %u\n\t State Flags [%s]",
+ GET_ETHERADDR_STRING(tlv_ptr.lacp_tlv_actor_partner_info->sys),
+ GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->sys_pri),
+ GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->key),
+ GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port),
+ GET_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port_pri),
+ bittok2str(lacp_tlv_actor_partner_info_state_values,
+ "none",
+ GET_U_1(tlv_ptr.lacp_tlv_actor_partner_info->state)));
+
+ break;
+
+ case ((SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO):
+ if (tlv_tlen !=
+ sizeof(struct lacp_tlv_collector_info_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be %zu",
+ sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_collector_info_t));
+ goto badlength;
+ }
+
+ tlv_ptr.lacp_tlv_collector_info = (const struct lacp_tlv_collector_info_t *)tlv_tptr;
+
+ ND_PRINT("\n\t Max Delay %u",
+ GET_BE_U_2(tlv_ptr.lacp_tlv_collector_info->max_delay));
+
+ break;
+
+ case ((SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO):
+ if (tlv_tlen !=
+ sizeof(struct marker_tlv_marker_info_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be %zu",
+ sizeof(struct tlv_header_t) + sizeof(struct marker_tlv_marker_info_t));
+ goto badlength;
+ }
+
+ tlv_ptr.marker_tlv_marker_info = (const struct marker_tlv_marker_info_t *)tlv_tptr;
+
+ ND_PRINT("\n\t Request System %s, Request Port %u, Request Transaction ID 0x%08x",
+ GET_ETHERADDR_STRING(tlv_ptr.marker_tlv_marker_info->req_sys),
+ GET_BE_U_2(tlv_ptr.marker_tlv_marker_info->req_port),
+ GET_BE_U_4(tlv_ptr.marker_tlv_marker_info->req_trans_id));
+
+ break;
+
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo, tlv_tptr, "\n\t ", tlv_tlen);
+ break;
+ }
+
+ badlength:
+ /* do we want to see an additional hexdump ? */
+ if (ndo->ndo_vflag > 1) {
+ print_unknown_data(ndo, tptr+sizeof(struct tlv_header_t), "\n\t ",
+ tlv_len-sizeof(struct tlv_header_t));
+ }
+
+ tptr+=tlv_len;
+ tlen-=tlv_len;
+ }
+ return;
+
+tooshort:
+ ND_PRINT("\n\t\t packet is too short");
+}
+
+static void
+slow_oam_print(netdissect_options *ndo,
+ const u_char *tptr, u_int tlen)
+{
+ uint8_t code;
+ uint8_t type, length;
+ uint8_t state;
+ uint8_t command;
+ u_int hexdump;
+
+ struct slow_oam_common_header_t {
+ nd_uint16_t flags;
+ nd_uint8_t code;
+ };
+
+ struct slow_oam_tlv_header_t {
+ nd_uint8_t type;
+ nd_uint8_t length;
+ };
+
+ union {
+ const struct slow_oam_common_header_t *slow_oam_common_header;
+ const struct slow_oam_tlv_header_t *slow_oam_tlv_header;
+ } ptr;
+
+ union {
+ const struct slow_oam_info_t *slow_oam_info;
+ const struct slow_oam_link_event_t *slow_oam_link_event;
+ const struct slow_oam_variablerequest_t *slow_oam_variablerequest;
+ const struct slow_oam_variableresponse_t *slow_oam_variableresponse;
+ const struct slow_oam_loopbackctrl_t *slow_oam_loopbackctrl;
+ } tlv;
+
+ ptr.slow_oam_common_header = (const struct slow_oam_common_header_t *)tptr;
+ if (tlen < sizeof(*ptr.slow_oam_common_header))
+ goto tooshort;
+ ND_TCHECK_SIZE(ptr.slow_oam_common_header);
+ tptr += sizeof(struct slow_oam_common_header_t);
+ tlen -= sizeof(struct slow_oam_common_header_t);
+
+ code = GET_U_1(ptr.slow_oam_common_header->code);
+ ND_PRINT("\n\tCode %s OAM PDU, Flags [%s]",
+ tok2str(slow_oam_code_values, "Unknown (%u)", code),
+ bittok2str(slow_oam_flag_values,
+ "none",
+ GET_BE_U_2(ptr.slow_oam_common_header->flags)));
+
+ switch (code) {
+ case SLOW_OAM_CODE_INFO:
+ while (tlen > 0) {
+ ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
+ if (tlen < sizeof(*ptr.slow_oam_tlv_header))
+ goto tooshort;
+ ND_TCHECK_SIZE(ptr.slow_oam_tlv_header);
+ type = GET_U_1(ptr.slow_oam_tlv_header->type);
+ length = GET_U_1(ptr.slow_oam_tlv_header->length);
+ ND_PRINT("\n\t %s Information Type (%u), length %u",
+ tok2str(slow_oam_info_type_values, "Reserved", type),
+ type,
+ length);
+
+ if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
+ /*
+ * As IEEE Std 802.3-2015 says for the End of TLV Marker,
+ * "(the length and value of the Type 0x00 TLV can be ignored)".
+ */
+ return;
+ }
+
+ /* length includes the type and length fields */
+ if (length < sizeof(struct slow_oam_tlv_header_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be >= %zu",
+ sizeof(struct slow_oam_tlv_header_t));
+ return;
+ }
+
+ if (tlen < length)
+ goto tooshort;
+ ND_TCHECK_LEN(tptr, length);
+
+ hexdump = FALSE;
+ switch (type) {
+ case SLOW_OAM_INFO_TYPE_LOCAL: /* identical format - fall through */
+ case SLOW_OAM_INFO_TYPE_REMOTE:
+ tlv.slow_oam_info = (const struct slow_oam_info_t *)tptr;
+
+ if (GET_U_1(tlv.slow_oam_info->info_length) !=
+ sizeof(struct slow_oam_info_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be %zu",
+ sizeof(struct slow_oam_info_t));
+ hexdump = TRUE;
+ goto badlength_code_info;
+ }
+
+ ND_PRINT("\n\t OAM-Version %u, Revision %u",
+ GET_U_1(tlv.slow_oam_info->oam_version),
+ GET_BE_U_2(tlv.slow_oam_info->revision));
+
+ state = GET_U_1(tlv.slow_oam_info->state);
+ ND_PRINT("\n\t State-Parser-Action %s, State-MUX-Action %s",
+ tok2str(slow_oam_info_type_state_parser_values, "Reserved",
+ state & OAM_INFO_TYPE_PARSER_MASK),
+ tok2str(slow_oam_info_type_state_mux_values, "Reserved",
+ state & OAM_INFO_TYPE_MUX_MASK));
+ ND_PRINT("\n\t OAM-Config Flags [%s], OAM-PDU-Config max-PDU size %u",
+ bittok2str(slow_oam_info_type_oam_config_values, "none",
+ GET_U_1(tlv.slow_oam_info->oam_config)),
+ GET_BE_U_2(tlv.slow_oam_info->oam_pdu_config) &
+ OAM_INFO_TYPE_PDU_SIZE_MASK);
+ ND_PRINT("\n\t OUI %s (0x%06x), Vendor-Private 0x%08x",
+ tok2str(oui_values, "Unknown",
+ GET_BE_U_3(tlv.slow_oam_info->oui)),
+ GET_BE_U_3(tlv.slow_oam_info->oui),
+ GET_BE_U_4(tlv.slow_oam_info->vendor_private));
+ break;
+
+ case SLOW_OAM_INFO_TYPE_ORG_SPECIFIC:
+ hexdump = TRUE;
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ badlength_code_info:
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag > 1 || hexdump==TRUE) {
+ print_unknown_data(ndo, tptr, "\n\t ",
+ length);
+ }
+
+ tlen -= length;
+ tptr += length;
+ }
+ break;
+
+ case SLOW_OAM_CODE_EVENT_NOTIF:
+ /* Sequence number */
+ if (tlen < 2)
+ goto tooshort;
+ ND_PRINT("\n\t Sequence Number %u", GET_BE_U_2(tptr));
+ tlen -= 2;
+ tptr += 2;
+
+ /* TLVs */
+ while (tlen > 0) {
+ ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
+ if (tlen < sizeof(*ptr.slow_oam_tlv_header))
+ goto tooshort;
+ type = GET_U_1(ptr.slow_oam_tlv_header->type);
+ length = GET_U_1(ptr.slow_oam_tlv_header->length);
+ ND_PRINT("\n\t %s Link Event Type (%u), length %u",
+ tok2str(slow_oam_link_event_values, "Reserved",
+ type),
+ type,
+ length);
+
+ if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
+ /*
+ * As IEEE Std 802.3-2015 says for the End of TLV Marker,
+ * "(the length and value of the Type 0x00 TLV can be ignored)".
+ */
+ return;
+ }
+
+ /* length includes the type and length fields */
+ if (length < sizeof(struct slow_oam_tlv_header_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be >= %zu",
+ sizeof(struct slow_oam_tlv_header_t));
+ return;
+ }
+
+ if (tlen < length)
+ goto tooshort;
+ ND_TCHECK_LEN(tptr, length);
+
+ hexdump = FALSE;
+ switch (type) {
+ case SLOW_OAM_LINK_EVENT_ERR_SYM_PER: /* identical format - fall through */
+ case SLOW_OAM_LINK_EVENT_ERR_FRM:
+ case SLOW_OAM_LINK_EVENT_ERR_FRM_PER:
+ case SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM:
+ tlv.slow_oam_link_event = (const struct slow_oam_link_event_t *)tptr;
+
+ if (GET_U_1(tlv.slow_oam_link_event->event_length) !=
+ sizeof(struct slow_oam_link_event_t)) {
+ ND_PRINT("\n\t ERROR: illegal length - should be %zu",
+ sizeof(struct slow_oam_link_event_t));
+ hexdump = TRUE;
+ goto badlength_event_notif;
+ }
+
+ ND_PRINT("\n\t Timestamp %u ms, Errored Window %" PRIu64
+ "\n\t Errored Threshold %" PRIu64
+ "\n\t Errors %" PRIu64
+ "\n\t Error Running Total %" PRIu64
+ "\n\t Event Running Total %u",
+ GET_BE_U_2(tlv.slow_oam_link_event->time_stamp)*100,
+ GET_BE_U_8(tlv.slow_oam_link_event->window),
+ GET_BE_U_8(tlv.slow_oam_link_event->threshold),
+ GET_BE_U_8(tlv.slow_oam_link_event->errors),
+ GET_BE_U_8(tlv.slow_oam_link_event->errors_running_total),
+ GET_BE_U_4(tlv.slow_oam_link_event->event_running_total));
+ break;
+
+ case SLOW_OAM_LINK_EVENT_ORG_SPECIFIC:
+ hexdump = TRUE;
+ break;
+
+ default:
+ hexdump = TRUE;
+ break;
+ }
+
+ badlength_event_notif:
+ /* do we also want to see a hex dump ? */
+ if (ndo->ndo_vflag > 1 || hexdump==TRUE) {
+ print_unknown_data(ndo, tptr, "\n\t ",
+ length);
+ }
+
+ tlen -= length;
+ tptr += length;
+ }
+ break;
+
+ case SLOW_OAM_CODE_LOOPBACK_CTRL:
+ tlv.slow_oam_loopbackctrl = (const struct slow_oam_loopbackctrl_t *)tptr;
+ if (tlen < sizeof(*tlv.slow_oam_loopbackctrl))
+ goto tooshort;
+ command = GET_U_1(tlv.slow_oam_loopbackctrl->command);
+ ND_PRINT("\n\t Command %s (%u)",
+ tok2str(slow_oam_loopbackctrl_cmd_values,
+ "Unknown",
+ command),
+ command);
+ tptr ++;
+ tlen --;
+ break;
+
+ /*
+ * FIXME those are the defined codes that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+ case SLOW_OAM_CODE_VAR_REQUEST:
+ case SLOW_OAM_CODE_VAR_RESPONSE:
+ case SLOW_OAM_CODE_PRIVATE:
+ default:
+ if (ndo->ndo_vflag <= 1) {
+ print_unknown_data(ndo, tptr, "\n\t ", tlen);
+ }
+ break;
+ }
+ return;
+
+tooshort:
+ ND_PRINT("\n\t\t packet is too short");
+}
diff --git a/print-smb.c b/print-smb.c
new file mode 100644
index 0000000..bcd7363
--- /dev/null
+++ b/print-smb.c
@@ -0,0 +1,1476 @@
+/*
+ * Copyright (C) Andrew Tridgell 1995-1999
+ *
+ * This software may be distributed either under the terms of the
+ * BSD-style license that accompanies tcpdump or the GNU GPL version 2
+ * or later
+ */
+
+/* \summary: SMB/CIFS printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "extract.h"
+#include "smb.h"
+
+
+static int request = 0;
+static int unicodestr = 0;
+
+extern const u_char *startbuf;
+
+const u_char *startbuf = NULL;
+
+struct smbdescript {
+ const char *req_f1;
+ const char *req_f2;
+ const char *rep_f1;
+ const char *rep_f2;
+ void (*fn)(netdissect_options *, const u_char *, const u_char *, const u_char *, const u_char *);
+};
+
+struct smbdescriptint {
+ const char *req_f1;
+ const char *req_f2;
+ const char *rep_f1;
+ const char *rep_f2;
+ void (*fn)(netdissect_options *, const u_char *, const u_char *, u_int, u_int);
+};
+
+struct smbfns
+{
+ int id;
+ const char *name;
+ int flags;
+ struct smbdescript descript;
+};
+
+struct smbfnsint
+{
+ int id;
+ const char *name;
+ int flags;
+ struct smbdescriptint descript;
+};
+
+#define DEFDESCRIPT { NULL, NULL, NULL, NULL, NULL }
+
+#define FLG_CHAIN (1 << 0)
+
+static const struct smbfns *
+smbfind(int id, const struct smbfns *list)
+{
+ int sindex;
+
+ for (sindex = 0; list[sindex].name; sindex++)
+ if (list[sindex].id == id)
+ return(&list[sindex]);
+
+ return(&list[0]);
+}
+
+static const struct smbfnsint *
+smbfindint(int id, const struct smbfnsint *list)
+{
+ int sindex;
+
+ for (sindex = 0; list[sindex].name; sindex++)
+ if (list[sindex].id == id)
+ return(&list[sindex]);
+
+ return(&list[0]);
+}
+
+static void
+trans2_findfirst(netdissect_options *ndo,
+ const u_char *param, const u_char *data, u_int pcnt, u_int dcnt)
+{
+ const char *fmt;
+
+ if (request)
+ fmt = "Attribute=[A]\nSearchCount=[u]\nFlags=[w]\nLevel=[uP4]\nFile=[S]\n";
+ else
+ fmt = "Handle=[w]\nCount=[u]\nEOS=[w]\nEoffset=[u]\nLastNameOfs=[w]\n";
+
+ smb_fdata(ndo, param, fmt, param + pcnt, unicodestr);
+ if (dcnt) {
+ ND_PRINT("data:\n");
+ smb_data_print(ndo, data, dcnt);
+ }
+}
+
+static void
+trans2_qfsinfo(netdissect_options *ndo,
+ const u_char *param, const u_char *data, u_int pcnt, u_int dcnt)
+{
+ static u_int level = 0;
+ const char *fmt="";
+
+ if (request) {
+ level = GET_LE_U_2(param);
+ fmt = "InfoLevel=[u]\n";
+ smb_fdata(ndo, param, fmt, param + pcnt, unicodestr);
+ } else {
+ switch (level) {
+ case 1:
+ fmt = "idFileSystem=[W]\nSectorUnit=[U]\nUnit=[U]\nAvail=[U]\nSectorSize=[u]\n";
+ break;
+ case 2:
+ fmt = "CreationTime=[T2]VolNameLength=[lb]\nVolumeLabel=[c]\n";
+ break;
+ case 0x105:
+ fmt = "Capabilities=[W]\nMaxFileLen=[U]\nVolNameLen=[lU]\nVolume=[C]\n";
+ break;
+ default:
+ fmt = "UnknownLevel\n";
+ break;
+ }
+ smb_fdata(ndo, data, fmt, data + dcnt, unicodestr);
+ }
+ if (dcnt) {
+ ND_PRINT("data:\n");
+ smb_data_print(ndo, data, dcnt);
+ }
+}
+
+static const struct smbfnsint trans2_fns[] = {
+ { 0, "TRANSACT2_OPEN", 0,
+ { "Flags2=[w]\nMode=[w]\nSearchAttrib=[A]\nAttrib=[A]\nTime=[T2]\nOFun=[w]\nSize=[U]\nRes=([w, w, w, w, w])\nPath=[S]",
+ NULL,
+ "Handle=[u]\nAttrib=[A]\nTime=[T2]\nSize=[U]\nAccess=[w]\nType=[w]\nState=[w]\nAction=[w]\nInode=[W]\nOffErr=[u]\n|EALength=[u]\n",
+ NULL, NULL }},
+ { 1, "TRANSACT2_FINDFIRST", 0,
+ { NULL, NULL, NULL, NULL, trans2_findfirst }},
+ { 2, "TRANSACT2_FINDNEXT", 0, DEFDESCRIPT },
+ { 3, "TRANSACT2_QFSINFO", 0,
+ { NULL, NULL, NULL, NULL, trans2_qfsinfo }},
+ { 4, "TRANSACT2_SETFSINFO", 0, DEFDESCRIPT },
+ { 5, "TRANSACT2_QPATHINFO", 0, DEFDESCRIPT },
+ { 6, "TRANSACT2_SETPATHINFO", 0, DEFDESCRIPT },
+ { 7, "TRANSACT2_QFILEINFO", 0, DEFDESCRIPT },
+ { 8, "TRANSACT2_SETFILEINFO", 0, DEFDESCRIPT },
+ { 9, "TRANSACT2_FSCTL", 0, DEFDESCRIPT },
+ { 10, "TRANSACT2_IOCTL", 0, DEFDESCRIPT },
+ { 11, "TRANSACT2_FINDNOTIFYFIRST", 0, DEFDESCRIPT },
+ { 12, "TRANSACT2_FINDNOTIFYNEXT", 0, DEFDESCRIPT },
+ { 13, "TRANSACT2_MKDIR", 0, DEFDESCRIPT },
+ { -1, NULL, 0, DEFDESCRIPT }
+};
+
+
+static void
+print_trans2(netdissect_options *ndo,
+ const u_char *words, const u_char *dat, const u_char *buf, const u_char *maxbuf)
+{
+ u_int bcc;
+ static const struct smbfnsint *fn = &trans2_fns[0];
+ const u_char *data, *param;
+ const u_char *w = words + 1;
+ const char *f1 = NULL, *f2 = NULL;
+ u_int pcnt, dcnt;
+
+ ND_TCHECK_1(words);
+ if (request) {
+ ND_TCHECK_2(w + (14 * 2));
+ pcnt = GET_LE_U_2(w + 9 * 2);
+ param = buf + GET_LE_U_2(w + 10 * 2);
+ dcnt = GET_LE_U_2(w + 11 * 2);
+ data = buf + GET_LE_U_2(w + 12 * 2);
+ fn = smbfindint(GET_LE_U_2(w + 14 * 2), trans2_fns);
+ } else {
+ if (GET_U_1(words) == 0) {
+ ND_PRINT("%s\n", fn->name);
+ ND_PRINT("Trans2Interim\n");
+ return;
+ }
+ ND_TCHECK_2(w + (7 * 2));
+ pcnt = GET_LE_U_2(w + 3 * 2);
+ param = buf + GET_LE_U_2(w + 4 * 2);
+ dcnt = GET_LE_U_2(w + 6 * 2);
+ data = buf + GET_LE_U_2(w + 7 * 2);
+ }
+
+ ND_PRINT("%s param_length=%u data_length=%u\n", fn->name, pcnt, dcnt);
+
+ if (request) {
+ if (GET_U_1(words) == 8) {
+ smb_fdata(ndo, words + 1,
+ "Trans2Secondary\nTotParam=[u]\nTotData=[u]\nParamCnt=[u]\nParamOff=[u]\nParamDisp=[u]\nDataCnt=[u]\nDataOff=[u]\nDataDisp=[u]\nHandle=[u]\n",
+ maxbuf, unicodestr);
+ return;
+ } else {
+ smb_fdata(ndo, words + 1,
+ "TotParam=[u]\nTotData=[u]\nMaxParam=[u]\nMaxData=[u]\nMaxSetup=[b][P1]\nFlags=[w]\nTimeOut=[D]\nRes1=[w]\nParamCnt=[u]\nParamOff=[u]\nDataCnt=[u]\nDataOff=[u]\nSetupCnt=[b][P1]\n",
+ words + 1 + 14 * 2, unicodestr);
+ }
+ f1 = fn->descript.req_f1;
+ f2 = fn->descript.req_f2;
+ } else {
+ smb_fdata(ndo, words + 1,
+ "TotParam=[u]\nTotData=[u]\nRes1=[w]\nParamCnt=[u]\nParamOff=[u]\nParamDisp[u]\nDataCnt=[u]\nDataOff=[u]\nDataDisp=[u]\nSetupCnt=[b][P1]\n",
+ words + 1 + 10 * 2, unicodestr);
+ f1 = fn->descript.rep_f1;
+ f2 = fn->descript.rep_f2;
+ }
+
+ bcc = GET_LE_U_2(dat);
+ ND_PRINT("smb_bcc=%u\n", bcc);
+ if (fn->descript.fn)
+ (*fn->descript.fn)(ndo, param, data, pcnt, dcnt);
+ else {
+ smb_fdata(ndo, param, f1 ? f1 : "Parameters=\n", param + pcnt, unicodestr);
+ smb_fdata(ndo, data, f2 ? f2 : "Data=\n", data + dcnt, unicodestr);
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static void
+print_browse(netdissect_options *ndo,
+ const u_char *param, u_int paramlen, const u_char *data, u_int datalen)
+{
+ const u_char *maxbuf = data + datalen;
+ u_int command;
+
+ command = GET_U_1(data);
+
+ smb_fdata(ndo, param, "BROWSE PACKET\n|Param ", param+paramlen, unicodestr);
+
+ switch (command) {
+ case 0xF:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (LocalMasterAnnouncement)\nUpdateCount=[w]\nRes1=[B]\nAnnounceInterval=[u]\nName=[n2]\nMajorVersion=[B]\nMinorVersion=[B]\nServerType=[W]\nElectionVersion=[w]\nBrowserConstant=[w]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0x1:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (HostAnnouncement)\nUpdateCount=[w]\nRes1=[B]\nAnnounceInterval=[u]\nName=[n2]\nMajorVersion=[B]\nMinorVersion=[B]\nServerType=[W]\nElectionVersion=[w]\nBrowserConstant=[w]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0x2:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (AnnouncementRequest)\nFlags=[B]\nReplySystemName=[S]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0xc:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (WorkgroupAnnouncement)\nUpdateCount=[w]\nRes1=[B]\nAnnounceInterval=[u]\nName=[n2]\nMajorVersion=[B]\nMinorVersion=[B]\nServerType=[W]\nCommentPointer=[W]\nServerName=[S]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0x8:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (ElectionFrame)\nElectionVersion=[B]\nOSSummary=[W]\nUptime=[(W, W)]\nServerName=[S]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0xb:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (BecomeBackupBrowser)\nName=[S]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0x9:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (GetBackupList)\nListCount?=[B]\nToken=[W]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0xa:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (BackupListResponse)\nServerCount?=[B]\nToken=[W]\n*Name=[S]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0xd:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (MasterAnnouncement)\nMasterName=[S]\n",
+ maxbuf, unicodestr);
+ break;
+
+ case 0xe:
+ data = smb_fdata(ndo, data,
+ "BROWSE PACKET:\nType=[B] (ResetBrowser)\nOptions=[B]\n", maxbuf, unicodestr);
+ break;
+
+ default:
+ data = smb_fdata(ndo, data, "Unknown Browser Frame ", maxbuf, unicodestr);
+ break;
+ }
+}
+
+
+static void
+print_ipc(netdissect_options *ndo,
+ const u_char *param, u_int paramlen, const u_char *data, u_int datalen)
+{
+ if (paramlen)
+ smb_fdata(ndo, param, "Command=[w]\nStr1=[S]\nStr2=[S]\n", param + paramlen,
+ unicodestr);
+ if (datalen)
+ smb_fdata(ndo, data, "IPC ", data + datalen, unicodestr);
+}
+
+
+static void
+print_trans(netdissect_options *ndo,
+ const u_char *words, const u_char *data1, const u_char *buf, const u_char *maxbuf)
+{
+ u_int bcc;
+ const char *f1, *f2, *f3, *f4;
+ const u_char *data, *param;
+ const u_char *w = words + 1;
+ u_int datalen, paramlen;
+
+ if (request) {
+ ND_TCHECK_2(w + (12 * 2));
+ paramlen = GET_LE_U_2(w + 9 * 2);
+ param = buf + GET_LE_U_2(w + 10 * 2);
+ datalen = GET_LE_U_2(w + 11 * 2);
+ data = buf + GET_LE_U_2(w + 12 * 2);
+ f1 = "TotParamCnt=[u]\nTotDataCnt=[u]\nMaxParmCnt=[u]\nMaxDataCnt=[u]\nMaxSCnt=[u]\nTransFlags=[w]\nRes1=[w]\nRes2=[w]\nRes3=[w]\nParamCnt=[u]\nParamOff=[u]\nDataCnt=[u]\nDataOff=[u]\nSUCnt=[u]\n";
+ f2 = "|Name=[S]\n";
+ f3 = "|Param ";
+ f4 = "|Data ";
+ } else {
+ ND_TCHECK_2(w + (7 * 2));
+ paramlen = GET_LE_U_2(w + 3 * 2);
+ param = buf + GET_LE_U_2(w + 4 * 2);
+ datalen = GET_LE_U_2(w + 6 * 2);
+ data = buf + GET_LE_U_2(w + 7 * 2);
+ f1 = "TotParamCnt=[u]\nTotDataCnt=[u]\nRes1=[u]\nParamCnt=[u]\nParamOff=[u]\nRes2=[u]\nDataCnt=[u]\nDataOff=[u]\nRes3=[u]\nLsetup=[u]\n";
+ f2 = "|Unknown ";
+ f3 = "|Param ";
+ f4 = "|Data ";
+ }
+
+ smb_fdata(ndo, words + 1, f1,
+ ND_MIN(words + 1 + 2 * GET_U_1(words), maxbuf),
+ unicodestr);
+
+ bcc = GET_LE_U_2(data1);
+ ND_PRINT("smb_bcc=%u\n", bcc);
+ if (bcc > 0) {
+ smb_fdata(ndo, data1 + 2, f2, maxbuf - (paramlen + datalen), unicodestr);
+
+#define MAILSLOT_BROWSE_STR "\\MAILSLOT\\BROWSE"
+ ND_TCHECK_LEN(data1 + 2, strlen(MAILSLOT_BROWSE_STR) + 1);
+ if (strcmp((const char *)(data1 + 2), MAILSLOT_BROWSE_STR) == 0) {
+ print_browse(ndo, param, paramlen, data, datalen);
+ return;
+ }
+#undef MAILSLOT_BROWSE_STR
+
+#define PIPE_LANMAN_STR "\\PIPE\\LANMAN"
+ ND_TCHECK_LEN(data1 + 2, strlen(PIPE_LANMAN_STR) + 1);
+ if (strcmp((const char *)(data1 + 2), PIPE_LANMAN_STR) == 0) {
+ print_ipc(ndo, param, paramlen, data, datalen);
+ return;
+ }
+#undef PIPE_LANMAN_STR
+
+ if (paramlen)
+ smb_fdata(ndo, param, f3, ND_MIN(param + paramlen, maxbuf), unicodestr);
+ if (datalen)
+ smb_fdata(ndo, data, f4, ND_MIN(data + datalen, maxbuf), unicodestr);
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+
+static void
+print_negprot(netdissect_options *ndo,
+ const u_char *words, const u_char *data, const u_char *buf _U_, const u_char *maxbuf)
+{
+ u_int wct, bcc;
+ const char *f1 = NULL, *f2 = NULL;
+
+ wct = GET_U_1(words);
+ if (request)
+ f2 = "*|Dialect=[Y]\n";
+ else {
+ if (wct == 1)
+ f1 = "Core Protocol\nDialectIndex=[u]";
+ else if (wct == 17)
+ f1 = "NT1 Protocol\nDialectIndex=[u]\nSecMode=[B]\nMaxMux=[u]\nNumVcs=[u]\nMaxBuffer=[U]\nRawSize=[U]\nSessionKey=[W]\nCapabilities=[W]\nServerTime=[T3]TimeZone=[u]\nCryptKey=";
+ else if (wct == 13)
+ f1 = "Coreplus/Lanman1/Lanman2 Protocol\nDialectIndex=[u]\nSecMode=[w]\nMaxXMit=[u]\nMaxMux=[u]\nMaxVcs=[u]\nBlkMode=[w]\nSessionKey=[W]\nServerTime=[T1]TimeZone=[u]\nRes=[W]\nCryptKey=";
+ }
+
+ if (f1)
+ smb_fdata(ndo, words + 1, f1, ND_MIN(words + 1 + wct * 2, maxbuf),
+ unicodestr);
+ else
+ smb_data_print(ndo, words + 1, ND_MIN(wct * 2, ND_BYTES_BETWEEN(maxbuf, words + 1)));
+
+ bcc = GET_LE_U_2(data);
+ ND_PRINT("smb_bcc=%u\n", bcc);
+ if (bcc > 0) {
+ if (f2)
+ smb_fdata(ndo, data + 2, f2, ND_MIN(data + 2 + GET_LE_U_2(data),
+ maxbuf), unicodestr);
+ else
+ smb_data_print(ndo, data + 2,
+ ND_MIN(GET_LE_U_2(data), ND_BYTES_BETWEEN(maxbuf, data + 2)));
+ }
+}
+
+static void
+print_sesssetup(netdissect_options *ndo,
+ const u_char *words, const u_char *data, const u_char *buf _U_, const u_char *maxbuf)
+{
+ u_int wct, bcc;
+ const char *f1 = NULL, *f2 = NULL;
+
+ wct = GET_U_1(words);
+ if (request) {
+ if (wct == 10)
+ f1 = "Com2=[w]\nOff2=[u]\nBufSize=[u]\nMpxMax=[u]\nVcNum=[u]\nSessionKey=[W]\nPassLen=[u]\nCryptLen=[u]\nCryptOff=[u]\nPass&Name=\n";
+ else
+ f1 = "Com2=[B]\nRes1=[B]\nOff2=[u]\nMaxBuffer=[u]\nMaxMpx=[u]\nVcNumber=[u]\nSessionKey=[W]\nCaseInsensitivePasswordLength=[u]\nCaseSensitivePasswordLength=[u]\nRes=[W]\nCapabilities=[W]\nPass1&Pass2&Account&Domain&OS&LanMan=\n";
+ } else {
+ if (wct == 3) {
+ f1 = "Com2=[w]\nOff2=[u]\nAction=[w]\n";
+ } else if (wct == 13) {
+ f1 = "Com2=[B]\nRes=[B]\nOff2=[u]\nAction=[w]\n";
+ f2 = "NativeOS=[S]\nNativeLanMan=[S]\nPrimaryDomain=[S]\n";
+ }
+ }
+
+ if (f1)
+ smb_fdata(ndo, words + 1, f1, ND_MIN(words + 1 + wct * 2, maxbuf),
+ unicodestr);
+ else
+ smb_data_print(ndo, words + 1, ND_MIN(wct * 2, ND_BYTES_BETWEEN(maxbuf, words + 1)));
+
+ bcc = GET_LE_U_2(data);
+ ND_PRINT("smb_bcc=%u\n", bcc);
+ if (bcc > 0) {
+ if (f2)
+ smb_fdata(ndo, data + 2, f2, ND_MIN(data + 2 + GET_LE_U_2(data),
+ maxbuf), unicodestr);
+ else
+ smb_data_print(ndo, data + 2,
+ ND_MIN(GET_LE_U_2(data), ND_BYTES_BETWEEN(maxbuf, data + 2)));
+ }
+}
+
+static void
+print_lockingandx(netdissect_options *ndo,
+ const u_char *words, const u_char *data, const u_char *buf _U_, const u_char *maxbuf)
+{
+ u_int wct, bcc;
+ const u_char *maxwords;
+ const char *f1 = NULL, *f2 = NULL;
+
+ wct = GET_U_1(words);
+ if (request) {
+ f1 = "Com2=[w]\nOff2=[u]\nHandle=[u]\nLockType=[w]\nTimeOut=[D]\nUnlockCount=[u]\nLockCount=[u]\n";
+ if (GET_U_1(words + 7) & 0x10)
+ f2 = "*Process=[u]\n[P2]Offset=[M]\nLength=[M]\n";
+ else
+ f2 = "*Process=[u]\nOffset=[D]\nLength=[U]\n";
+ } else {
+ f1 = "Com2=[w]\nOff2=[u]\n";
+ }
+
+ maxwords = ND_MIN(words + 1 + wct * 2, maxbuf);
+ if (wct)
+ smb_fdata(ndo, words + 1, f1, maxwords, unicodestr);
+
+ bcc = GET_LE_U_2(data);
+ ND_PRINT("smb_bcc=%u\n", bcc);
+ if (bcc > 0) {
+ if (f2)
+ smb_fdata(ndo, data + 2, f2, ND_MIN(data + 2 + GET_LE_U_2(data),
+ maxbuf), unicodestr);
+ else
+ smb_data_print(ndo, data + 2,
+ ND_MIN(GET_LE_U_2(data), ND_BYTES_BETWEEN(maxbuf, data + 2)));
+ }
+}
+
+
+static const struct smbfns smb_fns[] = {
+ { -1, "SMBunknown", 0, DEFDESCRIPT },
+
+ { SMBtcon, "SMBtcon", 0,
+ { NULL, "Path=[Z]\nPassword=[Z]\nDevice=[Z]\n",
+ "MaxXmit=[u]\nTreeId=[u]\n", NULL,
+ NULL } },
+
+ { SMBtdis, "SMBtdis", 0, DEFDESCRIPT },
+ { SMBexit, "SMBexit", 0, DEFDESCRIPT },
+ { SMBioctl, "SMBioctl", 0, DEFDESCRIPT },
+
+ { SMBecho, "SMBecho", 0,
+ { "ReverbCount=[u]\n", NULL,
+ "SequenceNum=[u]\n", NULL,
+ NULL } },
+
+ { SMBulogoffX, "SMBulogoffX", FLG_CHAIN, DEFDESCRIPT },
+
+ { SMBgetatr, "SMBgetatr", 0,
+ { NULL, "Path=[Z]\n",
+ "Attribute=[A]\nTime=[T2]Size=[U]\nRes=([w,w,w,w,w])\n", NULL,
+ NULL } },
+
+ { SMBsetatr, "SMBsetatr", 0,
+ { "Attribute=[A]\nTime=[T2]Res=([w,w,w,w,w])\n", "Path=[Z]\n",
+ NULL, NULL, NULL } },
+
+ { SMBchkpth, "SMBchkpth", 0,
+ { NULL, "Path=[Z]\n", NULL, NULL, NULL } },
+
+ { SMBsearch, "SMBsearch", 0,
+ { "Count=[u]\nAttrib=[A]\n",
+ "Path=[Z]\nBlkType=[B]\nBlkLen=[u]\n|Res1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\n",
+ "Count=[u]\n",
+ "BlkType=[B]\nBlkLen=[u]\n*\nRes1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\nAttrib=[a]\nTime=[T1]Size=[U]\nName=[s13]\n",
+ NULL } },
+
+ { SMBopen, "SMBopen", 0,
+ { "Mode=[w]\nAttribute=[A]\n", "Path=[Z]\n",
+ "Handle=[u]\nOAttrib=[A]\nTime=[T2]Size=[U]\nAccess=[w]\n",
+ NULL, NULL } },
+
+ { SMBcreate, "SMBcreate", 0,
+ { "Attrib=[A]\nTime=[T2]", "Path=[Z]\n", "Handle=[u]\n", NULL, NULL } },
+
+ { SMBmknew, "SMBmknew", 0,
+ { "Attrib=[A]\nTime=[T2]", "Path=[Z]\n", "Handle=[u]\n", NULL, NULL } },
+
+ { SMBunlink, "SMBunlink", 0,
+ { "Attrib=[A]\n", "Path=[Z]\n", NULL, NULL, NULL } },
+
+ { SMBread, "SMBread", 0,
+ { "Handle=[u]\nByteCount=[u]\nOffset=[D]\nCountLeft=[u]\n", NULL,
+ "Count=[u]\nRes=([w,w,w,w])\n", NULL, NULL } },
+
+ { SMBwrite, "SMBwrite", 0,
+ { "Handle=[u]\nByteCount=[u]\nOffset=[D]\nCountLeft=[u]\n", NULL,
+ "Count=[u]\n", NULL, NULL } },
+
+ { SMBclose, "SMBclose", 0,
+ { "Handle=[u]\nTime=[T2]", NULL, NULL, NULL, NULL } },
+
+ { SMBmkdir, "SMBmkdir", 0,
+ { NULL, "Path=[Z]\n", NULL, NULL, NULL } },
+
+ { SMBrmdir, "SMBrmdir", 0,
+ { NULL, "Path=[Z]\n", NULL, NULL, NULL } },
+
+ { SMBdskattr, "SMBdskattr", 0,
+ { NULL, NULL,
+ "TotalUnits=[u]\nBlocksPerUnit=[u]\nBlockSize=[u]\nFreeUnits=[u]\nMedia=[w]\n",
+ NULL, NULL } },
+
+ { SMBmv, "SMBmv", 0,
+ { "Attrib=[A]\n", "OldPath=[Z]\nNewPath=[Z]\n", NULL, NULL, NULL } },
+
+ /*
+ * this is a Pathworks specific call, allowing the
+ * changing of the root path
+ */
+ { pSETDIR, "SMBsetdir", 0, { NULL, "Path=[Z]\n", NULL, NULL, NULL } },
+
+ { SMBlseek, "SMBlseek", 0,
+ { "Handle=[u]\nMode=[w]\nOffset=[D]\n", "Offset=[D]\n", NULL, NULL, NULL } },
+
+ { SMBflush, "SMBflush", 0, { "Handle=[u]\n", NULL, NULL, NULL, NULL } },
+
+ { SMBsplopen, "SMBsplopen", 0,
+ { "SetupLen=[u]\nMode=[w]\n", "Ident=[Z]\n", "Handle=[u]\n",
+ NULL, NULL } },
+
+ { SMBsplclose, "SMBsplclose", 0,
+ { "Handle=[u]\n", NULL, NULL, NULL, NULL } },
+
+ { SMBsplretq, "SMBsplretq", 0,
+ { "MaxCount=[u]\nStartIndex=[u]\n", NULL,
+ "Count=[u]\nIndex=[u]\n",
+ "*Time=[T2]Status=[B]\nJobID=[u]\nSize=[U]\nRes=[B]Name=[s16]\n",
+ NULL } },
+
+ { SMBsplwr, "SMBsplwr", 0,
+ { "Handle=[u]\n", NULL, NULL, NULL, NULL } },
+
+ { SMBlock, "SMBlock", 0,
+ { "Handle=[u]\nCount=[U]\nOffset=[D]\n", NULL, NULL, NULL, NULL } },
+
+ { SMBunlock, "SMBunlock", 0,
+ { "Handle=[u]\nCount=[U]\nOffset=[D]\n", NULL, NULL, NULL, NULL } },
+
+ /* CORE+ PROTOCOL FOLLOWS */
+
+ { SMBreadbraw, "SMBreadbraw", 0,
+ { "Handle=[u]\nOffset=[D]\nMaxCount=[u]\nMinCount=[u]\nTimeOut=[D]\nRes=[u]\n",
+ NULL, NULL, NULL, NULL } },
+
+ { SMBwritebraw, "SMBwritebraw", 0,
+ { "Handle=[u]\nTotalCount=[u]\nRes=[w]\nOffset=[D]\nTimeOut=[D]\nWMode=[w]\nRes2=[W]\n|DataSize=[u]\nDataOff=[u]\n",
+ NULL, "WriteRawAck", NULL, NULL } },
+
+ { SMBwritec, "SMBwritec", 0,
+ { NULL, NULL, "Count=[u]\n", NULL, NULL } },
+
+ { SMBwriteclose, "SMBwriteclose", 0,
+ { "Handle=[u]\nCount=[u]\nOffset=[D]\nTime=[T2]Res=([w,w,w,w,w,w])",
+ NULL, "Count=[u]\n", NULL, NULL } },
+
+ { SMBlockread, "SMBlockread", 0,
+ { "Handle=[u]\nByteCount=[u]\nOffset=[D]\nCountLeft=[u]\n", NULL,
+ "Count=[u]\nRes=([w,w,w,w])\n", NULL, NULL } },
+
+ { SMBwriteunlock, "SMBwriteunlock", 0,
+ { "Handle=[u]\nByteCount=[u]\nOffset=[D]\nCountLeft=[u]\n", NULL,
+ "Count=[u]\n", NULL, NULL } },
+
+ { SMBreadBmpx, "SMBreadBmpx", 0,
+ { "Handle=[u]\nOffset=[D]\nMaxCount=[u]\nMinCount=[u]\nTimeOut=[D]\nRes=[w]\n",
+ NULL,
+ "Offset=[D]\nTotCount=[u]\nRemaining=[u]\nRes=([w,w])\nDataSize=[u]\nDataOff=[u]\n",
+ NULL, NULL } },
+
+ { SMBwriteBmpx, "SMBwriteBmpx", 0,
+ { "Handle=[u]\nTotCount=[u]\nRes=[w]\nOffset=[D]\nTimeOut=[D]\nWMode=[w]\nRes2=[W]\nDataSize=[u]\nDataOff=[u]\n", NULL,
+ "Remaining=[u]\n", NULL, NULL } },
+
+ { SMBwriteBs, "SMBwriteBs", 0,
+ { "Handle=[u]\nTotCount=[u]\nOffset=[D]\nRes=[W]\nDataSize=[u]\nDataOff=[u]\n",
+ NULL, "Count=[u]\n", NULL, NULL } },
+
+ { SMBsetattrE, "SMBsetattrE", 0,
+ { "Handle=[u]\nCreationTime=[T2]AccessTime=[T2]ModifyTime=[T2]", NULL,
+ NULL, NULL, NULL } },
+
+ { SMBgetattrE, "SMBgetattrE", 0,
+ { "Handle=[u]\n", NULL,
+ "CreationTime=[T2]AccessTime=[T2]ModifyTime=[T2]Size=[U]\nAllocSize=[U]\nAttribute=[A]\n",
+ NULL, NULL } },
+
+ { SMBtranss, "SMBtranss", 0, DEFDESCRIPT },
+ { SMBioctls, "SMBioctls", 0, DEFDESCRIPT },
+
+ { SMBcopy, "SMBcopy", 0,
+ { "TreeID2=[u]\nOFun=[w]\nFlags=[w]\n", "Path=[S]\nNewPath=[S]\n",
+ "CopyCount=[u]\n", "|ErrStr=[S]\n", NULL } },
+
+ { SMBmove, "SMBmove", 0,
+ { "TreeID2=[u]\nOFun=[w]\nFlags=[w]\n", "Path=[S]\nNewPath=[S]\n",
+ "MoveCount=[u]\n", "|ErrStr=[S]\n", NULL } },
+
+ { SMBopenX, "SMBopenX", FLG_CHAIN,
+ { "Com2=[w]\nOff2=[u]\nFlags=[w]\nMode=[w]\nSearchAttrib=[A]\nAttrib=[A]\nTime=[T2]OFun=[w]\nSize=[U]\nTimeOut=[D]\nRes=[W]\n",
+ "Path=[S]\n",
+ "Com2=[w]\nOff2=[u]\nHandle=[u]\nAttrib=[A]\nTime=[T2]Size=[U]\nAccess=[w]\nType=[w]\nState=[w]\nAction=[w]\nFileID=[W]\nRes=[w]\n",
+ NULL, NULL } },
+
+ { SMBreadX, "SMBreadX", FLG_CHAIN,
+ { "Com2=[w]\nOff2=[u]\nHandle=[u]\nOffset=[D]\nMaxCount=[u]\nMinCount=[u]\nTimeOut=[D]\nCountLeft=[u]\n",
+ NULL,
+ "Com2=[w]\nOff2=[u]\nRemaining=[u]\nRes=[W]\nDataSize=[u]\nDataOff=[u]\nRes=([w,w,w,w])\n",
+ NULL, NULL } },
+
+ { SMBwriteX, "SMBwriteX", FLG_CHAIN,
+ { "Com2=[w]\nOff2=[u]\nHandle=[u]\nOffset=[D]\nTimeOut=[D]\nWMode=[w]\nCountLeft=[u]\nRes=[w]\nDataSize=[u]\nDataOff=[u]\n",
+ NULL,
+ "Com2=[w]\nOff2=[u]\nCount=[u]\nRemaining=[u]\nRes=[W]\n",
+ NULL, NULL } },
+
+ { SMBffirst, "SMBffirst", 0,
+ { "Count=[u]\nAttrib=[A]\n",
+ "Path=[Z]\nBlkType=[B]\nBlkLen=[u]\n|Res1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\n",
+ "Count=[u]\n",
+ "BlkType=[B]\nBlkLen=[u]\n*\nRes1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\nAttrib=[a]\nTime=[T1]Size=[U]\nName=[s13]\n",
+ NULL } },
+
+ { SMBfunique, "SMBfunique", 0,
+ { "Count=[u]\nAttrib=[A]\n",
+ "Path=[Z]\nBlkType=[B]\nBlkLen=[u]\n|Res1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\n",
+ "Count=[u]\n",
+ "BlkType=[B]\nBlkLen=[u]\n*\nRes1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\nAttrib=[a]\nTime=[T1]Size=[U]\nName=[s13]\n",
+ NULL } },
+
+ { SMBfclose, "SMBfclose", 0,
+ { "Count=[u]\nAttrib=[A]\n",
+ "Path=[Z]\nBlkType=[B]\nBlkLen=[u]\n|Res1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\n",
+ "Count=[u]\n",
+ "BlkType=[B]\nBlkLen=[u]\n*\nRes1=[B]\nMask=[s11]\nSrv1=[B]\nDirIndex=[u]\nSrv2=[w]\nRes2=[W]\nAttrib=[a]\nTime=[T1]Size=[U]\nName=[s13]\n",
+ NULL } },
+
+ { SMBfindnclose, "SMBfindnclose", 0,
+ { "Handle=[u]\n", NULL, NULL, NULL, NULL } },
+
+ { SMBfindclose, "SMBfindclose", 0,
+ { "Handle=[u]\n", NULL, NULL, NULL, NULL } },
+
+ { SMBsends, "SMBsends", 0,
+ { NULL, "Source=[Z]\nDest=[Z]\n", NULL, NULL, NULL } },
+
+ { SMBsendstrt, "SMBsendstrt", 0,
+ { NULL, "Source=[Z]\nDest=[Z]\n", "GroupID=[u]\n", NULL, NULL } },
+
+ { SMBsendend, "SMBsendend", 0,
+ { "GroupID=[u]\n", NULL, NULL, NULL, NULL } },
+
+ { SMBsendtxt, "SMBsendtxt", 0,
+ { "GroupID=[u]\n", NULL, NULL, NULL, NULL } },
+
+ { SMBsendb, "SMBsendb", 0,
+ { NULL, "Source=[Z]\nDest=[Z]\n", NULL, NULL, NULL } },
+
+ { SMBfwdname, "SMBfwdname", 0, DEFDESCRIPT },
+ { SMBcancelf, "SMBcancelf", 0, DEFDESCRIPT },
+ { SMBgetmac, "SMBgetmac", 0, DEFDESCRIPT },
+
+ { SMBnegprot, "SMBnegprot", 0,
+ { NULL, NULL, NULL, NULL, print_negprot } },
+
+ { SMBsesssetupX, "SMBsesssetupX", FLG_CHAIN,
+ { NULL, NULL, NULL, NULL, print_sesssetup } },
+
+ { SMBtconX, "SMBtconX", FLG_CHAIN,
+ { "Com2=[w]\nOff2=[u]\nFlags=[w]\nPassLen=[u]\nPasswd&Path&Device=\n",
+ NULL, "Com2=[w]\nOff2=[u]\n", "ServiceType=[R]\n", NULL } },
+
+ { SMBlockingX, "SMBlockingX", FLG_CHAIN,
+ { NULL, NULL, NULL, NULL, print_lockingandx } },
+
+ { SMBtrans2, "SMBtrans2", 0, { NULL, NULL, NULL, NULL, print_trans2 } },
+
+ { SMBtranss2, "SMBtranss2", 0, DEFDESCRIPT },
+ { SMBctemp, "SMBctemp", 0, DEFDESCRIPT },
+ { SMBreadBs, "SMBreadBs", 0, DEFDESCRIPT },
+ { SMBtrans, "SMBtrans", 0, { NULL, NULL, NULL, NULL, print_trans } },
+
+ { SMBnttrans, "SMBnttrans", 0, DEFDESCRIPT },
+ { SMBnttranss, "SMBnttranss", 0, DEFDESCRIPT },
+
+ { SMBntcreateX, "SMBntcreateX", FLG_CHAIN,
+ { "Com2=[w]\nOff2=[u]\nRes=[b]\nNameLen=[lu]\nFlags=[W]\nRootDirectoryFid=[U]\nAccessMask=[W]\nAllocationSize=[L]\nExtFileAttributes=[W]\nShareAccess=[W]\nCreateDisposition=[W]\nCreateOptions=[W]\nImpersonationLevel=[W]\nSecurityFlags=[b]\n",
+ "Path=[C]\n",
+ "Com2=[w]\nOff2=[u]\nOplockLevel=[b]\nFid=[u]\nCreateAction=[W]\nCreateTime=[T3]LastAccessTime=[T3]LastWriteTime=[T3]ChangeTime=[T3]ExtFileAttributes=[W]\nAllocationSize=[L]\nEndOfFile=[L]\nFileType=[w]\nDeviceState=[w]\nDirectory=[b]\n",
+ NULL, NULL } },
+
+ { SMBntcancel, "SMBntcancel", 0, DEFDESCRIPT },
+
+ { -1, NULL, 0, DEFDESCRIPT }
+};
+
+
+/*
+ * print a SMB message
+ */
+static void
+print_smb(netdissect_options *ndo,
+ const u_char *buf, const u_char *maxbuf)
+{
+ uint16_t flags2;
+ u_int nterrcodes;
+ u_int command;
+ uint32_t nterror;
+ const u_char *words, *maxwords, *data;
+ const struct smbfns *fn;
+ const char *fmt_smbheader =
+ "[P4]SMB Command = [B]\nError class = [BP1]\nError code = [u]\nFlags1 = [B]\nFlags2 = [B][P13]\nTree ID = [u]\nProc ID = [u]\nUID = [u]\nMID = [u]\nWord Count = [b]\n";
+ u_int smboffset;
+
+ ndo->ndo_protocol = "smb";
+
+ request = (GET_U_1(buf + 9) & 0x80) ? 0 : 1;
+ startbuf = buf;
+
+ command = GET_U_1(buf + 4);
+
+ fn = smbfind(command, smb_fns);
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n");
+
+ ND_PRINT("SMB PACKET: %s (%s)", fn->name, request ? "REQUEST" : "REPLY");
+
+ if (ndo->ndo_vflag < 2)
+ return;
+
+ ND_PRINT("\n");
+ flags2 = GET_LE_U_2(buf + 10);
+ unicodestr = flags2 & 0x8000;
+ nterrcodes = flags2 & 0x4000;
+
+ /* print out the header */
+ smb_fdata(ndo, buf, fmt_smbheader, buf + 33, unicodestr);
+
+ if (nterrcodes) {
+ nterror = GET_LE_U_4(buf + 5);
+ if (nterror)
+ ND_PRINT("NTError = %s\n", nt_errstr(nterror));
+ } else {
+ if (GET_U_1(buf + 5))
+ ND_PRINT("SMBError = %s\n", smb_errstr(GET_U_1(buf + 5),
+ GET_LE_U_2(buf + 7)));
+ }
+
+ smboffset = 32;
+
+ for (;;) {
+ const char *f1, *f2;
+ int wct;
+ u_int bcc;
+ u_int newsmboffset;
+
+ words = buf + smboffset;
+ wct = GET_U_1(words);
+ data = words + 1 + wct * 2;
+ maxwords = ND_MIN(data, maxbuf);
+
+ if (request) {
+ f1 = fn->descript.req_f1;
+ f2 = fn->descript.req_f2;
+ } else {
+ f1 = fn->descript.rep_f1;
+ f2 = fn->descript.rep_f2;
+ }
+
+ smb_reset();
+ if (fn->descript.fn)
+ (*fn->descript.fn)(ndo, words, data, buf, maxbuf);
+ else {
+ if (wct) {
+ if (f1)
+ smb_fdata(ndo, words + 1, f1, words + 1 + wct * 2, unicodestr);
+ else {
+ u_int i;
+ u_int v;
+
+ for (i = 0; words + 1 + 2 * i < maxwords; i++) {
+ v = GET_LE_U_2(words + 1 + 2 * i);
+ ND_PRINT("smb_vwv[%u]=%u (0x%X)\n", i, v, v);
+ }
+ }
+ }
+
+ bcc = GET_LE_U_2(data);
+ ND_PRINT("smb_bcc=%u\n", bcc);
+ if (f2) {
+ if (bcc > 0)
+ smb_fdata(ndo, data + 2, f2, data + 2 + bcc, unicodestr);
+ } else {
+ if (bcc > 0) {
+ ND_PRINT("smb_buf[]=\n");
+ smb_data_print(ndo, data + 2, ND_MIN(bcc, ND_BYTES_BETWEEN(maxbuf, data + 2)));
+ }
+ }
+ }
+
+ if ((fn->flags & FLG_CHAIN) == 0)
+ break;
+ if (wct == 0)
+ break;
+ command = GET_U_1(words + 1);
+ if (command == 0xFF)
+ break;
+ newsmboffset = GET_LE_U_2(words + 3);
+
+ fn = smbfind(command, smb_fns);
+
+ ND_PRINT("\nSMB PACKET: %s (%s) (CHAINED)\n",
+ fn->name, request ? "REQUEST" : "REPLY");
+ if (newsmboffset <= smboffset) {
+ ND_PRINT("Bad andX offset: %u <= %u\n", newsmboffset, smboffset);
+ break;
+ }
+ smboffset = newsmboffset;
+ }
+}
+
+
+/*
+ * print a NBT packet received across tcp on port 139
+ */
+void
+nbt_tcp_print(netdissect_options *ndo,
+ const u_char *data, u_int length)
+{
+ u_int caplen;
+ u_int type;
+ u_int nbt_len;
+ const u_char *maxbuf;
+
+ ndo->ndo_protocol = "nbt_tcp";
+ if (length < 4)
+ goto trunc;
+ if (ndo->ndo_snapend < data)
+ goto trunc;
+ caplen = ND_BYTES_AVAILABLE_AFTER(data);
+ if (caplen < 4)
+ goto trunc;
+ maxbuf = data + caplen;
+ type = GET_U_1(data);
+ nbt_len = GET_BE_U_2(data + 2);
+ length -= 4;
+ caplen -= 4;
+
+ startbuf = data;
+
+ if (ndo->ndo_vflag < 2) {
+ ND_PRINT(" NBT Session Packet: ");
+ switch (type) {
+ case 0x00:
+ ND_PRINT("Session Message");
+ break;
+
+ case 0x81:
+ ND_PRINT("Session Request");
+ break;
+
+ case 0x82:
+ ND_PRINT("Session Granted");
+ break;
+
+ case 0x83:
+ {
+ u_int ecode;
+
+ if (nbt_len < 4)
+ goto trunc;
+ if (length < 4)
+ goto trunc;
+ if (caplen < 4)
+ goto trunc;
+ ecode = GET_U_1(data + 4);
+
+ ND_PRINT("Session Reject, ");
+ switch (ecode) {
+ case 0x80:
+ ND_PRINT("Not listening on called name");
+ break;
+ case 0x81:
+ ND_PRINT("Not listening for calling name");
+ break;
+ case 0x82:
+ ND_PRINT("Called name not present");
+ break;
+ case 0x83:
+ ND_PRINT("Called name present, but insufficient resources");
+ break;
+ default:
+ ND_PRINT("Unspecified error 0x%X", ecode);
+ break;
+ }
+ }
+ break;
+
+ case 0x85:
+ ND_PRINT("Session Keepalive");
+ break;
+
+ default:
+ data = smb_fdata(ndo, data, "Unknown packet type [rB]", maxbuf, 0);
+ break;
+ }
+ } else {
+ ND_PRINT("\n>>> NBT Session Packet\n");
+ switch (type) {
+ case 0x00:
+ data = smb_fdata(ndo, data, "[P1]NBT Session Message\nFlags=[B]\nLength=[ru]\n",
+ data + 4, 0);
+ if (data == NULL)
+ break;
+ if (nbt_len >= 4 && caplen >= 4 && memcmp(data,"\377SMB",4) == 0) {
+ if (nbt_len > caplen) {
+ if (nbt_len > length)
+ ND_PRINT("WARNING: Packet is continued in later TCP segments\n");
+ else
+ ND_PRINT("WARNING: Short packet. Try increasing the snap length by %u\n",
+ nbt_len - caplen);
+ }
+ print_smb(ndo, data, maxbuf > data + nbt_len ? data + nbt_len : maxbuf);
+ } else
+ ND_PRINT("Session packet:(raw data or continuation?)\n");
+ break;
+
+ case 0x81:
+ data = smb_fdata(ndo, data,
+ "[P1]NBT Session Request\nFlags=[B]\nLength=[ru]\nDestination=[n1]\nSource=[n1]\n",
+ maxbuf, 0);
+ break;
+
+ case 0x82:
+ data = smb_fdata(ndo, data, "[P1]NBT Session Granted\nFlags=[B]\nLength=[ru]\n", maxbuf, 0);
+ break;
+
+ case 0x83:
+ {
+ const u_char *origdata;
+ u_int ecode;
+
+ origdata = data;
+ data = smb_fdata(ndo, data, "[P1]NBT SessionReject\nFlags=[B]\nLength=[ru]\nReason=[B]\n",
+ maxbuf, 0);
+ if (data == NULL)
+ break;
+ if (nbt_len >= 1 && caplen >= 1) {
+ ecode = GET_U_1(origdata + 4);
+ switch (ecode) {
+ case 0x80:
+ ND_PRINT("Not listening on called name\n");
+ break;
+ case 0x81:
+ ND_PRINT("Not listening for calling name\n");
+ break;
+ case 0x82:
+ ND_PRINT("Called name not present\n");
+ break;
+ case 0x83:
+ ND_PRINT("Called name present, but insufficient resources\n");
+ break;
+ default:
+ ND_PRINT("Unspecified error 0x%X\n", ecode);
+ break;
+ }
+ }
+ }
+ break;
+
+ case 0x85:
+ data = smb_fdata(ndo, data, "[P1]NBT Session Keepalive\nFlags=[B]\nLength=[ru]\n", maxbuf, 0);
+ break;
+
+ default:
+ data = smb_fdata(ndo, data, "NBT - Unknown packet type\nType=[B]\n", maxbuf, 0);
+ break;
+ }
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+static const struct tok opcode_str[] = {
+ { 0, "QUERY" },
+ { 5, "REGISTRATION" },
+ { 6, "RELEASE" },
+ { 7, "WACK" },
+ { 8, "REFRESH(8)" },
+ { 9, "REFRESH" },
+ { 15, "MULTIHOMED REGISTRATION" },
+ { 0, NULL }
+};
+
+/*
+ * print a NBT packet received across udp on port 137
+ */
+void
+nbt_udp137_print(netdissect_options *ndo,
+ const u_char *data, u_int length)
+{
+ const u_char *maxbuf = data + length;
+ u_int name_trn_id, response, opcode, nm_flags, rcode;
+ u_int qdcount, ancount, nscount, arcount;
+ const u_char *p;
+ u_int total, i;
+
+ ndo->ndo_protocol = "nbt_udp137";
+ name_trn_id = GET_BE_U_2(data);
+ response = (GET_U_1(data + 2) >> 7);
+ opcode = (GET_U_1(data + 2) >> 3) & 0xF;
+ nm_flags = ((GET_U_1(data + 2) & 0x7) << 4) + (GET_U_1(data + 3) >> 4);
+ rcode = GET_U_1(data + 3) & 0xF;
+ qdcount = GET_BE_U_2(data + 4);
+ ancount = GET_BE_U_2(data + 6);
+ nscount = GET_BE_U_2(data + 8);
+ arcount = GET_BE_U_2(data + 10);
+ startbuf = data;
+
+ if (maxbuf <= data)
+ return;
+
+ if (ndo->ndo_vflag > 1)
+ ND_PRINT("\n>>> ");
+
+ ND_PRINT("NBT UDP PACKET(137): %s", tok2str(opcode_str, "OPUNKNOWN", opcode));
+ if (response) {
+ ND_PRINT("; %s", rcode ? "NEGATIVE" : "POSITIVE");
+ }
+ ND_PRINT("; %s; %s", response ? "RESPONSE" : "REQUEST",
+ (nm_flags & 1) ? "BROADCAST" : "UNICAST");
+
+ if (ndo->ndo_vflag < 2)
+ return;
+
+ ND_PRINT("\nTrnID=0x%X\nOpCode=%u\nNmFlags=0x%X\nRcode=%u\nQueryCount=%u\nAnswerCount=%u\nAuthorityCount=%u\nAddressRecCount=%u\n",
+ name_trn_id, opcode, nm_flags, rcode, qdcount, ancount, nscount,
+ arcount);
+
+ p = data + 12;
+
+ total = ancount + nscount + arcount;
+
+ if (qdcount > 100 || total > 100) {
+ ND_PRINT("Corrupt packet??\n");
+ return;
+ }
+
+ if (qdcount) {
+ ND_PRINT("QuestionRecords:\n");
+ for (i = 0; i < qdcount; i++) {
+ p = smb_fdata(ndo, p,
+ "|Name=[n1]\nQuestionType=[rw]\nQuestionClass=[rw]\n#",
+ maxbuf, 0);
+ if (p == NULL)
+ goto out;
+ }
+ }
+
+ if (total) {
+ ND_PRINT("\nResourceRecords:\n");
+ for (i = 0; i < total; i++) {
+ u_int rdlen;
+ u_int restype;
+
+ p = smb_fdata(ndo, p, "Name=[n1]\n#", maxbuf, 0);
+ if (p == NULL)
+ goto out;
+ restype = GET_BE_U_2(p);
+ p = smb_fdata(ndo, p, "ResType=[rw]\nResClass=[rw]\nTTL=[rU]\n", p + 8, 0);
+ if (p == NULL)
+ goto out;
+ rdlen = GET_BE_U_2(p);
+ ND_PRINT("ResourceLength=%u\nResourceData=\n", rdlen);
+ p += 2;
+ if (rdlen == 6) {
+ p = smb_fdata(ndo, p, "AddrType=[rw]\nAddress=[b.b.b.b]\n", p + rdlen, 0);
+ if (p == NULL)
+ goto out;
+ } else {
+ if (restype == 0x21) {
+ u_int numnames;
+
+ numnames = GET_U_1(p);
+ p = smb_fdata(ndo, p, "NumNames=[B]\n", p + 1, 0);
+ if (p == NULL)
+ goto out;
+ while (numnames) {
+ p = smb_fdata(ndo, p, "Name=[n2]\t#", maxbuf, 0);
+ if (p == NULL)
+ goto out;
+ ND_TCHECK_1(p);
+ if (p >= maxbuf)
+ goto out;
+ if (GET_U_1(p) & 0x80)
+ ND_PRINT("<GROUP> ");
+ switch (GET_U_1(p) & 0x60) {
+ case 0x00: ND_PRINT("B "); break;
+ case 0x20: ND_PRINT("P "); break;
+ case 0x40: ND_PRINT("M "); break;
+ case 0x60: ND_PRINT("_ "); break;
+ }
+ if (GET_U_1(p) & 0x10)
+ ND_PRINT("<DEREGISTERING> ");
+ if (GET_U_1(p) & 0x08)
+ ND_PRINT("<CONFLICT> ");
+ if (GET_U_1(p) & 0x04)
+ ND_PRINT("<ACTIVE> ");
+ if (GET_U_1(p) & 0x02)
+ ND_PRINT("<PERMANENT> ");
+ ND_PRINT("\n");
+ p += 2;
+ numnames--;
+ }
+ } else {
+ if (p >= maxbuf)
+ goto out;
+ smb_data_print(ndo, p, ND_MIN(rdlen, length - ND_BYTES_BETWEEN(p, data)));
+ p += rdlen;
+ }
+ }
+ }
+ }
+
+ if (p < maxbuf)
+ smb_fdata(ndo, p, "AdditionalData:\n", maxbuf, 0);
+
+out:
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+/*
+ * Print an SMB-over-TCP packet received across tcp on port 445
+ */
+void
+smb_tcp_print(netdissect_options *ndo,
+ const u_char * data, u_int length)
+{
+ u_int caplen;
+ u_int smb_len;
+ const u_char *maxbuf;
+
+ ndo->ndo_protocol = "smb_tcp";
+ if (length < 4)
+ goto trunc;
+ if (ndo->ndo_snapend < data)
+ goto trunc;
+ caplen = ND_BYTES_AVAILABLE_AFTER(data);
+ if (caplen < 4)
+ goto trunc;
+ maxbuf = data + caplen;
+ smb_len = GET_BE_U_3(data + 1);
+ length -= 4;
+ caplen -= 4;
+
+ startbuf = data;
+ data += 4;
+
+ if (smb_len >= 4 && caplen >= 4 && memcmp(data,"\377SMB",4) == 0) {
+ if (smb_len > caplen) {
+ if (smb_len > length)
+ ND_PRINT(" WARNING: Packet is continued in later TCP segments\n");
+ else
+ ND_PRINT(" WARNING: Short packet. Try increasing the snap length by %u\n",
+ smb_len - caplen);
+ } else
+ ND_PRINT(" ");
+ print_smb(ndo, data, maxbuf > data + smb_len ? data + smb_len : maxbuf);
+ } else
+ ND_PRINT(" SMB-over-TCP packet:(raw data or continuation?)\n");
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
+
+/*
+ * print a NBT packet received across udp on port 138
+ */
+void
+nbt_udp138_print(netdissect_options *ndo,
+ const u_char *data, u_int length)
+{
+ const u_char *maxbuf = data + length;
+
+ ndo->ndo_protocol = "nbt_udp138";
+ if (maxbuf > ndo->ndo_snapend)
+ maxbuf = ndo->ndo_snapend;
+ if (maxbuf <= data)
+ return;
+ startbuf = data;
+
+ if (ndo->ndo_vflag < 2) {
+ ND_PRINT("NBT UDP PACKET(138)");
+ return;
+ }
+
+ data = smb_fdata(ndo, data,
+ "\n>>> NBT UDP PACKET(138) Res=[rw] ID=[rw] IP=[b.b.b.b] Port=[ru] Length=[ru] Res2=[rw]\nSourceName=[n1]\nDestName=[n1]\n#",
+ maxbuf, 0);
+
+ if (data != NULL) {
+ /* If there isn't enough data for "\377SMB", don't check for it. */
+ if ((data + 3) >= maxbuf)
+ goto out;
+
+ if (memcmp(data, "\377SMB",4) == 0)
+ print_smb(ndo, data, maxbuf);
+ }
+out:
+ return;
+}
+
+
+/*
+ print netbeui frames
+*/
+static struct nbf_strings {
+ const char *name;
+ const char *nonverbose;
+ const char *verbose;
+} nbf_strings[0x20] = {
+ { "Add Group Name Query", ", [P23]Name to add=[n2]#",
+ "[P5]ResponseCorrelator=[w]\n[P16]Name to add=[n2]\n" },
+ { "Add Name Query", ", [P23]Name to add=[n2]#",
+ "[P5]ResponseCorrelator=[w]\n[P16]Name to add=[n2]\n" },
+ { "Name In Conflict", NULL, NULL },
+ { "Status Query", NULL, NULL },
+ { NULL, NULL, NULL }, /* not used */
+ { NULL, NULL, NULL }, /* not used */
+ { NULL, NULL, NULL }, /* not used */
+ { "Terminate Trace", NULL, NULL },
+ { "Datagram", NULL,
+ "[P7]Destination=[n2]\nSource=[n2]\n" },
+ { "Broadcast Datagram", NULL,
+ "[P7]Destination=[n2]\nSource=[n2]\n" },
+ { "Name Query", ", [P7]Name=[n2]#",
+ "[P1]SessionNumber=[B]\nNameType=[B][P2]\nResponseCorrelator=[w]\nName=[n2]\nName of sender=[n2]\n" },
+ { NULL, NULL, NULL }, /* not used */
+ { NULL, NULL, NULL }, /* not used */
+ { "Add Name Response", ", [P1]GroupName=[w] [P4]Destination=[n2] Source=[n2]#",
+ "AddNameInProcess=[B]\nGroupName=[w]\nTransmitCorrelator=[w][P2]\nDestination=[n2]\nSource=[n2]\n" },
+ { "Name Recognized", NULL,
+ "[P1]Data2=[w]\nTransmitCorrelator=[w]\nResponseCorelator=[w]\nDestination=[n2]\nSource=[n2]\n" },
+ { "Status Response", NULL, NULL },
+ { NULL, NULL, NULL }, /* not used */
+ { NULL, NULL, NULL }, /* not used */
+ { NULL, NULL, NULL }, /* not used */
+ { "Terminate Trace", NULL, NULL },
+ { "Data Ack", NULL,
+ "[P3]TransmitCorrelator=[w][P2]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { "Data First/Middle", NULL,
+ "Flags=[{RECEIVE_CONTINUE|NO_ACK||PIGGYBACK_ACK_INCLUDED|}]\nResyncIndicator=[w][P2]\nResponseCorelator=[w]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { "Data Only/Last", NULL,
+ "Flags=[{|NO_ACK|PIGGYBACK_ACK_ALLOWED|PIGGYBACK_ACK_INCLUDED|}]\nResyncIndicator=[w][P2]\nResponseCorelator=[w]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { "Session Confirm", NULL,
+ "Data1=[B]\nData2=[w]\nTransmitCorrelator=[w]\nResponseCorelator=[w]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { "Session End", NULL,
+ "[P1]Data2=[w][P4]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { "Session Initialize", NULL,
+ "Data1=[B]\nData2=[w]\nTransmitCorrelator=[w]\nResponseCorelator=[w]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { "No Receive", NULL,
+ "Flags=[{|SEND_NO_ACK}]\nDataBytesAccepted=[b][P4]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { "Receive Outstanding", NULL,
+ "[P1]DataBytesAccepted=[b][P4]\nRemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { "Receive Continue", NULL,
+ "[P2]TransmitCorrelator=[w]\n[P2]RemoteSessionNumber=[B]\nLocalSessionNumber=[B]\n" },
+ { NULL, NULL, NULL }, /* not used */
+ { NULL, NULL, NULL }, /* not used */
+ { "Session Alive", NULL, NULL }
+};
+
+void
+netbeui_print(netdissect_options *ndo,
+ u_short control, const u_char *data, u_int length)
+{
+ const u_char *maxbuf = data + length;
+ u_int len;
+ u_int command;
+ const u_char *data2;
+ int is_truncated = 0;
+
+ ndo->ndo_protocol = "netbeui";
+ if (maxbuf > ndo->ndo_snapend)
+ maxbuf = ndo->ndo_snapend;
+ len = GET_LE_U_2(data);
+ command = GET_U_1(data + 4);
+ data2 = data + len;
+ if (data2 >= maxbuf) {
+ data2 = maxbuf;
+ is_truncated = 1;
+ }
+
+ startbuf = data;
+
+ if (ndo->ndo_vflag < 2) {
+ ND_PRINT("NBF Packet: ");
+ data = smb_fdata(ndo, data, "[P5]#", maxbuf, 0);
+ } else {
+ ND_PRINT("\n>>> NBF Packet\nType=0x%X ", control);
+ data = smb_fdata(ndo, data, "Length=[u] Signature=[w] Command=[B]\n#", maxbuf, 0);
+ }
+ if (data == NULL)
+ goto out;
+
+ if (command > 0x1f || nbf_strings[command].name == NULL) {
+ if (ndo->ndo_vflag < 2)
+ data = smb_fdata(ndo, data, "Unknown NBF Command#", data2, 0);
+ else
+ data = smb_fdata(ndo, data, "Unknown NBF Command\n", data2, 0);
+ } else {
+ if (ndo->ndo_vflag < 2) {
+ ND_PRINT("%s", nbf_strings[command].name);
+ if (nbf_strings[command].nonverbose != NULL)
+ data = smb_fdata(ndo, data, nbf_strings[command].nonverbose, data2, 0);
+ } else {
+ ND_PRINT("%s:\n", nbf_strings[command].name);
+ if (nbf_strings[command].verbose != NULL)
+ data = smb_fdata(ndo, data, nbf_strings[command].verbose, data2, 0);
+ else
+ ND_PRINT("\n");
+ }
+ }
+
+ if (ndo->ndo_vflag < 2)
+ return;
+
+ if (data == NULL)
+ goto out;
+
+ if (is_truncated) {
+ /* data2 was past the end of the buffer */
+ goto out;
+ }
+
+ /* If this isn't a command that would contain an SMB message, quit. */
+ if (command != 0x08 && command != 0x09 && command != 0x15 &&
+ command != 0x16)
+ goto out;
+
+ /* If there isn't enough data for "\377SMB", don't look for it. */
+ if ((data2 + 3) >= maxbuf)
+ goto out;
+
+ if (memcmp(data2, "\377SMB",4) == 0)
+ print_smb(ndo, data2, maxbuf);
+ else {
+ u_int i;
+ for (i = 0; i < 128; i++) {
+ if ((data2 + i + 3) >= maxbuf)
+ break;
+ if (memcmp(data2 + i, "\377SMB", 4) == 0) {
+ ND_PRINT("found SMB packet at %u\n", i);
+ print_smb(ndo, data2 + i, maxbuf);
+ break;
+ }
+ }
+ }
+
+out:
+ return;
+}
+
+
+/*
+ * print IPX-Netbios frames
+ */
+void
+ipx_netbios_print(netdissect_options *ndo,
+ const u_char *data, u_int length)
+{
+ /*
+ * this is a hack till I work out how to parse the rest of the
+ * NetBIOS-over-IPX stuff
+ */
+ u_int i;
+ const u_char *maxbuf;
+
+ ndo->ndo_protocol = "ipx_netbios";
+ maxbuf = data + length;
+ /* Don't go past the end of the captured data in the packet. */
+ if (maxbuf > ndo->ndo_snapend)
+ maxbuf = ndo->ndo_snapend;
+ startbuf = data;
+ for (i = 0; i < 128; i++) {
+ if ((data + i + 4) > maxbuf)
+ break;
+ if (memcmp(data + i, "\377SMB", 4) == 0) {
+ smb_fdata(ndo, data, "\n>>> IPX transport ", data + i, 0);
+ print_smb(ndo, data + i, maxbuf);
+ break;
+ }
+ }
+ if (i == 128)
+ smb_fdata(ndo, data, "\n>>> Unknown IPX ", maxbuf, 0);
+}
diff --git a/print-smtp.c b/print-smtp.c
new file mode 100644
index 0000000..4acf87c
--- /dev/null
+++ b/print-smtp.c
@@ -0,0 +1,29 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Simple Mail Transfer Protocol (SMTP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+void
+smtp_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ ndo->ndo_protocol = "smtp";
+ txtproto_print(ndo, pptr, len, NULL, 0);
+}
diff --git a/print-snmp.c b/print-snmp.c
new file mode 100644
index 0000000..a38fee2
--- /dev/null
+++ b/print-snmp.c
@@ -0,0 +1,1932 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * John Robert LoVerso. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * This implementation has been influenced by the CMU SNMP release,
+ * by Steve Waldbusser. However, this shares no code with that system.
+ * Additional ASN.1 insight gained from Marshall T. Rose's _The_Open_Book_.
+ * Earlier forms of this implementation were derived and/or inspired by an
+ * awk script originally written by C. Philip Wood of LANL (but later
+ * heavily modified by John Robert LoVerso). The copyright notice for
+ * that work is preserved below, even though it may not rightly apply
+ * to this file.
+ *
+ * Support for SNMPv2c/SNMPv3 and the ability to link the module against
+ * the libsmi was added by J. Schoenwaelder, Copyright (c) 1999.
+ *
+ * This started out as a very simple program, but the incremental decoding
+ * (into the BE structure) complicated things.
+ *
+ # Los Alamos National Laboratory
+ #
+ # Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ # This software was produced under a U.S. Government contract
+ # (W-7405-ENG-36) by Los Alamos National Laboratory, which is
+ # operated by the University of California for the U.S. Department
+ # of Energy. The U.S. Government is licensed to use, reproduce,
+ # and distribute this software. Permission is granted to the
+ # public to copy and use this software without charge, provided
+ # that this Notice and any statement of authorship are reproduced
+ # on all copies. Neither the Government nor the University makes
+ # any warranty, express or implied, or assumes any liability or
+ # responsibility for the use of this software.
+ # @(#)snmp.awk.x 1.1 (LANL) 1/15/90
+ */
+
+/* \summary: Simple Network Management Protocol (SNMP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#ifdef USE_LIBSMI
+#include <smi.h>
+#endif
+
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+#undef OPAQUE /* defined in <wingdi.h> */
+
+
+/*
+ * Universal ASN.1 types
+ * (we only care about the tag values for those allowed in the Internet SMI)
+ */
+static const char *Universal[] = {
+ "U-0",
+ "Boolean",
+ "Integer",
+#define INTEGER 2
+ "Bitstring",
+ "String",
+#define STRING 4
+ "Null",
+#define ASN_NULL 5
+ "ObjID",
+#define OBJECTID 6
+ "ObjectDes",
+ "U-8","U-9","U-10","U-11", /* 8-11 */
+ "U-12","U-13","U-14","U-15", /* 12-15 */
+ "Sequence",
+#define SEQUENCE 16
+ "Set"
+};
+
+/*
+ * Application-wide ASN.1 types from the Internet SMI and their tags
+ */
+static const char *Application[] = {
+ "IpAddress",
+#define IPADDR 0
+ "Counter",
+#define COUNTER 1
+ "Gauge",
+#define GAUGE 2
+ "TimeTicks",
+#define TIMETICKS 3
+ "Opaque",
+#define OPAQUE 4
+ "C-5",
+ "Counter64"
+#define COUNTER64 6
+};
+
+/*
+ * Context-specific ASN.1 types for the SNMP PDUs and their tags
+ */
+static const char *Context[] = {
+ "GetRequest",
+#define GETREQ 0
+ "GetNextRequest",
+#define GETNEXTREQ 1
+ "GetResponse",
+#define GETRESP 2
+ "SetRequest",
+#define SETREQ 3
+ "Trap",
+#define TRAP 4
+ "GetBulk",
+#define GETBULKREQ 5
+ "Inform",
+#define INFORMREQ 6
+ "V2Trap",
+#define V2TRAP 7
+ "Report"
+#define REPORT 8
+};
+
+#define NOTIFY_CLASS(x) (x == TRAP || x == V2TRAP || x == INFORMREQ)
+#define READ_CLASS(x) (x == GETREQ || x == GETNEXTREQ || x == GETBULKREQ)
+#define WRITE_CLASS(x) (x == SETREQ)
+#define RESPONSE_CLASS(x) (x == GETRESP)
+#define INTERNAL_CLASS(x) (x == REPORT)
+
+/*
+ * Context-specific ASN.1 types for the SNMP Exceptions and their tags
+ */
+static const char *Exceptions[] = {
+ "noSuchObject",
+#define NOSUCHOBJECT 0
+ "noSuchInstance",
+#define NOSUCHINSTANCE 1
+ "endOfMibView",
+#define ENDOFMIBVIEW 2
+};
+
+/*
+ * Private ASN.1 types
+ * The Internet SMI does not specify any
+ */
+static const char *Private[] = {
+ "P-0"
+};
+
+/*
+ * error-status values for any SNMP PDU
+ */
+static const char *ErrorStatus[] = {
+ "noError",
+ "tooBig",
+ "noSuchName",
+ "badValue",
+ "readOnly",
+ "genErr",
+ "noAccess",
+ "wrongType",
+ "wrongLength",
+ "wrongEncoding",
+ "wrongValue",
+ "noCreation",
+ "inconsistentValue",
+ "resourceUnavailable",
+ "commitFailed",
+ "undoFailed",
+ "authorizationError",
+ "notWritable",
+ "inconsistentName"
+};
+#define DECODE_ErrorStatus(e) \
+ ( e >= 0 && (size_t)e < sizeof(ErrorStatus)/sizeof(ErrorStatus[0]) \
+ ? ErrorStatus[e] \
+ : (snprintf(errbuf, sizeof(errbuf), "err=%u", e), errbuf))
+
+/*
+ * generic-trap values in the SNMP Trap-PDU
+ */
+static const char *GenericTrap[] = {
+ "coldStart",
+ "warmStart",
+ "linkDown",
+ "linkUp",
+ "authenticationFailure",
+ "egpNeighborLoss",
+ "enterpriseSpecific"
+#define GT_ENTERPRISE 6
+};
+#define DECODE_GenericTrap(t) \
+ ( t >= 0 && (size_t)t < sizeof(GenericTrap)/sizeof(GenericTrap[0]) \
+ ? GenericTrap[t] \
+ : (snprintf(buf, sizeof(buf), "gt=%d", t), buf))
+
+/*
+ * ASN.1 type class table
+ * Ties together the preceding Universal, Application, Context, and Private
+ * type definitions.
+ */
+#define defineCLASS(x) { "x", x, sizeof(x)/sizeof(x[0]) } /* not ANSI-C */
+static const struct {
+ const char *name;
+ const char **Id;
+ int numIDs;
+ } Class[] = {
+ defineCLASS(Universal),
+#define UNIVERSAL 0
+ defineCLASS(Application),
+#define APPLICATION 1
+ defineCLASS(Context),
+#define CONTEXT 2
+ defineCLASS(Private),
+#define PRIVATE 3
+ defineCLASS(Exceptions),
+#define EXCEPTIONS 4
+};
+
+/*
+ * defined forms for ASN.1 types
+ */
+static const char *Form[] = {
+ "Primitive",
+#define PRIMITIVE 0
+ "Constructed",
+#define CONSTRUCTED 1
+};
+
+/*
+ * A structure for the OID tree for the compiled-in MIB.
+ * This is stored as a general-order tree.
+ */
+static struct obj {
+ const char *desc; /* name of object */
+ u_char oid; /* sub-id following parent */
+ u_char type; /* object type (unused) */
+ struct obj *child, *next; /* child and next sibling pointers */
+} *objp = NULL;
+
+/*
+ * Include the compiled in SNMP MIB. "mib.h" is produced by feeding
+ * RFC-1156 format files into "makemib". "mib.h" MUST define at least
+ * a value for `mibroot'.
+ *
+ * In particular, this is gross, as this is including initialized structures,
+ * and by right shouldn't be an "include" file.
+ */
+#include "mib.h"
+
+/*
+ * This defines a list of OIDs which will be abbreviated on output.
+ * Currently, this includes the prefixes for the Internet MIB, the
+ * private enterprises tree, and the experimental tree.
+ */
+#define OID_FIRST_OCTET(x, y) (((x)*40) + (y)) /* X.690 8.19.4 */
+
+#ifndef NO_ABREV_MIB
+static const uint8_t mib_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 2, 1 };
+#endif
+#ifndef NO_ABREV_ENTER
+static const uint8_t enterprises_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 4, 1 };
+#endif
+#ifndef NO_ABREV_EXPERI
+static const uint8_t experimental_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 3 };
+#endif
+#ifndef NO_ABBREV_SNMPMODS
+static const uint8_t snmpModules_oid[] = { OID_FIRST_OCTET(1, 3), 6, 1, 6, 3 };
+#endif
+
+#define OBJ_ABBREV_ENTRY(prefix, obj) \
+ { prefix, &_ ## obj ## _obj, obj ## _oid, sizeof (obj ## _oid) }
+static const struct obj_abrev {
+ const char *prefix; /* prefix for this abrev */
+ struct obj *node; /* pointer into object table */
+ const uint8_t *oid; /* ASN.1 encoded OID */
+ size_t oid_len; /* length of OID */
+} obj_abrev_list[] = {
+#ifndef NO_ABREV_MIB
+ /* .iso.org.dod.internet.mgmt.mib */
+ OBJ_ABBREV_ENTRY("", mib),
+#endif
+#ifndef NO_ABREV_ENTER
+ /* .iso.org.dod.internet.private.enterprises */
+ OBJ_ABBREV_ENTRY("E:", enterprises),
+#endif
+#ifndef NO_ABREV_EXPERI
+ /* .iso.org.dod.internet.experimental */
+ OBJ_ABBREV_ENTRY("X:", experimental),
+#endif
+#ifndef NO_ABBREV_SNMPMODS
+ /* .iso.org.dod.internet.snmpV2.snmpModules */
+ OBJ_ABBREV_ENTRY("S:", snmpModules),
+#endif
+ { 0,0,0,0 }
+};
+
+/*
+ * This is used in the OID print routine to walk down the object tree
+ * rooted at `mibroot'.
+ */
+#define OBJ_PRINT(o, suppressdot) \
+{ \
+ if (objp) { \
+ do { \
+ if ((o) == objp->oid) \
+ break; \
+ } while ((objp = objp->next) != NULL); \
+ } \
+ if (objp) { \
+ ND_PRINT(suppressdot?"%s":".%s", objp->desc); \
+ objp = objp->child; \
+ } else \
+ ND_PRINT(suppressdot?"%u":".%u", (o)); \
+}
+
+/*
+ * This is the definition for the Any-Data-Type storage used purely for
+ * temporary internal representation while decoding an ASN.1 data stream.
+ */
+struct be {
+ uint32_t asnlen;
+ union {
+ const uint8_t *raw;
+ int32_t integer;
+ uint32_t uns;
+ const u_char *str;
+ uint64_t uns64;
+ } data;
+ u_short id;
+ u_char form, class; /* tag info */
+ u_char type;
+#define BE_ANY 255
+#define BE_NONE 0
+#define BE_NULL 1
+#define BE_OCTET 2
+#define BE_OID 3
+#define BE_INT 4
+#define BE_UNS 5
+#define BE_STR 6
+#define BE_SEQ 7
+#define BE_INETADDR 8
+#define BE_PDU 9
+#define BE_UNS64 10
+#define BE_NOSUCHOBJECT 128
+#define BE_NOSUCHINST 129
+#define BE_ENDOFMIBVIEW 130
+};
+
+/*
+ * SNMP versions recognized by this module
+ */
+static const char *SnmpVersion[] = {
+ "SNMPv1",
+#define SNMP_VERSION_1 0
+ "SNMPv2c",
+#define SNMP_VERSION_2 1
+ "SNMPv2u",
+#define SNMP_VERSION_2U 2
+ "SNMPv3"
+#define SNMP_VERSION_3 3
+};
+
+/*
+ * Defaults for SNMP PDU components
+ */
+#define DEF_COMMUNITY "public"
+
+/*
+ * constants for ASN.1 decoding
+ */
+#define OIDMUX 40
+#define ASNLEN_INETADDR 4
+#define ASN_SHIFT7 7
+#define ASN_SHIFT8 8
+#define ASN_BIT8 0x80
+#define ASN_LONGLEN 0x80
+
+#define ASN_ID_BITS 0x1f
+#define ASN_FORM_BITS 0x20
+#define ASN_FORM_SHIFT 5
+#define ASN_CLASS_BITS 0xc0
+#define ASN_CLASS_SHIFT 6
+
+#define ASN_ID_EXT 0x1f /* extension ID in tag field */
+
+/*
+ * This decodes the next ASN.1 object in the stream pointed to by "p"
+ * (and of real-length "len") and stores the intermediate data in the
+ * provided BE object.
+ *
+ * This returns -l if it fails (i.e., the ASN.1 stream is not valid).
+ * O/w, this returns the number of bytes parsed from "p".
+ */
+static int
+asn1_parse(netdissect_options *ndo,
+ const u_char *p, u_int len, struct be *elem)
+{
+ u_char form, class, id;
+ u_int i, hdr;
+
+ elem->asnlen = 0;
+ elem->type = BE_ANY;
+ if (len < 1) {
+ ND_PRINT("[nothing to parse]");
+ return -1;
+ }
+
+ /*
+ * it would be nice to use a bit field, but you can't depend on them.
+ * +---+---+---+---+---+---+---+---+
+ * + class |frm| id |
+ * +---+---+---+---+---+---+---+---+
+ * 7 6 5 4 3 2 1 0
+ */
+ id = GET_U_1(p) & ASN_ID_BITS; /* lower 5 bits, range 00-1f */
+#ifdef notdef
+ form = (GET_U_1(p) & 0xe0) >> 5; /* move upper 3 bits to lower 3 */
+ class = form >> 1; /* bits 7&6 -> bits 1&0, range 0-3 */
+ form &= 0x1; /* bit 5 -> bit 0, range 0-1 */
+#else
+ form = (u_char)(GET_U_1(p) & ASN_FORM_BITS) >> ASN_FORM_SHIFT;
+ class = (u_char)(GET_U_1(p) & ASN_CLASS_BITS) >> ASN_CLASS_SHIFT;
+#endif
+ elem->form = form;
+ elem->class = class;
+ elem->id = id;
+ p++; len--; hdr = 1;
+ /* extended tag field */
+ if (id == ASN_ID_EXT) {
+ /*
+ * The ID follows, as a sequence of octets with the
+ * 8th bit set and the remaining 7 bits being
+ * the next 7 bits of the value, terminated with
+ * an octet with the 8th bit not set.
+ *
+ * First, assemble all the octets with the 8th
+ * bit set. XXX - this doesn't handle a value
+ * that won't fit in 32 bits.
+ */
+ id = 0;
+ while (GET_U_1(p) & ASN_BIT8) {
+ if (len < 1) {
+ ND_PRINT("[Xtagfield?]");
+ return -1;
+ }
+ id = (id << 7) | (GET_U_1(p) & ~ASN_BIT8);
+ len--;
+ hdr++;
+ p++;
+ }
+ if (len < 1) {
+ ND_PRINT("[Xtagfield?]");
+ return -1;
+ }
+ elem->id = id = (id << 7) | GET_U_1(p);
+ --len;
+ ++hdr;
+ ++p;
+ }
+ if (len < 1) {
+ ND_PRINT("[no asnlen]");
+ return -1;
+ }
+ elem->asnlen = GET_U_1(p);
+ p++; len--; hdr++;
+ if (elem->asnlen & ASN_BIT8) {
+ uint32_t noct = elem->asnlen % ASN_BIT8;
+ elem->asnlen = 0;
+ if (len < noct) {
+ ND_PRINT("[asnlen? %d<%d]", len, noct);
+ return -1;
+ }
+ ND_TCHECK_LEN(p, noct);
+ for (; noct != 0; len--, hdr++, noct--) {
+ elem->asnlen = (elem->asnlen << ASN_SHIFT8) | GET_U_1(p);
+ p++;
+ }
+ }
+ if (len < elem->asnlen) {
+ ND_PRINT("[len%d<asnlen%u]", len, elem->asnlen);
+ return -1;
+ }
+ if (form >= sizeof(Form)/sizeof(Form[0])) {
+ ND_PRINT("[form?%d]", form);
+ return -1;
+ }
+ if (class >= sizeof(Class)/sizeof(Class[0])) {
+ ND_PRINT("[class?%c/%d]", *Form[form], class);
+ return -1;
+ }
+ if ((int)id >= Class[class].numIDs) {
+ ND_PRINT("[id?%c/%s/%d]", *Form[form], Class[class].name, id);
+ return -1;
+ }
+ ND_TCHECK_LEN(p, elem->asnlen);
+
+ switch (form) {
+ case PRIMITIVE:
+ switch (class) {
+ case UNIVERSAL:
+ switch (id) {
+ case STRING:
+ elem->type = BE_STR;
+ elem->data.str = p;
+ break;
+
+ case INTEGER: {
+ int32_t data;
+ elem->type = BE_INT;
+ data = 0;
+
+ if (elem->asnlen == 0) {
+ ND_PRINT("[asnlen=0]");
+ return -1;
+ }
+ if (GET_U_1(p) & ASN_BIT8) /* negative */
+ data = -1;
+ for (i = elem->asnlen; i != 0; p++, i--)
+ data = (data << ASN_SHIFT8) | GET_U_1(p);
+ elem->data.integer = data;
+ break;
+ }
+
+ case OBJECTID:
+ elem->type = BE_OID;
+ elem->data.raw = (const uint8_t *)p;
+ break;
+
+ case ASN_NULL:
+ elem->type = BE_NULL;
+ elem->data.raw = NULL;
+ break;
+
+ default:
+ elem->type = BE_OCTET;
+ elem->data.raw = (const uint8_t *)p;
+ ND_PRINT("[P/U/%s]", Class[class].Id[id]);
+ break;
+ }
+ break;
+
+ case APPLICATION:
+ switch (id) {
+ case IPADDR:
+ elem->type = BE_INETADDR;
+ elem->data.raw = (const uint8_t *)p;
+ break;
+
+ case COUNTER:
+ case GAUGE:
+ case TIMETICKS: {
+ uint32_t data;
+ elem->type = BE_UNS;
+ data = 0;
+ for (i = elem->asnlen; i != 0; p++, i--)
+ data = (data << 8) + GET_U_1(p);
+ elem->data.uns = data;
+ break;
+ }
+
+ case COUNTER64: {
+ uint64_t data64;
+ elem->type = BE_UNS64;
+ data64 = 0;
+ for (i = elem->asnlen; i != 0; p++, i--)
+ data64 = (data64 << 8) + GET_U_1(p);
+ elem->data.uns64 = data64;
+ break;
+ }
+
+ default:
+ elem->type = BE_OCTET;
+ elem->data.raw = (const uint8_t *)p;
+ ND_PRINT("[P/A/%s]",
+ Class[class].Id[id]);
+ break;
+ }
+ break;
+
+ case CONTEXT:
+ switch (id) {
+ case NOSUCHOBJECT:
+ elem->type = BE_NOSUCHOBJECT;
+ elem->data.raw = NULL;
+ break;
+
+ case NOSUCHINSTANCE:
+ elem->type = BE_NOSUCHINST;
+ elem->data.raw = NULL;
+ break;
+
+ case ENDOFMIBVIEW:
+ elem->type = BE_ENDOFMIBVIEW;
+ elem->data.raw = NULL;
+ break;
+ }
+ break;
+
+ default:
+ ND_PRINT("[P/%s/%s]", Class[class].name, Class[class].Id[id]);
+ elem->type = BE_OCTET;
+ elem->data.raw = (const uint8_t *)p;
+ break;
+ }
+ break;
+
+ case CONSTRUCTED:
+ switch (class) {
+ case UNIVERSAL:
+ switch (id) {
+ case SEQUENCE:
+ elem->type = BE_SEQ;
+ elem->data.raw = (const uint8_t *)p;
+ break;
+
+ default:
+ elem->type = BE_OCTET;
+ elem->data.raw = (const uint8_t *)p;
+ ND_PRINT("C/U/%s", Class[class].Id[id]);
+ break;
+ }
+ break;
+
+ case CONTEXT:
+ elem->type = BE_PDU;
+ elem->data.raw = (const uint8_t *)p;
+ break;
+
+ default:
+ elem->type = BE_OCTET;
+ elem->data.raw = (const uint8_t *)p;
+ ND_PRINT("C/%s/%s", Class[class].name, Class[class].Id[id]);
+ break;
+ }
+ break;
+ }
+ p += elem->asnlen;
+ len -= elem->asnlen;
+ return elem->asnlen + hdr;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+static int
+asn1_print_octets(netdissect_options *ndo, struct be *elem)
+{
+ const u_char *p = (const u_char *)elem->data.raw;
+ uint32_t asnlen = elem->asnlen;
+ uint32_t i;
+
+ ND_TCHECK_LEN(p, asnlen);
+ for (i = asnlen; i != 0; p++, i--)
+ ND_PRINT("_%.2x", GET_U_1(p));
+ return 0;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+static int
+asn1_print_string(netdissect_options *ndo, struct be *elem)
+{
+ int printable = 1, first = 1;
+ const u_char *p;
+ uint32_t asnlen = elem->asnlen;
+ uint32_t i;
+
+ p = elem->data.str;
+ ND_TCHECK_LEN(p, asnlen);
+ for (i = asnlen; printable && i != 0; p++, i--)
+ printable = ND_ASCII_ISPRINT(GET_U_1(p));
+ p = elem->data.str;
+ if (printable) {
+ ND_PRINT("\"");
+ if (nd_printn(ndo, p, asnlen, ndo->ndo_snapend)) {
+ ND_PRINT("\"");
+ goto trunc;
+ }
+ ND_PRINT("\"");
+ } else {
+ for (i = asnlen; i != 0; p++, i--) {
+ ND_PRINT(first ? "%.2x" : "_%.2x", GET_U_1(p));
+ first = 0;
+ }
+ }
+ return 0;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+/*
+ * Display the ASN.1 object represented by the BE object.
+ * This used to be an integral part of asn1_parse() before the intermediate
+ * BE form was added.
+ */
+static int
+asn1_print(netdissect_options *ndo,
+ struct be *elem)
+{
+ const u_char *p;
+ uint32_t asnlen = elem->asnlen;
+ uint32_t i;
+
+ switch (elem->type) {
+
+ case BE_OCTET:
+ if (asn1_print_octets(ndo, elem) == -1)
+ return -1;
+ break;
+
+ case BE_NULL:
+ break;
+
+ case BE_OID: {
+ int o = 0, first = -1;
+
+ p = (const u_char *)elem->data.raw;
+ i = asnlen;
+ if (!ndo->ndo_nflag && asnlen > 2) {
+ const struct obj_abrev *a = &obj_abrev_list[0];
+ for (; a->node; a++) {
+ if (i < a->oid_len)
+ continue;
+ if (!ND_TTEST_LEN(p, a->oid_len))
+ continue;
+ if (memcmp(a->oid, p, a->oid_len) == 0) {
+ objp = a->node->child;
+ i -= a->oid_len;
+ p += a->oid_len;
+ ND_PRINT("%s", a->prefix);
+ first = 1;
+ break;
+ }
+ }
+ }
+
+ for (; i != 0; p++, i--) {
+ o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
+ if (GET_U_1(p) & ASN_LONGLEN)
+ continue;
+
+ /*
+ * first subitem encodes two items with
+ * 1st*OIDMUX+2nd
+ * (see X.690:1997 clause 8.19 for the details)
+ */
+ if (first < 0) {
+ int s;
+ if (!ndo->ndo_nflag)
+ objp = mibroot;
+ first = 0;
+ s = o / OIDMUX;
+ if (s > 2) s = 2;
+ OBJ_PRINT(s, first);
+ o -= s * OIDMUX;
+ }
+ OBJ_PRINT(o, first);
+ if (--first < 0)
+ first = 0;
+ o = 0;
+ }
+ break;
+ }
+
+ case BE_INT:
+ ND_PRINT("%d", elem->data.integer);
+ break;
+
+ case BE_UNS:
+ ND_PRINT("%u", elem->data.uns);
+ break;
+
+ case BE_UNS64:
+ ND_PRINT("%" PRIu64, elem->data.uns64);
+ break;
+
+ case BE_STR:
+ if (asn1_print_string(ndo, elem) == -1)
+ return -1;
+ break;
+
+ case BE_SEQ:
+ ND_PRINT("Seq(%u)", elem->asnlen);
+ break;
+
+ case BE_INETADDR:
+ if (asnlen != ASNLEN_INETADDR)
+ ND_PRINT("[inetaddr len!=%d]", ASNLEN_INETADDR);
+ p = (const u_char *)elem->data.raw;
+ ND_TCHECK_LEN(p, asnlen);
+ for (i = asnlen; i != 0; p++, i--) {
+ ND_PRINT((i == asnlen) ? "%u" : ".%u", GET_U_1(p));
+ }
+ break;
+
+ case BE_NOSUCHOBJECT:
+ case BE_NOSUCHINST:
+ case BE_ENDOFMIBVIEW:
+ ND_PRINT("[%s]", Class[EXCEPTIONS].Id[elem->id]);
+ break;
+
+ case BE_PDU:
+ ND_PRINT("%s(%u)", Class[CONTEXT].Id[elem->id], elem->asnlen);
+ break;
+
+ case BE_ANY:
+ ND_PRINT("[BE_ANY!?]");
+ break;
+
+ default:
+ ND_PRINT("[be!?]");
+ break;
+ }
+ return 0;
+
+trunc:
+ nd_print_trunc(ndo);
+ return -1;
+}
+
+#ifdef notdef
+/*
+ * This is a brute force ASN.1 printer: recurses to dump an entire structure.
+ * This will work for any ASN.1 stream, not just an SNMP PDU.
+ *
+ * By adding newlines and spaces at the correct places, this would print in
+ * Rose-Normal-Form.
+ *
+ * This is not currently used.
+ */
+static void
+asn1_decode(u_char *p, u_int length)
+{
+ struct be elem;
+ int i = 0;
+
+ while (i >= 0 && length > 0) {
+ i = asn1_parse(ndo, p, length, &elem);
+ if (i >= 0) {
+ ND_PRINT(" ");
+ if (asn1_print(ndo, &elem) < 0)
+ return;
+ if (elem.type == BE_SEQ || elem.type == BE_PDU) {
+ ND_PRINT(" {");
+ asn1_decode(elem.data.raw, elem.asnlen);
+ ND_PRINT(" }");
+ }
+ length -= i;
+ p += i;
+ }
+ }
+}
+#endif
+
+#ifdef USE_LIBSMI
+
+struct smi2be {
+ SmiBasetype basetype;
+ int be;
+};
+
+static const struct smi2be smi2betab[] = {
+ { SMI_BASETYPE_INTEGER32, BE_INT },
+ { SMI_BASETYPE_OCTETSTRING, BE_STR },
+ { SMI_BASETYPE_OCTETSTRING, BE_INETADDR },
+ { SMI_BASETYPE_OBJECTIDENTIFIER, BE_OID },
+ { SMI_BASETYPE_UNSIGNED32, BE_UNS },
+ { SMI_BASETYPE_INTEGER64, BE_NONE },
+ { SMI_BASETYPE_UNSIGNED64, BE_UNS64 },
+ { SMI_BASETYPE_FLOAT32, BE_NONE },
+ { SMI_BASETYPE_FLOAT64, BE_NONE },
+ { SMI_BASETYPE_FLOAT128, BE_NONE },
+ { SMI_BASETYPE_ENUM, BE_INT },
+ { SMI_BASETYPE_BITS, BE_STR },
+ { SMI_BASETYPE_UNKNOWN, BE_NONE }
+};
+
+static int
+smi_decode_oid(netdissect_options *ndo,
+ struct be *elem, unsigned int *oid,
+ unsigned int oidsize, unsigned int *oidlen)
+{
+ const u_char *p = (const u_char *)elem->data.raw;
+ uint32_t asnlen = elem->asnlen;
+ uint32_t i = asnlen;
+ int o = 0, first = -1;
+ unsigned int firstval;
+
+ for (*oidlen = 0; i != 0; p++, i--) {
+ o = (o << ASN_SHIFT7) + (GET_U_1(p) & ~ASN_BIT8);
+ if (GET_U_1(p) & ASN_LONGLEN)
+ continue;
+
+ /*
+ * first subitem encodes two items with 1st*OIDMUX+2nd
+ * (see X.690:1997 clause 8.19 for the details)
+ */
+ if (first < 0) {
+ first = 0;
+ firstval = o / OIDMUX;
+ if (firstval > 2) firstval = 2;
+ o -= firstval * OIDMUX;
+ if (*oidlen < oidsize) {
+ oid[(*oidlen)++] = firstval;
+ }
+ }
+ if (*oidlen < oidsize) {
+ oid[(*oidlen)++] = o;
+ }
+ o = 0;
+ }
+ return 0;
+}
+
+static int smi_check_type(SmiBasetype basetype, int be)
+{
+ int i;
+
+ for (i = 0; smi2betab[i].basetype != SMI_BASETYPE_UNKNOWN; i++) {
+ if (smi2betab[i].basetype == basetype && smi2betab[i].be == be) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int smi_check_a_range(SmiType *smiType, SmiRange *smiRange,
+ struct be *elem)
+{
+ int ok = 1;
+
+ switch (smiType->basetype) {
+ case SMI_BASETYPE_OBJECTIDENTIFIER:
+ case SMI_BASETYPE_OCTETSTRING:
+ if (smiRange->minValue.value.unsigned32
+ == smiRange->maxValue.value.unsigned32) {
+ ok = (elem->asnlen == smiRange->minValue.value.unsigned32);
+ } else {
+ ok = (elem->asnlen >= smiRange->minValue.value.unsigned32
+ && elem->asnlen <= smiRange->maxValue.value.unsigned32);
+ }
+ break;
+
+ case SMI_BASETYPE_INTEGER32:
+ ok = (elem->data.integer >= smiRange->minValue.value.integer32
+ && elem->data.integer <= smiRange->maxValue.value.integer32);
+ break;
+
+ case SMI_BASETYPE_UNSIGNED32:
+ ok = (elem->data.uns >= smiRange->minValue.value.unsigned32
+ && elem->data.uns <= smiRange->maxValue.value.unsigned32);
+ break;
+
+ case SMI_BASETYPE_UNSIGNED64:
+ /* XXX */
+ break;
+
+ /* case SMI_BASETYPE_INTEGER64: SMIng */
+ /* case SMI_BASETYPE_FLOAT32: SMIng */
+ /* case SMI_BASETYPE_FLOAT64: SMIng */
+ /* case SMI_BASETYPE_FLOAT128: SMIng */
+
+ case SMI_BASETYPE_ENUM:
+ case SMI_BASETYPE_BITS:
+ case SMI_BASETYPE_UNKNOWN:
+ ok = 1;
+ break;
+
+ default:
+ ok = 0;
+ break;
+ }
+
+ return ok;
+}
+
+static int smi_check_range(SmiType *smiType, struct be *elem)
+{
+ SmiRange *smiRange;
+ int ok = 1;
+
+ for (smiRange = smiGetFirstRange(smiType);
+ smiRange;
+ smiRange = smiGetNextRange(smiRange)) {
+
+ ok = smi_check_a_range(smiType, smiRange, elem);
+
+ if (ok) {
+ break;
+ }
+ }
+
+ if (ok) {
+ SmiType *parentType;
+ parentType = smiGetParentType(smiType);
+ if (parentType) {
+ ok = smi_check_range(parentType, elem);
+ }
+ }
+
+ return ok;
+}
+
+static SmiNode *
+smi_print_variable(netdissect_options *ndo,
+ struct be *elem, int *status)
+{
+ unsigned int oid[128], oidlen;
+ SmiNode *smiNode = NULL;
+ unsigned int i;
+
+ if (!nd_smi_module_loaded) {
+ *status = asn1_print(ndo, elem);
+ return NULL;
+ }
+ *status = smi_decode_oid(ndo, elem, oid, sizeof(oid) / sizeof(unsigned int),
+ &oidlen);
+ if (*status < 0)
+ return NULL;
+ smiNode = smiGetNodeByOID(oidlen, oid);
+ if (! smiNode) {
+ *status = asn1_print(ndo, elem);
+ return NULL;
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
+ }
+ ND_PRINT("%s", smiNode->name);
+ if (smiNode->oidlen < oidlen) {
+ for (i = smiNode->oidlen; i < oidlen; i++) {
+ ND_PRINT(".%u", oid[i]);
+ }
+ }
+ *status = 0;
+ return smiNode;
+}
+
+static int
+smi_print_value(netdissect_options *ndo,
+ SmiNode *smiNode, u_short pduid, struct be *elem)
+{
+ unsigned int i, oid[128], oidlen;
+ SmiType *smiType;
+ SmiNamedNumber *nn;
+ int done = 0;
+
+ if (! smiNode || ! (smiNode->nodekind
+ & (SMI_NODEKIND_SCALAR | SMI_NODEKIND_COLUMN))) {
+ return asn1_print(ndo, elem);
+ }
+
+ if (elem->type == BE_NOSUCHOBJECT
+ || elem->type == BE_NOSUCHINST
+ || elem->type == BE_ENDOFMIBVIEW) {
+ return asn1_print(ndo, elem);
+ }
+
+ if (NOTIFY_CLASS(pduid) && smiNode->access < SMI_ACCESS_NOTIFY) {
+ ND_PRINT("[notNotifyable]");
+ }
+
+ if (READ_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_ONLY) {
+ ND_PRINT("[notReadable]");
+ }
+
+ if (WRITE_CLASS(pduid) && smiNode->access < SMI_ACCESS_READ_WRITE) {
+ ND_PRINT("[notWritable]");
+ }
+
+ if (RESPONSE_CLASS(pduid)
+ && smiNode->access == SMI_ACCESS_NOT_ACCESSIBLE) {
+ ND_PRINT("[noAccess]");
+ }
+
+ smiType = smiGetNodeType(smiNode);
+ if (! smiType) {
+ return asn1_print(ndo, elem);
+ }
+
+ if (! smi_check_type(smiType->basetype, elem->type)) {
+ ND_PRINT("[wrongType]");
+ }
+
+ if (! smi_check_range(smiType, elem)) {
+ ND_PRINT("[outOfRange]");
+ }
+
+ /* resolve bits to named bits */
+
+ /* check whether instance identifier is valid */
+
+ /* apply display hints (integer, octetstring) */
+
+ /* convert instance identifier to index type values */
+
+ switch (elem->type) {
+ case BE_OID:
+ if (smiType->basetype == SMI_BASETYPE_BITS) {
+ /* print bit labels */
+ } else {
+ if (nd_smi_module_loaded &&
+ smi_decode_oid(ndo, elem, oid,
+ sizeof(oid)/sizeof(unsigned int),
+ &oidlen) == 0) {
+ smiNode = smiGetNodeByOID(oidlen, oid);
+ if (smiNode) {
+ if (ndo->ndo_vflag) {
+ ND_PRINT("%s::", smiGetNodeModule(smiNode)->name);
+ }
+ ND_PRINT("%s", smiNode->name);
+ if (smiNode->oidlen < oidlen) {
+ for (i = smiNode->oidlen;
+ i < oidlen; i++) {
+ ND_PRINT(".%u", oid[i]);
+ }
+ }
+ done++;
+ }
+ }
+ }
+ break;
+
+ case BE_INT:
+ if (smiType->basetype == SMI_BASETYPE_ENUM) {
+ for (nn = smiGetFirstNamedNumber(smiType);
+ nn;
+ nn = smiGetNextNamedNumber(nn)) {
+ if (nn->value.value.integer32
+ == elem->data.integer) {
+ ND_PRINT("%s", nn->name);
+ ND_PRINT("(%d)", elem->data.integer);
+ done++;
+ break;
+ }
+ }
+ }
+ break;
+ }
+
+ if (! done) {
+ return asn1_print(ndo, elem);
+ }
+ return 0;
+}
+#endif
+
+/*
+ * General SNMP header
+ * SEQUENCE {
+ * version INTEGER {version-1(0)},
+ * community OCTET STRING,
+ * data ANY -- PDUs
+ * }
+ * PDUs for all but Trap: (see rfc1157 from page 15 on)
+ * SEQUENCE {
+ * request-id INTEGER,
+ * error-status INTEGER,
+ * error-index INTEGER,
+ * varbindlist SEQUENCE OF
+ * SEQUENCE {
+ * name ObjectName,
+ * value ObjectValue
+ * }
+ * }
+ * PDU for Trap:
+ * SEQUENCE {
+ * enterprise OBJECT IDENTIFIER,
+ * agent-addr NetworkAddress,
+ * generic-trap INTEGER,
+ * specific-trap INTEGER,
+ * time-stamp TimeTicks,
+ * varbindlist SEQUENCE OF
+ * SEQUENCE {
+ * name ObjectName,
+ * value ObjectValue
+ * }
+ * }
+ */
+
+/*
+ * Decode SNMP varBind
+ */
+static void
+varbind_print(netdissect_options *ndo,
+ u_short pduid, const u_char *np, u_int length)
+{
+ struct be elem;
+ int count = 0, ind;
+#ifdef USE_LIBSMI
+ SmiNode *smiNode = NULL;
+#endif
+ int status;
+
+ /* Sequence of varBind */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_SEQ) {
+ ND_PRINT("[!SEQ of varbind]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if ((u_int)count < length)
+ ND_PRINT("[%d extra after SEQ of varbind]", length - count);
+ /* descend */
+ length = elem.asnlen;
+ np = (const u_char *)elem.data.raw;
+
+ for (ind = 1; length > 0; ind++) {
+ const u_char *vbend;
+ u_int vblength;
+
+ ND_PRINT(" ");
+
+ /* Sequence */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_SEQ) {
+ ND_PRINT("[!varbind]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ vbend = np + count;
+ vblength = length - count;
+ /* descend */
+ length = elem.asnlen;
+ np = (const u_char *)elem.data.raw;
+
+ /* objName (OID) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_OID) {
+ ND_PRINT("[objName!=OID]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+#ifdef USE_LIBSMI
+ smiNode = smi_print_variable(ndo, &elem, &status);
+#else
+ status = asn1_print(ndo, &elem);
+#endif
+ if (status < 0)
+ return;
+ length -= count;
+ np += count;
+
+ if (pduid != GETREQ && pduid != GETNEXTREQ
+ && pduid != GETBULKREQ)
+ ND_PRINT("=");
+
+ /* objVal (ANY) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (pduid == GETREQ || pduid == GETNEXTREQ
+ || pduid == GETBULKREQ) {
+ if (elem.type != BE_NULL) {
+ ND_PRINT("[objVal!=NULL]");
+ if (asn1_print(ndo, &elem) < 0)
+ return;
+ }
+ } else {
+ if (elem.type != BE_NULL) {
+#ifdef USE_LIBSMI
+ status = smi_print_value(ndo, smiNode, pduid, &elem);
+#else
+ status = asn1_print(ndo, &elem);
+#endif
+ }
+ if (status < 0)
+ return;
+ }
+ length = vblength;
+ np = vbend;
+ }
+}
+
+/*
+ * Decode SNMP PDUs: GetRequest, GetNextRequest, GetResponse, SetRequest,
+ * GetBulk, Inform, V2Trap, and Report
+ */
+static void
+snmppdu_print(netdissect_options *ndo,
+ u_short pduid, const u_char *np, u_int length)
+{
+ struct be elem;
+ int count = 0, error_status;
+
+ /* reqId (Integer) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[reqId!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if (ndo->ndo_vflag)
+ ND_PRINT("R=%d ", elem.data.integer);
+ length -= count;
+ np += count;
+
+ /* errorStatus (Integer) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[errorStatus!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ error_status = 0;
+ if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
+ || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
+ && elem.data.integer != 0) {
+ char errbuf[20];
+ ND_PRINT("[errorStatus(%s)!=0]",
+ DECODE_ErrorStatus(elem.data.integer));
+ } else if (pduid == GETBULKREQ) {
+ ND_PRINT(" N=%d", elem.data.integer);
+ } else if (elem.data.integer != 0) {
+ char errbuf[20];
+ ND_PRINT(" %s", DECODE_ErrorStatus(elem.data.integer));
+ error_status = elem.data.integer;
+ }
+ length -= count;
+ np += count;
+
+ /* errorIndex (Integer) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[errorIndex!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if ((pduid == GETREQ || pduid == GETNEXTREQ || pduid == SETREQ
+ || pduid == INFORMREQ || pduid == V2TRAP || pduid == REPORT)
+ && elem.data.integer != 0)
+ ND_PRINT("[errorIndex(%d)!=0]", elem.data.integer);
+ else if (pduid == GETBULKREQ)
+ ND_PRINT(" M=%d", elem.data.integer);
+ else if (elem.data.integer != 0) {
+ if (!error_status)
+ ND_PRINT("[errorIndex(%d) w/o errorStatus]", elem.data.integer);
+ else
+ ND_PRINT("@%d", elem.data.integer);
+ } else if (error_status) {
+ ND_PRINT("[errorIndex==0]");
+ }
+ length -= count;
+ np += count;
+
+ varbind_print(ndo, pduid, np, length);
+}
+
+/*
+ * Decode SNMP Trap PDU
+ */
+static void
+trappdu_print(netdissect_options *ndo,
+ const u_char *np, u_int length)
+{
+ struct be elem;
+ int count = 0, generic;
+
+ ND_PRINT(" ");
+
+ /* enterprise (oid) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_OID) {
+ ND_PRINT("[enterprise!=OID]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if (asn1_print(ndo, &elem) < 0)
+ return;
+ length -= count;
+ np += count;
+
+ ND_PRINT(" ");
+
+ /* agent-addr (inetaddr) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INETADDR) {
+ ND_PRINT("[agent-addr!=INETADDR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if (asn1_print(ndo, &elem) < 0)
+ return;
+ length -= count;
+ np += count;
+
+ /* generic-trap (Integer) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[generic-trap!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ generic = elem.data.integer;
+ {
+ char buf[20];
+ ND_PRINT(" %s", DECODE_GenericTrap(generic));
+ }
+ length -= count;
+ np += count;
+
+ /* specific-trap (Integer) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[specific-trap!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if (generic != GT_ENTERPRISE) {
+ if (elem.data.integer != 0)
+ ND_PRINT("[specific-trap(%d)!=0]", elem.data.integer);
+ } else
+ ND_PRINT(" s=%d", elem.data.integer);
+ length -= count;
+ np += count;
+
+ ND_PRINT(" ");
+
+ /* time-stamp (TimeTicks) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_UNS) { /* XXX */
+ ND_PRINT("[time-stamp!=TIMETICKS]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if (asn1_print(ndo, &elem) < 0)
+ return;
+ length -= count;
+ np += count;
+
+ varbind_print(ndo, TRAP, np, length);
+}
+
+/*
+ * Decode arbitrary SNMP PDUs.
+ */
+static void
+pdu_print(netdissect_options *ndo,
+ const u_char *np, u_int length, int version)
+{
+ struct be pdu;
+ int count = 0;
+
+ /* PDU (Context) */
+ if ((count = asn1_parse(ndo, np, length, &pdu)) < 0)
+ return;
+ if (pdu.type != BE_PDU) {
+ ND_PRINT("[no PDU]");
+ return;
+ }
+ if ((u_int)count < length)
+ ND_PRINT("[%d extra after PDU]", length - count);
+ if (ndo->ndo_vflag) {
+ ND_PRINT("{ ");
+ }
+ if (asn1_print(ndo, &pdu) < 0)
+ return;
+ ND_PRINT(" ");
+ /* descend into PDU */
+ length = pdu.asnlen;
+ np = (const u_char *)pdu.data.raw;
+
+ if (version == SNMP_VERSION_1 &&
+ (pdu.id == GETBULKREQ || pdu.id == INFORMREQ ||
+ pdu.id == V2TRAP || pdu.id == REPORT)) {
+ ND_PRINT("[v2 PDU in v1 message]");
+ return;
+ }
+
+ if (version == SNMP_VERSION_2 && pdu.id == TRAP) {
+ ND_PRINT("[v1 PDU in v2 message]");
+ return;
+ }
+
+ switch (pdu.id) {
+ case TRAP:
+ trappdu_print(ndo, np, length);
+ break;
+ case GETREQ:
+ case GETNEXTREQ:
+ case GETRESP:
+ case SETREQ:
+ case GETBULKREQ:
+ case INFORMREQ:
+ case V2TRAP:
+ case REPORT:
+ snmppdu_print(ndo, pdu.id, np, length);
+ break;
+ }
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" } ");
+ }
+}
+
+/*
+ * Decode a scoped SNMP PDU.
+ */
+static void
+scopedpdu_print(netdissect_options *ndo,
+ const u_char *np, u_int length, int version)
+{
+ struct be elem;
+ int count = 0;
+
+ /* Sequence */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_SEQ) {
+ ND_PRINT("[!scoped PDU]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length = elem.asnlen;
+ np = (const u_char *)elem.data.raw;
+
+ /* contextEngineID (OCTET STRING) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[contextEngineID!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ ND_PRINT("E=");
+ if (asn1_print_octets(ndo, &elem) == -1)
+ return;
+ ND_PRINT(" ");
+
+ /* contextName (OCTET STRING) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[contextName!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ ND_PRINT("C=");
+ if (asn1_print_string(ndo, &elem) == -1)
+ return;
+ ND_PRINT(" ");
+
+ pdu_print(ndo, np, length, version);
+}
+
+/*
+ * Decode SNMP Community Header (SNMPv1 and SNMPv2c)
+ */
+static void
+community_print(netdissect_options *ndo,
+ const u_char *np, u_int length, int version)
+{
+ struct be elem;
+ int count = 0;
+
+ /* Community (String) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[comm!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ /* default community */
+ if (!(elem.asnlen == sizeof(DEF_COMMUNITY) - 1 &&
+ strncmp((const char *)elem.data.str, DEF_COMMUNITY,
+ sizeof(DEF_COMMUNITY) - 1) == 0)) {
+ /* ! "public" */
+ ND_PRINT("C=");
+ if (asn1_print_string(ndo, &elem) == -1)
+ return;
+ ND_PRINT(" ");
+ }
+ length -= count;
+ np += count;
+
+ pdu_print(ndo, np, length, version);
+}
+
+/*
+ * Decode SNMPv3 User-based Security Message Header (SNMPv3)
+ */
+static void
+usm_print(netdissect_options *ndo,
+ const u_char *np, u_int length)
+{
+ struct be elem;
+ int count = 0;
+
+ /* Sequence */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_SEQ) {
+ ND_PRINT("[!usm]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length = elem.asnlen;
+ np = (const u_char *)elem.data.raw;
+
+ /* msgAuthoritativeEngineID (OCTET STRING) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[msgAuthoritativeEngineID!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ /* msgAuthoritativeEngineBoots (INTEGER) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[msgAuthoritativeEngineBoots!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if (ndo->ndo_vflag)
+ ND_PRINT("B=%d ", elem.data.integer);
+ length -= count;
+ np += count;
+
+ /* msgAuthoritativeEngineTime (INTEGER) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[msgAuthoritativeEngineTime!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if (ndo->ndo_vflag)
+ ND_PRINT("T=%d ", elem.data.integer);
+ length -= count;
+ np += count;
+
+ /* msgUserName (OCTET STRING) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[msgUserName!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ ND_PRINT("U=");
+ if (asn1_print_string(ndo, &elem) == -1)
+ return;
+ ND_PRINT(" ");
+
+ /* msgAuthenticationParameters (OCTET STRING) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[msgAuthenticationParameters!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ /* msgPrivacyParameters (OCTET STRING) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[msgPrivacyParameters!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ if ((u_int)count < length)
+ ND_PRINT("[%d extra after usm SEQ]", length - count);
+}
+
+/*
+ * Decode SNMPv3 Message Header (SNMPv3)
+ */
+static void
+v3msg_print(netdissect_options *ndo,
+ const u_char *np, u_int length)
+{
+ struct be elem;
+ int count = 0;
+ u_char flags;
+ int model;
+ const u_char *xnp = np;
+ int xlength = length;
+
+ /* Sequence */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_SEQ) {
+ ND_PRINT("[!message]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length = elem.asnlen;
+ np = (const u_char *)elem.data.raw;
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("{ ");
+ }
+
+ /* msgID (INTEGER) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[msgID!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ /* msgMaxSize (INTEGER) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[msgMaxSize!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ /* msgFlags (OCTET STRING) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[msgFlags!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if (elem.asnlen != 1) {
+ ND_PRINT("[msgFlags size %d]", elem.asnlen);
+ return;
+ }
+ flags = GET_U_1(elem.data.str);
+ if (flags != 0x00 && flags != 0x01 && flags != 0x03
+ && flags != 0x04 && flags != 0x05 && flags != 0x07) {
+ ND_PRINT("[msgFlags=0x%02X]", flags);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ ND_PRINT("F=%s%s%s ",
+ flags & 0x01 ? "a" : "",
+ flags & 0x02 ? "p" : "",
+ flags & 0x04 ? "r" : "");
+
+ /* msgSecurityModel (INTEGER) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[msgSecurityModel!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ model = elem.data.integer;
+ length -= count;
+ np += count;
+
+ if ((u_int)count < length)
+ ND_PRINT("[%d extra after message SEQ]", length - count);
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("} ");
+ }
+
+ if (model == 3) {
+ if (ndo->ndo_vflag) {
+ ND_PRINT("{ USM ");
+ }
+ } else {
+ ND_PRINT("[security model %d]", model);
+ return;
+ }
+
+ np = xnp + (np - xnp);
+ length = xlength - (np - xnp);
+
+ /* msgSecurityParameters (OCTET STRING) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_STR) {
+ ND_PRINT("[msgSecurityParameters!=STR]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ length -= count;
+ np += count;
+
+ if (model == 3) {
+ usm_print(ndo, elem.data.str, elem.asnlen);
+ if (ndo->ndo_vflag) {
+ ND_PRINT("} ");
+ }
+ }
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("{ ScopedPDU ");
+ }
+
+ scopedpdu_print(ndo, np, length, 3);
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("} ");
+ }
+}
+
+/*
+ * Decode SNMP header and pass on to PDU printing routines
+ */
+void
+snmp_print(netdissect_options *ndo,
+ const u_char *np, u_int length)
+{
+ struct be elem;
+ int count = 0;
+ int version = 0;
+
+ ndo->ndo_protocol = "snmp";
+ ND_PRINT(" ");
+
+ /* initial Sequence */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_SEQ) {
+ ND_PRINT("[!init SEQ]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+ if ((u_int)count < length)
+ ND_PRINT("[%d extra after iSEQ]", length - count);
+ /* descend */
+ length = elem.asnlen;
+ np = (const u_char *)elem.data.raw;
+
+ /* Version (INTEGER) */
+ if ((count = asn1_parse(ndo, np, length, &elem)) < 0)
+ return;
+ if (elem.type != BE_INT) {
+ ND_PRINT("[version!=INT]");
+ asn1_print(ndo, &elem);
+ return;
+ }
+
+ switch (elem.data.integer) {
+ case SNMP_VERSION_1:
+ case SNMP_VERSION_2:
+ case SNMP_VERSION_3:
+ if (ndo->ndo_vflag)
+ ND_PRINT("{ %s ", SnmpVersion[elem.data.integer]);
+ break;
+ default:
+ ND_PRINT("SNMP [version = %d]", elem.data.integer);
+ return;
+ }
+ version = elem.data.integer;
+ length -= count;
+ np += count;
+
+ switch (version) {
+ case SNMP_VERSION_1:
+ case SNMP_VERSION_2:
+ community_print(ndo, np, length, version);
+ break;
+ case SNMP_VERSION_3:
+ v3msg_print(ndo, np, length);
+ break;
+ default:
+ ND_PRINT("[version = %d]", elem.data.integer);
+ break;
+ }
+
+ if (ndo->ndo_vflag) {
+ ND_PRINT("} ");
+ }
+}
diff --git a/print-someip.c b/print-someip.c
new file mode 100644
index 0000000..210e413
--- /dev/null
+++ b/print-someip.c
@@ -0,0 +1,142 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Francesco Fondelli (francesco dot fondelli, gmail dot com)
+ */
+
+/* \summary: Autosar SOME/IP Protocol printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include "extract.h"
+#include "udp.h"
+
+/*
+ * SOMEIP Header (R19-11)
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Message ID (Service ID/Method ID) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Length |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Request ID (Client ID/Session ID) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Protocol Ver | Interface Ver | Message Type | Return Code |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Payload |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+static const struct tok message_type_values[] = {
+ { 0x00, "REQUEST" },
+ { 0x01, "REQUEST_NO_RETURN" },
+ { 0x02, "NOTIFICATION" },
+ { 0x80, "RESPONSE" },
+ { 0x81, "ERROR" },
+ { 0x20, "TP_REQUEST" },
+ { 0x21, "TP_REQUEST_NO_RETURN" },
+ { 0x22, "TP_NOTIFICATION" },
+ { 0xa0, "TP_RESPONSE" },
+ { 0xa1, "TP_ERROR" },
+ { 0, NULL }
+};
+
+static const struct tok return_code_values[] = {
+ { 0x00, "E_OK" },
+ { 0x01, "E_NOT_OK" },
+ { 0x02, "E_UNKNOWN_SERVICE" },
+ { 0x03, "E_UNKNOWN_METHOD" },
+ { 0x04, "E_NOT_READY" },
+ { 0x05, "E_NOT_REACHABLE" },
+ { 0x06, "E_TIMEOUT" },
+ { 0x07, "E_WRONG_PROTOCOL_VERSION" },
+ { 0x08, "E_WRONG_INTERFACE_VERSION" },
+ { 0x09, "E_MALFORMED_MESSAGE" },
+ { 0x0a, "E_WRONG_MESSAGE_TYPE" },
+ { 0x0b, "E_E2E_REPEATED" },
+ { 0x0c, "E_E2E_WRONG_SEQUENCE" },
+ { 0x0d, "E_E2E" },
+ { 0x0e, "E_E2E_NOT_AVAILABLE" },
+ { 0x0f, "E_E2E_NO_NEW_DATA" },
+ { 0, NULL }
+};
+
+void
+someip_print(netdissect_options *ndo, const u_char *bp, const u_int len)
+{
+ uint32_t message_id;
+ uint16_t service_id;
+ uint16_t method_or_event_id;
+ uint8_t event_flag;
+ uint32_t message_len;
+ uint32_t request_id;
+ uint16_t client_id;
+ uint16_t session_id;
+ uint8_t protocol_version;
+ uint8_t interface_version;
+ uint8_t message_type;
+ uint8_t return_code;
+
+ ndo->ndo_protocol = "someip";
+ nd_print_protocol_caps(ndo);
+
+ if (len < 16) {
+ goto invalid;
+ }
+
+ message_id = GET_BE_U_4(bp);
+ service_id = message_id >> 16;
+ event_flag = (message_id & 0x00008000) >> 15;
+ method_or_event_id = message_id & 0x00007FFF;
+ bp += 4;
+ ND_PRINT(", service %u, %s %u",
+ service_id, event_flag ? "event" : "method", method_or_event_id);
+
+ message_len = GET_BE_U_4(bp);
+ bp += 4;
+ ND_PRINT(", len %u", message_len);
+
+ request_id = GET_BE_U_4(bp);
+ client_id = request_id >> 16;
+ session_id = request_id & 0x0000FFFF;
+ bp += 4;
+ ND_PRINT(", client %u, session %u", client_id, session_id);
+
+ protocol_version = GET_U_1(bp);
+ bp += 1;
+ ND_PRINT(", pver %u", protocol_version);
+
+ interface_version = GET_U_1(bp);
+ bp += 1;
+ ND_PRINT(", iver %u", interface_version);
+
+ message_type = GET_U_1(bp);
+ bp += 1;
+ ND_PRINT(", msgtype %s",
+ tok2str(message_type_values, "Unknown", message_type));
+
+ return_code = GET_U_1(bp);
+ bp += 1;
+ ND_PRINT(", retcode %s\n",
+ tok2str(return_code_values, "Unknown", return_code));
+
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-ssh.c b/print-ssh.c
new file mode 100644
index 0000000..5207c52
--- /dev/null
+++ b/print-ssh.c
@@ -0,0 +1,99 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Secure Shell (SSH) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+static int
+ssh_print_version(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ u_int idx = 0;
+
+ if ( GET_U_1(pptr+idx) != 'S' )
+ return 0;
+ idx++;
+ if ( GET_U_1(pptr+idx) != 'S' )
+ return 0;
+ idx++;
+ if ( GET_U_1(pptr+idx) != 'H' )
+ return 0;
+ idx++;
+ if ( GET_U_1(pptr+idx) != '-' )
+ return 0;
+ idx++;
+
+ while (idx < len) {
+ u_char c;
+
+ c = GET_U_1(pptr + idx);
+ if (c == '\n') {
+ /*
+ * LF without CR; end of line.
+ * Skip the LF and print the line, with the
+ * exception of the LF.
+ */
+ goto print;
+ } else if (c == '\r') {
+ /* CR - any LF? */
+ if ((idx+1) >= len) {
+ /* not in this packet */
+ goto trunc;
+ }
+ if (GET_U_1(pptr + idx + 1) == '\n') {
+ /*
+ * CR-LF; end of line.
+ * Skip the CR-LF and print the line, with
+ * the exception of the CR-LF.
+ */
+ goto print;
+ }
+
+ /*
+ * CR followed by something else; treat this as
+ * if it were binary data and don't print it.
+ */
+ goto trunc;
+ } else if (!ND_ASCII_ISPRINT(c) ) {
+ /*
+ * Not a printable ASCII character; treat this
+ * as if it were binary data and don't print it.
+ */
+ goto trunc;
+ }
+ idx++;
+ }
+trunc:
+ return -1;
+print:
+ ND_PRINT(": ");
+ nd_print_protocol_caps(ndo);
+ ND_PRINT(": %.*s", (int)idx, pptr);
+ return idx;
+}
+
+void
+ssh_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ ndo->ndo_protocol = "ssh";
+
+ ssh_print_version(ndo, pptr, len);
+}
diff --git a/print-stp.c b/print-stp.c
new file mode 100644
index 0000000..5030b21
--- /dev/null
+++ b/print-stp.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2000 Lennert Buytenhek
+ *
+ * This software may be distributed either under the terms of the
+ * BSD-style license that accompanies tcpdump or the GNU General
+ * Public License
+ *
+ * Contributed by Lennert Buytenhek <buytenh@gnu.org>
+ */
+
+/* \summary: IEEE 802.1d Spanning Tree Protocol (STP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+
+#include "netdissect.h"
+#include "extract.h"
+
+#define RSTP_EXTRACT_PORT_ROLE(x) (((x)&0x0C)>>2)
+/* STP timers are expressed in multiples of 1/256th second */
+#define STP_TIME_BASE 256
+#define STP_BPDU_MSTP_MIN_LEN 102
+
+struct stp_bpdu_ {
+ nd_uint16_t protocol_id;
+ nd_uint8_t protocol_version;
+ nd_uint8_t bpdu_type;
+ nd_uint8_t flags;
+ nd_byte root_id[8];
+ nd_uint32_t root_path_cost;
+ nd_byte bridge_id[8];
+ nd_uint16_t port_id;
+ nd_uint16_t message_age;
+ nd_uint16_t max_age;
+ nd_uint16_t hello_time;
+ nd_uint16_t forward_delay;
+ nd_uint8_t v1_length;
+};
+
+#define STP_PROTO_REGULAR 0x00
+#define STP_PROTO_RAPID 0x02
+#define STP_PROTO_MSTP 0x03
+#define STP_PROTO_SPB 0x04
+
+static const struct tok stp_proto_values[] = {
+ { STP_PROTO_REGULAR, "802.1d" },
+ { STP_PROTO_RAPID, "802.1w" },
+ { STP_PROTO_MSTP, "802.1s" },
+ { STP_PROTO_SPB, "802.1aq" },
+ { 0, NULL}
+};
+
+#define STP_BPDU_TYPE_CONFIG 0x00
+#define STP_BPDU_TYPE_RSTP 0x02
+#define STP_BPDU_TYPE_TOPO_CHANGE 0x80
+
+static const struct tok stp_bpdu_flag_values[] = {
+ { 0x01, "Topology change" },
+ { 0x02, "Proposal" },
+ { 0x10, "Learn" },
+ { 0x20, "Forward" },
+ { 0x40, "Agreement" },
+ { 0x80, "Topology change ACK" },
+ { 0, NULL}
+};
+
+static const struct tok stp_bpdu_type_values[] = {
+ { STP_BPDU_TYPE_CONFIG, "Config" },
+ { STP_BPDU_TYPE_RSTP, "Rapid STP" },
+ { STP_BPDU_TYPE_TOPO_CHANGE, "Topology Change" },
+ { 0, NULL}
+};
+
+static const struct tok rstp_obj_port_role_values[] = {
+ { 0x00, "Unknown" },
+ { 0x01, "Alternate" },
+ { 0x02, "Root" },
+ { 0x03, "Designated" },
+ { 0, NULL}
+};
+
+static char *
+stp_print_bridge_id(netdissect_options *ndo, const u_char *p)
+{
+ static char bridge_id_str[sizeof("pppp.aa:bb:cc:dd:ee:ff")];
+
+ snprintf(bridge_id_str, sizeof(bridge_id_str),
+ "%.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x",
+ GET_U_1(p), GET_U_1(p + 1), GET_U_1(p + 2),
+ GET_U_1(p + 3), GET_U_1(p + 4), GET_U_1(p + 5),
+ GET_U_1(p + 6), GET_U_1(p + 7));
+
+ return bridge_id_str;
+}
+
+static void
+stp_print_config_bpdu(netdissect_options *ndo, const struct stp_bpdu_ *stp_bpdu,
+ u_int length)
+{
+ uint8_t bpdu_flags;
+
+ bpdu_flags = GET_U_1(stp_bpdu->flags);
+ ND_PRINT(", Flags [%s]",
+ bittok2str(stp_bpdu_flag_values, "none", bpdu_flags));
+
+ ND_PRINT(", bridge-id %s.%04x, length %u",
+ stp_print_bridge_id(ndo, stp_bpdu->bridge_id),
+ GET_BE_U_2(stp_bpdu->port_id), length);
+
+ /* in non-verbose mode just print the bridge-id */
+ if (!ndo->ndo_vflag) {
+ return;
+ }
+
+ ND_PRINT("\n\tmessage-age %.2fs, max-age %.2fs"
+ ", hello-time %.2fs, forwarding-delay %.2fs",
+ (float) GET_BE_U_2(stp_bpdu->message_age) / STP_TIME_BASE,
+ (float) GET_BE_U_2(stp_bpdu->max_age) / STP_TIME_BASE,
+ (float) GET_BE_U_2(stp_bpdu->hello_time) / STP_TIME_BASE,
+ (float) GET_BE_U_2(stp_bpdu->forward_delay) / STP_TIME_BASE);
+
+ ND_PRINT("\n\troot-id %s, root-pathcost %u",
+ stp_print_bridge_id(ndo, stp_bpdu->root_id),
+ GET_BE_U_4(stp_bpdu->root_path_cost));
+
+ /* Port role is only valid for 802.1w */
+ if (GET_U_1(stp_bpdu->protocol_version) == STP_PROTO_RAPID) {
+ ND_PRINT(", port-role %s",
+ tok2str(rstp_obj_port_role_values, "Unknown",
+ RSTP_EXTRACT_PORT_ROLE(bpdu_flags)));
+ }
+}
+
+/*
+ * MSTP packet format
+ * Ref. IEEE 802.1Q 2003 Ed. Section 14
+ *
+ * MSTP BPDU
+ *
+ * 2 - bytes Protocol Id
+ * 1 - byte Protocol Ver.
+ * 1 - byte BPDU tye
+ * 1 - byte Flags
+ * 8 - bytes CIST Root Identifier
+ * 4 - bytes CIST External Path Cost
+ * 8 - bytes CIST Regional Root Identifier
+ * 2 - bytes CIST Port Identifier
+ * 2 - bytes Message Age
+ * 2 - bytes Max age
+ * 2 - bytes Hello Time
+ * 2 - bytes Forward delay
+ * 1 - byte Version 1 length. Must be 0
+ * 2 - bytes Version 3 length
+ * 1 - byte Config Identifier
+ * 32 - bytes Config Name
+ * 2 - bytes Revision level
+ * 16 - bytes Config Digest [MD5]
+ * 4 - bytes CIST Internal Root Path Cost
+ * 8 - bytes CIST Bridge Identifier
+ * 1 - byte CIST Remaining Hops
+ * 16 - bytes MSTI information [Max 64 MSTI, each 16 bytes]
+ *
+ *
+ * SPB BPDU
+ * Ref. IEEE 802.1aq. Section 14
+ *
+ * 2 - bytes Version 4 length
+ * 1 - byte Aux Config Identifier
+ * 32 - bytes Aux Config Name
+ * 2 - bytes Aux Revision level
+ * 16 - bytes Aux Config Digest [MD5]
+ * 1 - byte (1 - 2) Agreement Number
+ * (3 - 4) Discarded Agreement Number
+ * (5) Agreement Valid Flag
+ * (6) Restricted Role Flag
+ * (7 - 8) Unused sent zero
+ * 1 - byte Unused
+ * 1 - byte (1 - 4) Agreement Digest Format Identifier
+ * (5 - 8) Agreement Digest Format Capabilities
+ * 1 - byte (1 - 4) Agreement Digest Convention Identifier
+ * (5 - 8) Agreement Digest Convention Capabilities
+ * 2 - bytes Agreement Digest Edge Count
+ * 8 - byte Reserved Set
+ * 20 - bytes Computed Topology Digest
+ *
+ *
+ * MSTI Payload
+ *
+ * 1 - byte MSTI flag
+ * 8 - bytes MSTI Regional Root Identifier
+ * 4 - bytes MSTI Regional Path Cost
+ * 1 - byte MSTI Bridge Priority
+ * 1 - byte MSTI Port Priority
+ * 1 - byte MSTI Remaining Hops
+ *
+ */
+
+#define MST_BPDU_MSTI_LENGTH 16
+#define MST_BPDU_CONFIG_INFO_LENGTH 64
+
+/* Offsets of fields from the beginning for the packet */
+#define MST_BPDU_VER3_LEN_OFFSET 36
+#define MST_BPDU_CONFIG_NAME_OFFSET 39
+#define MST_BPDU_CONFIG_DIGEST_OFFSET 73
+#define MST_BPDU_CIST_INT_PATH_COST_OFFSET 89
+#define MST_BPDU_CIST_BRIDGE_ID_OFFSET 93
+#define MST_BPDU_CIST_REMAIN_HOPS_OFFSET 101
+#define MST_BPDU_MSTI_OFFSET 102
+/* Offsets within an MSTI */
+#define MST_BPDU_MSTI_ROOT_PRIO_OFFSET 1
+#define MST_BPDU_MSTI_ROOT_PATH_COST_OFFSET 9
+#define MST_BPDU_MSTI_BRIDGE_PRIO_OFFSET 13
+#define MST_BPDU_MSTI_PORT_PRIO_OFFSET 14
+#define MST_BPDU_MSTI_REMAIN_HOPS_OFFSET 15
+
+#define SPB_BPDU_MIN_LEN 87
+#define SPB_BPDU_CONFIG_NAME_OFFSET 3
+#define SPB_BPDU_CONFIG_REV_OFFSET SPB_BPDU_CONFIG_NAME_OFFSET + 32
+#define SPB_BPDU_CONFIG_DIGEST_OFFSET SPB_BPDU_CONFIG_REV_OFFSET + 2
+#define SPB_BPDU_AGREEMENT_OFFSET SPB_BPDU_CONFIG_DIGEST_OFFSET + 16
+#define SPB_BPDU_AGREEMENT_UNUSED_OFFSET SPB_BPDU_AGREEMENT_OFFSET + 1
+#define SPB_BPDU_AGREEMENT_FORMAT_OFFSET SPB_BPDU_AGREEMENT_UNUSED_OFFSET + 1
+#define SPB_BPDU_AGREEMENT_CON_OFFSET SPB_BPDU_AGREEMENT_FORMAT_OFFSET + 1
+#define SPB_BPDU_AGREEMENT_EDGE_OFFSET SPB_BPDU_AGREEMENT_CON_OFFSET + 1
+#define SPB_BPDU_AGREEMENT_RES1_OFFSET SPB_BPDU_AGREEMENT_EDGE_OFFSET + 2
+#define SPB_BPDU_AGREEMENT_RES2_OFFSET SPB_BPDU_AGREEMENT_RES1_OFFSET + 4
+#define SPB_BPDU_AGREEMENT_DIGEST_OFFSET SPB_BPDU_AGREEMENT_RES2_OFFSET + 4
+
+static void
+stp_print_mstp_bpdu(netdissect_options *ndo, const struct stp_bpdu_ *stp_bpdu,
+ u_int length)
+{
+ const u_char *ptr;
+ uint8_t bpdu_flags;
+ uint16_t v3len;
+ uint16_t len;
+ uint16_t msti;
+ u_int offset;
+
+ ptr = (const u_char *)stp_bpdu;
+ bpdu_flags = GET_U_1(stp_bpdu->flags);
+ ND_PRINT(", CIST Flags [%s], length %u",
+ bittok2str(stp_bpdu_flag_values, "none", bpdu_flags), length);
+
+ /*
+ * in non-verbose mode just print the flags.
+ */
+ if (!ndo->ndo_vflag) {
+ return;
+ }
+
+ ND_PRINT("\n\tport-role %s, ",
+ tok2str(rstp_obj_port_role_values, "Unknown",
+ RSTP_EXTRACT_PORT_ROLE(bpdu_flags)));
+
+ ND_PRINT("CIST root-id %s, CIST ext-pathcost %u",
+ stp_print_bridge_id(ndo, stp_bpdu->root_id),
+ GET_BE_U_4(stp_bpdu->root_path_cost));
+
+ ND_PRINT("\n\tCIST regional-root-id %s, ",
+ stp_print_bridge_id(ndo, stp_bpdu->bridge_id));
+
+ ND_PRINT("CIST port-id %04x,", GET_BE_U_2(stp_bpdu->port_id));
+
+ ND_PRINT("\n\tmessage-age %.2fs, max-age %.2fs"
+ ", hello-time %.2fs, forwarding-delay %.2fs",
+ (float) GET_BE_U_2(stp_bpdu->message_age) / STP_TIME_BASE,
+ (float) GET_BE_U_2(stp_bpdu->max_age) / STP_TIME_BASE,
+ (float) GET_BE_U_2(stp_bpdu->hello_time) / STP_TIME_BASE,
+ (float) GET_BE_U_2(stp_bpdu->forward_delay) / STP_TIME_BASE);
+
+ ND_PRINT("\n\tv3len %u, ", GET_BE_U_2(ptr + MST_BPDU_VER3_LEN_OFFSET));
+ ND_PRINT("MCID Name ");
+ nd_printjnp(ndo, ptr + MST_BPDU_CONFIG_NAME_OFFSET, 32);
+ ND_PRINT(", rev %u,"
+ "\n\t\tdigest %08x%08x%08x%08x, ",
+ GET_BE_U_2(ptr + MST_BPDU_CONFIG_NAME_OFFSET + 32),
+ GET_BE_U_4(ptr + MST_BPDU_CONFIG_DIGEST_OFFSET),
+ GET_BE_U_4(ptr + MST_BPDU_CONFIG_DIGEST_OFFSET + 4),
+ GET_BE_U_4(ptr + MST_BPDU_CONFIG_DIGEST_OFFSET + 8),
+ GET_BE_U_4(ptr + MST_BPDU_CONFIG_DIGEST_OFFSET + 12));
+
+ ND_PRINT("CIST int-root-pathcost %u,",
+ GET_BE_U_4(ptr + MST_BPDU_CIST_INT_PATH_COST_OFFSET));
+
+ ND_PRINT("\n\tCIST bridge-id %s, ",
+ stp_print_bridge_id(ndo, ptr + MST_BPDU_CIST_BRIDGE_ID_OFFSET));
+
+ ND_PRINT("CIST remaining-hops %u",
+ GET_U_1(ptr + MST_BPDU_CIST_REMAIN_HOPS_OFFSET));
+
+ /* Dump all MSTI's */
+ v3len = GET_BE_U_2(ptr + MST_BPDU_VER3_LEN_OFFSET);
+ if (v3len > MST_BPDU_CONFIG_INFO_LENGTH) {
+ len = v3len - MST_BPDU_CONFIG_INFO_LENGTH;
+ offset = MST_BPDU_MSTI_OFFSET;
+ while (len >= MST_BPDU_MSTI_LENGTH) {
+ msti = GET_BE_U_2(ptr + offset + MST_BPDU_MSTI_ROOT_PRIO_OFFSET);
+ msti = msti & 0x0FFF;
+
+ ND_PRINT("\n\tMSTI %u, Flags [%s], port-role %s",
+ msti,
+ bittok2str(stp_bpdu_flag_values, "none", GET_U_1(ptr + offset)),
+ tok2str(rstp_obj_port_role_values, "Unknown",
+ RSTP_EXTRACT_PORT_ROLE(GET_U_1(ptr + offset))));
+ ND_PRINT("\n\t\tMSTI regional-root-id %s, pathcost %u",
+ stp_print_bridge_id(ndo, ptr + offset +
+ MST_BPDU_MSTI_ROOT_PRIO_OFFSET),
+ GET_BE_U_4(ptr + offset + MST_BPDU_MSTI_ROOT_PATH_COST_OFFSET));
+ ND_PRINT("\n\t\tMSTI bridge-prio %u, port-prio %u, hops %u",
+ GET_U_1(ptr + offset + MST_BPDU_MSTI_BRIDGE_PRIO_OFFSET) >> 4,
+ GET_U_1(ptr + offset + MST_BPDU_MSTI_PORT_PRIO_OFFSET) >> 4,
+ GET_U_1(ptr + offset + MST_BPDU_MSTI_REMAIN_HOPS_OFFSET));
+
+ len -= MST_BPDU_MSTI_LENGTH;
+ offset += MST_BPDU_MSTI_LENGTH;
+ }
+ }
+}
+
+static void
+stp_print_spb_bpdu(netdissect_options *ndo, const struct stp_bpdu_ *stp_bpdu,
+ u_int offset)
+{
+ const u_char *ptr;
+
+ /*
+ * in non-verbose mode don't print anything.
+ */
+ if (!ndo->ndo_vflag) {
+ return;
+ }
+
+ ptr = (const u_char *)stp_bpdu;
+
+ ND_PRINT("\n\tv4len %u, ", GET_BE_U_2(ptr + offset));
+ ND_PRINT("AUXMCID Name ");
+ nd_printjnp(ndo, ptr + offset + SPB_BPDU_CONFIG_NAME_OFFSET, 32);
+ ND_PRINT(", Rev %u,\n\t\tdigest %08x%08x%08x%08x",
+ GET_BE_U_2(ptr + offset + SPB_BPDU_CONFIG_REV_OFFSET),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_CONFIG_DIGEST_OFFSET),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_CONFIG_DIGEST_OFFSET + 4),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_CONFIG_DIGEST_OFFSET + 8),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_CONFIG_DIGEST_OFFSET + 12));
+
+ ND_PRINT("\n\tAgreement num %u, Discarded Agreement num %u, Agreement valid-"
+ "flag %u,\n\tRestricted role-flag: %u, Format id %u cap %u, "
+ "Convention id %u cap %u,\n\tEdge count %u, "
+ "Agreement digest %08x%08x%08x%08x%08x",
+ GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_OFFSET)>>6,
+ GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_OFFSET)>>4 & 0x3,
+ GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_OFFSET)>>3 & 0x1,
+ GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_OFFSET)>>2 & 0x1,
+ GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_FORMAT_OFFSET)>>4,
+ GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_FORMAT_OFFSET)&0x00ff,
+ GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_CON_OFFSET)>>4,
+ GET_U_1(ptr + offset + SPB_BPDU_AGREEMENT_CON_OFFSET)&0x00ff,
+ GET_BE_U_2(ptr + offset + SPB_BPDU_AGREEMENT_EDGE_OFFSET),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET + 4),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET + 8),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET + 12),
+ GET_BE_U_4(ptr + offset + SPB_BPDU_AGREEMENT_DIGEST_OFFSET + 16));
+}
+
+/*
+ * Print 802.1d / 802.1w / 802.1q (mstp) / 802.1aq (spb) packets.
+ */
+void
+stp_print(netdissect_options *ndo, const u_char *p, u_int length)
+{
+ const struct stp_bpdu_ *stp_bpdu;
+ u_int protocol_version;
+ u_int bpdu_type;
+ u_int mstp_len;
+ u_int spb_len;
+
+ ndo->ndo_protocol = "stp";
+ stp_bpdu = (const struct stp_bpdu_*)p;
+
+ /* Minimum STP Frame size. */
+ if (length < 4)
+ goto invalid;
+
+ if (GET_BE_U_2(stp_bpdu->protocol_id)) {
+ ND_PRINT("unknown STP version, length %u", length);
+ return;
+ }
+
+ protocol_version = GET_U_1(stp_bpdu->protocol_version);
+ ND_PRINT("STP %s", tok2str(stp_proto_values, "Unknown STP protocol (0x%02x)",
+ protocol_version));
+
+ switch (protocol_version) {
+ case STP_PROTO_REGULAR:
+ case STP_PROTO_RAPID:
+ case STP_PROTO_MSTP:
+ case STP_PROTO_SPB:
+ break;
+ default:
+ return;
+ }
+
+ bpdu_type = GET_U_1(stp_bpdu->bpdu_type);
+ ND_PRINT(", %s", tok2str(stp_bpdu_type_values, "Unknown BPDU Type (0x%02x)",
+ bpdu_type));
+
+ switch (bpdu_type) {
+ case STP_BPDU_TYPE_CONFIG:
+ if (length < sizeof(struct stp_bpdu_) - 1) {
+ goto invalid;
+ }
+ stp_print_config_bpdu(ndo, stp_bpdu, length);
+ break;
+
+ case STP_BPDU_TYPE_RSTP:
+ if (protocol_version == STP_PROTO_RAPID) {
+ if (length < sizeof(struct stp_bpdu_)) {
+ goto invalid;
+ }
+ stp_print_config_bpdu(ndo, stp_bpdu, length);
+ } else if (protocol_version == STP_PROTO_MSTP ||
+ protocol_version == STP_PROTO_SPB) {
+ if (length < STP_BPDU_MSTP_MIN_LEN) {
+ goto invalid;
+ }
+
+ if (GET_U_1(stp_bpdu->v1_length) != 0) {
+ /* FIX ME: Emit a message here ? */
+ goto invalid;
+ }
+
+ /* Validate v3 length */
+ mstp_len = GET_BE_U_2(p + MST_BPDU_VER3_LEN_OFFSET);
+ mstp_len += 2; /* length encoding itself is 2 bytes */
+ if (length < (sizeof(struct stp_bpdu_) + mstp_len)) {
+ goto invalid;
+ }
+ stp_print_mstp_bpdu(ndo, stp_bpdu, length);
+
+ if (protocol_version == STP_PROTO_SPB)
+ {
+ /* Validate v4 length */
+ spb_len = GET_BE_U_2(p + MST_BPDU_VER3_LEN_OFFSET + mstp_len);
+ spb_len += 2;
+ if (length < (sizeof(struct stp_bpdu_) + mstp_len + spb_len) ||
+ spb_len < SPB_BPDU_MIN_LEN) {
+ goto invalid;
+ }
+ stp_print_spb_bpdu(ndo, stp_bpdu, (sizeof(struct stp_bpdu_) + mstp_len));
+ }
+ }
+ break;
+
+ case STP_BPDU_TYPE_TOPO_CHANGE:
+ /* always empty message - just break out */
+ break;
+
+ default:
+ break;
+ }
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-sunatm.c b/print-sunatm.c
new file mode 100644
index 0000000..0fe5eee
--- /dev/null
+++ b/print-sunatm.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 1997 Yen Yen Lim and North Dakota State University
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Yen Yen Lim and
+ North Dakota State University
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: SunATM DLPI capture printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+#include "atm.h"
+
+/* SunATM header for ATM packet */
+#define DIR_POS 0 /* Direction (0x80 = transmit, 0x00 = receive) */
+#define VPI_POS 1 /* VPI */
+#define VCI_POS 2 /* VCI */
+#define PKT_BEGIN_POS 4 /* Start of the ATM packet */
+
+/* Protocol type values in the bottom for bits of the byte at SUNATM_DIR_POS. */
+#define PT_LANE 0x01 /* LANE */
+#define PT_LLC 0x02 /* LLC encapsulation */
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the SunATM pseudo-header for the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+sunatm_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int caplen = h->caplen;
+ u_int length = h->len;
+ u_short vci;
+ u_char vpi;
+ u_int traftype;
+
+ ndo->ndo_protocol = "sunatm";
+
+ if (ndo->ndo_eflag) {
+ ND_PRINT(GET_U_1(p + DIR_POS) & 0x80 ? "Tx: " : "Rx: ");
+ }
+
+ switch (GET_U_1(p + DIR_POS) & 0x0f) {
+
+ case PT_LANE:
+ traftype = ATM_LANE;
+ break;
+
+ case PT_LLC:
+ traftype = ATM_LLC;
+ break;
+
+ default:
+ traftype = ATM_UNKNOWN;
+ break;
+ }
+
+ vpi = GET_U_1(p + VPI_POS);
+ vci = GET_BE_U_2(p + VCI_POS);
+
+ p += PKT_BEGIN_POS;
+ caplen -= PKT_BEGIN_POS;
+ length -= PKT_BEGIN_POS;
+ ndo->ndo_ll_hdr_len += PKT_BEGIN_POS;
+ atm_print(ndo, vpi, vci, traftype, p, length, caplen);
+}
diff --git a/print-symantec.c b/print-symantec.c
new file mode 100644
index 0000000..0d394e3
--- /dev/null
+++ b/print-symantec.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Symantec Enterprise Firewall printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "ethertype.h"
+
+struct symantec_header {
+ nd_byte stuff1[6];
+ nd_uint16_t ether_type;
+ nd_byte stuff2[36];
+};
+
+static void
+symantec_hdr_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ const struct symantec_header *sp;
+ uint16_t etype;
+
+ sp = (const struct symantec_header *)bp;
+
+ etype = GET_BE_U_2(sp->ether_type);
+ if (!ndo->ndo_qflag) {
+ if (etype <= MAX_ETHERNET_LENGTH_VAL)
+ ND_PRINT("invalid ethertype %u", etype);
+ else
+ ND_PRINT("ethertype %s (0x%04x)",
+ tok2str(ethertype_values,"Unknown", etype),
+ etype);
+ } else {
+ if (etype <= MAX_ETHERNET_LENGTH_VAL)
+ ND_PRINT("invalid ethertype %u", etype);
+ else
+ ND_PRINT("%s", tok2str(ethertype_values,"Unknown Ethertype (0x%04x)", etype));
+ }
+
+ ND_PRINT(", length %u: ", length);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the ether header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+symantec_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ u_int length = h->len;
+ u_int caplen = h->caplen;
+ const struct symantec_header *sp;
+ u_short ether_type;
+
+ ndo->ndo_protocol = "symantec";
+ ND_TCHECK_LEN(p, sizeof(struct symantec_header));
+
+ ndo->ndo_ll_hdr_len += sizeof (struct symantec_header);
+ if (ndo->ndo_eflag)
+ symantec_hdr_print(ndo, p, length);
+
+ length -= sizeof (struct symantec_header);
+ caplen -= sizeof (struct symantec_header);
+ sp = (const struct symantec_header *)p;
+ p += sizeof (struct symantec_header);
+
+ ether_type = GET_BE_U_2(sp->ether_type);
+
+ if (ether_type <= MAX_ETHERNET_LENGTH_VAL) {
+ /* ether_type not known, print raw packet */
+ if (!ndo->ndo_eflag)
+ symantec_hdr_print(ndo, (const u_char *)sp, length + sizeof (struct symantec_header));
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ } else if (ethertype_print(ndo, ether_type, p, length, caplen, NULL, NULL) == 0) {
+ /* ether_type not known, print raw packet */
+ if (!ndo->ndo_eflag)
+ symantec_hdr_print(ndo, (const u_char *)sp, length + sizeof (struct symantec_header));
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+}
diff --git a/print-syslog.c b/print-syslog.c
new file mode 100644
index 0000000..b0e1c91
--- /dev/null
+++ b/print-syslog.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1998-2004 Hannes Gredler <hannes@gredler.at>
+ * The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Syslog protocol printer */
+/* specification: RFC 3164 (not RFC 5424) */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+
+/*
+ * tokenlists and #defines taken from Ethereal - Network traffic analyzer
+ * by Gerald Combs <gerald@ethereal.com>
+ */
+
+#define SYSLOG_SEVERITY_MASK 0x0007 /* 0000 0000 0000 0111 */
+#define SYSLOG_FACILITY_MASK 0x03f8 /* 0000 0011 1111 1000 */
+#define SYSLOG_MAX_DIGITS 3 /* The maximum number of priority digits to read in. */
+
+static const struct tok syslog_severity_values[] = {
+ { 0, "emergency" },
+ { 1, "alert" },
+ { 2, "critical" },
+ { 3, "error" },
+ { 4, "warning" },
+ { 5, "notice" },
+ { 6, "info" },
+ { 7, "debug" },
+ { 0, NULL },
+};
+
+static const struct tok syslog_facility_values[] = {
+ { 0, "kernel" },
+ { 1, "user" },
+ { 2, "mail" },
+ { 3, "daemon" },
+ { 4, "auth" },
+ { 5, "syslog" },
+ { 6, "lpr" },
+ { 7, "news" },
+ { 8, "uucp" },
+ { 9, "cron" },
+ { 10, "authpriv" },
+ { 11, "ftp" },
+ { 12, "ntp" },
+ { 13, "security" },
+ { 14, "console" },
+ { 15, "cron" },
+ { 16, "local0" },
+ { 17, "local1" },
+ { 18, "local2" },
+ { 19, "local3" },
+ { 20, "local4" },
+ { 21, "local5" },
+ { 22, "local6" },
+ { 23, "local7" },
+ { 0, NULL },
+};
+
+void
+syslog_print(netdissect_options *ndo,
+ const u_char *pptr, u_int len)
+{
+ uint16_t msg_off = 0;
+ uint16_t pri = 0;
+ uint16_t facility,severity;
+
+ ndo->ndo_protocol = "syslog";
+ /* extract decimal figures that are
+ * encapsulated within < > tags
+ * based on this decimal figure extract the
+ * severity and facility values
+ */
+
+ if (GET_U_1(pptr) != '<')
+ goto invalid;
+ msg_off++;
+
+ while (msg_off <= SYSLOG_MAX_DIGITS &&
+ GET_U_1(pptr + msg_off) >= '0' &&
+ GET_U_1(pptr + msg_off) <= '9') {
+ pri = pri * 10 + (GET_U_1(pptr + msg_off) - '0');
+ msg_off++;
+ }
+
+ if (GET_U_1(pptr + msg_off) != '>')
+ goto invalid;
+ msg_off++;
+
+ facility = (pri & SYSLOG_FACILITY_MASK) >> 3;
+ severity = pri & SYSLOG_SEVERITY_MASK;
+
+ if (ndo->ndo_vflag < 1 )
+ {
+ ND_PRINT("SYSLOG %s.%s, length: %u",
+ tok2str(syslog_facility_values, "unknown (%u)", facility),
+ tok2str(syslog_severity_values, "unknown (%u)", severity),
+ len);
+ return;
+ }
+
+ ND_PRINT("SYSLOG, length: %u\n\tFacility %s (%u), Severity %s (%u)\n\tMsg: ",
+ len,
+ tok2str(syslog_facility_values, "unknown (%u)", facility),
+ facility,
+ tok2str(syslog_severity_values, "unknown (%u)", severity),
+ severity);
+
+ /* print the syslog text in verbose mode */
+ /*
+ * RFC 3164 Section 4.1.3: "There is no ending delimiter to this part.
+ * The MSG part of the syslog packet MUST contain visible (printing)
+ * characters."
+ *
+ * RFC 5424 Section 8.2: "This document does not impose any mandatory
+ * restrictions on the MSG or PARAM-VALUE content. As such, they MAY
+ * contain control characters, including the NUL character."
+ *
+ * Hence, to aid in protocol debugging, print the full MSG without
+ * beautification to make it clear what was transmitted on the wire.
+ */
+ if (len > msg_off)
+ (void)nd_printn(ndo, pptr + msg_off, len - msg_off, NULL);
+
+ if (ndo->ndo_vflag > 1)
+ print_unknown_data(ndo, pptr, "\n\t", len);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-tcp.c b/print-tcp.c
new file mode 100644
index 0000000..68ef3f2
--- /dev/null
+++ b/print-tcp.c
@@ -0,0 +1,913 @@
+/* $NetBSD: print-tcp.c,v 1.9 2007/07/26 18:15:12 plunky Exp $ */
+
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1999-2004 The tcpdump.org project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: TCP printer */
+
+#ifndef lint
+#else
+__RCSID("$NetBSD: print-tcp.c,v 1.8 2007/07/24 11:53:48 drochner Exp $");
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#include "tcp.h"
+
+#include "ip.h"
+#include "ip6.h"
+#include "ipproto.h"
+
+#ifdef HAVE_LIBCRYPTO
+#include <openssl/md5.h>
+#include "signature.h"
+
+static int tcp_verify_signature(netdissect_options *ndo,
+ const struct ip *ip, const struct tcphdr *tp,
+ const u_char *data, u_int length, const u_char *rcvsig);
+#endif
+
+static void print_tcp_rst_data(netdissect_options *, const u_char *sp, u_int length);
+static void print_tcp_fastopen_option(netdissect_options *ndo, const u_char *cp,
+ u_int datalen, int exp);
+
+#define MAX_RST_DATA_LEN 30
+
+
+struct tha {
+ nd_ipv4 src;
+ nd_ipv4 dst;
+ u_int port;
+};
+
+struct tcp_seq_hash {
+ struct tcp_seq_hash *nxt;
+ struct tha addr;
+ uint32_t seq;
+ uint32_t ack;
+};
+
+struct tha6 {
+ nd_ipv6 src;
+ nd_ipv6 dst;
+ u_int port;
+};
+
+struct tcp_seq_hash6 {
+ struct tcp_seq_hash6 *nxt;
+ struct tha6 addr;
+ uint32_t seq;
+ uint32_t ack;
+};
+
+#define TSEQ_HASHSIZE 919
+
+/* These tcp options do not have the size octet */
+#define ZEROLENOPT(o) ((o) == TCPOPT_EOL || (o) == TCPOPT_NOP)
+
+static struct tcp_seq_hash tcp_seq_hash4[TSEQ_HASHSIZE];
+static struct tcp_seq_hash6 tcp_seq_hash6[TSEQ_HASHSIZE];
+
+static const struct tok tcp_flag_values[] = {
+ { TH_FIN, "F" },
+ { TH_SYN, "S" },
+ { TH_RST, "R" },
+ { TH_PUSH, "P" },
+ { TH_ACK, "." },
+ { TH_URG, "U" },
+ { TH_ECNECHO, "E" },
+ { TH_CWR, "W" },
+ { 0, NULL }
+};
+
+static const struct tok tcp_option_values[] = {
+ { TCPOPT_EOL, "eol" },
+ { TCPOPT_NOP, "nop" },
+ { TCPOPT_MAXSEG, "mss" },
+ { TCPOPT_WSCALE, "wscale" },
+ { TCPOPT_SACKOK, "sackOK" },
+ { TCPOPT_SACK, "sack" },
+ { TCPOPT_ECHO, "echo" },
+ { TCPOPT_ECHOREPLY, "echoreply" },
+ { TCPOPT_TIMESTAMP, "TS" },
+ { TCPOPT_CC, "cc" },
+ { TCPOPT_CCNEW, "ccnew" },
+ { TCPOPT_CCECHO, "" },
+ { TCPOPT_SIGNATURE, "md5" },
+ { TCPOPT_SCPS, "scps" },
+ { TCPOPT_UTO, "uto" },
+ { TCPOPT_TCPAO, "tcp-ao" },
+ { TCPOPT_MPTCP, "mptcp" },
+ { TCPOPT_FASTOPEN, "tfo" },
+ { TCPOPT_EXPERIMENT2, "exp" },
+ { 0, NULL }
+};
+
+static uint16_t
+tcp_cksum(netdissect_options *ndo,
+ const struct ip *ip,
+ const struct tcphdr *tp,
+ u_int len)
+{
+ return nextproto4_cksum(ndo, ip, (const uint8_t *)tp, len, len,
+ IPPROTO_TCP);
+}
+
+static uint16_t
+tcp6_cksum(netdissect_options *ndo,
+ const struct ip6_hdr *ip6,
+ const struct tcphdr *tp,
+ u_int len)
+{
+ return nextproto6_cksum(ndo, ip6, (const uint8_t *)tp, len, len,
+ IPPROTO_TCP);
+}
+
+void
+tcp_print(netdissect_options *ndo,
+ const u_char *bp, u_int length,
+ const u_char *bp2, int fragmented)
+{
+ const struct tcphdr *tp;
+ const struct ip *ip;
+ u_char flags;
+ u_int hlen;
+ char ch;
+ uint16_t sport, dport, win, urp;
+ uint32_t seq, ack, thseq, thack;
+ u_int utoval;
+ uint16_t magic;
+ int rev;
+ const struct ip6_hdr *ip6;
+
+ ndo->ndo_protocol = "tcp";
+ tp = (const struct tcphdr *)bp;
+ ip = (const struct ip *)bp2;
+ if (IP_V(ip) == 6)
+ ip6 = (const struct ip6_hdr *)bp2;
+ else
+ ip6 = NULL;
+ ch = '\0';
+ if (!ND_TTEST_2(tp->th_dport)) {
+ if (ip6) {
+ ND_PRINT("%s > %s:",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ GET_IP6ADDR_STRING(ip6->ip6_dst));
+ } else {
+ ND_PRINT("%s > %s:",
+ GET_IPADDR_STRING(ip->ip_src),
+ GET_IPADDR_STRING(ip->ip_dst));
+ }
+ nd_print_trunc(ndo);
+ return;
+ }
+
+ sport = GET_BE_U_2(tp->th_sport);
+ dport = GET_BE_U_2(tp->th_dport);
+
+ if (ip6) {
+ if (GET_U_1(ip6->ip6_nxt) == IPPROTO_TCP) {
+ ND_PRINT("%s.%s > %s.%s: ",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ tcpport_string(ndo, sport),
+ GET_IP6ADDR_STRING(ip6->ip6_dst),
+ tcpport_string(ndo, dport));
+ } else {
+ ND_PRINT("%s > %s: ",
+ tcpport_string(ndo, sport), tcpport_string(ndo, dport));
+ }
+ } else {
+ if (GET_U_1(ip->ip_p) == IPPROTO_TCP) {
+ ND_PRINT("%s.%s > %s.%s: ",
+ GET_IPADDR_STRING(ip->ip_src),
+ tcpport_string(ndo, sport),
+ GET_IPADDR_STRING(ip->ip_dst),
+ tcpport_string(ndo, dport));
+ } else {
+ ND_PRINT("%s > %s: ",
+ tcpport_string(ndo, sport), tcpport_string(ndo, dport));
+ }
+ }
+
+ ND_TCHECK_SIZE(tp);
+
+ hlen = TH_OFF(tp) * 4;
+
+ if (hlen < sizeof(*tp)) {
+ ND_PRINT(" tcp %u [bad hdr length %u - too short, < %zu]",
+ length - hlen, hlen, sizeof(*tp));
+ return;
+ }
+
+ seq = GET_BE_U_4(tp->th_seq);
+ ack = GET_BE_U_4(tp->th_ack);
+ win = GET_BE_U_2(tp->th_win);
+ urp = GET_BE_U_2(tp->th_urp);
+
+ if (ndo->ndo_qflag) {
+ ND_PRINT("tcp %u", length - hlen);
+ if (hlen > length) {
+ ND_PRINT(" [bad hdr length %u - too long, > %u]",
+ hlen, length);
+ }
+ return;
+ }
+
+ flags = GET_U_1(tp->th_flags);
+ ND_PRINT("Flags [%s]", bittok2str_nosep(tcp_flag_values, "none", flags));
+
+ if (!ndo->ndo_Sflag && (flags & TH_ACK)) {
+ /*
+ * Find (or record) the initial sequence numbers for
+ * this conversation. (we pick an arbitrary
+ * collating order so there's only one entry for
+ * both directions).
+ */
+ rev = 0;
+ if (ip6) {
+ struct tcp_seq_hash6 *th;
+ struct tcp_seq_hash6 *tcp_seq_hash;
+ const void *src, *dst;
+ struct tha6 tha;
+
+ tcp_seq_hash = tcp_seq_hash6;
+ src = (const void *)ip6->ip6_src;
+ dst = (const void *)ip6->ip6_dst;
+ if (sport > dport)
+ rev = 1;
+ else if (sport == dport) {
+ if (UNALIGNED_MEMCMP(src, dst, sizeof(ip6->ip6_dst)) > 0)
+ rev = 1;
+ }
+ if (rev) {
+ UNALIGNED_MEMCPY(&tha.src, dst, sizeof(ip6->ip6_dst));
+ UNALIGNED_MEMCPY(&tha.dst, src, sizeof(ip6->ip6_src));
+ tha.port = ((u_int)dport) << 16 | sport;
+ } else {
+ UNALIGNED_MEMCPY(&tha.dst, dst, sizeof(ip6->ip6_dst));
+ UNALIGNED_MEMCPY(&tha.src, src, sizeof(ip6->ip6_src));
+ tha.port = ((u_int)sport) << 16 | dport;
+ }
+
+ for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE];
+ th->nxt; th = th->nxt)
+ if (memcmp((char *)&tha, (char *)&th->addr,
+ sizeof(th->addr)) == 0)
+ break;
+
+ if (!th->nxt || (flags & TH_SYN)) {
+ /* didn't find it or new conversation */
+ /* calloc() return used by the 'tcp_seq_hash6'
+ hash table: do not free() */
+ if (th->nxt == NULL) {
+ th->nxt = (struct tcp_seq_hash6 *)
+ calloc(1, sizeof(*th));
+ if (th->nxt == NULL)
+ (*ndo->ndo_error)(ndo,
+ S_ERR_ND_MEM_ALLOC,
+ "%s: calloc", __func__);
+ }
+ th->addr = tha;
+ if (rev)
+ th->ack = seq, th->seq = ack - 1;
+ else
+ th->seq = seq, th->ack = ack - 1;
+ } else {
+ if (rev)
+ seq -= th->ack, ack -= th->seq;
+ else
+ seq -= th->seq, ack -= th->ack;
+ }
+
+ thseq = th->seq;
+ thack = th->ack;
+ } else {
+ struct tcp_seq_hash *th;
+ struct tcp_seq_hash *tcp_seq_hash;
+ struct tha tha;
+
+ tcp_seq_hash = tcp_seq_hash4;
+ if (sport > dport)
+ rev = 1;
+ else if (sport == dport) {
+ if (UNALIGNED_MEMCMP(ip->ip_src, ip->ip_dst, sizeof(ip->ip_dst)) > 0)
+ rev = 1;
+ }
+ if (rev) {
+ UNALIGNED_MEMCPY(&tha.src, ip->ip_dst,
+ sizeof(ip->ip_dst));
+ UNALIGNED_MEMCPY(&tha.dst, ip->ip_src,
+ sizeof(ip->ip_src));
+ tha.port = ((u_int)dport) << 16 | sport;
+ } else {
+ UNALIGNED_MEMCPY(&tha.dst, ip->ip_dst,
+ sizeof(ip->ip_dst));
+ UNALIGNED_MEMCPY(&tha.src, ip->ip_src,
+ sizeof(ip->ip_src));
+ tha.port = ((u_int)sport) << 16 | dport;
+ }
+
+ for (th = &tcp_seq_hash[tha.port % TSEQ_HASHSIZE];
+ th->nxt; th = th->nxt)
+ if (memcmp((char *)&tha, (char *)&th->addr,
+ sizeof(th->addr)) == 0)
+ break;
+
+ if (!th->nxt || (flags & TH_SYN)) {
+ /* didn't find it or new conversation */
+ /* calloc() return used by the 'tcp_seq_hash4'
+ hash table: do not free() */
+ if (th->nxt == NULL) {
+ th->nxt = (struct tcp_seq_hash *)
+ calloc(1, sizeof(*th));
+ if (th->nxt == NULL)
+ (*ndo->ndo_error)(ndo,
+ S_ERR_ND_MEM_ALLOC,
+ "%s: calloc", __func__);
+ }
+ th->addr = tha;
+ if (rev)
+ th->ack = seq, th->seq = ack - 1;
+ else
+ th->seq = seq, th->ack = ack - 1;
+ } else {
+ if (rev)
+ seq -= th->ack, ack -= th->seq;
+ else
+ seq -= th->seq, ack -= th->ack;
+ }
+
+ thseq = th->seq;
+ thack = th->ack;
+ }
+ } else {
+ /*fool gcc*/
+ thseq = thack = rev = 0;
+ }
+ if (hlen > length) {
+ ND_PRINT(" [bad hdr length %u - too long, > %u]",
+ hlen, length);
+ return;
+ }
+
+ if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented) {
+ /* Check the checksum, if possible. */
+ uint16_t sum, tcp_sum;
+
+ if (IP_V(ip) == 4) {
+ if (ND_TTEST_LEN(tp->th_sport, length)) {
+ sum = tcp_cksum(ndo, ip, tp, length);
+ tcp_sum = GET_BE_U_2(tp->th_sum);
+
+ ND_PRINT(", cksum 0x%04x", tcp_sum);
+ if (sum != 0)
+ ND_PRINT(" (incorrect -> 0x%04x)",
+ in_cksum_shouldbe(tcp_sum, sum));
+ else
+ ND_PRINT(" (correct)");
+ }
+ } else if (IP_V(ip) == 6) {
+ if (ND_TTEST_LEN(tp->th_sport, length)) {
+ sum = tcp6_cksum(ndo, ip6, tp, length);
+ tcp_sum = GET_BE_U_2(tp->th_sum);
+
+ ND_PRINT(", cksum 0x%04x", tcp_sum);
+ if (sum != 0)
+ ND_PRINT(" (incorrect -> 0x%04x)",
+ in_cksum_shouldbe(tcp_sum, sum));
+ else
+ ND_PRINT(" (correct)");
+
+ }
+ }
+ }
+
+ length -= hlen;
+ if (ndo->ndo_vflag > 1 || length > 0 || flags & (TH_SYN | TH_FIN | TH_RST)) {
+ ND_PRINT(", seq %u", seq);
+
+ if (length > 0) {
+ ND_PRINT(":%u", seq + length);
+ }
+ }
+
+ if (flags & TH_ACK) {
+ ND_PRINT(", ack %u", ack);
+ }
+
+ ND_PRINT(", win %u", win);
+
+ if (flags & TH_URG)
+ ND_PRINT(", urg %u", urp);
+ /*
+ * Handle any options.
+ */
+ if (hlen > sizeof(*tp)) {
+ const u_char *cp;
+ u_int i, opt, datalen;
+ u_int len;
+
+ hlen -= sizeof(*tp);
+ cp = (const u_char *)tp + sizeof(*tp);
+ ND_PRINT(", options [");
+ while (hlen > 0) {
+ if (ch != '\0')
+ ND_PRINT("%c", ch);
+ opt = GET_U_1(cp);
+ cp++;
+ if (ZEROLENOPT(opt))
+ len = 1;
+ else {
+ len = GET_U_1(cp);
+ cp++; /* total including type, len */
+ if (len < 2 || len > hlen)
+ goto bad;
+ --hlen; /* account for length byte */
+ }
+ --hlen; /* account for type byte */
+ datalen = 0;
+
+/* Bail if "l" bytes of data are not left or were not captured */
+#define LENCHECK(l) { if ((l) > hlen) goto bad; ND_TCHECK_LEN(cp, l); }
+
+
+ ND_PRINT("%s", tok2str(tcp_option_values, "unknown-%u", opt));
+
+ switch (opt) {
+
+ case TCPOPT_MAXSEG:
+ datalen = 2;
+ LENCHECK(datalen);
+ ND_PRINT(" %u", GET_BE_U_2(cp));
+ break;
+
+ case TCPOPT_WSCALE:
+ datalen = 1;
+ LENCHECK(datalen);
+ ND_PRINT(" %u", GET_U_1(cp));
+ break;
+
+ case TCPOPT_SACK:
+ datalen = len - 2;
+ if (datalen % 8 != 0) {
+ ND_PRINT(" invalid sack");
+ } else {
+ uint32_t s, e;
+
+ ND_PRINT(" %u ", datalen / 8);
+ for (i = 0; i < datalen; i += 8) {
+ LENCHECK(i + 4);
+ s = GET_BE_U_4(cp + i);
+ LENCHECK(i + 8);
+ e = GET_BE_U_4(cp + i + 4);
+ if (rev) {
+ s -= thseq;
+ e -= thseq;
+ } else {
+ s -= thack;
+ e -= thack;
+ }
+ ND_PRINT("{%u:%u}", s, e);
+ }
+ }
+ break;
+
+ case TCPOPT_CC:
+ case TCPOPT_CCNEW:
+ case TCPOPT_CCECHO:
+ case TCPOPT_ECHO:
+ case TCPOPT_ECHOREPLY:
+
+ /*
+ * those options share their semantics.
+ * fall through
+ */
+ datalen = 4;
+ LENCHECK(datalen);
+ ND_PRINT(" %u", GET_BE_U_4(cp));
+ break;
+
+ case TCPOPT_TIMESTAMP:
+ datalen = 8;
+ LENCHECK(datalen);
+ ND_PRINT(" val %u ecr %u",
+ GET_BE_U_4(cp),
+ GET_BE_U_4(cp + 4));
+ break;
+
+ case TCPOPT_SIGNATURE:
+ datalen = TCP_SIGLEN;
+ LENCHECK(datalen);
+ ND_PRINT(" ");
+#ifdef HAVE_LIBCRYPTO
+ switch (tcp_verify_signature(ndo, ip, tp,
+ bp + TH_OFF(tp) * 4, length, cp)) {
+
+ case SIGNATURE_VALID:
+ ND_PRINT("valid");
+ break;
+
+ case SIGNATURE_INVALID:
+ nd_print_invalid(ndo);
+ break;
+
+ case CANT_CHECK_SIGNATURE:
+ ND_PRINT("can't check - ");
+ for (i = 0; i < TCP_SIGLEN; ++i)
+ ND_PRINT("%02x",
+ GET_U_1(cp + i));
+ break;
+ }
+#else
+ for (i = 0; i < TCP_SIGLEN; ++i)
+ ND_PRINT("%02x", GET_U_1(cp + i));
+#endif
+ break;
+
+ case TCPOPT_SCPS:
+ datalen = 2;
+ LENCHECK(datalen);
+ ND_PRINT(" cap %02x id %u", GET_U_1(cp),
+ GET_U_1(cp + 1));
+ break;
+
+ case TCPOPT_TCPAO:
+ datalen = len - 2;
+ /* RFC 5925 Section 2.2:
+ * "The Length value MUST be greater than or equal to 4."
+ * (This includes the Kind and Length fields already processed
+ * at this point.)
+ */
+ if (datalen < 2) {
+ nd_print_invalid(ndo);
+ } else {
+ LENCHECK(1);
+ ND_PRINT(" keyid %u", GET_U_1(cp));
+ LENCHECK(2);
+ ND_PRINT(" rnextkeyid %u",
+ GET_U_1(cp + 1));
+ if (datalen > 2) {
+ ND_PRINT(" mac 0x");
+ for (i = 2; i < datalen; i++) {
+ LENCHECK(i + 1);
+ ND_PRINT("%02x",
+ GET_U_1(cp + i));
+ }
+ }
+ }
+ break;
+
+ case TCPOPT_EOL:
+ case TCPOPT_NOP:
+ case TCPOPT_SACKOK:
+ /*
+ * Nothing interesting.
+ * fall through
+ */
+ break;
+
+ case TCPOPT_UTO:
+ datalen = 2;
+ LENCHECK(datalen);
+ utoval = GET_BE_U_2(cp);
+ ND_PRINT(" 0x%x", utoval);
+ if (utoval & 0x0001)
+ utoval = (utoval >> 1) * 60;
+ else
+ utoval >>= 1;
+ ND_PRINT(" %u", utoval);
+ break;
+
+ case TCPOPT_MPTCP:
+ datalen = len - 2;
+ LENCHECK(datalen);
+ if (!mptcp_print(ndo, cp-2, len, flags))
+ goto bad;
+ break;
+
+ case TCPOPT_FASTOPEN:
+ datalen = len - 2;
+ LENCHECK(datalen);
+ ND_PRINT(" ");
+ print_tcp_fastopen_option(ndo, cp, datalen, FALSE);
+ break;
+
+ case TCPOPT_EXPERIMENT2:
+ datalen = len - 2;
+ LENCHECK(datalen);
+ if (datalen < 2)
+ goto bad;
+ /* RFC6994 */
+ magic = GET_BE_U_2(cp);
+ ND_PRINT("-");
+
+ switch(magic) {
+
+ case 0xf989: /* TCP Fast Open RFC 7413 */
+ print_tcp_fastopen_option(ndo, cp + 2, datalen - 2, TRUE);
+ break;
+
+ default:
+ /* Unknown magic number */
+ ND_PRINT("%04x", magic);
+ break;
+ }
+ break;
+
+ default:
+ datalen = len - 2;
+ if (datalen)
+ ND_PRINT(" 0x");
+ for (i = 0; i < datalen; ++i) {
+ LENCHECK(i + 1);
+ ND_PRINT("%02x", GET_U_1(cp + i));
+ }
+ break;
+ }
+
+ /* Account for data printed */
+ cp += datalen;
+ hlen -= datalen;
+
+ /* Check specification against observed length */
+ ++datalen; /* option octet */
+ if (!ZEROLENOPT(opt))
+ ++datalen; /* size octet */
+ if (datalen != len)
+ ND_PRINT("[len %u]", len);
+ ch = ',';
+ if (opt == TCPOPT_EOL)
+ break;
+ }
+ ND_PRINT("]");
+ }
+
+ /*
+ * Print length field before crawling down the stack.
+ */
+ ND_PRINT(", length %u", length);
+
+ if (length <= 0)
+ return;
+
+ /*
+ * Decode payload if necessary.
+ */
+ bp += TH_OFF(tp) * 4;
+ if ((flags & TH_RST) && ndo->ndo_vflag) {
+ print_tcp_rst_data(ndo, bp, length);
+ return;
+ }
+
+ if (ndo->ndo_packettype) {
+ switch (ndo->ndo_packettype) {
+ case PT_ZMTP1:
+ zmtp1_print(ndo, bp, length);
+ break;
+ case PT_RESP:
+ resp_print(ndo, bp, length);
+ break;
+ case PT_DOMAIN:
+ /* over_tcp: TRUE, is_mdns: FALSE */
+ domain_print(ndo, bp, length, TRUE, FALSE);
+ break;
+ }
+ return;
+ }
+
+ if (IS_SRC_OR_DST_PORT(TELNET_PORT)) {
+ telnet_print(ndo, bp, length);
+ } else if (IS_SRC_OR_DST_PORT(SMTP_PORT)) {
+ ND_PRINT(": ");
+ smtp_print(ndo, bp, length);
+ } else if (IS_SRC_OR_DST_PORT(WHOIS_PORT)) {
+ ND_PRINT(": ");
+ ndo->ndo_protocol = "whois"; /* needed by txtproto_print() */
+ txtproto_print(ndo, bp, length, NULL, 0); /* RFC 3912 */
+ } else if (IS_SRC_OR_DST_PORT(BGP_PORT))
+ bgp_print(ndo, bp, length);
+ else if (IS_SRC_OR_DST_PORT(PPTP_PORT))
+ pptp_print(ndo, bp);
+ else if (IS_SRC_OR_DST_PORT(REDIS_PORT))
+ resp_print(ndo, bp, length);
+ else if (IS_SRC_OR_DST_PORT(SSH_PORT))
+ ssh_print(ndo, bp, length);
+#ifdef ENABLE_SMB
+ else if (IS_SRC_OR_DST_PORT(NETBIOS_SSN_PORT))
+ nbt_tcp_print(ndo, bp, length);
+ else if (IS_SRC_OR_DST_PORT(SMB_PORT))
+ smb_tcp_print(ndo, bp, length);
+#endif
+ else if (IS_SRC_OR_DST_PORT(BEEP_PORT))
+ beep_print(ndo, bp, length);
+ else if (IS_SRC_OR_DST_PORT(OPENFLOW_PORT_OLD) || IS_SRC_OR_DST_PORT(OPENFLOW_PORT_IANA))
+ openflow_print(ndo, bp, length);
+ else if (IS_SRC_OR_DST_PORT(FTP_PORT)) {
+ ND_PRINT(": ");
+ ftp_print(ndo, bp, length);
+ } else if (IS_SRC_OR_DST_PORT(HTTP_PORT) || IS_SRC_OR_DST_PORT(HTTP_PORT_ALT)) {
+ ND_PRINT(": ");
+ http_print(ndo, bp, length);
+ } else if (IS_SRC_OR_DST_PORT(RTSP_PORT) || IS_SRC_OR_DST_PORT(RTSP_PORT_ALT)) {
+ ND_PRINT(": ");
+ rtsp_print(ndo, bp, length);
+ } else if (IS_SRC_OR_DST_PORT(NAMESERVER_PORT)) {
+ /* over_tcp: TRUE, is_mdns: FALSE */
+ domain_print(ndo, bp, length, TRUE, FALSE);
+ } else if (IS_SRC_OR_DST_PORT(MSDP_PORT)) {
+ msdp_print(ndo, bp, length);
+ } else if (IS_SRC_OR_DST_PORT(RPKI_RTR_PORT)) {
+ rpki_rtr_print(ndo, bp, length);
+ } else if (IS_SRC_OR_DST_PORT(LDP_PORT)) {
+ ldp_print(ndo, bp, length);
+ } else if ((IS_SRC_OR_DST_PORT(NFS_PORT)) &&
+ length >= 4 && ND_TTEST_4(bp)) {
+ ND_PRINT("[https://fxbug.dev/84481] RPC/NFS REQUEST UNHANDLED");
+ }
+
+ return;
+bad:
+ ND_PRINT("[bad opt]");
+ if (ch != '\0')
+ ND_PRINT("]");
+ return;
+trunc:
+ nd_print_trunc(ndo);
+ if (ch != '\0')
+ ND_PRINT(">");
+}
+
+/*
+ * RFC1122 says the following on data in RST segments:
+ *
+ * 4.2.2.12 RST Segment: RFC-793 Section 3.4
+ *
+ * A TCP SHOULD allow a received RST segment to include data.
+ *
+ * DISCUSSION
+ * It has been suggested that a RST segment could contain
+ * ASCII text that encoded and explained the cause of the
+ * RST. No standard has yet been established for such
+ * data.
+ *
+ */
+
+static void
+print_tcp_rst_data(netdissect_options *ndo,
+ const u_char *sp, u_int length)
+{
+ u_char c;
+
+ ND_PRINT(ND_TTEST_LEN(sp, length) ? " [RST" : " [!RST");
+ if (length > MAX_RST_DATA_LEN) {
+ length = MAX_RST_DATA_LEN; /* can use -X for longer */
+ ND_PRINT("+"); /* indicate we truncate */
+ }
+ ND_PRINT(" ");
+ while (length && sp < ndo->ndo_snapend) {
+ c = GET_U_1(sp);
+ sp++;
+ fn_print_char(ndo, c);
+ length--;
+ }
+ ND_PRINT("]");
+}
+
+static void
+print_tcp_fastopen_option(netdissect_options *ndo, const u_char *cp,
+ u_int datalen, int exp)
+{
+ u_int i;
+
+ if (exp)
+ ND_PRINT("tfo");
+
+ if (datalen == 0) {
+ /* Fast Open Cookie Request */
+ ND_PRINT(" cookiereq");
+ } else {
+ /* Fast Open Cookie */
+ if (datalen % 2 != 0 || datalen < 4 || datalen > 16) {
+ nd_print_invalid(ndo);
+ } else {
+ ND_PRINT(" cookie ");
+ for (i = 0; i < datalen; ++i)
+ ND_PRINT("%02x", GET_U_1(cp + i));
+ }
+ }
+}
+
+#ifdef HAVE_LIBCRYPTO
+USES_APPLE_DEPRECATED_API
+static int
+tcp_verify_signature(netdissect_options *ndo,
+ const struct ip *ip, const struct tcphdr *tp,
+ const u_char *data, u_int length, const u_char *rcvsig)
+{
+ struct tcphdr tp1;
+ u_char sig[TCP_SIGLEN];
+ char zero_proto = 0;
+ MD5_CTX ctx;
+ uint16_t savecsum, tlen;
+ const struct ip6_hdr *ip6;
+ uint32_t len32;
+ uint8_t nxt;
+
+ if (data + length > ndo->ndo_snapend) {
+ ND_PRINT("snaplen too short, ");
+ return (CANT_CHECK_SIGNATURE);
+ }
+
+ tp1 = *tp;
+
+ if (ndo->ndo_sigsecret == NULL) {
+ ND_PRINT("shared secret not supplied with -M, ");
+ return (CANT_CHECK_SIGNATURE);
+ }
+
+ MD5_Init(&ctx);
+ /*
+ * Step 1: Update MD5 hash with IP pseudo-header.
+ */
+ if (IP_V(ip) == 4) {
+ MD5_Update(&ctx, (const char *)&ip->ip_src, sizeof(ip->ip_src));
+ MD5_Update(&ctx, (const char *)&ip->ip_dst, sizeof(ip->ip_dst));
+ MD5_Update(&ctx, (const char *)&zero_proto, sizeof(zero_proto));
+ MD5_Update(&ctx, (const char *)&ip->ip_p, sizeof(ip->ip_p));
+ tlen = GET_BE_U_2(ip->ip_len) - IP_HL(ip) * 4;
+ tlen = htons(tlen);
+ MD5_Update(&ctx, (const char *)&tlen, sizeof(tlen));
+ } else if (IP_V(ip) == 6) {
+ ip6 = (const struct ip6_hdr *)ip;
+ MD5_Update(&ctx, (const char *)&ip6->ip6_src, sizeof(ip6->ip6_src));
+ MD5_Update(&ctx, (const char *)&ip6->ip6_dst, sizeof(ip6->ip6_dst));
+ len32 = htonl(GET_BE_U_2(ip6->ip6_plen));
+ MD5_Update(&ctx, (const char *)&len32, sizeof(len32));
+ nxt = 0;
+ MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt));
+ MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt));
+ MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt));
+ nxt = IPPROTO_TCP;
+ MD5_Update(&ctx, (const char *)&nxt, sizeof(nxt));
+ } else {
+ ND_PRINT("IP version not 4 or 6, ");
+ return (CANT_CHECK_SIGNATURE);
+ }
+
+ /*
+ * Step 2: Update MD5 hash with TCP header, excluding options.
+ * The TCP checksum must be set to zero.
+ */
+ memcpy(&savecsum, tp1.th_sum, sizeof(savecsum));
+ memset(tp1.th_sum, 0, sizeof(tp1.th_sum));
+ MD5_Update(&ctx, (const char *)&tp1, sizeof(struct tcphdr));
+ memcpy(tp1.th_sum, &savecsum, sizeof(tp1.th_sum));
+ /*
+ * Step 3: Update MD5 hash with TCP segment data, if present.
+ */
+ if (length > 0)
+ MD5_Update(&ctx, data, length);
+ /*
+ * Step 4: Update MD5 hash with shared secret.
+ */
+ MD5_Update(&ctx, ndo->ndo_sigsecret, strlen(ndo->ndo_sigsecret));
+ MD5_Final(sig, &ctx);
+
+ if (memcmp(rcvsig, sig, TCP_SIGLEN) == 0)
+ return (SIGNATURE_VALID);
+ else
+ return (SIGNATURE_INVALID);
+}
+USES_APPLE_RST
+#endif /* HAVE_LIBCRYPTO */
diff --git a/print-telnet.c b/print-telnet.c
new file mode 100644
index 0000000..b0283f2
--- /dev/null
+++ b/print-telnet.c
@@ -0,0 +1,540 @@
+/* $NetBSD: print-telnet.c,v 1.2 1999/10/11 12:40:12 sjg Exp $ */
+
+/*-
+ * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Simon J. Gerraty.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
+ * @(#)Copyright (c) 1994, Simon J. Gerraty.
+ *
+ * This is free software. It comes with NO WARRANTY.
+ * Permission to use, modify and distribute this source code
+ * is granted subject to the following conditions.
+ * 1/ that the above copyright notice and this notice
+ * are preserved in all copies.
+ */
+
+/* \summary: Telnet option printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+
+#include "netdissect.h"
+#include "extract.h"
+
+
+/* NetBSD: telnet.h,v 1.9 2001/06/11 01:50:50 wiz Exp */
+
+/*
+ * Definitions for the TELNET protocol.
+ */
+#define IAC 255 /* interpret as command: */
+#define DONT 254 /* you are not to use option */
+#define DO 253 /* please, you use option */
+#define WONT 252 /* I won't use option */
+#define WILL 251 /* I will use option */
+#define SB 250 /* interpret as subnegotiation */
+#define GA 249 /* you may reverse the line */
+#define EL 248 /* erase the current line */
+#define EC 247 /* erase the current character */
+#define AYT 246 /* are you there */
+#define AO 245 /* abort output--but let prog finish */
+#define IP 244 /* interrupt process--permanently */
+#define BREAK 243 /* break */
+#define DM 242 /* data mark--for connect. cleaning */
+#define NOP 241 /* nop */
+#define SE 240 /* end sub negotiation */
+#define EOR 239 /* end of record (transparent mode) */
+#define ABORT 238 /* Abort process */
+#define SUSP 237 /* Suspend process */
+#define xEOF 236 /* End of file: EOF is already used... */
+
+#define SYNCH 242 /* for telfunc calls */
+
+static const char *telcmds[] = {
+ "EOF", "SUSP", "ABORT", "EOR",
+ "SE", "NOP", "DMARK", "BRK", "IP", "AO", "AYT", "EC",
+ "EL", "GA", "SB", "WILL", "WONT", "DO", "DONT", "IAC", 0,
+};
+
+#define TELCMD_FIRST xEOF
+#define TELCMD_LAST IAC
+#define TELCMD_OK(x) ((unsigned int)(x) <= TELCMD_LAST && \
+ (unsigned int)(x) >= TELCMD_FIRST)
+#define TELCMD(x) telcmds[(x)-TELCMD_FIRST]
+
+/* telnet options */
+#define TELOPT_BINARY 0 /* 8-bit data path */
+#define TELOPT_ECHO 1 /* echo */
+#define TELOPT_RCP 2 /* prepare to reconnect */
+#define TELOPT_SGA 3 /* suppress go ahead */
+#define TELOPT_NAMS 4 /* approximate message size */
+#define TELOPT_STATUS 5 /* give status */
+#define TELOPT_TM 6 /* timing mark */
+#define TELOPT_RCTE 7 /* remote controlled transmission and echo */
+#define TELOPT_NAOL 8 /* negotiate about output line width */
+#define TELOPT_NAOP 9 /* negotiate about output page size */
+#define TELOPT_NAOCRD 10 /* negotiate about CR disposition */
+#define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */
+#define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */
+#define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */
+#define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */
+#define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */
+#define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */
+#define TELOPT_XASCII 17 /* extended ascic character set */
+#define TELOPT_LOGOUT 18 /* force logout */
+#define TELOPT_BM 19 /* byte macro */
+#define TELOPT_DET 20 /* data entry terminal */
+#define TELOPT_SUPDUP 21 /* supdup protocol */
+#define TELOPT_SUPDUPOUTPUT 22 /* supdup output */
+#define TELOPT_SNDLOC 23 /* send location */
+#define TELOPT_TTYPE 24 /* terminal type */
+#define TELOPT_EOR 25 /* end or record */
+#define TELOPT_TUID 26 /* TACACS user identification */
+#define TELOPT_OUTMRK 27 /* output marking */
+#define TELOPT_TTYLOC 28 /* terminal location number */
+#define TELOPT_3270REGIME 29 /* 3270 regime */
+#define TELOPT_X3PAD 30 /* X.3 PAD */
+#define TELOPT_NAWS 31 /* window size */
+#define TELOPT_TSPEED 32 /* terminal speed */
+#define TELOPT_LFLOW 33 /* remote flow control */
+#define TELOPT_LINEMODE 34 /* Linemode option */
+#define TELOPT_XDISPLOC 35 /* X Display Location */
+#define TELOPT_OLD_ENVIRON 36 /* Old - Environment variables */
+#define TELOPT_AUTHENTICATION 37/* Authenticate */
+#define TELOPT_ENCRYPT 38 /* Encryption option */
+#define TELOPT_NEW_ENVIRON 39 /* New - Environment variables */
+#define TELOPT_EXOPL 255 /* extended-options-list */
+
+
+#define NTELOPTS (1+TELOPT_NEW_ENVIRON)
+static const char *telopts[NTELOPTS+1] = {
+ "BINARY", "ECHO", "RCP", "SUPPRESS GO AHEAD", "NAME",
+ "STATUS", "TIMING MARK", "RCTE", "NAOL", "NAOP",
+ "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD", "NAOVTS",
+ "NAOVTD", "NAOLFD", "EXTEND ASCII", "LOGOUT", "BYTE MACRO",
+ "DATA ENTRY TERMINAL", "SUPDUP", "SUPDUP OUTPUT",
+ "SEND LOCATION", "TERMINAL TYPE", "END OF RECORD",
+ "TACACS UID", "OUTPUT MARKING", "TTYLOC",
+ "3270 REGIME", "X.3 PAD", "NAWS", "TSPEED", "LFLOW",
+ "LINEMODE", "XDISPLOC", "OLD-ENVIRON", "AUTHENTICATION",
+ "ENCRYPT", "NEW-ENVIRON",
+ 0,
+};
+#define TELOPT_FIRST TELOPT_BINARY
+#define TELOPT_LAST TELOPT_NEW_ENVIRON
+#define TELOPT_OK(x) ((unsigned int)(x) <= TELOPT_LAST)
+#define TELOPT(x) telopts[(x)-TELOPT_FIRST]
+
+/* sub-option qualifiers */
+#define TELQUAL_IS 0 /* option is... */
+#define TELQUAL_SEND 1 /* send option */
+#define TELQUAL_INFO 2 /* ENVIRON: informational version of IS */
+#define TELQUAL_REPLY 2 /* AUTHENTICATION: client version of IS */
+#define TELQUAL_NAME 3 /* AUTHENTICATION: client version of IS */
+
+#define LFLOW_OFF 0 /* Disable remote flow control */
+#define LFLOW_ON 1 /* Enable remote flow control */
+#define LFLOW_RESTART_ANY 2 /* Restart output on any char */
+#define LFLOW_RESTART_XON 3 /* Restart output only on XON */
+
+/*
+ * LINEMODE suboptions
+ */
+
+#define LM_MODE 1
+#define LM_FORWARDMASK 2
+#define LM_SLC 3
+
+#define MODE_EDIT 0x01
+#define MODE_TRAPSIG 0x02
+#define MODE_ACK 0x04
+#define MODE_SOFT_TAB 0x08
+#define MODE_LIT_ECHO 0x10
+
+#define MODE_MASK 0x1f
+
+#define SLC_SYNCH 1
+#define SLC_BRK 2
+#define SLC_IP 3
+#define SLC_AO 4
+#define SLC_AYT 5
+#define SLC_EOR 6
+#define SLC_ABORT 7
+#define SLC_EOF 8
+#define SLC_SUSP 9
+#define SLC_EC 10
+#define SLC_EL 11
+#define SLC_EW 12
+#define SLC_RP 13
+#define SLC_LNEXT 14
+#define SLC_XON 15
+#define SLC_XOFF 16
+#define SLC_FORW1 17
+#define SLC_FORW2 18
+#define SLC_MCL 19
+#define SLC_MCR 20
+#define SLC_MCWL 21
+#define SLC_MCWR 22
+#define SLC_MCBOL 23
+#define SLC_MCEOL 24
+#define SLC_INSRT 25
+#define SLC_OVER 26
+#define SLC_ECR 27
+#define SLC_EWR 28
+#define SLC_EBOL 29
+#define SLC_EEOL 30
+
+#define NSLC 30
+
+/*
+ * For backwards compatibility, we define SLC_NAMES to be the
+ * list of names if SLC_NAMES is not defined.
+ */
+#define SLC_NAMELIST "0", "SYNCH", "BRK", "IP", "AO", "AYT", "EOR", \
+ "ABORT", "EOF", "SUSP", "EC", "EL", "EW", "RP", \
+ "LNEXT", "XON", "XOFF", "FORW1", "FORW2", \
+ "MCL", "MCR", "MCWL", "MCWR", "MCBOL", \
+ "MCEOL", "INSRT", "OVER", "ECR", "EWR", \
+ "EBOL", "EEOL", \
+ 0,
+
+#ifdef SLC_NAMES
+const char *slc_names[] = {
+ SLC_NAMELIST
+};
+#else
+extern char *slc_names[];
+#define SLC_NAMES SLC_NAMELIST
+#endif
+
+#define SLC_NAME_OK(x) ((unsigned int)(x) <= NSLC)
+#define SLC_NAME(x) slc_names[x]
+
+#define SLC_NOSUPPORT 0
+#define SLC_CANTCHANGE 1
+#define SLC_VARIABLE 2
+#define SLC_DEFAULT 3
+#define SLC_LEVELBITS 0x03
+
+#define SLC_FUNC 0
+#define SLC_FLAGS 1
+#define SLC_VALUE 2
+
+#define SLC_ACK 0x80
+#define SLC_FLUSHIN 0x40
+#define SLC_FLUSHOUT 0x20
+
+#define OLD_ENV_VAR 1
+#define OLD_ENV_VALUE 0
+#define NEW_ENV_VAR 0
+#define NEW_ENV_VALUE 1
+#define ENV_ESC 2
+#define ENV_USERVAR 3
+
+/*
+ * AUTHENTICATION suboptions
+ */
+
+/*
+ * Who is authenticating who ...
+ */
+#define AUTH_WHO_CLIENT 0 /* Client authenticating server */
+#define AUTH_WHO_SERVER 1 /* Server authenticating client */
+#define AUTH_WHO_MASK 1
+
+#define AUTHTYPE_NULL 0
+#define AUTHTYPE_KERBEROS_V4 1
+#define AUTHTYPE_KERBEROS_V5 2
+#define AUTHTYPE_SPX 3
+#define AUTHTYPE_MINK 4
+#define AUTHTYPE_CNT 5
+
+#define AUTHTYPE_TEST 99
+
+#ifdef AUTH_NAMES
+const char *authtype_names[] = {
+ "NULL", "KERBEROS_V4", "KERBEROS_V5", "SPX", "MINK", 0,
+};
+#else
+extern char *authtype_names[];
+#endif
+
+#define AUTHTYPE_NAME_OK(x) ((unsigned int)(x) < AUTHTYPE_CNT)
+#define AUTHTYPE_NAME(x) authtype_names[x]
+
+/*
+ * ENCRYPTion suboptions
+ */
+#define ENCRYPT_IS 0 /* I pick encryption type ... */
+#define ENCRYPT_SUPPORT 1 /* I support encryption types ... */
+#define ENCRYPT_REPLY 2 /* Initial setup response */
+#define ENCRYPT_START 3 /* Am starting to send encrypted */
+#define ENCRYPT_END 4 /* Am ending encrypted */
+#define ENCRYPT_REQSTART 5 /* Request you start encrypting */
+#define ENCRYPT_REQEND 6 /* Request you send encrypting */
+#define ENCRYPT_ENC_KEYID 7
+#define ENCRYPT_DEC_KEYID 8
+#define ENCRYPT_CNT 9
+
+#define ENCTYPE_ANY 0
+#define ENCTYPE_DES_CFB64 1
+#define ENCTYPE_DES_OFB64 2
+#define ENCTYPE_CNT 3
+
+#ifdef ENCRYPT_NAMES
+const char *encrypt_names[] = {
+ "IS", "SUPPORT", "REPLY", "START", "END",
+ "REQUEST-START", "REQUEST-END", "ENC-KEYID", "DEC-KEYID",
+ 0,
+};
+const char *enctype_names[] = {
+ "ANY", "DES_CFB64", "DES_OFB64", 0,
+};
+#else
+extern char *encrypt_names[];
+extern char *enctype_names[];
+#endif
+
+#define ENCRYPT_NAME_OK(x) ((unsigned int)(x) < ENCRYPT_CNT)
+#define ENCRYPT_NAME(x) encrypt_names[x]
+
+#define ENCTYPE_NAME_OK(x) ((unsigned int)(x) < ENCTYPE_CNT)
+#define ENCTYPE_NAME(x) enctype_names[x]
+
+/* normal */
+static const char *cmds[] = {
+ "IS", "SEND", "INFO",
+};
+
+/* 37: Authentication */
+static const char *authcmd[] = {
+ "IS", "SEND", "REPLY", "NAME",
+};
+static const char *authtype[] = {
+ "NULL", "KERBEROS_V4", "KERBEROS_V5", "SPX", "MINK",
+ "SRP", "RSA", "SSL", NULL, NULL,
+ "LOKI", "SSA", "KEA_SJ", "KEA_SJ_INTEG", "DSS",
+ "NTLM",
+};
+
+/* 38: Encryption */
+static const char *enccmd[] = {
+ "IS", "SUPPORT", "REPLY", "START", "END",
+ "REQUEST-START", "REQUEST-END", "END_KEYID", "DEC_KEYID",
+};
+static const char *enctype[] = {
+ "NULL", "DES_CFB64", "DES_OFB64", "DES3_CFB64", "DES3_OFB64",
+ NULL, "CAST5_40_CFB64", "CAST5_40_OFB64", "CAST128_CFB64", "CAST128_OFB64",
+};
+
+#define STR_OR_ID(x, tab) \
+ (((x) < sizeof(tab)/sizeof(tab[0]) && tab[(x)]) ? tab[(x)] : numstr(x))
+
+static char *
+numstr(int x)
+{
+ static char buf[20];
+
+ snprintf(buf, sizeof(buf), "%#x", x);
+ return buf;
+}
+
+/* sp points to IAC byte */
+static int
+telnet_parse(netdissect_options *ndo, const u_char *sp, u_int length, int print)
+{
+ int i, x;
+ u_int c;
+ const u_char *osp, *p;
+#define FETCH(c, sp, length) \
+ do { \
+ if (length < 1) \
+ goto pktend; \
+ c = GET_U_1(sp); \
+ sp++; \
+ length--; \
+ } while (0)
+
+ osp = sp;
+
+ FETCH(c, sp, length);
+ if (c != IAC)
+ goto pktend;
+ FETCH(c, sp, length);
+ if (c == IAC) { /* <IAC><IAC>! */
+ if (print)
+ ND_PRINT("IAC IAC");
+ goto done;
+ }
+
+ i = c - TELCMD_FIRST;
+ if (i < 0 || i > IAC - TELCMD_FIRST)
+ goto pktend;
+
+ switch (c) {
+ case DONT:
+ case DO:
+ case WONT:
+ case WILL:
+ case SB:
+ /* DONT/DO/WONT/WILL x */
+ FETCH(x, sp, length);
+ if (x >= 0 && x < NTELOPTS) {
+ if (print)
+ ND_PRINT("%s %s", telcmds[i], telopts[x]);
+ } else {
+ if (print)
+ ND_PRINT("%s %#x", telcmds[i], x);
+ }
+ if (c != SB)
+ break;
+ /* IAC SB .... IAC SE */
+ p = sp;
+ while (length > (u_int)(p + 1 - sp)) {
+ if (GET_U_1(p) == IAC && GET_U_1(p + 1) == SE)
+ break;
+ p++;
+ }
+ if (GET_U_1(p) != IAC)
+ goto pktend;
+
+ switch (x) {
+ case TELOPT_AUTHENTICATION:
+ if (p <= sp)
+ break;
+ FETCH(c, sp, length);
+ if (print)
+ ND_PRINT(" %s", STR_OR_ID(c, authcmd));
+ if (p <= sp)
+ break;
+ FETCH(c, sp, length);
+ if (print)
+ ND_PRINT(" %s", STR_OR_ID(c, authtype));
+ break;
+ case TELOPT_ENCRYPT:
+ if (p <= sp)
+ break;
+ FETCH(c, sp, length);
+ if (print)
+ ND_PRINT(" %s", STR_OR_ID(c, enccmd));
+ if (p <= sp)
+ break;
+ FETCH(c, sp, length);
+ if (print)
+ ND_PRINT(" %s", STR_OR_ID(c, enctype));
+ break;
+ default:
+ if (p <= sp)
+ break;
+ FETCH(c, sp, length);
+ if (print)
+ ND_PRINT(" %s", STR_OR_ID(c, cmds));
+ break;
+ }
+ while (p > sp) {
+ FETCH(x, sp, length);
+ if (print)
+ ND_PRINT(" %#x", x);
+ }
+ /* terminating IAC SE */
+ if (print)
+ ND_PRINT(" SE");
+ sp += 2;
+ break;
+ default:
+ if (print)
+ ND_PRINT("%s", telcmds[i]);
+ goto done;
+ }
+
+done:
+ return (int)(sp - osp);
+
+pktend:
+ return -1;
+#undef FETCH
+}
+
+void
+telnet_print(netdissect_options *ndo, const u_char *sp, u_int length)
+{
+ int first = 1;
+ const u_char *osp;
+ int l;
+
+ ndo->ndo_protocol = "telnet";
+ osp = sp;
+
+ while (length > 0 && GET_U_1(sp) == IAC) {
+ /*
+ * Parse the Telnet command without printing it,
+ * to determine its length.
+ */
+ l = telnet_parse(ndo, sp, length, 0);
+ if (l < 0)
+ break;
+
+ /*
+ * now print it
+ */
+ if (ndo->ndo_Xflag && 2 < ndo->ndo_vflag) {
+ if (first)
+ ND_PRINT("\nTelnet:");
+ hex_print_with_offset(ndo, "\n", sp, l, (u_int)(sp - osp));
+ if (l > 8)
+ ND_PRINT("\n\t\t\t\t");
+ else
+ ND_PRINT("%*s\t", (8 - l) * 3, "");
+ } else
+ ND_PRINT("%s", (first) ? " [telnet " : ", ");
+
+ (void)telnet_parse(ndo, sp, length, 1);
+ first = 0;
+
+ sp += l;
+ length -= l;
+ }
+ if (!first) {
+ if (ndo->ndo_Xflag && 2 < ndo->ndo_vflag)
+ ND_PRINT("\n");
+ else
+ ND_PRINT("]");
+ }
+}
diff --git a/print-tftp.c b/print-tftp.c
new file mode 100644
index 0000000..39fc696
--- /dev/null
+++ b/print-tftp.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Trivial File Transfer Protocol (TFTP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+/*
+ * Trivial File Transfer Protocol (IEN-133)
+ */
+
+/*
+ * Packet types.
+ */
+#define RRQ 01 /* read request */
+#define WRQ 02 /* write request */
+#define DATA 03 /* data packet */
+#define ACK 04 /* acknowledgement */
+#define TFTP_ERROR 05 /* error code */
+#define OACK 06 /* option acknowledgement */
+
+/*
+ * Error codes.
+ */
+#define EUNDEF 0 /* not defined */
+#define ENOTFOUND 1 /* file not found */
+#define EACCESS 2 /* access violation */
+#define ENOSPACE 3 /* disk full or allocation exceeded */
+#define EBADOP 4 /* illegal TFTP operation */
+#define EBADID 5 /* unknown transfer ID */
+#define EEXISTS 6 /* file already exists */
+#define ENOUSER 7 /* no such user */
+
+
+/* op code to string mapping */
+static const struct tok op2str[] = {
+ { RRQ, "RRQ" }, /* read request */
+ { WRQ, "WRQ" }, /* write request */
+ { DATA, "DATA" }, /* data packet */
+ { ACK, "ACK" }, /* acknowledgement */
+ { TFTP_ERROR, "ERROR" }, /* error code */
+ { OACK, "OACK" }, /* option acknowledgement */
+ { 0, NULL }
+};
+
+/* error code to string mapping */
+static const struct tok err2str[] = {
+ { EUNDEF, "EUNDEF" }, /* not defined */
+ { ENOTFOUND, "ENOTFOUND" }, /* file not found */
+ { EACCESS, "EACCESS" }, /* access violation */
+ { ENOSPACE, "ENOSPACE" }, /* disk full or allocation exceeded */
+ { EBADOP, "EBADOP" }, /* illegal TFTP operation */
+ { EBADID, "EBADID" }, /* unknown transfer ID */
+ { EEXISTS, "EEXISTS" }, /* file already exists */
+ { ENOUSER, "ENOUSER" }, /* no such user */
+ { 0, NULL }
+};
+
+/*
+ * Print trivial file transfer program requests
+ */
+void
+tftp_print(netdissect_options *ndo,
+ const u_char *bp, u_int length)
+{
+ const char *cp;
+ u_int opcode;
+ u_int ui;
+
+ ndo->ndo_protocol = "tftp";
+
+ /* Print protocol */
+ nd_print_protocol_caps(ndo);
+ /* Print length */
+ ND_PRINT(", length %u", length);
+
+ /* Print tftp request type */
+ if (length < 2)
+ goto trunc;
+ opcode = GET_BE_U_2(bp);
+ cp = tok2str(op2str, "tftp-#%u", opcode);
+ ND_PRINT(", %s", cp);
+ /* Bail if bogus opcode */
+ if (*cp == 't')
+ return;
+ bp += 2;
+ length -= 2;
+
+ switch (opcode) {
+
+ case RRQ:
+ case WRQ:
+ if (length == 0)
+ goto trunc;
+ ND_PRINT(" ");
+ /* Print filename */
+ ND_PRINT("\"");
+ ui = nd_printztn(ndo, bp, length, ndo->ndo_snapend);
+ ND_PRINT("\"");
+ if (ui == 0)
+ goto trunc;
+ bp += ui;
+ length -= ui;
+
+ /* Print the mode - RRQ and WRQ only */
+ if (length == 0)
+ goto trunc; /* no mode */
+ ND_PRINT(" ");
+ ui = nd_printztn(ndo, bp, length, ndo->ndo_snapend);
+ if (ui == 0)
+ goto trunc;
+ bp += ui;
+ length -= ui;
+
+ /* Print options, if any */
+ while (length != 0) {
+ if (GET_U_1(bp) != '\0')
+ ND_PRINT(" ");
+ ui = nd_printztn(ndo, bp, length, ndo->ndo_snapend);
+ if (ui == 0)
+ goto trunc;
+ bp += ui;
+ length -= ui;
+ }
+ break;
+
+ case OACK:
+ /* Print options */
+ while (length != 0) {
+ if (GET_U_1(bp) != '\0')
+ ND_PRINT(" ");
+ ui = nd_printztn(ndo, bp, length, ndo->ndo_snapend);
+ if (ui == 0)
+ goto trunc;
+ bp += ui;
+ length -= ui;
+ }
+ break;
+
+ case ACK:
+ case DATA:
+ if (length < 2)
+ goto trunc; /* no block number */
+ ND_PRINT(" block %u", GET_BE_U_2(bp));
+ break;
+
+ case TFTP_ERROR:
+ /* Print error code string */
+ if (length < 2)
+ goto trunc; /* no error code */
+ ND_PRINT(" %s", tok2str(err2str, "tftp-err-#%u \"",
+ GET_BE_U_2(bp)));
+ bp += 2;
+ length -= 2;
+ /* Print error message string */
+ if (length == 0)
+ goto trunc; /* no error message */
+ ND_PRINT(" \"");
+ ui = nd_printztn(ndo, bp, length, ndo->ndo_snapend);
+ ND_PRINT("\"");
+ if (ui == 0)
+ goto trunc;
+ break;
+
+ default:
+ /* We shouldn't get here */
+ ND_PRINT("(unknown #%u)", opcode);
+ break;
+ }
+ return;
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-timed.c b/print-timed.c
new file mode 100644
index 0000000..ebd0ac8
--- /dev/null
+++ b/print-timed.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2000 Ben Smithurst <ben@scientia.demon.co.uk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Berkeley UNIX Time Synchronization Protocol */
+
+/* specification: https://docs.freebsd.org/44doc/smm/12.timed/paper.pdf */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+struct tsp_timeval {
+ nd_int32_t tv_sec;
+ nd_int32_t tv_usec;
+};
+
+struct tsp {
+ nd_uint8_t tsp_type;
+ nd_uint8_t tsp_vers;
+ nd_uint16_t tsp_seq;
+ union {
+ struct tsp_timeval tspu_time;
+ nd_int8_t tspu_hopcnt;
+ } tsp_u;
+ nd_byte tsp_name[256]; /* null-terminated string up to 256 */
+};
+
+#define tsp_time tsp_u.tspu_time
+#define tsp_hopcnt tsp_u.tspu_hopcnt
+
+/*
+ * Command types.
+ */
+#define TSP_ANY 0 /* match any types */
+#define TSP_ADJTIME 1 /* send adjtime */
+#define TSP_ACK 2 /* generic acknowledgement */
+#define TSP_MASTERREQ 3 /* ask for master's name */
+#define TSP_MASTERACK 4 /* acknowledge master request */
+#define TSP_SETTIME 5 /* send network time */
+#define TSP_MASTERUP 6 /* inform slaves that master is up */
+#define TSP_SLAVEUP 7 /* slave is up but not polled */
+#define TSP_ELECTION 8 /* advance candidature for master */
+#define TSP_ACCEPT 9 /* support candidature of master */
+#define TSP_REFUSE 10 /* reject candidature of master */
+#define TSP_CONFLICT 11 /* two or more masters present */
+#define TSP_RESOLVE 12 /* masters' conflict resolution */
+#define TSP_QUIT 13 /* reject candidature if master is up */
+#define TSP_DATE 14 /* reset the time (date command) */
+#define TSP_DATEREQ 15 /* remote request to reset the time */
+#define TSP_DATEACK 16 /* acknowledge time setting */
+#define TSP_TRACEON 17 /* turn tracing on */
+#define TSP_TRACEOFF 18 /* turn tracing off */
+#define TSP_MSITE 19 /* find out master's site */
+#define TSP_MSITEREQ 20 /* remote master's site request */
+#define TSP_TEST 21 /* for testing election algo */
+#define TSP_SETDATE 22 /* New from date command */
+#define TSP_SETDATEREQ 23 /* New remote for above */
+#define TSP_LOOP 24 /* loop detection packet */
+static const struct tok tsptype_str[] = {
+ { TSP_ANY, "TSP_ANY" },
+ { TSP_ADJTIME, "TSP_ADJTIME" },
+ { TSP_ACK, "TSP_ACK" },
+ { TSP_MASTERREQ, "TSP_MASTERREQ" },
+ { TSP_MASTERACK, "TSP_MASTERACK" },
+ { TSP_SETTIME, "TSP_SETTIME" },
+ { TSP_MASTERUP, "TSP_MASTERUP" },
+ { TSP_SLAVEUP, "TSP_SLAVEUP" },
+ { TSP_ELECTION, "TSP_ELECTION" },
+ { TSP_ACCEPT, "TSP_ACCEPT" },
+ { TSP_REFUSE, "TSP_REFUSE" },
+ { TSP_CONFLICT, "TSP_CONFLICT" },
+ { TSP_RESOLVE, "TSP_RESOLVE" },
+ { TSP_QUIT, "TSP_QUIT" },
+ { TSP_DATE, "TSP_DATE" },
+ { TSP_DATEREQ, "TSP_DATEREQ" },
+ { TSP_DATEACK, "TSP_DATEACK" },
+ { TSP_TRACEON, "TSP_TRACEON" },
+ { TSP_TRACEOFF, "TSP_TRACEOFF" },
+ { TSP_MSITE, "TSP_MSITE" },
+ { TSP_MSITEREQ, "TSP_MSITEREQ" },
+ { TSP_TEST, "TSP_TEST" },
+ { TSP_SETDATE, "TSP_SETDATE" },
+ { TSP_SETDATEREQ, "TSP_SETDATEREQ" },
+ { TSP_LOOP, "TSP_LOOP" },
+ { 0, NULL }
+};
+
+void
+timed_print(netdissect_options *ndo,
+ const u_char *bp)
+{
+ const struct tsp *tsp = (const struct tsp *)bp;
+ uint8_t tsp_type;
+ int sec, usec;
+
+ ndo->ndo_protocol = "timed";
+ tsp_type = GET_U_1(tsp->tsp_type);
+ ND_PRINT("%s", tok2str(tsptype_str, "(tsp_type %#x)", tsp_type));
+
+ ND_PRINT(" vers %u", GET_U_1(tsp->tsp_vers));
+
+ ND_PRINT(" seq %u", GET_BE_U_2(tsp->tsp_seq));
+
+ switch (tsp_type) {
+ case TSP_LOOP:
+ ND_PRINT(" hopcnt %u", GET_U_1(tsp->tsp_hopcnt));
+ break;
+ case TSP_SETTIME:
+ case TSP_ADJTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ sec = GET_BE_S_4(tsp->tsp_time.tv_sec);
+ usec = GET_BE_S_4(tsp->tsp_time.tv_usec);
+ /* XXX The comparison below is always false? */
+ if (usec < 0)
+ /* invalid, skip the rest of the packet */
+ return;
+ ND_PRINT(" time ");
+ if (sec < 0 && usec != 0) {
+ sec++;
+ if (sec == 0)
+ ND_PRINT("-");
+ usec = 1000000 - usec;
+ }
+ ND_PRINT("%d.%06d", sec, usec);
+ break;
+ }
+ ND_PRINT(" name ");
+ nd_printjnp(ndo, tsp->tsp_name, sizeof(tsp->tsp_name));
+}
diff --git a/print-tipc.c b/print-tipc.c
new file mode 100644
index 0000000..54179a4
--- /dev/null
+++ b/print-tipc.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Transparent Inter-Process Communication (TIPC) protocol printer */
+
+/*
+ * specification:
+ * https://web.archive.org/web/20150302152944/http://tipc.sourceforge.net/doc/draft-spec-tipc-07.html
+ * https://web.archive.org/web/20161025110514/http://tipc.sourceforge.net/doc/tipc_message_formats.html
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "ethertype.h"
+#include "extract.h"
+
+
+#define TIPC_USER_LOW_IMPORTANCE 0
+#define TIPC_USER_MEDIUM_IMPORTANCE 1
+#define TIPC_USER_HIGH_IMPORTANCE 2
+#define TIPC_USER_CRITICAL_IMPORTANCE 3
+#define TIPC_USER_BCAST_PROTOCOL 5
+#define TIPC_USER_MSG_BUNDLER 6
+#define TIPC_USER_LINK_PROTOCOL 7
+#define TIPC_USER_CONN_MANAGER 8
+#define TIPC_USER_CHANGEOVER_PROTOCOL 10
+#define TIPC_USER_NAME_DISTRIBUTOR 11
+#define TIPC_USER_MSG_FRAGMENTER 12
+#define TIPC_USER_LINK_CONFIG 13
+
+#define TIPC_CONN_MSG 0
+#define TIPC_DIRECT_MSG 1
+#define TIPC_NAMED_MSG 2
+#define TIPC_MCAST_MSG 3
+
+#define TIPC_ZONE(addr) (((addr) >> 24) & 0xFF)
+#define TIPC_CLUSTER(addr) (((addr) >> 12) & 0xFFF)
+#define TIPC_NODE(addr) (((addr) >> 0) & 0xFFF)
+
+struct tipc_pkthdr {
+ nd_uint32_t w0;
+ nd_uint32_t w1;
+};
+
+#define TIPC_VER(w0) (((w0) >> 29) & 0x07)
+#define TIPC_USER(w0) (((w0) >> 25) & 0x0F)
+#define TIPC_HSIZE(w0) (((w0) >> 21) & 0x0F)
+#define TIPC_MSIZE(w0) (((w0) >> 0) & 0x1FFFF)
+#define TIPC_MTYPE(w1) (((w1) >> 29) & 0x07)
+#define TIPC_BROADCAST_ACK(w1) (((w1) >> 0) & 0xFFFF)
+#define TIPC_LINK_ACK(w2) (((w2) >> 16) & 0xFFFF)
+#define TIPC_LINK_SEQ(w2) (((w2) >> 0) & 0xFFFF)
+
+static const struct tok tipcuser_values[] = {
+ { TIPC_USER_LOW_IMPORTANCE, "Low Importance Data payload" },
+ { TIPC_USER_MEDIUM_IMPORTANCE, "Medium Importance Data payload" },
+ { TIPC_USER_HIGH_IMPORTANCE, "High Importance Data payload" },
+ { TIPC_USER_CRITICAL_IMPORTANCE, "Critical Importance Data payload" },
+ { TIPC_USER_BCAST_PROTOCOL, "Broadcast Link Protocol internal" },
+ { TIPC_USER_MSG_BUNDLER, "Message Bundler Protocol internal" },
+ { TIPC_USER_LINK_PROTOCOL, "Link State Protocol internal" },
+ { TIPC_USER_CONN_MANAGER, "Connection Manager internal" },
+ { TIPC_USER_CHANGEOVER_PROTOCOL, "Link Changeover Protocol internal" },
+ { TIPC_USER_NAME_DISTRIBUTOR, "Name Table Update Protocol internal" },
+ { TIPC_USER_MSG_FRAGMENTER, "Message Fragmentation Protocol internal" },
+ { TIPC_USER_LINK_CONFIG, "Neighbor Detection Protocol internal" },
+ { 0, NULL }
+};
+
+static const struct tok tipcmtype_values[] = {
+ { TIPC_CONN_MSG, "CONN_MSG" },
+ { TIPC_DIRECT_MSG, "MCAST_MSG" },
+ { TIPC_NAMED_MSG, "NAMED_MSG" },
+ { TIPC_MCAST_MSG, "DIRECT_MSG" },
+ { 0, NULL }
+};
+
+static const struct tok tipc_linkconf_mtype_values[] = {
+ { 0, "Link request" },
+ { 1, "Link response" },
+ { 0, NULL }
+};
+
+struct payload_tipc_pkthdr {
+ nd_uint32_t w0;
+ nd_uint32_t w1;
+ nd_uint32_t w2;
+ nd_uint32_t prev_node;
+ nd_uint32_t orig_port;
+ nd_uint32_t dest_port;
+ nd_uint32_t orig_node;
+ nd_uint32_t dest_node;
+ nd_uint32_t name_type;
+ nd_uint32_t w9;
+ nd_uint32_t wA;
+};
+
+struct internal_tipc_pkthdr {
+ nd_uint32_t w0;
+ nd_uint32_t w1;
+ nd_uint32_t w2;
+ nd_uint32_t prev_node;
+ nd_uint32_t w4;
+ nd_uint32_t w5;
+ nd_uint32_t orig_node;
+ nd_uint32_t dest_node;
+ nd_uint32_t trans_seq;
+ nd_uint32_t w9;
+};
+
+#define TIPC_SEQ_GAP(w1) (((w1) >> 16) & 0x1FFF)
+#define TIPC_BC_GAP_AFTER(w2) (((w2) >> 16) & 0xFFFF)
+#define TIPC_BC_GAP_TO(w2) (((w2) >> 0) & 0xFFFF)
+#define TIPC_LAST_SENT_FRAG(w4) (((w4) >> 16) & 0xFFFF)
+#define TIPC_NEXT_SENT_FRAG(w4) (((w4) >> 0) & 0xFFFF)
+#define TIPC_SESS_NO(w5) (((w5) >> 16) & 0xFFFF)
+#define TIPC_MSG_CNT(w9) (((w9) >> 16) & 0xFFFF)
+#define TIPC_LINK_TOL(w9) (((w9) >> 0) & 0xFFFF)
+
+struct link_conf_tipc_pkthdr {
+ nd_uint32_t w0;
+ nd_uint32_t w1;
+ nd_uint32_t dest_domain;
+ nd_uint32_t prev_node;
+ nd_uint32_t ntwrk_id;
+ nd_uint32_t w5;
+ nd_byte media_address[16];
+};
+
+#define TIPC_NODE_SIG(w1) (((w1) >> 0) & 0xFFFF)
+#define TIPC_MEDIA_ID(w5) (((w5) >> 0) & 0xFF)
+
+static void
+print_payload(netdissect_options *ndo, const struct payload_tipc_pkthdr *ap)
+{
+ uint32_t w0, w1, w2;
+ u_int user;
+ u_int hsize;
+ u_int msize;
+ u_int mtype;
+ u_int broadcast_ack;
+ u_int link_ack;
+ u_int link_seq;
+ u_int prev_node;
+ u_int orig_port;
+ u_int dest_port;
+ u_int orig_node;
+ u_int dest_node;
+
+ w0 = GET_BE_U_4(ap->w0);
+ user = TIPC_USER(w0);
+ hsize = TIPC_HSIZE(w0);
+ msize = TIPC_MSIZE(w0);
+ w1 = GET_BE_U_4(ap->w1);
+ mtype = TIPC_MTYPE(w1);
+ prev_node = GET_BE_U_4(ap->prev_node);
+ orig_port = GET_BE_U_4(ap->orig_port);
+ dest_port = GET_BE_U_4(ap->dest_port);
+ if (hsize <= 6) {
+ ND_PRINT("TIPC v%u.0 %u.%u.%u:%u > %u, headerlength %u bytes, MessageSize %u bytes, %s, messageType %s",
+ TIPC_VER(w0),
+ TIPC_ZONE(prev_node), TIPC_CLUSTER(prev_node), TIPC_NODE(prev_node),
+ orig_port, dest_port,
+ hsize*4, msize,
+ tok2str(tipcuser_values, "unknown", user),
+ tok2str(tipcmtype_values, "Unknown", mtype));
+ } else {
+ orig_node = GET_BE_U_4(ap->orig_node);
+ dest_node = GET_BE_U_4(ap->dest_node);
+ ND_PRINT("TIPC v%u.0 %u.%u.%u:%u > %u.%u.%u:%u, headerlength %u bytes, MessageSize %u bytes, %s, messageType %s",
+ TIPC_VER(w0),
+ TIPC_ZONE(orig_node), TIPC_CLUSTER(orig_node), TIPC_NODE(orig_node),
+ orig_port,
+ TIPC_ZONE(dest_node), TIPC_CLUSTER(dest_node), TIPC_NODE(dest_node),
+ dest_port,
+ hsize*4, msize,
+ tok2str(tipcuser_values, "unknown", user),
+ tok2str(tipcmtype_values, "Unknown", mtype));
+
+ if (ndo->ndo_vflag) {
+ broadcast_ack = TIPC_BROADCAST_ACK(w1);
+ w2 = GET_BE_U_4(ap->w2);
+ link_ack = TIPC_LINK_ACK(w2);
+ link_seq = TIPC_LINK_SEQ(w2);
+ ND_PRINT("\n\tPrevious Node %u.%u.%u, Broadcast Ack %u, Link Ack %u, Link Sequence %u",
+ TIPC_ZONE(prev_node), TIPC_CLUSTER(prev_node), TIPC_NODE(prev_node),
+ broadcast_ack, link_ack, link_seq);
+ }
+ }
+}
+
+static void
+print_internal(netdissect_options *ndo, const struct internal_tipc_pkthdr *ap)
+{
+ uint32_t w0, w1, w2, w4, w5, w9;
+ u_int user;
+ u_int hsize;
+ u_int msize;
+ u_int mtype;
+ u_int seq_gap;
+ u_int broadcast_ack;
+ u_int bc_gap_after;
+ u_int bc_gap_to;
+ u_int prev_node;
+ u_int last_sent_frag;
+ u_int next_sent_frag;
+ u_int sess_no;
+ u_int orig_node;
+ u_int dest_node;
+ u_int trans_seq;
+ u_int msg_cnt;
+ u_int link_tol;
+
+ w0 = GET_BE_U_4(ap->w0);
+ user = TIPC_USER(w0);
+ hsize = TIPC_HSIZE(w0);
+ msize = TIPC_MSIZE(w0);
+ w1 = GET_BE_U_4(ap->w1);
+ mtype = TIPC_MTYPE(w1);
+ orig_node = GET_BE_U_4(ap->orig_node);
+ dest_node = GET_BE_U_4(ap->dest_node);
+ ND_PRINT("TIPC v%u.0 %u.%u.%u > %u.%u.%u, headerlength %u bytes, MessageSize %u bytes, %s, messageType %s (0x%08x)",
+ TIPC_VER(w0),
+ TIPC_ZONE(orig_node), TIPC_CLUSTER(orig_node), TIPC_NODE(orig_node),
+ TIPC_ZONE(dest_node), TIPC_CLUSTER(dest_node), TIPC_NODE(dest_node),
+ hsize*4, msize,
+ tok2str(tipcuser_values, "unknown", user),
+ tok2str(tipcmtype_values, "Unknown", mtype), w1);
+
+ if (ndo->ndo_vflag) {
+ seq_gap = TIPC_SEQ_GAP(w1);
+ broadcast_ack = TIPC_BROADCAST_ACK(w1);
+ w2 = GET_BE_U_4(ap->w2);
+ bc_gap_after = TIPC_BC_GAP_AFTER(w2);
+ bc_gap_to = TIPC_BC_GAP_TO(w2);
+ prev_node = GET_BE_U_4(ap->prev_node);
+ w4 = GET_BE_U_4(ap->w4);
+ last_sent_frag = TIPC_LAST_SENT_FRAG(w4);
+ next_sent_frag = TIPC_NEXT_SENT_FRAG(w4);
+ w5 = GET_BE_U_4(ap->w5);
+ sess_no = TIPC_SESS_NO(w5);
+ trans_seq = GET_BE_U_4(ap->trans_seq);
+ w9 = GET_BE_U_4(ap->w9);
+ msg_cnt = TIPC_MSG_CNT(w9);
+ link_tol = TIPC_LINK_TOL(w9);
+ ND_PRINT("\n\tPrevious Node %u.%u.%u, Session No. %u, Broadcast Ack %u, Sequence Gap %u, Broadcast Gap After %u, Broadcast Gap To %u, Last Sent Packet No. %u, Next sent Packet No. %u, Transport Sequence %u, msg_count %u, Link Tolerance %u",
+ TIPC_ZONE(prev_node), TIPC_CLUSTER(prev_node), TIPC_NODE(prev_node),
+ sess_no, broadcast_ack, seq_gap, bc_gap_after, bc_gap_to,
+ last_sent_frag, next_sent_frag, trans_seq, msg_cnt,
+ link_tol);
+ }
+}
+
+static void
+print_link_conf(netdissect_options *ndo, const struct link_conf_tipc_pkthdr *ap)
+{
+ uint32_t w0, w1, w5;
+ u_int user;
+ u_int hsize;
+ u_int msize;
+ u_int mtype;
+ u_int node_sig;
+ u_int prev_node;
+ u_int dest_domain;
+ u_int ntwrk_id;
+ u_int media_id;
+
+ w0 = GET_BE_U_4(ap->w0);
+ user = TIPC_USER(w0);
+ hsize = TIPC_HSIZE(w0);
+ msize = TIPC_MSIZE(w0);
+ w1 = GET_BE_U_4(ap->w1);
+ mtype = TIPC_MTYPE(w1);
+ dest_domain = GET_BE_U_4(ap->dest_domain);
+ prev_node = GET_BE_U_4(ap->prev_node);
+
+ ND_PRINT("TIPC v%u.0 %u.%u.%u > %u.%u.%u, headerlength %u bytes, MessageSize %u bytes, %s, messageType %s",
+ TIPC_VER(w0),
+ TIPC_ZONE(prev_node), TIPC_CLUSTER(prev_node), TIPC_NODE(prev_node),
+ TIPC_ZONE(dest_domain), TIPC_CLUSTER(dest_domain), TIPC_NODE(dest_domain),
+ hsize*4, msize,
+ tok2str(tipcuser_values, "unknown", user),
+ tok2str(tipc_linkconf_mtype_values, "Unknown", mtype));
+ if (ndo->ndo_vflag) {
+ node_sig = TIPC_NODE_SIG(w1);
+ ntwrk_id = GET_BE_U_4(ap->ntwrk_id);
+ w5 = GET_BE_U_4(ap->w5);
+ media_id = TIPC_MEDIA_ID(w5);
+ ND_PRINT("\n\tNodeSignature %u, network_id %u, media_id %u",
+ node_sig, ntwrk_id, media_id);
+ }
+}
+
+void
+tipc_print(netdissect_options *ndo, const u_char *bp, u_int length _U_,
+ u_int caplen _U_)
+{
+ const struct tipc_pkthdr *ap;
+ uint32_t w0;
+ u_int user;
+
+ ndo->ndo_protocol = "tipc";
+ ap = (const struct tipc_pkthdr *)bp;
+ w0 = GET_BE_U_4(ap->w0);
+ user = TIPC_USER(w0);
+
+ switch (user)
+ {
+ case TIPC_USER_LOW_IMPORTANCE:
+ case TIPC_USER_MEDIUM_IMPORTANCE:
+ case TIPC_USER_HIGH_IMPORTANCE:
+ case TIPC_USER_CRITICAL_IMPORTANCE:
+ case TIPC_USER_NAME_DISTRIBUTOR:
+ case TIPC_USER_CONN_MANAGER:
+ print_payload(ndo, (const struct payload_tipc_pkthdr *)bp);
+ break;
+
+ case TIPC_USER_LINK_CONFIG:
+ print_link_conf(ndo, (const struct link_conf_tipc_pkthdr *)bp);
+ break;
+
+ case TIPC_USER_BCAST_PROTOCOL:
+ case TIPC_USER_MSG_BUNDLER:
+ case TIPC_USER_LINK_PROTOCOL:
+ case TIPC_USER_CHANGEOVER_PROTOCOL:
+ case TIPC_USER_MSG_FRAGMENTER:
+ print_internal(ndo, (const struct internal_tipc_pkthdr *)bp);
+ break;
+
+ }
+}
diff --git a/print-token.c b/print-token.c
new file mode 100644
index 0000000..bcb7258
--- /dev/null
+++ b/print-token.c
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Hacked version of print-ether.c Larry Lile <lile@stdio.com>
+ *
+ * Further tweaked to more closely resemble print-fddi.c
+ * Guy Harris <guy@alum.mit.edu>
+ */
+
+/* \summary: Token Ring printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+/*
+ * Copyright (c) 1998, Larry Lile
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions, and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#define TOKEN_HDRLEN 14
+#define ROUTING_SEGMENT_MAX 16
+#define IS_SOURCE_ROUTED(trp) ((trp)->token_shost[0] & 0x80)
+#define FRAME_TYPE(trp) ((GET_U_1((trp)->token_fc) & 0xC0) >> 6)
+#define TOKEN_FC_LLC 1
+
+#define BROADCAST(trp) ((GET_BE_U_2((trp)->token_rcf) & 0xE000) >> 13)
+#define RIF_LENGTH(trp) ((GET_BE_U_2((trp)->token_rcf) & 0x1f00) >> 8)
+#define DIRECTION(trp) ((GET_BE_U_2((trp)->token_rcf) & 0x0080) >> 7)
+#define LARGEST_FRAME(trp) ((GET_BE_U_2((trp)->token_rcf) & 0x0070) >> 4)
+#define RING_NUMBER(trp, x) ((GET_BE_U_2((trp)->token_rseg[x]) & 0xfff0) >> 4)
+#define BRIDGE_NUMBER(trp, x) (GET_BE_U_2((trp)->token_rseg[x]) & 0x000f)
+#define SEGMENT_COUNT(trp) ((int)((RIF_LENGTH(trp) - 2) / 2))
+
+struct token_header {
+ nd_uint8_t token_ac;
+ nd_uint8_t token_fc;
+ nd_mac_addr token_dhost;
+ nd_mac_addr token_shost;
+ nd_uint16_t token_rcf;
+ nd_uint16_t token_rseg[ROUTING_SEGMENT_MAX];
+};
+
+
+/* Extract src, dst addresses */
+static void
+extract_token_addrs(const struct token_header *trp, char *fsrc, char *fdst)
+{
+ memcpy(fdst, (const char *)trp->token_dhost, 6);
+ memcpy(fsrc, (const char *)trp->token_shost, 6);
+}
+
+/*
+ * Print the TR MAC header
+ */
+static void
+token_hdr_print(netdissect_options *ndo,
+ const struct token_header *trp, u_int length,
+ const u_char *fsrc, const u_char *fdst)
+{
+ const char *srcname, *dstname;
+
+ srcname = etheraddr_string(ndo, fsrc);
+ dstname = etheraddr_string(ndo, fdst);
+
+ if (!ndo->ndo_qflag)
+ ND_PRINT("%02x %02x ",
+ GET_U_1(trp->token_ac),
+ GET_U_1(trp->token_fc));
+ ND_PRINT("%s > %s, length %u: ",
+ srcname, dstname,
+ length);
+}
+
+static const char *broadcast_indicator[] = {
+ "Non-Broadcast", "Non-Broadcast",
+ "Non-Broadcast", "Non-Broadcast",
+ "All-routes", "All-routes",
+ "Single-route", "Single-route"
+};
+
+static const char *direction[] = {
+ "Forward", "Backward"
+};
+
+static const char *largest_frame[] = {
+ "516",
+ "1500",
+ "2052",
+ "4472",
+ "8144",
+ "11407",
+ "17800",
+ "??"
+};
+
+u_int
+token_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen)
+{
+ const struct token_header *trp;
+ int llc_hdrlen;
+ nd_mac_addr srcmac, dstmac;
+ struct lladdr_info src, dst;
+ u_int route_len = 0, hdr_len = TOKEN_HDRLEN;
+ int seg;
+
+ ndo->ndo_protocol = "token-ring";
+ trp = (const struct token_header *)p;
+
+ if (caplen < TOKEN_HDRLEN) {
+ nd_print_trunc(ndo);
+ return hdr_len;
+ }
+
+ /*
+ * Get the TR addresses into a canonical form
+ */
+ extract_token_addrs(trp, (char*)srcmac, (char*)dstmac);
+
+ /* Adjust for source routing information in the MAC header */
+ if (IS_SOURCE_ROUTED(trp)) {
+ /* Clear source-routed bit */
+ srcmac[0] &= 0x7f;
+
+ if (ndo->ndo_eflag)
+ token_hdr_print(ndo, trp, length, srcmac, dstmac);
+
+ if (caplen < TOKEN_HDRLEN + 2) {
+ nd_print_trunc(ndo);
+ return hdr_len;
+ }
+ route_len = RIF_LENGTH(trp);
+ hdr_len += route_len;
+ if (caplen < hdr_len) {
+ nd_print_trunc(ndo);
+ return hdr_len;
+ }
+ if (ndo->ndo_vflag) {
+ ND_PRINT("%s ", broadcast_indicator[BROADCAST(trp)]);
+ ND_PRINT("%s", direction[DIRECTION(trp)]);
+
+ for (seg = 0; seg < SEGMENT_COUNT(trp); seg++)
+ ND_PRINT(" [%u:%u]", RING_NUMBER(trp, seg),
+ BRIDGE_NUMBER(trp, seg));
+ } else {
+ ND_PRINT("rt = %x", GET_BE_U_2(trp->token_rcf));
+
+ for (seg = 0; seg < SEGMENT_COUNT(trp); seg++)
+ ND_PRINT(":%x",
+ GET_BE_U_2(trp->token_rseg[seg]));
+ }
+ ND_PRINT(" (%s) ", largest_frame[LARGEST_FRAME(trp)]);
+ } else {
+ if (ndo->ndo_eflag)
+ token_hdr_print(ndo, trp, length, srcmac, dstmac);
+ }
+
+ src.addr = srcmac;
+ src.addr_string = etheraddr_string;
+ dst.addr = dstmac;
+ dst.addr_string = etheraddr_string;
+
+ /* Skip over token ring MAC header and routing information */
+ length -= hdr_len;
+ p += hdr_len;
+ caplen -= hdr_len;
+
+ /* Frame Control field determines interpretation of packet */
+ if (FRAME_TYPE(trp) == TOKEN_FC_LLC) {
+ /* Try to print the LLC-layer header & higher layers */
+ llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst);
+ if (llc_hdrlen < 0) {
+ /* packet type not known, print raw packet */
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ llc_hdrlen = -llc_hdrlen;
+ }
+ hdr_len += llc_hdrlen;
+ } else {
+ /* Some kinds of TR packet we cannot handle intelligently */
+ /* XXX - dissect MAC packets if frame type is 0 */
+ if (!ndo->ndo_eflag)
+ token_hdr_print(ndo, trp, length + TOKEN_HDRLEN + route_len,
+ srcmac, dstmac);
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(p, caplen);
+ }
+ return (hdr_len);
+}
+
+/*
+ * This is the top level routine of the printer. 'p' points
+ * to the TR header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+token_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h, const u_char *p)
+{
+ ndo->ndo_protocol = "token-ring";
+ ndo->ndo_ll_hdr_len += token_print(ndo, p, h->len, h->caplen);
+}
diff --git a/print-udld.c b/print-udld.c
new file mode 100644
index 0000000..aec1d9e
--- /dev/null
+++ b/print-udld.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 1998-2007 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Carles Kishimoto <carles.kishimoto@gmail.com>
+ */
+
+/* \summary: Cisco UniDirectional Link Detection (UDLD) protocol printer */
+
+/* specification: RFC 5171 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+
+#define UDLD_HEADER_LEN 4
+#define UDLD_TLV_HEADER_LEN 4
+#define UDLD_DEVICE_ID_TLV 0x0001
+#define UDLD_PORT_ID_TLV 0x0002
+#define UDLD_ECHO_TLV 0x0003
+#define UDLD_MESSAGE_INTERVAL_TLV 0x0004
+#define UDLD_TIMEOUT_INTERVAL_TLV 0x0005
+#define UDLD_DEVICE_NAME_TLV 0x0006
+#define UDLD_SEQ_NUMBER_TLV 0x0007
+
+static const struct tok udld_tlv_values[] = {
+ { UDLD_DEVICE_ID_TLV, "Device-ID TLV"},
+ { UDLD_PORT_ID_TLV, "Port-ID TLV"},
+ { UDLD_ECHO_TLV, "Echo TLV"},
+ { UDLD_MESSAGE_INTERVAL_TLV, "Message Interval TLV"},
+ { UDLD_TIMEOUT_INTERVAL_TLV, "Timeout Interval TLV"},
+ { UDLD_DEVICE_NAME_TLV, "Device Name TLV"},
+ { UDLD_SEQ_NUMBER_TLV,"Sequence Number TLV"},
+ { 0, NULL}
+};
+
+static const struct tok udld_code_values[] = {
+ { 0x00, "Reserved"},
+ { 0x01, "Probe message"},
+ { 0x02, "Echo message"},
+ { 0x03, "Flush message"},
+ { 0, NULL}
+};
+
+static const struct tok udld_flags_bitmap_str[] = {
+ { 1U << 0, "RT" },
+ { 1U << 1, "RSY" },
+ { 1U << 2, "MBZ-2" },
+ { 1U << 3, "MBZ-3" },
+ { 1U << 4, "MBZ-4" },
+ { 1U << 5, "MBZ-5" },
+ { 1U << 6, "MBZ-6" },
+ { 1U << 7, "MBZ-7" },
+ { 0, NULL}
+};
+
+/*
+ * UDLD's Protocol Data Unit format:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Ver | Opcode | Flags | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | List of TLVs (variable length list) |
+ * | ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * TLV format:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | TYPE | LENGTH |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VALUE |
+ * | ... |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * LENGTH: Length in bytes of the Type, Length, and Value fields.
+ */
+
+#define UDLD_EXTRACT_VERSION(x) (((x)&0xe0)>>5)
+#define UDLD_EXTRACT_OPCODE(x) ((x)&0x1f)
+
+void
+udld_print(netdissect_options *ndo,
+ const u_char *tptr, u_int length)
+{
+ uint8_t ver, code, flags;
+
+ ndo->ndo_protocol = "udld";
+ if (length < UDLD_HEADER_LEN)
+ goto invalid;
+
+ ver = UDLD_EXTRACT_VERSION(GET_U_1(tptr));
+ code = UDLD_EXTRACT_OPCODE(GET_U_1(tptr));
+ tptr += 1;
+ length -= 1;
+
+ flags = GET_U_1(tptr);
+ tptr += 1;
+ length -= 1;
+
+ ND_PRINT("UDLDv%u, Code %s (%x), Flags [%s] (0x%02x), length %u",
+ ver,
+ tok2str(udld_code_values, "Reserved", code),
+ code,
+ bittok2str(udld_flags_bitmap_str, "none", flags),
+ flags,
+ length + 2);
+
+ /*
+ * In non-verbose mode, just print version and opcode type
+ */
+ if (ndo->ndo_vflag < 1) {
+ goto tcheck_remainder;
+ }
+
+ ND_PRINT("\n\tChecksum 0x%04x (unverified)", GET_BE_U_2(tptr));
+ tptr += 2;
+ length -= 2;
+
+ while (length) {
+ uint16_t type, len;
+
+ if (length < UDLD_TLV_HEADER_LEN)
+ goto invalid;
+
+ type = GET_BE_U_2(tptr);
+ tptr += 2;
+ length -= 2;
+
+ len = GET_BE_U_2(tptr);
+ tptr += 2;
+ length -= 2;
+
+ ND_PRINT("\n\t%s (0x%04x) TLV, length %u",
+ tok2str(udld_tlv_values, "Unknown", type),
+ type, len);
+
+ /* infinite loop check */
+ if (len <= UDLD_TLV_HEADER_LEN)
+ goto invalid;
+
+ len -= UDLD_TLV_HEADER_LEN;
+ if (length < len)
+ goto invalid;
+
+ switch (type) {
+ case UDLD_DEVICE_ID_TLV:
+ case UDLD_PORT_ID_TLV:
+ case UDLD_DEVICE_NAME_TLV:
+ ND_PRINT(", ");
+ nd_printjnp(ndo, tptr, len);
+ break;
+
+ case UDLD_ECHO_TLV:
+ ND_PRINT(", ");
+ (void)nd_printn(ndo, tptr, len, NULL);
+ break;
+
+ case UDLD_MESSAGE_INTERVAL_TLV:
+ case UDLD_TIMEOUT_INTERVAL_TLV:
+ if (len != 1)
+ goto invalid;
+ ND_PRINT(", %us", (GET_U_1(tptr)));
+ break;
+
+ case UDLD_SEQ_NUMBER_TLV:
+ if (len != 4)
+ goto invalid;
+ ND_PRINT(", %u", GET_BE_U_4(tptr));
+ break;
+
+ default:
+ ND_TCHECK_LEN(tptr, len);
+ break;
+ }
+ tptr += len;
+ length -= len;
+ }
+
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+tcheck_remainder:
+ ND_TCHECK_LEN(tptr, length);
+}
diff --git a/print-udp.c b/print-udp.c
new file mode 100644
index 0000000..b4a5457
--- /dev/null
+++ b/print-udp.c
@@ -0,0 +1,705 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: UDP printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+#include "appletalk.h"
+
+#include "udp.h"
+
+#include "ip.h"
+#include "ip6.h"
+#include "ipproto.h"
+
+#include "nfs.h"
+
+
+struct rtcphdr {
+ nd_uint16_t rh_flags; /* T:2 P:1 CNT:5 PT:8 */
+ nd_uint16_t rh_len; /* length of message (in words) */
+ nd_uint32_t rh_ssrc; /* synchronization src id */
+};
+
+typedef struct {
+ nd_uint32_t upper; /* more significant 32 bits */
+ nd_uint32_t lower; /* less significant 32 bits */
+} ntp64;
+
+/*
+ * Sender report.
+ */
+struct rtcp_sr {
+ ntp64 sr_ntp; /* 64-bit ntp timestamp */
+ nd_uint32_t sr_ts; /* reference media timestamp */
+ nd_uint32_t sr_np; /* no. packets sent */
+ nd_uint32_t sr_nb; /* no. bytes sent */
+};
+
+/*
+ * Receiver report.
+ * Time stamps are middle 32-bits of ntp timestamp.
+ */
+struct rtcp_rr {
+ nd_uint32_t rr_srcid; /* sender being reported */
+ nd_uint32_t rr_nl; /* no. packets lost */
+ nd_uint32_t rr_ls; /* extended last seq number received */
+ nd_uint32_t rr_dv; /* jitter (delay variance) */
+ nd_uint32_t rr_lsr; /* orig. ts from last rr from this src */
+ nd_uint32_t rr_dlsr; /* time from recpt of last rr to xmit time */
+};
+
+/*XXX*/
+#define RTCP_PT_SR 200
+#define RTCP_PT_RR 201
+#define RTCP_PT_SDES 202
+#define RTCP_SDES_CNAME 1
+#define RTCP_SDES_NAME 2
+#define RTCP_SDES_EMAIL 3
+#define RTCP_SDES_PHONE 4
+#define RTCP_SDES_LOC 5
+#define RTCP_SDES_TOOL 6
+#define RTCP_SDES_NOTE 7
+#define RTCP_SDES_PRIV 8
+#define RTCP_PT_BYE 203
+#define RTCP_PT_APP 204
+
+static void
+vat_print(netdissect_options *ndo, const u_char *hdr, u_int length)
+{
+ /* vat/vt audio */
+ u_int ts;
+
+ ndo->ndo_protocol = "vat";
+ if (length < 2) {
+ ND_PRINT("udp/va/vat, length %u < 2", length);
+ return;
+ }
+ ts = GET_BE_U_2(hdr);
+ if ((ts & 0xf060) != 0) {
+ /* probably vt */
+ ND_PRINT("udp/vt %u %u / %u",
+ length,
+ ts & 0x3ff, ts >> 10);
+ } else {
+ /* probably vat */
+ uint32_t i0, i1;
+
+ if (length < 8) {
+ ND_PRINT("udp/vat, length %u < 8", length);
+ return;
+ }
+ i0 = GET_BE_U_4(&((const u_int *)hdr)[0]);
+ i1 = GET_BE_U_4(&((const u_int *)hdr)[1]);
+ ND_PRINT("udp/vat %u c%u %u%s",
+ length - 8,
+ i0 & 0xffff,
+ i1, i0 & 0x800000? "*" : "");
+ /* audio format */
+ if (i0 & 0x1f0000)
+ ND_PRINT(" f%u", (i0 >> 16) & 0x1f);
+ if (i0 & 0x3f000000)
+ ND_PRINT(" s%u", (i0 >> 24) & 0x3f);
+ }
+}
+
+static void
+rtp_print(netdissect_options *ndo, const u_char *hdr, u_int len)
+{
+ /* rtp v1 or v2 */
+ const u_int *ip = (const u_int *)hdr;
+ u_int hasopt, hasext, contype, hasmarker, dlen;
+ uint32_t i0, i1;
+ const char * ptype;
+
+ ndo->ndo_protocol = "rtp";
+ if (len < 8) {
+ ND_PRINT("udp/rtp, length %u < 8", len);
+ return;
+ }
+ i0 = GET_BE_U_4(&((const u_int *)hdr)[0]);
+ i1 = GET_BE_U_4(&((const u_int *)hdr)[1]);
+ dlen = len - 8;
+ ip += 2;
+ len >>= 2;
+ len -= 2;
+ hasopt = 0;
+ hasext = 0;
+ if ((i0 >> 30) == 1) {
+ /* rtp v1 - draft-ietf-avt-rtp-04 */
+ hasopt = i0 & 0x800000;
+ contype = (i0 >> 16) & 0x3f;
+ hasmarker = i0 & 0x400000;
+ ptype = "rtpv1";
+ } else {
+ /* rtp v2 - RFC 3550 */
+ if (dlen < 4) {
+ ND_PRINT("udp/rtp, length %u < 12", dlen + 8);
+ return;
+ }
+ hasext = i0 & 0x10000000;
+ contype = (i0 >> 16) & 0x7f;
+ hasmarker = i0 & 0x800000;
+ dlen -= 4;
+ ptype = "rtp";
+ ip += 1;
+ len -= 1;
+ }
+ ND_PRINT("udp/%s %u c%u %s%s %u %u",
+ ptype,
+ dlen,
+ contype,
+ (hasopt || hasext)? "+" : "",
+ hasmarker? "*" : "",
+ i0 & 0xffff,
+ i1);
+ if (ndo->ndo_vflag) {
+ ND_PRINT(" %u", GET_BE_U_4(&((const u_int *)hdr)[2]));
+ if (hasopt) {
+ u_int i2, optlen;
+ do {
+ i2 = GET_BE_U_4(ip);
+ optlen = (i2 >> 16) & 0xff;
+ if (optlen == 0 || optlen > len) {
+ ND_PRINT(" !opt");
+ return;
+ }
+ ip += optlen;
+ len -= optlen;
+ } while ((int)i2 >= 0);
+ }
+ if (hasext) {
+ u_int i2, extlen;
+ i2 = GET_BE_U_4(ip);
+ extlen = (i2 & 0xffff) + 1;
+ if (extlen > len) {
+ ND_PRINT(" !ext");
+ return;
+ }
+ ip += extlen;
+ }
+ if (contype == 0x1f) /*XXX H.261 */
+ ND_PRINT(" 0x%04x", GET_BE_U_4(ip) >> 16);
+ }
+}
+
+static const u_char *
+rtcp_print(netdissect_options *ndo, const u_char *hdr, const u_char *ep)
+{
+ /* rtp v2 control (rtcp) */
+ const struct rtcp_rr *rr = 0;
+ const struct rtcp_sr *sr;
+ const struct rtcphdr *rh = (const struct rtcphdr *)hdr;
+ u_int len;
+ uint16_t flags;
+ u_int cnt;
+ double ts, dts;
+
+ ndo->ndo_protocol = "rtcp";
+ if ((const u_char *)(rh + 1) > ep)
+ goto trunc;
+ ND_TCHECK_SIZE(rh);
+ len = (GET_BE_U_2(rh->rh_len) + 1) * 4;
+ flags = GET_BE_U_2(rh->rh_flags);
+ cnt = (flags >> 8) & 0x1f;
+ switch (flags & 0xff) {
+ case RTCP_PT_SR:
+ sr = (const struct rtcp_sr *)(rh + 1);
+ ND_PRINT(" sr");
+ if (len != cnt * sizeof(*rr) + sizeof(*sr) + sizeof(*rh))
+ ND_PRINT(" [%u]", len);
+ if (ndo->ndo_vflag)
+ ND_PRINT(" %u", GET_BE_U_4(rh->rh_ssrc));
+ if ((const u_char *)(sr + 1) > ep)
+ goto trunc;
+ ND_TCHECK_SIZE(sr);
+ ts = (double)(GET_BE_U_4(sr->sr_ntp.upper)) +
+ ((double)(GET_BE_U_4(sr->sr_ntp.lower)) /
+ FMAXINT);
+ ND_PRINT(" @%.2f %u %up %ub", ts, GET_BE_U_4(sr->sr_ts),
+ GET_BE_U_4(sr->sr_np), GET_BE_U_4(sr->sr_nb));
+ rr = (const struct rtcp_rr *)(sr + 1);
+ break;
+ case RTCP_PT_RR:
+ ND_PRINT(" rr");
+ if (len != cnt * sizeof(*rr) + sizeof(*rh))
+ ND_PRINT(" [%u]", len);
+ rr = (const struct rtcp_rr *)(rh + 1);
+ if (ndo->ndo_vflag)
+ ND_PRINT(" %u", GET_BE_U_4(rh->rh_ssrc));
+ break;
+ case RTCP_PT_SDES:
+ ND_PRINT(" sdes %u", len);
+ if (ndo->ndo_vflag)
+ ND_PRINT(" %u", GET_BE_U_4(rh->rh_ssrc));
+ cnt = 0;
+ break;
+ case RTCP_PT_BYE:
+ ND_PRINT(" bye %u", len);
+ if (ndo->ndo_vflag)
+ ND_PRINT(" %u", GET_BE_U_4(rh->rh_ssrc));
+ cnt = 0;
+ break;
+ default:
+ ND_PRINT(" type-0x%x %u", flags & 0xff, len);
+ cnt = 0;
+ break;
+ }
+ if (cnt > 1)
+ ND_PRINT(" c%u", cnt);
+ while (cnt != 0) {
+ if ((const u_char *)(rr + 1) > ep)
+ goto trunc;
+ ND_TCHECK_SIZE(rr);
+ if (ndo->ndo_vflag)
+ ND_PRINT(" %u", GET_BE_U_4(rr->rr_srcid));
+ ts = (double)(GET_BE_U_4(rr->rr_lsr)) / 65536.;
+ dts = (double)(GET_BE_U_4(rr->rr_dlsr)) / 65536.;
+ ND_PRINT(" %ul %us %uj @%.2f+%.2f",
+ GET_BE_U_4(rr->rr_nl) & 0x00ffffff,
+ GET_BE_U_4(rr->rr_ls),
+ GET_BE_U_4(rr->rr_dv), ts, dts);
+ cnt--;
+ }
+ return (hdr + len);
+
+trunc:
+ nd_print_trunc(ndo);
+ return ep;
+}
+
+static uint16_t udp_cksum(netdissect_options *ndo, const struct ip *ip,
+ const struct udphdr *up,
+ u_int len)
+{
+ return nextproto4_cksum(ndo, ip, (const uint8_t *)(const void *)up, len, len,
+ IPPROTO_UDP);
+}
+
+static uint16_t udp6_cksum(netdissect_options *ndo, const struct ip6_hdr *ip6,
+ const struct udphdr *up, u_int len)
+{
+ return nextproto6_cksum(ndo, ip6, (const uint8_t *)(const void *)up, len, len,
+ IPPROTO_UDP);
+}
+
+static void
+udpipaddr_print(netdissect_options *ndo, const struct ip *ip, int sport, int dport)
+{
+ const struct ip6_hdr *ip6;
+
+ if (IP_V(ip) == 6)
+ ip6 = (const struct ip6_hdr *)ip;
+ else
+ ip6 = NULL;
+
+ if (ip6) {
+ if (GET_U_1(ip6->ip6_nxt) == IPPROTO_UDP) {
+ if (sport == -1) {
+ ND_PRINT("%s > %s: ",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ GET_IP6ADDR_STRING(ip6->ip6_dst));
+ } else {
+ ND_PRINT("%s.%s > %s.%s: ",
+ GET_IP6ADDR_STRING(ip6->ip6_src),
+ udpport_string(ndo, (uint16_t)sport),
+ GET_IP6ADDR_STRING(ip6->ip6_dst),
+ udpport_string(ndo, (uint16_t)dport));
+ }
+ } else {
+ if (sport != -1) {
+ ND_PRINT("%s > %s: ",
+ udpport_string(ndo, (uint16_t)sport),
+ udpport_string(ndo, (uint16_t)dport));
+ }
+ }
+ } else {
+ if (GET_U_1(ip->ip_p) == IPPROTO_UDP) {
+ if (sport == -1) {
+ ND_PRINT("%s > %s: ",
+ GET_IPADDR_STRING(ip->ip_src),
+ GET_IPADDR_STRING(ip->ip_dst));
+ } else {
+ ND_PRINT("%s.%s > %s.%s: ",
+ GET_IPADDR_STRING(ip->ip_src),
+ udpport_string(ndo, (uint16_t)sport),
+ GET_IPADDR_STRING(ip->ip_dst),
+ udpport_string(ndo, (uint16_t)dport));
+ }
+ } else {
+ if (sport != -1) {
+ ND_PRINT("%s > %s: ",
+ udpport_string(ndo, (uint16_t)sport),
+ udpport_string(ndo, (uint16_t)dport));
+ }
+ }
+ }
+}
+
+void
+udp_print(netdissect_options *ndo, const u_char *bp, u_int length,
+ const u_char *bp2, int fragmented, u_int ttl_hl)
+{
+ const struct udphdr *up;
+ const struct ip *ip;
+ const u_char *cp;
+ const u_char *ep = ndo->ndo_snapend;
+ uint16_t sport, dport;
+ u_int ulen;
+ const struct ip6_hdr *ip6;
+
+ ndo->ndo_protocol = "udp";
+ up = (const struct udphdr *)bp;
+ ip = (const struct ip *)bp2;
+ if (IP_V(ip) == 6)
+ ip6 = (const struct ip6_hdr *)bp2;
+ else
+ ip6 = NULL;
+ if (!ND_TTEST_2(up->uh_dport)) {
+ udpipaddr_print(ndo, ip, -1, -1);
+ goto trunc;
+ }
+
+ sport = GET_BE_U_2(up->uh_sport);
+ dport = GET_BE_U_2(up->uh_dport);
+
+ if (length < sizeof(struct udphdr)) {
+ udpipaddr_print(ndo, ip, sport, dport);
+ ND_PRINT("truncated-udp %u", length);
+ return;
+ }
+ if (!ND_TTEST_2(up->uh_ulen)) {
+ udpipaddr_print(ndo, ip, sport, dport);
+ goto trunc;
+ }
+ ulen = GET_BE_U_2(up->uh_ulen);
+ /*
+ * IPv6 Jumbo Datagrams; see RFC 2675.
+ * If the length is zero, and the length provided to us is
+ * > 65535, use the provided length as the length.
+ */
+ if (ulen == 0 && length > 65535)
+ ulen = length;
+ if (ulen < sizeof(struct udphdr)) {
+ udpipaddr_print(ndo, ip, sport, dport);
+ ND_PRINT("truncated-udplength %u", ulen);
+ return;
+ }
+ ulen -= sizeof(struct udphdr);
+ length -= sizeof(struct udphdr);
+ if (ulen < length)
+ length = ulen;
+
+ cp = (const u_char *)(up + 1);
+ if (cp > ndo->ndo_snapend) {
+ udpipaddr_print(ndo, ip, sport, dport);
+ goto trunc;
+ }
+
+ if (ndo->ndo_packettype) {
+ switch (ndo->ndo_packettype) {
+
+ case PT_VAT:
+ udpipaddr_print(ndo, ip, sport, dport);
+ vat_print(ndo, cp, length);
+ break;
+
+ case PT_WB:
+ udpipaddr_print(ndo, ip, sport, dport);
+ wb_print(ndo, cp, length);
+ break;
+
+ case PT_RPC:
+ ND_PRINT("[https://fxbug.dev/84481] PT_RPC not supported");
+ break;
+
+ case PT_RTP:
+ udpipaddr_print(ndo, ip, sport, dport);
+ rtp_print(ndo, cp, length);
+ break;
+
+ case PT_RTCP:
+ udpipaddr_print(ndo, ip, sport, dport);
+ while (cp < ep)
+ cp = rtcp_print(ndo, cp, ep);
+ break;
+
+ case PT_SNMP:
+ udpipaddr_print(ndo, ip, sport, dport);
+ snmp_print(ndo, cp, length);
+ break;
+
+ case PT_CNFP:
+ udpipaddr_print(ndo, ip, sport, dport);
+ cnfp_print(ndo, cp);
+ break;
+
+ case PT_TFTP:
+ udpipaddr_print(ndo, ip, sport, dport);
+ tftp_print(ndo, cp, length);
+ break;
+
+ case PT_AODV:
+ udpipaddr_print(ndo, ip, sport, dport);
+ aodv_print(ndo, cp, length,
+ ip6 != NULL);
+ break;
+
+ case PT_RADIUS:
+ udpipaddr_print(ndo, ip, sport, dport);
+ radius_print(ndo, cp, length);
+ break;
+
+ case PT_VXLAN:
+ udpipaddr_print(ndo, ip, sport, dport);
+ vxlan_print(ndo, cp, length);
+ break;
+
+ case PT_PGM:
+ case PT_PGM_ZMTP1:
+ udpipaddr_print(ndo, ip, sport, dport);
+ pgm_print(ndo, cp, length, bp2);
+ break;
+ case PT_LMP:
+ udpipaddr_print(ndo, ip, sport, dport);
+ lmp_print(ndo, cp, length);
+ break;
+ case PT_PTP:
+ udpipaddr_print(ndo, ip, sport, dport);
+ ptp_print(ndo, cp, length);
+ break;
+ case PT_SOMEIP:
+ udpipaddr_print(ndo, ip, sport, dport);
+ someip_print(ndo, cp, length);
+ break;
+ case PT_DOMAIN:
+ udpipaddr_print(ndo, ip, sport, dport);
+ /* over_tcp: FALSE, is_mdns: FALSE */
+ domain_print(ndo, cp, length, FALSE, FALSE);
+ break;
+ }
+ return;
+ }
+
+ udpipaddr_print(ndo, ip, sport, dport);
+ if (!ndo->ndo_qflag) {
+ // UNIMPLEMENTED(https://fxbug.dev/84481)
+ }
+
+ if (ndo->ndo_vflag && !ndo->ndo_Kflag && !fragmented) {
+ /* Check the checksum, if possible. */
+ uint16_t sum, udp_sum;
+
+ /*
+ * XXX - do this even if vflag == 1?
+ * TCP does, and we do so for UDP-over-IPv6.
+ */
+ if (IP_V(ip) == 4 && (ndo->ndo_vflag > 1)) {
+ udp_sum = GET_BE_U_2(up->uh_sum);
+ if (udp_sum == 0) {
+ ND_PRINT("[no cksum] ");
+ } else if (ND_TTEST_LEN(cp, length)) {
+ sum = udp_cksum(ndo, ip, up, length + sizeof(struct udphdr));
+
+ if (sum != 0) {
+ ND_PRINT("[bad udp cksum 0x%04x -> 0x%04x!] ",
+ udp_sum,
+ in_cksum_shouldbe(udp_sum, sum));
+ } else
+ ND_PRINT("[udp sum ok] ");
+ }
+ }
+ else if (IP_V(ip) == 6) {
+ /* for IPv6, UDP checksum is mandatory */
+ if (ND_TTEST_LEN(cp, length)) {
+ sum = udp6_cksum(ndo, ip6, up, length + sizeof(struct udphdr));
+ udp_sum = GET_BE_U_2(up->uh_sum);
+
+ if (sum != 0) {
+ ND_PRINT("[bad udp cksum 0x%04x -> 0x%04x!] ",
+ udp_sum,
+ in_cksum_shouldbe(udp_sum, sum));
+ } else
+ ND_PRINT("[udp sum ok] ");
+ }
+ }
+ }
+
+ if (!ndo->ndo_qflag) {
+ if (IS_SRC_OR_DST_PORT(NAMESERVER_PORT))
+ /* over_tcp: FALSE, is_mdns: FALSE */
+ domain_print(ndo, cp, length, FALSE, FALSE);
+ else if (IS_SRC_OR_DST_PORT(MULTICASTDNS_PORT))
+ /* over_tcp: FALSE, is_mdns: TRUE */
+ domain_print(ndo, cp, length, FALSE, TRUE);
+ else if (IS_SRC_OR_DST_PORT(TIMED_PORT))
+ timed_print(ndo, (const u_char *)cp);
+ else if (IS_SRC_OR_DST_PORT(TFTP_PORT))
+ tftp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(BOOTPC_PORT) || IS_SRC_OR_DST_PORT(BOOTPS_PORT))
+ bootp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(RIP_PORT))
+ rip_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(AODV_PORT))
+ aodv_print(ndo, cp, length,
+ ip6 != NULL);
+ else if (IS_SRC_OR_DST_PORT(ISAKMP_PORT))
+ isakmp_print(ndo, cp, length, bp2);
+ else if (IS_SRC_OR_DST_PORT(ISAKMP_PORT_NATT))
+ isakmp_rfc3948_print(ndo, cp, length, bp2, IP_V(ip), fragmented, ttl_hl);
+ else if (IS_SRC_OR_DST_PORT(ISAKMP_PORT_USER1) || IS_SRC_OR_DST_PORT(ISAKMP_PORT_USER2))
+ isakmp_print(ndo, cp, length, bp2);
+ else if (IS_SRC_OR_DST_PORT(SNMP_PORT) || IS_SRC_OR_DST_PORT(SNMPTRAP_PORT))
+ snmp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(NTP_PORT))
+ ntp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(KERBEROS_PORT) || IS_SRC_OR_DST_PORT(KERBEROS_SEC_PORT))
+ krb_print(ndo, (const u_char *)cp);
+ else if (IS_SRC_OR_DST_PORT(L2TP_PORT))
+ l2tp_print(ndo, cp, length);
+#ifdef ENABLE_SMB
+ else if (IS_SRC_OR_DST_PORT(NETBIOS_NS_PORT))
+ nbt_udp137_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(NETBIOS_DGRAM_PORT))
+ nbt_udp138_print(ndo, cp, length);
+#endif
+ else if (dport == VAT_PORT)
+ vat_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(ZEPHYR_SRV_PORT) || IS_SRC_OR_DST_PORT(ZEPHYR_CLT_PORT))
+ zephyr_print(ndo, cp, length);
+ /*
+ * Since there are 10 possible ports to check, I think
+ * a <> test would be more efficient
+ */
+ else if ((sport >= RX_PORT_LOW && sport <= RX_PORT_HIGH) ||
+ (dport >= RX_PORT_LOW && dport <= RX_PORT_HIGH))
+ rx_print(ndo, cp, length, sport, dport,
+ (const u_char *) ip);
+ else if (IS_SRC_OR_DST_PORT(RIPNG_PORT))
+ ripng_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(DHCP6_SERV_PORT) || IS_SRC_OR_DST_PORT(DHCP6_CLI_PORT))
+ dhcp6_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(AHCP_PORT))
+ ahcp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(BABEL_PORT) || IS_SRC_OR_DST_PORT(BABEL_PORT_OLD))
+ babel_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(HNCP_PORT))
+ hncp_print(ndo, cp, length);
+ /*
+ * Kludge in test for whiteboard packets.
+ */
+ else if (dport == WB_PORT)
+ wb_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(CISCO_AUTORP_PORT))
+ cisco_autorp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(RADIUS_PORT) ||
+ IS_SRC_OR_DST_PORT(RADIUS_NEW_PORT) ||
+ IS_SRC_OR_DST_PORT(RADIUS_ACCOUNTING_PORT) ||
+ IS_SRC_OR_DST_PORT(RADIUS_NEW_ACCOUNTING_PORT) ||
+ IS_SRC_OR_DST_PORT(RADIUS_CISCO_COA_PORT) ||
+ IS_SRC_OR_DST_PORT(RADIUS_COA_PORT) )
+ radius_print(ndo, cp, length);
+ else if (dport == HSRP_PORT)
+ hsrp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(LWRES_PORT))
+ lwres_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(LDP_PORT))
+ ldp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(OLSR_PORT))
+ olsr_print(ndo, cp, length,
+ (IP_V(ip) == 6) ? 1 : 0);
+ else if (IS_SRC_OR_DST_PORT(MPLS_LSP_PING_PORT))
+ lspping_print(ndo, cp, length);
+ else if (sport == BCM_LI_PORT)
+ bcm_li_print(ndo, cp, length);
+ else if (dport == BFD_CONTROL_PORT ||
+ dport == BFD_MULTIHOP_PORT ||
+ dport == BFD_LAG_PORT ||
+ dport == BFD_ECHO_PORT )
+ bfd_print(ndo, cp, length, dport);
+ else if (IS_SRC_OR_DST_PORT(LMP_PORT))
+ lmp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(VQP_PORT))
+ vqp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(SFLOW_PORT))
+ sflow_print(ndo, cp, length);
+ else if (dport == LWAPP_CONTROL_PORT)
+ lwapp_control_print(ndo, cp, length, 1);
+ else if (sport == LWAPP_CONTROL_PORT)
+ lwapp_control_print(ndo, cp, length, 0);
+ else if (IS_SRC_OR_DST_PORT(LWAPP_DATA_PORT))
+ lwapp_data_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(SIP_PORT))
+ sip_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(SYSLOG_PORT))
+ syslog_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(OTV_PORT))
+ otv_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(VXLAN_PORT))
+ vxlan_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(GENEVE_PORT))
+ geneve_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(LISP_CONTROL_PORT))
+ lisp_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(VXLAN_GPE_PORT))
+ vxlan_gpe_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(ZEP_PORT))
+ zep_print(ndo, cp, length);
+ else if (IS_SRC_OR_DST_PORT(MPLS_PORT))
+ mpls_print(ndo, cp, length);
+ else if (ND_TTEST_1(((const struct LAP *)cp)->type) &&
+ GET_U_1(((const struct LAP *)cp)->type) == lapDDP &&
+ (atalk_port(sport) || atalk_port(dport))) {
+ if (ndo->ndo_vflag)
+ ND_PRINT("kip ");
+ llap_print(ndo, cp, length);
+ } else if (IS_SRC_OR_DST_PORT(PTP_EVENT_PORT) ||
+ IS_SRC_OR_DST_PORT(PTP_GENERAL_PORT)) {
+ ptp_print(ndo, cp, length);
+ } else if (IS_SRC_OR_DST_PORT(SOMEIP_PORT))
+ someip_print(ndo, cp, length);
+ else {
+ if (ulen > length && !fragmented)
+ ND_PRINT("UDP, bad length %u > %u",
+ ulen, length);
+ else
+ ND_PRINT("UDP, length %u", ulen);
+ }
+ } else {
+ if (ulen > length && !fragmented)
+ ND_PRINT("UDP, bad length %u > %u",
+ ulen, length);
+ else
+ ND_PRINT("UDP, length %u", ulen);
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
diff --git a/print-unsupported.c b/print-unsupported.c
new file mode 100644
index 0000000..009cf6f
--- /dev/null
+++ b/print-unsupported.c
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2020 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: unsupported link-layer protocols printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+void
+unsupported_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h,
+ const u_char *p)
+{
+ ndo->ndo_protocol = "unsupported";
+ nd_print_protocol_caps(ndo);
+ hex_and_ascii_print(ndo, "\n\t", p, h->caplen);
+}
diff --git a/print-usb.c b/print-usb.c
new file mode 100644
index 0000000..3f5937b
--- /dev/null
+++ b/print-usb.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright 2009 Bert Vermeulen <bert@biot.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by Paolo Abeni.''
+ * The name of author may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Support for USB packets
+ *
+ */
+
+/* \summary: USB printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+#ifdef DLT_USB_LINUX
+/*
+ * possible transfer mode
+ */
+#define URB_TRANSFER_IN 0x80
+#define URB_ISOCHRONOUS 0x0
+#define URB_INTERRUPT 0x1
+#define URB_CONTROL 0x2
+#define URB_BULK 0x3
+
+/*
+ * possible event type
+ */
+#define URB_SUBMIT 'S'
+#define URB_COMPLETE 'C'
+#define URB_ERROR 'E'
+
+/*
+ * USB setup header as defined in USB specification.
+ * Appears at the front of each Control S-type packet in DLT_USB captures.
+ */
+typedef struct _usb_setup {
+ nd_uint8_t bmRequestType;
+ nd_uint8_t bRequest;
+ nd_uint16_t wValue;
+ nd_uint16_t wIndex;
+ nd_uint16_t wLength;
+} pcap_usb_setup;
+
+/*
+ * Information from the URB for Isochronous transfers.
+ */
+typedef struct _iso_rec {
+ nd_int32_t error_count;
+ nd_int32_t numdesc;
+} iso_rec;
+
+/*
+ * Header prepended by linux kernel to each event.
+ * Appears at the front of each packet in DLT_USB_LINUX captures.
+ */
+typedef struct _usb_header {
+ nd_uint64_t id;
+ nd_uint8_t event_type;
+ nd_uint8_t transfer_type;
+ nd_uint8_t endpoint_number;
+ nd_uint8_t device_address;
+ nd_uint16_t bus_id;
+ nd_uint8_t setup_flag;/*if !=0 the urb setup header is not present*/
+ nd_uint8_t data_flag; /*if !=0 no urb data is present*/
+ nd_int64_t ts_sec;
+ nd_int32_t ts_usec;
+ nd_int32_t status;
+ nd_uint32_t urb_len;
+ nd_uint32_t data_len; /* amount of urb data really present in this event*/
+ pcap_usb_setup setup;
+} pcap_usb_header;
+
+/*
+ * Header prepended by linux kernel to each event for the 2.6.31
+ * and later kernels; for the 2.6.21 through 2.6.30 kernels, the
+ * "iso_rec" information, and the fields starting with "interval"
+ * are zeroed-out padding fields.
+ *
+ * Appears at the front of each packet in DLT_USB_LINUX_MMAPPED captures.
+ */
+typedef struct _usb_header_mmapped {
+ nd_uint64_t id;
+ nd_uint8_t event_type;
+ nd_uint8_t transfer_type;
+ nd_uint8_t endpoint_number;
+ nd_uint8_t device_address;
+ nd_uint16_t bus_id;
+ nd_uint8_t setup_flag;/*if !=0 the urb setup header is not present*/
+ nd_uint8_t data_flag; /*if !=0 no urb data is present*/
+ nd_int64_t ts_sec;
+ nd_int32_t ts_usec;
+ nd_int32_t status;
+ nd_uint32_t urb_len;
+ nd_uint32_t data_len; /* amount of urb data really present in this event*/
+ union {
+ pcap_usb_setup setup;
+ iso_rec iso;
+ } s;
+ nd_int32_t interval; /* for Interrupt and Isochronous events */
+ nd_int32_t start_frame; /* for Isochronous events */
+ nd_uint32_t xfer_flags; /* copy of URB's transfer flags */
+ nd_uint32_t ndesc; /* number of isochronous descriptors */
+} pcap_usb_header_mmapped;
+
+/*
+ * Isochronous descriptors; for isochronous transfers there might be
+ * one or more of these at the beginning of the packet data. The
+ * number of descriptors is given by the "ndesc" field in the header;
+ * as indicated, in older kernels that don't put the descriptors at
+ * the beginning of the packet, that field is zeroed out, so that field
+ * can be trusted even in captures from older kernels.
+ */
+typedef struct _usb_isodesc {
+ nd_int32_t status;
+ nd_uint32_t offset;
+ nd_uint32_t len;
+ nd_byte pad[4];
+} usb_isodesc;
+
+
+/* returns direction: 1=inbound 2=outbound -1=invalid */
+static int
+get_direction(int transfer_type, int event_type)
+{
+ int direction;
+
+ direction = -1;
+ switch(transfer_type){
+ case URB_BULK:
+ case URB_CONTROL:
+ case URB_ISOCHRONOUS:
+ switch(event_type)
+ {
+ case URB_SUBMIT:
+ direction = 2;
+ break;
+ case URB_COMPLETE:
+ case URB_ERROR:
+ direction = 1;
+ break;
+ default:
+ direction = -1;
+ }
+ break;
+ case URB_INTERRUPT:
+ switch(event_type)
+ {
+ case URB_SUBMIT:
+ direction = 1;
+ break;
+ case URB_COMPLETE:
+ case URB_ERROR:
+ direction = 2;
+ break;
+ default:
+ direction = -1;
+ }
+ break;
+ default:
+ direction = -1;
+ }
+
+ return direction;
+}
+
+static void
+usb_header_print(netdissect_options *ndo, const pcap_usb_header *uh)
+{
+ int direction;
+ uint8_t transfer_type, event_type;
+
+ ndo->ndo_protocol = "usb";
+
+ nd_print_protocol_caps(ndo);
+ if (ndo->ndo_qflag)
+ return;
+
+ ND_PRINT(" ");
+ transfer_type = GET_U_1(uh->transfer_type);
+ switch(transfer_type)
+ {
+ case URB_ISOCHRONOUS:
+ ND_PRINT("ISOCHRONOUS");
+ break;
+ case URB_INTERRUPT:
+ ND_PRINT("INTERRUPT");
+ break;
+ case URB_CONTROL:
+ ND_PRINT("CONTROL");
+ break;
+ case URB_BULK:
+ ND_PRINT("BULK");
+ break;
+ default:
+ ND_PRINT(" ?");
+ }
+
+ event_type = GET_U_1(uh->event_type);
+ switch(event_type)
+ {
+ case URB_SUBMIT:
+ ND_PRINT(" SUBMIT");
+ break;
+ case URB_COMPLETE:
+ ND_PRINT(" COMPLETE");
+ break;
+ case URB_ERROR:
+ ND_PRINT(" ERROR");
+ break;
+ default:
+ ND_PRINT(" ?");
+ }
+
+ direction = get_direction(transfer_type, event_type);
+ if(direction == 1)
+ ND_PRINT(" from");
+ else if(direction == 2)
+ ND_PRINT(" to");
+ ND_PRINT(" %u:%u:%u", GET_HE_U_2(uh->bus_id),
+ GET_U_1(uh->device_address),
+ GET_U_1(uh->endpoint_number) & 0x7f);
+}
+
+/*
+ * This is the top level routine of the printer for captures with a
+ * 48-byte header.
+ *
+ * 'p' points to the header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+usb_linux_48_byte_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h _U_, const u_char *p)
+{
+ ndo->ndo_protocol = "usb_linux_48_byte";
+ ND_TCHECK_LEN(p, sizeof(pcap_usb_header));
+ ndo->ndo_ll_hdr_len += sizeof (pcap_usb_header);
+
+ usb_header_print(ndo, (const pcap_usb_header *) p);
+}
+
+#ifdef DLT_USB_LINUX_MMAPPED
+/*
+ * This is the top level routine of the printer for captures with a
+ * 64-byte header.
+ *
+ * 'p' points to the header of the packet, 'h->ts' is the timestamp,
+ * 'h->len' is the length of the packet off the wire, and 'h->caplen'
+ * is the number of bytes actually captured.
+ */
+void
+usb_linux_64_byte_if_print(netdissect_options *ndo,
+ const struct pcap_pkthdr *h _U_, const u_char *p)
+{
+ ndo->ndo_protocol = "usb_linux_64_byte";
+ ND_TCHECK_LEN(p, sizeof(pcap_usb_header_mmapped));
+ ndo->ndo_ll_hdr_len += sizeof (pcap_usb_header_mmapped);
+
+ usb_header_print(ndo, (const pcap_usb_header *) p);
+}
+#endif /* DLT_USB_LINUX_MMAPPED */
+
+#endif /* DLT_USB_LINUX */
+
diff --git a/print-vjc.c b/print-vjc.c
new file mode 100644
index 0000000..8303307
--- /dev/null
+++ b/print-vjc.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: PPP Van Jacobson compression printer */
+
+/* specification: RFC 1144 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "slcompress.h"
+#include "ppp.h"
+
+/*
+ * XXX - for BSD/OS PPP, what packets get supplied with a PPP header type
+ * of PPP_VJC and what packets get supplied with a PPP header type of
+ * PPP_VJNC? PPP_VJNC is for "UNCOMPRESSED_TCP" packets, and PPP_VJC
+ * is for COMPRESSED_TCP packets (PPP_IP is used for TYPE_IP packets).
+ *
+ * RFC 1144 implies that, on the wire, the packet type is *not* needed
+ * for PPP, as different PPP protocol types can be used; it only needs
+ * to be put on the wire for SLIP.
+ *
+ * It also indicates that, for compressed SLIP:
+ *
+ * If the COMPRESSED_TCP bit is set in the first byte, it's
+ * a COMPRESSED_TCP packet; that byte is the change byte, and
+ * the COMPRESSED_TCP bit, 0x80, isn't used in the change byte.
+ *
+ * If the upper 4 bits of the first byte are 7, it's an
+ * UNCOMPRESSED_TCP packet; that byte is the first byte of
+ * the UNCOMPRESSED_TCP modified IP header, with a connection
+ * number in the protocol field, and with the version field
+ * being 7, not 4.
+ *
+ * Otherwise, the packet is an IPv4 packet (where the upper 4 bits
+ * of the packet are 4).
+ *
+ * So this routine looks as if it's sort-of intended to handle
+ * compressed SLIP, although it doesn't handle UNCOMPRESSED_TCP
+ * correctly for that (it doesn't fix the version number and doesn't
+ * do anything to the protocol field), and doesn't check for COMPRESSED_TCP
+ * packets correctly for that (you only check the first bit - see
+ * B.1 in RFC 1144).
+ *
+ * But it's called for BSD/OS PPP, not SLIP - perhaps BSD/OS does weird
+ * things with the headers?
+ *
+ * Without a BSD/OS VJC-compressed PPP trace, or knowledge of what the
+ * BSD/OS VJC code does, we can't say what's the case.
+ *
+ * We therefore leave "proto" - which is the PPP protocol type - in place,
+ * *not* marked as unused, for now, so that GCC warnings about the
+ * unused argument remind us that we should fix this some day.
+ *
+ * XXX - also, it fetches the TCP checksum field in COMPRESSED_TCP
+ * packets with GET_HE_U_2, rather than with GET_BE_U_2(); RFC 1144 says
+ * it's "the unmodified TCP checksum", which would imply that it's
+ * big-endian, but perhaps, on the platform where this was developed,
+ * the packets were munged by the networking stack before being handed
+ * to the packet capture mechanism.
+ */
+int
+vjc_print(netdissect_options *ndo, const u_char *bp, u_short proto _U_)
+{
+ int i;
+
+ ndo->ndo_protocol = "vjc";
+ switch (GET_U_1(bp) & 0xf0) {
+ case TYPE_IP:
+ if (ndo->ndo_eflag)
+ ND_PRINT("(vjc type=IP) ");
+ return PPP_IP;
+ case TYPE_UNCOMPRESSED_TCP:
+ if (ndo->ndo_eflag)
+ ND_PRINT("(vjc type=raw TCP) ");
+ return PPP_IP;
+ case TYPE_COMPRESSED_TCP:
+ if (ndo->ndo_eflag)
+ ND_PRINT("(vjc type=compressed TCP) ");
+ for (i = 0; i < 8; i++) {
+ if (GET_U_1(bp + 1) & (0x80 >> i))
+ ND_PRINT("%c", "?CI?SAWU"[i]);
+ }
+ if (GET_U_1(bp + 1))
+ ND_PRINT(" ");
+ ND_PRINT("C=0x%02x ", GET_U_1(bp + 2));
+ ND_PRINT("sum=0x%04x ", GET_HE_U_2(bp + 3));
+ return -1;
+ case TYPE_ERROR:
+ if (ndo->ndo_eflag)
+ ND_PRINT("(vjc type=error) ");
+ return -1;
+ default:
+ if (ndo->ndo_eflag)
+ ND_PRINT("(vjc type=0x%02x) ", GET_U_1(bp) & 0xf0);
+ return -1;
+ }
+}
diff --git a/print-vqp.c b/print-vqp.c
new file mode 100644
index 0000000..f87898e
--- /dev/null
+++ b/print-vqp.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 1998-2006 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Carles Kishimoto <Carles.Kishimoto@bsc.es>
+ */
+
+/* \summary: Cisco VLAN Query Protocol (VQP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+#define VQP_VERSION 1
+
+/*
+ * VQP common header
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Constant | Packet type | Error Code | nitems |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Packet Sequence Number (4 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+struct vqp_common_header_t {
+ nd_uint8_t version;
+ nd_uint8_t msg_type;
+ nd_uint8_t error_code;
+ nd_uint8_t nitems;
+ nd_uint32_t sequence;
+};
+
+struct vqp_obj_tlv_t {
+ nd_uint32_t obj_type;
+ nd_uint16_t obj_length;
+};
+
+#define VQP_OBJ_REQ_JOIN_PORT 0x01
+#define VQP_OBJ_RESP_VLAN 0x02
+#define VQP_OBJ_REQ_RECONFIRM 0x03
+#define VQP_OBJ_RESP_RECONFIRM 0x04
+
+static const struct tok vqp_msg_type_values[] = {
+ { VQP_OBJ_REQ_JOIN_PORT, "Request, Join Port"},
+ { VQP_OBJ_RESP_VLAN, "Response, VLAN"},
+ { VQP_OBJ_REQ_RECONFIRM, "Request, Reconfirm"},
+ { VQP_OBJ_RESP_RECONFIRM, "Response, Reconfirm"},
+ { 0, NULL}
+};
+
+static const struct tok vqp_error_code_values[] = {
+ { 0x00, "No error"},
+ { 0x03, "Access denied"},
+ { 0x04, "Shutdown port"},
+ { 0x05, "Wrong VTP domain"},
+ { 0, NULL}
+};
+
+/* FIXME the heading 0x0c looks ugly - those must be flags etc. */
+#define VQP_OBJ_IP_ADDRESS 0x0c01
+#define VQP_OBJ_PORT_NAME 0x0c02
+#define VQP_OBJ_VLAN_NAME 0x0c03
+#define VQP_OBJ_VTP_DOMAIN 0x0c04
+#define VQP_OBJ_ETHERNET_PKT 0x0c05
+#define VQP_OBJ_MAC_NULL 0x0c06
+#define VQP_OBJ_MAC_ADDRESS 0x0c08
+
+static const struct tok vqp_obj_values[] = {
+ { VQP_OBJ_IP_ADDRESS, "Client IP Address" },
+ { VQP_OBJ_PORT_NAME, "Port Name" },
+ { VQP_OBJ_VLAN_NAME, "VLAN Name" },
+ { VQP_OBJ_VTP_DOMAIN, "VTP Domain" },
+ { VQP_OBJ_ETHERNET_PKT, "Ethernet Packet" },
+ { VQP_OBJ_MAC_NULL, "MAC Null" },
+ { VQP_OBJ_MAC_ADDRESS, "MAC Address" },
+ { 0, NULL}
+};
+
+void
+vqp_print(netdissect_options *ndo, const u_char *pptr, u_int len)
+{
+ const struct vqp_common_header_t *vqp_common_header;
+ const struct vqp_obj_tlv_t *vqp_obj_tlv;
+
+ const u_char *tptr;
+ uint8_t version;
+ uint16_t vqp_obj_len;
+ uint32_t vqp_obj_type;
+ u_int tlen;
+ uint8_t nitems;
+
+ ndo->ndo_protocol = "vqp";
+ tptr=pptr;
+ tlen = len;
+ vqp_common_header = (const struct vqp_common_header_t *)pptr;
+ ND_TCHECK_SIZE(vqp_common_header);
+ if (sizeof(struct vqp_common_header_t) > tlen)
+ goto invalid;
+ version = GET_U_1(vqp_common_header->version);
+
+ /*
+ * Sanity checking of the header.
+ */
+ if (version != VQP_VERSION) {
+ ND_PRINT("VQP version %u packet not supported",
+ version);
+ return;
+ }
+
+ /* in non-verbose mode just lets print the basic Message Type */
+ if (ndo->ndo_vflag < 1) {
+ ND_PRINT("VQPv%u %s Message, error-code %s (%u), length %u",
+ version,
+ tok2str(vqp_msg_type_values, "unknown (%u)",GET_U_1(vqp_common_header->msg_type)),
+ tok2str(vqp_error_code_values, "unknown (%u)",GET_U_1(vqp_common_header->error_code)),
+ GET_U_1(vqp_common_header->error_code),
+ len);
+ return;
+ }
+
+ /* ok they seem to want to know everything - lets fully decode it */
+ nitems = GET_U_1(vqp_common_header->nitems);
+ ND_PRINT("\n\tVQPv%u, %s Message, error-code %s (%u), seq 0x%08x, items %u, length %u",
+ version,
+ tok2str(vqp_msg_type_values, "unknown (%u)",GET_U_1(vqp_common_header->msg_type)),
+ tok2str(vqp_error_code_values, "unknown (%u)",GET_U_1(vqp_common_header->error_code)),
+ GET_U_1(vqp_common_header->error_code),
+ GET_BE_U_4(vqp_common_header->sequence),
+ nitems,
+ len);
+
+ /* skip VQP Common header */
+ tptr+=sizeof(struct vqp_common_header_t);
+ tlen-=sizeof(struct vqp_common_header_t);
+
+ while (nitems != 0 && tlen != 0) {
+
+ vqp_obj_tlv = (const struct vqp_obj_tlv_t *)tptr;
+ ND_TCHECK_SIZE(vqp_obj_tlv);
+ if (sizeof(struct vqp_obj_tlv_t) > tlen)
+ goto invalid;
+ vqp_obj_type = GET_BE_U_4(vqp_obj_tlv->obj_type);
+ vqp_obj_len = GET_BE_U_2(vqp_obj_tlv->obj_length);
+ tptr+=sizeof(struct vqp_obj_tlv_t);
+ tlen-=sizeof(struct vqp_obj_tlv_t);
+
+ ND_PRINT("\n\t %s Object (0x%08x), length %u, value: ",
+ tok2str(vqp_obj_values, "Unknown", vqp_obj_type),
+ vqp_obj_type, vqp_obj_len);
+
+ /* basic sanity check */
+ if (vqp_obj_type == 0 || vqp_obj_len ==0) {
+ return;
+ }
+
+ /* did we capture enough for fully decoding the object ? */
+ ND_TCHECK_LEN(tptr, vqp_obj_len);
+ if (vqp_obj_len > tlen)
+ goto invalid;
+
+ switch(vqp_obj_type) {
+ case VQP_OBJ_IP_ADDRESS:
+ if (vqp_obj_len != 4)
+ goto invalid;
+ ND_PRINT("%s (0x%08x)", GET_IPADDR_STRING(tptr),
+ GET_BE_U_4(tptr));
+ break;
+ /* those objects have similar semantics - fall through */
+ case VQP_OBJ_PORT_NAME:
+ case VQP_OBJ_VLAN_NAME:
+ case VQP_OBJ_VTP_DOMAIN:
+ case VQP_OBJ_ETHERNET_PKT:
+ nd_printjnp(ndo, tptr, vqp_obj_len);
+ break;
+ /* those objects have similar semantics - fall through */
+ case VQP_OBJ_MAC_ADDRESS:
+ case VQP_OBJ_MAC_NULL:
+ if (vqp_obj_len != MAC_ADDR_LEN)
+ goto invalid;
+ ND_PRINT("%s", GET_ETHERADDR_STRING(tptr));
+ break;
+ default:
+ if (ndo->ndo_vflag <= 1)
+ print_unknown_data(ndo,tptr, "\n\t ", vqp_obj_len);
+ break;
+ }
+ tptr += vqp_obj_len;
+ tlen -= vqp_obj_len;
+ nitems--;
+ }
+ return;
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-vrrp.c b/print-vrrp.c
new file mode 100644
index 0000000..ee97974
--- /dev/null
+++ b/print-vrrp.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2000 William C. Fenner.
+ * All rights reserved.
+ *
+ * Kevin Steves <ks@hp.se> July 2000
+ * Modified to:
+ * - print version, type string and packet length
+ * - print IP address count if > 1 (-v)
+ * - verify checksum (-v)
+ * - print authentication string (-v)
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * The name of William C. Fenner may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Virtual Router Redundancy Protocol (VRRP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "addrtoname.h"
+
+#include "ip.h"
+#include "ipproto.h"
+/*
+ * RFC 2338 (VRRP v2):
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Version| Type | Virtual Rtr ID| Priority | Count IP Addrs|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Auth Type | Adver Int | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IP Address (1) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | . |
+ * | . |
+ * | . |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IP Address (n) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Authentication Data (1) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Authentication Data (2) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ *
+ * RFC 5798 (VRRP v3):
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | IPv4 Fields or IPv6 Fields |
+ * ... ...
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |Version| Type | Virtual Rtr ID| Priority |Count IPvX Addr|
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |(rsvd) | Max Adver Int | Checksum |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * + +
+ * | IPvX Address(es) |
+ * + +
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+/* Type */
+#define VRRP_TYPE_ADVERTISEMENT 1
+
+static const struct tok type2str[] = {
+ { VRRP_TYPE_ADVERTISEMENT, "Advertisement" },
+ { 0, NULL }
+};
+
+/* Auth Type */
+#define VRRP_AUTH_NONE 0
+#define VRRP_AUTH_SIMPLE 1
+#define VRRP_AUTH_AH 2
+
+static const struct tok auth2str[] = {
+ { VRRP_AUTH_NONE, "none" },
+ { VRRP_AUTH_SIMPLE, "simple" },
+ { VRRP_AUTH_AH, "ah" },
+ { 0, NULL }
+};
+
+void
+vrrp_print(netdissect_options *ndo,
+ const u_char *bp, u_int len,
+ const u_char *bp2, int ttl)
+{
+ int version, type, auth_type = VRRP_AUTH_NONE; /* keep compiler happy */
+ const char *type_s;
+
+ ndo->ndo_protocol = "vrrp";
+ version = (GET_U_1(bp) & 0xf0) >> 4;
+ type = GET_U_1(bp) & 0x0f;
+ type_s = tok2str(type2str, "unknown type (%u)", type);
+ ND_PRINT("VRRPv%u, %s", version, type_s);
+ if (ttl != 255)
+ ND_PRINT(", (ttl %u)", ttl);
+ if (version < 2 || version > 3 || type != VRRP_TYPE_ADVERTISEMENT)
+ return;
+ ND_PRINT(", vrid %u, prio %u", GET_U_1(bp + 1), GET_U_1(bp + 2));
+
+ if (version == 2) {
+ auth_type = GET_U_1(bp + 4);
+ ND_PRINT(", authtype %s", tok2str(auth2str, NULL, auth_type));
+ ND_PRINT(", intvl %us, length %u", GET_U_1(bp + 5), len);
+ } else { /* version == 3 */
+ uint16_t intvl = (GET_U_1(bp + 4) & 0x0f) << 8 | GET_U_1(bp + 5);
+ ND_PRINT(", intvl %ucs, length %u", intvl, len);
+ }
+
+ if (ndo->ndo_vflag) {
+ u_int naddrs = GET_U_1(bp + 3);
+ u_int i;
+ char c;
+
+ if (version == 2 && ND_TTEST_LEN(bp, len)) {
+ struct cksum_vec vec[1];
+
+ vec[0].ptr = bp;
+ vec[0].len = len;
+ if (in_cksum(vec, 1))
+ ND_PRINT(", (bad vrrp cksum %x)",
+ GET_BE_U_2(bp + 6));
+ }
+
+ if (version == 3 && ND_TTEST_LEN(bp, len)) {
+ uint16_t cksum = nextproto4_cksum(ndo, (const struct ip *)bp2, bp,
+ len, len, IPPROTO_VRRP);
+ if (cksum)
+ ND_PRINT(", (bad vrrp cksum %x)",
+ GET_BE_U_2(bp + 6));
+ }
+
+ ND_PRINT(", addrs");
+ if (naddrs > 1)
+ ND_PRINT("(%u)", naddrs);
+ ND_PRINT(":");
+ c = ' ';
+ bp += 8;
+ for (i = 0; i < naddrs; i++) {
+ ND_PRINT("%c%s", c, GET_IPADDR_STRING(bp));
+ c = ',';
+ bp += 4;
+ }
+ if (version == 2 && auth_type == VRRP_AUTH_SIMPLE) { /* simple text password */
+ ND_PRINT(" auth \"");
+ /*
+ * RFC 2338 Section 5.3.10: "If the configured authentication string
+ * is shorter than 8 bytes, the remaining space MUST be zero-filled.
+ */
+ nd_printjnp(ndo, bp, 8);
+ ND_PRINT("\"");
+ }
+ }
+}
diff --git a/print-vsock.c b/print-vsock.c
new file mode 100644
index 0000000..bb18c92
--- /dev/null
+++ b/print-vsock.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2016 Gerard Garcia <nouboh@gmail.com>
+ * Copyright (c) 2017 Red Hat, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: Linux vsock printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include <stddef.h>
+
+#include "netdissect.h"
+#include "extract.h"
+
+enum af_vsockmon_transport {
+ AF_VSOCK_TRANSPORT_UNKNOWN = 0,
+ AF_VSOCK_TRANSPORT_NO_INFO = 1, /* No transport information */
+ AF_VSOCK_TRANSPORT_VIRTIO = 2, /* Virtio transport header */
+};
+
+static const struct tok vsock_transport[] = {
+ {AF_VSOCK_TRANSPORT_UNKNOWN, "UNKNOWN"},
+ {AF_VSOCK_TRANSPORT_NO_INFO, "NO_INFO"},
+ {AF_VSOCK_TRANSPORT_VIRTIO, "VIRTIO"},
+ { 0, NULL }
+};
+
+enum af_vsockmon_op {
+ AF_VSOCK_OP_UNKNOWN = 0,
+ AF_VSOCK_OP_CONNECT = 1,
+ AF_VSOCK_OP_DISCONNECT = 2,
+ AF_VSOCK_OP_CONTROL = 3,
+ AF_VSOCK_OP_PAYLOAD = 4,
+};
+
+static const struct tok vsock_op[] = {
+ {AF_VSOCK_OP_UNKNOWN, "UNKNOWN"},
+ {AF_VSOCK_OP_CONNECT, "CONNECT"},
+ {AF_VSOCK_OP_DISCONNECT, "DISCONNECT"},
+ {AF_VSOCK_OP_CONTROL, "CONTROL"},
+ {AF_VSOCK_OP_PAYLOAD, "PAYLOAD"},
+ { 0, NULL }
+};
+
+enum virtio_vsock_type {
+ VIRTIO_VSOCK_TYPE_STREAM = 1,
+};
+
+static const struct tok virtio_type[] = {
+ {VIRTIO_VSOCK_TYPE_STREAM, "STREAM"},
+ { 0, NULL }
+};
+
+enum virtio_vsock_op {
+ VIRTIO_VSOCK_OP_INVALID = 0,
+ VIRTIO_VSOCK_OP_REQUEST = 1,
+ VIRTIO_VSOCK_OP_RESPONSE = 2,
+ VIRTIO_VSOCK_OP_RST = 3,
+ VIRTIO_VSOCK_OP_SHUTDOWN = 4,
+ VIRTIO_VSOCK_OP_RW = 5,
+ VIRTIO_VSOCK_OP_CREDIT_UPDATE = 6,
+ VIRTIO_VSOCK_OP_CREDIT_REQUEST = 7,
+};
+
+static const struct tok virtio_op[] = {
+ {VIRTIO_VSOCK_OP_INVALID, "INVALID"},
+ {VIRTIO_VSOCK_OP_REQUEST, "REQUEST"},
+ {VIRTIO_VSOCK_OP_RESPONSE, "RESPONSE"},
+ {VIRTIO_VSOCK_OP_RST, "RST"},
+ {VIRTIO_VSOCK_OP_SHUTDOWN, "SHUTDOWN"},
+ {VIRTIO_VSOCK_OP_RW, "RW"},
+ {VIRTIO_VSOCK_OP_CREDIT_UPDATE, "CREDIT UPDATE"},
+ {VIRTIO_VSOCK_OP_CREDIT_REQUEST, "CREDIT REQUEST"},
+ { 0, NULL }
+};
+
+/* All fields are little-endian */
+
+struct virtio_vsock_hdr {
+ nd_uint64_t src_cid;
+ nd_uint64_t dst_cid;
+ nd_uint32_t src_port;
+ nd_uint32_t dst_port;
+ nd_uint32_t len;
+ nd_uint16_t type; /* enum virtio_vsock_type */
+ nd_uint16_t op; /* enum virtio_vsock_op */
+ nd_uint32_t flags;
+ nd_uint32_t buf_alloc;
+ nd_uint32_t fwd_cnt;
+};
+
+struct af_vsockmon_hdr {
+ nd_uint64_t src_cid;
+ nd_uint64_t dst_cid;
+ nd_uint32_t src_port;
+ nd_uint32_t dst_port;
+ nd_uint16_t op; /* enum af_vsockmon_op */
+ nd_uint16_t transport; /* enum af_vosckmon_transport */
+ nd_uint16_t len; /* size of transport header */
+ nd_uint8_t reserved[2];
+};
+
+static void
+vsock_virtio_hdr_print(netdissect_options *ndo, const struct virtio_vsock_hdr *hdr)
+{
+ uint16_t u16_v;
+ uint32_t u32_v;
+
+ u32_v = GET_LE_U_4(hdr->len);
+ ND_PRINT("len %u", u32_v);
+
+ u16_v = GET_LE_U_2(hdr->type);
+ ND_PRINT(", type %s",
+ tok2str(virtio_type, "Invalid type (%hu)", u16_v));
+
+ u16_v = GET_LE_U_2(hdr->op);
+ ND_PRINT(", op %s",
+ tok2str(virtio_op, "Invalid op (%hu)", u16_v));
+
+ u32_v = GET_LE_U_4(hdr->flags);
+ ND_PRINT(", flags %x", u32_v);
+
+ u32_v = GET_LE_U_4(hdr->buf_alloc);
+ ND_PRINT(", buf_alloc %u", u32_v);
+
+ u32_v = GET_LE_U_4(hdr->fwd_cnt);
+ ND_PRINT(", fwd_cnt %u", u32_v);
+}
+
+/*
+ * This size had better fit in a u_int.
+ */
+static u_int
+vsock_transport_hdr_size(uint16_t transport)
+{
+ switch (transport) {
+ case AF_VSOCK_TRANSPORT_VIRTIO:
+ return (u_int)sizeof(struct virtio_vsock_hdr);
+ default:
+ return 0;
+ }
+}
+
+/* Returns 0 on success, -1 on truncation */
+static int
+vsock_transport_hdr_print(netdissect_options *ndo, uint16_t transport,
+ const u_char *p, const u_int caplen)
+{
+ u_int transport_size = vsock_transport_hdr_size(transport);
+ const void *hdr;
+
+ if (caplen < sizeof(struct af_vsockmon_hdr) + transport_size) {
+ return -1;
+ }
+
+ hdr = p + sizeof(struct af_vsockmon_hdr);
+ switch (transport) {
+ case AF_VSOCK_TRANSPORT_VIRTIO:
+ ND_PRINT(" (");
+ vsock_virtio_hdr_print(ndo, hdr);
+ ND_PRINT(")");
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void
+vsock_hdr_print(netdissect_options *ndo, const u_char *p, const u_int caplen)
+{
+ const struct af_vsockmon_hdr *hdr = (const struct af_vsockmon_hdr *)p;
+ uint16_t hdr_transport, hdr_op;
+ uint32_t hdr_src_port, hdr_dst_port;
+ uint64_t hdr_src_cid, hdr_dst_cid;
+ u_int total_hdr_size;
+ int ret = 0;
+
+ hdr_transport = GET_LE_U_2(hdr->transport);
+ ND_PRINT("%s",
+ tok2str(vsock_transport, "Invalid transport (%u)",
+ hdr_transport));
+
+ /* If verbose level is more than 0 print transport details */
+ if (ndo->ndo_vflag) {
+ ret = vsock_transport_hdr_print(ndo, hdr_transport, p, caplen);
+ if (ret == 0)
+ ND_PRINT("\n\t");
+ } else
+ ND_PRINT(" ");
+
+ hdr_src_cid = GET_LE_U_8(hdr->src_cid);
+ hdr_dst_cid = GET_LE_U_8(hdr->dst_cid);
+ hdr_src_port = GET_LE_U_4(hdr->src_port);
+ hdr_dst_port = GET_LE_U_4(hdr->dst_port);
+ hdr_op = GET_LE_U_2(hdr->op);
+ ND_PRINT("%" PRIu64 ".%u > %" PRIu64 ".%u %s, length %u",
+ hdr_src_cid, hdr_src_port,
+ hdr_dst_cid, hdr_dst_port,
+ tok2str(vsock_op, " invalid op (%u)", hdr_op),
+ caplen);
+
+ if (ret < 0)
+ goto trunc;
+
+ /* If debug level is more than 1 print payload contents */
+ /* This size had better fit in a u_int */
+ total_hdr_size = (u_int)sizeof(struct af_vsockmon_hdr) +
+ vsock_transport_hdr_size(hdr_transport);
+ if (ndo->ndo_vflag > 1 && hdr_op == AF_VSOCK_OP_PAYLOAD) {
+ if (caplen > total_hdr_size) {
+ const u_char *payload = p + total_hdr_size;
+
+ ND_PRINT("\n");
+ print_unknown_data(ndo, payload, "\t",
+ caplen - total_hdr_size);
+ } else
+ goto trunc;
+ }
+ return;
+
+trunc:
+ nd_print_trunc(ndo);
+}
+
+void
+vsock_if_print(netdissect_options *ndo, const struct pcap_pkthdr *h,
+ const u_char *cp)
+{
+ u_int caplen = h->caplen;
+
+ ndo->ndo_protocol = "vsock";
+
+ if (caplen < sizeof(struct af_vsockmon_hdr)) {
+ nd_print_trunc(ndo);
+ ndo->ndo_ll_hdr_len += caplen;
+ return;
+ }
+ ndo->ndo_ll_hdr_len += sizeof(struct af_vsockmon_hdr);
+ vsock_hdr_print(ndo, cp, caplen);
+}
diff --git a/print-vtp.c b/print-vtp.c
new file mode 100644
index 0000000..bcee64c
--- /dev/null
+++ b/print-vtp.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 1998-2007 The TCPDUMP project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Reference documentation:
+ * https://www.cisco.com/c/en/us/support/docs/lan-switching/vtp/10558-21.html
+ * https://docstore.mik.ua/univercd/cc/td/doc/product/lan/trsrb/frames.htm
+ *
+ * Original code ode by Carles Kishimoto <carles.kishimoto@gmail.com>
+ */
+
+/* \summary: Cisco VLAN Trunking Protocol (VTP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+#define VTP_HEADER_LEN 36
+#define VTP_DOMAIN_NAME_LEN 32
+#define VTP_MD5_DIGEST_LEN 16
+#define VTP_UPDATE_TIMESTAMP_LEN 12
+#define VTP_VLAN_INFO_FIXED_PART_LEN 12 /* length of VLAN info before VLAN name */
+
+#define VTP_SUMMARY_ADV 0x01
+#define VTP_SUBSET_ADV 0x02
+#define VTP_ADV_REQUEST 0x03
+#define VTP_JOIN_MESSAGE 0x04
+
+struct vtp_vlan_ {
+ nd_uint8_t len;
+ nd_uint8_t status;
+ nd_uint8_t type;
+ nd_uint8_t name_len;
+ nd_uint16_t vlanid;
+ nd_uint16_t mtu;
+ nd_uint32_t index;
+};
+
+static const struct tok vtp_message_type_values[] = {
+ { VTP_SUMMARY_ADV, "Summary advertisement"},
+ { VTP_SUBSET_ADV, "Subset advertisement"},
+ { VTP_ADV_REQUEST, "Advertisement request"},
+ { VTP_JOIN_MESSAGE, "Join message"},
+ { 0, NULL }
+};
+
+static const struct tok vtp_header_values[] = {
+ { 0x01, "Followers"}, /* On Summary advertisement, 3rd byte is Followers */
+ { 0x02, "Seq number"}, /* On Subset advertisement, 3rd byte is Sequence number */
+ { 0x03, "Rsvd"}, /* On Adver. requests 3rd byte is Rsvd */
+ { 0x04, "Rsvd"}, /* On Adver. requests 3rd byte is Rsvd */
+ { 0, NULL }
+};
+
+static const struct tok vtp_vlan_type_values[] = {
+ { 0x01, "Ethernet"},
+ { 0x02, "FDDI"},
+ { 0x03, "TrCRF"},
+ { 0x04, "FDDI-net"},
+ { 0x05, "TrBRF"},
+ { 0, NULL }
+};
+
+static const struct tok vtp_vlan_status[] = {
+ { 0x00, "Operational"},
+ { 0x01, "Suspended"},
+ { 0, NULL }
+};
+
+#define VTP_VLAN_SOURCE_ROUTING_RING_NUMBER 0x01
+#define VTP_VLAN_SOURCE_ROUTING_BRIDGE_NUMBER 0x02
+#define VTP_VLAN_STP_TYPE 0x03
+#define VTP_VLAN_PARENT_VLAN 0x04
+#define VTP_VLAN_TRANS_BRIDGED_VLAN 0x05
+#define VTP_VLAN_PRUNING 0x06
+#define VTP_VLAN_BRIDGE_TYPE 0x07
+#define VTP_VLAN_ARP_HOP_COUNT 0x08
+#define VTP_VLAN_STE_HOP_COUNT 0x09
+#define VTP_VLAN_BACKUP_CRF_MODE 0x0A
+
+static const struct tok vtp_vlan_tlv_values[] = {
+ { VTP_VLAN_SOURCE_ROUTING_RING_NUMBER, "Source-Routing Ring Number TLV"},
+ { VTP_VLAN_SOURCE_ROUTING_BRIDGE_NUMBER, "Source-Routing Bridge Number TLV"},
+ { VTP_VLAN_STP_TYPE, "STP type TLV"},
+ { VTP_VLAN_PARENT_VLAN, "Parent VLAN TLV"},
+ { VTP_VLAN_TRANS_BRIDGED_VLAN, "Translationally bridged VLANs TLV"},
+ { VTP_VLAN_PRUNING, "Pruning TLV"},
+ { VTP_VLAN_BRIDGE_TYPE, "Bridge Type TLV"},
+ { VTP_VLAN_ARP_HOP_COUNT, "Max ARP Hop Count TLV"},
+ { VTP_VLAN_STE_HOP_COUNT, "Max STE Hop Count TLV"},
+ { VTP_VLAN_BACKUP_CRF_MODE, "Backup CRF Mode TLV"},
+ { 0, NULL }
+};
+
+static const struct tok vtp_stp_type_values[] = {
+ { 1, "SRT"},
+ { 2, "SRB"},
+ { 3, "Auto"},
+ { 0, NULL }
+};
+
+void
+vtp_print(netdissect_options *ndo,
+ const u_char *pptr, const u_int length)
+{
+ u_int type, len, name_len, tlv_len, tlv_value, mgmtd_len;
+ const u_char *tptr;
+ const struct vtp_vlan_ *vtp_vlan;
+
+ ndo->ndo_protocol = "vtp";
+ if (length < VTP_HEADER_LEN)
+ goto invalid;
+
+ tptr = pptr;
+
+ ND_TCHECK_LEN(tptr, VTP_HEADER_LEN);
+
+ type = GET_U_1(tptr + 1);
+ ND_PRINT("VTPv%u, Message %s (0x%02x), length %u",
+ GET_U_1(tptr),
+ tok2str(vtp_message_type_values,"Unknown message type", type),
+ type,
+ length);
+
+ /* In non-verbose mode, just print version and message type */
+ if (ndo->ndo_vflag < 1) {
+ goto tcheck_full_packet;
+ }
+
+ /* verbose mode print all fields */
+ ND_PRINT("\n\tDomain name: ");
+ mgmtd_len = GET_U_1(tptr + 3);
+ if (mgmtd_len < 1 || mgmtd_len > VTP_DOMAIN_NAME_LEN) {
+ ND_PRINT(" [invalid MgmtD Len %u]", mgmtd_len);
+ goto invalid;
+ }
+ nd_printjnp(ndo, tptr + 4, mgmtd_len);
+ ND_PRINT(", %s: %u",
+ tok2str(vtp_header_values, "Unknown", type),
+ GET_U_1(tptr + 2));
+
+ tptr += VTP_HEADER_LEN;
+
+ switch (type) {
+
+ case VTP_SUMMARY_ADV:
+
+ /*
+ * SUMMARY ADVERTISEMENT
+ *
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version | Code | Followers | MgmtD Len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Management Domain Name (zero-padded to 32 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Configuration revision number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Updater Identity IP address |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Update Timestamp (12 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | MD5 digest (16 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+ ND_PRINT("\n\t Config Rev %x, Updater %s",
+ GET_BE_U_4(tptr),
+ GET_IPADDR_STRING(tptr+4));
+ tptr += 8;
+ ND_PRINT(", Timestamp 0x%08x 0x%08x 0x%08x",
+ GET_BE_U_4(tptr),
+ GET_BE_U_4(tptr + 4),
+ GET_BE_U_4(tptr + 8));
+ tptr += VTP_UPDATE_TIMESTAMP_LEN;
+ ND_PRINT(", MD5 digest: %08x%08x%08x%08x",
+ GET_BE_U_4(tptr),
+ GET_BE_U_4(tptr + 4),
+ GET_BE_U_4(tptr + 8),
+ GET_BE_U_4(tptr + 12));
+ tptr += VTP_MD5_DIGEST_LEN;
+ break;
+
+ case VTP_SUBSET_ADV:
+
+ /*
+ * SUBSET ADVERTISEMENT
+ *
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version | Code | Seq number | MgmtD Len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Management Domain Name (zero-padded to 32 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Configuration revision number |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VLAN info field 1 |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ................ |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VLAN info field N |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+ ND_PRINT(", Config Rev %x", GET_BE_U_4(tptr));
+
+ /*
+ * VLAN INFORMATION
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | V info len | Status | VLAN type | VLAN name len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | ISL vlan id | MTU size |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | 802.10 index (SAID) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VLAN name |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+ tptr += 4;
+ while ((unsigned)(tptr - pptr) < length) {
+
+ len = GET_U_1(tptr);
+ if (len == 0)
+ break;
+
+ ND_TCHECK_LEN(tptr, len);
+
+ vtp_vlan = (const struct vtp_vlan_*)tptr;
+ if (len < VTP_VLAN_INFO_FIXED_PART_LEN)
+ goto invalid;
+ ND_PRINT("\n\tVLAN info status %s, type %s, VLAN-id %u, MTU %u, SAID 0x%08x, Name ",
+ tok2str(vtp_vlan_status,"Unknown",GET_U_1(vtp_vlan->status)),
+ tok2str(vtp_vlan_type_values,"Unknown",GET_U_1(vtp_vlan->type)),
+ GET_BE_U_2(vtp_vlan->vlanid),
+ GET_BE_U_2(vtp_vlan->mtu),
+ GET_BE_U_4(vtp_vlan->index));
+ len -= VTP_VLAN_INFO_FIXED_PART_LEN;
+ tptr += VTP_VLAN_INFO_FIXED_PART_LEN;
+ name_len = GET_U_1(vtp_vlan->name_len);
+ if (len < 4*((name_len + 3)/4))
+ goto invalid;
+ nd_printjnp(ndo, tptr, name_len);
+
+ /*
+ * Vlan names are aligned to 32-bit boundaries.
+ */
+ len -= 4*((name_len + 3)/4);
+ tptr += 4*((name_len + 3)/4);
+
+ /* TLV information follows */
+
+ while (len > 0) {
+
+ /*
+ * Cisco specs say 2 bytes for type + 2 bytes for length;
+ * see https://docstore.mik.ua/univercd/cc/td/doc/product/lan/trsrb/frames.htm
+ * However, actual packets on the wire appear to use 1
+ * byte for the type and 1 byte for the length, so that's
+ * what we do.
+ */
+ if (len < 2)
+ goto invalid;
+ type = GET_U_1(tptr);
+ tlv_len = GET_U_1(tptr + 1);
+
+ ND_PRINT("\n\t\t%s (0x%04x) TLV",
+ tok2str(vtp_vlan_tlv_values, "Unknown", type),
+ type);
+
+ if (len < tlv_len * 2 + 2) {
+ ND_PRINT(" (TLV goes past the end of the packet)");
+ goto invalid;
+ }
+ ND_TCHECK_LEN(tptr, tlv_len * 2 + 2);
+
+ /*
+ * We assume the value is a 2-byte integer; the length is
+ * in units of 16-bit words.
+ */
+ if (tlv_len != 1) {
+ ND_PRINT(" (invalid TLV length %u != 1)", tlv_len);
+ goto invalid;
+ } else {
+ tlv_value = GET_BE_U_2(tptr + 2);
+
+ switch (type) {
+ case VTP_VLAN_STE_HOP_COUNT:
+ ND_PRINT(", %u", tlv_value);
+ break;
+
+ case VTP_VLAN_PRUNING:
+ ND_PRINT(", %s (%u)",
+ tlv_value == 1 ? "Enabled" : "Disabled",
+ tlv_value);
+ break;
+
+ case VTP_VLAN_STP_TYPE:
+ ND_PRINT(", %s (%u)",
+ tok2str(vtp_stp_type_values, "Unknown", tlv_value),
+ tlv_value);
+ break;
+
+ case VTP_VLAN_BRIDGE_TYPE:
+ ND_PRINT(", %s (%u)",
+ tlv_value == 1 ? "SRB" : "SRT",
+ tlv_value);
+ break;
+
+ case VTP_VLAN_BACKUP_CRF_MODE:
+ ND_PRINT(", %s (%u)",
+ tlv_value == 1 ? "Backup" : "Not backup",
+ tlv_value);
+ break;
+
+ /*
+ * FIXME those are the defined TLVs that lack a decoder
+ * you are welcome to contribute code ;-)
+ */
+
+ case VTP_VLAN_SOURCE_ROUTING_RING_NUMBER:
+ case VTP_VLAN_SOURCE_ROUTING_BRIDGE_NUMBER:
+ case VTP_VLAN_PARENT_VLAN:
+ case VTP_VLAN_TRANS_BRIDGED_VLAN:
+ case VTP_VLAN_ARP_HOP_COUNT:
+ default:
+ print_unknown_data(ndo, tptr, "\n\t\t ", 2 + tlv_len*2);
+ break;
+ }
+ }
+ len -= 2 + tlv_len*2;
+ tptr += 2 + tlv_len*2;
+ }
+ }
+ break;
+
+ case VTP_ADV_REQUEST:
+
+ /*
+ * ADVERTISEMENT REQUEST
+ *
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Version | Code | Reserved | MgmtD Len |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Management Domain Name (zero-padded to 32 bytes) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Start value |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+
+ ND_PRINT("\n\tStart value: %u", GET_BE_U_4(tptr));
+ break;
+
+ case VTP_JOIN_MESSAGE:
+
+ /* FIXME - Could not find message format */
+ break;
+
+ default:
+ break;
+ }
+
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+tcheck_full_packet:
+ ND_TCHECK_LEN(pptr, length);
+}
diff --git a/print-vxlan-gpe.c b/print-vxlan-gpe.c
new file mode 100644
index 0000000..13cba42
--- /dev/null
+++ b/print-vxlan-gpe.c
@@ -0,0 +1,124 @@
+/* Copyright (c) 2015, bugyo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: Generic Protocol Extension for VXLAN (VXLAN GPE) printer */
+
+/* specification: draft-ietf-nvo3-vxlan-gpe-10 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+static const struct tok vxlan_gpe_flags [] = {
+ { 0x08, "I" },
+ { 0x04, "P" },
+ { 0x02, "B" },
+ { 0x01, "O" },
+ { 0, NULL }
+};
+
+#define VXLAN_GPE_HDR_LEN 8
+
+/*
+ * VXLAN GPE header, draft-ietf-nvo3-vxlan-gpe-01
+ * Generic Protocol Extension for VXLAN
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|R|Ver|I|P|R|O| Reserved |Next Protocol |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VXLAN Network Identifier (VNI) | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+void
+vxlan_gpe_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ uint8_t flags;
+ uint8_t next_protocol;
+ uint32_t vni;
+
+ ndo->ndo_protocol = "vxlan_gpe";
+ ND_PRINT("VXLAN-GPE, ");
+ if (len < VXLAN_GPE_HDR_LEN) {
+ ND_PRINT(" (len %u < %u)", len, VXLAN_GPE_HDR_LEN);
+ goto invalid;
+ }
+
+ flags = GET_U_1(bp);
+ bp += 1;
+ len -= 1;
+ ND_PRINT("flags [%s], ",
+ bittok2str_nosep(vxlan_gpe_flags, "none", flags));
+
+ /* Reserved */
+ bp += 2;
+ len -= 2;
+
+ next_protocol = GET_U_1(bp);
+ bp += 1;
+ len -= 1;
+
+ vni = GET_BE_U_3(bp);
+ bp += 3;
+ len -= 3;
+
+ /* Reserved */
+ ND_TCHECK_1(bp);
+ bp += 1;
+ len -= 1;
+
+ ND_PRINT("vni %u", vni);
+ ND_PRINT(ndo->ndo_vflag ? "\n " : ": ");
+
+ switch (next_protocol) {
+ case 0x1:
+ ip_print(ndo, bp, len);
+ break;
+ case 0x2:
+ ip6_print(ndo, bp, len);
+ break;
+ case 0x3:
+ ether_print(ndo, bp, len, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+ break;
+ case 0x4:
+ nsh_print(ndo, bp, len);
+ break;
+ default:
+ ND_PRINT("ERROR: unknown-next-protocol");
+ goto invalid;
+ }
+
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
+
diff --git a/print-vxlan.c b/print-vxlan.c
new file mode 100644
index 0000000..60dcd44
--- /dev/null
+++ b/print-vxlan.c
@@ -0,0 +1,83 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Original code by Francesco Fondelli (francesco dot fondelli, gmail dot com)
+ */
+
+/* \summary: Virtual eXtensible Local Area Network (VXLAN) printer */
+
+/* specification: RFC 7348 */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "extract.h"
+
+static const struct tok vxlan_flags [] = {
+ { 0x08, "I" },
+ { 0, NULL }
+};
+#define VXLAN_HDR_LEN 8
+
+/*
+ * VXLAN header, RFC7348
+ * Virtual eXtensible Local Area Network (VXLAN): A Framework
+ * for Overlaying Virtualized Layer 2 Networks over Layer 3 Networks
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|R|R|R|I|R|R|R| Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | VXLAN Network Identifier (VNI) | Reserved |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ */
+
+void
+vxlan_print(netdissect_options *ndo, const u_char *bp, u_int len)
+{
+ uint8_t flags;
+ uint32_t vni;
+
+ ndo->ndo_protocol = "vxlan";
+ nd_print_protocol_caps(ndo);
+ if (len < VXLAN_HDR_LEN)
+ goto invalid;
+
+ flags = GET_U_1(bp);
+ bp += 1;
+ ND_PRINT(", flags [%s] (0x%02x), ",
+ bittok2str_nosep(vxlan_flags, "invalid", flags), flags);
+
+ /* 1st Reserved */
+ bp += 3;
+
+ vni = GET_BE_U_3(bp);
+ bp += 3;
+ ND_PRINT("vni %u\n", vni);
+
+ /* 2nd Reserved */
+ ND_TCHECK_1(bp);
+ bp += 1;
+
+ ether_print(ndo, bp, len - VXLAN_HDR_LEN, ND_BYTES_AVAILABLE_AFTER(bp), NULL, NULL);
+
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-wb.c b/print-wb.c
new file mode 100644
index 0000000..35b5a19
--- /dev/null
+++ b/print-wb.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 1993, 1994, 1995, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: White Board printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#define ND_LONGJMP_FROM_TCHECK
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "extract.h"
+
+
+#if 0
+/*
+ * Largest packet size. Everything should fit within this space.
+ * For instance, multiline objects are sent piecewise.
+ */
+#define MAXFRAMESIZE 1024
+#endif
+
+/*
+ * Multiple drawing ops can be sent in one packet. Each one starts on a
+ * an even multiple of DOP_ALIGN bytes, which must be a power of two.
+ */
+#define DOP_ALIGN 4
+#define DOP_ROUNDUP(x) roundup2(x, DOP_ALIGN)
+#define DOP_NEXT(d)\
+ ((const struct dophdr *)((const u_char *)(d) + \
+ DOP_ROUNDUP(GET_BE_U_2((d)->dh_len) + sizeof(*(d)))))
+
+/*
+ * Format of the whiteboard packet header.
+ * The transport level header.
+ */
+struct pkt_hdr {
+ nd_uint32_t ph_src; /* site id of source */
+ nd_uint32_t ph_ts; /* time stamp (for skew computation) */
+ nd_uint16_t ph_version; /* version number */
+ nd_uint8_t ph_type; /* message type */
+ nd_uint8_t ph_flags; /* message flags */
+};
+
+/* Packet types */
+#define PT_DRAWOP 0 /* drawing operation */
+#define PT_ID 1 /* announcement packet */
+#define PT_RREQ 2 /* repair request */
+#define PT_RREP 3 /* repair reply */
+#define PT_KILL 4 /* terminate participation */
+#define PT_PREQ 5 /* page vector request */
+#define PT_PREP 7 /* page vector reply */
+
+#if 0
+#ifdef PF_USER
+#undef PF_USER /* {Digital,Tru64} UNIX define this, alas */
+#endif
+
+/* flags */
+#define PF_USER 0x01 /* hint that packet has interactive data */
+#define PF_VIS 0x02 /* only visible ops wanted */
+#endif
+
+struct PageID {
+ nd_uint32_t p_sid; /* session id of initiator */
+ nd_uint32_t p_uid; /* page number */
+};
+
+struct dophdr {
+ nd_uint32_t dh_ts; /* sender's timestamp */
+ nd_uint16_t dh_len; /* body length */
+ nd_uint8_t dh_flags;
+ nd_uint8_t dh_type; /* body type */
+ /* body follows */
+};
+/*
+ * Drawing op sub-types.
+ */
+#define DT_RECT 2
+#define DT_LINE 3
+#define DT_ML 4
+#define DT_DEL 5
+#define DT_XFORM 6
+#define DT_ELL 7
+#define DT_CHAR 8
+#define DT_STR 9
+#define DT_NOP 10
+#define DT_PSCODE 11
+#define DT_PSCOMP 12
+#define DT_REF 13
+#define DT_SKIP 14
+#define DT_HOLE 15
+static const struct tok dop_str[] = {
+ { DT_RECT, "RECT" },
+ { DT_LINE, "LINE" },
+ { DT_ML, "ML" },
+ { DT_DEL, "DEL" },
+ { DT_XFORM, "XFORM" },
+ { DT_ELL, "ELL" },
+ { DT_CHAR, "CHAR" },
+ { DT_STR, "STR" },
+ { DT_NOP, "NOP" },
+ { DT_PSCODE, "PSCODE" },
+ { DT_PSCOMP, "PSCOMP" },
+ { DT_REF, "REF" },
+ { DT_SKIP, "SKIP" },
+ { DT_HOLE, "HOLE" },
+ { 0, NULL }
+};
+
+/*
+ * A drawing operation.
+ */
+struct pkt_dop {
+ struct PageID pd_page; /* page that operations apply to */
+ nd_uint32_t pd_sseq; /* start sequence number */
+ nd_uint32_t pd_eseq; /* end sequence number */
+ /* drawing ops follow */
+};
+
+/*
+ * A repair request.
+ */
+struct pkt_rreq {
+ nd_uint32_t pr_id; /* source id of drawops to be repaired */
+ struct PageID pr_page; /* page of drawops */
+ nd_uint32_t pr_sseq; /* start seqno */
+ nd_uint32_t pr_eseq; /* end seqno */
+};
+
+/*
+ * A repair reply.
+ */
+struct pkt_rrep {
+ nd_uint32_t pr_id; /* original site id of ops */
+ struct pkt_dop pr_dop;
+ /* drawing ops follow */
+};
+
+struct id_off {
+ nd_uint32_t id;
+ nd_uint32_t off;
+};
+
+struct pgstate {
+ nd_uint32_t slot;
+ struct PageID page;
+ nd_uint16_t nid;
+ nd_uint16_t rsvd;
+ /* seqptr's */
+};
+
+/*
+ * An announcement packet.
+ */
+struct pkt_id {
+ nd_uint32_t pi_mslot;
+ struct PageID pi_mpage; /* current page */
+ struct pgstate pi_ps;
+ /* seqptr's */
+ /* null-terminated site name */
+};
+
+struct pkt_preq {
+ struct PageID pp_page;
+ nd_uint32_t pp_low;
+ nd_uint32_t pp_high;
+};
+
+struct pkt_prep {
+ nd_uint32_t pp_n; /* size of pageid array */
+ /* pgstate's follow */
+};
+
+static int
+wb_id(netdissect_options *ndo,
+ const struct pkt_id *id, u_int len)
+{
+ u_int i;
+ const u_char *sitename;
+ const struct id_off *io;
+ char c;
+ u_int nid;
+
+ ND_PRINT(" wb-id:");
+ if (len < sizeof(*id))
+ return (-1);
+ len -= sizeof(*id);
+
+ ND_PRINT(" %u/%s:%u (max %u/%s:%u) ",
+ GET_BE_U_4(id->pi_ps.slot),
+ GET_IPADDR_STRING(id->pi_ps.page.p_sid),
+ GET_BE_U_4(id->pi_ps.page.p_uid),
+ GET_BE_U_4(id->pi_mslot),
+ GET_IPADDR_STRING(id->pi_mpage.p_sid),
+ GET_BE_U_4(id->pi_mpage.p_uid));
+ /* now the rest of the fixed-size part of struct pkt_id */
+ ND_TCHECK_SIZE(id);
+
+ nid = GET_BE_U_2(id->pi_ps.nid);
+ if (len < sizeof(*io) * nid)
+ return (-1);
+ len -= sizeof(*io) * nid;
+ io = (const struct id_off *)(id + 1);
+ sitename = (const u_char *)(io + nid);
+
+ c = '<';
+ for (i = 0; i < nid; ++io, ++i) {
+ ND_PRINT("%c%s:%u",
+ c, GET_IPADDR_STRING(io->id), GET_BE_U_4(io->off));
+ c = ',';
+ }
+ ND_PRINT("> \"");
+ nd_printjnp(ndo, sitename, len);
+ ND_PRINT("\"");
+ return (0);
+}
+
+static int
+wb_rreq(netdissect_options *ndo,
+ const struct pkt_rreq *rreq, u_int len)
+{
+ ND_PRINT(" wb-rreq:");
+ if (len < sizeof(*rreq))
+ return (-1);
+
+ ND_PRINT(" please repair %s %s:%u<%u:%u>",
+ GET_IPADDR_STRING(rreq->pr_id),
+ GET_IPADDR_STRING(rreq->pr_page.p_sid),
+ GET_BE_U_4(rreq->pr_page.p_uid),
+ GET_BE_U_4(rreq->pr_sseq),
+ GET_BE_U_4(rreq->pr_eseq));
+ return (0);
+}
+
+static int
+wb_preq(netdissect_options *ndo,
+ const struct pkt_preq *preq, u_int len)
+{
+ ND_PRINT(" wb-preq:");
+ if (len < sizeof(*preq))
+ return (-1);
+
+ ND_PRINT(" need %u/%s:%u",
+ GET_BE_U_4(preq->pp_low),
+ GET_IPADDR_STRING(preq->pp_page.p_sid),
+ GET_BE_U_4(preq->pp_page.p_uid));
+ /* now the rest of the fixed-size part of struct pkt_req */
+ ND_TCHECK_SIZE(preq);
+ return (0);
+}
+
+static int
+wb_prep(netdissect_options *ndo,
+ const struct pkt_prep *prep, u_int len)
+{
+ u_int n;
+ const struct pgstate *ps;
+
+ ND_PRINT(" wb-prep:");
+ if (len < sizeof(*prep))
+ return (-1);
+ n = GET_BE_U_4(prep->pp_n);
+ ps = (const struct pgstate *)(prep + 1);
+ while (n != 0) {
+ const struct id_off *io, *ie;
+ char c = '<';
+
+ ND_PRINT(" %u/%s:%u",
+ GET_BE_U_4(ps->slot),
+ GET_IPADDR_STRING(ps->page.p_sid),
+ GET_BE_U_4(ps->page.p_uid));
+ /* now the rest of the fixed-size part of struct pgstate */
+ ND_TCHECK_SIZE(ps);
+ io = (const struct id_off *)(ps + 1);
+ for (ie = io + GET_U_1(ps->nid); io < ie; ++io) {
+ ND_PRINT("%c%s:%u", c, GET_IPADDR_STRING(io->id),
+ GET_BE_U_4(io->off));
+ c = ',';
+ }
+ ND_PRINT(">");
+ ps = (const struct pgstate *)io;
+ n--;
+ }
+ return 0;
+}
+
+static void
+wb_dops(netdissect_options *ndo, const struct pkt_dop *dop,
+ uint32_t ss, uint32_t es)
+{
+ const struct dophdr *dh = (const struct dophdr *)((const u_char *)dop + sizeof(*dop));
+
+ ND_PRINT(" <");
+ for ( ; ss <= es; ++ss) {
+ u_int t;
+
+ t = GET_U_1(dh->dh_type);
+
+ ND_PRINT(" %s", tok2str(dop_str, "dop-%u!", t));
+ if (t == DT_SKIP || t == DT_HOLE) {
+ uint32_t ts = GET_BE_U_4(dh->dh_ts);
+ ND_PRINT("%u", ts - ss + 1);
+ if (ss > ts || ts > es) {
+ ND_PRINT("[|]");
+ if (ts < ss)
+ return;
+ }
+ ss = ts;
+ }
+ dh = DOP_NEXT(dh);
+ }
+ ND_PRINT(" >");
+}
+
+static int
+wb_rrep(netdissect_options *ndo,
+ const struct pkt_rrep *rrep, u_int len)
+{
+ const struct pkt_dop *dop = &rrep->pr_dop;
+
+ ND_PRINT(" wb-rrep:");
+ if (len < sizeof(*rrep))
+ return (-1);
+ len -= sizeof(*rrep);
+
+ ND_PRINT(" for %s %s:%u<%u:%u>",
+ GET_IPADDR_STRING(rrep->pr_id),
+ GET_IPADDR_STRING(dop->pd_page.p_sid),
+ GET_BE_U_4(dop->pd_page.p_uid),
+ GET_BE_U_4(dop->pd_sseq),
+ GET_BE_U_4(dop->pd_eseq));
+
+ if (ndo->ndo_vflag)
+ wb_dops(ndo, dop,
+ GET_BE_U_4(dop->pd_sseq),
+ GET_BE_U_4(dop->pd_eseq));
+ return (0);
+}
+
+static int
+wb_drawop(netdissect_options *ndo,
+ const struct pkt_dop *dop, u_int len)
+{
+ ND_PRINT(" wb-dop:");
+ if (len < sizeof(*dop))
+ return (-1);
+ len -= sizeof(*dop);
+
+ ND_PRINT(" %s:%u<%u:%u>",
+ GET_IPADDR_STRING(dop->pd_page.p_sid),
+ GET_BE_U_4(dop->pd_page.p_uid),
+ GET_BE_U_4(dop->pd_sseq),
+ GET_BE_U_4(dop->pd_eseq));
+
+ if (ndo->ndo_vflag)
+ wb_dops(ndo, dop,
+ GET_BE_U_4(dop->pd_sseq),
+ GET_BE_U_4(dop->pd_eseq));
+ return (0);
+}
+
+/*
+ * Print whiteboard multicast packets.
+ */
+void
+wb_print(netdissect_options *ndo,
+ const u_char *hdr, u_int len)
+{
+ const struct pkt_hdr *ph;
+ uint8_t type;
+ int print_result;
+
+ ndo->ndo_protocol = "wb";
+ ph = (const struct pkt_hdr *)hdr;
+ if (len < sizeof(*ph))
+ goto invalid;
+ ND_TCHECK_SIZE(ph);
+ len -= sizeof(*ph);
+
+ if (GET_U_1(ph->ph_flags))
+ ND_PRINT("*");
+ type = GET_U_1(ph->ph_type);
+ switch (type) {
+
+ case PT_KILL:
+ ND_PRINT(" wb-kill");
+ return;
+
+ case PT_ID:
+ print_result = wb_id(ndo, (const struct pkt_id *)(ph + 1), len);
+ break;
+
+ case PT_RREQ:
+ print_result = wb_rreq(ndo, (const struct pkt_rreq *)(ph + 1), len);
+ break;
+
+ case PT_RREP:
+ print_result = wb_rrep(ndo, (const struct pkt_rrep *)(ph + 1), len);
+ break;
+
+ case PT_DRAWOP:
+ print_result = wb_drawop(ndo, (const struct pkt_dop *)(ph + 1), len);
+ break;
+
+ case PT_PREQ:
+ print_result = wb_preq(ndo, (const struct pkt_preq *)(ph + 1), len);
+ break;
+
+ case PT_PREP:
+ print_result = wb_prep(ndo, (const struct pkt_prep *)(ph + 1), len);
+ break;
+
+ default:
+ ND_PRINT(" wb-%u!", type);
+ print_result = -1;
+ }
+ if (print_result < 0)
+ goto invalid;
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-zep.c b/print-zep.c
new file mode 100644
index 0000000..ac4e017
--- /dev/null
+++ b/print-zep.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* \summary: ZigBee Encapsulation Protocol (ZEP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+
+#include "extract.h"
+
+/* From wireshark packet-zep.c:
+ *
+ ***********************************************************************
+ *
+ * ZEP Packets must be received in the following format:
+ *
+ * |UDP Header| ZEP Header |IEEE 802.15.4 Packet|
+ * | 8 bytes | 16/32 bytes | <= 127 bytes |
+ *
+ ***********************************************************************
+ *
+ * ZEP v1 Header will have the following format:
+ * |Preamble|Version|Channel ID|Device ID|CRC/LQI Mode|LQI Val|Reserved|Length|
+ * |2 bytes |1 byte | 1 byte | 2 bytes | 1 byte |1 byte |7 bytes |1 byte|
+ *
+ * ZEP v2 Header will have the following format (if type=1/Data):
+ * |Prmbl|Ver |Type |ChnlID|DevID|C/L Mode|LQI|NTP TS|Seq#|Res |Len|
+ * | 2 | 1 | 1 | 1 | 2 | 1 | 1 | 8 | 4 | 10 | 1 |
+ *
+ * ZEP v2 Header will have the following format (if type=2/Ack):
+ * |Preamble|Version| Type |Sequence#|
+ * |2 bytes |1 byte |1 byte| 4 bytes |
+ *------------------------------------------------------------
+ */
+
+#define JAN_1970 2208988800U
+
+/* Print timestamp */
+static void zep_print_ts(netdissect_options *ndo, const u_char *p)
+{
+ int32_t i;
+ uint32_t uf;
+ uint32_t f;
+ float ff;
+
+ i = GET_BE_U_4(p);
+ uf = GET_BE_U_4(p + 4);
+ ff = (float) uf;
+ if (ff < 0.0) /* some compilers are buggy */
+ ff += FMAXINT;
+ ff = (float) (ff / FMAXINT); /* shift radix point by 32 bits */
+ f = (uint32_t) (ff * 1000000000.0); /* treat fraction as parts per
+ billion */
+ ND_PRINT("%u.%09d", i, f);
+
+#ifdef HAVE_STRFTIME
+ /*
+ * print the time in human-readable format.
+ */
+ if (i) {
+ time_t seconds = i - JAN_1970;
+ struct tm *tm;
+ char time_buf[128];
+
+ tm = localtime(&seconds);
+ strftime(time_buf, sizeof (time_buf), "%Y/%m/%d %H:%M:%S", tm);
+ ND_PRINT(" (%s)", time_buf);
+ }
+#endif
+}
+
+/*
+ * Main function to print packets.
+ */
+
+void
+zep_print(netdissect_options *ndo,
+ const u_char *bp, u_int len)
+{
+ uint8_t version, inner_len;
+ uint32_t seq_no;
+
+ ndo->ndo_protocol = "zep";
+
+ nd_print_protocol_caps(ndo);
+
+ /* Preamble Code (must be "EX") */
+ if (GET_U_1(bp) != 'E' || GET_U_1(bp + 1) != 'X') {
+ ND_PRINT(" [Preamble Code: ");
+ fn_print_char(ndo, GET_U_1(bp));
+ fn_print_char(ndo, GET_U_1(bp + 1));
+ ND_PRINT("]");
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ version = GET_U_1(bp + 2);
+ ND_PRINT("v%u ", version);
+
+ if (version == 1) {
+ /* ZEP v1 packet. */
+ ND_PRINT("Channel ID %u, Device ID 0x%04x, ",
+ GET_U_1(bp + 3), GET_BE_U_2(bp + 4));
+ if (GET_U_1(bp + 6))
+ ND_PRINT("CRC, ");
+ else
+ ND_PRINT("LQI %u, ", GET_U_1(bp + 7));
+ inner_len = GET_U_1(bp + 15);
+ ND_PRINT("inner len = %u", inner_len);
+
+ bp += 16;
+ len -= 16;
+ } else {
+ /* ZEP v2 packet. */
+ if (GET_U_1(bp + 3) == 2) {
+ /* ZEP v2 ack. */
+ seq_no = GET_BE_U_4(bp + 4);
+ ND_PRINT("ACK, seq# = %u", seq_no);
+ inner_len = 0;
+ bp += 8;
+ len -= 8;
+ } else {
+ /* ZEP v2 data, or some other. */
+ ND_PRINT("Type %u, Channel ID %u, Device ID 0x%04x, ",
+ GET_U_1(bp + 3), GET_U_1(bp + 4),
+ GET_BE_U_2(bp + 5));
+ if (GET_U_1(bp + 7))
+ ND_PRINT("CRC, ");
+ else
+ ND_PRINT("LQI %u, ", GET_U_1(bp + 8));
+
+ zep_print_ts(ndo, bp + 9);
+ seq_no = GET_BE_U_4(bp + 17);
+ inner_len = GET_U_1(bp + 31);
+ ND_PRINT(", seq# = %u, inner len = %u",
+ seq_no, inner_len);
+ bp += 32;
+ len -= 32;
+ }
+ }
+
+ if (inner_len != 0) {
+ /* Call 802.15.4 dissector. */
+ ND_PRINT("\n\t");
+ if (ieee802_15_4_print(ndo, bp, inner_len)) {
+ bp += len;
+ len = 0;
+ }
+ }
+
+ if (!ndo->ndo_suppress_default_print)
+ ND_DEFAULTPRINT(bp, len);
+}
diff --git a/print-zephyr.c b/print-zephyr.c
new file mode 100644
index 0000000..7f60f1f
--- /dev/null
+++ b/print-zephyr.c
@@ -0,0 +1,345 @@
+/*
+ * Decode and print Zephyr packets.
+ *
+ * https://web.mit.edu/zephyr/doc/protocol
+ *
+ * Copyright (c) 2001 Nickolai Zeldovich <kolya@MIT.EDU>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * The name of the author(s) may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ */
+
+/* \summary: Zephyr printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+struct z_packet {
+ const char *version;
+ int numfields;
+ int kind;
+ const char *uid;
+ int port;
+ int auth;
+ int authlen;
+ const char *authdata;
+ const char *class;
+ const char *inst;
+ const char *opcode;
+ const char *sender;
+ const char *recipient;
+ const char *format;
+ int cksum;
+ int multi;
+ const char *multi_uid;
+ /* Other fields follow here.. */
+};
+
+enum z_packet_type {
+ Z_PACKET_UNSAFE = 0,
+ Z_PACKET_UNACKED,
+ Z_PACKET_ACKED,
+ Z_PACKET_HMACK,
+ Z_PACKET_HMCTL,
+ Z_PACKET_SERVACK,
+ Z_PACKET_SERVNAK,
+ Z_PACKET_CLIENTACK,
+ Z_PACKET_STAT
+};
+
+static const struct tok z_types[] = {
+ { Z_PACKET_UNSAFE, "unsafe" },
+ { Z_PACKET_UNACKED, "unacked" },
+ { Z_PACKET_ACKED, "acked" },
+ { Z_PACKET_HMACK, "hm-ack" },
+ { Z_PACKET_HMCTL, "hm-ctl" },
+ { Z_PACKET_SERVACK, "serv-ack" },
+ { Z_PACKET_SERVNAK, "serv-nak" },
+ { Z_PACKET_CLIENTACK, "client-ack" },
+ { Z_PACKET_STAT, "stat" },
+ { 0, NULL }
+};
+
+static char z_buf[256];
+
+static const char *
+parse_field(netdissect_options *ndo, const char **pptr, int *len)
+{
+ const char *s;
+
+ /* Start of string */
+ s = *pptr;
+ /* Scan for the NUL terminator */
+ for (;;) {
+ if (*len == 0) {
+ /* Ran out of packet data without finding it */
+ return NULL;
+ }
+ if (GET_U_1(*pptr) == '\0') {
+ /* Found it */
+ break;
+ }
+ /* Keep scanning */
+ (*pptr)++;
+ (*len)--;
+ }
+ /* Skip the NUL terminator */
+ (*pptr)++;
+ (*len)--;
+ return s;
+}
+
+static const char *
+z_triple(const char *class, const char *inst, const char *recipient)
+{
+ if (!*recipient)
+ recipient = "*";
+ snprintf(z_buf, sizeof(z_buf), "<%s,%s,%s>", class, inst, recipient);
+ z_buf[sizeof(z_buf)-1] = '\0';
+ return z_buf;
+}
+
+static const char *
+str_to_lower(const char *string)
+{
+ char *zb_string;
+
+ strncpy(z_buf, string, sizeof(z_buf));
+ z_buf[sizeof(z_buf)-1] = '\0';
+
+ zb_string = z_buf;
+ while (*zb_string) {
+ *zb_string = ND_ASCII_TOLOWER(*zb_string);
+ zb_string++;
+ }
+
+ return z_buf;
+}
+
+void
+zephyr_print(netdissect_options *ndo, const u_char *cp, int length)
+{
+ struct z_packet z = {
+ NULL, /* version */
+ 0, /* numfields */
+ 0, /* kind */
+ NULL, /* uid */
+ 0, /* port */
+ 0, /* auth */
+ 0, /* authlen */
+ NULL, /* authdata */
+ NULL, /* class */
+ NULL, /* inst */
+ NULL, /* opcode */
+ NULL, /* sender */
+ NULL, /* recipient */
+ NULL, /* format */
+ 0, /* cksum */
+ 0, /* multi */
+ NULL /* multi_uid */
+ };
+ const char *parse = (const char *) cp;
+ int parselen = length;
+ const char *s;
+ int lose = 0;
+
+ ndo->ndo_protocol = "zephyr";
+ /* squelch compiler warnings */
+
+#define PARSE_STRING \
+ s = parse_field(ndo, &parse, &parselen); \
+ if (!s) lose = 1;
+
+#define PARSE_FIELD_INT(field) \
+ PARSE_STRING \
+ if (!lose) field = strtol(s, 0, 16);
+
+#define PARSE_FIELD_STR(field) \
+ PARSE_STRING \
+ if (!lose) field = s;
+
+ PARSE_FIELD_STR(z.version);
+ if (lose)
+ goto invalid;
+
+ if (strncmp(z.version, "ZEPH", 4))
+ return;
+
+ PARSE_FIELD_INT(z.numfields);
+ PARSE_FIELD_INT(z.kind);
+ PARSE_FIELD_STR(z.uid);
+ PARSE_FIELD_INT(z.port);
+ PARSE_FIELD_INT(z.auth);
+ PARSE_FIELD_INT(z.authlen);
+ PARSE_FIELD_STR(z.authdata);
+ PARSE_FIELD_STR(z.class);
+ PARSE_FIELD_STR(z.inst);
+ PARSE_FIELD_STR(z.opcode);
+ PARSE_FIELD_STR(z.sender);
+ PARSE_FIELD_STR(z.recipient);
+ PARSE_FIELD_STR(z.format);
+ PARSE_FIELD_INT(z.cksum);
+ PARSE_FIELD_INT(z.multi);
+ PARSE_FIELD_STR(z.multi_uid);
+
+ if (lose)
+ goto invalid;
+
+ ND_PRINT(" zephyr");
+ if (strncmp(z.version+4, "0.2", 3)) {
+ ND_PRINT(" v%s", z.version+4);
+ return;
+ }
+
+ ND_PRINT(" %s", tok2str(z_types, "type %d", z.kind));
+ if (z.kind == Z_PACKET_SERVACK) {
+ /* Initialization to silence warnings */
+ const char *ackdata = NULL;
+ PARSE_FIELD_STR(ackdata);
+ if (!lose && strcmp(ackdata, "SENT"))
+ ND_PRINT("/%s", str_to_lower(ackdata));
+ }
+ if (*z.sender) ND_PRINT(" %s", z.sender);
+
+ if (!strcmp(z.class, "USER_LOCATE")) {
+ if (!strcmp(z.opcode, "USER_HIDE"))
+ ND_PRINT(" hide");
+ else if (!strcmp(z.opcode, "USER_UNHIDE"))
+ ND_PRINT(" unhide");
+ else
+ ND_PRINT(" locate %s", z.inst);
+ return;
+ }
+
+ if (!strcmp(z.class, "ZEPHYR_ADMIN")) {
+ ND_PRINT(" zephyr-admin %s", str_to_lower(z.opcode));
+ return;
+ }
+
+ if (!strcmp(z.class, "ZEPHYR_CTL")) {
+ if (!strcmp(z.inst, "CLIENT")) {
+ if (!strcmp(z.opcode, "SUBSCRIBE") ||
+ !strcmp(z.opcode, "SUBSCRIBE_NODEFS") ||
+ !strcmp(z.opcode, "UNSUBSCRIBE")) {
+
+ ND_PRINT(" %ssub%s", strcmp(z.opcode, "SUBSCRIBE") ? "un" : "",
+ strcmp(z.opcode, "SUBSCRIBE_NODEFS") ? "" :
+ "-nodefs");
+ if (z.kind != Z_PACKET_SERVACK) {
+ /* Initialization to silence warnings */
+ const char *c = NULL, *i = NULL, *r = NULL;
+ PARSE_FIELD_STR(c);
+ PARSE_FIELD_STR(i);
+ PARSE_FIELD_STR(r);
+ if (!lose) ND_PRINT(" %s", z_triple(c, i, r));
+ }
+ return;
+ }
+
+ if (!strcmp(z.opcode, "GIMME")) {
+ ND_PRINT(" ret");
+ return;
+ }
+
+ if (!strcmp(z.opcode, "GIMMEDEFS")) {
+ ND_PRINT(" gimme-defs");
+ return;
+ }
+
+ if (!strcmp(z.opcode, "CLEARSUB")) {
+ ND_PRINT(" clear-subs");
+ return;
+ }
+
+ ND_PRINT(" %s", str_to_lower(z.opcode));
+ return;
+ }
+
+ if (!strcmp(z.inst, "HM")) {
+ ND_PRINT(" %s", str_to_lower(z.opcode));
+ return;
+ }
+
+ if (!strcmp(z.inst, "REALM")) {
+ if (!strcmp(z.opcode, "ADD_SUBSCRIBE"))
+ ND_PRINT(" realm add-subs");
+ if (!strcmp(z.opcode, "REQ_SUBSCRIBE"))
+ ND_PRINT(" realm req-subs");
+ if (!strcmp(z.opcode, "RLM_SUBSCRIBE"))
+ ND_PRINT(" realm rlm-sub");
+ if (!strcmp(z.opcode, "RLM_UNSUBSCRIBE"))
+ ND_PRINT(" realm rlm-unsub");
+ return;
+ }
+ }
+
+ if (!strcmp(z.class, "HM_CTL")) {
+ ND_PRINT(" hm_ctl %s", str_to_lower(z.inst));
+ ND_PRINT(" %s", str_to_lower(z.opcode));
+ return;
+ }
+
+ if (!strcmp(z.class, "HM_STAT")) {
+ if (!strcmp(z.inst, "HMST_CLIENT") && !strcmp(z.opcode, "GIMMESTATS")) {
+ ND_PRINT(" get-client-stats");
+ return;
+ }
+ }
+
+ if (!strcmp(z.class, "WG_CTL")) {
+ ND_PRINT(" wg_ctl %s", str_to_lower(z.inst));
+ ND_PRINT(" %s", str_to_lower(z.opcode));
+ return;
+ }
+
+ if (!strcmp(z.class, "LOGIN")) {
+ if (!strcmp(z.opcode, "USER_FLUSH")) {
+ ND_PRINT(" flush_locs");
+ return;
+ }
+
+ if (!strcmp(z.opcode, "NONE") ||
+ !strcmp(z.opcode, "OPSTAFF") ||
+ !strcmp(z.opcode, "REALM-VISIBLE") ||
+ !strcmp(z.opcode, "REALM-ANNOUNCED") ||
+ !strcmp(z.opcode, "NET-VISIBLE") ||
+ !strcmp(z.opcode, "NET-ANNOUNCED")) {
+ ND_PRINT(" set-exposure %s", str_to_lower(z.opcode));
+ return;
+ }
+ }
+
+ if (!*z.recipient)
+ z.recipient = "*";
+
+ ND_PRINT(" to %s", z_triple(z.class, z.inst, z.recipient));
+ if (*z.opcode)
+ ND_PRINT(" op %s", z.opcode);
+ return;
+
+invalid:
+ nd_print_invalid(ndo);
+}
diff --git a/print-zeromq.c b/print-zeromq.c
new file mode 100644
index 0000000..c702046
--- /dev/null
+++ b/print-zeromq.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2013 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* \summary: ZeroMQ Message Transport Protocol (ZMTP) printer */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "extract.h"
+
+
+/* Maximum number of ZMTP/1.0 frame body bytes (without the flags) to dump in
+ * hex and ASCII under a single "-v" flag.
+ */
+#define VBYTES 128
+
+/*
+ * Below is an excerpt from the "13/ZMTP" specification:
+ *
+ * A ZMTP message consists of 1 or more frames.
+ *
+ * A ZMTP frame consists of a length, followed by a flags field and a frame
+ * body of (length - 1) octets. Note: the length includes the flags field, so
+ * an empty frame has a length of 1.
+ *
+ * For frames with a length of 1 to 254 octets, the length SHOULD BE encoded
+ * as a single octet. The minimum valid length of a frame is 1 octet, thus a
+ * length of 0 is invalid and such frames SHOULD be discarded silently.
+ *
+ * For frames with lengths of 255 and greater, the length SHALL BE encoded as
+ * a single octet with the value 255, followed by the length encoded as a
+ * 64-bit unsigned integer in network byte order. For frames with lengths of
+ * 1 to 254 octets this encoding MAY be also used.
+ *
+ * The flags field consists of a single octet containing various control
+ * flags. Bit 0 is the least significant bit.
+ *
+ * - Bit 0 (MORE): More frames to follow. A value of 0 indicates that there
+ * are no more frames to follow. A value of 1 indicates that more frames
+ * will follow. On messages consisting of a single frame the MORE flag MUST
+ * be 0.
+ *
+ * - Bits 1-7: Reserved. Bits 1-7 are reserved for future use and SHOULD be
+ * zero.
+ */
+
+static const u_char *
+zmtp1_print_frame(netdissect_options *ndo, const u_char *cp, const u_char *ep)
+{
+ uint64_t body_len_declared, body_len_captured, header_len;
+ uint8_t flags;
+
+ ND_PRINT("\n\t");
+
+ if (GET_U_1(cp) != 0xFF) { /* length/0xFF */
+ header_len = 1; /* length */
+ body_len_declared = GET_U_1(cp);
+ ND_PRINT(" frame flags+body (8-bit) length %" PRIu64, body_len_declared);
+ } else {
+ header_len = 1 + 8; /* 0xFF, length */
+ ND_PRINT(" frame flags+body (64-bit) length");
+ ND_TCHECK_LEN(cp, header_len); /* 0xFF, length */
+ body_len_declared = GET_BE_U_8(cp + 1);
+ ND_PRINT(" %" PRIu64, body_len_declared);
+ }
+ if (body_len_declared == 0)
+ return cp + header_len; /* skip to the next frame */
+ ND_TCHECK_LEN(cp, header_len + 1); /* ..., flags */
+ flags = GET_U_1(cp + header_len);
+
+ body_len_captured = ep - cp - header_len;
+ if (body_len_declared > body_len_captured)
+ ND_PRINT(" (%" PRIu64 " captured)", body_len_captured);
+ ND_PRINT(", flags 0x%02x", flags);
+
+ if (ndo->ndo_vflag) {
+ uint64_t body_len_printed = ND_MIN(body_len_captured, body_len_declared);
+
+ ND_PRINT(" (%s|%s|%s|%s|%s|%s|%s|%s)",
+ flags & 0x80 ? "MBZ" : "-",
+ flags & 0x40 ? "MBZ" : "-",
+ flags & 0x20 ? "MBZ" : "-",
+ flags & 0x10 ? "MBZ" : "-",
+ flags & 0x08 ? "MBZ" : "-",
+ flags & 0x04 ? "MBZ" : "-",
+ flags & 0x02 ? "MBZ" : "-",
+ flags & 0x01 ? "MORE" : "-");
+
+ if (ndo->ndo_vflag == 1)
+ body_len_printed = ND_MIN(VBYTES + 1, body_len_printed);
+ if (body_len_printed > 1) {
+ ND_PRINT(", first %" PRIu64 " byte(s) of body:", body_len_printed - 1);
+ hex_and_ascii_print(ndo, "\n\t ", cp + header_len + 1, body_len_printed - 1);
+ }
+ }
+
+ /*
+ * Do not advance cp by the sum of header_len and body_len_declared
+ * before each offset has successfully passed ND_TCHECK_LEN() as the
+ * sum can roll over (9 + 0xfffffffffffffff7 = 0) and cause an
+ * infinite loop.
+ */
+ cp += header_len;
+ ND_TCHECK_LEN(cp, body_len_declared); /* Next frame within the buffer ? */
+ return cp + body_len_declared;
+
+trunc:
+ nd_trunc_longjmp(ndo);
+}
+
+void
+zmtp1_print(netdissect_options *ndo, const u_char *cp, u_int len)
+{
+ const u_char *ep = ND_MIN(ndo->ndo_snapend, cp + len);
+
+ ndo->ndo_protocol = "zmtp1";
+ ND_PRINT(": ZMTP/1.0");
+ while (cp < ep)
+ cp = zmtp1_print_frame(ndo, cp, ep);
+}
+
+/* The functions below decode a ZeroMQ datagram, supposedly stored in the "Data"
+ * field of an ODATA/RDATA [E]PGM packet. An excerpt from zmq_pgm(7) man page
+ * follows.
+ *
+ * In order for late joining consumers to be able to identify message
+ * boundaries, each PGM datagram payload starts with a 16-bit unsigned integer
+ * in network byte order specifying either the offset of the first message frame
+ * in the datagram or containing the value 0xFFFF if the datagram contains
+ * solely an intermediate part of a larger message.
+ *
+ * Note that offset specifies where the first message begins rather than the
+ * first message part. Thus, if there are trailing message parts at the
+ * beginning of the packet the offset ignores them and points to first initial
+ * message part in the packet.
+ */
+
+static const u_char *
+zmtp1_print_intermediate_part(netdissect_options *ndo, const u_char *cp, const u_int len)
+{
+ u_int frame_offset;
+ u_int remaining_len;
+
+ frame_offset = GET_BE_U_2(cp);
+ ND_PRINT("\n\t frame offset 0x%04x", frame_offset);
+ cp += 2;
+ remaining_len = ND_BYTES_AVAILABLE_AFTER(cp); /* without the frame length */
+
+ if (frame_offset == 0xFFFF)
+ frame_offset = len - 2; /* always within the declared length */
+ else if (2 + frame_offset > len) {
+ ND_PRINT(" (exceeds datagram declared length)");
+ goto trunc;
+ }
+
+ /* offset within declared length of the datagram */
+ if (frame_offset) {
+ ND_PRINT("\n\t frame intermediate part, %u bytes", frame_offset);
+ if (frame_offset > remaining_len)
+ ND_PRINT(" (%u captured)", remaining_len);
+ if (ndo->ndo_vflag) {
+ u_int len_printed = ND_MIN(frame_offset, remaining_len);
+
+ if (ndo->ndo_vflag == 1)
+ len_printed = ND_MIN(VBYTES, len_printed);
+ if (len_printed > 1) {
+ ND_PRINT(", first %u byte(s):", len_printed);
+ hex_and_ascii_print(ndo, "\n\t ", cp, len_printed);
+ }
+ }
+ }
+ return cp + frame_offset;
+
+trunc:
+ nd_trunc_longjmp(ndo);
+}
+
+void
+zmtp1_datagram_print(netdissect_options *ndo, const u_char *cp, const u_int len)
+{
+ const u_char *ep = ND_MIN(ndo->ndo_snapend, cp + len);
+
+ ndo->ndo_protocol = "zmtp1";
+ cp = zmtp1_print_intermediate_part(ndo, cp, len);
+ while (cp < ep)
+ cp = zmtp1_print_frame(ndo, cp, ep);
+}
diff --git a/print.c b/print.c
new file mode 100644
index 0000000..823cea6
--- /dev/null
+++ b/print.c
@@ -0,0 +1,563 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Support for splitting captures into multiple files with a maximum
+ * file size:
+ *
+ * Copyright (c) 2001
+ * Seth Webster <swebster@sst.ll.mit.edu>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <setjmp.h>
+
+#include "netdissect-stdinc.h"
+
+#include "netdissect.h"
+#include "addrtoname.h"
+#include "print.h"
+#include "netdissect-alloc.h"
+
+#include "pcap-missing.h"
+
+struct printer {
+ if_printer f;
+ int type;
+};
+
+static const struct printer printers[] = {
+#ifdef DLT_APPLE_IP_OVER_IEEE1394
+ { ap1394_if_print, DLT_APPLE_IP_OVER_IEEE1394 },
+#endif
+ { arcnet_if_print, DLT_ARCNET },
+#ifdef DLT_ARCNET_LINUX
+ { arcnet_linux_if_print, DLT_ARCNET_LINUX },
+#endif
+ { atm_if_print, DLT_ATM_RFC1483 },
+#ifdef DLT_DSA_TAG_BRCM
+ { brcm_tag_if_print, DLT_DSA_TAG_BRCM },
+#endif
+#ifdef DLT_DSA_TAG_BRCM_PREPEND
+ { brcm_tag_prepend_if_print, DLT_DSA_TAG_BRCM_PREPEND },
+#endif
+#ifdef DLT_BLUETOOTH_HCI_H4_WITH_PHDR
+ { bt_if_print, DLT_BLUETOOTH_HCI_H4_WITH_PHDR},
+#endif
+#ifdef DLT_C_HDLC
+ { chdlc_if_print, DLT_C_HDLC },
+#endif
+#ifdef DLT_HDLC
+ { chdlc_if_print, DLT_HDLC },
+#endif
+#ifdef DLT_ATM_CLIP
+ { cip_if_print, DLT_ATM_CLIP },
+#endif
+#ifdef DLT_CIP
+ { cip_if_print, DLT_CIP },
+#endif
+#ifdef DLT_DSA_TAG_DSA
+ { dsa_if_print, DLT_DSA_TAG_DSA },
+#endif
+#ifdef DLT_DSA_TAG_EDSA
+ { edsa_if_print, DLT_DSA_TAG_EDSA },
+#endif
+#ifdef DLT_ENC
+ { enc_if_print, DLT_ENC },
+#endif
+ { ether_if_print, DLT_EN10MB },
+ { fddi_if_print, DLT_FDDI },
+#ifdef DLT_FR
+ { fr_if_print, DLT_FR },
+#endif
+#ifdef DLT_FRELAY
+ { fr_if_print, DLT_FRELAY },
+#endif
+#ifdef DLT_IEEE802_11
+ { ieee802_11_if_print, DLT_IEEE802_11},
+#endif
+#ifdef DLT_IEEE802_11_RADIO_AVS
+ { ieee802_11_radio_avs_if_print, DLT_IEEE802_11_RADIO_AVS },
+#endif
+#ifdef DLT_IEEE802_11_RADIO
+ { ieee802_11_radio_if_print, DLT_IEEE802_11_RADIO },
+#endif
+#ifdef DLT_IEEE802_15_4
+ { ieee802_15_4_if_print, DLT_IEEE802_15_4 },
+#endif
+#ifdef DLT_IEEE802_15_4_NOFCS
+ { ieee802_15_4_if_print, DLT_IEEE802_15_4_NOFCS },
+#endif
+#ifdef DLT_IEEE802_15_4_TAP
+ { ieee802_15_4_tap_if_print, DLT_IEEE802_15_4_TAP },
+#endif
+#ifdef DLT_IP_OVER_FC
+ { ipfc_if_print, DLT_IP_OVER_FC },
+#endif
+#ifdef DLT_IPNET
+ { ipnet_if_print, DLT_IPNET },
+#endif
+#ifdef DLT_IPOIB
+ { ipoib_if_print, DLT_IPOIB },
+#endif
+#ifdef DLT_JUNIPER_ATM1
+ { juniper_atm1_if_print, DLT_JUNIPER_ATM1 },
+#endif
+#ifdef DLT_JUNIPER_ATM2
+ { juniper_atm2_if_print, DLT_JUNIPER_ATM2 },
+#endif
+#ifdef DLT_JUNIPER_CHDLC
+ { juniper_chdlc_if_print, DLT_JUNIPER_CHDLC },
+#endif
+#ifdef DLT_JUNIPER_ES
+ { juniper_es_if_print, DLT_JUNIPER_ES },
+#endif
+#ifdef DLT_JUNIPER_ETHER
+ { juniper_ether_if_print, DLT_JUNIPER_ETHER },
+#endif
+#ifdef DLT_JUNIPER_FRELAY
+ { juniper_frelay_if_print, DLT_JUNIPER_FRELAY },
+#endif
+#ifdef DLT_JUNIPER_GGSN
+ { juniper_ggsn_if_print, DLT_JUNIPER_GGSN },
+#endif
+#ifdef DLT_JUNIPER_MFR
+ { juniper_mfr_if_print, DLT_JUNIPER_MFR },
+#endif
+#ifdef DLT_JUNIPER_MLFR
+ { juniper_mlfr_if_print, DLT_JUNIPER_MLFR },
+#endif
+#ifdef DLT_JUNIPER_MLPPP
+ { juniper_mlppp_if_print, DLT_JUNIPER_MLPPP },
+#endif
+#ifdef DLT_JUNIPER_MONITOR
+ { juniper_monitor_if_print, DLT_JUNIPER_MONITOR },
+#endif
+#ifdef DLT_JUNIPER_PPP
+ { juniper_ppp_if_print, DLT_JUNIPER_PPP },
+#endif
+#ifdef DLT_JUNIPER_PPPOE_ATM
+ { juniper_pppoe_atm_if_print, DLT_JUNIPER_PPPOE_ATM },
+#endif
+#ifdef DLT_JUNIPER_PPPOE
+ { juniper_pppoe_if_print, DLT_JUNIPER_PPPOE },
+#endif
+#ifdef DLT_JUNIPER_SERVICES
+ { juniper_services_if_print, DLT_JUNIPER_SERVICES },
+#endif
+#ifdef DLT_LTALK
+ { ltalk_if_print, DLT_LTALK },
+#endif
+#ifdef DLT_MFR
+ { mfr_if_print, DLT_MFR },
+#endif
+#ifdef DLT_NETANALYZER
+ { netanalyzer_if_print, DLT_NETANALYZER },
+#endif
+#ifdef DLT_NETANALYZER_TRANSPARENT
+ { netanalyzer_transparent_if_print, DLT_NETANALYZER_TRANSPARENT },
+#endif
+#ifdef DLT_NFLOG
+ { nflog_if_print, DLT_NFLOG},
+#endif
+ { null_if_print, DLT_NULL },
+#ifdef DLT_LOOP
+ { null_if_print, DLT_LOOP },
+#endif
+#if defined(DLT_PFLOG) && defined(HAVE_NET_IF_PFLOG_H)
+ { pflog_if_print, DLT_PFLOG },
+#endif
+#ifdef DLT_PKTAP
+ { pktap_if_print, DLT_PKTAP },
+#endif
+#ifdef DLT_PPI
+ { ppi_if_print, DLT_PPI },
+#endif
+#ifdef DLT_PPP_BSDOS
+ { ppp_bsdos_if_print, DLT_PPP_BSDOS },
+#endif
+#ifdef DLT_PPP_SERIAL
+ { ppp_hdlc_if_print, DLT_PPP_SERIAL },
+#endif
+ { ppp_if_print, DLT_PPP },
+#ifdef DLT_PPP_PPPD
+ { ppp_if_print, DLT_PPP_PPPD },
+#endif
+#ifdef DLT_PPP_ETHER
+ { pppoe_if_print, DLT_PPP_ETHER },
+#endif
+#ifdef DLT_PRISM_HEADER
+ { prism_if_print, DLT_PRISM_HEADER },
+#endif
+ { raw_if_print, DLT_RAW },
+#ifdef DLT_IPV4
+ { raw_if_print, DLT_IPV4 },
+#endif
+#ifdef DLT_IPV6
+ { raw_if_print, DLT_IPV6 },
+#endif
+#ifdef DLT_SLIP_BSDOS
+ { sl_bsdos_if_print, DLT_SLIP_BSDOS },
+#endif
+ { sl_if_print, DLT_SLIP },
+#ifdef DLT_LINUX_SLL
+ { sll_if_print, DLT_LINUX_SLL },
+#endif
+#ifdef DLT_LINUX_SLL2
+ { sll2_if_print, DLT_LINUX_SLL2 },
+#endif
+#ifdef DLT_SUNATM
+ { sunatm_if_print, DLT_SUNATM },
+#endif
+#ifdef DLT_SYMANTEC_FIREWALL
+ { symantec_if_print, DLT_SYMANTEC_FIREWALL },
+#endif
+ { token_if_print, DLT_IEEE802 },
+#ifdef DLT_USB_LINUX
+ { usb_linux_48_byte_if_print, DLT_USB_LINUX},
+#endif /* DLT_USB_LINUX */
+#ifdef DLT_USB_LINUX_MMAPPED
+ { usb_linux_64_byte_if_print, DLT_USB_LINUX_MMAPPED},
+#endif /* DLT_USB_LINUX_MMAPPED */
+#ifdef DLT_VSOCK
+ { vsock_if_print, DLT_VSOCK },
+#endif
+ { NULL, 0 },
+};
+
+static void ndo_default_print(netdissect_options *ndo, const u_char *bp,
+ u_int length);
+
+static void NORETURN ndo_error(netdissect_options *ndo,
+ status_exit_codes_t status,
+ FORMAT_STRING(const char *fmt), ...)
+ PRINTFLIKE(3, 4);
+static void ndo_warning(netdissect_options *ndo,
+ FORMAT_STRING(const char *fmt), ...)
+ PRINTFLIKE(2, 3);
+
+static int ndo_printf(netdissect_options *ndo,
+ FORMAT_STRING(const char *fmt), ...)
+ PRINTFLIKE(2, 3);
+
+void
+init_print(netdissect_options *ndo, uint32_t localnet, uint32_t mask)
+{
+
+ init_addrtoname(ndo, localnet, mask);
+ init_checksum();
+}
+
+if_printer
+lookup_printer(int type)
+{
+ const struct printer *p;
+
+ for (p = printers; p->f; ++p)
+ if (type == p->type)
+ return p->f;
+
+#if defined(DLT_USER2) && defined(DLT_PKTAP)
+ /*
+ * Apple incorrectly chose to use DLT_USER2 for their PKTAP
+ * header.
+ *
+ * We map DLT_PKTAP, whether it's DLT_USER2 as it is on Darwin-
+ * based OSes or the same value as LINKTYPE_PKTAP as it is on
+ * other OSes, to LINKTYPE_PKTAP, so files written with
+ * this version of libpcap for a DLT_PKTAP capture have a link-
+ * layer header type of LINKTYPE_PKTAP.
+ *
+ * However, files written on OS X Mavericks for a DLT_PKTAP
+ * capture have a link-layer header type of LINKTYPE_USER2.
+ * If we don't have a printer for DLT_USER2, and type is
+ * DLT_USER2, we look up the printer for DLT_PKTAP and use
+ * that.
+ */
+ if (type == DLT_USER2) {
+ for (p = printers; p->f; ++p)
+ if (DLT_PKTAP == p->type)
+ return p->f;
+ }
+#endif
+
+ return NULL;
+ /* NOTREACHED */
+}
+
+int
+has_printer(int type)
+{
+ return (lookup_printer(type) != NULL);
+}
+
+if_printer
+get_if_printer(int type)
+{
+ if_printer printer;
+
+ printer = lookup_printer(type);
+ if (printer == NULL)
+ printer = unsupported_if_print;
+ return printer;
+}
+
+void
+pretty_print_packet(netdissect_options *ndo, const struct pcap_pkthdr *h,
+ const u_char *sp, u_int packets_captured)
+{
+ u_int hdrlen = 0;
+ int invalid_header = 0;
+
+ if (ndo->ndo_packet_number)
+ ND_PRINT("%5u ", packets_captured);
+
+ /* Sanity checks on packet length / capture length */
+ if (h->caplen == 0) {
+ invalid_header = 1;
+ ND_PRINT("[Invalid header: caplen==0");
+ }
+ if (h->len == 0) {
+ if (!invalid_header) {
+ invalid_header = 1;
+ ND_PRINT("[Invalid header:");
+ } else
+ ND_PRINT(",");
+ ND_PRINT(" len==0");
+ } else if (h->len < h->caplen) {
+ if (!invalid_header) {
+ invalid_header = 1;
+ ND_PRINT("[Invalid header:");
+ } else
+ ND_PRINT(",");
+ ND_PRINT(" len(%u) < caplen(%u)", h->len, h->caplen);
+ }
+ if (h->caplen > MAXIMUM_SNAPLEN) {
+ if (!invalid_header) {
+ invalid_header = 1;
+ ND_PRINT("[Invalid header:");
+ } else
+ ND_PRINT(",");
+ ND_PRINT(" caplen(%u) > %u", h->caplen, MAXIMUM_SNAPLEN);
+ }
+ if (h->len > MAXIMUM_SNAPLEN) {
+ if (!invalid_header) {
+ invalid_header = 1;
+ ND_PRINT("[Invalid header:");
+ } else
+ ND_PRINT(",");
+ ND_PRINT(" len(%u) > %u", h->len, MAXIMUM_SNAPLEN);
+ }
+ if (invalid_header) {
+ ND_PRINT("]\n");
+ return;
+ }
+
+ /*
+ * At this point:
+ * capture length != 0,
+ * packet length != 0,
+ * capture length <= MAXIMUM_SNAPLEN,
+ * packet length <= MAXIMUM_SNAPLEN,
+ * packet length >= capture length.
+ *
+ * Currently, there is no D-Bus printer, thus no need for
+ * bigger lengths.
+ */
+
+ ts_print(ndo, &h->ts);
+
+ /*
+ * Printers must check that they're not walking off the end of
+ * the packet.
+ * Rather than pass it all the way down, we set this member
+ * of the netdissect_options structure.
+ */
+ ndo->ndo_snapend = sp + h->caplen;
+
+ ndo->ndo_protocol = "";
+ ndo->ndo_ll_hdr_len = 0;
+ switch (setjmp(ndo->ndo_early_end)) {
+ case 0:
+ /* Print the packet. */
+ (ndo->ndo_if_printer)(ndo, h, sp);
+ break;
+ case ND_TRUNCATED:
+ /* A printer quit because the packet was truncated; report it */
+ nd_print_trunc(ndo);
+ /* Print the full packet */
+ ndo->ndo_ll_hdr_len = 0;
+ break;
+ }
+ hdrlen = ndo->ndo_ll_hdr_len;
+
+ /*
+ * Empty the stack of packet information, freeing all pushed buffers;
+ * if we got here by a printer quitting, we need to release anything
+ * that didn't get released because we longjmped out of the code
+ * before it popped the packet information.
+ */
+ nd_pop_all_packet_info(ndo);
+
+ /*
+ * Restore the original snapend, as a printer might have
+ * changed it.
+ */
+ ndo->ndo_snapend = sp + h->caplen;
+ if (ndo->ndo_Xflag) {
+ /*
+ * Print the raw packet data in hex and ASCII.
+ */
+ if (ndo->ndo_Xflag > 1) {
+ /*
+ * Include the link-layer header.
+ */
+ hex_and_ascii_print(ndo, "\n\t", sp, h->caplen);
+ } else {
+ /*
+ * Don't include the link-layer header - and if
+ * we have nothing past the link-layer header,
+ * print nothing.
+ */
+ if (h->caplen > hdrlen)
+ hex_and_ascii_print(ndo, "\n\t", sp + hdrlen,
+ h->caplen - hdrlen);
+ }
+ } else if (ndo->ndo_xflag) {
+ /*
+ * Print the raw packet data in hex.
+ */
+ if (ndo->ndo_xflag > 1) {
+ /*
+ * Include the link-layer header.
+ */
+ hex_print(ndo, "\n\t", sp, h->caplen);
+ } else {
+ /*
+ * Don't include the link-layer header - and if
+ * we have nothing past the link-layer header,
+ * print nothing.
+ */
+ if (h->caplen > hdrlen)
+ hex_print(ndo, "\n\t", sp + hdrlen,
+ h->caplen - hdrlen);
+ }
+ } else if (ndo->ndo_Aflag) {
+ /*
+ * Print the raw packet data in ASCII.
+ */
+ if (ndo->ndo_Aflag > 1) {
+ /*
+ * Include the link-layer header.
+ */
+ ascii_print(ndo, sp, h->caplen);
+ } else {
+ /*
+ * Don't include the link-layer header - and if
+ * we have nothing past the link-layer header,
+ * print nothing.
+ */
+ if (h->caplen > hdrlen)
+ ascii_print(ndo, sp + hdrlen, h->caplen - hdrlen);
+ }
+ }
+
+ ND_PRINT("\n");
+ nd_free_all(ndo);
+}
+
+/*
+ * By default, print the specified data out in hex and ASCII.
+ */
+static void
+ndo_default_print(netdissect_options *ndo, const u_char *bp, u_int length)
+{
+ hex_and_ascii_print(ndo, "\n\t", bp, length); /* pass on lf and indentation string */
+}
+
+/* VARARGS */
+static void
+ndo_error(netdissect_options *ndo, status_exit_codes_t status,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (ndo->program_name)
+ (void)fprintf(stderr, "%s: ", ndo->program_name);
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+ nd_cleanup();
+ exit(status);
+ /* NOTREACHED */
+}
+
+/* VARARGS */
+static void
+ndo_warning(netdissect_options *ndo, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (ndo->program_name)
+ (void)fprintf(stderr, "%s: ", ndo->program_name);
+ (void)fprintf(stderr, "WARNING: ");
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+}
+
+static int
+ndo_printf(netdissect_options *ndo, const char *fmt, ...)
+{
+ va_list args;
+ int ret;
+
+ va_start(args, fmt);
+ ret = vfprintf(stdout, fmt, args);
+ va_end(args);
+
+ if (ret < 0)
+ ndo_error(ndo, S_ERR_ND_WRITE_FILE,
+ "Unable to write output: %s", pcap_strerror(errno));
+ return (ret);
+}
+
+void
+ndo_set_function_pointers(netdissect_options *ndo)
+{
+ ndo->ndo_default_print=ndo_default_print;
+ ndo->ndo_printf=ndo_printf;
+ ndo->ndo_error=ndo_error;
+ ndo->ndo_warning=ndo_warning;
+}
diff --git a/print.h b/print.h
new file mode 100644
index 0000000..9caba40
--- /dev/null
+++ b/print.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Support for splitting captures into multiple files with a maximum
+ * file size:
+ *
+ * Copyright (c) 2001
+ * Seth Webster <swebster@sst.ll.mit.edu>
+ */
+
+#ifndef print_h
+#define print_h
+
+void init_print(netdissect_options *ndo, uint32_t localnet, uint32_t mask);
+
+int has_printer(int type);
+
+if_printer get_if_printer(int type);
+
+void pretty_print_packet(netdissect_options *ndo,
+ const struct pcap_pkthdr *h, const u_char *sp,
+ u_int packets_captured);
+
+void ndo_set_function_pointers(netdissect_options *ndo);
+
+#endif /* print_h */
diff --git a/signature.c b/signature.c
new file mode 100644
index 0000000..ca3aec9
--- /dev/null
+++ b/signature.c
@@ -0,0 +1,207 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Functions for signature and digest verification.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "netdissect.h"
+#include "signature.h"
+
+#ifdef HAVE_LIBCRYPTO
+#include <openssl/md5.h>
+#endif
+
+const struct tok signature_check_values[] = {
+ { SIGNATURE_VALID, "valid"},
+ { SIGNATURE_INVALID, "invalid"},
+ { CANT_ALLOCATE_COPY, "can't allocate memory"},
+ { CANT_CHECK_SIGNATURE, "unchecked"},
+ { 0, NULL }
+};
+
+
+#ifdef HAVE_LIBCRYPTO
+/*
+ * Compute a HMAC MD5 sum.
+ * Taken from rfc2104, Appendix.
+ */
+USES_APPLE_DEPRECATED_API
+static void
+signature_compute_hmac_md5(const uint8_t *text, int text_len, unsigned char *key,
+ unsigned int key_len, uint8_t *digest)
+{
+ MD5_CTX context;
+ unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */
+ unsigned char k_opad[65]; /* outer padding - key XORd with opad */
+ unsigned char tk[16];
+ int i;
+
+ /* if key is longer than 64 bytes reset it to key=MD5(key) */
+ if (key_len > 64) {
+
+ MD5_CTX tctx;
+
+ MD5_Init(&tctx);
+ MD5_Update(&tctx, key, key_len);
+ MD5_Final(tk, &tctx);
+
+ key = tk;
+ key_len = 16;
+ }
+
+ /*
+ * the HMAC_MD5 transform looks like:
+ *
+ * MD5(K XOR opad, MD5(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 64 times
+ * opad is the byte 0x5c repeated 64 times
+ * and text is the data being protected
+ */
+
+ /* start out by storing key in pads */
+ memset(k_ipad, 0, sizeof(k_ipad));
+ memset(k_opad, 0, sizeof(k_opad));
+ memcpy(k_ipad, key, key_len);
+ memcpy(k_opad, key, key_len);
+
+ /* XOR key with ipad and opad values */
+ for (i=0; i<64; i++) {
+ k_ipad[i] ^= 0x36;
+ k_opad[i] ^= 0x5c;
+ }
+
+ /*
+ * perform inner MD5
+ */
+ MD5_Init(&context); /* init context for 1st pass */
+ MD5_Update(&context, k_ipad, 64); /* start with inner pad */
+ MD5_Update(&context, text, text_len); /* then text of datagram */
+ MD5_Final(digest, &context); /* finish up 1st pass */
+
+ /*
+ * perform outer MD5
+ */
+ MD5_Init(&context); /* init context for 2nd pass */
+ MD5_Update(&context, k_opad, 64); /* start with outer pad */
+ MD5_Update(&context, digest, 16); /* then results of 1st hash */
+ MD5_Final(digest, &context); /* finish up 2nd pass */
+}
+USES_APPLE_RST
+
+/*
+ * Verify a cryptographic signature of the packet.
+ * Currently only MD5 is supported.
+ */
+int
+signature_verify(netdissect_options *ndo, const u_char *pptr, u_int plen,
+ const u_char *sig_ptr, void (*clear_rtn)(void *),
+ const void *clear_arg)
+{
+ uint8_t *packet_copy, *sig_copy;
+ uint8_t sig[16];
+ unsigned int i;
+
+ if (!ndo->ndo_sigsecret) {
+ return (CANT_CHECK_SIGNATURE);
+ }
+
+ /*
+ * Do we have all the packet data to be checked?
+ */
+ if (!ND_TTEST_LEN(pptr, plen)) {
+ /* No. */
+ return (CANT_CHECK_SIGNATURE);
+ }
+
+ /*
+ * Do we have the entire signature to check?
+ */
+ if (!ND_TTEST_LEN(sig_ptr, sizeof(sig))) {
+ /* No. */
+ return (CANT_CHECK_SIGNATURE);
+ }
+ if (sig_ptr + sizeof(sig) > pptr + plen) {
+ /* No. */
+ return (CANT_CHECK_SIGNATURE);
+ }
+
+ /*
+ * Make a copy of the packet, so we don't overwrite the original.
+ */
+ packet_copy = malloc(plen);
+ if (packet_copy == NULL) {
+ return (CANT_ALLOCATE_COPY);
+ }
+
+ memcpy(packet_copy, pptr, plen);
+
+ /*
+ * Clear the signature in the copy.
+ */
+ sig_copy = packet_copy + (sig_ptr - pptr);
+ memset(sig_copy, 0, sizeof(sig));
+
+ /*
+ * Clear anything else that needs to be cleared in the copy.
+ * Our caller is assumed to have vetted the clear_arg pointer.
+ */
+ (*clear_rtn)((void *)(packet_copy + ((const uint8_t *)clear_arg - pptr)));
+
+ /*
+ * Compute the signature.
+ */
+ signature_compute_hmac_md5(packet_copy, plen,
+ (unsigned char *)ndo->ndo_sigsecret,
+ strlen(ndo->ndo_sigsecret), sig);
+
+ /*
+ * Free the copy.
+ */
+ free(packet_copy);
+
+ /*
+ * Does the computed signature match the signature in the packet?
+ */
+ if (memcmp(sig_ptr, sig, sizeof(sig)) == 0) {
+ /* Yes. */
+ return (SIGNATURE_VALID);
+ } else {
+ /* No - print the computed signature. */
+ for (i = 0; i < sizeof(sig); ++i) {
+ ND_PRINT("%02x", sig[i]);
+ }
+
+ return (SIGNATURE_INVALID);
+ }
+}
+#else
+int
+signature_verify(netdissect_options *ndo _U_, const u_char *pptr _U_,
+ u_int plen _U_, const u_char *sig_ptr _U_,
+ void (*clear_rtn)(void *) _U_, const void *clear_arg _U_)
+{
+ return (CANT_CHECK_SIGNATURE);
+}
+#endif
diff --git a/signature.h b/signature.h
new file mode 100644
index 0000000..bbb63ed
--- /dev/null
+++ b/signature.h
@@ -0,0 +1,29 @@
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ *
+ * Functions for signature and digest verification.
+ *
+ * Original code by Hannes Gredler (hannes@gredler.at)
+ */
+
+/* for netdissect_options */
+#include "netdissect.h"
+
+/* signature checking result codes */
+#define SIGNATURE_VALID 0
+#define SIGNATURE_INVALID 1
+#define CANT_ALLOCATE_COPY 2
+#define CANT_CHECK_SIGNATURE 3
+
+extern const struct tok signature_check_values[];
+extern int signature_verify(netdissect_options *, const u_char *, u_int,
+ const u_char *, void (*)(void *), const void *);
diff --git a/slcompress.h b/slcompress.h
new file mode 100644
index 0000000..644c755
--- /dev/null
+++ b/slcompress.h
@@ -0,0 +1,85 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * Copyright (c) 1989, 1990, 1992, 1993 Regents of the University of
+ * California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
diff --git a/smb.h b/smb.h
new file mode 100644
index 0000000..40bba50
--- /dev/null
+++ b/smb.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) Andrew Tridgell 1995-1999
+ *
+ * This software may be distributed either under the terms of the
+ * BSD-style license that accompanies tcpdump or the GNU GPL version 2
+ * or later
+ */
+
+/* for netdissect_options */
+#include "netdissect.h"
+
+/* the complete */
+#define SMBmkdir 0x00 /* create directory */
+#define SMBrmdir 0x01 /* delete directory */
+#define SMBopen 0x02 /* open file */
+#define SMBcreate 0x03 /* create file */
+#define SMBclose 0x04 /* close file */
+#define SMBflush 0x05 /* flush file */
+#define SMBunlink 0x06 /* delete file */
+#define SMBmv 0x07 /* rename file */
+#define SMBgetatr 0x08 /* get file attributes */
+#define SMBsetatr 0x09 /* set file attributes */
+#define SMBread 0x0A /* read from file */
+#define SMBwrite 0x0B /* write to file */
+#define SMBlock 0x0C /* lock byte range */
+#define SMBunlock 0x0D /* unlock byte range */
+#define SMBctemp 0x0E /* create temporary file */
+#define SMBmknew 0x0F /* make new file */
+#define SMBchkpth 0x10 /* check directory path */
+#define SMBexit 0x11 /* process exit */
+#define SMBlseek 0x12 /* seek */
+#define SMBtcon 0x70 /* tree connect */
+#define SMBtconX 0x75 /* tree connect and X*/
+#define SMBtdis 0x71 /* tree disconnect */
+#define SMBnegprot 0x72 /* negotiate protocol */
+#define SMBdskattr 0x80 /* get disk attributes */
+#define SMBsearch 0x81 /* search directory */
+#define SMBsplopen 0xC0 /* open print spool file */
+#define SMBsplwr 0xC1 /* write to print spool file */
+#define SMBsplclose 0xC2 /* close print spool file */
+#define SMBsplretq 0xC3 /* return print queue */
+#define SMBsends 0xD0 /* send single block message */
+#define SMBsendb 0xD1 /* send broadcast message */
+#define SMBfwdname 0xD2 /* forward user name */
+#define SMBcancelf 0xD3 /* cancel forward */
+#define SMBgetmac 0xD4 /* get machine name */
+#define SMBsendstrt 0xD5 /* send start of multi-block message */
+#define SMBsendend 0xD6 /* send end of multi-block message */
+#define SMBsendtxt 0xD7 /* send text of multi-block message */
+
+/* Core+ protocol */
+#define SMBlockread 0x13 /* Lock a range and read */
+#define SMBwriteunlock 0x14 /* Unlock a range then write */
+#define SMBreadbraw 0x1a /* read a block of data with no smb header */
+#define SMBwritebraw 0x1d /* write a block of data with no smb header */
+#define SMBwritec 0x20 /* secondary write request */
+#define SMBwriteclose 0x2c /* write a file then close it */
+
+/* dos extended protocol */
+#define SMBreadBraw 0x1A /* read block raw */
+#define SMBreadBmpx 0x1B /* read block multiplexed */
+#define SMBreadBs 0x1C /* read block (secondary response) */
+#define SMBwriteBraw 0x1D /* write block raw */
+#define SMBwriteBmpx 0x1E /* write block multiplexed */
+#define SMBwriteBs 0x1F /* write block (secondary request) */
+#define SMBwriteC 0x20 /* write complete response */
+#define SMBsetattrE 0x22 /* set file attributes expanded */
+#define SMBgetattrE 0x23 /* get file attributes expanded */
+#define SMBlockingX 0x24 /* lock/unlock byte ranges and X */
+#define SMBtrans 0x25 /* transaction - name, bytes in/out */
+#define SMBtranss 0x26 /* transaction (secondary request/response) */
+#define SMBioctl 0x27 /* IOCTL */
+#define SMBioctls 0x28 /* IOCTL (secondary request/response) */
+#define SMBcopy 0x29 /* copy */
+#define SMBmove 0x2A /* move */
+#define SMBecho 0x2B /* echo */
+#define SMBopenX 0x2D /* open and X */
+#define SMBreadX 0x2E /* read and X */
+#define SMBwriteX 0x2F /* write and X */
+#define SMBsesssetupX 0x73 /* Session Set Up & X (including User Logon) */
+#define SMBffirst 0x82 /* find first */
+#define SMBfunique 0x83 /* find unique */
+#define SMBfclose 0x84 /* find close */
+#define SMBinvalid 0xFE /* invalid command */
+
+/* Extended 2.0 protocol */
+#define SMBtrans2 0x32 /* TRANS2 protocol set */
+#define SMBtranss2 0x33 /* TRANS2 protocol set, secondary command */
+#define SMBfindclose 0x34 /* Terminate a TRANSACT2_FINDFIRST */
+#define SMBfindnclose 0x35 /* Terminate a TRANSACT2_FINDNOTIFYFIRST */
+#define SMBulogoffX 0x74 /* user logoff */
+
+/* NT SMB extensions. */
+#define SMBnttrans 0xA0 /* NT transact */
+#define SMBnttranss 0xA1 /* NT transact secondary */
+#define SMBntcreateX 0xA2 /* NT create and X */
+#define SMBntcancel 0xA4 /* NT cancel */
+
+/* pathworks special */
+#define pSETDIR '\377'
+
+
+/* these are the TRANS2 sub commands */
+#define TRANSACT2_OPEN 0
+#define TRANSACT2_FINDFIRST 1
+#define TRANSACT2_FINDNEXT 2
+#define TRANSACT2_QFSINFO 3
+#define TRANSACT2_SETFSINFO 4
+#define TRANSACT2_QPATHINFO 5
+#define TRANSACT2_SETPATHINFO 6
+#define TRANSACT2_QFILEINFO 7
+#define TRANSACT2_SETFILEINFO 8
+#define TRANSACT2_FSCTL 9
+#define TRANSACT2_IOCTL 10
+#define TRANSACT2_FINDNOTIFYFIRST 11
+#define TRANSACT2_FINDNOTIFYNEXT 12
+#define TRANSACT2_MKDIR 13
+
+/* some protos */
+void smb_reset(void);
+const u_char *smb_fdata(netdissect_options *, const u_char *, const char *, const u_char *, int);
+extern void smb_data_print(netdissect_options *, const u_char *, u_int);
+extern const char *smb_errstr(int, int);
+extern const char *nt_errstr(uint32_t);
diff --git a/smbutil.c b/smbutil.c
new file mode 100644
index 0000000..ff32ecc
--- /dev/null
+++ b/smbutil.c
@@ -0,0 +1,1971 @@
+/*
+ * Copyright (C) Andrew Tridgell 1995-1999
+ *
+ * This software may be distributed either under the terms of the
+ * BSD-style license that accompanies tcpdump or the GNU GPL version 2
+ * or later
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "smb.h"
+
+static int stringlen_is_set;
+static uint32_t stringlen;
+extern const u_char *startbuf;
+
+/*
+ * Reset SMB state.
+ */
+void
+smb_reset(void)
+{
+ stringlen_is_set = 0;
+ stringlen = 0;
+}
+
+/*
+ * interpret a 32 bit dos packed date/time to some parameters
+ */
+static void
+interpret_dos_date(uint32_t date, struct tm *tp)
+{
+ uint32_t p0, p1, p2, p3;
+
+ p0 = date & 0xFF;
+ p1 = ((date & 0xFF00) >> 8) & 0xFF;
+ p2 = ((date & 0xFF0000) >> 16) & 0xFF;
+ p3 = ((date & 0xFF000000) >> 24) & 0xFF;
+
+ tp->tm_sec = 2 * (p0 & 0x1F);
+ tp->tm_min = ((p0 >> 5) & 0xFF) + ((p1 & 0x7) << 3);
+ tp->tm_hour = (p1 >> 3) & 0xFF;
+ tp->tm_mday = (p2 & 0x1F);
+ tp->tm_mon = ((p2 >> 5) & 0xFF) + ((p3 & 0x1) << 3) - 1;
+ tp->tm_year = ((p3 >> 1) & 0xFF) + 80;
+}
+
+/*
+ * common portion:
+ * create a unix date from a dos date
+ */
+static time_t
+int_unix_date(uint32_t dos_date)
+{
+ struct tm t;
+
+ if (dos_date == 0)
+ return(0);
+
+ interpret_dos_date(dos_date, &t);
+ t.tm_wday = 1;
+ t.tm_yday = 1;
+ t.tm_isdst = 0;
+
+ return (mktime(&t));
+}
+
+/*
+ * create a unix date from a dos date
+ * in network byte order
+ */
+static time_t
+make_unix_date(netdissect_options *ndo, const u_char *date_ptr)
+{
+ uint32_t dos_date = 0;
+
+ dos_date = GET_LE_U_4(date_ptr);
+
+ return int_unix_date(dos_date);
+}
+
+/*
+ * create a unix date from a dos date
+ * in halfword-swapped network byte order!
+ */
+static time_t
+make_unix_date2(netdissect_options *ndo, const u_char *date_ptr)
+{
+ uint32_t x, x2;
+
+ x = GET_LE_U_4(date_ptr);
+ x2 = ((x & 0xFFFF) << 16) | ((x & 0xFFFF0000) >> 16);
+ return int_unix_date(x2);
+}
+
+/*
+ * interpret an 8 byte "filetime" structure to a time_t
+ * It's originally in "100ns units since jan 1st 1601"
+ */
+static time_t
+interpret_long_date(netdissect_options *ndo, const u_char *p)
+{
+ double d;
+ time_t ret;
+
+ /* this gives us seconds since jan 1st 1601 (approx) */
+ d = (GET_LE_U_4(p + 4) * 256.0 + GET_U_1(p + 3)) * (1.0e-7 * (1 << 24));
+
+ /* now adjust by 369 years to make the secs since 1970 */
+ d -= 369.0 * 365.25 * 24 * 60 * 60;
+
+ /* and a fudge factor as we got it wrong by a few days */
+ d += (3 * 24 * 60 * 60 + 6 * 60 * 60 + 2);
+
+ if (d < 0)
+ return(0);
+
+ ret = (time_t)d;
+
+ return(ret);
+}
+
+/*
+ * interpret the weird netbios "name". Return the name type, or -1 if
+ * we run past the end of the buffer
+ */
+static int
+name_interpret(netdissect_options *ndo,
+ const u_char *in, const u_char *maxbuf, char *out)
+{
+ int ret;
+ u_int len;
+
+ if (in >= maxbuf)
+ return(-1); /* name goes past the end of the buffer */
+ len = GET_U_1(in) / 2;
+ in++;
+
+ *out=0;
+
+ if (len > 30 || len == 0)
+ return(0);
+
+ while (len) {
+ ND_TCHECK_2(in);
+ if (in + 1 >= maxbuf)
+ return(-1); /* name goes past the end of the buffer */
+ if (GET_U_1(in) < 'A' || GET_U_1(in) > 'P' ||
+ GET_U_1(in + 1) < 'A' || GET_U_1(in + 1) > 'P') {
+ *out = 0;
+ return(0);
+ }
+ *out = ((GET_U_1(in) - 'A') << 4) + (GET_U_1(in + 1) - 'A');
+ in += 2;
+ out++;
+ len--;
+ }
+ *out = 0;
+ ret = out[-1];
+
+ return(ret);
+
+trunc:
+ return(-1);
+}
+
+/*
+ * find a pointer to a netbios name
+ */
+static const u_char *
+name_ptr(netdissect_options *ndo,
+ const u_char *buf, u_int ofs, const u_char *maxbuf)
+{
+ const u_char *p;
+ u_char c;
+
+ p = buf + ofs;
+ if (p >= maxbuf)
+ return(NULL); /* name goes past the end of the buffer */
+
+ c = GET_U_1(p);
+
+ /* XXX - this should use the same code that the DNS dissector does */
+ if ((c & 0xC0) == 0xC0) {
+ uint16_t l;
+
+ ND_TCHECK_2(p);
+ if ((p + 1) >= maxbuf)
+ return(NULL); /* name goes past the end of the buffer */
+ l = GET_BE_U_2(p) & 0x3FFF;
+ if (l == 0) {
+ /* We have a pointer that points to itself. */
+ return(NULL);
+ }
+ p = buf + l;
+ if (p >= maxbuf)
+ return(NULL); /* name goes past the end of the buffer */
+ ND_TCHECK_1(p);
+ }
+ return(p);
+
+trunc:
+ return(NULL); /* name goes past the end of the buffer */
+}
+
+/*
+ * extract a netbios name from a buf
+ */
+static int
+name_extract(netdissect_options *ndo,
+ const u_char *buf, u_int ofs, const u_char *maxbuf, char *name)
+{
+ const u_char *p = name_ptr(ndo, buf, ofs, maxbuf);
+ if (p == NULL)
+ return(-1); /* error (probably name going past end of buffer) */
+ name[0] = '\0';
+ return(name_interpret(ndo, p, maxbuf, name));
+}
+
+
+/*
+ * return the total storage length of a mangled name
+ */
+static int
+name_len(netdissect_options *ndo,
+ const u_char *s, const u_char *maxbuf)
+{
+ const u_char *s0 = s;
+ unsigned char c;
+
+ if (s >= maxbuf)
+ return(-1); /* name goes past the end of the buffer */
+ c = GET_U_1(s);
+ if ((c & 0xC0) == 0xC0)
+ return(2);
+ while (GET_U_1(s)) {
+ if (s >= maxbuf)
+ return(-1); /* name goes past the end of the buffer */
+ s += GET_U_1(s) + 1;
+ ND_TCHECK_1(s);
+ }
+ return(ND_BYTES_BETWEEN(s, s0) + 1);
+
+trunc:
+ return(-1); /* name goes past the end of the buffer */
+}
+
+static void
+print_asc(netdissect_options *ndo,
+ const u_char *buf, u_int len)
+{
+ u_int i;
+ for (i = 0; i < len; i++)
+ fn_print_char(ndo, GET_U_1(buf + i));
+}
+
+static const char *
+name_type_str(int name_type)
+{
+ const char *f = NULL;
+
+ switch (name_type) {
+ case 0: f = "Workstation"; break;
+ case 0x03: f = "Client?"; break;
+ case 0x20: f = "Server"; break;
+ case 0x1d: f = "Master Browser"; break;
+ case 0x1b: f = "Domain Controller"; break;
+ case 0x1e: f = "Browser Server"; break;
+ default: f = "Unknown"; break;
+ }
+ return(f);
+}
+
+void
+smb_data_print(netdissect_options *ndo, const u_char *buf, u_int len)
+{
+ u_int i = 0;
+
+ if (len == 0)
+ return;
+ ND_PRINT("[%03X] ", i);
+ for (i = 0; i < len; /*nothing*/) {
+ ND_PRINT("%02X ", GET_U_1(buf + i) & 0xff);
+ i++;
+ if (i%8 == 0)
+ ND_PRINT(" ");
+ if (i % 16 == 0) {
+ print_asc(ndo, buf + i - 16, 8);
+ ND_PRINT(" ");
+ print_asc(ndo, buf + i - 8, 8);
+ ND_PRINT("\n");
+ if (i < len)
+ ND_PRINT("[%03X] ", i);
+ }
+ }
+ if (i % 16) {
+ int n;
+
+ n = 16 - (i % 16);
+ ND_PRINT(" ");
+ if (n>8)
+ ND_PRINT(" ");
+ while (n--)
+ ND_PRINT(" ");
+
+ n = ND_MIN(8, i % 16);
+ print_asc(ndo, buf + i - (i % 16), n);
+ ND_PRINT(" ");
+ n = (i % 16) - n;
+ if (n > 0)
+ print_asc(ndo, buf + i - n, n);
+ ND_PRINT("\n");
+ }
+}
+
+
+static void
+write_bits(netdissect_options *ndo,
+ unsigned int val, const char *fmt)
+{
+ const char *p = fmt;
+ u_int i = 0;
+
+ while ((p = strchr(fmt, '|'))) {
+ u_int l = ND_BYTES_BETWEEN(p, fmt);
+ if (l && (val & (1 << i)))
+ ND_PRINT("%.*s ", (int)l, fmt);
+ fmt = p + 1;
+ i++;
+ }
+}
+
+/* convert a UCS-2 string into an ASCII string */
+#define MAX_UNISTR_SIZE 1000
+static const u_char *
+unistr(netdissect_options *ndo, char (*buf)[MAX_UNISTR_SIZE+1],
+ const u_char *s, uint32_t strsize, int is_null_terminated,
+ int use_unicode)
+{
+ u_int c;
+ size_t l = 0;
+ const u_char *sp;
+
+ if (use_unicode) {
+ /*
+ * Skip padding that puts the string on an even boundary.
+ */
+ if (((s - startbuf) % 2) != 0) {
+ ND_TCHECK_1(s);
+ s++;
+ }
+ }
+ if (is_null_terminated) {
+ /*
+ * Null-terminated string.
+ * Find the length, counting the terminating NUL.
+ */
+ strsize = 0;
+ sp = s;
+ if (!use_unicode) {
+ for (;;) {
+ c = GET_U_1(sp);
+ sp++;
+ strsize++;
+ if (c == '\0')
+ break;
+ }
+ } else {
+ for (;;) {
+ c = GET_LE_U_2(sp);
+ sp += 2;
+ strsize += 2;
+ if (c == '\0')
+ break;
+ }
+ }
+ }
+ if (!use_unicode) {
+ while (strsize != 0) {
+ c = GET_U_1(s);
+ s++;
+ strsize--;
+ if (c == 0) {
+ /*
+ * Even counted strings may have embedded null
+ * terminators, so quit here, and skip past
+ * the rest of the data.
+ *
+ * Make sure, however, that the rest of the data
+ * is there, so we don't overflow the buffer when
+ * skipping past it.
+ */
+ ND_TCHECK_LEN(s, strsize);
+ s += strsize;
+ strsize = 0;
+ break;
+ }
+ if (l < MAX_UNISTR_SIZE) {
+ if (ND_ASCII_ISPRINT(c)) {
+ /* It's a printable ASCII character */
+ (*buf)[l] = (char)c;
+ } else {
+ /* It's a non-ASCII character or a non-printable ASCII character */
+ (*buf)[l] = '.';
+ }
+ l++;
+ }
+ }
+ } else {
+ while (strsize > 1) {
+ c = GET_LE_U_2(s);
+ s += 2;
+ strsize -= 2;
+ if (c == 0) {
+ /*
+ * Even counted strings may have embedded null
+ * terminators, so quit here, and skip past
+ * the rest of the data.
+ *
+ * Make sure, however, that the rest of the data
+ * is there, so we don't overflow the buffer when
+ * skipping past it.
+ */
+ ND_TCHECK_LEN(s, strsize);
+ s += strsize;
+ strsize = 0;
+ break;
+ }
+ if (l < MAX_UNISTR_SIZE) {
+ if (ND_ASCII_ISPRINT(c)) {
+ /* It's a printable ASCII character */
+ (*buf)[l] = (char)c;
+ } else {
+ /* It's a non-ASCII character or a non-printable ASCII character */
+ (*buf)[l] = '.';
+ }
+ l++;
+ }
+ }
+ if (strsize == 1) {
+ /* We have half of a code point; skip past it */
+ ND_TCHECK_1(s);
+ s++;
+ }
+ }
+ (*buf)[l] = 0;
+ return s;
+
+trunc:
+ (*buf)[l] = 0;
+ return NULL;
+}
+
+static const u_char *
+smb_fdata1(netdissect_options *ndo,
+ const u_char *buf, const char *fmt, const u_char *maxbuf,
+ int unicodestr)
+{
+ int reverse = 0;
+ const char *attrib_fmt = "READONLY|HIDDEN|SYSTEM|VOLUME|DIR|ARCHIVE|";
+ char strbuf[MAX_UNISTR_SIZE+1];
+
+ while (*fmt && buf<maxbuf) {
+ switch (*fmt) {
+ case 'a':
+ write_bits(ndo, GET_U_1(buf), attrib_fmt);
+ buf++;
+ fmt++;
+ break;
+
+ case 'A':
+ write_bits(ndo, GET_LE_U_2(buf), attrib_fmt);
+ buf += 2;
+ fmt++;
+ break;
+
+ case '{':
+ {
+ char bitfmt[128];
+ char *p;
+ u_int l;
+
+ p = strchr(++fmt, '}');
+ l = ND_BYTES_BETWEEN(p, fmt);
+
+ if (l > sizeof(bitfmt) - 1)
+ l = sizeof(bitfmt)-1;
+
+ strncpy(bitfmt, fmt, l);
+ bitfmt[l] = '\0';
+ fmt = p + 1;
+ write_bits(ndo, GET_U_1(buf), bitfmt);
+ buf++;
+ break;
+ }
+
+ case 'P':
+ {
+ int l = atoi(fmt + 1);
+ ND_TCHECK_LEN(buf, l);
+ buf += l;
+ fmt++;
+ while (ND_ASCII_ISDIGIT(*fmt))
+ fmt++;
+ break;
+ }
+ case 'r':
+ reverse = !reverse;
+ fmt++;
+ break;
+ case 'b':
+ {
+ unsigned int x;
+ x = GET_U_1(buf);
+ ND_PRINT("%u (0x%x)", x, x);
+ buf += 1;
+ fmt++;
+ break;
+ }
+ case 'd':
+ {
+ int x;
+ x = reverse ? GET_BE_S_2(buf) :
+ GET_LE_S_2(buf);
+ ND_PRINT("%d (0x%x)", x, x);
+ buf += 2;
+ fmt++;
+ break;
+ }
+ case 'D':
+ {
+ int x;
+ x = reverse ? GET_BE_S_4(buf) :
+ GET_LE_S_4(buf);
+ ND_PRINT("%d (0x%x)", x, x);
+ buf += 4;
+ fmt++;
+ break;
+ }
+ case 'L':
+ {
+ uint64_t x;
+ x = reverse ? GET_BE_U_8(buf) :
+ GET_LE_U_8(buf);
+ ND_PRINT("%" PRIu64 " (0x%" PRIx64 ")", x, x);
+ buf += 8;
+ fmt++;
+ break;
+ }
+ case 'u':
+ {
+ unsigned int x;
+ x = reverse ? GET_BE_U_2(buf) :
+ GET_LE_U_2(buf);
+ ND_PRINT("%u (0x%x)", x, x);
+ buf += 2;
+ fmt++;
+ break;
+ }
+ case 'U':
+ {
+ unsigned int x;
+ x = reverse ? GET_BE_U_4(buf) :
+ GET_LE_U_4(buf);
+ ND_PRINT("%u (0x%x)", x, x);
+ buf += 4;
+ fmt++;
+ break;
+ }
+ case 'M':
+ {
+ /* Weird mixed-endian length values in 64-bit locks */
+ uint32_t x1, x2;
+ uint64_t x;
+ ND_TCHECK_8(buf);
+ x1 = reverse ? GET_BE_U_4(buf) :
+ GET_LE_U_4(buf);
+ x2 = reverse ? GET_BE_U_4(buf + 4) :
+ GET_LE_U_4(buf + 4);
+ x = (((uint64_t)x1) << 32) | x2;
+ ND_PRINT("%" PRIu64 " (0x%" PRIx64 ")", x, x);
+ buf += 8;
+ fmt++;
+ break;
+ }
+ case 'B':
+ {
+ unsigned int x;
+ x = GET_U_1(buf);
+ ND_PRINT("0x%X", x);
+ buf += 1;
+ fmt++;
+ break;
+ }
+ case 'w':
+ {
+ unsigned int x;
+ x = reverse ? GET_BE_U_2(buf) :
+ GET_LE_U_2(buf);
+ ND_PRINT("0x%X", x);
+ buf += 2;
+ fmt++;
+ break;
+ }
+ case 'W':
+ {
+ unsigned int x;
+ x = reverse ? GET_BE_U_4(buf) :
+ GET_LE_U_4(buf);
+ ND_PRINT("0x%X", x);
+ buf += 4;
+ fmt++;
+ break;
+ }
+ case 'l':
+ {
+ fmt++;
+ switch (*fmt) {
+
+ case 'b':
+ stringlen = GET_U_1(buf);
+ stringlen_is_set = 1;
+ ND_PRINT("%u", stringlen);
+ buf += 1;
+ break;
+
+ case 'd':
+ case 'u':
+ stringlen = reverse ? GET_BE_U_2(buf) :
+ GET_LE_U_2(buf);
+ stringlen_is_set = 1;
+ ND_PRINT("%u", stringlen);
+ buf += 2;
+ break;
+
+ case 'D':
+ case 'U':
+ stringlen = reverse ? GET_BE_U_4(buf) :
+ GET_LE_U_4(buf);
+ stringlen_is_set = 1;
+ ND_PRINT("%u", stringlen);
+ buf += 4;
+ break;
+ }
+ fmt++;
+ break;
+ }
+ case 'S':
+ case 'R': /* like 'S', but always ASCII */
+ {
+ /*XXX unistr() */
+ buf = unistr(ndo, &strbuf, buf, 0, 1, (*fmt == 'R') ? 0 : unicodestr);
+ ND_PRINT("%s", strbuf);
+ if (buf == NULL)
+ goto trunc;
+ fmt++;
+ break;
+ }
+ case 'Z':
+ case 'Y': /* like 'Z', but always ASCII */
+ {
+ if (GET_U_1(buf) != 4 && GET_U_1(buf) != 2) {
+ ND_PRINT("Error! ASCIIZ buffer of type %u", GET_U_1(buf));
+ return maxbuf; /* give up */
+ }
+ buf = unistr(ndo, &strbuf, buf + 1, 0, 1, (*fmt == 'Y') ? 0 : unicodestr);
+ ND_PRINT("%s", strbuf);
+ if (buf == NULL)
+ goto trunc;
+ fmt++;
+ break;
+ }
+ case 's':
+ {
+ int l = atoi(fmt + 1);
+ ND_TCHECK_LEN(buf, l);
+ ND_PRINT("%-*.*s", l, l, buf);
+ buf += l;
+ fmt++;
+ while (ND_ASCII_ISDIGIT(*fmt))
+ fmt++;
+ break;
+ }
+ case 'c':
+ {
+ if (!stringlen_is_set) {
+ ND_PRINT("{stringlen not set}");
+ goto trunc;
+ }
+ ND_TCHECK_LEN(buf, stringlen);
+ ND_PRINT("%-*.*s", (int)stringlen, (int)stringlen, buf);
+ buf += stringlen;
+ fmt++;
+ while (ND_ASCII_ISDIGIT(*fmt))
+ fmt++;
+ break;
+ }
+ case 'C':
+ {
+ if (!stringlen_is_set) {
+ ND_PRINT("{stringlen not set}");
+ goto trunc;
+ }
+ buf = unistr(ndo, &strbuf, buf, stringlen, 0, unicodestr);
+ ND_PRINT("%s", strbuf);
+ if (buf == NULL)
+ goto trunc;
+ fmt++;
+ break;
+ }
+ case 'h':
+ {
+ int l = atoi(fmt + 1);
+ ND_TCHECK_LEN(buf, l);
+ while (l--) {
+ ND_PRINT("%02x", GET_U_1(buf));
+ buf++;
+ }
+ fmt++;
+ while (ND_ASCII_ISDIGIT(*fmt))
+ fmt++;
+ break;
+ }
+ case 'n':
+ {
+ int t = atoi(fmt+1);
+ char nbuf[255];
+ int name_type;
+ int len;
+
+ switch (t) {
+ case 1:
+ name_type = name_extract(ndo, startbuf, ND_BYTES_BETWEEN(buf, startbuf),
+ maxbuf, nbuf);
+ if (name_type < 0)
+ goto trunc;
+ len = name_len(ndo, buf, maxbuf);
+ if (len < 0)
+ goto trunc;
+ buf += len;
+ ND_PRINT("%-15.15s NameType=0x%02X (%s)", nbuf, name_type,
+ name_type_str(name_type));
+ break;
+ case 2:
+ name_type = GET_U_1(buf + 15);
+ ND_PRINT("%-15.15s NameType=0x%02X (%s)", buf, name_type,
+ name_type_str(name_type));
+ buf += 16;
+ break;
+ }
+ fmt++;
+ while (ND_ASCII_ISDIGIT(*fmt))
+ fmt++;
+ break;
+ }
+ case 'T':
+ {
+ time_t t;
+ struct tm *lt;
+ const char *tstring;
+ uint32_t x;
+
+ switch (atoi(fmt + 1)) {
+ case 1:
+ x = GET_LE_U_4(buf);
+ if (x == 0 || x == 0xFFFFFFFF)
+ t = 0;
+ else
+ t = make_unix_date(ndo, buf);
+ buf += 4;
+ break;
+ case 2:
+ x = GET_LE_U_4(buf);
+ if (x == 0 || x == 0xFFFFFFFF)
+ t = 0;
+ else
+ t = make_unix_date2(ndo, buf);
+ buf += 4;
+ break;
+ case 3:
+ ND_TCHECK_8(buf);
+ t = interpret_long_date(ndo, buf);
+ buf += 8;
+ break;
+ default:
+ t = 0;
+ break;
+ }
+ if (t != 0) {
+ lt = localtime(&t);
+ if (lt != NULL)
+ tstring = asctime(lt);
+ else
+ tstring = "(Can't convert time)\n";
+ } else
+ tstring = "NULL\n";
+ ND_PRINT("%s", tstring);
+ fmt++;
+ while (ND_ASCII_ISDIGIT(*fmt))
+ fmt++;
+ break;
+ }
+ default:
+ ND_PRINT("%c", *fmt);
+ fmt++;
+ break;
+ }
+ }
+
+ if (buf >= maxbuf && *fmt)
+ ND_PRINT("END OF BUFFER\n");
+
+ return(buf);
+
+trunc:
+ nd_print_trunc(ndo);
+ return(NULL);
+}
+
+const u_char *
+smb_fdata(netdissect_options *ndo,
+ const u_char *buf, const char *fmt, const u_char *maxbuf,
+ int unicodestr)
+{
+ static int depth = 0;
+ char s[128];
+ char *p;
+
+ while (*fmt) {
+ switch (*fmt) {
+ case '*':
+ /*
+ * List of multiple instances of something described by the
+ * remainder of the string (which may itself include a list
+ * of multiple instances of something, so we recurse).
+ */
+ fmt++;
+ while (buf < maxbuf) {
+ const u_char *buf2;
+ depth++;
+ /*
+ * In order to avoid stack exhaustion recurse at most 10
+ * levels; that "should not happen", as no SMB structure
+ * should be nested *that* deeply, and we thus shouldn't
+ * have format strings with that level of nesting.
+ */
+ if (depth == 10) {
+ ND_PRINT("(too many nested levels, not recursing)");
+ buf2 = buf;
+ } else
+ buf2 = smb_fdata(ndo, buf, fmt, maxbuf, unicodestr);
+ depth--;
+ if (buf2 == NULL)
+ return(NULL);
+ if (buf2 == buf)
+ return(buf);
+ buf = buf2;
+ }
+ return(buf);
+
+ case '|':
+ /*
+ * Just do a bounds check.
+ */
+ fmt++;
+ if (buf >= maxbuf)
+ return(buf);
+ break;
+
+ case '%':
+ /*
+ * XXX - unused?
+ */
+ fmt++;
+ buf = maxbuf;
+ break;
+
+ case '#':
+ /*
+ * Done?
+ */
+ fmt++;
+ return(buf);
+ break;
+
+ case '[':
+ /*
+ * Format of an item, enclosed in square brackets; dissect
+ * the item with smb_fdata1().
+ */
+ fmt++;
+ if (buf >= maxbuf)
+ return(buf);
+ memset(s, 0, sizeof(s));
+ p = strchr(fmt, ']');
+ if ((size_t)(p - fmt + 1) > sizeof(s)) {
+ /* overrun */
+ return(buf);
+ }
+ strncpy(s, fmt, p - fmt);
+ s[p - fmt] = '\0';
+ fmt = p + 1;
+ buf = smb_fdata1(ndo, buf, s, maxbuf, unicodestr);
+ if (buf == NULL) {
+ /*
+ * Truncated.
+ * Is the next character a newline?
+ * If so, print it before quitting, so we don't
+ * get stuff in the middle of the line.
+ */
+ if (*fmt == '\n')
+ ND_PRINT("\n");
+ return(NULL);
+ }
+ break;
+
+ default:
+ /*
+ * Not a formatting character, so just print it.
+ */
+ ND_PRINT("%c", *fmt);
+ fmt++;
+ break;
+ }
+ }
+ if (!depth && buf < maxbuf) {
+ u_int len = ND_BYTES_BETWEEN(maxbuf, buf);
+ ND_PRINT("Data: (%u bytes)\n", len);
+ smb_data_print(ndo, buf, len);
+ return(buf + len);
+ }
+ return(buf);
+}
+
+typedef struct {
+ const char *name;
+ int code;
+ const char *message;
+} err_code_struct;
+
+/* DOS Error Messages */
+static const err_code_struct dos_msgs[] = {
+ { "ERRbadfunc", 1, "Invalid function." },
+ { "ERRbadfile", 2, "File not found." },
+ { "ERRbadpath", 3, "Directory invalid." },
+ { "ERRnofids", 4, "No file descriptors available" },
+ { "ERRnoaccess", 5, "Access denied." },
+ { "ERRbadfid", 6, "Invalid file handle." },
+ { "ERRbadmcb", 7, "Memory control blocks destroyed." },
+ { "ERRnomem", 8, "Insufficient server memory to perform the requested function." },
+ { "ERRbadmem", 9, "Invalid memory block address." },
+ { "ERRbadenv", 10, "Invalid environment." },
+ { "ERRbadformat", 11, "Invalid format." },
+ { "ERRbadaccess", 12, "Invalid open mode." },
+ { "ERRbaddata", 13, "Invalid data." },
+ { "ERR", 14, "reserved." },
+ { "ERRbaddrive", 15, "Invalid drive specified." },
+ { "ERRremcd", 16, "A Delete Directory request attempted to remove the server's current directory." },
+ { "ERRdiffdevice", 17, "Not same device." },
+ { "ERRnofiles", 18, "A File Search command can find no more files matching the specified criteria." },
+ { "ERRbadshare", 32, "The sharing mode specified for an Open conflicts with existing FIDs on the file." },
+ { "ERRlock", 33, "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process." },
+ { "ERRfilexists", 80, "The file named in a Create Directory, Make New File or Link request already exists." },
+ { "ERRbadpipe", 230, "Pipe invalid." },
+ { "ERRpipebusy", 231, "All instances of the requested pipe are busy." },
+ { "ERRpipeclosing", 232, "Pipe close in progress." },
+ { "ERRnotconnected", 233, "No process on other end of pipe." },
+ { "ERRmoredata", 234, "There is more data to be returned." },
+ { NULL, -1, NULL }
+ };
+
+/* Server Error Messages */
+static const err_code_struct server_msgs[] = {
+ { "ERRerror", 1, "Non-specific error code." },
+ { "ERRbadpw", 2, "Bad password - name/password pair in a Tree Connect or Session Setup are invalid." },
+ { "ERRbadtype", 3, "reserved." },
+ { "ERRaccess", 4, "The requester does not have the necessary access rights within the specified context for the requested function. The context is defined by the TID or the UID." },
+ { "ERRinvnid", 5, "The tree ID (TID) specified in a command was invalid." },
+ { "ERRinvnetname", 6, "Invalid network name in tree connect." },
+ { "ERRinvdevice", 7, "Invalid device - printer request made to non-printer connection or non-printer request made to printer connection." },
+ { "ERRqfull", 49, "Print queue full (files) -- returned by open print file." },
+ { "ERRqtoobig", 50, "Print queue full -- no space." },
+ { "ERRqeof", 51, "EOF on print queue dump." },
+ { "ERRinvpfid", 52, "Invalid print file FID." },
+ { "ERRsmbcmd", 64, "The server did not recognize the command received." },
+ { "ERRsrverror", 65, "The server encountered an internal error, e.g., system file unavailable." },
+ { "ERRfilespecs", 67, "The file handle (FID) and pathname parameters contained an invalid combination of values." },
+ { "ERRreserved", 68, "reserved." },
+ { "ERRbadpermits", 69, "The access permissions specified for a file or directory are not a valid combination. The server cannot set the requested attribute." },
+ { "ERRreserved", 70, "reserved." },
+ { "ERRsetattrmode", 71, "The attribute mode in the Set File Attribute request is invalid." },
+ { "ERRpaused", 81, "Server is paused." },
+ { "ERRmsgoff", 82, "Not receiving messages." },
+ { "ERRnoroom", 83, "No room to buffer message." },
+ { "ERRrmuns", 87, "Too many remote user names." },
+ { "ERRtimeout", 88, "Operation timed out." },
+ { "ERRnoresource", 89, "No resources currently available for request." },
+ { "ERRtoomanyuids", 90, "Too many UIDs active on this session." },
+ { "ERRbaduid", 91, "The UID is not known as a valid ID on this session." },
+ { "ERRusempx", 250, "Temp unable to support Raw, use MPX mode." },
+ { "ERRusestd", 251, "Temp unable to support Raw, use standard read/write." },
+ { "ERRcontmpx", 252, "Continue in MPX mode." },
+ { "ERRreserved", 253, "reserved." },
+ { "ERRreserved", 254, "reserved." },
+ { "ERRnosupport", 0xFFFF, "Function not supported." },
+ { NULL, -1, NULL }
+};
+
+/* Hard Error Messages */
+static const err_code_struct hard_msgs[] = {
+ { "ERRnowrite", 19, "Attempt to write on write-protected diskette." },
+ { "ERRbadunit", 20, "Unknown unit." },
+ { "ERRnotready", 21, "Drive not ready." },
+ { "ERRbadcmd", 22, "Unknown command." },
+ { "ERRdata", 23, "Data error (CRC)." },
+ { "ERRbadreq", 24, "Bad request structure length." },
+ { "ERRseek", 25 , "Seek error." },
+ { "ERRbadmedia", 26, "Unknown media type." },
+ { "ERRbadsector", 27, "Sector not found." },
+ { "ERRnopaper", 28, "Printer out of paper." },
+ { "ERRwrite", 29, "Write fault." },
+ { "ERRread", 30, "Read fault." },
+ { "ERRgeneral", 31, "General failure." },
+ { "ERRbadshare", 32, "A open conflicts with an existing open." },
+ { "ERRlock", 33, "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process." },
+ { "ERRwrongdisk", 34, "The wrong disk was found in a drive." },
+ { "ERRFCBUnavail", 35, "No FCBs are available to process request." },
+ { "ERRsharebufexc", 36, "A sharing buffer has been exceeded." },
+ { NULL, -1, NULL }
+};
+
+static const struct {
+ int code;
+ const char *class;
+ const err_code_struct *err_msgs;
+} err_classes[] = {
+ { 0, "SUCCESS", NULL },
+ { 0x01, "ERRDOS", dos_msgs },
+ { 0x02, "ERRSRV", server_msgs },
+ { 0x03, "ERRHRD", hard_msgs },
+ { 0x04, "ERRXOS", NULL },
+ { 0xE1, "ERRRMX1", NULL },
+ { 0xE2, "ERRRMX2", NULL },
+ { 0xE3, "ERRRMX3", NULL },
+ { 0xFF, "ERRCMD", NULL },
+ { -1, NULL, NULL }
+};
+
+/*
+ * return a SMB error string from a SMB buffer
+ */
+const char *
+smb_errstr(int class, int num)
+{
+ static char ret[128];
+ int i, j;
+
+ ret[0] = 0;
+
+ for (i = 0; err_classes[i].class; i++)
+ if (err_classes[i].code == class) {
+ if (err_classes[i].err_msgs) {
+ const err_code_struct *err = err_classes[i].err_msgs;
+ for (j = 0; err[j].name; j++)
+ if (num == err[j].code) {
+ snprintf(ret, sizeof(ret), "%s - %s (%s)",
+ err_classes[i].class, err[j].name, err[j].message);
+ return ret;
+ }
+ }
+
+ snprintf(ret, sizeof(ret), "%s - %d", err_classes[i].class, num);
+ return ret;
+ }
+
+ snprintf(ret, sizeof(ret), "ERROR: Unknown error (%d,%d)", class, num);
+ return(ret);
+}
+
+typedef struct {
+ uint32_t code;
+ const char *name;
+} nt_err_code_struct;
+
+/*
+ * NT Error codes
+ */
+static const nt_err_code_struct nt_errors[] = {
+ { 0x00000000, "STATUS_SUCCESS" },
+ { 0x00000000, "STATUS_WAIT_0" },
+ { 0x00000001, "STATUS_WAIT_1" },
+ { 0x00000002, "STATUS_WAIT_2" },
+ { 0x00000003, "STATUS_WAIT_3" },
+ { 0x0000003F, "STATUS_WAIT_63" },
+ { 0x00000080, "STATUS_ABANDONED" },
+ { 0x00000080, "STATUS_ABANDONED_WAIT_0" },
+ { 0x000000BF, "STATUS_ABANDONED_WAIT_63" },
+ { 0x000000C0, "STATUS_USER_APC" },
+ { 0x00000100, "STATUS_KERNEL_APC" },
+ { 0x00000101, "STATUS_ALERTED" },
+ { 0x00000102, "STATUS_TIMEOUT" },
+ { 0x00000103, "STATUS_PENDING" },
+ { 0x00000104, "STATUS_REPARSE" },
+ { 0x00000105, "STATUS_MORE_ENTRIES" },
+ { 0x00000106, "STATUS_NOT_ALL_ASSIGNED" },
+ { 0x00000107, "STATUS_SOME_NOT_MAPPED" },
+ { 0x00000108, "STATUS_OPLOCK_BREAK_IN_PROGRESS" },
+ { 0x00000109, "STATUS_VOLUME_MOUNTED" },
+ { 0x0000010A, "STATUS_RXACT_COMMITTED" },
+ { 0x0000010B, "STATUS_NOTIFY_CLEANUP" },
+ { 0x0000010C, "STATUS_NOTIFY_ENUM_DIR" },
+ { 0x0000010D, "STATUS_NO_QUOTAS_FOR_ACCOUNT" },
+ { 0x0000010E, "STATUS_PRIMARY_TRANSPORT_CONNECT_FAILED" },
+ { 0x00000110, "STATUS_PAGE_FAULT_TRANSITION" },
+ { 0x00000111, "STATUS_PAGE_FAULT_DEMAND_ZERO" },
+ { 0x00000112, "STATUS_PAGE_FAULT_COPY_ON_WRITE" },
+ { 0x00000113, "STATUS_PAGE_FAULT_GUARD_PAGE" },
+ { 0x00000114, "STATUS_PAGE_FAULT_PAGING_FILE" },
+ { 0x00000115, "STATUS_CACHE_PAGE_LOCKED" },
+ { 0x00000116, "STATUS_CRASH_DUMP" },
+ { 0x00000117, "STATUS_BUFFER_ALL_ZEROS" },
+ { 0x00000118, "STATUS_REPARSE_OBJECT" },
+ { 0x0000045C, "STATUS_NO_SHUTDOWN_IN_PROGRESS" },
+ { 0x40000000, "STATUS_OBJECT_NAME_EXISTS" },
+ { 0x40000001, "STATUS_THREAD_WAS_SUSPENDED" },
+ { 0x40000002, "STATUS_WORKING_SET_LIMIT_RANGE" },
+ { 0x40000003, "STATUS_IMAGE_NOT_AT_BASE" },
+ { 0x40000004, "STATUS_RXACT_STATE_CREATED" },
+ { 0x40000005, "STATUS_SEGMENT_NOTIFICATION" },
+ { 0x40000006, "STATUS_LOCAL_USER_SESSION_KEY" },
+ { 0x40000007, "STATUS_BAD_CURRENT_DIRECTORY" },
+ { 0x40000008, "STATUS_SERIAL_MORE_WRITES" },
+ { 0x40000009, "STATUS_REGISTRY_RECOVERED" },
+ { 0x4000000A, "STATUS_FT_READ_RECOVERY_FROM_BACKUP" },
+ { 0x4000000B, "STATUS_FT_WRITE_RECOVERY" },
+ { 0x4000000C, "STATUS_SERIAL_COUNTER_TIMEOUT" },
+ { 0x4000000D, "STATUS_NULL_LM_PASSWORD" },
+ { 0x4000000E, "STATUS_IMAGE_MACHINE_TYPE_MISMATCH" },
+ { 0x4000000F, "STATUS_RECEIVE_PARTIAL" },
+ { 0x40000010, "STATUS_RECEIVE_EXPEDITED" },
+ { 0x40000011, "STATUS_RECEIVE_PARTIAL_EXPEDITED" },
+ { 0x40000012, "STATUS_EVENT_DONE" },
+ { 0x40000013, "STATUS_EVENT_PENDING" },
+ { 0x40000014, "STATUS_CHECKING_FILE_SYSTEM" },
+ { 0x40000015, "STATUS_FATAL_APP_EXIT" },
+ { 0x40000016, "STATUS_PREDEFINED_HANDLE" },
+ { 0x40000017, "STATUS_WAS_UNLOCKED" },
+ { 0x40000018, "STATUS_SERVICE_NOTIFICATION" },
+ { 0x40000019, "STATUS_WAS_LOCKED" },
+ { 0x4000001A, "STATUS_LOG_HARD_ERROR" },
+ { 0x4000001B, "STATUS_ALREADY_WIN32" },
+ { 0x4000001C, "STATUS_WX86_UNSIMULATE" },
+ { 0x4000001D, "STATUS_WX86_CONTINUE" },
+ { 0x4000001E, "STATUS_WX86_SINGLE_STEP" },
+ { 0x4000001F, "STATUS_WX86_BREAKPOINT" },
+ { 0x40000020, "STATUS_WX86_EXCEPTION_CONTINUE" },
+ { 0x40000021, "STATUS_WX86_EXCEPTION_LASTCHANCE" },
+ { 0x40000022, "STATUS_WX86_EXCEPTION_CHAIN" },
+ { 0x40000023, "STATUS_IMAGE_MACHINE_TYPE_MISMATCH_EXE" },
+ { 0x40000024, "STATUS_NO_YIELD_PERFORMED" },
+ { 0x40000025, "STATUS_TIMER_RESUME_IGNORED" },
+ { 0x80000001, "STATUS_GUARD_PAGE_VIOLATION" },
+ { 0x80000002, "STATUS_DATATYPE_MISALIGNMENT" },
+ { 0x80000003, "STATUS_BREAKPOINT" },
+ { 0x80000004, "STATUS_SINGLE_STEP" },
+ { 0x80000005, "STATUS_BUFFER_OVERFLOW" },
+ { 0x80000006, "STATUS_NO_MORE_FILES" },
+ { 0x80000007, "STATUS_WAKE_SYSTEM_DEBUGGER" },
+ { 0x8000000A, "STATUS_HANDLES_CLOSED" },
+ { 0x8000000B, "STATUS_NO_INHERITANCE" },
+ { 0x8000000C, "STATUS_GUID_SUBSTITUTION_MADE" },
+ { 0x8000000D, "STATUS_PARTIAL_COPY" },
+ { 0x8000000E, "STATUS_DEVICE_PAPER_EMPTY" },
+ { 0x8000000F, "STATUS_DEVICE_POWERED_OFF" },
+ { 0x80000010, "STATUS_DEVICE_OFF_LINE" },
+ { 0x80000011, "STATUS_DEVICE_BUSY" },
+ { 0x80000012, "STATUS_NO_MORE_EAS" },
+ { 0x80000013, "STATUS_INVALID_EA_NAME" },
+ { 0x80000014, "STATUS_EA_LIST_INCONSISTENT" },
+ { 0x80000015, "STATUS_INVALID_EA_FLAG" },
+ { 0x80000016, "STATUS_VERIFY_REQUIRED" },
+ { 0x80000017, "STATUS_EXTRANEOUS_INFORMATION" },
+ { 0x80000018, "STATUS_RXACT_COMMIT_NECESSARY" },
+ { 0x8000001A, "STATUS_NO_MORE_ENTRIES" },
+ { 0x8000001B, "STATUS_FILEMARK_DETECTED" },
+ { 0x8000001C, "STATUS_MEDIA_CHANGED" },
+ { 0x8000001D, "STATUS_BUS_RESET" },
+ { 0x8000001E, "STATUS_END_OF_MEDIA" },
+ { 0x8000001F, "STATUS_BEGINNING_OF_MEDIA" },
+ { 0x80000020, "STATUS_MEDIA_CHECK" },
+ { 0x80000021, "STATUS_SETMARK_DETECTED" },
+ { 0x80000022, "STATUS_NO_DATA_DETECTED" },
+ { 0x80000023, "STATUS_REDIRECTOR_HAS_OPEN_HANDLES" },
+ { 0x80000024, "STATUS_SERVER_HAS_OPEN_HANDLES" },
+ { 0x80000025, "STATUS_ALREADY_DISCONNECTED" },
+ { 0x80000026, "STATUS_LONGJUMP" },
+ { 0x80040111, "MAPI_E_LOGON_FAILED" },
+ { 0x80090300, "SEC_E_INSUFFICIENT_MEMORY" },
+ { 0x80090301, "SEC_E_INVALID_HANDLE" },
+ { 0x80090302, "SEC_E_UNSUPPORTED_FUNCTION" },
+ { 0x8009030B, "SEC_E_NO_IMPERSONATION" },
+ { 0x8009030D, "SEC_E_UNKNOWN_CREDENTIALS" },
+ { 0x8009030E, "SEC_E_NO_CREDENTIALS" },
+ { 0x8009030F, "SEC_E_MESSAGE_ALTERED" },
+ { 0x80090310, "SEC_E_OUT_OF_SEQUENCE" },
+ { 0x80090311, "SEC_E_NO_AUTHENTICATING_AUTHORITY" },
+ { 0xC0000001, "STATUS_UNSUCCESSFUL" },
+ { 0xC0000002, "STATUS_NOT_IMPLEMENTED" },
+ { 0xC0000003, "STATUS_INVALID_INFO_CLASS" },
+ { 0xC0000004, "STATUS_INFO_LENGTH_MISMATCH" },
+ { 0xC0000005, "STATUS_ACCESS_VIOLATION" },
+ { 0xC0000006, "STATUS_IN_PAGE_ERROR" },
+ { 0xC0000007, "STATUS_PAGEFILE_QUOTA" },
+ { 0xC0000008, "STATUS_INVALID_HANDLE" },
+ { 0xC0000009, "STATUS_BAD_INITIAL_STACK" },
+ { 0xC000000A, "STATUS_BAD_INITIAL_PC" },
+ { 0xC000000B, "STATUS_INVALID_CID" },
+ { 0xC000000C, "STATUS_TIMER_NOT_CANCELED" },
+ { 0xC000000D, "STATUS_INVALID_PARAMETER" },
+ { 0xC000000E, "STATUS_NO_SUCH_DEVICE" },
+ { 0xC000000F, "STATUS_NO_SUCH_FILE" },
+ { 0xC0000010, "STATUS_INVALID_DEVICE_REQUEST" },
+ { 0xC0000011, "STATUS_END_OF_FILE" },
+ { 0xC0000012, "STATUS_WRONG_VOLUME" },
+ { 0xC0000013, "STATUS_NO_MEDIA_IN_DEVICE" },
+ { 0xC0000014, "STATUS_UNRECOGNIZED_MEDIA" },
+ { 0xC0000015, "STATUS_NONEXISTENT_SECTOR" },
+ { 0xC0000016, "STATUS_MORE_PROCESSING_REQUIRED" },
+ { 0xC0000017, "STATUS_NO_MEMORY" },
+ { 0xC0000018, "STATUS_CONFLICTING_ADDRESSES" },
+ { 0xC0000019, "STATUS_NOT_MAPPED_VIEW" },
+ { 0xC000001A, "STATUS_UNABLE_TO_FREE_VM" },
+ { 0xC000001B, "STATUS_UNABLE_TO_DELETE_SECTION" },
+ { 0xC000001C, "STATUS_INVALID_SYSTEM_SERVICE" },
+ { 0xC000001D, "STATUS_ILLEGAL_INSTRUCTION" },
+ { 0xC000001E, "STATUS_INVALID_LOCK_SEQUENCE" },
+ { 0xC000001F, "STATUS_INVALID_VIEW_SIZE" },
+ { 0xC0000020, "STATUS_INVALID_FILE_FOR_SECTION" },
+ { 0xC0000021, "STATUS_ALREADY_COMMITTED" },
+ { 0xC0000022, "STATUS_ACCESS_DENIED" },
+ { 0xC0000023, "STATUS_BUFFER_TOO_SMALL" },
+ { 0xC0000024, "STATUS_OBJECT_TYPE_MISMATCH" },
+ { 0xC0000025, "STATUS_NONCONTINUABLE_EXCEPTION" },
+ { 0xC0000026, "STATUS_INVALID_DISPOSITION" },
+ { 0xC0000027, "STATUS_UNWIND" },
+ { 0xC0000028, "STATUS_BAD_STACK" },
+ { 0xC0000029, "STATUS_INVALID_UNWIND_TARGET" },
+ { 0xC000002A, "STATUS_NOT_LOCKED" },
+ { 0xC000002B, "STATUS_PARITY_ERROR" },
+ { 0xC000002C, "STATUS_UNABLE_TO_DECOMMIT_VM" },
+ { 0xC000002D, "STATUS_NOT_COMMITTED" },
+ { 0xC000002E, "STATUS_INVALID_PORT_ATTRIBUTES" },
+ { 0xC000002F, "STATUS_PORT_MESSAGE_TOO_LONG" },
+ { 0xC0000030, "STATUS_INVALID_PARAMETER_MIX" },
+ { 0xC0000031, "STATUS_INVALID_QUOTA_LOWER" },
+ { 0xC0000032, "STATUS_DISK_CORRUPT_ERROR" },
+ { 0xC0000033, "STATUS_OBJECT_NAME_INVALID" },
+ { 0xC0000034, "STATUS_OBJECT_NAME_NOT_FOUND" },
+ { 0xC0000035, "STATUS_OBJECT_NAME_COLLISION" },
+ { 0xC0000037, "STATUS_PORT_DISCONNECTED" },
+ { 0xC0000038, "STATUS_DEVICE_ALREADY_ATTACHED" },
+ { 0xC0000039, "STATUS_OBJECT_PATH_INVALID" },
+ { 0xC000003A, "STATUS_OBJECT_PATH_NOT_FOUND" },
+ { 0xC000003B, "STATUS_OBJECT_PATH_SYNTAX_BAD" },
+ { 0xC000003C, "STATUS_DATA_OVERRUN" },
+ { 0xC000003D, "STATUS_DATA_LATE_ERROR" },
+ { 0xC000003E, "STATUS_DATA_ERROR" },
+ { 0xC000003F, "STATUS_CRC_ERROR" },
+ { 0xC0000040, "STATUS_SECTION_TOO_BIG" },
+ { 0xC0000041, "STATUS_PORT_CONNECTION_REFUSED" },
+ { 0xC0000042, "STATUS_INVALID_PORT_HANDLE" },
+ { 0xC0000043, "STATUS_SHARING_VIOLATION" },
+ { 0xC0000044, "STATUS_QUOTA_EXCEEDED" },
+ { 0xC0000045, "STATUS_INVALID_PAGE_PROTECTION" },
+ { 0xC0000046, "STATUS_MUTANT_NOT_OWNED" },
+ { 0xC0000047, "STATUS_SEMAPHORE_LIMIT_EXCEEDED" },
+ { 0xC0000048, "STATUS_PORT_ALREADY_SET" },
+ { 0xC0000049, "STATUS_SECTION_NOT_IMAGE" },
+ { 0xC000004A, "STATUS_SUSPEND_COUNT_EXCEEDED" },
+ { 0xC000004B, "STATUS_THREAD_IS_TERMINATING" },
+ { 0xC000004C, "STATUS_BAD_WORKING_SET_LIMIT" },
+ { 0xC000004D, "STATUS_INCOMPATIBLE_FILE_MAP" },
+ { 0xC000004E, "STATUS_SECTION_PROTECTION" },
+ { 0xC000004F, "STATUS_EAS_NOT_SUPPORTED" },
+ { 0xC0000050, "STATUS_EA_TOO_LARGE" },
+ { 0xC0000051, "STATUS_NONEXISTENT_EA_ENTRY" },
+ { 0xC0000052, "STATUS_NO_EAS_ON_FILE" },
+ { 0xC0000053, "STATUS_EA_CORRUPT_ERROR" },
+ { 0xC0000054, "STATUS_FILE_LOCK_CONFLICT" },
+ { 0xC0000055, "STATUS_LOCK_NOT_GRANTED" },
+ { 0xC0000056, "STATUS_DELETE_PENDING" },
+ { 0xC0000057, "STATUS_CTL_FILE_NOT_SUPPORTED" },
+ { 0xC0000058, "STATUS_UNKNOWN_REVISION" },
+ { 0xC0000059, "STATUS_REVISION_MISMATCH" },
+ { 0xC000005A, "STATUS_INVALID_OWNER" },
+ { 0xC000005B, "STATUS_INVALID_PRIMARY_GROUP" },
+ { 0xC000005C, "STATUS_NO_IMPERSONATION_TOKEN" },
+ { 0xC000005D, "STATUS_CANT_DISABLE_MANDATORY" },
+ { 0xC000005E, "STATUS_NO_LOGON_SERVERS" },
+ { 0xC000005F, "STATUS_NO_SUCH_LOGON_SESSION" },
+ { 0xC0000060, "STATUS_NO_SUCH_PRIVILEGE" },
+ { 0xC0000061, "STATUS_PRIVILEGE_NOT_HELD" },
+ { 0xC0000062, "STATUS_INVALID_ACCOUNT_NAME" },
+ { 0xC0000063, "STATUS_USER_EXISTS" },
+ { 0xC0000064, "STATUS_NO_SUCH_USER" },
+ { 0xC0000065, "STATUS_GROUP_EXISTS" },
+ { 0xC0000066, "STATUS_NO_SUCH_GROUP" },
+ { 0xC0000067, "STATUS_MEMBER_IN_GROUP" },
+ { 0xC0000068, "STATUS_MEMBER_NOT_IN_GROUP" },
+ { 0xC0000069, "STATUS_LAST_ADMIN" },
+ { 0xC000006A, "STATUS_WRONG_PASSWORD" },
+ { 0xC000006B, "STATUS_ILL_FORMED_PASSWORD" },
+ { 0xC000006C, "STATUS_PASSWORD_RESTRICTION" },
+ { 0xC000006D, "STATUS_LOGON_FAILURE" },
+ { 0xC000006E, "STATUS_ACCOUNT_RESTRICTION" },
+ { 0xC000006F, "STATUS_INVALID_LOGON_HOURS" },
+ { 0xC0000070, "STATUS_INVALID_WORKSTATION" },
+ { 0xC0000071, "STATUS_PASSWORD_EXPIRED" },
+ { 0xC0000072, "STATUS_ACCOUNT_DISABLED" },
+ { 0xC0000073, "STATUS_NONE_MAPPED" },
+ { 0xC0000074, "STATUS_TOO_MANY_LUIDS_REQUESTED" },
+ { 0xC0000075, "STATUS_LUIDS_EXHAUSTED" },
+ { 0xC0000076, "STATUS_INVALID_SUB_AUTHORITY" },
+ { 0xC0000077, "STATUS_INVALID_ACL" },
+ { 0xC0000078, "STATUS_INVALID_SID" },
+ { 0xC0000079, "STATUS_INVALID_SECURITY_DESCR" },
+ { 0xC000007A, "STATUS_PROCEDURE_NOT_FOUND" },
+ { 0xC000007B, "STATUS_INVALID_IMAGE_FORMAT" },
+ { 0xC000007C, "STATUS_NO_TOKEN" },
+ { 0xC000007D, "STATUS_BAD_INHERITANCE_ACL" },
+ { 0xC000007E, "STATUS_RANGE_NOT_LOCKED" },
+ { 0xC000007F, "STATUS_DISK_FULL" },
+ { 0xC0000080, "STATUS_SERVER_DISABLED" },
+ { 0xC0000081, "STATUS_SERVER_NOT_DISABLED" },
+ { 0xC0000082, "STATUS_TOO_MANY_GUIDS_REQUESTED" },
+ { 0xC0000083, "STATUS_GUIDS_EXHAUSTED" },
+ { 0xC0000084, "STATUS_INVALID_ID_AUTHORITY" },
+ { 0xC0000085, "STATUS_AGENTS_EXHAUSTED" },
+ { 0xC0000086, "STATUS_INVALID_VOLUME_LABEL" },
+ { 0xC0000087, "STATUS_SECTION_NOT_EXTENDED" },
+ { 0xC0000088, "STATUS_NOT_MAPPED_DATA" },
+ { 0xC0000089, "STATUS_RESOURCE_DATA_NOT_FOUND" },
+ { 0xC000008A, "STATUS_RESOURCE_TYPE_NOT_FOUND" },
+ { 0xC000008B, "STATUS_RESOURCE_NAME_NOT_FOUND" },
+ { 0xC000008C, "STATUS_ARRAY_BOUNDS_EXCEEDED" },
+ { 0xC000008D, "STATUS_FLOAT_DENORMAL_OPERAND" },
+ { 0xC000008E, "STATUS_FLOAT_DIVIDE_BY_ZERO" },
+ { 0xC000008F, "STATUS_FLOAT_INEXACT_RESULT" },
+ { 0xC0000090, "STATUS_FLOAT_INVALID_OPERATION" },
+ { 0xC0000091, "STATUS_FLOAT_OVERFLOW" },
+ { 0xC0000092, "STATUS_FLOAT_STACK_CHECK" },
+ { 0xC0000093, "STATUS_FLOAT_UNDERFLOW" },
+ { 0xC0000094, "STATUS_INTEGER_DIVIDE_BY_ZERO" },
+ { 0xC0000095, "STATUS_INTEGER_OVERFLOW" },
+ { 0xC0000096, "STATUS_PRIVILEGED_INSTRUCTION" },
+ { 0xC0000097, "STATUS_TOO_MANY_PAGING_FILES" },
+ { 0xC0000098, "STATUS_FILE_INVALID" },
+ { 0xC0000099, "STATUS_ALLOTTED_SPACE_EXCEEDED" },
+ { 0xC000009A, "STATUS_INSUFFICIENT_RESOURCES" },
+ { 0xC000009B, "STATUS_DFS_EXIT_PATH_FOUND" },
+ { 0xC000009C, "STATUS_DEVICE_DATA_ERROR" },
+ { 0xC000009D, "STATUS_DEVICE_NOT_CONNECTED" },
+ { 0xC000009E, "STATUS_DEVICE_POWER_FAILURE" },
+ { 0xC000009F, "STATUS_FREE_VM_NOT_AT_BASE" },
+ { 0xC00000A0, "STATUS_MEMORY_NOT_ALLOCATED" },
+ { 0xC00000A1, "STATUS_WORKING_SET_QUOTA" },
+ { 0xC00000A2, "STATUS_MEDIA_WRITE_PROTECTED" },
+ { 0xC00000A3, "STATUS_DEVICE_NOT_READY" },
+ { 0xC00000A4, "STATUS_INVALID_GROUP_ATTRIBUTES" },
+ { 0xC00000A5, "STATUS_BAD_IMPERSONATION_LEVEL" },
+ { 0xC00000A6, "STATUS_CANT_OPEN_ANONYMOUS" },
+ { 0xC00000A7, "STATUS_BAD_VALIDATION_CLASS" },
+ { 0xC00000A8, "STATUS_BAD_TOKEN_TYPE" },
+ { 0xC00000A9, "STATUS_BAD_MASTER_BOOT_RECORD" },
+ { 0xC00000AA, "STATUS_INSTRUCTION_MISALIGNMENT" },
+ { 0xC00000AB, "STATUS_INSTANCE_NOT_AVAILABLE" },
+ { 0xC00000AC, "STATUS_PIPE_NOT_AVAILABLE" },
+ { 0xC00000AD, "STATUS_INVALID_PIPE_STATE" },
+ { 0xC00000AE, "STATUS_PIPE_BUSY" },
+ { 0xC00000AF, "STATUS_ILLEGAL_FUNCTION" },
+ { 0xC00000B0, "STATUS_PIPE_DISCONNECTED" },
+ { 0xC00000B1, "STATUS_PIPE_CLOSING" },
+ { 0xC00000B2, "STATUS_PIPE_CONNECTED" },
+ { 0xC00000B3, "STATUS_PIPE_LISTENING" },
+ { 0xC00000B4, "STATUS_INVALID_READ_MODE" },
+ { 0xC00000B5, "STATUS_IO_TIMEOUT" },
+ { 0xC00000B6, "STATUS_FILE_FORCED_CLOSED" },
+ { 0xC00000B7, "STATUS_PROFILING_NOT_STARTED" },
+ { 0xC00000B8, "STATUS_PROFILING_NOT_STOPPED" },
+ { 0xC00000B9, "STATUS_COULD_NOT_INTERPRET" },
+ { 0xC00000BA, "STATUS_FILE_IS_A_DIRECTORY" },
+ { 0xC00000BB, "STATUS_NOT_SUPPORTED" },
+ { 0xC00000BC, "STATUS_REMOTE_NOT_LISTENING" },
+ { 0xC00000BD, "STATUS_DUPLICATE_NAME" },
+ { 0xC00000BE, "STATUS_BAD_NETWORK_PATH" },
+ { 0xC00000BF, "STATUS_NETWORK_BUSY" },
+ { 0xC00000C0, "STATUS_DEVICE_DOES_NOT_EXIST" },
+ { 0xC00000C1, "STATUS_TOO_MANY_COMMANDS" },
+ { 0xC00000C2, "STATUS_ADAPTER_HARDWARE_ERROR" },
+ { 0xC00000C3, "STATUS_INVALID_NETWORK_RESPONSE" },
+ { 0xC00000C4, "STATUS_UNEXPECTED_NETWORK_ERROR" },
+ { 0xC00000C5, "STATUS_BAD_REMOTE_ADAPTER" },
+ { 0xC00000C6, "STATUS_PRINT_QUEUE_FULL" },
+ { 0xC00000C7, "STATUS_NO_SPOOL_SPACE" },
+ { 0xC00000C8, "STATUS_PRINT_CANCELLED" },
+ { 0xC00000C9, "STATUS_NETWORK_NAME_DELETED" },
+ { 0xC00000CA, "STATUS_NETWORK_ACCESS_DENIED" },
+ { 0xC00000CB, "STATUS_BAD_DEVICE_TYPE" },
+ { 0xC00000CC, "STATUS_BAD_NETWORK_NAME" },
+ { 0xC00000CD, "STATUS_TOO_MANY_NAMES" },
+ { 0xC00000CE, "STATUS_TOO_MANY_SESSIONS" },
+ { 0xC00000CF, "STATUS_SHARING_PAUSED" },
+ { 0xC00000D0, "STATUS_REQUEST_NOT_ACCEPTED" },
+ { 0xC00000D1, "STATUS_REDIRECTOR_PAUSED" },
+ { 0xC00000D2, "STATUS_NET_WRITE_FAULT" },
+ { 0xC00000D3, "STATUS_PROFILING_AT_LIMIT" },
+ { 0xC00000D4, "STATUS_NOT_SAME_DEVICE" },
+ { 0xC00000D5, "STATUS_FILE_RENAMED" },
+ { 0xC00000D6, "STATUS_VIRTUAL_CIRCUIT_CLOSED" },
+ { 0xC00000D7, "STATUS_NO_SECURITY_ON_OBJECT" },
+ { 0xC00000D8, "STATUS_CANT_WAIT" },
+ { 0xC00000D9, "STATUS_PIPE_EMPTY" },
+ { 0xC00000DA, "STATUS_CANT_ACCESS_DOMAIN_INFO" },
+ { 0xC00000DB, "STATUS_CANT_TERMINATE_SELF" },
+ { 0xC00000DC, "STATUS_INVALID_SERVER_STATE" },
+ { 0xC00000DD, "STATUS_INVALID_DOMAIN_STATE" },
+ { 0xC00000DE, "STATUS_INVALID_DOMAIN_ROLE" },
+ { 0xC00000DF, "STATUS_NO_SUCH_DOMAIN" },
+ { 0xC00000E0, "STATUS_DOMAIN_EXISTS" },
+ { 0xC00000E1, "STATUS_DOMAIN_LIMIT_EXCEEDED" },
+ { 0xC00000E2, "STATUS_OPLOCK_NOT_GRANTED" },
+ { 0xC00000E3, "STATUS_INVALID_OPLOCK_PROTOCOL" },
+ { 0xC00000E4, "STATUS_INTERNAL_DB_CORRUPTION" },
+ { 0xC00000E5, "STATUS_INTERNAL_ERROR" },
+ { 0xC00000E6, "STATUS_GENERIC_NOT_MAPPED" },
+ { 0xC00000E7, "STATUS_BAD_DESCRIPTOR_FORMAT" },
+ { 0xC00000E8, "STATUS_INVALID_USER_BUFFER" },
+ { 0xC00000E9, "STATUS_UNEXPECTED_IO_ERROR" },
+ { 0xC00000EA, "STATUS_UNEXPECTED_MM_CREATE_ERR" },
+ { 0xC00000EB, "STATUS_UNEXPECTED_MM_MAP_ERROR" },
+ { 0xC00000EC, "STATUS_UNEXPECTED_MM_EXTEND_ERR" },
+ { 0xC00000ED, "STATUS_NOT_LOGON_PROCESS" },
+ { 0xC00000EE, "STATUS_LOGON_SESSION_EXISTS" },
+ { 0xC00000EF, "STATUS_INVALID_PARAMETER_1" },
+ { 0xC00000F0, "STATUS_INVALID_PARAMETER_2" },
+ { 0xC00000F1, "STATUS_INVALID_PARAMETER_3" },
+ { 0xC00000F2, "STATUS_INVALID_PARAMETER_4" },
+ { 0xC00000F3, "STATUS_INVALID_PARAMETER_5" },
+ { 0xC00000F4, "STATUS_INVALID_PARAMETER_6" },
+ { 0xC00000F5, "STATUS_INVALID_PARAMETER_7" },
+ { 0xC00000F6, "STATUS_INVALID_PARAMETER_8" },
+ { 0xC00000F7, "STATUS_INVALID_PARAMETER_9" },
+ { 0xC00000F8, "STATUS_INVALID_PARAMETER_10" },
+ { 0xC00000F9, "STATUS_INVALID_PARAMETER_11" },
+ { 0xC00000FA, "STATUS_INVALID_PARAMETER_12" },
+ { 0xC00000FB, "STATUS_REDIRECTOR_NOT_STARTED" },
+ { 0xC00000FC, "STATUS_REDIRECTOR_STARTED" },
+ { 0xC00000FD, "STATUS_STACK_OVERFLOW" },
+ { 0xC00000FE, "STATUS_NO_SUCH_PACKAGE" },
+ { 0xC00000FF, "STATUS_BAD_FUNCTION_TABLE" },
+ { 0xC0000100, "STATUS_VARIABLE_NOT_FOUND" },
+ { 0xC0000101, "STATUS_DIRECTORY_NOT_EMPTY" },
+ { 0xC0000102, "STATUS_FILE_CORRUPT_ERROR" },
+ { 0xC0000103, "STATUS_NOT_A_DIRECTORY" },
+ { 0xC0000104, "STATUS_BAD_LOGON_SESSION_STATE" },
+ { 0xC0000105, "STATUS_LOGON_SESSION_COLLISION" },
+ { 0xC0000106, "STATUS_NAME_TOO_LONG" },
+ { 0xC0000107, "STATUS_FILES_OPEN" },
+ { 0xC0000108, "STATUS_CONNECTION_IN_USE" },
+ { 0xC0000109, "STATUS_MESSAGE_NOT_FOUND" },
+ { 0xC000010A, "STATUS_PROCESS_IS_TERMINATING" },
+ { 0xC000010B, "STATUS_INVALID_LOGON_TYPE" },
+ { 0xC000010C, "STATUS_NO_GUID_TRANSLATION" },
+ { 0xC000010D, "STATUS_CANNOT_IMPERSONATE" },
+ { 0xC000010E, "STATUS_IMAGE_ALREADY_LOADED" },
+ { 0xC000010F, "STATUS_ABIOS_NOT_PRESENT" },
+ { 0xC0000110, "STATUS_ABIOS_LID_NOT_EXIST" },
+ { 0xC0000111, "STATUS_ABIOS_LID_ALREADY_OWNED" },
+ { 0xC0000112, "STATUS_ABIOS_NOT_LID_OWNER" },
+ { 0xC0000113, "STATUS_ABIOS_INVALID_COMMAND" },
+ { 0xC0000114, "STATUS_ABIOS_INVALID_LID" },
+ { 0xC0000115, "STATUS_ABIOS_SELECTOR_NOT_AVAILABLE" },
+ { 0xC0000116, "STATUS_ABIOS_INVALID_SELECTOR" },
+ { 0xC0000117, "STATUS_NO_LDT" },
+ { 0xC0000118, "STATUS_INVALID_LDT_SIZE" },
+ { 0xC0000119, "STATUS_INVALID_LDT_OFFSET" },
+ { 0xC000011A, "STATUS_INVALID_LDT_DESCRIPTOR" },
+ { 0xC000011B, "STATUS_INVALID_IMAGE_NE_FORMAT" },
+ { 0xC000011C, "STATUS_RXACT_INVALID_STATE" },
+ { 0xC000011D, "STATUS_RXACT_COMMIT_FAILURE" },
+ { 0xC000011E, "STATUS_MAPPED_FILE_SIZE_ZERO" },
+ { 0xC000011F, "STATUS_TOO_MANY_OPENED_FILES" },
+ { 0xC0000120, "STATUS_CANCELLED" },
+ { 0xC0000121, "STATUS_CANNOT_DELETE" },
+ { 0xC0000122, "STATUS_INVALID_COMPUTER_NAME" },
+ { 0xC0000123, "STATUS_FILE_DELETED" },
+ { 0xC0000124, "STATUS_SPECIAL_ACCOUNT" },
+ { 0xC0000125, "STATUS_SPECIAL_GROUP" },
+ { 0xC0000126, "STATUS_SPECIAL_USER" },
+ { 0xC0000127, "STATUS_MEMBERS_PRIMARY_GROUP" },
+ { 0xC0000128, "STATUS_FILE_CLOSED" },
+ { 0xC0000129, "STATUS_TOO_MANY_THREADS" },
+ { 0xC000012A, "STATUS_THREAD_NOT_IN_PROCESS" },
+ { 0xC000012B, "STATUS_TOKEN_ALREADY_IN_USE" },
+ { 0xC000012C, "STATUS_PAGEFILE_QUOTA_EXCEEDED" },
+ { 0xC000012D, "STATUS_COMMITMENT_LIMIT" },
+ { 0xC000012E, "STATUS_INVALID_IMAGE_LE_FORMAT" },
+ { 0xC000012F, "STATUS_INVALID_IMAGE_NOT_MZ" },
+ { 0xC0000130, "STATUS_INVALID_IMAGE_PROTECT" },
+ { 0xC0000131, "STATUS_INVALID_IMAGE_WIN_16" },
+ { 0xC0000132, "STATUS_LOGON_SERVER_CONFLICT" },
+ { 0xC0000133, "STATUS_TIME_DIFFERENCE_AT_DC" },
+ { 0xC0000134, "STATUS_SYNCHRONIZATION_REQUIRED" },
+ { 0xC0000135, "STATUS_DLL_NOT_FOUND" },
+ { 0xC0000136, "STATUS_OPEN_FAILED" },
+ { 0xC0000137, "STATUS_IO_PRIVILEGE_FAILED" },
+ { 0xC0000138, "STATUS_ORDINAL_NOT_FOUND" },
+ { 0xC0000139, "STATUS_ENTRYPOINT_NOT_FOUND" },
+ { 0xC000013A, "STATUS_CONTROL_C_EXIT" },
+ { 0xC000013B, "STATUS_LOCAL_DISCONNECT" },
+ { 0xC000013C, "STATUS_REMOTE_DISCONNECT" },
+ { 0xC000013D, "STATUS_REMOTE_RESOURCES" },
+ { 0xC000013E, "STATUS_LINK_FAILED" },
+ { 0xC000013F, "STATUS_LINK_TIMEOUT" },
+ { 0xC0000140, "STATUS_INVALID_CONNECTION" },
+ { 0xC0000141, "STATUS_INVALID_ADDRESS" },
+ { 0xC0000142, "STATUS_DLL_INIT_FAILED" },
+ { 0xC0000143, "STATUS_MISSING_SYSTEMFILE" },
+ { 0xC0000144, "STATUS_UNHANDLED_EXCEPTION" },
+ { 0xC0000145, "STATUS_APP_INIT_FAILURE" },
+ { 0xC0000146, "STATUS_PAGEFILE_CREATE_FAILED" },
+ { 0xC0000147, "STATUS_NO_PAGEFILE" },
+ { 0xC0000148, "STATUS_INVALID_LEVEL" },
+ { 0xC0000149, "STATUS_WRONG_PASSWORD_CORE" },
+ { 0xC000014A, "STATUS_ILLEGAL_FLOAT_CONTEXT" },
+ { 0xC000014B, "STATUS_PIPE_BROKEN" },
+ { 0xC000014C, "STATUS_REGISTRY_CORRUPT" },
+ { 0xC000014D, "STATUS_REGISTRY_IO_FAILED" },
+ { 0xC000014E, "STATUS_NO_EVENT_PAIR" },
+ { 0xC000014F, "STATUS_UNRECOGNIZED_VOLUME" },
+ { 0xC0000150, "STATUS_SERIAL_NO_DEVICE_INITED" },
+ { 0xC0000151, "STATUS_NO_SUCH_ALIAS" },
+ { 0xC0000152, "STATUS_MEMBER_NOT_IN_ALIAS" },
+ { 0xC0000153, "STATUS_MEMBER_IN_ALIAS" },
+ { 0xC0000154, "STATUS_ALIAS_EXISTS" },
+ { 0xC0000155, "STATUS_LOGON_NOT_GRANTED" },
+ { 0xC0000156, "STATUS_TOO_MANY_SECRETS" },
+ { 0xC0000157, "STATUS_SECRET_TOO_LONG" },
+ { 0xC0000158, "STATUS_INTERNAL_DB_ERROR" },
+ { 0xC0000159, "STATUS_FULLSCREEN_MODE" },
+ { 0xC000015A, "STATUS_TOO_MANY_CONTEXT_IDS" },
+ { 0xC000015B, "STATUS_LOGON_TYPE_NOT_GRANTED" },
+ { 0xC000015C, "STATUS_NOT_REGISTRY_FILE" },
+ { 0xC000015D, "STATUS_NT_CROSS_ENCRYPTION_REQUIRED" },
+ { 0xC000015E, "STATUS_DOMAIN_CTRLR_CONFIG_ERROR" },
+ { 0xC000015F, "STATUS_FT_MISSING_MEMBER" },
+ { 0xC0000160, "STATUS_ILL_FORMED_SERVICE_ENTRY" },
+ { 0xC0000161, "STATUS_ILLEGAL_CHARACTER" },
+ { 0xC0000162, "STATUS_UNMAPPABLE_CHARACTER" },
+ { 0xC0000163, "STATUS_UNDEFINED_CHARACTER" },
+ { 0xC0000164, "STATUS_FLOPPY_VOLUME" },
+ { 0xC0000165, "STATUS_FLOPPY_ID_MARK_NOT_FOUND" },
+ { 0xC0000166, "STATUS_FLOPPY_WRONG_CYLINDER" },
+ { 0xC0000167, "STATUS_FLOPPY_UNKNOWN_ERROR" },
+ { 0xC0000168, "STATUS_FLOPPY_BAD_REGISTERS" },
+ { 0xC0000169, "STATUS_DISK_RECALIBRATE_FAILED" },
+ { 0xC000016A, "STATUS_DISK_OPERATION_FAILED" },
+ { 0xC000016B, "STATUS_DISK_RESET_FAILED" },
+ { 0xC000016C, "STATUS_SHARED_IRQ_BUSY" },
+ { 0xC000016D, "STATUS_FT_ORPHANING" },
+ { 0xC000016E, "STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT" },
+ { 0xC0000172, "STATUS_PARTITION_FAILURE" },
+ { 0xC0000173, "STATUS_INVALID_BLOCK_LENGTH" },
+ { 0xC0000174, "STATUS_DEVICE_NOT_PARTITIONED" },
+ { 0xC0000175, "STATUS_UNABLE_TO_LOCK_MEDIA" },
+ { 0xC0000176, "STATUS_UNABLE_TO_UNLOAD_MEDIA" },
+ { 0xC0000177, "STATUS_EOM_OVERFLOW" },
+ { 0xC0000178, "STATUS_NO_MEDIA" },
+ { 0xC000017A, "STATUS_NO_SUCH_MEMBER" },
+ { 0xC000017B, "STATUS_INVALID_MEMBER" },
+ { 0xC000017C, "STATUS_KEY_DELETED" },
+ { 0xC000017D, "STATUS_NO_LOG_SPACE" },
+ { 0xC000017E, "STATUS_TOO_MANY_SIDS" },
+ { 0xC000017F, "STATUS_LM_CROSS_ENCRYPTION_REQUIRED" },
+ { 0xC0000180, "STATUS_KEY_HAS_CHILDREN" },
+ { 0xC0000181, "STATUS_CHILD_MUST_BE_VOLATILE" },
+ { 0xC0000182, "STATUS_DEVICE_CONFIGURATION_ERROR" },
+ { 0xC0000183, "STATUS_DRIVER_INTERNAL_ERROR" },
+ { 0xC0000184, "STATUS_INVALID_DEVICE_STATE" },
+ { 0xC0000185, "STATUS_IO_DEVICE_ERROR" },
+ { 0xC0000186, "STATUS_DEVICE_PROTOCOL_ERROR" },
+ { 0xC0000187, "STATUS_BACKUP_CONTROLLER" },
+ { 0xC0000188, "STATUS_LOG_FILE_FULL" },
+ { 0xC0000189, "STATUS_TOO_LATE" },
+ { 0xC000018A, "STATUS_NO_TRUST_LSA_SECRET" },
+ { 0xC000018B, "STATUS_NO_TRUST_SAM_ACCOUNT" },
+ { 0xC000018C, "STATUS_TRUSTED_DOMAIN_FAILURE" },
+ { 0xC000018D, "STATUS_TRUSTED_RELATIONSHIP_FAILURE" },
+ { 0xC000018E, "STATUS_EVENTLOG_FILE_CORRUPT" },
+ { 0xC000018F, "STATUS_EVENTLOG_CANT_START" },
+ { 0xC0000190, "STATUS_TRUST_FAILURE" },
+ { 0xC0000191, "STATUS_MUTANT_LIMIT_EXCEEDED" },
+ { 0xC0000192, "STATUS_NETLOGON_NOT_STARTED" },
+ { 0xC0000193, "STATUS_ACCOUNT_EXPIRED" },
+ { 0xC0000194, "STATUS_POSSIBLE_DEADLOCK" },
+ { 0xC0000195, "STATUS_NETWORK_CREDENTIAL_CONFLICT" },
+ { 0xC0000196, "STATUS_REMOTE_SESSION_LIMIT" },
+ { 0xC0000197, "STATUS_EVENTLOG_FILE_CHANGED" },
+ { 0xC0000198, "STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT" },
+ { 0xC0000199, "STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT" },
+ { 0xC000019A, "STATUS_NOLOGON_SERVER_TRUST_ACCOUNT" },
+ { 0xC000019B, "STATUS_DOMAIN_TRUST_INCONSISTENT" },
+ { 0xC000019C, "STATUS_FS_DRIVER_REQUIRED" },
+ { 0xC0000202, "STATUS_NO_USER_SESSION_KEY" },
+ { 0xC0000203, "STATUS_USER_SESSION_DELETED" },
+ { 0xC0000204, "STATUS_RESOURCE_LANG_NOT_FOUND" },
+ { 0xC0000205, "STATUS_INSUFF_SERVER_RESOURCES" },
+ { 0xC0000206, "STATUS_INVALID_BUFFER_SIZE" },
+ { 0xC0000207, "STATUS_INVALID_ADDRESS_COMPONENT" },
+ { 0xC0000208, "STATUS_INVALID_ADDRESS_WILDCARD" },
+ { 0xC0000209, "STATUS_TOO_MANY_ADDRESSES" },
+ { 0xC000020A, "STATUS_ADDRESS_ALREADY_EXISTS" },
+ { 0xC000020B, "STATUS_ADDRESS_CLOSED" },
+ { 0xC000020C, "STATUS_CONNECTION_DISCONNECTED" },
+ { 0xC000020D, "STATUS_CONNECTION_RESET" },
+ { 0xC000020E, "STATUS_TOO_MANY_NODES" },
+ { 0xC000020F, "STATUS_TRANSACTION_ABORTED" },
+ { 0xC0000210, "STATUS_TRANSACTION_TIMED_OUT" },
+ { 0xC0000211, "STATUS_TRANSACTION_NO_RELEASE" },
+ { 0xC0000212, "STATUS_TRANSACTION_NO_MATCH" },
+ { 0xC0000213, "STATUS_TRANSACTION_RESPONDED" },
+ { 0xC0000214, "STATUS_TRANSACTION_INVALID_ID" },
+ { 0xC0000215, "STATUS_TRANSACTION_INVALID_TYPE" },
+ { 0xC0000216, "STATUS_NOT_SERVER_SESSION" },
+ { 0xC0000217, "STATUS_NOT_CLIENT_SESSION" },
+ { 0xC0000218, "STATUS_CANNOT_LOAD_REGISTRY_FILE" },
+ { 0xC0000219, "STATUS_DEBUG_ATTACH_FAILED" },
+ { 0xC000021A, "STATUS_SYSTEM_PROCESS_TERMINATED" },
+ { 0xC000021B, "STATUS_DATA_NOT_ACCEPTED" },
+ { 0xC000021C, "STATUS_NO_BROWSER_SERVERS_FOUND" },
+ { 0xC000021D, "STATUS_VDM_HARD_ERROR" },
+ { 0xC000021E, "STATUS_DRIVER_CANCEL_TIMEOUT" },
+ { 0xC000021F, "STATUS_REPLY_MESSAGE_MISMATCH" },
+ { 0xC0000220, "STATUS_MAPPED_ALIGNMENT" },
+ { 0xC0000221, "STATUS_IMAGE_CHECKSUM_MISMATCH" },
+ { 0xC0000222, "STATUS_LOST_WRITEBEHIND_DATA" },
+ { 0xC0000223, "STATUS_CLIENT_SERVER_PARAMETERS_INVALID" },
+ { 0xC0000224, "STATUS_PASSWORD_MUST_CHANGE" },
+ { 0xC0000225, "STATUS_NOT_FOUND" },
+ { 0xC0000226, "STATUS_NOT_TINY_STREAM" },
+ { 0xC0000227, "STATUS_RECOVERY_FAILURE" },
+ { 0xC0000228, "STATUS_STACK_OVERFLOW_READ" },
+ { 0xC0000229, "STATUS_FAIL_CHECK" },
+ { 0xC000022A, "STATUS_DUPLICATE_OBJECTID" },
+ { 0xC000022B, "STATUS_OBJECTID_EXISTS" },
+ { 0xC000022C, "STATUS_CONVERT_TO_LARGE" },
+ { 0xC000022D, "STATUS_RETRY" },
+ { 0xC000022E, "STATUS_FOUND_OUT_OF_SCOPE" },
+ { 0xC000022F, "STATUS_ALLOCATE_BUCKET" },
+ { 0xC0000230, "STATUS_PROPSET_NOT_FOUND" },
+ { 0xC0000231, "STATUS_MARSHALL_OVERFLOW" },
+ { 0xC0000232, "STATUS_INVALID_VARIANT" },
+ { 0xC0000233, "STATUS_DOMAIN_CONTROLLER_NOT_FOUND" },
+ { 0xC0000234, "STATUS_ACCOUNT_LOCKED_OUT" },
+ { 0xC0000235, "STATUS_HANDLE_NOT_CLOSABLE" },
+ { 0xC0000236, "STATUS_CONNECTION_REFUSED" },
+ { 0xC0000237, "STATUS_GRACEFUL_DISCONNECT" },
+ { 0xC0000238, "STATUS_ADDRESS_ALREADY_ASSOCIATED" },
+ { 0xC0000239, "STATUS_ADDRESS_NOT_ASSOCIATED" },
+ { 0xC000023A, "STATUS_CONNECTION_INVALID" },
+ { 0xC000023B, "STATUS_CONNECTION_ACTIVE" },
+ { 0xC000023C, "STATUS_NETWORK_UNREACHABLE" },
+ { 0xC000023D, "STATUS_HOST_UNREACHABLE" },
+ { 0xC000023E, "STATUS_PROTOCOL_UNREACHABLE" },
+ { 0xC000023F, "STATUS_PORT_UNREACHABLE" },
+ { 0xC0000240, "STATUS_REQUEST_ABORTED" },
+ { 0xC0000241, "STATUS_CONNECTION_ABORTED" },
+ { 0xC0000242, "STATUS_BAD_COMPRESSION_BUFFER" },
+ { 0xC0000243, "STATUS_USER_MAPPED_FILE" },
+ { 0xC0000244, "STATUS_AUDIT_FAILED" },
+ { 0xC0000245, "STATUS_TIMER_RESOLUTION_NOT_SET" },
+ { 0xC0000246, "STATUS_CONNECTION_COUNT_LIMIT" },
+ { 0xC0000247, "STATUS_LOGIN_TIME_RESTRICTION" },
+ { 0xC0000248, "STATUS_LOGIN_WKSTA_RESTRICTION" },
+ { 0xC0000249, "STATUS_IMAGE_MP_UP_MISMATCH" },
+ { 0xC0000250, "STATUS_INSUFFICIENT_LOGON_INFO" },
+ { 0xC0000251, "STATUS_BAD_DLL_ENTRYPOINT" },
+ { 0xC0000252, "STATUS_BAD_SERVICE_ENTRYPOINT" },
+ { 0xC0000253, "STATUS_LPC_REPLY_LOST" },
+ { 0xC0000254, "STATUS_IP_ADDRESS_CONFLICT1" },
+ { 0xC0000255, "STATUS_IP_ADDRESS_CONFLICT2" },
+ { 0xC0000256, "STATUS_REGISTRY_QUOTA_LIMIT" },
+ { 0xC0000257, "STATUS_PATH_NOT_COVERED" },
+ { 0xC0000258, "STATUS_NO_CALLBACK_ACTIVE" },
+ { 0xC0000259, "STATUS_LICENSE_QUOTA_EXCEEDED" },
+ { 0xC000025A, "STATUS_PWD_TOO_SHORT" },
+ { 0xC000025B, "STATUS_PWD_TOO_RECENT" },
+ { 0xC000025C, "STATUS_PWD_HISTORY_CONFLICT" },
+ { 0xC000025E, "STATUS_PLUGPLAY_NO_DEVICE" },
+ { 0xC000025F, "STATUS_UNSUPPORTED_COMPRESSION" },
+ { 0xC0000260, "STATUS_INVALID_HW_PROFILE" },
+ { 0xC0000261, "STATUS_INVALID_PLUGPLAY_DEVICE_PATH" },
+ { 0xC0000262, "STATUS_DRIVER_ORDINAL_NOT_FOUND" },
+ { 0xC0000263, "STATUS_DRIVER_ENTRYPOINT_NOT_FOUND" },
+ { 0xC0000264, "STATUS_RESOURCE_NOT_OWNED" },
+ { 0xC0000265, "STATUS_TOO_MANY_LINKS" },
+ { 0xC0000266, "STATUS_QUOTA_LIST_INCONSISTENT" },
+ { 0xC0000267, "STATUS_FILE_IS_OFFLINE" },
+ { 0xC0000268, "STATUS_EVALUATION_EXPIRATION" },
+ { 0xC0000269, "STATUS_ILLEGAL_DLL_RELOCATION" },
+ { 0xC000026A, "STATUS_LICENSE_VIOLATION" },
+ { 0xC000026B, "STATUS_DLL_INIT_FAILED_LOGOFF" },
+ { 0xC000026C, "STATUS_DRIVER_UNABLE_TO_LOAD" },
+ { 0xC000026D, "STATUS_DFS_UNAVAILABLE" },
+ { 0xC000026E, "STATUS_VOLUME_DISMOUNTED" },
+ { 0xC000026F, "STATUS_WX86_INTERNAL_ERROR" },
+ { 0xC0000270, "STATUS_WX86_FLOAT_STACK_CHECK" },
+ { 0xC0000271, "STATUS_VALIDATE_CONTINUE" },
+ { 0xC0000272, "STATUS_NO_MATCH" },
+ { 0xC0000273, "STATUS_NO_MORE_MATCHES" },
+ { 0xC0000275, "STATUS_NOT_A_REPARSE_POINT" },
+ { 0xC0000276, "STATUS_IO_REPARSE_TAG_INVALID" },
+ { 0xC0000277, "STATUS_IO_REPARSE_TAG_MISMATCH" },
+ { 0xC0000278, "STATUS_IO_REPARSE_DATA_INVALID" },
+ { 0xC0000279, "STATUS_IO_REPARSE_TAG_NOT_HANDLED" },
+ { 0xC0000280, "STATUS_REPARSE_POINT_NOT_RESOLVED" },
+ { 0xC0000281, "STATUS_DIRECTORY_IS_A_REPARSE_POINT" },
+ { 0xC0000282, "STATUS_RANGE_LIST_CONFLICT" },
+ { 0xC0000283, "STATUS_SOURCE_ELEMENT_EMPTY" },
+ { 0xC0000284, "STATUS_DESTINATION_ELEMENT_FULL" },
+ { 0xC0000285, "STATUS_ILLEGAL_ELEMENT_ADDRESS" },
+ { 0xC0000286, "STATUS_MAGAZINE_NOT_PRESENT" },
+ { 0xC0000287, "STATUS_REINITIALIZATION_NEEDED" },
+ { 0x80000288, "STATUS_DEVICE_REQUIRES_CLEANING" },
+ { 0x80000289, "STATUS_DEVICE_DOOR_OPEN" },
+ { 0xC000028A, "STATUS_ENCRYPTION_FAILED" },
+ { 0xC000028B, "STATUS_DECRYPTION_FAILED" },
+ { 0xC000028C, "STATUS_RANGE_NOT_FOUND" },
+ { 0xC000028D, "STATUS_NO_RECOVERY_POLICY" },
+ { 0xC000028E, "STATUS_NO_EFS" },
+ { 0xC000028F, "STATUS_WRONG_EFS" },
+ { 0xC0000290, "STATUS_NO_USER_KEYS" },
+ { 0xC0000291, "STATUS_FILE_NOT_ENCRYPTED" },
+ { 0xC0000292, "STATUS_NOT_EXPORT_FORMAT" },
+ { 0xC0000293, "STATUS_FILE_ENCRYPTED" },
+ { 0x40000294, "STATUS_WAKE_SYSTEM" },
+ { 0xC0000295, "STATUS_WMI_GUID_NOT_FOUND" },
+ { 0xC0000296, "STATUS_WMI_INSTANCE_NOT_FOUND" },
+ { 0xC0000297, "STATUS_WMI_ITEMID_NOT_FOUND" },
+ { 0xC0000298, "STATUS_WMI_TRY_AGAIN" },
+ { 0xC0000299, "STATUS_SHARED_POLICY" },
+ { 0xC000029A, "STATUS_POLICY_OBJECT_NOT_FOUND" },
+ { 0xC000029B, "STATUS_POLICY_ONLY_IN_DS" },
+ { 0xC000029C, "STATUS_VOLUME_NOT_UPGRADED" },
+ { 0xC000029D, "STATUS_REMOTE_STORAGE_NOT_ACTIVE" },
+ { 0xC000029E, "STATUS_REMOTE_STORAGE_MEDIA_ERROR" },
+ { 0xC000029F, "STATUS_NO_TRACKING_SERVICE" },
+ { 0xC00002A0, "STATUS_SERVER_SID_MISMATCH" },
+ { 0xC00002A1, "STATUS_DS_NO_ATTRIBUTE_OR_VALUE" },
+ { 0xC00002A2, "STATUS_DS_INVALID_ATTRIBUTE_SYNTAX" },
+ { 0xC00002A3, "STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED" },
+ { 0xC00002A4, "STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS" },
+ { 0xC00002A5, "STATUS_DS_BUSY" },
+ { 0xC00002A6, "STATUS_DS_UNAVAILABLE" },
+ { 0xC00002A7, "STATUS_DS_NO_RIDS_ALLOCATED" },
+ { 0xC00002A8, "STATUS_DS_NO_MORE_RIDS" },
+ { 0xC00002A9, "STATUS_DS_INCORRECT_ROLE_OWNER" },
+ { 0xC00002AA, "STATUS_DS_RIDMGR_INIT_ERROR" },
+ { 0xC00002AB, "STATUS_DS_OBJ_CLASS_VIOLATION" },
+ { 0xC00002AC, "STATUS_DS_CANT_ON_NON_LEAF" },
+ { 0xC00002AD, "STATUS_DS_CANT_ON_RDN" },
+ { 0xC00002AE, "STATUS_DS_CANT_MOD_OBJ_CLASS" },
+ { 0xC00002AF, "STATUS_DS_CROSS_DOM_MOVE_FAILED" },
+ { 0xC00002B0, "STATUS_DS_GC_NOT_AVAILABLE" },
+ { 0xC00002B1, "STATUS_DIRECTORY_SERVICE_REQUIRED" },
+ { 0xC00002B2, "STATUS_REPARSE_ATTRIBUTE_CONFLICT" },
+ { 0xC00002B3, "STATUS_CANT_ENABLE_DENY_ONLY" },
+ { 0xC00002B4, "STATUS_FLOAT_MULTIPLE_FAULTS" },
+ { 0xC00002B5, "STATUS_FLOAT_MULTIPLE_TRAPS" },
+ { 0xC00002B6, "STATUS_DEVICE_REMOVED" },
+ { 0xC00002B7, "STATUS_JOURNAL_DELETE_IN_PROGRESS" },
+ { 0xC00002B8, "STATUS_JOURNAL_NOT_ACTIVE" },
+ { 0xC00002B9, "STATUS_NOINTERFACE" },
+ { 0xC00002C1, "STATUS_DS_ADMIN_LIMIT_EXCEEDED" },
+ { 0xC00002C2, "STATUS_DRIVER_FAILED_SLEEP" },
+ { 0xC00002C3, "STATUS_MUTUAL_AUTHENTICATION_FAILED" },
+ { 0xC00002C4, "STATUS_CORRUPT_SYSTEM_FILE" },
+ { 0xC00002C5, "STATUS_DATATYPE_MISALIGNMENT_ERROR" },
+ { 0xC00002C6, "STATUS_WMI_READ_ONLY" },
+ { 0xC00002C7, "STATUS_WMI_SET_FAILURE" },
+ { 0xC00002C8, "STATUS_COMMITMENT_MINIMUM" },
+ { 0xC00002C9, "STATUS_REG_NAT_CONSUMPTION" },
+ { 0xC00002CA, "STATUS_TRANSPORT_FULL" },
+ { 0xC00002CB, "STATUS_DS_SAM_INIT_FAILURE" },
+ { 0xC00002CC, "STATUS_ONLY_IF_CONNECTED" },
+ { 0xC00002CD, "STATUS_DS_SENSITIVE_GROUP_VIOLATION" },
+ { 0xC00002CE, "STATUS_PNP_RESTART_ENUMERATION" },
+ { 0xC00002CF, "STATUS_JOURNAL_ENTRY_DELETED" },
+ { 0xC00002D0, "STATUS_DS_CANT_MOD_PRIMARYGROUPID" },
+ { 0xC00002D1, "STATUS_SYSTEM_IMAGE_BAD_SIGNATURE" },
+ { 0xC00002D2, "STATUS_PNP_REBOOT_REQUIRED" },
+ { 0xC00002D3, "STATUS_POWER_STATE_INVALID" },
+ { 0xC00002D4, "STATUS_DS_INVALID_GROUP_TYPE" },
+ { 0xC00002D5, "STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN" },
+ { 0xC00002D6, "STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN" },
+ { 0xC00002D7, "STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER" },
+ { 0xC00002D8, "STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER" },
+ { 0xC00002D9, "STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER" },
+ { 0xC00002DA, "STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER" },
+ { 0xC00002DB, "STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER" },
+ { 0xC00002DC, "STATUS_DS_HAVE_PRIMARY_MEMBERS" },
+ { 0xC00002DD, "STATUS_WMI_NOT_SUPPORTED" },
+ { 0xC00002DE, "STATUS_INSUFFICIENT_POWER" },
+ { 0xC00002DF, "STATUS_SAM_NEED_BOOTKEY_PASSWORD" },
+ { 0xC00002E0, "STATUS_SAM_NEED_BOOTKEY_FLOPPY" },
+ { 0xC00002E1, "STATUS_DS_CANT_START" },
+ { 0xC00002E2, "STATUS_DS_INIT_FAILURE" },
+ { 0xC00002E3, "STATUS_SAM_INIT_FAILURE" },
+ { 0xC00002E4, "STATUS_DS_GC_REQUIRED" },
+ { 0xC00002E5, "STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY" },
+ { 0xC00002E6, "STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS" },
+ { 0xC00002E7, "STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED" },
+ { 0xC00002E8, "STATUS_MULTIPLE_FAULT_VIOLATION" },
+ { 0xC0000300, "STATUS_NOT_SUPPORTED_ON_SBS" },
+ { 0xC0009898, "STATUS_WOW_ASSERTION" },
+ { 0xC0020001, "RPC_NT_INVALID_STRING_BINDING" },
+ { 0xC0020002, "RPC_NT_WRONG_KIND_OF_BINDING" },
+ { 0xC0020003, "RPC_NT_INVALID_BINDING" },
+ { 0xC0020004, "RPC_NT_PROTSEQ_NOT_SUPPORTED" },
+ { 0xC0020005, "RPC_NT_INVALID_RPC_PROTSEQ" },
+ { 0xC0020006, "RPC_NT_INVALID_STRING_UUID" },
+ { 0xC0020007, "RPC_NT_INVALID_ENDPOINT_FORMAT" },
+ { 0xC0020008, "RPC_NT_INVALID_NET_ADDR" },
+ { 0xC0020009, "RPC_NT_NO_ENDPOINT_FOUND" },
+ { 0xC002000A, "RPC_NT_INVALID_TIMEOUT" },
+ { 0xC002000B, "RPC_NT_OBJECT_NOT_FOUND" },
+ { 0xC002000C, "RPC_NT_ALREADY_REGISTERED" },
+ { 0xC002000D, "RPC_NT_TYPE_ALREADY_REGISTERED" },
+ { 0xC002000E, "RPC_NT_ALREADY_LISTENING" },
+ { 0xC002000F, "RPC_NT_NO_PROTSEQS_REGISTERED" },
+ { 0xC0020010, "RPC_NT_NOT_LISTENING" },
+ { 0xC0020011, "RPC_NT_UNKNOWN_MGR_TYPE" },
+ { 0xC0020012, "RPC_NT_UNKNOWN_IF" },
+ { 0xC0020013, "RPC_NT_NO_BINDINGS" },
+ { 0xC0020014, "RPC_NT_NO_PROTSEQS" },
+ { 0xC0020015, "RPC_NT_CANT_CREATE_ENDPOINT" },
+ { 0xC0020016, "RPC_NT_OUT_OF_RESOURCES" },
+ { 0xC0020017, "RPC_NT_SERVER_UNAVAILABLE" },
+ { 0xC0020018, "RPC_NT_SERVER_TOO_BUSY" },
+ { 0xC0020019, "RPC_NT_INVALID_NETWORK_OPTIONS" },
+ { 0xC002001A, "RPC_NT_NO_CALL_ACTIVE" },
+ { 0xC002001B, "RPC_NT_CALL_FAILED" },
+ { 0xC002001C, "RPC_NT_CALL_FAILED_DNE" },
+ { 0xC002001D, "RPC_NT_PROTOCOL_ERROR" },
+ { 0xC002001F, "RPC_NT_UNSUPPORTED_TRANS_SYN" },
+ { 0xC0020021, "RPC_NT_UNSUPPORTED_TYPE" },
+ { 0xC0020022, "RPC_NT_INVALID_TAG" },
+ { 0xC0020023, "RPC_NT_INVALID_BOUND" },
+ { 0xC0020024, "RPC_NT_NO_ENTRY_NAME" },
+ { 0xC0020025, "RPC_NT_INVALID_NAME_SYNTAX" },
+ { 0xC0020026, "RPC_NT_UNSUPPORTED_NAME_SYNTAX" },
+ { 0xC0020028, "RPC_NT_UUID_NO_ADDRESS" },
+ { 0xC0020029, "RPC_NT_DUPLICATE_ENDPOINT" },
+ { 0xC002002A, "RPC_NT_UNKNOWN_AUTHN_TYPE" },
+ { 0xC002002B, "RPC_NT_MAX_CALLS_TOO_SMALL" },
+ { 0xC002002C, "RPC_NT_STRING_TOO_LONG" },
+ { 0xC002002D, "RPC_NT_PROTSEQ_NOT_FOUND" },
+ { 0xC002002E, "RPC_NT_PROCNUM_OUT_OF_RANGE" },
+ { 0xC002002F, "RPC_NT_BINDING_HAS_NO_AUTH" },
+ { 0xC0020030, "RPC_NT_UNKNOWN_AUTHN_SERVICE" },
+ { 0xC0020031, "RPC_NT_UNKNOWN_AUTHN_LEVEL" },
+ { 0xC0020032, "RPC_NT_INVALID_AUTH_IDENTITY" },
+ { 0xC0020033, "RPC_NT_UNKNOWN_AUTHZ_SERVICE" },
+ { 0xC0020034, "EPT_NT_INVALID_ENTRY" },
+ { 0xC0020035, "EPT_NT_CANT_PERFORM_OP" },
+ { 0xC0020036, "EPT_NT_NOT_REGISTERED" },
+ { 0xC0020037, "RPC_NT_NOTHING_TO_EXPORT" },
+ { 0xC0020038, "RPC_NT_INCOMPLETE_NAME" },
+ { 0xC0020039, "RPC_NT_INVALID_VERS_OPTION" },
+ { 0xC002003A, "RPC_NT_NO_MORE_MEMBERS" },
+ { 0xC002003B, "RPC_NT_NOT_ALL_OBJS_UNEXPORTED" },
+ { 0xC002003C, "RPC_NT_INTERFACE_NOT_FOUND" },
+ { 0xC002003D, "RPC_NT_ENTRY_ALREADY_EXISTS" },
+ { 0xC002003E, "RPC_NT_ENTRY_NOT_FOUND" },
+ { 0xC002003F, "RPC_NT_NAME_SERVICE_UNAVAILABLE" },
+ { 0xC0020040, "RPC_NT_INVALID_NAF_ID" },
+ { 0xC0020041, "RPC_NT_CANNOT_SUPPORT" },
+ { 0xC0020042, "RPC_NT_NO_CONTEXT_AVAILABLE" },
+ { 0xC0020043, "RPC_NT_INTERNAL_ERROR" },
+ { 0xC0020044, "RPC_NT_ZERO_DIVIDE" },
+ { 0xC0020045, "RPC_NT_ADDRESS_ERROR" },
+ { 0xC0020046, "RPC_NT_FP_DIV_ZERO" },
+ { 0xC0020047, "RPC_NT_FP_UNDERFLOW" },
+ { 0xC0020048, "RPC_NT_FP_OVERFLOW" },
+ { 0xC0021007, "RPC_P_RECEIVE_ALERTED" },
+ { 0xC0021008, "RPC_P_CONNECTION_CLOSED" },
+ { 0xC0021009, "RPC_P_RECEIVE_FAILED" },
+ { 0xC002100A, "RPC_P_SEND_FAILED" },
+ { 0xC002100B, "RPC_P_TIMEOUT" },
+ { 0xC002100C, "RPC_P_SERVER_TRANSPORT_ERROR" },
+ { 0xC002100E, "RPC_P_EXCEPTION_OCCURRED" },
+ { 0xC0021012, "RPC_P_CONNECTION_SHUTDOWN" },
+ { 0xC0021015, "RPC_P_THREAD_LISTENING" },
+ { 0xC0030001, "RPC_NT_NO_MORE_ENTRIES" },
+ { 0xC0030002, "RPC_NT_SS_CHAR_TRANS_OPEN_FAIL" },
+ { 0xC0030003, "RPC_NT_SS_CHAR_TRANS_SHORT_FILE" },
+ { 0xC0030004, "RPC_NT_SS_IN_NULL_CONTEXT" },
+ { 0xC0030005, "RPC_NT_SS_CONTEXT_MISMATCH" },
+ { 0xC0030006, "RPC_NT_SS_CONTEXT_DAMAGED" },
+ { 0xC0030007, "RPC_NT_SS_HANDLES_MISMATCH" },
+ { 0xC0030008, "RPC_NT_SS_CANNOT_GET_CALL_HANDLE" },
+ { 0xC0030009, "RPC_NT_NULL_REF_POINTER" },
+ { 0xC003000A, "RPC_NT_ENUM_VALUE_OUT_OF_RANGE" },
+ { 0xC003000B, "RPC_NT_BYTE_COUNT_TOO_SMALL" },
+ { 0xC003000C, "RPC_NT_BAD_STUB_DATA" },
+ { 0xC0020049, "RPC_NT_CALL_IN_PROGRESS" },
+ { 0xC002004A, "RPC_NT_NO_MORE_BINDINGS" },
+ { 0xC002004B, "RPC_NT_GROUP_MEMBER_NOT_FOUND" },
+ { 0xC002004C, "EPT_NT_CANT_CREATE" },
+ { 0xC002004D, "RPC_NT_INVALID_OBJECT" },
+ { 0xC002004F, "RPC_NT_NO_INTERFACES" },
+ { 0xC0020050, "RPC_NT_CALL_CANCELLED" },
+ { 0xC0020051, "RPC_NT_BINDING_INCOMPLETE" },
+ { 0xC0020052, "RPC_NT_COMM_FAILURE" },
+ { 0xC0020053, "RPC_NT_UNSUPPORTED_AUTHN_LEVEL" },
+ { 0xC0020054, "RPC_NT_NO_PRINC_NAME" },
+ { 0xC0020055, "RPC_NT_NOT_RPC_ERROR" },
+ { 0x40020056, "RPC_NT_UUID_LOCAL_ONLY" },
+ { 0xC0020057, "RPC_NT_SEC_PKG_ERROR" },
+ { 0xC0020058, "RPC_NT_NOT_CANCELLED" },
+ { 0xC0030059, "RPC_NT_INVALID_ES_ACTION" },
+ { 0xC003005A, "RPC_NT_WRONG_ES_VERSION" },
+ { 0xC003005B, "RPC_NT_WRONG_STUB_VERSION" },
+ { 0xC003005C, "RPC_NT_INVALID_PIPE_OBJECT" },
+ { 0xC003005D, "RPC_NT_INVALID_PIPE_OPERATION" },
+ { 0xC003005E, "RPC_NT_WRONG_PIPE_VERSION" },
+ { 0x400200AF, "RPC_NT_SEND_INCOMPLETE" },
+ { 0, NULL }
+};
+
+/*
+ * return an NT error string from a SMB buffer
+ */
+const char *
+nt_errstr(uint32_t err)
+{
+ static char ret[128];
+ int i;
+
+ ret[0] = 0;
+
+ for (i = 0; nt_errors[i].name; i++) {
+ if (err == nt_errors[i].code)
+ return nt_errors[i].name;
+ }
+
+ snprintf(ret, sizeof(ret), "0x%08x", err);
+ return ret;
+}
diff --git a/status-exit-codes.h b/status-exit-codes.h
new file mode 100644
index 0000000..34d9d16
--- /dev/null
+++ b/status-exit-codes.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2018 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef status_exit_codes_h
+#define status_exit_codes_h
+
+/* S_ERR_ND_* are libnetdissect status */
+
+typedef enum {
+ S_SUCCESS = 0, /* not a libnetdissect status */
+ S_ERR_HOST_PROGRAM = 1, /* not a libnetdissect status */
+ S_ERR_ND_NO_PRINTER = 11,
+ S_ERR_ND_MEM_ALLOC = 12,
+ S_ERR_ND_OPEN_FILE = 13,
+ S_ERR_ND_WRITE_FILE = 14,
+ S_ERR_ND_ESP_SECRET = 15
+} status_exit_codes_t;
+
+#endif /* status_exit_codes_h */
diff --git a/strtoaddr.c b/strtoaddr.c
new file mode 100644
index 0000000..c6f79d9
--- /dev/null
+++ b/strtoaddr.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+#include <stddef.h>
+#include <string.h>
+
+#include "netdissect-ctype.h"
+
+#include "strtoaddr.h"
+
+#ifndef NS_INADDRSZ
+#define NS_INADDRSZ 4 /* IPv4 T_A */
+#endif
+
+#ifndef NS_IN6ADDRSZ
+#define NS_IN6ADDRSZ 16 /* IPv6 T_AAAA */
+#endif
+
+#ifndef NS_INT16SZ
+#define NS_INT16SZ 2 /* #/bytes of data in a uint16_t */
+#endif
+
+/*%
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+/* int
+ * strtoaddr(src, dst)
+ * convert presentation level IPv4 address to network order binary form.
+ * return:
+ * 1 if `src' is a valid input, else 0.
+ * notice:
+ * does not touch `dst' unless it's returning 1.
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+strtoaddr(const char *src, void *dst)
+{
+ uint32_t val;
+ u_int digit;
+ ptrdiff_t n;
+ unsigned char c;
+ u_int parts[4];
+ u_int *pp = parts;
+
+ c = *src;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, isdigit=decimal.
+ */
+ if (!ND_ASCII_ISDIGIT(c))
+ return (0);
+ val = 0;
+ if (c == '0') {
+ c = *++src;
+ if (c == 'x' || c == 'X')
+ return (0);
+ else if (ND_ASCII_ISDIGIT(c) && c != '9')
+ return (0);
+ }
+ for (;;) {
+ if (ND_ASCII_ISDIGIT(c)) {
+ digit = c - '0';
+ val = (val * 10) + digit;
+ c = *++src;
+ } else
+ break;
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ * a (with a treated as 32 bits)
+ */
+ if (pp >= parts + 3)
+ return (0);
+ *pp++ = val;
+ c = *++src;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && c != ' ' && c != '\t')
+ return (0);
+ /*
+ * Find the number of parts specified.
+ * It must be 4; we only support dotted quads, we don't
+ * support shorthand.
+ */
+ n = pp - parts + 1;
+ if (n != 4)
+ return (0);
+ /*
+ * parts[0-2] were set to the first 3 parts of the address;
+ * val was set to the 4th part.
+ *
+ * Check if any part is bigger than 255.
+ */
+ if ((parts[0] | parts[1] | parts[2] | val) > 0xff)
+ return (0);
+ /*
+ * Add the other three parts to val.
+ */
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ if (dst) {
+ val = htonl(val);
+ memcpy(dst, &val, NS_INADDRSZ);
+ }
+ return (1);
+}
+
+/* int
+ * strtoaddr6(src, dst)
+ * convert presentation level IPv6 address to network order binary form.
+ * return:
+ * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ * (1) does not touch `dst' unless it's returning 1.
+ * (2) :: in a full address is silently ignored.
+ * credit:
+ * inspired by Mark Andrews.
+ * author:
+ * Paul Vixie, 1996.
+ */
+int
+strtoaddr6(const char *src, void *dst)
+{
+ static const char xdigits_l[] = "0123456789abcdef",
+ xdigits_u[] = "0123456789ABCDEF";
+ u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
+ const char *xdigits, *curtok;
+ int ch, seen_xdigits;
+ u_int val;
+
+ memset((tp = tmp), '\0', NS_IN6ADDRSZ);
+ endp = tp + NS_IN6ADDRSZ;
+ colonp = NULL;
+ /* Leading :: requires some special handling. */
+ if (*src == ':')
+ if (*++src != ':')
+ return (0);
+ curtok = src;
+ seen_xdigits = 0;
+ val = 0;
+ while ((ch = *src++) != '\0') {
+ const char *pch;
+
+ if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
+ pch = strchr((xdigits = xdigits_u), ch);
+ if (pch != NULL) {
+ val <<= 4;
+ val |= (int)(pch - xdigits);
+ if (++seen_xdigits > 4)
+ return (0);
+ continue;
+ }
+ if (ch == ':') {
+ curtok = src;
+ if (!seen_xdigits) {
+ if (colonp)
+ return (0);
+ colonp = tp;
+ continue;
+ } else if (*src == '\0')
+ return (0);
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (u_char) (val >> 8) & 0xff;
+ *tp++ = (u_char) val & 0xff;
+ seen_xdigits = 0;
+ val = 0;
+ continue;
+ }
+ if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
+ strtoaddr(curtok, tp) > 0) {
+ tp += NS_INADDRSZ;
+ seen_xdigits = 0;
+ break; /*%< '\\0' was seen by strtoaddr(). */
+ }
+ return (0);
+ }
+ if (seen_xdigits) {
+ if (tp + NS_INT16SZ > endp)
+ return (0);
+ *tp++ = (u_char) (val >> 8) & 0xff;
+ *tp++ = (u_char) val & 0xff;
+ }
+ if (colonp != NULL) {
+ /*
+ * Since some memmove()'s erroneously fail to handle
+ * overlapping regions, we'll do the shift by hand.
+ */
+ const ptrdiff_t n = tp - colonp;
+ int i;
+
+ if (tp == endp)
+ return (0);
+ for (i = 1; i <= n; i++) {
+ endp[- i] = colonp[n - i];
+ colonp[n - i] = 0;
+ }
+ tp = endp;
+ }
+ if (tp != endp)
+ return (0);
+ memcpy(dst, tmp, NS_IN6ADDRSZ);
+ return (1);
+}
diff --git a/strtoaddr.h b/strtoaddr.h
new file mode 100644
index 0000000..8bd22c7
--- /dev/null
+++ b/strtoaddr.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (c) 1996,1999 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
+ * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Text string to address translation routines. */
+
+int strtoaddr(const char *src, void *dst);
+int strtoaddr6(const char *src, void *dst);
+
+
diff --git a/tcp.h b/tcp.h
new file mode 100644
index 0000000..491157b
--- /dev/null
+++ b/tcp.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * TCP header.
+ * Per RFC 793, September, 1981.
+ */
+struct tcphdr {
+ nd_uint16_t th_sport; /* source port */
+ nd_uint16_t th_dport; /* destination port */
+ nd_uint32_t th_seq; /* sequence number */
+ nd_uint32_t th_ack; /* acknowledgement number */
+ nd_uint8_t th_offx2; /* data offset, rsvd */
+ nd_uint8_t th_flags;
+ nd_uint16_t th_win; /* window */
+ nd_uint16_t th_sum; /* checksum */
+ nd_uint16_t th_urp; /* urgent pointer */
+};
+
+#define TH_OFF(th) ((GET_U_1((th)->th_offx2) & 0xf0) >> 4)
+
+/* TCP flags */
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+#define TH_ECNECHO 0x40 /* ECN Echo */
+#define TH_CWR 0x80 /* ECN Cwnd Reduced */
+
+
+#define TCPOPT_EOL 0
+#define TCPOPT_NOP 1
+#define TCPOPT_MAXSEG 2
+#define TCPOLEN_MAXSEG 4
+#define TCPOPT_WSCALE 3 /* window scale factor (rfc1323) */
+#define TCPOPT_SACKOK 4 /* selective ack ok (rfc2018) */
+#define TCPOPT_SACK 5 /* selective ack (rfc2018) */
+#define TCPOPT_ECHO 6 /* echo (rfc1072) */
+#define TCPOPT_ECHOREPLY 7 /* echo (rfc1072) */
+#define TCPOPT_TIMESTAMP 8 /* timestamp (rfc1323) */
+#define TCPOLEN_TIMESTAMP 10
+#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
+#define TCPOPT_CC 11 /* T/TCP CC options (rfc1644) */
+#define TCPOPT_CCNEW 12 /* T/TCP CC options (rfc1644) */
+#define TCPOPT_CCECHO 13 /* T/TCP CC options (rfc1644) */
+#define TCPOPT_SIGNATURE 19 /* Keyed MD5 (rfc2385) */
+#define TCPOLEN_SIGNATURE 18
+#define TCP_SIGLEN 16 /* length of an option 19 digest */
+#define TCPOPT_SCPS 20 /* SCPS-TP (CCSDS 714.0-B-2) */
+#define TCPOPT_UTO 28 /* tcp user timeout (rfc5482) */
+#define TCPOLEN_UTO 4
+#define TCPOPT_TCPAO 29 /* TCP authentication option (rfc5925) */
+#define TCPOPT_MPTCP 30 /* MPTCP options */
+#define TCPOPT_FASTOPEN 34 /* TCP Fast Open (rfc7413) */
+#define TCPOPT_EXPERIMENT2 254 /* experimental headers (rfc4727) */
+
+#define TCPOPT_TSTAMP_HDR \
+ (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
+
+#ifndef FTP_PORT
+#define FTP_PORT 21
+#endif
+#ifndef SSH_PORT
+#define SSH_PORT 22
+#endif
+#ifndef TELNET_PORT
+#define TELNET_PORT 23
+#endif
+#ifndef SMTP_PORT
+#define SMTP_PORT 25
+#endif
+#ifndef WHOIS_PORT
+#define WHOIS_PORT 43
+#endif
+#ifndef NAMESERVER_PORT
+#define NAMESERVER_PORT 53
+#endif
+#ifndef HTTP_PORT
+#define HTTP_PORT 80
+#endif
+#ifndef NETBIOS_SSN_PORT
+#define NETBIOS_SSN_PORT 139 /* RFC 1001, RFC 1002 */
+#endif
+#ifndef BGP_PORT
+#define BGP_PORT 179
+#endif
+#ifndef RPKI_RTR_PORT
+#define RPKI_RTR_PORT 323
+#endif
+#ifndef SMB_PORT
+#define SMB_PORT 445
+#endif
+#ifndef RTSP_PORT
+#define RTSP_PORT 554
+#endif
+#ifndef MSDP_PORT
+#define MSDP_PORT 639
+#endif
+#ifndef LDP_PORT
+#define LDP_PORT 646
+#endif
+#ifndef PPTP_PORT
+#define PPTP_PORT 1723
+#endif
+#ifndef NFS_PORT
+#define NFS_PORT 2049
+#endif
+#ifndef OPENFLOW_PORT_OLD
+#define OPENFLOW_PORT_OLD 6633
+#endif
+#ifndef OPENFLOW_PORT_IANA
+#define OPENFLOW_PORT_IANA 6653
+#endif
+#ifndef HTTP_PORT_ALT
+#define HTTP_PORT_ALT 8080
+#endif
+#ifndef RTSP_PORT_ALT
+#define RTSP_PORT_ALT 8554
+#endif
+#ifndef BEEP_PORT
+#define BEEP_PORT 10288
+#endif
+#ifndef REDIS_PORT
+#define REDIS_PORT 6379
+#endif
diff --git a/tcpdump.c b/tcpdump.c
new file mode 100644
index 0000000..a6e6b58
--- /dev/null
+++ b/tcpdump.c
@@ -0,0 +1,3199 @@
+/*
+ * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Support for splitting captures into multiple files with a maximum
+ * file size:
+ *
+ * Copyright (c) 2001
+ * Seth Webster <swebster@sst.ll.mit.edu>
+ */
+
+/*
+ * tcpdump - dump traffic on a network
+ *
+ * First written in 1987 by Van Jacobson, Lawrence Berkeley Laboratory.
+ * Mercilessly hacked and occasionally improved since then via the
+ * combined efforts of Van, Steve McCanne and Craig Leres of LBL.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+/*
+ * Some older versions of Mac OS X may ship pcap.h from libpcap 0.6 with a
+ * libpcap based on 0.8. That means it has pcap_findalldevs() but the
+ * header doesn't define pcap_if_t, meaning that we can't actually *use*
+ * pcap_findalldevs().
+ */
+#ifdef HAVE_PCAP_FINDALLDEVS
+#ifndef HAVE_PCAP_IF_T
+#undef HAVE_PCAP_FINDALLDEVS
+#endif
+#endif
+
+#include "netdissect-stdinc.h"
+
+/*
+ * This must appear after including netdissect-stdinc.h, so that _U_ is
+ * defined.
+ */
+#ifndef lint
+static const char copyright[] _U_ =
+ "@(#) Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2000\n\
+The Regents of the University of California. All rights reserved.\n";
+#endif
+
+#include <sys/stat.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+
+#ifdef HAVE_LIBCRYPTO
+#include <openssl/crypto.h>
+#endif
+
+#ifdef HAVE_GETOPT_LONG
+#include <getopt.h>
+#else
+#include "missing/getopt_long.h"
+#endif
+/* Capsicum-specific code requires macros from <net/bpf.h>, which will fail
+ * to compile if <pcap.h> has already been included; including the headers
+ * in the opposite order works fine.
+ */
+#ifdef HAVE_CAPSICUM
+#include <sys/capsicum.h>
+#include <sys/ioccom.h>
+#include <net/bpf.h>
+#include <libgen.h>
+#ifdef HAVE_CASPER
+#include <libcasper.h>
+#include <casper/cap_dns.h>
+#include <sys/nv.h>
+#endif /* HAVE_CASPER */
+#endif /* HAVE_CAPSICUM */
+#ifdef HAVE_PCAP_OPEN
+/*
+ * We found pcap_open() in the capture library, so we'll be using
+ * the remote capture APIs; define PCAP_REMOTE before we include pcap.h,
+ * so we get those APIs declared, and the types and #defines that they
+ * use defined.
+ *
+ * WinPcap's headers require that PCAP_REMOTE be defined in order to get
+ * remote-capture APIs declared and types and #defines that they use
+ * defined.
+ *
+ * (Versions of libpcap with those APIs, and thus Npcap, which is based on
+ * those versions of libpcap, don't require it.)
+ */
+#define HAVE_REMOTE
+#endif
+#include <pcap.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <pwd.h>
+#include <grp.h>
+#endif /* _WIN32 */
+
+/*
+ * Pathname separator.
+ * Use this in pathnames, but do *not* use it in URLs.
+ */
+#ifdef _WIN32
+#define PATH_SEPARATOR '\\'
+#else
+#define PATH_SEPARATOR '/'
+#endif
+
+/* capabilities convenience library */
+/* If a code depends on HAVE_LIBCAP_NG, it depends also on HAVE_CAP_NG_H.
+ * If HAVE_CAP_NG_H is not defined, undefine HAVE_LIBCAP_NG.
+ * Thus, the later tests are done only on HAVE_LIBCAP_NG.
+ */
+#ifdef HAVE_LIBCAP_NG
+#ifdef HAVE_CAP_NG_H
+#include <cap-ng.h>
+#else
+#undef HAVE_LIBCAP_NG
+#endif /* HAVE_CAP_NG_H */
+#endif /* HAVE_LIBCAP_NG */
+
+#ifdef __FreeBSD__
+#include <sys/sysctl.h>
+#endif /* __FreeBSD__ */
+
+#include "netdissect-stdinc.h"
+#include "netdissect.h"
+#include "interface.h"
+#include "addrtoname.h"
+#include "machdep.h"
+#include "pcap-missing.h"
+#include "ascii_strcasecmp.h"
+
+#include "print.h"
+
+#include "fptype.h"
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+#if defined(SIGINFO)
+#define SIGNAL_REQ_INFO SIGINFO
+#elif defined(SIGUSR1)
+#define SIGNAL_REQ_INFO SIGUSR1
+#endif
+
+#if defined(HAVE_PCAP_DUMP_FLUSH) && defined(SIGUSR2)
+#define SIGNAL_FLUSH_PCAP SIGUSR2
+#endif
+
+#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
+static int Bflag; /* buffer size */
+#endif
+#ifdef HAVE_PCAP_DUMP_FTELL64
+static int64_t Cflag; /* rotate dump files after this many bytes */
+#else
+static long Cflag; /* rotate dump files after this many bytes */
+#endif
+static int Cflag_count; /* Keep track of which file number we're writing */
+#ifdef HAVE_PCAP_FINDALLDEVS
+static int Dflag; /* list available devices and exit */
+#endif
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+static char *remote_interfaces_source; /* list available devices from this source and exit */
+#endif
+
+/*
+ * This is exported because, in some versions of libpcap, if libpcap
+ * is built with optimizer debugging code (which is *NOT* the default
+ * configuration!), the library *imports*(!) a variable named dflag,
+ * under the expectation that tcpdump is exporting it, to govern
+ * how much debugging information to print when optimizing
+ * the generated BPF code.
+ *
+ * This is a horrible hack; newer versions of libpcap don't import
+ * dflag but, instead, *if* built with optimizer debugging code,
+ * *export* a routine to set that flag.
+ */
+extern int dflag;
+int dflag; /* print filter code */
+static int Gflag; /* rotate dump files after this many seconds */
+static int Gflag_count; /* number of files created with Gflag rotation */
+static time_t Gflag_time; /* The last time_t the dump file was rotated. */
+static int Lflag; /* list available data link types and exit */
+static int Iflag; /* rfmon (monitor) mode */
+#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+static int Jflag; /* list available time stamp types */
+static int jflag = -1; /* packet time stamp source */
+#endif
+static int lflag; /* line-buffered output */
+static int pflag; /* don't go promiscuous */
+#ifdef HAVE_PCAP_SETDIRECTION
+static int Qflag = -1; /* restrict captured packet by send/receive direction */
+#endif
+#ifdef HAVE_PCAP_DUMP_FLUSH
+static int Uflag; /* "unbuffered" output of dump files */
+#endif
+static int Wflag; /* recycle output files after this number of files */
+static int WflagChars;
+static char *zflag = NULL; /* compress each savefile using a specified command (like gzip or bzip2) */
+static int timeout = 1000; /* default timeout = 1000 ms = 1 s */
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+static int immediate_mode;
+#endif
+static int count_mode;
+
+static int infodelay;
+static int infoprint;
+
+char *program_name;
+
+#ifdef HAVE_CASPER
+cap_channel_t *capdns;
+#endif
+
+/* Forwards */
+static NORETURN void error(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2);
+static void warning(FORMAT_STRING(const char *), ...) PRINTFLIKE(1, 2);
+static NORETURN void exit_tcpdump(int);
+static void (*setsignal (int sig, void (*func)(int)))(int);
+static void cleanup(int);
+static void child_cleanup(int);
+static void print_version(FILE *);
+static void print_usage(FILE *);
+#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+static NORETURN void show_tstamp_types_and_exit(pcap_t *, const char *device);
+#endif
+static NORETURN void show_dlts_and_exit(pcap_t *, const char *device);
+#ifdef HAVE_PCAP_FINDALLDEVS
+static NORETURN void show_devices_and_exit(void);
+#endif
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+static NORETURN void show_remote_devices_and_exit(void);
+#endif
+
+static void print_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
+static void dump_packet_and_trunc(u_char *, const struct pcap_pkthdr *, const u_char *);
+static void dump_packet(u_char *, const struct pcap_pkthdr *, const u_char *);
+static void droproot(const char *, const char *);
+
+#ifdef SIGNAL_REQ_INFO
+static void requestinfo(int);
+#endif
+
+#ifdef SIGNAL_FLUSH_PCAP
+static void flushpcap(int);
+#endif
+
+#ifdef _WIN32
+ static HANDLE timer_handle = INVALID_HANDLE_VALUE;
+ static void CALLBACK verbose_stats_dump(PVOID param, BOOLEAN timer_fired);
+#else /* _WIN32 */
+ static void verbose_stats_dump(int sig);
+#endif /* _WIN32 */
+
+static void info(int);
+static u_int packets_captured;
+
+#ifdef HAVE_PCAP_FINDALLDEVS
+static const struct tok status_flags[] = {
+#ifdef PCAP_IF_UP
+ { PCAP_IF_UP, "Up" },
+#endif
+#ifdef PCAP_IF_RUNNING
+ { PCAP_IF_RUNNING, "Running" },
+#endif
+ { PCAP_IF_LOOPBACK, "Loopback" },
+#ifdef PCAP_IF_WIRELESS
+ { PCAP_IF_WIRELESS, "Wireless" },
+#endif
+ { 0, NULL }
+};
+#endif
+
+static pcap_t *pd;
+static pcap_dumper_t *pdd = NULL;
+
+static int supports_monitor_mode;
+
+extern int optind;
+extern int opterr;
+extern char *optarg;
+
+struct dump_info {
+ char *WFileName;
+ char *CurrentFileName;
+ pcap_t *pd;
+ pcap_dumper_t *pdd;
+ netdissect_options *ndo;
+#ifdef HAVE_CAPSICUM
+ int dirfd;
+#endif
+};
+
+#if defined(HAVE_PCAP_SET_PARSER_DEBUG)
+/*
+ * We have pcap_set_parser_debug() in libpcap; declare it (it's not declared
+ * by any libpcap header, because it's a special hack, only available if
+ * libpcap was configured to include it, and only intended for use by
+ * libpcap developers trying to debug the parser for filter expressions).
+ */
+#ifdef _WIN32
+__declspec(dllimport)
+#else /* _WIN32 */
+extern
+#endif /* _WIN32 */
+void pcap_set_parser_debug(int);
+#elif defined(HAVE_PCAP_DEBUG) || defined(HAVE_YYDEBUG)
+/*
+ * We don't have pcap_set_parser_debug() in libpcap, but we do have
+ * pcap_debug or yydebug. Make a local version of pcap_set_parser_debug()
+ * to set the flag, and define HAVE_PCAP_SET_PARSER_DEBUG.
+ */
+static void
+pcap_set_parser_debug(int value)
+{
+#ifdef HAVE_PCAP_DEBUG
+ extern int pcap_debug;
+
+ pcap_debug = value;
+#else /* HAVE_PCAP_DEBUG */
+ extern int yydebug;
+
+ yydebug = value;
+#endif /* HAVE_PCAP_DEBUG */
+}
+
+#define HAVE_PCAP_SET_PARSER_DEBUG
+#endif
+
+#if defined(HAVE_PCAP_SET_OPTIMIZER_DEBUG)
+/*
+ * We have pcap_set_optimizer_debug() in libpcap; declare it (it's not declared
+ * by any libpcap header, because it's a special hack, only available if
+ * libpcap was configured to include it, and only intended for use by
+ * libpcap developers trying to debug the optimizer for filter expressions).
+ */
+#ifdef _WIN32
+__declspec(dllimport)
+#else /* _WIN32 */
+extern
+#endif /* _WIN32 */
+void pcap_set_optimizer_debug(int);
+#endif
+
+/* VARARGS */
+static void
+error(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "%s: ", program_name);
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+ exit_tcpdump(S_ERR_HOST_PROGRAM);
+ /* NOTREACHED */
+}
+
+/* VARARGS */
+static void
+warning(const char *fmt, ...)
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "%s: WARNING: ", program_name);
+ va_start(ap, fmt);
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+}
+
+static void
+exit_tcpdump(int status)
+{
+ nd_cleanup();
+ exit(status);
+}
+
+#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+static void
+show_tstamp_types_and_exit(pcap_t *pc, const char *device)
+{
+ int n_tstamp_types;
+ int *tstamp_types = 0;
+ const char *tstamp_type_name;
+ int i;
+
+ n_tstamp_types = pcap_list_tstamp_types(pc, &tstamp_types);
+ if (n_tstamp_types < 0)
+ error("%s", pcap_geterr(pc));
+
+ if (n_tstamp_types == 0) {
+ fprintf(stderr, "Time stamp type cannot be set for %s\n",
+ device);
+ exit_tcpdump(S_SUCCESS);
+ }
+ fprintf(stderr, "Time stamp types for %s (use option -j to set):\n",
+ device);
+ for (i = 0; i < n_tstamp_types; i++) {
+ tstamp_type_name = pcap_tstamp_type_val_to_name(tstamp_types[i]);
+ if (tstamp_type_name != NULL) {
+ (void) fprintf(stderr, " %s (%s)\n", tstamp_type_name,
+ pcap_tstamp_type_val_to_description(tstamp_types[i]));
+ } else {
+ (void) fprintf(stderr, " %d\n", tstamp_types[i]);
+ }
+ }
+ pcap_free_tstamp_types(tstamp_types);
+ exit_tcpdump(S_SUCCESS);
+}
+#endif
+
+static void
+show_dlts_and_exit(pcap_t *pc, const char *device)
+{
+ int n_dlts, i;
+ int *dlts = 0;
+ const char *dlt_name;
+
+ n_dlts = pcap_list_datalinks(pc, &dlts);
+ if (n_dlts < 0)
+ error("%s", pcap_geterr(pc));
+ else if (n_dlts == 0 || !dlts)
+ error("No data link types.");
+
+ /*
+ * If the interface is known to support monitor mode, indicate
+ * whether these are the data link types available when not in
+ * monitor mode, if -I wasn't specified, or when in monitor mode,
+ * when -I was specified (the link-layer types available in
+ * monitor mode might be different from the ones available when
+ * not in monitor mode).
+ */
+ if (supports_monitor_mode)
+ (void) fprintf(stderr, "Data link types for %s %s (use option -y to set):\n",
+ device,
+ Iflag ? "when in monitor mode" : "when not in monitor mode");
+ else
+ (void) fprintf(stderr, "Data link types for %s (use option -y to set):\n",
+ device);
+
+ for (i = 0; i < n_dlts; i++) {
+ dlt_name = pcap_datalink_val_to_name(dlts[i]);
+ if (dlt_name != NULL) {
+ (void) fprintf(stderr, " %s (%s)", dlt_name,
+ pcap_datalink_val_to_description(dlts[i]));
+
+ /*
+ * OK, does tcpdump handle that type?
+ */
+ if (!has_printer(dlts[i]))
+ (void) fprintf(stderr, " (printing not supported)");
+ fprintf(stderr, "\n");
+ } else {
+ (void) fprintf(stderr, " DLT %d (printing not supported)\n",
+ dlts[i]);
+ }
+ }
+#ifdef HAVE_PCAP_FREE_DATALINKS
+ pcap_free_datalinks(dlts);
+#endif
+ exit_tcpdump(S_SUCCESS);
+}
+
+#ifdef HAVE_PCAP_FINDALLDEVS
+static void
+show_devices_and_exit(void)
+{
+ pcap_if_t *dev, *devlist;
+ char ebuf[PCAP_ERRBUF_SIZE];
+ int i;
+
+ if (pcap_findalldevs(&devlist, ebuf) < 0)
+ error("%s", ebuf);
+ for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) {
+ printf("%d.%s", i+1, dev->name);
+ if (dev->description != NULL)
+ printf(" (%s)", dev->description);
+ if (dev->flags != 0) {
+ printf(" [");
+ printf("%s", bittok2str(status_flags, "none", dev->flags));
+#ifdef PCAP_IF_WIRELESS
+ if (dev->flags & PCAP_IF_WIRELESS) {
+ switch (dev->flags & PCAP_IF_CONNECTION_STATUS) {
+
+ case PCAP_IF_CONNECTION_STATUS_UNKNOWN:
+ printf(", Association status unknown");
+ break;
+
+ case PCAP_IF_CONNECTION_STATUS_CONNECTED:
+ printf(", Associated");
+ break;
+
+ case PCAP_IF_CONNECTION_STATUS_DISCONNECTED:
+ printf(", Not associated");
+ break;
+
+ case PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE:
+ break;
+ }
+ } else {
+ switch (dev->flags & PCAP_IF_CONNECTION_STATUS) {
+
+ case PCAP_IF_CONNECTION_STATUS_UNKNOWN:
+ printf(", Connection status unknown");
+ break;
+
+ case PCAP_IF_CONNECTION_STATUS_CONNECTED:
+ printf(", Connected");
+ break;
+
+ case PCAP_IF_CONNECTION_STATUS_DISCONNECTED:
+ printf(", Disconnected");
+ break;
+
+ case PCAP_IF_CONNECTION_STATUS_NOT_APPLICABLE:
+ break;
+ }
+ }
+#endif
+ printf("]");
+ }
+ printf("\n");
+ }
+ pcap_freealldevs(devlist);
+ exit_tcpdump(S_SUCCESS);
+}
+#endif /* HAVE_PCAP_FINDALLDEVS */
+
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+static void
+show_remote_devices_and_exit(void)
+{
+ pcap_if_t *dev, *devlist;
+ char ebuf[PCAP_ERRBUF_SIZE];
+ int i;
+
+ if (pcap_findalldevs_ex(remote_interfaces_source, NULL, &devlist,
+ ebuf) < 0)
+ error("%s", ebuf);
+ for (i = 0, dev = devlist; dev != NULL; i++, dev = dev->next) {
+ printf("%d.%s", i+1, dev->name);
+ if (dev->description != NULL)
+ printf(" (%s)", dev->description);
+ if (dev->flags != 0)
+ printf(" [%s]", bittok2str(status_flags, "none", dev->flags));
+ printf("\n");
+ }
+ pcap_freealldevs(devlist);
+ exit_tcpdump(S_SUCCESS);
+}
+#endif /* HAVE_PCAP_FINDALLDEVS */
+
+/*
+ * Short options.
+ *
+ * Note that there we use all letters for short options except for g, k,
+ * o, and P, and those are used by other versions of tcpdump, and we should
+ * only use them for the same purposes that the other versions of tcpdump
+ * use them:
+ *
+ * macOS tcpdump uses -g to force non--v output for IP to be on one
+ * line, making it more "g"repable;
+ *
+ * macOS tcpdump uses -k to specify that packet comments in pcapng files
+ * should be printed;
+ *
+ * OpenBSD tcpdump uses -o to indicate that OS fingerprinting should be done
+ * for hosts sending TCP SYN packets;
+ *
+ * macOS tcpdump uses -P to indicate that -w should write pcapng rather
+ * than pcap files.
+ *
+ * macOS tcpdump also uses -Q to specify expressions that match packet
+ * metadata, including but not limited to the packet direction.
+ * The expression syntax is different from a simple "in|out|inout",
+ * and those expressions aren't accepted by macOS tcpdump, but the
+ * equivalents would be "in" = "dir=in", "out" = "dir=out", and
+ * "inout" = "dir=in or dir=out", and the parser could conceivably
+ * special-case "in", "out", and "inout" as expressions for backwards
+ * compatibility, so all is not (yet) lost.
+ */
+
+/*
+ * Set up flags that might or might not be supported depending on the
+ * version of libpcap we're using.
+ */
+#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
+#define B_FLAG "B:"
+#define B_FLAG_USAGE " [ -B size ]"
+#else /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
+#define B_FLAG
+#define B_FLAG_USAGE
+#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
+
+#ifdef HAVE_PCAP_FINDALLDEVS
+#define D_FLAG "D"
+#else
+#define D_FLAG
+#endif
+
+#ifdef HAVE_PCAP_CREATE
+#define I_FLAG "I"
+#else /* HAVE_PCAP_CREATE */
+#define I_FLAG
+#endif /* HAVE_PCAP_CREATE */
+
+#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+#define j_FLAG "j:"
+#define j_FLAG_USAGE " [ -j tstamptype ]"
+#define J_FLAG "J"
+#else /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */
+#define j_FLAG
+#define j_FLAG_USAGE
+#define J_FLAG
+#endif /* PCAP_ERROR_TSTAMP_TYPE_NOTSUP */
+
+#ifdef USE_LIBSMI
+#define m_FLAG_USAGE "[ -m module ] ..."
+#endif
+
+#ifdef HAVE_PCAP_SETDIRECTION
+#define Q_FLAG "Q:"
+#define Q_FLAG_USAGE " [ -Q in|out|inout ]"
+#else
+#define Q_FLAG
+#define Q_FLAG_USAGE
+#endif
+
+#ifdef HAVE_PCAP_DUMP_FLUSH
+#define U_FLAG "U"
+#else
+#define U_FLAG
+#endif
+
+#define SHORTOPTS "aAb" B_FLAG "c:C:d" D_FLAG "eE:fF:G:hHi:" I_FLAG j_FLAG J_FLAG "KlLm:M:nNOpq" Q_FLAG "r:s:StT:u" U_FLAG "vV:w:W:xXy:Yz:Z:#"
+
+/*
+ * Long options.
+ *
+ * We do not currently have long options corresponding to all short
+ * options; we should probably pick appropriate option names for them.
+ *
+ * However, the short options where the number of times the option is
+ * specified matters, such as -v and -d and -t, should probably not
+ * just map to a long option, as saying
+ *
+ * tcpdump --verbose --verbose
+ *
+ * doesn't make sense; it should be --verbosity={N} or something such
+ * as that.
+ *
+ * For long options with no corresponding short options, we define values
+ * outside the range of ASCII graphic characters, make that the last
+ * component of the entry for the long option, and have a case for that
+ * option in the switch statement.
+ */
+#define OPTION_VERSION 128
+#define OPTION_TSTAMP_PRECISION 129
+#define OPTION_IMMEDIATE_MODE 130
+#define OPTION_PRINT 131
+#define OPTION_LIST_REMOTE_INTERFACES 132
+#define OPTION_TSTAMP_MICRO 133
+#define OPTION_TSTAMP_NANO 134
+#define OPTION_FP_TYPE 135
+#define OPTION_COUNT 136
+
+static const struct option longopts[] = {
+#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
+ { "buffer-size", required_argument, NULL, 'B' },
+#endif
+ { "list-interfaces", no_argument, NULL, 'D' },
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+ { "list-remote-interfaces", required_argument, NULL, OPTION_LIST_REMOTE_INTERFACES },
+#endif
+ { "help", no_argument, NULL, 'h' },
+ { "interface", required_argument, NULL, 'i' },
+#ifdef HAVE_PCAP_CREATE
+ { "monitor-mode", no_argument, NULL, 'I' },
+#endif
+#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+ { "time-stamp-type", required_argument, NULL, 'j' },
+ { "list-time-stamp-types", no_argument, NULL, 'J' },
+#endif
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ { "micro", no_argument, NULL, OPTION_TSTAMP_MICRO},
+ { "nano", no_argument, NULL, OPTION_TSTAMP_NANO},
+ { "time-stamp-precision", required_argument, NULL, OPTION_TSTAMP_PRECISION},
+#endif
+ { "dont-verify-checksums", no_argument, NULL, 'K' },
+ { "list-data-link-types", no_argument, NULL, 'L' },
+ { "no-optimize", no_argument, NULL, 'O' },
+ { "no-promiscuous-mode", no_argument, NULL, 'p' },
+#ifdef HAVE_PCAP_SETDIRECTION
+ { "direction", required_argument, NULL, 'Q' },
+#endif
+ { "snapshot-length", required_argument, NULL, 's' },
+ { "absolute-tcp-sequence-numbers", no_argument, NULL, 'S' },
+#ifdef HAVE_PCAP_DUMP_FLUSH
+ { "packet-buffered", no_argument, NULL, 'U' },
+#endif
+ { "linktype", required_argument, NULL, 'y' },
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ { "immediate-mode", no_argument, NULL, OPTION_IMMEDIATE_MODE },
+#endif
+#ifdef HAVE_PCAP_SET_PARSER_DEBUG
+ { "debug-filter-parser", no_argument, NULL, 'Y' },
+#endif
+ { "relinquish-privileges", required_argument, NULL, 'Z' },
+ { "count", no_argument, NULL, OPTION_COUNT },
+ { "fp-type", no_argument, NULL, OPTION_FP_TYPE },
+ { "number", no_argument, NULL, '#' },
+ { "print", no_argument, NULL, OPTION_PRINT },
+ { "version", no_argument, NULL, OPTION_VERSION },
+ { NULL, 0, NULL, 0 }
+};
+
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+#define LIST_REMOTE_INTERFACES_USAGE "[ --list-remote-interfaces remote-source ]"
+#else
+#define LIST_REMOTE_INTERFACES_USAGE
+#endif
+
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+#define IMMEDIATE_MODE_USAGE " [ --immediate-mode ]"
+#else
+#define IMMEDIATE_MODE_USAGE ""
+#endif
+
+#ifndef _WIN32
+/* Drop root privileges and chroot if necessary */
+static void
+droproot(const char *username, const char *chroot_dir)
+{
+ struct passwd *pw = NULL;
+
+ if (chroot_dir && !username)
+ error("Chroot without dropping root is insecure");
+
+ pw = getpwnam(username);
+ if (pw) {
+ if (chroot_dir) {
+ if (chroot(chroot_dir) != 0 || chdir ("/") != 0)
+ error("Couldn't chroot/chdir to '%.64s': %s",
+ chroot_dir, pcap_strerror(errno));
+ }
+#ifdef HAVE_LIBCAP_NG
+ {
+ int ret = capng_change_id(pw->pw_uid, pw->pw_gid, CAPNG_NO_FLAG);
+ if (ret < 0)
+ error("capng_change_id(): return %d\n", ret);
+ else
+ fprintf(stderr, "dropped privs to %s\n", username);
+ }
+#else
+ if (initgroups(pw->pw_name, pw->pw_gid) != 0 ||
+ setgid(pw->pw_gid) != 0 || setuid(pw->pw_uid) != 0)
+ error("Couldn't change to '%.32s' uid=%lu gid=%lu: %s",
+ username,
+ (unsigned long)pw->pw_uid,
+ (unsigned long)pw->pw_gid,
+ pcap_strerror(errno));
+ else {
+ fprintf(stderr, "dropped privs to %s\n", username);
+ }
+#endif /* HAVE_LIBCAP_NG */
+ } else
+ error("Couldn't find user '%.32s'", username);
+#ifdef HAVE_LIBCAP_NG
+ /* We don't need CAP_SETUID, CAP_SETGID and CAP_SYS_CHROOT any more. */
+DIAG_OFF_CLANG(assign-enum)
+ capng_updatev(
+ CAPNG_DROP,
+ CAPNG_EFFECTIVE | CAPNG_PERMITTED,
+ CAP_SETUID,
+ CAP_SETGID,
+ CAP_SYS_CHROOT,
+ -1);
+DIAG_ON_CLANG(assign-enum)
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+
+}
+#endif /* _WIN32 */
+
+static int
+getWflagChars(int x)
+{
+ int c = 0;
+
+ x -= 1;
+ while (x > 0) {
+ c += 1;
+ x /= 10;
+ }
+
+ return c;
+}
+
+
+static void
+MakeFilename(char *buffer, char *orig_name, int cnt, int max_chars)
+{
+ char *filename = malloc(PATH_MAX + 1);
+ if (filename == NULL)
+ error("%s: malloc", __func__);
+
+ /* Process with strftime if Gflag is set. */
+ if (Gflag != 0) {
+ struct tm *local_tm;
+
+ /* Convert Gflag_time to a usable format */
+ if ((local_tm = localtime(&Gflag_time)) == NULL) {
+ error("%s: localtime", __func__);
+ }
+
+ /* There's no good way to detect an error in strftime since a return
+ * value of 0 isn't necessarily failure.
+ */
+ strftime(filename, PATH_MAX, orig_name, local_tm);
+ } else {
+ strncpy(filename, orig_name, PATH_MAX);
+ }
+
+ if (cnt == 0 && max_chars == 0)
+ strncpy(buffer, filename, PATH_MAX + 1);
+ else
+ if (snprintf(buffer, PATH_MAX + 1, "%s%0*d", filename, max_chars, cnt) > PATH_MAX)
+ /* Report an error if the filename is too large */
+ error("too many output files or filename is too long (> %d)", PATH_MAX);
+ free(filename);
+}
+
+static char *
+get_next_file(FILE *VFile, char *ptr)
+{
+ char *ret;
+ size_t len;
+
+ ret = fgets(ptr, PATH_MAX, VFile);
+ if (!ret)
+ return NULL;
+
+ len = strlen (ptr);
+ if (len > 0 && ptr[len - 1] == '\n')
+ ptr[len - 1] = '\0';
+
+ return ret;
+}
+
+#ifdef HAVE_CASPER
+static cap_channel_t *
+capdns_setup(void)
+{
+ cap_channel_t *capcas, *capdnsloc;
+ const char *types[1];
+ int families[2];
+
+ capcas = cap_init();
+ if (capcas == NULL)
+ error("unable to create casper process");
+ capdnsloc = cap_service_open(capcas, "system.dns");
+ /* Casper capability no longer needed. */
+ cap_close(capcas);
+ if (capdnsloc == NULL)
+ error("unable to open system.dns service");
+ /* Limit system.dns to reverse DNS lookups. */
+ types[0] = "ADDR";
+ if (cap_dns_type_limit(capdnsloc, types, 1) < 0)
+ error("unable to limit access to system.dns service");
+ families[0] = AF_INET;
+ families[1] = AF_INET6;
+ if (cap_dns_family_limit(capdnsloc, families, 2) < 0)
+ error("unable to limit access to system.dns service");
+
+ return (capdnsloc);
+}
+#endif /* HAVE_CASPER */
+
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+static int
+tstamp_precision_from_string(const char *precision)
+{
+ if (strncmp(precision, "nano", strlen("nano")) == 0)
+ return PCAP_TSTAMP_PRECISION_NANO;
+
+ if (strncmp(precision, "micro", strlen("micro")) == 0)
+ return PCAP_TSTAMP_PRECISION_MICRO;
+
+ return -EINVAL;
+}
+
+static const char *
+tstamp_precision_to_string(int precision)
+{
+ switch (precision) {
+
+ case PCAP_TSTAMP_PRECISION_MICRO:
+ return "micro";
+
+ case PCAP_TSTAMP_PRECISION_NANO:
+ return "nano";
+
+ default:
+ return "unknown";
+ }
+}
+#endif
+
+#ifdef HAVE_CAPSICUM
+/*
+ * Ensure that, on a dump file's descriptor, we have all the rights
+ * necessary to make the standard I/O library work with an fdopen()ed
+ * FILE * from that descriptor.
+ *
+ * A long time ago in a galaxy far, far away, AT&T decided that, instead
+ * of providing separate APIs for getting and setting the FD_ flags on a
+ * descriptor, getting and setting the O_ flags on a descriptor, and
+ * locking files, they'd throw them all into a kitchen-sink fcntl() call
+ * along the lines of ioctl(), the fact that ioctl() operations are
+ * largely specific to particular character devices but fcntl() operations
+ * are either generic to all descriptors or generic to all descriptors for
+ * regular files nonwithstanding.
+ *
+ * The Capsicum people decided that fine-grained control of descriptor
+ * operations was required, so that you need to grant permission for
+ * reading, writing, seeking, and fcntl-ing. The latter, courtesy of
+ * AT&T's decision, means that "fcntl-ing" isn't a thing, but a motley
+ * collection of things, so there are *individual* fcntls for which
+ * permission needs to be granted.
+ *
+ * The FreeBSD standard I/O people implemented some optimizations that
+ * requires that the standard I/O routines be able to determine whether
+ * the descriptor for the FILE * is open append-only or not; as that
+ * descriptor could have come from an open() rather than an fopen(),
+ * that requires that it be able to do an F_GETFL fcntl() to read
+ * the O_ flags.
+ *
+ * Tcpdump uses ftell() to determine how much data has been written
+ * to a file in order to, when used with -C, determine when it's time
+ * to rotate capture files. ftell() therefore needs to do an lseek()
+ * to find out the file offset and must, thanks to the aforementioned
+ * optimization, also know whether the descriptor is open append-only
+ * or not.
+ *
+ * The net result of all the above is that we need to grant CAP_SEEK,
+ * CAP_WRITE, and CAP_FCNTL with the CAP_FCNTL_GETFL subcapability.
+ *
+ * Perhaps this is the universe's way of saying that either
+ *
+ * 1) there needs to be an fopenat() call and a pcap_dump_openat() call
+ * using it, so that Capsicum-capable tcpdump wouldn't need to do
+ * an fdopen()
+ *
+ * or
+ *
+ * 2) there needs to be a cap_fdopen() call in the FreeBSD standard
+ * I/O library that knows what rights are needed by the standard
+ * I/O library, based on the open mode, and assigns them, perhaps
+ * with an additional argument indicating, for example, whether
+ * seeking should be allowed, so that tcpdump doesn't need to know
+ * what the standard I/O library happens to require this week.
+ */
+static void
+set_dumper_capsicum_rights(pcap_dumper_t *p)
+{
+ int fd = fileno(pcap_dump_file(p));
+ cap_rights_t rights;
+
+ cap_rights_init(&rights, CAP_SEEK, CAP_WRITE, CAP_FCNTL);
+ if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS) {
+ error("unable to limit dump descriptor");
+ }
+ if (cap_fcntls_limit(fd, CAP_FCNTL_GETFL) < 0 && errno != ENOSYS) {
+ error("unable to limit dump descriptor fcntls");
+ }
+}
+#endif
+
+/*
+ * Copy arg vector into a new buffer, concatenating arguments with spaces.
+ */
+static char *
+copy_argv(char **argv)
+{
+ char **p;
+ size_t len = 0;
+ char *buf;
+ char *src, *dst;
+
+ p = argv;
+ if (*p == NULL)
+ return 0;
+
+ while (*p)
+ len += strlen(*p++) + 1;
+
+ buf = (char *)malloc(len);
+ if (buf == NULL)
+ error("%s: malloc", __func__);
+
+ p = argv;
+ dst = buf;
+ while ((src = *p++) != NULL) {
+ while ((*dst++ = *src++) != '\0')
+ ;
+ dst[-1] = ' ';
+ }
+ dst[-1] = '\0';
+
+ return buf;
+}
+
+/*
+ * On Windows, we need to open the file in binary mode, so that
+ * we get all the bytes specified by the size we get from "fstat()".
+ * On UNIX, that's not necessary. O_BINARY is defined on Windows;
+ * we define it as 0 if it's not defined, so it does nothing.
+ */
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+static char *
+read_infile(char *fname)
+{
+ int i, fd;
+ ssize_t cc;
+ char *cp;
+ our_statb buf;
+
+ fd = open(fname, O_RDONLY|O_BINARY);
+ if (fd < 0)
+ error("can't open %s: %s", fname, pcap_strerror(errno));
+
+ if (our_fstat(fd, &buf) < 0)
+ error("can't stat %s: %s", fname, pcap_strerror(errno));
+
+ /*
+ * Reject files whose size doesn't fit into an int; a filter
+ * *that* large will probably be too big.
+ */
+ if (buf.st_size > INT_MAX)
+ error("%s is too large", fname);
+
+ cp = malloc((u_int)buf.st_size + 1);
+ if (cp == NULL)
+ error("malloc(%d) for %s: %s", (u_int)buf.st_size + 1,
+ fname, pcap_strerror(errno));
+ cc = read(fd, cp, (u_int)buf.st_size);
+ if (cc < 0)
+ error("read %s: %s", fname, pcap_strerror(errno));
+ if (cc != buf.st_size)
+ error("short read %s (%d != %d)", fname, (int) cc,
+ (int)buf.st_size);
+
+ close(fd);
+ /* replace "# comment" with spaces */
+ for (i = 0; i < cc; i++) {
+ if (cp[i] == '#')
+ while (i < cc && cp[i] != '\n')
+ cp[i++] = ' ';
+ }
+ cp[cc] = '\0';
+ return (cp);
+}
+
+#ifdef HAVE_PCAP_FINDALLDEVS
+static long
+parse_interface_number(const char *device)
+{
+ const char *p;
+ long devnum;
+ char *end;
+
+ /*
+ * Search for a colon, terminating any scheme at the beginning
+ * of the device.
+ */
+ p = strchr(device, ':');
+ if (p != NULL) {
+ /*
+ * We found it. Is it followed by "//"?
+ */
+ p++; /* skip the : */
+ if (strncmp(p, "//", 2) == 0) {
+ /*
+ * Yes. Search for the next /, at the end of the
+ * authority part of the URL.
+ */
+ p += 2; /* skip the // */
+ p = strchr(p, '/');
+ if (p != NULL) {
+ /*
+ * OK, past the / is the path.
+ */
+ device = p + 1;
+ }
+ }
+ }
+ devnum = strtol(device, &end, 10);
+ if (device != end && *end == '\0') {
+ /*
+ * It's all-numeric, but is it a valid number?
+ */
+ if (devnum <= 0) {
+ /*
+ * No, it's not an ordinal.
+ */
+ error("Invalid adapter index");
+ }
+ return (devnum);
+ } else {
+ /*
+ * It's not all-numeric; return -1, so our caller
+ * knows that.
+ */
+ return (-1);
+ }
+}
+
+static char *
+find_interface_by_number(const char *url
+#ifndef HAVE_PCAP_FINDALLDEVS_EX
+_U_
+#endif
+, long devnum)
+{
+ pcap_if_t *dev, *devlist;
+ long i;
+ char ebuf[PCAP_ERRBUF_SIZE];
+ char *device;
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+ const char *endp;
+ char *host_url;
+#endif
+ int status;
+
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+ /*
+ * Search for a colon, terminating any scheme at the beginning
+ * of the URL.
+ */
+ endp = strchr(url, ':');
+ if (endp != NULL) {
+ /*
+ * We found it. Is it followed by "//"?
+ */
+ endp++; /* skip the : */
+ if (strncmp(endp, "//", 2) == 0) {
+ /*
+ * Yes. Search for the next /, at the end of the
+ * authority part of the URL.
+ */
+ endp += 2; /* skip the // */
+ endp = strchr(endp, '/');
+ } else
+ endp = NULL;
+ }
+ if (endp != NULL) {
+ /*
+ * OK, everything from device to endp is a URL to hand
+ * to pcap_findalldevs_ex().
+ */
+ endp++; /* Include the trailing / in the URL; pcap_findalldevs_ex() requires it */
+ host_url = malloc(endp - url + 1);
+ if (host_url == NULL && (endp - url + 1) > 0)
+ error("Invalid allocation for host");
+
+ memcpy(host_url, url, endp - url);
+ host_url[endp - url] = '\0';
+ status = pcap_findalldevs_ex(host_url, NULL, &devlist, ebuf);
+ free(host_url);
+ } else
+#endif
+ status = pcap_findalldevs(&devlist, ebuf);
+ if (status < 0)
+ error("%s", ebuf);
+ /*
+ * Look for the devnum-th entry in the list of devices (1-based).
+ */
+ for (i = 0, dev = devlist; i < devnum-1 && dev != NULL;
+ i++, dev = dev->next)
+ ;
+ if (dev == NULL)
+ error("Invalid adapter index");
+ device = strdup(dev->name);
+ pcap_freealldevs(devlist);
+ return (device);
+}
+#endif
+
+#ifdef HAVE_PCAP_OPEN
+/*
+ * Prefixes for rpcap URLs.
+ */
+static char rpcap_prefix[] = "rpcap://";
+static char rpcap_ssl_prefix[] = "rpcaps://";
+#endif
+
+static pcap_t *
+open_interface(const char *device, netdissect_options *ndo, char *ebuf)
+{
+ pcap_t *pc;
+#ifdef HAVE_PCAP_CREATE
+ int status;
+ char *cp;
+#endif
+
+#ifdef HAVE_PCAP_OPEN
+ /*
+ * Is this an rpcap URL?
+ */
+ if (strncmp(device, rpcap_prefix, sizeof(rpcap_prefix) - 1) == 0 ||
+ strncmp(device, rpcap_ssl_prefix, sizeof(rpcap_ssl_prefix) - 1) == 0) {
+ /*
+ * Yes. Open it with pcap_open().
+ */
+ *ebuf = '\0';
+ pc = pcap_open(device, ndo->ndo_snaplen,
+ pflag ? 0 : PCAP_OPENFLAG_PROMISCUOUS, timeout, NULL,
+ ebuf);
+ if (pc == NULL) {
+ /*
+ * If this failed with "No such device" or "The system
+ * cannot find the device specified", that means
+ * the interface doesn't exist; return NULL, so that
+ * the caller can see whether the device name is
+ * actually an interface index.
+ */
+ if (strstr(ebuf, "No such device") != NULL ||
+ strstr(ebuf, "The system cannot find the device specified") != NULL)
+ return (NULL);
+ error("%s", ebuf);
+ }
+ if (*ebuf)
+ warning("%s", ebuf);
+ return (pc);
+ }
+#endif /* HAVE_PCAP_OPEN */
+
+#ifdef HAVE_PCAP_CREATE
+ pc = pcap_create(device, ebuf);
+ if (pc == NULL) {
+ /*
+ * If this failed with "No such device", that means
+ * the interface doesn't exist; return NULL, so that
+ * the caller can see whether the device name is
+ * actually an interface index.
+ */
+ if (strstr(ebuf, "No such device") != NULL)
+ return (NULL);
+ error("%s", ebuf);
+ }
+#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+ if (Jflag)
+ show_tstamp_types_and_exit(pc, device);
+#endif
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ status = pcap_set_tstamp_precision(pc, ndo->ndo_tstamp_precision);
+ if (status != 0)
+ error("%s: Can't set %ssecond time stamp precision: %s",
+ device,
+ tstamp_precision_to_string(ndo->ndo_tstamp_precision),
+ pcap_statustostr(status));
+#endif
+
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ if (immediate_mode) {
+ status = pcap_set_immediate_mode(pc, 1);
+ if (status != 0)
+ error("%s: Can't set immediate mode: %s",
+ device, pcap_statustostr(status));
+ }
+#endif
+ /*
+ * Is this an interface that supports monitor mode?
+ */
+ if (pcap_can_set_rfmon(pc) == 1)
+ supports_monitor_mode = 1;
+ else
+ supports_monitor_mode = 0;
+ if (ndo->ndo_snaplen != 0) {
+ /*
+ * A snapshot length was explicitly specified;
+ * use it.
+ */
+ status = pcap_set_snaplen(pc, ndo->ndo_snaplen);
+ if (status != 0)
+ error("%s: Can't set snapshot length: %s",
+ device, pcap_statustostr(status));
+ }
+ status = pcap_set_promisc(pc, !pflag);
+ if (status != 0)
+ error("%s: Can't set promiscuous mode: %s",
+ device, pcap_statustostr(status));
+ if (Iflag) {
+ status = pcap_set_rfmon(pc, 1);
+ if (status != 0)
+ error("%s: Can't set monitor mode: %s",
+ device, pcap_statustostr(status));
+ }
+ status = pcap_set_timeout(pc, timeout);
+ if (status != 0)
+ error("%s: pcap_set_timeout failed: %s",
+ device, pcap_statustostr(status));
+ if (Bflag != 0) {
+ status = pcap_set_buffer_size(pc, Bflag);
+ if (status != 0)
+ error("%s: Can't set buffer size: %s",
+ device, pcap_statustostr(status));
+ }
+#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+ if (jflag != -1) {
+ status = pcap_set_tstamp_type(pc, jflag);
+ if (status < 0)
+ error("%s: Can't set time stamp type: %s",
+ device, pcap_statustostr(status));
+ else if (status > 0)
+ warning("When trying to set timestamp type '%s' on %s: %s",
+ pcap_tstamp_type_val_to_name(jflag), device,
+ pcap_statustostr(status));
+ }
+#endif
+ status = pcap_activate(pc);
+ if (status < 0) {
+ /*
+ * pcap_activate() failed.
+ */
+ cp = pcap_geterr(pc);
+ if (status == PCAP_ERROR)
+ error("%s", cp);
+ else if (status == PCAP_ERROR_NO_SUCH_DEVICE) {
+ /*
+ * Return an error for our caller to handle.
+ */
+ snprintf(ebuf, PCAP_ERRBUF_SIZE, "%s: %s\n(%s)",
+ device, pcap_statustostr(status), cp);
+ } else if (status == PCAP_ERROR_PERM_DENIED && *cp != '\0')
+ error("%s: %s\n(%s)", device,
+ pcap_statustostr(status), cp);
+#ifdef __FreeBSD__
+ else if (status == PCAP_ERROR_RFMON_NOTSUP &&
+ strncmp(device, "wlan", 4) == 0) {
+ char parent[8], newdev[8];
+ char sysctl[32];
+ size_t s = sizeof(parent);
+
+ snprintf(sysctl, sizeof(sysctl),
+ "net.wlan.%d.%%parent", atoi(device + 4));
+ sysctlbyname(sysctl, parent, &s, NULL, 0);
+ strlcpy(newdev, device, sizeof(newdev));
+ /* Suggest a new wlan device. */
+ /* FIXME: incrementing the index this way is not going to work well
+ * when the index is 9 or greater but the only consequence in this
+ * specific case would be an error message that looks a bit odd.
+ */
+ newdev[strlen(newdev)-1]++;
+ error("%s is not a monitor mode VAP\n"
+ "To create a new monitor mode VAP use:\n"
+ " ifconfig %s create wlandev %s wlanmode monitor\n"
+ "and use %s as the tcpdump interface",
+ device, newdev, parent, newdev);
+ }
+#endif
+ else
+ error("%s: %s", device,
+ pcap_statustostr(status));
+ pcap_close(pc);
+ return (NULL);
+ } else if (status > 0) {
+ /*
+ * pcap_activate() succeeded, but it's warning us
+ * of a problem it had.
+ */
+ cp = pcap_geterr(pc);
+ if (status == PCAP_WARNING)
+ warning("%s", cp);
+ else if (status == PCAP_WARNING_PROMISC_NOTSUP &&
+ *cp != '\0')
+ warning("%s: %s\n(%s)", device,
+ pcap_statustostr(status), cp);
+ else
+ warning("%s: %s", device,
+ pcap_statustostr(status));
+ }
+#ifdef HAVE_PCAP_SETDIRECTION
+ if (Qflag != -1) {
+ status = pcap_setdirection(pc, Qflag);
+ if (status != 0)
+ error("%s: pcap_setdirection() failed: %s",
+ device, pcap_geterr(pc));
+ }
+#endif /* HAVE_PCAP_SETDIRECTION */
+#else /* HAVE_PCAP_CREATE */
+ *ebuf = '\0';
+ /*
+ * If no snapshot length was specified, or a length of 0 was
+ * specified, default to 256KB.
+ */
+ if (ndo->ndo_snaplen == 0)
+ ndo->ndo_snaplen = MAXIMUM_SNAPLEN;
+ pc = pcap_open_live(device, ndo->ndo_snaplen, !pflag, timeout, ebuf);
+ if (pc == NULL) {
+ /*
+ * If this failed with "No such device", that means
+ * the interface doesn't exist; return NULL, so that
+ * the caller can see whether the device name is
+ * actually an interface index.
+ */
+ if (strstr(ebuf, "No such device") != NULL)
+ return (NULL);
+ error("%s", ebuf);
+ }
+ if (*ebuf)
+ warning("%s", ebuf);
+#endif /* HAVE_PCAP_CREATE */
+
+ return (pc);
+}
+
+int
+main(int argc, char **argv)
+{
+ int cnt, op, i;
+ bpf_u_int32 localnet = 0, netmask = 0;
+ char *cp, *infile, *cmdbuf, *device, *RFileName, *VFileName, *WFileName;
+ char *endp;
+ pcap_handler callback;
+ int dlt;
+ const char *dlt_name;
+ struct bpf_program fcode;
+#ifndef _WIN32
+ void (*oldhandler)(int);
+#endif
+ struct dump_info dumpinfo;
+ u_char *pcap_userdata;
+ char ebuf[PCAP_ERRBUF_SIZE];
+ char VFileLine[PATH_MAX + 1];
+ const char *username = NULL;
+#ifndef _WIN32
+ const char *chroot_dir = NULL;
+#endif
+ char *ret = NULL;
+ char *end;
+#ifdef HAVE_PCAP_FINDALLDEVS
+ pcap_if_t *devlist;
+ long devnum;
+#endif
+ int status;
+ FILE *VFile;
+#ifdef HAVE_CAPSICUM
+ cap_rights_t rights;
+ int cansandbox;
+#endif /* HAVE_CAPSICUM */
+ int Oflag = 1; /* run filter code optimizer */
+ int yflag_dlt = -1;
+ const char *yflag_dlt_name = NULL;
+ int print = 0;
+
+ netdissect_options Ndo;
+ netdissect_options *ndo = &Ndo;
+
+ /*
+ * Initialize the netdissect code.
+ */
+ if (nd_init(ebuf, sizeof(ebuf)) == -1)
+ error("%s", ebuf);
+
+ memset(ndo, 0, sizeof(*ndo));
+ ndo_set_function_pointers(ndo);
+
+ cnt = -1;
+ device = NULL;
+ infile = NULL;
+ RFileName = NULL;
+ VFileName = NULL;
+ VFile = NULL;
+ WFileName = NULL;
+ dlt = -1;
+ if ((cp = strrchr(argv[0], PATH_SEPARATOR)) != NULL)
+ ndo->program_name = program_name = cp + 1;
+ else
+ ndo->program_name = program_name = argv[0];
+
+#if defined(HAVE_PCAP_WSOCKINIT)
+ if (pcap_wsockinit() != 0)
+ error("Attempting to initialize Winsock failed");
+#elif defined(HAVE_WSOCKINIT)
+ if (wsockinit() != 0)
+ error("Attempting to initialize Winsock failed");
+#endif
+
+ /*
+ * On platforms where the CPU doesn't support unaligned loads,
+ * force unaligned accesses to abort with SIGBUS, rather than
+ * being fixed up (slowly) by the OS kernel; on those platforms,
+ * misaligned accesses are bugs, and we want tcpdump to crash so
+ * that the bugs are reported.
+ */
+ if (abort_on_misalignment(ebuf, sizeof(ebuf)) < 0)
+ error("%s", ebuf);
+
+ while (
+ (op = getopt_long(argc, argv, SHORTOPTS, longopts, NULL)) != -1)
+ switch (op) {
+
+ case 'a':
+ /* compatibility for old -a */
+ break;
+
+ case 'A':
+ ++ndo->ndo_Aflag;
+ break;
+
+ case 'b':
+ ++ndo->ndo_bflag;
+ break;
+
+#if defined(HAVE_PCAP_CREATE) || defined(_WIN32)
+ case 'B':
+ Bflag = atoi(optarg)*1024;
+ if (Bflag <= 0)
+ error("invalid packet buffer size %s", optarg);
+ break;
+#endif /* defined(HAVE_PCAP_CREATE) || defined(_WIN32) */
+
+ case 'c':
+ cnt = atoi(optarg);
+ if (cnt <= 0)
+ error("invalid packet count %s", optarg);
+ break;
+
+ case 'C':
+ errno = 0;
+#ifdef HAVE_PCAP_DUMP_FTELL64
+ Cflag = strtoint64_t(optarg, &endp, 10);
+#else
+ Cflag = strtol(optarg, &endp, 10);
+#endif
+ if (endp == optarg || *endp != '\0' || errno != 0
+ || Cflag <= 0)
+ error("invalid file size %s", optarg);
+ /*
+ * Will multiplying it by 1000000 overflow?
+ */
+#ifdef HAVE_PCAP_DUMP_FTELL64
+ if (Cflag > INT64_T_CONSTANT(0x7fffffffffffffff) / 1000000)
+#else
+ if (Cflag > LONG_MAX / 1000000)
+#endif
+ error("file size %s is too large", optarg);
+ Cflag *= 1000000;
+ break;
+
+ case 'd':
+ ++dflag;
+ break;
+
+#ifdef HAVE_PCAP_FINDALLDEVS
+ case 'D':
+ Dflag++;
+ break;
+#endif
+
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+ case OPTION_LIST_REMOTE_INTERFACES:
+ remote_interfaces_source = optarg;
+ break;
+#endif
+
+ case 'L':
+ Lflag++;
+ break;
+
+ case 'e':
+ ++ndo->ndo_eflag;
+ break;
+
+ case 'E':
+#ifndef HAVE_LIBCRYPTO
+ warning("crypto code not compiled in");
+#endif
+ ndo->ndo_espsecret = optarg;
+ break;
+
+ case 'f':
+ ++ndo->ndo_fflag;
+ break;
+
+ case 'F':
+ infile = optarg;
+ break;
+
+ case 'G':
+ Gflag = atoi(optarg);
+ if (Gflag < 0)
+ error("invalid number of seconds %s", optarg);
+
+ /* We will create one file initially. */
+ Gflag_count = 0;
+
+ /* Grab the current time for rotation use. */
+ if ((Gflag_time = time(NULL)) == (time_t)-1) {
+ error("%s: can't get current time: %s",
+ __func__, pcap_strerror(errno));
+ }
+ break;
+
+ case 'h':
+ print_usage(stdout);
+ exit_tcpdump(S_SUCCESS);
+ break;
+
+ case 'H':
+ ++ndo->ndo_Hflag;
+ break;
+
+ case 'i':
+ device = optarg;
+ break;
+
+#ifdef HAVE_PCAP_CREATE
+ case 'I':
+ ++Iflag;
+ break;
+#endif /* HAVE_PCAP_CREATE */
+
+#ifdef HAVE_PCAP_SET_TSTAMP_TYPE
+ case 'j':
+ jflag = pcap_tstamp_type_name_to_val(optarg);
+ if (jflag < 0)
+ error("invalid time stamp type %s", optarg);
+ break;
+
+ case 'J':
+ Jflag++;
+ break;
+#endif
+
+ case 'l':
+#ifdef _WIN32
+ /*
+ * _IOLBF is the same as _IOFBF in Microsoft's C
+ * libraries; the only alternative they offer
+ * is _IONBF.
+ *
+ * XXX - this should really be checking for MSVC++,
+ * not _WIN32, if, for example, MinGW has its own
+ * C library that is more UNIX-compatible.
+ */
+ setvbuf(stdout, NULL, _IONBF, 0);
+#else /* _WIN32 */
+#ifdef HAVE_SETLINEBUF
+ setlinebuf(stdout);
+#else
+ setvbuf(stdout, NULL, _IOLBF, 0);
+#endif
+#endif /* _WIN32 */
+ lflag = 1;
+ break;
+
+ case 'K':
+ ++ndo->ndo_Kflag;
+ break;
+
+ case 'm':
+ if (nd_have_smi_support()) {
+ if (nd_load_smi_module(optarg, ebuf, sizeof(ebuf)) == -1)
+ error("%s", ebuf);
+ } else {
+ (void)fprintf(stderr, "%s: ignoring option `-m %s' ",
+ program_name, optarg);
+ (void)fprintf(stderr, "(no libsmi support)\n");
+ }
+ break;
+
+ case 'M':
+ /* TCP-MD5 shared secret */
+#ifndef HAVE_LIBCRYPTO
+ warning("crypto code not compiled in");
+#endif
+ ndo->ndo_sigsecret = optarg;
+ break;
+
+ case 'n':
+ ++ndo->ndo_nflag;
+ break;
+
+ case 'N':
+ ++ndo->ndo_Nflag;
+ break;
+
+ case 'O':
+ Oflag = 0;
+ break;
+
+ case 'p':
+ ++pflag;
+ break;
+
+ case 'q':
+ ++ndo->ndo_qflag;
+ ++ndo->ndo_suppress_default_print;
+ break;
+
+#ifdef HAVE_PCAP_SETDIRECTION
+ case 'Q':
+ if (ascii_strcasecmp(optarg, "in") == 0)
+ Qflag = PCAP_D_IN;
+ else if (ascii_strcasecmp(optarg, "out") == 0)
+ Qflag = PCAP_D_OUT;
+ else if (ascii_strcasecmp(optarg, "inout") == 0)
+ Qflag = PCAP_D_INOUT;
+ else
+ error("unknown capture direction `%s'", optarg);
+ break;
+#endif /* HAVE_PCAP_SETDIRECTION */
+
+ case 'r':
+ RFileName = optarg;
+ break;
+
+ case 's':
+ ndo->ndo_snaplen = (int)strtol(optarg, &end, 0);
+ if (optarg == end || *end != '\0'
+ || ndo->ndo_snaplen < 0 || ndo->ndo_snaplen > MAXIMUM_SNAPLEN)
+ error("invalid snaplen %s (must be >= 0 and <= %d)",
+ optarg, MAXIMUM_SNAPLEN);
+ break;
+
+ case 'S':
+ ++ndo->ndo_Sflag;
+ break;
+
+ case 't':
+ ++ndo->ndo_tflag;
+ break;
+
+ case 'T':
+ if (ascii_strcasecmp(optarg, "vat") == 0)
+ ndo->ndo_packettype = PT_VAT;
+ else if (ascii_strcasecmp(optarg, "wb") == 0)
+ ndo->ndo_packettype = PT_WB;
+ else if (ascii_strcasecmp(optarg, "rpc") == 0)
+ ndo->ndo_packettype = PT_RPC;
+ else if (ascii_strcasecmp(optarg, "rtp") == 0)
+ ndo->ndo_packettype = PT_RTP;
+ else if (ascii_strcasecmp(optarg, "rtcp") == 0)
+ ndo->ndo_packettype = PT_RTCP;
+ else if (ascii_strcasecmp(optarg, "snmp") == 0)
+ ndo->ndo_packettype = PT_SNMP;
+ else if (ascii_strcasecmp(optarg, "cnfp") == 0)
+ ndo->ndo_packettype = PT_CNFP;
+ else if (ascii_strcasecmp(optarg, "tftp") == 0)
+ ndo->ndo_packettype = PT_TFTP;
+ else if (ascii_strcasecmp(optarg, "aodv") == 0)
+ ndo->ndo_packettype = PT_AODV;
+ else if (ascii_strcasecmp(optarg, "carp") == 0)
+ ndo->ndo_packettype = PT_CARP;
+ else if (ascii_strcasecmp(optarg, "radius") == 0)
+ ndo->ndo_packettype = PT_RADIUS;
+ else if (ascii_strcasecmp(optarg, "zmtp1") == 0)
+ ndo->ndo_packettype = PT_ZMTP1;
+ else if (ascii_strcasecmp(optarg, "vxlan") == 0)
+ ndo->ndo_packettype = PT_VXLAN;
+ else if (ascii_strcasecmp(optarg, "pgm") == 0)
+ ndo->ndo_packettype = PT_PGM;
+ else if (ascii_strcasecmp(optarg, "pgm_zmtp1") == 0)
+ ndo->ndo_packettype = PT_PGM_ZMTP1;
+ else if (ascii_strcasecmp(optarg, "lmp") == 0)
+ ndo->ndo_packettype = PT_LMP;
+ else if (ascii_strcasecmp(optarg, "resp") == 0)
+ ndo->ndo_packettype = PT_RESP;
+ else if (ascii_strcasecmp(optarg, "ptp") == 0)
+ ndo->ndo_packettype = PT_PTP;
+ else if (ascii_strcasecmp(optarg, "someip") == 0)
+ ndo->ndo_packettype = PT_SOMEIP;
+ else if (ascii_strcasecmp(optarg, "domain") == 0)
+ ndo->ndo_packettype = PT_DOMAIN;
+ else
+ error("unknown packet type `%s'", optarg);
+ break;
+
+ case 'u':
+ ++ndo->ndo_uflag;
+ break;
+
+#ifdef HAVE_PCAP_DUMP_FLUSH
+ case 'U':
+ ++Uflag;
+ break;
+#endif
+
+ case 'v':
+ ++ndo->ndo_vflag;
+ break;
+
+ case 'V':
+ VFileName = optarg;
+ break;
+
+ case 'w':
+ WFileName = optarg;
+ break;
+
+ case 'W':
+ Wflag = atoi(optarg);
+ if (Wflag <= 0)
+ error("invalid number of output files %s", optarg);
+ WflagChars = getWflagChars(Wflag);
+ break;
+
+ case 'x':
+ ++ndo->ndo_xflag;
+ ++ndo->ndo_suppress_default_print;
+ break;
+
+ case 'X':
+ ++ndo->ndo_Xflag;
+ ++ndo->ndo_suppress_default_print;
+ break;
+
+ case 'y':
+ yflag_dlt_name = optarg;
+ yflag_dlt =
+ pcap_datalink_name_to_val(yflag_dlt_name);
+ if (yflag_dlt < 0)
+ error("invalid data link type %s", yflag_dlt_name);
+ break;
+
+#ifdef HAVE_PCAP_SET_PARSER_DEBUG
+ case 'Y':
+ {
+ /* Undocumented flag */
+ pcap_set_parser_debug(1);
+ }
+ break;
+#endif
+ case 'z':
+ zflag = optarg;
+ break;
+
+ case 'Z':
+ username = optarg;
+ break;
+
+ case '#':
+ ndo->ndo_packet_number = 1;
+ break;
+
+ case OPTION_VERSION:
+ print_version(stdout);
+ exit_tcpdump(S_SUCCESS);
+ break;
+
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ case OPTION_TSTAMP_PRECISION:
+ ndo->ndo_tstamp_precision = tstamp_precision_from_string(optarg);
+ if (ndo->ndo_tstamp_precision < 0)
+ error("unsupported time stamp precision");
+ break;
+#endif
+
+#ifdef HAVE_PCAP_SET_IMMEDIATE_MODE
+ case OPTION_IMMEDIATE_MODE:
+ immediate_mode = 1;
+ break;
+#endif
+
+ case OPTION_PRINT:
+ print = 1;
+ break;
+
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ case OPTION_TSTAMP_MICRO:
+ ndo->ndo_tstamp_precision = PCAP_TSTAMP_PRECISION_MICRO;
+ break;
+
+ case OPTION_TSTAMP_NANO:
+ ndo->ndo_tstamp_precision = PCAP_TSTAMP_PRECISION_NANO;
+ break;
+#endif
+
+ case OPTION_FP_TYPE:
+ /*
+ * Print out the type of floating-point arithmetic
+ * we're doing; it's probably IEEE, unless somebody
+ * tries to run this on a VAX, but the precision
+ * may differ (e.g., it might be 32-bit, 64-bit,
+ * or 80-bit).
+ */
+ float_type_check(0x4e93312d);
+ return 0;
+
+ case OPTION_COUNT:
+ count_mode = 1;
+ break;
+
+ default:
+ print_usage(stderr);
+ exit_tcpdump(S_ERR_HOST_PROGRAM);
+ /* NOTREACHED */
+ }
+
+#ifdef HAVE_PCAP_FINDALLDEVS
+ if (Dflag)
+ show_devices_and_exit();
+#endif
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+ if (remote_interfaces_source != NULL)
+ show_remote_devices_and_exit();
+#endif
+
+#if defined(DLT_LINUX_SLL2) && defined(HAVE_PCAP_SET_DATALINK)
+/* Set default linktype DLT_LINUX_SLL2 when capturing on the "any" device */
+ if (device != NULL &&
+ strncmp (device, "any", strlen("any")) == 0
+ && yflag_dlt == -1)
+ yflag_dlt = DLT_LINUX_SLL2;
+#endif
+
+ switch (ndo->ndo_tflag) {
+
+ case 0: /* Default */
+ case 1: /* No time stamp */
+ case 2: /* Unix timeval style */
+ case 3: /* Microseconds/nanoseconds since previous packet */
+ case 4: /* Date + Default */
+ case 5: /* Microseconds/nanoseconds since first packet */
+ break;
+
+ default: /* Not supported */
+ error("only -t, -tt, -ttt, -tttt and -ttttt are supported");
+ break;
+ }
+
+ if (ndo->ndo_fflag != 0 && (VFileName != NULL || RFileName != NULL))
+ error("-f can not be used with -V or -r");
+
+ if (VFileName != NULL && RFileName != NULL)
+ error("-V and -r are mutually exclusive.");
+
+ /*
+ * If we're printing dissected packets to the standard output,
+ * and either the standard output is a terminal or we're doing
+ * "line" buffering, set the capture timeout to .1 second rather
+ * than 1 second, as the user's probably expecting to see packets
+ * pop up immediately shortly after they arrive.
+ *
+ * XXX - would there be some value appropriate for all cases,
+ * based on, say, the buffer size and packet input rate?
+ */
+ if ((WFileName == NULL || print) && (isatty(1) || lflag))
+ timeout = 100;
+
+#ifdef WITH_CHROOT
+ /* if run as root, prepare for chrooting */
+ if (getuid() == 0 || geteuid() == 0) {
+ /* future extensibility for cmd-line arguments */
+ if (!chroot_dir)
+ chroot_dir = WITH_CHROOT;
+ }
+#endif
+
+#ifdef WITH_USER
+ /* if run as root, prepare for dropping root privileges */
+ if (getuid() == 0 || geteuid() == 0) {
+ /* Run with '-Z root' to restore old behaviour */
+ if (!username)
+ username = WITH_USER;
+ }
+#endif
+
+ if (RFileName != NULL || VFileName != NULL) {
+ /*
+ * If RFileName is non-null, it's the pathname of a
+ * savefile to read. If VFileName is non-null, it's
+ * the pathname of a file containing a list of pathnames
+ * (one per line) of savefiles to read.
+ *
+ * In either case, we're reading a savefile, not doing
+ * a live capture.
+ */
+#ifndef _WIN32
+ /*
+ * We don't need network access, so relinquish any set-UID
+ * or set-GID privileges we have (if any).
+ *
+ * We do *not* want set-UID privileges when opening a
+ * trace file, as that might let the user read other
+ * people's trace files (especially if we're set-UID
+ * root).
+ */
+ if (setgid(getgid()) != 0 || setuid(getuid()) != 0 )
+ fprintf(stderr, "Warning: setgid/setuid failed !\n");
+#endif /* _WIN32 */
+ if (VFileName != NULL) {
+ if (VFileName[0] == '-' && VFileName[1] == '\0')
+ VFile = stdin;
+ else
+ VFile = fopen(VFileName, "r");
+
+ if (VFile == NULL)
+ error("Unable to open file: %s\n", pcap_strerror(errno));
+
+ ret = get_next_file(VFile, VFileLine);
+ if (!ret)
+ error("Nothing in %s\n", VFileName);
+ RFileName = VFileLine;
+ }
+
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ pd = pcap_open_offline_with_tstamp_precision(RFileName,
+ ndo->ndo_tstamp_precision, ebuf);
+#else
+ pd = pcap_open_offline(RFileName, ebuf);
+#endif
+
+ if (pd == NULL)
+ error("%s", ebuf);
+#ifdef HAVE_CAPSICUM
+ cap_rights_init(&rights, CAP_READ);
+ if (cap_rights_limit(fileno(pcap_file(pd)), &rights) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit pcap descriptor");
+ }
+#endif
+ dlt = pcap_datalink(pd);
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ fprintf(stderr, "reading from file %s", RFileName);
+ if (dlt_name == NULL) {
+ fprintf(stderr, ", link-type %u", dlt);
+ } else {
+ fprintf(stderr, ", link-type %s (%s)", dlt_name,
+ pcap_datalink_val_to_description(dlt));
+ }
+ fprintf(stderr, ", snapshot length %d\n", pcap_snapshot(pd));
+#ifdef DLT_LINUX_SLL2
+ if (dlt == DLT_LINUX_SLL2)
+ fprintf(stderr, "Warning: interface names might be incorrect\n");
+#endif
+ } else if (dflag && !device) {
+ int dump_dlt = DLT_EN10MB;
+ /*
+ * We're dumping the compiled code without an explicit
+ * device specification. (If a device is specified, we
+ * definitely want to open it to use the DLT of that device.)
+ * Either default to DLT_EN10MB with a warning, or use
+ * the user-specified value if supplied.
+ */
+ /*
+ * If no snapshot length was specified, or a length of 0 was
+ * specified, default to 256KB.
+ */
+ if (ndo->ndo_snaplen == 0)
+ ndo->ndo_snaplen = MAXIMUM_SNAPLEN;
+ /*
+ * If a DLT was specified with the -y flag, use that instead.
+ */
+ if (yflag_dlt != -1)
+ dump_dlt = yflag_dlt;
+ else
+ fprintf(stderr, "Warning: assuming Ethernet\n");
+ pd = pcap_open_dead(dump_dlt, ndo->ndo_snaplen);
+ } else {
+ /*
+ * We're doing a live capture.
+ */
+ if (device == NULL) {
+ /*
+ * No interface was specified. Pick one.
+ */
+#ifdef HAVE_PCAP_FINDALLDEVS
+ /*
+ * Find the list of interfaces, and pick
+ * the first interface.
+ */
+ if (pcap_findalldevs(&devlist, ebuf) == -1)
+ error("%s", ebuf);
+ if (devlist == NULL)
+ error("no interfaces available for capture");
+ device = strdup(devlist->name);
+ pcap_freealldevs(devlist);
+#else /* HAVE_PCAP_FINDALLDEVS */
+ /*
+ * Use whatever interface pcap_lookupdev()
+ * chooses.
+ */
+ device = pcap_lookupdev(ebuf);
+ if (device == NULL)
+ error("%s", ebuf);
+#endif
+ }
+
+ /*
+ * Try to open the interface with the specified name.
+ */
+ pd = open_interface(device, ndo, ebuf);
+ if (pd == NULL) {
+ /*
+ * That failed. If we can get a list of
+ * interfaces, and the interface name
+ * is purely numeric, try to use it as
+ * a 1-based index in the list of
+ * interfaces.
+ */
+#ifdef HAVE_PCAP_FINDALLDEVS
+ devnum = parse_interface_number(device);
+ if (devnum == -1) {
+ /*
+ * It's not a number; just report
+ * the open error and fail.
+ */
+ error("%s", ebuf);
+ }
+
+ /*
+ * OK, it's a number; try to find the
+ * interface with that index, and try
+ * to open it.
+ *
+ * find_interface_by_number() exits if it
+ * couldn't be found.
+ */
+ device = find_interface_by_number(device, devnum);
+ pd = open_interface(device, ndo, ebuf);
+ if (pd == NULL)
+ error("%s", ebuf);
+#else /* HAVE_PCAP_FINDALLDEVS */
+ /*
+ * We can't get a list of interfaces; just
+ * fail.
+ */
+ error("%s", ebuf);
+#endif /* HAVE_PCAP_FINDALLDEVS */
+ }
+
+ /*
+ * Let user own process after capture device has
+ * been opened.
+ */
+#ifndef _WIN32
+ if (setgid(getgid()) != 0 || setuid(getuid()) != 0)
+ fprintf(stderr, "Warning: setgid/setuid failed !\n");
+#endif /* _WIN32 */
+#if !defined(HAVE_PCAP_CREATE) && defined(_WIN32)
+ if(Bflag != 0)
+ if(pcap_setbuff(pd, Bflag)==-1){
+ error("%s", pcap_geterr(pd));
+ }
+#endif /* !defined(HAVE_PCAP_CREATE) && defined(_WIN32) */
+ if (Lflag)
+ show_dlts_and_exit(pd, device);
+ if (yflag_dlt >= 0) {
+#ifdef HAVE_PCAP_SET_DATALINK
+ if (pcap_set_datalink(pd, yflag_dlt) < 0)
+ error("%s", pcap_geterr(pd));
+#else
+ /*
+ * We don't actually support changing the
+ * data link type, so we only let them
+ * set it to what it already is.
+ */
+ if (yflag_dlt != pcap_datalink(pd)) {
+ error("%s is not one of the DLTs supported by this device\n",
+ yflag_dlt_name);
+ }
+#endif
+ (void)fprintf(stderr, "%s: data link type %s\n",
+ program_name,
+ pcap_datalink_val_to_name(yflag_dlt));
+ (void)fflush(stderr);
+ }
+ i = pcap_snapshot(pd);
+ if (ndo->ndo_snaplen < i) {
+ if (ndo->ndo_snaplen != 0)
+ warning("snaplen raised from %d to %d", ndo->ndo_snaplen, i);
+ ndo->ndo_snaplen = i;
+ } else if (ndo->ndo_snaplen > i) {
+ warning("snaplen lowered from %d to %d", ndo->ndo_snaplen, i);
+ ndo->ndo_snaplen = i;
+ }
+ if(ndo->ndo_fflag != 0) {
+ if (pcap_lookupnet(device, &localnet, &netmask, ebuf) < 0) {
+ warning("foreign (-f) flag used but: %s", ebuf);
+ }
+ }
+
+ }
+ if (infile)
+ cmdbuf = read_infile(infile);
+ else
+ cmdbuf = copy_argv(&argv[optind]);
+
+#ifdef HAVE_PCAP_SET_OPTIMIZER_DEBUG
+ pcap_set_optimizer_debug(dflag);
+#endif
+ if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
+ error("%s", pcap_geterr(pd));
+ if (dflag) {
+ bpf_dump(&fcode, dflag);
+ pcap_close(pd);
+ free(cmdbuf);
+ pcap_freecode(&fcode);
+ exit_tcpdump(S_SUCCESS);
+ }
+
+#ifdef HAVE_CASPER
+ if (!ndo->ndo_nflag)
+ capdns = capdns_setup();
+#endif /* HAVE_CASPER */
+
+ init_print(ndo, localnet, netmask);
+
+#ifndef _WIN32
+ (void)setsignal(SIGPIPE, cleanup);
+ (void)setsignal(SIGTERM, cleanup);
+#endif /* _WIN32 */
+ (void)setsignal(SIGINT, cleanup);
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
+ (void)setsignal(SIGCHLD, child_cleanup);
+#endif
+ /* Cooperate with nohup(1) */
+#ifndef _WIN32
+ if ((oldhandler = setsignal(SIGHUP, cleanup)) != SIG_DFL)
+ (void)setsignal(SIGHUP, oldhandler);
+#endif /* _WIN32 */
+
+#ifndef _WIN32
+ /*
+ * If a user name was specified with "-Z", attempt to switch to
+ * that user's UID. This would probably be used with sudo,
+ * to allow tcpdump to be run in a special restricted
+ * account (if you just want to allow users to open capture
+ * devices, and can't just give users that permission,
+ * you'd make tcpdump set-UID or set-GID).
+ *
+ * Tcpdump doesn't necessarily write only to one savefile;
+ * the general only way to allow a -Z instance to write to
+ * savefiles as the user under whose UID it's run, rather
+ * than as the user specified with -Z, would thus be to switch
+ * to the original user ID before opening a capture file and
+ * then switch back to the -Z user ID after opening the savefile.
+ * Switching to the -Z user ID only after opening the first
+ * savefile doesn't handle the general case.
+ */
+
+ if (getuid() == 0 || geteuid() == 0) {
+#ifdef HAVE_LIBCAP_NG
+ /* Initialize capng */
+ capng_clear(CAPNG_SELECT_BOTH);
+ if (username) {
+DIAG_OFF_CLANG(assign-enum)
+ capng_updatev(
+ CAPNG_ADD,
+ CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+ CAP_SETUID,
+ CAP_SETGID,
+ -1);
+DIAG_ON_CLANG(assign-enum)
+ }
+ if (chroot_dir) {
+DIAG_OFF_CLANG(assign-enum)
+ capng_update(
+ CAPNG_ADD,
+ CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+ CAP_SYS_CHROOT
+ );
+DIAG_ON_CLANG(assign-enum)
+ }
+
+ if (WFileName) {
+DIAG_OFF_CLANG(assign-enum)
+ capng_update(
+ CAPNG_ADD,
+ CAPNG_PERMITTED | CAPNG_EFFECTIVE,
+ CAP_DAC_OVERRIDE
+ );
+DIAG_ON_CLANG(assign-enum)
+ }
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+ if (username || chroot_dir)
+ droproot(username, chroot_dir);
+
+ }
+#endif /* _WIN32 */
+
+ if (pcap_setfilter(pd, &fcode) < 0)
+ error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ if (RFileName == NULL && VFileName == NULL && pcap_fileno(pd) != -1) {
+ static const unsigned long cmds[] = { BIOCGSTATS, BIOCROTZBUF };
+
+ /*
+ * The various libpcap devices use a combination of
+ * read (bpf), ioctl (bpf, netmap), poll (netmap)
+ * so we add the relevant access rights.
+ */
+ cap_rights_init(&rights, CAP_IOCTL, CAP_READ, CAP_EVENT);
+ if (cap_rights_limit(pcap_fileno(pd), &rights) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit pcap descriptor");
+ }
+ if (cap_ioctls_limit(pcap_fileno(pd), cmds,
+ sizeof(cmds) / sizeof(cmds[0])) < 0 && errno != ENOSYS) {
+ error("unable to limit ioctls on pcap descriptor");
+ }
+ }
+#endif
+ if (WFileName) {
+ /* Do not exceed the default PATH_MAX for files. */
+ dumpinfo.CurrentFileName = (char *)malloc(PATH_MAX + 1);
+
+ if (dumpinfo.CurrentFileName == NULL)
+ error("malloc of dumpinfo.CurrentFileName");
+
+ /* We do not need numbering for dumpfiles if Cflag isn't set. */
+ if (Cflag != 0)
+ MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, WflagChars);
+ else
+ MakeFilename(dumpinfo.CurrentFileName, WFileName, 0, 0);
+
+ pdd = pcap_dump_open(pd, dumpinfo.CurrentFileName);
+#ifdef HAVE_LIBCAP_NG
+ /* Give up CAP_DAC_OVERRIDE capability.
+ * Only allow it to be restored if the -C or -G flag have been
+ * set since we may need to create more files later on.
+ */
+ capng_update(
+ CAPNG_DROP,
+ (Cflag || Gflag ? 0 : CAPNG_PERMITTED)
+ | CAPNG_EFFECTIVE,
+ CAP_DAC_OVERRIDE
+ );
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+ if (pdd == NULL)
+ error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ set_dumper_capsicum_rights(pdd);
+#endif
+ if (Cflag != 0 || Gflag != 0) {
+#ifdef HAVE_CAPSICUM
+ dumpinfo.WFileName = strdup(basename(WFileName));
+ if (dumpinfo.WFileName == NULL) {
+ error("Unable to allocate memory for file %s",
+ WFileName);
+ }
+ dumpinfo.dirfd = open(dirname(WFileName),
+ O_DIRECTORY | O_RDONLY);
+ if (dumpinfo.dirfd < 0) {
+ error("unable to open directory %s",
+ dirname(WFileName));
+ }
+ cap_rights_init(&rights, CAP_CREATE, CAP_FCNTL,
+ CAP_FTRUNCATE, CAP_LOOKUP, CAP_SEEK, CAP_WRITE);
+ if (cap_rights_limit(dumpinfo.dirfd, &rights) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit directory rights");
+ }
+ if (cap_fcntls_limit(dumpinfo.dirfd, CAP_FCNTL_GETFL) < 0 &&
+ errno != ENOSYS) {
+ error("unable to limit dump descriptor fcntls");
+ }
+#else /* !HAVE_CAPSICUM */
+ dumpinfo.WFileName = WFileName;
+#endif
+ callback = dump_packet_and_trunc;
+ dumpinfo.pd = pd;
+ dumpinfo.pdd = pdd;
+ pcap_userdata = (u_char *)&dumpinfo;
+ } else {
+ callback = dump_packet;
+ dumpinfo.WFileName = WFileName;
+ dumpinfo.pd = pd;
+ dumpinfo.pdd = pdd;
+ pcap_userdata = (u_char *)&dumpinfo;
+ }
+ if (print) {
+ dlt = pcap_datalink(pd);
+ ndo->ndo_if_printer = get_if_printer(dlt);
+ dumpinfo.ndo = ndo;
+ } else
+ dumpinfo.ndo = NULL;
+
+#ifdef HAVE_PCAP_DUMP_FLUSH
+ if (Uflag)
+ pcap_dump_flush(pdd);
+#endif
+ } else {
+ dlt = pcap_datalink(pd);
+ ndo->ndo_if_printer = get_if_printer(dlt);
+ callback = print_packet;
+ pcap_userdata = (u_char *)ndo;
+ }
+
+#ifdef SIGNAL_REQ_INFO
+ /*
+ * We can't get statistics when reading from a file rather
+ * than capturing from a device.
+ */
+ if (RFileName == NULL)
+ (void)setsignal(SIGNAL_REQ_INFO, requestinfo);
+#endif
+#ifdef SIGNAL_FLUSH_PCAP
+ (void)setsignal(SIGNAL_FLUSH_PCAP, flushpcap);
+#endif
+
+ if (ndo->ndo_vflag > 0 && WFileName && RFileName == NULL && !print) {
+ /*
+ * When capturing to a file, if "--print" wasn't specified,
+ *"-v" means tcpdump should, once per second,
+ * "v"erbosely report the number of packets captured.
+ * Except when reading from a file, because -r, -w and -v
+ * together used to make a corner case, in which pcap_loop()
+ * errored due to EINTR (see GH #155 for details).
+ */
+#ifdef _WIN32
+ /*
+ * https://blogs.msdn.microsoft.com/oldnewthing/20151230-00/?p=92741
+ *
+ * suggests that this dates back to W2K.
+ *
+ * I don't know what a "long wait" is, but we'll assume
+ * that printing the stats could be a "long wait".
+ */
+ CreateTimerQueueTimer(&timer_handle, NULL,
+ verbose_stats_dump, NULL, 1000, 1000,
+ WT_EXECUTEDEFAULT|WT_EXECUTELONGFUNCTION);
+ setvbuf(stderr, NULL, _IONBF, 0);
+#else /* _WIN32 */
+ /*
+ * Assume this is UN*X, and that it has setitimer(); that
+ * dates back to UNIX 95.
+ */
+ struct itimerval timer;
+ (void)setsignal(SIGALRM, verbose_stats_dump);
+ timer.it_interval.tv_sec = 1;
+ timer.it_interval.tv_usec = 0;
+ timer.it_value.tv_sec = 1;
+ timer.it_value.tv_usec = 1;
+ setitimer(ITIMER_REAL, &timer, NULL);
+#endif /* _WIN32 */
+ }
+
+ if (RFileName == NULL) {
+ /*
+ * Live capture (if -V was specified, we set RFileName
+ * to a file from the -V file). Print a message to
+ * the standard error on UN*X.
+ */
+ if (!ndo->ndo_vflag && !WFileName) {
+ (void)fprintf(stderr,
+ "%s: verbose output suppressed, use -v[v]... for full protocol decode\n",
+ program_name);
+ } else
+ (void)fprintf(stderr, "%s: ", program_name);
+ dlt = pcap_datalink(pd);
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ (void)fprintf(stderr, "listening on %s", device);
+ if (dlt_name == NULL) {
+ (void)fprintf(stderr, ", link-type %u", dlt);
+ } else {
+ (void)fprintf(stderr, ", link-type %s (%s)", dlt_name,
+ pcap_datalink_val_to_description(dlt));
+ }
+ (void)fprintf(stderr, ", snapshot length %d bytes\n", ndo->ndo_snaplen);
+ (void)fflush(stderr);
+ }
+
+#ifdef HAVE_CAPSICUM
+ cansandbox = (VFileName == NULL && zflag == NULL);
+#ifdef HAVE_CASPER
+ cansandbox = (cansandbox && (ndo->ndo_nflag || capdns != NULL));
+#else
+ cansandbox = (cansandbox && ndo->ndo_nflag);
+#endif /* HAVE_CASPER */
+ if (cansandbox && cap_enter() < 0 && errno != ENOSYS)
+ error("unable to enter the capability mode");
+#endif /* HAVE_CAPSICUM */
+
+ do {
+ status = pcap_loop(pd, cnt, callback, pcap_userdata);
+ if (WFileName == NULL) {
+ /*
+ * We're printing packets. Flush the printed output,
+ * so it doesn't get intermingled with error output.
+ */
+ if (status == -2) {
+ /*
+ * We got interrupted, so perhaps we didn't
+ * manage to finish a line we were printing.
+ * Print an extra newline, just in case.
+ */
+ putchar('\n');
+ }
+ (void)fflush(stdout);
+ }
+ if (status == -2) {
+ /*
+ * We got interrupted. If we are reading multiple
+ * files (via -V) set these so that we stop.
+ */
+ VFileName = NULL;
+ ret = NULL;
+ }
+ if (status == -1) {
+ /*
+ * Error. Report it.
+ */
+ (void)fprintf(stderr, "%s: pcap_loop: %s\n",
+ program_name, pcap_geterr(pd));
+ }
+ if (RFileName == NULL) {
+ /*
+ * We're doing a live capture. Report the capture
+ * statistics.
+ */
+ info(1);
+ }
+ pcap_close(pd);
+ if (VFileName != NULL) {
+ ret = get_next_file(VFile, VFileLine);
+ if (ret) {
+ int new_dlt;
+
+ RFileName = VFileLine;
+ pd = pcap_open_offline(RFileName, ebuf);
+ if (pd == NULL)
+ error("%s", ebuf);
+#ifdef HAVE_CAPSICUM
+ cap_rights_init(&rights, CAP_READ);
+ if (cap_rights_limit(fileno(pcap_file(pd)),
+ &rights) < 0 && errno != ENOSYS) {
+ error("unable to limit pcap descriptor");
+ }
+#endif
+ new_dlt = pcap_datalink(pd);
+ if (new_dlt != dlt) {
+ /*
+ * The new file has a different
+ * link-layer header type from the
+ * previous one.
+ */
+ if (WFileName != NULL) {
+ /*
+ * We're writing raw packets
+ * that match the filter to
+ * a pcap file. pcap files
+ * don't support multiple
+ * different link-layer
+ * header types, so we fail
+ * here.
+ */
+ error("%s: new dlt does not match original", RFileName);
+ }
+
+ /*
+ * We're printing the decoded packets;
+ * switch to the new DLT.
+ *
+ * To do that, we need to change
+ * the printer, change the DLT name,
+ * and recompile the filter with
+ * the new DLT.
+ */
+ dlt = new_dlt;
+ ndo->ndo_if_printer = get_if_printer(dlt);
+ if (pcap_compile(pd, &fcode, cmdbuf, Oflag, netmask) < 0)
+ error("%s", pcap_geterr(pd));
+ }
+
+ /*
+ * Set the filter on the new file.
+ */
+ if (pcap_setfilter(pd, &fcode) < 0)
+ error("%s", pcap_geterr(pd));
+
+ /*
+ * Report the new file.
+ */
+ dlt_name = pcap_datalink_val_to_name(dlt);
+ fprintf(stderr, "reading from file %s", RFileName);
+ if (dlt_name == NULL) {
+ fprintf(stderr, ", link-type %u", dlt);
+ } else {
+ fprintf(stderr, ", link-type %s (%s)",
+ dlt_name,
+ pcap_datalink_val_to_description(dlt));
+ }
+ fprintf(stderr, ", snapshot length %d\n", pcap_snapshot(pd));
+ }
+ }
+ }
+ while (ret != NULL);
+
+ if (count_mode && RFileName != NULL)
+ fprintf(stdout, "%u packet%s\n", packets_captured,
+ PLURAL_SUFFIX(packets_captured));
+
+ free(cmdbuf);
+ pcap_freecode(&fcode);
+ exit_tcpdump(status == -1 ? 1 : 0);
+}
+
+/*
+ * Catch a signal.
+ */
+static void
+(*setsignal (int sig, void (*func)(int)))(int)
+{
+#ifdef _WIN32
+ return (signal(sig, func));
+#else
+ struct sigaction old, new;
+
+ memset(&new, 0, sizeof(new));
+ new.sa_handler = func;
+ if (sig == SIGCHLD)
+ new.sa_flags = SA_RESTART;
+ if (sigaction(sig, &new, &old) < 0)
+ return (SIG_ERR);
+ return (old.sa_handler);
+#endif
+}
+
+/* make a clean exit on interrupts */
+static void
+cleanup(int signo _U_)
+{
+#ifdef _WIN32
+ if (timer_handle != INVALID_HANDLE_VALUE) {
+ DeleteTimerQueueTimer(NULL, timer_handle, NULL);
+ CloseHandle(timer_handle);
+ timer_handle = INVALID_HANDLE_VALUE;
+ }
+#else /* _WIN32 */
+ struct itimerval timer;
+
+ timer.it_interval.tv_sec = 0;
+ timer.it_interval.tv_usec = 0;
+ timer.it_value.tv_sec = 0;
+ timer.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &timer, NULL);
+#endif /* _WIN32 */
+
+#ifdef HAVE_PCAP_BREAKLOOP
+ /*
+ * We have "pcap_breakloop()"; use it, so that we do as little
+ * as possible in the signal handler (it's probably not safe
+ * to do anything with standard I/O streams in a signal handler -
+ * the ANSI C standard doesn't say it is).
+ */
+ pcap_breakloop(pd);
+#else
+ /*
+ * We don't have "pcap_breakloop()"; this isn't safe, but
+ * it's the best we can do. Print the summary if we're
+ * not reading from a savefile - i.e., if we're doing a
+ * live capture - and exit.
+ */
+ if (pd != NULL && pcap_file(pd) == NULL) {
+ /*
+ * We got interrupted, so perhaps we didn't
+ * manage to finish a line we were printing.
+ * Print an extra newline, just in case.
+ */
+ putchar('\n');
+ (void)fflush(stdout);
+ info(1);
+ }
+ exit_tcpdump(S_SUCCESS);
+#endif
+}
+
+/*
+ On windows, we do not use a fork, so we do not care less about
+ waiting a child processes to die
+ */
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
+static void
+child_cleanup(int signo _U_)
+{
+ wait(NULL);
+}
+#endif /* HAVE_FORK && HAVE_VFORK */
+
+static void
+info(int verbose)
+{
+ struct pcap_stat stats;
+
+ /*
+ * Older versions of libpcap didn't set ps_ifdrop on some
+ * platforms; initialize it to 0 to handle that.
+ */
+ stats.ps_ifdrop = 0;
+ if (pcap_stats(pd, &stats) < 0) {
+ (void)fprintf(stderr, "pcap_stats: %s\n", pcap_geterr(pd));
+ infoprint = 0;
+ return;
+ }
+
+ if (!verbose)
+ fprintf(stderr, "%s: ", program_name);
+
+ (void)fprintf(stderr, "%u packet%s captured", packets_captured,
+ PLURAL_SUFFIX(packets_captured));
+ if (!verbose)
+ fputs(", ", stderr);
+ else
+ putc('\n', stderr);
+ (void)fprintf(stderr, "%u packet%s received by filter", stats.ps_recv,
+ PLURAL_SUFFIX(stats.ps_recv));
+ if (!verbose)
+ fputs(", ", stderr);
+ else
+ putc('\n', stderr);
+ (void)fprintf(stderr, "%u packet%s dropped by kernel", stats.ps_drop,
+ PLURAL_SUFFIX(stats.ps_drop));
+ if (stats.ps_ifdrop != 0) {
+ if (!verbose)
+ fputs(", ", stderr);
+ else
+ putc('\n', stderr);
+ (void)fprintf(stderr, "%u packet%s dropped by interface\n",
+ stats.ps_ifdrop, PLURAL_SUFFIX(stats.ps_ifdrop));
+ } else
+ putc('\n', stderr);
+ infoprint = 0;
+}
+
+#if defined(HAVE_FORK) || defined(HAVE_VFORK)
+#ifdef HAVE_FORK
+#define fork_subprocess() fork()
+#else
+#define fork_subprocess() vfork()
+#endif
+static void
+compress_savefile(const char *filename)
+{
+ pid_t child;
+
+ child = fork_subprocess();
+ if (child == -1) {
+ fprintf(stderr,
+ "compress_savefile: fork failed: %s\n",
+ pcap_strerror(errno));
+ return;
+ }
+ if (child != 0) {
+ /* Parent process. */
+ return;
+ }
+
+ /*
+ * Child process.
+ * Set to lowest priority so that this doesn't disturb the capture.
+ */
+#ifdef NZERO
+ setpriority(PRIO_PROCESS, 0, NZERO - 1);
+#else
+ setpriority(PRIO_PROCESS, 0, 19);
+#endif
+ if (execlp(zflag, zflag, filename, (char *)NULL) == -1)
+ fprintf(stderr,
+ "compress_savefile: execlp(%s, %s) failed: %s\n",
+ zflag,
+ filename,
+ pcap_strerror(errno));
+#ifdef HAVE_FORK
+ exit(S_ERR_HOST_PROGRAM);
+#else
+ _exit(S_ERR_HOST_PROGRAM);
+#endif
+}
+#else /* HAVE_FORK && HAVE_VFORK */
+static void
+compress_savefile(const char *filename)
+{
+ fprintf(stderr,
+ "compress_savefile failed. Functionality not implemented under your system\n");
+}
+#endif /* HAVE_FORK && HAVE_VFORK */
+
+static void
+dump_packet_and_trunc(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
+{
+ struct dump_info *dump_info;
+
+ ++packets_captured;
+
+ ++infodelay;
+
+ dump_info = (struct dump_info *)user;
+
+ /*
+ * XXX - this won't force the file to rotate on the specified time
+ * boundary, but it will rotate on the first packet received after the
+ * specified Gflag number of seconds. Note: if a Gflag time boundary
+ * and a Cflag size boundary coincide, the time rotation will occur
+ * first thereby cancelling the Cflag boundary (since the file should
+ * be 0).
+ */
+ if (Gflag != 0) {
+ /* Check if it is time to rotate */
+ time_t t;
+
+ /* Get the current time */
+ if ((t = time(NULL)) == (time_t)-1) {
+ error("%s: can't get current_time: %s",
+ __func__, pcap_strerror(errno));
+ }
+
+
+ /* If the time is greater than the specified window, rotate */
+ if (t - Gflag_time >= Gflag) {
+#ifdef HAVE_CAPSICUM
+ FILE *fp;
+ int fd;
+#endif
+
+ /* Update the Gflag_time */
+ Gflag_time = t;
+ /* Update Gflag_count */
+ Gflag_count++;
+ /*
+ * Close the current file and open a new one.
+ */
+ pcap_dump_close(dump_info->pdd);
+
+ /*
+ * Compress the file we just closed, if the user asked for it
+ */
+ if (zflag != NULL)
+ compress_savefile(dump_info->CurrentFileName);
+
+ /*
+ * Check to see if we've exceeded the Wflag (when
+ * not using Cflag).
+ */
+ if (Cflag == 0 && Wflag > 0 && Gflag_count >= Wflag) {
+ (void)fprintf(stderr, "Maximum file limit reached: %d\n",
+ Wflag);
+ info(1);
+ exit_tcpdump(S_SUCCESS);
+ /* NOTREACHED */
+ }
+ if (dump_info->CurrentFileName != NULL)
+ free(dump_info->CurrentFileName);
+ /* Allocate space for max filename + \0. */
+ dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
+ if (dump_info->CurrentFileName == NULL)
+ error("dump_packet_and_trunc: malloc");
+ /*
+ * Gflag was set otherwise we wouldn't be here. Reset the count
+ * so multiple files would end with 1,2,3 in the filename.
+ * The counting is handled with the -C flow after this.
+ */
+ Cflag_count = 0;
+
+ /*
+ * This is always the first file in the Cflag
+ * rotation: e.g. 0
+ * We also don't need numbering if Cflag is not set.
+ */
+ if (Cflag != 0)
+ MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0,
+ WflagChars);
+ else
+ MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, 0, 0);
+
+#ifdef HAVE_LIBCAP_NG
+ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+#ifdef HAVE_CAPSICUM
+ fd = openat(dump_info->dirfd,
+ dump_info->CurrentFileName,
+ O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ if (fd < 0) {
+ error("unable to open file %s",
+ dump_info->CurrentFileName);
+ }
+ fp = fdopen(fd, "w");
+ if (fp == NULL) {
+ error("unable to fdopen file %s",
+ dump_info->CurrentFileName);
+ }
+ dump_info->pdd = pcap_dump_fopen(dump_info->pd, fp);
+#else /* !HAVE_CAPSICUM */
+ dump_info->pdd = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
+#endif
+#ifdef HAVE_LIBCAP_NG
+ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+ if (dump_info->pdd == NULL)
+ error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ set_dumper_capsicum_rights(dump_info->pdd);
+#endif
+ }
+ }
+
+ /*
+ * XXX - this won't prevent capture files from getting
+ * larger than Cflag - the last packet written to the
+ * file could put it over Cflag.
+ */
+ if (Cflag != 0) {
+#ifdef HAVE_PCAP_DUMP_FTELL64
+ int64_t size = pcap_dump_ftell64(dump_info->pdd);
+#else
+ /*
+ * XXX - this only handles a Cflag value > 2^31-1 on
+ * LP64 platforms; to handle ILP32 (32-bit UN*X and
+ * Windows) or LLP64 (64-bit Windows) would require
+ * a version of libpcap with pcap_dump_ftell64().
+ */
+ long size = pcap_dump_ftell(dump_info->pdd);
+#endif
+
+ if (size == -1)
+ error("ftell fails on output file");
+ if (size > Cflag) {
+#ifdef HAVE_CAPSICUM
+ FILE *fp;
+ int fd;
+#endif
+
+ /*
+ * Close the current file and open a new one.
+ */
+ pcap_dump_close(dump_info->pdd);
+
+ /*
+ * Compress the file we just closed, if the user
+ * asked for it.
+ */
+ if (zflag != NULL)
+ compress_savefile(dump_info->CurrentFileName);
+
+ Cflag_count++;
+ if (Wflag > 0) {
+ if (Cflag_count >= Wflag)
+ Cflag_count = 0;
+ }
+ if (dump_info->CurrentFileName != NULL)
+ free(dump_info->CurrentFileName);
+ dump_info->CurrentFileName = (char *)malloc(PATH_MAX + 1);
+ if (dump_info->CurrentFileName == NULL)
+ error("%s: malloc", __func__);
+ MakeFilename(dump_info->CurrentFileName, dump_info->WFileName, Cflag_count, WflagChars);
+#ifdef HAVE_LIBCAP_NG
+ capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+#ifdef HAVE_CAPSICUM
+ fd = openat(dump_info->dirfd, dump_info->CurrentFileName,
+ O_CREAT | O_WRONLY | O_TRUNC, 0644);
+ if (fd < 0) {
+ error("unable to open file %s",
+ dump_info->CurrentFileName);
+ }
+ fp = fdopen(fd, "w");
+ if (fp == NULL) {
+ error("unable to fdopen file %s",
+ dump_info->CurrentFileName);
+ }
+ dump_info->pdd = pcap_dump_fopen(dump_info->pd, fp);
+#else /* !HAVE_CAPSICUM */
+ dump_info->pdd = pcap_dump_open(dump_info->pd, dump_info->CurrentFileName);
+#endif
+#ifdef HAVE_LIBCAP_NG
+ capng_update(CAPNG_DROP, CAPNG_EFFECTIVE, CAP_DAC_OVERRIDE);
+ capng_apply(CAPNG_SELECT_BOTH);
+#endif /* HAVE_LIBCAP_NG */
+ if (dump_info->pdd == NULL)
+ error("%s", pcap_geterr(pd));
+#ifdef HAVE_CAPSICUM
+ set_dumper_capsicum_rights(dump_info->pdd);
+#endif
+ }
+ }
+
+ pcap_dump((u_char *)dump_info->pdd, h, sp);
+#ifdef HAVE_PCAP_DUMP_FLUSH
+ if (Uflag)
+ pcap_dump_flush(dump_info->pdd);
+#endif
+
+ if (dump_info->ndo != NULL)
+ pretty_print_packet(dump_info->ndo, h, sp, packets_captured);
+
+ --infodelay;
+ if (infoprint)
+ info(0);
+}
+
+static void
+dump_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
+{
+ struct dump_info *dump_info;
+
+ ++packets_captured;
+
+ ++infodelay;
+
+ dump_info = (struct dump_info *)user;
+
+ pcap_dump((u_char *)dump_info->pdd, h, sp);
+#ifdef HAVE_PCAP_DUMP_FLUSH
+ if (Uflag)
+ pcap_dump_flush(dump_info->pdd);
+#endif
+
+ if (dump_info->ndo != NULL)
+ pretty_print_packet(dump_info->ndo, h, sp, packets_captured);
+
+ --infodelay;
+ if (infoprint)
+ info(0);
+}
+
+static void
+print_packet(u_char *user, const struct pcap_pkthdr *h, const u_char *sp)
+{
+ ++packets_captured;
+
+ ++infodelay;
+
+ if (!count_mode)
+ pretty_print_packet((netdissect_options *)user, h, sp, packets_captured);
+
+ --infodelay;
+ if (infoprint)
+ info(0);
+}
+
+#ifdef SIGNAL_REQ_INFO
+static void
+requestinfo(int signo _U_)
+{
+ if (infodelay)
+ ++infoprint;
+ else
+ info(0);
+}
+#endif
+
+#ifdef SIGNAL_FLUSH_PCAP
+static void
+flushpcap(int signo _U_)
+{
+ if (pdd != NULL)
+ pcap_dump_flush(pdd);
+}
+#endif
+
+static void
+print_packets_captured (void)
+{
+ static u_int prev_packets_captured, first = 1;
+
+ if (infodelay == 0 && (first || packets_captured != prev_packets_captured)) {
+ fprintf(stderr, "Got %u\r", packets_captured);
+ first = 0;
+ prev_packets_captured = packets_captured;
+ }
+}
+
+/*
+ * Called once each second in verbose mode while dumping to file
+ */
+#ifdef _WIN32
+static void CALLBACK verbose_stats_dump(PVOID param _U_,
+ BOOLEAN timer_fired _U_)
+{
+ print_packets_captured();
+}
+#else /* _WIN32 */
+static void verbose_stats_dump(int sig _U_)
+{
+ print_packets_captured();
+}
+#endif /* _WIN32 */
+
+USES_APPLE_DEPRECATED_API
+static void
+print_version(FILE *f)
+{
+#ifndef HAVE_PCAP_LIB_VERSION
+ #ifdef HAVE_PCAP_VERSION
+ extern char pcap_version[];
+ #else /* HAVE_PCAP_VERSION */
+ static char pcap_version[] = "unknown";
+ #endif /* HAVE_PCAP_VERSION */
+#endif /* HAVE_PCAP_LIB_VERSION */
+ const char *smi_version_string;
+
+ (void)fprintf(f, "%s version " PACKAGE_VERSION "\n", program_name);
+#ifdef HAVE_PCAP_LIB_VERSION
+ (void)fprintf(f, "%s\n", pcap_lib_version());
+#else /* HAVE_PCAP_LIB_VERSION */
+ (void)fprintf(f, "libpcap version %s\n", pcap_version);
+#endif /* HAVE_PCAP_LIB_VERSION */
+
+#if defined(HAVE_LIBCRYPTO) && defined(SSLEAY_VERSION)
+ (void)fprintf (f, "%s\n", SSLeay_version(SSLEAY_VERSION));
+#endif
+
+ smi_version_string = nd_smi_version_string();
+ if (smi_version_string != NULL)
+ (void)fprintf (f, "SMI-library: %s\n", smi_version_string);
+
+#if defined(__SANITIZE_ADDRESS__)
+ (void)fprintf (f, "Compiled with AddressSanitizer/GCC.\n");
+#elif defined(__has_feature)
+# if __has_feature(address_sanitizer)
+ (void)fprintf (f, "Compiled with AddressSanitizer/Clang.\n");
+# elif __has_feature(memory_sanitizer)
+ (void)fprintf (f, "Compiled with MemorySanitizer/Clang.\n");
+# endif
+#endif /* __SANITIZE_ADDRESS__ or __has_feature */
+}
+USES_APPLE_RST
+
+static void
+print_usage(FILE *f)
+{
+ print_version(f);
+ (void)fprintf(f,
+"Usage: %s [-Abd" D_FLAG "efhH" I_FLAG J_FLAG "KlLnNOpqStu" U_FLAG "vxX#]" B_FLAG_USAGE " [ -c count ] [--count]\n", program_name);
+ (void)fprintf(f,
+"\t\t[ -C file_size ] [ -E algo:secret ] [ -F file ] [ -G seconds ]\n");
+ (void)fprintf(f,
+"\t\t[ -i interface ]" IMMEDIATE_MODE_USAGE j_FLAG_USAGE "\n");
+#ifdef HAVE_PCAP_FINDALLDEVS_EX
+ (void)fprintf(f,
+"\t\t" LIST_REMOTE_INTERFACES_USAGE "\n");
+#endif
+#ifdef USE_LIBSMI
+ (void)fprintf(f,
+"\t\t" m_FLAG_USAGE "\n");
+#endif
+ (void)fprintf(f,
+"\t\t[ -M secret ] [ --number ] [ --print ]" Q_FLAG_USAGE "\n");
+ (void)fprintf(f,
+"\t\t[ -r file ] [ -s snaplen ] [ -T type ] [ --version ]\n");
+ (void)fprintf(f,
+"\t\t[ -V file ] [ -w file ] [ -W filecount ] [ -y datalinktype ]\n");
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ (void)fprintf(f,
+"\t\t[ --time-stamp-precision precision ] [ --micro ] [ --nano ]\n");
+#endif
+ (void)fprintf(f,
+"\t\t[ -z postrotate-command ] [ -Z user ] [ expression ]\n");
+}
diff --git a/timeval-operations.h b/timeval-operations.h
new file mode 100644
index 0000000..177027d
--- /dev/null
+++ b/timeval-operations.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015 The TCPDUMP project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef netdissect_timeval_operations_h
+#define netdissect_timeval_operations_h
+
+/* Operations on timevals. */
+
+#define ND_MICRO_PER_SEC 1000000
+#define ND_NANO_PER_SEC 1000000000
+
+#define netdissect_timevalclear(tvp) ((tvp)->tv_sec = (tvp)->tv_usec = 0)
+
+#define netdissect_timevalisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec)
+
+#define netdissect_timevalcmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_usec cmp (uvp)->tv_usec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+
+#define netdissect_timevaladd(tvp, uvp, vvp, nano_prec) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
+ if (nano_prec) { \
+ if ((vvp)->tv_usec >= ND_NANO_PER_SEC) { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_usec -= ND_NANO_PER_SEC; \
+ } \
+ } else { \
+ if ((vvp)->tv_usec >= ND_MICRO_PER_SEC) { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_usec -= ND_MICRO_PER_SEC; \
+ } \
+ } \
+ } while (0)
+
+#define netdissect_timevalsub(tvp, uvp, vvp, nano_prec) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += (nano_prec ? ND_NANO_PER_SEC : \
+ ND_MICRO_PER_SEC); \
+ } \
+ } while (0)
+
+#endif /* netdissect_timeval_operations_h */
diff --git a/udp.h b/udp.h
new file mode 100644
index 0000000..70d3315
--- /dev/null
+++ b/udp.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)udp.h 8.1 (Berkeley) 6/10/93
+ */
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr {
+ nd_uint16_t uh_sport; /* source port */
+ nd_uint16_t uh_dport; /* destination port */
+ nd_uint16_t uh_ulen; /* udp length */
+ nd_uint16_t uh_sum; /* udp checksum */
+};
+
+#ifndef NAMESERVER_PORT
+#define NAMESERVER_PORT 53
+#endif
+#ifndef BOOTPS_PORT
+#define BOOTPS_PORT 67 /* RFC951 */
+#endif
+#ifndef BOOTPC_PORT
+#define BOOTPC_PORT 68 /* RFC951 */
+#endif
+#ifndef TFTP_PORT
+#define TFTP_PORT 69 /*XXX*/
+#endif
+#ifndef KERBEROS_PORT
+#define KERBEROS_PORT 88 /*XXX*/
+#endif
+#ifndef SUNRPC_PORT
+#define SUNRPC_PORT 111 /*XXX*/
+#endif
+#ifndef NTP_PORT
+#define NTP_PORT 123 /*XXX*/
+#endif
+#ifndef NETBIOS_NS_PORT
+#define NETBIOS_NS_PORT 137 /* RFC 1001, RFC 1002 */
+#endif
+#ifndef NETBIOS_DGRAM_PORT
+#define NETBIOS_DGRAM_PORT 138 /* RFC 1001, RFC 1002 */
+#endif
+#ifndef SNMP_PORT
+#define SNMP_PORT 161 /*XXX*/
+#endif
+#ifndef SNMPTRAP_PORT
+#define SNMPTRAP_PORT 162 /*XXX*/
+#endif
+#ifndef PTP_EVENT_PORT
+#define PTP_EVENT_PORT 319 /* IANA */
+#endif
+#ifndef PTP_GENERAL_PORT
+#define PTP_GENERAL_PORT 320 /* IANA */
+#endif
+#ifndef CISCO_AUTORP_PORT
+#define CISCO_AUTORP_PORT 496 /*XXX*/
+#endif
+#ifndef ISAKMP_PORT
+#define ISAKMP_PORT 500 /*XXX*/
+#endif
+#ifndef SYSLOG_PORT
+#define SYSLOG_PORT 514 /* rfc3164 */
+#endif
+#ifndef RIP_PORT
+#define RIP_PORT 520 /*XXX*/
+#endif
+#ifndef RIPNG_PORT
+#define RIPNG_PORT 521 /* RFC 2080 */
+#endif
+#ifndef TIMED_PORT
+#define TIMED_PORT 525 /*XXX*/
+#endif
+#ifndef DHCP6_SERV_PORT
+#define DHCP6_SERV_PORT 546 /*XXX*/
+#endif
+#ifndef DHCP6_CLI_PORT
+#define DHCP6_CLI_PORT 547 /*XXX*/
+#endif
+#ifndef LDP_PORT
+#define LDP_PORT 646
+#endif
+#ifndef AQDV_PORT
+#define AODV_PORT 654 /*XXX*/
+#endif
+#ifndef OLSR_PORT
+#define OLSR_PORT 698 /* rfc3626 */
+#endif
+#ifndef LMP_PORT
+#define LMP_PORT 701 /* rfc4204 */
+#endif
+#ifndef KERBEROS_SEC_PORT
+#define KERBEROS_SEC_PORT 750 /*XXX - Kerberos v4 */
+#endif
+#ifndef LWRES_PORT
+#define LWRES_PORT 921 /*XXX*/
+#endif
+#ifndef VQP_PORT
+#define VQP_PORT 1589 /*XXX*/
+#endif
+#ifndef RADIUS_PORT
+#define RADIUS_PORT 1645 /*XXX*/
+#endif
+#ifndef RADIUS_ACCOUNTING_PORT
+#define RADIUS_ACCOUNTING_PORT 1646
+#endif
+#ifndef RADIUS_CISCO_COA_PORT
+#define RADIUS_CISCO_COA_PORT 1700
+#endif
+#ifndef L2TP_PORT
+#define L2TP_PORT 1701 /*XXX*/
+#endif
+#ifndef RADIUS_NEW_PORT
+#define RADIUS_NEW_PORT 1812 /*XXX*/
+#endif
+#ifndef RADIUS_NEW_ACCOUNTING_PORT
+#define RADIUS_NEW_ACCOUNTING_PORT 1813
+#endif
+#ifndef HSRP_PORT
+#define HSRP_PORT 1985 /*XXX*/
+#endif
+#ifndef ZEPHYR_SRV_PORT
+#define ZEPHYR_SRV_PORT 2103 /*XXX*/
+#endif
+#ifndef ZEPHYR_CLI_PORT
+#define ZEPHYR_CLT_PORT 2104 /*XXX*/
+#endif
+#ifndef VAT_PORT
+#define VAT_PORT 3456 /*XXX*/
+#endif
+#ifndef MPLS_LSP_PING_PORT
+#define MPLS_LSP_PING_PORT 3503 /* draft-ietf-mpls-lsp-ping-02.txt */
+#endif
+#ifndef BCM_LI_PORT
+#define BCM_LI_PORT 49152 /* SDK default */
+#endif
+#ifndef BFD_CONTROL_PORT
+#define BFD_CONTROL_PORT 3784 /* RFC 5881 */
+#endif
+#ifndef BFD_ECHO_PORT
+#define BFD_ECHO_PORT 3785 /* RFC 5881 */
+#endif
+#ifndef RADIUS_COA_PORT
+#define RADIUS_COA_PORT 3799 /* RFC 5176 */
+#endif
+#ifndef LISP_CONTROL_PORT
+#define LISP_CONTROL_PORT 4342 /* RFC 6830 */
+#endif
+#ifndef ISAKMP_PORT_NATT
+#define ISAKMP_PORT_NATT 4500 /* rfc3948 */
+#endif
+#ifndef WB_PORT
+#define WB_PORT 4567
+#endif
+#ifndef BFD_MULTIHOP_PORT
+#define BFD_MULTIHOP_PORT 4784 /* RFC 5883 */
+#endif
+#ifndef VXLAN_PORT
+#define VXLAN_PORT 4789 /* RFC 7348 */
+#endif
+#ifndef VXLAN_GPE_PORT
+#define VXLAN_GPE_PORT 4790 /* draft-ietf-nvo3-vxlan-gpe-01 */
+#endif
+#ifndef SIP_PORT
+#define SIP_PORT 5060
+#endif
+#ifndef MULTICASTDNS_PORT
+#define MULTICASTDNS_PORT 5353 /* RFC 6762 */
+#endif
+#ifndef AHCP_PORT
+#define AHCP_PORT 5359 /* draft-chroboczek-ahcp-00 */
+#endif
+#ifndef GENEVE_PORT
+#define GENEVE_PORT 6081 /* draft-gross-geneve-02 */
+#endif
+#ifndef SFLOW_PORT
+#define SFLOW_PORT 6343 /* https://sflow.org/developers/specifications.php */
+#endif
+#ifndef MPLS_PORT
+#define MPLS_PORT 6635 /* RFC 7510 */
+#endif
+#ifndef BABEL_PORT
+#define BABEL_PORT 6696 /* RFC 6126 errata */
+#endif
+#ifndef BABEL_PORT_OLD
+#define BABEL_PORT_OLD 6697 /* RFC 6126 */
+#endif
+#ifndef BFD_LAG_PORT
+#define BFD_LAG_PORT 6784 /* RFC 7310 */
+#endif
+#ifndef RX_PORT_LOW
+#define RX_PORT_LOW 7000 /*XXX*/
+#endif
+#ifndef RX_PORT_HIGH
+#define RX_PORT_HIGH 7009 /*XXX*/
+#endif
+#ifndef ISAKMP_PORT_USER1
+#define ISAKMP_PORT_USER1 7500 /*XXX - nonstandard*/
+#endif
+#ifndef HNCP_PORT
+#define HNCP_PORT 8231 /* RFC 7788 */
+#endif
+#ifndef OTV_PORT
+#define OTV_PORT 8472 /* draft-hasmit-otv-04 */
+#endif
+#ifndef ISAKMP_PORT_USER2
+#define ISAKMP_PORT_USER2 8500 /*XXX - nonstandard*/
+#endif
+#ifndef LWAPP_DATA_PORT
+#define LWAPP_DATA_PORT 12222 /* RFC 5412 */
+#endif
+#ifndef LWAPP_CONTROL_PORT
+#define LWAPP_CONTROL_PORT 12223 /* RFC 5412 */
+#endif
+#ifndef ZEP_PORT
+#define ZEP_PORT 17754 /* XXX */
+#endif
+#ifndef SOMEIP_PORT
+#define SOMEIP_PORT 30490 /* https://www.autosar.org/standards/foundation */
+#endif
diff --git a/util-print.c b/util-print.c
new file mode 100644
index 0000000..f9ea618
--- /dev/null
+++ b/util-print.c
@@ -0,0 +1,986 @@
+/*
+ * Copyright (c) 1990, 1991, 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * txtproto_print() derived from original code by Hannes Gredler
+ * (hannes@gredler.at):
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code
+ * distributions retain the above copyright notice and this paragraph
+ * in its entirety, and (2) distributions including binary code include
+ * the above copyright notice and this paragraph in its entirety in
+ * the documentation or other materials provided with the distribution.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT
+ * LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "netdissect-stdinc.h"
+
+#include <sys/stat.h>
+
+#ifdef HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "netdissect-ctype.h"
+
+#include "netdissect.h"
+#include "extract.h"
+#include "ascii_strcasecmp.h"
+#include "timeval-operations.h"
+
+#define TOKBUFSIZE 128
+
+enum date_flag { WITHOUT_DATE = 0, WITH_DATE = 1 };
+enum time_flag { UTC_TIME = 0, LOCAL_TIME = 1 };
+
+/*
+ * Print out a character, filtering out the non-printable ones
+ */
+void
+fn_print_char(netdissect_options *ndo, u_char c)
+{
+ if (!ND_ISASCII(c)) {
+ c = ND_TOASCII(c);
+ ND_PRINT("M-");
+ }
+ if (!ND_ASCII_ISPRINT(c)) {
+ c ^= 0x40; /* DEL to ?, others to alpha */
+ ND_PRINT("^");
+ }
+ ND_PRINT("%c", c);
+}
+
+/*
+ * Print a null-terminated string, filtering out non-printable characters.
+ * DON'T USE IT with a pointer on the packet buffer because there is no
+ * truncation check. For this use, see the nd_printX() functions below.
+ */
+void
+fn_print_str(netdissect_options *ndo, const u_char *s)
+{
+ while (*s != '\0') {
+ fn_print_char(ndo, *s);
+ s++;
+ }
+}
+
+/*
+ * Print out a null-terminated filename (or other ASCII string), part of
+ * the packet buffer.
+ * If ep is NULL, assume no truncation check is needed.
+ * Return true if truncated.
+ * Stop at ep (if given) or before the null char, whichever is first.
+ */
+int
+nd_print(netdissect_options *ndo,
+ const u_char *s, const u_char *ep)
+{
+ int ret;
+ u_char c;
+
+ ret = 1; /* assume truncated */
+ while (ep == NULL || s < ep) {
+ c = GET_U_1(s);
+ s++;
+ if (c == '\0') {
+ ret = 0;
+ break;
+ }
+ fn_print_char(ndo, c);
+ }
+ return(ret);
+}
+
+/*
+ * Print out a null-terminated filename (or other ASCII string) from
+ * a fixed-length field in the packet buffer, or from what remains of
+ * the packet.
+ *
+ * n is the length of the fixed-length field, or the number of bytes
+ * remaining in the packet based on its on-the-network length.
+ *
+ * If ep is non-null, it should point just past the last captured byte
+ * of the packet, e.g. ndo->ndo_snapend. If ep is NULL, we assume no
+ * truncation check, other than the checks of the field length/remaining
+ * packet data length, is needed.
+ *
+ * Return the number of bytes of string processed, including the
+ * terminating null, if not truncated; as the terminating null is
+ * included in the count, and as there must be a terminating null,
+ * this will always be non-zero. Return 0 if truncated.
+ */
+u_int
+nd_printztn(netdissect_options *ndo,
+ const u_char *s, u_int n, const u_char *ep)
+{
+ u_int bytes;
+ u_char c;
+
+ bytes = 0;
+ for (;;) {
+ if (n == 0 || (ep != NULL && s >= ep)) {
+ /*
+ * Truncated. This includes "no null before we
+ * got to the end of the fixed-length buffer or
+ * the end of the packet".
+ *
+ * XXX - BOOTP says "null-terminated", which
+ * means the maximum length of the string, in
+ * bytes, is 1 less than the size of the buffer,
+ * as there must always be a terminating null.
+ */
+ bytes = 0;
+ break;
+ }
+
+ c = GET_U_1(s);
+ s++;
+ bytes++;
+ n--;
+ if (c == '\0') {
+ /* End of string */
+ break;
+ }
+ fn_print_char(ndo, c);
+ }
+ return(bytes);
+}
+
+/*
+ * Print out a counted filename (or other ASCII string), part of
+ * the packet buffer.
+ * If ep is NULL, assume no truncation check is needed.
+ * Return true if truncated.
+ * Stop at ep (if given) or after n bytes, whichever is first.
+ */
+int
+nd_printn(netdissect_options *ndo,
+ const u_char *s, u_int n, const u_char *ep)
+{
+ u_char c;
+
+ while (n > 0 && (ep == NULL || s < ep)) {
+ n--;
+ c = GET_U_1(s);
+ s++;
+ fn_print_char(ndo, c);
+ }
+ return (n == 0) ? 0 : 1;
+}
+
+/*
+ * Print a null-padded filename (or other ASCII string), part of
+ * the packet buffer, filtering out non-printable characters.
+ * Stop if truncated (via GET_U_1/longjmp) or after n bytes or before
+ * the null char, whichever occurs first.
+ * The suffix comes from: j:longJmp, n:after N bytes, p:null-Padded.
+ */
+void
+nd_printjnp(netdissect_options *ndo, const u_char *s, u_int n)
+{
+ u_char c;
+
+ while (n > 0) {
+ c = GET_U_1(s);
+ if (c == '\0')
+ break;
+ fn_print_char(ndo, c);
+ n--;
+ s++;
+ }
+}
+
+/*
+ * Print the timestamp .FRAC part (Microseconds/nanoseconds)
+ */
+static void
+ts_frac_print(netdissect_options *ndo, long usec)
+{
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ switch (ndo->ndo_tstamp_precision) {
+
+ case PCAP_TSTAMP_PRECISION_MICRO:
+ ND_PRINT(".%06u", (unsigned)usec);
+ break;
+
+ case PCAP_TSTAMP_PRECISION_NANO:
+ ND_PRINT(".%09u", (unsigned)usec);
+ break;
+
+ default:
+ ND_PRINT(".{unknown}");
+ break;
+ }
+#else
+ ND_PRINT(".%06u", (unsigned)usec);
+#endif
+}
+
+/*
+ * Print the timestamp as [YY:MM:DD] HH:MM:SS.FRAC.
+ * if time_flag == LOCAL_TIME print local time else UTC/GMT time
+ * if date_flag == WITH_DATE print YY:MM:DD before HH:MM:SS.FRAC
+ */
+static void
+ts_date_hmsfrac_print(netdissect_options *ndo, long sec, long usec,
+ enum date_flag date_flag, enum time_flag time_flag)
+{
+ time_t Time = sec;
+ struct tm *tm;
+ char timestr[32];
+
+ if ((unsigned)sec & 0x80000000) {
+ ND_PRINT("[Error converting time]");
+ return;
+ }
+
+ if (time_flag == LOCAL_TIME)
+ tm = localtime(&Time);
+ else
+ tm = gmtime(&Time);
+
+ if (!tm) {
+ ND_PRINT("[Error converting time]");
+ return;
+ }
+ if (date_flag == WITH_DATE)
+ strftime(timestr, sizeof(timestr), "%Y-%m-%d %H:%M:%S", tm);
+ else
+ strftime(timestr, sizeof(timestr), "%H:%M:%S", tm);
+ ND_PRINT("%s", timestr);
+
+ ts_frac_print(ndo, usec);
+}
+
+/*
+ * Print the timestamp - Unix timeval style, as SECS.FRAC.
+ */
+static void
+ts_unix_print(netdissect_options *ndo, long sec, long usec)
+{
+ if ((unsigned)sec & 0x80000000) {
+ ND_PRINT("[Error converting time]");
+ return;
+ }
+
+ ND_PRINT("%u", (unsigned)sec);
+ ts_frac_print(ndo, usec);
+}
+
+/*
+ * Print the timestamp
+ */
+void
+ts_print(netdissect_options *ndo,
+ const struct timeval *tvp)
+{
+ static struct timeval tv_ref;
+ struct timeval tv_result;
+ int negative_offset;
+ int nano_prec;
+
+ switch (ndo->ndo_tflag) {
+
+ case 0: /* Default */
+ ts_date_hmsfrac_print(ndo, tvp->tv_sec, tvp->tv_usec,
+ WITHOUT_DATE, LOCAL_TIME);
+ ND_PRINT(" ");
+ break;
+
+ case 1: /* No time stamp */
+ break;
+
+ case 2: /* Unix timeval style */
+ ts_unix_print(ndo, tvp->tv_sec, tvp->tv_usec);
+ ND_PRINT(" ");
+ break;
+
+ case 3: /* Microseconds/nanoseconds since previous packet */
+ case 5: /* Microseconds/nanoseconds since first packet */
+#ifdef HAVE_PCAP_SET_TSTAMP_PRECISION
+ switch (ndo->ndo_tstamp_precision) {
+ case PCAP_TSTAMP_PRECISION_MICRO:
+ nano_prec = 0;
+ break;
+ case PCAP_TSTAMP_PRECISION_NANO:
+ nano_prec = 1;
+ break;
+ default:
+ nano_prec = 0;
+ break;
+ }
+#else
+ nano_prec = 0;
+#endif
+ if (!(netdissect_timevalisset(&tv_ref)))
+ tv_ref = *tvp; /* set timestamp for first packet */
+
+ negative_offset = netdissect_timevalcmp(tvp, &tv_ref, <);
+ if (negative_offset)
+ netdissect_timevalsub(&tv_ref, tvp, &tv_result, nano_prec);
+ else
+ netdissect_timevalsub(tvp, &tv_ref, &tv_result, nano_prec);
+
+ ND_PRINT((negative_offset ? "-" : " "));
+ ts_date_hmsfrac_print(ndo, tv_result.tv_sec, tv_result.tv_usec,
+ WITHOUT_DATE, UTC_TIME);
+ ND_PRINT(" ");
+
+ if (ndo->ndo_tflag == 3)
+ tv_ref = *tvp; /* set timestamp for previous packet */
+ break;
+
+ case 4: /* Date + Default */
+ ts_date_hmsfrac_print(ndo, tvp->tv_sec, tvp->tv_usec,
+ WITH_DATE, LOCAL_TIME);
+ ND_PRINT(" ");
+ break;
+ }
+}
+
+/*
+ * Print an unsigned relative number of seconds (e.g. hold time, prune timer)
+ * in the form 5m1s. This does no truncation, so 32230861 seconds
+ * is represented as 1y1w1d1h1m1s.
+ */
+void
+unsigned_relts_print(netdissect_options *ndo,
+ uint32_t secs)
+{
+ static const char *lengths[] = {"y", "w", "d", "h", "m", "s"};
+ static const u_int seconds[] = {31536000, 604800, 86400, 3600, 60, 1};
+ const char **l = lengths;
+ const u_int *s = seconds;
+
+ if (secs == 0) {
+ ND_PRINT("0s");
+ return;
+ }
+ while (secs > 0) {
+ if (secs >= *s) {
+ ND_PRINT("%u%s", secs / *s, *l);
+ secs -= (secs / *s) * *s;
+ }
+ s++;
+ l++;
+ }
+}
+
+/*
+ * Print a signed relative number of seconds (e.g. hold time, prune timer)
+ * in the form 5m1s. This does no truncation, so 32230861 seconds
+ * is represented as 1y1w1d1h1m1s.
+ */
+void
+signed_relts_print(netdissect_options *ndo,
+ int32_t secs)
+{
+ if (secs < 0) {
+ ND_PRINT("-");
+ if (secs == INT32_MIN) {
+ /*
+ * -2^31; you can't fit its absolute value into
+ * a 32-bit signed integer.
+ *
+ * Just directly pass said absolute value to
+ * unsigned_relts_print() directly.
+ *
+ * (XXX - does ISO C guarantee that -(-2^n),
+ * when calculated and cast to an n-bit unsigned
+ * integer type, will have the value 2^n?)
+ */
+ unsigned_relts_print(ndo, 2147483648U);
+ } else {
+ /*
+ * We now know -secs will fit into an int32_t;
+ * negate it and pass that to unsigned_relts_print().
+ */
+ unsigned_relts_print(ndo, -secs);
+ }
+ return;
+ }
+ unsigned_relts_print(ndo, secs);
+}
+
+/* Print the truncated string */
+void nd_print_trunc(netdissect_options *ndo)
+{
+ ND_PRINT(" [|%s]", ndo->ndo_protocol);
+}
+
+/* Print the protocol name */
+void nd_print_protocol(netdissect_options *ndo)
+{
+ ND_PRINT("%s", ndo->ndo_protocol);
+}
+
+/* Print the protocol name in caps (uppercases) */
+void nd_print_protocol_caps(netdissect_options *ndo)
+{
+ const char *p;
+ for (p = ndo->ndo_protocol; *p != '\0'; p++)
+ ND_PRINT("%c", ND_ASCII_TOUPPER(*p));
+}
+
+/* Print the invalid string */
+void nd_print_invalid(netdissect_options *ndo)
+{
+ ND_PRINT(" (invalid)");
+}
+
+/*
+ * this is a generic routine for printing unknown data;
+ * we pass on the linefeed plus indentation string to
+ * get a proper output - returns 0 on error
+ */
+
+int
+print_unknown_data(netdissect_options *ndo, const u_char *cp,
+ const char *ident, u_int len)
+{
+ u_int len_to_print;
+
+ len_to_print = len;
+ if (!ND_TTEST_LEN(cp, 0)) {
+ ND_PRINT("%sDissector error: print_unknown_data called with pointer past end of packet",
+ ident);
+ return(0);
+ }
+ if (ND_BYTES_AVAILABLE_AFTER(cp) < len_to_print)
+ len_to_print = ND_BYTES_AVAILABLE_AFTER(cp);
+ hex_print(ndo, ident, cp, len_to_print);
+ return(1); /* everything is ok */
+}
+
+/*
+ * Convert a token value to a string; use "fmt" if not found.
+ */
+const char *
+tok2strbuf(const struct tok *lp, const char *fmt,
+ u_int v, char *buf, size_t bufsize)
+{
+ if (lp != NULL) {
+ while (lp->s != NULL) {
+ if (lp->v == v)
+ return (lp->s);
+ ++lp;
+ }
+ }
+ if (fmt == NULL)
+ fmt = "#%d";
+
+ (void)snprintf(buf, bufsize, fmt, v);
+ return (const char *)buf;
+}
+
+/*
+ * Convert a token value to a string; use "fmt" if not found.
+ * Uses tok2strbuf() on one of four local static buffers of size TOKBUFSIZE
+ * in round-robin fashion.
+ */
+const char *
+tok2str(const struct tok *lp, const char *fmt,
+ u_int v)
+{
+ static char buf[4][TOKBUFSIZE];
+ static int idx = 0;
+ char *ret;
+
+ ret = buf[idx];
+ idx = (idx+1) & 3;
+ return tok2strbuf(lp, fmt, v, ret, sizeof(buf[0]));
+}
+
+/*
+ * Convert a bit token value to a string; use "fmt" if not found.
+ * this is useful for parsing bitfields, the output strings are separated
+ * if the s field is positive.
+ *
+ * A token matches iff it has one or more bits set and every bit that is set
+ * in the token is set in v. Consequently, a 0 token never matches.
+ */
+static char *
+bittok2str_internal(const struct tok *lp, const char *fmt,
+ u_int v, const char *sep)
+{
+ static char buf[1024+1]; /* our string buffer */
+ char *bufp = buf;
+ size_t space_left = sizeof(buf), string_size;
+ const char * sepstr = "";
+
+ while (lp != NULL && lp->s != NULL) {
+ if (lp->v && (v & lp->v) == lp->v) {
+ /* ok we have found something */
+ if (space_left <= 1)
+ return (buf); /* only enough room left for NUL, if that */
+ string_size = strlcpy(bufp, sepstr, space_left);
+ if (string_size >= space_left)
+ return (buf); /* we ran out of room */
+ bufp += string_size;
+ space_left -= string_size;
+ if (space_left <= 1)
+ return (buf); /* only enough room left for NUL, if that */
+ string_size = strlcpy(bufp, lp->s, space_left);
+ if (string_size >= space_left)
+ return (buf); /* we ran out of room */
+ bufp += string_size;
+ space_left -= string_size;
+ sepstr = sep;
+ }
+ lp++;
+ }
+
+ if (bufp == buf)
+ /* bummer - lets print the "unknown" message as advised in the fmt string if we got one */
+ (void)snprintf(buf, sizeof(buf), fmt == NULL ? "#%08x" : fmt, v);
+ return (buf);
+}
+
+/*
+ * Convert a bit token value to a string; use "fmt" if not found.
+ * this is useful for parsing bitfields, the output strings are not separated.
+ */
+char *
+bittok2str_nosep(const struct tok *lp, const char *fmt,
+ u_int v)
+{
+ return (bittok2str_internal(lp, fmt, v, ""));
+}
+
+/*
+ * Convert a bit token value to a string; use "fmt" if not found.
+ * this is useful for parsing bitfields, the output strings are comma separated.
+ */
+char *
+bittok2str(const struct tok *lp, const char *fmt,
+ u_int v)
+{
+ return (bittok2str_internal(lp, fmt, v, ", "));
+}
+
+/*
+ * Convert a value to a string using an array; the macro
+ * tok2strary() in <netdissect.h> is the public interface to
+ * this function and ensures that the second argument is
+ * correct for bounds-checking.
+ */
+const char *
+tok2strary_internal(const char **lp, int n, const char *fmt,
+ int v)
+{
+ static char buf[TOKBUFSIZE];
+
+ if (v >= 0 && v < n && lp[v] != NULL)
+ return lp[v];
+ if (fmt == NULL)
+ fmt = "#%d";
+ (void)snprintf(buf, sizeof(buf), fmt, v);
+ return (buf);
+}
+
+const struct tok *
+uint2tokary_internal(const struct uint_tokary dict[], const size_t size,
+ const u_int val)
+{
+ size_t i;
+ /* Try a direct lookup before the full scan. */
+ if (val < size && dict[val].uintval == val)
+ return dict[val].tokary; /* OK if NULL */
+ for (i = 0; i < size; i++)
+ if (dict[i].uintval == val)
+ return dict[i].tokary; /* OK if NULL */
+ return NULL;
+}
+
+/*
+ * Convert a 32-bit netmask to prefixlen if possible
+ * the function returns the prefix-len; if plen == -1
+ * then conversion was not possible;
+ */
+
+int
+mask2plen(uint32_t mask)
+{
+ uint32_t bitmasks[33] = {
+ 0x00000000,
+ 0x80000000, 0xc0000000, 0xe0000000, 0xf0000000,
+ 0xf8000000, 0xfc000000, 0xfe000000, 0xff000000,
+ 0xff800000, 0xffc00000, 0xffe00000, 0xfff00000,
+ 0xfff80000, 0xfffc0000, 0xfffe0000, 0xffff0000,
+ 0xffff8000, 0xffffc000, 0xffffe000, 0xfffff000,
+ 0xfffff800, 0xfffffc00, 0xfffffe00, 0xffffff00,
+ 0xffffff80, 0xffffffc0, 0xffffffe0, 0xfffffff0,
+ 0xfffffff8, 0xfffffffc, 0xfffffffe, 0xffffffff
+ };
+ int prefix_len = 32;
+
+ /* let's see if we can transform the mask into a prefixlen */
+ while (prefix_len >= 0) {
+ if (bitmasks[prefix_len] == mask)
+ break;
+ prefix_len--;
+ }
+ return (prefix_len);
+}
+
+int
+mask62plen(const u_char *mask)
+{
+ u_char bitmasks[9] = {
+ 0x00,
+ 0x80, 0xc0, 0xe0, 0xf0,
+ 0xf8, 0xfc, 0xfe, 0xff
+ };
+ int byte;
+ int cidr_len = 0;
+
+ for (byte = 0; byte < 16; byte++) {
+ u_int bits;
+
+ for (bits = 0; bits < (sizeof (bitmasks) / sizeof (bitmasks[0])); bits++) {
+ if (mask[byte] == bitmasks[bits]) {
+ cidr_len += bits;
+ break;
+ }
+ }
+
+ if (mask[byte] != 0xff)
+ break;
+ }
+ return (cidr_len);
+}
+
+/*
+ * Routine to print out information for text-based protocols such as FTP,
+ * HTTP, SMTP, RTSP, SIP, ....
+ */
+#define MAX_TOKEN 128
+
+/*
+ * Fetch a token from a packet, starting at the specified index,
+ * and return the length of the token.
+ *
+ * Returns 0 on error; yes, this is indistinguishable from an empty
+ * token, but an "empty token" isn't a valid token - it just means
+ * either a space character at the beginning of the line (this
+ * includes a blank line) or no more tokens remaining on the line.
+ */
+static int
+fetch_token(netdissect_options *ndo, const u_char *pptr, u_int idx, u_int len,
+ u_char *tbuf, size_t tbuflen)
+{
+ size_t toklen = 0;
+ u_char c;
+
+ for (; idx < len; idx++) {
+ if (!ND_TTEST_1(pptr + idx)) {
+ /* ran past end of captured data */
+ return (0);
+ }
+ c = GET_U_1(pptr + idx);
+ if (!ND_ISASCII(c)) {
+ /* not an ASCII character */
+ return (0);
+ }
+ if (c == ' ' || c == '\t' || c == '\r' || c == '\n') {
+ /* end of token */
+ break;
+ }
+ if (!ND_ASCII_ISPRINT(c)) {
+ /* not part of a command token or response code */
+ return (0);
+ }
+ if (toklen + 2 > tbuflen) {
+ /* no room for this character and terminating '\0' */
+ return (0);
+ }
+ tbuf[toklen] = c;
+ toklen++;
+ }
+ if (toklen == 0) {
+ /* no token */
+ return (0);
+ }
+ tbuf[toklen] = '\0';
+
+ /*
+ * Skip past any white space after the token, until we see
+ * an end-of-line (CR or LF).
+ */
+ for (; idx < len; idx++) {
+ if (!ND_TTEST_1(pptr + idx)) {
+ /* ran past end of captured data */
+ break;
+ }
+ c = GET_U_1(pptr + idx);
+ if (c == '\r' || c == '\n') {
+ /* end of line */
+ break;
+ }
+ if (!ND_ASCII_ISPRINT(c)) {
+ /* not a printable ASCII character */
+ break;
+ }
+ if (c != ' ' && c != '\t' && c != '\r' && c != '\n') {
+ /* beginning of next token */
+ break;
+ }
+ }
+ return (idx);
+}
+
+/*
+ * Scan a buffer looking for a line ending - LF or CR-LF.
+ * Return the index of the character after the line ending or 0 if
+ * we encounter a non-ASCII or non-printable character or don't find
+ * the line ending.
+ */
+static u_int
+print_txt_line(netdissect_options *ndo, const char *prefix,
+ const u_char *pptr, u_int idx, u_int len)
+{
+ u_int startidx;
+ u_int linelen;
+ u_char c;
+
+ startidx = idx;
+ while (idx < len) {
+ c = GET_U_1(pptr + idx);
+ if (c == '\n') {
+ /*
+ * LF without CR; end of line.
+ * Skip the LF and print the line, with the
+ * exception of the LF.
+ */
+ linelen = idx - startidx;
+ idx++;
+ goto print;
+ } else if (c == '\r') {
+ /* CR - any LF? */
+ if ((idx+1) >= len) {
+ /* not in this packet */
+ return (0);
+ }
+ if (GET_U_1(pptr + idx + 1) == '\n') {
+ /*
+ * CR-LF; end of line.
+ * Skip the CR-LF and print the line, with
+ * the exception of the CR-LF.
+ */
+ linelen = idx - startidx;
+ idx += 2;
+ goto print;
+ }
+
+ /*
+ * CR followed by something else; treat this
+ * as if it were binary data, and don't print
+ * it.
+ */
+ return (0);
+ } else if (!ND_ASCII_ISPRINT(c) && c != '\t') {
+ /*
+ * Not a printable ASCII character and not a tab;
+ * treat this as if it were binary data, and
+ * don't print it.
+ */
+ return (0);
+ }
+ idx++;
+ }
+
+ /*
+ * All printable ASCII, but no line ending after that point
+ * in the buffer; treat this as if it were truncated.
+ */
+ linelen = idx - startidx;
+ ND_PRINT("%s%.*s", prefix, (int)linelen, pptr + startidx);
+ nd_print_trunc(ndo);
+ return (0);
+
+print:
+ ND_PRINT("%s%.*s", prefix, (int)linelen, pptr + startidx);
+ return (idx);
+}
+
+/* Assign needed before calling txtproto_print(): ndo->ndo_protocol = "proto" */
+void
+txtproto_print(netdissect_options *ndo, const u_char *pptr, u_int len,
+ const char **cmds, u_int flags)
+{
+ u_int idx, eol;
+ u_char token[MAX_TOKEN+1];
+ const char *cmd;
+ int print_this = 0;
+
+ if (cmds != NULL) {
+ /*
+ * This protocol has more than just request and
+ * response lines; see whether this looks like a
+ * request or response and, if so, print it and,
+ * in verbose mode, print everything after it.
+ *
+ * This is for HTTP-like protocols, where we
+ * want to print requests and responses, but
+ * don't want to print continuations of request
+ * or response bodies in packets that don't
+ * contain the request or response line.
+ */
+ idx = fetch_token(ndo, pptr, 0, len, token, sizeof(token));
+ if (idx != 0) {
+ /* Is this a valid request name? */
+ while ((cmd = *cmds++) != NULL) {
+ if (ascii_strcasecmp((const char *)token, cmd) == 0) {
+ /* Yes. */
+ print_this = 1;
+ break;
+ }
+ }
+
+ /*
+ * No - is this a valid response code (3 digits)?
+ *
+ * Is this token the response code, or is the next
+ * token the response code?
+ */
+ if (flags & RESP_CODE_SECOND_TOKEN) {
+ /*
+ * Next token - get it.
+ */
+ idx = fetch_token(ndo, pptr, idx, len, token,
+ sizeof(token));
+ }
+ if (idx != 0) {
+ if (ND_ASCII_ISDIGIT(token[0]) && ND_ASCII_ISDIGIT(token[1]) &&
+ ND_ASCII_ISDIGIT(token[2]) && token[3] == '\0') {
+ /* Yes. */
+ print_this = 1;
+ }
+ }
+ }
+ } else {
+ /*
+ * Either:
+ *
+ * 1) This protocol has only request and response lines
+ * (e.g., FTP, where all the data goes over a different
+ * connection); assume the payload is a request or
+ * response.
+ *
+ * or
+ *
+ * 2) This protocol is just text, so that we should
+ * always, at minimum, print the first line and,
+ * in verbose mode, print all lines.
+ */
+ print_this = 1;
+ }
+
+ nd_print_protocol_caps(ndo);
+
+ if (print_this) {
+ /*
+ * In non-verbose mode, just print the protocol, followed
+ * by the first line.
+ *
+ * In verbose mode, print lines as text until we run out
+ * of characters or see something that's not a
+ * printable-ASCII line.
+ */
+ if (ndo->ndo_vflag) {
+ /*
+ * We're going to print all the text lines in the
+ * request or response; just print the length
+ * on the first line of the output.
+ */
+ ND_PRINT(", length: %u", len);
+ for (idx = 0;
+ idx < len && (eol = print_txt_line(ndo, "\n\t", pptr, idx, len)) != 0;
+ idx = eol)
+ ;
+ } else {
+ /*
+ * Just print the first text line.
+ */
+ print_txt_line(ndo, ": ", pptr, 0, len);
+ }
+ }
+}
+
+#if (defined(__i386__) || defined(_M_IX86) || defined(__X86__) || defined(__x86_64__) || defined(_M_X64)) || \
+ (defined(__arm__) || defined(_M_ARM) || defined(__aarch64__)) || \
+ (defined(__m68k__) && (!defined(__mc68000__) && !defined(__mc68010__))) || \
+ (defined(__ppc__) || defined(__ppc64__) || defined(_M_PPC) || defined(_ARCH_PPC) || defined(_ARCH_PPC64)) || \
+ (defined(__s390__) || defined(__s390x__) || defined(__zarch__)) || \
+ defined(__vax__)
+/*
+ * The procesor natively handles unaligned loads, so just use memcpy()
+ * and memcmp(), to enable those optimizations.
+ *
+ * XXX - are those all the x86 tests we need?
+ * XXX - do we need to worry about ARMv1 through ARMv5, which didn't
+ * support unaligned loads, and, if so, do we need to worry about all
+ * of them, or just some of them, e.g. ARMv5?
+ * XXX - are those the only 68k tests we need not to generated
+ * unaligned accesses if the target is the 68000 or 68010?
+ * XXX - are there any tests we don't need, because some definitions are for
+ * compilers that also predefine the GCC symbols?
+ * XXX - do we need to test for both 32-bit and 64-bit versions of those
+ * architectures in all cases?
+ */
+#else
+/*
+ * The processor doesn't natively handle unaligned loads,
+ * and the compiler might "helpfully" optimize memcpy()
+ * and memcmp(), when handed pointers that would normally
+ * be properly aligned, into sequences that assume proper
+ * alignment.
+ *
+ * Do copies and compares of possibly-unaligned data by
+ * calling routines that wrap memcpy() and memcmp(), to
+ * prevent that optimization.
+ */
+void
+unaligned_memcpy(void *p, const void *q, size_t l)
+{
+ memcpy(p, q, l);
+}
+
+/* As with memcpy(), so with memcmp(). */
+int
+unaligned_memcmp(const void *p, const void *q, size_t l)
+{
+ return (memcmp(p, q, l));
+}
+#endif
+
diff --git a/varattrs.h b/varattrs.h
new file mode 100644
index 0000000..b3c1689
--- /dev/null
+++ b/varattrs.h
@@ -0,0 +1,59 @@
+/* -*- Mode: c; tab-width: 8; indent-tabs-mode: 1; c-basic-offset: 8; -*- */
+/*
+ * Copyright (c) 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef varattrs_h
+#define varattrs_h
+
+#include "compiler-tests.h"
+
+/*
+ * Attributes to apply to variables, using various compiler-specific
+ * extensions.
+ */
+
+#if __has_attribute(unused) \
+ || ND_IS_AT_LEAST_GNUC_VERSION(2,0)
+ /*
+ * Compiler with support for __attribute__((unused)), or GCC 2.0 and
+ * later, so it supports __attribute__((unused)).
+ */
+ #define _U_ __attribute__((unused))
+#else
+ /*
+ * We don't know of any way to mark a variable as unused.
+ */
+ #define _U_
+#endif
+
+#endif