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