Skip to content

Commit 3ad7d24

Browse files
kmjohansendavem330
authored andcommitted
Ipvlan should return an error when an address is already in use.
The ipvlan code already knows how to detect when a duplicate address is about to be assigned to an ipvlan device. However, that failure is not propogated outward and leads to a silent failure. Introduce a validation step at ip address creation time and allow device drivers to register to validate the incoming ip addresses. The ipvlan code is the first consumer. If it detects an address in use, we can return an error to the user before beginning to commit the new ifa in the networking code. This can be especially useful if it is necessary to provision many ipvlans in containers. The provisioning software (or operator) can use this to detect situations where an ip address is unexpectedly in use. Signed-off-by: Krister Johansen <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 2b3a9af commit 3ad7d24

File tree

6 files changed

+153
-2
lines changed

6 files changed

+153
-2
lines changed

drivers/net/ipvlan/ipvlan_main.c

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,33 @@ static int ipvlan_addr6_event(struct notifier_block *unused,
824824
return NOTIFY_OK;
825825
}
826826

827+
static int ipvlan_addr6_validator_event(struct notifier_block *unused,
828+
unsigned long event, void *ptr)
829+
{
830+
struct in6_validator_info *i6vi = (struct in6_validator_info *)ptr;
831+
struct net_device *dev = (struct net_device *)i6vi->i6vi_dev->dev;
832+
struct ipvl_dev *ipvlan = netdev_priv(dev);
833+
834+
/* FIXME IPv6 autoconf calls us from bh without RTNL */
835+
if (in_softirq())
836+
return NOTIFY_DONE;
837+
838+
if (!netif_is_ipvlan(dev))
839+
return NOTIFY_DONE;
840+
841+
if (!ipvlan || !ipvlan->port)
842+
return NOTIFY_DONE;
843+
844+
switch (event) {
845+
case NETDEV_UP:
846+
if (ipvlan_addr_busy(ipvlan->port, &i6vi->i6vi_addr, true))
847+
return notifier_from_errno(-EADDRINUSE);
848+
break;
849+
}
850+
851+
return NOTIFY_OK;
852+
}
853+
827854
static int ipvlan_add_addr4(struct ipvl_dev *ipvlan, struct in_addr *ip4_addr)
828855
{
829856
if (ipvlan_addr_busy(ipvlan->port, ip4_addr, false)) {
@@ -871,10 +898,37 @@ static int ipvlan_addr4_event(struct notifier_block *unused,
871898
return NOTIFY_OK;
872899
}
873900

901+
static int ipvlan_addr4_validator_event(struct notifier_block *unused,
902+
unsigned long event, void *ptr)
903+
{
904+
struct in_validator_info *ivi = (struct in_validator_info *)ptr;
905+
struct net_device *dev = (struct net_device *)ivi->ivi_dev->dev;
906+
struct ipvl_dev *ipvlan = netdev_priv(dev);
907+
908+
if (!netif_is_ipvlan(dev))
909+
return NOTIFY_DONE;
910+
911+
if (!ipvlan || !ipvlan->port)
912+
return NOTIFY_DONE;
913+
914+
switch (event) {
915+
case NETDEV_UP:
916+
if (ipvlan_addr_busy(ipvlan->port, &ivi->ivi_addr, false))
917+
return notifier_from_errno(-EADDRINUSE);
918+
break;
919+
}
920+
921+
return NOTIFY_OK;
922+
}
923+
874924
static struct notifier_block ipvlan_addr4_notifier_block __read_mostly = {
875925
.notifier_call = ipvlan_addr4_event,
876926
};
877927

928+
static struct notifier_block ipvlan_addr4_vtor_notifier_block __read_mostly = {
929+
.notifier_call = ipvlan_addr4_validator_event,
930+
};
931+
878932
static struct notifier_block ipvlan_notifier_block __read_mostly = {
879933
.notifier_call = ipvlan_device_event,
880934
};
@@ -883,6 +937,10 @@ static struct notifier_block ipvlan_addr6_notifier_block __read_mostly = {
883937
.notifier_call = ipvlan_addr6_event,
884938
};
885939

940+
static struct notifier_block ipvlan_addr6_vtor_notifier_block __read_mostly = {
941+
.notifier_call = ipvlan_addr6_validator_event,
942+
};
943+
886944
static void ipvlan_ns_exit(struct net *net)
887945
{
888946
struct ipvlan_netns *vnet = net_generic(net, ipvlan_netid);
@@ -907,7 +965,10 @@ static int __init ipvlan_init_module(void)
907965
ipvlan_init_secret();
908966
register_netdevice_notifier(&ipvlan_notifier_block);
909967
register_inet6addr_notifier(&ipvlan_addr6_notifier_block);
968+
register_inet6addr_validator_notifier(
969+
&ipvlan_addr6_vtor_notifier_block);
910970
register_inetaddr_notifier(&ipvlan_addr4_notifier_block);
971+
register_inetaddr_validator_notifier(&ipvlan_addr4_vtor_notifier_block);
911972

912973
err = register_pernet_subsys(&ipvlan_net_ops);
913974
if (err < 0)
@@ -922,7 +983,11 @@ static int __init ipvlan_init_module(void)
922983
return 0;
923984
error:
924985
unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
986+
unregister_inetaddr_validator_notifier(
987+
&ipvlan_addr4_vtor_notifier_block);
925988
unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
989+
unregister_inet6addr_validator_notifier(
990+
&ipvlan_addr6_vtor_notifier_block);
926991
unregister_netdevice_notifier(&ipvlan_notifier_block);
927992
return err;
928993
}
@@ -933,7 +998,11 @@ static void __exit ipvlan_cleanup_module(void)
933998
unregister_pernet_subsys(&ipvlan_net_ops);
934999
unregister_netdevice_notifier(&ipvlan_notifier_block);
9351000
unregister_inetaddr_notifier(&ipvlan_addr4_notifier_block);
1001+
unregister_inetaddr_validator_notifier(
1002+
&ipvlan_addr4_vtor_notifier_block);
9361003
unregister_inet6addr_notifier(&ipvlan_addr6_notifier_block);
1004+
unregister_inet6addr_validator_notifier(
1005+
&ipvlan_addr6_vtor_notifier_block);
9371006
}
9381007

9391008
module_init(ipvlan_init_module);

include/linux/inetdevice.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,15 @@ struct in_ifaddr {
150150
unsigned long ifa_tstamp; /* updated timestamp */
151151
};
152152

153+
struct in_validator_info {
154+
__be32 ivi_addr;
155+
struct in_device *ivi_dev;
156+
};
157+
153158
int register_inetaddr_notifier(struct notifier_block *nb);
154159
int unregister_inetaddr_notifier(struct notifier_block *nb);
160+
int register_inetaddr_validator_notifier(struct notifier_block *nb);
161+
int unregister_inetaddr_validator_notifier(struct notifier_block *nb);
155162

156163
void inet_netconf_notify_devconf(struct net *net, int event, int type,
157164
int ifindex, struct ipv4_devconf *devconf);

include/net/addrconf.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,15 @@ struct prefix_info {
4848
struct in6_addr prefix;
4949
};
5050

51-
5251
#include <linux/netdevice.h>
5352
#include <net/if_inet6.h>
5453
#include <net/ipv6.h>
5554

55+
struct in6_validator_info {
56+
struct in6_addr i6vi_addr;
57+
struct inet6_dev *i6vi_dev;
58+
};
59+
5660
#define IN6_ADDR_HSIZE_SHIFT 4
5761
#define IN6_ADDR_HSIZE (1 << IN6_ADDR_HSIZE_SHIFT)
5862

@@ -278,6 +282,10 @@ int register_inet6addr_notifier(struct notifier_block *nb);
278282
int unregister_inet6addr_notifier(struct notifier_block *nb);
279283
int inet6addr_notifier_call_chain(unsigned long val, void *v);
280284

285+
int register_inet6addr_validator_notifier(struct notifier_block *nb);
286+
int unregister_inet6addr_validator_notifier(struct notifier_block *nb);
287+
int inet6addr_validator_notifier_call_chain(unsigned long val, void *v);
288+
281289
void inet6_netconf_notify_devconf(struct net *net, int event, int type,
282290
int ifindex, struct ipv6_devconf *devconf);
283291

net/ipv4/devinet.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ EXPORT_SYMBOL(__ip_dev_find);
176176
static void rtmsg_ifa(int event, struct in_ifaddr *, struct nlmsghdr *, u32);
177177

178178
static BLOCKING_NOTIFIER_HEAD(inetaddr_chain);
179+
static BLOCKING_NOTIFIER_HEAD(inetaddr_validator_chain);
179180
static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap,
180181
int destroy);
181182
#ifdef CONFIG_SYSCTL
@@ -441,6 +442,8 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
441442
{
442443
struct in_device *in_dev = ifa->ifa_dev;
443444
struct in_ifaddr *ifa1, **ifap, **last_primary;
445+
struct in_validator_info ivi;
446+
int ret;
444447

445448
ASSERT_RTNL();
446449

@@ -471,6 +474,23 @@ static int __inet_insert_ifa(struct in_ifaddr *ifa, struct nlmsghdr *nlh,
471474
}
472475
}
473476

