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