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