Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of OmniWheels by
ipv6_fragmentation.c
00001 /* 00002 * Copyright (c) 2015-2017, Arm Limited and affiliates. 00003 * SPDX-License-Identifier: Apache-2.0 00004 * 00005 * Licensed under the Apache License, Version 2.0 (the "License"); 00006 * you may not use this file except in compliance with the License. 00007 * You may obtain a copy of the License at 00008 * 00009 * http://www.apache.org/licenses/LICENSE-2.0 00010 * 00011 * Unless required by applicable law or agreed to in writing, software 00012 * distributed under the License is distributed on an "AS IS" BASIS, 00013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00014 * See the License for the specific language governing permissions and 00015 * limitations under the License. 00016 */ 00017 /* IPv6 fragmentation and defragmentation 00018 * 00019 * (Could fairly easily be modified to also do IPv4) 00020 * 00021 * References: 00022 * 00023 * RFC 815 IP Datagram Reassembly Algorithms 00024 * RFC 2460 Internet Protocol. Version 6 (IPv6) Specification 00025 * RFC 3168 The Addition of Explicit Congestion Notification (ECN) to IP 00026 * RFC 5722 Handling of Overlapping IPv6 Fragments 00027 * RFC 6040 Tunnelling of Explicit Congestion Notification 00028 * RFC 6145 IP/ICMP Translation Algorithm [sections on Path MTU] 00029 * RFC 6660 Encoding Three Pre-Congestion Notification (PCN) States in the 00030 * IP Header Using a Single Diffserv Codepoint (DSCP) 00031 * RFC 6946 Processing of IPv6 "Atomic" Fragments 00032 * RFC 7112 Implications of Oversized IPv6 Header Chains 00033 */ 00034 #include "nsconfig.h" 00035 #include "ns_types.h" 00036 #include "ns_list.h" 00037 #include "ns_trace.h" 00038 #include "common_functions.h" 00039 #include "nsdynmemLIB.h" 00040 #include <string.h> 00041 #include "ns_trace.h" 00042 #include "Core/include/socket.h" 00043 #include "NWK_INTERFACE/Include/protocol.h" 00044 #include "Common_Protocols/ip.h" 00045 #include "Common_Protocols/ipv6.h" 00046 #include "Common_Protocols/icmpv6.h" 00047 #include "Common_Protocols/ipv6_fragmentation.h" 00048 00049 #include "NWK_INTERFACE/Include/protocol_stats.h" 00050 00051 #define TRACE_GROUP "Ifrg" 00052 00053 /* FRAGMENT REASSEMBLY 00054 * 00055 * Allow fragment RX to be disabled for really constrained systems. 00056 * This would violate RFC 2460 and RFC 6434 - all IPv6 nodes must be able to 00057 * process fragment headers and reassemble 1500-octet datagrams. 00058 */ 00059 #ifndef NO_IP_FRAGMENT_RX 00060 00061 static uint16_t ipv6_frag_mru = IPV6_FRAG_MRU; 00062 00063 typedef struct ip_fragmented_datagram { 00064 uint8_t age; 00065 bool discard; /* Set to ignore all future fragments (and not send Time Exceeded) */ 00066 bool had_last; 00067 int8_t ecn; 00068 uint32_t id; 00069 uint16_t fragmentable; /* Offset in buf->buf[] of fragmentable part */ 00070 uint16_t first_hole; /* Offset of first hole (relative to fragmentable part) */ 00071 buffer_t *buf; 00072 ns_list_link_t link; 00073 } ip_fragmented_datagram_t; 00074 00075 /* We reassemble into the datagram buffer in basically the style of RFC 815 */ 00076 /* An 6-byte hole descriptor is placed directly in buffer holes */ 00077 /* We link them them by buffer offset (relative to start of fragmentable section) */ 00078 /* Note the possible need to align means we can't use more than 7 bytes */ 00079 typedef struct hole { 00080 uint16_t first; 00081 uint16_t last; 00082 uint16_t next; 00083 } hole_t; 00084 00085 /* Given the offset of a hole in the datagram buffer, return an aligned pointer 00086 * to put a hole_t in it. We assume a "normal" platform requiring 2-byte 00087 * alignment for hole_t, and letting us manipulate uintptr_t in the conventional 00088 * fashion. 00089 */ 00090 static hole_t *hole_pointer(const ip_fragmented_datagram_t *dgram, uint16_t offset) 00091 { 00092 uintptr_t ptr = (uintptr_t)(dgram->buf->buf + dgram->fragmentable + offset); 00093 00094 return (hole_t *)((ptr + 1) & ~(uintptr_t) 1); 00095 } 00096 00097 static NS_LIST_DEFINE(frag_list, ip_fragmented_datagram_t, link); 00098 00099 /* Maximum time to hold fragments in seconds */ 00100 #define FRAG_TTL 60 00101 00102 /* How many partially-assembled datagrams we will hold */ 00103 #define MAX_FRAG_DATAGRAMS 4 00104 00105 /* Dummy negative ECN value used during assembly */ 00106 #define IP_ECN__ILLEGAL (-1) 00107 00108 /* RFC 5722 - discard already-received *and future* fragments */ 00109 static void invalidate_datagram(ip_fragmented_datagram_t *dgram) 00110 { 00111 // Would like to free the buffer here, but it contains the 00112 // source and destination address we need to match the datagram entry. 00113 dgram->discard = true; 00114 } 00115 00116 static void free_datagram(ip_fragmented_datagram_t *dgram) 00117 { 00118 ns_list_remove(&frag_list, dgram); 00119 if (dgram->buf) { 00120 buffer_free(dgram->buf); 00121 } 00122 ns_dyn_mem_free(dgram); 00123 } 00124 00125 /* We would be in trouble if last fragment is < 8 bytes, and we didn't have 00126 * room for the hole descriptor. Avoid a problem by ensuring that we always 00127 * allocate a multiple-of-8 reassembly buffer. 00128 */ 00129 uint16_t ipv6_frag_set_mru(uint16_t frag_mru) 00130 { 00131 frag_mru = (frag_mru + 7) &~ UINT16_C(7); 00132 if (frag_mru < IPV6_MIN_FRAG_MRU) { 00133 frag_mru = (IPV6_MIN_FRAG_MRU + 7) &~ UINT16_C(7); 00134 } 00135 if (ipv6_frag_mru != frag_mru) { 00136 /* I don't want to worry about the complications of changing MRU while 00137 * we've got ongoing reassembly. Simplest just to drop any pending. 00138 */ 00139 ns_list_foreach_safe(ip_fragmented_datagram_t, dgram, &frag_list) { 00140 free_datagram(dgram); 00141 } 00142 ipv6_frag_mru = frag_mru; 00143 } 00144 return ipv6_frag_mru; 00145 } 00146 00147 void ipv6_frag_timer(uint8_t secs) 00148 { 00149 ns_list_foreach_safe(ip_fragmented_datagram_t, dgram, &frag_list) { 00150 if ((dgram->age += secs) > FRAG_TTL) { 00151 uint16_t first_hole = dgram->first_hole; 00152 /* If we've received the first fragment, can send "time exceeded" */ 00153 if (first_hole != 0 && !dgram->discard) { 00154 /* Take as much as we've got, up to first hole; icmpv6_error will limit to min MTU */ 00155 dgram->buf->buf_end = dgram->fragmentable + first_hole; 00156 /* Fill in IP header length */ 00157 common_write_16_bit(buffer_data_length(dgram->buf) - 40, buffer_data_pointer(dgram->buf) + 4); 00158 00159 buffer_t *err = icmpv6_error(dgram->buf, NULL, ICMPV6_TYPE_ERROR_TIME_EXCEEDED, ICMPV6_CODE_TME_EXCD_FRG_REASS_TME_EXCD, 0); 00160 protocol_push(err); 00161 dgram->buf = NULL; 00162 } 00163 free_datagram(dgram); 00164 } 00165 } 00166 } 00167 00168 static void delete_hole(ip_fragmented_datagram_t *dgram, uint16_t hole, uint16_t *prev_ptr) 00169 { 00170 hole_t *hole_ptr = hole_pointer(dgram, hole); 00171 00172 *prev_ptr = hole_ptr->next; 00173 } 00174 00175 static hole_t *create_hole(ip_fragmented_datagram_t *dgram, uint16_t first, uint16_t last, uint16_t *prev_ptr) 00176 { 00177 hole_t *hole_ptr = hole_pointer(dgram, first); 00178 hole_ptr->first = first; 00179 hole_ptr->last = last; 00180 hole_ptr->next = *prev_ptr; 00181 00182 *prev_ptr = first; 00183 return hole_ptr; 00184 } 00185 00186 static ip_fragmented_datagram_t *ip_frag_dgram_lookup(buffer_t *buf, uint32_t id, uint16_t unfrag_len) 00187 { 00188 int_fast8_t count = 0; 00189 ns_list_foreach(ip_fragmented_datagram_t, dgram, &frag_list) { 00190 if (id == dgram->id && 00191 addr_ipv6_equal(buf->src_sa .address , dgram->buf->src_sa.address) && 00192 addr_ipv6_equal(buf->dst_sa .address , dgram->buf->dst_sa.address)) { 00193 return dgram; 00194 } 00195 count++; 00196 } 00197 00198 /* Not found - create one */ 00199 if (count >= MAX_FRAG_DATAGRAMS) { 00200 free_datagram(ns_list_get_last(&frag_list)); 00201 } 00202 00203 ip_fragmented_datagram_t *new_dgram = ns_dyn_mem_temporary_alloc(sizeof(ip_fragmented_datagram_t)); 00204 if (!new_dgram) { 00205 return NULL; 00206 } 00207 00208 /* We track payload holes as per RFC 815, roughly, and reserve header 00209 * room in front, based on the unfragmentable size of the first-received 00210 * fragment. 00211 * 00212 * So initial state is: 00213 * 00214 * buf_ptr -> default buffer headroom + first-received-fragment header size 00215 * fragmentable = buf_end = buf_ptr = offset of where fragments are assembled. 00216 * 00217 * When we receive the first (0-offset) fragment, we move down buf_ptr to 00218 * put in its header, and when we receive the final (M=0) fragment, we 00219 * set buf_end accordingly. 00220 * 00221 * Two odd cases to worry about: 00222 * 00223 * 1) First fragment is not received first, and has a larger 00224 * header than our first-received fragment. In this case, we 00225 * shuffle data if required when we get that first fragment. 00226 * (Actual shuffle will normally be avoided by buffer headroom slack). 00227 * 2) First fragment is not received first, and has a smaller 00228 * header than our first-received fragment, meaning an IPV6_MRU-sized 00229 * datagram may have more fragmented payload than we expected. Avoid 00230 * a problem in this case by allocating a bigger-than-IPV6_MRU buffer 00231 * if first-received fragment has extension headers. 00232 */ 00233 new_dgram->buf = buffer_get(unfrag_len + ipv6_frag_mru - 40); 00234 if (!new_dgram->buf) { 00235 ns_dyn_mem_free(new_dgram); 00236 return NULL; 00237 } 00238 00239 new_dgram->fragmentable = new_dgram->buf->buf_end = new_dgram->buf->buf_ptr += unfrag_len; 00240 new_dgram->first_hole = 0xffff; 00241 create_hole(new_dgram, 0, 0xffff, &new_dgram->first_hole); 00242 00243 new_dgram->buf->src_sa = buf->src_sa ; 00244 new_dgram->buf->dst_sa = buf->dst_sa ; 00245 new_dgram->id = id; 00246 new_dgram->age = 0; 00247 new_dgram->discard = false; 00248 new_dgram->had_last = false; 00249 new_dgram->ecn = buf->options .traffic_class & IP_TCLASS_ECN_MASK; 00250 ns_list_add_to_start(&frag_list, new_dgram); 00251 00252 return new_dgram; 00253 } 00254 00255 /* 00256 * 4x4 combination array implementing the ECN combination rules from RFC 3168. 00257 * 00258 * Summary visualisation: N10C 00259 * +---- 00260 * N|NNN- 00261 * 1|N11C 00262 * 0|N10C 00263 * C|-CCC 00264 * 00265 * Each of the 16 entries, with justification: 00266 */ 00267 static const int8_t frag_ecn_combination[4][4] = { 00268 // We MUST preserve the ECN codepoint when all fragments match. 00269 [IP_ECN_NOT_ECT][IP_ECN_NOT_ECT] = IP_ECN_NOT_ECT, 00270 [IP_ECN_ECT_0 ][IP_ECN_ECT_0 ] = IP_ECN_ECT_0, 00271 [IP_ECN_ECT_1 ][IP_ECN_ECT_1 ] = IP_ECN_ECT_1, 00272 [IP_ECN_CE ][IP_ECN_CE ] = IP_ECN_CE, 00273 00274 // We MUST set CE if any fragment has CE... 00275 [IP_ECN_CE ][IP_ECN_ECT_0 ] = IP_ECN_CE, 00276 [IP_ECN_CE ][IP_ECN_ECT_1 ] = IP_ECN_CE, 00277 [IP_ECN_ECT_0 ][IP_ECN_CE ] = IP_ECN_CE, 00278 [IP_ECN_ECT_1 ][IP_ECN_CE ] = IP_ECN_CE, 00279 00280 // ...except we MUST drop the packet if we see CE + Not-ECT. 00281 [IP_ECN_CE ][IP_ECN_NOT_ECT] = IP_ECN__ILLEGAL, 00282 [IP_ECN_NOT_ECT][IP_ECN_CE ] = IP_ECN__ILLEGAL, 00283 00284 // For the remaining cases, RFC 3168 leaves us free to do anything. 00285 // To make the above CE+Not-ECT rule work in all delivery orders, with 00286 // intervening ECT fragments, Not-ECT overrides ECT. 00287 [IP_ECN_NOT_ECT][IP_ECN_ECT_0 ] = IP_ECN_NOT_ECT, 00288 [IP_ECN_NOT_ECT][IP_ECN_ECT_1 ] = IP_ECN_NOT_ECT, 00289 [IP_ECN_ECT_0 ][IP_ECN_NOT_ECT] = IP_ECN_NOT_ECT, 00290 [IP_ECN_ECT_1 ][IP_ECN_NOT_ECT] = IP_ECN_NOT_ECT, 00291 00292 // Last two cases - RFC 3168 doesn't specify, but we follow the 00293 // model of RFC 6040 and RFC 6660 which for tunnelling make ECT(1) 00294 // take priority, as it can be used as a mild congestion indication. 00295 [IP_ECN_ECT_0 ][IP_ECN_ECT_1 ] = IP_ECN_ECT_1, 00296 [IP_ECN_ECT_1 ][IP_ECN_ECT_0 ] = IP_ECN_ECT_1 00297 }; 00298 00299 /* 00300 * RFC 2460 notes: 00301 * 00302 * fragment packets: 00303 * 00304 * +------------------+--------+--------------+ 00305 * | Unfragmentable |Fragment| first | 00306 * | Part | Header | fragment | 00307 * +------------------+--------+--------------+ 00308 * 00309 * +------------------+--------+--------------+ 00310 * | Unfragmentable |Fragment| second | 00311 * | Part | Header | fragment | 00312 * +------------------+--------+--------------+ 00313 * o 00314 * o 00315 * o 00316 * +------------------+--------+----------+ 00317 * | Unfragmentable |Fragment| last | 00318 * | Part | Header | fragment | 00319 * +------------------+--------+----------+ 00320 * 00321 * reassembled original packet: 00322 * 00323 * +------------------+----------------------//------------------------+ 00324 * | Unfragmentable | Fragmentable | 00325 * | Part | Part | 00326 * +------------------+----------------------//------------------------+ 00327 * 00328 * The following rules govern reassembly: 00329 * 00330 * An original packet is reassembled only from fragment packets that 00331 * have the same Source Address, Destination Address, and Fragment 00332 * Identification. 00333 * 00334 * The Unfragmentable Part of the reassembled packet consists of all 00335 * headers up to, but not including, the Fragment header of the first 00336 * fragment packet (that is, the packet whose Fragment Offset is 00337 * zero), with the following two changes: 00338 * 00339 * The Next Header field of the last header of the Unfragmentable 00340 * Part is obtained from the Next Header field of the first 00341 * fragment's Fragment header. 00342 * 00343 * The Payload Length of the reassembled packet is computed from 00344 * the length of the Unfragmentable Part and the length and offset 00345 * of the last fragment. 00346 * 00347 * Fragment Header 00348 * 00349 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00350 * | Next Header | Reserved | Fragment Offset |Res|M| 00351 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00352 * | Identification | 00353 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00354 * 00355 * Fragment Offset 13-bit unsigned integer. The offset, in 8-octet 00356 * units, of the data following this header, 00357 * relative to the start of the Fragmentable Part 00358 * of the original packet. 00359 * 00360 * M flag 1 = more fragments; 0 = last fragment. 00361 */ 00362 00363 /* On entry: frag_hdr -> fragment header 00364 * nh_ptr -> Next Header octet in previous header 00365 * payload_length = length of remaining data, including this header 00366 * buffer data pointers describe entire IP fragment packet 00367 * buffer src/dst filled in 00368 * Returns: Either reassembled packet (B_DIR_UP | B_TO_IPV6_FWD) 00369 * or ICMP error response (B_DIR_DOWN | B_TO_ICMP) 00370 * or NULL (fragment accepted, reassembly in progress) 00371 */ 00372 buffer_t *ipv6_frag_up(buffer_t *frag_buf, const uint8_t *frag_hdr, uint8_t *nh_ptr, uint16_t payload_length) 00373 { 00374 if (payload_length <= 8) { 00375 return icmpv6_error(frag_buf, NULL, ICMPV6_TYPE_ERROR_PARAMETER_PROBLEM, ICMPV6_CODE_PARAM_PRB_HDR_ERR, 4); 00376 } 00377 00378 payload_length -= 8; 00379 00380 uint8_t *ip_hdr = buffer_data_pointer(frag_buf); 00381 uint16_t unfrag_len = frag_hdr - ip_hdr; 00382 uint16_t fragment_first = common_read_16_bit(frag_hdr + 2) & 0xFFF8; 00383 uint16_t fragment_last = fragment_first + payload_length - 1; 00384 bool more = frag_hdr[3] & 1; 00385 00386 /* All fragments apart from last must be multiples of 8 */ 00387 if (more && (payload_length & 7)) { 00388 return icmpv6_error(frag_buf, NULL, ICMPV6_TYPE_ERROR_PARAMETER_PROBLEM, ICMPV6_CODE_PARAM_PRB_HDR_ERR, 4); 00389 } 00390 00391 /* Check we don't overflow 16-bit size */ 00392 if (fragment_last < fragment_first) { 00393 return icmpv6_error(frag_buf, NULL, ICMPV6_TYPE_ERROR_PARAMETER_PROBLEM, ICMPV6_CODE_PARAM_PRB_HDR_ERR, frag_hdr + 2 - ip_hdr); 00394 } 00395 00396 if (fragment_first == 0) { 00397 /* Replace "Next Header" byte in previous header */ 00398 *nh_ptr = frag_hdr[0]; 00399 00400 if (!more) { 00401 /* Atomic fragment handling - strip out the fragment header. 00402 * See RFC 6946, which says that we require a special case for atomic 00403 * fragments: 00404 * 00405 * A host that receives an IPv6 packet that includes a Fragment 00406 * Header with the "Fragment Offset" equal to 0 and the "M" flag 00407 * equal to 0 MUST process that packet in isolation from any other 00408 * packets/fragments, even if such packets/fragments contain the same 00409 * set {IPv6 Source Address, IPv6 Destination Address, Fragment 00410 * Identification}. 00411 * 00412 * (Conceivably, we could just skip the header and keep parsing, 00413 * but this keeps it consistent with real fragments). 00414 */ 00415 00416 /* Move unfragmentable part up, eliminating fragment header */ 00417 memmove(ip_hdr + 8, ip_hdr, unfrag_len); 00418 ip_hdr = buffer_data_strip_header(frag_buf, 8); 00419 00420 /* Reduce Payload Length in IP header */ 00421 uint16_t len = common_read_16_bit(ip_hdr + 4); 00422 common_write_16_bit(len - 8, ip_hdr + 4); 00423 00424 frag_buf->offset = unfrag_len; 00425 frag_buf->options .ip_extflags |= IPEXT_FRAGMENT; 00426 frag_buf->info = (buffer_info_t)(B_DIR_UP | B_TO_IPV6_FWD | B_FROM_IPV6_FWD); 00427 return frag_buf; 00428 } 00429 } 00430 00431 /* Adjust buffer pointer to point to fragment data. ip_ptr remains 00432 * pointing at IP header, which we need for first fragment. */ 00433 buffer_data_pointer_set(frag_buf, frag_hdr + 8); 00434 00435 /* Locate or create datagram assembly buffer */ 00436 uint32_t id = common_read_32_bit(frag_hdr + 4); 00437 ip_fragmented_datagram_t *dgram = ip_frag_dgram_lookup(frag_buf, id, unfrag_len); 00438 if (!dgram || dgram->discard) { 00439 protocol_stats_update(STATS_IP_RX_DROP, 1); 00440 return buffer_free(frag_buf); 00441 } 00442 00443 buffer_t *dgram_buf = dgram->buf ; 00444 00445 /* Length checks. For predictability, best to ensure we always try to 00446 * respect IPV6_MRU as a hard limit, which means a bit of care. */ 00447 uint16_t limit; 00448 if (dgram_buf->buf_ptr == dgram->fragmentable) { 00449 /* Haven't yet got final header size - good enough to do rough check; 00450 * we have enough buffer to fit MRU - min IP header size */ 00451 limit = ipv6_frag_mru - 40; 00452 } else { 00453 /* We do know final header size, so can do precise MRU check */ 00454 limit = ipv6_frag_mru - (dgram->fragmentable - dgram_buf->buf_ptr ); 00455 } 00456 /* Make sure we have room for following data, and hence a hole descriptor */ 00457 if (more) { 00458 limit -= 8; 00459 } 00460 00461 if (fragment_last >= limit) { 00462 /* Fragment would make datagram exceed MRU */ 00463 tr_warn("Datagram size %u too big", fragment_last + 1); 00464 fail: 00465 invalidate_datagram(dgram); 00466 protocol_stats_update(STATS_IP_RX_DROP, 1); 00467 return buffer_free(frag_buf); 00468 } 00469 00470 /* Hole-filling algorithm, basically as per RFC815, but with added 00471 * checks for overlap (RFC 5722). We keep the hole list sorted to aid this, 00472 * (and Time Exceeded messages) - something RFC 815 doesn't strictly require. 00473 */ 00474 uint16_t hole_off = dgram->first_hole; 00475 uint16_t *prev_ptr = &dgram->first_hole; 00476 bool okay = false; 00477 do { 00478 hole_t *hole = hole_pointer(dgram, hole_off); 00479 uint_fast16_t hole_first = hole->first; 00480 uint_fast16_t hole_last = hole->last; 00481 00482 /* Fragment is beyond this hole - move to next (RFC 815 step 2) */ 00483 if (fragment_first > hole_last) { 00484 prev_ptr = &hole->next; 00485 hole_off = hole->next; 00486 continue; 00487 } 00488 00489 /* RFC 815 step 3 would have us check for fragment_last < hole_first, 00490 * and skipping, but we don't need/want to do that - it's covered by 00491 * the next check. 00492 */ 00493 00494 /* Unlike RFC 815, we now check for any overlap (RFC 5722) */ 00495 if (fragment_first < hole_first || fragment_last > hole_last) { 00496 break; 00497 } 00498 00499 /* Unhook this hole from the hole list (RFC 815 step 4) */ 00500 delete_hole(dgram, hole_off, prev_ptr); 00501 hole = NULL; 00502 00503 /* Create a new hole in front if necessary (RFC 815 step 5) */ 00504 if (fragment_first > hole_first) { 00505 prev_ptr = &create_hole(dgram, hole_first, fragment_first - 1, prev_ptr)->next; 00506 } 00507 00508 if (more) { 00509 /* Create a following hole if necessary (RFC 815 step 6) */ 00510 if (fragment_last < hole_last) { 00511 create_hole(dgram, fragment_last + 1, hole_last, prev_ptr); 00512 } 00513 } else { 00514 /* If we already have some later data, it's broken. */ 00515 if (hole_last != 0xffff) { 00516 break; 00517 } 00518 dgram->had_last = true; 00519 } 00520 00521 /* Update end of buffer, if this is the last-placed fragment so far */ 00522 if (hole_last == 0xffff) { 00523 dgram_buf->buf_end = dgram->fragmentable + fragment_last + 1; 00524 } 00525 00526 /* Unlike RFC 815, we're now done. We don't allow overlaps, so we finish 00527 * as soon as we identify one hole that it entirely or partially fills */ 00528 okay = true; 00529 break; 00530 } while (hole_off != 0xffff); 00531 00532 /* If /any/ reassembly problems - overlaps etc - abandon the datagram */ 00533 if (!okay) { 00534 tr_warn("Reassembly error"); 00535 goto fail; 00536 } 00537 00538 /* Hole list updated, can now copy in the fragment data */ 00539 memcpy(dgram_buf->buf + dgram->fragmentable + fragment_first, buffer_data_pointer(frag_buf), fragment_last + 1 - fragment_first); 00540 00541 /* Combine the "improper security" flags, so reassembled buffer's flag is set if any fragment wasn't secure */ 00542 /* XXX should have some sort of overall "merge buffer metadata" routine handling this and whatever else */ 00543 dgram_buf->options .ll_security_bypass_rx |= frag_buf->options .ll_security_bypass_rx ; 00544 00545 /* Combine the ECN field */ 00546 dgram->ecn = frag_ecn_combination[dgram->ecn][frag_buf->options .traffic_class & IP_TCLASS_ECN_MASK]; 00547 if (dgram->ecn == IP_ECN__ILLEGAL) { 00548 tr_warn("Illegal ECN"); 00549 goto fail; 00550 } 00551 00552 /* Overlap checks above ensure first-packet processing only happens once */ 00553 if (fragment_first == 0) { 00554 /* Now know final header size, so repeat MRU check */ 00555 uint16_t frag_so_far = dgram_buf->buf_end - dgram->fragmentable; 00556 if (!dgram->had_last) { 00557 /* This fudge factor represents our expectation of more data, and 00558 * also makes sure we memmove the trailing hole descriptor. */ 00559 frag_so_far += 8; 00560 } 00561 if (unfrag_len + frag_so_far > ipv6_frag_mru) { 00562 tr_warn("Datagram size %u too big", unfrag_len + frag_so_far); 00563 goto fail; 00564 } 00565 00566 if (dgram_buf->buf_ptr < unfrag_len) { 00567 /* Didn't reserve enough space for header. Shuffle data up into what will be final position */ 00568 /* We know we have buffer room, thanks to previous checks against IPV6_MRU */ 00569 uint16_t new_frag_offset = dgram_buf->size - ipv6_frag_mru + unfrag_len; 00570 memmove(dgram_buf->buf + new_frag_offset, dgram_buf->buf + dgram->fragmentable, frag_so_far); 00571 dgram->buf->buf_ptr = dgram->fragmentable = new_frag_offset; 00572 } 00573 00574 /* Move the start pointer, and copy the header */ 00575 memcpy(buffer_data_reserve_header(dgram_buf, unfrag_len), ip_hdr, unfrag_len); 00576 00577 /* Clone the buffer header from this first fragment, preserving only size + pointers */ 00578 /* Also the security flag, already merged above */ 00579 bool buf_security = dgram_buf->options .ll_security_bypass_rx ; 00580 buffer_copy_metadata(dgram_buf, frag_buf, true); 00581 dgram_buf->options .ll_security_bypass_rx = buf_security; 00582 /* Mark position of fragment header - allows skipping previous headers */ 00583 dgram_buf->offset = unfrag_len; 00584 dgram_buf->options .ip_extflags |= IPEXT_FRAGMENT; 00585 } 00586 00587 /* Free the original fragment buffer - we've extracted its juice */ 00588 buffer_free(frag_buf); 00589 00590 /* Thanks to David Clark, completion check is now simple */ 00591 if (dgram->first_hole != 0xffff) { 00592 /* Not yet complete - processing finished on this fragment */ 00593 return NULL; 00594 } 00595 00596 /* First 8 bytes of the IP header, currently from the first fragment, 00597 * that we need to patch: 00598 * . . . . . 00599 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00600 * |Version| DSCP |ECN| Flow Label | 00601 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00602 * | Payload Length | Next Header | Hop Limit | 00603 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 00604 */ 00605 00606 /* Fill in the combined ECN - 2 bits in the middle of the second byte */ 00607 buffer_data_pointer(dgram_buf)[1] &= ~(3 << 4); 00608 buffer_data_pointer(dgram_buf)[1] |= (dgram->ecn << 4); 00609 00610 /* Fill in final IP header length */ 00611 common_write_16_bit(buffer_data_length(dgram_buf) - 40, buffer_data_pointer(dgram_buf) + 4); 00612 00613 /* We've completed the datagram. Free the assembly structures (but not the buffer!) */ 00614 dgram->buf = NULL; 00615 free_datagram(dgram); 00616 00617 /* Send on the completed datagram */ 00618 dgram_buf->info = (buffer_info_t)(B_DIR_UP | B_TO_IPV6_FWD | B_FROM_IPV6_FWD); 00619 return dgram_buf; 00620 } 00621 #endif /* NO_IP_FRAGMENT_RX */ 00622 00623 00624 /* FRAGMENT CREATION 00625 * 00626 * Allow fragment TX to be disabled for constrained systems. 00627 * This would violate RFC 6434, which says all IPv6 nodes must be able to 00628 * generate fragment headers. (Even if our only link has the minimum 1280-byte 00629 * MTU, we may still need to insert a fragment header). 00630 */ 00631 #ifndef NO_IP_FRAGMENT_TX 00632 buffer_t *ipv6_frag_down(buffer_t *dgram_buf) 00633 { 00634 uint8_t *ip_ptr = buffer_data_pointer(dgram_buf); 00635 uint16_t pmtu = ipv6_mtu(dgram_buf); 00636 uint8_t *frag_hdr; 00637 buffer_list_t frags_list = NS_LIST_INIT(frags_list); 00638 ipv6_destination_t *dest = ipv6_destination_lookup_or_create(dgram_buf->dst_sa .address , dgram_buf->interface ->id); 00639 if (!dest) { 00640 return buffer_free(dgram_buf); 00641 } 00642 00643 /* Skip over HbH and Routing headers to reach fragmentable part. Assume 00644 * packet well-formed (we created it...). 00645 */ 00646 uint8_t *nh_ptr = &ip_ptr[6]; 00647 uint8_t nh = *nh_ptr; 00648 uint8_t *fragmentable = ip_ptr + 40; 00649 while (nh == IPV6_NH_HOP_BY_HOP || nh == IPV6_NH_ROUTING) { 00650 nh_ptr = &fragmentable[0]; 00651 nh = *nh_ptr; 00652 fragmentable += (fragmentable[1] + 1) * 8; 00653 } 00654 uint16_t unfrag_len = fragmentable - ip_ptr; 00655 uint16_t fragmentable_len = buffer_data_end(dgram_buf) - fragmentable; 00656 00657 *nh_ptr = IPV6_NH_FRAGMENT; 00658 00659 /* Special case for atomic fragments (caused by a small PMTU) */ 00660 /* Note that we DO have the option of actually fragmenting and obeying 00661 * a small PMTU, which would avoid this special case. 00662 */ 00663 if (buffer_data_length(dgram_buf) <= IPV6_MIN_LINK_MTU - 8) { 00664 dgram_buf = buffer_headroom(dgram_buf, 8); 00665 if (!dgram_buf) { 00666 return NULL; 00667 } 00668 00669 /* Move unfragmentable section back 8 bytes; increase IP length field */ 00670 ip_ptr = buffer_data_reserve_header(dgram_buf, 8); 00671 memmove(ip_ptr, ip_ptr + 8, unfrag_len); 00672 common_write_16_bit(common_read_16_bit(ip_ptr + 4) + 8, ip_ptr + 4); 00673 00674 /* Write atomic fragment header into the gap */ 00675 frag_hdr = ip_ptr + unfrag_len; 00676 frag_hdr[0] = nh; 00677 frag_hdr[1] = 0; 00678 common_write_16_bit(0, frag_hdr + 2); 00679 common_write_32_bit(++dest->fragment_id, frag_hdr + 4); 00680 return dgram_buf; 00681 } 00682 00683 /* We won't fragment below minimum MTU. (Although we could...) */ 00684 if (pmtu < IPV6_MIN_LINK_MTU) { 00685 pmtu = IPV6_MIN_LINK_MTU; 00686 } 00687 00688 /* Check for silly situation - can't fit any fragment data (8 for fragment 00689 * header, 8 for minimum fragment payload) */ 00690 if (unfrag_len + 8 + 8 > pmtu) { 00691 goto failed; 00692 } 00693 00694 ++dest->fragment_id; 00695 00696 /* RFC 7112 requires the entire header chain to be in the first fragment. */ 00697 /* We don't explicitly check for this, but it would be spectacularly unlikely. */ 00698 /* I think it would require a super-sized routing header */ 00699 00700 /* This is much simpler (more simplistic?) than the 6LoWPAN fragmentation, 00701 * which relies on co-operation with lower layers to ensure it works one 00702 * fragment at a time. We make all the fragments in one go, meaning higher 00703 * overhead, but IP fragmentation should be pretty rare - we don't need 00704 * to optimise this. 00705 */ 00706 for (uint16_t frag_offset = 0; fragmentable_len;) { 00707 /* How much going in this packet? */ 00708 uint16_t frag_len = (pmtu - unfrag_len - 8); 00709 if (fragmentable_len > frag_len) { 00710 frag_len &= ~7; 00711 } else { 00712 frag_len = fragmentable_len; 00713 } 00714 00715 buffer_t *frag_buf = buffer_get(unfrag_len + 8 + frag_len); 00716 if (!frag_buf) { 00717 goto failed; 00718 } 00719 00720 /* Clone the buffer header, apart from size+ptr */ 00721 buffer_copy_metadata(frag_buf, dgram_buf, false); 00722 00723 /* We splat the socket, so no upper-layer callbacks from the fragments */ 00724 buffer_socket_set(frag_buf, NULL); 00725 00726 /* Construct the new packet contents */ 00727 buffer_data_length_set(frag_buf, unfrag_len + 8 + frag_len); 00728 uint8_t *ptr = buffer_data_pointer(frag_buf); 00729 /* Unfragmentable part */ 00730 memcpy(ptr, ip_ptr, unfrag_len); 00731 /* Adjust length in IP header */ 00732 common_write_16_bit(unfrag_len - 40 + 8 + frag_len, ptr + 4); 00733 /* Fragment header */ 00734 frag_hdr = ptr + unfrag_len; 00735 frag_hdr[0] = nh; 00736 frag_hdr[1] = 0; 00737 common_write_16_bit(frag_offset | (frag_len != fragmentable_len), frag_hdr + 2); 00738 common_write_32_bit(dest->fragment_id, frag_hdr + 4); 00739 /* Fragment data */ 00740 memcpy(frag_hdr + 8, fragmentable + frag_offset, frag_len); 00741 fragmentable_len -= frag_len; 00742 frag_offset += frag_len; 00743 00744 /* Add to our fragment list */ 00745 ns_list_add_to_start(&frags_list, frag_buf); 00746 } 00747 00748 /* Now have a list of fragment buffers - report "success" to the socket */ 00749 /* (TCP may save the dgram payload here? It strips off headers, so okay...) */ 00750 socket_tx_buffer_event_and_free(dgram_buf, SOCKET_TX_DONE); 00751 00752 /* Push the fragments. Backwards, as it happens, but who cares? */ 00753 ns_list_foreach_safe(buffer_t, f, &frags_list) { 00754 ns_list_remove(&frags_list, f); 00755 protocol_push(f); 00756 } 00757 00758 return NULL; 00759 00760 failed: 00761 /* Failed to allocate a buffer - no point sending any fragments if we 00762 * can't send all. 00763 */ 00764 ns_list_foreach_safe(buffer_t, f, &frags_list) { 00765 ns_list_remove(&frags_list, f); 00766 buffer_free(f); 00767 } 00768 00769 socket_tx_buffer_event_and_free(dgram_buf, SOCKET_NO_RAM); 00770 return NULL; 00771 } 00772 #endif /* NO_IP_FRAGMENT_TX */
Generated on Fri Jul 22 2022 04:53:50 by
1.7.2
