Paul Ionescu's patch to add support for IPX over GRE.
[obnox/wireshark/wip.git] / packet-gre.c
1 /* packet-gre.c
2  * Routines for the Generic Routing Encapsulation (GRE) protocol
3  * Brad Robel-Forrest <brad.robel-forrest@watchguard.com>
4  *
5  * $Id: packet-gre.c,v 1.20 2000/05/18 08:41:13 guy Exp $
6  *
7  * Ethereal - Network traffic analyzer
8  * By Gerald Combs <gerald@zing.org>
9  * Copyright 1998 Gerald Combs
10  *
11  * 
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  * 
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #ifdef HAVE_SYS_TYPES_H
32 # include <sys/types.h>
33 #endif
34
35 #ifdef HAVE_NETINET_IN_H
36 #include <netinet/in.h>
37 #endif
38 #include <glib.h>
39 #include "packet.h"
40 #include "packet-ip.h"
41 #include "packet-ppp.h"
42 #include "packet-ipx.h"
43
44 static int proto_gre = -1;
45 static int hf_gre_proto = -1;
46
47 static gint ett_gre = -1;
48 static gint ett_gre_flags = -1;
49
50 /* bit positions for flags in header */
51 #define GH_B_C          0x8000
52 #define GH_B_R          0x4000
53 #define GH_B_K          0x2000
54 #define GH_B_S          0x1000
55 #define GH_B_s          0x0800
56 #define GH_B_RECUR      0x0700
57 #define GH_P_A          0x0080  /* only in special PPTPized GRE header */
58 #define GH_P_FLAGS      0x0078  /* only in special PPTPized GRE header */
59 #define GH_R_FLAGS      0x00F8
60 #define GH_B_VER        0x0007
61
62 #define GRE_PPP         0x880B
63 #define GRE_IP          0x0800
64 #define GRE_WCCP        0x883E
65 #define GRE_IPX         0x8137
66
67 static void add_flags_and_ver(proto_tree *, guint16, int, int);
68
69 static const value_string typevals[] = {
70         { GRE_PPP,  "PPP" },
71         { GRE_IP,   "IP" },
72         { GRE_WCCP, "WCCP"},
73         { GRE_IPX,  "IPX"},
74         { 0,        NULL  }
75 };
76
77 static void
78 dissect_gre(const u_char *pd, int offset, frame_data *fd, proto_tree *tree) {
79   
80   guint16       flags_and_ver = pntohs(pd + offset);
81   guint16       type          = pntohs(pd + offset + sizeof(flags_and_ver));
82   guint16       sre_af;
83   guint8        sre_length;
84
85   if (check_col(fd, COL_PROTOCOL))
86     col_add_str(fd, COL_PROTOCOL, "GRE");
87         
88   if (check_col(fd, COL_INFO)) {
89     col_add_fstr(fd, COL_INFO, "Encapsulated %s",
90         val_to_str(type, typevals, "0x%04X (unknown)"));
91   }
92                 
93   if (IS_DATA_IN_FRAME(offset) && tree) {
94     gboolean            is_ppp = FALSE;
95     gboolean            is_wccp2 = FALSE;
96     proto_item *        ti;
97     proto_tree *        gre_tree;
98     guint               len = 4;
99
100     if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R)
101       len += 4;
102     if (flags_and_ver & GH_B_K)
103       len += 4;
104     if (flags_and_ver & GH_B_S)
105       len += 4;
106     switch (type) {
107
108     case GRE_PPP:
109       if (flags_and_ver & GH_P_A)
110         len += 4;
111       is_ppp = TRUE;
112       break;
113
114     case GRE_WCCP:
115       /* WCCP2 apparently puts an extra 4 octets into the header, but uses
116          the same encapsulation type; if it looks as if the first octet of
117          the packet isn't the beginning of an IPv4 header, assume it's
118          WCCP2. */
119       if ((pd[offset + sizeof(flags_and_ver) + sizeof(type)] & 0xF0) != 0x40) {
120         len += 4;
121         is_wccp2 = TRUE;
122       }
123       break;
124     }
125
126     ti = proto_tree_add_protocol_format(tree, proto_gre, NullTVB, offset, len,
127       "Generic Routing Encapsulation (%s)",
128       val_to_str(type, typevals, "0x%04X - unknown"));
129     gre_tree = proto_item_add_subtree(ti, ett_gre);
130     add_flags_and_ver(gre_tree, flags_and_ver, offset, is_ppp);
131
132     offset += sizeof(flags_and_ver);
133
134     proto_tree_add_item(gre_tree, hf_gre_proto, NullTVB, offset, sizeof(type), type);
135     offset += sizeof(type);
136
137     if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) {
138       guint16 checksum = pntohs(pd + offset);
139       proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(checksum),
140                           "Checksum: %u", checksum);
141       offset += sizeof(checksum);
142     }
143     
144     if (flags_and_ver & GH_B_C || flags_and_ver & GH_B_R) {
145       guint16 rtoffset = pntohs(pd + offset);
146       proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(rtoffset),
147                           "Offset: %u", rtoffset);
148       offset += sizeof(rtoffset);
149     }
150
151     if (flags_and_ver & GH_B_K) {
152       if (is_ppp) {
153         guint16 paylen;
154         guint16 callid;
155         
156         paylen = pntohs(pd + offset);
157         proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(paylen),
158                             "Payload length: %u", paylen);
159         offset += sizeof(paylen);
160
161         callid = pntohs(pd + offset);
162         proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(callid),
163                             "Call ID: %u", callid);
164         offset += sizeof(callid);
165       }
166       else {
167         guint32 key = pntohl(pd + offset);
168         proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(key),
169                             "Key: %u", key);
170         offset += sizeof(key);
171       }
172     }
173     
174     if (flags_and_ver & GH_B_S) {
175       guint32 seqnum = pntohl(pd + offset);
176       proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(seqnum),
177                           "Sequence number: %u", seqnum);
178       offset += sizeof(seqnum);
179     }
180
181     if (is_ppp && flags_and_ver & GH_P_A) {
182       guint32 acknum = pntohl(pd + offset);
183       proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(acknum),
184                           "Acknowledgement number: %u", acknum);
185       offset += sizeof(acknum);
186     }
187
188     if (flags_and_ver & GH_B_R) {
189       for (;;) {
190         sre_af = pntohs(pd + offset);
191         proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(guint16),
192                           "Address family: %u", sre_af);
193         offset += sizeof(guint16);
194         proto_tree_add_text(gre_tree, NullTVB, offset, 1,
195                           "SRE offset: %u", pd[offset++]);
196         sre_length = pd[offset];
197         proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(guint8),
198                           "SRE length: %u", sre_length);
199         offset += sizeof(guint8);
200         if (sre_af == 0 && sre_length == 0)
201           break;
202         offset += sre_length;
203       }
204     }
205
206     switch (type) {
207       case GRE_PPP:
208         dissect_ppp(pd, offset, fd, tree);
209         break;
210       case GRE_IP:
211         dissect_ip(pd, offset, fd, tree);
212         break;
213       case GRE_WCCP:
214         if (is_wccp2) {
215           proto_tree_add_text(gre_tree, NullTVB, offset, sizeof(guint32), "WCCPv2 Data");
216           offset += 4;
217         }
218         dissect_ip(pd, offset, fd, tree);
219         break;
220       case GRE_IPX:
221         dissect_ipx(pd, offset, fd, tree);
222         break;
223       default:
224         dissect_data(pd, offset, fd, gre_tree);
225         break;
226     }
227   }
228 }
229
230 static void
231 add_flags_and_ver(proto_tree *tree, guint16 flags_and_ver, int offset, int is_ppp) {
232
233   proto_item *  ti;
234   proto_tree *  fv_tree;
235   int           nbits = sizeof(flags_and_ver) * 8;
236   
237   ti = proto_tree_add_text(tree, NullTVB, offset, 2, 
238                            "Flags and version: %#04x", flags_and_ver);
239   fv_tree = proto_item_add_subtree(ti, ett_gre_flags);
240   
241   proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
242                       decode_boolean_bitfield(flags_and_ver, GH_B_C, nbits,
243                                               "Checksum", "No checksum"));
244   proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
245                       decode_boolean_bitfield(flags_and_ver, GH_B_R, nbits,
246                                               "Routing", "No routing"));
247   proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
248                       decode_boolean_bitfield(flags_and_ver, GH_B_K, nbits,
249                                               "Key", "No key"));
250   proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
251                       decode_boolean_bitfield(flags_and_ver, GH_B_S, nbits,
252                                               "Sequence number", "No sequence number"));
253   proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
254                       decode_boolean_bitfield(flags_and_ver, GH_B_s, nbits,
255                                               "Strict source route", "No strict source route"));
256   proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
257                       decode_numeric_bitfield(flags_and_ver, GH_B_RECUR, nbits,
258                                               "Recursion control: %u"));
259   if (is_ppp) {
260     proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
261                         decode_boolean_bitfield(flags_and_ver, GH_P_A, nbits,
262                                                 "Acknowledgment number", "No acknowledgment number"));
263     proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
264                         decode_numeric_bitfield(flags_and_ver, GH_P_FLAGS, nbits,
265                                                 "Flags: %u"));
266   }
267   else {
268     proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
269                         decode_numeric_bitfield(flags_and_ver, GH_R_FLAGS, nbits,
270                                                 "Flags: %u"));
271   }
272
273   proto_tree_add_text(fv_tree, NullTVB, offset, sizeof(flags_and_ver), "%s",
274                       decode_numeric_bitfield(flags_and_ver, GH_B_VER, nbits,
275                                               "Version: %u"));
276  }
277  
278 void
279 proto_register_gre(void)
280 {
281         static hf_register_info hf[] = {
282                 { &hf_gre_proto,
283                         { "Protocol Type", "gre.proto", FT_UINT16, BASE_HEX, VALS(typevals), 0x0,
284                                 "The protocol that is GRE encapsulated"}
285                 },
286         };
287         static gint *ett[] = {
288                 &ett_gre,
289                 &ett_gre_flags,
290         };
291
292         proto_gre = proto_register_protocol("Generic Routing Encapsulation", "gre");
293         proto_register_field_array(proto_gre, hf, array_length(hf));
294         proto_register_subtree_array(ett, array_length(ett));
295 }
296
297 void
298 proto_reg_handoff_gre(void)
299 {
300         dissector_add("ip.proto", IP_PROTO_GRE, dissect_gre);
301 }