|
@@ -153,6 +153,9 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist,
|
|
|
addr->a.v4.sin_family = AF_INET;
|
|
|
addr->a.v4.sin_port = 0;
|
|
|
addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
|
|
|
+ addr->valid = 1;
|
|
|
+ INIT_LIST_HEAD(&addr->list);
|
|
|
+ INIT_RCU_HEAD(&addr->rcu);
|
|
|
list_add_tail(&addr->list, addrlist);
|
|
|
}
|
|
|
}
|
|
@@ -192,16 +195,24 @@ static void sctp_free_local_addr_list(void)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void sctp_local_addr_free(struct rcu_head *head)
|
|
|
+{
|
|
|
+ struct sctp_sockaddr_entry *e = container_of(head,
|
|
|
+ struct sctp_sockaddr_entry, rcu);
|
|
|
+ kfree(e);
|
|
|
+}
|
|
|
+
|
|
|
/* Copy the local addresses which are valid for 'scope' into 'bp'. */
|
|
|
int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope,
|
|
|
gfp_t gfp, int copy_flags)
|
|
|
{
|
|
|
struct sctp_sockaddr_entry *addr;
|
|
|
int error = 0;
|
|
|
- struct list_head *pos, *temp;
|
|
|
|
|
|
- list_for_each_safe(pos, temp, &sctp_local_addr_list) {
|
|
|
- addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
|
|
+ rcu_read_lock();
|
|
|
+ list_for_each_entry_rcu(addr, &sctp_local_addr_list, list) {
|
|
|
+ if (!addr->valid)
|
|
|
+ continue;
|
|
|
if (sctp_in_scope(&addr->a, scope)) {
|
|
|
/* Now that the address is in scope, check to see if
|
|
|
* the address type is really supported by the local
|
|
@@ -221,6 +232,7 @@ int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope,
|
|
|
}
|
|
|
|
|
|
end_copy:
|
|
|
+ rcu_read_unlock();
|
|
|
return error;
|
|
|
}
|
|
|
|
|
@@ -600,13 +612,18 @@ static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr)
|
|
|
seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr));
|
|
|
}
|
|
|
|
|
|
-/* Event handler for inet address addition/deletion events. */
|
|
|
+/* Event handler for inet address addition/deletion events.
|
|
|
+ * The sctp_local_addr_list needs to be protocted by a spin lock since
|
|
|
+ * multiple notifiers (say IPv4 and IPv6) may be running at the same
|
|
|
+ * time and thus corrupt the list.
|
|
|
+ * The reader side is protected with RCU.
|
|
|
+ */
|
|
|
static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
|
|
|
void *ptr)
|
|
|
{
|
|
|
struct in_ifaddr *ifa = (struct in_ifaddr *)ptr;
|
|
|
- struct sctp_sockaddr_entry *addr;
|
|
|
- struct list_head *pos, *temp;
|
|
|
+ struct sctp_sockaddr_entry *addr = NULL;
|
|
|
+ struct sctp_sockaddr_entry *temp;
|
|
|
|
|
|
switch (ev) {
|
|
|
case NETDEV_UP:
|
|
@@ -615,19 +632,25 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
|
|
|
addr->a.v4.sin_family = AF_INET;
|
|
|
addr->a.v4.sin_port = 0;
|
|
|
addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
|
|
|
- list_add_tail(&addr->list, &sctp_local_addr_list);
|
|
|
+ addr->valid = 1;
|
|
|
+ spin_lock_bh(&sctp_local_addr_lock);
|
|
|
+ list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
|
|
|
+ spin_unlock_bh(&sctp_local_addr_lock);
|
|
|
}
|
|
|
break;
|
|
|
case NETDEV_DOWN:
|
|
|
- list_for_each_safe(pos, temp, &sctp_local_addr_list) {
|
|
|
- addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
|
|
+ spin_lock_bh(&sctp_local_addr_lock);
|
|
|
+ list_for_each_entry_safe(addr, temp,
|
|
|
+ &sctp_local_addr_list, list) {
|
|
|
if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) {
|
|
|
- list_del(pos);
|
|
|
- kfree(addr);
|
|
|
+ addr->valid = 0;
|
|
|
+ list_del_rcu(&addr->list);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+ spin_unlock_bh(&sctp_local_addr_lock);
|
|
|
+ if (addr && !addr->valid)
|
|
|
+ call_rcu(&addr->rcu, sctp_local_addr_free);
|
|
|
break;
|
|
|
}
|
|
|
|
|
@@ -1160,6 +1183,7 @@ SCTP_STATIC __init int sctp_init(void)
|
|
|
|
|
|
/* Initialize the local address list. */
|
|
|
INIT_LIST_HEAD(&sctp_local_addr_list);
|
|
|
+ spin_lock_init(&sctp_local_addr_lock);
|
|
|
sctp_get_local_addr_list();
|
|
|
|
|
|
/* Register notifier for inet address additions/deletions. */
|
|
@@ -1227,6 +1251,9 @@ SCTP_STATIC __exit void sctp_exit(void)
|
|
|
sctp_v6_del_protocol();
|
|
|
inet_del_protocol(&sctp_protocol, IPPROTO_SCTP);
|
|
|
|
|
|
+ /* Unregister notifier for inet address additions/deletions. */
|
|
|
+ unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
|
|
|
+
|
|
|
/* Free the local address list. */
|
|
|
sctp_free_local_addr_list();
|
|
|
|
|
@@ -1240,9 +1267,6 @@ SCTP_STATIC __exit void sctp_exit(void)
|
|
|
inet_unregister_protosw(&sctp_stream_protosw);
|
|
|
inet_unregister_protosw(&sctp_seqpacket_protosw);
|
|
|
|
|
|
- /* Unregister notifier for inet address additions/deletions. */
|
|
|
- unregister_inetaddr_notifier(&sctp_inetaddr_notifier);
|
|
|
-
|
|
|
sctp_sysctl_unregister();
|
|
|
list_del(&sctp_ipv4_specific.list);
|
|
|
|