xref: /netbsd-src/external/bsd/tcpdump/dist/netdissect.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
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 <sys/cdefs.h>
26 #ifndef lint
27 __RCSID("$NetBSD: netdissect.c,v 1.3 2023/08/17 20:19:40 christos Exp $");
28 #endif
29 
30 #ifdef HAVE_CONFIG_H
31 #include <config.h>
32 #endif
33 
34 #include "netdissect-stdinc.h"
35 #include "netdissect.h"
36 #include <string.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 
40 #ifdef USE_LIBSMI
41 #include <smi.h>
42 #endif
43 
44 /*
45  * Initialize anything that must be initialized before dissecting
46  * packets.
47  *
48  * This should be called at the beginning of the program; it does
49  * not need to be called, and should not be called, for every
50  * netdissect_options structure.
51  */
52 int
53 nd_init(char *errbuf, size_t errbuf_size)
54 {
55 #ifdef _WIN32
56 	WORD wVersionRequested;
57 	WSADATA wsaData;
58 	int err;
59 
60 	/*
61 	 * Request Winsock 2.2; we expect Winsock 2.
62 	 */
63 	wVersionRequested = MAKEWORD(2, 2);
64 	err = WSAStartup(wVersionRequested, &wsaData);
65 	if (err != 0) {
66 		strlcpy(errbuf, "Attempting to initialize Winsock failed",
67 		    errbuf_size);
68 		return (-1);
69 	}
70 #endif /* _WIN32 */
71 
72 #ifdef USE_LIBSMI
73 	/*
74 	 * XXX - should we just fail if this fails?  Some of the
75 	 * libsmi calls may fail.
76 	 */
77 	smiInit("tcpdump");
78 #endif
79 
80 	/*
81 	 * Clears the error buffer, and uses it so we don't get
82 	 * "unused argument" warnings at compile time.
83 	 */
84 	strlcpy(errbuf, "", errbuf_size);
85 	return (0);
86 }
87 
88 /*
89  * Clean up anything that ndo_init() did.
90  */
91 void
92 nd_cleanup(void)
93 {
94 #ifdef USE_LIBSMI
95 	/*
96 	 * This appears, in libsmi 0.4.8, to do nothing if smiInit()
97 	 * wasn't done or failed, so we call it unconditionally.
98 	 */
99 	smiExit();
100 #endif
101 
102 #ifdef _WIN32
103 	/*
104 	 * Undo the WSAStartup() call above.
105 	 */
106 	WSACleanup();
107 #endif
108 }
109 
110 int
111 nd_have_smi_support(void)
112 {
113 #ifdef USE_LIBSMI
114 	return (1);
115 #else
116 	return (0);
117 #endif
118 }
119 
120 /*
121  * Indicates whether an SMI module has been loaded, so that we can use
122  * libsmi to translate OIDs.
123  */
124 int nd_smi_module_loaded;
125 
126 int
127 nd_load_smi_module(const char *module, char *errbuf, size_t errbuf_size)
128 {
129 #ifdef USE_LIBSMI
130 	if (smiLoadModule(module) == 0) {
131 		snprintf(errbuf, errbuf_size, "could not load MIB module %s",
132 		    module);
133 		return (-1);
134 	}
135 	nd_smi_module_loaded = 1;
136 	return (0);
137 #else
138 	snprintf(errbuf, errbuf_size, "MIB module %s not loaded: no libsmi support",
139 	    module);
140 	return (-1);
141 #endif
142 }
143 
144 const char *
145 nd_smi_version_string(void)
146 {
147 #ifdef USE_LIBSMI
148 	return (smi_version_string);
149 #else
150 	return (NULL);
151 #endif
152 }
153 
154 
155 int
156 nd_push_buffer(netdissect_options *ndo, u_char *new_buffer,
157 	       const u_char *new_packetp, const u_int newlen)
158 {
159 	struct netdissect_saved_packet_info *ndspi;
160 
161 	ndspi = (struct netdissect_saved_packet_info *)malloc(sizeof(struct netdissect_saved_packet_info));
162 	if (ndspi == NULL)
163 		return (0);	/* fail */
164 	ndspi->ndspi_buffer = new_buffer;
165 	ndspi->ndspi_packetp = ndo->ndo_packetp;
166 	ndspi->ndspi_snapend = ndo->ndo_snapend;
167 	ndspi->ndspi_prev = ndo->ndo_packet_info_stack;
168 
169 	ndo->ndo_packetp = new_packetp;
170 	ndo->ndo_snapend = new_packetp + newlen;
171 	ndo->ndo_packet_info_stack = ndspi;
172 
173 	return (1);	/* success */
174 }
175 
176 
177 /*
178  * In a given netdissect_options structure:
179  *
180  *    push the current packet information onto the packet information
181  *    stack;
182  *
183  *    given a pointer into the packet and a length past that point in
184  *    the packet, calculate a new snapshot end that's at the lower
185  *    of the current snapshot end and that point in the packet;
186  *
187  *    set the snapshot end to that new value.
188  */
189 int
190 nd_push_snaplen(netdissect_options *ndo, const u_char *bp, const u_int newlen)
191 {
192 	struct netdissect_saved_packet_info *ndspi;
193 	u_int snaplen_remaining;
194 
195 	ndspi = (struct netdissect_saved_packet_info *)malloc(sizeof(struct netdissect_saved_packet_info));
196 	if (ndspi == NULL)
197 		return (0);	/* fail */
198 	ndspi->ndspi_buffer = NULL;	/* no new buffer */
199 	ndspi->ndspi_packetp = ndo->ndo_packetp;
200 	ndspi->ndspi_snapend = ndo->ndo_snapend;
201 	ndspi->ndspi_prev = ndo->ndo_packet_info_stack;
202 
203 	/*
204 	 * Push the saved previous data onto the stack.
205 	 */
206 	ndo->ndo_packet_info_stack = ndspi;
207 
208 	/*
209 	 * Find out how many bytes remain after the current snapend.
210 	 *
211 	 * We're restricted to packets with at most UINT_MAX bytes;
212 	 * cast the result to u_int, so that we don't get truncation
213 	 * warnings on LP64 and LLP64 platforms.  (ptrdiff_t is
214 	 * signed and we want an unsigned difference; the pointer
215 	 * should at most be equal to snapend, and must *never*
216 	 * be past snapend.)
217 	 */
218 	snaplen_remaining = (u_int)(ndo->ndo_snapend - bp);
219 
220 	/*
221 	 * If the new snapend is smaller than the one calculated
222 	 * above, set the snapend to that value, otherwise leave
223 	 * it unchanged.
224 	 */
225 	if (newlen <= snaplen_remaining) {
226 		/* Snapend isn't past the previous snapend */
227 		ndo->ndo_snapend = bp + newlen;
228 	}
229 
230 	return (1);	/* success */
231 }
232 
233 /*
234  * In a given netdissect_options structure:
235  *
236  *    given a pointer into the packet and a length past that point in
237  *    the packet, calculate a new snapshot end that's at the lower
238  *    of the previous snapshot end - or, if there is no previous
239  *    snapshot end, the current snapshot end - and that point in the
240  *    packet;
241  *
242  *    set the snapshot end to that new value.
243  *
244  * This is to change the current snapshot end.  This may increase the
245  * snapshot end, as it may be used, for example, for a Jumbo Payload
246  * option in IPv6.  It must not increase it past the snapshot length
247  * atop which the current one was pushed, however.
248  */
249 void
250 nd_change_snaplen(netdissect_options *ndo, const u_char *bp, const u_int newlen)
251 {
252 	struct netdissect_saved_packet_info *ndspi;
253 	const u_char *previous_snapend;
254 	u_int snaplen_remaining;
255 
256 	ndspi = ndo->ndo_packet_info_stack;
257 	if (ndspi->ndspi_prev != NULL)
258 		previous_snapend = ndspi->ndspi_prev->ndspi_snapend;
259 	else
260 		previous_snapend = ndo->ndo_snapend;
261 
262 	/*
263 	 * Find out how many bytes remain after the previous
264 	 * snapend - or, if there is no previous snapend, after
265 	 * the current snapend.
266 	 *
267 	 * We're restricted to packets with at most UINT_MAX bytes;
268 	 * cast the result to u_int, so that we don't get truncation
269 	 * warnings on LP64 and LLP64 platforms.  (ptrdiff_t is
270 	 * signed and we want an unsigned difference; the pointer
271 	 * should at most be equal to snapend, and must *never*
272 	 * be past snapend.)
273 	 */
274 	snaplen_remaining = (u_int)(previous_snapend - bp);
275 
276 	/*
277 	 * If the new snapend is smaller than the one calculated
278 	 * above, set the snapend to that value, otherwise leave
279 	 * it unchanged.
280 	 */
281 	if (newlen <= snaplen_remaining) {
282 		/* Snapend isn't past the previous snapend */
283 		ndo->ndo_snapend = bp + newlen;
284 	}
285 }
286 
287 void
288 nd_pop_packet_info(netdissect_options *ndo)
289 {
290 	struct netdissect_saved_packet_info *ndspi;
291 
292 	ndspi = ndo->ndo_packet_info_stack;
293 	ndo->ndo_packetp = ndspi->ndspi_packetp;
294 	ndo->ndo_snapend = ndspi->ndspi_snapend;
295 	ndo->ndo_packet_info_stack = ndspi->ndspi_prev;
296 
297 	free(ndspi->ndspi_buffer);
298 	free(ndspi);
299 }
300 
301 void
302 nd_pop_all_packet_info(netdissect_options *ndo)
303 {
304 	while (ndo->ndo_packet_info_stack != NULL)
305 		nd_pop_packet_info(ndo);
306 }
307