[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