aboutsummaryrefslogtreecommitdiffstats
path: root/net/phonet/pn_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/phonet/pn_dev.c')
-rw-r--r--net/phonet/pn_dev.c100
1 files changed, 94 insertions, 6 deletions
diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c
index 5f42f30dd168..71fffa587783 100644
--- a/net/phonet/pn_dev.c
+++ b/net/phonet/pn_dev.c
@@ -33,8 +33,14 @@
33#include <net/netns/generic.h> 33#include <net/netns/generic.h>
34#include <net/phonet/pn_dev.h> 34#include <net/phonet/pn_dev.h>
35 35
36struct phonet_routes {
37 spinlock_t lock;
38 struct net_device *table[64];
39};
40
36struct phonet_net { 41struct phonet_net {
37 struct phonet_device_list pndevs; 42 struct phonet_device_list pndevs;
43 struct phonet_routes routes;
38}; 44};
39 45
40int phonet_net_id; 46int phonet_net_id;
@@ -154,10 +160,11 @@ int phonet_address_del(struct net_device *dev, u8 addr)
154} 160}
155 161
156/* Gets a source address toward a destination, through a interface. */ 162/* Gets a source address toward a destination, through a interface. */
157u8 phonet_address_get(struct net_device *dev, u8 addr) 163u8 phonet_address_get(struct net_device *dev, u8 daddr)
158{ 164{
159 struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); 165 struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev));
160 struct phonet_device *pnd; 166 struct phonet_device *pnd;
167 u8 saddr;
161 168
162 spin_lock_bh(&pndevs->lock); 169 spin_lock_bh(&pndevs->lock);
163 pnd = __phonet_get(dev); 170 pnd = __phonet_get(dev);
@@ -165,12 +172,26 @@ u8 phonet_address_get(struct net_device *dev, u8 addr)
165 BUG_ON(bitmap_empty(pnd->addrs, 64)); 172 BUG_ON(bitmap_empty(pnd->addrs, 64));
166 173
167 /* Use same source address as destination, if possible */ 174 /* Use same source address as destination, if possible */
168 if (!test_bit(addr >> 2, pnd->addrs)) 175 if (test_bit(daddr >> 2, pnd->addrs))
169 addr = find_first_bit(pnd->addrs, 64) << 2; 176 saddr = daddr;
177 else
178 saddr = find_first_bit(pnd->addrs, 64) << 2;
170 } else 179 } else
171 addr = PN_NO_ADDR; 180 saddr = PN_NO_ADDR;
172 spin_unlock_bh(&pndevs->lock); 181 spin_unlock_bh(&pndevs->lock);
173 return addr; 182
183 if (saddr == PN_NO_ADDR) {
184 /* Fallback to another device */
185 struct net_device *def_dev;
186
187 def_dev = phonet_device_get(dev_net(dev));
188 if (def_dev) {
189 if (def_dev != dev)
190 saddr = phonet_address_get(def_dev, daddr);
191 dev_put(def_dev);
192 }
193 }
194 return saddr;
174} 195}
175 196
176int phonet_address_lookup(struct net *net, u8 addr) 197int phonet_address_lookup(struct net *net, u8 addr)
@@ -246,7 +267,7 @@ static struct notifier_block phonet_device_notifier = {
246/* Per-namespace Phonet devices handling */ 267/* Per-namespace Phonet devices handling */
247static int phonet_init_net(struct net *net) 268static int phonet_init_net(struct net *net)
248{ 269{
249 struct phonet_net *pnn = kmalloc(sizeof(*pnn), GFP_KERNEL); 270 struct phonet_net *pnn = kzalloc(sizeof(*pnn), GFP_KERNEL);
250 if (!pnn) 271 if (!pnn)
251 return -ENOMEM; 272 return -ENOMEM;
252 273
@@ -257,6 +278,7 @@ static int phonet_init_net(struct net *net)
257 278
258 INIT_LIST_HEAD(&pnn->pndevs.list); 279 INIT_LIST_HEAD(&pnn->pndevs.list);
259 spin_lock_init(&pnn->pndevs.lock); 280 spin_lock_init(&pnn->pndevs.lock);
281 spin_lock_init(&pnn->routes.lock);
260 net_assign_generic(net, phonet_net_id, pnn); 282 net_assign_generic(net, phonet_net_id, pnn);
261 return 0; 283 return 0;
262} 284}
@@ -300,3 +322,69 @@ void phonet_device_exit(void)
300 unregister_netdevice_notifier(&phonet_device_notifier); 322 unregister_netdevice_notifier(&phonet_device_notifier);
301 unregister_pernet_gen_device(phonet_net_id, &phonet_net_ops); 323 unregister_pernet_gen_device(phonet_net_id, &phonet_net_ops);
302} 324}
325
326int phonet_route_add(struct net_device *dev, u8 daddr)
327{
328 struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
329 struct phonet_routes *routes = &pnn->routes;
330 int err = -EEXIST;
331
332 daddr = daddr >> 2;
333 spin_lock_bh(&routes->lock);
334 if (routes->table[daddr] == NULL) {
335 routes->table[daddr] = dev;
336 dev_hold(dev);
337 err = 0;
338 }
339 spin_unlock_bh(&routes->lock);
340 return err;
341}
342
343int phonet_route_del(struct net_device *dev, u8 daddr)
344{
345 struct phonet_net *pnn = net_generic(dev_net(dev), phonet_net_id);
346 struct phonet_routes *routes = &pnn->routes;
347 int err = -ENOENT;
348
349 daddr = daddr >> 2;
350 spin_lock_bh(&routes->lock);
351 if (dev == routes->table[daddr]) {
352 routes->table[daddr] = NULL;
353 dev_put(dev);
354 err = 0;
355 }
356 spin_unlock_bh(&routes->lock);
357 return err;
358}
359
360struct net_device *phonet_route_get(struct net *net, u8 daddr)
361{
362 struct phonet_net *pnn = net_generic(net, phonet_net_id);
363 struct phonet_routes *routes = &pnn->routes;
364 struct net_device *dev;
365
366 ASSERT_RTNL(); /* no need to hold the device */
367
368 daddr >>= 2;
369 spin_lock_bh(&routes->lock);
370 dev = routes->table[daddr];
371 spin_unlock_bh(&routes->lock);
372 return dev;
373}
374
375struct net_device *phonet_route_output(struct net *net, u8 daddr)
376{
377 struct phonet_net *pnn = net_generic(net, phonet_net_id);
378 struct phonet_routes *routes = &pnn->routes;
379 struct net_device *dev;
380
381 spin_lock_bh(&routes->lock);
382 dev = routes->table[daddr >> 2];
383 if (dev)
384 dev_hold(dev);
385 spin_unlock_bh(&routes->lock);
386
387 if (!dev)
388 dev = phonet_device_get(net); /* Default route */
389 return dev;
390}