|
@@ -623,6 +623,142 @@ static void sctp_v4_ecn_capable(struct sock *sk)
|
|
|
INET_ECN_xmit(sk);
|
|
|
}
|
|
|
|
|
|
+void sctp_addr_wq_timeout_handler(unsigned long arg)
|
|
|
+{
|
|
|
+ struct sctp_sockaddr_entry *addrw, *temp;
|
|
|
+ struct sctp_sock *sp;
|
|
|
+
|
|
|
+ spin_lock_bh(&sctp_addr_wq_lock);
|
|
|
+
|
|
|
+ list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) {
|
|
|
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addrwq_timo_handler: the first ent in wq %p is ",
|
|
|
+ " for cmd %d at entry %p\n", &sctp_addr_waitq, &addrw->a, addrw->state,
|
|
|
+ addrw);
|
|
|
+
|
|
|
+ /* Now we send an ASCONF for each association */
|
|
|
+ /* Note. we currently don't handle link local IPv6 addressees */
|
|
|
+ if (addrw->a.sa.sa_family == AF_INET6) {
|
|
|
+ struct in6_addr *in6;
|
|
|
+
|
|
|
+ if (ipv6_addr_type(&addrw->a.v6.sin6_addr) &
|
|
|
+ IPV6_ADDR_LINKLOCAL)
|
|
|
+ goto free_next;
|
|
|
+
|
|
|
+ in6 = (struct in6_addr *)&addrw->a.v6.sin6_addr;
|
|
|
+ if (ipv6_chk_addr(&init_net, in6, NULL, 0) == 0 &&
|
|
|
+ addrw->state == SCTP_ADDR_NEW) {
|
|
|
+ unsigned long timeo_val;
|
|
|
+
|
|
|
+ SCTP_DEBUG_PRINTK("sctp_timo_handler: this is on DAD, trying %d sec later\n",
|
|
|
+ SCTP_ADDRESS_TICK_DELAY);
|
|
|
+ timeo_val = jiffies;
|
|
|
+ timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
|
|
|
+ mod_timer(&sctp_addr_wq_timer, timeo_val);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ list_for_each_entry(sp, &sctp_auto_asconf_splist, auto_asconf_list) {
|
|
|
+ struct sock *sk;
|
|
|
+
|
|
|
+ sk = sctp_opt2sk(sp);
|
|
|
+ /* ignore bound-specific endpoints */
|
|
|
+ if (!sctp_is_ep_boundall(sk))
|
|
|
+ continue;
|
|
|
+ sctp_bh_lock_sock(sk);
|
|
|
+ if (sctp_asconf_mgmt(sp, addrw) < 0)
|
|
|
+ SCTP_DEBUG_PRINTK("sctp_addrwq_timo_handler: sctp_asconf_mgmt failed\n");
|
|
|
+ sctp_bh_unlock_sock(sk);
|
|
|
+ }
|
|
|
+free_next:
|
|
|
+ list_del(&addrw->list);
|
|
|
+ kfree(addrw);
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&sctp_addr_wq_lock);
|
|
|
+}
|
|
|
+
|
|
|
+static void sctp_free_addr_wq(void)
|
|
|
+{
|
|
|
+ struct sctp_sockaddr_entry *addrw;
|
|
|
+ struct sctp_sockaddr_entry *temp;
|
|
|
+
|
|
|
+ spin_lock_bh(&sctp_addr_wq_lock);
|
|
|
+ del_timer(&sctp_addr_wq_timer);
|
|
|
+ list_for_each_entry_safe(addrw, temp, &sctp_addr_waitq, list) {
|
|
|
+ list_del(&addrw->list);
|
|
|
+ kfree(addrw);
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&sctp_addr_wq_lock);
|
|
|
+}
|
|
|
+
|
|
|
+/* lookup the entry for the same address in the addr_waitq
|
|
|
+ * sctp_addr_wq MUST be locked
|
|
|
+ */
|
|
|
+static struct sctp_sockaddr_entry *sctp_addr_wq_lookup(struct sctp_sockaddr_entry *addr)
|
|
|
+{
|
|
|
+ struct sctp_sockaddr_entry *addrw;
|
|
|
+
|
|
|
+ list_for_each_entry(addrw, &sctp_addr_waitq, list) {
|
|
|
+ if (addrw->a.sa.sa_family != addr->a.sa.sa_family)
|
|
|
+ continue;
|
|
|
+ if (addrw->a.sa.sa_family == AF_INET) {
|
|
|
+ if (addrw->a.v4.sin_addr.s_addr ==
|
|
|
+ addr->a.v4.sin_addr.s_addr)
|
|
|
+ return addrw;
|
|
|
+ } else if (addrw->a.sa.sa_family == AF_INET6) {
|
|
|
+ if (ipv6_addr_equal(&addrw->a.v6.sin6_addr,
|
|
|
+ &addr->a.v6.sin6_addr))
|
|
|
+ return addrw;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *addr, int cmd)
|
|
|
+{
|
|
|
+ struct sctp_sockaddr_entry *addrw;
|
|
|
+ unsigned long timeo_val;
|
|
|
+
|
|
|
+ /* first, we check if an opposite message already exist in the queue.
|
|
|
+ * If we found such message, it is removed.
|
|
|
+ * This operation is a bit stupid, but the DHCP client attaches the
|
|
|
+ * new address after a couple of addition and deletion of that address
|
|
|
+ */
|
|
|
+
|
|
|
+ spin_lock_bh(&sctp_addr_wq_lock);
|
|
|
+ /* Offsets existing events in addr_wq */
|
|
|
+ addrw = sctp_addr_wq_lookup(addr);
|
|
|
+ if (addrw) {
|
|
|
+ if (addrw->state != cmd) {
|
|
|
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt offsets existing entry for %d ",
|
|
|
+ " in wq %p\n", addrw->state, &addrw->a,
|
|
|
+ &sctp_addr_waitq);
|
|
|
+ list_del(&addrw->list);
|
|
|
+ kfree(addrw);
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&sctp_addr_wq_lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* OK, we have to add the new address to the wait queue */
|
|
|
+ addrw = kmemdup(addr, sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC);
|
|
|
+ if (addrw == NULL) {
|
|
|
+ spin_unlock_bh(&sctp_addr_wq_lock);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ addrw->state = cmd;
|
|
|
+ list_add_tail(&addrw->list, &sctp_addr_waitq);
|
|
|
+ SCTP_DEBUG_PRINTK_IPADDR("sctp_addr_wq_mgmt add new entry for cmd:%d ",
|
|
|
+ " in wq %p\n", addrw->state, &addrw->a, &sctp_addr_waitq);
|
|
|
+
|
|
|
+ if (!timer_pending(&sctp_addr_wq_timer)) {
|
|
|
+ timeo_val = jiffies;
|
|
|
+ timeo_val += msecs_to_jiffies(SCTP_ADDRESS_TICK_DELAY);
|
|
|
+ mod_timer(&sctp_addr_wq_timer, timeo_val);
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&sctp_addr_wq_lock);
|
|
|
+}
|
|
|
+
|
|
|
/* 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
|
|
@@ -650,6 +786,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
|
|
|
addr->valid = 1;
|
|
|
spin_lock_bh(&sctp_local_addr_lock);
|
|
|
list_add_tail_rcu(&addr->list, &sctp_local_addr_list);
|
|
|
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_NEW);
|
|
|
spin_unlock_bh(&sctp_local_addr_lock);
|
|
|
}
|
|
|
break;
|
|
@@ -660,6 +797,7 @@ static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev,
|
|
|
if (addr->a.sa.sa_family == AF_INET &&
|
|
|
addr->a.v4.sin_addr.s_addr ==
|
|
|
ifa->ifa_local) {
|
|
|
+ sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL);
|
|
|
found = 1;
|
|
|
addr->valid = 0;
|
|
|
list_del_rcu(&addr->list);
|
|
@@ -1242,6 +1380,7 @@ SCTP_STATIC __init int sctp_init(void)
|
|
|
/* Disable ADDIP by default. */
|
|
|
sctp_addip_enable = 0;
|
|
|
sctp_addip_noauth = 0;
|
|
|
+ sctp_default_auto_asconf = 0;
|
|
|
|
|
|
/* Enable PR-SCTP by default. */
|
|
|
sctp_prsctp_enable = 1;
|
|
@@ -1266,6 +1405,13 @@ SCTP_STATIC __init int sctp_init(void)
|
|
|
spin_lock_init(&sctp_local_addr_lock);
|
|
|
sctp_get_local_addr_list();
|
|
|
|
|
|
+ /* Initialize the address event list */
|
|
|
+ INIT_LIST_HEAD(&sctp_addr_waitq);
|
|
|
+ INIT_LIST_HEAD(&sctp_auto_asconf_splist);
|
|
|
+ spin_lock_init(&sctp_addr_wq_lock);
|
|
|
+ sctp_addr_wq_timer.expires = 0;
|
|
|
+ setup_timer(&sctp_addr_wq_timer, sctp_addr_wq_timeout_handler, 0);
|
|
|
+
|
|
|
status = sctp_v4_protosw_init();
|
|
|
|
|
|
if (status)
|
|
@@ -1337,6 +1483,7 @@ SCTP_STATIC __exit void sctp_exit(void)
|
|
|
/* Unregister with inet6/inet layers. */
|
|
|
sctp_v6_del_protocol();
|
|
|
sctp_v4_del_protocol();
|
|
|
+ sctp_free_addr_wq();
|
|
|
|
|
|
/* Free the control endpoint. */
|
|
|
inet_ctl_sock_destroy(sctp_ctl_sock);
|