Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9f7d653b authored by Michio Honda's avatar Michio Honda Committed by David S. Miller
Browse files

sctp: Add Auto-ASCONF support (core).



SCTP reconfigure the IP addresses in the association by using
ASCONF chunks as mentioned in RFC5061.  For example, we can
start to use the newly configured IP address in the existing
association.  This patch implements automatic ASCONF operation
in the SCTP stack with address events in the host computer,
which is called auto_asconf.

Signed-off-by: default avatarMichio Honda <micchie@sfc.wide.ad.jp>
Signed-off-by: default avatarYOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
Acked-by: default avatarWei Yongjun <yjwei@cn.fujitsu.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 3ced2ddd
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -120,6 +120,7 @@ extern int sctp_copy_local_addr_list(struct sctp_bind_addr *,
				     int flags);
extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family);
extern int sctp_register_pf(struct sctp_pf *, sa_family_t);
extern void sctp_addr_wq_mgmt(struct sctp_sockaddr_entry *, int);

/*
 * sctp/socket.c
@@ -134,6 +135,7 @@ void sctp_sock_rfree(struct sk_buff *skb);
void sctp_copy_sock(struct sock *newsk, struct sock *sk,
		    struct sctp_association *asoc);
extern struct percpu_counter sctp_sockets_allocated;
extern int sctp_asconf_mgmt(struct sctp_sock *, struct sctp_sockaddr_entry *);

/*
 * sctp/primitive.c
+15 −0
Original line number Diff line number Diff line
@@ -205,6 +205,11 @@ extern struct sctp_globals {
	 * It is a list of sctp_sockaddr_entry.
	 */
	struct list_head local_addr_list;
	int default_auto_asconf;
	struct list_head addr_waitq;
	struct timer_list addr_wq_timer;
	struct list_head auto_asconf_splist;
	spinlock_t addr_wq_lock;

	/* Lock that protects the local_addr_list writers */
	spinlock_t addr_list_lock;
@@ -264,6 +269,11 @@ extern struct sctp_globals {
#define sctp_port_hashtable		(sctp_globals.port_hashtable)
#define sctp_local_addr_list		(sctp_globals.local_addr_list)
#define sctp_local_addr_lock		(sctp_globals.addr_list_lock)
#define sctp_auto_asconf_splist		(sctp_globals.auto_asconf_splist)
#define sctp_addr_waitq			(sctp_globals.addr_waitq)
#define sctp_addr_wq_timer		(sctp_globals.addr_wq_timer)
#define sctp_addr_wq_lock		(sctp_globals.addr_wq_lock)
#define sctp_default_auto_asconf	(sctp_globals.default_auto_asconf)
#define sctp_scope_policy		(sctp_globals.ipv4_scope_policy)
#define sctp_addip_enable		(sctp_globals.addip_enable)
#define sctp_addip_noauth		(sctp_globals.addip_noauth_enable)
@@ -341,6 +351,8 @@ struct sctp_sock {
	atomic_t pd_mode;
	/* Receive to here while partial delivery is in effect. */
	struct sk_buff_head pd_lobby;
	struct list_head auto_asconf_list;
	int do_auto_asconf;
};

static inline struct sctp_sock *sctp_sk(const struct sock *sk)
@@ -792,6 +804,8 @@ struct sctp_sockaddr_entry {
	__u8 valid;
};

#define SCTP_ADDRESS_TICK_DELAY	500

typedef struct sctp_chunk *(sctp_packet_phandler_t)(struct sctp_association *);

/* This structure holds lists of chunks as we are assembling for
@@ -1236,6 +1250,7 @@ sctp_scope_t sctp_scope(const union sctp_addr *);
int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope);
int sctp_is_any(struct sock *sk, const union sctp_addr *addr);
int sctp_addr_is_valid(const union sctp_addr *addr);
int sctp_is_ep_boundall(struct sock *sk);


/* What type of endpoint?  */
+15 −0
Original line number Diff line number Diff line
@@ -534,6 +534,21 @@ int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
	return 0;
}

int sctp_is_ep_boundall(struct sock *sk)
{
	struct sctp_bind_addr *bp;
	struct sctp_sockaddr_entry *addr;

	bp = &sctp_sk(sk)->ep->base.bind_addr;
	if (sctp_list_single_entry(&bp->address_list)) {
		addr = list_entry(bp->address_list.next,
				  struct sctp_sockaddr_entry, list);
		if (sctp_is_any(sk, &addr->a))
			return 1;
	}
	return 0;
}

/********************************************************************
 * 3rd Level Abstractions
 ********************************************************************/
+2 −0
Original line number Diff line number Diff line
@@ -112,6 +112,7 @@ static int sctp_inet6addr_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;
@@ -122,6 +123,7 @@ static int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev,
			if (addr->a.sa.sa_family == AF_INET6 &&
					ipv6_addr_equal(&addr->a.v6.sin6_addr,
						&ifa->addr)) {
				sctp_addr_wq_mgmt(addr, SCTP_ADDR_DEL);
				found = 1;
				addr->valid = 0;
				list_del_rcu(&addr->list);
+147 −0
Original line number Diff line number Diff line
@@ -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);
Loading