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