1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22 /*
23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 *
26 * Copyright 2024 Oxide Computer Company
27 */
28
29 #include <stdio.h>
30 #include <string.h>
31 #include <fcntl.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/time.h>
35
36 #include <sys/socket.h>
37 #include <net/if.h>
38 #include <netinet/in_systm.h>
39 #include <netinet/in.h>
40 #include <netinet/ip.h>
41 #include <netinet/if_ether.h>
42 #include <netinet/tcp.h>
43 #include "snoop.h"
44
45 extern char *dlc_header;
46
47 #define TCPOPT_HEADER_LEN 2
48 #define TCPOPT_TSTAMP_LEN 10
49 #define TCPOPT_SACK_LEN 8
50 #define TCPOPT_MD5_LEN 18
51
52 /*
53 * Convert a network byte order 32 bit integer to a host order integer.
54 * ntohl() cannot be used because option values may not be aligned properly.
55 */
56 #define GET_UINT32(opt) (((uint_t)*((uchar_t *)(opt) + 0) << 24) | \
57 ((uint_t)*((uchar_t *)(opt) + 1) << 16) | \
58 ((uint_t)*((uchar_t *)(opt) + 2) << 8) | \
59 ((uint_t)*((uchar_t *)(opt) + 3)))
60
61 static void print_tcpoptions_summary(uchar_t *, int, char *);
62 static void print_tcpoptions(uchar_t *, int);
63
64 static const struct {
65 unsigned int tf_flag;
66 const char *tf_name;
67 } tcp_flags[] = {
68 { TH_SYN, "Syn" },
69 { TH_FIN, "Fin" },
70 { TH_RST, "Rst" },
71 { TH_PUSH, "Push" },
72 { TH_ECE, "ECE" },
73 { TH_CWR, "CWR" },
74 { 0, NULL }
75 };
76
77 int
interpret_tcp(int flags,struct tcphdr * tcp,int iplen,int fraglen)78 interpret_tcp(int flags, struct tcphdr *tcp, int iplen, int fraglen)
79 {
80 char *data;
81 int hdrlen, tcplen;
82 int sunrpc = 0;
83 char *pname;
84 char buff[32];
85 char *line, *endline;
86 unsigned int i;
87
88 hdrlen = tcp->th_off * 4;
89 data = (char *)tcp + hdrlen;
90 tcplen = iplen - hdrlen;
91 fraglen -= hdrlen;
92 if (fraglen < 0)
93 return (fraglen + hdrlen); /* incomplete header */
94 if (fraglen > tcplen)
95 fraglen = tcplen;
96
97 if (flags & F_SUM) {
98 line = get_sum_line();
99 endline = line + MAXLINE;
100 (void) snprintf(line, endline - line, "TCP D=%d S=%d",
101 ntohs(tcp->th_dport), ntohs(tcp->th_sport));
102 line += strlen(line);
103
104 for (i = 0; tcp_flags[i].tf_name != NULL; i++) {
105 if (tcp->th_flags & tcp_flags[i].tf_flag) {
106 (void) snprintf(line, endline - line, " %s",
107 tcp_flags[i].tf_name);
108 line += strlen(line);
109 }
110 }
111
112 if (tcp->th_flags & TH_URG) {
113 (void) snprintf(line, endline - line, " Urg=%u",
114 ntohs(tcp->th_urp));
115 line += strlen(line);
116 }
117 if (tcp->th_flags & TH_ACK) {
118 (void) snprintf(line, endline - line, " Ack=%u",
119 ntohl(tcp->th_ack));
120 line += strlen(line);
121 }
122 if (ntohl(tcp->th_seq)) {
123 (void) snprintf(line, endline - line, " Seq=%u Len=%d",
124 ntohl(tcp->th_seq), tcplen);
125 line += strlen(line);
126 }
127 (void) snprintf(line, endline - line, " Win=%d",
128 ntohs(tcp->th_win));
129 print_tcpoptions_summary((uchar_t *)(tcp + 1),
130 (int)(tcp->th_off * 4 - sizeof (struct tcphdr)), line);
131 }
132
133 sunrpc = !reservedport(IPPROTO_TCP, ntohs(tcp->th_dport)) &&
134 !reservedport(IPPROTO_TCP, ntohs(tcp->th_sport)) &&
135 valid_rpc(data + 4, fraglen - 4);
136
137 if (flags & F_DTAIL) {
138
139 show_header("TCP: ", "TCP Header", tcplen);
140 show_space();
141 (void) sprintf(get_line((char *)(uintptr_t)tcp->th_sport -
142 dlc_header, 2), "Source port = %d", ntohs(tcp->th_sport));
143
144 if (sunrpc) {
145 pname = "(Sun RPC)";
146 } else {
147 pname = getportname(IPPROTO_TCP, ntohs(tcp->th_dport));
148 if (pname == NULL) {
149 pname = "";
150 } else {
151 (void) sprintf(buff, "(%s)", pname);
152 pname = buff;
153 }
154 }
155 (void) sprintf(get_line((char *)(uintptr_t)tcp->th_dport -
156 dlc_header, 2), "Destination port = %d %s",
157 ntohs(tcp->th_dport), pname);
158 (void) sprintf(get_line((char *)(uintptr_t)tcp->th_seq -
159 dlc_header, 4), "Sequence number = %u",
160 ntohl(tcp->th_seq));
161 (void) sprintf(get_line((char *)(uintptr_t)tcp->th_ack - dlc_header, 4),
162 "Acknowledgement number = %u",
163 ntohl(tcp->th_ack));
164 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_ack - dlc_header) +
165 4, 1), "Data offset = %d bytes", tcp->th_off * 4);
166 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
167 dlc_header) + 4, 1), "Flags = 0x%02x", tcp->th_flags);
168 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
169 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_CWR,
170 "ECN congestion window reduced",
171 "No ECN congestion window reduced"));
172 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
173 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_ECE,
174 "ECN echo", "No ECN echo"));
175 (void) sprintf(
176 get_line(((char *)(uintptr_t)tcp->th_flags - dlc_header) + 4, 1),
177 " %s", getflag(tcp->th_flags, TH_URG,
178 "Urgent pointer", "No urgent pointer"));
179 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
180 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_ACK,
181 "Acknowledgement", "No acknowledgement"));
182 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
183 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_PUSH,
184 "Push", "No push"));
185 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
186 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_RST,
187 "Reset", "No reset"));
188 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
189 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_SYN,
190 "Syn", "No Syn"));
191 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_flags -
192 dlc_header) + 4, 1), " %s", getflag(tcp->th_flags, TH_FIN,
193 "Fin", "No Fin"));
194 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_win - dlc_header) +
195 4, 1), "Window = %d", ntohs(tcp->th_win));
196 /* XXX need to compute checksum and print whether correct */
197 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_sum - dlc_header) +
198 4, 1), "Checksum = 0x%04x", ntohs(tcp->th_sum));
199 (void) sprintf(get_line(((char *)(uintptr_t)tcp->th_urp - dlc_header) +
200 4, 1), "Urgent pointer = %d", ntohs(tcp->th_urp));
201
202 /* Print TCP options - if any */
203
204 print_tcpoptions((uchar_t *)(tcp + 1),
205 tcp->th_off * 4 - sizeof (struct tcphdr));
206
207 show_space();
208 }
209
210 /* is there any data? */
211 if (tcplen == 0)
212 return (tcplen);
213
214 /* go to the next protocol layer */
215
216 if (!interpret_reserved(flags, IPPROTO_TCP,
217 ntohs(tcp->th_sport), ntohs(tcp->th_dport), data, fraglen)) {
218 if (sunrpc && fraglen > 0)
219 interpret_rpc(flags, data, fraglen, IPPROTO_TCP);
220 }
221
222 return (tcplen);
223 }
224
225 static void
print_tcpoptions(uchar_t * opt,int optlen)226 print_tcpoptions(uchar_t *opt, int optlen)
227 {
228 int len;
229 char *line;
230
231 if (optlen <= 0) {
232 (void) sprintf(get_line((char *)&opt - dlc_header, 1),
233 "No options");
234 return;
235 }
236
237 (void) sprintf(get_line((char *)&opt - dlc_header, 1),
238 "Options: (%d bytes)", optlen);
239
240 while (optlen > 0) {
241 line = get_line((char *)&opt - dlc_header, 1);
242 len = opt[1];
243 switch (opt[0]) {
244 case TCPOPT_EOL:
245 (void) strcpy(line, " - End of option list");
246 return;
247 case TCPOPT_NOP:
248 (void) strcpy(line, " - No operation");
249 len = 1;
250 break;
251 case TCPOPT_MAXSEG:
252 (void) sprintf(line,
253 " - Maximum segment size = %d bytes",
254 (opt[2] << 8) + opt[3]);
255 break;
256 case TCPOPT_WSCALE:
257 (void) sprintf(line, " - Window scale = %d", opt[2]);
258 break;
259 case TCPOPT_TSTAMP:
260 /* Sanity check. */
261 if (optlen < TCPOPT_TSTAMP_LEN) {
262 (void) sprintf(line,
263 " - Incomplete TS option");
264 } else {
265 (void) sprintf(line,
266 " - TS Val = %u, TS Echo = %u",
267 GET_UINT32(opt + 2),
268 GET_UINT32(opt + 6));
269 }
270 break;
271 case TCPOPT_SACK_PERMITTED:
272 (void) sprintf(line, " - SACK permitted option");
273 break;
274 case TCPOPT_SACK: {
275 uchar_t *sack_opt, *end_opt;
276 int sack_len;
277
278 /*
279 * Sanity check. Total length should be greater
280 * than just the option header length.
281 */
282 if (optlen <= TCPOPT_HEADER_LEN ||
283 len < TCPOPT_HEADER_LEN) {
284 (void) sprintf(line,
285 " - Incomplete SACK option");
286 break;
287 }
288 sack_len = len - TCPOPT_HEADER_LEN;
289 sack_opt = opt + TCPOPT_HEADER_LEN;
290 end_opt = opt + optlen;
291
292 (void) sprintf(line, " - SACK blocks:");
293 line = get_line((char *)&opt - dlc_header, 1);
294 (void) sprintf(line, " ");
295 while (sack_len > 0) {
296 char sack_blk[MAXLINE + 1];
297
298 /*
299 * sack_len may not tell us the truth about
300 * the real length... Need to be careful
301 * not to step beyond the option buffer.
302 */
303 if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
304 (void) strcat(line,
305 "...incomplete SACK block");
306 break;
307 }
308 (void) sprintf(sack_blk, "(%u-%u) ",
309 GET_UINT32(sack_opt),
310 GET_UINT32(sack_opt + 4));
311 (void) strcat(line, sack_blk);
312 sack_opt += TCPOPT_SACK_LEN;
313 sack_len -= TCPOPT_SACK_LEN;
314 }
315 break;
316 }
317 case TCPOPT_MD5: {
318 uint_t i;
319
320 if (optlen < TCPOPT_MD5_LEN || len != TCPOPT_MD5_LEN) {
321 (void) sprintf(line,
322 " - Incomplete MD5 option");
323 break;
324 }
325
326 (void) sprintf(line, " - TCP MD5 Signature = 0x");
327 for (i = 2; i < len; i++) {
328 char options[3];
329
330 (void) sprintf(options, "%02x", opt[i]);
331 (void) strcat(line, options);
332 }
333 break;
334 }
335 default:
336 (void) sprintf(line,
337 " - Option %d (unknown - %d bytes) %s",
338 opt[0], len - 2, tohex((char *)&opt[2], len - 2));
339 break;
340 }
341 if (len <= 0) {
342 (void) sprintf(line, " - Incomplete option len %d",
343 len);
344 break;
345 }
346 opt += len;
347 optlen -= len;
348 }
349 }
350
351 /*
352 * This function is basically the same as print_tcpoptions() except that
353 * all options are printed on the same line.
354 */
355 static void
print_tcpoptions_summary(uchar_t * opt,int optlen,char * line)356 print_tcpoptions_summary(uchar_t *opt, int optlen, char *line)
357 {
358 int len;
359 char options[MAXLINE + 1];
360
361 if (optlen <= 0) {
362 return;
363 }
364
365 (void) strcat(line, " Options=<");
366 while (optlen > 0) {
367 len = opt[1];
368 switch (opt[0]) {
369 case TCPOPT_EOL:
370 (void) strcat(line, "eol>");
371 return;
372 case TCPOPT_NOP:
373 (void) strcat(line, "nop");
374 len = 1;
375 break;
376 case TCPOPT_MAXSEG:
377 (void) sprintf(options, "mss %d",
378 (opt[2] << 8) + opt[3]);
379 (void) strcat(line, options);
380 break;
381 case TCPOPT_WSCALE:
382 (void) sprintf(options, "wscale %d", opt[2]);
383 (void) strcat(line, options);
384 break;
385 case TCPOPT_TSTAMP:
386 /* Sanity check. */
387 if (optlen < TCPOPT_TSTAMP_LEN) {
388 (void) strcat(line, "tstamp|");
389 } else {
390 (void) sprintf(options,
391 "tstamp %u %u", GET_UINT32(opt + 2),
392 GET_UINT32(opt + 6));
393 (void) strcat(line, options);
394 }
395 break;
396 case TCPOPT_SACK_PERMITTED:
397 (void) strcat(line, "sackOK");
398 break;
399 case TCPOPT_SACK: {
400 uchar_t *sack_opt, *end_opt;
401 int sack_len;
402
403 /*
404 * Sanity check. Total length should be greater
405 * than just the option header length.
406 */
407 if (optlen <= TCPOPT_HEADER_LEN ||
408 len < TCPOPT_HEADER_LEN) {
409 (void) strcat(line, "sack|");
410 break;
411 }
412 sack_len = len - TCPOPT_HEADER_LEN;
413 sack_opt = opt + TCPOPT_HEADER_LEN;
414 end_opt = opt + optlen;
415
416 (void) strcat(line, "sack");
417 while (sack_len > 0) {
418 /*
419 * sack_len may not tell us the truth about
420 * the real length... Need to be careful
421 * not to step beyond the option buffer.
422 */
423 if (sack_opt + TCPOPT_SACK_LEN > end_opt) {
424 (void) strcat(line, "|");
425 break;
426 }
427 (void) sprintf(options, " %u-%u",
428 GET_UINT32(sack_opt),
429 GET_UINT32(sack_opt + 4));
430 (void) strcat(line, options);
431 sack_opt += TCPOPT_SACK_LEN;
432 sack_len -= TCPOPT_SACK_LEN;
433 }
434 break;
435 }
436 case TCPOPT_MD5: {
437 uint_t i;
438
439 if (optlen < TCPOPT_MD5_LEN || len != TCPOPT_MD5_LEN) {
440 (void) strcat(line, "md5|");
441 break;
442 }
443
444 (void) strcat(line, "md5 0x");
445 for (i = 2; i < len; i++) {
446 (void) sprintf(options, "%02x", opt[i]);
447 (void) strcat(line, options);
448 }
449 break;
450 }
451 default:
452 (void) sprintf(options, "unknown %d", opt[0]);
453 (void) strcat(line, options);
454 break;
455 }
456 if (len <= 0) {
457 (void) sprintf(options, "optlen %d", len);
458 (void) strcat(line, options);
459 break;
460 }
461 opt += len;
462 optlen -= len;
463 if (optlen > 0) {
464 (void) strcat(line, ",");
465 }
466 }
467 (void) strcat(line, ">");
468 }
469