LCOV - code coverage report
Current view: top level - home/net-next/net/quic - connid.c (source / functions) Hit Total Coverage
Test: quic.info Lines: 103 113 91.2 %
Date: 2025-07-04 13:24:45 Functions: 12 13 92.3 %

          Line data    Source code
       1             : // SPDX-License-Identifier: GPL-2.0-or-later
       2             : /* QUIC kernel implementation
       3             :  * (C) Copyright Red Hat Corp. 2023
       4             :  *
       5             :  * This file is part of the QUIC kernel implementation
       6             :  *
       7             :  * Initialization/cleanup for QUIC protocol support.
       8             :  *
       9             :  * Written or modified by:
      10             :  *    Xin Long <lucien.xin@gmail.com>
      11             :  */
      12             : 
      13             : #include <linux/quic.h>
      14             : #include <net/sock.h>
      15             : 
      16             : #include "common.h"
      17             : #include "connid.h"
      18             : 
      19             : /* Lookup a source connection ID (scid) in the global source connection ID hash table. */
      20     6573255 : struct quic_conn_id *quic_conn_id_lookup(struct net *net, u8 *scid, u32 len)
      21             : {
      22     6573255 :         struct quic_hash_head *head = quic_source_conn_id_head(net, scid);
      23     6573186 :         struct quic_source_conn_id *s_conn_id;
      24             : 
      25     6573186 :         spin_lock(&head->s_lock);
      26    14797196 :         hlist_for_each_entry(s_conn_id, &head->head, node) {
      27     8221650 :                 if (net == sock_net(s_conn_id->sk) && s_conn_id->common.id.len == len &&
      28    16442878 :                     !memcmp(scid, &s_conn_id->common.id.data, s_conn_id->common.id.len))
      29             :                         break;
      30             :         }
      31             : 
      32     6574318 :         spin_unlock(&head->s_lock);
      33     6574346 :         return &s_conn_id->common.id;
      34             : }
      35             : 
      36             : /* Check if a given stateless reset token exists in any connection ID in the connection ID set. */
      37           0 : bool quic_conn_id_token_exists(struct quic_conn_id_set *id_set, u8 *token)
      38             : {
      39           0 :         struct quic_common_conn_id *common;
      40           0 :         struct quic_dest_conn_id *dcid;
      41             : 
      42           0 :         dcid = (struct quic_dest_conn_id *)id_set->active;
      43           0 :         if (!memcmp(dcid->token, token, QUIC_CONN_ID_TOKEN_LEN)) /* Fast path. */
      44             :                 return true;
      45             : 
      46           0 :         list_for_each_entry(common, &id_set->head, list) {
      47           0 :                 dcid = (struct quic_dest_conn_id *)common;
      48           0 :                 if (common == id_set->active)
      49           0 :                         continue;
      50           0 :                 if (!memcmp(dcid->token, token, QUIC_CONN_ID_TOKEN_LEN))
      51             :                         return true;
      52             :         }
      53             :         return false;
      54             : }
      55             : 
      56        6975 : static void quic_source_conn_id_free_rcu(struct rcu_head *head)
      57             : {
      58        6975 :         struct quic_source_conn_id *s_conn_id;
      59             : 
      60        6975 :         s_conn_id = container_of(head, struct quic_source_conn_id, rcu);
      61        6975 :         kfree(s_conn_id);
      62        6981 : }
      63             : 
      64        6985 : static void quic_source_conn_id_free(struct quic_source_conn_id *s_conn_id)
      65             : {
      66        6985 :         u8 *data = s_conn_id->common.id.data;
      67        6985 :         struct quic_hash_head *head;
      68             : 
      69        6985 :         if (!hlist_unhashed(&s_conn_id->node)) {
      70        6985 :                 head = quic_source_conn_id_head(sock_net(s_conn_id->sk), data);
      71        6985 :                 spin_lock_bh(&head->s_lock);
      72        6986 :                 hlist_del_init(&s_conn_id->node);
      73        6986 :                 spin_unlock_bh(&head->s_lock);
      74             :         }
      75             : 
      76             :         /* Freeing is deferred via RCU to avoid use-after-free during concurrent lookups. */
      77        6986 :         call_rcu(&s_conn_id->rcu, quic_source_conn_id_free_rcu);
      78        6987 : }
      79             : 
      80       12922 : static void quic_conn_id_del(struct quic_common_conn_id *common)
      81             : {
      82       12922 :         list_del(&common->list);
      83       12925 :         if (!common->hashed) {
      84        5940 :                 kfree(common);
      85        5940 :                 return;
      86             :         }
      87        6985 :         quic_source_conn_id_free((struct quic_source_conn_id *)common);
      88             : }
      89             : 
      90             : /* Add a connection ID with sequence number and associated private data to the connection ID set. */
      91       13467 : int quic_conn_id_add(struct quic_conn_id_set *id_set,
      92             :                      struct quic_conn_id *conn_id, u32 number, void *data)
      93             : {
      94       13467 :         struct quic_source_conn_id *s_conn_id;
      95       13467 :         struct quic_dest_conn_id *d_conn_id;
      96       13467 :         struct quic_common_conn_id *common;
      97       13467 :         struct quic_hash_head *head;
      98       13467 :         struct list_head *list;
      99             : 
     100             :         /* Locate insertion point to keep list ordered by number. */
     101       13467 :         list = &id_set->head;
     102       49093 :         list_for_each_entry(common, list, list) {
     103       37445 :                 if (number == common->number)
     104             :                         return 0; /* Ignore if it is already exists on the list. */
     105       36911 :                 if (number < common->number) {
     106             :                         list = &common->list;
     107             :                         break;
     108             :                 }
     109             :         }
     110             : 
     111       12933 :         if (conn_id->len > QUIC_CONN_ID_MAX_LEN)
     112             :                 return -EINVAL;
     113       12933 :         common = kzalloc(id_set->entry_size, GFP_ATOMIC);
     114       12933 :         if (!common)
     115             :                 return -ENOMEM;
     116       12933 :         common->id = *conn_id;
     117       12933 :         common->number = number;
     118       12933 :         if (id_set->entry_size == sizeof(struct quic_dest_conn_id)) {
     119             :                 /* For destination connection IDs, copy the stateless reset token if available. */
     120        5944 :                 if (data) {
     121        4825 :                         d_conn_id = (struct quic_dest_conn_id *)common;
     122        9650 :                         memcpy(d_conn_id->token, data, QUIC_CONN_ID_TOKEN_LEN);
     123             :                 }
     124             :         } else {
     125             :                 /* For source connection IDs, mark as hashed and insert into the global source
     126             :                  * connection ID hashtable.
     127             :                  */
     128        6989 :                 common->hashed = 1;
     129        6989 :                 s_conn_id = (struct quic_source_conn_id *)common;
     130        6989 :                 s_conn_id->sk = data;
     131             : 
     132        6989 :                 head = quic_source_conn_id_head(sock_net(s_conn_id->sk), common->id.data);
     133        6989 :                 spin_lock_bh(&head->s_lock);
     134        6989 :                 hlist_add_head(&s_conn_id->node, &head->head);
     135        6989 :                 spin_unlock_bh(&head->s_lock);
     136             :         }
     137       12933 :         list_add_tail(&common->list, list);
     138             : 
     139       12933 :         if (number == quic_conn_id_last_number(id_set) + 1) {
     140       11638 :                 if (!id_set->active)
     141        2238 :                         id_set->active = common;
     142       11638 :                 id_set->count++;
     143             : 
     144             :                 /* Increment count for consecutive following IDs. */
     145       12921 :                 list_for_each_entry_continue(common, &id_set->head, list) {
     146        1286 :                         if (common->number != ++number)
     147             :                                 break;
     148        1283 :                         id_set->count++;
     149             :                 }
     150             :         }
     151             :         return 0;
     152             : }
     153             : 
     154             : /* Remove connection IDs from the set with sequence numbers less than or equal to a number. */
     155         275 : void quic_conn_id_remove(struct quic_conn_id_set *id_set, u32 number)
     156             : {
     157         275 :         struct quic_common_conn_id *common, *tmp;
     158         275 :         struct list_head *list;
     159             : 
     160         275 :         list = &id_set->head;
     161        2061 :         list_for_each_entry_safe(common, tmp, list, list) {
     162        1786 :                 if (common->number <= number) {
     163         284 :                         if (id_set->active == common)
     164         226 :                                 id_set->active = tmp;
     165         284 :                         quic_conn_id_del(common);
     166         284 :                         id_set->count--;
     167             :                 }
     168             :         }
     169         275 : }
     170             : 
     171          96 : struct quic_conn_id *quic_conn_id_find(struct quic_conn_id_set *id_set, u32 number)
     172             : {
     173          96 :         struct quic_common_conn_id *common;
     174             : 
     175         332 :         list_for_each_entry(common, &id_set->head, list)
     176         324 :                 if (common->number == number)
     177          88 :                         return &common->id;
     178             :         return NULL;
     179             : }
     180             : 
     181          81 : void quic_conn_id_update_active(struct quic_conn_id_set *id_set, u32 number)
     182             : {
     183          81 :         struct quic_conn_id *conn_id;
     184             : 
     185          81 :         if (number == id_set->active->number)
     186             :                 return;
     187          68 :         conn_id = quic_conn_id_find(id_set, number);
     188          68 :         if (!conn_id)
     189             :                 return;
     190          68 :         quic_conn_id_set_active(id_set, conn_id);
     191             : }
     192             : 
     193        2238 : void quic_conn_id_set_init(struct quic_conn_id_set *id_set, bool source)
     194             : {
     195        2238 :         id_set->entry_size = source ? sizeof(struct quic_source_conn_id)
     196             :                                     : sizeof(struct quic_dest_conn_id);
     197        2238 :         INIT_LIST_HEAD(&id_set->head);
     198        2238 : }
     199             : 
     200        2236 : void quic_conn_id_set_free(struct quic_conn_id_set *id_set)
     201             : {
     202        2236 :         struct quic_common_conn_id *common, *tmp;
     203             : 
     204       14877 :         list_for_each_entry_safe(common, tmp, &id_set->head, list)
     205       12641 :                 quic_conn_id_del(common);
     206        2236 :         id_set->count = 0;
     207        2236 :         id_set->active = NULL;
     208        2236 : }
     209             : 
     210        2079 : void quic_conn_id_get_param(struct quic_conn_id_set *id_set, struct quic_transport_param *p)
     211             : {
     212        2079 :         p->active_connection_id_limit = id_set->max_count;
     213        2079 : }
     214             : 
     215        3172 : void quic_conn_id_set_param(struct quic_conn_id_set *id_set, struct quic_transport_param *p)
     216             : {
     217        3172 :         id_set->max_count = p->active_connection_id_limit;
     218        3172 : }

Generated by: LCOV version 1.14