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