LCOV - code coverage report
Current view: top level - root/contrail/vrouter/dp-core - vr_fragment.c (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 270 386 69.9 %
Date: 2026-11-09 02:10:49 Functions: 26 29 89.7 %
Legend: Lines: hit not hit

          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             : }

Generated by: LCOV version 1.14