1 /* Structure dynamic extension infrastructure 2 * Copyright (C) 2004 Rusty Russell IBM Corporation 3 * Copyright (C) 2007 Netfilter Core Team <coreteam@netfilter.org> 4 * Copyright (C) 2007 USAGI/WIDE Project <http://www.linux-ipv6.org> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 9 * 2 of the License, or (at your option) any later version. 10 */ 11 #include <linux/kernel.h> 12 #include <linux/module.h> 13 #include <linux/mutex.h> 14 #include <linux/rcupdate.h> 15 #include <linux/slab.h> 16 #include <linux/skbuff.h> 17 #include <net/netfilter/nf_conntrack_extend.h> 18 19 static struct nf_ct_ext_type __rcu *nf_ct_ext_types[NF_CT_EXT_NUM]; 20 static DEFINE_MUTEX(nf_ct_ext_type_mutex); 21 22 void __nf_ct_ext_destroy(struct nf_conn *ct) 23 { 24 unsigned int i; 25 struct nf_ct_ext_type *t; 26 struct nf_ct_ext *ext = ct->ext; 27 28 for (i = 0; i < NF_CT_EXT_NUM; i++) { 29 if (!__nf_ct_ext_exist(ext, i)) 30 continue; 31 32 rcu_read_lock(); 33 t = rcu_dereference(nf_ct_ext_types[i]); 34 35 /* Here the nf_ct_ext_type might have been unregisterd. 36 * I.e., it has responsible to cleanup private 37 * area in all conntracks when it is unregisterd. 38 */ 39 if (t && t->destroy) 40 t->destroy(ct); 41 rcu_read_unlock(); 42 } 43 } 44 EXPORT_SYMBOL(__nf_ct_ext_destroy); 45 46 static void * 47 nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, 48 size_t var_alloc_len, gfp_t gfp) 49 { 50 unsigned int off, len; 51 struct nf_ct_ext_type *t; 52 size_t alloc_size; 53 54 rcu_read_lock(); 55 t = rcu_dereference(nf_ct_ext_types[id]); 56 BUG_ON(t == NULL); 57 off = ALIGN(sizeof(struct nf_ct_ext), t->align); 58 len = off + t->len + var_alloc_len; 59 alloc_size = t->alloc_size + var_alloc_len; 60 rcu_read_unlock(); 61 62 *ext = kzalloc(alloc_size, gfp); 63 if (!*ext) 64 return NULL; 65 66 (*ext)->offset[id] = off; 67 (*ext)->len = len; 68 69 return (void *)(*ext) + off; 70 } 71 72 void *__nf_ct_ext_add_length(struct nf_conn *ct, enum nf_ct_ext_id id, 73 size_t var_alloc_len, gfp_t gfp) 74 { 75 struct nf_ct_ext *old, *new; 76 int newlen, newoff; 77 struct nf_ct_ext_type *t; 78 79 /* Conntrack must not be confirmed to avoid races on reallocation. */ 80 NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); 81 82 old = ct->ext; 83 if (!old) 84 return nf_ct_ext_create(&ct->ext, id, var_alloc_len, gfp); 85 86 if (__nf_ct_ext_exist(old, id)) 87 return NULL; 88 89 rcu_read_lock(); 90 t = rcu_dereference(nf_ct_ext_types[id]); 91 BUG_ON(t == NULL); 92 93 newoff = ALIGN(old->len, t->align); 94 newlen = newoff + t->len + var_alloc_len; 95 rcu_read_unlock(); 96 97 new = __krealloc(old, newlen, gfp); 98 if (!new) 99 return NULL; 100 101 if (new != old) { 102 kfree_rcu(old, rcu); 103 rcu_assign_pointer(ct->ext, new); 104 } 105 106 new->offset[id] = newoff; 107 new->len = newlen; 108 memset((void *)new + newoff, 0, newlen - newoff); 109 return (void *)new + newoff; 110 } 111 EXPORT_SYMBOL(__nf_ct_ext_add_length); 112 113 static void update_alloc_size(struct nf_ct_ext_type *type) 114 { 115 int i, j; 116 struct nf_ct_ext_type *t1, *t2; 117 enum nf_ct_ext_id min = 0, max = NF_CT_EXT_NUM - 1; 118 119 /* unnecessary to update all types */ 120 if ((type->flags & NF_CT_EXT_F_PREALLOC) == 0) { 121 min = type->id; 122 max = type->id; 123 } 124 125 /* This assumes that extended areas in conntrack for the types 126 whose NF_CT_EXT_F_PREALLOC bit set are allocated in order */ 127 for (i = min; i <= max; i++) { 128 t1 = rcu_dereference_protected(nf_ct_ext_types[i], 129 lockdep_is_held(&nf_ct_ext_type_mutex)); 130 if (!t1) 131 continue; 132 133 t1->alloc_size = ALIGN(sizeof(struct nf_ct_ext), t1->align) + 134 t1->len; 135 for (j = 0; j < NF_CT_EXT_NUM; j++) { 136 t2 = rcu_dereference_protected(nf_ct_ext_types[j], 137 lockdep_is_held(&nf_ct_ext_type_mutex)); 138 if (t2 == NULL || t2 == t1 || 139 (t2->flags & NF_CT_EXT_F_PREALLOC) == 0) 140 continue; 141 142 t1->alloc_size = ALIGN(t1->alloc_size, t2->align) 143 + t2->len; 144 } 145 } 146 } 147 148 /* This MUST be called in process context. */ 149 int nf_ct_extend_register(struct nf_ct_ext_type *type) 150 { 151 int ret = 0; 152 153 mutex_lock(&nf_ct_ext_type_mutex); 154 if (nf_ct_ext_types[type->id]) { 155 ret = -EBUSY; 156 goto out; 157 } 158 159 /* This ensures that nf_ct_ext_create() can allocate enough area 160 before updating alloc_size */ 161 type->alloc_size = ALIGN(sizeof(struct nf_ct_ext), type->align) 162 + type->len; 163 rcu_assign_pointer(nf_ct_ext_types[type->id], type); 164 update_alloc_size(type); 165 out: 166 mutex_unlock(&nf_ct_ext_type_mutex); 167 return ret; 168 } 169 EXPORT_SYMBOL_GPL(nf_ct_extend_register); 170 171 /* This MUST be called in process context. */ 172 void nf_ct_extend_unregister(struct nf_ct_ext_type *type) 173 { 174 mutex_lock(&nf_ct_ext_type_mutex); 175 RCU_INIT_POINTER(nf_ct_ext_types[type->id], NULL); 176 update_alloc_size(type); 177 mutex_unlock(&nf_ct_ext_type_mutex); 178 rcu_barrier(); /* Wait for completion of call_rcu()'s */ 179 } 180 EXPORT_SYMBOL_GPL(nf_ct_extend_unregister); 181