diff options
author | David S. Miller <davem@davemloft.net> | 2009-05-27 19:56:47 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-05-27 19:56:47 -0400 |
commit | 5615968a70845157adaffc11062c997d045339ee (patch) | |
tree | 36958722da6bbcd2eaec9be90885190e9d96f4f7 | |
parent | 385a154cac8763284f65cdfa25f6796c9eb1ca21 (diff) |
appletalk: Add proper locking around IPDDP routing table.
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/appletalk/ipddp.c | 40 |
1 files changed, 32 insertions, 8 deletions
diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c index f939e92fcf8a..9832b757f109 100644 --- a/drivers/net/appletalk/ipddp.c +++ b/drivers/net/appletalk/ipddp.c | |||
@@ -39,6 +39,7 @@ | |||
39 | static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n"; | 39 | static const char version[] = KERN_INFO "ipddp.c:v0.01 8/28/97 Bradford W. Johnson <johns393@maroon.tc.umn.edu>\n"; |
40 | 40 | ||
41 | static struct ipddp_route *ipddp_route_list; | 41 | static struct ipddp_route *ipddp_route_list; |
42 | static DEFINE_SPINLOCK(ipddp_route_lock); | ||
42 | 43 | ||
43 | #ifdef CONFIG_IPDDP_ENCAP | 44 | #ifdef CONFIG_IPDDP_ENCAP |
44 | static int ipddp_mode = IPDDP_ENCAP; | 45 | static int ipddp_mode = IPDDP_ENCAP; |
@@ -50,7 +51,7 @@ static int ipddp_mode = IPDDP_DECAP; | |||
50 | static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev); | 51 | static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev); |
51 | static int ipddp_create(struct ipddp_route *new_rt); | 52 | static int ipddp_create(struct ipddp_route *new_rt); |
52 | static int ipddp_delete(struct ipddp_route *rt); | 53 | static int ipddp_delete(struct ipddp_route *rt); |
53 | static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt); | 54 | static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt); |
54 | static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); | 55 | static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd); |
55 | 56 | ||
56 | static const struct net_device_ops ipddp_netdev_ops = { | 57 | static const struct net_device_ops ipddp_netdev_ops = { |
@@ -119,6 +120,8 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev) | |||
119 | struct ipddp_route *rt; | 120 | struct ipddp_route *rt; |
120 | struct atalk_addr *our_addr; | 121 | struct atalk_addr *our_addr; |
121 | 122 | ||
123 | spin_lock(&ipddp_route_lock); | ||
124 | |||
122 | /* | 125 | /* |
123 | * Find appropriate route to use, based only on IP number. | 126 | * Find appropriate route to use, based only on IP number. |
124 | */ | 127 | */ |
@@ -127,8 +130,10 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev) | |||
127 | if(rt->ip == paddr) | 130 | if(rt->ip == paddr) |
128 | break; | 131 | break; |
129 | } | 132 | } |
130 | if(rt == NULL) | 133 | if(rt == NULL) { |
134 | spin_unlock(&ipddp_route_lock); | ||
131 | return 0; | 135 | return 0; |
136 | } | ||
132 | 137 | ||
133 | our_addr = atalk_find_dev_addr(rt->dev); | 138 | our_addr = atalk_find_dev_addr(rt->dev); |
134 | 139 | ||
@@ -174,6 +179,8 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev) | |||
174 | if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0) | 179 | if(aarp_send_ddp(rt->dev, skb, &rt->at, NULL) < 0) |
175 | dev_kfree_skb(skb); | 180 | dev_kfree_skb(skb); |
176 | 181 | ||
182 | spin_unlock(&ipddp_route_lock); | ||
183 | |||
177 | return 0; | 184 | return 0; |
178 | } | 185 | } |
179 | 186 | ||
@@ -196,7 +203,9 @@ static int ipddp_create(struct ipddp_route *new_rt) | |||
196 | return -ENETUNREACH; | 203 | return -ENETUNREACH; |
197 | } | 204 | } |
198 | 205 | ||
199 | if (ipddp_find_route(rt)) { | 206 | spin_lock_bh(&ipddp_route_lock); |
207 | if (__ipddp_find_route(rt)) { | ||
208 | spin_unlock_bh(&ipddp_route_lock); | ||
200 | kfree(rt); | 209 | kfree(rt); |
201 | return -EEXIST; | 210 | return -EEXIST; |
202 | } | 211 | } |
@@ -204,6 +213,8 @@ static int ipddp_create(struct ipddp_route *new_rt) | |||
204 | rt->next = ipddp_route_list; | 213 | rt->next = ipddp_route_list; |
205 | ipddp_route_list = rt; | 214 | ipddp_route_list = rt; |
206 | 215 | ||
216 | spin_unlock_bh(&ipddp_route_lock); | ||
217 | |||
207 | return 0; | 218 | return 0; |
208 | } | 219 | } |
209 | 220 | ||
@@ -216,6 +227,7 @@ static int ipddp_delete(struct ipddp_route *rt) | |||
216 | struct ipddp_route **r = &ipddp_route_list; | 227 | struct ipddp_route **r = &ipddp_route_list; |
217 | struct ipddp_route *tmp; | 228 | struct ipddp_route *tmp; |
218 | 229 | ||
230 | spin_lock_bh(&ipddp_route_lock); | ||
219 | while((tmp = *r) != NULL) | 231 | while((tmp = *r) != NULL) |
220 | { | 232 | { |
221 | if(tmp->ip == rt->ip | 233 | if(tmp->ip == rt->ip |
@@ -223,19 +235,21 @@ static int ipddp_delete(struct ipddp_route *rt) | |||
223 | && tmp->at.s_node == rt->at.s_node) | 235 | && tmp->at.s_node == rt->at.s_node) |
224 | { | 236 | { |
225 | *r = tmp->next; | 237 | *r = tmp->next; |
238 | spin_unlock_bh(&ipddp_route_lock); | ||
226 | kfree(tmp); | 239 | kfree(tmp); |
227 | return 0; | 240 | return 0; |
228 | } | 241 | } |
229 | r = &tmp->next; | 242 | r = &tmp->next; |
230 | } | 243 | } |
231 | 244 | ||
245 | spin_unlock_bh(&ipddp_route_lock); | ||
232 | return (-ENOENT); | 246 | return (-ENOENT); |
233 | } | 247 | } |
234 | 248 | ||
235 | /* | 249 | /* |
236 | * Find a routing entry, we only return a FULL match | 250 | * Find a routing entry, we only return a FULL match |
237 | */ | 251 | */ |
238 | static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt) | 252 | static struct ipddp_route* __ipddp_find_route(struct ipddp_route *rt) |
239 | { | 253 | { |
240 | struct ipddp_route *f; | 254 | struct ipddp_route *f; |
241 | 255 | ||
@@ -253,7 +267,7 @@ static struct ipddp_route* ipddp_find_route(struct ipddp_route *rt) | |||
253 | static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 267 | static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
254 | { | 268 | { |
255 | struct ipddp_route __user *rt = ifr->ifr_data; | 269 | struct ipddp_route __user *rt = ifr->ifr_data; |
256 | struct ipddp_route rcp; | 270 | struct ipddp_route rcp, rcp2, *rp; |
257 | 271 | ||
258 | if(!capable(CAP_NET_ADMIN)) | 272 | if(!capable(CAP_NET_ADMIN)) |
259 | return -EPERM; | 273 | return -EPERM; |
@@ -267,9 +281,19 @@ static int ipddp_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
267 | return (ipddp_create(&rcp)); | 281 | return (ipddp_create(&rcp)); |
268 | 282 | ||
269 | case SIOCFINDIPDDPRT: | 283 | case SIOCFINDIPDDPRT: |
270 | if(copy_to_user(rt, ipddp_find_route(&rcp), sizeof(struct ipddp_route))) | 284 | spin_lock_bh(&ipddp_route_lock); |
271 | return -EFAULT; | 285 | rp = __ipddp_find_route(&rcp); |
272 | return 0; | 286 | if (rp) |
287 | memcpy(&rcp2, rp, sizeof(rcp2)); | ||
288 | spin_unlock_bh(&ipddp_route_lock); | ||
289 | |||
290 | if (rp) { | ||
291 | if (copy_to_user(rt, &rcp2, | ||
292 | sizeof(struct ipddp_route))) | ||
293 | return -EFAULT; | ||
294 | return 0; | ||
295 | } else | ||
296 | return -ENOENT; | ||
273 | 297 | ||
274 | case SIOCDELIPDDPRT: | 298 | case SIOCDELIPDDPRT: |
275 | return (ipddp_delete(&rcp)); | 299 | return (ipddp_delete(&rcp)); |