diff options
-rw-r--r-- | include/net/phonet/pn_dev.h | 5 | ||||
-rw-r--r-- | net/phonet/pn_dev.c | 100 |
2 files changed, 99 insertions, 6 deletions
diff --git a/include/net/phonet/pn_dev.h b/include/net/phonet/pn_dev.h index 44c923c9e21d..87b5d8112e18 100644 --- a/include/net/phonet/pn_dev.h +++ b/include/net/phonet/pn_dev.h | |||
@@ -47,6 +47,11 @@ u8 phonet_address_get(struct net_device *dev, u8 addr); | |||
47 | int phonet_address_lookup(struct net *net, u8 addr); | 47 | int phonet_address_lookup(struct net *net, u8 addr); |
48 | void phonet_address_notify(int event, struct net_device *dev, u8 addr); | 48 | void phonet_address_notify(int event, struct net_device *dev, u8 addr); |
49 | 49 | ||
50 | int phonet_route_add(struct net_device *dev, u8 daddr); | ||
51 | int phonet_route_del(struct net_device *dev, u8 daddr); | ||
52 | struct net_device *phonet_route_get(struct net *net, u8 daddr); | ||
53 | struct net_device *phonet_route_output(struct net *net, u8 daddr); | ||
54 | |||
50 | #define PN_NO_ADDR 0xff | 55 | #define PN_NO_ADDR 0xff |
51 | 56 | ||
52 | extern const struct file_operations pn_sock_seq_fops; | 57 | extern const struct file_operations pn_sock_seq_fops; |
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 | ||
36 | struct phonet_routes { | ||
37 | spinlock_t lock; | ||
38 | struct net_device *table[64]; | ||
39 | }; | ||
40 | |||
36 | struct phonet_net { | 41 | struct phonet_net { |
37 | struct phonet_device_list pndevs; | 42 | struct phonet_device_list pndevs; |
43 | struct phonet_routes routes; | ||
38 | }; | 44 | }; |
39 | 45 | ||
40 | int phonet_net_id; | 46 | int 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. */ |
157 | u8 phonet_address_get(struct net_device *dev, u8 addr) | 163 | u8 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 | ||
176 | int phonet_address_lookup(struct net *net, u8 addr) | 197 | int 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 */ |
247 | static int phonet_init_net(struct net *net) | 268 | static 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 | |||
326 | int 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 | |||
343 | int 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 | |||
360 | struct 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 | |||
375 | struct 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 | } | ||