477+
/* Allow any devices that wish to register ifaddr validtors to weigh
478+
* in now, before changes are committed. The rntl lock is serializing
479+
* access here, so the state should not change between a validator call
480+
* and a final notify on commit. This isn't invoked on promotion under
481+
* the assumption that validators are checking the address itself, and
482+
* not the flags.
483+
*/
484+
ivi.ivi_addr = ifa->ifa_address;
485+
ivi.ivi_dev = ifa->ifa_dev;
486+
ret = blocking_notifier_call_chain(&inetaddr_validator_chain,
487+
NETDEV_UP, &ivi);
488+
ret = notifier_to_errno(ret);
489+
if (ret) {
490+
inet_free_ifa(ifa);
491+
return ret;
492+
}
493+
474494
if (!(ifa->ifa_flags & IFA_F_SECONDARY)) {
475495
prandom_seed((__force u32) ifa->ifa_local);
476496
ifap = last_primary;
@@ -1356,6 +1376,19 @@ int unregister_inetaddr_notifier(struct notifier_block *nb)
13561376
}
13571377
EXPORT_SYMBOL(unregister_inetaddr_notifier);
13581378

1379+
int register_inetaddr_validator_notifier(struct notifier_block *nb)
1380+
{
1381+
return blocking_notifier_chain_register(&inetaddr_validator_chain, nb);
1382+
}
1383+
EXPORT_SYMBOL(register_inetaddr_validator_notifier);
1384+
1385+
int unregister_inetaddr_validator_notifier(struct notifier_block *nb)
1386+
{
1387+
return blocking_notifier_chain_unregister(&inetaddr_validator_chain,
1388+
nb);
1389+
}
1390+
EXPORT_SYMBOL(unregister_inetaddr_validator_notifier);
1391+
13591392
/* Rename ifa_labels for a device name change. Make some effort to preserve
13601393
* existing alias numbering and to create unique labels if possible.
13611394
*/

