Line data Source code
1 : /*
2 : * vr_fragment.c -- basic fragment handling code
3 : *
4 : * Copyright (c) 2013, Juniper Networks Private Limited
5 : * Copyright Contributors to the OpenSDN project
6 : * All rights reserved
7 : */
8 : #include <vr_os.h>
9 : #include <vr_packet.h>
10 : #include "vr_interface.h"
11 : #include "vr_btable.h"
12 : #include "vr_fragment.h"
13 : #include "vr_hash.h"
14 :
15 : /*
16 : * Handling out of order fragment arrival:
17 : *
18 : * Every fragment that does not have a corresponding entry in the fragment
19 : * metadata table (which is the primary table that the datapath will rely on),
20 : * will be enqueued to the assembler. The head of an ip fragment is a special
21 : * case. It will be both forwarded to the destination and enqueued to the
22 : * assembler. The assembler just extracts the required data from the packet
23 : * and will not forward the packet . The assembler will use the head of the
24 : * fragment to search and then to forward the other fragments of the packet,
25 : * while not forwarding the head itself.
26 :
27 : * Enqueue to per-cpu queue
28 : * ------------------------
29 :
30 : * Its important that locks are avoided as far as possible in datapath. To avoid
31 : * contention between multiple threads running datapath, we will have a per-cpu
32 : * queue to the assembler. Since both the assembler and the datapath will be
33 : * dequeueing from and enqueueing to the queue and frees will be involved, its
34 : * much easier and safer to enqueue the packet to the head of the queue (which
35 : * is a memory that is never freed and involves only updating the next pointer,
36 : * which can be a stale memory, but a safe operation nevertheless) rather than
37 : * at the tail (which is an element whose life cycle is not easy to determine).
38 :
39 : * Dequeue from the per-cpu queue
40 : * ------------------------------
41 :
42 : * The assembler will do an atomic read and update of head to NULL. There is a
43 : * small consistency issue to be taken care of while updating the head. The
44 : * enqueuer will first update the next pointer of the queued element and then
45 : * update the head. Updation of the head will be atomic read and update. Post
46 : * update, if the old head is not the same as the next, next will be updated to
47 : * NULL.
48 :
49 : * The Assembler
50 : * -------------
51 :
52 : * The assembler will be (a) kernel thread(s) which will wakeup when there is
53 : * work to do and go to sleep when there is nothing to do. The infrastructure to
54 : * wake up a thread when there is work to do is highly OS specific. Hence, to
55 : * accommodate that need, we will introduce a new host os entry point called
56 : * 'enqueue_to_assembler'. This entry point will enqueue to the per-cpu queue
57 : * as discussed above and will wakeup the kthread that does the assembly. The
58 : * enqueue code can be in platform independent part, so that all platforms can
59 : * reuse the code, with the infrastructure to wakeup the assembler being in
60 : * tail of the 'enqueue_to_assembler'.
61 :
62 : * For Linux kernel, we will use the workqueue infrastructure. We will create
63 : * a new workqueue for assembling the packets. The workqueue will have a dedicated
64 : * thread for each processor in the system(kernel infrastructure). So, once you
65 : * queue work to the queue, the thread for that processor will wakeup and do the
66 : * work of dequeuing it from the percpu queue and enqueuing it to the assembler
67 : * table.
68 :
69 : * When the assembler is woken up, it goes through the per-cpu queue and
70 : * dequeues all packets that have been enqueued and inserts them into the hash
71 : * list. The work area of the assembler is the hash table, where each bucket will
72 : * be a pointer to list of fragment metadata. Each such bucket will be protected
73 : * by a spinlock. The spinlock is mainly meant for exclusion between the aging
74 : * timer and the assembler.
75 :
76 : * Since a spinlock/mutex structure is needed and such structures are OS specific,
77 : * the assembler's origin will be in the OS specific part. The assembler will
78 : * define the hash buckets (that has the spinlock), with the individual and list
79 : * definitions coming from the OS independent part. While going through each
80 : * bucket, assembler will hold the bucket lock and pass control to the os
81 : * independent part.
82 :
83 : * For the linux kernel implementation, we will use spinlock rather than a mutex,
84 : * since the aging timer is invoked in an atomic context and hence can't block.
85 :
86 : * The assembler will dequeue packets from per-cpu queue and queue it in the
87 : * hash list. When the head of the fragment arrives, every fragment is dequeued
88 : * and flushed out of that entry, while still maintaining the metadata.
89 : */
90 :
91 : struct vr_timer *vr_assembler_table_scan_timer;
92 :
93 : static inline void
94 99 : __fragment_key(struct vr_fragment_key *key, unsigned short vrf,
95 : uint64_t sip_u, uint64_t sip_l, uint64_t dip_u, uint64_t dip_l,
96 : uint32_t id, unsigned short custom)
97 : {
98 99 : key->fk_sip_u = sip_u;
99 99 : key->fk_sip_l = sip_l;
100 99 : key->fk_dip_u = dip_u;
101 99 : key->fk_dip_l = dip_l;
102 99 : key->fk_id = id;
103 99 : key->fk_vrf = vrf;
104 99 : key->fk_custom = custom;
105 :
106 99 : return;
107 : }
108 :
109 : #define VR_FRAGMENT_FROM_HENTRY(entry) \
110 : (struct vr_fragment *)((entry) ?\
111 : CONTAINER_OF(f_hentry, struct vr_fragment, entry) :\
112 : NULL)
113 :
114 : #define VR_HENTRY_FROM_FRAGMENT(fe) \
115 : (vr_hentry_t *)((fe) ? &fe->f_hentry : NULL)
116 :
117 : static vr_hentry_key
118 20 : vr_fragment_get_entry_key(vr_htable_t table, vr_hentry_t *entry,
119 : unsigned int *key_len)
120 : {
121 20 : struct vr_fragment *fe =
122 : (struct vr_fragment *)CONTAINER_OF(f_hentry, struct vr_fragment, entry);
123 :
124 20 : if (key_len) {
125 20 : *key_len = sizeof(struct vr_fragment_key);
126 : }
127 :
128 20 : return &fe->f_key;
129 : }
130 :
131 : static inline void
132 5 : fragment_entry_set(struct vr_fragment *fe, struct vr_fragment_key *key,
133 : unsigned short sport, unsigned short dport)
134 : {
135 : uint64_t sec, nsec;
136 :
137 5 : fe->f_sip_u = key->fk_sip_u;
138 5 : fe->f_sip_l = key->fk_sip_l;
139 5 : fe->f_dip_u = key->fk_dip_u;
140 5 : fe->f_dip_l = key->fk_dip_l;
141 5 : fe->f_id = key->fk_id;
142 5 : fe->f_vrf = key->fk_vrf;
143 5 : fe->f_custom = key->fk_custom;
144 5 : fe->f_sport = sport;
145 5 : fe->f_dport = dport;
146 5 : vr_get_mono_time(&sec, &nsec);
147 5 : fe->f_time = sec;
148 5 : fe->f_expected = 0;
149 5 : fe->f_received = 0;
150 :
151 5 : return;
152 : }
153 :
154 : void
155 0 : vr_fragment_queue_free(struct vr_fragment_queue *queue)
156 : {
157 : struct vr_fragment_queue_element *vfqe, *next;
158 :
159 0 : vfqe = queue->vfq_tail;
160 0 : queue->vfq_tail = NULL;
161 0 : while (vfqe) {
162 0 : next = vfqe->fqe_next;
163 0 : if (vfqe->fqe_pnode.pl_packet) {
164 0 : PKT_LOG(VP_DROP_MISC, 0, 0, VR_FRAGMENT_C, __LINE__);
165 0 : vr_pfree(vfqe->fqe_pnode.pl_packet, VP_DROP_MISC);
166 0 : vfqe->fqe_pnode.pl_packet = NULL;
167 : }
168 0 : vr_free(vfqe, VR_FRAGMENT_QUEUE_ELEMENT_OBJECT);
169 0 : vfqe = next;
170 : }
171 :
172 0 : return;
173 : }
174 :
175 : static void
176 21 : vr_fragment_queue_element_free(struct vr_fragment_queue_element *vfqe,
177 : unsigned int drop_reason)
178 : {
179 21 : if (vfqe->fqe_pnode.pl_packet) {
180 13 : PKT_LOG(drop_reason, vfqe->fqe_pnode.pl_packet, 0, VR_FRAGMENT_C, __LINE__);
181 13 : vr_pfree(vfqe->fqe_pnode.pl_packet, drop_reason);
182 : }
183 :
184 21 : vr_free(vfqe, VR_FRAGMENT_QUEUE_ELEMENT_OBJECT);
185 21 : return;
186 : }
187 :
188 : static void
189 4 : fragment_free_frag(struct vr_fragment *frag)
190 : {
191 : struct vr_fragment_queue_element *fqe;
192 :
193 12 : while ((fqe = frag->f_qe)) {
194 8 : frag->f_qe = fqe->fqe_next;
195 8 : vr_fragment_queue_element_free(fqe, VP_DROP_FRAGMENTS);
196 : }
197 :
198 4 : vr_free(frag, VR_FRAGMENT_OBJECT);
199 4 : return;
200 : }
201 :
202 : static void
203 4 : fragment_unlink_frag(struct vr_fragment **prev, struct vr_fragment *frag)
204 : {
205 4 : *prev = frag->f_next;
206 4 : return;
207 : }
208 :
209 : /* Scan assembler_table[][] and free up stale entries
210 : * Arguments:
211 : * - head: Bucket entry in assembler_table[][] for the
212 : * forwarding core which executes it
213 : * It has all fragment entries which hash to that bucket
214 : */
215 : unsigned int
216 2553 : vr_assembler_table_scan(struct vr_fragment **head)
217 : {
218 2553 : unsigned int scanned = 0;
219 : uint64_t sec, nsec, dest;
220 2553 : struct vr_fragment *frag = *head, *next, **prev;
221 :
222 2553 : prev = head;
223 5106 : while (frag) {
224 2553 : next = frag->f_next;
225 :
226 2553 : vr_get_mono_time(&sec, &nsec);
227 2553 : dest = frag->f_time + VR_ASSEMBLER_TIMEOUT_SECS;
228 2553 : if (dest < frag->f_time) {
229 0 : if ((sec < frag->f_time) && (dest < sec)) {
230 0 : fragment_unlink_frag(prev, frag);
231 0 : fragment_free_frag(frag);
232 : } else {
233 0 : prev = &frag->f_next;
234 : }
235 : } else {
236 2553 : if ((sec > dest) || (sec < frag->f_time)) {
237 2 : fragment_unlink_frag(prev, frag);
238 2 : fragment_free_frag(frag);
239 : } else {
240 2551 : prev = &frag->f_next;
241 : }
242 : }
243 2553 : scanned++;
244 2553 : frag = next;
245 : }
246 :
247 2553 : return scanned;
248 : }
249 :
250 :
251 : void
252 53 : vr_assembler_table_scan_exit(void)
253 : {
254 53 : if (vr_assembler_table_scan_timer) {
255 0 : vr_delete_timer(vr_assembler_table_scan_timer);
256 0 : vr_free(vr_assembler_table_scan_timer, VR_TIMER_OBJECT);
257 0 : vr_assembler_table_scan_timer = NULL;
258 : }
259 :
260 53 : return;
261 : }
262 :
263 : /* Initialize scanning of assembler_table[][] */
264 : int
265 0 : vr_assembler_table_scan_init(void (*scanner)(void *))
266 : {
267 : struct vr_timer *vtimer;
268 :
269 0 : vr_assembler_table_scan_timer = vr_zalloc(sizeof(*vtimer), VR_TIMER_OBJECT);
270 0 : if (!vr_assembler_table_scan_timer)
271 0 : return -ENOMEM;
272 :
273 0 : vtimer = vr_assembler_table_scan_timer;
274 0 : vtimer->vt_timer = scanner;
275 0 : vtimer->vt_vr_arg = NULL;
276 0 : vtimer->vt_msecs =
277 : (VR_ASSEMBLER_TIMEOUT_SECS * 1000) / VR_ASSEMBLER_BUCKET_COUNT;
278 0 : if (vr_create_timer(vtimer)) {
279 0 : vr_free(vtimer, VR_TIMER_OBJECT);
280 0 : vr_assembler_table_scan_timer = NULL;
281 0 : return -ENOMEM;
282 : }
283 :
284 0 : return 0;
285 : }
286 :
287 : /* Flush a fragment queue entry and re-inject for packet processing */
288 : static void
289 8 : vr_fragment_flush_queue_element(struct vr_fragment_queue_element *vfqe)
290 : {
291 : struct vrouter *router;
292 : struct vr_packet *pkt;
293 :
294 : struct vr_forwarding_md fmd;
295 : struct vr_packet_node *pnode;
296 :
297 8 : if (!vfqe) {
298 0 : PKT_LOG(VP_DROP_CLONED_ORIGINAL, 0, 0, VR_FRAGMENT_C, __LINE__);
299 0 : return;
300 : }
301 :
302 8 : router = vfqe->fqe_router;
303 8 : pnode = &vfqe->fqe_pnode;
304 8 : pkt = pnode->pl_packet;
305 8 : if (!pkt) {
306 0 : PKT_LOG(VP_DROP_CLONED_ORIGINAL, 0, 0, VR_FRAGMENT_C, __LINE__);
307 0 : goto exit_flush;
308 : }
309 :
310 8 : vr_init_forwarding_md(&fmd);
311 8 : fmd.fmd_vlan = pnode->pl_vlan;
312 8 : fmd.fmd_dvrf = pnode->pl_vrf;
313 8 : vr_flow_flush_pnode(router, pnode, NULL, &fmd);
314 :
315 8 : exit_flush:
316 8 : vr_fragment_queue_element_free(vfqe, VP_DROP_CLONED_ORIGINAL);
317 8 : return;
318 : }
319 :
320 : /* Main assembler function to process fragments from per-cpu queue to
321 : * assembler_table
322 : * - Create a head fragment entry if head fragment arrives
323 : * - If a non-head fragment arrives and a corresponding head-fragment entry
324 : * is already present, flush it
325 : * - Flush non-head fragments which are already enqueued if their head fragment arrives
326 : * - Enqueue non-head fragment if their head fragment has not yet arrived
327 : *
328 : * Arguments:
329 : * - head_p: bucket entry in assembler_table
330 : * - vfqe : fragment present in the per-cpu queue
331 : */
332 : int
333 21 : vr_fragment_assemble(struct vr_fragment **head_p,
334 : struct vr_fragment_queue_element *vfqe)
335 : {
336 21 : int ret = 0;
337 : uint64_t sec, nsec;
338 21 : unsigned int list_length = 0, drop_reason;
339 21 : bool found = false, frag_head = false;
340 : uint64_t *v6_addr;
341 :
342 : struct vrouter *router;
343 : struct vr_ip *ip;
344 : struct vr_ip6 *ip6;
345 : struct vr_ip6_frag *v6_frag;
346 : struct vr_packet *pkt;
347 : struct vr_packet_node *pnode;
348 21 : struct vr_fragment *frag, *frag_flow, **prev = NULL;
349 : struct vr_fragment_queue_element *fqe;
350 : struct vr_fragment_key vfk;
351 :
352 :
353 21 : router = vfqe->fqe_router;
354 21 : pnode = &vfqe->fqe_pnode;
355 :
356 : /* Is it head fragment? */
357 21 : if (pnode->pl_flags & PN_FLAG_FRAGMENT_HEAD)
358 5 : frag_head = true;
359 :
360 21 : pkt = pnode->pl_packet;
361 21 : ip = (struct vr_ip *)pkt_network_header(pkt);
362 :
363 : /* Get hash of the fragment key */
364 21 : if (vr_ip_is_ip6(ip)) {
365 0 : ip6 = (struct vr_ip6 *)ip;
366 0 : v6_frag = (struct vr_ip6_frag *)(ip6 + 1);
367 0 : v6_addr = (uint64_t *)(ip6->ip6_src);
368 0 : __fragment_key(&vfk, pnode->pl_vrf, *v6_addr, *(v6_addr + 1),
369 0 : *(v6_addr +2 ), *(v6_addr + 3), v6_frag->ip6_frag_id,
370 0 : pnode->pl_custom);
371 : } else {
372 21 : __fragment_key(&vfk, pnode->pl_vrf, 0, pnode->pl_inner_src_ip,
373 21 : 0, pnode->pl_inner_dst_ip, ip->ip_id, pnode->pl_custom);
374 : }
375 :
376 : /* Check if the fragment with the same key is found
377 : * in the assembler bucket
378 : */
379 21 : frag = *head_p;
380 21 : prev = head_p;
381 21 : while (frag) {
382 14 : list_length++;
383 14 : if (!memcmp(&frag->f_key, &vfk, sizeof(vfk))) {
384 14 : found = true;
385 14 : break;
386 : }
387 :
388 0 : prev = &frag->f_next;
389 0 : frag = frag->f_next;
390 : }
391 :
392 : /* If its a non-head fragment and the head fragment has
393 : * already arrived, just flush (re-inject) it for regular
394 : * packet processing
395 : */
396 21 : if (!frag_head) {
397 16 : frag_flow = vr_fragment_get(router, pnode->pl_vrf, ip, pnode->pl_custom);
398 16 : if (frag_flow) {
399 0 : vr_fragment_flush_queue_element(vfqe);
400 0 : return 0;
401 : }
402 : }
403 :
404 : /* If fragment entry is not found */
405 21 : if (!found) {
406 : /* If its a fragment head, it's a cloned packet
407 : * Nothing to be done, just exit
408 : */
409 7 : if (frag_head) {
410 3 : drop_reason = VP_DROP_CLONED_ORIGINAL;
411 3 : PKT_LOG(drop_reason, pkt, 0, VR_FRAGMENT_C, __LINE__);
412 3 : goto exit_assembly;
413 : }
414 :
415 : /* Check if max length of assembler bucket is exceeded */
416 4 : if (list_length > VR_MAX_FRAGMENTS_PER_ASSEMBLER_QUEUE) {
417 0 : drop_reason = VP_DROP_FRAGMENT_QUEUE_FAIL;
418 0 : PKT_LOG(drop_reason, pkt, 0, VR_FRAGMENT_C, __LINE__);
419 0 : goto exit_assembly;
420 : }
421 :
422 : /* If it's a non-head fragment, allocate a new entry */
423 4 : frag = vr_zalloc(sizeof(*frag), VR_FRAGMENT_OBJECT);
424 4 : if (!frag) {
425 0 : ret = -ENOMEM;
426 0 : drop_reason = VP_DROP_NO_MEMORY;
427 0 : PKT_LOG(drop_reason, pkt, 0, VR_FRAGMENT_C, __LINE__);
428 0 : goto exit_assembly;
429 : }
430 :
431 4 : memcpy(&frag->f_key, &vfk, sizeof(vfk));
432 : /* This is a non-head fragment, so the port info
433 : * is not valid since head fragment has not arrived
434 : */
435 4 : frag->f_port_info_valid = false;
436 : }
437 :
438 : /* Timestamp the fragment */
439 18 : vr_get_mono_time(&sec, &nsec);
440 18 : frag->f_time = sec;
441 :
442 : /* If frag entry is not found, create a new entry
443 : * at the head of the assembler bucket
444 : */
445 18 : if (!found) {
446 4 : prev = head_p;
447 4 : frag->f_next = *head_p;
448 4 : *head_p = frag;
449 : }
450 :
451 : /* If the packet is non a fragment head,
452 : * append the packet to the end of the fragment
453 : * queue entry of the assembler bucket
454 : */
455 18 : if (!frag_head) {
456 16 : vfqe->fqe_next = NULL;
457 16 : fqe = frag->f_qe;
458 16 : if (!fqe) {
459 4 : frag->f_qe = vfqe;
460 : } else {
461 24 : while (fqe) {
462 24 : if (fqe->fqe_next) {
463 12 : fqe = fqe->fqe_next;
464 : } else {
465 12 : break;
466 : }
467 : }
468 :
469 12 : fqe->fqe_next = vfqe;
470 : }
471 : } else {
472 : /* If the packet is a fragment head, make the port
473 : * info as valid since it contains a valid transport
474 : * header. Also, free the cloned head-fragment
475 : */
476 2 : frag->f_port_info_valid = true;
477 2 : PKT_LOG(VP_DROP_CLONED_ORIGINAL, pkt, 0, VR_FRAGMENT_C, __LINE__);
478 2 : vr_fragment_queue_element_free(vfqe, VP_DROP_CLONED_ORIGINAL);
479 : }
480 :
481 : /* If a head fragment arrives after the non-head
482 : * fragments, it means that we have the valid port info.
483 : * In this case, flush the non-head fragments and
484 : * re-inject them for normal packet processing
485 : */
486 18 : if (frag->f_port_info_valid) {
487 10 : while ((fqe = frag->f_qe)) {
488 8 : frag->f_qe = fqe->fqe_next;
489 8 : vr_fragment_flush_queue_element(fqe);
490 : }
491 :
492 : /* After flushing, remove the fragment entries from assembler table */
493 2 : fragment_unlink_frag(prev, frag);
494 2 : fragment_free_frag(frag);
495 : }
496 :
497 18 : return 0;
498 :
499 3 : exit_assembly:
500 3 : vr_fragment_queue_element_free(vfqe, drop_reason);
501 3 : return ret;
502 : }
503 :
504 : void
505 21 : vr_fragment_assemble_queue(struct vr_fragment_queue *vfq)
506 : {
507 : struct vr_packet_node *pnode;
508 : struct vr_fragment_queue_element *tail, *tail_n, *tail_p, *tail_pn;
509 :
510 21 : tail = vr_sync_lock_test_and_set_p(&vfq->vfq_tail, NULL);
511 21 : if (!tail) {
512 0 : return;
513 : }
514 :
515 : /*
516 : * first, reverse the list, since packets that came later are at the
517 : * head of the list
518 : */
519 21 : tail_p = tail->fqe_next;
520 21 : tail->fqe_next = NULL;
521 21 : while (tail_p) {
522 0 : tail_pn = tail_p->fqe_next;
523 0 : tail_p->fqe_next = tail;
524 0 : tail = tail_p;
525 0 : tail_p = tail_pn;
526 : }
527 :
528 : /* go through the list and insert it in the assembler work area */
529 42 : while (tail) {
530 21 : tail_n = tail->fqe_next;
531 21 : tail->fqe_next = NULL;
532 :
533 21 : pnode = &tail->fqe_pnode;
534 21 : if (pnode->pl_packet) {
535 21 : vr_fragment_sync_assemble(tail);
536 : }
537 :
538 21 : tail = tail_n;
539 : }
540 : }
541 :
542 : static uint32_t
543 21 : __vr_fragment_get_hash(unsigned int vrf, uint64_t sip_u, uint64_t sip_l,
544 : uint64_t dip_u, uint64_t dip_l, uint32_t id, unsigned short custom)
545 : {
546 : struct vr_fragment_key vfk;
547 :
548 21 : __fragment_key(&vfk, vrf, sip_u, sip_l, dip_u, dip_l, id, custom);
549 :
550 21 : return vr_hash(&vfk, sizeof(vfk), 0);
551 : }
552 :
553 : uint32_t
554 21 : vr_fragment_get_hash(struct vr_packet_node *pnode)
555 : {
556 : uint64_t *v6_addr;
557 : struct vr_ip *ip;
558 : struct vr_ip6 *ip6;
559 : struct vr_ip6_frag *v6_frag;
560 : struct vr_packet *pkt;
561 :
562 21 : if (!pnode || !pnode->pl_packet)
563 0 : return (uint32_t)-1;
564 :
565 21 : pkt = pnode->pl_packet;
566 :
567 21 : ip = (struct vr_ip *)pkt_network_header(pkt);
568 21 : if (vr_ip_is_ip6(ip)) {
569 0 : ip6 = (struct vr_ip6 *)pkt_network_header(pkt);
570 0 : v6_frag = (struct vr_ip6_frag *)(ip6 + 1);
571 0 : v6_addr = (uint64_t *)(ip6->ip6_src);
572 :
573 0 : return __vr_fragment_get_hash(pnode->pl_vrf, *v6_addr, *(v6_addr+ 1),
574 0 : *(v6_addr + 2), *(v6_addr + 3), v6_frag->ip6_frag_id,
575 0 : pnode->pl_custom);
576 21 : } else if(vr_ip_is_ip4(ip)) {
577 21 : return __vr_fragment_get_hash(pnode->pl_vrf, 0, pnode->pl_inner_src_ip,
578 21 : 0, pnode->pl_inner_dst_ip, ip->ip_id, pnode->pl_custom);
579 : }
580 :
581 0 : return (uint32_t)-1;
582 : }
583 :
584 : /* Enqueue fragment in per cpu queue */
585 : int
586 21 : vr_fragment_enqueue(struct vrouter *router,
587 : struct vr_fragment_queue *vfq,
588 : struct vr_packet *pkt, struct vr_forwarding_md *fmd)
589 : {
590 21 : bool swapped = false;
591 : unsigned int i;
592 :
593 : struct vr_packet_node *pnode;
594 21 : struct vr_fragment_queue_element *fqe = NULL, *tail, **tailp;
595 :
596 21 : tailp = &vfq->vfq_tail;
597 21 : if (*tailp == NULL) {
598 21 : vfq->vfq_length = 0;
599 : } else {
600 0 : if ((vfq->vfq_length + 1) > VR_MAX_FRAGMENTS_PER_CPU_QUEUE) {
601 0 : PKT_LOG(VP_DROP_FRAGMENTS, pkt, 0, VR_FRAGMENT_C, __LINE__);
602 0 : goto fail;
603 : }
604 : }
605 :
606 : /* Check if the total number of fragmented packets across
607 : * all cores exceeded. */
608 42 : if (vrouter_host->hos_is_frag_limit_exceeded &&
609 21 : vrouter_host->hos_is_frag_limit_exceeded()) {
610 0 : PKT_LOG(VP_DROP_FRAGMENTS, pkt, 0, VR_FRAGMENT_C, __LINE__);
611 0 : goto fail;
612 : }
613 :
614 21 : fqe = vr_malloc(sizeof(*fqe), VR_FRAGMENT_QUEUE_ELEMENT_OBJECT);
615 21 : if (!fqe) {
616 0 : PKT_LOG(VP_DROP_FRAGMENTS, pkt, 0, VR_FRAGMENT_C, __LINE__);
617 0 : goto fail;
618 : }
619 21 : fqe->fqe_router = router;
620 21 : fqe->fqe_next = NULL;
621 :
622 21 : pkt->vp_flags &= ~VP_FLAG_FLOW_SET;
623 :
624 21 : pnode = &fqe->fqe_pnode;
625 21 : vr_flow_fill_pnode(pnode, pkt, fmd);
626 :
627 : /*
628 : * we are actually competing with an existing assembler work that must
629 : * be in the process of dequeueing the list from the per-cpu queue.
630 : * we try thrice to enqueue our element. It is unlikely that it will
631 : * fail more than once
632 : *
633 : * calculation of vfq_length could be erroneous. But, we will err by
634 : * maximum 1, which is fine.
635 : */
636 21 : for (i = 0; i < VR_FRAG_ENQUEUE_ATTEMPTS; i++) {
637 21 : tail = *tailp;
638 21 : fqe->fqe_next = tail;
639 21 : vfq->vfq_length++;
640 21 : swapped = vr_sync_bool_compare_and_swap_p(tailp, tail, fqe);
641 21 : if (swapped) {
642 21 : if (tail == NULL)
643 21 : vfq->vfq_length = 1;
644 21 : break;
645 : } else {
646 0 : vfq->vfq_length--;
647 0 : if (i == (VR_FRAG_ENQUEUE_ATTEMPTS - 1)) {
648 0 : PKT_LOG(VP_DROP_FRAGMENTS, pkt, 0, VR_FRAGMENT_C, __LINE__);
649 0 : goto fail;
650 : }
651 : }
652 : }
653 :
654 21 : return 0;
655 :
656 0 : fail:
657 0 : if (fqe)
658 0 : vr_free(fqe, VR_FRAGMENT_QUEUE_ELEMENT_OBJECT);
659 :
660 0 : vr_pfree(pkt, VP_DROP_FRAGMENTS);
661 0 : return -1;
662 : }
663 :
664 :
665 : /* Delete fragment from the fragment hash table */
666 : void
667 5 : vr_fragment_del(vr_htable_t table, struct vr_fragment *fe)
668 : {
669 5 : fe->f_dip_u = fe->f_dip_l = 0;
670 5 : fe->f_received = 0;
671 5 : vr_htable_release_hentry(table, VR_HENTRY_FROM_FRAGMENT(fe));
672 :
673 5 : return;
674 : }
675 :
676 : /* Add fragment from the fragment hash table */
677 : static int
678 5 : vr_fragment_add(struct vrouter *router, struct vr_fragment_key *key,
679 : unsigned short sport, unsigned short dport, unsigned short len)
680 : {
681 : void *fe_ent;
682 : struct vr_fragment *fe;
683 5 : vr_htable_t ftable = router->vr_fragment_table;
684 :
685 5 : fe_ent = (void *) vr_htable_find_hentry(ftable, key, 0);
686 5 : fe = VR_FRAGMENT_FROM_HENTRY(fe_ent);
687 5 : if (fe)
688 0 : return 0;
689 :
690 5 : fe_ent = (void *) vr_htable_find_free_hentry(ftable, key, 0);
691 5 : fe = VR_FRAGMENT_FROM_HENTRY(fe_ent);
692 5 : if (!fe)
693 0 : return -ENOMEM;
694 :
695 5 : fragment_entry_set(fe, key, sport, dport);
696 5 : fe->f_received += len;
697 :
698 5 : return 0;
699 : }
700 :
701 : /* Add v4 fragment from the fragment hash table */
702 : int
703 5 : vr_v4_fragment_add(struct vrouter *router, unsigned short vrf,
704 : struct vr_ip *iph, unsigned short sport, unsigned short dport,
705 : unsigned short custom)
706 : {
707 : struct vr_fragment_key key;
708 :
709 5 : __fragment_key(&key, vrf, 0, iph->ip_saddr, 0,
710 5 : iph->ip_daddr, iph->ip_id, custom);
711 :
712 10 : return vr_fragment_add(router, &key, sport, dport,
713 5 : (ntohs(iph->ip_len) - iph->ip_hl * 4));
714 : }
715 :
716 : /* Add v6 fragment from the fragment hash table */
717 : int
718 0 : vr_v6_fragment_add(struct vrouter *router, unsigned short vrf,
719 : struct vr_ip6 *ip6, unsigned short sport, unsigned short dport,
720 : unsigned short custom)
721 : {
722 : uint64_t *v6_addr;
723 : struct vr_fragment_key key;
724 : struct vr_ip6_frag *v6_frag;
725 :
726 0 : v6_addr = (uint64_t *)(ip6->ip6_src);
727 0 : v6_frag = (struct vr_ip6_frag *)(ip6 + 1);
728 0 : __fragment_key(&key, vrf, *v6_addr, *(v6_addr + 1), *(v6_addr + 2),
729 0 : *(v6_addr + 3), v6_frag->ip6_frag_id, custom);
730 :
731 0 : return vr_fragment_add(router, &key, sport, dport,
732 0 : (ntohs(ip6->ip6_plen) - sizeof(struct vr_ip6_frag)));
733 : }
734 :
735 : /* Check if fragment entry exists in the fragment hash table */
736 : struct vr_fragment *
737 52 : vr_fragment_get(struct vrouter *router, unsigned short vrf,
738 : struct vr_ip *ip, unsigned short custom)
739 : {
740 : uint64_t sec, nsec;
741 : uint64_t *v6_addr;
742 : struct vr_ip6 *ip6;
743 : struct vr_ip6_frag *v6_frag;
744 : struct vr_fragment *fe;
745 : struct vr_fragment_key key;
746 : vr_htable_t ftable;
747 : void *fe_ent;
748 :
749 52 : if (vr_ip_is_ip6(ip)) {
750 0 : ip6 = (struct vr_ip6 *)ip;
751 0 : if (ip6->ip6_nxt == VR_IP6_PROTO_FRAG) {
752 0 : v6_frag = (struct vr_ip6_frag *)(ip6 + 1);
753 0 : v6_addr = (uint64_t *)(ip6->ip6_src);
754 0 : __fragment_key(&key, vrf, *v6_addr, *(v6_addr+ 1),
755 0 : *(v6_addr + 2), *(v6_addr+ 3), v6_frag->ip6_frag_id,
756 : custom);
757 : }
758 52 : } else if (vr_ip_is_ip4(ip)) {
759 52 : __fragment_key(&key, vrf, 0, ip->ip_saddr, 0, ip->ip_daddr,
760 52 : ip->ip_id, custom);
761 : } else {
762 0 : return NULL;
763 : }
764 :
765 :
766 52 : ftable = router->vr_fragment_table;
767 :
768 52 : fe_ent = (void *) vr_htable_find_hentry(ftable, &key, 0);
769 52 : fe = VR_FRAGMENT_FROM_HENTRY(fe_ent);
770 52 : if (fe) {
771 20 : vr_get_mono_time(&sec, &nsec);
772 20 : fe->f_time = sec;
773 : }
774 :
775 52 : return fe;
776 : }
777 :
778 :
779 : struct scanner_params {
780 : struct vrouter *sp_router;
781 : int sp_scan_marker;
782 : };
783 :
784 : static void
785 12918784 : __fragment_reap(vr_htable_t table, vr_hentry_t *ent,
786 : unsigned int index, void *data)
787 : {
788 : uint64_t sec, nsec;
789 : struct vr_fragment *fe;
790 :
791 12918784 : fe = VR_FRAGMENT_FROM_HENTRY(ent);
792 12918784 : if (!fe || ((!fe->f_dip_u) && !(fe->f_dip_l)))
793 12918784 : return;
794 :
795 0 : vr_get_mono_time(&sec, &nsec);
796 0 : if (sec > fe->f_time + VR_FRAG_HASH_TABLE_TIMEOUT_SECS) {
797 0 : vr_fragment_del(table, fe);
798 : }
799 :
800 0 : return;
801 : }
802 :
803 : /* Reap/cleanup stale entries from fragment hash table */
804 : static int
805 6308 : fragment_reap(vr_htable_t htable, int start)
806 : {
807 6308 : return vr_htable_trav_range(htable, start,
808 : VR_FRAG_HASH_TABLE_ENTRIES_PER_SCAN,
809 : __fragment_reap, NULL);
810 : }
811 :
812 : /* Scanner callback for fragment hash table */
813 : static void
814 6308 : fragment_table_scanner(void *arg)
815 : {
816 : int ret;
817 6308 : struct scanner_params *sp = (struct scanner_params *)arg;
818 :
819 6308 : ret = fragment_reap(sp->sp_router->vr_fragment_table, sp->sp_scan_marker);
820 6308 : if (ret < 0)
821 0 : return;
822 :
823 6308 : sp->sp_scan_marker = ret;
824 6308 : return;
825 : }
826 :
827 : /* Initialize scanner for fragment hash table */
828 : static struct vr_timer *
829 53 : fragment_table_scanner_init(struct vrouter *router)
830 : {
831 : struct vr_timer *vtimer;
832 : struct scanner_params *scanner;
833 :
834 53 : scanner = vr_zalloc(sizeof(*scanner), VR_FRAGMENT_SCANNER_OBJECT);
835 53 : if (!scanner) {
836 0 : vr_module_error(-ENOMEM, __FUNCTION__, __LINE__, sizeof(*scanner));
837 0 : return NULL;
838 : }
839 53 : scanner->sp_router = router;
840 53 : scanner->sp_scan_marker = 0;
841 :
842 53 : vtimer = vr_malloc(sizeof(*vtimer), VR_TIMER_OBJECT);
843 53 : if (!vtimer) {
844 0 : vr_module_error(-ENOMEM, __FUNCTION__, __LINE__, sizeof(*vtimer));
845 0 : goto fail_init;
846 : }
847 :
848 53 : vtimer->vt_timer = fragment_table_scanner;
849 53 : vtimer->vt_vr_arg = scanner;
850 53 : vtimer->vt_msecs = VR_FRAG_HASH_TABLE_SCANNER_INTERVAL_MSEC;
851 53 : if (vr_create_timer(vtimer)) {
852 0 : vr_module_error(-ENOMEM, __FUNCTION__, __LINE__, 0);
853 0 : goto fail_init;
854 : }
855 :
856 53 : return vtimer;
857 :
858 0 : fail_init:
859 0 : if (scanner)
860 0 : vr_free(scanner, VR_FRAGMENT_SCANNER_OBJECT);
861 :
862 0 : return NULL;
863 : }
864 :
865 : static void
866 53 : vr_fragment_table_scanner_exit(struct vrouter *router)
867 : {
868 53 : if (router->vr_fragment_table_scanner) {
869 53 : vr_delete_timer(router->vr_fragment_table_scanner);
870 53 : vr_free(router->vr_fragment_table_scanner->vt_vr_arg,
871 : VR_FRAGMENT_SCANNER_OBJECT);
872 53 : vr_free(router->vr_fragment_table_scanner, VR_TIMER_OBJECT);
873 53 : router->vr_fragment_table_scanner = NULL;
874 : }
875 :
876 53 : return;
877 : }
878 :
879 : static int
880 53 : vr_fragment_table_scanner_init(struct vrouter *router)
881 : {
882 53 : if (!router->vr_fragment_table_scanner) {
883 53 : router->vr_fragment_table_scanner =
884 53 : fragment_table_scanner_init(router);
885 53 : if (!router->vr_fragment_table_scanner) {
886 0 : return -ENOMEM;
887 : }
888 : }
889 :
890 53 : return 0;
891 : }
892 :
893 : void
894 53 : vr_fragment_table_exit(struct vrouter *router)
895 : {
896 53 : vr_fragment_table_scanner_exit(router);
897 :
898 53 : if (router->vr_fragment_table) {
899 53 : vr_htable_delete(router->vr_fragment_table);
900 53 : router->vr_fragment_table = NULL;
901 : }
902 :
903 53 : return;
904 : }
905 :
906 : /* Initialize fragment hash table */
907 : int
908 53 : vr_fragment_table_init(struct vrouter *router)
909 : {
910 : int ret;
911 :
912 53 : if (!router->vr_fragment_table) {
913 53 : router->vr_fragment_table = vr_htable_create(router,
914 : VR_FRAG_HASH_TABLE_ENTRIES, VR_FRAG_HASH_OTABLE_ENTRIES,
915 : sizeof(struct vr_fragment), sizeof(struct vr_fragment_key),
916 : VR_FRAG_HASH_TABLE_BUCKETS, vr_fragment_get_entry_key);
917 53 : if (!router->vr_fragment_table) {
918 0 : return vr_module_error(-ENOMEM, __FUNCTION__, __LINE__,
919 : VR_FRAG_HASH_TABLE_ENTRIES + VR_FRAG_HASH_OTABLE_ENTRIES);
920 : }
921 : }
922 :
923 53 : if ((ret = vr_fragment_table_scanner_init(router)))
924 0 : return ret;
925 :
926 53 : return 0;
927 : }
|