nf_conntrack_extend.c (4519B)
1// SPDX-License-Identifier: GPL-2.0-or-later 2/* Structure dynamic extension infrastructure 3 * Copyright (C) 2004 Rusty Russell IBM Corporation 4 * Copyright (C) 2007 Netfilter Core Team <coreteam@netfilter.org> 5 * Copyright (C) 2007 USAGI/WIDE Project <http://www.linux-ipv6.org> 6 */ 7#include <linux/kernel.h> 8#include <linux/kmemleak.h> 9#include <linux/module.h> 10#include <linux/mutex.h> 11#include <linux/rcupdate.h> 12#include <linux/slab.h> 13#include <linux/skbuff.h> 14#include <net/netfilter/nf_conntrack_extend.h> 15 16#include <net/netfilter/nf_conntrack_helper.h> 17#include <net/netfilter/nf_conntrack_acct.h> 18#include <net/netfilter/nf_conntrack_seqadj.h> 19#include <net/netfilter/nf_conntrack_ecache.h> 20#include <net/netfilter/nf_conntrack_zones.h> 21#include <net/netfilter/nf_conntrack_timestamp.h> 22#include <net/netfilter/nf_conntrack_timeout.h> 23#include <net/netfilter/nf_conntrack_labels.h> 24#include <net/netfilter/nf_conntrack_synproxy.h> 25#include <net/netfilter/nf_conntrack_act_ct.h> 26#include <net/netfilter/nf_nat.h> 27 28#define NF_CT_EXT_PREALLOC 128u /* conntrack events are on by default */ 29 30atomic_t nf_conntrack_ext_genid __read_mostly = ATOMIC_INIT(1); 31 32static const u8 nf_ct_ext_type_len[NF_CT_EXT_NUM] = { 33 [NF_CT_EXT_HELPER] = sizeof(struct nf_conn_help), 34#if IS_ENABLED(CONFIG_NF_NAT) 35 [NF_CT_EXT_NAT] = sizeof(struct nf_conn_nat), 36#endif 37 [NF_CT_EXT_SEQADJ] = sizeof(struct nf_conn_seqadj), 38 [NF_CT_EXT_ACCT] = sizeof(struct nf_conn_acct), 39#ifdef CONFIG_NF_CONNTRACK_EVENTS 40 [NF_CT_EXT_ECACHE] = sizeof(struct nf_conntrack_ecache), 41#endif 42#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP 43 [NF_CT_EXT_TSTAMP] = sizeof(struct nf_conn_acct), 44#endif 45#ifdef CONFIG_NF_CONNTRACK_TIMEOUT 46 [NF_CT_EXT_TIMEOUT] = sizeof(struct nf_conn_tstamp), 47#endif 48#ifdef CONFIG_NF_CONNTRACK_LABELS 49 [NF_CT_EXT_LABELS] = sizeof(struct nf_conn_labels), 50#endif 51#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) 52 [NF_CT_EXT_SYNPROXY] = sizeof(struct nf_conn_synproxy), 53#endif 54#if IS_ENABLED(CONFIG_NET_ACT_CT) 55 [NF_CT_EXT_ACT_CT] = sizeof(struct nf_conn_act_ct_ext), 56#endif 57}; 58 59static __always_inline unsigned int total_extension_size(void) 60{ 61 /* remember to add new extensions below */ 62 BUILD_BUG_ON(NF_CT_EXT_NUM > 10); 63 64 return sizeof(struct nf_ct_ext) + 65 sizeof(struct nf_conn_help) 66#if IS_ENABLED(CONFIG_NF_NAT) 67 + sizeof(struct nf_conn_nat) 68#endif 69 + sizeof(struct nf_conn_seqadj) 70 + sizeof(struct nf_conn_acct) 71#ifdef CONFIG_NF_CONNTRACK_EVENTS 72 + sizeof(struct nf_conntrack_ecache) 73#endif 74#ifdef CONFIG_NF_CONNTRACK_TIMESTAMP 75 + sizeof(struct nf_conn_tstamp) 76#endif 77#ifdef CONFIG_NF_CONNTRACK_TIMEOUT 78 + sizeof(struct nf_conn_timeout) 79#endif 80#ifdef CONFIG_NF_CONNTRACK_LABELS 81 + sizeof(struct nf_conn_labels) 82#endif 83#if IS_ENABLED(CONFIG_NETFILTER_SYNPROXY) 84 + sizeof(struct nf_conn_synproxy) 85#endif 86#if IS_ENABLED(CONFIG_NET_ACT_CT) 87 + sizeof(struct nf_conn_act_ct_ext) 88#endif 89 ; 90} 91 92void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) 93{ 94 unsigned int newlen, newoff, oldlen, alloc; 95 struct nf_ct_ext *new; 96 97 /* Conntrack must not be confirmed to avoid races on reallocation. */ 98 WARN_ON(nf_ct_is_confirmed(ct)); 99 100 /* struct nf_ct_ext uses u8 to store offsets/size */ 101 BUILD_BUG_ON(total_extension_size() > 255u); 102 103 if (ct->ext) { 104 const struct nf_ct_ext *old = ct->ext; 105 106 if (__nf_ct_ext_exist(old, id)) 107 return NULL; 108 oldlen = old->len; 109 } else { 110 oldlen = sizeof(*new); 111 } 112 113 newoff = ALIGN(oldlen, __alignof__(struct nf_ct_ext)); 114 newlen = newoff + nf_ct_ext_type_len[id]; 115 116 alloc = max(newlen, NF_CT_EXT_PREALLOC); 117 new = krealloc(ct->ext, alloc, gfp); 118 if (!new) 119 return NULL; 120 121 if (!ct->ext) { 122 memset(new->offset, 0, sizeof(new->offset)); 123 new->gen_id = atomic_read(&nf_conntrack_ext_genid); 124 } 125 126 new->offset[id] = newoff; 127 new->len = newlen; 128 memset((void *)new + newoff, 0, newlen - newoff); 129 130 ct->ext = new; 131 return (void *)new + newoff; 132} 133EXPORT_SYMBOL(nf_ct_ext_add); 134 135/* Use nf_ct_ext_find wrapper. This is only useful for unconfirmed entries. */ 136void *__nf_ct_ext_find(const struct nf_ct_ext *ext, u8 id) 137{ 138 unsigned int gen_id = atomic_read(&nf_conntrack_ext_genid); 139 unsigned int this_id = READ_ONCE(ext->gen_id); 140 141 if (!__nf_ct_ext_exist(ext, id)) 142 return NULL; 143 144 if (this_id == 0 || ext->gen_id == gen_id) 145 return (void *)ext + ext->offset[id]; 146 147 return NULL; 148} 149EXPORT_SYMBOL(__nf_ct_ext_find); 150 151void nf_ct_ext_bump_genid(void) 152{ 153 unsigned int value = atomic_inc_return(&nf_conntrack_ext_genid); 154 155 if (value == UINT_MAX) 156 atomic_set(&nf_conntrack_ext_genid, 1); 157 158 msleep(HZ); 159}