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 30 atomic_t nf_conntrack_ext_genid __read_mostly = ATOMIC_INIT(1); 31 32 static 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 59 static __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 92 void *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 } 133 EXPORT_SYMBOL(nf_ct_ext_add); 134 135 /* Use nf_ct_ext_find wrapper. This is only useful for unconfirmed entries. */ 136 void *__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 } 149 EXPORT_SYMBOL(__nf_ct_ext_find); 150 151 void 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 } 160