Put the value(s) of a parameter into the top-level item for that
[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.11 2004/03/09 20:08:26 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_CACHE_MAX_ENTRIES    100
126
127 struct v9_template_entry {
128         guint16 type;
129         guint16 length;
130 };
131
132 struct v9_template {
133         guint16 id;
134         guint16 count;
135         guint32 length;
136         guint32 source_id;
137         guint32 source_addr;
138         struct v9_template_entry *entries;
139 };
140
141 static struct v9_template v9_template_cache[V9TEMPLATE_CACHE_MAX_ENTRIES];
142
143 /*
144  * ethereal tree identifiers
145  */
146
147 static int      proto_netflow = -1;
148 static int      ett_netflow = -1;
149 static int      ett_unixtime = -1;
150 static int      ett_flow = -1;
151 static int      ett_template = -1;
152 static int      ett_dataflowset = -1;
153
154 /*
155  * cflow header 
156  */
157
158 static int      hf_cflow_version = -1;
159 static int      hf_cflow_count = -1;
160 static int      hf_cflow_sysuptime = -1;
161 static int      hf_cflow_unix_secs = -1;
162 static int      hf_cflow_unix_nsecs = -1;
163 static int      hf_cflow_timestamp = -1;
164 static int      hf_cflow_samplerate = -1;
165
166 /*
167  * cflow version specific info 
168  */
169 static int      hf_cflow_sequence = -1;
170 static int      hf_cflow_engine_type = -1;
171 static int      hf_cflow_engine_id = -1;
172 static int      hf_cflow_source_id = -1;
173
174 static int      hf_cflow_aggmethod = -1;
175 static int      hf_cflow_aggversion = -1;
176
177 /* Version 9 */
178
179 static int      hf_cflow_template_flowset_id = -1;
180 static int      hf_cflow_data_flowset_id = -1;
181 static int      hf_cflow_options_flowset_id = -1;
182 static int      hf_cflow_flowset_id = -1;
183 static int      hf_cflow_flowset_length = -1;
184 static int      hf_cflow_template_id = -1;
185 static int      hf_cflow_template_field_count = -1;
186 static int      hf_cflow_template_field_type = -1;
187 static int      hf_cflow_template_field_length = -1;
188
189 /*
190  * pdu storage
191  */
192 static int      hf_cflow_srcaddr = -1;
193 static int      hf_cflow_srcaddr_v6 = -1;
194 static int      hf_cflow_srcnet = -1;
195 static int      hf_cflow_dstaddr = -1;
196 static int      hf_cflow_dstaddr_v6 = -1;
197 static int      hf_cflow_dstnet = -1;
198 static int      hf_cflow_nexthop = -1;
199 static int      hf_cflow_nexthop_v6 = -1;
200 static int      hf_cflow_bgpnexthop = -1;
201 static int      hf_cflow_bgpnexthop_v6 = -1;
202 static int      hf_cflow_inputint = -1;
203 static int      hf_cflow_outputint = -1;
204 static int      hf_cflow_flows = -1;
205 static int      hf_cflow_packets = -1;
206 static int      hf_cflow_packets64 = -1;
207 static int      hf_cflow_packetsout = -1;
208 static int      hf_cflow_octets = -1;
209 static int      hf_cflow_octets64 = -1;
210 static int      hf_cflow_timestart = -1;
211 static int      hf_cflow_timeend = -1;
212 static int      hf_cflow_srcport = -1;
213 static int      hf_cflow_dstport = -1;
214 static int      hf_cflow_prot = -1;
215 static int      hf_cflow_tos = -1;
216 static int      hf_cflow_flags = -1;
217 static int      hf_cflow_tcpflags = -1;
218 static int      hf_cflow_dstas = -1;
219 static int      hf_cflow_srcas = -1;
220 static int      hf_cflow_dstmask = -1;
221 static int      hf_cflow_srcmask = -1;
222 static int      hf_cflow_routersc = -1;
223 static int      hf_cflow_mulpackets = -1;
224 static int      hf_cflow_muloctets = -1;
225 static int      hf_cflow_octets_exp = -1;
226 static int      hf_cflow_packets_exp = -1;
227 static int      hf_cflow_flows_exp = -1;
228
229 void            proto_reg_handoff_netflow(void);
230
231 typedef int     dissect_pdu_t(proto_tree * pdutree, tvbuff_t * tvb, int offset,
232                               int verspec);
233 static int      dissect_pdu(proto_tree * tree, tvbuff_t * tvb, int offset,
234                             int verspec);
235 static int      dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb,
236                                   int offset, int verspec);
237 static int      dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb,
238                                    int offset, int verspec);
239 static int      dissect_v9_flowset(proto_tree * pdutree, tvbuff_t * tvb,
240                                    int offset, int verspec);
241 static int      dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb,
242                                int offset, guint16 id, guint length);
243 static void     dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb,
244                                int offset, struct v9_template * template);
245 #if 0
246 static int      dissect_v9_options(proto_tree * pdutree, tvbuff_t * tvb,
247                                int offset);
248 #endif
249 static int      dissect_v9_template(proto_tree * pdutree, tvbuff_t * tvb,
250                                     int offset);
251 static void     v9_template_add(struct v9_template * template);
252 static struct v9_template *v9_template_get(guint16 id, guint32 src_addr,
253                                            guint32 src_id);
254
255 static gchar   *getprefix(const guint32 * address, int prefix);
256 static void     dissect_netflow(tvbuff_t * tvb, packet_info * pinfo,
257                                 proto_tree * tree);
258
259 static int      flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb,
260                                   int offset);
261 static int      flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb,
262                                    int offset);
263 static int      flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb,
264                                         int offset);
265 static int      flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb,
266                                     int offset);
267 static int      flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb,
268                                        int offset);
269 static int      flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb,
270                                        int offset, int bytes,
271                                        const char *text);
272
273
274 static void
275 dissect_netflow(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
276 {
277         proto_tree     *netflow_tree = NULL;
278         proto_tree     *ti;
279         proto_item     *timeitem, *pduitem;
280         proto_tree     *timetree, *pdutree;
281         unsigned int    pduret, ver = 0, pdus = 0, x = 1, vspec;
282         size_t          available, pdusize, offset = 0;
283         nstime_t        ts;
284         dissect_pdu_t  *pduptr;
285
286         if (check_col(pinfo->cinfo, COL_PROTOCOL))
287                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "CFLOW");
288         if (check_col(pinfo->cinfo, COL_INFO))
289                 col_clear(pinfo->cinfo, COL_INFO);
290
291         if (tree) {
292                 ti = proto_tree_add_item(tree, proto_netflow, tvb,
293                                          offset, -1, FALSE);
294                 netflow_tree = proto_item_add_subtree(ti, ett_netflow);
295         }
296
297         ver = tvb_get_ntohs(tvb, offset);
298         vspec = ver;
299         switch (ver) {
300         case 1:
301                 pdusize = V1PDU_SIZE;
302                 pduptr = &dissect_pdu;
303                 break;
304         case 5:
305                 pdusize = V5PDU_SIZE;
306                 pduptr = &dissect_pdu;
307                 break;
308         case 7:
309                 pdusize = V7PDU_SIZE;
310                 pduptr = &dissect_pdu;
311                 break;
312         case 8:
313                 pdusize = -1;   /* deferred */
314                 pduptr = &dissect_v8_aggpdu;
315                 break;
316         case 9:
317                 pdusize = -1;   /* deferred */
318                 pduptr = &dissect_v9_flowset;
319                 break;
320         default:
321                 return;
322         }
323
324         if (tree)
325                 proto_tree_add_uint(netflow_tree, hf_cflow_version, tvb,
326                                     offset, 2, ver);
327         offset += 2;
328
329         pdus = tvb_get_ntohs(tvb, offset);
330         if (pdus <= 0)
331                 return;
332         if (tree)
333                 proto_tree_add_uint(netflow_tree, hf_cflow_count, tvb,
334                                     offset, 2, pdus);
335         offset += 2;
336
337         /*
338          * set something interesting in the display now that we have info 
339          */
340         if (check_col(pinfo->cinfo, COL_INFO)) {
341                 if (ver == 9) {
342                         col_add_fstr(pinfo->cinfo, COL_INFO,
343                             "total: %u (v%u) FlowSets", pdus, ver);
344                 } else {
345                         col_add_fstr(pinfo->cinfo, COL_INFO,
346                             "total: %u (v%u) flows", pdus, ver);
347                 }
348         }
349
350         /*
351          * the rest is only interesting if we're displaying/searching the
352          * packet 
353          */
354         if (!tree)
355                 return;
356
357         proto_tree_add_item(netflow_tree, hf_cflow_sysuptime, tvb,
358                             offset, 4, FALSE);
359         offset += 4;
360
361         ts.secs = tvb_get_ntohl(tvb, offset);
362         ts.nsecs = tvb_get_ntohl(tvb, offset + 4);
363         timeitem = proto_tree_add_time(netflow_tree,
364                                        hf_cflow_timestamp, tvb, offset,
365                                        8, &ts);
366         timetree = proto_item_add_subtree(timeitem, ett_unixtime);
367
368         proto_tree_add_item(timetree, hf_cflow_unix_secs, tvb,
369                             offset, 4, FALSE);
370         offset += 4;
371
372         if (ver != 9) {
373                 proto_tree_add_item(timetree, hf_cflow_unix_nsecs, tvb,
374                                     offset, 4, FALSE);
375                 offset += 4;
376         }
377
378         /*
379          * version specific header 
380          */
381         if (ver == 5 || ver == 7 || ver == 8 || ver == 9) {
382                 proto_tree_add_item(netflow_tree, hf_cflow_sequence,
383                                     tvb, offset, 4, FALSE);
384                 offset += 4;
385         }
386         if (ver == 5 || ver == 8) {
387                 proto_tree_add_item(netflow_tree, hf_cflow_engine_type,
388                                     tvb, offset++, 1, FALSE);
389                 proto_tree_add_item(netflow_tree, hf_cflow_engine_id,
390                                     tvb, offset++, 1, FALSE);
391         } else if (ver == 9) {
392                 proto_tree_add_item(netflow_tree, hf_cflow_source_id,
393                                     tvb, offset, 4, FALSE);
394                 offset += 4;
395         }
396         if (ver == 8) {
397                 vspec = tvb_get_guint8(tvb, offset);
398                 switch (vspec) {
399                 case V8PDU_AS_METHOD:
400                         pdusize = V8PDU_AS_SIZE;
401                         break;
402                 case V8PDU_PROTO_METHOD:
403                         pdusize = V8PDU_PROTO_SIZE;
404                         break;
405                 case V8PDU_SPREFIX_METHOD:
406                         pdusize = V8PDU_SPREFIX_SIZE;
407                         break;
408                 case V8PDU_DPREFIX_METHOD:
409                         pdusize = V8PDU_DPREFIX_SIZE;
410                         break;
411                 case V8PDU_MATRIX_METHOD:
412                         pdusize = V8PDU_MATRIX_SIZE;
413                         break;
414                 case V8PDU_DESTONLY_METHOD:
415                         pdusize = V8PDU_DESTONLY_SIZE;
416                         pduptr = &dissect_v8_flowpdu;
417                         break;
418                 case V8PDU_SRCDEST_METHOD:
419                         pdusize = V8PDU_SRCDEST_SIZE;
420                         pduptr = &dissect_v8_flowpdu;
421                         break;
422                 case V8PDU_FULL_METHOD:
423                         pdusize = V8PDU_FULL_SIZE;
424                         pduptr = &dissect_v8_flowpdu;
425                         break;
426                 case V8PDU_TOSAS_METHOD:
427                         pdusize = V8PDU_TOSAS_SIZE;
428                         break;
429                 case V8PDU_TOSPROTOPORT_METHOD:
430                         pdusize = V8PDU_TOSPROTOPORT_SIZE;
431                         break;
432                 case V8PDU_TOSSRCPREFIX_METHOD:
433                         pdusize = V8PDU_TOSSRCPREFIX_SIZE;
434                         break;
435                 case V8PDU_TOSDSTPREFIX_METHOD:
436                         pdusize = V8PDU_TOSDSTPREFIX_SIZE;
437                         break;
438                 case V8PDU_TOSMATRIX_METHOD:
439                         pdusize = V8PDU_TOSMATRIX_SIZE;
440                         break;
441                 case V8PDU_PREPORTPROTOCOL_METHOD:
442                         pdusize = V8PDU_PREPORTPROTOCOL_SIZE;
443                         break;
444                 default:
445                         pdusize = -1;
446                         vspec = 0;
447                         break;
448                 }
449                 proto_tree_add_uint(netflow_tree, hf_cflow_aggmethod,
450                                     tvb, offset++, 1, vspec);
451                 proto_tree_add_item(netflow_tree, hf_cflow_aggversion,
452                                     tvb, offset++, 1, FALSE);
453         }
454         if (ver == 7 || ver == 8)
455                 offset = flow_process_textfield(netflow_tree, tvb, offset, 4,
456                                                 "reserved");
457         else if (ver == 5) {
458                 proto_tree_add_item(netflow_tree, hf_cflow_samplerate,
459                                     tvb, offset, 2, FALSE);
460                 offset += 2;
461         }
462
463         /*
464          * everything below here should be payload 
465          */
466         for (x = 1; x < pdus + 1; x++) {
467                 /*
468                  * make sure we have a pdu's worth of data 
469                  */
470                 available = tvb_length_remaining(tvb, offset);
471                 if (ver == 9 && available >= 4) {
472                         /* pdusize can be different for each v9 flowset */
473                         pdusize = tvb_get_ntohs(tvb, offset + 2);
474                 }
475
476                 if (available < pdusize)
477                         break;
478
479                 if (ver == 9) {
480                         pduitem = proto_tree_add_text(netflow_tree, tvb,
481                             offset, pdusize, "FlowSet %u/%u", x, pdus);
482                 } else {
483                         pduitem = proto_tree_add_text(netflow_tree, tvb,
484                             offset, pdusize, "pdu %u/%u", x, pdus);
485                 }
486                 pdutree = proto_item_add_subtree(pduitem, ett_flow);
487
488                 pduret = pduptr(pdutree, tvb, offset, vspec);
489
490                 /*
491                  * if we came up short, stop processing 
492                  */
493                 if (pduret == pdusize)
494                         offset += pduret;
495                 else
496                         break;
497         }
498 }
499
500 /*
501  * flow_process_* == common groups of fields, probably could be inline 
502  */
503
504 static int
505 flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb, int offset)
506 {
507         proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2, FALSE);
508         offset += 2;
509
510         proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2,
511                             FALSE);
512         offset += 2;
513
514         return offset;
515 }
516
517 static int
518 flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb, int offset)
519 {
520         proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2, FALSE);
521         offset += 2;
522
523         proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2, FALSE);
524         offset += 2;
525
526         return offset;
527 }
528
529 static int
530 flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb, int offset)
531 {
532         nstime_t        ts;
533
534         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
535         ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000);
536         proto_tree_add_time(pdutree, hf_cflow_timestart, tvb, offset, 4, &ts);
537         offset += 4;
538
539         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
540         ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000);
541         proto_tree_add_time(pdutree, hf_cflow_timeend, tvb, offset, 4, &ts);
542         offset += 4;
543
544         return offset;
545 }
546
547
548 static int
549 flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb, int offset)
550 {
551         proto_tree_add_item(pdutree, hf_cflow_srcas, tvb, offset, 2, FALSE);
552         offset += 2;
553
554         proto_tree_add_item(pdutree, hf_cflow_dstas, tvb, offset, 2, FALSE);
555         offset += 2;
556
557         return offset;
558 }
559
560 static int
561 flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb, int offset)
562 {
563         proto_tree_add_item(pdutree, hf_cflow_packets, tvb, offset, 4, FALSE);
564         offset += 4;
565
566         proto_tree_add_item(pdutree, hf_cflow_octets, tvb, offset, 4, FALSE);
567         offset += 4;
568
569         return offset;
570 }
571
572 static int
573 flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb, int offset,
574                        int bytes, const char *text)
575 {
576         proto_tree_add_text(pdutree, tvb, offset, bytes, text);
577         offset += bytes;
578
579         return offset;
580 }
581
582 static int
583 dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
584                    int verspec)
585 {
586         int             startoffset = offset;
587
588         proto_tree_add_item(pdutree, hf_cflow_dstaddr, tvb, offset, 4, FALSE);
589         offset += 4;
590
591         if (verspec != V8PDU_DESTONLY_METHOD) {
592                 proto_tree_add_item(pdutree, hf_cflow_srcaddr, tvb, offset, 4,
593                                     FALSE);
594                 offset += 4;
595         }
596         if (verspec == V8PDU_FULL_METHOD) {
597                 proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2,
598                                     FALSE);
599                 offset += 2;
600                 proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2,
601                                     FALSE);
602                 offset += 2;
603         }
604
605         offset = flow_process_sizecount(pdutree, tvb, offset);
606         offset = flow_process_timeperiod(pdutree, tvb, offset);
607
608         proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2,
609                             FALSE);
610         offset += 2;
611
612         if (verspec != V8PDU_DESTONLY_METHOD) {
613                 proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2,
614                                     FALSE);
615                 offset += 2;
616         }
617
618         proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE);
619         if (verspec == V8PDU_FULL_METHOD)
620                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
621                                     FALSE);
622         offset = flow_process_textfield(pdutree, tvb, offset, 1, "marked tos");
623
624         if (verspec == V8PDU_SRCDEST_METHOD)
625                 offset =
626                     flow_process_textfield(pdutree, tvb, offset, 2,
627                                            "reserved");
628         else if (verspec == V8PDU_FULL_METHOD)
629                 offset =
630                     flow_process_textfield(pdutree, tvb, offset, 1, "padding");
631
632         offset =
633             flow_process_textfield(pdutree, tvb, offset, 4, "extra packets");
634
635         proto_tree_add_item(pdutree, hf_cflow_routersc, tvb, offset, 4, FALSE);
636         offset += 4;
637
638         return (offset - startoffset);
639 }
640
641 /*
642  * dissect a version 8 pdu, returning the length of the pdu processed 
643  */
644
645 static int
646 dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
647                   int verspec)
648 {
649         int             startoffset = offset;
650
651         proto_tree_add_item(pdutree, hf_cflow_flows, tvb, offset, 4, FALSE);
652         offset += 4;
653
654         offset = flow_process_sizecount(pdutree, tvb, offset);
655         offset = flow_process_timeperiod(pdutree, tvb, offset);
656
657         switch (verspec) {
658         case V8PDU_AS_METHOD:
659         case V8PDU_TOSAS_METHOD:
660                 offset = flow_process_aspair(pdutree, tvb, offset);
661
662                 if (verspec == V8PDU_TOSAS_METHOD) {
663                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
664                                             offset++, 1, FALSE);
665                         offset =
666                             flow_process_textfield(pdutree, tvb, offset, 1,
667                                                    "padding");
668                         offset =
669                             flow_process_textfield(pdutree, tvb, offset, 2,
670                                                    "reserved");
671                 }
672                 break;
673         case V8PDU_PROTO_METHOD:
674         case V8PDU_TOSPROTOPORT_METHOD:
675                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
676                                     FALSE);
677
678                 if (verspec == V8PDU_PROTO_METHOD)
679                         offset =
680                             flow_process_textfield(pdutree, tvb, offset, 1,
681                                                    "padding");
682                 else if (verspec == V8PDU_TOSPROTOPORT_METHOD)
683                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
684                                             offset++, 1, FALSE);
685
686                 offset =
687                     flow_process_textfield(pdutree, tvb, offset, 2,
688                                            "reserved");
689                 offset = flow_process_ports(pdutree, tvb, offset);
690
691                 if (verspec == V8PDU_TOSPROTOPORT_METHOD)
692                         offset = flow_process_ints(pdutree, tvb, offset);
693                 break;
694         case V8PDU_SPREFIX_METHOD:
695         case V8PDU_DPREFIX_METHOD:
696         case V8PDU_TOSSRCPREFIX_METHOD:
697         case V8PDU_TOSDSTPREFIX_METHOD:
698                 proto_tree_add_item(pdutree,
699                                     verspec ==
700                                     V8PDU_SPREFIX_METHOD ?
701                                     hf_cflow_srcnet : hf_cflow_dstnet, tvb,
702                                     offset, 4, FALSE);
703                 offset += 4;
704
705                 proto_tree_add_item(pdutree,
706                                     verspec ==
707                                     V8PDU_SPREFIX_METHOD ?
708                                     hf_cflow_srcmask : hf_cflow_dstmask, tvb,
709                                     offset++, 1, FALSE);
710
711                 if (verspec == V8PDU_SPREFIX_METHOD
712                     || verspec == V8PDU_DPREFIX_METHOD)
713                         offset =
714                             flow_process_textfield(pdutree, tvb, offset, 1,
715                                                    "padding");
716                 else if (verspec == V8PDU_TOSSRCPREFIX_METHOD
717                          || verspec == V8PDU_TOSDSTPREFIX_METHOD)
718                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
719                                             offset++, 1, FALSE);
720
721                 proto_tree_add_item(pdutree,
722                                     verspec ==
723                                     V8PDU_SPREFIX_METHOD ? hf_cflow_srcas
724                                     : hf_cflow_dstas, tvb, offset, 2, FALSE);
725                 offset += 2;
726
727                 proto_tree_add_item(pdutree,
728                                     verspec ==
729                                     V8PDU_SPREFIX_METHOD ?
730                                     hf_cflow_inputint : hf_cflow_outputint,
731                                     tvb, offset, 2, FALSE);
732                 offset += 2;
733
734                 offset =
735                     flow_process_textfield(pdutree, tvb, offset, 2,
736                                            "reserved");
737                 break;
738         case V8PDU_MATRIX_METHOD:
739         case V8PDU_TOSMATRIX_METHOD:
740         case V8PDU_PREPORTPROTOCOL_METHOD:
741                 proto_tree_add_item(pdutree, hf_cflow_srcnet, tvb, offset, 4,
742                                     FALSE);
743                 offset += 4;
744
745                 proto_tree_add_item(pdutree, hf_cflow_dstnet, tvb, offset, 4,
746                                     FALSE);
747                 offset += 4;
748
749                 proto_tree_add_item(pdutree, hf_cflow_srcmask, tvb, offset++,
750                                     1, FALSE);
751
752                 proto_tree_add_item(pdutree, hf_cflow_dstmask, tvb, offset++,
753                                     1, FALSE);
754
755                 if (verspec == V8PDU_TOSMATRIX_METHOD ||
756                     verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
757                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
758                                             offset++, 1, FALSE);
759                         if (verspec == V8PDU_TOSMATRIX_METHOD) {
760                                 offset =
761                                     flow_process_textfield(pdutree, tvb,
762                                                            offset, 1,
763                                                            "padding");
764                         } else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
765                                 proto_tree_add_item(pdutree, hf_cflow_prot,
766                                                     tvb, offset++, 1, FALSE);
767                         }
768                 } else {
769                         offset =
770                             flow_process_textfield(pdutree, tvb, offset, 2,
771                                                    "reserved");
772                 }
773
774                 if (verspec == V8PDU_MATRIX_METHOD
775                     || verspec == V8PDU_TOSMATRIX_METHOD) {
776                         offset = flow_process_aspair(pdutree, tvb, offset);
777                 } else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
778                         offset = flow_process_ports(pdutree, tvb, offset);
779                 }
780
781                 offset = flow_process_ints(pdutree, tvb, offset);
782                 break;
783         }
784
785
786         return (offset - startoffset);
787 }
788
789 /* Dissect a version 9 FlowSet and return the length we processed. */
790
791 static int
792 dissect_v9_flowset(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver)
793 {
794         int length;
795         guint16 flowset_id;
796
797         if (ver != 9)
798                 return (0);
799
800         flowset_id = tvb_get_ntohs(tvb, offset);
801         if (flowset_id == 0) {
802                 /* Template */
803                 proto_tree_add_item(pdutree, hf_cflow_template_flowset_id, tvb,
804                     offset, 2, FALSE);
805                 offset += 2;
806
807                 length = tvb_get_ntohs(tvb, offset);
808                 proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
809                     offset, 2, FALSE);
810                 offset += 2;
811
812                 dissect_v9_template(pdutree, tvb, offset);
813         } else if (flowset_id == 1) {
814                 /* Options */
815                 proto_tree_add_item(pdutree, hf_cflow_options_flowset_id, tvb,
816                     offset, 2, FALSE);
817                 offset += 2;
818
819                 length = tvb_get_ntohs(tvb, offset);
820                 proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
821                     offset, 2, FALSE);
822                 offset += 2;
823
824                 /* dissect_v9_options(pdutree, tvb, offset); */
825         } else if (flowset_id >= 2 && flowset_id <= 255) {
826                 /* Reserved */
827                 proto_tree_add_item(pdutree, hf_cflow_flowset_id, tvb,
828                     offset, 2, FALSE);
829                 offset += 2;
830
831                 length = tvb_get_ntohs(tvb, offset);
832                 proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
833                     offset, 2, FALSE);
834                 offset += 2;
835         } else {
836                 /* Data */
837                 proto_tree_add_item(pdutree, hf_cflow_data_flowset_id, tvb,
838                     offset, 2, FALSE);
839                 offset += 2;
840
841                 length = tvb_get_ntohs(tvb, offset);
842                 proto_tree_add_item(pdutree, hf_cflow_flowset_length, tvb,
843                     offset, 2, FALSE);
844                 offset += 2;
845
846                 /*
847                  * The length includes the length of the FlowSet ID and
848                  * the length field itself.
849                  */
850                 length -= 4;
851                 if (length > 0) {
852                         dissect_v9_data(pdutree, tvb, offset, flowset_id,
853                             (guint)length);
854                 }
855         }
856
857         return (length);
858 }
859
860 static int
861 dissect_v9_data(proto_tree * pdutree, tvbuff_t * tvb, int offset,
862     guint16 id, guint length)
863 {
864         struct v9_template *template;
865         proto_tree *data_tree;
866         proto_item *data_item;
867
868         template = v9_template_get(id, 0, 0);
869         if (template != NULL && template->length != 0) {
870                 int count;
871
872                 count = 1;
873                 while (length >= template->length) {
874                         data_item = proto_tree_add_text(pdutree, tvb,
875                             offset, template->length, "pdu %d", count++);
876                         data_tree = proto_item_add_subtree(data_item,
877                             ett_dataflowset);
878
879                         dissect_v9_pdu(data_tree, tvb, offset, template);
880
881                         offset += template->length;
882                         length -= template->length;
883                 }
884                 if (length != 0) {
885                         proto_tree_add_text(pdutree, tvb, offset, length,
886                             "Padding (%u byte%s)",
887                             length, plurality(length, "", "s"));
888                 }
889         } else {
890                 proto_tree_add_text(pdutree, tvb, offset, length,
891                     "Data (%u byte%s), no template found",
892                     length, plurality(length, "", "s"));
893         }
894
895         return (0);
896 }
897
898 static void
899 dissect_v9_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
900     struct v9_template * template)
901 {
902         int i;
903
904         for (i = 0; i < template->count; i++) {
905                 guint32 ipv4addr;
906                 guint8  ipv6addr[16];
907                 guint16 type, length;
908                 nstime_t ts;
909
910                 type = template->entries[i].type;
911                 length = template->entries[i].length;
912
913                 switch (type) {
914                 case 1: /* bytes */
915                         if (length == 4) {
916                                 proto_tree_add_item(pdutree, hf_cflow_octets,
917                                     tvb, offset, length, FALSE);
918                         } else if (length == 8) {
919                                 proto_tree_add_item(pdutree, hf_cflow_octets64,
920                                     tvb, offset, length, FALSE);
921                         } else {
922                                 proto_tree_add_text(pdutree,
923                                     tvb, offset, length,
924                                     "Octets: length %u", length);
925                         }
926                         break;
927
928                 case 2: /* packets */
929                         if (length == 4) {
930                                 proto_tree_add_item(pdutree, hf_cflow_packets,
931                                     tvb, offset, length, FALSE);
932                         } else if (length == 8) {
933                                 proto_tree_add_item(pdutree, hf_cflow_packets64,
934                                     tvb, offset, length, FALSE);
935                         } else {
936                                 proto_tree_add_text(pdutree,
937                                     tvb, offset, length,
938                                     "Packets: length %u", length);
939                         }
940                         break;
941
942                 case 3: /* flows */
943                         if (length == 4) {
944                                 proto_tree_add_item(pdutree, hf_cflow_flows,
945                                     tvb, offset, length, FALSE);
946                         } else {
947                                 proto_tree_add_text(pdutree,
948                                     tvb, offset, length,
949                                     "Flows: length %u", length);
950                         }
951                         break;
952
953                 case 4: /* proto */
954                         proto_tree_add_item(pdutree, hf_cflow_prot,
955                             tvb, offset, length, FALSE);
956                         break;
957
958                 case 5: /* TOS */
959                         proto_tree_add_item(pdutree, hf_cflow_tos,
960                             tvb, offset, length, FALSE);
961                         break;
962
963                 case 6: /* TCP flags */
964                         proto_tree_add_item(pdutree, hf_cflow_tcpflags,
965                             tvb, offset, length, FALSE);
966                         break;
967
968                 case 7: /* source port */
969                         proto_tree_add_item(pdutree, hf_cflow_srcport,
970                             tvb, offset, length, FALSE);
971                         break;
972
973                 case 8: /* source IP */
974                         if (length == 4) {
975                                 tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
976                                     sizeof(ipv4addr));
977                                 proto_tree_add_ipv4(pdutree, hf_cflow_srcaddr,
978                                     tvb, offset, length, ipv4addr);
979                         } else if (length == 16) {
980                                 tvb_memcpy(tvb, ipv6addr, offset,
981                                     sizeof(ipv6addr));
982                                 proto_tree_add_ipv6(pdutree, hf_cflow_srcaddr_v6,
983                                     tvb, offset, length, ipv6addr);
984                         } else {
985                                 proto_tree_add_text(pdutree,
986                                     tvb, offset, length,
987                                     "SrcAddr: length %u", length);
988                         }
989                         break;
990
991                 case 9: /* source mask */
992                         proto_tree_add_item(pdutree, hf_cflow_srcmask,
993                             tvb, offset, length, FALSE);
994                         break;
995
996                 case 10: /* input SNMP */
997                         proto_tree_add_item(pdutree, hf_cflow_inputint,
998                             tvb, offset, length, FALSE);
999                         break;
1000
1001                 case 11: /* dest port */
1002                         proto_tree_add_item(pdutree, hf_cflow_dstport,
1003                             tvb, offset, length, FALSE);
1004                         break;
1005
1006                 case 12: /* dest IP */
1007                         if (length == 4) {
1008                                 tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
1009                                     sizeof(ipv4addr));
1010                                 proto_tree_add_ipv4(pdutree, hf_cflow_dstaddr,
1011                                     tvb, offset, length, ipv4addr);
1012                         } else if (length == 16) {
1013                                 tvb_memcpy(tvb, ipv6addr, offset,
1014                                     sizeof(ipv6addr));
1015                                 proto_tree_add_ipv6(pdutree, hf_cflow_dstaddr_v6,
1016                                     tvb, offset, length, ipv6addr);
1017                         } else {
1018                                 proto_tree_add_text(pdutree,
1019                                     tvb, offset, length,
1020                                     "DstAddr: length %u", length);
1021                         }
1022                         break;
1023
1024                 case 13: /* dest mask */
1025                         proto_tree_add_item(pdutree, hf_cflow_dstmask,
1026                             tvb, offset, length, FALSE);
1027                         break;
1028
1029                 case 14: /* output SNMP */
1030                         proto_tree_add_item(pdutree, hf_cflow_outputint,
1031                             tvb, offset, length, FALSE);
1032                         break;
1033
1034                 case 15: /* nexthop IP */
1035                         if (length == 4) {
1036                                 tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
1037                                     sizeof(ipv4addr));
1038                                 proto_tree_add_ipv4(pdutree, hf_cflow_nexthop,
1039                                     tvb, offset, length, ipv4addr);
1040                         } else if (length == 16) {
1041                                 tvb_memcpy(tvb, ipv6addr, offset,
1042                                     sizeof(ipv6addr));
1043                                 proto_tree_add_ipv6(pdutree, hf_cflow_nexthop_v6,
1044                                     tvb, offset, length, ipv6addr);
1045                         } else {
1046                                 proto_tree_add_text(pdutree,
1047                                     tvb, offset, length,
1048                                     "NextHop: length %u", length);
1049                         }
1050                         break;
1051
1052                 case 16: /* source AS */
1053                         proto_tree_add_item(pdutree, hf_cflow_srcas,
1054                             tvb, offset, length, FALSE);
1055                         break;
1056
1057                 case 17: /* dest AS */
1058                         proto_tree_add_item(pdutree, hf_cflow_dstas,
1059                             tvb, offset, length, FALSE);
1060                         break;
1061
1062                 case 18: /* BGP nexthop IP */
1063                         if (length == 4) {
1064                                 tvb_memcpy(tvb, (guint8 *)&ipv4addr, offset,
1065                                     sizeof(ipv4addr));
1066                                 proto_tree_add_ipv4(pdutree, hf_cflow_bgpnexthop,
1067                                     tvb, offset, length, ipv4addr);
1068                         } else if (length == 16) {
1069                                 tvb_memcpy(tvb, ipv6addr, offset,
1070                                     sizeof(ipv6addr));
1071                                 proto_tree_add_ipv6(pdutree, hf_cflow_bgpnexthop_v6,
1072                                     tvb, offset, length, ipv6addr);
1073                         } else {
1074                                 proto_tree_add_text(pdutree,
1075                                     tvb, offset, length,
1076                                     "BGPNextHop: length %u", length);
1077                         }
1078                         break;
1079
1080                 case 19: /* multicast packets */
1081                         proto_tree_add_item(pdutree, hf_cflow_mulpackets,
1082                             tvb, offset, length, FALSE);
1083                         break;
1084
1085                 case 20: /* multicast octets */
1086                         proto_tree_add_item(pdutree, hf_cflow_muloctets,
1087                             tvb, offset, length, FALSE);
1088                         break;
1089
1090                 case 21: /* last switched */
1091                         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
1092                         ts.nsecs = 0;
1093                         proto_tree_add_time(pdutree, hf_cflow_timeend,
1094                             tvb, offset, length, &ts);
1095                         break;
1096
1097                 case 22: /* first switched */
1098                         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
1099                         ts.nsecs = 0;
1100                         proto_tree_add_time(pdutree, hf_cflow_timestart,
1101                             tvb, offset, length, &ts);
1102                         break;
1103
1104                 case 40: /* bytes exported */
1105                         proto_tree_add_item(pdutree, hf_cflow_octets_exp,
1106                             tvb, offset, length, FALSE);
1107                         break;
1108
1109                 case 41: /* packets exported */
1110                         proto_tree_add_item(pdutree, hf_cflow_packets_exp,
1111                             tvb, offset, length, FALSE);
1112                         break;
1113
1114                 case 42: /* flows exported */
1115                         proto_tree_add_item(pdutree, hf_cflow_flows_exp,
1116                             tvb, offset, length, FALSE);
1117                         break;
1118
1119                 default:
1120                         proto_tree_add_text(pdutree, tvb, offset, length,
1121                             "Type %u", type);
1122                         break;
1123                 }
1124
1125                 offset += length;
1126         }
1127 }
1128
1129 #if 0
1130 static int
1131 dissect_v9_options(proto_tree * pdutree, tvbuff_t * tvb, int offset)
1132 {
1133         return (0);
1134 }
1135 #endif
1136
1137 static int
1138 dissect_v9_template(proto_tree * pdutree, tvbuff_t * tvb, int offset)
1139 {
1140         struct v9_template template;
1141         proto_tree *template_tree;
1142         proto_item *template_item;
1143         guint16 id, count;
1144         gint32 i;
1145
1146         id = tvb_get_ntohs(tvb, offset);
1147         proto_tree_add_item(pdutree, hf_cflow_template_id, tvb,
1148             offset, 2, FALSE);
1149         offset += 2;
1150
1151         count = tvb_get_ntohs(tvb, offset);
1152         proto_tree_add_item(pdutree, hf_cflow_template_field_count, tvb,
1153             offset, 2, FALSE);
1154         offset += 2;
1155
1156         /* Cache template */
1157         memset(&template, 0, sizeof(template));
1158         template.id = id;
1159         template.count = count;
1160         template.source_addr = 0;       /* XXX */
1161         template.source_id = 0;         /* XXX */
1162         template.entries = g_malloc(count * sizeof(struct v9_template_entry));
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
1372 static void
1373 netflow_reinit(void)
1374 {
1375         int i;
1376
1377         /*
1378          * Clear out the template cache.
1379          * Free the table of fields for each entry, and then zero out
1380          * the cache.
1381          */
1382         for (i = 0; i < V9TEMPLATE_CACHE_MAX_ENTRIES; i++)
1383                 g_free(v9_template_cache[i].entries);
1384         memset(v9_template_cache, 0, sizeof v9_template_cache);
1385 }
1386
1387 void
1388 proto_register_netflow(void)
1389 {
1390         static hf_register_info hf[] = {
1391                 /*
1392                  * flow header 
1393                  */
1394                 {&hf_cflow_version,
1395                  {"Version", "cflow.version",
1396                   FT_UINT16, BASE_DEC, NULL, 0x0,
1397                   "NetFlow Version", HFILL}
1398                  },
1399                 {&hf_cflow_count,
1400                  {"Count", "cflow.count",
1401                   FT_UINT16, BASE_DEC, NULL, 0x0,
1402                   "Count of PDUs", HFILL}
1403                  },
1404                 {&hf_cflow_sysuptime,
1405                  {"SysUptime", "cflow.sysuptime",
1406                   FT_UINT32, BASE_DEC, NULL, 0x0,
1407                   "Time since router booted (in milliseconds)", HFILL}
1408                  },
1409
1410                 {&hf_cflow_timestamp,
1411                  {"Timestamp", "cflow.timestamp",
1412                   FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
1413                   "Current seconds since epoch", HFILL}
1414                  },
1415                 {&hf_cflow_unix_secs,
1416                  {"CurrentSecs", "cflow.unix_secs",
1417                   FT_UINT32, BASE_DEC, NULL, 0x0,
1418                   "Current seconds since epoch", HFILL}
1419                  },
1420                 {&hf_cflow_unix_nsecs,
1421                  {"CurrentNSecs", "cflow.unix_nsecs",
1422                   FT_UINT32, BASE_DEC, NULL, 0x0,
1423                   "Residual nanoseconds since epoch", HFILL}
1424                  },
1425                 {&hf_cflow_samplerate,
1426                  {"SampleRate", "cflow.samplerate",
1427                   FT_UINT16, BASE_DEC, NULL, 0x0,
1428                   "Sample Frequency of exporter", HFILL}
1429                  },
1430
1431                 /*
1432                  * end version-agnostic header
1433                  * version-specific flow header 
1434                  */
1435                 {&hf_cflow_sequence,
1436                  {"FlowSequence", "cflow.sequence",
1437                   FT_UINT32, BASE_DEC, NULL, 0x0,
1438                   "Sequence number of flows seen", HFILL}
1439                  },
1440                 {&hf_cflow_engine_type,
1441                  {"EngineType", "cflow.engine_type",
1442                   FT_UINT8, BASE_DEC, NULL, 0x0,
1443                   "Flow switching engine type", HFILL}
1444                  },
1445                 {&hf_cflow_engine_id,
1446                  {"EngineId", "cflow.engine_id",
1447                   FT_UINT8, BASE_DEC, NULL, 0x0,
1448                   "Slot number of switching engine", HFILL}
1449                  },
1450                 {&hf_cflow_source_id,
1451                  {"SourceId", "cflow.source_id",
1452                   FT_UINT32, BASE_DEC, NULL, 0x0,
1453                   "Identifier for export device", HFILL}
1454                  },
1455                 {&hf_cflow_aggmethod,
1456                  {"AggMethod", "cflow.aggmethod",
1457                   FT_UINT8, BASE_DEC, VALS(v8_agg), 0x0,
1458                   "CFlow V8 Aggregation Method", HFILL}
1459                  },
1460                 {&hf_cflow_aggversion,
1461                  {"AggVersion", "cflow.aggversion",
1462                   FT_UINT8, BASE_DEC, NULL, 0x0,
1463                   "CFlow V8 Aggregation Version", HFILL}
1464                  },
1465                 /*
1466                  * end version specific header storage 
1467                  */
1468                 /*
1469                  * Version 9
1470                  */
1471                 {&hf_cflow_flowset_id,
1472                  {"FlowSet Id", "cflow.flowset_id",
1473                   FT_UINT16, BASE_DEC, NULL, 0x0,
1474                   "FlowSet Id", HFILL}
1475                  },
1476                 {&hf_cflow_data_flowset_id,
1477                  {"Data FlowSet (Template Id)", "cflow.data_flowset_id",
1478                   FT_UINT16, BASE_DEC, NULL, 0x0,
1479                   "Data FlowSet with corresponding to a template Id", HFILL}
1480                  },
1481                 {&hf_cflow_options_flowset_id,
1482                  {"Options FlowSet", "cflow.options_flowset_id",
1483                   FT_UINT16, BASE_DEC, NULL, 0x0,
1484                   "Options FlowSet", HFILL}
1485                  },
1486                 {&hf_cflow_template_flowset_id,
1487                  {"Template FlowSet", "cflow.template_flowset_id",
1488                   FT_UINT16, BASE_DEC, NULL, 0x0,
1489                   "Template FlowSet", HFILL}
1490                  },
1491                 {&hf_cflow_flowset_length,
1492                  {"FlowSet Length", "cflow.flowset_length",
1493                   FT_UINT16, BASE_DEC, NULL, 0x0,
1494                   "FlowSet length", HFILL}
1495                  },
1496                 {&hf_cflow_template_id,
1497                  {"Template Id", "cflow.template_id",
1498                   FT_UINT16, BASE_DEC, NULL, 0x0,
1499                   "Template Id", HFILL}
1500                  },
1501                 {&hf_cflow_template_field_count,
1502                  {"Field Count", "cflow.template_field_count",
1503                   FT_UINT16, BASE_DEC, NULL, 0x0,
1504                   "Template field count", HFILL}
1505                  },
1506                 {&hf_cflow_template_field_type,
1507                  {"Type", "cflow.template_field_type",
1508                   FT_UINT16, BASE_DEC, VALS(v9_template_types), 0x0,
1509                   "Template field type", HFILL}
1510                  },
1511                 {&hf_cflow_template_field_length,
1512                  {"Length", "cflow.template_field_length",
1513                   FT_UINT16, BASE_DEC, NULL, 0x0,
1514                   "Template field length", HFILL}
1515                  },
1516                 /*
1517                  * begin pdu content storage 
1518                  */
1519                 {&hf_cflow_srcaddr,
1520                  {"SrcAddr", "cflow.srcaddr",
1521                   FT_IPv4, BASE_NONE, NULL, 0x0,
1522                   "Flow Source Address", HFILL}
1523                  },
1524                 {&hf_cflow_srcaddr_v6,
1525                  {"SrcAddr", "cflow.srcaddrv6",
1526                   FT_IPv6, BASE_NONE, NULL, 0x0,
1527                   "Flow Source Address", HFILL}
1528                  },
1529                 {&hf_cflow_srcnet,
1530                  {"SrcNet", "cflow.srcnet",
1531                   FT_IPv4, BASE_NONE, NULL, 0x0,
1532                   "Flow Source Network", HFILL}
1533                  },
1534                 {&hf_cflow_dstaddr,
1535                  {"DstAddr", "cflow.dstaddr",
1536                   FT_IPv4, BASE_NONE, NULL, 0x0,
1537                   "Flow Destination Address", HFILL}
1538                  },
1539                 {&hf_cflow_dstaddr_v6,
1540                  {"DstAddr", "cflow.dstaddrv6",
1541                   FT_IPv6, BASE_NONE, NULL, 0x0,
1542                   "Flow Destination Address", HFILL}
1543                  },
1544                 {&hf_cflow_dstnet,
1545                  {"DstNet", "cflow.dstaddr",
1546                   FT_IPv4, BASE_NONE, NULL, 0x0,
1547                   "Flow Destination Network", HFILL}
1548                  },
1549                 {&hf_cflow_nexthop,
1550                  {"NextHop", "cflow.nexthop",
1551                   FT_IPv4, BASE_NONE, NULL, 0x0,
1552                   "Router nexthop", HFILL}
1553                  },
1554                 {&hf_cflow_nexthop_v6,
1555                  {"NextHop", "cflow.nexthopv6",
1556                   FT_IPv6, BASE_NONE, NULL, 0x0,
1557                   "Router nexthop", HFILL}
1558                  },
1559                 {&hf_cflow_bgpnexthop,
1560                  {"BGPNextHop", "cflow.bgpnexthop",
1561                   FT_IPv4, BASE_NONE, NULL, 0x0,
1562                   "BGP Router Nexthop", HFILL}
1563                  },
1564                 {&hf_cflow_bgpnexthop_v6,
1565                  {"BGPNextHop", "cflow.bgpnexthopv6",
1566                   FT_IPv6, BASE_NONE, NULL, 0x0,
1567                   "BGP Router Nexthop", HFILL}
1568                  },
1569                 {&hf_cflow_inputint,
1570                  {"InputInt", "cflow.inputint",
1571                   FT_UINT16, BASE_DEC, NULL, 0x0,
1572                   "Flow Input Interface", HFILL}
1573                  },
1574                 {&hf_cflow_outputint,
1575                  {"OutputInt", "cflow.outputint",
1576                   FT_UINT16, BASE_DEC, NULL, 0x0,
1577                   "Flow Output Interface", HFILL}
1578                  },
1579                 {&hf_cflow_flows,
1580                  {"Flows", "cflow.flows",
1581                   FT_UINT32, BASE_DEC, NULL, 0x0,
1582                   "Flows Aggregated in PDU", HFILL}
1583                  },
1584                 {&hf_cflow_packets,
1585                  {"Packets", "cflow.packets",
1586                   FT_UINT32, BASE_DEC, NULL, 0x0,
1587                   "Count of packets", HFILL}
1588                  },
1589                 {&hf_cflow_packets64,
1590                  {"Packets", "cflow.packets64",
1591                   FT_UINT64, BASE_DEC, NULL, 0x0,
1592                   "Count of packets", HFILL}
1593                  },
1594                 {&hf_cflow_packetsout,
1595                  {"PacketsOut", "cflow.packetsout",
1596                   FT_UINT64, BASE_DEC, NULL, 0x0,
1597                   "Count of packets going out", HFILL}
1598                  },
1599                 {&hf_cflow_octets,
1600                  {"Octets", "cflow.octets",
1601                   FT_UINT32, BASE_DEC, NULL, 0x0,
1602                   "Count of bytes", HFILL}
1603                  },
1604                 {&hf_cflow_octets64,
1605                  {"Octets", "cflow.octets64",
1606                   FT_UINT64, BASE_DEC, NULL, 0x0,
1607                   "Count of bytes", HFILL}
1608                  },
1609                 {&hf_cflow_timestart,
1610                  {"StartTime", "cflow.timestart",
1611                   FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1612                   "Uptime at start of flow", HFILL}
1613                  },
1614                 {&hf_cflow_timeend,
1615                  {"EndTime", "cflow.timeend",
1616                   FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
1617                   "Uptime at end of flow", HFILL}
1618                  },
1619                 {&hf_cflow_srcport,
1620                  {"SrcPort", "cflow.srcport",
1621                   FT_UINT16, BASE_DEC, NULL, 0x0,
1622                   "Flow Source Port", HFILL}
1623                  },
1624                 {&hf_cflow_dstport,
1625                  {"DstPort", "cflow.dstport",
1626                   FT_UINT16, BASE_DEC, NULL, 0x0,
1627                   "Flow Destination Port", HFILL}
1628                  },
1629                 {&hf_cflow_prot,
1630                  {"Protocol", "cflow.protocol",
1631                   FT_UINT8, BASE_DEC, NULL, 0x0,
1632                   "IP Protocol", HFILL}
1633                  },
1634                 {&hf_cflow_tos,
1635                  {"IP ToS", "cflow.tos",
1636                   FT_UINT8, BASE_HEX, NULL, 0x0,
1637                   "IP Type of Service", HFILL}
1638                  },
1639                 {&hf_cflow_flags,
1640                  {"Export Flags", "cflow.flags",
1641                   FT_UINT8, BASE_HEX, NULL, 0x0,
1642                   "CFlow Flags", HFILL}
1643                  },
1644                 {&hf_cflow_tcpflags,
1645                  {"TCP Flags", "cflow.tcpflags",
1646                   FT_UINT8, BASE_HEX, NULL, 0x0,
1647                   "TCP Flags", HFILL}
1648                  },
1649                 {&hf_cflow_srcas,
1650                  {"SrcAS", "cflow.srcas",
1651                   FT_UINT16, BASE_DEC, NULL, 0x0,
1652                   "Source AS", HFILL}
1653                  },
1654                 {&hf_cflow_dstas,
1655                  {"DstAS", "cflow.dstas",
1656                   FT_UINT16, BASE_DEC, NULL, 0x0,
1657                   "Destination AS", HFILL}
1658                  },
1659                 {&hf_cflow_srcmask,
1660                  {"SrcMask", "cflow.srcmask",
1661                   FT_UINT8, BASE_DEC, NULL, 0x0,
1662                   "Source Prefix Mask", HFILL}
1663                  },
1664                 {&hf_cflow_dstmask,
1665                  {"DstMask", "cflow.dstmask",
1666                   FT_UINT8, BASE_DEC, NULL, 0x0,
1667                   "Destination Prefix Mask", HFILL}
1668                  },
1669                 {&hf_cflow_routersc,
1670                  {"Router Shortcut", "cflow.routersc",
1671                   FT_IPv4, BASE_NONE, NULL, 0x0,
1672                   "Router shortcut by switch", HFILL}
1673                  },
1674                 {&hf_cflow_mulpackets,
1675                  {"MulticastPackets", "cflow.mulpackets",
1676                   FT_UINT32, BASE_DEC, NULL, 0x0,
1677                   "Count of multicast packets", HFILL}
1678                  },
1679                 {&hf_cflow_muloctets,
1680                  {"MulticastOctets", "cflow.muloctets",
1681                   FT_UINT32, BASE_DEC, NULL, 0x0,
1682                   "Count of multicast octets", HFILL}
1683                  },
1684                 {&hf_cflow_octets_exp,
1685                  {"OctetsExp", "cflow.octetsexp",
1686                   FT_UINT32, BASE_DEC, NULL, 0x0,
1687                   "Octets exported", HFILL}
1688                  },
1689                 {&hf_cflow_packets_exp,
1690                  {"PacketsExp", "cflow.packetsexp",
1691                   FT_UINT32, BASE_DEC, NULL, 0x0,
1692                   "Packets exported", HFILL}
1693                  },
1694                 {&hf_cflow_flows_exp,
1695                  {"FlowsExp", "cflow.flowsexp",
1696                   FT_UINT32, BASE_DEC, NULL, 0x0,
1697                   "Flows exported", HFILL}
1698                  }
1699                 /*
1700                  * end pdu content storage 
1701                  */
1702         };
1703
1704         static gint    *ett[] = {
1705                 &ett_netflow,
1706                 &ett_unixtime,
1707                 &ett_flow,
1708                 &ett_template,
1709                 &ett_dataflowset
1710         };
1711
1712         module_t *netflow_module;
1713
1714         proto_netflow = proto_register_protocol("Cisco NetFlow", "CFLOW",
1715                                                 "cflow");
1716
1717         proto_register_field_array(proto_netflow, hf, array_length(hf));
1718         proto_register_subtree_array(ett, array_length(ett));
1719
1720         /* Register our configuration options for NetFlow */
1721         netflow_module = prefs_register_protocol(proto_netflow,
1722             proto_reg_handoff_netflow);
1723
1724         prefs_register_uint_preference(netflow_module, "udp.port",
1725             "NetFlow UDP Port", "Set the port for NetFlow messages",
1726             10, &global_netflow_udp_port);
1727
1728         register_init_routine(&netflow_reinit);
1729 }
1730
1731
1732 /*
1733  * protocol/port association 
1734  */
1735 void
1736 proto_reg_handoff_netflow(void)
1737 {
1738         static int netflow_prefs_initialized = FALSE;
1739         static dissector_handle_t netflow_handle;
1740
1741         if (!netflow_prefs_initialized) {
1742                 netflow_handle = create_dissector_handle(dissect_netflow,
1743                     proto_netflow);
1744                 netflow_prefs_initialized = TRUE;
1745         } else {
1746                 dissector_delete("udp.port", netflow_udp_port, netflow_handle);
1747         }
1748
1749         /* Set out port number for future use */
1750         netflow_udp_port = global_netflow_udp_port;
1751
1752         dissector_add("udp.port", netflow_udp_port, netflow_handle);
1753 }