xref: /titanic_50/usr/src/cmd/mdb/common/modules/ip/ip.c (revision fa9e4066f08beec538e775443c5be79dd423fcab)
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 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/stropts.h>
31 #include <sys/stream.h>
32 #include <sys/socket.h>
33 #include <sys/avl_impl.h>
34 #include <net/if.h>
35 #include <net/route.h>
36 #include <netinet/in.h>
37 #include <netinet/ip6.h>
38 #include <netinet/udp.h>
39 #include <netinet/sctp.h>
40 #include <inet/mib2.h>
41 #include <inet/common.h>
42 #include <inet/ip.h>
43 #include <inet/ip_ire.h>
44 #include <inet/ip6.h>
45 #include <inet/ipclassifier.h>
46 #include <inet/mi.h>
47 #include <sys/squeue_impl.h>
48 
49 #include <mdb/mdb_modapi.h>
50 #include <mdb/mdb_ks.h>
51 
52 #define	ADDR_WIDTH 11
53 
54 typedef struct {
55 	const char *bit_name;	/* name of bit */
56 	const char *bit_descr;	/* description of bit's purpose */
57 } bitname_t;
58 
59 static const bitname_t squeue_states[] = {
60 	{ "SQS_PROC",		"being processed" },
61 	{ "SQS_WORKER",		"... by a worker thread" },
62 	{ "SQS_ENTER",		"... by an squeue_enter() thread" },
63 	{ "SQS_FAST",		"... in fast-path mode" },
64 	{ "SQS_USER", 		"A non interrupt user" },
65 	{ "SQS_BOUND",		"worker thread bound to CPU" },
66 	{ "SQS_PROFILE",	"profiling enabled" },
67 	{ "SQS_REENTER",	"re-entered thred" },
68 	{ NULL }
69 };
70 
71 typedef struct illif_walk_data {
72 	ill_g_head_t ill_g_heads[MAX_G_HEADS];
73 	int ill_list;
74 	ill_if_t ill_if;
75 } illif_walk_data_t;
76 
77 static int iphdr(uintptr_t, uint_t, int, const mdb_arg_t *);
78 static int ip6hdr(uintptr_t, uint_t, int, const mdb_arg_t *);
79 
80 int
81 illif_walk_init(mdb_walk_state_t *wsp)
82 {
83 	illif_walk_data_t *iw;
84 
85 	if (wsp->walk_addr != NULL) {
86 		mdb_warn("illif supports only global walks\n");
87 		return (WALK_ERR);
88 	}
89 
90 	iw = mdb_alloc(sizeof (illif_walk_data_t), UM_SLEEP);
91 
92 	if (mdb_readsym(iw->ill_g_heads, MAX_G_HEADS * sizeof (ill_g_head_t),
93 	    "ill_g_heads") == -1) {
94 		mdb_warn("failed to read 'ill_g_heads'");
95 		mdb_free(iw, sizeof (illif_walk_data_t));
96 		return (WALK_ERR);
97 	}
98 
99 	iw->ill_list = 0;
100 	wsp->walk_addr = (uintptr_t)iw->IP_VX_ILL_G_LIST(0);
101 	wsp->walk_data = iw;
102 
103 	return (WALK_NEXT);
104 }
105 
106 int
107 illif_walk_step(mdb_walk_state_t *wsp)
108 {
109 	uintptr_t addr = wsp->walk_addr;
110 	illif_walk_data_t *iw = wsp->walk_data;
111 	int list = iw->ill_list;
112 
113 	if (mdb_vread(&iw->ill_if, sizeof (ill_if_t), addr) == -1) {
114 		mdb_warn("failed to read ill_if_t at %p", addr);
115 		return (WALK_ERR);
116 	}
117 
118 	wsp->walk_addr = (uintptr_t)iw->ill_if.illif_next;
119 
120 	if (wsp->walk_addr == (uintptr_t)iw->IP_VX_ILL_G_LIST(list)) {
121 
122 		if (++list >= MAX_G_HEADS)
123 			return (WALK_DONE);
124 
125 		iw->ill_list = list;
126 		wsp->walk_addr = (uintptr_t)iw->IP_VX_ILL_G_LIST(list);
127 		return (WALK_NEXT);
128 	}
129 
130 	return (wsp->walk_callback(addr, iw, wsp->walk_cbdata));
131 }
132 
133 void
134 illif_walk_fini(mdb_walk_state_t *wsp)
135 {
136 	mdb_free(wsp->walk_data, sizeof (illif_walk_data_t));
137 }
138 
139 typedef struct illif_cbdata {
140 	uint_t ill_flags;
141 	uintptr_t ill_addr;
142 	int ill_printlist;	/* list to be printed (MAX_G_HEADS for all) */
143 	boolean_t ill_printed;
144 } illif_cbdata_t;
145 
146 static int
147 illif_cb(uintptr_t addr, const illif_walk_data_t *iw, illif_cbdata_t *id)
148 {
149 	const char *version;
150 
151 	if (id->ill_printlist < MAX_G_HEADS &&
152 	    id->ill_printlist != iw->ill_list)
153 		return (WALK_NEXT);
154 
155 	if (id->ill_flags & DCMD_ADDRSPEC && id->ill_addr != addr)
156 		return (WALK_NEXT);
157 
158 	if (id->ill_flags & DCMD_PIPE_OUT) {
159 		mdb_printf("%p\n", addr);
160 		return (WALK_NEXT);
161 	}
162 
163 	switch (iw->ill_list) {
164 		case IP_V4_G_HEAD:	version = "v4";	break;
165 		case IP_V6_G_HEAD:	version = "v6";	break;
166 		default:		version = "??"; break;
167 	}
168 
169 	mdb_printf("%?p %2s %?p %10d %?p %s\n",
170 	    addr, version, addr + offsetof(ill_if_t, illif_avl_by_ppa),
171 	    iw->ill_if.illif_avl_by_ppa.avl_numnodes,
172 	    iw->ill_if.illif_ppa_arena, iw->ill_if.illif_name);
173 
174 	id->ill_printed = TRUE;
175 
176 	return (WALK_NEXT);
177 }
178 
179 int
180 illif(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
181 {
182 	illif_cbdata_t id;
183 	ill_if_t ill_if;
184 	const char *opt_P = NULL;
185 	int printlist = MAX_G_HEADS;
186 
187 	if (mdb_getopts(argc, argv,
188 	    'P', MDB_OPT_STR, &opt_P, NULL) != argc)
189 		return (DCMD_USAGE);
190 
191 	if (opt_P != NULL) {
192 		if (strcmp("v4", opt_P) == 0) {
193 			printlist = IP_V4_G_HEAD;
194 		} else if (strcmp("v6", opt_P) == 0) {
195 			printlist = IP_V6_G_HEAD;
196 		} else {
197 			mdb_warn("invalid protocol '%s'\n", opt_P);
198 			return (DCMD_USAGE);
199 		}
200 	}
201 
202 	if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
203 		mdb_printf("%<u>%?s %2s %?s %10s %?s %-10s%</u>\n",
204 		    "ADDR", "IP", "AVLADDR", "NUMNODES", "ARENA", "NAME");
205 	}
206 
207 	id.ill_flags = flags;
208 	id.ill_addr = addr;
209 	id.ill_printlist = printlist;
210 	id.ill_printed = FALSE;
211 
212 	if (mdb_walk("illif", (mdb_walk_cb_t)illif_cb, &id) == -1) {
213 		mdb_warn("can't walk ill_if_t structures");
214 		return (DCMD_ERR);
215 	}
216 
217 	if (!(flags & DCMD_ADDRSPEC) || opt_P != NULL || id.ill_printed)
218 		return (DCMD_OK);
219 
220 	/*
221 	 * If an address is specified and the walk doesn't find it,
222 	 * print it anyway.
223 	 */
224 	if (mdb_vread(&ill_if, sizeof (ill_if_t), addr) == -1) {
225 		mdb_warn("failed to read ill_if_t at %p", addr);
226 		return (DCMD_ERR);
227 	}
228 
229 	mdb_printf("%?p %2s %?p %10d %?p %s\n",
230 	    addr, "??", addr + offsetof(ill_if_t, illif_avl_by_ppa),
231 	    ill_if.illif_avl_by_ppa.avl_numnodes,
232 	    ill_if.illif_ppa_arena, ill_if.illif_name);
233 
234 	return (DCMD_OK);
235 }
236 
237 static void
238 illif_help(void)
239 {
240 	mdb_printf("Options:\n");
241 	mdb_printf("\t-P v4 | v6"
242 	    "\tfilter interface structures for the specified protocol\n");
243 }
244 
245 int
246 ire_walk_init(mdb_walk_state_t *wsp)
247 {
248 	if (mdb_layered_walk("ire_cache", wsp) == -1) {
249 		mdb_warn("can't walk 'ire_cache'");
250 		return (WALK_ERR);
251 	}
252 
253 	return (WALK_NEXT);
254 }
255 
256 int
257 ire_walk_step(mdb_walk_state_t *wsp)
258 {
259 	ire_t ire;
260 
261 	if (mdb_vread(&ire, sizeof (ire), wsp->walk_addr) == -1) {
262 		mdb_warn("can't read ire at %p", wsp->walk_addr);
263 		return (WALK_ERR);
264 	}
265 
266 	return (wsp->walk_callback(wsp->walk_addr, &ire, wsp->walk_cbdata));
267 }
268 
269 static int
270 ire_format(uintptr_t addr, const ire_t *irep, uint_t *verbose)
271 {
272 	static const mdb_bitmask_t tmasks[] = {
273 		{ "BROADCAST",	IRE_BROADCAST,		IRE_BROADCAST	},
274 		{ "DEFAULT",	IRE_DEFAULT,		IRE_DEFAULT	},
275 		{ "LOCAL",	IRE_LOCAL,		IRE_LOCAL	},
276 		{ "LOOPBACK",	IRE_LOOPBACK,		IRE_LOOPBACK	},
277 		{ "PREFIX",	IRE_PREFIX,		IRE_PREFIX	},
278 		{ "CACHE",	IRE_CACHE,		IRE_CACHE	},
279 		{ "IF_NORESOLVER", IRE_IF_NORESOLVER,	IRE_IF_NORESOLVER },
280 		{ "IF_RESOLVER", IRE_IF_RESOLVER,	IRE_IF_RESOLVER	},
281 		{ "HOST",	IRE_HOST,		IRE_HOST	},
282 		{ "HOST_REDIRECT", IRE_HOST_REDIRECT,	IRE_HOST_REDIRECT },
283 		{ "MIPRTUN",	IRE_MIPRTUN,		IRE_MIPRTUN	},
284 		{ NULL,		0,			0		}
285 	};
286 
287 	static const mdb_bitmask_t mmasks[] = {
288 		{ "CONDEMNED",	IRE_MARK_CONDEMNED,	IRE_MARK_CONDEMNED },
289 		{ "NORECV",	IRE_MARK_NORECV,	IRE_MARK_NORECV	},
290 		{ "HIDDEN",	IRE_MARK_HIDDEN,	IRE_MARK_HIDDEN	},
291 		{ "NOADD",	IRE_MARK_NOADD,		IRE_MARK_NOADD	},
292 		{ "TEMPORARY",	IRE_MARK_TEMPORARY,	IRE_MARK_TEMPORARY },
293 		{ NULL,		0,			0		}
294 	};
295 
296 	static const mdb_bitmask_t fmasks[] = {
297 		{ "UP",		RTF_UP,			RTF_UP		},
298 		{ "GATEWAY",	RTF_GATEWAY,		RTF_GATEWAY	},
299 		{ "HOST",	RTF_HOST,		RTF_HOST	},
300 		{ "REJECT",	RTF_REJECT,		RTF_REJECT	},
301 		{ "DYNAMIC",	RTF_DYNAMIC,		RTF_DYNAMIC	},
302 		{ "MODIFIED",	RTF_MODIFIED,		RTF_MODIFIED	},
303 		{ "DONE",	RTF_DONE,		RTF_DONE	},
304 		{ "MASK",	RTF_MASK,		RTF_MASK	},
305 		{ "CLONING",	RTF_CLONING,		RTF_CLONING	},
306 		{ "XRESOLVE",	RTF_XRESOLVE,		RTF_XRESOLVE	},
307 		{ "LLINFO",	RTF_LLINFO,		RTF_LLINFO	},
308 		{ "STATIC",	RTF_STATIC,		RTF_STATIC	},
309 		{ "BLACKHOLE",	RTF_BLACKHOLE,		RTF_BLACKHOLE	},
310 		{ "PRIVATE",	RTF_PRIVATE,		RTF_PRIVATE	},
311 		{ "PROTO2",	RTF_PROTO2,		RTF_PROTO2	},
312 		{ "PROTO1",	RTF_PROTO1,		RTF_PROTO1	},
313 		{ "MULTIRT",	RTF_MULTIRT,		RTF_MULTIRT	},
314 		{ "SETSRC",	RTF_SETSRC,		RTF_SETSRC	},
315 		{ NULL,		0,			0		}
316 	};
317 
318 	if (irep->ire_ipversion == 6 && *verbose) {
319 
320 		mdb_printf("%<b>%?p%</b> %40N <%hb>\n"
321 		    "%?s %40N <%hb>\n"
322 		    "%?s %40d <%hb>\n",
323 		    addr, &irep->ire_src_addr_v6, irep->ire_type, tmasks,
324 		    "", &irep->ire_addr_v6, (ushort_t)irep->ire_marks, mmasks,
325 		    "", irep->ire_zoneid, irep->ire_flags, fmasks);
326 
327 	} else if (irep->ire_ipversion == 6) {
328 
329 		mdb_printf("%?p %30N %30N %4d\n", addr, &irep->ire_src_addr_v6,
330 		    &irep->ire_addr_v6, irep->ire_zoneid);
331 
332 	} else if (*verbose) {
333 
334 		mdb_printf("%<b>%?p%</b> %40I <%hb>\n"
335 		    "%?s %40I <%hb>\n"
336 		    "%?s %40d <%hb>\n",
337 		    addr, irep->ire_src_addr, irep->ire_type, tmasks,
338 		    "", irep->ire_addr, (ushort_t)irep->ire_marks, mmasks,
339 		    "", irep->ire_zoneid, irep->ire_flags, fmasks);
340 
341 	} else {
342 
343 		mdb_printf("%?p %30I %30I %4d\n", addr, irep->ire_src_addr,
344 		    irep->ire_addr, irep->ire_zoneid);
345 	}
346 
347 	return (WALK_NEXT);
348 }
349 
350 /*
351  * There are faster ways to do this.  Given the interactive nature of this
352  * use I don't think its worth much effort.
353  */
354 static unsigned short
355 ipcksum(void *p, int len)
356 {
357 	int32_t	sum = 0;
358 
359 	while (len > 1) {
360 		/* alignment */
361 		sum += *(uint16_t *)p;
362 		p = (char *)p + sizeof (uint16_t);
363 		if (sum & 0x80000000)
364 			sum = (sum & 0xFFFF) + (sum >> 16);
365 		len -= 2;
366 	}
367 
368 	if (len)
369 		sum += (uint16_t)*(unsigned char *)p;
370 
371 	while (sum >> 16)
372 		sum = (sum & 0xFFFF) + (sum >> 16);
373 
374 	return (~sum);
375 }
376 
377 static const mdb_bitmask_t tcp_flags[] = {
378 	{ "SYN",	TH_SYN,		TH_SYN	},
379 	{ "ACK",	TH_ACK,		TH_ACK	},
380 	{ "FIN",	TH_FIN,		TH_FIN	},
381 	{ "RST",	TH_RST,		TH_RST	},
382 	{ "PSH",	TH_PUSH,	TH_PUSH	},
383 	{ "ECE",	TH_ECE,		TH_ECE	},
384 	{ "CWR",	TH_CWR,		TH_CWR	},
385 	{ NULL,		0,		0	}
386 };
387 
388 static void
389 tcphdr_print(struct tcphdr *tcph)
390 {
391 	in_port_t	sport, dport;
392 	tcp_seq		seq, ack;
393 	uint16_t	win, urp;
394 
395 	mdb_printf("%<b>TCP header%</b>\n");
396 
397 	mdb_nhconvert(&sport, &tcph->th_sport, sizeof (sport));
398 	mdb_nhconvert(&dport, &tcph->th_dport, sizeof (dport));
399 	mdb_nhconvert(&seq, &tcph->th_seq, sizeof (seq));
400 	mdb_nhconvert(&ack, &tcph->th_ack, sizeof (ack));
401 	mdb_nhconvert(&win, &tcph->th_win, sizeof (win));
402 	mdb_nhconvert(&urp, &tcph->th_urp, sizeof (urp));
403 
404 	mdb_printf("%<u>%6s %6s %10s %10s %4s %5s %5s %5s %-15s%</u>\n",
405 	    "SPORT", "DPORT", "SEQ", "ACK", "HLEN", "WIN", "CSUM", "URP",
406 	    "FLAGS");
407 	mdb_printf("%6hu %6hu %10u %10u %4d %5hu %5hu %5hu <%b>\n",
408 	    sport, dport, seq, ack, tcph->th_off << 2, win,
409 	    tcph->th_sum, urp, tcph->th_flags, tcp_flags);
410 	mdb_printf("0x%04x 0x%04x 0x%08x 0x%08x\n\n",
411 	    sport, dport, seq, ack);
412 }
413 
414 /* ARGSUSED */
415 static int
416 tcphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
417 {
418 	struct tcphdr	tcph;
419 
420 	if (!(flags & DCMD_ADDRSPEC))
421 		return (DCMD_USAGE);
422 
423 	if (mdb_vread(&tcph, sizeof (tcph), addr) == -1) {
424 		mdb_warn("failed to read TCP header at %p", addr);
425 		return (DCMD_ERR);
426 	}
427 	tcphdr_print(&tcph);
428 	return (DCMD_OK);
429 }
430 
431 static void
432 udphdr_print(struct udphdr *udph)
433 {
434 	in_port_t	sport, dport;
435 	uint16_t	hlen;
436 
437 	mdb_printf("%<b>UDP header%</b>\n");
438 
439 	mdb_nhconvert(&sport, &udph->uh_sport, sizeof (sport));
440 	mdb_nhconvert(&dport, &udph->uh_dport, sizeof (dport));
441 	mdb_nhconvert(&hlen, &udph->uh_ulen, sizeof (hlen));
442 
443 	mdb_printf("%<u>%14s %14s %5s %6s%</u>\n",
444 	    "SPORT", "DPORT", "LEN", "CSUM");
445 	mdb_printf("%5hu (0x%04x) %5hu (0x%04x) %5hu 0x%04hx\n\n", sport, sport,
446 	    dport, dport, hlen, udph->uh_sum);
447 }
448 
449 /* ARGSUSED */
450 static int
451 udphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
452 {
453 	struct udphdr	udph;
454 
455 	if (!(flags & DCMD_ADDRSPEC))
456 		return (DCMD_USAGE);
457 
458 	if (mdb_vread(&udph, sizeof (udph), addr) == -1) {
459 		mdb_warn("failed to read UDP header at %p", addr);
460 		return (DCMD_ERR);
461 	}
462 	udphdr_print(&udph);
463 	return (DCMD_OK);
464 }
465 
466 static void
467 sctphdr_print(sctp_hdr_t *sctph)
468 {
469 	in_port_t sport, dport;
470 
471 	mdb_printf("%<b>SCTP header%</b>\n");
472 	mdb_nhconvert(&sport, &sctph->sh_sport, sizeof (sport));
473 	mdb_nhconvert(&dport, &sctph->sh_dport, sizeof (dport));
474 
475 	mdb_printf("%<u>%14s %14s %10s %10s%</u>\n",
476 	    "SPORT", "DPORT", "VTAG", "CHKSUM");
477 	mdb_printf("%5hu (0x%04x) %5hu (0x%04x) %10u 0x%08x\n\n", sport, sport,
478 	    dport, dport, sctph->sh_verf, sctph->sh_chksum);
479 }
480 
481 /* ARGSUSED */
482 static int
483 sctphdr(uintptr_t addr, uint_t flags, int ac, const mdb_arg_t *av)
484 {
485 	sctp_hdr_t sctph;
486 
487 	if (!(flags & DCMD_ADDRSPEC))
488 		return (DCMD_USAGE);
489 
490 	if (mdb_vread(&sctph, sizeof (sctph), addr) == -1) {
491 		mdb_warn("failed to read SCTP header at %p", addr);
492 		return (DCMD_ERR);
493 	}
494 
495 	sctphdr_print(&sctph);
496 	return (DCMD_OK);
497 }
498 
499 static int
500 transport_hdr(int proto, uintptr_t addr)
501 {
502 	mdb_printf("\n");
503 	switch (proto) {
504 	case IPPROTO_TCP: {
505 		struct tcphdr tcph;
506 
507 		if (mdb_vread(&tcph, sizeof (tcph), addr) == -1) {
508 			mdb_warn("failed to read TCP header at %p", addr);
509 			return (DCMD_ERR);
510 		}
511 		tcphdr_print(&tcph);
512 		break;
513 	}
514 	case IPPROTO_UDP:  {
515 		struct udphdr udph;
516 
517 		if (mdb_vread(&udph, sizeof (udph), addr) == -1) {
518 			mdb_warn("failed to read UDP header at %p", addr);
519 			return (DCMD_ERR);
520 		}
521 		udphdr_print(&udph);
522 		break;
523 	}
524 	case IPPROTO_SCTP: {
525 		sctp_hdr_t sctph;
526 
527 		if (mdb_vread(&sctph, sizeof (sctph), addr) == -1) {
528 			mdb_warn("failed to read SCTP header at %p", addr);
529 			return (DCMD_ERR);
530 		}
531 		sctphdr_print(&sctph);
532 		break;
533 	}
534 	default:
535 		break;
536 	}
537 
538 	return (DCMD_OK);
539 }
540 
541 static const mdb_bitmask_t ip_flags[] = {
542 	{ "DF",	IPH_DF, IPH_DF	},
543 	{ "MF", IPH_MF,	IPH_MF	},
544 	{ NULL, 0,	0	}
545 };
546 
547 /* ARGSUSED */
548 static int
549 iphdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
550 {
551 	uint_t		verbose = FALSE, force = FALSE;
552 	ipha_t		iph[1];
553 	uint16_t	ver, totlen, hdrlen, ipid, off, csum;
554 	uintptr_t	nxt_proto;
555 	char		exp_csum[8];
556 
557 	if (mdb_getopts(argc, argv,
558 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
559 	    'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc)
560 		return (DCMD_USAGE);
561 
562 	if (mdb_vread(iph, sizeof (*iph), addr) == -1) {
563 		mdb_warn("failed to read IPv4 header at %p", addr);
564 		return (DCMD_ERR);
565 	}
566 
567 	ver = (iph->ipha_version_and_hdr_length & 0xf0) >> 4;
568 	if (ver != IPV4_VERSION) {
569 		if (ver == IPV6_VERSION) {
570 			return (ip6hdr(addr, flags, argc, argv));
571 		} else if (!force) {
572 			mdb_warn("unknown IP version: %d\n", ver);
573 			return (DCMD_ERR);
574 		}
575 	}
576 
577 	mdb_printf("%<b>IPv4 header%</b>\n");
578 	mdb_printf("%-34s %-34s\n"
579 	    "%<u>%-4s %-4s %-5s %-5s %-6s %-5s %-5s %-6s %-8s %-6s%</u>\n",
580 	    "SRC", "DST",
581 	    "HLEN", "TOS", "LEN", "ID", "OFFSET", "TTL", "PROTO", "CHKSUM",
582 	    "EXP-CSUM", "FLGS");
583 
584 	hdrlen = (iph->ipha_version_and_hdr_length & 0x0f) << 2;
585 	mdb_nhconvert(&totlen, &iph->ipha_length, sizeof (totlen));
586 	mdb_nhconvert(&ipid, &iph->ipha_ident, sizeof (ipid));
587 	mdb_nhconvert(&off, &iph->ipha_fragment_offset_and_flags, sizeof (off));
588 	if (hdrlen == IP_SIMPLE_HDR_LENGTH) {
589 		if ((csum = ipcksum(iph, sizeof (*iph))) != 0)
590 			csum = ~(~csum + ~iph->ipha_hdr_checksum);
591 		else
592 			csum = iph->ipha_hdr_checksum;
593 		mdb_snprintf(exp_csum, 8, "%u", csum);
594 	} else {
595 		mdb_snprintf(exp_csum, 8, "<n/a>");
596 	}
597 
598 	mdb_printf("%-34I %-34I%\n"
599 	    "%-4d %-4d %-5hu %-5hu %-6hu %-5hu %-5hu %-6u %-8s <%5hb>\n",
600 	    iph->ipha_src, iph->ipha_dst,
601 	    hdrlen, iph->ipha_type_of_service, totlen, ipid,
602 	    (off << 3) & 0xffff, iph->ipha_ttl, iph->ipha_protocol,
603 	    iph->ipha_hdr_checksum, exp_csum, off, ip_flags);
604 
605 	if (verbose) {
606 		nxt_proto = addr + hdrlen;
607 		return (transport_hdr(iph->ipha_protocol, nxt_proto));
608 	} else {
609 		return (DCMD_OK);
610 	}
611 }
612 
613 /* ARGSUSED */
614 static int
615 ip6hdr(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
616 {
617 	uint_t		verbose = FALSE, force = FALSE;
618 	ip6_t		iph[1];
619 	int		ver, class, flow;
620 	uint16_t	plen;
621 	uintptr_t	nxt_proto;
622 
623 	if (mdb_getopts(argc, argv,
624 	    'v', MDB_OPT_SETBITS, TRUE, &verbose,
625 	    'f', MDB_OPT_SETBITS, TRUE, &force, NULL) != argc)
626 		return (DCMD_USAGE);
627 
628 	if (mdb_vread(iph, sizeof (*iph), addr) == -1) {
629 		mdb_warn("failed to read IPv6 header at %p", addr);
630 		return (DCMD_ERR);
631 	}
632 
633 	ver = (iph->ip6_vfc & 0xf0) >> 4;
634 	if (ver != IPV6_VERSION) {
635 		if (ver == IPV4_VERSION) {
636 			return (iphdr(addr, flags, argc, argv));
637 		} else if (!force) {
638 			mdb_warn("unknown IP version: %d\n", ver);
639 			return (DCMD_ERR);
640 		}
641 	}
642 
643 	mdb_printf("%<b>IPv6 header%</b>\n");
644 	mdb_printf("%<u>%-26s %-26s %4s %7s %5s %3s %3s%</u>\n",
645 	    "SRC", "DST", "TCLS", "FLOW-ID", "PLEN", "NXT", "HOP");
646 
647 	class = (iph->ip6_vcf & IPV6_FLOWINFO_TCLASS) >> 20;
648 	mdb_nhconvert(&class, &class, sizeof (class));
649 	flow = iph->ip6_vcf & IPV6_FLOWINFO_FLOWLABEL;
650 	mdb_nhconvert(&flow, &flow, sizeof (flow));
651 	mdb_nhconvert(&plen, &iph->ip6_plen, sizeof (plen));
652 
653 	mdb_printf("%-26N %-26N %4d %7d %5hu %3d %3d\n",
654 	    &iph->ip6_src, &iph->ip6_dst,
655 	    class, flow, plen, iph->ip6_nxt, iph->ip6_hlim);
656 
657 	if (verbose) {
658 		nxt_proto = addr + sizeof (ip6_t);
659 		return (transport_hdr(iph->ip6_nxt, nxt_proto));
660 	} else {
661 		return (DCMD_OK);
662 	}
663 }
664 
665 int
666 ire(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
667 {
668 	uint_t verbose = FALSE;
669 	ire_t ire;
670 
671 	if (mdb_getopts(argc, argv,
672 	    'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL) != argc)
673 		return (DCMD_USAGE);
674 
675 	if ((flags & DCMD_LOOPFIRST) || !(flags & DCMD_LOOP)) {
676 
677 		if (verbose) {
678 			mdb_printf("%?s %40s %-20s%\n"
679 			    "%?s %40s %-20s%\n"
680 			    "%<u>%?s %40s %-20s%</u>\n",
681 			    "ADDR", "SRC", "TYPE",
682 			    "", "DST", "MARKS",
683 			    "", "ZONE", "FLAGS");
684 		} else {
685 			mdb_printf("%<u>%?s %30s %30s %4s%</u>\n",
686 			    "ADDR", "SRC", "DST", "ZONE");
687 		}
688 	}
689 
690 	if (flags & DCMD_ADDRSPEC) {
691 		(void) mdb_vread(&ire, sizeof (ire_t), addr);
692 		(void) ire_format(addr, &ire, &verbose);
693 	} else if (mdb_walk("ire", (mdb_walk_cb_t)ire_format, &verbose) == -1) {
694 		mdb_warn("failed to walk ire table");
695 		return (DCMD_ERR);
696 	}
697 
698 	return (DCMD_OK);
699 }
700 
701 static size_t
702 mi_osize(const queue_t *q)
703 {
704 	/*
705 	 * The code in common/inet/mi.c allocates an extra word to store the
706 	 * size of the allocation.  An mi_o_s is thus a size_t plus an mi_o_s.
707 	 */
708 	struct mi_block {
709 		size_t mi_nbytes;
710 		struct mi_o_s mi_o;
711 	} m;
712 
713 	if (mdb_vread(&m, sizeof (m), (uintptr_t)q->q_ptr -
714 	    sizeof (m)) == sizeof (m))
715 		return (m.mi_nbytes - sizeof (m));
716 
717 	return (0);
718 }
719 
720 static void
721 ip_ill_qinfo(const queue_t *q, char *buf, size_t nbytes)
722 {
723 	char name[32];
724 	ill_t ill;
725 
726 	if (mdb_vread(&ill, sizeof (ill),
727 	    (uintptr_t)q->q_ptr) == sizeof (ill) &&
728 	    mdb_readstr(name, sizeof (name), (uintptr_t)ill.ill_name) > 0)
729 		(void) mdb_snprintf(buf, nbytes, "if: %s", name);
730 }
731 
732 void
733 ip_qinfo(const queue_t *q, char *buf, size_t nbytes)
734 {
735 	size_t size = mi_osize(q);
736 
737 	if (size == sizeof (ill_t))
738 		ip_ill_qinfo(q, buf, nbytes);
739 }
740 
741 uintptr_t
742 ip_rnext(const queue_t *q)
743 {
744 	size_t size = mi_osize(q);
745 	ill_t ill;
746 
747 	if (size == sizeof (ill_t) && mdb_vread(&ill, sizeof (ill),
748 	    (uintptr_t)q->q_ptr) == sizeof (ill))
749 		return ((uintptr_t)ill.ill_rq);
750 
751 	return (NULL);
752 }
753 
754 uintptr_t
755 ip_wnext(const queue_t *q)
756 {
757 	size_t size = mi_osize(q);
758 	ill_t ill;
759 
760 	if (size == sizeof (ill_t) && mdb_vread(&ill, sizeof (ill),
761 	    (uintptr_t)q->q_ptr) == sizeof (ill))
762 		return ((uintptr_t)ill.ill_wq);
763 
764 	return (NULL);
765 }
766 
767 /*
768  * Print the core fields in an squeue_t.  With the "-v" argument,
769  * provide more verbose output.
770  */
771 static int
772 squeue(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
773 {
774 	unsigned int	i;
775 	unsigned int	verbose = FALSE;
776 	const int	SQUEUE_STATEDELT = (int)(sizeof (uintptr_t) + 9);
777 	boolean_t	arm;
778 	squeue_t	squeue;
779 
780 	if (!(flags & DCMD_ADDRSPEC)) {
781 		if (mdb_walk_dcmd("genunix`squeue_cache", "ip`squeue",
782 		    argc, argv) == -1) {
783 			mdb_warn("failed to walk squeue cache");
784 			return (DCMD_ERR);
785 		}
786 		return (DCMD_OK);
787 	}
788 
789 	if (mdb_getopts(argc, argv, 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL)
790 	    != argc)
791 		return (DCMD_USAGE);
792 
793 	if (!DCMD_HDRSPEC(flags) && verbose)
794 		mdb_printf("\n\n");
795 
796 	if (DCMD_HDRSPEC(flags) || verbose) {
797 		mdb_printf("%?s %-5s %-3s %?s %?s %?s\n",
798 		    "ADDR", "STATE", "CPU",
799 		    "FIRST", "LAST", "WORKER");
800 	}
801 
802 	if (mdb_vread(&squeue, sizeof (squeue_t), addr) == -1) {
803 		mdb_warn("cannot read squeue_t at %p", addr);
804 		return (DCMD_ERR);
805 	}
806 
807 	mdb_printf("%0?p %05x %3d %0?p %0?p %0?p\n",
808 	    addr, squeue.sq_state, squeue.sq_bind,
809 	    squeue.sq_first, squeue.sq_last, squeue.sq_worker);
810 
811 	if (!verbose)
812 		return (DCMD_OK);
813 
814 	arm = B_TRUE;
815 	for (i = 0; squeue_states[i].bit_name != NULL; i++) {
816 		if (((squeue.sq_state) & (1 << i)) == 0)
817 			continue;
818 
819 		if (arm) {
820 			mdb_printf("%*s|\n", SQUEUE_STATEDELT, "");
821 			mdb_printf("%*s+-->  ", SQUEUE_STATEDELT, "");
822 			arm = B_FALSE;
823 		} else
824 			mdb_printf("%*s      ", SQUEUE_STATEDELT, "");
825 
826 		mdb_printf("%-12s %s\n", squeue_states[i].bit_name,
827 		    squeue_states[i].bit_descr);
828 	}
829 
830 	return (DCMD_OK);
831 }
832 
833 static void
834 ip_squeue_help(void)
835 {
836 	mdb_printf("Print the core information for a given NCA squeue_t.\n\n");
837 	mdb_printf("Options:\n");
838 	mdb_printf("\t-v\tbe verbose (more descriptive)\n");
839 }
840 
841 static const mdb_dcmd_t dcmds[] = {
842 	{ "illif", "?[-P v4 | v6]",
843 	    "display or filter IP Lower Level InterFace structures", illif,
844 	    illif_help },
845 	{ "iphdr", ":[-vf]", "display an IPv4 header", iphdr },
846 	{ "ip6hdr", ":[-vf]", "display an IPv6 header", ip6hdr },
847 	{ "ire", "?[-v]", "display Internet Route Entry structures", ire },
848 	{ "squeue", ":[-v]", "print core squeue_t info", squeue,
849 	    ip_squeue_help },
850 	{ "tcphdr", ":", "display a TCP header", tcphdr },
851 	{ "udphdr", ":", "display an UDP header", udphdr },
852 	{ "sctphdr", ":", "display an SCTP header", sctphdr },
853 	{ NULL }
854 };
855 
856 static const mdb_walker_t walkers[] = {
857 	{ "illif", "walk list of ill interface types",
858 		illif_walk_init, illif_walk_step, illif_walk_fini },
859 	{ "ire", "walk active ire_t structures",
860 		ire_walk_init, ire_walk_step, NULL },
861 	{ NULL }
862 };
863 
864 static const mdb_qops_t ip_qops = { ip_qinfo, ip_rnext, ip_wnext };
865 static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers };
866 
867 const mdb_modinfo_t *
868 _mdb_init(void)
869 {
870 	GElf_Sym sym;
871 
872 	if (mdb_lookup_by_obj("ip", "winit", &sym) == 0)
873 		mdb_qops_install(&ip_qops, (uintptr_t)sym.st_value);
874 
875 	return (&modinfo);
876 }
877 
878 void
879 _mdb_fini(void)
880 {
881 	GElf_Sym sym;
882 
883 	if (mdb_lookup_by_obj("ip", "winit", &sym) == 0)
884 		mdb_qops_remove(&ip_qops, (uintptr_t)sym.st_value);
885 }
886