xref: /freebsd/contrib/tcpdump/print-ip6opts.c (revision e6083790f217ba7f89cd2957922bd45e35466359)
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