3 --- This file is part of secnet.
4 --- See README for full list of copyright holders.
6 --- secnet is free software; you can redistribute it and/or modify it
7 --- under the terms of the GNU General Public License as published by
8 --- the Free Software Foundation; either version d of the License, or
9 --- (at your option) any later version.
11 --- secnet is distributed in the hope that it will be useful, but
12 --- WITHOUT ANY WARRANTY; without even the implied warranty of
13 --- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 --- General Public License for more details.
16 --- You should have received a copy of the GNU General Public License
17 --- version 3 along with secnet; if not, see
18 --- https://www.gnu.org/licenses/gpl.html.
20 local secnet = Proto("secnet", "Secnet VPN")
22 -----------------------------------------------------------------------------
25 --- This is the hardest part of the dissector.
27 -- Timelines. A timeline associates pieces of information with times T.
29 local function tl_new()
30 -- Return a fresh shiny timeline.
35 local function tl__find(tl, t)
36 -- Find and return the earliest association in TL not earlier than T. If
37 -- there is no such entry, return nil.
42 -- Plain old binary search. The active interval is half-open, [lo, hi).
45 if w == 0 then return nil end
46 local mid = lo + math.floor(w/2)
48 if tv.t > t then hi = mid
49 elseif tv.t == t or w == 1 then return tv
55 local function tl_find(tl, t)
56 -- Find and return the state of the timeline at time T, i.e., the earliest
57 -- value in TL not earlier than T. If there is no such entry, return nil.
59 local tv = tl__find(tl, t)
60 if tv == nil then return nil else return tv.v end
63 local function tl_add(tl, t, v)
64 -- Associate the value V with time T in TL.
66 local tv = tl__find(tl, t)
67 if tv ~= nil and tv.t == t then
70 -- Append the new item. If necessary, sort the vector; we expect that
71 -- we'll see everything in the right order, so this won't be a problem.
73 tl[n + 1] = { t = t, v = v }
74 if n > 0 and tl[n].t > t then
75 table.sort(tl, function (tv0, tv1) return tv0.t < tv1.t end)
80 local function dump_timeline(tl, cvt)
81 -- Dump a timeline TL, using the function CVT to convert each value to a
84 for _, tv in ipairs(tl) do print("\t" .. tv.t .. ": " .. cvt(tv.v)) end
87 local function get_timeline_create(map, index)
88 -- If MAP[INDEX] exists, return it; otherwise set MAP[INDEX] to a fresh
89 -- timeline and return that.
92 if tl == nil then tl = tl_new(); map[index] = tl end
96 local function lookup_timeline(map, index, t)
97 -- If it exists, MAP[INDEX] should be a timeline; find its state at time T.
98 -- Return nil if there's nothing there, or T is too early.
100 local tl = map[index]
101 if tl == nil then return nil
102 else return tl_find(tl, t)
106 -- The `SITEMAP' maps site names to little structures.
108 -- * `algs' is a map from peer site names to a timeline of structures
111 -- * `index' is a map from site indices to a timeline of names, reflecting
112 -- that, at some time T, this site thought that some index I referred to
115 -- The `algs' map contains the following slots, populated during .
117 -- * `xform' is a timeline of transform names.
120 -- The `ADDRMAP' maps (IPv4 or IPv6) socket addresses in the form
121 -- `[ADDR]:PORT' to a timeline of site names, populated based on claims made
122 -- by senders about themselves. The `GUESSMAP' is similar, but populated
123 -- based on assertions about recipients.
127 local function snd_sockname(st)
128 -- Return the sender's socket name as a thing which can be used as a table
131 local pinfo = st.pinfo
132 return string.format("[%s]:%d", pinfo.net_src, pinfo.src_port)
135 local function rcv_sockname(st)
136 -- Return the recipient's socket name as a thing which can be used as a
139 local pinfo = st.pinfo
140 return string.format("[%s]:%d", pinfo.net_dst, pinfo.dst_port)
143 local function get_site_create(name)
144 -- If NAME refers to a known site, then return its information structure;
145 -- otherwise create a new one and return that.
147 local site = SITEMAP[name]
149 site = { algs = { }, index = { } }
155 local function notice_site_name(map, st, sock, name)
156 -- Record in MAP that the packet described in the state ST tells us that,
157 -- at that time, the site NAME appeared to be at address SOCK.
159 tl_add(get_timeline_create(map, sock), st.pinfo.rel_ts, name)
162 local function dump_algs(algs)
163 -- Dump the algorithms selection ALGS from a site structure.
165 return "xform=" .. algs.transform .. "; dh=" .. algs.dhgroup
168 local function dump_str(str) return str end
170 local function dump_addrmap(what, map)
171 -- Dump MAP, which is an address map like `ADDRMAP' or `GUESSMAP'; WHAT is
172 -- a string describing which map it is.
175 for addr, tl in pairs(map) do
177 dump_timeline(tl, dump_str)
181 local function dump_tracking_state()
182 -- Dump the entire tracking state to standard output.
184 dump_addrmap("Address map", ADDRMAP)
185 dump_addrmap("Guess map", GUESSMAP)
187 for name, site in pairs(SITEMAP) do
190 for peer, tl in pairs(site.algs) do
192 dump_timeline(tl, dump_algs)
195 for ix, tl in pairs(site.index) do
197 dump_timeline(tl, dump_str)
202 local function notice_sndname(st, name)
203 -- Record that sender of the packet described by state ST is called NAME.
206 notice_site_name(ADDRMAP, st, snd_sockname(st), name)
209 local function notice_rcvname(st, name)
210 -- Record that the sender of the packet described by ST thought that its
211 -- recipient was called NAME.
214 notice_site_name(GUESSMAP, st, rcv_sockname(st), name)
215 if st.sndname ~= nil then
216 local site = get_site_create(st.sndname)
217 tl_add(get_timeline_create(site.index, st.sndix), st.pinfo.rel_ts, name)
221 -- Tables describing the kinds of algorithms which can be selected.
223 [8] = { name = "serpent256cbc", kind = "transform",
224 desc = "Deprecated Serpent256-CBC transform" },
225 [9] = { name = "eaxserpent", kind = "transform",
226 desc = "Serpent256-EAX transform" },
227 [10] = { name = "tradzp", kind = "dhgroup",
228 desc = "Traditional Z_p Diffie--Hellman key agreement" },
229 [11] = { name = "x25519", kind = "dhgroup",
230 desc = "X25519 elliptic curve Diffie--Hellman key agreement" },
231 [12] = { name = "x448", kind = "dhgroup",
232 desc = "X448 elliptic curve Diffie--Hellman key agreement" },
233 [31] = { name = "mobile-priority", kind = "early",
234 desc = "Mobile site takes priority in case of MSG1 crossing" }
237 local function get_algname(kind, cap, dflt)
238 -- Fetch an algorithm of the given KIND, given its capability number CAP;
239 -- if CAP is nil, then return DFLT instead.
245 local info = CAPTAB[cap]
246 if info ~= nil and info.kind == kind then name = info.name
247 else name = string.format("Unknown %s #%d", kind, cap)
253 local function notice_alg_selection(st)
254 -- Record the algorithm selections declared in the packet described by ST.
256 local transform = get_algname("transform", st.transform, "serpent256cbc")
257 local dhgroup = get_algname("dhgroup", st.dhgroup, "tradzp")
258 local site = get_site_create(st.sndname)
259 local peer = get_site_create(st.rcvname)
260 local now = st.pinfo.rel_ts
261 local algs = { transform = transform, dhgroup = dhgroup }
262 tl_add(get_timeline_create(site.algs, st.rcvname), now, algs)
263 tl_add(get_timeline_create(peer.algs, st.sndname), now, algs)
266 -----------------------------------------------------------------------------
267 --- Protocol dissection primitives.
269 local PF = { } -- The table of protocol fields, filled in later.
270 local F = { } -- A table of field values, also filled in later.
272 local function msgcode(major, minor)
273 -- Construct a Secnet message number according to the complicated rules.
275 local majlo = bit.band(major, 0x000f)
276 local majhi = bit.band(major, 0xfff0)
277 local minlo = bit.band(minor, 0x000f)
278 local minhi = bit.band(minor, 0xfff0)
279 return bit.bxor(bit.lshift(majlo, 0),
280 bit.lshift(majlo, 8),
281 bit.lshift(majlo, 16),
282 bit.lshift(majlo, 24),
283 bit.lshift(majhi, 4),
284 bit.lshift(minlo, 4),
285 bit.lshift(minlo, 28),
286 bit.lshift(minhi, 16))
289 local function msgmajor(label)
290 -- Return the major message number from a LABEL.
292 local lo = bit.band(label, 0x000f)
293 local hi = bit.band(bit.rshift(label, 4), 0xfff0)
294 return bit.bxor(lo, bit.lshift(lo, 4), bit.lshift(lo, 12), hi)
297 local function msgminor(label)
298 -- Return the minor message number from a LABEL.
300 return bit.bxor(bit.lshift(bit.band(label, 0x00ff), 8),
301 bit.band(bit.rshift(label, 4), 0x000f),
302 bit.band(bit.rshift(label, 16), 0xfff0))
305 -- Main message-number table.
306 local M = { NAK = msgcode( 0, 0),
307 MSG0 = msgcode(0x2020, 0), -- !
308 MSG1 = msgcode( 1, 0),
309 MSG2 = msgcode( 2, 0),
310 MSG3 = msgcode( 3, 0),
311 MSG3BIS = msgcode( 3, 1),
312 MSG3TER = msgcode( 3, 2),
313 MSG4 = msgcode( 4, 0),
314 MSG5 = msgcode( 5, 0),
315 MSG6 = msgcode( 6, 0),
316 MSG7 = msgcode( 7, 0),
317 MSG8 = msgcode( 8, 0),
318 MSG9 = msgcode( 9, 0),
319 PROD = msgcode( 10, 0)}
321 -- The `dissect_*' functions follow a common protocol. They parse a thing
322 -- from a packet buffer BUF, of size SZ, starting from POS, and store
323 -- interesting things in a given TREE; when they're done, they return the
324 -- updated index where the next interesting thing might be, and maybe store
325 -- interesting things in the state ST. As a result, it's usually a simple
326 -- matter to parse a packet by invoking the appropriate primitive dissectors
327 -- in the right order.
329 local function dissect_sequence(dissect, st, buf, tree, pos, sz)
330 -- Dissect pieces of the packed in BUF with each of the dissectors in the
331 -- list DISSECT in turn.
333 for _, d in ipairs(dissect) do pos = d(st, buf, tree, pos, sz) end
337 local function dissect_wtf(st, buf, tree, pos, sz)
338 -- If POS is not at the end of the buffer, note that there's unexpected
339 -- stuff in the packet.
341 if pos < sz then tree:add(PF["secnet.wtf"], buf(pos, sz - pos)) end
347 -- This will be a list of the capability protocol field names, in the right
348 -- order. We just have to figure out what that will be.
354 -- Firstly, build, in `caps', a list of the capability names and their
357 caps[i] = { i = 15, cap = "explicit" }; i = 1 + 1
358 for j, cap in pairs(CAPTAB) do
359 caps[i] = { i = j, cap = cap.name }
363 -- Sort the list. Now they're in the right order.
364 table.sort(caps, function (v0, v1) return v0.i < v1.i end)
366 -- Finally, write the entries to `caplist', with the `user' entry at the
367 -- start and the `unassigned' entry at the end.
369 caplist[i] = "secnet.cap.user"; i = i + 1
370 for _, v in ipairs(caps) do
371 caplist[i] = "secnet.cap." .. v.cap
374 caplist[i] = "secnet.cap.unassigned"; i = i + 1
377 function dissect_caps(st, buf, tree, pos, sz)
378 -- Dissect a capabilities word.
381 local cap = tree:add(PF["secnet.cap"], buf(pos, 4))
382 for _, pf in ipairs(caplist) do cap:add(PF[pf], buf(pos, 4)) end
389 local function dissect_mtu(st, buf, tree, pos, sz)
390 -- Dissect an MTU request.
392 if pos < sz then tree:add(PF["secnet.mtu"], buf(pos, 2)); pos = pos + 2 end
396 local function make_dissect_name_xinfo(label, dissect_xinfo, hook)
397 -- Return a dissector function for reading a name and extra information.
398 -- The function will dissect a subtree rooted at the protocol field LABEL;
399 -- it will dissect the extra information using the list DISSECT_XINFO
400 -- (processed using `dissect_sequence'); and finally, if the packet hasn't
401 -- been visited yet, it will call HOOK(ST, NAME), where NAME is the name
402 -- string extracted from the packet.
404 return function (st, buf, tree, pos, sz)
406 -- Find the length of the whole thing.
407 local len = buf(pos, 2):uint()
409 -- Make the subtree root.
410 local sub = tree:add(PF[label], buf(pos, len + 2))
412 -- Find the length of the name. This is rather irritating: I'd like to
413 -- get Wireshark to do this, but it seems that `stringz' doesn't pay
414 -- attention to the buffer limits it's given. So read the whole lot and
415 -- find the null by hand.
416 local name = buf(pos + 2, len):string()
417 local z, _ = string.find(name, "\0", 1, true)
422 name = string.sub(name, 1, z)
425 -- Fill in the subtree.
426 sub:add(PF["secnet.namex.len"], buf(pos, 2)); pos = pos + 2
427 sub:add(PF["secnet.namex.name"], buf(pos, z))
429 dissect_sequence(dissect_xinfo, st, buf, sub, pos + z + 1, pos + len)
432 -- Maybe call the hook.
433 if hook ~= nil and not st.pinfo.visited then hook(st, name) end
440 local function dissect_sndnonce(st, buf, tree, pos, sz)
441 -- Dissect the sender's nonce.
443 tree:add(PF["secnet.kx.sndnonce"], buf(pos, 8)); pos = pos + 8
447 local function dissect_rcvnonce(st, buf, tree, pos, sz)
448 -- Dissect the recipient's nonce.
450 tree:add(PF["secnet.kx.rcvnonce"], buf(pos, 8)); pos = pos + 8
454 local function dissect_transform(st, buf, tree, pos, sz)
455 -- Dissect the selected transform. Note this in the packet state for
458 st.transform = buf(pos, 1):uint()
459 tree:add(PF["secnet.kx.transform"], buf(pos, 1)); pos = pos + 1
463 local function dissect_dhgroup(st, buf, tree, pos, sz)
464 -- Dissect the selected DH group. Note this in the packet state for later.
466 st.dhgroup = buf(pos, 1):uint()
467 tree:add(PF["secnet.kx.dhgroup"], buf(pos, 1)); pos = pos + 1
471 local function dissect_lenstr(st, buf, tree, label, pos, sz)
472 -- Dissect a simple string given its length.
473 local len = buf(pos, 2):uint()
474 local sub = tree:add(PF[label], buf(pos, len + 2))
475 sub:add(PF[label .. ".len"], buf(pos, 2)); pos = pos + 2
476 sub:add(PF[label .. ".text"], buf(pos, len)); pos = pos + len
480 local function dissect_dhval(st, buf, tree, pos, sz)
481 -- Dissect a Diffie--Hellman public value.
483 local len = buf(pos, 2):uint()
484 local sub = tree:add(PF["secnet.kx.dhval"], buf(pos, len + 2))
485 sub:add(PF["secnet.kx.dhval.len"], buf(pos, 2)); pos = pos + 2
486 sub:add(PF["secnet.kx.dhval.bytes"], buf(pos, len)); pos = pos + len
490 local function dissect_sig(st, buf, tree, pos, sz)
491 -- Dissect a signature.
493 return dissect_lenstr(st, buf, tree, "secnet.kx.sig", pos, sz)
496 local function find_algs_lookup(map, sock, now, ix)
497 -- Utility for `find_algs': look SOCK up in the address map ADDR, to find a
498 -- site; find its peer with index IX; and return the algorithm selection
499 -- current between the pair at time NOW. If the lookup fails, return nil.
501 local name = lookup_timeline(map, sock, now)
502 if name == nil then return nil end
503 local site = SITEMAP[name]
504 if site == nil then return nil end
505 local peername = lookup_timeline(site.index, ix, now)
506 if peername == nil then return nil end
507 return lookup_timeline(site.algs, peername, now)
510 local function find_algs(st)
511 -- Return the algorithm selection which applies to the packet described in
514 local now = st.pinfo.rel_ts
515 local sock = snd_sockname(st)
516 local algs = find_algs_lookup(ADDRMAP, sock, now, st.sndix)
517 if algs ~= nil then return algs
518 else return find_algs_lookup(GUESSMAP, sock, now, st.rcvix)
522 -- Transform-specific dissectors...
523 local dissect_ct = { }
524 function dissect_ct.unknown(st, why, buf, tree, pos, sz)
525 tree:add(PF["secnet.ciphertext.unknown"], buf(pos, sz - pos),
526 "Ciphertext with unknown structure: " .. why)
529 function dissect_ct.serpent256cbc(st, buf, tree, pos, sz)
530 tree:add(PF["secnet.ciphertext.iv"], buf(pos, 4)); pos = pos + 4
531 tree:add(PF["secnet.ciphertext.payload"], buf(pos, sz - pos))
534 function dissect_ct.eaxserpent(st, buf, tree, pos, sz)
535 local len = sz - pos - 20
536 tree:add(PF["secnet.ciphertext.payload"], buf(pos, len)); pos = pos + len
537 tree:add(PF["secnet.ciphertext.tag"], buf(pos, 16)); pos = pos + 16
538 tree:add(PF["secnet.ciphertext.sequence"], buf(pos, 4)); pos = pos + 4
542 local function dissect_ciphertext(st, buf, tree, pos, sz)
543 -- Dissect a ciphertext.
545 local sub = tree:add(PF["secnet.ciphertext"], buf(pos, sz - pos))
546 local algs = find_algs(st)
548 if algs == nil then xform = nil else xform = algs.transform end
550 pos = dissect_ct.unknown(st, "unable to find negotiated transform",
553 local func = dissect_ct[xform]
555 pos = dissect_ct.unknown(st, "unsupported transform " .. xform,
558 pos = func(st, buf, sub, pos, sz)
564 -----------------------------------------------------------------------------
565 --- The protocol information table.
568 -- This is the main table which describes the protocol. The top level maps
569 -- message labels to structures:
571 -- * `label' is the category code's symbolic name;
573 -- * `info' is a prefix for the information column display; and
575 -- * `dissect' is a sequence of primitive dissectors to run in order to
576 -- parse the rest of the packet.
580 info = "Stimulate fresh key exchange",
581 dissect = { dissect_wtf }
586 dissect = { dissect_ciphertext }
591 dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
592 { dissect_caps, dissect_wtf },
594 make_dissect_name_xinfo("secnet.kx.rcvname",
603 dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
604 { dissect_caps, dissect_wtf },
606 make_dissect_name_xinfo("secnet.kx.rcvname",
609 dissect_sndnonce, dissect_rcvnonce,
615 dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
620 make_dissect_name_xinfo("secnet.kx.rcvname",
623 dissect_sndnonce, dissect_rcvnonce,
625 hook = notice_alg_selection
630 dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
635 make_dissect_name_xinfo("secnet.kx.rcvname",
638 dissect_sndnonce, dissect_rcvnonce,
640 dissect_dhval, dissect_sig,
642 hook = notice_alg_selection
647 dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
652 make_dissect_name_xinfo("secnet.kx.rcvname",
655 dissect_sndnonce, dissect_rcvnonce,
656 dissect_transform, dissect_dhgroup,
657 dissect_dhval, dissect_sig,
659 hook = notice_alg_selection
664 dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
669 make_dissect_name_xinfo("secnet.kx.rcvname",
672 dissect_sndnonce, dissect_rcvnonce,
673 dissect_dhval, dissect_sig,
679 dissect = { dissect_ciphertext }
684 dissect = { dissect_ciphertext }
689 dissect = { make_dissect_name_xinfo("secnet.kx.sndname",
693 make_dissect_name_xinfo("secnet.kx.rcvname",
701 -- Work through the master table and build the `msgtab'' table, mapping
702 -- message codes to their symbolic names for presentation.
704 for i, v in pairs(PKTINFO) do msgtab[i] = v.label end
706 local capmap = { transform = { }, dhgroup = { }, early = { } }
707 for i, v in pairs(CAPTAB) do capmap[v.kind][i] = v.desc end
710 -- The protocol fields. This table maps the field names to structures
711 -- used to build the fields, which are then stored in `PF' (declared way
714 -- * `name' is the field name to show in the dissector tree view;
716 -- * `type' is the field type;
718 -- * `base' is a tweak describing how the field should be formatted;
720 -- * `mask' is used to single out a piece of a larger bitfield;
722 -- * `tab' names a mapping table used to convert numerical values to
723 -- symbolic names; and
725 -- * `hook' is a hook function to run the first time we see a packet,
726 -- to keep track of things.
729 name = "Common message header", type = ftypes.NONE
731 ["secnet.hdr.rcvix"] = {
732 name = "Recipient's site index for sender",
733 type = ftypes.UINT32, base = base.DEC
735 ["secnet.hdr.sndix"] = {
736 name = "Sender's site index for recipient",
737 type = ftypes.UINT32, base = base.DEC
739 ["secnet.hdr.label"] = {
740 name = "Message label", type = ftypes.UINT32,
741 base = base.HEX, tab = msgtab
743 ["secnet.kx.sndname"] = {
744 name = "Sender's site name and extended information",
747 ["secnet.kx.rcvname"] = {
748 name = "Recipient's site name and extended information",
751 ["secnet.namex.len"] = {
752 name = "Name/extended info length",
753 type = ftypes.UINT16, base = base.DEC
755 ["secnet.namex.name"] = {
756 name = "Site name", type = ftypes.STRING,
757 field = true, base = base.ASCII,
760 name = "Advertised capability bits",
761 type = ftypes.UINT32, base = base.HEX
763 ["secnet.cap.user"] = {
764 name = "User-assigned capability bits",
765 type = ftypes.UINT32, mask = 0x000000ff, base = base.HEX
767 ["secnet.cap.explicit"] = {
768 name = "Transforms listed explicitly; all capability bits used",
769 type = ftypes.BOOLEAN, mask = 0x00008000, base = 32
772 name = "Sender's requested MTU", type = ftypes.UINT16, base = base.DEC
774 ["secnet.kx.sndnonce"] = {
775 name = "Sender's nonce", type = ftypes.BYTES, base = base.SPACE
777 ["secnet.kx.rcvnonce"] = {
778 name = "Recipient's nonce", type = ftypes.BYTES, base = base.SPACE
780 ["secnet.kx.transform"] = {
781 name = "Selected bulk-crypto transform", type = ftypes.UINT8,
782 base = base.DEC, tab = capmap.transform
784 ["secnet.kx.dhgroup"] = {
785 name = "Selected Diffie--Hellman group kind", type = ftypes.UINT8,
786 base = base.DEC, tab = capmap.dhgroup
788 ["secnet.kx.dhval"] = {
789 name = "Sender's public Diffie--Hellman value", type = ftypes.NONE
791 ["secnet.kx.dhval.len"] = {
792 name = "Sender's public Diffie--Hellman length",
793 type = ftypes.UINT16, base = base.DEC
795 ["secnet.kx.dhval.bytes"] = {
796 name = "Sender's public Diffie--Hellman value bytes",
797 type = ftypes.BYTES, base = base.SPACE
799 ["secnet.kx.sig"] = {
800 name = "Sender's signature", type = ftypes.NONE
802 ["secnet.kx.sig.len"] = {
803 name = "Sender's signature length",
804 type = ftypes.UINT16, base = base.DEC
806 ["secnet.kx.sig.text"] = {
807 name = "Sender's signature text", type = ftypes.STRING,
810 ["secnet.ciphertext"] = {
811 name = "Encrypted data", type = ftypes.NONE
813 ["secnet.ciphertext.unknown"] = {
814 name = "Ciphertext with unknown structure",
815 type = ftypes.BYTES, base = base.SPACE
817 ["secnet.ciphertext.iv"] = {
818 name = "Initialization vector", type = ftypes.BYTES, base = base.SPACE
820 ["secnet.ciphertext.sequence"] = {
821 name = "Sequence number", type = ftypes.UINT32, base = base.DEC
823 ["secnet.ciphertext.payload"] = {
824 name = "Encrypted payload", type = ftypes.BYTES, base = base.SPACE
826 ["secnet.ciphertext.tag"] = {
827 name = "Authentication tag", type = ftypes.BYTES, base = base.SPACE
830 name = "Unexpected trailing data",
831 type = ftypes.BYTES, base = base.SPACE
835 -- Add the remaining capability fields. Calculate the unassigned mask
836 -- based on the assigned bits.
837 local unasgn = 0x7fff7f00
838 for i, v in pairs(CAPTAB) do
839 local flag = bit.lshift(1, i)
840 ftab["secnet.cap." .. v.name] = {
841 name = v.desc, type = ftypes.BOOLEAN,
842 mask = flag, base = 32
844 unasgn = bit.band(unasgn, bit.bnot(flag))
846 ftab["secnet.cap.unassigned"] = {
847 name = "Unassigned capability bits",
848 type = ftypes.UINT32, mask = unasgn, base = base.HEX
851 -- Convert this table into the protocol fields, and populate `PF'.
855 -- Figure out whether we can use `none' fields (see below).
856 local use_none_p = rawget(ProtoField, 'none') ~= nil
857 for abbr, args in pairs(ftab) do
859 -- An annoying hack. Older versions of Wireshark don't allow setting
860 -- fields with type `none', which is a shame because they're ideal as
861 -- internal tree nodes.
864 if ty == ftypes.NONE then
873 -- Go make the field.
874 local f = ProtoField.new(args.name, abbr, ty,
875 args.tab, b, args.mask, args.descr)
881 -- Make readable fields corresponding to especially interesting protocol
883 for abbr, args in pairs(ftab) do
884 if args.field then F[abbr] = Field.new(abbr) end
888 -----------------------------------------------------------------------------
889 --- The main dissector.
891 function secnet.dissector(buf, pinfo, tree)
893 -- Fill in the obvious stuff.
894 pinfo.cols.protocol = "Secnet"
896 local sz = buf:reported_length_remaining()
897 local sub = tree:add(secnet, buf(0, sz), "Secnet packet")
900 -- Decode the message header.
901 hdr = sub:add(PF["secnet.hdr"], buf(0, 12))
902 local rcvix = buf(0, 4):uint(); hdr:add(PF["secnet.hdr.rcvix"], buf(0, 4))
903 local sndix = buf(4, 4):uint(); hdr:add(PF["secnet.hdr.sndix"], buf(4, 4))
904 local label = buf(8, 4):uint()
905 hdr:add(PF["secnet.hdr.label"], buf(8, 4), label,
906 string.format("Message label (major = 0x%04x, minor = 0x%04x)",
907 msgmajor(label), msgminor(label)))
908 local st = { pinfo = pinfo, label = label, rcvix = rcvix, sndix = sndix }
909 local info = PKTINFO[label]
911 -- Dispatch using the master protocol table.
913 pinfo.cols.info = string.format("Unknown message label 0x%08x", label)
915 pinfo.cols.info = info.info
916 p = dissect_sequence(info.dissect, st, buf, sub, p, sz)
919 -- Invoke the hook if necessary.
920 if not pinfo.visited and info.hook ~= nil then info.hook(st) end
922 -- Return the final position we reached.
926 -- We're done. Register the dissector.
927 DissectorTable.get("udp.port"):add(410, secnet)
929 -------- That's all, folks --------------------------------------------------