net/ipv6/addrconf.c

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -963,6 +963,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
963963
struct net *net = dev_net(idev->dev);
964964
struct inet6_ifaddr *ifa = NULL;
965965
struct rt6_info *rt;
966+
struct in6_validator_info i6vi;
966967
unsigned int hash;
967968
int err = 0;
968969
int addr_type = ipv6_addr_type(addr);
@@ -974,6 +975,9 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
974975
return ERR_PTR(-EADDRNOTAVAIL);
975976

976977
rcu_read_lock_bh();
978+
979+
in6_dev_hold(idev);
980+
977981
if (idev->dead) {
978982
err = -ENODEV; /*XXX*/
979983
goto out2;
@@ -984,6 +988,17 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
984988
goto out2;
985989
}
986990

991+
i6vi.i6vi_addr = *addr;
992+
i6vi.i6vi_dev = idev;
993+
rcu_read_unlock_bh();
994+
995+
err = inet6addr_validator_notifier_call_chain(NETDEV_UP, &i6vi);
996+
997+
rcu_read_lock_bh();
998+
err = notifier_to_errno(err);
999+
if (err)
1000+
goto out2;
1001+
9871002
spin_lock(&addrconf_hash_lock);
9881003

9891004
/* Ignore adding duplicate addresses on an interface */
@@ -1034,7 +1049,6 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
10341049
ifa->rt = rt;
10351050

10361051
ifa->idev = idev;
1037-
in6_dev_hold(idev);
10381052
/* For caller */
10391053
in6_ifa_hold(ifa);
10401054

@@ -1062,6 +1076,7 @@ ipv6_add_addr(struct inet6_dev *idev, const struct in6_addr *addr,
10621076
inet6addr_notifier_call_chain(NETDEV_UP, ifa);
10631077
else {
10641078
kfree(ifa);
1079+
in6_dev_put(idev);
10651080
ifa = ERR_PTR(err);
10661081
}
10671082

net/ipv6/addrconf_core.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ int __ipv6_addr_type(const struct in6_addr *addr)
8888
EXPORT_SYMBOL(__ipv6_addr_type);
8989

9090
static ATOMIC_NOTIFIER_HEAD(inet6addr_chain);
91+
static ATOMIC_NOTIFIER_HEAD(inet6addr_validator_chain);
9192

9293
int register_inet6addr_notifier(struct notifier_block *nb)
9394
{
@@ -107,6 +108,24 @@ int inet6addr_notifier_call_chain(unsigned long val, void *v)
107108
}
108109
EXPORT_SYMBOL(inet6addr_notifier_call_chain);
109110

111+
int register_inet6addr_validator_notifier(struct notifier_block *nb)
112+
{
113+
return atomic_notifier_chain_register(&inet6addr_validator_chain, nb);
114+
}
115+
EXPORT_SYMBOL(register_inet6addr_validator_notifier);
116+
117+
int unregister_inet6addr_validator_notifier(struct notifier_block *nb)
118+
{
119+
return atomic_notifier_chain_unregister(&inet6addr_validator_chain, nb);
120+
}
121+
EXPORT_SYMBOL(unregister_inet6addr_validator_notifier);
122+
123+
int inet6addr_validator_notifier_call_chain(unsigned long val, void *v)
124+
{
125+
return atomic_notifier_call_chain(&inet6addr_validator_chain, val, v);
126+
}
127+
EXPORT_SYMBOL(inet6addr_validator_notifier_call_chain);
128+
110129
static int eafnosupport_ipv6_dst_lookup(struct net *net, struct sock *u1,
111130
struct dst_entry **u2,
112131
struct flowi6 *u3)

0 commit comments

Comments
 (0)