aboutsummaryrefslogtreecommitdiffstats
path: root/security/selinux/netnode.c
diff options
context:
space:
mode:
authorPaul Moore <paul.moore@hp.com>2008-04-25 15:03:34 -0400
committerJames Morris <jmorris@namei.org>2008-04-27 19:36:23 -0400
commita639e7ca8e8282b75be2724a28bfc788aa3bb156 (patch)
tree1a3308a354874ce1bc6b3c9ec71427a5204da7b5 /security/selinux/netnode.c
parent7b41b1733ca1d3278c8eb891e17905d7d54f5bfa (diff)
SELinux: Made netnode cache adds faster
When adding new entries to the network node cache we would walk the entire hash bucket to make sure we didn't cross a threshold (done to bound the cache size). This isn't a very quick or elegant solution for something which is supposed to be quick-ish so add a counter to each hash bucket to track the size of the bucket and eliminate the need to walk the entire bucket list on each add. Signed-off-by: Paul Moore <paul.moore@hp.com> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/selinux/netnode.c')
-rw-r--r--security/selinux/netnode.c104
1 files changed, 49 insertions, 55 deletions
diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c
index 2edc4c5e0c61..b6ccd09379f1 100644
--- a/security/selinux/netnode.c
+++ b/security/selinux/netnode.c
@@ -40,11 +40,17 @@
40#include <net/ipv6.h> 40#include <net/ipv6.h>
41#include <asm/bug.h> 41#include <asm/bug.h>
42 42
43#include "netnode.h"
43#include "objsec.h" 44#include "objsec.h"
44 45
45#define SEL_NETNODE_HASH_SIZE 256 46#define SEL_NETNODE_HASH_SIZE 256
46#define SEL_NETNODE_HASH_BKT_LIMIT 16 47#define SEL_NETNODE_HASH_BKT_LIMIT 16
47 48
49struct sel_netnode_bkt {
50 unsigned int size;
51 struct list_head list;
52};
53
48struct sel_netnode { 54struct sel_netnode {
49 struct netnode_security_struct nsec; 55 struct netnode_security_struct nsec;
50 56
@@ -60,7 +66,7 @@ struct sel_netnode {
60 66
61static LIST_HEAD(sel_netnode_list); 67static LIST_HEAD(sel_netnode_list);
62static DEFINE_SPINLOCK(sel_netnode_lock); 68static DEFINE_SPINLOCK(sel_netnode_lock);
63static struct list_head sel_netnode_hash[SEL_NETNODE_HASH_SIZE]; 69static struct sel_netnode_bkt sel_netnode_hash[SEL_NETNODE_HASH_SIZE];
64 70
65/** 71/**
66 * sel_netnode_free - Frees a node entry 72 * sel_netnode_free - Frees a node entry
@@ -87,7 +93,7 @@ static void sel_netnode_free(struct rcu_head *p)
87 * the bucket number for the given IP address. 93 * the bucket number for the given IP address.
88 * 94 *
89 */ 95 */
90static u32 sel_netnode_hashfn_ipv4(__be32 addr) 96static unsigned int sel_netnode_hashfn_ipv4(__be32 addr)
91{ 97{
92 /* at some point we should determine if the mismatch in byte order 98 /* at some point we should determine if the mismatch in byte order
93 * affects the hash function dramatically */ 99 * affects the hash function dramatically */
@@ -103,7 +109,7 @@ static u32 sel_netnode_hashfn_ipv4(__be32 addr)
103 * the bucket number for the given IP address. 109 * the bucket number for the given IP address.
104 * 110 *
105 */ 111 */
106static u32 sel_netnode_hashfn_ipv6(const struct in6_addr *addr) 112static unsigned int sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
107{ 113{
108 /* just hash the least significant 32 bits to keep things fast (they 114 /* just hash the least significant 32 bits to keep things fast (they
109 * are the most likely to be different anyway), we can revisit this 115 * are the most likely to be different anyway), we can revisit this
@@ -123,7 +129,7 @@ static u32 sel_netnode_hashfn_ipv6(const struct in6_addr *addr)
123 */ 129 */
124static struct sel_netnode *sel_netnode_find(const void *addr, u16 family) 130static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
125{ 131{
126 u32 idx; 132 unsigned int idx;
127 struct sel_netnode *node; 133 struct sel_netnode *node;
128 134
129 switch (family) { 135 switch (family) {
@@ -137,7 +143,7 @@ static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
137 BUG(); 143 BUG();
138 } 144 }
139 145
140 list_for_each_entry_rcu(node, &sel_netnode_hash[idx], list) 146 list_for_each_entry_rcu(node, &sel_netnode_hash[idx].list, list)
141 if (node->nsec.family == family) 147 if (node->nsec.family == family)
142 switch (family) { 148 switch (family) {
143 case PF_INET: 149 case PF_INET:
@@ -159,15 +165,12 @@ static struct sel_netnode *sel_netnode_find(const void *addr, u16 family)
159 * @node: the new node record 165 * @node: the new node record
160 * 166 *
161 * Description: 167 * Description:
162 * Add a new node record to the network address hash table. Returns zero on 168 * Add a new node record to the network address hash table.
163 * success, negative values on failure.
164 * 169 *
165 */ 170 */
166static int sel_netnode_insert(struct sel_netnode *node) 171static void sel_netnode_insert(struct sel_netnode *node)
167{ 172{
168 u32 idx; 173 unsigned int idx;
169 u32 count = 0;
170 struct sel_netnode *iter;
171 174
172 switch (node->nsec.family) { 175 switch (node->nsec.family) {
173 case PF_INET: 176 case PF_INET:
@@ -179,32 +182,21 @@ static int sel_netnode_insert(struct sel_netnode *node)
179 default: 182 default:
180 BUG(); 183 BUG();
181 } 184 }
182 list_add_rcu(&node->list, &sel_netnode_hash[idx]); 185
186 INIT_RCU_HEAD(&node->rcu);
183 187
184 /* we need to impose a limit on the growth of the hash table so check 188 /* we need to impose a limit on the growth of the hash table so check
185 * this bucket to make sure it is within the specified bounds */ 189 * this bucket to make sure it is within the specified bounds */
186 list_for_each_entry(iter, &sel_netnode_hash[idx], list) 190 list_add_rcu(&node->list, &sel_netnode_hash[idx].list);
187 if (++count > SEL_NETNODE_HASH_BKT_LIMIT) { 191 if (sel_netnode_hash[idx].size == SEL_NETNODE_HASH_BKT_LIMIT) {
188 list_del_rcu(&iter->list); 192 struct sel_netnode *tail;
189 call_rcu(&iter->rcu, sel_netnode_free); 193 tail = list_entry(
190 break; 194 rcu_dereference(sel_netnode_hash[idx].list.prev),
191 } 195 struct sel_netnode, list);
192 196 list_del_rcu(&tail->list);
193 return 0; 197 call_rcu(&tail->rcu, sel_netnode_free);
194} 198 } else
195 199 sel_netnode_hash[idx].size++;
196/**
197 * sel_netnode_destroy - Remove a node record from the table
198 * @node: the existing node record
199 *
200 * Description:
201 * Remove an existing node record from the network address table.
202 *
203 */
204static void sel_netnode_destroy(struct sel_netnode *node)
205{
206 list_del_rcu(&node->list);
207 call_rcu(&node->rcu, sel_netnode_free);
208} 200}
209 201
210/** 202/**
@@ -222,7 +214,7 @@ static void sel_netnode_destroy(struct sel_netnode *node)
222 */ 214 */
223static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid) 215static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
224{ 216{
225 int ret; 217 int ret = -ENOMEM;
226 struct sel_netnode *node; 218 struct sel_netnode *node;
227 struct sel_netnode *new = NULL; 219 struct sel_netnode *new = NULL;
228 220
@@ -230,25 +222,21 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
230 node = sel_netnode_find(addr, family); 222 node = sel_netnode_find(addr, family);
231 if (node != NULL) { 223 if (node != NULL) {
232 *sid = node->nsec.sid; 224 *sid = node->nsec.sid;
233 ret = 0; 225 spin_unlock_bh(&sel_netnode_lock);
234 goto out; 226 return 0;
235 } 227 }
236 new = kzalloc(sizeof(*new), GFP_ATOMIC); 228 new = kzalloc(sizeof(*new), GFP_ATOMIC);
237 if (new == NULL) { 229 if (new == NULL)
238 ret = -ENOMEM;
239 goto out; 230 goto out;
240 }
241 switch (family) { 231 switch (family) {
242 case PF_INET: 232 case PF_INET:
243 ret = security_node_sid(PF_INET, 233 ret = security_node_sid(PF_INET,
244 addr, sizeof(struct in_addr), 234 addr, sizeof(struct in_addr), sid);
245 &new->nsec.sid);
246 new->nsec.addr.ipv4 = *(__be32 *)addr; 235 new->nsec.addr.ipv4 = *(__be32 *)addr;
247 break; 236 break;
248 case PF_INET6: 237 case PF_INET6:
249 ret = security_node_sid(PF_INET6, 238 ret = security_node_sid(PF_INET6,
250 addr, sizeof(struct in6_addr), 239 addr, sizeof(struct in6_addr), sid);
251 &new->nsec.sid);
252 ipv6_addr_copy(&new->nsec.addr.ipv6, addr); 240 ipv6_addr_copy(&new->nsec.addr.ipv6, addr);
253 break; 241 break;
254 default: 242 default:
@@ -256,11 +244,10 @@ static int sel_netnode_sid_slow(void *addr, u16 family, u32 *sid)
256 } 244 }
257 if (ret != 0) 245 if (ret != 0)
258 goto out; 246 goto out;
247
259 new->nsec.family = family; 248 new->nsec.family = family;
260 ret = sel_netnode_insert(new); 249 new->nsec.sid = *sid;
261 if (ret != 0) 250 sel_netnode_insert(new);
262 goto out;
263 *sid = new->nsec.sid;
264 251
265out: 252out:
266 spin_unlock_bh(&sel_netnode_lock); 253 spin_unlock_bh(&sel_netnode_lock);
@@ -312,13 +299,18 @@ int sel_netnode_sid(void *addr, u16 family, u32 *sid)
312 */ 299 */
313static void sel_netnode_flush(void) 300static void sel_netnode_flush(void)
314{ 301{
315 u32 idx; 302 unsigned int idx;
316 struct sel_netnode *node; 303 struct sel_netnode *node, *node_tmp;
317 304
318 spin_lock_bh(&sel_netnode_lock); 305 spin_lock_bh(&sel_netnode_lock);
319 for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) 306 for (idx = 0; idx < SEL_NETNODE_HASH_SIZE; idx++) {
320 list_for_each_entry(node, &sel_netnode_hash[idx], list) 307 list_for_each_entry_safe(node, node_tmp,
321 sel_netnode_destroy(node); 308 &sel_netnode_hash[idx].list, list) {
309 list_del_rcu(&node->list);
310 call_rcu(&node->rcu, sel_netnode_free);
311 }
312 sel_netnode_hash[idx].size = 0;
313 }
322 spin_unlock_bh(&sel_netnode_lock); 314 spin_unlock_bh(&sel_netnode_lock);
323} 315}
324 316
@@ -340,8 +332,10 @@ static __init int sel_netnode_init(void)
340 if (!selinux_enabled) 332 if (!selinux_enabled)
341 return 0; 333 return 0;
342 334
343 for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) 335 for (iter = 0; iter < SEL_NETNODE_HASH_SIZE; iter++) {
344 INIT_LIST_HEAD(&sel_netnode_hash[iter]); 336 INIT_LIST_HEAD(&sel_netnode_hash[iter].list);
337 sel_netnode_hash[iter].size = 0;
338 }
345 339
346 ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET, 340 ret = avc_add_callback(sel_netnode_avc_callback, AVC_CALLBACK_RESET,
347 SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0); 341 SECSID_NULL, SECSID_NULL, SECCLASS_NULL, 0);