1 | #include <machine/rtems-bsd-kernel-space.h> |
---|
2 | |
---|
3 | /*- |
---|
4 | * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project. |
---|
5 | * All rights reserved. |
---|
6 | * |
---|
7 | * Redistribution and use in source and binary forms, with or without |
---|
8 | * modification, are permitted provided that the following conditions |
---|
9 | * are met: |
---|
10 | * 1. Redistributions of source code must retain the above copyright |
---|
11 | * notice, this list of conditions and the following disclaimer. |
---|
12 | * 2. Redistributions in binary form must reproduce the above copyright |
---|
13 | * notice, this list of conditions and the following disclaimer in the |
---|
14 | * documentation and/or other materials provided with the distribution. |
---|
15 | * 3. Neither the name of the project nor the names of its contributors |
---|
16 | * may be used to endorse or promote products derived from this software |
---|
17 | * without specific prior written permission. |
---|
18 | * |
---|
19 | * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND |
---|
20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
---|
21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
---|
22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE |
---|
23 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
---|
24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
---|
25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
---|
26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
---|
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
---|
28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
---|
29 | * SUCH DAMAGE. |
---|
30 | * |
---|
31 | * $KAME: frag6.c,v 1.33 2002/01/07 11:34:48 kjc Exp $ |
---|
32 | */ |
---|
33 | |
---|
34 | #include <sys/cdefs.h> |
---|
35 | __FBSDID("$FreeBSD$"); |
---|
36 | |
---|
37 | #include <rtems/bsd/sys/param.h> |
---|
38 | #include <sys/systm.h> |
---|
39 | #include <sys/malloc.h> |
---|
40 | #include <sys/mbuf.h> |
---|
41 | #include <sys/domain.h> |
---|
42 | #include <sys/protosw.h> |
---|
43 | #include <sys/socket.h> |
---|
44 | #include <rtems/bsd/sys/errno.h> |
---|
45 | #include <rtems/bsd/sys/time.h> |
---|
46 | #include <sys/kernel.h> |
---|
47 | #include <sys/syslog.h> |
---|
48 | |
---|
49 | #include <net/if.h> |
---|
50 | #include <net/route.h> |
---|
51 | #include <net/vnet.h> |
---|
52 | |
---|
53 | #include <netinet/in.h> |
---|
54 | #include <netinet/in_var.h> |
---|
55 | #include <netinet/ip6.h> |
---|
56 | #include <netinet6/ip6_var.h> |
---|
57 | #include <netinet/icmp6.h> |
---|
58 | #include <netinet/in_systm.h> /* for ECN definitions */ |
---|
59 | #include <netinet/ip.h> /* for ECN definitions */ |
---|
60 | |
---|
61 | #include <security/mac/mac_framework.h> |
---|
62 | |
---|
63 | /* |
---|
64 | * Define it to get a correct behavior on per-interface statistics. |
---|
65 | * You will need to perform an extra routing table lookup, per fragment, |
---|
66 | * to do it. This may, or may not be, a performance hit. |
---|
67 | */ |
---|
68 | #define IN6_IFSTAT_STRICT |
---|
69 | |
---|
70 | static void frag6_enq(struct ip6asfrag *, struct ip6asfrag *); |
---|
71 | static void frag6_deq(struct ip6asfrag *); |
---|
72 | static void frag6_insque(struct ip6q *, struct ip6q *); |
---|
73 | static void frag6_remque(struct ip6q *); |
---|
74 | static void frag6_freef(struct ip6q *); |
---|
75 | |
---|
76 | static struct mtx ip6qlock; |
---|
77 | /* |
---|
78 | * These fields all protected by ip6qlock. |
---|
79 | */ |
---|
80 | static VNET_DEFINE(u_int, frag6_nfragpackets); |
---|
81 | static VNET_DEFINE(u_int, frag6_nfrags); |
---|
82 | static VNET_DEFINE(struct ip6q, ip6q); /* ip6 reassemble queue */ |
---|
83 | |
---|
84 | #define V_frag6_nfragpackets VNET(frag6_nfragpackets) |
---|
85 | #define V_frag6_nfrags VNET(frag6_nfrags) |
---|
86 | #define V_ip6q VNET(ip6q) |
---|
87 | |
---|
88 | #define IP6Q_LOCK_INIT() mtx_init(&ip6qlock, "ip6qlock", NULL, MTX_DEF); |
---|
89 | #define IP6Q_LOCK() mtx_lock(&ip6qlock) |
---|
90 | #define IP6Q_TRYLOCK() mtx_trylock(&ip6qlock) |
---|
91 | #define IP6Q_LOCK_ASSERT() mtx_assert(&ip6qlock, MA_OWNED) |
---|
92 | #define IP6Q_UNLOCK() mtx_unlock(&ip6qlock) |
---|
93 | |
---|
94 | static MALLOC_DEFINE(M_FTABLE, "fragment", "fragment reassembly header"); |
---|
95 | |
---|
96 | /* |
---|
97 | * Initialise reassembly queue and fragment identifier. |
---|
98 | */ |
---|
99 | static void |
---|
100 | frag6_change(void *tag) |
---|
101 | { |
---|
102 | |
---|
103 | V_ip6_maxfragpackets = nmbclusters / 4; |
---|
104 | V_ip6_maxfrags = nmbclusters / 4; |
---|
105 | } |
---|
106 | |
---|
107 | void |
---|
108 | frag6_init(void) |
---|
109 | { |
---|
110 | |
---|
111 | V_ip6_maxfragpackets = nmbclusters / 4; |
---|
112 | V_ip6_maxfrags = nmbclusters / 4; |
---|
113 | V_ip6q.ip6q_next = V_ip6q.ip6q_prev = &V_ip6q; |
---|
114 | |
---|
115 | if (!IS_DEFAULT_VNET(curvnet)) |
---|
116 | return; |
---|
117 | |
---|
118 | EVENTHANDLER_REGISTER(nmbclusters_change, |
---|
119 | frag6_change, NULL, EVENTHANDLER_PRI_ANY); |
---|
120 | |
---|
121 | IP6Q_LOCK_INIT(); |
---|
122 | } |
---|
123 | |
---|
124 | /* |
---|
125 | * In RFC2460, fragment and reassembly rule do not agree with each other, |
---|
126 | * in terms of next header field handling in fragment header. |
---|
127 | * While the sender will use the same value for all of the fragmented packets, |
---|
128 | * receiver is suggested not to check the consistency. |
---|
129 | * |
---|
130 | * fragment rule (p20): |
---|
131 | * (2) A Fragment header containing: |
---|
132 | * The Next Header value that identifies the first header of |
---|
133 | * the Fragmentable Part of the original packet. |
---|
134 | * -> next header field is same for all fragments |
---|
135 | * |
---|
136 | * reassembly rule (p21): |
---|
137 | * The Next Header field of the last header of the Unfragmentable |
---|
138 | * Part is obtained from the Next Header field of the first |
---|
139 | * fragment's Fragment header. |
---|
140 | * -> should grab it from the first fragment only |
---|
141 | * |
---|
142 | * The following note also contradicts with fragment rule - noone is going to |
---|
143 | * send different fragment with different next header field. |
---|
144 | * |
---|
145 | * additional note (p22): |
---|
146 | * The Next Header values in the Fragment headers of different |
---|
147 | * fragments of the same original packet may differ. Only the value |
---|
148 | * from the Offset zero fragment packet is used for reassembly. |
---|
149 | * -> should grab it from the first fragment only |
---|
150 | * |
---|
151 | * There is no explicit reason given in the RFC. Historical reason maybe? |
---|
152 | */ |
---|
153 | /* |
---|
154 | * Fragment input |
---|
155 | */ |
---|
156 | int |
---|
157 | frag6_input(struct mbuf **mp, int *offp, int proto) |
---|
158 | { |
---|
159 | struct mbuf *m = *mp, *t; |
---|
160 | struct ip6_hdr *ip6; |
---|
161 | struct ip6_frag *ip6f; |
---|
162 | struct ip6q *q6; |
---|
163 | struct ip6asfrag *af6, *ip6af, *af6dwn; |
---|
164 | #ifdef IN6_IFSTAT_STRICT |
---|
165 | struct in6_ifaddr *ia; |
---|
166 | #endif |
---|
167 | int offset = *offp, nxt, i, next; |
---|
168 | int first_frag = 0; |
---|
169 | int fragoff, frgpartlen; /* must be larger than u_int16_t */ |
---|
170 | struct ifnet *dstifp; |
---|
171 | u_int8_t ecn, ecn0; |
---|
172 | #if 0 |
---|
173 | char ip6buf[INET6_ADDRSTRLEN]; |
---|
174 | #endif |
---|
175 | |
---|
176 | ip6 = mtod(m, struct ip6_hdr *); |
---|
177 | #ifndef PULLDOWN_TEST |
---|
178 | IP6_EXTHDR_CHECK(m, offset, sizeof(struct ip6_frag), IPPROTO_DONE); |
---|
179 | ip6f = (struct ip6_frag *)((caddr_t)ip6 + offset); |
---|
180 | #else |
---|
181 | IP6_EXTHDR_GET(ip6f, struct ip6_frag *, m, offset, sizeof(*ip6f)); |
---|
182 | if (ip6f == NULL) |
---|
183 | return (IPPROTO_DONE); |
---|
184 | #endif |
---|
185 | |
---|
186 | dstifp = NULL; |
---|
187 | #ifdef IN6_IFSTAT_STRICT |
---|
188 | /* find the destination interface of the packet. */ |
---|
189 | if ((ia = ip6_getdstifaddr(m)) != NULL) { |
---|
190 | dstifp = ia->ia_ifp; |
---|
191 | ifa_free(&ia->ia_ifa); |
---|
192 | } |
---|
193 | #else |
---|
194 | /* we are violating the spec, this is not the destination interface */ |
---|
195 | if ((m->m_flags & M_PKTHDR) != 0) |
---|
196 | dstifp = m->m_pkthdr.rcvif; |
---|
197 | #endif |
---|
198 | |
---|
199 | /* jumbo payload can't contain a fragment header */ |
---|
200 | if (ip6->ip6_plen == 0) { |
---|
201 | icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, offset); |
---|
202 | in6_ifstat_inc(dstifp, ifs6_reass_fail); |
---|
203 | return IPPROTO_DONE; |
---|
204 | } |
---|
205 | |
---|
206 | /* |
---|
207 | * check whether fragment packet's fragment length is |
---|
208 | * multiple of 8 octets. |
---|
209 | * sizeof(struct ip6_frag) == 8 |
---|
210 | * sizeof(struct ip6_hdr) = 40 |
---|
211 | */ |
---|
212 | if ((ip6f->ip6f_offlg & IP6F_MORE_FRAG) && |
---|
213 | (((ntohs(ip6->ip6_plen) - offset) & 0x7) != 0)) { |
---|
214 | icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, |
---|
215 | offsetof(struct ip6_hdr, ip6_plen)); |
---|
216 | in6_ifstat_inc(dstifp, ifs6_reass_fail); |
---|
217 | return IPPROTO_DONE; |
---|
218 | } |
---|
219 | |
---|
220 | IP6STAT_INC(ip6s_fragments); |
---|
221 | in6_ifstat_inc(dstifp, ifs6_reass_reqd); |
---|
222 | |
---|
223 | /* offset now points to data portion */ |
---|
224 | offset += sizeof(struct ip6_frag); |
---|
225 | |
---|
226 | /* |
---|
227 | * RFC 6946: Handle "atomic" fragments (offset and m bit set to 0) |
---|
228 | * upfront, unrelated to any reassembly. Just skip the fragment header. |
---|
229 | */ |
---|
230 | if ((ip6f->ip6f_offlg & ~IP6F_RESERVED_MASK) == 0) { |
---|
231 | /* XXX-BZ we want dedicated counters for this. */ |
---|
232 | IP6STAT_INC(ip6s_reassembled); |
---|
233 | in6_ifstat_inc(dstifp, ifs6_reass_ok); |
---|
234 | *offp = offset; |
---|
235 | return (ip6f->ip6f_nxt); |
---|
236 | } |
---|
237 | |
---|
238 | IP6Q_LOCK(); |
---|
239 | |
---|
240 | /* |
---|
241 | * Enforce upper bound on number of fragments. |
---|
242 | * If maxfrag is 0, never accept fragments. |
---|
243 | * If maxfrag is -1, accept all fragments without limitation. |
---|
244 | */ |
---|
245 | if (V_ip6_maxfrags < 0) |
---|
246 | ; |
---|
247 | else if (V_frag6_nfrags >= (u_int)V_ip6_maxfrags) |
---|
248 | goto dropfrag; |
---|
249 | |
---|
250 | for (q6 = V_ip6q.ip6q_next; q6 != &V_ip6q; q6 = q6->ip6q_next) |
---|
251 | if (ip6f->ip6f_ident == q6->ip6q_ident && |
---|
252 | IN6_ARE_ADDR_EQUAL(&ip6->ip6_src, &q6->ip6q_src) && |
---|
253 | IN6_ARE_ADDR_EQUAL(&ip6->ip6_dst, &q6->ip6q_dst) |
---|
254 | #ifdef MAC |
---|
255 | && mac_ip6q_match(m, q6) |
---|
256 | #endif |
---|
257 | ) |
---|
258 | break; |
---|
259 | |
---|
260 | if (q6 == &V_ip6q) { |
---|
261 | /* |
---|
262 | * the first fragment to arrive, create a reassembly queue. |
---|
263 | */ |
---|
264 | first_frag = 1; |
---|
265 | |
---|
266 | /* |
---|
267 | * Enforce upper bound on number of fragmented packets |
---|
268 | * for which we attempt reassembly; |
---|
269 | * If maxfragpackets is 0, never accept fragments. |
---|
270 | * If maxfragpackets is -1, accept all fragments without |
---|
271 | * limitation. |
---|
272 | */ |
---|
273 | if (V_ip6_maxfragpackets < 0) |
---|
274 | ; |
---|
275 | else if (V_frag6_nfragpackets >= (u_int)V_ip6_maxfragpackets) |
---|
276 | goto dropfrag; |
---|
277 | V_frag6_nfragpackets++; |
---|
278 | q6 = (struct ip6q *)malloc(sizeof(struct ip6q), M_FTABLE, |
---|
279 | M_NOWAIT); |
---|
280 | if (q6 == NULL) |
---|
281 | goto dropfrag; |
---|
282 | bzero(q6, sizeof(*q6)); |
---|
283 | #ifdef MAC |
---|
284 | if (mac_ip6q_init(q6, M_NOWAIT) != 0) { |
---|
285 | free(q6, M_FTABLE); |
---|
286 | goto dropfrag; |
---|
287 | } |
---|
288 | mac_ip6q_create(m, q6); |
---|
289 | #endif |
---|
290 | frag6_insque(q6, &V_ip6q); |
---|
291 | |
---|
292 | /* ip6q_nxt will be filled afterwards, from 1st fragment */ |
---|
293 | q6->ip6q_down = q6->ip6q_up = (struct ip6asfrag *)q6; |
---|
294 | #ifdef notyet |
---|
295 | q6->ip6q_nxtp = (u_char *)nxtp; |
---|
296 | #endif |
---|
297 | q6->ip6q_ident = ip6f->ip6f_ident; |
---|
298 | q6->ip6q_ttl = IPV6_FRAGTTL; |
---|
299 | q6->ip6q_src = ip6->ip6_src; |
---|
300 | q6->ip6q_dst = ip6->ip6_dst; |
---|
301 | q6->ip6q_ecn = |
---|
302 | (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; |
---|
303 | q6->ip6q_unfrglen = -1; /* The 1st fragment has not arrived. */ |
---|
304 | |
---|
305 | q6->ip6q_nfrag = 0; |
---|
306 | } |
---|
307 | |
---|
308 | /* |
---|
309 | * If it's the 1st fragment, record the length of the |
---|
310 | * unfragmentable part and the next header of the fragment header. |
---|
311 | */ |
---|
312 | fragoff = ntohs(ip6f->ip6f_offlg & IP6F_OFF_MASK); |
---|
313 | if (fragoff == 0) { |
---|
314 | q6->ip6q_unfrglen = offset - sizeof(struct ip6_hdr) - |
---|
315 | sizeof(struct ip6_frag); |
---|
316 | q6->ip6q_nxt = ip6f->ip6f_nxt; |
---|
317 | } |
---|
318 | |
---|
319 | /* |
---|
320 | * Check that the reassembled packet would not exceed 65535 bytes |
---|
321 | * in size. |
---|
322 | * If it would exceed, discard the fragment and return an ICMP error. |
---|
323 | */ |
---|
324 | frgpartlen = sizeof(struct ip6_hdr) + ntohs(ip6->ip6_plen) - offset; |
---|
325 | if (q6->ip6q_unfrglen >= 0) { |
---|
326 | /* The 1st fragment has already arrived. */ |
---|
327 | if (q6->ip6q_unfrglen + fragoff + frgpartlen > IPV6_MAXPACKET) { |
---|
328 | icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, |
---|
329 | offset - sizeof(struct ip6_frag) + |
---|
330 | offsetof(struct ip6_frag, ip6f_offlg)); |
---|
331 | IP6Q_UNLOCK(); |
---|
332 | return (IPPROTO_DONE); |
---|
333 | } |
---|
334 | } else if (fragoff + frgpartlen > IPV6_MAXPACKET) { |
---|
335 | icmp6_error(m, ICMP6_PARAM_PROB, ICMP6_PARAMPROB_HEADER, |
---|
336 | offset - sizeof(struct ip6_frag) + |
---|
337 | offsetof(struct ip6_frag, ip6f_offlg)); |
---|
338 | IP6Q_UNLOCK(); |
---|
339 | return (IPPROTO_DONE); |
---|
340 | } |
---|
341 | /* |
---|
342 | * If it's the first fragment, do the above check for each |
---|
343 | * fragment already stored in the reassembly queue. |
---|
344 | */ |
---|
345 | if (fragoff == 0) { |
---|
346 | for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; |
---|
347 | af6 = af6dwn) { |
---|
348 | af6dwn = af6->ip6af_down; |
---|
349 | |
---|
350 | if (q6->ip6q_unfrglen + af6->ip6af_off + af6->ip6af_frglen > |
---|
351 | IPV6_MAXPACKET) { |
---|
352 | struct mbuf *merr = IP6_REASS_MBUF(af6); |
---|
353 | struct ip6_hdr *ip6err; |
---|
354 | int erroff = af6->ip6af_offset; |
---|
355 | |
---|
356 | /* dequeue the fragment. */ |
---|
357 | frag6_deq(af6); |
---|
358 | free(af6, M_FTABLE); |
---|
359 | |
---|
360 | /* adjust pointer. */ |
---|
361 | ip6err = mtod(merr, struct ip6_hdr *); |
---|
362 | |
---|
363 | /* |
---|
364 | * Restore source and destination addresses |
---|
365 | * in the erroneous IPv6 header. |
---|
366 | */ |
---|
367 | ip6err->ip6_src = q6->ip6q_src; |
---|
368 | ip6err->ip6_dst = q6->ip6q_dst; |
---|
369 | |
---|
370 | icmp6_error(merr, ICMP6_PARAM_PROB, |
---|
371 | ICMP6_PARAMPROB_HEADER, |
---|
372 | erroff - sizeof(struct ip6_frag) + |
---|
373 | offsetof(struct ip6_frag, ip6f_offlg)); |
---|
374 | } |
---|
375 | } |
---|
376 | } |
---|
377 | |
---|
378 | ip6af = (struct ip6asfrag *)malloc(sizeof(struct ip6asfrag), M_FTABLE, |
---|
379 | M_NOWAIT); |
---|
380 | if (ip6af == NULL) |
---|
381 | goto dropfrag; |
---|
382 | bzero(ip6af, sizeof(*ip6af)); |
---|
383 | ip6af->ip6af_mff = ip6f->ip6f_offlg & IP6F_MORE_FRAG; |
---|
384 | ip6af->ip6af_off = fragoff; |
---|
385 | ip6af->ip6af_frglen = frgpartlen; |
---|
386 | ip6af->ip6af_offset = offset; |
---|
387 | IP6_REASS_MBUF(ip6af) = m; |
---|
388 | |
---|
389 | if (first_frag) { |
---|
390 | af6 = (struct ip6asfrag *)q6; |
---|
391 | goto insert; |
---|
392 | } |
---|
393 | |
---|
394 | /* |
---|
395 | * Handle ECN by comparing this segment with the first one; |
---|
396 | * if CE is set, do not lose CE. |
---|
397 | * drop if CE and not-ECT are mixed for the same packet. |
---|
398 | */ |
---|
399 | ecn = (ntohl(ip6->ip6_flow) >> 20) & IPTOS_ECN_MASK; |
---|
400 | ecn0 = q6->ip6q_ecn; |
---|
401 | if (ecn == IPTOS_ECN_CE) { |
---|
402 | if (ecn0 == IPTOS_ECN_NOTECT) { |
---|
403 | free(ip6af, M_FTABLE); |
---|
404 | goto dropfrag; |
---|
405 | } |
---|
406 | if (ecn0 != IPTOS_ECN_CE) |
---|
407 | q6->ip6q_ecn = IPTOS_ECN_CE; |
---|
408 | } |
---|
409 | if (ecn == IPTOS_ECN_NOTECT && ecn0 != IPTOS_ECN_NOTECT) { |
---|
410 | free(ip6af, M_FTABLE); |
---|
411 | goto dropfrag; |
---|
412 | } |
---|
413 | |
---|
414 | /* |
---|
415 | * Find a segment which begins after this one does. |
---|
416 | */ |
---|
417 | for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; |
---|
418 | af6 = af6->ip6af_down) |
---|
419 | if (af6->ip6af_off > ip6af->ip6af_off) |
---|
420 | break; |
---|
421 | |
---|
422 | #if 0 |
---|
423 | /* |
---|
424 | * If there is a preceding segment, it may provide some of |
---|
425 | * our data already. If so, drop the data from the incoming |
---|
426 | * segment. If it provides all of our data, drop us. |
---|
427 | */ |
---|
428 | if (af6->ip6af_up != (struct ip6asfrag *)q6) { |
---|
429 | i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen |
---|
430 | - ip6af->ip6af_off; |
---|
431 | if (i > 0) { |
---|
432 | if (i >= ip6af->ip6af_frglen) |
---|
433 | goto dropfrag; |
---|
434 | m_adj(IP6_REASS_MBUF(ip6af), i); |
---|
435 | ip6af->ip6af_off += i; |
---|
436 | ip6af->ip6af_frglen -= i; |
---|
437 | } |
---|
438 | } |
---|
439 | |
---|
440 | /* |
---|
441 | * While we overlap succeeding segments trim them or, |
---|
442 | * if they are completely covered, dequeue them. |
---|
443 | */ |
---|
444 | while (af6 != (struct ip6asfrag *)q6 && |
---|
445 | ip6af->ip6af_off + ip6af->ip6af_frglen > af6->ip6af_off) { |
---|
446 | i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; |
---|
447 | if (i < af6->ip6af_frglen) { |
---|
448 | af6->ip6af_frglen -= i; |
---|
449 | af6->ip6af_off += i; |
---|
450 | m_adj(IP6_REASS_MBUF(af6), i); |
---|
451 | break; |
---|
452 | } |
---|
453 | af6 = af6->ip6af_down; |
---|
454 | m_freem(IP6_REASS_MBUF(af6->ip6af_up)); |
---|
455 | frag6_deq(af6->ip6af_up); |
---|
456 | } |
---|
457 | #else |
---|
458 | /* |
---|
459 | * If the incoming framgent overlaps some existing fragments in |
---|
460 | * the reassembly queue, drop it, since it is dangerous to override |
---|
461 | * existing fragments from a security point of view. |
---|
462 | * We don't know which fragment is the bad guy - here we trust |
---|
463 | * fragment that came in earlier, with no real reason. |
---|
464 | * |
---|
465 | * Note: due to changes after disabling this part, mbuf passed to |
---|
466 | * m_adj() below now does not meet the requirement. |
---|
467 | */ |
---|
468 | if (af6->ip6af_up != (struct ip6asfrag *)q6) { |
---|
469 | i = af6->ip6af_up->ip6af_off + af6->ip6af_up->ip6af_frglen |
---|
470 | - ip6af->ip6af_off; |
---|
471 | if (i > 0) { |
---|
472 | #if 0 /* suppress the noisy log */ |
---|
473 | log(LOG_ERR, "%d bytes of a fragment from %s " |
---|
474 | "overlaps the previous fragment\n", |
---|
475 | i, ip6_sprintf(ip6buf, &q6->ip6q_src)); |
---|
476 | #endif |
---|
477 | free(ip6af, M_FTABLE); |
---|
478 | goto dropfrag; |
---|
479 | } |
---|
480 | } |
---|
481 | if (af6 != (struct ip6asfrag *)q6) { |
---|
482 | i = (ip6af->ip6af_off + ip6af->ip6af_frglen) - af6->ip6af_off; |
---|
483 | if (i > 0) { |
---|
484 | #if 0 /* suppress the noisy log */ |
---|
485 | log(LOG_ERR, "%d bytes of a fragment from %s " |
---|
486 | "overlaps the succeeding fragment", |
---|
487 | i, ip6_sprintf(ip6buf, &q6->ip6q_src)); |
---|
488 | #endif |
---|
489 | free(ip6af, M_FTABLE); |
---|
490 | goto dropfrag; |
---|
491 | } |
---|
492 | } |
---|
493 | #endif |
---|
494 | |
---|
495 | insert: |
---|
496 | #ifdef MAC |
---|
497 | if (!first_frag) |
---|
498 | mac_ip6q_update(m, q6); |
---|
499 | #endif |
---|
500 | |
---|
501 | /* |
---|
502 | * Stick new segment in its place; |
---|
503 | * check for complete reassembly. |
---|
504 | * Move to front of packet queue, as we are |
---|
505 | * the most recently active fragmented packet. |
---|
506 | */ |
---|
507 | frag6_enq(ip6af, af6->ip6af_up); |
---|
508 | V_frag6_nfrags++; |
---|
509 | q6->ip6q_nfrag++; |
---|
510 | #if 0 /* xxx */ |
---|
511 | if (q6 != V_ip6q.ip6q_next) { |
---|
512 | frag6_remque(q6); |
---|
513 | frag6_insque(q6, &V_ip6q); |
---|
514 | } |
---|
515 | #endif |
---|
516 | next = 0; |
---|
517 | for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; |
---|
518 | af6 = af6->ip6af_down) { |
---|
519 | if (af6->ip6af_off != next) { |
---|
520 | IP6Q_UNLOCK(); |
---|
521 | return IPPROTO_DONE; |
---|
522 | } |
---|
523 | next += af6->ip6af_frglen; |
---|
524 | } |
---|
525 | if (af6->ip6af_up->ip6af_mff) { |
---|
526 | IP6Q_UNLOCK(); |
---|
527 | return IPPROTO_DONE; |
---|
528 | } |
---|
529 | |
---|
530 | /* |
---|
531 | * Reassembly is complete; concatenate fragments. |
---|
532 | */ |
---|
533 | ip6af = q6->ip6q_down; |
---|
534 | t = m = IP6_REASS_MBUF(ip6af); |
---|
535 | af6 = ip6af->ip6af_down; |
---|
536 | frag6_deq(ip6af); |
---|
537 | while (af6 != (struct ip6asfrag *)q6) { |
---|
538 | af6dwn = af6->ip6af_down; |
---|
539 | frag6_deq(af6); |
---|
540 | while (t->m_next) |
---|
541 | t = t->m_next; |
---|
542 | t->m_next = IP6_REASS_MBUF(af6); |
---|
543 | m_adj(t->m_next, af6->ip6af_offset); |
---|
544 | free(af6, M_FTABLE); |
---|
545 | af6 = af6dwn; |
---|
546 | } |
---|
547 | |
---|
548 | /* adjust offset to point where the original next header starts */ |
---|
549 | offset = ip6af->ip6af_offset - sizeof(struct ip6_frag); |
---|
550 | free(ip6af, M_FTABLE); |
---|
551 | ip6 = mtod(m, struct ip6_hdr *); |
---|
552 | ip6->ip6_plen = htons((u_short)next + offset - sizeof(struct ip6_hdr)); |
---|
553 | if (q6->ip6q_ecn == IPTOS_ECN_CE) |
---|
554 | ip6->ip6_flow |= htonl(IPTOS_ECN_CE << 20); |
---|
555 | nxt = q6->ip6q_nxt; |
---|
556 | #ifdef notyet |
---|
557 | *q6->ip6q_nxtp = (u_char)(nxt & 0xff); |
---|
558 | #endif |
---|
559 | |
---|
560 | /* Delete frag6 header */ |
---|
561 | if (m->m_len >= offset + sizeof(struct ip6_frag)) { |
---|
562 | /* This is the only possible case with !PULLDOWN_TEST */ |
---|
563 | ovbcopy((caddr_t)ip6, (caddr_t)ip6 + sizeof(struct ip6_frag), |
---|
564 | offset); |
---|
565 | m->m_data += sizeof(struct ip6_frag); |
---|
566 | m->m_len -= sizeof(struct ip6_frag); |
---|
567 | } else { |
---|
568 | /* this comes with no copy if the boundary is on cluster */ |
---|
569 | if ((t = m_split(m, offset, M_DONTWAIT)) == NULL) { |
---|
570 | frag6_remque(q6); |
---|
571 | V_frag6_nfrags -= q6->ip6q_nfrag; |
---|
572 | #ifdef MAC |
---|
573 | mac_ip6q_destroy(q6); |
---|
574 | #endif |
---|
575 | free(q6, M_FTABLE); |
---|
576 | V_frag6_nfragpackets--; |
---|
577 | goto dropfrag; |
---|
578 | } |
---|
579 | m_adj(t, sizeof(struct ip6_frag)); |
---|
580 | m_cat(m, t); |
---|
581 | } |
---|
582 | |
---|
583 | /* |
---|
584 | * Store NXT to the original. |
---|
585 | */ |
---|
586 | { |
---|
587 | char *prvnxtp = ip6_get_prevhdr(m, offset); /* XXX */ |
---|
588 | *prvnxtp = nxt; |
---|
589 | } |
---|
590 | |
---|
591 | frag6_remque(q6); |
---|
592 | V_frag6_nfrags -= q6->ip6q_nfrag; |
---|
593 | #ifdef MAC |
---|
594 | mac_ip6q_reassemble(q6, m); |
---|
595 | mac_ip6q_destroy(q6); |
---|
596 | #endif |
---|
597 | free(q6, M_FTABLE); |
---|
598 | V_frag6_nfragpackets--; |
---|
599 | |
---|
600 | if (m->m_flags & M_PKTHDR) { /* Isn't it always true? */ |
---|
601 | int plen = 0; |
---|
602 | for (t = m; t; t = t->m_next) |
---|
603 | plen += t->m_len; |
---|
604 | m->m_pkthdr.len = plen; |
---|
605 | } |
---|
606 | |
---|
607 | IP6STAT_INC(ip6s_reassembled); |
---|
608 | in6_ifstat_inc(dstifp, ifs6_reass_ok); |
---|
609 | |
---|
610 | /* |
---|
611 | * Tell launch routine the next header |
---|
612 | */ |
---|
613 | |
---|
614 | *mp = m; |
---|
615 | *offp = offset; |
---|
616 | |
---|
617 | IP6Q_UNLOCK(); |
---|
618 | return nxt; |
---|
619 | |
---|
620 | dropfrag: |
---|
621 | IP6Q_UNLOCK(); |
---|
622 | in6_ifstat_inc(dstifp, ifs6_reass_fail); |
---|
623 | IP6STAT_INC(ip6s_fragdropped); |
---|
624 | m_freem(m); |
---|
625 | return IPPROTO_DONE; |
---|
626 | } |
---|
627 | |
---|
628 | /* |
---|
629 | * Free a fragment reassembly header and all |
---|
630 | * associated datagrams. |
---|
631 | */ |
---|
632 | void |
---|
633 | frag6_freef(struct ip6q *q6) |
---|
634 | { |
---|
635 | struct ip6asfrag *af6, *down6; |
---|
636 | |
---|
637 | IP6Q_LOCK_ASSERT(); |
---|
638 | |
---|
639 | for (af6 = q6->ip6q_down; af6 != (struct ip6asfrag *)q6; |
---|
640 | af6 = down6) { |
---|
641 | struct mbuf *m = IP6_REASS_MBUF(af6); |
---|
642 | |
---|
643 | down6 = af6->ip6af_down; |
---|
644 | frag6_deq(af6); |
---|
645 | |
---|
646 | /* |
---|
647 | * Return ICMP time exceeded error for the 1st fragment. |
---|
648 | * Just free other fragments. |
---|
649 | */ |
---|
650 | if (af6->ip6af_off == 0) { |
---|
651 | struct ip6_hdr *ip6; |
---|
652 | |
---|
653 | /* adjust pointer */ |
---|
654 | ip6 = mtod(m, struct ip6_hdr *); |
---|
655 | |
---|
656 | /* restore source and destination addresses */ |
---|
657 | ip6->ip6_src = q6->ip6q_src; |
---|
658 | ip6->ip6_dst = q6->ip6q_dst; |
---|
659 | |
---|
660 | icmp6_error(m, ICMP6_TIME_EXCEEDED, |
---|
661 | ICMP6_TIME_EXCEED_REASSEMBLY, 0); |
---|
662 | } else |
---|
663 | m_freem(m); |
---|
664 | free(af6, M_FTABLE); |
---|
665 | } |
---|
666 | frag6_remque(q6); |
---|
667 | V_frag6_nfrags -= q6->ip6q_nfrag; |
---|
668 | #ifdef MAC |
---|
669 | mac_ip6q_destroy(q6); |
---|
670 | #endif |
---|
671 | free(q6, M_FTABLE); |
---|
672 | V_frag6_nfragpackets--; |
---|
673 | } |
---|
674 | |
---|
675 | /* |
---|
676 | * Put an ip fragment on a reassembly chain. |
---|
677 | * Like insque, but pointers in middle of structure. |
---|
678 | */ |
---|
679 | void |
---|
680 | frag6_enq(struct ip6asfrag *af6, struct ip6asfrag *up6) |
---|
681 | { |
---|
682 | |
---|
683 | IP6Q_LOCK_ASSERT(); |
---|
684 | |
---|
685 | af6->ip6af_up = up6; |
---|
686 | af6->ip6af_down = up6->ip6af_down; |
---|
687 | up6->ip6af_down->ip6af_up = af6; |
---|
688 | up6->ip6af_down = af6; |
---|
689 | } |
---|
690 | |
---|
691 | /* |
---|
692 | * To frag6_enq as remque is to insque. |
---|
693 | */ |
---|
694 | void |
---|
695 | frag6_deq(struct ip6asfrag *af6) |
---|
696 | { |
---|
697 | |
---|
698 | IP6Q_LOCK_ASSERT(); |
---|
699 | |
---|
700 | af6->ip6af_up->ip6af_down = af6->ip6af_down; |
---|
701 | af6->ip6af_down->ip6af_up = af6->ip6af_up; |
---|
702 | } |
---|
703 | |
---|
704 | void |
---|
705 | frag6_insque(struct ip6q *new, struct ip6q *old) |
---|
706 | { |
---|
707 | |
---|
708 | IP6Q_LOCK_ASSERT(); |
---|
709 | |
---|
710 | new->ip6q_prev = old; |
---|
711 | new->ip6q_next = old->ip6q_next; |
---|
712 | old->ip6q_next->ip6q_prev= new; |
---|
713 | old->ip6q_next = new; |
---|
714 | } |
---|
715 | |
---|
716 | void |
---|
717 | frag6_remque(struct ip6q *p6) |
---|
718 | { |
---|
719 | |
---|
720 | IP6Q_LOCK_ASSERT(); |
---|
721 | |
---|
722 | p6->ip6q_prev->ip6q_next = p6->ip6q_next; |
---|
723 | p6->ip6q_next->ip6q_prev = p6->ip6q_prev; |
---|
724 | } |
---|
725 | |
---|
726 | /* |
---|
727 | * IPv6 reassembling timer processing; |
---|
728 | * if a timer expires on a reassembly |
---|
729 | * queue, discard it. |
---|
730 | */ |
---|
731 | void |
---|
732 | frag6_slowtimo(void) |
---|
733 | { |
---|
734 | VNET_ITERATOR_DECL(vnet_iter); |
---|
735 | struct ip6q *q6; |
---|
736 | |
---|
737 | VNET_LIST_RLOCK_NOSLEEP(); |
---|
738 | IP6Q_LOCK(); |
---|
739 | VNET_FOREACH(vnet_iter) { |
---|
740 | CURVNET_SET(vnet_iter); |
---|
741 | q6 = V_ip6q.ip6q_next; |
---|
742 | if (q6) |
---|
743 | while (q6 != &V_ip6q) { |
---|
744 | --q6->ip6q_ttl; |
---|
745 | q6 = q6->ip6q_next; |
---|
746 | if (q6->ip6q_prev->ip6q_ttl == 0) { |
---|
747 | IP6STAT_INC(ip6s_fragtimeout); |
---|
748 | /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ |
---|
749 | frag6_freef(q6->ip6q_prev); |
---|
750 | } |
---|
751 | } |
---|
752 | /* |
---|
753 | * If we are over the maximum number of fragments |
---|
754 | * (due to the limit being lowered), drain off |
---|
755 | * enough to get down to the new limit. |
---|
756 | */ |
---|
757 | while (V_frag6_nfragpackets > (u_int)V_ip6_maxfragpackets && |
---|
758 | V_ip6q.ip6q_prev) { |
---|
759 | IP6STAT_INC(ip6s_fragoverflow); |
---|
760 | /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ |
---|
761 | frag6_freef(V_ip6q.ip6q_prev); |
---|
762 | } |
---|
763 | CURVNET_RESTORE(); |
---|
764 | } |
---|
765 | IP6Q_UNLOCK(); |
---|
766 | VNET_LIST_RUNLOCK_NOSLEEP(); |
---|
767 | } |
---|
768 | |
---|
769 | /* |
---|
770 | * Drain off all datagram fragments. |
---|
771 | */ |
---|
772 | void |
---|
773 | frag6_drain(void) |
---|
774 | { |
---|
775 | VNET_ITERATOR_DECL(vnet_iter); |
---|
776 | |
---|
777 | VNET_LIST_RLOCK_NOSLEEP(); |
---|
778 | if (IP6Q_TRYLOCK() == 0) { |
---|
779 | VNET_LIST_RUNLOCK_NOSLEEP(); |
---|
780 | return; |
---|
781 | } |
---|
782 | VNET_FOREACH(vnet_iter) { |
---|
783 | CURVNET_SET(vnet_iter); |
---|
784 | while (V_ip6q.ip6q_next != &V_ip6q) { |
---|
785 | IP6STAT_INC(ip6s_fragdropped); |
---|
786 | /* XXX in6_ifstat_inc(ifp, ifs6_reass_fail) */ |
---|
787 | frag6_freef(V_ip6q.ip6q_next); |
---|
788 | } |
---|
789 | CURVNET_RESTORE(); |
---|
790 | } |
---|
791 | IP6Q_UNLOCK(); |
---|
792 | VNET_LIST_RUNLOCK_NOSLEEP(); |
---|
793 | } |
---|