1/*
2 * Copyright (c) 1988, 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that: (1) source code distributions
7 * retain the above copyright notice and this paragraph in its entirety, (2)
8 * distributions including binary code include the above copyright notice and
9 * this paragraph in its entirety in the documentation or other materials
10 * provided with the distribution, and (3) all advertising materials mentioning
11 * features or use of this software display the following acknowledgement:
12 * ``This product includes software developed by the University of California,
13 * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
14 * the University nor the names of its contributors may be used to endorse
15 * or promote products derived from this software without specific prior
16 * written permission.
17 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 */
21
22/* \summary: IP printer */
23
24#ifdef HAVE_CONFIG_H
25#include "config.h"
26#endif
27
28#include <netdissect-stdinc.h>
29
30#include <string.h>
31
32#include "netdissect.h"
33#include "addrtoname.h"
34#include "extract.h"
35
36#include "ip.h"
37#include "ipproto.h"
38
39static const char tstr[] = "[|ip]";
40
41static const struct tok ip_option_values[] = {
42    { IPOPT_EOL, "EOL" },
43    { IPOPT_NOP, "NOP" },
44    { IPOPT_TS, "timestamp" },
45    { IPOPT_SECURITY, "security" },
46    { IPOPT_RR, "RR" },
47    { IPOPT_SSRR, "SSRR" },
48    { IPOPT_LSRR, "LSRR" },
49    { IPOPT_RA, "RA" },
50    { IPOPT_RFC1393, "traceroute" },
51    { 0, NULL }
52};
53
54/*
55 * print the recorded route in an IP RR, LSRR or SSRR option.
56 */
57static int
58ip_printroute(netdissect_options *ndo,
59              register const u_char *cp, u_int length)
60{
61	register u_int ptr;
62	register u_int len;
63
64	if (length < 3) {
65		ND_PRINT((ndo, " [bad length %u]", length));
66		return (0);
67	}
68	if ((length + 1) & 3)
69		ND_PRINT((ndo, " [bad length %u]", length));
70	ND_TCHECK(cp[2]);
71	ptr = cp[2] - 1;
72	if (ptr < 3 || ((ptr + 1) & 3) || ptr > length + 1)
73		ND_PRINT((ndo, " [bad ptr %u]", cp[2]));
74
75	for (len = 3; len < length; len += 4) {
76		ND_TCHECK2(cp[len], 4);
77		ND_PRINT((ndo, " %s", ipaddr_string(ndo, &cp[len])));
78		if (ptr > len)
79			ND_PRINT((ndo, ","));
80	}
81	return (0);
82
83trunc:
84	return (-1);
85}
86
87/*
88 * If source-routing is present and valid, return the final destination.
89 * Otherwise, return IP destination.
90 *
91 * This is used for UDP and TCP pseudo-header in the checksum
92 * calculation.
93 */
94static uint32_t
95ip_finddst(netdissect_options *ndo,
96           const struct ip *ip)
97{
98	int length;
99	int len;
100	const u_char *cp;
101	uint32_t retval;
102
103	cp = (const u_char *)(ip + 1);
104	length = (IP_HL(ip) << 2) - sizeof(struct ip);
105
106	for (; length > 0; cp += len, length -= len) {
107		int tt;
108
109		ND_TCHECK(*cp);
110		tt = *cp;
111		if (tt == IPOPT_EOL)
112			break;
113		else if (tt == IPOPT_NOP)
114			len = 1;
115		else {
116			ND_TCHECK(cp[1]);
117			len = cp[1];
118			if (len < 2)
119				break;
120		}
121		ND_TCHECK2(*cp, len);
122		switch (tt) {
123
124		case IPOPT_SSRR:
125		case IPOPT_LSRR:
126			if (len < 7)
127				break;
128			UNALIGNED_MEMCPY(&retval, cp + len - 4, 4);
129			return retval;
130		}
131	}
132trunc:
133	UNALIGNED_MEMCPY(&retval, &ip->ip_dst, sizeof(uint32_t));
134	return retval;
135}
136
137/*
138 * Compute a V4-style checksum by building a pseudoheader.
139 */
140int
141nextproto4_cksum(netdissect_options *ndo,
142                 const struct ip *ip, const uint8_t *data,
143                 u_int len, u_int covlen, u_int next_proto)
144{
145	struct phdr {
146		uint32_t src;
147		uint32_t dst;
148		u_char mbz;
149		u_char proto;
150		uint16_t len;
151	} ph;
152	struct cksum_vec vec[2];
153
154	/* pseudo-header.. */
155	ph.len = htons((uint16_t)len);
156	ph.mbz = 0;
157	ph.proto = next_proto;
158	UNALIGNED_MEMCPY(&ph.src, &ip->ip_src, sizeof(uint32_t));
159	if (IP_HL(ip) == 5)
160		UNALIGNED_MEMCPY(&ph.dst, &ip->ip_dst, sizeof(uint32_t));
161	else
162		ph.dst = ip_finddst(ndo, ip);
163
164	vec[0].ptr = (const uint8_t *)(void *)&ph;
165	vec[0].len = sizeof(ph);
166	vec[1].ptr = data;
167	vec[1].len = covlen;
168	return (in_cksum(vec, 2));
169}
170
171static int
172ip_printts(netdissect_options *ndo,
173           register const u_char *cp, u_int length)
174{
175	register u_int ptr;
176	register u_int len;
177	int hoplen;
178	const char *type;
179
180	if (length < 4) {
181		ND_PRINT((ndo, "[bad length %u]", length));
182		return (0);
183	}
184	ND_PRINT((ndo, " TS{"));
185	hoplen = ((cp[3]&0xF) != IPOPT_TS_TSONLY) ? 8 : 4;
186	if ((length - 4) & (hoplen-1))
187		ND_PRINT((ndo, "[bad length %u]", length));
188	ND_TCHECK(cp[2]);
189	ptr = cp[2] - 1;
190	len = 0;
191	if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1)
192		ND_PRINT((ndo, "[bad ptr %u]", cp[2]));
193	ND_TCHECK(cp[3]);
194	switch (cp[3]&0xF) {
195	case IPOPT_TS_TSONLY:
196		ND_PRINT((ndo, "TSONLY"));
197		break;
198	case IPOPT_TS_TSANDADDR:
199		ND_PRINT((ndo, "TS+ADDR"));
200		break;
201	/*
202	 * prespecified should really be 3, but some ones might send 2
203	 * instead, and the IPOPT_TS_PRESPEC constant can apparently
204	 * have both values, so we have to hard-code it here.
205	 */
206
207	case 2:
208		ND_PRINT((ndo, "PRESPEC2.0"));
209		break;
210	case 3:			/* IPOPT_TS_PRESPEC */
211		ND_PRINT((ndo, "PRESPEC"));
212		break;
213	default:
214		ND_PRINT((ndo, "[bad ts type %d]", cp[3]&0xF));
215		goto done;
216	}
217
218	type = " ";
219	for (len = 4; len < length; len += hoplen) {
220		if (ptr == len)
221			type = " ^ ";
222		ND_TCHECK2(cp[len], hoplen);
223		ND_PRINT((ndo, "%s%d@%s", type, EXTRACT_32BITS(&cp[len+hoplen-4]),
224		       hoplen!=8 ? "" : ipaddr_string(ndo, &cp[len])));
225		type = " ";
226	}
227
228done:
229	ND_PRINT((ndo, "%s", ptr == len ? " ^ " : ""));
230
231	if (cp[3]>>4)
232		ND_PRINT((ndo, " [%d hops not recorded]} ", cp[3]>>4));
233	else
234		ND_PRINT((ndo, "}"));
235	return (0);
236
237trunc:
238	return (-1);
239}
240
241/*
242 * print IP options.
243 */
244static void
245ip_optprint(netdissect_options *ndo,
246            register const u_char *cp, u_int length)
247{
248	register u_int option_len;
249	const char *sep = "";
250
251	for (; length > 0; cp += option_len, length -= option_len) {
252		u_int option_code;
253
254		ND_PRINT((ndo, "%s", sep));
255		sep = ",";
256
257		ND_TCHECK(*cp);
258		option_code = *cp;
259
260		ND_PRINT((ndo, "%s",
261		          tok2str(ip_option_values,"unknown %u",option_code)));
262
263		if (option_code == IPOPT_NOP ||
264                    option_code == IPOPT_EOL)
265			option_len = 1;
266
267		else {
268			ND_TCHECK(cp[1]);
269			option_len = cp[1];
270			if (option_len < 2) {
271				ND_PRINT((ndo, " [bad length %u]", option_len));
272				return;
273			}
274		}
275
276		if (option_len > length) {
277			ND_PRINT((ndo, " [bad length %u]", option_len));
278			return;
279		}
280
281		ND_TCHECK2(*cp, option_len);
282
283		switch (option_code) {
284		case IPOPT_EOL:
285			return;
286
287		case IPOPT_TS:
288			if (ip_printts(ndo, cp, option_len) == -1)
289				goto trunc;
290			break;
291
292		case IPOPT_RR:       /* fall through */
293		case IPOPT_SSRR:
294		case IPOPT_LSRR:
295			if (ip_printroute(ndo, cp, option_len) == -1)
296				goto trunc;
297			break;
298
299		case IPOPT_RA:
300			if (option_len < 4) {
301				ND_PRINT((ndo, " [bad length %u]", option_len));
302				break;
303			}
304			ND_TCHECK(cp[3]);
305			if (EXTRACT_16BITS(&cp[2]) != 0)
306				ND_PRINT((ndo, " value %u", EXTRACT_16BITS(&cp[2])));
307			break;
308
309		case IPOPT_NOP:       /* nothing to print - fall through */
310		case IPOPT_SECURITY:
311		default:
312			break;
313		}
314	}
315	return;
316
317trunc:
318	ND_PRINT((ndo, "%s", tstr));
319}
320
321#define IP_RES 0x8000
322
323static const struct tok ip_frag_values[] = {
324        { IP_MF,        "+" },
325        { IP_DF,        "DF" },
326	{ IP_RES,       "rsvd" }, /* The RFC3514 evil ;-) bit */
327        { 0,            NULL }
328};
329
330struct ip_print_demux_state {
331	const struct ip *ip;
332	const u_char *cp;
333	u_int   len, off;
334	u_char  nh;
335	int     advance;
336};
337
338static void
339ip_print_demux(netdissect_options *ndo,
340	       struct ip_print_demux_state *ipds)
341{
342	const char *p_name;
343
344again:
345	switch (ipds->nh) {
346
347	case IPPROTO_AH:
348		if (!ND_TTEST(*ipds->cp)) {
349			ND_PRINT((ndo, "[|AH]"));
350			break;
351		}
352		ipds->nh = *ipds->cp;
353		ipds->advance = ah_print(ndo, ipds->cp);
354		if (ipds->advance <= 0)
355			break;
356		ipds->cp += ipds->advance;
357		ipds->len -= ipds->advance;
358		goto again;
359
360	case IPPROTO_ESP:
361	{
362		int enh, padlen;
363		ipds->advance = esp_print(ndo, ipds->cp, ipds->len,
364				    (const u_char *)ipds->ip,
365				    &enh, &padlen);
366		if (ipds->advance <= 0)
367			break;
368		ipds->cp += ipds->advance;
369		ipds->len -= ipds->advance + padlen;
370		ipds->nh = enh & 0xff;
371		goto again;
372	}
373
374	case IPPROTO_IPCOMP:
375	{
376		ipcomp_print(ndo, ipds->cp);
377		/*
378		 * Either this has decompressed the payload and
379		 * printed it, in which case there's nothing more
380		 * to do, or it hasn't, in which case there's
381		 * nothing more to do.
382		 */
383		break;
384	}
385
386	case IPPROTO_SCTP:
387		sctp_print(ndo, ipds->cp, (const u_char *)ipds->ip, ipds->len);
388		break;
389
390	case IPPROTO_DCCP:
391		dccp_print(ndo, ipds->cp, (const u_char *)ipds->ip, ipds->len);
392		break;
393
394	case IPPROTO_TCP:
395		/* pass on the MF bit plus the offset to detect fragments */
396		tcp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip,
397			  ipds->off & (IP_MF|IP_OFFMASK));
398		break;
399
400	case IPPROTO_UDP:
401		/* pass on the MF bit plus the offset to detect fragments */
402		udp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip,
403			  ipds->off & (IP_MF|IP_OFFMASK));
404		break;
405
406	case IPPROTO_ICMP:
407		/* pass on the MF bit plus the offset to detect fragments */
408		icmp_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip,
409			   ipds->off & (IP_MF|IP_OFFMASK));
410		break;
411
412	case IPPROTO_PIGP:
413		/*
414		 * XXX - the current IANA protocol number assignments
415		 * page lists 9 as "any private interior gateway
416		 * (used by Cisco for their IGRP)" and 88 as
417		 * "EIGRP" from Cisco.
418		 *
419		 * Recent BSD <netinet/in.h> headers define
420		 * IP_PROTO_PIGP as 9 and IP_PROTO_IGRP as 88.
421		 * We define IP_PROTO_PIGP as 9 and
422		 * IP_PROTO_EIGRP as 88; those names better
423		 * match was the current protocol number
424		 * assignments say.
425		 */
426		igrp_print(ndo, ipds->cp, ipds->len);
427		break;
428
429	case IPPROTO_EIGRP:
430		eigrp_print(ndo, ipds->cp, ipds->len);
431		break;
432
433	case IPPROTO_ND:
434		ND_PRINT((ndo, " nd %d", ipds->len));
435		break;
436
437	case IPPROTO_EGP:
438		egp_print(ndo, ipds->cp, ipds->len);
439		break;
440
441	case IPPROTO_OSPF:
442		ospf_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip);
443		break;
444
445	case IPPROTO_IGMP:
446		igmp_print(ndo, ipds->cp, ipds->len);
447		break;
448
449	case IPPROTO_IPV4:
450		/* DVMRP multicast tunnel (ip-in-ip encapsulation) */
451		ip_print(ndo, ipds->cp, ipds->len);
452		if (! ndo->ndo_vflag) {
453			ND_PRINT((ndo, " (ipip-proto-4)"));
454			return;
455		}
456		break;
457
458	case IPPROTO_IPV6:
459		/* ip6-in-ip encapsulation */
460		ip6_print(ndo, ipds->cp, ipds->len);
461		break;
462
463	case IPPROTO_RSVP:
464		rsvp_print(ndo, ipds->cp, ipds->len);
465		break;
466
467	case IPPROTO_GRE:
468		/* do it */
469		gre_print(ndo, ipds->cp, ipds->len);
470		break;
471
472	case IPPROTO_MOBILE:
473		mobile_print(ndo, ipds->cp, ipds->len);
474		break;
475
476	case IPPROTO_PIM:
477		pim_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip);
478		break;
479
480	case IPPROTO_VRRP:
481		if (ndo->ndo_packettype == PT_CARP) {
482			if (ndo->ndo_vflag)
483				ND_PRINT((ndo, "carp %s > %s: ",
484					     ipaddr_string(ndo, &ipds->ip->ip_src),
485					     ipaddr_string(ndo, &ipds->ip->ip_dst)));
486			carp_print(ndo, ipds->cp, ipds->len, ipds->ip->ip_ttl);
487		} else {
488			if (ndo->ndo_vflag)
489				ND_PRINT((ndo, "vrrp %s > %s: ",
490					     ipaddr_string(ndo, &ipds->ip->ip_src),
491					     ipaddr_string(ndo, &ipds->ip->ip_dst)));
492			vrrp_print(ndo, ipds->cp, ipds->len,
493				(const u_char *)ipds->ip, ipds->ip->ip_ttl);
494		}
495		break;
496
497	case IPPROTO_PGM:
498		pgm_print(ndo, ipds->cp, ipds->len, (const u_char *)ipds->ip);
499		break;
500
501#if defined(HAVE_NET_PFVAR_H)
502	case IPPROTO_PFSYNC:
503		pfsync_ip_print(ndo, ipds->cp, ipds->len);
504		break;
505#endif
506
507	default:
508		if (ndo->ndo_nflag==0 && (p_name = netdb_protoname(ipds->nh)) != NULL)
509			ND_PRINT((ndo, " %s", p_name));
510		else
511			ND_PRINT((ndo, " ip-proto-%d", ipds->nh));
512		ND_PRINT((ndo, " %d", ipds->len));
513		break;
514	}
515}
516
517void
518ip_print_inner(netdissect_options *ndo,
519	       const u_char *bp,
520	       u_int length, u_int nh,
521	       const u_char *bp2)
522{
523	struct ip_print_demux_state  ipd;
524
525	ipd.ip = (const struct ip *)bp2;
526	ipd.cp = bp;
527	ipd.len  = length;
528	ipd.off  = 0;
529	ipd.nh   = nh;
530	ipd.advance = 0;
531
532	ip_print_demux(ndo, &ipd);
533}
534
535
536/*
537 * print an IP datagram.
538 */
539void
540ip_print(netdissect_options *ndo,
541	 const u_char *bp,
542	 u_int length)
543{
544	struct ip_print_demux_state  ipd;
545	struct ip_print_demux_state *ipds=&ipd;
546	const u_char *ipend;
547	u_int hlen;
548	struct cksum_vec vec[1];
549	uint16_t sum, ip_sum;
550	const char *p_name;
551
552	ipds->ip = (const struct ip *)bp;
553	ND_TCHECK(ipds->ip->ip_vhl);
554	if (IP_V(ipds->ip) != 4) { /* print version and fail if != 4 */
555	    if (IP_V(ipds->ip) == 6)
556	      ND_PRINT((ndo, "IP6, wrong link-layer encapsulation "));
557	    else
558	      ND_PRINT((ndo, "IP%u ", IP_V(ipds->ip)));
559	    return;
560	}
561	if (!ndo->ndo_eflag)
562		ND_PRINT((ndo, "IP "));
563
564	ND_TCHECK(*ipds->ip);
565	if (length < sizeof (struct ip)) {
566		ND_PRINT((ndo, "truncated-ip %u", length));
567		return;
568	}
569	hlen = IP_HL(ipds->ip) * 4;
570	if (hlen < sizeof (struct ip)) {
571		ND_PRINT((ndo, "bad-hlen %u", hlen));
572		return;
573	}
574
575	ipds->len = EXTRACT_16BITS(&ipds->ip->ip_len);
576	if (length < ipds->len)
577		ND_PRINT((ndo, "truncated-ip - %u bytes missing! ",
578			ipds->len - length));
579	if (ipds->len < hlen) {
580#ifdef GUESS_TSO
581            if (ipds->len) {
582                ND_PRINT((ndo, "bad-len %u", ipds->len));
583                return;
584            }
585            else {
586                /* we guess that it is a TSO send */
587                ipds->len = length;
588            }
589#else
590            ND_PRINT((ndo, "bad-len %u", ipds->len));
591            return;
592#endif /* GUESS_TSO */
593	}
594
595	/*
596	 * Cut off the snapshot length to the end of the IP payload.
597	 */
598	ipend = bp + ipds->len;
599	if (ipend < ndo->ndo_snapend)
600		ndo->ndo_snapend = ipend;
601
602	ipds->len -= hlen;
603
604	ipds->off = EXTRACT_16BITS(&ipds->ip->ip_off);
605
606        if (ndo->ndo_vflag) {
607            ND_PRINT((ndo, "(tos 0x%x", (int)ipds->ip->ip_tos));
608            /* ECN bits */
609            switch (ipds->ip->ip_tos & 0x03) {
610
611            case 0:
612                break;
613
614            case 1:
615                ND_PRINT((ndo, ",ECT(1)"));
616                break;
617
618            case 2:
619                ND_PRINT((ndo, ",ECT(0)"));
620                break;
621
622            case 3:
623                ND_PRINT((ndo, ",CE"));
624                break;
625            }
626
627            if (ipds->ip->ip_ttl >= 1)
628                ND_PRINT((ndo, ", ttl %u", ipds->ip->ip_ttl));
629
630	    /*
631	     * for the firewall guys, print id, offset.
632             * On all but the last stick a "+" in the flags portion.
633	     * For unfragmented datagrams, note the don't fragment flag.
634	     */
635
636	    ND_PRINT((ndo, ", id %u, offset %u, flags [%s], proto %s (%u)",
637                         EXTRACT_16BITS(&ipds->ip->ip_id),
638                         (ipds->off & 0x1fff) * 8,
639                         bittok2str(ip_frag_values, "none", ipds->off&0xe000),
640                         tok2str(ipproto_values,"unknown",ipds->ip->ip_p),
641                         ipds->ip->ip_p));
642
643            ND_PRINT((ndo, ", length %u", EXTRACT_16BITS(&ipds->ip->ip_len)));
644
645            if ((hlen - sizeof(struct ip)) > 0) {
646                ND_PRINT((ndo, ", options ("));
647                ip_optprint(ndo, (const u_char *)(ipds->ip + 1), hlen - sizeof(struct ip));
648                ND_PRINT((ndo, ")"));
649            }
650
651	    if (!ndo->ndo_Kflag && (const u_char *)ipds->ip + hlen <= ndo->ndo_snapend) {
652	        vec[0].ptr = (const uint8_t *)(const void *)ipds->ip;
653	        vec[0].len = hlen;
654	        sum = in_cksum(vec, 1);
655		if (sum != 0) {
656		    ip_sum = EXTRACT_16BITS(&ipds->ip->ip_sum);
657		    ND_PRINT((ndo, ", bad cksum %x (->%x)!", ip_sum,
658			     in_cksum_shouldbe(ip_sum, sum)));
659		}
660	    }
661
662		ND_PRINT((ndo, ")\n    "));
663	}
664
665	/*
666	 * If this is fragment zero, hand it to the next higher
667	 * level protocol.
668	 */
669	if ((ipds->off & 0x1fff) == 0) {
670		ipds->cp = (const u_char *)ipds->ip + hlen;
671		ipds->nh = ipds->ip->ip_p;
672
673		if (ipds->nh != IPPROTO_TCP && ipds->nh != IPPROTO_UDP &&
674		    ipds->nh != IPPROTO_SCTP && ipds->nh != IPPROTO_DCCP) {
675			ND_PRINT((ndo, "%s > %s: ",
676				     ipaddr_string(ndo, &ipds->ip->ip_src),
677				     ipaddr_string(ndo, &ipds->ip->ip_dst)));
678		}
679		ip_print_demux(ndo, ipds);
680	} else {
681		/*
682		 * Ultra quiet now means that all this stuff should be
683		 * suppressed.
684		 */
685		if (ndo->ndo_qflag > 1)
686			return;
687
688		/*
689		 * This isn't the first frag, so we're missing the
690		 * next level protocol header.  print the ip addr
691		 * and the protocol.
692		 */
693		ND_PRINT((ndo, "%s > %s:", ipaddr_string(ndo, &ipds->ip->ip_src),
694		          ipaddr_string(ndo, &ipds->ip->ip_dst)));
695		if (!ndo->ndo_nflag && (p_name = netdb_protoname(ipds->ip->ip_p)) != NULL)
696			ND_PRINT((ndo, " %s", p_name));
697		else
698			ND_PRINT((ndo, " ip-proto-%d", ipds->ip->ip_p));
699	}
700	return;
701
702trunc:
703	ND_PRINT((ndo, "%s", tstr));
704	return;
705}
706
707void
708ipN_print(netdissect_options *ndo, register const u_char *bp, register u_int length)
709{
710	if (length < 1) {
711		ND_PRINT((ndo, "truncated-ip %d", length));
712		return;
713	}
714
715	ND_TCHECK(*bp);
716	switch (*bp & 0xF0) {
717	case 0x40:
718		ip_print (ndo, bp, length);
719		break;
720	case 0x60:
721		ip6_print (ndo, bp, length);
722		break;
723	default:
724		ND_PRINT((ndo, "unknown ip %d", (*bp & 0xF0) >> 4));
725		break;
726	}
727	return;
728
729trunc:
730	ND_PRINT((ndo, "%s", tstr));
731	return;
732}
733
734/*
735 * Local Variables:
736 * c-style: whitesmith
737 * c-basic-offset: 8
738 * End:
739 */
740
741
742