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

Generated by: LCOV version 1.14