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