Remove a bunch of duplicate semicolons.
[obnox/wireshark/wip.git] / packet-netflow.c
1 /*
2  ** packet-netflow.c
3  ** 
4  *****************************************************************************
5  ** (c) 2002 bill fumerola <fumerola@yahoo-inc.com>
6  ** All rights reserved.
7  ** 
8  ** This program is free software; you can redistribute it and/or
9  ** modify it under the terms of the GNU General Public License
10  ** as published by the Free Software Foundation; either version 2
11  ** of the License, or (at your option) any later version.
12  ** 
13  ** This program is distributed in the hope that it will be useful,
14  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  ** GNU General Public License for more details.
17  ** 
18  ** You should have received a copy of the GNU General Public License
19  ** along with this program; if not, write to the Free Software
20  ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  *****************************************************************************
22  **
23  ** Previous NetFlow dissector written by Matthew Smart <smart@monkey.org>
24  ** NetFlow v9 support added by same.
25  **
26  ** See
27  **
28  ** http://www.cisco.com/warp/public/cc/pd/iosw/prodlit/tflow_wp.htm
29  **
30  ** for NetFlow v9 information.
31  **
32  *****************************************************************************
33  **
34  ** this code was written from the following documentation:
35  **
36  ** http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_6/iug/format.pdf
37  ** http://www.caida.org/tools/measurement/cflowd/configuration/configuration-9.html
38  **
39  ** some documentation is more accurate then others. in some cases, live data and
40  ** information contained in responses from vendors were also used. some fields
41  ** are dissected as vendor specific fields.
42  **
43  ** See also
44  **
45  ** http://www.cisco.com/univercd/cc/td/doc/cisintwk/intsolns/netflsol/nfwhite.htm
46  **
47  ** $Yahoo: //depot/fumerola/packet-netflow/packet-netflow.c#14 $
48  ** $Id: packet-netflow.c,v 1.10 2003/03/07 00:43:30 guy Exp $
49  */
50
51 #ifdef HAVE_CONFIG_H
52 # include "config.h"
53 #endif
54
55 #include <glib.h>
56 #include <epan/packet.h>
57 #include <string.h>
58
59 #include "prefs.h"
60
61 #define UDP_PORT_NETFLOW        2055
62
63 static guint global_netflow_udp_port = UDP_PORT_NETFLOW;
64 static guint netflow_udp_port = 0;
65
66 /*
67  * pdu identifiers & sizes 
68  */
69
70 #define V1PDU_SIZE              (4 * 12)
71 #define V5PDU_SIZE              (4 * 12)
72 #define V7PDU_SIZE              (4 * 13)
73 #define V8PDU_AS_SIZE           (4 * 7)
74 #define V8PDU_PROTO_SIZE        (4 * 7)
75 #define V8PDU_SPREFIX_SIZE      (4 * 8)
76 #define V8PDU_DPREFIX_SIZE      (4 * 8)
77 #define V8PDU_MATRIX_SIZE       (4 * 10)
78 #define V8PDU_DESTONLY_SIZE     (4 * 8)
79 #define V8PDU_SRCDEST_SIZE      (4 * 10)
80 #define V8PDU_FULL_SIZE         (4 * 11)
81 #define V8PDU_TOSAS_SIZE        (V8PDU_AS_SIZE + 4)
82 #define V8PDU_TOSPROTOPORT_SIZE (V8PDU_PROTO_SIZE + 4)
83 #define V8PDU_TOSSRCPREFIX_SIZE V8PDU_SPREFIX_SIZE
84 #define V8PDU_TOSDSTPREFIX_SIZE V8PDU_DPREFIX_SIZE
85 #define V8PDU_TOSMATRIX_SIZE    V8PDU_MATRIX_SIZE
86 #define V8PDU_PREPORTPROTOCOL_SIZE (4 * 10)
87
88 enum {
89         V8PDU_NO_METHOD = 0,
90         V8PDU_AS_METHOD,
91         V8PDU_PROTO_METHOD,
92         V8PDU_SPREFIX_METHOD,
93         V8PDU_DPREFIX_METHOD,
94         V8PDU_MATRIX_METHOD,
95         V8PDU_DESTONLY_METHOD,
96         V8PDU_SRCDEST_METHOD,
97         V8PDU_FULL_METHOD,
98         V8PDU_TOSAS_METHOD,
99         V8PDU_TOSPROTOPORT_METHOD,
100         V8PDU_TOSSRCPREFIX_METHOD,
101         V8PDU_TOSDSTPREFIX_METHOD,
102         V8PDU_TOSMATRIX_METHOD,
103         V8PDU_PREPORTPROTOCOL_METHOD
104 };
105
106 static const value_string v8_agg[] = {
107         {V8PDU_AS_METHOD, "V8 AS aggregation"},
108         {V8PDU_PROTO_METHOD, "V8 Proto/Port aggregation"},
109         {V8PDU_SPREFIX_METHOD, "V8 Source Prefix aggregation"},
110         {V8PDU_DPREFIX_METHOD, "V8 Destination Prefix aggregation"},
111         {V8PDU_MATRIX_METHOD, "V8 Network Matrix aggregation"},
112         {V8PDU_DESTONLY_METHOD, "V8 Destination aggregation (Cisco Catalyst)"},
113         {V8PDU_SRCDEST_METHOD, "V8 Src/Dest aggregation (Cisco Catalyst)"},
114         {V8PDU_FULL_METHOD, "V8 Full aggregation (Cisco Catalyst)"},
115         {V8PDU_TOSAS_METHOD, "V8 TOS+AS aggregation aggregation"},
116         {V8PDU_TOSPROTOPORT_METHOD, "V8 TOS+Protocol aggregation"},
117         {V8PDU_TOSSRCPREFIX_METHOD, "V8 TOS+Source Prefix aggregation"},
118         {V8PDU_TOSDSTPREFIX_METHOD, "V8 TOS+Destination Prefix aggregation"},
119         {V8PDU_TOSMATRIX_METHOD, "V8 TOS+Prefix Matrix aggregation"},
120         {V8PDU_PREPORTPROTOCOL_METHOD, "V8 Port+Protocol aggregation"},
121         {0, NULL}
122 };
123
124 /* Version 9 template cache structures */
125 #define V9TEMPLATE_MAX_ENTRIES          64
126 #define V9TEMPLATE_CACHE_MAX_ENTRIES    100
127
128 struct v9_template_entry {
129         guint16 type;
130         guint16 length;
131 };
132
133 struct v9_template {
134         guint16 id;
135         guint16 count;
136         guint32 length;
137         guint32 source_id;
138         guint32 source_addr;
139         struct v9_template_entry entries[V9TEMPLATE_MAX_ENTRIES];
140 };
141
142 static struct v9_template v9_template_cache[V9TEMPLATE_CACHE_MAX_ENTRIES];
143
144 /*
145  * ethereal tree identifiers
146  */
147
148 static int      proto_netflow = -1;
149 static int      ett_netflow = -1;
150 static int      ett_unixtime = -1;
151 static int      ett_flow = -1;
152 static int      ett_template = -1;
153 static int      ett_dataflowset = -1;
154
155 /*
156  * cflow header 
157  */
158
159 static int      hf_cflow_version = -1;
160 static int      hf_cflow_count = -1;
161 static int      hf_cflow_sysuptime = -1;
162 static int      hf_cflow_unix_secs = -1;
163 static int      hf_cflow_unix_nsecs = -1;
164 static int      hf_cflow_timestamp = -1;
165 static int      hf_cflow_samplerate = -1;
166
167 /*
168  * cflow version specific info 
169  */
170 static int      hf_cflow_sequence = -1;
171 static int      hf_cflow_engine_type = -1;
172 static int      hf_cflow_engine_id = -1;
173 static int      hf_cflow_source_id = -1;
174
175 static int      hf_cflow_aggmethod = -1;
176 static int      hf_cflow_aggversion = -1;
177
178 /* Version 9 */
179
180 static int      hf_cflow_template_flowset_id = -1;
181 static int      hf_cflow_data_flowset_id = -1;
182 static int      hf_cflow_options_flowset_id = -1;
183 static int      hf_cflow_flowset_id = -1;
184 static int      hf_cflow_flowset_length = -1;
185 static int      hf_cflow_template_id = -1;
186 static int      hf_cflow_template_field_count = -1;
187 static int      hf_cflow_template_field_type = -1;
188 static int      hf_cflow_template_field_length = -1;
189
190 /*
191  * pdu storage
192  */
193 static int      hf_cflow_srcaddr = -1;
194 static int      hf_cflow_srcaddr_v6 = -1;
195 static int      hf_cflow_srcnet = -1;
196 static int      hf_cflow_dstaddr = -1;
197 static int      hf_cflow_dstaddr_v6 = -1;
198 static int      hf_cflow_dstnet = -1;
199 static int      hf_cflow_nexthop = -1;
200 static int      hf_cflow_nexthop_v6 = -1;
201 static int      hf_cflow_bgpnexthop = -1;
202 static int      hf_cflow_bgpnexthop_v6 = -1;
203 static int      hf_cflow_inputint = -1;
204 static int      hf_cflow_outputint = -1;
205 static int      hf_cflow_flows = -1;
206 static int      hf_cflow_packets = -1;
207 static int      hf_cflow_packets64 = -1;
208 static int      hf_cflow_packetsout = -1;
209 static int      hf_cflow_octets = -1;
210 static int      hf_cflow_octets64 = -1;
211 static int      hf_cflow_timestart = -1;
212 static int      hf_cflow_timeend = -1;
213 static int      hf_cflow_srcport = -1;
214 static int      hf_cflow_dstport = -1;
215 static int      hf_cflow_prot = -1;
216 static int      hf_cflow_tos = -1;
217 static int      hf_cflow_flags = -1;
218 static int      hf_cflow_tcpflags = -1;
219 static int      hf_cflow_dstas = -1;
220 static int      hf_cflow_srcas = -1;
221 static int      hf_cflow_dstmask = -1;
222 static int      hf_cflow_srcmask = -1;
223 static int      hf_cflow_routersc = -1;
224 static int      hf_cflow_mulpackets = -1;
225 static int      hf_cflow_muloctets = -1;
226 static int      hf_cflow_octets_exp = -1;
227 static int      hf_cflow_packets_exp = -1;
228 static int      hf_cflow_flows_exp = -1;
229
230 void            proto_reg_handoff_netflow(void);
231
232 typedef int     dissect_pdu_t(proto_tree * pdutree, tvbuff_t * tvb, int offset,
233                               int verspec);
234 static int      dissect_pdu(proto_tree * tree, tvbuff_t * tvb, int offset,
235                             int verspec);
236 static int      dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb,
237                                   int offset, int verspec);
238 static int      dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb,
239                                    int offset, int verspec);
240 static int      dissect_v9_flowset(proto_tree * pdutree, tvbuff_t * tvb,
241                                    int offset, int verspec);
242 static int      dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb,
243                                int offset, guint16 id, guint length);
244 static void     dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb,
245                                int offset, struct v9_template * template);
246 #if 0
247 static int      dissect_v9_options(proto_tree * pdutree, tvbuff_t * tvb,
248                                int offset);
249 #endif
250 static int      dissect_v9_template(proto_tree * pdutree, tvbuff_t * tvb,
251                                     int offset);
252 static void     v9_template_add(struct v9_template * template);
253 static struct v9_template *v9_template_get(guint16 id, guint32 src_addr,
254                                            guint32 src_id);
255
256 static gchar   *getprefix(const guint32 * address, int prefix);
257 static void     dissect_netflow(tvbuff_t * tvb, packet_info * pinfo,
258                                 proto_tree * tree);
259
260 static int      flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb,
261                                   int offset);
262 static int      flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb,
263                                    int offset);
264 static int      flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb,
265                                         int offset);
266 static int      flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb,
267                                     int offset);
268 static int      flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb,
269                                        int offset);
270 static int      flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb,
271                                        int offset, int bytes,
272                                        const char *text);
273
274
275 static void
276 dissect_netflow(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
277 {
278         proto_tree     *netflow_tree = NULL;
279         proto_tree     *ti;
280         proto_item     *timeitem, *pduitem;
281         proto_tree     *timetree, *pdutree;
282         unsigned int    pduret, ver = 0, pdus = 0, x = 1, vspec;
283         size_t          available, pdusize, offset = 0;
284         nstime_t        ts;
285         dissect_pdu_t  *pduptr;
286
287         if (check_col(pinfo->cinfo, COL_PROTOCOL))
288                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "CFLOW");
289         if (check_col(pinfo->cinfo, COL_INFO))
290                 col_clear(pinfo->cinfo, COL_INFO);
291
292         if (tree) {
293                 ti = proto_tree_add_item(tree, proto_netflow, tvb,
294                                          offset, -1, FALSE);
295                 netflow_tree = proto_item_add_subtree(ti, ett_netflow);
296         }
297
298         ver = tvb_get_ntohs(tvb, offset);
299         vspec = ver;
300         switch (ver) {
301         case 1:
302                 pdusize = V1PDU_SIZE;
303                 pduptr = &dissect_pdu;
304                 break;
305         case 5:
306                 pdusize = V5PDU_SIZE;
307                 pduptr = &dissect_pdu;
308                 break;
309         case 7:
310                 pdusize = V7PDU_SIZE;
311                 pduptr = &dissect_pdu;
312                 break;
313         case 8:
314                 pdusize = -1;   /* deferred */
315                 pduptr = &dissect_v8_aggpdu;
316                 break;
317         case 9:
318                 pdusize = -1;   /* deferred */
319                 pduptr = &dissect_v9_flowset;
320                 break;
321         default:
322                 return;
323         }
324
325         if (tree)
326                 proto_tree_add_uint(netflow_tree, hf_cflow_version, tvb,
327                                     offset, 2, ver);
328         offset += 2;
329
330         pdus = tvb_get_ntohs(tvb, offset);
331         if (pdus <= 0)
332                 return;
333         if (tree)
334                 proto_tree_add_uint(netflow_tree, hf_cflow_count, tvb,
335                                     offset, 2, pdus);
336         offset += 2;
337
338         /*
339          * set something interesting in the display now that we have info 
340          */
341         if (check_col(pinfo->cinfo, COL_INFO)) {
342                 if (ver == 9) {
343                         col_add_fstr(pinfo->cinfo, COL_INFO,
344                             "total: %u (v%u) FlowSets", pdus, ver);
345                 } else {
346                         col_add_fstr(pinfo->cinfo, COL_INFO,
347                             "total: %u (v%u) flows", pdus, ver);
348                 }
349         }
350
351         /*
352          * the rest is only interesting if we're displaying/searching the
353          * packet 
354          */
355         if (!tree)
356                 return;
357
358         proto_tree_add_item(netflow_tree, hf_cflow_sysuptime, tvb,
359                             offset, 4, FALSE);
360         offset += 4;
361
362         ts.secs = tvb_get_ntohl(tvb, offset);
363         ts.nsecs = tvb_get_ntohl(tvb, offset + 4);
364         timeitem = proto_tree_add_time(netflow_tree,
365                                        hf_cflow_timestamp, tvb, offset,
366                                        8, &ts);
367         timetree = proto_item_add_subtree(timeitem, ett_unixtime);
368
369         proto_tree_add_item(timetree, hf_cflow_unix_secs, tvb,
370                             offset, 4, FALSE);
371         offset += 4;
372
373         if (ver != 9) {
374                 proto_tree_add_item(timetree, hf_cflow_unix_nsecs, tvb,
375                                     offset, 4, FALSE);
376                 offset += 4;
377         }
378
379         /*
380          * version specific header 
381          */
382         if (ver == 5 || ver == 7 || ver == 8 || ver == 9) {
383                 proto_tree_add_item(netflow_tree, hf_cflow_sequence,
384                                     tvb, offset, 4, FALSE);
385                 offset += 4;
386         }
387         if (ver == 5 || ver == 8) {
388                 proto_tree_add_item(netflow_tree, hf_cflow_engine_type,
389                                     tvb, offset++, 1, FALSE);
390                 proto_tree_add_item(netflow_tree, hf_cflow_engine_id,
391                                     tvb, offset++, 1, FALSE);
392         } else if (ver == 9) {
393                 proto_tree_add_item(netflow_tree, hf_cflow_source_id,
394                                     tvb, offset, 4, FALSE);
395                 offset += 4;
396         }
397         if (ver == 8) {
398                 vspec = tvb_get_guint8(tvb, offset);
399                 switch (vspec) {
400                 case V8PDU_AS_METHOD:
401                         pdusize = V8PDU_AS_SIZE;
402                         break;
403                 case V8PDU_PROTO_METHOD:
404                         pdusize = V8PDU_PROTO_SIZE;
405                         break;
406                 case V8PDU_SPREFIX_METHOD:
407                         pdusize = V8PDU_SPREFIX_SIZE;
408                         break;
409                 case V8PDU_DPREFIX_METHOD:
410                         pdusize = V8PDU_DPREFIX_SIZE;
411                         break;
412                 case V8PDU_MATRIX_METHOD:
413                         pdusize = V8PDU_MATRIX_SIZE;
414                         break;
415                 case V8PDU_DESTONLY_METHOD:
416                         pdusize = V8PDU_DESTONLY_SIZE;
417                         pduptr = &dissect_v8_flowpdu;
418                         break;
419                 case V8PDU_SRCDEST_METHOD:
420                         pdusize = V8PDU_SRCDEST_SIZE;
421                         pduptr = &dissect_v8_flowpdu;
422                         break;
423                 case V8PDU_FULL_METHOD:
424                         pdusize = V8PDU_FULL_SIZE;
425                         pduptr = &dissect_v8_flowpdu;
426                         break;
427                 case V8PDU_TOSAS_METHOD:
428                         pdusize = V8PDU_TOSAS_SIZE;
429                         break;
430                 case V8PDU_TOSPROTOPORT_METHOD:
431                         pdusize = V8PDU_TOSPROTOPORT_SIZE;
432                         break;
433                 case V8PDU_TOSSRCPREFIX_METHOD:
434                         pdusize = V8PDU_TOSSRCPREFIX_SIZE;
435                         break;
436                 case V8PDU_TOSDSTPREFIX_METHOD:
437                         pdusize = V8PDU_TOSDSTPREFIX_SIZE;
438                         break;
439                 case V8PDU_TOSMATRIX_METHOD:
440                         pdusize = V8PDU_TOSMATRIX_SIZE;
441                         break;
442                 case V8PDU_PREPORTPROTOCOL_METHOD:
443                         pdusize = V8PDU_PREPORTPROTOCOL_SIZE;
444                         break;
445                 default:
446                         pdusize = -1;
447                         vspec = 0;
448                         break;
449                 }
450                 proto_tree_add_uint(netflow_tree, hf_cflow_aggmethod,
451                                     tvb, offset++, 1, vspec);
452                 proto_tree_add_item(netflow_tree, hf_cflow_aggversion,
453                                     tvb, offset++, 1, FALSE);
454         }
455         if (ver == 7 || ver == 8)
456                 offset = flow_process_textfield(netflow_tree, tvb, offset, 4,
457                                                 "reserved");
458         else if (ver == 5) {
459                 proto_tree_add_item(netflow_tree, hf_cflow_samplerate,
460                                     tvb, offset, 2, FALSE);
461                 offset += 2;
462         }
463
464         /*
465          * everything below here should be payload 
466          */
467         for (x = 1; x < pdus + 1; x++) {
468                 /*
469                  * make sure we have a pdu's worth of data 
470                  */
471                 available = tvb_length_remaining(tvb, offset);
472                 if (ver == 9 && available >= 4) {
473                         /* pdusize can be different for each v9 flowset */
474                         pdusize = tvb_get_ntohs(tvb, offset + 2);
475                 }
476
477                 if (available < pdusize)
478                         break;
479
480                 if (ver == 9) {
481                         pduitem = proto_tree_add_text(netflow_tree, tvb,
482                             offset, pdusize, "FlowSet %u/%u", x, pdus);
483                 } else {
484                         pduitem = proto_tree_add_text(netflow_tree, tvb,
485                             offset, pdusize, "pdu %u/%u", x, pdus);
486                 }
487                 pdutree = proto_item_add_subtree(pduitem, ett_flow);
488
489                 pduret = pduptr(pdutree, tvb, offset, vspec);
490
491                 /*
492                  * if we came up short, stop processing 
493                  */
494                 if (pduret == pdusize)
495                         offset += pduret;
496                 else
497                         break;
498         }
499 }
500
501 /*
502  * flow_process_* == common groups of fields, probably could be inline 
503  */
504
505 static int
506 flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb, int offset)
507 {
508         proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2, FALSE);
509         offset += 2;
510
511         proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2,
512                             FALSE);
513         offset += 2;
514
515         return offset;
516 }
517
518 static int
519 flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb, int offset)
520 {
521         proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2, FALSE);
522         offset += 2;
523
524         proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2, FALSE);
525         offset += 2;
526
527         return offset;
528 }
529
530 static int
531 flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb, int offset)
532 {
533         nstime_t        ts;
534
535         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
536         ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000);
537         proto_tree_add_time(pdutree, hf_cflow_timestart, tvb, offset, 4, &ts);
538         offset += 4;
539
540         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
541         ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000);
542         proto_tree_add_time(pdutree, hf_cflow_timeend, tvb, offset, 4, &ts);
543         offset += 4;
544
545         return offset;
546 }
547
548
549 static int
550 flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb, int offset)
551 {
552         proto_tree_add_item(pdutree, hf_cflow_srcas, tvb, offset, 2, FALSE);
553         offset += 2;
554
555         proto_tree_add_item(pdutree, hf_cflow_dstas, tvb, offset, 2, FALSE);
556         offset += 2;
557
558         return offset;
559 }
560
561 static int
562 flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb, int offset)
563 {
564         proto_tree_add_item(pdutree, hf_cflow_packets, tvb, offset, 4, FALSE);
565         offset += 4;
566
567         proto_tree_add_item(pdutree, hf_cflow_octets, tvb, offset, 4, FALSE);
568         offset += 4;
569
570         return offset;
571 }
572
573 static int
574 flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb, int offset,
575                        int bytes, const char *text)
576 {
577         proto_tree_add_text(pdutree, tvb, offset, bytes, text);
578         offset += bytes;
579
580         return offset;
581 }
582
583 static int
584 dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
585                    int verspec)
586 {
587         int             startoffset = offset;
588
589         proto_tree_add_item(pdutree, hf_cflow_dstaddr, tvb, offset, 4, FALSE);
590         offset += 4;
591
592         if (verspec != V8PDU_DESTONLY_METHOD) {
593                 proto_tree_add_item(pdutree, hf_cflow_srcaddr, tvb, offset, 4,
594                                     FALSE);
595                 offset += 4;
596         }
597         if (verspec == V8PDU_FULL_METHOD) {
598                 proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2,
599                                     FALSE);
600                 offset += 2;
601                 proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2,
602                                     FALSE);
603                 offset += 2;
604         }
605
606         offset = flow_process_sizecount(pdutree, tvb, offset);
607         offset = flow_process_timeperiod(pdutree, tvb, offset);
608
609         proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2,
610                             FALSE);
611         offset += 2;
612
613         if (verspec != V8PDU_DESTONLY_METHOD) {
614                 proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2,
615                                     FALSE);
616                 offset += 2;
617         }
618
619         proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE);
620         if (verspec == V8PDU_FULL_METHOD)
621                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
622                                     FALSE);
623         offset = flow_process_textfield(pdutree, tvb, offset, 1, "marked tos");
624
625         if (verspec == V8PDU_SRCDEST_METHOD)
626                 offset =
627                     flow_process_textfield(pdutree, tvb, offset, 2,
628                                            "reserved");
629         else if (verspec == V8PDU_FULL_METHOD)
630                 offset =
631                     flow_process_textfield(pdutree, tvb, offset, 1, "padding");
632
633         offset =
634             flow_process_textfield(pdutree, tvb, offset, 4, "extra packets");
635
636         proto_tree_add_item(pdutree, hf_cflow_routersc, tvb, offset, 4, FALSE);
637         offset += 4;
638
639         return (offset - startoffset);
640 }
641
642 /*
643  * dissect a version 8 pdu, returning the length of the pdu processed 
644  */
645
646 static int
647 dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
648                   int verspec)
649 {
650         int             startoffset = offset;
651
652         proto_tree_add_item(pdutree, hf_cflow_flows, tvb, offset, 4, FALSE);
653         offset += 4;
654
655         offset = flow_process_sizecount(pdutree, tvb, offset);
656         offset = flow_process_timeperiod(pdutree, tvb, offset);
657
658         switch (verspec) {
659         case V8PDU_AS_METHOD:
660         case V8PDU_TOSAS_METHOD:
661                 offset = flow_process_aspair(pdutree, tvb, offset);
662
663                 if (verspec == V8PDU_TOSAS_METHOD) {
664                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
665                                             offset++, 1, FALSE);
666                         offset =
667                             flow_process_textfield(pdutree, tvb, offset, 1,
668                                                    "padding");
669                         offset =
670                             flow_process_textfield(pdutree, tvb, offset, 2,
671                                                    "reserved");
672                 }
673                 break;
674         case V8PDU_PROTO_METHOD:
675         case V8PDU_TOSPROTOPORT_METHOD:
676                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
677                                     FALSE);
678
679                 if (verspec == V8PDU_PROTO_METHOD)
680                         offset =
681                             flow_process_textfield(pdutree, tvb, offset, 1,
682                                                    "padding");
683                 else if (verspec == V8PDU_TOSPROTOPORT_METHOD)
684                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
685                                             offset++, 1, FALSE);
686
687                 offset =
688                     flow_process_textfield(pdutree, tvb, offset, 2,
689                                            "reserved");
690                 offset = flow_process_ports(pdutree, tvb, offset);
691
692                 if (verspec == V8PDU_TOSPROTOPORT_METHOD)
693                         offset = flow_process_ints(pdutree, tvb, offset);
694                 break;
695         case V8PDU_SPREFIX_METHOD:
696         case V8PDU_DPREFIX_METHOD:
697         case V8PDU_TOSSRCPREFIX_METHOD:
698         case V8PDU_TOSDSTPREFIX_METHOD:
699                 proto_tree_add_item(pdutree,
700                                     verspec ==
701                                     V8PDU_SPREFIX_METHOD ?
702                                     hf_cflow_srcnet : hf_cflow_dstnet, tvb,
703                                     offset, 4, FALSE);
704                 offset += 4;
705
706                 proto_tree_add_item(pdutree,
707                                     verspec ==
708                                     V8PDU_SPREFIX_METHOD ?
709                                     hf_cflow_srcmask : hf_cflow_dstmask, tvb,
710                                     offset++, 1, FALSE);
711
712                 if (verspec == V8PDU_SPREFIX_METHOD
713                     || verspec == V8PDU_DPREFIX_METHOD)
714                         offset =
715                             flow_process_textfield(pdutree, tvb, offset, 1,
716                                                    "padding");
717                 else if (verspec == V8PDU_TOSSRCPREFIX_METHOD
718                          || verspec == V8PDU_TOSDSTPREFIX_METHOD)
719                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
720                                             offset++, 1, FALSE);
721
722                 proto_tree_add_item(pdutree,
723                                     verspec ==
724                                     V8PDU_SPREFIX_METHOD ? hf_cflow_srcas
725                                     : hf_cflow_dstas, tvb, offset, 2, FALSE);
726                 offset += 2;
727
728                 proto_tree_add_item(pdutree,
729                                     verspec ==
730                                     V8PDU_SPREFIX_METHOD ?
731                                     hf_cflow_inputint : hf_cflow_outputint,
732                                     tvb, offset, 2, FALSE);
733                 offset += 2;
734
735                 offset =
736                     flow_process_textfield(pdutree, tvb, offset, 2,
737                                            "reserved");
738                 break;
739         case V8PDU_MATRIX_METHOD:
740         case V8PDU_TOSMATRIX_METHOD:
741         case V8PDU_PREPORTPROTOCOL_METHOD:
742                 proto_tree_add_item(pdutree, hf_cflow_srcnet, tvb, offset, 4,
743                                     FALSE);
744                 offset += 4;
745
746                 proto_tree_add_item(pdutree, hf_cflow_dstnet, tvb, offset, 4,
747                                     FALSE);
748                 offset += 4;
749
750                 proto_tree_add_item(pdutree, hf_cflow_srcmask, tvb, offset++,
751                                     1, FALSE);
752
753                 proto_tree_add_item(pdutree, hf_cflow_dstmask, tvb, offset++,
754                                     1, FALSE);
755
756                 if (verspec == V8PDU_TOSMATRIX_METHOD ||
757                     verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
758                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
759                                             offset++, 1, FALSE);
760                         if (verspec == V8PDU_TOSMATRIX_METHOD) {
761                                 offset =
762                                     flow_process_textfield(pdutree, tvb,
763                                                            offset, 1,
764                                                            "padding");
765                         } else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
766                                 proto_tree_add_item(pdutree, hf_cflow_prot,
767                                                     tvb, offset++, 1, FALSE);
768                         }
769                 } else {
770                         offset =
771                             flow_process_textfield(pdutree, tvb, offset, 2,
772                                                    "reserved");
773                 }
774
775                 if (verspec == V8PDU_MATRIX_METHOD
776                     || verspec == V8PDU_TOSMATRIX_METHOD) {
777                         offset = flow_process_aspair(pdutree, tvb, offset);
778                 } else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
779                         offset = flow_process_ports(pdutree, tvb, offset);
780                 }
781
782                 offset = flow_process_ints(pdutree, tvb, offset);
783                 break;
784         }
785
786
787         return (offset - startoffset);
788 }
789
790 /* Dissect a version 9 FlowSet and return the length we processed. */
791
792 static int
793 dissect_v9_flowset(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver)
794 {
795         int length;
796         guint16 flowset_id;
797
798         if (ver != 9)
799                 return (0);
800
801         flowset_id = tvb_get_ntohs(tvb, offset);
802         if (flowset_id == 0) {
803                 /* Template */
804                 proto_tree_add_item(pdutree, hf_cflow_template_flowset_id, tvb,
805                     offset, 2, FALSE);
806                 offset += 2;
807
808                 length = tvb_get_ntohs(tvb, offset);
809                 proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
810                     offset, 2, FALSE);
811                 offset += 2;
812
813                 dissect_v9_template(pdutree, tvb, offset);
814         } else if (flowset_id == 1) {
815                 /* Options */
816                 proto_tree_add_item(pdutree, hf_cflow_options_flowset_id, tvb,
817                     offset, 2, FALSE);
818                 offset += 2;
819
820                 length = tvb_get_ntohs(tvb, offset);
821                 proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
822                     offset, 2, FALSE);
823                 offset += 2;
824
825                 /* dissect_v9_options(pdutree, tvb, offset); */
826         } else if (flowset_id >= 2 && flowset_id <= 255) {
827                 /* Reserved */
828                 proto_tree_add_item(pdutree, hf_cflow_flowset_id, tvb,
829                     offset, 2, FALSE);
830                 offset += 2;
831
832                 length = tvb_get_ntohs(tvb, offset);
833                 proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
834                     offset, 2, FALSE);
835                 offset += 2;
836         } else {
837                 /* Data */
838                 proto_tree_add_item(pdutree, hf_cflow_data_flowset_id, tvb,
839                     offset, 2, FALSE);
840                 offset += 2;
841
842                 length = tvb_get_ntohs(tvb, offset);
843                 proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
844                     offset, 2, FALSE);
845                 offset += 2;
846
847                 /*
848                  * The length includes the length of the FlowSet ID and
849                  * the length field itself.
850                  */
851                 length -= 4;
852                 if (length > 0) {
853                         dissect_v9_data(pdutree, tvb, offset, flowset_id,
854                             (guint)length);
855                 }
856         }
857
858         return (length);
859 }
860
861 static int
862 dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb, int offset,
863     guint16 id, guint length)
864 {
865         struct v9_template *template;
866         proto_tree *data_tree;
867         proto_item *data_item;
868
869         template = v9_template_get(id, 0, 0);
870         if (template != NULL && template->length != 0) {
871                 int count;
872
873                 count = 1;
874                 while (length >= template->length) {
875                         data_item = proto_tree_add_text(pdutree, tvb,
876                             offset, template->length, "pdu %d", count++);
877                         data_tree = proto_item_add_subtree(data_item,
878                             ett_dataflowset);
879
880                         dissect_v9_pdu(data_tree, tvb, offset, template);
881
882                         offset += template->length;
883                         length -= template->length;
884                 }
885                 if (length != 0) {
886                         proto_tree_add_text(pdutree, tvb, offset, length,
887                             "Padding (%u byte%s)",
888                             length, plurality(length, "", "s"));
889                 }
890         } else {
891                 proto_tree_add_text(pdutree, tvb, offset, length,
892                     "Data (%u byte%s), no template found",
893                     length, plurality(length, "", "s"));
894         }
895
896         return (0);
897 }
898
899 static void
900 dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
901     struct v9_template * template)
902 {
903         int i;
904
905         for (i = 0; i < template->count; i++) {
906                 guint32 ipv4addr;
907                 guint8  ipv6addr[16];
908                 guint16 type, length;
909                 nstime_t ts;
910
911                 type = template->entries[i].type;
912                 length = template->entries[i].length;
913
914                 switch (type) {
915                 case 1: /* bytes */
916                         if (length == 4) {
917                                 proto_tree_add_item(pdutree, hf_cflow_octets,
918                                     tvb, offset, length, FALSE);
919                         } else if (length == 8) {
920                                 proto_tree_add_item(pdutree, hf_cflow_octets64,
921                                     tvb, offset, length, FALSE);
922                         } else {
923                                 proto_tree_add_text(pdutree,
924                                     tvb, offset, length,
925                                     "Octets: length %u", length);
926                         }
927                         break;
928
929                 case 2: /* packets */
930                         if (length == 4) {
931                                 proto_tree_add_item(pdutree, hf_cflow_packets,
932                                     tvb, offset, length, FALSE);
933                         } else if (length == 8) {
934                                 proto_tree_add_item(pdutree, hf_cflow_packets64,
935                                     tvb, offset, length, FALSE);
936                         } else {
937                                 proto_tree_add_text(pdutree,
938                                     tvb, offset, length,
939                                     "Packets: length %u", length);
940                         }
941                         break;
942
943                 case 3: /* flows */
944                         if (length == 4) {
945                                 proto_tree_add_item(pdutree, hf_cflow_flows,
946                                     tvb, offset, length, FALSE);
947                         } else {
948                                 proto_tree_add_text(pdutree,
949                                     tvb, offset, length,
950                                     "Flows: length %u", length);
951                         }
952                         break;
953
954                 case 4: /* proto */
955                         proto_tree_add_item(pdutree, hf_cflow_prot,
956                             tvb, offset, length, FALSE);
957                         break;
958
959                 case 5: /* TOS */
960                         proto_tree_add_item(pdutree, hf_cflow_tos,
961                             tvb, offset, length, FALSE);
962                         break;
963
964                 case 6: /* TCP flags */
965                         proto_tree_add_item(pdutree, hf_cflow_tcpflags,
966                             tvb, offset, length, FALSE);
967                         break;
968
969                 case 7: /* source port */
970                         proto_tree_add_item(pdutree, hf_cflow_srcport,
971                             tvb, offset, length, FALSE);
972                         break;
973
974                 case 8: /* source IP */
975                         if (length == 4) {
976                                 tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
977                                     sizeof(ipv4addr));
978                                 proto_tree_add_ipv4(pdutree, hf_cflow_srcaddr,
979                                     tvb, offset, length, ipv4addr);
980                         } else if (length == 16) {
981                                 tvb_memcpy(tvb, ipv6addr, offset,
982                                     sizeof(ipv6addr));
983                                 proto_tree_add_ipv6(pdutree, hf_cflow_srcaddr_v6,
984                                     tvb, offset, length, ipv6addr);
985                         } else {
986                                 proto_tree_add_text(pdutree,
987                                     tvb, offset, length,
988                                     "SrcAddr: length %u", length);
989                         }
990                         break;
991
992                 case 9: /* source mask */
993                         proto_tree_add_item(pdutree, hf_cflow_srcmask,
994                             tvb, offset, length, FALSE);
995                         break;
996
997                 case 10: /* input SNMP */
998                         proto_tree_add_item(pdutree, hf_cflow_inputint,
999                             tvb, offset, length, FALSE);
1000                         break;
1001
1002                 case 11: /* dest port */
1003                         proto_tree_add_item(pdutree, hf_cflow_dstport,
1004                             tvb, offset, length, FALSE);
1005                         break;
1006
1007                 case 12: /* dest IP */
1008                         if (length == 4) {
1009                                 tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
1010                                     sizeof(ipv4addr));
1011                                 proto_tree_add_ipv4(pdutree, hf_cflow_dstaddr,
1012                                     tvb, offset, length, ipv4addr);
1013                         } else if (length == 16) {
1014                                 tvb_memcpy(tvb, ipv6addr, offset,
1015                                     sizeof(ipv6addr));
1016                                 proto_tree_add_ipv6(pdutree, hf_cflow_dstaddr_v6,
1017                                     tvb, offset, length, ipv6addr);
1018                         } else {
1019                                 proto_tree_add_text(pdutree,
1020                                     tvb, offset, length,
1021                                     "DstAddr: length %u", length);
1022                         }
1023                         break;
1024
1025                 case 13: /* dest mask */
1026                         proto_tree_add_item(pdutree, hf_cflow_dstmask,
1027                             tvb, offset, length, FALSE);
1028                         break;
1029
1030                 case 14: /* output SNMP */
1031                         proto_tree_add_item(pdutree, hf_cflow_outputint,
1032                             tvb, offset, length, FALSE);
1033                         break;
1034
1035                 case 15: /* nexthop IP */
1036                         if (length == 4) {
1037                                 tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
1038                                     sizeof(ipv4addr));
1039                                 proto_tree_add_ipv4(pdutree, hf_cflow_nexthop,
1040                                     tvb, offset, length, ipv4addr);
1041                         } else if (length == 16) {
1042                                 tvb_memcpy(tvb, ipv6addr, offset,
1043                                     sizeof(ipv6addr));
1044                                 proto_tree_add_ipv6(pdutree, hf_cflow_nexthop_v6,
1045                                     tvb, offset, length, ipv6addr);
1046                         } else {
1047                                 proto_tree_add_text(pdutree,
1048                                     tvb, offset, length,
1049                                     "NextHop: length %u", length);
1050                         }
1051                         break;
1052
1053                 case 16: /* source AS */
1054                         proto_tree_add_item(pdutree, hf_cflow_srcas,
1055                             tvb, offset, length, FALSE);
1056                         break;
1057
1058                 case 17: /* dest AS */
1059                         proto_tree_add_item(pdutree, hf_cflow_dstas,
1060                             tvb, offset, length, FALSE);
1061                         break;
1062
1063                 case 18: /* BGP nexthop IP */
1064                         if (length == 4) {
1065                                 tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
1066                                     sizeof(ipv4addr));
1067                                 proto_tree_add_ipv4(pdutree, hf_cflow_bgpnexthop,
1068                                     tvb, offset, length, ipv4addr);
1069                         } else if (length == 16) {
1070                                 tvb_memcpy(tvb, ipv6addr, offset,
1071                                     sizeof(ipv6addr));
1072                                 proto_tree_add_ipv6(pdutree, hf_cflow_bgpnexthop_v6,
1073                                     tvb, offset, length, ipv6addr);
1074                         } else {
1075                                 proto_tree_add_text(pdutree,
1076                                     tvb, offset, length,
1077                                     "BGPNextHop: length %u", length);
1078                         }
1079                         break;
1080
1081                 case 19: /* multicast packets */
1082                         proto_tree_add_item(pdutree, hf_cflow_mulpackets,
1083                             tvb, offset, length, FALSE);
1084                         break;
1085
1086                 case 20: /* multicast octets */
1087                         proto_tree_add_item(pdutree, hf_cflow_muloctets,
1088                             tvb, offset, length, FALSE);
1089                         break;
1090
1091                 case 21: /* last switched */
1092                         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
1093                         ts.nsecs = 0;
1094                         proto_tree_add_time(pdutree, hf_cflow_timeend,
1095                             tvb, offset, length, &ts);
1096                         break;
1097
1098                 case 22: /* first switched */
1099                         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
1100                         ts.nsecs = 0;
1101                         proto_tree_add_time(pdutree, hf_cflow_timestart,
1102                             tvb, offset, length, &ts);
1103                         break;
1104
1105                 case 40: /* bytes exported */
1106                         proto_tree_add_item(pdutree, hf_cflow_octets_exp,
1107                             tvb, offset, length, FALSE);
1108                         break;
1109
1110                 case 41: /* packets exported */
1111                         proto_tree_add_item(pdutree, hf_cflow_packets_exp,
1112                             tvb, offset, length, FALSE);
1113                         break;
1114
1115                 case 42: /* flows exported */
1116                         proto_tree_add_item(pdutree, hf_cflow_flows_exp,
1117                             tvb, offset, length, FALSE);
1118                         break;
1119
1120                 default:
1121                         proto_tree_add_text(pdutree, tvb, offset, length,
1122                             "Type %u", type);
1123                         break;
1124                 }
1125
1126                 offset += length;
1127         }
1128 }
1129
1130 #if 0
1131 static int
1132 dissect_v9_options(proto_tree * pdutree, tvbuff_t * tvb, int offset)
1133 {
1134         return (0);
1135 }
1136 #endif
1137
1138 static int
1139 dissect_v9_template(proto_tree * pdutree, tvbuff_t * tvb, int offset)
1140 {
1141         struct v9_template template;
1142         proto_tree *template_tree;
1143         proto_item *template_item;
1144         guint16 id, count;
1145         gint32 i;
1146
1147         id = tvb_get_ntohs(tvb, offset);
1148         proto_tree_add_item(pdutree, hf_cflow_template_id, tvb,
1149             offset, 2, FALSE);
1150         offset += 2;
1151
1152         count = tvb_get_ntohs(tvb, offset);
1153         proto_tree_add_item(pdutree, hf_cflow_template_field_count, tvb,
1154             offset, 2, FALSE);
1155         offset += 2;
1156
1157         /* Cache template */
1158         memset(&template, 0, sizeof(template));
1159         template.id = id;
1160         template.count = count;
1161         template.source_addr = 0;       /* XXX */
1162         template.source_id = 0;         /* XXX */
1163         tvb_memcpy(tvb, (guint8 *)template.entries, offset,
1164             count * sizeof(struct v9_template_entry));
1165         v9_template_add(&template);
1166
1167         for (i = 1; i <= count; i++) {
1168                 guint16 type, length;
1169
1170                 type = tvb_get_ntohs(tvb, offset);
1171                 length = tvb_get_ntohs(tvb, offset + 2);
1172
1173                 template_item = proto_tree_add_text(pdutree, tvb,
1174                     offset, 4, "Field (%u/%u)", i, count);
1175                 template_tree = proto_item_add_subtree(template_item, ett_template);
1176
1177                 proto_tree_add_item(template_tree,
1178                     hf_cflow_template_field_type, tvb, offset, 2, FALSE);
1179                 offset += 2;
1180
1181                 proto_tree_add_item(template_tree,
1182                     hf_cflow_template_field_length, tvb, offset, 2, FALSE);
1183                 offset += 2;
1184         }
1185
1186         return (0);
1187 }
1188
1189 static value_string v9_template_types[] = {
1190         { 1, "BYTES" },
1191         { 2, "PKTS" },
1192         { 3, "FLOWS" },
1193         { 4, "PROT" },
1194         { 5, "TOS" },
1195         { 6, "TCP_FLAGS" },
1196         { 7, "L4_SRC_PORT" },
1197         { 8, "IP_SRC_ADDR" },
1198         { 9, "SRC_MASK" },
1199         { 10, "INPUT_SNMP" },
1200         { 11, "L4_DST_PORT" },
1201         { 12, "IP_DST_ADDR" },
1202         { 13, "DST_MASK" },
1203         { 14, "OUTPUT_SNMP" },
1204         { 15, "IP_NEXT_HOP" },
1205         { 16, "SRC_AS" },
1206         { 17, "DST_AS" },
1207         { 18, "BGP_NEXT_HOP" },
1208         { 19, "MUL_DPKTS" },
1209         { 20, "MUL_DOCTETS" },
1210         { 21, "LAST_SWITCHED" },
1211         { 22, "FIRST_SWITCHED" },
1212         { 24, "OUT_PKTS" },
1213         { 40, "TOTAL_BYTES_EXP" },
1214         { 41, "TOTAL_PKTS_EXP" },
1215         { 42, "TOTAL_FLOWS_EXP" },
1216         { 0, NULL },
1217 };
1218
1219 static void
1220 v9_template_add(struct v9_template *template)
1221 {
1222         int i;
1223
1224         /* Add up the actual length of the data and store in proper byte order */
1225         template->length = 0;
1226         for (i = 0; i < template->count; i++) {
1227                 template->entries[i].type = g_ntohs(template->entries[i].type);
1228                 template->entries[i].length = g_ntohs(template->entries[i].length);
1229                 template->length += template->entries[i].length;
1230         }
1231
1232         memmove(&v9_template_cache[template->id % V9TEMPLATE_CACHE_MAX_ENTRIES],
1233             template, sizeof(*template));
1234 }
1235
1236 static struct v9_template *
1237 v9_template_get(guint16 id, guint32 src_addr, guint32 src_id)
1238 {
1239         struct v9_template *template;
1240
1241         src_addr = 0;
1242         template = &v9_template_cache[id % V9TEMPLATE_CACHE_MAX_ENTRIES];
1243
1244         if (template->id != id ||
1245             template->source_addr != src_addr ||
1246             template->source_id != src_id) {
1247                 template = NULL;
1248         }
1249
1250         return (template);
1251 }
1252
1253 /*
1254  * dissect a version 1, 5, or 7 pdu and return the length of the pdu we
1255  * processed
1256  */
1257
1258 static int
1259 dissect_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver)
1260 {
1261         int             startoffset = offset;
1262         guint32         srcaddr, dstaddr;
1263         guint8          mask;
1264         nstime_t        ts;
1265
1266         memset(&ts, '\0', sizeof(ts));
1267
1268         /*
1269          * memcpy so we can use the values later to calculate a prefix 
1270          */
1271         tvb_memcpy(tvb, (guint8 *) & srcaddr, offset, 4);
1272         proto_tree_add_ipv4(pdutree, hf_cflow_srcaddr, tvb, offset, 4,
1273                             srcaddr);
1274         offset += 4;
1275
1276         tvb_memcpy(tvb, (guint8 *) & dstaddr, offset, 4);
1277         proto_tree_add_ipv4(pdutree, hf_cflow_dstaddr, tvb, offset, 4,
1278                             dstaddr);
1279         offset += 4;
1280
1281         proto_tree_add_item(pdutree, hf_cflow_nexthop, tvb, offset, 4, FALSE);
1282         offset += 4;
1283
1284         offset = flow_process_ints(pdutree, tvb, offset);
1285         offset = flow_process_sizecount(pdutree, tvb, offset);
1286         offset = flow_process_timeperiod(pdutree, tvb, offset);
1287         offset = flow_process_ports(pdutree, tvb, offset);
1288
1289         /*
1290          * and the similarities end here 
1291          */
1292         if (ver == 1) {
1293                 offset =
1294                     flow_process_textfield(pdutree, tvb, offset, 2, "padding");
1295
1296                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
1297                                     FALSE);
1298
1299                 proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1,
1300                                     FALSE);
1301
1302                 proto_tree_add_item(pdutree, hf_cflow_tcpflags, tvb, offset++,
1303                                     1, FALSE);
1304
1305                 offset =
1306                     flow_process_textfield(pdutree, tvb, offset, 3, "padding");
1307
1308                 offset =
1309                     flow_process_textfield(pdutree, tvb, offset, 4,
1310                                            "reserved");
1311         } else {
1312                 if (ver == 5)
1313                         offset =
1314                             flow_process_textfield(pdutree, tvb, offset, 1,
1315                                                    "padding");
1316                 else {
1317                         proto_tree_add_item(pdutree, hf_cflow_flags, tvb,
1318                                             offset++, 1, FALSE);
1319                 }
1320
1321                 proto_tree_add_item(pdutree, hf_cflow_tcpflags, tvb, offset++,
1322                                     1, FALSE);
1323
1324                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
1325                                     FALSE);
1326
1327                 proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1,
1328                                     FALSE);
1329
1330                 offset = flow_process_aspair(pdutree, tvb, offset);
1331
1332                 mask = tvb_get_guint8(tvb, offset);
1333                 proto_tree_add_text(pdutree, tvb, offset, 1,
1334                                     "SrcMask: %u (prefix: %s/%u)",
1335                                     mask, getprefix(&srcaddr, mask),
1336                                     mask != 0 ? mask : 32);
1337                 proto_tree_add_uint_hidden(pdutree, hf_cflow_srcmask, tvb,
1338                                            offset++, 1, mask);
1339
1340                 mask = tvb_get_guint8(tvb, offset);
1341                 proto_tree_add_text(pdutree, tvb, offset, 1,
1342                                     "DstMask: %u (prefix: %s/%u)",
1343                                     mask, getprefix(&dstaddr, mask),
1344                                     mask != 0 ? mask : 32);
1345                 proto_tree_add_uint_hidden(pdutree, hf_cflow_dstmask, tvb,
1346                                            offset++, 1, mask);
1347
1348                 offset =
1349                     flow_process_textfield(pdutree, tvb, offset, 2, "padding");
1350
1351                 if (ver == 7) {
1352                         proto_tree_add_item(pdutree, hf_cflow_routersc, tvb,
1353                                             offset, 4, FALSE);
1354                         offset += 4;
1355                 }
1356         }
1357
1358         return (offset - startoffset);
1359 }
1360
1361 static gchar   *
1362 getprefix(const guint32 * address, int prefix)
1363 {
1364         guint32         gprefix;
1365
1366         gprefix = *address & g_htonl((0xffffffff << (32 - prefix)));
1367
1368         return (ip_to_str((const guint8 *)&gprefix));
1369 }
1370
1371 void
1372 proto_register_netflow(void)
1373 {
1374         static hf_register_info hf[] = {
1375                 /*
1376                  * flow header 
1377                  */
1378                 {&hf_cflow_version,
1379                  {"Version", "cflow.version",
1380                   FT_UINT16, BASE_DEC, NULL, 0x0,
1381                   "NetFlow Version", HFILL}
1382                  },
1383                 {&hf_cflow_count,
1384                  {"Count", "cflow.count",
1385                   FT_UINT16, BASE_DEC, NULL, 0x0,
1386                   "Count of PDUs", HFILL}
1387                  },
1388                 {&hf_cflow_sysuptime,
1389                  {"SysUptime", "cflow.sysuptime",
1390                   FT_UINT32, BASE_DEC, NULL, 0x0,
1391                   "Time since router booted (in milliseconds)", HFILL}
1392                  },
1393
1394                 {&hf_cflow_timestamp,
1395                  {"Timestamp", "cflow.timestamp",
1396                   FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
1397                   "Current seconds since epoch", HFILL}
1398                  },
1399                 {&hf_cflow_unix_secs,
1400                  {"CurrentSecs", "cflow.unix_secs",
1401                   FT_UINT32, BASE_DEC, NULL, 0x0,
1402                   "Current seconds since epoch", HFILL}
1403                  },
1404                 {&hf_cflow_unix_nsecs,
1405                  {"CurrentNSecs", "cflow.unix_nsecs",
1406                   FT_UINT32, BASE_DEC, NULL, 0x0,
1407                   "Residual nanoseconds since epoch", HFILL}
1408                  },
1409                 {&hf_cflow_samplerate,
1410                  {"SampleRate", "cflow.samplerate",
1411                   FT_UINT16, BASE_DEC, NULL, 0x0,
1412                   "Sample Frequency of exporter", HFILL}
1413                  },
1414
1415                 /*
1416                  * end version-agnostic header
1417                  * version-specific flow header 
1418                  */
1419                 {&hf_cflow_sequence,
1420                  {"FlowSequence", "cflow.sequence",
1421                   FT_UINT32, BASE_DEC, NULL, 0x0,
1422                   "Sequence number of flows seen", HFILL}
1423                  },
1424                 {&hf_cflow_engine_type,
1425                  {"EngineType", "cflow.engine_type",
1426                   FT_UINT8, BASE_DEC, NULL, 0x0,
1427                   "Flow switching engine type", HFILL}
1428                  },
1429                 {&hf_cflow_engine_id,
1430                  {"EngineId", "cflow.engine_id",
1431                   FT_UINT8, BASE_DEC, NULL, 0x0,
1432                   "Slot number of switching engine", HFILL}
1433                  },
1434                 {&hf_cflow_source_id,
1435                  {"SourceId", "cflow.source_id",
1436                   FT_UINT32, BASE_DEC, NULL, 0x0,
1437                   "Identifier for export device", HFILL}
1438                  },
1439                 {&hf_cflow_aggmethod,
1440                  {"AggMethod", "cflow.aggmethod",
1441                   FT_UINT8, BASE_DEC, VALS(v8_agg), 0x0,
1442                   "CFlow V8 Aggregation Method", HFILL}
1443                  },
1444                 {&hf_cflow_aggversion,
1445                  {"AggVersion", "cflow.aggversion",
1446                   FT_UINT8, BASE_DEC, NULL, 0x0,
1447                   "CFlow V8 Aggregation Version", HFILL}
1448                  },
1449                 /*
1450                  * end version specific header storage 
1451                  */
1452                 /*
1453                  * Version 9
1454                  */
1455                 {&hf_cflow_flowset_id,
1456                  {"FlowSet Id", "cflow.flowset_id",
1457                   FT_UINT16, BASE_DEC, NULL, 0x0,
1458                   "FlowSet Id", HFILL}
1459                  },
1460                 {&hf_cflow_data_flowset_id,
1461                  {"Data FlowSet (Template Id)", "cflow.data_flowset_id",
1462                   FT_UINT16, BASE_DEC, NULL, 0x0,
1463                   "Data FlowSet with corresponding to a template Id", HFILL}
1464                  },
1465                 {&hf_cflow_options_flowset_id,
1466                  {"Options FlowSet", "cflow.options_flowset_id",
1467                   FT_UINT16, BASE_DEC, NULL, 0x0,
1468                   "Options FlowSet", HFILL}
1469                  },
1470                 {&hf_cflow_template_flowset_id,
1471                  {"Template FlowSet", "cflow.template_flowset_id",
1472                   FT_UINT16, BASE_DEC, NULL, 0x0,
1473                   "Template FlowSet", HFILL}
1474                  },
1475                 {&hf_cflow_flowset_length,
1476                  {"FlowSet Length", "cflow.flowset_length",
1477                   FT_UINT16, BASE_DEC, NULL, 0x0,
1478                   "FlowSet length", HFILL}
1479                  },
1480                 {&hf_cflow_template_id,
1481                  {"Template Id", "cflow.template_id",
1482                   FT_UINT16, BASE_DEC, NULL, 0x0,
1483                   "Template Id", HFILL}
1484                  },
1485                 {&hf_cflow_template_field_count,
1486                  {"Field Count", "cflow.template_field_count",
1487                   FT_UINT16, BASE_DEC, NULL, 0x0,
1488                   "Template field count", HFILL}
1489                  },
1490                 {&hf_cflow_template_field_type,
1491                  {"Type", "cflow.template_field_type",
1492                   FT_UINT16, BASE_DEC, VALS(v9_template_types), 0x0,
1493                   "Template field type", HFILL}
1494                  },
1495                 {&hf_cflow_template_field_length,
1496                  {"Length", "cflow.template_field_length",
1497                   FT_UINT16, BASE_DEC, NULL, 0x0,
1498                   "Template field length", HFILL}
1499                  },
1500                 /*
1501                  * begin pdu content storage 
1502                  */
1503                 {&hf_cflow_srcaddr,
1504                  {"SrcAddr", "cflow.srcaddr",
1505                   FT_IPv4, BASE_NONE, NULL, 0x0,
1506                   "Flow Source Address", HFILL}
1507                  },
1508                 {&hf_cflow_srcaddr_v6,
1509                  {"SrcAddr", "cflow.srcaddrv6",
1510                   FT_IPv6, BASE_NONE, NULL, 0x0,
1511                   "Flow Source Address", HFILL}
1512                  },
1513                 {&hf_cflow_srcnet,
1514                  {"SrcNet", "cflow.srcnet",
1515                   FT_IPv4, BASE_NONE, NULL, 0x0,
1516                   "Flow Source Network", HFILL}
1517                  },
1518                 {&hf_cflow_dstaddr,
1519                  {"DstAddr", "cflow.dstaddr",
1520                   FT_IPv4, BASE_NONE, NULL, 0x0,
1521                   "Flow Destination Address", HFILL}
1522                  },
1523                 {&hf_cflow_dstaddr_v6,
1524                  {"DstAddr", "cflow.dstaddrv6",
1525                   FT_IPv6, BASE_NONE, NULL, 0x0,
1526                   "Flow Destination Address", HFILL}
1527                  },
1528                 {&hf_cflow_dstnet,
1529                  {"DstNet", "cflow.dstaddr",
1530                   FT_IPv4, BASE_NONE, NULL, 0x0,
1531                   "Flow Destination Network", HFILL}
1532                  },
1533                 {&hf_cflow_nexthop,
1534                  {"NextHop", "cflow.nexthop",
1535                   FT_IPv4, BASE_NONE, NULL, 0x0,
1536                   "Router nexthop", HFILL}
1537                  },
1538                 {&hf_cflow_nexthop_v6,
1539                  {"NextHop", "cflow.nexthopv6",
1540                   FT_IPv6, BASE_NONE, NULL, 0x0,
1541                   "Router nexthop", HFILL}
1542                  },
1543                 {&hf_cflow_bgpnexthop,
1544                  {"BGPNextHop", "cflow.bgpnexthop",
1545                   FT_IPv4, BASE_NONE, NULL, 0x0,
1546                   "BGP Router Nexthop", HFILL}
1547                  },
1548                 {&hf_cflow_bgpnexthop_v6,
1549                  {"BGPNextHop", "cflow.bgpnexthopv6",
1550                   FT_IPv6, BASE_NONE, NULL, 0x0,
1551                   "BGP Router Nexthop", HFILL}
1552                  },
1553                 {&hf_cflow_inputint,
1554                  {"InputInt", "cflow.inputint",
1555                   FT_UINT16, BASE_DEC, NULL, 0x0,
1556                   "Flow Input Interface", HFILL}
1557                  },
1558                 {&hf_cflow_outputint,
1559                  {"OutputInt", "cflow.outputint",
1560                   FT_UINT16, BASE_DEC, NULL, 0x0,
1561                   "Flow Output Interface", HFILL}
1562                  },
1563                 {&hf_cflow_flows,
1564                  {"Flows", "cflow.flows",
1565                   FT_UINT32, BASE_DEC, NULL, 0x0,
1566                   "Flows Aggregated in PDU", HFILL}
1567                  },
1568                 {&hf_cflow_packets,
1569                  {"Packets", "cflow.packets",
1570                   FT_UINT32, BASE_DEC, NULL, 0x0,
1571                   "Count of packets", HFILL}
1572                  },
1573                 {&hf_cflow_packets64,
1574                  {"Packets", "cflow.packets64",
1575                   FT_UINT64, BASE_DEC, NULL, 0x0,
1576                   "Count of packets", HFILL}
1577                  },
1578                 {&hf_cflow_packetsout,
1579                  {"PacketsOut", "cflow.packetsout",
1580                   FT_UINT64, BASE_DEC, NULL, 0x0,
1581                   "Count of packets going out", HFILL}
1582                  },
1583                 {&hf_cflow_octets,
1584                  {"Octets", "cflow.octets",
1585                   FT_UINT32, BASE_DEC, NULL, 0x0,
1586                   "Count of bytes", HFILL}
1587                  },
1588                 {&hf_cflow_octets64,
1589                  {"Octets", "cflow.octets64",
1590                   FT_UINT64, BASE_DEC, NULL, 0x0,
1591                   "Count of bytes", HFILL}
1592                  },
1593                 {&hf_cflow_timestart,
1594                  {"StartTime", "cflow.timestart",
1595                   FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1596                   "Uptime at start of flow", HFILL}
1597                  },
1598                 {&hf_cflow_timeend,
1599                  {"EndTime", "cflow.timeend",
1600                   FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1601                   "Uptime at end of flow", HFILL}
1602                  },
1603                 {&hf_cflow_srcport,
1604                  {"SrcPort", "cflow.srcport",
1605                   FT_UINT16, BASE_DEC, NULL, 0x0,
1606                   "Flow Source Port", HFILL}
1607                  },
1608                 {&hf_cflow_dstport,
1609                  {"DstPort", "cflow.dstport",
1610                   FT_UINT16, BASE_DEC, NULL, 0x0,
1611                   "Flow Destination Port", HFILL}
1612                  },
1613                 {&hf_cflow_prot,
1614                  {"Protocol", "cflow.protocol",
1615                   FT_UINT8, BASE_DEC, NULL, 0x0,
1616                   "IP Protocol", HFILL}
1617                  },
1618                 {&hf_cflow_tos,
1619                  {"IP ToS", "cflow.tos",
1620                   FT_UINT8, BASE_HEX, NULL, 0x0,
1621                   "IP Type of Service", HFILL}
1622                  },
1623                 {&hf_cflow_flags,
1624                  {"Export Flags", "cflow.flags",
1625                   FT_UINT8, BASE_HEX, NULL, 0x0,
1626                   "CFlow Flags", HFILL}
1627                  },
1628                 {&hf_cflow_tcpflags,
1629                  {"TCP Flags", "cflow.tcpflags",
1630                   FT_UINT8, BASE_HEX, NULL, 0x0,
1631                   "TCP Flags", HFILL}
1632                  },
1633                 {&hf_cflow_srcas,
1634                  {"SrcAS", "cflow.srcas",
1635                   FT_UINT16, BASE_DEC, NULL, 0x0,
1636                   "Source AS", HFILL}
1637                  },
1638                 {&hf_cflow_dstas,
1639                  {"DstAS", "cflow.dstas",
1640                   FT_UINT16, BASE_DEC, NULL, 0x0,
1641                   "Destination AS", HFILL}
1642                  },
1643                 {&hf_cflow_srcmask,
1644                  {"SrcMask", "cflow.srcmask",
1645                   FT_UINT8, BASE_DEC, NULL, 0x0,
1646                   "Source Prefix Mask", HFILL}
1647                  },
1648                 {&hf_cflow_dstmask,
1649                  {"DstMask", "cflow.dstmask",
1650                   FT_UINT8, BASE_DEC, NULL, 0x0,
1651                   "Destination Prefix Mask", HFILL}
1652                  },
1653                 {&hf_cflow_routersc,
1654                  {"Router Shortcut", "cflow.routersc",
1655                   FT_IPv4, BASE_NONE, NULL, 0x0,
1656                   "Router shortcut by switch", HFILL}
1657                  },
1658                 {&hf_cflow_mulpackets,
1659                  {"MulticastPackets", "cflow.mulpackets",
1660                   FT_UINT32, BASE_DEC, NULL, 0x0,
1661                   "Count of multicast packets", HFILL}
1662                  },
1663                 {&hf_cflow_muloctets,
1664                  {"MulticastOctets", "cflow.muloctets",
1665                   FT_UINT32, BASE_DEC, NULL, 0x0,
1666                   "Count of multicast octets", HFILL}
1667                  },
1668                 {&hf_cflow_octets_exp,
1669                  {"OctetsExp", "cflow.octetsexp",
1670                   FT_UINT32, BASE_DEC, NULL, 0x0,
1671                   "Octets exported", HFILL}
1672                  },
1673                 {&hf_cflow_packets_exp,
1674                  {"PacketsExp", "cflow.packetsexp",
1675                   FT_UINT32, BASE_DEC, NULL, 0x0,
1676                   "Packets exported", HFILL}
1677                  },
1678                 {&hf_cflow_flows_exp,
1679                  {"FlowsExp", "cflow.flowsexp",
1680                   FT_UINT32, BASE_DEC, NULL, 0x0,
1681                   "Flows exported", HFILL}
1682                  }
1683                 /*
1684                  * end pdu content storage 
1685                  */
1686         };
1687
1688         static gint    *ett[] = {
1689                 &ett_netflow,
1690                 &ett_unixtime,
1691                 &ett_flow,
1692                 &ett_template,
1693                 &ett_dataflowset
1694         };
1695
1696         module_t *netflow_module;
1697
1698         proto_netflow = proto_register_protocol("Cisco NetFlow", "CFLOW",
1699                                                 "cflow");
1700
1701         proto_register_field_array(proto_netflow, hf, array_length(hf));
1702         proto_register_subtree_array(ett, array_length(ett));
1703
1704         /* Register our configuration options for NetFlow */
1705         netflow_module = prefs_register_protocol(proto_netflow,
1706             proto_reg_handoff_netflow);
1707
1708         prefs_register_uint_preference(netflow_module, "udp.port",
1709             "NetFlow UDP Port", "Set the port for NetFlow messages",
1710             10, &global_netflow_udp_port);
1711
1712         register_dissector("cflow", dissect_netflow, proto_netflow);
1713 }
1714
1715
1716 /*
1717  * protocol/port association 
1718  */
1719 void
1720 proto_reg_handoff_netflow(void)
1721 {
1722         static int netflow_prefs_initialized = FALSE;
1723         static dissector_handle_t netflow_handle;
1724
1725         if (!netflow_prefs_initialized) {
1726                 netflow_handle = create_dissector_handle(dissect_netflow,
1727                     proto_netflow);
1728                 netflow_prefs_initialized = TRUE;
1729         } else {
1730                 dissector_delete("udp.port", netflow_udp_port, netflow_handle);
1731         }
1732
1733         /* Set out port number for future use */
1734         netflow_udp_port = global_netflow_udp_port;
1735
1736         dissector_add("udp.port", netflow_udp_port, netflow_handle);
1737 }