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 (c) 2001 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29#include <stdio.h>
30#include <string.h>
31#include <sys/types.h>
32#include <sys/socket.h>
33#include <sys/sysmacros.h>
34#include <netinet/in.h>
35#include <arpa/inet.h>
36#include <net/pppoe.h>
37#include "snoop.h"
38
39/*
40 * These two macros extract the version and type fields respectively from
41 * the first byte of the PPPoE header.
42 */
43#define	POE_VERS(x)	(((x) >> 4) & 0x0f)
44#define	POE_TYPE(x)	((x) & 0x0f)
45
46typedef void interpret_func_t(uint8_t *, uint16_t);
47
48typedef struct taginfo {
49	char *tag_name;
50	uint16_t tag_type;
51	interpret_func_t *interpret_tagvalue;
52} taginfo_t;
53
54
55static char *pppoe_codetoname(int, boolean_t);
56static taginfo_t *pppoe_gettaginfo(uint16_t);
57static void print_hexdata(char *, uint8_t *, uint16_t);
58static void print_utf8string(char *, char *, uint16_t);
59static char *print_linetag(char *);
60static interpret_func_t interpret_tags;
61static interpret_func_t interpret_hexdata;
62static interpret_func_t interpret_service;
63static interpret_func_t interpret_access;
64static interpret_func_t interpret_cookie;
65static interpret_func_t interpret_vendor;
66static interpret_func_t interpret_relay;
67static interpret_func_t interpret_error;
68static interpret_func_t interpret_hurl;
69static interpret_func_t interpret_motm;
70static interpret_func_t interpret_rteadd;
71
72
73static taginfo_t taginfo_array[] = {
74	{ "End-Of-List",	POETT_END,	interpret_hexdata },
75	{ "Service-Name",	POETT_SERVICE,	interpret_service },
76	{ "AC-Name",		POETT_ACCESS,	interpret_access },
77	{ "Host-Uniq",		POETT_UNIQ,	interpret_hexdata },
78	{ "AC-Cookie",		POETT_COOKIE,	interpret_cookie },
79	{ "Vendor-Specific",	POETT_VENDOR,	interpret_vendor },
80	{ "Relay-Session-Id",	POETT_RELAY,	interpret_relay },
81	{ "Service-Name-Error",	POETT_NAMERR,	interpret_error },
82	{ "AC-System-Error",	POETT_SYSERR,	interpret_error },
83	{ "Generic-Error",	POETT_GENERR,	interpret_error },
84	{ "Multicast-Capable",	POETT_MULTI,	interpret_hexdata },
85	{ "Host-URL",		POETT_HURL,	interpret_hurl },
86	{ "Message-Of-The-Minute", POETT_MOTM,	interpret_motm },
87	{ "IP-Route-Add",	POETT_RTEADD,	interpret_rteadd },
88	{ "Unknown TAG",	0,		NULL }
89};
90
91
92int
93interpret_pppoe(int flags, poep_t *poep, int len)
94{
95	uint8_t code = poep->poep_code;
96	uint8_t *payload;
97
98	if (len < sizeof (poep_t))
99		return (len);
100
101	payload = (uint8_t *)poep + sizeof (poep_t);
102
103	if (flags & F_SUM) {
104		(void) sprintf(get_sum_line(), "PPPoE %s",
105		    pppoe_codetoname(code, B_FALSE));
106	} else { /* flags & F_DTAIL */
107		show_header("PPPoE:  ", "PPP Over Ethernet", len);
108		show_space();
109
110		(void) sprintf(get_line(0, 0),
111		    "Version = %d", POE_VERS(poep->poep_version_type));
112
113		(void) sprintf(get_line(0, 0),
114		    "Type = %d", POE_TYPE(poep->poep_version_type));
115
116		(void) sprintf(get_line(0, 0),
117		    "Code = %d (%s)", code, pppoe_codetoname(code, B_TRUE));
118
119		(void) sprintf(get_line(0, 0),
120		    "Session Id = %d", ntohs(poep->poep_session_id));
121
122		(void) sprintf(get_line(0, 0),
123		    "Length = %d bytes", ntohs(poep->poep_length));
124
125		show_space();
126
127		len -= sizeof (poep_t);
128		len = MIN(len, ntohs(poep->poep_length));
129
130		if (poep->poep_code != 0 && poep->poep_length > 0) {
131			interpret_tags(payload, len);
132		}
133	}
134
135	if (poep->poep_code == 0) {
136		return (interpret_ppp(flags, payload, len));
137	}
138	return (len);
139}
140
141
142/*
143 * interpret_tags() prints PPPoE Discovery Stage TAGs in detail.
144 */
145static void
146interpret_tags(uint8_t *payload, uint16_t length)
147{
148	uint8_t *tagptr = payload;
149	uint16_t tag_length;
150	uint16_t tag_type;
151	uint8_t *tag_value;
152	taginfo_t *tinfo;
153
154	while (length >= POET_HDRLEN) {
155		tag_type = POET_GET_TYPE(tagptr);
156		tag_length = POET_GET_LENG(tagptr);
157
158		tinfo = pppoe_gettaginfo(tag_type);
159
160		show_header("PPPoE:  ", tinfo->tag_name,
161		    tag_length + POET_HDRLEN);
162
163		(void) sprintf(get_line(0, 0),
164		    "Tag Type = %d", tag_type);
165
166		(void) sprintf(get_line(0, 0),
167		    "Tag Length = %d bytes", tag_length);
168
169		length -= POET_HDRLEN;
170		if (tag_length > length) {
171			(void) sprintf(get_line(0, 0),
172			    "Warning: Truncated Packet");
173			show_space();
174			break;
175		}
176
177		/*
178		 * unknown tags or tags which should always have 0 length
179		 * are not interpreted any further.
180		 */
181		tag_value = POET_DATA(tagptr);
182		if (tag_length != 0 && tinfo->interpret_tagvalue != NULL)
183			tinfo->interpret_tagvalue(tag_value, tag_length);
184
185		show_space();
186		length -= tag_length;
187		tagptr = POET_NEXT(tagptr);
188	}
189}
190
191static char *
192pppoe_codetoname(int code, boolean_t verbose)
193{
194	char *name;
195
196	switch (code) {
197	case POECODE_DATA:
198		name = "Session";
199		break;
200	case POECODE_PADO:
201		if (verbose)
202			name = "Active Discovery Offer";
203		else
204			name = "PADO";
205		break;
206	case POECODE_PADI:
207		if (verbose)
208			name = "Active Discovery Initiation";
209		else
210			name = "PADI";
211		break;
212	case POECODE_PADR:
213		if (verbose)
214			name = "Active Discovery Request";
215		else
216			name = "PADR";
217		break;
218	case POECODE_PADS:
219		if (verbose)
220			name = "Active Discovery Session-Confirmation";
221		else
222			name = "PADS";
223		break;
224	case POECODE_PADT:
225		if (verbose)
226			name = "Active Discovery Terminate";
227		else
228			name = "PADT";
229		break;
230	case POECODE_PADM:
231		if (verbose)
232			name = "Active Discovery Message";
233		else
234			name = "PADM";
235		break;
236	case POECODE_PADN:
237		if (verbose)
238			name = "Active Discovery Network";
239		else
240			name = "PADN";
241		break;
242	default:
243		name = "Unknown Code";
244	}
245
246	return (name);
247}
248
249static taginfo_t *
250pppoe_gettaginfo(uint16_t type)
251{
252	taginfo_t *taginfo_ptr = &taginfo_array[0];
253	int i = 0;
254
255	while (taginfo_ptr->tag_type != type &&
256	    taginfo_ptr->interpret_tagvalue != NULL) {
257		taginfo_ptr = &taginfo_array[++i];
258	}
259
260	return (taginfo_ptr);
261}
262
263static void
264interpret_hexdata(uint8_t *tag_value, uint16_t tag_length)
265{
266	char *endofline;
267
268	endofline = print_linetag("Data = ");
269	print_hexdata(endofline, tag_value, tag_length);
270}
271
272static void
273interpret_service(uint8_t *tag_value, uint16_t tag_length)
274{
275	char *endofline;
276
277	endofline = print_linetag("Service Name = ");
278	print_utf8string(endofline, (char *)tag_value, tag_length);
279}
280
281static void
282interpret_access(uint8_t *tag_value, uint16_t tag_length)
283{
284	char *endofline;
285
286	endofline = print_linetag("AC Name = ");
287	print_utf8string(endofline, (char *)tag_value, tag_length);
288}
289
290static void
291interpret_cookie(uint8_t *tag_value, uint16_t tag_length)
292{
293	char *endofline;
294
295	endofline = print_linetag("Cookie = ");
296	print_hexdata(endofline, tag_value, tag_length);
297}
298
299static void
300interpret_vendor(uint8_t *tag_value, uint16_t tag_length)
301{
302	uint8_t *vendor_data;
303	uint32_t vendorid;
304	char *endofline;
305
306	vendorid = ntohl(*(uint32_t *)tag_value);
307	(void) sprintf(get_line(0, 0),
308	    "Vendor ID = %d", vendorid);
309
310	if (tag_length > 4) {
311		vendor_data = tag_value + 4;
312		endofline = print_linetag("Vendor Data = ");
313		print_hexdata(endofline, vendor_data, tag_length - 4);
314	}
315}
316
317static void
318interpret_relay(uint8_t *tag_value, uint16_t tag_length)
319{
320	char *endofline;
321
322	endofline = print_linetag("ID = ");
323	print_hexdata(endofline, tag_value, tag_length);
324}
325
326static void
327interpret_error(uint8_t *tag_value, uint16_t tag_length)
328{
329	char *endofline;
330
331	endofline = print_linetag("Error = ");
332	print_utf8string(endofline, (char *)tag_value, tag_length);
333}
334
335static void
336interpret_hurl(uint8_t *tag_value, uint16_t tag_length)
337{
338	char *endofline;
339
340	endofline = print_linetag("URL = ");
341	print_utf8string(endofline, (char *)tag_value, tag_length);
342}
343
344static void
345interpret_motm(uint8_t *tag_value, uint16_t tag_length)
346{
347	char *endofline;
348
349	endofline = print_linetag("Message = ");
350	print_utf8string(endofline, (char *)tag_value, tag_length);
351}
352
353static void
354interpret_rteadd(uint8_t *tag_value, uint16_t tag_length)
355{
356	char dest[INET_ADDRSTRLEN];
357	char mask[INET_ADDRSTRLEN];
358	char gateway[INET_ADDRSTRLEN];
359	uint32_t metric;
360
361	if (tag_length == 16) {
362		(void) inet_ntop(AF_INET, tag_value, dest,
363		    INET_ADDRSTRLEN);
364		(void) inet_ntop(AF_INET, &tag_value[4], mask,
365		    INET_ADDRSTRLEN);
366		(void) inet_ntop(AF_INET, &tag_value[8], gateway,
367		    INET_ADDRSTRLEN);
368		metric = ntohl(*(uint32_t *)&tag_value[12]);
369		sprintf(get_line(0, 0),
370		    "Destination\tNetmask\tGateway\tMetric");
371		sprintf(get_line(0, 0),
372		    "%s\t%s\t%s\t%d", dest, mask, gateway, metric);
373	}
374}
375
376static void
377print_hexdata(char *line, uint8_t *data, uint16_t length)
378{
379	uint16_t index = 0;
380
381	line += sprintf(line, "0x");
382
383	while (index < length) {
384		line += sprintf(line, "%02x", data[index++]);
385	}
386}
387
388static void
389print_utf8string(char *firstline, char *string, uint16_t length)
390{
391	(void) sprintf(firstline, "%.*s", length, string);
392}
393
394static char *
395print_linetag(char *string)
396{
397	char *line = get_line(0, 0);
398	return (line + sprintf(line, string));
399}
400