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/* \summary: IPv6 DHCP printer */
31
32/*
33 * RFC3315: DHCPv6
34 * supported DHCPv6 options:
35 *  RFC3319: Session Initiation Protocol (SIP) Servers options,
36 *  RFC3633: IPv6 Prefix options,
37 *  RFC3646: DNS Configuration options,
38 *  RFC3898: Network Information Service (NIS) Configuration options,
39 *  RFC4075: Simple Network Time Protocol (SNTP) Configuration option,
40 *  RFC4242: Information Refresh Time option,
41 *  RFC4280: Broadcast and Multicast Control Servers options,
42 *  RFC5908: Network Time Protocol (NTP) Server Option for DHCPv6
43 *  RFC6334: Dual-Stack Lite option,
44 */
45
46#include <sys/cdefs.h>
47#ifndef lint
48__RCSID("$NetBSD: print-dhcp6.c,v 1.9 2023/08/17 20:19:40 christos Exp $");
49#endif
50
51#ifdef HAVE_CONFIG_H
52#include <config.h>
53#endif
54
55#include "netdissect-stdinc.h"
56
57#include "netdissect.h"
58#include "addrtoname.h"
59#include "extract.h"
60
61/* lease duration */
62#define DHCP6_DURATION_INFINITE 0xffffffff
63
64/* Error Values */
65#define DH6ERR_FAILURE		16
66#define DH6ERR_AUTHFAIL		17
67#define DH6ERR_POORLYFORMED	18
68#define DH6ERR_UNAVAIL		19
69#define DH6ERR_OPTUNAVAIL	20
70
71/* Message type */
72#define DH6_SOLICIT	1
73#define DH6_ADVERTISE	2
74#define DH6_REQUEST	3
75#define DH6_CONFIRM	4
76#define DH6_RENEW	5
77#define DH6_REBIND	6
78#define DH6_REPLY	7
79#define DH6_RELEASE	8
80#define DH6_DECLINE	9
81#define DH6_RECONFIGURE	10
82#define DH6_INFORM_REQ	11
83#define DH6_RELAY_FORW	12
84#define DH6_RELAY_REPLY	13
85#define DH6_LEASEQUERY	14
86#define DH6_LQ_REPLY	15
87
88static const struct tok dh6_msgtype_str[] = {
89	{ DH6_SOLICIT,     "solicit"          },
90	{ DH6_ADVERTISE,   "advertise"        },
91	{ DH6_REQUEST,     "request"          },
92	{ DH6_CONFIRM,     "confirm"          },
93	{ DH6_RENEW,       "renew"            },
94	{ DH6_REBIND,      "rebind"           },
95	{ DH6_REPLY,       "reply"            },
96	{ DH6_RELEASE,     "release"          },
97	{ DH6_DECLINE,     "decline"          },
98	{ DH6_RECONFIGURE, "reconfigure"      },
99	{ DH6_INFORM_REQ,  "inf-req"          },
100	{ DH6_RELAY_FORW,  "relay-fwd"        },
101	{ DH6_RELAY_REPLY, "relay-reply"      },
102	{ DH6_LEASEQUERY,  "leasequery"       },
103	{ DH6_LQ_REPLY,    "leasequery-reply" },
104	{ 0, NULL }
105};
106
107/* DHCP6 base packet format */
108struct dhcp6 {
109	union {
110		nd_uint8_t msgtype;
111		nd_uint32_t xid;
112	} dh6_msgtypexid;
113	/* options follow */
114};
115#define DH6_XIDMASK	0x00ffffff
116
117/* DHCPv6 relay messages */
118struct dhcp6_relay {
119	nd_uint8_t dh6relay_msgtype;
120	nd_uint8_t dh6relay_hcnt;
121	nd_ipv6    dh6relay_linkaddr;	/* XXX: badly aligned */
122	nd_ipv6    dh6relay_peeraddr;
123	/* options follow */
124};
125
126/* options */
127#define DH6OPT_CLIENTID	1
128#define DH6OPT_SERVERID	2
129#define DH6OPT_IA_NA 3
130#define DH6OPT_IA_TA 4
131#define DH6OPT_IA_ADDR 5
132#define DH6OPT_ORO 6
133#define DH6OPT_PREFERENCE 7
134#  define DH6OPT_PREF_MAX 255
135#define DH6OPT_ELAPSED_TIME 8
136#define DH6OPT_RELAY_MSG 9
137/*#define DH6OPT_SERVER_MSG 10 deprecated */
138#define DH6OPT_AUTH 11
139#  define DH6OPT_AUTHPROTO_DELAYED 2
140#  define DH6OPT_AUTHPROTO_RECONFIG 3
141#  define DH6OPT_AUTHALG_HMACMD5 1
142#  define DH6OPT_AUTHRDM_MONOCOUNTER 0
143#  define DH6OPT_AUTHRECONFIG_KEY 1
144#  define DH6OPT_AUTHRECONFIG_HMACMD5 2
145#define DH6OPT_UNICAST 12
146#define DH6OPT_STATUS_CODE 13
147#  define DH6OPT_STCODE_SUCCESS 0
148#  define DH6OPT_STCODE_UNSPECFAIL 1
149#  define DH6OPT_STCODE_NOADDRAVAIL 2
150#  define DH6OPT_STCODE_NOBINDING 3
151#  define DH6OPT_STCODE_NOTONLINK 4
152#  define DH6OPT_STCODE_USEMULTICAST 5
153#  define DH6OPT_STCODE_NOPREFIXAVAIL 6
154#  define DH6OPT_STCODE_UNKNOWNQUERYTYPE 7
155#  define DH6OPT_STCODE_MALFORMEDQUERY 8
156#  define DH6OPT_STCODE_NOTCONFIGURED 9
157#  define DH6OPT_STCODE_NOTALLOWED 10
158#define DH6OPT_RAPID_COMMIT 14
159#define DH6OPT_USER_CLASS 15
160#define DH6OPT_VENDOR_CLASS 16
161#define DH6OPT_VENDOR_OPTS 17
162#define DH6OPT_INTERFACE_ID 18
163#define DH6OPT_RECONF_MSG 19
164#define DH6OPT_RECONF_ACCEPT 20
165#define DH6OPT_SIP_SERVER_D 21
166#define DH6OPT_SIP_SERVER_A 22
167#define DH6OPT_DNS_SERVERS 23
168#define DH6OPT_DOMAIN_LIST 24
169#define DH6OPT_IA_PD 25
170#define DH6OPT_IA_PD_PREFIX 26
171#define DH6OPT_NIS_SERVERS 27
172#define DH6OPT_NISP_SERVERS 28
173#define DH6OPT_NIS_NAME 29
174#define DH6OPT_NISP_NAME 30
175#define DH6OPT_SNTP_SERVERS 31
176#define DH6OPT_LIFETIME 32
177#define DH6OPT_BCMCS_SERVER_D 33
178#define DH6OPT_BCMCS_SERVER_A 34
179#define DH6OPT_GEOCONF_CIVIC 36
180#define DH6OPT_REMOTE_ID 37
181#define DH6OPT_SUBSCRIBER_ID 38
182#define DH6OPT_CLIENT_FQDN 39
183#define DH6OPT_PANA_AGENT 40
184#define DH6OPT_NEW_POSIX_TIMEZONE 41
185#define DH6OPT_NEW_TZDB_TIMEZONE 42
186#define DH6OPT_ERO 43
187#define DH6OPT_LQ_QUERY 44
188#define DH6OPT_CLIENT_DATA 45
189#define DH6OPT_CLT_TIME 46
190#define DH6OPT_LQ_RELAY_DATA 47
191#define DH6OPT_LQ_CLIENT_LINK 48
192#define DH6OPT_NTP_SERVER 56
193#  define DH6OPT_NTP_SUBOPTION_SRV_ADDR 1
194#  define DH6OPT_NTP_SUBOPTION_MC_ADDR 2
195#  define DH6OPT_NTP_SUBOPTION_SRV_FQDN 3
196#define DH6OPT_AFTR_NAME 64
197#define DH6OPT_MUDURL 112
198
199static const struct tok dh6opt_str[] = {
200	{ DH6OPT_CLIENTID,           "client-ID"            },
201	{ DH6OPT_SERVERID,           "server-ID"            },
202	{ DH6OPT_IA_NA,              "IA_NA"                },
203	{ DH6OPT_IA_TA,              "IA_TA"                },
204	{ DH6OPT_IA_ADDR,            "IA_ADDR"              },
205	{ DH6OPT_ORO,                "option-request"       },
206	{ DH6OPT_PREFERENCE,         "preference"           },
207	{ DH6OPT_ELAPSED_TIME,       "elapsed-time"         },
208	{ DH6OPT_RELAY_MSG,          "relay-message"        },
209	{ DH6OPT_AUTH,               "authentication"       },
210	{ DH6OPT_UNICAST,            "server-unicast"       },
211	{ DH6OPT_STATUS_CODE,        "status-code"          },
212	{ DH6OPT_RAPID_COMMIT,       "rapid-commit"         },
213	{ DH6OPT_USER_CLASS,         "user-class"           },
214	{ DH6OPT_VENDOR_CLASS,       "vendor-class"         },
215	{ DH6OPT_VENDOR_OPTS,        "vendor-specific-info" },
216	{ DH6OPT_INTERFACE_ID,       "interface-ID"         },
217	{ DH6OPT_RECONF_MSG,         "reconfigure-message"  },
218	{ DH6OPT_RECONF_ACCEPT,      "reconfigure-accept"   },
219	{ DH6OPT_SIP_SERVER_D,       "SIP-servers-domain"   },
220	{ DH6OPT_SIP_SERVER_A,       "SIP-servers-address"  },
221	{ DH6OPT_DNS_SERVERS,        "DNS-server"           },
222	{ DH6OPT_DOMAIN_LIST,        "DNS-search-list"      },
223	{ DH6OPT_IA_PD,              "IA_PD"                },
224	{ DH6OPT_IA_PD_PREFIX,       "IA_PD-prefix"         },
225	{ DH6OPT_SNTP_SERVERS,       "SNTP-servers"         },
226	{ DH6OPT_LIFETIME,           "lifetime"             },
227	{ DH6OPT_NIS_SERVERS,        "NIS-server"           },
228	{ DH6OPT_NISP_SERVERS,       "NIS+-server"          },
229	{ DH6OPT_NIS_NAME,           "NIS-domain-name"      },
230	{ DH6OPT_NISP_NAME,          "NIS+-domain-name"     },
231	{ DH6OPT_BCMCS_SERVER_D,     "BCMCS-domain-name"    },
232	{ DH6OPT_BCMCS_SERVER_A,     "BCMCS-server"         },
233	{ DH6OPT_GEOCONF_CIVIC,      "Geoconf-Civic"        },
234	{ DH6OPT_REMOTE_ID,          "Remote-ID"            },
235	{ DH6OPT_SUBSCRIBER_ID,      "Subscriber-ID"        },
236	{ DH6OPT_CLIENT_FQDN,        "Client-FQDN"          },
237	{ DH6OPT_PANA_AGENT,         "PANA-agent"           },
238	{ DH6OPT_NEW_POSIX_TIMEZONE, "POSIX-timezone"       },
239	{ DH6OPT_NEW_TZDB_TIMEZONE,  "POSIX-tz-database"    },
240	{ DH6OPT_ERO,                "Echo-request-option"  },
241	{ DH6OPT_LQ_QUERY,           "Lease-query"          },
242	{ DH6OPT_CLIENT_DATA,        "LQ-client-data"       },
243	{ DH6OPT_CLT_TIME,           "Clt-time"             },
244	{ DH6OPT_LQ_RELAY_DATA,      "LQ-relay-data"        },
245	{ DH6OPT_LQ_CLIENT_LINK,     "LQ-client-link"       },
246	{ DH6OPT_NTP_SERVER,         "NTP-server"           },
247	{ DH6OPT_AFTR_NAME,          "AFTR-Name"            },
248	{ DH6OPT_MUDURL,             "MUD-URL"              },
249	{ 0, NULL }
250};
251
252static const struct tok dh6opt_stcode_str[] = {
253	{ DH6OPT_STCODE_SUCCESS,          "Success"          }, /* RFC3315 */
254	{ DH6OPT_STCODE_UNSPECFAIL,       "UnspecFail"       }, /* RFC3315 */
255	{ DH6OPT_STCODE_NOADDRAVAIL,      "NoAddrsAvail"     }, /* RFC3315 */
256	{ DH6OPT_STCODE_NOBINDING,        "NoBinding"        }, /* RFC3315 */
257	{ DH6OPT_STCODE_NOTONLINK,        "NotOnLink"        }, /* RFC3315 */
258	{ DH6OPT_STCODE_USEMULTICAST,     "UseMulticast"     }, /* RFC3315 */
259	{ DH6OPT_STCODE_NOPREFIXAVAIL,    "NoPrefixAvail"    }, /* RFC3633 */
260	{ DH6OPT_STCODE_UNKNOWNQUERYTYPE, "UnknownQueryType" }, /* RFC5007 */
261	{ DH6OPT_STCODE_MALFORMEDQUERY,   "MalformedQuery"   }, /* RFC5007 */
262	{ DH6OPT_STCODE_NOTCONFIGURED,    "NotConfigured"    }, /* RFC5007 */
263	{ DH6OPT_STCODE_NOTALLOWED,       "NotAllowed"       }, /* RFC5007 */
264	{ 0, NULL }
265};
266
267struct dhcp6opt {
268	nd_uint16_t dh6opt_type;
269	nd_uint16_t dh6opt_len;
270	/* type-dependent data follows */
271};
272
273static const char *
274dhcp6stcode(const uint16_t code)
275{
276	return code > 255 ? "INVALID code" : tok2str(dh6opt_stcode_str, "code%u", code);
277}
278
279static void
280dhcp6opt_print(netdissect_options *ndo,
281               const u_char *cp, const u_char *ep)
282{
283	const struct dhcp6opt *dh6o;
284	const u_char *tp;
285	u_int i;
286	uint16_t opttype;
287	uint16_t optlen;
288	uint8_t auth_proto;
289	uint8_t auth_alg;
290	uint8_t auth_rdm;
291	u_int authinfolen, authrealmlen;
292	u_int remain_len;  /* Length of remaining options */
293	u_int label_len;   /* Label length */
294	uint16_t subopt_code;
295	uint16_t subopt_len;
296	uint8_t dh6_reconf_type;
297	uint8_t dh6_lq_query_type;
298
299	if (cp == ep)
300		return;
301	while (cp < ep) {
302		if (ep < cp + sizeof(*dh6o))
303			goto trunc;
304		dh6o = (const struct dhcp6opt *)cp;
305		ND_TCHECK_SIZE(dh6o);
306		optlen = GET_BE_U_2(dh6o->dh6opt_len);
307		if (ep < cp + sizeof(*dh6o) + optlen)
308			goto trunc;
309		opttype = GET_BE_U_2(dh6o->dh6opt_type);
310		ND_PRINT(" (%s", tok2str(dh6opt_str, "opt_%u", opttype));
311		ND_TCHECK_LEN(cp + sizeof(*dh6o), optlen);
312		switch (opttype) {
313		case DH6OPT_CLIENTID:
314		case DH6OPT_SERVERID:
315			if (optlen < 2) {
316				/*(*/
317				ND_PRINT(" ?)");
318				break;
319			}
320			tp = (const u_char *)(dh6o + 1);
321			switch (GET_BE_U_2(tp)) {
322			case 1:
323				if (optlen >= 2 + 6) {
324					ND_PRINT(" hwaddr/time type %u time %u ",
325					    GET_BE_U_2(tp + 2),
326					    GET_BE_U_4(tp + 4));
327					for (i = 8; i < optlen; i++)
328						ND_PRINT("%02x",
329							 GET_U_1(tp + i));
330					/*(*/
331					ND_PRINT(")");
332				} else {
333					/*(*/
334					ND_PRINT(" ?)");
335				}
336				break;
337			case 2:
338				if (optlen >= 2 + 8) {
339					ND_PRINT(" vid ");
340					for (i = 2; i < 2 + 8; i++)
341						ND_PRINT("%02x",
342							 GET_U_1(tp + i));
343					/*(*/
344					ND_PRINT(")");
345				} else {
346					/*(*/
347					ND_PRINT(" ?)");
348				}
349				break;
350			case 3:
351				if (optlen >= 2 + 2) {
352					ND_PRINT(" hwaddr type %u ",
353					    GET_BE_U_2(tp + 2));
354					for (i = 4; i < optlen; i++)
355						ND_PRINT("%02x",
356							 GET_U_1(tp + i));
357					/*(*/
358					ND_PRINT(")");
359				} else {
360					/*(*/
361					ND_PRINT(" ?)");
362				}
363				break;
364			default:
365				ND_PRINT(" type %u)", GET_BE_U_2(tp));
366				break;
367			}
368			break;
369		case DH6OPT_IA_ADDR:
370			if (optlen < 24) {
371				/*(*/
372				ND_PRINT(" ?)");
373				break;
374			}
375			tp = (const u_char *)(dh6o + 1);
376			ND_PRINT(" %s", GET_IP6ADDR_STRING(tp));
377			ND_PRINT(" pltime:%u vltime:%u",
378			    GET_BE_U_4(tp + 16),
379			    GET_BE_U_4(tp + 20));
380			if (optlen > 24) {
381				/* there are sub-options */
382				dhcp6opt_print(ndo, tp + 24, tp + optlen);
383			}
384			ND_PRINT(")");
385			break;
386		case DH6OPT_ORO:
387		case DH6OPT_ERO:
388			if (optlen % 2) {
389				ND_PRINT(" ?)");
390				break;
391			}
392			tp = (const u_char *)(dh6o + 1);
393			for (i = 0; i < optlen; i += 2) {
394				ND_PRINT(" %s",
395				    tok2str(dh6opt_str, "opt_%u", GET_BE_U_2(tp + i)));
396			}
397			ND_PRINT(")");
398			break;
399		case DH6OPT_PREFERENCE:
400			if (optlen != 1) {
401				ND_PRINT(" ?)");
402				break;
403			}
404			tp = (const u_char *)(dh6o + 1);
405			ND_PRINT(" %u)", GET_U_1(tp));
406			break;
407		case DH6OPT_ELAPSED_TIME:
408			if (optlen != 2) {
409				ND_PRINT(" ?)");
410				break;
411			}
412			tp = (const u_char *)(dh6o + 1);
413			ND_PRINT(" %u)", GET_BE_U_2(tp));
414			break;
415		case DH6OPT_RELAY_MSG:
416		    {
417			const u_char *snapend_save;
418
419			ND_PRINT(" (");
420			tp = (const u_char *)(dh6o + 1);
421			/*
422			 * Update the snapend to the end of the option before
423			 * calling recursively dhcp6_print() for the nested
424			 * packet. Other options may be present after the
425			 * nested DHCPv6 packet. This prevents that, in
426			 * dhcp6_print(), for the nested DHCPv6 packet, the
427			 * remaining length < remaining caplen.
428			 */
429			snapend_save = ndo->ndo_snapend;
430			ndo->ndo_snapend = ND_MIN(tp + optlen, ndo->ndo_snapend);
431			dhcp6_print(ndo, tp, optlen);
432			ndo->ndo_snapend = snapend_save;
433			ND_PRINT(")");
434			break;
435		    }
436		case DH6OPT_AUTH:
437			if (optlen < 11) {
438				ND_PRINT(" ?)");
439				break;
440			}
441			tp = (const u_char *)(dh6o + 1);
442			auth_proto = GET_U_1(tp);
443			switch (auth_proto) {
444			case DH6OPT_AUTHPROTO_DELAYED:
445				ND_PRINT(" proto: delayed");
446				break;
447			case DH6OPT_AUTHPROTO_RECONFIG:
448				ND_PRINT(" proto: reconfigure");
449				break;
450			default:
451				ND_PRINT(" proto: %u", auth_proto);
452				break;
453			}
454			tp++;
455			auth_alg = GET_U_1(tp);
456			switch (auth_alg) {
457			case DH6OPT_AUTHALG_HMACMD5:
458				/* XXX: may depend on the protocol */
459				ND_PRINT(", alg: HMAC-MD5");
460				break;
461			default:
462				ND_PRINT(", alg: %u", auth_alg);
463				break;
464			}
465			tp++;
466			auth_rdm = GET_U_1(tp);
467			switch (auth_rdm) {
468			case DH6OPT_AUTHRDM_MONOCOUNTER:
469				ND_PRINT(", RDM: mono");
470				break;
471			default:
472				ND_PRINT(", RDM: %u", auth_rdm);
473				break;
474			}
475			tp++;
476			ND_PRINT(", RD:");
477			for (i = 0; i < 4; i++, tp += 2)
478				ND_PRINT(" %04x", GET_BE_U_2(tp));
479
480			/* protocol dependent part */
481			authinfolen = optlen - 11;
482			switch (auth_proto) {
483			case DH6OPT_AUTHPROTO_DELAYED:
484				if (authinfolen == 0)
485					break;
486				if (authinfolen < 20) {
487					ND_PRINT(" ??");
488					break;
489				}
490				authrealmlen = authinfolen - 20;
491				if (authrealmlen > 0) {
492					ND_PRINT(", realm: ");
493				}
494				for (i = 0; i < authrealmlen; i++, tp++)
495					ND_PRINT("%02x", GET_U_1(tp));
496				ND_PRINT(", key ID: %08x", GET_BE_U_4(tp));
497				tp += 4;
498				ND_PRINT(", HMAC-MD5:");
499				for (i = 0; i < 4; i++, tp+= 4)
500					ND_PRINT(" %08x", GET_BE_U_4(tp));
501				break;
502			case DH6OPT_AUTHPROTO_RECONFIG:
503				if (authinfolen != 17) {
504					ND_PRINT(" ??");
505					break;
506				}
507				switch (GET_U_1(tp)) {
508				case DH6OPT_AUTHRECONFIG_KEY:
509					ND_PRINT(" reconfig-key");
510					break;
511				case DH6OPT_AUTHRECONFIG_HMACMD5:
512					ND_PRINT(" type: HMAC-MD5");
513					break;
514				default:
515					ND_PRINT(" type: ??");
516					break;
517				}
518				tp++;
519				ND_PRINT(" value:");
520				for (i = 0; i < 4; i++, tp+= 4)
521					ND_PRINT(" %08x", GET_BE_U_4(tp));
522				break;
523			default:
524				ND_PRINT(" ??");
525				break;
526			}
527
528			ND_PRINT(")");
529			break;
530		case DH6OPT_RAPID_COMMIT: /* nothing todo */
531			ND_PRINT(")");
532			break;
533		case DH6OPT_INTERFACE_ID:
534		case DH6OPT_SUBSCRIBER_ID:
535			/*
536			 * Since we cannot predict the encoding, print hex dump
537			 * at most 10 characters.
538			 */
539			tp = (const u_char *)(dh6o + 1);
540			ND_PRINT(" ");
541			for (i = 0; i < optlen && i < 10; i++)
542				ND_PRINT("%02x", GET_U_1(tp + i));
543			ND_PRINT("...)");
544			break;
545		case DH6OPT_RECONF_MSG:
546			if (optlen != 1) {
547				ND_PRINT(" ?)");
548				break;
549			}
550			tp = (const u_char *)(dh6o + 1);
551			dh6_reconf_type = GET_U_1(tp);
552			switch (dh6_reconf_type) {
553			case DH6_RENEW:
554				ND_PRINT(" for renew)");
555				break;
556			case DH6_INFORM_REQ:
557				ND_PRINT(" for inf-req)");
558				break;
559			default:
560				ND_PRINT(" for ?\?\?(%02x))", dh6_reconf_type);
561				break;
562			}
563			break;
564		case DH6OPT_RECONF_ACCEPT: /* nothing todo */
565			ND_PRINT(")");
566			break;
567		case DH6OPT_SIP_SERVER_A:
568		case DH6OPT_DNS_SERVERS:
569		case DH6OPT_SNTP_SERVERS:
570		case DH6OPT_NIS_SERVERS:
571		case DH6OPT_NISP_SERVERS:
572		case DH6OPT_BCMCS_SERVER_A:
573		case DH6OPT_PANA_AGENT:
574		case DH6OPT_LQ_CLIENT_LINK:
575			if (optlen % 16) {
576				ND_PRINT(" ?)");
577				break;
578			}
579			tp = (const u_char *)(dh6o + 1);
580			for (i = 0; i < optlen; i += 16)
581				ND_PRINT(" %s", GET_IP6ADDR_STRING(tp + i));
582			ND_PRINT(")");
583			break;
584		case DH6OPT_SIP_SERVER_D:
585		case DH6OPT_DOMAIN_LIST:
586			tp = (const u_char *)(dh6o + 1);
587			while (tp < cp + sizeof(*dh6o) + optlen) {
588				ND_PRINT(" ");
589				if ((tp = fqdn_print(ndo, tp, cp + sizeof(*dh6o) + optlen)) == NULL)
590					goto trunc;
591			}
592			ND_PRINT(")");
593			break;
594		case DH6OPT_STATUS_CODE:
595			if (optlen < 2) {
596				ND_PRINT(" ?)");
597				break;
598			}
599			tp = (const u_char *)(dh6o + 1);
600			ND_PRINT(" %s)", dhcp6stcode(GET_BE_U_2(tp)));
601			break;
602		case DH6OPT_IA_NA:
603		case DH6OPT_IA_PD:
604			if (optlen < 12) {
605				ND_PRINT(" ?)");
606				break;
607			}
608			tp = (const u_char *)(dh6o + 1);
609			ND_PRINT(" IAID:%u T1:%u T2:%u",
610			    GET_BE_U_4(tp),
611			    GET_BE_U_4(tp + 4),
612			    GET_BE_U_4(tp + 8));
613			if (optlen > 12) {
614				/* there are sub-options */
615				dhcp6opt_print(ndo, tp + 12, tp + optlen);
616			}
617			ND_PRINT(")");
618			break;
619		case DH6OPT_IA_TA:
620			if (optlen < 4) {
621				ND_PRINT(" ?)");
622				break;
623			}
624			tp = (const u_char *)(dh6o + 1);
625			ND_PRINT(" IAID:%u", GET_BE_U_4(tp));
626			if (optlen > 4) {
627				/* there are sub-options */
628				dhcp6opt_print(ndo, tp + 4, tp + optlen);
629			}
630			ND_PRINT(")");
631			break;
632		case DH6OPT_IA_PD_PREFIX:
633			if (optlen < 25) {
634				ND_PRINT(" ?)");
635				break;
636			}
637			tp = (const u_char *)(dh6o + 1);
638			ND_PRINT(" %s/%u", GET_IP6ADDR_STRING(tp + 9),
639				 GET_U_1(tp + 8));
640			ND_PRINT(" pltime:%u vltime:%u",
641			    GET_BE_U_4(tp),
642			    GET_BE_U_4(tp + 4));
643			if (optlen > 25) {
644				/* there are sub-options */
645				dhcp6opt_print(ndo, tp + 25, tp + optlen);
646			}
647			ND_PRINT(")");
648			break;
649		case DH6OPT_LIFETIME:
650		case DH6OPT_CLT_TIME:
651			if (optlen != 4) {
652				ND_PRINT(" ?)");
653				break;
654			}
655			tp = (const u_char *)(dh6o + 1);
656			ND_PRINT(" %u)", GET_BE_U_4(tp));
657			break;
658		case DH6OPT_REMOTE_ID:
659			if (optlen < 4) {
660				ND_PRINT(" ?)");
661				break;
662			}
663			tp = (const u_char *)(dh6o + 1);
664			ND_PRINT(" %u ", GET_BE_U_4(tp));
665			/*
666			 * Print hex dump first 10 characters.
667			 */
668			for (i = 4; i < optlen && i < 14; i++)
669				ND_PRINT("%02x", GET_U_1(tp + i));
670			ND_PRINT("...)");
671			break;
672		case DH6OPT_LQ_QUERY:
673			if (optlen < 17) {
674				ND_PRINT(" ?)");
675				break;
676			}
677			tp = (const u_char *)(dh6o + 1);
678			dh6_lq_query_type = GET_U_1(tp);
679			switch (dh6_lq_query_type) {
680			case 1:
681				ND_PRINT(" by-address");
682				break;
683			case 2:
684				ND_PRINT(" by-clientID");
685				break;
686			default:
687				ND_PRINT(" type_%u", dh6_lq_query_type);
688				break;
689			}
690			ND_PRINT(" %s", GET_IP6ADDR_STRING(tp + 1));
691			if (optlen > 17) {
692				/* there are query-options */
693				dhcp6opt_print(ndo, tp + 17, tp + optlen);
694			}
695			ND_PRINT(")");
696			break;
697		case DH6OPT_CLIENT_DATA:
698			tp = (const u_char *)(dh6o + 1);
699			if (optlen > 0) {
700				/* there are encapsulated options */
701				dhcp6opt_print(ndo, tp, tp + optlen);
702			}
703			ND_PRINT(")");
704			break;
705		case DH6OPT_LQ_RELAY_DATA:
706			if (optlen < 16) {
707				ND_PRINT(" ?)");
708				break;
709			}
710			tp = (const u_char *)(dh6o + 1);
711			ND_PRINT(" %s ", GET_IP6ADDR_STRING(tp));
712			/*
713			 * Print hex dump first 10 characters.
714			 */
715			for (i = 16; i < optlen && i < 26; i++)
716				ND_PRINT("%02x", GET_U_1(tp + i));
717			ND_PRINT("...)");
718			break;
719		case DH6OPT_NTP_SERVER:
720			if (optlen < 4) {
721				ND_PRINT(" ?)");
722				break;
723			}
724			tp = (const u_char *)(dh6o + 1);
725			while (tp < cp + sizeof(*dh6o) + optlen - 4) {
726				subopt_code = GET_BE_U_2(tp);
727				tp += 2;
728				subopt_len = GET_BE_U_2(tp);
729				tp += 2;
730				if (tp + subopt_len > cp + sizeof(*dh6o) + optlen)
731					goto trunc;
732				ND_PRINT(" subopt:%u", subopt_code);
733				switch (subopt_code) {
734				case DH6OPT_NTP_SUBOPTION_SRV_ADDR:
735				case DH6OPT_NTP_SUBOPTION_MC_ADDR:
736					if (subopt_len != 16) {
737						ND_PRINT(" ?");
738						break;
739					}
740					ND_PRINT(" %s", GET_IP6ADDR_STRING(tp));
741					break;
742				case DH6OPT_NTP_SUBOPTION_SRV_FQDN:
743					ND_PRINT(" ");
744					if (fqdn_print(ndo, tp, tp + subopt_len) == NULL)
745						goto trunc;
746					break;
747				default:
748					ND_PRINT(" ?");
749					break;
750				}
751				tp += subopt_len;
752			}
753			ND_PRINT(")");
754			break;
755		case DH6OPT_AFTR_NAME:
756			if (optlen < 3) {
757				ND_PRINT(" ?)");
758				break;
759			}
760			tp = (const u_char *)(dh6o + 1);
761			remain_len = optlen;
762			ND_PRINT(" ");
763			/* Encoding is described in section 3.1 of RFC 1035 */
764			while (remain_len && GET_U_1(tp)) {
765				label_len = GET_U_1(tp);
766				tp++;
767				if (label_len < remain_len - 1) {
768					nd_printjnp(ndo, tp, label_len);
769					tp += label_len;
770					remain_len -= (label_len + 1);
771					if(GET_U_1(tp)) ND_PRINT(".");
772				} else {
773					ND_PRINT(" ?");
774					break;
775				}
776			}
777			ND_PRINT(")");
778			break;
779		case DH6OPT_NEW_POSIX_TIMEZONE: /* all three of these options */
780		case DH6OPT_NEW_TZDB_TIMEZONE:	/* are encoded similarly */
781		case DH6OPT_MUDURL:		/* although GMT might not work */
782		        if (optlen < 5) {
783				ND_PRINT(" ?)");
784				break;
785			}
786			tp = (const u_char *)(dh6o + 1);
787			ND_PRINT(" ");
788			nd_printjnp(ndo, tp, optlen);
789			ND_PRINT(")");
790			break;
791
792		default:
793			ND_PRINT(")");
794			break;
795		}
796
797		cp += sizeof(*dh6o) + optlen;
798	}
799	return;
800
801trunc:
802	nd_print_trunc(ndo);
803}
804
805/*
806 * Print dhcp6 packets
807 */
808void
809dhcp6_print(netdissect_options *ndo,
810            const u_char *cp, u_int length)
811{
812	const struct dhcp6 *dh6;
813	const struct dhcp6_relay *dh6relay;
814	uint8_t msgtype;
815	const u_char *ep;
816	const u_char *extp;
817	const char *name;
818
819	ndo->ndo_protocol = "dhcp6";
820	ND_PRINT("dhcp6");
821
822	ep = ndo->ndo_snapend;
823	if (cp + length < ep)
824		ep = cp + length;
825
826	dh6 = (const struct dhcp6 *)cp;
827	dh6relay = (const struct dhcp6_relay *)cp;
828	ND_TCHECK_4(dh6->dh6_msgtypexid.xid);
829	msgtype = GET_U_1(dh6->dh6_msgtypexid.msgtype);
830	name = tok2str(dh6_msgtype_str, "msgtype-%u", msgtype);
831
832	if (!ndo->ndo_vflag) {
833		ND_PRINT(" %s", name);
834		return;
835	}
836
837	/* XXX relay agent messages have to be handled differently */
838
839	ND_PRINT(" %s (", name);	/*)*/
840	if (msgtype != DH6_RELAY_FORW && msgtype != DH6_RELAY_REPLY) {
841		ND_PRINT("xid=%x",
842			 GET_BE_U_4(dh6->dh6_msgtypexid.xid) & DH6_XIDMASK);
843		extp = (const u_char *)(dh6 + 1);
844		dhcp6opt_print(ndo, extp, ep);
845	} else {		/* relay messages */
846		ND_PRINT("linkaddr=%s", GET_IP6ADDR_STRING(dh6relay->dh6relay_linkaddr));
847
848		ND_PRINT(" peeraddr=%s", GET_IP6ADDR_STRING(dh6relay->dh6relay_peeraddr));
849
850		dhcp6opt_print(ndo, (const u_char *)(dh6relay + 1), ep);
851	}
852	/*(*/
853	ND_PRINT(")");
854	return;
855
856trunc:
857	nd_print_trunc(ndo);
858}
859