Convert enumprinterdataex rpc to ndr format calls.
[metze/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  **
25  *****************************************************************************
26  **
27  ** this code was written from the following documentation:
28  **
29  ** http://www.cisco.com/univercd/cc/td/doc/product/rtrmgmt/nfc/nfc_3_6/iug/format.pdf
30  ** http://www.caida.org/tools/measurement/cflowd/configuration/configuration-9.html
31  **
32  ** some documentation is more accurate then others. in some cases, live data and
33  ** information contained in responses from vendors were also used. some fields
34  ** are dissected as vendor specific fields.
35  **
36  ** $Yahoo: //depot/fumerola/packet-netflow/packet-netflow.c#14 $
37  ** $Id: packet-netflow.c,v 1.7 2002/10/08 19:26:35 guy Exp $
38  */
39
40 #ifdef HAVE_CONFIG_H
41 # include "config.h"
42 #endif
43
44 #include <glib.h>
45 #include <epan/packet.h>
46 #include <string.h>
47
48 #define UDP_PORT_NETFLOW        2055
49
50 /*
51  * pdu identifiers & sizes 
52  */
53
54 #define V1PDU_SIZE              (4 * 12)
55 #define V5PDU_SIZE              (4 * 12)
56 #define V7PDU_SIZE              (4 * 13)
57 #define V8PDU_AS_SIZE           (4 * 7)
58 #define V8PDU_PROTO_SIZE        (4 * 7)
59 #define V8PDU_SPREFIX_SIZE      (4 * 8)
60 #define V8PDU_DPREFIX_SIZE      (4 * 8)
61 #define V8PDU_MATRIX_SIZE       (4 * 10)
62 #define V8PDU_DESTONLY_SIZE     (4 * 8)
63 #define V8PDU_SRCDEST_SIZE      (4 * 10)
64 #define V8PDU_FULL_SIZE         (4 * 11)
65 #define V8PDU_TOSAS_SIZE        (V8PDU_AS_SIZE + 4)
66 #define V8PDU_TOSPROTOPORT_SIZE (V8PDU_PROTO_SIZE + 4)
67 #define V8PDU_TOSSRCPREFIX_SIZE V8PDU_SPREFIX_SIZE
68 #define V8PDU_TOSDSTPREFIX_SIZE V8PDU_DPREFIX_SIZE
69 #define V8PDU_TOSMATRIX_SIZE    V8PDU_MATRIX_SIZE
70 #define V8PDU_PREPORTPROTOCOL_SIZE (4 * 10)
71
72 enum {
73         V8PDU_NO_METHOD = 0,
74         V8PDU_AS_METHOD,
75         V8PDU_PROTO_METHOD,
76         V8PDU_SPREFIX_METHOD,
77         V8PDU_DPREFIX_METHOD,
78         V8PDU_MATRIX_METHOD,
79         V8PDU_DESTONLY_METHOD,
80         V8PDU_SRCDEST_METHOD,
81         V8PDU_FULL_METHOD,
82         V8PDU_TOSAS_METHOD,
83         V8PDU_TOSPROTOPORT_METHOD,
84         V8PDU_TOSSRCPREFIX_METHOD,
85         V8PDU_TOSDSTPREFIX_METHOD,
86         V8PDU_TOSMATRIX_METHOD,
87         V8PDU_PREPORTPROTOCOL_METHOD
88 };
89
90 static const value_string v8_agg[] = {
91         {V8PDU_AS_METHOD, "V8 AS aggregation"},
92         {V8PDU_PROTO_METHOD, "V8 Proto/Port aggregation"},
93         {V8PDU_SPREFIX_METHOD, "V8 Source Prefix aggregation"},
94         {V8PDU_DPREFIX_METHOD, "V8 Destination Prefix aggregation"},
95         {V8PDU_MATRIX_METHOD, "V8 Network Matrix aggregation"},
96         {V8PDU_DESTONLY_METHOD, "V8 Destination aggregation (Cisco Catalyst)"},
97         {V8PDU_SRCDEST_METHOD, "V8 Src/Dest aggregation (Cisco Catalyst)"},
98         {V8PDU_FULL_METHOD, "V8 Full aggregation (Cisco Catalyst)"},
99         {V8PDU_TOSAS_METHOD, "V8 TOS+AS aggregation aggregation"},
100         {V8PDU_TOSPROTOPORT_METHOD, "V8 TOS+Protocol aggregation"},
101         {V8PDU_TOSSRCPREFIX_METHOD, "V8 TOS+Source Prefix aggregation"},
102         {V8PDU_TOSDSTPREFIX_METHOD, "V8 TOS+Destination Prefix aggregation"},
103         {V8PDU_TOSMATRIX_METHOD, "V8 TOS+Prefix Matrix aggregation"},
104         {V8PDU_PREPORTPROTOCOL_METHOD, "V8 Port+Protocol aggregation"},
105         {0, NULL}
106 };
107
108
109 /*
110  * ethereal tree identifiers
111  */
112
113 static int      proto_netflow = -1;
114 static int      ett_netflow = -1;
115 static int      ett_unixtime = -1;
116 static int      ett_flow = -1;
117
118 /*
119  * cflow header 
120  */
121
122 static int      hf_cflow_version = -1;
123 static int      hf_cflow_count = -1;
124 static int      hf_cflow_sysuptime = -1;
125 static int      hf_cflow_unix_secs = -1;
126 static int      hf_cflow_unix_nsecs = -1;
127 static int      hf_cflow_timestamp = -1;
128 static int      hf_cflow_samplerate = -1;
129
130 /*
131  * cflow version specific info 
132  */
133 static int      hf_cflow_sequence = -1;
134 static int      hf_cflow_engine_type = -1;
135 static int      hf_cflow_engine_id = -1;
136
137 static int      hf_cflow_aggmethod = -1;
138 static int      hf_cflow_aggversion = -1;
139
140 /*
141  * pdu storage
142  */
143 static int      hf_cflow_srcaddr = -1;
144 static int      hf_cflow_srcnet = -1;
145 static int      hf_cflow_dstaddr = -1;
146 static int      hf_cflow_dstnet = -1;
147 static int      hf_cflow_nexthop = -1;
148 static int      hf_cflow_inputint = -1;
149 static int      hf_cflow_outputint = -1;
150 static int      hf_cflow_flows = -1;
151 static int      hf_cflow_packets = -1;
152 static int      hf_cflow_octets = -1;
153 static int      hf_cflow_timestart = -1;
154 static int      hf_cflow_timeend = -1;
155 static int      hf_cflow_srcport = -1;
156 static int      hf_cflow_dstport = -1;
157 static int      hf_cflow_prot = -1;
158 static int      hf_cflow_tos = -1;
159 static int      hf_cflow_flags = -1;
160 static int      hf_cflow_tcpflags = -1;
161 static int      hf_cflow_dstas = -1;
162 static int      hf_cflow_srcas = -1;
163 static int      hf_cflow_dstmask = -1;
164 static int      hf_cflow_srcmask = -1;
165 static int      hf_cflow_routersc = -1;
166
167 typedef int     dissect_pdu_t(proto_tree * pdutree, tvbuff_t * tvb, int offset,
168                               int verspec);
169 static int      dissect_pdu(proto_tree * tree, tvbuff_t * tvb, int offset,
170                             int verspec);
171 static int      dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb,
172                                   int offset, int verspec);
173 static int      dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb,
174                                    int offset, int verspec);
175
176 static gchar   *getprefix(const guint32 * address, int prefix);
177 static void     dissect_netflow(tvbuff_t * tvb, packet_info * pinfo,
178                                 proto_tree * tree);
179
180 static int      flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb,
181                                   int offset);
182 static int      flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb,
183                                    int offset);
184 static int      flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb,
185                                         int offset);
186 static int      flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb,
187                                     int offset);
188 static int      flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb,
189                                        int offset);
190 static int      flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb,
191                                        int offset, int bytes,
192                                        const char *text);
193
194
195 static void
196 dissect_netflow(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
197 {
198         proto_tree     *netflow_tree = NULL;
199         proto_tree     *ti;
200         proto_item     *timeitem, *pduitem;
201         proto_tree     *timetree, *pdutree;
202         unsigned int    pduret, ver = 0, pdus = 0, x = 1, vspec;
203         size_t          available, pdusize, offset = 0;
204         nstime_t        ts;
205         dissect_pdu_t  *pduptr;
206
207         if (check_col(pinfo->cinfo, COL_PROTOCOL))
208                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "CFLOW");
209         if (check_col(pinfo->cinfo, COL_INFO))
210                 col_clear(pinfo->cinfo, COL_INFO);
211
212         if (tree) {
213                 ti = proto_tree_add_item(tree, proto_netflow, tvb,
214                                          offset, -1, FALSE);
215                 netflow_tree = proto_item_add_subtree(ti, ett_netflow);
216         }
217
218         ver = tvb_get_ntohs(tvb, offset);
219         vspec = ver;
220         switch (ver) {
221         case 1:
222                 pdusize = V1PDU_SIZE;
223                 pduptr = &dissect_pdu;
224                 break;
225         case 5:
226                 pdusize = V5PDU_SIZE;
227                 pduptr = &dissect_pdu;
228                 break;
229         case 7:
230                 pdusize = V7PDU_SIZE;
231                 pduptr = &dissect_pdu;
232                 break;
233         case 8:
234                 pdusize = -1;   /* deferred */
235                 pduptr = &dissect_v8_aggpdu;
236                 break;
237         default:
238                 return;
239         }
240
241         if (tree)
242                 proto_tree_add_uint(netflow_tree, hf_cflow_version, tvb,
243                                     offset, 2, ver);
244         offset += 2;
245
246         pdus = tvb_get_ntohs(tvb, offset);
247         if (pdus <= 0)
248                 return;
249         if (tree)
250                 proto_tree_add_uint(netflow_tree, hf_cflow_count, tvb,
251                                     offset, 2, pdus);
252         offset += 2;
253
254         /*
255          * set something interesting in the display now that we have info 
256          */
257         if (check_col(pinfo->cinfo, COL_INFO))
258                 col_add_fstr(pinfo->cinfo, COL_INFO, "total: %u (v%u) flows",
259                              pdus, ver);
260
261         /*
262          * the rest is only interesting if we're displaying/searching the
263          * packet 
264          */
265         if (!tree)
266                 return;
267
268         proto_tree_add_item(netflow_tree, hf_cflow_sysuptime, tvb,
269                             offset, 4, FALSE);
270         offset += 4;
271
272         ts.secs = tvb_get_ntohl(tvb, offset);
273         ts.nsecs = tvb_get_ntohl(tvb, offset + 4);
274         timeitem = proto_tree_add_time(netflow_tree,
275                                        hf_cflow_timestamp, tvb, offset,
276                                        8, &ts);
277         timetree = proto_item_add_subtree(timeitem, ett_unixtime);
278
279         proto_tree_add_item(timetree, hf_cflow_unix_secs, tvb,
280                             offset, 4, FALSE);
281         offset += 4;
282
283         proto_tree_add_item(timetree, hf_cflow_unix_nsecs, tvb,
284                             offset, 4, FALSE);
285         offset += 4;
286
287         /*
288          * version specific header 
289          */
290         if (ver == 5 || ver == 7 || ver == 8) {
291                 proto_tree_add_item(netflow_tree, hf_cflow_sequence,
292                                     tvb, offset, 4, FALSE);
293                 offset += 4;
294         }
295         if (ver == 5 || ver == 8) {
296                 proto_tree_add_item(netflow_tree, hf_cflow_engine_type,
297                                     tvb, offset++, 1, FALSE);
298                 proto_tree_add_item(netflow_tree, hf_cflow_engine_id,
299                                     tvb, offset++, 1, FALSE);
300         }
301         if (ver == 8) {
302                 vspec = tvb_get_guint8(tvb, offset);
303                 switch (vspec) {
304                 case V8PDU_AS_METHOD:
305                         pdusize = V8PDU_AS_SIZE;
306                         break;
307                 case V8PDU_PROTO_METHOD:
308                         pdusize = V8PDU_PROTO_SIZE;
309                         break;
310                 case V8PDU_SPREFIX_METHOD:
311                         pdusize = V8PDU_SPREFIX_SIZE;
312                         break;
313                 case V8PDU_DPREFIX_METHOD:
314                         pdusize = V8PDU_DPREFIX_SIZE;
315                         break;
316                 case V8PDU_MATRIX_METHOD:
317                         pdusize = V8PDU_MATRIX_SIZE;
318                         break;
319                 case V8PDU_DESTONLY_METHOD:
320                         pdusize = V8PDU_DESTONLY_SIZE;
321                         pduptr = &dissect_v8_flowpdu;
322                         break;
323                 case V8PDU_SRCDEST_METHOD:
324                         pdusize = V8PDU_SRCDEST_SIZE;
325                         pduptr = &dissect_v8_flowpdu;
326                         break;
327                 case V8PDU_FULL_METHOD:
328                         pdusize = V8PDU_FULL_SIZE;
329                         pduptr = &dissect_v8_flowpdu;
330                         break;
331                 case V8PDU_TOSAS_METHOD:
332                         pdusize = V8PDU_TOSAS_SIZE;
333                         break;
334                 case V8PDU_TOSPROTOPORT_METHOD:
335                         pdusize = V8PDU_TOSPROTOPORT_SIZE;
336                         break;
337                 case V8PDU_TOSSRCPREFIX_METHOD:
338                         pdusize = V8PDU_TOSSRCPREFIX_SIZE;
339                         break;
340                 case V8PDU_TOSDSTPREFIX_METHOD:
341                         pdusize = V8PDU_TOSDSTPREFIX_SIZE;
342                         break;
343                 case V8PDU_TOSMATRIX_METHOD:
344                         pdusize = V8PDU_TOSMATRIX_SIZE;
345                         break;
346                 case V8PDU_PREPORTPROTOCOL_METHOD:
347                         pdusize = V8PDU_PREPORTPROTOCOL_SIZE;
348                         break;
349                 default:
350                         pdusize = -1;
351                         vspec = 0;
352                         break;
353                 }
354                 proto_tree_add_uint(netflow_tree, hf_cflow_aggmethod,
355                                     tvb, offset++, 1, vspec);
356                 proto_tree_add_item(netflow_tree, hf_cflow_aggversion,
357                                     tvb, offset++, 1, FALSE);
358         }
359         if (ver == 7 || ver == 8)
360                 offset = flow_process_textfield(netflow_tree, tvb, offset, 4,
361                                                 "reserved");
362         else if (ver == 5) {
363                 proto_tree_add_item(netflow_tree, hf_cflow_samplerate,
364                                     tvb, offset, 2, FALSE);
365                 offset += 2;
366         }
367
368         /*
369          * everything below here should be payload 
370          */
371         for (x = 1; x < pdus + 1; x++) {
372                 /*
373                  * make sure we have a pdu's worth of data 
374                  */
375                 available = tvb_length_remaining(tvb, offset);
376                 if (available < pdusize)
377                         break;
378
379                 pduitem =
380                     proto_tree_add_text(netflow_tree, tvb, offset, pdusize,
381                                         "pdu %u/%u", x, pdus);
382                 pdutree = proto_item_add_subtree(pduitem, ett_flow);
383
384                 pduret = pduptr(pdutree, tvb, offset, vspec);
385
386                 /*
387                  * if we came up short, stop processing 
388                  */
389                 if (pduret == pdusize)
390                         offset += pduret;
391                 else
392                         break;
393         }
394
395         return;
396 }
397
398 /*
399  * flow_process_* == common groups of fields, probably could be inline 
400  */
401
402 static int
403 flow_process_ints(proto_tree * pdutree, tvbuff_t * tvb, int offset)
404 {
405         proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2, FALSE);
406         offset += 2;
407
408         proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2,
409                             FALSE);
410         offset += 2;
411
412         return offset;
413 }
414
415 static int
416 flow_process_ports(proto_tree * pdutree, tvbuff_t * tvb, int offset)
417 {
418         proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2, FALSE);
419         offset += 2;
420
421         proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2, FALSE);
422         offset += 2;
423
424         return offset;
425 }
426
427 static int
428 flow_process_timeperiod(proto_tree * pdutree, tvbuff_t * tvb, int offset)
429 {
430         nstime_t        ts;
431
432         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
433         ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000);
434         proto_tree_add_time(pdutree, hf_cflow_timestart, tvb, offset, 4, &ts);
435         offset += 4;
436
437         ts.secs = tvb_get_ntohl(tvb, offset) / 1000;
438         ts.nsecs = ((tvb_get_ntohl(tvb, offset) % 1000) * 1000000);
439         proto_tree_add_time(pdutree, hf_cflow_timeend, tvb, offset, 4, &ts);
440         offset += 4;
441
442         return offset;
443 }
444
445
446 static int
447 flow_process_aspair(proto_tree * pdutree, tvbuff_t * tvb, int offset)
448 {
449         proto_tree_add_item(pdutree, hf_cflow_srcas, tvb, offset, 2, FALSE);
450         offset += 2;
451
452         proto_tree_add_item(pdutree, hf_cflow_dstas, tvb, offset, 2, FALSE);
453         offset += 2;
454
455         return offset;
456 }
457
458 static int
459 flow_process_sizecount(proto_tree * pdutree, tvbuff_t * tvb, int offset)
460 {
461         proto_tree_add_item(pdutree, hf_cflow_packets, tvb, offset, 4, FALSE);
462         offset += 4;
463
464         proto_tree_add_item(pdutree, hf_cflow_octets, tvb, offset, 4, FALSE);
465         offset += 4;
466
467         return offset;
468 }
469
470 static int
471 flow_process_textfield(proto_tree * pdutree, tvbuff_t * tvb, int offset,
472                        int bytes, const char *text)
473 {
474         proto_tree_add_text(pdutree, tvb, offset, bytes, text);
475         offset += bytes;
476
477         return offset;
478 }
479
480 static int
481 dissect_v8_flowpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
482                    int verspec)
483 {
484         int             startoffset = offset;
485
486         proto_tree_add_item(pdutree, hf_cflow_dstaddr, tvb, offset, 4, FALSE);
487         offset += 4;
488
489         if (verspec != V8PDU_DESTONLY_METHOD) {
490                 proto_tree_add_item(pdutree, hf_cflow_srcaddr, tvb, offset, 4,
491                                     FALSE);
492                 offset += 4;
493         }
494         if (verspec == V8PDU_FULL_METHOD) {
495                 proto_tree_add_item(pdutree, hf_cflow_dstport, tvb, offset, 2,
496                                     FALSE);
497                 offset += 2;
498                 proto_tree_add_item(pdutree, hf_cflow_srcport, tvb, offset, 2,
499                                     FALSE);
500                 offset += 2;
501         }
502
503         offset = flow_process_sizecount(pdutree, tvb, offset);
504         offset = flow_process_timeperiod(pdutree, tvb, offset);
505
506         proto_tree_add_item(pdutree, hf_cflow_outputint, tvb, offset, 2,
507                             FALSE);
508         offset += 2;
509
510         if (verspec != V8PDU_DESTONLY_METHOD) {
511                 proto_tree_add_item(pdutree, hf_cflow_inputint, tvb, offset, 2,
512                                     FALSE);
513                 offset += 2;
514         }
515
516         proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1, FALSE);
517         if (verspec == V8PDU_FULL_METHOD)
518                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
519                                     FALSE);
520         offset = flow_process_textfield(pdutree, tvb, offset, 1, "marked tos");
521
522         if (verspec == V8PDU_SRCDEST_METHOD)
523                 offset =
524                     flow_process_textfield(pdutree, tvb, offset, 2,
525                                            "reserved");
526         else if (verspec == V8PDU_FULL_METHOD)
527                 offset =
528                     flow_process_textfield(pdutree, tvb, offset, 1, "padding");
529
530         offset =
531             flow_process_textfield(pdutree, tvb, offset, 4, "extra packets");
532
533         proto_tree_add_item(pdutree, hf_cflow_routersc, tvb, offset, 4, FALSE);
534         offset += 4;
535
536         return (offset - startoffset);
537 }
538
539 /*
540  * dissect a version 8 pdu, returning the length of the pdu processed 
541  */
542
543 static int
544 dissect_v8_aggpdu(proto_tree * pdutree, tvbuff_t * tvb, int offset,
545                   int verspec)
546 {
547         int             startoffset = offset;
548
549         proto_tree_add_item(pdutree, hf_cflow_flows, tvb, offset, 4, FALSE);
550         offset += 4;
551
552         offset = flow_process_sizecount(pdutree, tvb, offset);
553         offset = flow_process_timeperiod(pdutree, tvb, offset);
554
555         switch (verspec) {
556         case V8PDU_AS_METHOD:
557         case V8PDU_TOSAS_METHOD:
558                 offset = flow_process_aspair(pdutree, tvb, offset);
559
560                 if (verspec == V8PDU_TOSAS_METHOD) {
561                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
562                                             offset++, 1, FALSE);
563                         offset =
564                             flow_process_textfield(pdutree, tvb, offset, 1,
565                                                    "padding");
566                         offset =
567                             flow_process_textfield(pdutree, tvb, offset, 2,
568                                                    "reserved");
569                 }
570                 break;
571         case V8PDU_PROTO_METHOD:
572         case V8PDU_TOSPROTOPORT_METHOD:
573                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
574                                     FALSE);
575
576                 if (verspec == V8PDU_PROTO_METHOD)
577                         offset =
578                             flow_process_textfield(pdutree, tvb, offset, 1,
579                                                    "padding");
580                 else if (verspec == V8PDU_TOSPROTOPORT_METHOD)
581                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
582                                             offset++, 1, FALSE);
583
584                 offset =
585                     flow_process_textfield(pdutree, tvb, offset, 2,
586                                            "reserved");
587                 offset = flow_process_ports(pdutree, tvb, offset);
588
589                 if (verspec == V8PDU_TOSPROTOPORT_METHOD)
590                         offset = flow_process_ints(pdutree, tvb, offset);
591                 break;
592         case V8PDU_SPREFIX_METHOD:
593         case V8PDU_DPREFIX_METHOD:
594         case V8PDU_TOSSRCPREFIX_METHOD:
595         case V8PDU_TOSDSTPREFIX_METHOD:
596                 proto_tree_add_item(pdutree,
597                                     verspec ==
598                                     V8PDU_SPREFIX_METHOD ?
599                                     hf_cflow_srcnet : hf_cflow_dstnet, tvb,
600                                     offset, 4, FALSE);
601                 offset += 4;
602
603                 proto_tree_add_item(pdutree,
604                                     verspec ==
605                                     V8PDU_SPREFIX_METHOD ?
606                                     hf_cflow_srcmask : hf_cflow_dstmask, tvb,
607                                     offset++, 1, FALSE);
608
609                 if (verspec == V8PDU_SPREFIX_METHOD
610                     || verspec == V8PDU_DPREFIX_METHOD)
611                         offset =
612                             flow_process_textfield(pdutree, tvb, offset, 1,
613                                                    "padding");
614                 else if (verspec == V8PDU_TOSSRCPREFIX_METHOD
615                          || verspec == V8PDU_TOSDSTPREFIX_METHOD)
616                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
617                                             offset++, 1, FALSE);
618
619                 proto_tree_add_item(pdutree,
620                                     verspec ==
621                                     V8PDU_SPREFIX_METHOD ? hf_cflow_srcas
622                                     : hf_cflow_dstas, tvb, offset, 2, FALSE);
623                 offset += 2;
624
625                 proto_tree_add_item(pdutree,
626                                     verspec ==
627                                     V8PDU_SPREFIX_METHOD ?
628                                     hf_cflow_inputint : hf_cflow_outputint,
629                                     tvb, offset, 2, FALSE);
630                 offset += 2;
631
632                 offset =
633                     flow_process_textfield(pdutree, tvb, offset, 2,
634                                            "reserved");
635                 break;
636         case V8PDU_MATRIX_METHOD:
637         case V8PDU_TOSMATRIX_METHOD:
638         case V8PDU_PREPORTPROTOCOL_METHOD:
639                 proto_tree_add_item(pdutree, hf_cflow_srcnet, tvb, offset, 4,
640                                     FALSE);
641                 offset += 4;
642
643                 proto_tree_add_item(pdutree, hf_cflow_dstnet, tvb, offset, 4,
644                                     FALSE);
645                 offset += 4;
646
647                 proto_tree_add_item(pdutree, hf_cflow_srcmask, tvb, offset++,
648                                     1, FALSE);
649
650                 proto_tree_add_item(pdutree, hf_cflow_dstmask, tvb, offset++,
651                                     1, FALSE);
652
653                 if (verspec == V8PDU_TOSMATRIX_METHOD ||
654                     verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
655                         proto_tree_add_item(pdutree, hf_cflow_tos, tvb,
656                                             offset++, 1, FALSE);
657                         if (verspec == V8PDU_TOSMATRIX_METHOD) {
658                                 offset =
659                                     flow_process_textfield(pdutree, tvb,
660                                                            offset, 1,
661                                                            "padding");
662                         } else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
663                                 proto_tree_add_item(pdutree, hf_cflow_prot,
664                                                     tvb, offset++, 1, FALSE);
665                         }
666                 } else {
667                         offset =
668                             flow_process_textfield(pdutree, tvb, offset, 2,
669                                                    "reserved");
670                 }
671
672                 if (verspec == V8PDU_MATRIX_METHOD
673                     || verspec == V8PDU_TOSMATRIX_METHOD) {
674                         offset = flow_process_aspair(pdutree, tvb, offset);
675                 } else if (verspec == V8PDU_PREPORTPROTOCOL_METHOD) {
676                         offset = flow_process_ports(pdutree, tvb, offset);
677                 }
678
679                 offset = flow_process_ints(pdutree, tvb, offset);
680                 break;
681         }
682
683
684         return (offset - startoffset);
685 }
686
687 /*
688  * dissect a version 1, 5, or 7 pdu and return the length of the pdu we
689  * processed
690  */
691
692 static int
693 dissect_pdu(proto_tree * pdutree, tvbuff_t * tvb, int offset, int ver)
694 {
695         int             startoffset = offset;
696         guint32         srcaddr, dstaddr;
697         guint8          mask;
698         nstime_t        ts;
699
700         memset(&ts, '\0', sizeof(ts));
701
702         /*
703          * memcpy so we can use the values later to calculate a prefix 
704          */
705         tvb_memcpy(tvb, (guint8 *) & srcaddr, offset, 4);
706         proto_tree_add_ipv4(pdutree, hf_cflow_srcaddr, tvb, offset, 4,
707                             srcaddr);
708         offset += 4;
709
710         tvb_memcpy(tvb, (guint8 *) & dstaddr, offset, 4);
711         proto_tree_add_ipv4(pdutree, hf_cflow_dstaddr, tvb, offset, 4,
712                             dstaddr);
713         offset += 4;
714
715         proto_tree_add_item(pdutree, hf_cflow_nexthop, tvb, offset, 4, FALSE);
716         offset += 4;
717
718         offset = flow_process_ints(pdutree, tvb, offset);
719         offset = flow_process_sizecount(pdutree, tvb, offset);
720         offset = flow_process_timeperiod(pdutree, tvb, offset);
721         offset = flow_process_ports(pdutree, tvb, offset);
722
723         /*
724          * and the similarities end here 
725          */
726         if (ver == 1) {
727                 offset =
728                     flow_process_textfield(pdutree, tvb, offset, 2, "padding");
729
730                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
731                                     FALSE);
732
733                 proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1,
734                                     FALSE);
735
736                 proto_tree_add_item(pdutree, hf_cflow_tcpflags, tvb, offset++,
737                                     1, FALSE);
738
739                 offset =
740                     flow_process_textfield(pdutree, tvb, offset, 3, "padding");
741
742                 offset =
743                     flow_process_textfield(pdutree, tvb, offset, 4,
744                                            "reserved");
745         } else {
746                 if (ver == 5)
747                         offset =
748                             flow_process_textfield(pdutree, tvb, offset, 1,
749                                                    "padding");
750                 else {
751                         proto_tree_add_item(pdutree, hf_cflow_flags, tvb,
752                                             offset++, 1, FALSE);
753                 }
754
755                 proto_tree_add_item(pdutree, hf_cflow_tcpflags, tvb, offset++,
756                                     1, FALSE);
757
758                 proto_tree_add_item(pdutree, hf_cflow_prot, tvb, offset++, 1,
759                                     FALSE);
760
761                 proto_tree_add_item(pdutree, hf_cflow_tos, tvb, offset++, 1,
762                                     FALSE);
763
764                 offset = flow_process_aspair(pdutree, tvb, offset);
765
766                 mask = tvb_get_guint8(tvb, offset);
767                 proto_tree_add_text(pdutree, tvb, offset, 1,
768                                     "SrcMask: %u (prefix: %s/%u)",
769                                     mask, getprefix(&srcaddr, mask),
770                                     mask != 0 ? mask : 32);
771                 proto_tree_add_uint_hidden(pdutree, hf_cflow_srcmask, tvb,
772                                            offset++, 1, mask);
773
774                 mask = tvb_get_guint8(tvb, offset);
775                 proto_tree_add_text(pdutree, tvb, offset, 1,
776                                     "DstMask: %u (prefix: %s/%u)",
777                                     mask, getprefix(&dstaddr, mask),
778                                     mask != 0 ? mask : 32);
779                 proto_tree_add_uint_hidden(pdutree, hf_cflow_dstmask, tvb,
780                                            offset++, 1, mask);
781
782                 offset =
783                     flow_process_textfield(pdutree, tvb, offset, 2, "padding");
784
785                 if (ver == 7) {
786                         proto_tree_add_item(pdutree, hf_cflow_routersc, tvb,
787                                             offset, 4, FALSE);
788                         offset += 4;
789                 }
790         }
791
792         return (offset - startoffset);
793 }
794
795 static gchar   *
796 getprefix(const guint32 * address, int prefix)
797 {
798         guint32         gprefix;
799
800         gprefix = *address & g_htonl((0xffffffff << (32 - prefix)));
801
802         return (ip_to_str((const guint8 *)&gprefix));
803 }
804
805 void
806 proto_register_netflow(void)
807 {
808         static hf_register_info hf[] = {
809                 /*
810                  * flow header 
811                  */
812                 {&hf_cflow_version,
813                  {"Version", "cflow.version",
814                   FT_UINT16, BASE_DEC, NULL, 0x0,
815                   "NetFlow Version", HFILL}
816                  },
817                 {&hf_cflow_count,
818                  {"Count", "cflow.count",
819                   FT_UINT16, BASE_DEC, NULL, 0x0,
820                   "Count of PDUs", HFILL}
821                  },
822                 {&hf_cflow_sysuptime,
823                  {"SysUptime", "cflow.sysuptime",
824                   FT_UINT32, BASE_DEC, NULL, 0x0,
825                   "Time since router booted (in milliseconds)", HFILL}
826                  },
827
828                 {&hf_cflow_timestamp,
829                  {"Timestamp", "cflow.timestamp",
830                   FT_ABSOLUTE_TIME, BASE_NONE, NULL, 0x0,
831                   "Current seconds since epoch", HFILL}
832                  },
833                 {&hf_cflow_unix_secs,
834                  {"CurrentSecs", "cflow.unix_secs",
835                   FT_UINT32, BASE_DEC, NULL, 0x0,
836                   "Current seconds since epoch", HFILL}
837                  },
838                 {&hf_cflow_unix_nsecs,
839                  {"CurrentNSecs", "cflow.unix_nsecs",
840                   FT_UINT32, BASE_DEC, NULL, 0x0,
841                   "Residual nanoseconds since epoch", HFILL}
842                  },
843                 {&hf_cflow_samplerate,
844                  {"SampleRate", "cflow.samplerate",
845                   FT_UINT16, BASE_DEC, NULL, 0x0,
846                   "Sample Frequency of exporter", HFILL}
847                  },
848
849                 /*
850                  * end version-agnostic header
851                  * version-specific flow header 
852                  */
853                 {&hf_cflow_sequence,
854                  {"FlowSequence", "cflow.sequence",
855                   FT_UINT32, BASE_DEC, NULL, 0x0,
856                   "Sequence number of flows seen", HFILL}
857                  },
858                 {&hf_cflow_engine_type,
859                  {"EngineType", "cflow.engine_type",
860                   FT_UINT8, BASE_DEC, NULL, 0x0,
861                   "Flow switching engine type", HFILL}
862                  },
863                 {&hf_cflow_engine_id,
864                  {"EngineId", "cflow.engine_id",
865                   FT_UINT8, BASE_DEC, NULL, 0x0,
866                   "Slot number of switching engine", HFILL}
867                  },
868                 {&hf_cflow_aggmethod,
869                  {"AggMethod", "cflow.aggmethod",
870                   FT_UINT8, BASE_DEC, VALS(v8_agg), 0x0,
871                   "CFlow V8 Aggregation Method", HFILL}
872                  },
873                 {&hf_cflow_aggversion,
874                  {"AggVersion", "cflow.aggversion",
875                   FT_UINT8, BASE_DEC, NULL, 0x0,
876                   "CFlow V8 Aggregation Version", HFILL}
877                  },
878                 /*
879                  * end version specific header storage 
880                  */
881                 /*
882                  * begin pdu content storage 
883                  */
884                 {&hf_cflow_srcaddr,
885                  {"SrcAddr", "cflow.srcaddr",
886                   FT_IPv4, BASE_NONE, NULL, 0x0,
887                   "Flow Source Address", HFILL}
888                  },
889                 {&hf_cflow_srcnet,
890                  {"SrcNet", "cflow.srcnet",
891                   FT_IPv4, BASE_NONE, NULL, 0x0,
892                   "Flow Source Network", HFILL}
893                  },
894                 {&hf_cflow_dstaddr,
895                  {"DstAddr", "cflow.dstaddr",
896                   FT_IPv4, BASE_NONE, NULL, 0x0,
897                   "Flow Destination Address", HFILL}
898                  },
899                 {&hf_cflow_dstnet,
900                  {"DstNet", "cflow.dstaddr",
901                   FT_IPv4, BASE_NONE, NULL, 0x0,
902                   "Flow Destination Network", HFILL}
903                  },
904                 {&hf_cflow_nexthop,
905                  {"NextHop", "cflow.nexthop",
906                   FT_IPv4, BASE_NONE, NULL, 0x0,
907                   "Router nexthop", HFILL}
908                  },
909                 {&hf_cflow_inputint,
910                  {"InputInt", "cflow.inputint",
911                   FT_UINT16, BASE_DEC, NULL, 0x0,
912                   "Flow Input Interface", HFILL}
913                  },
914                 {&hf_cflow_outputint,
915                  {"OutputInt", "cflow.outputint",
916                   FT_UINT16, BASE_DEC, NULL, 0x0,
917                   "Flow Output Interface", HFILL}
918                  },
919                 {&hf_cflow_flows,
920                  {"Flows", "cflow.flows",
921                   FT_UINT32, BASE_DEC, NULL, 0x0,
922                   "Flows Aggregated in PDU", HFILL}
923                  },
924                 {&hf_cflow_packets,
925                  {"Packets", "cflow.packets",
926                   FT_UINT32, BASE_DEC, NULL, 0x0,
927                   "Count of packets", HFILL}
928                  },
929                 {&hf_cflow_octets,
930                  {"Octets", "cflow.octets",
931                   FT_UINT32, BASE_DEC, NULL, 0x0,
932                   "Count of bytes", HFILL}
933                  },
934                 {&hf_cflow_timestart,
935                  {"StartTime", "cflow.timestart",
936                   FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
937                   "Uptime at start of flow", HFILL}
938                  },
939                 {&hf_cflow_timeend,
940                  {"EndTime", "cflow.timeend",
941                   FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
942                   "Uptime at end of flow", HFILL}
943                  },
944                 {&hf_cflow_srcport,
945                  {"SrcPort", "cflow.srcport",
946                   FT_UINT16, BASE_DEC, NULL, 0x0,
947                   "Flow Source Port", HFILL}
948                  },
949                 {&hf_cflow_dstport,
950                  {"DstPort", "cflow.dstport",
951                   FT_UINT16, BASE_DEC, NULL, 0x0,
952                   "Flow Destination Port", HFILL}
953                  },
954                 {&hf_cflow_prot,
955                  {"Protocol", "cflow.protocol",
956                   FT_UINT8, BASE_DEC, NULL, 0x0,
957                   "IP Protocol", HFILL}
958                  },
959                 {&hf_cflow_tos,
960                  {"IP ToS", "cflow.tos",
961                   FT_UINT8, BASE_HEX, NULL, 0x0,
962                   "IP Type of Service", HFILL}
963                  },
964                 {&hf_cflow_flags,
965                  {"Export Flags", "cflow.flags",
966                   FT_UINT8, BASE_HEX, NULL, 0x0,
967                   "CFlow Flags", HFILL}
968                  },
969                 {&hf_cflow_tcpflags,
970                  {"TCP Flags", "cflow.tcpflags",
971                   FT_UINT8, BASE_HEX, NULL, 0x0,
972                   "TCP Flags", HFILL}
973                  },
974                 {&hf_cflow_srcas,
975                  {"SrcAS", "cflow.srcas",
976                   FT_UINT16, BASE_DEC, NULL, 0x0,
977                   "Source AS", HFILL}
978                  },
979                 {&hf_cflow_dstas,
980                  {"DstAS", "cflow.dstas",
981                   FT_UINT16, BASE_DEC, NULL, 0x0,
982                   "Destination AS", HFILL}
983                  },
984                 {&hf_cflow_srcmask,
985                  {"SrcMask", "cflow.srcmask",
986                   FT_UINT8, BASE_DEC, NULL, 0x0,
987                   "Source Prefix Mask", HFILL}
988                  },
989                 {&hf_cflow_dstmask,
990                  {"DstMask", "cflow.dstmask",
991                   FT_UINT8, BASE_DEC, NULL, 0x0,
992                   "Destination Prefix Mask", HFILL}
993                  },
994                 {&hf_cflow_routersc,
995                  {"Router Shortcut", "cflow.routersc",
996                   FT_IPv4, BASE_NONE, NULL, 0x0,
997                   "Router shortcut by switch", HFILL}
998                  }
999                 /*
1000                  * end pdu content storage 
1001                  */
1002         };
1003
1004         static gint    *ett[] = {
1005                 &ett_netflow,
1006                 &ett_unixtime,
1007                 &ett_flow
1008         };
1009
1010         proto_netflow = proto_register_protocol("Cisco NetFlow", "CFLOW",
1011                                                 "cflow");
1012
1013         proto_register_field_array(proto_netflow, hf, array_length(hf));
1014         proto_register_subtree_array(ett, array_length(ett));
1015
1016         register_dissector("cflow", dissect_netflow, proto_netflow);
1017 }
1018
1019
1020 /*
1021  * protocol/port association 
1022  */
1023 void
1024 proto_reg_handoff_netflow(void)
1025 {
1026         dissector_handle_t netflow_handle;
1027
1028         netflow_handle = create_dissector_handle(dissect_netflow,
1029                                                  proto_netflow);
1030         dissector_add("udp.port", UDP_PORT_NETFLOW, netflow_handle);
1031 }