diff options
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r-- | net/ipv4/devinet.c | 158 |
1 files changed, 86 insertions, 72 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index e049da8311b5..5cdbc102a418 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c | |||
@@ -140,11 +140,11 @@ void in_dev_finish_destroy(struct in_device *idev) | |||
140 | #endif | 140 | #endif |
141 | dev_put(dev); | 141 | dev_put(dev); |
142 | if (!idev->dead) | 142 | if (!idev->dead) |
143 | printk("Freeing alive in_device %p\n", idev); | 143 | pr_err("Freeing alive in_device %p\n", idev); |
144 | else { | 144 | else |
145 | kfree(idev); | 145 | kfree(idev); |
146 | } | ||
147 | } | 146 | } |
147 | EXPORT_SYMBOL(in_dev_finish_destroy); | ||
148 | 148 | ||
149 | static struct in_device *inetdev_init(struct net_device *dev) | 149 | static struct in_device *inetdev_init(struct net_device *dev) |
150 | { | 150 | { |
@@ -159,7 +159,8 @@ static struct in_device *inetdev_init(struct net_device *dev) | |||
159 | sizeof(in_dev->cnf)); | 159 | sizeof(in_dev->cnf)); |
160 | in_dev->cnf.sysctl = NULL; | 160 | in_dev->cnf.sysctl = NULL; |
161 | in_dev->dev = dev; | 161 | in_dev->dev = dev; |
162 | if ((in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl)) == NULL) | 162 | in_dev->arp_parms = neigh_parms_alloc(dev, &arp_tbl); |
163 | if (!in_dev->arp_parms) | ||
163 | goto out_kfree; | 164 | goto out_kfree; |
164 | if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) | 165 | if (IPV4_DEVCONF(in_dev->cnf, FORWARDING)) |
165 | dev_disable_lro(dev); | 166 | dev_disable_lro(dev); |
@@ -405,13 +406,15 @@ struct in_device *inetdev_by_index(struct net *net, int ifindex) | |||
405 | { | 406 | { |
406 | struct net_device *dev; | 407 | struct net_device *dev; |
407 | struct in_device *in_dev = NULL; | 408 | struct in_device *in_dev = NULL; |
408 | read_lock(&dev_base_lock); | 409 | |
409 | dev = __dev_get_by_index(net, ifindex); | 410 | rcu_read_lock(); |
411 | dev = dev_get_by_index_rcu(net, ifindex); | ||
410 | if (dev) | 412 | if (dev) |
411 | in_dev = in_dev_get(dev); | 413 | in_dev = in_dev_get(dev); |
412 | read_unlock(&dev_base_lock); | 414 | rcu_read_unlock(); |
413 | return in_dev; | 415 | return in_dev; |
414 | } | 416 | } |
417 | EXPORT_SYMBOL(inetdev_by_index); | ||
415 | 418 | ||
416 | /* Called only from RTNL semaphored context. No locks. */ | 419 | /* Called only from RTNL semaphored context. No locks. */ |
417 | 420 | ||
@@ -557,7 +560,7 @@ static int inet_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg | |||
557 | * Determine a default network mask, based on the IP address. | 560 | * Determine a default network mask, based on the IP address. |
558 | */ | 561 | */ |
559 | 562 | ||
560 | static __inline__ int inet_abc_len(__be32 addr) | 563 | static inline int inet_abc_len(__be32 addr) |
561 | { | 564 | { |
562 | int rc = -1; /* Something else, probably a multicast. */ | 565 | int rc = -1; /* Something else, probably a multicast. */ |
563 | 566 | ||
@@ -646,13 +649,15 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
646 | rtnl_lock(); | 649 | rtnl_lock(); |
647 | 650 | ||
648 | ret = -ENODEV; | 651 | ret = -ENODEV; |
649 | if ((dev = __dev_get_by_name(net, ifr.ifr_name)) == NULL) | 652 | dev = __dev_get_by_name(net, ifr.ifr_name); |
653 | if (!dev) | ||
650 | goto done; | 654 | goto done; |
651 | 655 | ||
652 | if (colon) | 656 | if (colon) |
653 | *colon = ':'; | 657 | *colon = ':'; |
654 | 658 | ||
655 | if ((in_dev = __in_dev_get_rtnl(dev)) != NULL) { | 659 | in_dev = __in_dev_get_rtnl(dev); |
660 | if (in_dev) { | ||
656 | if (tryaddrmatch) { | 661 | if (tryaddrmatch) { |
657 | /* Matthias Andree */ | 662 | /* Matthias Andree */ |
658 | /* compare label and address (4.4BSD style) */ | 663 | /* compare label and address (4.4BSD style) */ |
@@ -720,7 +725,8 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
720 | 725 | ||
721 | if (!ifa) { | 726 | if (!ifa) { |
722 | ret = -ENOBUFS; | 727 | ret = -ENOBUFS; |
723 | if ((ifa = inet_alloc_ifa()) == NULL) | 728 | ifa = inet_alloc_ifa(); |
729 | if (!ifa) | ||
724 | break; | 730 | break; |
725 | if (colon) | 731 | if (colon) |
726 | memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); | 732 | memcpy(ifa->ifa_label, ifr.ifr_name, IFNAMSIZ); |
@@ -822,10 +828,10 @@ static int inet_gifconf(struct net_device *dev, char __user *buf, int len) | |||
822 | struct ifreq ifr; | 828 | struct ifreq ifr; |
823 | int done = 0; | 829 | int done = 0; |
824 | 830 | ||
825 | if (!in_dev || (ifa = in_dev->ifa_list) == NULL) | 831 | if (!in_dev) |
826 | goto out; | 832 | goto out; |
827 | 833 | ||
828 | for (; ifa; ifa = ifa->ifa_next) { | 834 | for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { |
829 | if (!buf) { | 835 | if (!buf) { |
830 | done += sizeof(ifr); | 836 | done += sizeof(ifr); |
831 | continue; | 837 | continue; |
@@ -875,36 +881,33 @@ __be32 inet_select_addr(const struct net_device *dev, __be32 dst, int scope) | |||
875 | if (!addr) | 881 | if (!addr) |
876 | addr = ifa->ifa_local; | 882 | addr = ifa->ifa_local; |
877 | } endfor_ifa(in_dev); | 883 | } endfor_ifa(in_dev); |
878 | no_in_dev: | ||
879 | rcu_read_unlock(); | ||
880 | 884 | ||
881 | if (addr) | 885 | if (addr) |
882 | goto out; | 886 | goto out_unlock; |
887 | no_in_dev: | ||
883 | 888 | ||
884 | /* Not loopback addresses on loopback should be preferred | 889 | /* Not loopback addresses on loopback should be preferred |
885 | in this case. It is importnat that lo is the first interface | 890 | in this case. It is importnat that lo is the first interface |
886 | in dev_base list. | 891 | in dev_base list. |
887 | */ | 892 | */ |
888 | read_lock(&dev_base_lock); | 893 | for_each_netdev_rcu(net, dev) { |
889 | rcu_read_lock(); | 894 | in_dev = __in_dev_get_rcu(dev); |
890 | for_each_netdev(net, dev) { | 895 | if (!in_dev) |
891 | if ((in_dev = __in_dev_get_rcu(dev)) == NULL) | ||
892 | continue; | 896 | continue; |
893 | 897 | ||
894 | for_primary_ifa(in_dev) { | 898 | for_primary_ifa(in_dev) { |
895 | if (ifa->ifa_scope != RT_SCOPE_LINK && | 899 | if (ifa->ifa_scope != RT_SCOPE_LINK && |
896 | ifa->ifa_scope <= scope) { | 900 | ifa->ifa_scope <= scope) { |
897 | addr = ifa->ifa_local; | 901 | addr = ifa->ifa_local; |
898 | goto out_unlock_both; | 902 | goto out_unlock; |
899 | } | 903 | } |
900 | } endfor_ifa(in_dev); | 904 | } endfor_ifa(in_dev); |
901 | } | 905 | } |
902 | out_unlock_both: | 906 | out_unlock: |
903 | read_unlock(&dev_base_lock); | ||
904 | rcu_read_unlock(); | 907 | rcu_read_unlock(); |
905 | out: | ||
906 | return addr; | 908 | return addr; |
907 | } | 909 | } |
910 | EXPORT_SYMBOL(inet_select_addr); | ||
908 | 911 | ||
909 | static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, | 912 | static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, |
910 | __be32 local, int scope) | 913 | __be32 local, int scope) |
@@ -940,7 +943,7 @@ static __be32 confirm_addr_indev(struct in_device *in_dev, __be32 dst, | |||
940 | } | 943 | } |
941 | } endfor_ifa(in_dev); | 944 | } endfor_ifa(in_dev); |
942 | 945 | ||
943 | return same? addr : 0; | 946 | return same ? addr : 0; |
944 | } | 947 | } |
945 | 948 | ||
946 | /* | 949 | /* |
@@ -961,17 +964,16 @@ __be32 inet_confirm_addr(struct in_device *in_dev, | |||
961 | return confirm_addr_indev(in_dev, dst, local, scope); | 964 | return confirm_addr_indev(in_dev, dst, local, scope); |
962 | 965 | ||
963 | net = dev_net(in_dev->dev); | 966 | net = dev_net(in_dev->dev); |
964 | read_lock(&dev_base_lock); | ||
965 | rcu_read_lock(); | 967 | rcu_read_lock(); |
966 | for_each_netdev(net, dev) { | 968 | for_each_netdev_rcu(net, dev) { |
967 | if ((in_dev = __in_dev_get_rcu(dev))) { | 969 | in_dev = __in_dev_get_rcu(dev); |
970 | if (in_dev) { | ||
968 | addr = confirm_addr_indev(in_dev, dst, local, scope); | 971 | addr = confirm_addr_indev(in_dev, dst, local, scope); |
969 | if (addr) | 972 | if (addr) |
970 | break; | 973 | break; |
971 | } | 974 | } |
972 | } | 975 | } |
973 | rcu_read_unlock(); | 976 | rcu_read_unlock(); |
974 | read_unlock(&dev_base_lock); | ||
975 | 977 | ||
976 | return addr; | 978 | return addr; |
977 | } | 979 | } |
@@ -984,14 +986,16 @@ int register_inetaddr_notifier(struct notifier_block *nb) | |||
984 | { | 986 | { |
985 | return blocking_notifier_chain_register(&inetaddr_chain, nb); | 987 | return blocking_notifier_chain_register(&inetaddr_chain, nb); |
986 | } | 988 | } |
989 | EXPORT_SYMBOL(register_inetaddr_notifier); | ||
987 | 990 | ||
988 | int unregister_inetaddr_notifier(struct notifier_block *nb) | 991 | int unregister_inetaddr_notifier(struct notifier_block *nb) |
989 | { | 992 | { |
990 | return blocking_notifier_chain_unregister(&inetaddr_chain, nb); | 993 | return blocking_notifier_chain_unregister(&inetaddr_chain, nb); |
991 | } | 994 | } |
995 | EXPORT_SYMBOL(unregister_inetaddr_notifier); | ||
992 | 996 | ||
993 | /* Rename ifa_labels for a device name change. Make some effort to preserve existing | 997 | /* Rename ifa_labels for a device name change. Make some effort to preserve |
994 | * alias numbering and to create unique labels if possible. | 998 | * existing alias numbering and to create unique labels if possible. |
995 | */ | 999 | */ |
996 | static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) | 1000 | static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) |
997 | { | 1001 | { |
@@ -1010,11 +1014,10 @@ static void inetdev_changename(struct net_device *dev, struct in_device *in_dev) | |||
1010 | sprintf(old, ":%d", named); | 1014 | sprintf(old, ":%d", named); |
1011 | dot = old; | 1015 | dot = old; |
1012 | } | 1016 | } |
1013 | if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) { | 1017 | if (strlen(dot) + strlen(dev->name) < IFNAMSIZ) |
1014 | strcat(ifa->ifa_label, dot); | 1018 | strcat(ifa->ifa_label, dot); |
1015 | } else { | 1019 | else |
1016 | strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); | 1020 | strcpy(ifa->ifa_label + (IFNAMSIZ - strlen(dot) - 1), dot); |
1017 | } | ||
1018 | skip: | 1021 | skip: |
1019 | rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); | 1022 | rtmsg_ifa(RTM_NEWADDR, ifa, NULL, 0); |
1020 | } | 1023 | } |
@@ -1061,8 +1064,9 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, | |||
1061 | if (!inetdev_valid_mtu(dev->mtu)) | 1064 | if (!inetdev_valid_mtu(dev->mtu)) |
1062 | break; | 1065 | break; |
1063 | if (dev->flags & IFF_LOOPBACK) { | 1066 | if (dev->flags & IFF_LOOPBACK) { |
1064 | struct in_ifaddr *ifa; | 1067 | struct in_ifaddr *ifa = inet_alloc_ifa(); |
1065 | if ((ifa = inet_alloc_ifa()) != NULL) { | 1068 | |
1069 | if (ifa) { | ||
1066 | ifa->ifa_local = | 1070 | ifa->ifa_local = |
1067 | ifa->ifa_address = htonl(INADDR_LOOPBACK); | 1071 | ifa->ifa_address = htonl(INADDR_LOOPBACK); |
1068 | ifa->ifa_prefixlen = 8; | 1072 | ifa->ifa_prefixlen = 8; |
@@ -1170,38 +1174,54 @@ nla_put_failure: | |||
1170 | static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) | 1174 | static int inet_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb) |
1171 | { | 1175 | { |
1172 | struct net *net = sock_net(skb->sk); | 1176 | struct net *net = sock_net(skb->sk); |
1173 | int idx, ip_idx; | 1177 | int h, s_h; |
1178 | int idx, s_idx; | ||
1179 | int ip_idx, s_ip_idx; | ||
1174 | struct net_device *dev; | 1180 | struct net_device *dev; |
1175 | struct in_device *in_dev; | 1181 | struct in_device *in_dev; |
1176 | struct in_ifaddr *ifa; | 1182 | struct in_ifaddr *ifa; |
1177 | int s_ip_idx, s_idx = cb->args[0]; | 1183 | struct hlist_head *head; |
1184 | struct hlist_node *node; | ||
1178 | 1185 | ||
1179 | s_ip_idx = ip_idx = cb->args[1]; | 1186 | s_h = cb->args[0]; |
1180 | idx = 0; | 1187 | s_idx = idx = cb->args[1]; |
1181 | for_each_netdev(net, dev) { | 1188 | s_ip_idx = ip_idx = cb->args[2]; |
1182 | if (idx < s_idx) | 1189 | |
1183 | goto cont; | 1190 | for (h = s_h; h < NETDEV_HASHENTRIES; h++, s_idx = 0) { |
1184 | if (idx > s_idx) | 1191 | idx = 0; |
1185 | s_ip_idx = 0; | 1192 | head = &net->dev_index_head[h]; |
1186 | if ((in_dev = __in_dev_get_rtnl(dev)) == NULL) | 1193 | rcu_read_lock(); |
1187 | goto cont; | 1194 | hlist_for_each_entry_rcu(dev, node, head, index_hlist) { |
1188 | 1195 | if (idx < s_idx) | |
1189 | for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; | 1196 | goto cont; |
1190 | ifa = ifa->ifa_next, ip_idx++) { | 1197 | if (idx > s_idx) |
1191 | if (ip_idx < s_ip_idx) | 1198 | s_ip_idx = 0; |
1192 | continue; | 1199 | in_dev = __in_dev_get_rcu(dev); |
1193 | if (inet_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, | 1200 | if (!in_dev) |
1201 | goto cont; | ||
1202 | |||
1203 | for (ifa = in_dev->ifa_list, ip_idx = 0; ifa; | ||
1204 | ifa = ifa->ifa_next, ip_idx++) { | ||
1205 | if (ip_idx < s_ip_idx) | ||
1206 | continue; | ||
1207 | if (inet_fill_ifaddr(skb, ifa, | ||
1208 | NETLINK_CB(cb->skb).pid, | ||
1194 | cb->nlh->nlmsg_seq, | 1209 | cb->nlh->nlmsg_seq, |
1195 | RTM_NEWADDR, NLM_F_MULTI) <= 0) | 1210 | RTM_NEWADDR, NLM_F_MULTI) <= 0) { |
1196 | goto done; | 1211 | rcu_read_unlock(); |
1197 | } | 1212 | goto done; |
1213 | } | ||
1214 | } | ||
1198 | cont: | 1215 | cont: |
1199 | idx++; | 1216 | idx++; |
1217 | } | ||
1218 | rcu_read_unlock(); | ||
1200 | } | 1219 | } |
1201 | 1220 | ||
1202 | done: | 1221 | done: |
1203 | cb->args[0] = idx; | 1222 | cb->args[0] = h; |
1204 | cb->args[1] = ip_idx; | 1223 | cb->args[1] = idx; |
1224 | cb->args[2] = ip_idx; | ||
1205 | 1225 | ||
1206 | return skb->len; | 1226 | return skb->len; |
1207 | } | 1227 | } |
@@ -1239,18 +1259,18 @@ static void devinet_copy_dflt_conf(struct net *net, int i) | |||
1239 | { | 1259 | { |
1240 | struct net_device *dev; | 1260 | struct net_device *dev; |
1241 | 1261 | ||
1242 | read_lock(&dev_base_lock); | 1262 | rcu_read_lock(); |
1243 | for_each_netdev(net, dev) { | 1263 | for_each_netdev_rcu(net, dev) { |
1244 | struct in_device *in_dev; | 1264 | struct in_device *in_dev; |
1245 | rcu_read_lock(); | 1265 | |
1246 | in_dev = __in_dev_get_rcu(dev); | 1266 | in_dev = __in_dev_get_rcu(dev); |
1247 | if (in_dev && !test_bit(i, in_dev->cnf.state)) | 1267 | if (in_dev && !test_bit(i, in_dev->cnf.state)) |
1248 | in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; | 1268 | in_dev->cnf.data[i] = net->ipv4.devconf_dflt->data[i]; |
1249 | rcu_read_unlock(); | ||
1250 | } | 1269 | } |
1251 | read_unlock(&dev_base_lock); | 1270 | rcu_read_unlock(); |
1252 | } | 1271 | } |
1253 | 1272 | ||
1273 | /* called with RTNL locked */ | ||
1254 | static void inet_forward_change(struct net *net) | 1274 | static void inet_forward_change(struct net *net) |
1255 | { | 1275 | { |
1256 | struct net_device *dev; | 1276 | struct net_device *dev; |
@@ -1259,7 +1279,6 @@ static void inet_forward_change(struct net *net) | |||
1259 | IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; | 1279 | IPV4_DEVCONF_ALL(net, ACCEPT_REDIRECTS) = !on; |
1260 | IPV4_DEVCONF_DFLT(net, FORWARDING) = on; | 1280 | IPV4_DEVCONF_DFLT(net, FORWARDING) = on; |
1261 | 1281 | ||
1262 | read_lock(&dev_base_lock); | ||
1263 | for_each_netdev(net, dev) { | 1282 | for_each_netdev(net, dev) { |
1264 | struct in_device *in_dev; | 1283 | struct in_device *in_dev; |
1265 | if (on) | 1284 | if (on) |
@@ -1270,7 +1289,6 @@ static void inet_forward_change(struct net *net) | |||
1270 | IN_DEV_CONF_SET(in_dev, FORWARDING, on); | 1289 | IN_DEV_CONF_SET(in_dev, FORWARDING, on); |
1271 | rcu_read_unlock(); | 1290 | rcu_read_unlock(); |
1272 | } | 1291 | } |
1273 | read_unlock(&dev_base_lock); | ||
1274 | } | 1292 | } |
1275 | 1293 | ||
1276 | static int devinet_conf_proc(ctl_table *ctl, int write, | 1294 | static int devinet_conf_proc(ctl_table *ctl, int write, |
@@ -1378,6 +1396,7 @@ static struct devinet_sysctl_table { | |||
1378 | DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), | 1396 | DEVINET_SYSCTL_RW_ENTRY(SEND_REDIRECTS, "send_redirects"), |
1379 | DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, | 1397 | DEVINET_SYSCTL_RW_ENTRY(ACCEPT_SOURCE_ROUTE, |
1380 | "accept_source_route"), | 1398 | "accept_source_route"), |
1399 | DEVINET_SYSCTL_RW_ENTRY(ACCEPT_LOCAL, "accept_local"), | ||
1381 | DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), | 1400 | DEVINET_SYSCTL_RW_ENTRY(PROXY_ARP, "proxy_arp"), |
1382 | DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), | 1401 | DEVINET_SYSCTL_RW_ENTRY(MEDIUM_ID, "medium_id"), |
1383 | DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), | 1402 | DEVINET_SYSCTL_RW_ENTRY(BOOTP_RELAY, "bootp_relay"), |
@@ -1512,7 +1531,7 @@ static __net_init int devinet_init_net(struct net *net) | |||
1512 | all = &ipv4_devconf; | 1531 | all = &ipv4_devconf; |
1513 | dflt = &ipv4_devconf_dflt; | 1532 | dflt = &ipv4_devconf_dflt; |
1514 | 1533 | ||
1515 | if (net != &init_net) { | 1534 | if (!net_eq(net, &init_net)) { |
1516 | all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); | 1535 | all = kmemdup(all, sizeof(ipv4_devconf), GFP_KERNEL); |
1517 | if (all == NULL) | 1536 | if (all == NULL) |
1518 | goto err_alloc_all; | 1537 | goto err_alloc_all; |
@@ -1603,8 +1622,3 @@ void __init devinet_init(void) | |||
1603 | rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); | 1622 | rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); |
1604 | } | 1623 | } |
1605 | 1624 | ||
1606 | EXPORT_SYMBOL(in_dev_finish_destroy); | ||
1607 | EXPORT_SYMBOL(inet_select_addr); | ||
1608 | EXPORT_SYMBOL(inetdev_by_index); | ||
1609 | EXPORT_SYMBOL(register_inetaddr_notifier); | ||
1610 | EXPORT_SYMBOL(unregister_inetaddr_notifier); | ||