Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: Handle different key sizes between address families in flow cache

With the conversion of struct flowi to a union of AF-specific structs, some
operations on the flow cache need to account for the exact size of the key.

Signed-off-by: David Ward <david.ward@ll.mit.edu>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

dpward and committed by
David S. Miller
aa1c366e 728871bc

+36 -14
+19
include/net/flow.h
··· 7 7 #ifndef _NET_FLOW_H 8 8 #define _NET_FLOW_H 9 9 10 + #include <linux/socket.h> 10 11 #include <linux/in6.h> 11 12 #include <linux/atomic.h> 12 13 ··· 160 159 static inline struct flowi *flowidn_to_flowi(struct flowidn *fldn) 161 160 { 162 161 return container_of(fldn, struct flowi, u.dn); 162 + } 163 + 164 + typedef unsigned long flow_compare_t; 165 + 166 + static inline size_t flow_key_size(u16 family) 167 + { 168 + switch (family) { 169 + case AF_INET: 170 + BUILD_BUG_ON(sizeof(struct flowi4) % sizeof(flow_compare_t)); 171 + return sizeof(struct flowi4) / sizeof(flow_compare_t); 172 + case AF_INET6: 173 + BUILD_BUG_ON(sizeof(struct flowi6) % sizeof(flow_compare_t)); 174 + return sizeof(struct flowi6) / sizeof(flow_compare_t); 175 + case AF_DECnet: 176 + BUILD_BUG_ON(sizeof(struct flowidn) % sizeof(flow_compare_t)); 177 + return sizeof(struct flowidn) / sizeof(flow_compare_t); 178 + } 179 + return 0; 163 180 } 164 181 165 182 #define FLOW_DIR_IN 0
+17 -14
net/core/flow.c
··· 173 173 174 174 static u32 flow_hash_code(struct flow_cache *fc, 175 175 struct flow_cache_percpu *fcp, 176 - const struct flowi *key) 176 + const struct flowi *key, 177 + size_t keysize) 177 178 { 178 179 const u32 *k = (const u32 *) key; 180 + const u32 length = keysize * sizeof(flow_compare_t) / sizeof(u32); 179 181 180 - return jhash2(k, (sizeof(*key) / sizeof(u32)), fcp->hash_rnd) 182 + return jhash2(k, length, fcp->hash_rnd) 181 183 & (flow_cache_hash_size(fc) - 1); 182 184 } 183 185 184 - typedef unsigned long flow_compare_t; 185 - 186 186 /* I hear what you're saying, use memcmp. But memcmp cannot make 187 - * important assumptions that we can here, such as alignment and 188 - * constant size. 187 + * important assumptions that we can here, such as alignment. 189 188 */ 190 - static int flow_key_compare(const struct flowi *key1, const struct flowi *key2) 189 + static int flow_key_compare(const struct flowi *key1, const struct flowi *key2, 190 + size_t keysize) 191 191 { 192 192 const flow_compare_t *k1, *k1_lim, *k2; 193 - const int n_elem = sizeof(struct flowi) / sizeof(flow_compare_t); 194 - 195 - BUILD_BUG_ON(sizeof(struct flowi) % sizeof(flow_compare_t)); 196 193 197 194 k1 = (const flow_compare_t *) key1; 198 - k1_lim = k1 + n_elem; 195 + k1_lim = k1 + keysize; 199 196 200 197 k2 = (const flow_compare_t *) key2; 201 198 ··· 213 216 struct flow_cache_entry *fle, *tfle; 214 217 struct hlist_node *entry; 215 218 struct flow_cache_object *flo; 219 + size_t keysize; 216 220 unsigned int hash; 217 221 218 222 local_bh_disable(); ··· 221 223 222 224 fle = NULL; 223 225 flo = NULL; 226 + 227 + keysize = flow_key_size(family); 228 + if (!keysize) 229 + goto nocache; 230 + 224 231 /* Packet really early in init? Making flow_cache_init a 225 232 * pre-smp initcall would solve this. --RR */ 226 233 if (!fcp->hash_table) ··· 234 231 if (fcp->hash_rnd_recalc) 235 232 flow_new_hash_rnd(fc, fcp); 236 233 237 - hash = flow_hash_code(fc, fcp, key); 234 + hash = flow_hash_code(fc, fcp, key, keysize); 238 235 hlist_for_each_entry(tfle, entry, &fcp->hash_table[hash], u.hlist) { 239 236 if (tfle->net == net && 240 237 tfle->family == family && 241 238 tfle->dir == dir && 242 - flow_key_compare(key, &tfle->key) == 0) { 239 + flow_key_compare(key, &tfle->key, keysize) == 0) { 243 240 fle = tfle; 244 241 break; 245 242 } ··· 254 251 fle->net = net; 255 252 fle->family = family; 256 253 fle->dir = dir; 257 - memcpy(&fle->key, key, sizeof(*key)); 254 + memcpy(&fle->key, key, keysize * sizeof(flow_compare_t)); 258 255 fle->object = NULL; 259 256 hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]); 260 257 fcp->hash_count++;