1272 lines · 34.1 KB · C
RawDownload
/* SCTP kernel reference Implementation * (C) Copyright IBM Corp. 2001, 2004 * Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2001 Motorola, Inc. * Copyright (c) 2001 Intel Corp. * Copyright (c) 2001 Nokia, Inc. * Copyright (c) 2001 La Monte H.P. Yarroll * * This file is part of the SCTP kernel reference Implementation * * Initialization/cleanup for SCTP protocol support. * * The SCTP reference implementation is free software; * you can redistribute it and/or modify it under the terms of * the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * The SCTP reference implementation is distributed in the hope that it * will be useful, but WITHOUT ANY WARRANTY; without even the implied *                 ************************ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU CC; see the file COPYING.  If not, write to * the Free Software Foundation, 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Please send any bug reports or fixes you make to the * email address(es): *    lksctp developers <lksctp-developers@lists.sourceforge.net> * * Or submit a bug report through the following website: *    http://www.sf.net/projects/lksctp * * Written or modified by: *    La Monte H.P. Yarroll <piggy@acm.org> *    Karl Knutson <karl@athena.chicago.il.us> *    Jon Grimm <jgrimm@us.ibm.com> *    Sridhar Samudrala <sri@us.ibm.com> *    Daisy Chang <daisyc@us.ibm.com> *    Ardelle Fan <ardelle.fan@intel.com> * * Any bugs reported given to us we will try to fix... any fixes shared will * be incorporated into the next SCTP release. */ #include <linux/module.h>#include <linux/init.h>#include <linux/netdevice.h>#include <linux/inetdevice.h>#include <linux/seq_file.h>#include <net/protocol.h>#include <net/ip.h>#include <net/ipv6.h>#include <net/route.h>#include <net/sctp/sctp.h>#include <net/addrconf.h>#include <net/inet_common.h>#include <net/inet_ecn.h> /* Global data structures. */struct sctp_globals sctp_globals;struct proc_dir_entry	*proc_net_sctp;DEFINE_SNMP_STAT(struct sctp_mib, sctp_statistics) __read_mostly; struct idr sctp_assocs_id;DEFINE_SPINLOCK(sctp_assocs_id_lock); /* This is the global socket data structure used for responding to * the Out-of-the-blue (OOTB) packets.  A control sock will be created * for this socket at the initialization time. */static struct socket *sctp_ctl_socket; static struct sctp_pf *sctp_pf_inet6_specific;static struct sctp_pf *sctp_pf_inet_specific;static struct sctp_af *sctp_af_v4_specific;static struct sctp_af *sctp_af_v6_specific; kmem_cache_t *sctp_chunk_cachep __read_mostly;kmem_cache_t *sctp_bucket_cachep __read_mostly; extern int sctp_snmp_proc_init(void);extern int sctp_snmp_proc_exit(void);extern int sctp_eps_proc_init(void);extern int sctp_eps_proc_exit(void);extern int sctp_assocs_proc_init(void);extern int sctp_assocs_proc_exit(void); /* Return the address of the control sock. */struct sock *sctp_get_ctl_sock(void){	return sctp_ctl_socket->sk;} /* Set up the proc fs entry for the SCTP protocol. */static __init int sctp_proc_init(void){	if (!proc_net_sctp) {		struct proc_dir_entry *ent;		ent = proc_mkdir("net/sctp", NULL);		if (ent) {			ent->owner = THIS_MODULE;			proc_net_sctp = ent;		} else			goto out_nomem;	} 	if (sctp_snmp_proc_init())		goto out_nomem;		if (sctp_eps_proc_init())		goto out_nomem;		if (sctp_assocs_proc_init())		goto out_nomem;	 	return 0; out_nomem:	return -ENOMEM;} /* Clean up the proc fs entry for the SCTP protocol.  * Note: Do not make this __exit as it is used in the init error * path. */static void sctp_proc_exit(void){	sctp_snmp_proc_exit();	sctp_eps_proc_exit();	sctp_assocs_proc_exit(); 	if (proc_net_sctp) {		proc_net_sctp = NULL;		remove_proc_entry("net/sctp", NULL);	}} /* Private helper to extract ipv4 address and stash them in * the protocol structure. */static void sctp_v4_copy_addrlist(struct list_head *addrlist,				  struct net_device *dev){	struct in_device *in_dev;	struct in_ifaddr *ifa;	struct sctp_sockaddr_entry *addr; 	rcu_read_lock();	if ((in_dev = __in_dev_get_rcu(dev)) == NULL) {		rcu_read_unlock();		return;	} 	for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {		/* Add the address to the local list.  */		addr = t_new(struct sctp_sockaddr_entry, GFP_ATOMIC);		if (addr) {			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, addrlist);		}	} 	rcu_read_unlock();} /* Extract our IP addresses from the system and stash them in the * protocol structure. */static void __sctp_get_local_addr_list(void){	struct net_device *dev;	struct list_head *pos;	struct sctp_af *af; 	read_lock(&dev_base_lock);	for (dev = dev_base; dev; dev = dev->next) {		__list_for_each(pos, &sctp_address_families) {			af = list_entry(pos, struct sctp_af, list);			af->copy_addrlist(&sctp_local_addr_list, dev);		}	}	read_unlock(&dev_base_lock);} static void sctp_get_local_addr_list(void){	unsigned long flags; 	sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);	__sctp_get_local_addr_list();	sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);} /* Free the existing local addresses.  */static void __sctp_free_local_addr_list(void){	struct sctp_sockaddr_entry *addr;	struct list_head *pos, *temp; 	list_for_each_safe(pos, temp, &sctp_local_addr_list) {		addr = list_entry(pos, struct sctp_sockaddr_entry, list);		list_del(pos);		kfree(addr);	}} /* Free the existing local addresses.  */static void sctp_free_local_addr_list(void){	unsigned long flags; 	sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);	__sctp_free_local_addr_list();	sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);} /* 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;	unsigned long flags; 	sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags);	list_for_each(pos, &sctp_local_addr_list) {		addr = list_entry(pos, struct sctp_sockaddr_entry, list);		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			 * sock as well as the remote peer.			 */			if ((((AF_INET == addr->a.sa.sa_family) &&			      (copy_flags & SCTP_ADDR4_PEERSUPP))) ||			    (((AF_INET6 == addr->a.sa.sa_family) &&			      (copy_flags & SCTP_ADDR6_ALLOWED) &&			      (copy_flags & SCTP_ADDR6_PEERSUPP)))) {				error = sctp_add_bind_addr(bp, &addr->a, 							   GFP_ATOMIC);				if (error)					goto end_copy;			}		}	} end_copy:	sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags);	return error;} /* Initialize a sctp_addr from in incoming skb.  */static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,			     int is_saddr){	void *from;	__u16 *port;	struct sctphdr *sh; 	port = &addr->v4.sin_port;	addr->v4.sin_family = AF_INET; 	sh = (struct sctphdr *) skb->h.raw;	if (is_saddr) {		*port  = ntohs(sh->source);		from = &skb->nh.iph->saddr;	} else {		*port = ntohs(sh->dest);		from = &skb->nh.iph->daddr;	}	memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr));} /* Initialize an sctp_addr from a socket. */static void sctp_v4_from_sk(union sctp_addr *addr, struct sock *sk){	addr->v4.sin_family = AF_INET;	addr->v4.sin_port = inet_sk(sk)->num;	addr->v4.sin_addr.s_addr = inet_sk(sk)->rcv_saddr;} /* Initialize sk->sk_rcv_saddr from sctp_addr. */static void sctp_v4_to_sk_saddr(union sctp_addr *addr, struct sock *sk){	inet_sk(sk)->rcv_saddr = addr->v4.sin_addr.s_addr;} /* Initialize sk->sk_daddr from sctp_addr. */static void sctp_v4_to_sk_daddr(union sctp_addr *addr, struct sock *sk){	inet_sk(sk)->daddr = addr->v4.sin_addr.s_addr;} /* Initialize a sctp_addr from an address parameter. */static void sctp_v4_from_addr_param(union sctp_addr *addr,				    union sctp_addr_param *param,				    __u16 port, int iif){	addr->v4.sin_family = AF_INET;	addr->v4.sin_port = port;	addr->v4.sin_addr.s_addr = param->v4.addr.s_addr;} /* Initialize an address parameter from a sctp_addr and return the length * of the address parameter. */static int sctp_v4_to_addr_param(const union sctp_addr *addr,				 union sctp_addr_param *param){	int length = sizeof(sctp_ipv4addr_param_t); 	param->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS;	param->v4.param_hdr.length = ntohs(length);	param->v4.addr.s_addr = addr->v4.sin_addr.s_addr;	 	return length;} /* Initialize a sctp_addr from a dst_entry. */static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst,			      unsigned short port){	struct rtable *rt = (struct rtable *)dst;	saddr->v4.sin_family = AF_INET;	saddr->v4.sin_port = port;	saddr->v4.sin_addr.s_addr = rt->rt_src;} /* Compare two addresses exactly. */static int sctp_v4_cmp_addr(const union sctp_addr *addr1,			    const union sctp_addr *addr2){	if (addr1->sa.sa_family != addr2->sa.sa_family)		return 0;	if (addr1->v4.sin_port != addr2->v4.sin_port)		return 0;	if (addr1->v4.sin_addr.s_addr != addr2->v4.sin_addr.s_addr)		return 0; 	return 1;} /* Initialize addr struct to INADDR_ANY. */static void sctp_v4_inaddr_any(union sctp_addr *addr, unsigned short port){	addr->v4.sin_family = AF_INET;	addr->v4.sin_addr.s_addr = INADDR_ANY;	addr->v4.sin_port = port;} /* Is this a wildcard address? */static int sctp_v4_is_any(const union sctp_addr *addr){	return INADDR_ANY == addr->v4.sin_addr.s_addr;} /* This function checks if the address is a valid address to be used for * SCTP binding. * * Output: * Return 0 - If the address is a non-unicast or an illegal address. * Return 1 - If the address is a unicast. */static int sctp_v4_addr_valid(union sctp_addr *addr,			      struct sctp_sock *sp,			      const struct sk_buff *skb){	/* Is this a non-unicast address or a unusable SCTP address? */	if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))		return 0;  	/* Is this a broadcast address? */ 	if (skb && ((struct rtable *)skb->dst)->rt_flags & RTCF_BROADCAST) 		return 0; 	return 1;} /* Should this be available for binding?   */static int sctp_v4_available(union sctp_addr *addr, struct sctp_sock *sp){	int ret = inet_addr_type(addr->v4.sin_addr.s_addr);  	if (addr->v4.sin_addr.s_addr != INADDR_ANY &&	   ret != RTN_LOCAL &&	   !sp->inet.freebind &&	   !sysctl_ip_nonlocal_bind)		return 0; 	return 1;} /* Checking the loopback, private and other address scopes as defined in * RFC 1918.   The IPv4 scoping is based on the draft for SCTP IPv4 * scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>. * * Level 0 - unusable SCTP addresses * Level 1 - loopback address * Level 2 - link-local addresses * Level 3 - private addresses. * Level 4 - global addresses * For INIT and INIT-ACK address list, let L be the level of * of requested destination address, sender and receiver * SHOULD include all of its addresses with level greater * than or equal to L. */static sctp_scope_t sctp_v4_scope(union sctp_addr *addr){	sctp_scope_t retval; 	/* Should IPv4 scoping be a sysctl configurable option	 * so users can turn it off (default on) for certain	 * unconventional networking environments?	 */ 	/* Check for unusable SCTP addresses. */	if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) {		retval =  SCTP_SCOPE_UNUSABLE;	} else if (LOOPBACK(addr->v4.sin_addr.s_addr)) {		retval = SCTP_SCOPE_LOOPBACK;	} else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) {		retval = SCTP_SCOPE_LINK;	} else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) {		retval = SCTP_SCOPE_PRIVATE;	} else {		retval = SCTP_SCOPE_GLOBAL;	} 	return retval;} /* Returns a valid dst cache entry for the given source and destination ip * addresses. If an association is passed, trys to get a dst entry with a * source address that matches an address in the bind address list. */static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,					 union sctp_addr *daddr,					 union sctp_addr *saddr){	struct rtable *rt;	struct flowi fl;	struct sctp_bind_addr *bp;	rwlock_t *addr_lock;	struct sctp_sockaddr_entry *laddr;	struct list_head *pos;	struct dst_entry *dst = NULL;	union sctp_addr dst_saddr; 	memset(&fl, 0x0, sizeof(struct flowi));	fl.fl4_dst  = daddr->v4.sin_addr.s_addr;	fl.proto = IPPROTO_SCTP;	if (asoc) {		fl.fl4_tos = RT_CONN_FLAGS(asoc->base.sk);		fl.oif = asoc->base.sk->sk_bound_dev_if;	}	if (saddr)		fl.fl4_src = saddr->v4.sin_addr.s_addr; 	SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",			  __FUNCTION__, NIPQUAD(fl.fl4_dst),			  NIPQUAD(fl.fl4_src)); 	if (!ip_route_output_key(&rt, &fl)) {		dst = &rt->u.dst;	} 	/* If there is no association or if a source address is passed, no	 * more validation is required.	 */	if (!asoc || saddr)		goto out; 	bp = &asoc->base.bind_addr;	addr_lock = &asoc->base.addr_lock; 	if (dst) {		/* Walk through the bind address list and look for a bind		 * address that matches the source address of the returned dst.		 */		sctp_read_lock(addr_lock);		list_for_each(pos, &bp->address_list) {			laddr = list_entry(pos, struct sctp_sockaddr_entry,					   list);			sctp_v4_dst_saddr(&dst_saddr, dst, bp->port);			if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))				goto out_unlock;		}		sctp_read_unlock(addr_lock); 		/* None of the bound addresses match the source address of the		 * dst. So release it.		 */		dst_release(dst);		dst = NULL;	}

Lines 1–500 of 1272.