1/*
2 * Copyright (C) 1998 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#include <sys/cdefs.h>
31#ifndef lint
32__RCSID("$NetBSD: print-ip6opts.c,v 1.8 2023/08/17 20:19:40 christos Exp $");
33#endif
34
35/* \summary: IPv6 header option printer */
36
37#ifdef HAVE_CONFIG_H
38#include <config.h>
39#endif
40
41#include "netdissect-stdinc.h"
42
43#include "netdissect.h"
44#include "addrtoname.h"
45#include "extract.h"
46
47#include "ip6.h"
48
49static int
50ip6_sopt_print(netdissect_options *ndo, const u_char *bp, int len)
51{
52    int i;
53    int optlen;
54
55    for (i = 0; i < len; i += optlen) {
56	if (GET_U_1(bp + i) == IP6OPT_PAD1)
57	    optlen = 1;
58	else {
59	    if (i + 1 < len)
60		optlen = GET_U_1(bp + i + 1) + 2;
61	    else
62		goto trunc;
63	}
64	if (i + optlen > len)
65	    goto trunc;
66
67	switch (GET_U_1(bp + i)) {
68	case IP6OPT_PAD1:
69            ND_PRINT(", pad1");
70	    break;
71	case IP6OPT_PADN:
72	    if (len - i < IP6OPT_MINLEN) {
73		ND_PRINT(", padn: trunc");
74		goto trunc;
75	    }
76            ND_PRINT(", padn");
77	    break;
78	default:
79	    if (len - i < IP6OPT_MINLEN) {
80		ND_PRINT(", sopt_type %u: trunc)", GET_U_1(bp + i));
81		goto trunc;
82	    }
83	    ND_PRINT(", sopt_type 0x%02x: len=%u", GET_U_1(bp + i),
84                     GET_U_1(bp + i + 1));
85	    break;
86	}
87    }
88    return 0;
89
90trunc:
91    return -1;
92}
93
94static int
95ip6_opt_process(netdissect_options *ndo, const u_char *bp, int len,
96		int *found_jumbop, uint32_t *payload_len)
97{
98    int i;
99    int optlen = 0;
100    int found_jumbo = 0;
101    uint32_t jumbolen = 0;
102
103    if (len == 0)
104        return 0;
105    for (i = 0; i < len; i += optlen) {
106	if (GET_U_1(bp + i) == IP6OPT_PAD1)
107	    optlen = 1;
108	else {
109	    if (i + 1 < len)
110		optlen = GET_U_1(bp + i + 1) + 2;
111	    else
112		goto trunc;
113	}
114	if (i + optlen > len)
115	    goto trunc;
116
117	switch (GET_U_1(bp + i)) {
118	case IP6OPT_PAD1:
119	    if (ndo->ndo_vflag)
120                ND_PRINT("(pad1)");
121	    break;
122	case IP6OPT_PADN:
123	    if (len - i < IP6OPT_MINLEN) {
124		ND_PRINT("(padn: trunc)");
125		goto trunc;
126	    }
127	    if (ndo->ndo_vflag)
128                ND_PRINT("(padn)");
129	    break;
130	case IP6OPT_ROUTER_ALERT:
131	    if (len - i < IP6OPT_RTALERT_LEN) {
132		ND_PRINT("(rtalert: trunc)");
133		goto trunc;
134	    }
135	    if (GET_U_1(bp + i + 1) != IP6OPT_RTALERT_LEN - 2) {
136		ND_PRINT("(rtalert: invalid len %u)", GET_U_1(bp + i + 1));
137		goto trunc;
138	    }
139	    if (ndo->ndo_vflag)
140		ND_PRINT("(rtalert: 0x%04x) ", GET_BE_U_2(bp + i + 2));
141	    break;
142	case IP6OPT_JUMBO:
143	    if (len - i < IP6OPT_JUMBO_LEN) {
144		ND_PRINT("(jumbo: trunc)");
145		goto trunc;
146	    }
147	    if (GET_U_1(bp + i + 1) != IP6OPT_JUMBO_LEN - 2) {
148		ND_PRINT("(jumbo: invalid len %u)", GET_U_1(bp + i + 1));
149		goto trunc;
150	    }
151	    jumbolen = GET_BE_U_4(bp + i + 2);
152	    if (found_jumbo) {
153		/* More than one Jumbo Payload option */
154		if (ndo->ndo_vflag)
155		    ND_PRINT("(jumbo: %u - already seen) ", jumbolen);
156	    } else {
157		found_jumbo = 1;
158		if (payload_len == NULL) {
159		    /* Not a hop-by-hop option - not valid */
160		    if (ndo->ndo_vflag)
161			ND_PRINT("(jumbo: %u - not a hop-by-hop option) ", jumbolen);
162		} else if (*payload_len != 0) {
163		    /* Payload length was non-zero - not valid */
164		    if (ndo->ndo_vflag)
165			ND_PRINT("(jumbo: %u - payload len != 0) ", jumbolen);
166		} else {
167		    /*
168		     * This is a hop-by-hop option, and Payload length
169		     * was zero in the IPv6 header.
170		     */
171		    if (jumbolen < 65536) {
172			/* Too short */
173			if (ndo->ndo_vflag)
174			    ND_PRINT("(jumbo: %u - < 65536) ", jumbolen);
175		    } else {
176			/* OK, this is valid */
177			*found_jumbop = 1;
178			*payload_len = jumbolen;
179			if (ndo->ndo_vflag)
180			    ND_PRINT("(jumbo: %u) ", jumbolen);
181		    }
182		}
183	    }
184	    break;
185        case IP6OPT_HOME_ADDRESS:
186	    if (len - i < IP6OPT_HOMEADDR_MINLEN) {
187		ND_PRINT("(homeaddr: trunc)");
188		goto trunc;
189	    }
190	    if (GET_U_1(bp + i + 1) < IP6OPT_HOMEADDR_MINLEN - 2) {
191		ND_PRINT("(homeaddr: invalid len %u)", GET_U_1(bp + i + 1));
192		goto trunc;
193	    }
194	    if (ndo->ndo_vflag) {
195		ND_PRINT("(homeaddr: %s", GET_IP6ADDR_STRING(bp + i + 2));
196		if (GET_U_1(bp + i + 1) > IP6OPT_HOMEADDR_MINLEN - 2) {
197		    if (ip6_sopt_print(ndo, bp + i + IP6OPT_HOMEADDR_MINLEN,
198				       (optlen - IP6OPT_HOMEADDR_MINLEN)) == -1)
199			goto trunc;
200		}
201		ND_PRINT(")");
202	    }
203	    break;
204	default:
205	    if (len - i < IP6OPT_MINLEN) {
206		ND_PRINT("(type %u: trunc)", GET_U_1(bp + i));
207		goto trunc;
208	    }
209	    if (ndo->ndo_vflag)
210		ND_PRINT("(opt_type 0x%02x: len=%u)", GET_U_1(bp + i),
211			 GET_U_1(bp + i + 1));
212	    break;
213	}
214    }
215    if (ndo->ndo_vflag)
216        ND_PRINT(" ");
217    return 0;
218
219trunc:
220    return -1;
221}
222
223int
224hbhopt_process(netdissect_options *ndo, const u_char *bp, int *found_jumbo,
225	       uint32_t *jumbolen)
226{
227    const struct ip6_hbh *dp = (const struct ip6_hbh *)bp;
228    u_int hbhlen = 0;
229
230    ndo->ndo_protocol = "hbhopt";
231    hbhlen = (GET_U_1(dp->ip6h_len) + 1) << 3;
232    ND_TCHECK_LEN(dp, hbhlen);
233    ND_PRINT("HBH ");
234    if (ip6_opt_process(ndo, (const u_char *)dp + sizeof(*dp),
235			hbhlen - sizeof(*dp), found_jumbo, jumbolen) == -1)
236	goto trunc;
237    return hbhlen;
238
239trunc:
240    nd_print_trunc(ndo);
241    return -1;
242}
243
244int
245dstopt_process(netdissect_options *ndo, const u_char *bp)
246{
247    const struct ip6_dest *dp = (const struct ip6_dest *)bp;
248    u_int dstoptlen = 0;
249
250    ndo->ndo_protocol = "dstopt";
251    dstoptlen = (GET_U_1(dp->ip6d_len) + 1) << 3;
252    ND_TCHECK_LEN(dp, dstoptlen);
253    ND_PRINT("DSTOPT ");
254    if (ndo->ndo_vflag) {
255	/*
256	 * The Jumbo Payload option is a hop-by-hop option; we don't
257	 * honor Jumbo Payload destination options, reporting them
258	 * as invalid.
259	 */
260	if (ip6_opt_process(ndo, (const u_char *)dp + sizeof(*dp),
261			    dstoptlen - sizeof(*dp), NULL, NULL) == -1)
262	    goto trunc;
263    }
264
265    return dstoptlen;
266
267trunc:
268    nd_print_trunc(ndo);
269    return -1;
270}
271