diff options
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r-- | net/ipv4/devinet.c | 116 |
1 files changed, 111 insertions, 5 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index df4616fce929..cd9ca0811cfa 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -51,6 +51,7 @@ | |||
51 | #include <linux/inetdevice.h> | 51 | #include <linux/inetdevice.h> |
52 | #include <linux/igmp.h> | 52 | #include <linux/igmp.h> |
53 | #include <linux/slab.h> | 53 | #include <linux/slab.h> |
54 | #include <linux/hash.h> | ||
54 | #ifdef CONFIG_SYSCTL | 55 | #ifdef CONFIG_SYSCTL |
55 | #include <linux/sysctl.h> | 56 | #include <linux/sysctl.h> |
56 | #endif | 57 | #endif |
@@ -63,6 +64,8 @@ | |||
63 | #include <net/rtnetlink.h> | 64 | #include <net/rtnetlink.h> |
64 | #include <net/net_namespace.h> | 65 | #include <net/net_namespace.h> |
65 | 66 | ||
67 | #include "fib_lookup.h" | ||
68 | |||
66 | static struct ipv4_devconf ipv4_devconf = { | 69 | static struct ipv4_devconf ipv4_devconf = { |
67 | .data = { | 70 | .data = { |
68 | [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, | 71 | [IPV4_DEVCONF_ACCEPT_REDIRECTS - 1] = 1, |
@@ -92,6 +95,85 @@ static const struct nla_policy ifa_ipv4_policy[IFA_MAX+1] = { | |||
92 | [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, | 95 | [IFA_LABEL] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, |
93 | }; | 96 | }; |
94 | 97 | ||
98 | /* inet_addr_hash's shifting is dependent upon this IN4_ADDR_HSIZE | ||
99 | * value. So if you change this define, make appropriate changes to | ||
100 | * inet_addr_hash as well. | ||
101 | */ | ||
102 | #define IN4_ADDR_HSIZE 256 | ||
103 | static struct hlist_head inet_addr_lst[IN4_ADDR_HSIZE]; | ||
104 | static DEFINE_SPINLOCK(inet_addr_hash_lock); | ||
105 | |||
106 | static inline unsigned int inet_addr_hash(struct net *net, __be32 addr) | ||
107 | { | ||
108 | u32 val = (__force u32) addr ^ hash_ptr(net, 8); | ||
109 | |||
110 | return ((val ^ (val >> 8) ^ (val >> 16) ^ (val >> 24)) & | ||
111 | (IN4_ADDR_HSIZE - 1)); | ||
112 | } | ||
113 | |||
114 | static void inet_hash_insert(struct net *net, struct in_ifaddr *ifa) | ||
115 | { | ||
116 | unsigned int hash = inet_addr_hash(net, ifa->ifa_local); | ||
117 | |||
118 | spin_lock(&inet_addr_hash_lock); | ||
119 | hlist_add_head_rcu(&ifa->hash, &inet_addr_lst[hash]); | ||
120 | spin_unlock(&inet_addr_hash_lock); | ||
121 | } | ||
122 | |||
123 | static void inet_hash_remove(struct in_ifaddr *ifa) | ||
124 | { | ||
125 | spin_lock(&inet_addr_hash_lock); | ||
126 | hlist_del_init_rcu(&ifa->hash); | ||
127 | spin_unlock(&inet_addr_hash_lock); | ||
128 | } | ||
129 | |||
130 | /** | ||
131 | * __ip_dev_find - find the first device with a given source address. | ||
132 | * @net: the net namespace | ||
133 | * @addr: the source address | ||
134 | * @devref: if true, take a reference on the found device | ||
135 | * | ||
136 | * If a caller uses devref=false, it should be protected by RCU, or RTNL | ||
137 | */ | ||
138 | struct net_device *__ip_dev_find(struct net *net, __be32 addr, bool devref) | ||
139 | { | ||
140 | unsigned int hash = inet_addr_hash(net, addr); | ||
141 | struct net_device *result = NULL; | ||
142 | struct in_ifaddr *ifa; | ||
143 | struct hlist_node *node; | ||
144 | |||
145 | rcu_read_lock(); | ||
146 | hlist_for_each_entry_rcu(ifa, node, &inet_addr_lst[hash], hash) { | ||
147 | struct net_device *dev = ifa->ifa_dev->dev; | ||
148 | |||
149 | if (!net_eq(dev_net(dev), net)) | ||
150 | continue; | ||
151 | if (ifa->ifa_local == addr) { | ||
152 | result = dev; | ||
153 | break; | ||
154 | } | ||
155 | } | ||
156 | if (!result) { | ||
157 | struct flowi4 fl4 = { .daddr = addr }; | ||
158 | struct fib_result res = { 0 }; | ||
159 | struct fib_table *local; | ||
160 | |||
161 | /* Fallback to FIB local table so that communication | ||
162 | * over loopback subnets work. | ||
163 | */ | ||
164 | local = fib_get_table(net, RT_TABLE_LOCAL); | ||
165 | if (local && | ||
166 | !fib_table_lookup(local, &fl4, &res, FIB_LOOKUP_NOREF) && | ||
167 | res.type == RTN_LOCAL) | ||
168 | result = FIB_RES_DEV(res); | ||
169 | } | ||
170 | if (result && devref) | ||
171 | dev_hold(result); | ||
172 | rcu_read_unlock(); | ||
173 | return result; | ||
174 | } | ||
175 | EXPORT_SYMBOL(__ip_dev_find); | ||
176 | |||
95 | static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); | 177 | static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32); |
96 | 178 | ||
97 | static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); | 179 | static BLOCKING_NOTIFIER_HEAD(inetaddr_chain); |
@@ -265,6 +347,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
265 | } | 347 | } |
266 | 348 | ||
267 | if (!do_promote) { | 349 | if (!do_promote) { |
350 | inet_hash_remove(ifa); | ||
268 | *ifap1 = ifa->ifa_next; | 351 | *ifap1 = ifa->ifa_next; |
269 | 352 | ||
270 | rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid); | 353 | rtmsg_ifa(RTM_DELADDR, ifa, nlh, pid); |
@@ -278,9 +361,21 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
278 | } | 361 | } |
279 | } | 362 | } |
280 | 363 | ||
364 | /* On promotion all secondaries from subnet are changing | ||
365 | * the primary IP, we must remove all their routes silently | ||
366 | * and later to add them back with new prefsrc. Do this | ||
367 | * while all addresses are on the device list. | ||
368 | */ | ||
369 | for (ifa = promote; ifa; ifa = ifa->ifa_next) { | ||
370 | if (ifa1->ifa_mask == ifa->ifa_mask && | ||
371 | inet_ifa_match(ifa1->ifa_address, ifa)) | ||
372 | fib_del_ifaddr(ifa, ifa1); | ||
373 | } | ||
374 | |||
281 | /* 2. Unlink it */ | 375 | /* 2. Unlink it */ |
282 | 376 | ||
283 | *ifap = ifa1->ifa_next; | 377 | *ifap = ifa1->ifa_next; |
378 | inet_hash_remove(ifa1); | ||
284 | 379 | ||
285 | /* 3. Announce address deletion */ | 380 | /* 3. Announce address deletion */ |
286 | 381 | ||
@@ -296,6 +391,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
296 | blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); | 391 | blocking_notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa1); |
297 | 392 | ||
298 | if (promote) { | 393 | if (promote) { |
394 | struct in_ifaddr *next_sec = promote->ifa_next; | ||
299 | 395 | ||
300 | if (prev_prom) { | 396 | if (prev_prom) { |
301 | prev_prom->ifa_next = promote->ifa_next; | 397 | prev_prom->ifa_next = promote->ifa_next; |
@@ -307,7 +403,7 @@ static void __inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
307 | rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid); | 403 | rtmsg_ifa(RTM_NEWADDR, promote, nlh, pid); |
308 | blocking_notifier_call_chain(&inetaddr_chain, | 404 | blocking_notifier_call_chain(&inetaddr_chain, |
309 | NETDEV_UP, promote); | 405 | NETDEV_UP, promote); |
310 | for (ifa = promote->ifa_next; ifa; ifa = ifa->ifa_next) { | 406 | for (ifa = next_sec; ifa; ifa = ifa->ifa_next) { |
311 | if (ifa1->ifa_mask != ifa->ifa_mask || | 407 | if (ifa1->ifa_mask != ifa->ifa_mask || |
312 | !inet_ifa_match(ifa1->ifa_address, ifa)) | 408 | !inet_ifa_match(ifa1->ifa_address, ifa)) |
313 | continue; | 409 | continue; |
@@ -368,6 +464,8 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh, | |||
368 | ifa->ifa_next = *ifap; | 464 | ifa->ifa_next = *ifap; |
369 | *ifap = ifa; | 465 | *ifap = ifa; |
370 | 466 | ||
467 | inet_hash_insert(dev_net(in_dev->dev), ifa); | ||
468 | |||
371 | /* Send message first, then call notifier. | 469 | /* Send message first, then call notifier. |
372 | Notifier will trigger FIB update, so that | 470 | Notifier will trigger FIB update, so that |
373 | listeners of netlink will know about new ifaddr */ | 471 | listeners of netlink will know about new ifaddr */ |
@@ -521,6 +619,7 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) | |||
521 | if (tb[IFA_ADDRESS] == NULL) | 619 | if (tb[IFA_ADDRESS] == NULL) |
522 | tb[IFA_ADDRESS] = tb[IFA_LOCAL]; | 620 | tb[IFA_ADDRESS] = tb[IFA_LOCAL]; |
523 | 621 | ||
622 | INIT_HLIST_NODE(&ifa->hash); | ||
524 | ifa->ifa_prefixlen = ifm->ifa_prefixlen; | 623 | ifa->ifa_prefixlen = ifm->ifa_prefixlen; |
525 | ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); | 624 | ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); |
526 | ifa->ifa_flags = ifm->ifa_flags; | 625 | ifa->ifa_flags = ifm->ifa_flags; |
@@ -670,7 +769,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
670 | ifap = &ifa->ifa_next) { | 769 | ifap = &ifa->ifa_next) { |
671 | if (!strcmp(ifr.ifr_name, ifa->ifa_label) && | 770 | if (!strcmp(ifr.ifr_name, ifa->ifa_label) && |
672 | sin_orig.sin_addr.s_addr == | 771 | sin_orig.sin_addr.s_addr == |
673 | ifa->ifa_address) { | 772 | ifa->ifa_local) { |
674 | break; /* found */ | 773 | break; /* found */ |
675 | } | 774 | } |
676 | } | 775 | } |
@@ -728,6 +827,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
728 | if (!ifa) { | 827 | if (!ifa) { |
729 | ret = -ENOBUFS; | 828 | ret = -ENOBUFS; |
730 | ifa = inet_alloc_ifa(); | 829 | ifa = inet_alloc_ifa(); |
830 | INIT_HLIST_NODE(&ifa->hash); | ||
731 | if (!ifa) | 831 | if (!ifa) |
732 | break; | 832 | break; |
733 | if (colon) | 833 | if (colon) |
@@ -1040,8 +1140,8 @@ static void inetdev_send_gratuitous_arp(struct net_device *dev, | |||
1040 | return; | 1140 | return; |
1041 | 1141 | ||
1042 | arp_send(ARPOP_REQUEST, ETH_P_ARP, | 1142 | arp_send(ARPOP_REQUEST, ETH_P_ARP, |
1043 | ifa->ifa_address, dev, | 1143 | ifa->ifa_local, dev, |
1044 | ifa->ifa_address, NULL, | 1144 | ifa->ifa_local, NULL, |
1045 | dev->dev_addr, NULL); | 1145 | dev->dev_addr, NULL); |
1046 | } | 1146 | } |
1047 | 1147 | ||
@@ -1084,6 +1184,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, | |||
1084 | struct in_ifaddr *ifa = inet_alloc_ifa(); | 1184 | struct in_ifaddr *ifa = inet_alloc_ifa(); |
1085 | 1185 | ||
1086 | if (ifa) { | 1186 | if (ifa) { |
1187 | INIT_HLIST_NODE(&ifa->hash); | ||
1087 | ifa->ifa_local = | 1188 | ifa->ifa_local = |
1088 | ifa->ifa_address = htonl(INADDR_LOOPBACK); | 1189 | ifa->ifa_address = htonl(INADDR_LOOPBACK); |
1089 | ifa->ifa_prefixlen = 8; | 1190 | ifa->ifa_prefixlen = 8; |
@@ -1579,7 +1680,7 @@ static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) | |||
1579 | return; | 1680 | return; |
1580 | 1681 | ||
1581 | cnf->sysctl = NULL; | 1682 | cnf->sysctl = NULL; |
1582 | unregister_sysctl_table(t->sysctl_header); | 1683 | unregister_net_sysctl_table(t->sysctl_header); |
1583 | kfree(t->dev_name); | 1684 | kfree(t->dev_name); |
1584 | kfree(t); | 1685 | kfree(t); |
1585 | } | 1686 | } |
@@ -1720,6 +1821,11 @@ static struct rtnl_af_ops inet_af_ops = { | |||
1720 | 1821 | ||
1721 | void __init devinet_init(void) | 1822 | void __init devinet_init(void) |
1722 | { | 1823 | { |
1824 | int i; | ||
1825 | |||
1826 | for (i = 0; i < IN4_ADDR_HSIZE; i++) | ||
1827 | INIT_HLIST_HEAD(&inet_addr_lst[i]); | ||
1828 | |||
1723 | register_pernet_subsys(&devinet_ops); | 1829 | register_pernet_subsys(&devinet_ops); |
1724 | 1830 | ||
1725 | register_gifconf(PF_INET, inet_gifconf); | 1831 | register_gifconf(PF_INET, inet_gifconf); |