diff options
Diffstat (limited to 'net/ipv4/devinet.c')
-rw-r--r-- | net/ipv4/devinet.c | 250 |
1 files changed, 229 insertions, 21 deletions
diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index da14c49284f4..0d4a184af16f 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); |
@@ -209,7 +291,7 @@ static void inetdev_destroy(struct in_device *in_dev) | |||
209 | inet_free_ifa(ifa); | 291 | inet_free_ifa(ifa); |
210 | } | 292 | } |
211 | 293 | ||
212 | dev->ip_ptr = NULL; | 294 | rcu_assign_pointer(dev->ip_ptr, NULL); |
213 | 295 | ||
214 | devinet_sysctl_unregister(in_dev); | 296 | devinet_sysctl_unregister(in_dev); |
215 | neigh_parms_release(&arp_tbl, in_dev->arp_parms); | 297 | neigh_parms_release(&arp_tbl, in_dev->arp_parms); |
@@ -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 */ |
@@ -403,6 +501,9 @@ static int inet_set_ifa(struct net_device *dev, struct in_ifaddr *ifa) | |||
403 | return inet_insert_ifa(ifa); | 501 | return inet_insert_ifa(ifa); |
404 | } | 502 | } |
405 | 503 | ||
504 | /* Caller must hold RCU or RTNL : | ||
505 | * We dont take a reference on found in_device | ||
506 | */ | ||
406 | struct in_device *inetdev_by_index(struct net *net, int ifindex) | 507 | struct in_device *inetdev_by_index(struct net *net, int ifindex) |
407 | { | 508 | { |
408 | struct net_device *dev; | 509 | struct net_device *dev; |
@@ -411,7 +512,7 @@ struct in_device *inetdev_by_index(struct net *net, int ifindex) | |||
411 | rcu_read_lock(); | 512 | rcu_read_lock(); |
412 | dev = dev_get_by_index_rcu(net, ifindex); | 513 | dev = dev_get_by_index_rcu(net, ifindex); |
413 | if (dev) | 514 | if (dev) |
414 | in_dev = in_dev_get(dev); | 515 | in_dev = rcu_dereference_rtnl(dev->ip_ptr); |
415 | rcu_read_unlock(); | 516 | rcu_read_unlock(); |
416 | return in_dev; | 517 | return in_dev; |
417 | } | 518 | } |
@@ -453,8 +554,6 @@ static int inet_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg | |||
453 | goto errout; | 554 | goto errout; |
454 | } | 555 | } |
455 | 556 | ||
456 | __in_dev_put(in_dev); | ||
457 | |||
458 | for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; | 557 | for (ifap = &in_dev->ifa_list; (ifa = *ifap) != NULL; |
459 | ifap = &ifa->ifa_next) { | 558 | ifap = &ifa->ifa_next) { |
460 | if (tb[IFA_LOCAL] && | 559 | if (tb[IFA_LOCAL] && |
@@ -520,6 +619,7 @@ static struct in_ifaddr *rtm_to_ifaddr(struct net *net, struct nlmsghdr *nlh) | |||
520 | if (tb[IFA_ADDRESS] == NULL) | 619 | if (tb[IFA_ADDRESS] == NULL) |
521 | tb[IFA_ADDRESS] = tb[IFA_LOCAL]; | 620 | tb[IFA_ADDRESS] = tb[IFA_LOCAL]; |
522 | 621 | ||
622 | INIT_HLIST_NODE(&ifa->hash); | ||
523 | ifa->ifa_prefixlen = ifm->ifa_prefixlen; | 623 | ifa->ifa_prefixlen = ifm->ifa_prefixlen; |
524 | ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); | 624 | ifa->ifa_mask = inet_make_mask(ifm->ifa_prefixlen); |
525 | ifa->ifa_flags = ifm->ifa_flags; | 625 | ifa->ifa_flags = ifm->ifa_flags; |
@@ -669,7 +769,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
669 | ifap = &ifa->ifa_next) { | 769 | ifap = &ifa->ifa_next) { |
670 | if (!strcmp(ifr.ifr_name, ifa->ifa_label) && | 770 | if (!strcmp(ifr.ifr_name, ifa->ifa_label) && |
671 | sin_orig.sin_addr.s_addr == | 771 | sin_orig.sin_addr.s_addr == |
672 | ifa->ifa_address) { | 772 | ifa->ifa_local) { |
673 | break; /* found */ | 773 | break; /* found */ |
674 | } | 774 | } |
675 | } | 775 | } |
@@ -727,6 +827,7 @@ int devinet_ioctl(struct net *net, unsigned int cmd, void __user *arg) | |||
727 | if (!ifa) { | 827 | if (!ifa) { |
728 | ret = -ENOBUFS; | 828 | ret = -ENOBUFS; |
729 | ifa = inet_alloc_ifa(); | 829 | ifa = inet_alloc_ifa(); |
830 | INIT_HLIST_NODE(&ifa->hash); | ||
730 | if (!ifa) | 831 | if (!ifa) |
731 | break; | 832 | break; |
732 | if (colon) | 833 | if (colon) |
@@ -1029,6 +1130,21 @@ static inline bool inetdev_valid_mtu(unsigned mtu) | |||
1029 | return mtu >= 68; | 1130 | return mtu >= 68; |
1030 | } | 1131 | } |
1031 | 1132 | ||
1133 | static void inetdev_send_gratuitous_arp(struct net_device *dev, | ||
1134 | struct in_device *in_dev) | ||
1135 | |||
1136 | { | ||
1137 | struct in_ifaddr *ifa = in_dev->ifa_list; | ||
1138 | |||
1139 | if (!ifa) | ||
1140 | return; | ||
1141 | |||
1142 | arp_send(ARPOP_REQUEST, ETH_P_ARP, | ||
1143 | ifa->ifa_local, dev, | ||
1144 | ifa->ifa_local, NULL, | ||
1145 | dev->dev_addr, NULL); | ||
1146 | } | ||
1147 | |||
1032 | /* Called only under RTNL semaphore */ | 1148 | /* Called only under RTNL semaphore */ |
1033 | 1149 | ||
1034 | static int inetdev_event(struct notifier_block *this, unsigned long event, | 1150 | static int inetdev_event(struct notifier_block *this, unsigned long event, |
@@ -1059,7 +1175,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, | |||
1059 | switch (event) { | 1175 | switch (event) { |
1060 | case NETDEV_REGISTER: | 1176 | case NETDEV_REGISTER: |
1061 | printk(KERN_DEBUG "inetdev_event: bug\n"); | 1177 | printk(KERN_DEBUG "inetdev_event: bug\n"); |
1062 | dev->ip_ptr = NULL; | 1178 | rcu_assign_pointer(dev->ip_ptr, NULL); |
1063 | break; | 1179 | break; |
1064 | case NETDEV_UP: | 1180 | case NETDEV_UP: |
1065 | if (!inetdev_valid_mtu(dev->mtu)) | 1181 | if (!inetdev_valid_mtu(dev->mtu)) |
@@ -1068,6 +1184,7 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, | |||
1068 | struct in_ifaddr *ifa = inet_alloc_ifa(); | 1184 | struct in_ifaddr *ifa = inet_alloc_ifa(); |
1069 | 1185 | ||
1070 | if (ifa) { | 1186 | if (ifa) { |
1187 | INIT_HLIST_NODE(&ifa->hash); | ||
1071 | ifa->ifa_local = | 1188 | ifa->ifa_local = |
1072 | ifa->ifa_address = htonl(INADDR_LOOPBACK); | 1189 | ifa->ifa_address = htonl(INADDR_LOOPBACK); |
1073 | ifa->ifa_prefixlen = 8; | 1190 | ifa->ifa_prefixlen = 8; |
@@ -1081,18 +1198,13 @@ static int inetdev_event(struct notifier_block *this, unsigned long event, | |||
1081 | } | 1198 | } |
1082 | ip_mc_up(in_dev); | 1199 | ip_mc_up(in_dev); |
1083 | /* fall through */ | 1200 | /* fall through */ |
1084 | case NETDEV_NOTIFY_PEERS: | ||
1085 | case NETDEV_CHANGEADDR: | 1201 | case NETDEV_CHANGEADDR: |
1202 | if (!IN_DEV_ARP_NOTIFY(in_dev)) | ||
1203 | break; | ||
1204 | /* fall through */ | ||
1205 | case NETDEV_NOTIFY_PEERS: | ||
1086 | /* Send gratuitous ARP to notify of link change */ | 1206 | /* Send gratuitous ARP to notify of link change */ |
1087 | if (IN_DEV_ARP_NOTIFY(in_dev)) { | 1207 | inetdev_send_gratuitous_arp(dev, in_dev); |
1088 | struct in_ifaddr *ifa = in_dev->ifa_list; | ||
1089 | |||
1090 | if (ifa) | ||
1091 | arp_send(ARPOP_REQUEST, ETH_P_ARP, | ||
1092 | ifa->ifa_address, dev, | ||
1093 | ifa->ifa_address, NULL, | ||
1094 | dev->dev_addr, NULL); | ||
1095 | } | ||
1096 | break; | 1208 | break; |
1097 | case NETDEV_DOWN: | 1209 | case NETDEV_DOWN: |
1098 | ip_mc_down(in_dev); | 1210 | ip_mc_down(in_dev); |
@@ -1255,6 +1367,87 @@ errout: | |||
1255 | rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); | 1367 | rtnl_set_sk_err(net, RTNLGRP_IPV4_IFADDR, err); |
1256 | } | 1368 | } |
1257 | 1369 | ||
1370 | static size_t inet_get_link_af_size(const struct net_device *dev) | ||
1371 | { | ||
1372 | struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); | ||
1373 | |||
1374 | if (!in_dev) | ||
1375 | return 0; | ||
1376 | |||
1377 | return nla_total_size(IPV4_DEVCONF_MAX * 4); /* IFLA_INET_CONF */ | ||
1378 | } | ||
1379 | |||
1380 | static int inet_fill_link_af(struct sk_buff *skb, const struct net_device *dev) | ||
1381 | { | ||
1382 | struct in_device *in_dev = rcu_dereference_rtnl(dev->ip_ptr); | ||
1383 | struct nlattr *nla; | ||
1384 | int i; | ||
1385 | |||
1386 | if (!in_dev) | ||
1387 | return -ENODATA; | ||
1388 | |||
1389 | nla = nla_reserve(skb, IFLA_INET_CONF, IPV4_DEVCONF_MAX * 4); | ||
1390 | if (nla == NULL) | ||
1391 | return -EMSGSIZE; | ||
1392 | |||
1393 | for (i = 0; i < IPV4_DEVCONF_MAX; i++) | ||
1394 | ((u32 *) nla_data(nla))[i] = in_dev->cnf.data[i]; | ||
1395 | |||
1396 | return 0; | ||
1397 | } | ||
1398 | |||
1399 | static const struct nla_policy inet_af_policy[IFLA_INET_MAX+1] = { | ||
1400 | [IFLA_INET_CONF] = { .type = NLA_NESTED }, | ||
1401 | }; | ||
1402 | |||
1403 | static int inet_validate_link_af(const struct net_device *dev, | ||
1404 | const struct nlattr *nla) | ||
1405 | { | ||
1406 | struct nlattr *a, *tb[IFLA_INET_MAX+1]; | ||
1407 | int err, rem; | ||
1408 | |||
1409 | if (dev && !__in_dev_get_rtnl(dev)) | ||
1410 | return -EAFNOSUPPORT; | ||
1411 | |||
1412 | err = nla_parse_nested(tb, IFLA_INET_MAX, nla, inet_af_policy); | ||
1413 | if (err < 0) | ||
1414 | return err; | ||
1415 | |||
1416 | if (tb[IFLA_INET_CONF]) { | ||
1417 | nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) { | ||
1418 | int cfgid = nla_type(a); | ||
1419 | |||
1420 | if (nla_len(a) < 4) | ||
1421 | return -EINVAL; | ||
1422 | |||
1423 | if (cfgid <= 0 || cfgid > IPV4_DEVCONF_MAX) | ||
1424 | return -EINVAL; | ||
1425 | } | ||
1426 | } | ||
1427 | |||
1428 | return 0; | ||
1429 | } | ||
1430 | |||
1431 | static int inet_set_link_af(struct net_device *dev, const struct nlattr *nla) | ||
1432 | { | ||
1433 | struct in_device *in_dev = __in_dev_get_rtnl(dev); | ||
1434 | struct nlattr *a, *tb[IFLA_INET_MAX+1]; | ||
1435 | int rem; | ||
1436 | |||
1437 | if (!in_dev) | ||
1438 | return -EAFNOSUPPORT; | ||
1439 | |||
1440 | if (nla_parse_nested(tb, IFLA_INET_MAX, nla, NULL) < 0) | ||
1441 | BUG(); | ||
1442 | |||
1443 | if (tb[IFLA_INET_CONF]) { | ||
1444 | nla_for_each_nested(a, tb[IFLA_INET_CONF], rem) | ||
1445 | ipv4_devconf_set(in_dev, nla_type(a), nla_get_u32(a)); | ||
1446 | } | ||
1447 | |||
1448 | return 0; | ||
1449 | } | ||
1450 | |||
1258 | #ifdef CONFIG_SYSCTL | 1451 | #ifdef CONFIG_SYSCTL |
1259 | 1452 | ||
1260 | static void devinet_copy_dflt_conf(struct net *net, int i) | 1453 | static void devinet_copy_dflt_conf(struct net *net, int i) |
@@ -1348,9 +1541,9 @@ static int devinet_sysctl_forward(ctl_table *ctl, int write, | |||
1348 | return ret; | 1541 | return ret; |
1349 | } | 1542 | } |
1350 | 1543 | ||
1351 | int ipv4_doint_and_flush(ctl_table *ctl, int write, | 1544 | static int ipv4_doint_and_flush(ctl_table *ctl, int write, |
1352 | void __user *buffer, | 1545 | void __user *buffer, |
1353 | size_t *lenp, loff_t *ppos) | 1546 | size_t *lenp, loff_t *ppos) |
1354 | { | 1547 | { |
1355 | int *valp = ctl->data; | 1548 | int *valp = ctl->data; |
1356 | int val = *valp; | 1549 | int val = *valp; |
@@ -1487,7 +1680,7 @@ static void __devinet_sysctl_unregister(struct ipv4_devconf *cnf) | |||
1487 | return; | 1680 | return; |
1488 | 1681 | ||
1489 | cnf->sysctl = NULL; | 1682 | cnf->sysctl = NULL; |
1490 | unregister_sysctl_table(t->sysctl_header); | 1683 | unregister_net_sysctl_table(t->sysctl_header); |
1491 | kfree(t->dev_name); | 1684 | kfree(t->dev_name); |
1492 | kfree(t); | 1685 | kfree(t); |
1493 | } | 1686 | } |
@@ -1618,13 +1811,28 @@ static __net_initdata struct pernet_operations devinet_ops = { | |||
1618 | .exit = devinet_exit_net, | 1811 | .exit = devinet_exit_net, |
1619 | }; | 1812 | }; |
1620 | 1813 | ||
1814 | static struct rtnl_af_ops inet_af_ops = { | ||
1815 | .family = AF_INET, | ||
1816 | .fill_link_af = inet_fill_link_af, | ||
1817 | .get_link_af_size = inet_get_link_af_size, | ||
1818 | .validate_link_af = inet_validate_link_af, | ||
1819 | .set_link_af = inet_set_link_af, | ||
1820 | }; | ||
1821 | |||
1621 | void __init devinet_init(void) | 1822 | void __init devinet_init(void) |
1622 | { | 1823 | { |
1824 | int i; | ||
1825 | |||
1826 | for (i = 0; i < IN4_ADDR_HSIZE; i++) | ||
1827 | INIT_HLIST_HEAD(&inet_addr_lst[i]); | ||
1828 | |||
1623 | register_pernet_subsys(&devinet_ops); | 1829 | register_pernet_subsys(&devinet_ops); |
1624 | 1830 | ||
1625 | register_gifconf(PF_INET, inet_gifconf); | 1831 | register_gifconf(PF_INET, inet_gifconf); |
1626 | register_netdevice_notifier(&ip_netdev_notifier); | 1832 | register_netdevice_notifier(&ip_netdev_notifier); |
1627 | 1833 | ||
1834 | rtnl_af_register(&inet_af_ops); | ||
1835 | |||
1628 | rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); | 1836 | rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL); |
1629 | rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); | 1837 | rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL); |
1630 | rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); | 1838 | rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr); |