hc
2024-02-20 e636c8d336489bf3eed5878299e6cc045bbad077
kernel/net/ipv6/anycast.c
....@@ -1,3 +1,4 @@
1
+// SPDX-License-Identifier: GPL-2.0-or-later
12 /*
23 * Anycast support for IPv6
34 * Linux INET6 implementation
....@@ -6,11 +7,6 @@
67 * David L Stevens (dlstevens@us.ibm.com)
78 *
89 * based heavily on net/ipv6/mcast.c
9
- *
10
- * This program is free software; you can redistribute it and/or
11
- * modify it under the terms of the GNU General Public License
12
- * as published by the Free Software Foundation; either version
13
- * 2 of the License, or (at your option) any later version.
1410 */
1511
1612 #include <linux/capability.h>
....@@ -44,7 +40,21 @@
4440
4541 #include <net/checksum.h>
4642
43
+#define IN6_ADDR_HSIZE_SHIFT 8
44
+#define IN6_ADDR_HSIZE BIT(IN6_ADDR_HSIZE_SHIFT)
45
+/* anycast address hash table
46
+ */
47
+static struct hlist_head inet6_acaddr_lst[IN6_ADDR_HSIZE];
48
+static DEFINE_SPINLOCK(acaddr_hash_lock);
49
+
4750 static int ipv6_dev_ac_dec(struct net_device *dev, const struct in6_addr *addr);
51
+
52
+static u32 inet6_acaddr_hash(struct net *net, const struct in6_addr *addr)
53
+{
54
+ u32 val = ipv6_addr_hash(addr) ^ net_hash_mix(net);
55
+
56
+ return hash_32(val, IN6_ADDR_HSIZE_SHIFT);
57
+}
4858
4959 /*
5060 * socket join an anycast group
....@@ -211,16 +221,39 @@
211221 rtnl_unlock();
212222 }
213223
224
+static void ipv6_add_acaddr_hash(struct net *net, struct ifacaddr6 *aca)
225
+{
226
+ unsigned int hash = inet6_acaddr_hash(net, &aca->aca_addr);
227
+
228
+ spin_lock(&acaddr_hash_lock);
229
+ hlist_add_head_rcu(&aca->aca_addr_lst, &inet6_acaddr_lst[hash]);
230
+ spin_unlock(&acaddr_hash_lock);
231
+}
232
+
233
+static void ipv6_del_acaddr_hash(struct ifacaddr6 *aca)
234
+{
235
+ spin_lock(&acaddr_hash_lock);
236
+ hlist_del_init_rcu(&aca->aca_addr_lst);
237
+ spin_unlock(&acaddr_hash_lock);
238
+}
239
+
214240 static void aca_get(struct ifacaddr6 *aca)
215241 {
216242 refcount_inc(&aca->aca_refcnt);
217243 }
218244
245
+static void aca_free_rcu(struct rcu_head *h)
246
+{
247
+ struct ifacaddr6 *aca = container_of(h, struct ifacaddr6, rcu);
248
+
249
+ fib6_info_release(aca->aca_rt);
250
+ kfree(aca);
251
+}
252
+
219253 static void aca_put(struct ifacaddr6 *ac)
220254 {
221255 if (refcount_dec_and_test(&ac->aca_refcnt)) {
222
- fib6_info_release(ac->aca_rt);
223
- kfree(ac);
256
+ call_rcu(&ac->rcu, aca_free_rcu);
224257 }
225258 }
226259
....@@ -236,6 +269,7 @@
236269 aca->aca_addr = *addr;
237270 fib6_info_hold(f6i);
238271 aca->aca_rt = f6i;
272
+ INIT_HLIST_NODE(&aca->aca_addr_lst);
239273 aca->aca_users = 1;
240274 /* aca_tstamp should be updated upon changes */
241275 aca->aca_cstamp = aca->aca_tstamp = jiffies;
....@@ -292,6 +326,8 @@
292326 aca_get(aca);
293327 write_unlock_bh(&idev->lock);
294328
329
+ ipv6_add_acaddr_hash(net, aca);
330
+
295331 ip6_ins_rt(net, f6i);
296332
297333 addrconf_join_solict(idev->dev, &aca->aca_addr);
....@@ -332,9 +368,10 @@
332368 else
333369 idev->ac_list = aca->aca_next;
334370 write_unlock_bh(&idev->lock);
371
+ ipv6_del_acaddr_hash(aca);
335372 addrconf_leave_solict(idev, &aca->aca_addr);
336373
337
- ip6_del_rt(dev_net(idev->dev), aca->aca_rt);
374
+ ip6_del_rt(dev_net(idev->dev), aca->aca_rt, false);
338375
339376 aca_put(aca);
340377 return 0;
....@@ -359,9 +396,11 @@
359396 idev->ac_list = aca->aca_next;
360397 write_unlock_bh(&idev->lock);
361398
399
+ ipv6_del_acaddr_hash(aca);
400
+
362401 addrconf_leave_solict(idev, &aca->aca_addr);
363402
364
- ip6_del_rt(dev_net(idev->dev), aca->aca_rt);
403
+ ip6_del_rt(dev_net(idev->dev), aca->aca_rt, false);
365404
366405 aca_put(aca);
367406
....@@ -397,17 +436,27 @@
397436 bool ipv6_chk_acast_addr(struct net *net, struct net_device *dev,
398437 const struct in6_addr *addr)
399438 {
439
+ struct net_device *nh_dev;
440
+ struct ifacaddr6 *aca;
400441 bool found = false;
401442
402443 rcu_read_lock();
403444 if (dev)
404445 found = ipv6_chk_acast_dev(dev, addr);
405
- else
406
- for_each_netdev_rcu(net, dev)
407
- if (ipv6_chk_acast_dev(dev, addr)) {
446
+ else {
447
+ unsigned int hash = inet6_acaddr_hash(net, addr);
448
+
449
+ hlist_for_each_entry_rcu(aca, &inet6_acaddr_lst[hash],
450
+ aca_addr_lst) {
451
+ nh_dev = fib6_info_nh_dev(aca->aca_rt);
452
+ if (!nh_dev || !net_eq(dev_net(nh_dev), net))
453
+ continue;
454
+ if (ipv6_addr_equal(&aca->aca_addr, addr)) {
408455 found = true;
409456 break;
410457 }
458
+ }
459
+ }
411460 rcu_read_unlock();
412461 return found;
413462 }
....@@ -547,3 +596,24 @@
547596 remove_proc_entry("anycast6", net->proc_net);
548597 }
549598 #endif
599
+
600
+/* Init / cleanup code
601
+ */
602
+int __init ipv6_anycast_init(void)
603
+{
604
+ int i;
605
+
606
+ for (i = 0; i < IN6_ADDR_HSIZE; i++)
607
+ INIT_HLIST_HEAD(&inet6_acaddr_lst[i]);
608
+ return 0;
609
+}
610
+
611
+void ipv6_anycast_cleanup(void)
612
+{
613
+ int i;
614
+
615
+ spin_lock(&acaddr_hash_lock);
616
+ for (i = 0; i < IN6_ADDR_HSIZE; i++)
617
+ WARN_ON(!hlist_empty(&inet6_acaddr_lst[i]));
618
+ spin_unlock(&acaddr_hash_lock);
619
+}