xref: /freebsd/contrib/tcpdump/print-dhcp6.c (revision 6b3455a7665208c366849f0b2b3bc916fb97516e)
1 /*
2  * Copyright (C) 1998 and 1999 WIDE Project.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the project nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 /*
30  * RFC3315: DHCPv6
31  * supported DHCPv6 options:
32  *  RFC3319,
33  *  draft-ietf-dhc-dhcpv6-opt-dnsconfig-04.txt,
34  *  draft-ietf-dhc-dhcpv6-opt-prefix-delegation-05.txt
35  *  draft-ietf-dhc-dhcpv6-opt-timeconfig-02.txt,
36  */
37 
38 #ifndef lint
39 static const char rcsid[] _U_ =
40     "@(#) $Header: /tcpdump/master/tcpdump/print-dhcp6.c,v 1.27.2.4 2003/11/18 23:26:14 guy Exp $";
41 #endif
42 
43 #ifdef HAVE_CONFIG_H
44 #include "config.h"
45 #endif
46 
47 #include <tcpdump-stdinc.h>
48 
49 #include <stdio.h>
50 #include <string.h>
51 
52 #include "interface.h"
53 #include "addrtoname.h"
54 #include "extract.h"
55 
56 /* lease duration */
57 #define DHCP6_DURATITION_INFINITE 0xffffffff
58 
59 /* Error Values */
60 #define DH6ERR_FAILURE		16
61 #define DH6ERR_AUTHFAIL		17
62 #define DH6ERR_POORLYFORMED	18
63 #define DH6ERR_UNAVAIL		19
64 #define DH6ERR_OPTUNAVAIL	20
65 
66 /* Message type */
67 #define DH6_SOLICIT	1
68 #define DH6_ADVERTISE	2
69 #define DH6_REQUEST	3
70 #define DH6_CONFIRM	4
71 #define DH6_RENEW	5
72 #define DH6_REBIND	6
73 #define DH6_REPLY	7
74 #define DH6_RELEASE	8
75 #define DH6_DECLINE	9
76 #define DH6_RECONFIGURE	10
77 #define DH6_INFORM_REQ	11
78 #define DH6_RELAY_FORW	12
79 #define DH6_RELAY_REPLY	13
80 
81 /* DHCP6 base packet format */
82 struct dhcp6 {
83 	union {
84 		u_int8_t m;
85 		u_int32_t x;
86 	} dh6_msgtypexid;
87 	/* options follow */
88 };
89 #define dh6_msgtype	dh6_msgtypexid.m
90 #define dh6_xid		dh6_msgtypexid.x
91 #define DH6_XIDMASK	0x00ffffff
92 
93 /* DHCPv6 relay messages */
94 struct dhcp6_relay {
95 	u_int8_t dh6relay_msgtype;
96 	u_int8_t dh6relay_hcnt;
97 	u_int8_t dh6relay_linkaddr[16];	/* XXX: badly aligned */
98 	u_int8_t dh6relay_peeraddr[16];
99 	/* options follow */
100 };
101 
102 /* options */
103 #define DH6OPT_CLIENTID	1
104 #define DH6OPT_SERVERID	2
105 #define DH6OPT_IA_NA 3
106 #define DH6OPT_IA_TMP 4
107 #define DH6OPT_IADDR 5
108 #define DH6OPT_ORO 6
109 #define DH6OPT_PREFERENCE 7
110 #  define DH6OPT_PREF_UNDEF -1
111 #  define DH6OPT_PREF_MAX 255
112 #define DH6OPT_ELAPSED_TIME 8
113 #define DH6OPT_RELAY_MSG 9
114 /*#define DH6OPT_SERVER_MSG 10 deprecated */
115 #define DH6OPT_AUTH 11
116 #define DH6OPT_UNICAST 12
117 #define DH6OPT_STATUS_CODE 13
118 #  define DH6OPT_STCODE_SUCCESS 0
119 #  define DH6OPT_STCODE_UNSPECFAIL 1
120 #  define DH6OPT_STCODE_NOADDRAVAIL 2
121 #  define DH6OPT_STCODE_NOBINDING 3
122 #  define DH6OPT_STCODE_NOTONLINK 4
123 #  define DH6OPT_STCODE_USEMULTICAST 5
124 #  define DH6OPT_STCODE_NOPREFIXAVAIL 6
125 #define DH6OPT_RAPID_COMMIT 14
126 #define DH6OPT_USER_CLASS 15
127 #define DH6OPT_VENDOR_CLASS 16
128 #define DH6OPT_VENDOR_OPTS 17
129 #define DH6OPT_INTERFACE_ID 18
130 #define DH6OPT_RECONF_MSG 19
131 #define DH6OPT_RECONF_ACCEPT 20
132 #define DH6OPT_SIP_SERVER_D 21
133 #define DH6OPT_SIP_SERVER_A 22
134 #define DH6OPT_DNS 23
135 #define DH6OPT_DNSNAME 24
136 
137 /*
138  * The option type has not been assigned for the following options.
139  * We temporarily adopt values used in the service specification document
140  * (200206xx version) by NTT Communications.
141  * Note that we'll change the following definitions if different type values
142  * are officially assigned.
143  */
144 #define DH6OPT_PREFIX_DELEGATION 30
145 #define DH6OPT_PREFIX_INFORMATION 31
146 #define DH6OPT_PREFIX_REQUEST 32
147 
148 /*
149  * The followings are also unassigned numbers.
150  * We temporarily use values as of KAME snap 20031013.
151  */
152 #define DH6OPT_IA_PD 33
153 #define DH6OPT_IA_PD_PREFIX 34
154 #define DH6OPT_NTP_SERVERS 35
155 
156 struct dhcp6opt {
157 	u_int16_t dh6opt_type;
158 	u_int16_t dh6opt_len;
159 	/* type-dependent data follows */
160 };
161 
162 struct dhcp6_ia {
163 	u_int16_t dh6opt_ia_type;
164 	u_int16_t dh6opt_ia_len;
165 	u_int32_t dh6opt_ia_iaid;
166 	u_int32_t dh6opt_ia_t1;
167 	u_int32_t dh6opt_ia_t2;
168 };
169 
170 struct dhcp6_ia_prefix {
171 	u_int16_t dh6opt_ia_prefix_type;
172 	u_int16_t dh6opt_ia_prefix_len;
173 	u_int32_t dh6opt_ia_prefix_pltime;
174 	u_int32_t dh6opt_ia_prefix_vltime;
175 	u_int8_t dh6opt_ia_prefix_plen;
176 	struct in6_addr dh6opt_ia_prefix_addr;
177 }  __attribute__ ((__packed__));
178 
179 static const char *
180 dhcp6opt_name(int type)
181 {
182 	static char genstr[sizeof("opt_65535") + 1]; /* XXX thread unsafe */
183 
184 	if (type > 65535)
185 		return "INVALID option";
186 
187 	switch(type) {
188 	case DH6OPT_CLIENTID:
189 		return "client ID";
190 	case DH6OPT_SERVERID:
191 		return "server ID";
192 	case DH6OPT_IA_NA:
193 		return "IA_NA";
194 	case DH6OPT_ORO:
195 		return "option request";
196 	case DH6OPT_PREFERENCE:
197 		return "preference";
198 	case DH6OPT_ELAPSED_TIME:
199 		return "elapsed time";
200 	case DH6OPT_RELAY_MSG:
201 		return "relay message";
202 	case DH6OPT_STATUS_CODE:
203 		return "status code";
204 	case DH6OPT_RAPID_COMMIT:
205 		return "rapid commit";
206 	case DH6OPT_INTERFACE_ID:
207 		return "interface ID";
208 	case DH6OPT_RECONF_MSG:
209 		return "reconfigure message";
210 	case DH6OPT_RECONF_ACCEPT:
211 		return "reconfigure accept";
212 	case DH6OPT_SIP_SERVER_D:
213 		return "SIP Servers Domain";
214 	case DH6OPT_SIP_SERVER_A:
215 		return "SIP Servers Address";
216 	case DH6OPT_DNS:
217 		return "DNS";
218 	case DH6OPT_PREFIX_DELEGATION:
219 		return "prefix delegation";
220 	case DH6OPT_PREFIX_INFORMATION:
221 		return "prefix information";
222 	case DH6OPT_IA_PD:
223 		return "IA_PD";
224 	case DH6OPT_IA_PD_PREFIX:
225 		return "IA_PD prefix";
226 	case DH6OPT_NTP_SERVERS:
227 		return "NTP Server";
228 	default:
229 		snprintf(genstr, sizeof(genstr), "opt_%d", type);
230 		return(genstr);
231 	}
232 }
233 
234 static const char *
235 dhcp6stcode(int code)
236 {
237 	static char genstr[sizeof("code255") + 1]; /* XXX thread unsafe */
238 
239 	if (code > 255)
240 		return "INVALID code";
241 
242 	switch(code) {
243 	case DH6OPT_STCODE_SUCCESS:
244 		return "success";
245 	case DH6OPT_STCODE_UNSPECFAIL:
246 		return "unspec failure";
247 	case DH6OPT_STCODE_NOADDRAVAIL:
248 		return "no addresses";
249 	case DH6OPT_STCODE_NOBINDING:
250 		return "no binding";
251 	case DH6OPT_STCODE_NOTONLINK:
252 		return "not on-link";
253 	case DH6OPT_STCODE_USEMULTICAST:
254 		return "use multicast";
255 	case DH6OPT_STCODE_NOPREFIXAVAIL:
256 		return "no prefixes";
257 	default:
258 		snprintf(genstr, sizeof(genstr), "code%d", code);
259 		return(genstr);
260 	}
261 }
262 
263 static void
264 dhcp6opt_print(const u_char *cp, const u_char *ep)
265 {
266 	struct dhcp6opt *dh6o;
267 	u_char *tp;
268 	size_t i;
269 	u_int16_t opttype;
270 	size_t optlen;
271 	u_int16_t val16;
272 	u_int32_t val32;
273 	struct in6_addr addr6;
274 	struct dhcp6_ia ia;
275 	struct dhcp6_ia_prefix ia_prefix;
276 
277 	if (cp == ep)
278 		return;
279 	while (cp < ep) {
280 		if (ep < cp + sizeof(*dh6o))
281 			goto trunc;
282 		dh6o = (struct dhcp6opt *)cp;
283 		optlen = EXTRACT_16BITS(&dh6o->dh6opt_len);
284 		if (ep < cp + sizeof(*dh6o) + optlen)
285 			goto trunc;
286 		opttype = EXTRACT_16BITS(&dh6o->dh6opt_type);
287 		printf(" (%s", dhcp6opt_name(opttype));
288 		switch (opttype) {
289 		case DH6OPT_CLIENTID:
290 		case DH6OPT_SERVERID:
291 			if (optlen < 2) {
292 				/*(*/
293 				printf(" ?)");
294 				break;
295 			}
296 			tp = (u_char *)(dh6o + 1);
297 			switch (EXTRACT_16BITS(tp)) {
298 			case 1:
299 				if (optlen >= 2 + 6) {
300 					printf(" hwaddr/time type %u time %u ",
301 					    EXTRACT_16BITS(&tp[2]),
302 					    EXTRACT_32BITS(&tp[4]));
303 					for (i = 8; i < optlen; i++)
304 						printf("%02x", tp[i]);
305 					/*(*/
306 					printf(")");
307 				} else {
308 					/*(*/
309 					printf(" ?)");
310 				}
311 				break;
312 			case 2:
313 				if (optlen >= 2 + 8) {
314 					printf(" vid ");
315 					for (i = 2; i < 2 + 8; i++)
316 						printf("%02x", tp[i]);
317 					/*(*/
318 					printf(")");
319 				} else {
320 					/*(*/
321 					printf(" ?)");
322 				}
323 				break;
324 			case 3:
325 				if (optlen >= 2 + 2) {
326 					printf(" hwaddr type %u ",
327 					    EXTRACT_16BITS(&tp[2]));
328 					for (i = 4; i < optlen; i++)
329 						printf("%02x", tp[i]);
330 					/*(*/
331 					printf(")");
332 				} else {
333 					/*(*/
334 					printf(" ?)");
335 				}
336 				break;
337 			default:
338 				printf(" type %d)", EXTRACT_16BITS(tp));
339 				break;
340 			}
341 			break;
342 		case DH6OPT_ORO:
343 			if (optlen % 2) {
344 				printf(" ?)");
345 				break;
346 			}
347 			tp = (u_char *)(dh6o + 1);
348 			for (i = 0; i < optlen; i += 2) {
349 				u_int16_t opt;
350 
351 				memcpy(&opt, &tp[i], sizeof(opt));
352 				printf(" %s", dhcp6opt_name(ntohs(opt)));
353 			}
354 			printf(")");
355 			break;
356 		case DH6OPT_PREFERENCE:
357 			if (optlen != 1) {
358 				printf(" ?)");
359 				break;
360 			}
361 			printf(" %d)", *((u_char *)(dh6o + 1) + 1));
362 			break;
363 		case DH6OPT_ELAPSED_TIME:
364 			if (optlen != 2) {
365 				printf(" ?)");
366 				break;
367 			}
368 			memcpy(&val16, dh6o + 1, sizeof(val16));
369 			val16 = ntohs(val16);
370 			printf(" %d)", (int)val16);
371 			break;
372 		case DH6OPT_RELAY_MSG:
373 			printf(" (");
374 			dhcp6_print((const u_char *)(dh6o + 1), optlen);
375 			printf(")");
376 			break;
377 		case DH6OPT_RAPID_COMMIT: /* nothing todo */
378 			printf(")");
379 			break;
380 		case DH6OPT_INTERFACE_ID:
381 			/*
382 			 * Since we cannot predict the encoding, print hex dump
383 			 * at most 10 characters.
384 			 */
385 			for (i = 0; i < optlen && i < 10; i++)
386 				printf("%02x", ((u_char *)(dh6o + 1))[i]);
387 			break;
388 		case DH6OPT_RECONF_MSG:
389 			tp = (u_char *)(dh6o + 1);
390 			switch (*tp) {
391 			case DH6_RENEW:
392 				printf(" for renew)");
393 				break;
394 			case DH6_INFORM_REQ:
395 				printf(" for inf-req)");
396 				break;
397 			default:
398 				printf(" for ?\?\?(%02x))", *tp);
399 				break;
400 			}
401 			break;
402 		case DH6OPT_RECONF_ACCEPT: /* nothing todo */
403 			printf(")");
404 			break;
405 		case DH6OPT_SIP_SERVER_A:
406 		case DH6OPT_DNS:
407 		case DH6OPT_NTP_SERVERS:
408 			if (optlen % 16) {
409 				printf(" ?)");
410 				break;
411 			}
412 			tp = (u_char *)(dh6o + 1);
413 			for (i = 0; i < optlen; i += 16)
414 				printf(" %s", ip6addr_string(&tp[i]));
415 			printf(")");
416 			break;
417 		case DH6OPT_PREFIX_DELEGATION:
418 			dhcp6opt_print((u_char *)(dh6o + 1),
419 			    (u_char *)(dh6o + 1) + optlen);
420 			printf(")");
421 			break;
422 		case DH6OPT_PREFIX_INFORMATION:
423 			if (optlen % 21)
424 				printf(" ?)");
425 			memcpy(&addr6, (u_char *)(dh6o + 1) + 5,
426 			    sizeof(addr6));
427 			printf(" %s/%d", ip6addr_string(&addr6),
428 			    (int)*((u_char *)(dh6o + 1) + 4));
429 			memcpy(&val32, dh6o + 1, sizeof(val32));
430 			val32 = ntohl(val32);
431 			if (val32 == DHCP6_DURATITION_INFINITE)
432 				printf(" lease-duration: infinite)");
433 			else
434 				printf(" lease-duration: %u)", val32);
435 			break;
436 		case DH6OPT_STATUS_CODE:
437 			if (optlen < 2) {
438 				printf(" ?)");
439 				break;
440 			}
441 			memcpy(&val16, (u_char *)(dh6o + 1), sizeof(val16));
442 			val16 = ntohs(val16);
443 			printf(" %s)", dhcp6stcode(val16));
444 			break;
445 		case DH6OPT_IA_NA:
446 		case DH6OPT_IA_PD:
447 			if (optlen < sizeof(ia) - 4) {
448 				printf(" ?)");
449 				break;
450 			}
451 			memcpy(&ia, (u_char *)dh6o, sizeof(ia));
452 			ia.dh6opt_ia_iaid = ntohl(ia.dh6opt_ia_iaid);
453 			ia.dh6opt_ia_t1 = ntohl(ia.dh6opt_ia_t1);
454 			ia.dh6opt_ia_t2 = ntohl(ia.dh6opt_ia_t2);
455 			printf(" IAID:%lu T1:%lu T2:%lu",
456 			    (unsigned long)ia.dh6opt_ia_iaid,
457 			    (unsigned long)ia.dh6opt_ia_t1,
458 			    (unsigned long)ia.dh6opt_ia_t2);
459 			if (optlen > sizeof(ia) - 4) {
460 				/* there are sub-options */
461 				dhcp6opt_print((u_char *)dh6o + sizeof(ia),
462 				    (u_char *)(dh6o + 1) + optlen);
463 			}
464 			printf(")");
465 			break;
466 		case DH6OPT_IA_PD_PREFIX:
467 			if (optlen < sizeof(ia_prefix) - 4) {
468 				printf(" ?)");
469 				break;
470 			}
471 			memcpy(&ia_prefix, (u_char *)dh6o, sizeof(ia_prefix));
472 			printf(" %s/%d",
473 			    ip6addr_string(&ia_prefix.dh6opt_ia_prefix_addr),
474 			    ia_prefix.dh6opt_ia_prefix_plen);
475 			ia_prefix.dh6opt_ia_prefix_pltime =
476 			    ntohl(ia_prefix.dh6opt_ia_prefix_pltime);
477 			ia_prefix.dh6opt_ia_prefix_vltime =
478 			    ntohl(ia_prefix.dh6opt_ia_prefix_vltime);
479 			printf(" pltime:%lu vltime:%lu",
480 			    (unsigned long)ia_prefix.dh6opt_ia_prefix_pltime,
481 			    (unsigned long)ia_prefix.dh6opt_ia_prefix_vltime);
482 			if (optlen > sizeof(ia_prefix) - 4) {
483 				/* there are sub-options */
484 				dhcp6opt_print((u_char *)dh6o +
485 				    sizeof(ia_prefix),
486 				    (u_char *)(dh6o + 1) + optlen);
487 			}
488 			printf(")");
489 			break;
490 		default:
491 			printf(")");
492 			break;
493 		}
494 
495 		cp += sizeof(*dh6o) + optlen;
496 	}
497 	return;
498 
499 trunc:
500 	printf("[|dhcp6ext]");
501 }
502 
503 /*
504  * Print dhcp6 packets
505  */
506 void
507 dhcp6_print(const u_char *cp, u_int length)
508 {
509 	struct dhcp6 *dh6;
510 	struct dhcp6_relay *dh6relay;
511 	const u_char *ep;
512 	u_char *extp;
513 	const char *name;
514 
515 	printf("dhcp6");
516 
517 	ep = (u_char *)snapend;
518 	if (cp + length < ep)
519 		ep = cp + length;
520 
521 	dh6 = (struct dhcp6 *)cp;
522 	dh6relay = (struct dhcp6_relay *)cp;
523 	TCHECK(dh6->dh6_xid);
524 	switch (dh6->dh6_msgtype) {
525 	case DH6_SOLICIT:
526 		name = "solicit";
527 		break;
528 	case DH6_ADVERTISE:
529 		name = "advertise";
530 		break;
531 	case DH6_REQUEST:
532 		name = "request";
533 		break;
534 	case DH6_CONFIRM:
535 		name = "confirm";
536 		break;
537 	case DH6_RENEW:
538 		name = "renew";
539 		break;
540 	case DH6_REBIND:
541 		name = "rebind";
542 		break;
543 	case DH6_REPLY:
544 		name = "reply";
545 		break;
546 	case DH6_RELEASE:
547 		name = "release";
548 		break;
549 	case DH6_DECLINE:
550 		name = "decline";
551 		break;
552 	case DH6_RECONFIGURE:
553 		name = "reconfigure";
554 		break;
555 	case DH6_INFORM_REQ:
556 		name= "inf-req";
557 		break;
558 	case DH6_RELAY_FORW:
559 		name= "relay-fwd";
560 		break;
561 	case DH6_RELAY_REPLY:
562 		name= "relay-reply";
563 		break;
564 	default:
565 		name = NULL;
566 		break;
567 	}
568 
569 	if (!vflag) {
570 		if (name)
571 			printf(" %s", name);
572 		else if (dh6->dh6_msgtype != DH6_RELAY_FORW &&
573 		    dh6->dh6_msgtype != DH6_RELAY_REPLY) {
574 			printf(" msgtype-%u", dh6->dh6_msgtype);
575 		}
576 		return;
577 	}
578 
579 	/* XXX relay agent messages have to be handled differently */
580 
581 	if (name)
582 		printf(" %s (", name);	/*)*/
583 	else
584 		printf(" msgtype-%u (", dh6->dh6_msgtype);	/*)*/
585 	if (dh6->dh6_msgtype != DH6_RELAY_FORW &&
586 	    dh6->dh6_msgtype != DH6_RELAY_REPLY) {
587 		printf("xid=%x", EXTRACT_32BITS(&dh6->dh6_xid) & DH6_XIDMASK);
588 		extp = (u_char *)(dh6 + 1);
589 		dhcp6opt_print(extp, ep);
590 	} else {		/* relay messages */
591 		struct in6_addr addr6;
592 
593 		TCHECK(dh6relay->dh6relay_peeraddr);
594 
595 		memcpy(&addr6, dh6relay->dh6relay_linkaddr, sizeof (addr6));
596 		printf("linkaddr=%s", ip6addr_string(&addr6));
597 
598 		memcpy(&addr6, dh6relay->dh6relay_peeraddr, sizeof (addr6));
599 		printf(" peeraddr=%s", ip6addr_string(&addr6));
600 
601 		dhcp6opt_print((u_char *)(dh6relay + 1), ep);
602 	}
603 	/*(*/
604 	printf(")");
605 	return;
606 
607 trunc:
608 	printf("[|dhcp6]");
609 }
610