diff options
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r-- | net/ipv4/devinet.c | 43 |
1 files changed, 33 insertions, 10 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 3cc96730c4ed..650dcb12d9a1 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -233,11 +233,14 @@ int inet_addr_onlink(struct in_device *in_dev, u32 a, u32 b) | |||
233 | static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | 233 | static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, |
234 | int destroy) | 234 | int destroy) |
235 | { | 235 | { |
236 | struct in_ifaddr *promote = NULL; | ||
236 | struct in_ifaddr *ifa1 = *ifap; | 237 | struct in_ifaddr *ifa1 = *ifap; |
237 | 238 | ||
238 | ASSERT_RTNL(); | 239 | ASSERT_RTNL(); |
239 | 240 | ||
240 | /* 1. Deleting primary ifaddr forces deletion all secondaries */ | 241 | /* 1. Deleting primary ifaddr forces deletion all secondaries |
242 | * unless alias promotion is set | ||
243 | **/ | ||
241 | 244 | ||
242 | if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { | 245 | if (!(ifa1->ifa_flags & IFA_F_SECONDARY)) { |
243 | struct in_ifaddr *ifa; | 246 | struct in_ifaddr *ifa; |
@@ -251,11 +254,16 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
251 | continue; | 254 | continue; |
252 | } | 255 | } |
253 | 256 | ||
254 | *ifap1 = ifa->ifa_next; | 257 | if (!IN_DEV_PROMOTE_SECONDARIES(in_dev)) { |
258 | *ifap1 = ifa->ifa_next; | ||
255 | 259 | ||
256 | rtmsg_ifa(RTM_DELADDR, ifa); | 260 | rtmsg_ifa(RTM_DELADDR, ifa); |
257 | notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); | 261 | notifier_call_chain(&inetaddr_chain, NETDEV_DOWN, ifa); |
258 | inet_free_ifa(ifa); | 262 | inet_free_ifa(ifa); |
263 | } else { | ||
264 | promote = ifa; | ||
265 | break; | ||
266 | } | ||
259 | } | 267 | } |
260 | } | 268 | } |
261 | 269 | ||
@@ -281,6 +289,13 @@ static void inet_del_ifa(struct in_device *in_dev, struct in_ifaddr **ifap, | |||
281 | if (!in_dev->ifa_list) | 289 | if (!in_dev->ifa_list) |
282 | inetdev_destroy(in_dev); | 290 | inetdev_destroy(in_dev); |
283 | } | 291 | } |
292 | |||
293 | if (promote && IN_DEV_PROMOTE_SECONDARIES(in_dev)) { | ||
294 | /* not sure if we should send a delete notify first? */ | ||
295 | promote->ifa_flags &= ~IFA_F_SECONDARY; | ||
296 | rtmsg_ifa(RTM_NEWADDR, promote); | ||
297 | notifier_call_chain(&inetaddr_chain, NETDEV_UP, promote); | ||
298 | } | ||
284 | } | 299 | } |
285 | 300 | ||
286 | static int inet_insert_ifa(struct in_ifaddr *ifa) | 301 | static int inet_insert_ifa(struct in_ifaddr *ifa) |
@@ -1015,14 +1030,13 @@ static struct notifier_block ip_netdev_notifier = { | |||
1015 | }; | 1030 | }; |
1016 | 1031 | ||
1017 | static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, | 1032 | static int inet_fill_ifaddr(struct sk_buff *skb, struct in_ifaddr *ifa, |
1018 | u32 pid, u32 seq, int event) | 1033 | u32 pid, u32 seq, int event, unsigned int flags) |
1019 | { | 1034 | { |
1020 | struct ifaddrmsg *ifm; | 1035 | struct ifaddrmsg *ifm; |
1021 | struct nlmsghdr *nlh; | 1036 | struct nlmsghdr *nlh; |
1022 | unsigned char *b = skb->tail; | 1037 | unsigned char *b = skb->tail; |
1023 | 1038 | ||
1024 | nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm)); | 1039 | nlh = NLMSG_NEW(skb, pid, seq, event, sizeof(*ifm), flags); |
1025 | if (pid) nlh->nlmsg_flags |= NLM_F_MULTI; | ||
1026 | ifm = NLMSG_DATA(nlh); | 1040 | ifm = NLMSG_DATA(nlh); |
1027 | ifm->ifa_family = AF_INET; | 1041 | ifm->ifa_family = AF_INET; |
1028 | ifm->ifa_prefixlen = ifa->ifa_prefixlen; | 1042 | ifm->ifa_prefixlen = ifa->ifa_prefixlen; |
@@ -1075,7 +1089,7 @@ static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) | |||
1075 | continue; | 1089 | continue; |
1076 | if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, | 1090 | if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, |
1077 | cb->nlh->nlmsg_seq, | 1091 | cb->nlh->nlmsg_seq, |
1078 | RTM_NEWADDR) <= 0) { | 1092 | RTM_NEWADDR, NLM_F_MULTI) <= 0) { |
1079 | rcu_read_unlock(); | 1093 | rcu_read_unlock(); |
1080 | goto done; | 1094 | goto done; |
1081 | } | 1095 | } |
@@ -1098,7 +1112,7 @@ static void rtmsg_ifa(int event, struct in_ifaddr* ifa) | |||
1098 | 1112 | ||
1099 | if (!skb) | 1113 | if (!skb) |
1100 | netlink_set_err(rtnl, 0, RTMGRP_IPV4_IFADDR, ENOBUFS); | 1114 | netlink_set_err(rtnl, 0, RTMGRP_IPV4_IFADDR, ENOBUFS); |
1101 | else if (inet_fill_ifaddr(skb, ifa, 0, 0, event) < 0) { | 1115 | else if (inet_fill_ifaddr(skb, ifa, current->pid, 0, event, 0) < 0) { |
1102 | kfree_skb(skb); | 1116 | kfree_skb(skb); |
1103 | netlink_set_err(rtnl, 0, RTMGRP_IPV4_IFADDR, EINVAL); | 1117 | netlink_set_err(rtnl, 0, RTMGRP_IPV4_IFADDR, EINVAL); |
1104 | } else { | 1118 | } else { |
@@ -1384,6 +1398,15 @@ static struct devinet_sysctl_table { | |||
1384 | .proc_handler = &ipv4_doint_and_flush, | 1398 | .proc_handler = &ipv4_doint_and_flush, |
1385 | .strategy = &ipv4_doint_and_flush_strategy, | 1399 | .strategy = &ipv4_doint_and_flush_strategy, |
1386 | }, | 1400 | }, |
1401 | { | ||
1402 | .ctl_name = NET_IPV4_CONF_PROMOTE_SECONDARIES, | ||
1403 | .procname = "promote_secondaries", | ||
1404 | .data = &ipv4_devconf.promote_secondaries, | ||
1405 | .maxlen = sizeof(int), | ||
1406 | .mode = 0644, | ||
1407 | .proc_handler = &ipv4_doint_and_flush, | ||
1408 | .strategy = &ipv4_doint_and_flush_strategy, | ||
1409 | }, | ||
1387 | }, | 1410 | }, |
1388 | .devinet_dev = { | 1411 | .devinet_dev = { |
1389 | { | 1412 | { |