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.4 2014/11/20 03:05:03 christos Exp $");
33 #endif
34 
35 #define NETDISSECT_REWORKED
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39 
40 #ifdef INET6
41 #include <tcpdump-stdinc.h>
42 
43 #include "ip6.h"
44 
45 #include "interface.h"
46 #include "addrtoname.h"
47 #include "extract.h"
48 
49 static void
ip6_sopt_print(netdissect_options * ndo,const u_char * bp,int len)50 ip6_sopt_print(netdissect_options *ndo, const u_char *bp, int len)
51 {
52     int i;
53     int optlen;
54 
55     for (i = 0; i < len; i += optlen) {
56 	if (bp[i] == IP6OPT_PAD1)
57 	    optlen = 1;
58 	else {
59 	    if (i + 1 < len)
60 		optlen = bp[i + 1] + 2;
61 	    else
62 		goto trunc;
63 	}
64 	if (i + optlen > len)
65 	    goto trunc;
66 
67 	switch (bp[i]) {
68 	case IP6OPT_PAD1:
69             ND_PRINT((ndo, ", pad1"));
70 	    break;
71 	case IP6OPT_PADN:
72 	    if (len - i < IP6OPT_MINLEN) {
73 		ND_PRINT((ndo, ", padn: trunc"));
74 		goto trunc;
75 	    }
76             ND_PRINT((ndo, ", padn"));
77 	    break;
78 	default:
79 	    if (len - i < IP6OPT_MINLEN) {
80 		ND_PRINT((ndo, ", sopt_type %d: trunc)", bp[i]));
81 		goto trunc;
82 	    }
83 	    ND_PRINT((ndo, ", sopt_type 0x%02x: len=%d", bp[i], bp[i + 1]));
84 	    break;
85 	}
86     }
87     return;
88 
89 trunc:
90     ND_PRINT((ndo, "[trunc] "));
91 }
92 
93 static void
ip6_opt_print(netdissect_options * ndo,const u_char * bp,int len)94 ip6_opt_print(netdissect_options *ndo, const u_char *bp, int len)
95 {
96     int i;
97     int optlen = 0;
98 
99     if (len == 0)
100         return;
101     for (i = 0; i < len; i += optlen) {
102 	if (bp[i] == IP6OPT_PAD1)
103 	    optlen = 1;
104 	else {
105 	    if (i + 1 < len)
106 		optlen = bp[i + 1] + 2;
107 	    else
108 		goto trunc;
109 	}
110 	if (i + optlen > len)
111 	    goto trunc;
112 
113 	switch (bp[i]) {
114 	case IP6OPT_PAD1:
115             ND_PRINT((ndo, "(pad1)"));
116 	    break;
117 	case IP6OPT_PADN:
118 	    if (len - i < IP6OPT_MINLEN) {
119 		ND_PRINT((ndo, "(padn: trunc)"));
120 		goto trunc;
121 	    }
122             ND_PRINT((ndo, "(padn)"));
123 	    break;
124 	case IP6OPT_ROUTER_ALERT:
125 	    if (len - i < IP6OPT_RTALERT_LEN) {
126 		ND_PRINT((ndo, "(rtalert: trunc)"));
127 		goto trunc;
128 	    }
129 	    if (bp[i + 1] != IP6OPT_RTALERT_LEN - 2) {
130 		ND_PRINT((ndo, "(rtalert: invalid len %d)", bp[i + 1]));
131 		goto trunc;
132 	    }
133 	    ND_PRINT((ndo, "(rtalert: 0x%04x) ", EXTRACT_16BITS(&bp[i + 2])));
134 	    break;
135 	case IP6OPT_JUMBO:
136 	    if (len - i < IP6OPT_JUMBO_LEN) {
137 		ND_PRINT((ndo, "(jumbo: trunc)"));
138 		goto trunc;
139 	    }
140 	    if (bp[i + 1] != IP6OPT_JUMBO_LEN - 2) {
141 		ND_PRINT((ndo, "(jumbo: invalid len %d)", bp[i + 1]));
142 		goto trunc;
143 	    }
144 	    ND_PRINT((ndo, "(jumbo: %u) ", EXTRACT_32BITS(&bp[i + 2])));
145 	    break;
146         case IP6OPT_HOME_ADDRESS:
147 	    if (len - i < IP6OPT_HOMEADDR_MINLEN) {
148 		ND_PRINT((ndo, "(homeaddr: trunc)"));
149 		goto trunc;
150 	    }
151 	    if (bp[i + 1] < IP6OPT_HOMEADDR_MINLEN - 2) {
152 		ND_PRINT((ndo, "(homeaddr: invalid len %d)", bp[i + 1]));
153 		goto trunc;
154 	    }
155 	    ND_PRINT((ndo, "(homeaddr: %s", ip6addr_string(ndo, &bp[i + 2])));
156             if (bp[i + 1] > IP6OPT_HOMEADDR_MINLEN - 2) {
157 		ip6_sopt_print(ndo, &bp[i + IP6OPT_HOMEADDR_MINLEN],
158 		    (optlen - IP6OPT_HOMEADDR_MINLEN));
159 	    }
160             ND_PRINT((ndo, ")"));
161 	    break;
162 	default:
163 	    if (len - i < IP6OPT_MINLEN) {
164 		ND_PRINT((ndo, "(type %d: trunc)", bp[i]));
165 		goto trunc;
166 	    }
167 	    ND_PRINT((ndo, "(opt_type 0x%02x: len=%d)", bp[i], bp[i + 1]));
168 	    break;
169 	}
170     }
171     ND_PRINT((ndo, " "));
172     return;
173 
174 trunc:
175     ND_PRINT((ndo, "[trunc] "));
176 }
177 
178 int
hbhopt_print(netdissect_options * ndo,register const u_char * bp)179 hbhopt_print(netdissect_options *ndo, register const u_char *bp)
180 {
181     const struct ip6_hbh *dp = (struct ip6_hbh *)bp;
182     int hbhlen = 0;
183 
184     ND_TCHECK(dp->ip6h_len);
185     hbhlen = (int)((dp->ip6h_len + 1) << 3);
186     ND_TCHECK2(*dp, hbhlen);
187     ND_PRINT((ndo, "HBH "));
188     if (ndo->ndo_vflag)
189 	ip6_opt_print(ndo, (const u_char *)dp + sizeof(*dp), hbhlen - sizeof(*dp));
190 
191     return(hbhlen);
192 
193   trunc:
194     ND_PRINT((ndo, "[|HBH]"));
195     return(-1);
196 }
197 
198 int
dstopt_print(netdissect_options * ndo,register const u_char * bp)199 dstopt_print(netdissect_options *ndo, register const u_char *bp)
200 {
201     const struct ip6_dest *dp = (struct ip6_dest *)bp;
202     int dstoptlen = 0;
203 
204     ND_TCHECK(dp->ip6d_len);
205     dstoptlen = (int)((dp->ip6d_len + 1) << 3);
206     ND_TCHECK2(*dp, dstoptlen);
207     ND_PRINT((ndo, "DSTOPT "));
208     if (ndo->ndo_vflag) {
209 	ip6_opt_print(ndo, (const u_char *)dp + sizeof(*dp),
210 	    dstoptlen - sizeof(*dp));
211     }
212 
213     return(dstoptlen);
214 
215   trunc:
216     ND_PRINT((ndo, "[|DSTOPT]"));
217     return(-1);
218 }
219 #endif /* INET6 */
220