xref: /netbsd-src/external/bsd/tcpdump/dist/print-ip6opts.c (revision ccd9df534e375a4366c5b55f23782053c7a98d82)
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 
49 static int
50 ip6_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 
90 trunc:
91     return -1;
92 }
93 
94 static int
95 ip6_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 
219 trunc:
220     return -1;
221 }
222 
223 int
224 hbhopt_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 
239 trunc:
240     nd_print_trunc(ndo);
241     return -1;
242 }
243 
244 int
245 dstopt_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 
267 trunc:
268     nd_print_trunc(ndo);
269     return -1;
270 }
271