1/*	$NetBSD: atalk.c,v 1.13 2008/04/24 04:09:50 thorpej Exp $	*/
2
3/*
4 * Copyright (c) 1983, 1988, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include <sys/cdefs.h>
33#ifndef lint
34#if 0
35static char sccsid[] = "from @(#)atalk.c	1.1 (Whistle) 6/6/96";
36#else
37__RCSID("$NetBSD: atalk.c,v 1.13 2008/04/24 04:09:50 thorpej Exp $");
38#endif
39#endif /* not lint */
40
41#include <sys/param.h>
42#include <sys/queue.h>
43#include <sys/socket.h>
44#include <sys/socketvar.h>
45#include <sys/mbuf.h>
46#include <sys/protosw.h>
47#include <sys/sysctl.h>
48
49#include <net/route.h>
50#include <net/if.h>
51
52#include <netinet/tcp_fsm.h>
53
54#include <netatalk/at.h>
55#include <netatalk/ddp_var.h>
56
57#include <err.h>
58#include <nlist.h>
59#include <kvm.h>
60#include <errno.h>
61#include <stdio.h>
62#include <string.h>
63#include "netstat.h"
64
65struct ddpcb    ddpcb;
66struct socket   sockb;
67
68static int first = 1;
69
70/*
71 * Print a summary of connections related to a Network Systems
72 * protocol.  For XXX, also give state of connection.
73 * Listening processes (aflag) are suppressed unless the
74 * -a (all) flag is specified.
75 */
76
77static const char *
78at_pr_net(const struct sockaddr_at *sat, int numeric)
79{
80	static char mybuf[50];
81
82	if (!numeric) {
83		switch (sat->sat_addr.s_net) {
84		case 0xffff:
85			return "????";
86		case ATADDR_ANYNET:
87			return ("*");
88		}
89	}
90	(void)snprintf(mybuf, sizeof(mybuf), "%hu", ntohs(sat->sat_addr.s_net));
91	return (mybuf);
92}
93
94static const char *
95at_pr_host(const struct sockaddr_at *sat, int numeric)
96{
97	static char mybuf[50];
98
99	if (!numeric) {
100		switch (sat->sat_addr.s_node) {
101		case ATADDR_BCAST:
102			return "bcast";
103		case ATADDR_ANYNODE:
104			return ("*");
105		}
106	}
107	(void)snprintf(mybuf, sizeof(mybuf), "%d",
108	    (unsigned int)sat->sat_addr.s_node);
109	return (mybuf);
110}
111
112static const char *
113at_pr_port(const struct sockaddr_at *sat)
114{
115	static char mybuf[50];
116
117	switch (sat->sat_port) {
118	case ATADDR_ANYPORT:
119		return ("*");
120	case 0xff:
121		return "????";
122	default:
123		(void)snprintf(mybuf, sizeof(mybuf), "%d",
124		    (unsigned int)sat->sat_port);
125		return (mybuf);
126	}
127}
128
129static const char *
130at_pr_range(const struct sockaddr_at *sat)
131{
132	static char mybuf[50];
133
134	if (sat->sat_range.r_netrange.nr_firstnet
135	    != sat->sat_range.r_netrange.nr_lastnet) {
136		(void)snprintf(mybuf, sizeof(mybuf), "%d-%d",
137			ntohs(sat->sat_range.r_netrange.nr_firstnet),
138			ntohs(sat->sat_range.r_netrange.nr_lastnet));
139	} else {
140		(void)snprintf(mybuf, sizeof(mybuf), "%d",
141			ntohs(sat->sat_range.r_netrange.nr_firstnet));
142	}
143	return (mybuf);
144}
145
146
147/* what == 0 for addr only == 3
148 *	1 for net
149 *	2 for host
150 *	4 for port
151 *	8 for numeric only
152 */
153const char *
154atalk_print(const struct sockaddr *sa, int what)
155{
156	const struct sockaddr_at *sat = (const struct sockaddr_at *) sa;
157	static char mybuf[50];
158	int numeric = (what & 0x08);
159
160	mybuf[0] = 0;
161	switch (what & 0x13) {
162	case 0:
163		mybuf[0] = 0;
164		break;
165	case 1:
166		(void)snprintf(mybuf, sizeof(mybuf), "%s",
167		    at_pr_net(sat, numeric));
168		break;
169	case 2:
170		(void)snprintf(mybuf, sizeof(mybuf), "%s",
171		    at_pr_host(sat, numeric));
172		break;
173	case 3:
174		(void)snprintf(mybuf, sizeof(mybuf), "%s.%s",
175			at_pr_net(sat, numeric),
176			at_pr_host(sat, numeric));
177		break;
178	case 0x10:
179		(void)snprintf(mybuf, sizeof(mybuf), "%s", at_pr_range(sat));
180	}
181	if (what & 4) {
182		(void)snprintf(mybuf + strlen(mybuf),
183		    sizeof(mybuf) - strlen(mybuf), ".%s", at_pr_port(sat));
184	}
185	return (mybuf);
186}
187
188const char *
189atalk_print2(const struct sockaddr *sa, const struct sockaddr *mask, int what)
190{
191	int		n, l;
192	static char     buf[100];
193	const struct sockaddr_at *sat1, *sat2;
194	struct sockaddr_at thesockaddr;
195	struct sockaddr *sa2;
196
197	sat1 = (const struct sockaddr_at *) sa;
198	sat2 = (const struct sockaddr_at *) mask;
199	sa2 = (struct sockaddr *) & thesockaddr;
200
201	thesockaddr.sat_addr.s_net = sat1->sat_addr.s_net &
202	    sat2->sat_addr.s_net;
203	n = snprintf(buf, sizeof(buf), "%s", atalk_print(sa2, 1 | (what & 8)));
204	if (n >= (int)sizeof(buf))
205		n = sizeof(buf) - 1;
206	else if (n == -1)
207		n = 0;	/* What else can be done ? */
208	if (sat2->sat_addr.s_net != 0xFFFF) {
209		thesockaddr.sat_addr.s_net = sat1->sat_addr.s_net |
210		    ~sat2->sat_addr.s_net;
211		l = snprintf(buf + n, sizeof(buf) - n,
212		    "-%s", atalk_print(sa2, 1 | (what & 8)));
213		if (l >= (int)(sizeof(buf) - n))
214			l = sizeof(buf) - n - 1;
215		if (l > 0)
216			n += l;
217	}
218	if (what & 2) {
219		l = snprintf(buf + n, sizeof(buf) - n, ".%s",
220		    atalk_print(sa, what & (~1)));
221		if (l >= (int)(sizeof(buf) - n))
222			l = sizeof(buf) - n - 1;
223		if (l > 0)
224			n += l;
225	}
226	return (buf);
227}
228
229void
230atalkprotopr(u_long off, const char *name)
231{
232	struct ddpcb    cb;
233	struct ddpcb *prev, *next;
234	struct ddpcb   *initial;
235	int width = 22;
236	if (off == 0)
237		return;
238	if (kread(off, (char *)&initial, sizeof(struct ddpcb *)) < 0)
239		return;
240	ddpcb = cb;
241	prev = (struct ddpcb *)off;
242	for (next = initial; next != NULL; prev = next) {
243		u_long	ppcb = (u_long)next;
244
245		if (kread((u_long)next, (char *)&ddpcb, sizeof(ddpcb)) < 0)
246			return;
247		next = ddpcb.ddp_next;
248#if 0
249		if (!aflag && atalk_nullhost(ddpcb.ddp_lsat)) {
250			continue;
251		}
252#endif
253		if (kread((u_long)ddpcb.ddp_socket,
254			  (char *)&sockb, sizeof(sockb)) < 0)
255			return;
256		if (first) {
257			printf("Active ATALK connections");
258			if (aflag)
259				printf(" (including servers)");
260			putchar('\n');
261			if (Aflag) {
262				width = 18;
263				printf("%-8.8s ", "PCB");
264			}
265			printf("%-5.5s %-6.6s %-6.6s  %*.*s %*.*s %s\n",
266			       "Proto", "Recv-Q", "Send-Q",
267			       -width, width, "Local Address",
268			       -width, width, "Foreign Address", "(state)");
269			first = 0;
270		}
271		if (Aflag)
272			printf("%8lx ", ppcb);
273		printf("%-5.5s %6ld %6ld ", name, sockb.so_rcv.sb_cc,
274		       sockb.so_snd.sb_cc);
275		printf(" %*.*s", -width, width,
276		       atalk_print((struct sockaddr *)&ddpcb.ddp_lsat, 7));
277		printf(" %*.*s", -width, width,
278		       atalk_print((struct sockaddr *)&ddpcb.ddp_fsat, 7));
279		putchar('\n');
280	}
281}
282#define ANY(x,y,z) \
283	((sflag==1 || (x)) ? printf("\t%llu %s%s%s\n",(unsigned long long)x,y,plural(x),z) : 0)
284
285/*
286 * Dump DDP statistics structure.
287 */
288void
289ddp_stats(u_long off, const char *name)
290{
291	uint64_t ddpstat[DDP_NSTATS];
292
293	if (use_sysctl) {
294		size_t size = sizeof(ddpstat);
295
296		if (sysctlbyname("net.atalk.ddp.stats", ddpstat, &size,
297				 NULL, 0) == -1)
298			return;
299	} else {
300		warnx("%s stats not available via KVM.", name);
301		return;
302	}
303
304	printf("%s:\n", name);
305
306	ANY(ddpstat[DDP_STAT_SHORT], "packet", " with short headers ");
307	ANY(ddpstat[DDP_STAT_LONG], "packet", " with long headers ");
308	ANY(ddpstat[DDP_STAT_NOSUM], "packet", " with no checksum ");
309	ANY(ddpstat[DDP_STAT_TOOSHORT], "packet", " too short ");
310	ANY(ddpstat[DDP_STAT_BADSUM], "packet", " with bad checksum ");
311	ANY(ddpstat[DDP_STAT_TOOSMALL], "packet", " with not enough data ");
312	ANY(ddpstat[DDP_STAT_FORWARD], "packet", " forwarded ");
313	ANY(ddpstat[DDP_STAT_ENCAP], "packet", " encapsulated ");
314	ANY(ddpstat[DDP_STAT_CANTFORWARD], "packet", " rcvd for unreachable dest ");
315	ANY(ddpstat[DDP_STAT_NOSOCKSPACE], "packet", " dropped due to no socket space ");
316}
317#undef ANY
318