xref: /freebsd/contrib/tcpdump/netdissect.c (revision 7a7741af18d6c8a804cc643cb7ecda9d730c6aa6)
1 /*
2  * Copyright (c) 1988-1997
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Copyright (c) 1998-2012  Michael Richardson <mcr@tcpdump.org>
6  *      The TCPDUMP project
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that: (1) source code distributions
10  * retain the above copyright notice and this paragraph in its entirety, (2)
11  * distributions including binary code include the above copyright notice and
12  * this paragraph in its entirety in the documentation or other materials
13  * provided with the distribution, and (3) all advertising materials mentioning
14  * features or use of this software display the following acknowledgement:
15  * ``This product includes software developed by the University of California,
16  * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
17  * the University nor the names of its contributors may be used to endorse
18  * or promote products derived from this software without specific prior
19  * written permission.
20  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
21  * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
22  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
23  */
24 
25 #include <config.h>
26 
27 #include "netdissect-stdinc.h"
28 #include "netdissect.h"
29 #include <string.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 #ifdef USE_LIBSMI
34 #include <smi.h>
35 #endif
36 
37 /*
38  * Initialize anything that must be initialized before dissecting
39  * packets.
40  *
41  * This should be called at the beginning of the program; it does
42  * not need to be called, and should not be called, for every
43  * netdissect_options structure.
44  */
45 int
46 nd_init(char *errbuf, size_t errbuf_size)
47 {
48 #ifdef _WIN32
49 	WORD wVersionRequested;
50 	WSADATA wsaData;
51 	int err;
52 
53 	/*
54 	 * Request Winsock 2.2; we expect Winsock 2.
55 	 */
56 	wVersionRequested = MAKEWORD(2, 2);
57 	err = WSAStartup(wVersionRequested, &wsaData);
58 	if (err != 0) {
59 		strlcpy(errbuf, "Attempting to initialize Winsock failed",
60 		    errbuf_size);
61 		return (-1);
62 	}
63 #endif /* _WIN32 */
64 
65 #ifdef USE_LIBSMI
66 	/*
67 	 * XXX - should we just fail if this fails?  Some of the
68 	 * libsmi calls may fail.
69 	 */
70 	smiInit("tcpdump");
71 #endif
72 
73 	/*
74 	 * Clears the error buffer, and uses it so we don't get
75 	 * "unused argument" warnings at compile time.
76 	 */
77 	strlcpy(errbuf, "", errbuf_size);
78 	return (0);
79 }
80 
81 /*
82  * Clean up anything that ndo_init() did.
83  */
84 void
85 nd_cleanup(void)
86 {
87 #ifdef USE_LIBSMI
88 	/*
89 	 * This appears, in libsmi 0.4.8, to do nothing if smiInit()
90 	 * wasn't done or failed, so we call it unconditionally.
91 	 */
92 	smiExit();
93 #endif
94 
95 #ifdef _WIN32
96 	/*
97 	 * Undo the WSAStartup() call above.
98 	 */
99 	WSACleanup();
100 #endif
101 }
102 
103 int
104 nd_have_smi_support(void)
105 {
106 #ifdef USE_LIBSMI
107 	return (1);
108 #else
109 	return (0);
110 #endif
111 }
112 
113 /*
114  * Indicates whether an SMI module has been loaded, so that we can use
115  * libsmi to translate OIDs.
116  */
117 int nd_smi_module_loaded;
118 
119 int
120 nd_load_smi_module(const char *module, char *errbuf, size_t errbuf_size)
121 {
122 #ifdef USE_LIBSMI
123 	if (smiLoadModule(module) == 0) {
124 		snprintf(errbuf, errbuf_size, "could not load MIB module %s",
125 		    module);
126 		return (-1);
127 	}
128 	nd_smi_module_loaded = 1;
129 	return (0);
130 #else
131 	snprintf(errbuf, errbuf_size, "MIB module %s not loaded: no libsmi support",
132 	    module);
133 	return (-1);
134 #endif
135 }
136 
137 const char *
138 nd_smi_version_string(void)
139 {
140 #ifdef USE_LIBSMI
141 	return (smi_version_string);
142 #else
143 	return (NULL);
144 #endif
145 }
146 
147 
148 int
149 nd_push_buffer(netdissect_options *ndo, u_char *new_buffer,
150 	       const u_char *new_packetp, const u_int newlen)
151 {
152 	struct netdissect_saved_packet_info *ndspi;
153 
154 	ndspi = (struct netdissect_saved_packet_info *)malloc(sizeof(struct netdissect_saved_packet_info));
155 	if (ndspi == NULL)
156 		return (0);	/* fail */
157 	ndspi->ndspi_buffer = new_buffer;
158 	ndspi->ndspi_packetp = ndo->ndo_packetp;
159 	ndspi->ndspi_snapend = ndo->ndo_snapend;
160 	ndspi->ndspi_prev = ndo->ndo_packet_info_stack;
161 
162 	ndo->ndo_packetp = new_packetp;
163 	ndo->ndo_snapend = new_packetp + newlen;
164 	ndo->ndo_packet_info_stack = ndspi;
165 
166 	return (1);	/* success */
167 }
168 
169 
170 /*
171  * In a given netdissect_options structure:
172  *
173  *    push the current packet information onto the packet information
174  *    stack;
175  *
176  *    given a pointer into the packet and a length past that point in
177  *    the packet, calculate a new snapshot end that's at the lower
178  *    of the current snapshot end and that point in the packet;
179  *
180  *    set the snapshot end to that new value.
181  */
182 int
183 nd_push_snaplen(netdissect_options *ndo, const u_char *bp, const u_int newlen)
184 {
185 	struct netdissect_saved_packet_info *ndspi;
186 	u_int snaplen_remaining;
187 
188 	ndspi = (struct netdissect_saved_packet_info *)malloc(sizeof(struct netdissect_saved_packet_info));
189 	if (ndspi == NULL)
190 		return (0);	/* fail */
191 	ndspi->ndspi_buffer = NULL;	/* no new buffer */
192 	ndspi->ndspi_packetp = ndo->ndo_packetp;
193 	ndspi->ndspi_snapend = ndo->ndo_snapend;
194 	ndspi->ndspi_prev = ndo->ndo_packet_info_stack;
195 
196 	/*
197 	 * Push the saved previous data onto the stack.
198 	 */
199 	ndo->ndo_packet_info_stack = ndspi;
200 
201 	/*
202 	 * Find out how many bytes remain after the current snapend.
203 	 *
204 	 * We're restricted to packets with at most UINT_MAX bytes;
205 	 * cast the result to u_int, so that we don't get truncation
206 	 * warnings on LP64 and LLP64 platforms.  (ptrdiff_t is
207 	 * signed and we want an unsigned difference; the pointer
208 	 * should at most be equal to snapend, and must *never*
209 	 * be past snapend.)
210 	 */
211 	snaplen_remaining = (u_int)(ndo->ndo_snapend - bp);
212 
213 	/*
214 	 * If the new snapend is smaller than the one calculated
215 	 * above, set the snapend to that value, otherwise leave
216 	 * it unchanged.
217 	 */
218 	if (newlen <= snaplen_remaining) {
219 		/* Snapend isn't past the previous snapend */
220 		ndo->ndo_snapend = bp + newlen;
221 	}
222 
223 	return (1);	/* success */
224 }
225 
226 /*
227  * In a given netdissect_options structure:
228  *
229  *    given a pointer into the packet and a length past that point in
230  *    the packet, calculate a new snapshot end that's at the lower
231  *    of the previous snapshot end - or, if there is no previous
232  *    snapshot end, the current snapshot end - and that point in the
233  *    packet;
234  *
235  *    set the snapshot end to that new value.
236  *
237  * This is to change the current snapshot end.  This may increase the
238  * snapshot end, as it may be used, for example, for a Jumbo Payload
239  * option in IPv6.  It must not increase it past the snapshot length
240  * atop which the current one was pushed, however.
241  */
242 void
243 nd_change_snaplen(netdissect_options *ndo, const u_char *bp, const u_int newlen)
244 {
245 	struct netdissect_saved_packet_info *ndspi;
246 	const u_char *previous_snapend;
247 	u_int snaplen_remaining;
248 
249 	ndspi = ndo->ndo_packet_info_stack;
250 	if (ndspi->ndspi_prev != NULL)
251 		previous_snapend = ndspi->ndspi_prev->ndspi_snapend;
252 	else
253 		previous_snapend = ndo->ndo_snapend;
254 
255 	/*
256 	 * Find out how many bytes remain after the previous
257 	 * snapend - or, if there is no previous snapend, after
258 	 * the current snapend.
259 	 *
260 	 * We're restricted to packets with at most UINT_MAX bytes;
261 	 * cast the result to u_int, so that we don't get truncation
262 	 * warnings on LP64 and LLP64 platforms.  (ptrdiff_t is
263 	 * signed and we want an unsigned difference; the pointer
264 	 * should at most be equal to snapend, and must *never*
265 	 * be past snapend.)
266 	 */
267 	snaplen_remaining = (u_int)(previous_snapend - bp);
268 
269 	/*
270 	 * If the new snapend is smaller than the one calculated
271 	 * above, set the snapend to that value, otherwise leave
272 	 * it unchanged.
273 	 */
274 	if (newlen <= snaplen_remaining) {
275 		/* Snapend isn't past the previous snapend */
276 		ndo->ndo_snapend = bp + newlen;
277 	}
278 }
279 
280 void
281 nd_pop_packet_info(netdissect_options *ndo)
282 {
283 	struct netdissect_saved_packet_info *ndspi;
284 
285 	ndspi = ndo->ndo_packet_info_stack;
286 	ndo->ndo_packetp = ndspi->ndspi_packetp;
287 	ndo->ndo_snapend = ndspi->ndspi_snapend;
288 	ndo->ndo_packet_info_stack = ndspi->ndspi_prev;
289 
290 	free(ndspi->ndspi_buffer);
291 	free(ndspi);
292 }
293 
294 void
295 nd_pop_all_packet_info(netdissect_options *ndo)
296 {
297 	while (ndo->ndo_packet_info_stack != NULL)
298 		nd_pop_packet_info(ndo);
299 }
300 
301 NORETURN void
302 nd_trunc_longjmp(netdissect_options *ndo)
303 {
304 	longjmp(ndo->ndo_early_end, ND_TRUNCATED);
305 #ifdef _AIX
306 	/*
307 	 * In AIX <setjmp.h> decorates longjmp() with "#pragma leaves", which tells
308 	 * XL C that the function is noreturn, but GCC remains unaware of that and
309 	 * yields a "'noreturn' function does return" warning.
310 	 */
311 	ND_UNREACHABLE
312 #endif /* _AIX */
313 }
314