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