2 * Copyright (c) 2012 Jiri Svoboda
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 * @brief Datagram reassembly.
38 #include <fibril_synch.h>
48 /** Datagram being reassembled.
50 * Uniquely identified by (source address, destination address, protocol,
51 * identification) per RFC 791 sec. 2.3 / Fragmentation.
55 /** List of fragments, @c reass_frag_t */
59 /** One datagram fragment */
65 /** Datagram map, list of reass_dgram_t */
66 static LIST_INITIALIZE(reass_dgram_map
);
67 /** Protects access to @c reass_dgram_map */
68 static FIBRIL_MUTEX_INITIALIZE(reass_dgram_map_lock
);
70 static reass_dgram_t
*reass_dgram_new(void);
71 static reass_dgram_t
*reass_dgram_get(inet_packet_t
*);
72 static int reass_dgram_insert_frag(reass_dgram_t
*, inet_packet_t
*);
73 static bool reass_dgram_complete(reass_dgram_t
*);
74 static void reass_dgram_remove(reass_dgram_t
*);
75 static int reass_dgram_deliver(reass_dgram_t
*);
76 static void reass_dgram_destroy(reass_dgram_t
*);
78 /** Queue packet for datagram reassembly.
80 * @param packet Packet
81 * @return EOK on success or ENOMEM.
83 int inet_reass_queue_packet(inet_packet_t
*packet
)
88 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "inet_reass_queue_packet()");
90 fibril_mutex_lock(&reass_dgram_map_lock
);
92 /* Get existing or new datagram */
93 rdg
= reass_dgram_get(packet
);
95 /* Only happens when we are out of memory */
96 fibril_mutex_unlock(&reass_dgram_map_lock
);
97 log_msg(LOG_DEFAULT
, LVL_DEBUG
, "Allocation failed, packet dropped.");
101 /* Insert fragment into the datagram */
102 rc
= reass_dgram_insert_frag(rdg
, packet
);
106 /* Check if datagram is complete */
107 if (reass_dgram_complete(rdg
)) {
108 /* Remove it from the map */
109 reass_dgram_remove(rdg
);
110 fibril_mutex_unlock(&reass_dgram_map_lock
);
112 /* Deliver complete datagram */
113 rc
= reass_dgram_deliver(rdg
);
114 reass_dgram_destroy(rdg
);
118 fibril_mutex_unlock(&reass_dgram_map_lock
);
122 /** Get datagram reassembly structure for packet.
124 * @param packet Packet
125 * @return Datagram reassembly structure matching @a packet
127 static reass_dgram_t
*reass_dgram_get(inet_packet_t
*packet
)
129 assert(fibril_mutex_is_locked(&reass_dgram_map_lock
));
131 list_foreach(reass_dgram_map
, link
) {
132 reass_dgram_t
*rdg
= list_get_instance(link
, reass_dgram_t
,
135 link_t
*f1_link
= list_first(&rdg
->frags
);
136 assert(f1_link
!= NULL
);
138 reass_frag_t
*f1
= list_get_instance(f1_link
, reass_frag_t
,
141 if (f1
->packet
.src
.ipv4
== packet
->src
.ipv4
&&
142 f1
->packet
.dest
.ipv4
== packet
->dest
.ipv4
&&
143 f1
->packet
.proto
== packet
->proto
&&
144 f1
->packet
.ident
== packet
->ident
) {
150 /* No existing reassembly structure. Create a new one. */
151 return reass_dgram_new();
154 /** Create new datagram reassembly structure.
156 * @return New datagram reassembly structure.
158 static reass_dgram_t
*reass_dgram_new(void)
162 rdg
= calloc(1, sizeof(reass_dgram_t
));
166 link_initialize(&rdg
->map_link
);
167 list_initialize(&rdg
->frags
);
172 static reass_frag_t
*reass_frag_new(void)
176 frag
= calloc(1, sizeof(reass_frag_t
));
180 link_initialize(&frag
->dgram_link
);
185 static int reass_dgram_insert_frag(reass_dgram_t
*rdg
, inet_packet_t
*packet
)
191 assert(fibril_mutex_is_locked(&reass_dgram_map_lock
));
193 frag
= reass_frag_new();
195 /* Clone the packet */
197 data_copy
= malloc(packet
->size
);
198 if (data_copy
== NULL
)
201 frag
->packet
= *packet
;
202 frag
->packet
.data
= data_copy
;
205 * XXX Make resource-consuming attacks harder, eliminate any duplicate
206 * data immediately. Possibly eliminate redundant packet headers.
210 * Insert into the list, which is sorted by offs member ascending.
213 link
= list_first(&rdg
->frags
);
214 while (link
!= NULL
) {
215 reass_frag_t
*qf
= list_get_instance(link
, reass_frag_t
,
218 if (qf
->packet
.offs
>= packet
->offs
)
225 list_insert_after(&frag
->dgram_link
, link
);
227 list_append(&frag
->dgram_link
, &rdg
->frags
);
232 /** Check if datagram is complete.
234 * @param rdg Datagram reassembly structure
235 * @return @c true if complete, @c false if not
237 static bool reass_dgram_complete(reass_dgram_t
*rdg
)
239 reass_frag_t
*frag
, *prev
;
242 assert(fibril_mutex_is_locked(&reass_dgram_map_lock
));
243 assert(!list_empty(&rdg
->frags
));
245 /* First fragment must be at offset zero */
246 frag
= list_get_instance(list_first(&rdg
->frags
), reass_frag_t
,
248 if (frag
->packet
.offs
!= 0)
253 link
= frag
->dgram_link
.next
;
257 /* Each next fragment must follow immediately or overlap */
258 frag
= list_get_instance(link
, reass_frag_t
, dgram_link
);
259 if (frag
->packet
.offs
> prev
->packet
.offs
+ prev
->packet
.size
)
262 /* No more fragments - datagram is complete */
263 if (!frag
->packet
.mf
)
272 /** Remove datagram from reassembly map.
274 * @param rdg Datagram reassembly structure
276 static void reass_dgram_remove(reass_dgram_t
*rdg
)
278 assert(fibril_mutex_is_locked(&reass_dgram_map_lock
));
279 list_remove(&rdg
->map_link
);
282 /** Deliver complete datagram.
284 * @param rdg Datagram reassembly structure.
286 static int reass_dgram_deliver(reass_dgram_t
*rdg
)
289 size_t fragoff_limit
;
295 * Potentially there could be something beyond the first packet
296 * that has !MF. Make sure we ignore that.
299 list_foreach(rdg
->frags
, link
) {
300 frag
= list_get_instance(link
, reass_frag_t
, dgram_link
);
302 if (!frag
->packet
.mf
)
306 assert(frag
!= NULL
);
307 assert(!frag
->packet
.mf
);
309 dgram_size
= frag
->packet
.offs
+ frag
->packet
.size
;
311 /* Upper bound for fragment offset field */
312 fragoff_limit
= 1 << (FF_FRAGOFF_h
- FF_FRAGOFF_l
);
314 /* Verify that total size of datagram is within reasonable bounds */
315 if (dgram_size
> FRAG_OFFS_UNIT
* fragoff_limit
)
318 dgram
.data
= calloc(dgram_size
, 1);
319 if (dgram
.data
== NULL
)
322 dgram
.size
= dgram_size
;
323 dgram
.src
= frag
->packet
.src
;
324 dgram
.dest
= frag
->packet
.dest
;
325 dgram
.tos
= frag
->packet
.tos
;
326 proto
= frag
->packet
.proto
;
328 /* Pull together data from individual fragments */
333 list_foreach(rdg
->frags
, link
) {
334 frag
= list_get_instance(link
, reass_frag_t
, dgram_link
);
338 cb
= max(doffs
, frag
->packet
.offs
);
339 ce
= min(dgram_size
, frag
->packet
.offs
+ frag
->packet
.size
);
342 memcpy(dgram
.data
+ cb
,
343 frag
->packet
.data
+ cb
- frag
->packet
.offs
,
347 if (!frag
->packet
.mf
)
351 return inet_recv_dgram_local(&dgram
, proto
);
354 /** Destroy datagram reassembly structure.
356 * @param rdg Datagram reassembly structure.
358 static void reass_dgram_destroy(reass_dgram_t
*rdg
)
360 while (!list_empty(&rdg
->frags
)) {
361 link_t
*flink
= list_first(&rdg
->frags
);
362 reass_frag_t
*frag
= list_get_instance(flink
, reass_frag_t
,
365 list_remove(&frag
->dgram_link
);
366 free(frag
->packet
.data
);