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