Creation of Cybook 2416 (actually Gen4) repository
This commit is contained in:
89
net/sctp/Kconfig
Normal file
89
net/sctp/Kconfig
Normal file
@@ -0,0 +1,89 @@
|
||||
#
|
||||
# SCTP configuration
|
||||
#
|
||||
|
||||
menu "SCTP Configuration (EXPERIMENTAL)"
|
||||
depends on INET && EXPERIMENTAL
|
||||
|
||||
config IP_SCTP
|
||||
tristate "The SCTP Protocol (EXPERIMENTAL)"
|
||||
depends on IPV6 || IPV6=n
|
||||
select CRYPTO if SCTP_HMAC_SHA1 || SCTP_HMAC_MD5
|
||||
select CRYPTO_HMAC if SCTP_HMAC_SHA1 || SCTP_HMAC_MD5
|
||||
select CRYPTO_SHA1 if SCTP_HMAC_SHA1
|
||||
select CRYPTO_MD5 if SCTP_HMAC_MD5
|
||||
---help---
|
||||
Stream Control Transmission Protocol
|
||||
|
||||
From RFC 2960 <http://www.ietf.org/rfc/rfc2960.txt>.
|
||||
|
||||
"SCTP is a reliable transport protocol operating on top of a
|
||||
connectionless packet network such as IP. It offers the following
|
||||
services to its users:
|
||||
|
||||
-- acknowledged error-free non-duplicated transfer of user data,
|
||||
-- data fragmentation to conform to discovered path MTU size,
|
||||
-- sequenced delivery of user messages within multiple streams,
|
||||
with an option for order-of-arrival delivery of individual user
|
||||
messages,
|
||||
-- optional bundling of multiple user messages into a single SCTP
|
||||
packet, and
|
||||
-- network-level fault tolerance through supporting of multi-
|
||||
homing at either or both ends of an association."
|
||||
|
||||
To compile this protocol support as a module, choose M here: the
|
||||
module will be called sctp.
|
||||
|
||||
If in doubt, say N.
|
||||
|
||||
config SCTP_DBG_MSG
|
||||
bool "SCTP: Debug messages"
|
||||
depends on IP_SCTP
|
||||
help
|
||||
If you say Y, this will enable verbose debugging messages.
|
||||
|
||||
If unsure, say N. However, if you are running into problems, use
|
||||
this option to gather detailed trace information
|
||||
|
||||
config SCTP_DBG_OBJCNT
|
||||
bool "SCTP: Debug object counts"
|
||||
depends on IP_SCTP
|
||||
help
|
||||
If you say Y, this will enable debugging support for counting the
|
||||
type of objects that are currently allocated. This is useful for
|
||||
identifying memory leaks. If the /proc filesystem is enabled this
|
||||
debug information can be viewed by
|
||||
'cat /proc/net/sctp/sctp_dbg_objcnt'
|
||||
|
||||
If unsure, say N
|
||||
|
||||
choice
|
||||
prompt "SCTP: Cookie HMAC Algorithm"
|
||||
depends on IP_SCTP
|
||||
default SCTP_HMAC_MD5
|
||||
help
|
||||
HMAC algorithm to be used during association initialization. It
|
||||
is strongly recommended to use HMAC-SHA1 or HMAC-MD5. See
|
||||
configuration for Cryptographic API and enable those algorithms
|
||||
to make usable by SCTP.
|
||||
|
||||
config SCTP_HMAC_NONE
|
||||
bool "None"
|
||||
help
|
||||
Choosing this disables the use of an HMAC during association
|
||||
establishment. It is advised to use either HMAC-MD5 or HMAC-SHA1.
|
||||
|
||||
config SCTP_HMAC_SHA1
|
||||
bool "HMAC-SHA1"
|
||||
help
|
||||
Enable the use of HMAC-SHA1 during association establishment. It
|
||||
is advised to use either HMAC-MD5 or HMAC-SHA1.
|
||||
|
||||
config SCTP_HMAC_MD5
|
||||
bool "HMAC-MD5"
|
||||
help
|
||||
Enable the use of HMAC-MD5 during association establishment. It is
|
||||
advised to use either HMAC-MD5 or HMAC-SHA1.
|
||||
|
||||
endchoice
|
||||
endmenu
|
||||
17
net/sctp/Makefile
Normal file
17
net/sctp/Makefile
Normal file
@@ -0,0 +1,17 @@
|
||||
#
|
||||
# Makefile for SCTP support code.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IP_SCTP) += sctp.o
|
||||
|
||||
sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
|
||||
protocol.o endpointola.o associola.o \
|
||||
transport.o chunk.o sm_make_chunk.o ulpevent.o \
|
||||
inqueue.o outqueue.o ulpqueue.o command.o \
|
||||
tsnmap.o bind_addr.o socket.o primitive.o \
|
||||
output.o input.o debug.o ssnmap.o proc.o crc32c.o
|
||||
|
||||
sctp-$(CONFIG_SCTP_DBG_OBJCNT) += objcnt.o
|
||||
sctp-$(CONFIG_SYSCTL) += sysctl.o
|
||||
|
||||
sctp-$(subst m,y,$(CONFIG_IPV6)) += ipv6.o
|
||||
1369
net/sctp/associola.c
Normal file
1369
net/sctp/associola.c
Normal file
File diff suppressed because it is too large
Load Diff
422
net/sctp/bind_addr.c
Normal file
422
net/sctp/bind_addr.c
Normal file
@@ -0,0 +1,422 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* (C) Copyright IBM Corp. 2001, 2003
|
||||
* Copyright (c) Cisco 1999,2000
|
||||
* Copyright (c) Motorola 1999,2000,2001
|
||||
* Copyright (c) La Monte H.P. Yarroll 2001
|
||||
*
|
||||
* This file is part of the SCTP kernel reference implementation.
|
||||
*
|
||||
* A collection class to handle the storage of transport addresses.
|
||||
*
|
||||
* 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>
|
||||
* Daisy Chang <daisyc@us.ibm.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/types.h>
|
||||
#include <linux/in.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/if_inet6.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
/* Forward declarations for internal helpers. */
|
||||
static int sctp_copy_one_addr(struct sctp_bind_addr *, union sctp_addr *,
|
||||
sctp_scope_t scope, gfp_t gfp,
|
||||
int flags);
|
||||
static void sctp_bind_addr_clean(struct sctp_bind_addr *);
|
||||
|
||||
/* First Level Abstractions. */
|
||||
|
||||
/* Copy 'src' to 'dest' taking 'scope' into account. Omit addresses
|
||||
* in 'src' which have a broader scope than 'scope'.
|
||||
*/
|
||||
int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
|
||||
const struct sctp_bind_addr *src,
|
||||
sctp_scope_t scope, gfp_t gfp,
|
||||
int flags)
|
||||
{
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
struct list_head *pos;
|
||||
int error = 0;
|
||||
|
||||
/* All addresses share the same port. */
|
||||
dest->port = src->port;
|
||||
|
||||
/* Extract the addresses which are relevant for this scope. */
|
||||
list_for_each(pos, &src->address_list) {
|
||||
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
error = sctp_copy_one_addr(dest, &addr->a, scope,
|
||||
gfp, flags);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* If there are no addresses matching the scope and
|
||||
* this is global scope, try to get a link scope address, with
|
||||
* the assumption that we must be sitting behind a NAT.
|
||||
*/
|
||||
if (list_empty(&dest->address_list) && (SCTP_SCOPE_GLOBAL == scope)) {
|
||||
list_for_each(pos, &src->address_list) {
|
||||
addr = list_entry(pos, struct sctp_sockaddr_entry,
|
||||
list);
|
||||
error = sctp_copy_one_addr(dest, &addr->a,
|
||||
SCTP_SCOPE_LINK, gfp,
|
||||
flags);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (error)
|
||||
sctp_bind_addr_clean(dest);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Initialize the SCTP_bind_addr structure for either an endpoint or
|
||||
* an association.
|
||||
*/
|
||||
void sctp_bind_addr_init(struct sctp_bind_addr *bp, __u16 port)
|
||||
{
|
||||
bp->malloced = 0;
|
||||
|
||||
INIT_LIST_HEAD(&bp->address_list);
|
||||
bp->port = port;
|
||||
}
|
||||
|
||||
/* Dispose of the address list. */
|
||||
static void sctp_bind_addr_clean(struct sctp_bind_addr *bp)
|
||||
{
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
struct list_head *pos, *temp;
|
||||
|
||||
/* Empty the bind address list. */
|
||||
list_for_each_safe(pos, temp, &bp->address_list) {
|
||||
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
list_del(pos);
|
||||
kfree(addr);
|
||||
SCTP_DBG_OBJCNT_DEC(addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dispose of an SCTP_bind_addr structure */
|
||||
void sctp_bind_addr_free(struct sctp_bind_addr *bp)
|
||||
{
|
||||
/* Empty the bind address list. */
|
||||
sctp_bind_addr_clean(bp);
|
||||
|
||||
if (bp->malloced) {
|
||||
kfree(bp);
|
||||
SCTP_DBG_OBJCNT_DEC(bind_addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Add an address to the bind address list in the SCTP_bind_addr structure. */
|
||||
int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new,
|
||||
__u8 use_as_src, gfp_t gfp)
|
||||
{
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
|
||||
/* Add the address to the bind address list. */
|
||||
addr = t_new(struct sctp_sockaddr_entry, gfp);
|
||||
if (!addr)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(&addr->a, new, sizeof(*new));
|
||||
|
||||
/* Fix up the port if it has not yet been set.
|
||||
* Both v4 and v6 have the port at the same offset.
|
||||
*/
|
||||
if (!addr->a.v4.sin_port)
|
||||
addr->a.v4.sin_port = htons(bp->port);
|
||||
|
||||
addr->use_as_src = use_as_src;
|
||||
|
||||
INIT_LIST_HEAD(&addr->list);
|
||||
list_add_tail(&addr->list, &bp->address_list);
|
||||
SCTP_DBG_OBJCNT_INC(addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Delete an address from the bind address list in the SCTP_bind_addr
|
||||
* structure.
|
||||
*/
|
||||
int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr)
|
||||
{
|
||||
struct list_head *pos, *temp;
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
|
||||
list_for_each_safe(pos, temp, &bp->address_list) {
|
||||
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
if (sctp_cmp_addr_exact(&addr->a, del_addr)) {
|
||||
/* Found the exact match. */
|
||||
list_del(pos);
|
||||
kfree(addr);
|
||||
SCTP_DBG_OBJCNT_DEC(addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* Create a network byte-order representation of all the addresses
|
||||
* formated as SCTP parameters.
|
||||
*
|
||||
* The second argument is the return value for the length.
|
||||
*/
|
||||
union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp,
|
||||
int *addrs_len,
|
||||
gfp_t gfp)
|
||||
{
|
||||
union sctp_params addrparms;
|
||||
union sctp_params retval;
|
||||
int addrparms_len;
|
||||
union sctp_addr_param rawaddr;
|
||||
int len;
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
struct list_head *pos;
|
||||
struct sctp_af *af;
|
||||
|
||||
addrparms_len = 0;
|
||||
len = 0;
|
||||
|
||||
/* Allocate enough memory at once. */
|
||||
list_for_each(pos, &bp->address_list) {
|
||||
len += sizeof(union sctp_addr_param);
|
||||
}
|
||||
|
||||
/* Don't even bother embedding an address if there
|
||||
* is only one.
|
||||
*/
|
||||
if (len == sizeof(union sctp_addr_param)) {
|
||||
retval.v = NULL;
|
||||
goto end_raw;
|
||||
}
|
||||
|
||||
retval.v = kmalloc(len, gfp);
|
||||
if (!retval.v)
|
||||
goto end_raw;
|
||||
|
||||
addrparms = retval;
|
||||
|
||||
list_for_each(pos, &bp->address_list) {
|
||||
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
af = sctp_get_af_specific(addr->a.v4.sin_family);
|
||||
len = af->to_addr_param(&addr->a, &rawaddr);
|
||||
memcpy(addrparms.v, &rawaddr, len);
|
||||
addrparms.v += len;
|
||||
addrparms_len += len;
|
||||
}
|
||||
|
||||
end_raw:
|
||||
*addrs_len = addrparms_len;
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create an address list out of the raw address list format (IPv4 and IPv6
|
||||
* address parameters).
|
||||
*/
|
||||
int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw_addr_list,
|
||||
int addrs_len, __u16 port, gfp_t gfp)
|
||||
{
|
||||
union sctp_addr_param *rawaddr;
|
||||
struct sctp_paramhdr *param;
|
||||
union sctp_addr addr;
|
||||
int retval = 0;
|
||||
int len;
|
||||
struct sctp_af *af;
|
||||
|
||||
/* Convert the raw address to standard address format */
|
||||
while (addrs_len) {
|
||||
param = (struct sctp_paramhdr *)raw_addr_list;
|
||||
rawaddr = (union sctp_addr_param *)raw_addr_list;
|
||||
|
||||
af = sctp_get_af_specific(param_type2af(param->type));
|
||||
if (unlikely(!af)) {
|
||||
retval = -EINVAL;
|
||||
sctp_bind_addr_clean(bp);
|
||||
break;
|
||||
}
|
||||
|
||||
af->from_addr_param(&addr, rawaddr, htons(port), 0);
|
||||
retval = sctp_add_bind_addr(bp, &addr, 1, gfp);
|
||||
if (retval) {
|
||||
/* Can't finish building the list, clean up. */
|
||||
sctp_bind_addr_clean(bp);
|
||||
break;
|
||||
}
|
||||
|
||||
len = ntohs(param->length);
|
||||
addrs_len -= len;
|
||||
raw_addr_list += len;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 2nd Level Abstractions
|
||||
********************************************************************/
|
||||
|
||||
/* Does this contain a specified address? Allow wildcarding. */
|
||||
int sctp_bind_addr_match(struct sctp_bind_addr *bp,
|
||||
const union sctp_addr *addr,
|
||||
struct sctp_sock *opt)
|
||||
{
|
||||
struct sctp_sockaddr_entry *laddr;
|
||||
struct list_head *pos;
|
||||
|
||||
list_for_each(pos, &bp->address_list) {
|
||||
laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
if (opt->pf->cmp_addr(&laddr->a, addr, opt))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find the first address in the bind address list that is not present in
|
||||
* the addrs packed array.
|
||||
*/
|
||||
union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp,
|
||||
const union sctp_addr *addrs,
|
||||
int addrcnt,
|
||||
struct sctp_sock *opt)
|
||||
{
|
||||
struct sctp_sockaddr_entry *laddr;
|
||||
union sctp_addr *addr;
|
||||
void *addr_buf;
|
||||
struct sctp_af *af;
|
||||
struct list_head *pos;
|
||||
int i;
|
||||
|
||||
list_for_each(pos, &bp->address_list) {
|
||||
laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
|
||||
addr_buf = (union sctp_addr *)addrs;
|
||||
for (i = 0; i < addrcnt; i++) {
|
||||
addr = (union sctp_addr *)addr_buf;
|
||||
af = sctp_get_af_specific(addr->v4.sin_family);
|
||||
if (!af)
|
||||
return NULL;
|
||||
|
||||
if (opt->pf->cmp_addr(&laddr->a, addr, opt))
|
||||
break;
|
||||
|
||||
addr_buf += af->sockaddr_len;
|
||||
}
|
||||
if (i == addrcnt)
|
||||
return &laddr->a;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Copy out addresses from the global local address list. */
|
||||
static int sctp_copy_one_addr(struct sctp_bind_addr *dest,
|
||||
union sctp_addr *addr,
|
||||
sctp_scope_t scope, gfp_t gfp,
|
||||
int flags)
|
||||
{
|
||||
int error = 0;
|
||||
|
||||
if (sctp_is_any(addr)) {
|
||||
error = sctp_copy_local_addr_list(dest, scope, gfp, flags);
|
||||
} else if (sctp_in_scope(addr, scope)) {
|
||||
/* Now that the address is in scope, check to see if
|
||||
* the address type is supported by local sock as
|
||||
* well as the remote peer.
|
||||
*/
|
||||
if ((((AF_INET == addr->sa.sa_family) &&
|
||||
(flags & SCTP_ADDR4_PEERSUPP))) ||
|
||||
(((AF_INET6 == addr->sa.sa_family) &&
|
||||
(flags & SCTP_ADDR6_ALLOWED) &&
|
||||
(flags & SCTP_ADDR6_PEERSUPP))))
|
||||
error = sctp_add_bind_addr(dest, addr, 1, gfp);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Is this a wildcard address? */
|
||||
int sctp_is_any(const union sctp_addr *addr)
|
||||
{
|
||||
struct sctp_af *af = sctp_get_af_specific(addr->sa.sa_family);
|
||||
if (!af)
|
||||
return 0;
|
||||
return af->is_any(addr);
|
||||
}
|
||||
|
||||
/* Is 'addr' valid for 'scope'? */
|
||||
int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
|
||||
{
|
||||
sctp_scope_t addr_scope = sctp_scope(addr);
|
||||
|
||||
/* The unusable SCTP addresses will not be considered with
|
||||
* any defined scopes.
|
||||
*/
|
||||
if (SCTP_SCOPE_UNUSABLE == addr_scope)
|
||||
return 0;
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (addr_scope <= scope)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 3rd Level Abstractions
|
||||
********************************************************************/
|
||||
|
||||
/* What is the scope of 'addr'? */
|
||||
sctp_scope_t sctp_scope(const union sctp_addr *addr)
|
||||
{
|
||||
struct sctp_af *af;
|
||||
|
||||
af = sctp_get_af_specific(addr->sa.sa_family);
|
||||
if (!af)
|
||||
return SCTP_SCOPE_UNUSABLE;
|
||||
|
||||
return af->scope((union sctp_addr *)addr);
|
||||
}
|
||||
309
net/sctp/chunk.c
Normal file
309
net/sctp/chunk.c
Normal file
@@ -0,0 +1,309 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* (C) Copyright IBM Corp. 2003, 2004
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* This file contains the code relating the the chunk abstraction.
|
||||
*
|
||||
* 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:
|
||||
* Jon Grimm <jgrimm@us.ibm.com>
|
||||
* Sridhar Samudrala <sri@us.ibm.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/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/net.h>
|
||||
#include <linux/inet.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
/* This file is mostly in anticipation of future work, but initially
|
||||
* populate with fragment tracking for an outbound message.
|
||||
*/
|
||||
|
||||
/* Initialize datamsg from memory. */
|
||||
static void sctp_datamsg_init(struct sctp_datamsg *msg)
|
||||
{
|
||||
atomic_set(&msg->refcnt, 1);
|
||||
msg->send_failed = 0;
|
||||
msg->send_error = 0;
|
||||
msg->can_abandon = 0;
|
||||
msg->expires_at = 0;
|
||||
INIT_LIST_HEAD(&msg->chunks);
|
||||
}
|
||||
|
||||
/* Allocate and initialize datamsg. */
|
||||
SCTP_STATIC struct sctp_datamsg *sctp_datamsg_new(gfp_t gfp)
|
||||
{
|
||||
struct sctp_datamsg *msg;
|
||||
msg = kmalloc(sizeof(struct sctp_datamsg), gfp);
|
||||
if (msg)
|
||||
sctp_datamsg_init(msg);
|
||||
SCTP_DBG_OBJCNT_INC(datamsg);
|
||||
return msg;
|
||||
}
|
||||
|
||||
/* Final destructruction of datamsg memory. */
|
||||
static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
|
||||
{
|
||||
struct list_head *pos, *temp;
|
||||
struct sctp_chunk *chunk;
|
||||
struct sctp_sock *sp;
|
||||
struct sctp_ulpevent *ev;
|
||||
struct sctp_association *asoc = NULL;
|
||||
int error = 0, notify;
|
||||
|
||||
/* If we failed, we may need to notify. */
|
||||
notify = msg->send_failed ? -1 : 0;
|
||||
|
||||
/* Release all references. */
|
||||
list_for_each_safe(pos, temp, &msg->chunks) {
|
||||
list_del_init(pos);
|
||||
chunk = list_entry(pos, struct sctp_chunk, frag_list);
|
||||
/* Check whether we _really_ need to notify. */
|
||||
if (notify < 0) {
|
||||
asoc = chunk->asoc;
|
||||
if (msg->send_error)
|
||||
error = msg->send_error;
|
||||
else
|
||||
error = asoc->outqueue.error;
|
||||
|
||||
sp = sctp_sk(asoc->base.sk);
|
||||
notify = sctp_ulpevent_type_enabled(SCTP_SEND_FAILED,
|
||||
&sp->subscribe);
|
||||
}
|
||||
|
||||
/* Generate a SEND FAILED event only if enabled. */
|
||||
if (notify > 0) {
|
||||
int sent;
|
||||
if (chunk->has_tsn)
|
||||
sent = SCTP_DATA_SENT;
|
||||
else
|
||||
sent = SCTP_DATA_UNSENT;
|
||||
|
||||
ev = sctp_ulpevent_make_send_failed(asoc, chunk, sent,
|
||||
error, GFP_ATOMIC);
|
||||
if (ev)
|
||||
sctp_ulpq_tail_event(&asoc->ulpq, ev);
|
||||
}
|
||||
|
||||
sctp_chunk_put(chunk);
|
||||
}
|
||||
|
||||
SCTP_DBG_OBJCNT_DEC(datamsg);
|
||||
kfree(msg);
|
||||
}
|
||||
|
||||
/* Hold a reference. */
|
||||
static void sctp_datamsg_hold(struct sctp_datamsg *msg)
|
||||
{
|
||||
atomic_inc(&msg->refcnt);
|
||||
}
|
||||
|
||||
/* Release a reference. */
|
||||
void sctp_datamsg_put(struct sctp_datamsg *msg)
|
||||
{
|
||||
if (atomic_dec_and_test(&msg->refcnt))
|
||||
sctp_datamsg_destroy(msg);
|
||||
}
|
||||
|
||||
/* Free a message. Really just give up a reference, the
|
||||
* really free happens in sctp_datamsg_destroy().
|
||||
*/
|
||||
void sctp_datamsg_free(struct sctp_datamsg *msg)
|
||||
{
|
||||
sctp_datamsg_put(msg);
|
||||
}
|
||||
|
||||
/* Hold on to all the fragments until all chunks have been sent. */
|
||||
void sctp_datamsg_track(struct sctp_chunk *chunk)
|
||||
{
|
||||
sctp_chunk_hold(chunk);
|
||||
}
|
||||
|
||||
/* Assign a chunk to this datamsg. */
|
||||
static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chunk)
|
||||
{
|
||||
sctp_datamsg_hold(msg);
|
||||
chunk->msg = msg;
|
||||
}
|
||||
|
||||
|
||||
/* A data chunk can have a maximum payload of (2^16 - 20). Break
|
||||
* down any such message into smaller chunks. Opportunistically, fragment
|
||||
* the chunks down to the current MTU constraints. We may get refragmented
|
||||
* later if the PMTU changes, but it is _much better_ to fragment immediately
|
||||
* with a reasonable guess than always doing our fragmentation on the
|
||||
* soft-interrupt.
|
||||
*/
|
||||
struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc,
|
||||
struct sctp_sndrcvinfo *sinfo,
|
||||
struct msghdr *msgh, int msg_len)
|
||||
{
|
||||
int max, whole, i, offset, over, err;
|
||||
int len, first_len;
|
||||
struct sctp_chunk *chunk;
|
||||
struct sctp_datamsg *msg;
|
||||
struct list_head *pos, *temp;
|
||||
__u8 frag;
|
||||
|
||||
msg = sctp_datamsg_new(GFP_KERNEL);
|
||||
if (!msg)
|
||||
return NULL;
|
||||
|
||||
/* Note: Calculate this outside of the loop, so that all fragments
|
||||
* have the same expiration.
|
||||
*/
|
||||
if (sinfo->sinfo_timetolive) {
|
||||
/* sinfo_timetolive is in milliseconds */
|
||||
msg->expires_at = jiffies +
|
||||
msecs_to_jiffies(sinfo->sinfo_timetolive);
|
||||
msg->can_abandon = 1;
|
||||
SCTP_DEBUG_PRINTK("%s: msg:%p expires_at: %ld jiffies:%ld\n",
|
||||
__FUNCTION__, msg, msg->expires_at, jiffies);
|
||||
}
|
||||
|
||||
max = asoc->frag_point;
|
||||
|
||||
whole = 0;
|
||||
first_len = max;
|
||||
|
||||
/* Encourage Cookie-ECHO bundling. */
|
||||
if (asoc->state < SCTP_STATE_COOKIE_ECHOED) {
|
||||
whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN);
|
||||
|
||||
/* Account for the DATA to be bundled with the COOKIE-ECHO. */
|
||||
if (whole) {
|
||||
first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN;
|
||||
msg_len -= first_len;
|
||||
whole = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* How many full sized? How many bytes leftover? */
|
||||
whole += msg_len / max;
|
||||
over = msg_len % max;
|
||||
offset = 0;
|
||||
|
||||
if ((whole > 1) || (whole && over))
|
||||
SCTP_INC_STATS_USER(SCTP_MIB_FRAGUSRMSGS);
|
||||
|
||||
/* Create chunks for all the full sized DATA chunks. */
|
||||
for (i=0, len=first_len; i < whole; i++) {
|
||||
frag = SCTP_DATA_MIDDLE_FRAG;
|
||||
|
||||
if (0 == i)
|
||||
frag |= SCTP_DATA_FIRST_FRAG;
|
||||
|
||||
if ((i == (whole - 1)) && !over)
|
||||
frag |= SCTP_DATA_LAST_FRAG;
|
||||
|
||||
chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
|
||||
|
||||
if (!chunk)
|
||||
goto errout;
|
||||
err = sctp_user_addto_chunk(chunk, offset, len, msgh->msg_iov);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
|
||||
offset += len;
|
||||
|
||||
/* Put the chunk->skb back into the form expected by send. */
|
||||
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
|
||||
- (__u8 *)chunk->skb->data);
|
||||
|
||||
sctp_datamsg_assign(msg, chunk);
|
||||
list_add_tail(&chunk->frag_list, &msg->chunks);
|
||||
|
||||
/* The first chunk, the first chunk was likely short
|
||||
* to allow bundling, so reset to full size.
|
||||
*/
|
||||
if (0 == i)
|
||||
len = max;
|
||||
}
|
||||
|
||||
/* .. now the leftover bytes. */
|
||||
if (over) {
|
||||
if (!whole)
|
||||
frag = SCTP_DATA_NOT_FRAG;
|
||||
else
|
||||
frag = SCTP_DATA_LAST_FRAG;
|
||||
|
||||
chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
|
||||
|
||||
if (!chunk)
|
||||
goto errout;
|
||||
|
||||
err = sctp_user_addto_chunk(chunk, offset, over,msgh->msg_iov);
|
||||
|
||||
/* Put the chunk->skb back into the form expected by send. */
|
||||
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
|
||||
- (__u8 *)chunk->skb->data);
|
||||
if (err < 0)
|
||||
goto errout;
|
||||
|
||||
sctp_datamsg_assign(msg, chunk);
|
||||
list_add_tail(&chunk->frag_list, &msg->chunks);
|
||||
}
|
||||
|
||||
return msg;
|
||||
|
||||
errout:
|
||||
list_for_each_safe(pos, temp, &msg->chunks) {
|
||||
list_del_init(pos);
|
||||
chunk = list_entry(pos, struct sctp_chunk, frag_list);
|
||||
sctp_chunk_free(chunk);
|
||||
}
|
||||
sctp_datamsg_free(msg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Check whether this message has expired. */
|
||||
int sctp_chunk_abandoned(struct sctp_chunk *chunk)
|
||||
{
|
||||
struct sctp_datamsg *msg = chunk->msg;
|
||||
|
||||
if (!msg->can_abandon)
|
||||
return 0;
|
||||
|
||||
if (time_after(jiffies, msg->expires_at))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This chunk (and consequently entire message) has failed in its sending. */
|
||||
void sctp_chunk_fail(struct sctp_chunk *chunk, int error)
|
||||
{
|
||||
chunk->msg->send_failed = 1;
|
||||
chunk->msg->send_error = error;
|
||||
}
|
||||
81
net/sctp/command.c
Normal file
81
net/sctp/command.c
Normal file
@@ -0,0 +1,81 @@
|
||||
/* SCTP kernel reference Implementation Copyright (C) 1999-2001
|
||||
* Cisco, Motorola, and IBM
|
||||
* Copyright 2001 La Monte H.P. Yarroll
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* These functions manipulate sctp command sequences.
|
||||
*
|
||||
* 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>
|
||||
*
|
||||
* Any bugs reported given to us we will try to fix... any fixes shared will
|
||||
* be incorporated into the next SCTP release.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
/* Initialize a block of memory as a command sequence. */
|
||||
int sctp_init_cmd_seq(sctp_cmd_seq_t *seq)
|
||||
{
|
||||
memset(seq, 0, sizeof(sctp_cmd_seq_t));
|
||||
return 1; /* We always succeed. */
|
||||
}
|
||||
|
||||
/* Add a command to a sctp_cmd_seq_t.
|
||||
* Return 0 if the command sequence is full.
|
||||
*/
|
||||
int sctp_add_cmd(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj)
|
||||
{
|
||||
if (seq->next_free_slot >= SCTP_MAX_NUM_COMMANDS)
|
||||
goto fail;
|
||||
|
||||
seq->cmds[seq->next_free_slot].verb = verb;
|
||||
seq->cmds[seq->next_free_slot++].obj = obj;
|
||||
|
||||
return 1;
|
||||
|
||||
fail:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Return the next command structure in a sctp_cmd_seq.
|
||||
* Returns NULL at the end of the sequence.
|
||||
*/
|
||||
sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq)
|
||||
{
|
||||
sctp_cmd_t *retval = NULL;
|
||||
|
||||
if (seq->next_cmd < seq->next_free_slot)
|
||||
retval = &seq->cmds[seq->next_cmd++];
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
220
net/sctp/crc32c.c
Normal file
220
net/sctp/crc32c.c
Normal file
@@ -0,0 +1,220 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* Copyright (c) 1999-2001 Motorola, Inc.
|
||||
* Copyright (c) 2001-2003 International Business Machines, Corp.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* SCTP Checksum functions
|
||||
*
|
||||
* 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:
|
||||
* Dinakaran Joseph
|
||||
* Jon Grimm <jgrimm@us.ibm.com>
|
||||
* Sridhar Samudrala <sri@us.ibm.com>
|
||||
*
|
||||
* Any bugs reported given to us we will try to fix... any fixes shared will
|
||||
* be incorporated into the next SCTP release.
|
||||
*/
|
||||
|
||||
/* The following code has been taken directly from
|
||||
* draft-ietf-tsvwg-sctpcsum-03.txt
|
||||
*
|
||||
* The code has now been modified specifically for SCTP knowledge.
|
||||
*/
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
|
||||
#define CRC32C_POLY 0x1EDC6F41
|
||||
#define CRC32C(c,d) (c=(c>>8)^crc_c[(c^(d))&0xFF])
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
/* Copyright 2001, D. Otis. Use this program, code or tables */
|
||||
/* extracted from it, as desired without restriction. */
|
||||
/* */
|
||||
/* 32 Bit Reflected CRC table generation for SCTP. */
|
||||
/* To accommodate serial byte data being shifted out least */
|
||||
/* significant bit first, the table's 32 bit words are reflected */
|
||||
/* which flips both byte and bit MS and LS positions. The CRC */
|
||||
/* is calculated MS bits first from the perspective of the serial*/
|
||||
/* stream. The x^32 term is implied and the x^0 term may also */
|
||||
/* be shown as +1. The polynomial code used is 0x1EDC6F41. */
|
||||
/* Castagnoli93 */
|
||||
/* x^32+x^28+x^27+x^26+x^25+x^23+x^22+x^20+x^19+x^18+x^14+x^13+ */
|
||||
/* x^11+x^10+x^9+x^8+x^6+x^0 */
|
||||
/* Guy Castagnoli Stefan Braeuer and Martin Herrman */
|
||||
/* "Optimization of Cyclic Redundancy-Check Codes */
|
||||
/* with 24 and 32 Parity Bits", */
|
||||
/* IEEE Transactions on Communications, Vol.41, No.6, June 1993 */
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
static const __u32 crc_c[256] = {
|
||||
0x00000000, 0xF26B8303, 0xE13B70F7, 0x1350F3F4,
|
||||
0xC79A971F, 0x35F1141C, 0x26A1E7E8, 0xD4CA64EB,
|
||||
0x8AD958CF, 0x78B2DBCC, 0x6BE22838, 0x9989AB3B,
|
||||
0x4D43CFD0, 0xBF284CD3, 0xAC78BF27, 0x5E133C24,
|
||||
0x105EC76F, 0xE235446C, 0xF165B798, 0x030E349B,
|
||||
0xD7C45070, 0x25AFD373, 0x36FF2087, 0xC494A384,
|
||||
0x9A879FA0, 0x68EC1CA3, 0x7BBCEF57, 0x89D76C54,
|
||||
0x5D1D08BF, 0xAF768BBC, 0xBC267848, 0x4E4DFB4B,
|
||||
0x20BD8EDE, 0xD2D60DDD, 0xC186FE29, 0x33ED7D2A,
|
||||
0xE72719C1, 0x154C9AC2, 0x061C6936, 0xF477EA35,
|
||||
0xAA64D611, 0x580F5512, 0x4B5FA6E6, 0xB93425E5,
|
||||
0x6DFE410E, 0x9F95C20D, 0x8CC531F9, 0x7EAEB2FA,
|
||||
0x30E349B1, 0xC288CAB2, 0xD1D83946, 0x23B3BA45,
|
||||
0xF779DEAE, 0x05125DAD, 0x1642AE59, 0xE4292D5A,
|
||||
0xBA3A117E, 0x4851927D, 0x5B016189, 0xA96AE28A,
|
||||
0x7DA08661, 0x8FCB0562, 0x9C9BF696, 0x6EF07595,
|
||||
0x417B1DBC, 0xB3109EBF, 0xA0406D4B, 0x522BEE48,
|
||||
0x86E18AA3, 0x748A09A0, 0x67DAFA54, 0x95B17957,
|
||||
0xCBA24573, 0x39C9C670, 0x2A993584, 0xD8F2B687,
|
||||
0x0C38D26C, 0xFE53516F, 0xED03A29B, 0x1F682198,
|
||||
0x5125DAD3, 0xA34E59D0, 0xB01EAA24, 0x42752927,
|
||||
0x96BF4DCC, 0x64D4CECF, 0x77843D3B, 0x85EFBE38,
|
||||
0xDBFC821C, 0x2997011F, 0x3AC7F2EB, 0xC8AC71E8,
|
||||
0x1C661503, 0xEE0D9600, 0xFD5D65F4, 0x0F36E6F7,
|
||||
0x61C69362, 0x93AD1061, 0x80FDE395, 0x72966096,
|
||||
0xA65C047D, 0x5437877E, 0x4767748A, 0xB50CF789,
|
||||
0xEB1FCBAD, 0x197448AE, 0x0A24BB5A, 0xF84F3859,
|
||||
0x2C855CB2, 0xDEEEDFB1, 0xCDBE2C45, 0x3FD5AF46,
|
||||
0x7198540D, 0x83F3D70E, 0x90A324FA, 0x62C8A7F9,
|
||||
0xB602C312, 0x44694011, 0x5739B3E5, 0xA55230E6,
|
||||
0xFB410CC2, 0x092A8FC1, 0x1A7A7C35, 0xE811FF36,
|
||||
0x3CDB9BDD, 0xCEB018DE, 0xDDE0EB2A, 0x2F8B6829,
|
||||
0x82F63B78, 0x709DB87B, 0x63CD4B8F, 0x91A6C88C,
|
||||
0x456CAC67, 0xB7072F64, 0xA457DC90, 0x563C5F93,
|
||||
0x082F63B7, 0xFA44E0B4, 0xE9141340, 0x1B7F9043,
|
||||
0xCFB5F4A8, 0x3DDE77AB, 0x2E8E845F, 0xDCE5075C,
|
||||
0x92A8FC17, 0x60C37F14, 0x73938CE0, 0x81F80FE3,
|
||||
0x55326B08, 0xA759E80B, 0xB4091BFF, 0x466298FC,
|
||||
0x1871A4D8, 0xEA1A27DB, 0xF94AD42F, 0x0B21572C,
|
||||
0xDFEB33C7, 0x2D80B0C4, 0x3ED04330, 0xCCBBC033,
|
||||
0xA24BB5A6, 0x502036A5, 0x4370C551, 0xB11B4652,
|
||||
0x65D122B9, 0x97BAA1BA, 0x84EA524E, 0x7681D14D,
|
||||
0x2892ED69, 0xDAF96E6A, 0xC9A99D9E, 0x3BC21E9D,
|
||||
0xEF087A76, 0x1D63F975, 0x0E330A81, 0xFC588982,
|
||||
0xB21572C9, 0x407EF1CA, 0x532E023E, 0xA145813D,
|
||||
0x758FE5D6, 0x87E466D5, 0x94B49521, 0x66DF1622,
|
||||
0x38CC2A06, 0xCAA7A905, 0xD9F75AF1, 0x2B9CD9F2,
|
||||
0xFF56BD19, 0x0D3D3E1A, 0x1E6DCDEE, 0xEC064EED,
|
||||
0xC38D26C4, 0x31E6A5C7, 0x22B65633, 0xD0DDD530,
|
||||
0x0417B1DB, 0xF67C32D8, 0xE52CC12C, 0x1747422F,
|
||||
0x49547E0B, 0xBB3FFD08, 0xA86F0EFC, 0x5A048DFF,
|
||||
0x8ECEE914, 0x7CA56A17, 0x6FF599E3, 0x9D9E1AE0,
|
||||
0xD3D3E1AB, 0x21B862A8, 0x32E8915C, 0xC083125F,
|
||||
0x144976B4, 0xE622F5B7, 0xF5720643, 0x07198540,
|
||||
0x590AB964, 0xAB613A67, 0xB831C993, 0x4A5A4A90,
|
||||
0x9E902E7B, 0x6CFBAD78, 0x7FAB5E8C, 0x8DC0DD8F,
|
||||
0xE330A81A, 0x115B2B19, 0x020BD8ED, 0xF0605BEE,
|
||||
0x24AA3F05, 0xD6C1BC06, 0xC5914FF2, 0x37FACCF1,
|
||||
0x69E9F0D5, 0x9B8273D6, 0x88D28022, 0x7AB90321,
|
||||
0xAE7367CA, 0x5C18E4C9, 0x4F48173D, 0xBD23943E,
|
||||
0xF36E6F75, 0x0105EC76, 0x12551F82, 0xE03E9C81,
|
||||
0x34F4F86A, 0xC69F7B69, 0xD5CF889D, 0x27A40B9E,
|
||||
0x79B737BA, 0x8BDCB4B9, 0x988C474D, 0x6AE7C44E,
|
||||
0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
|
||||
};
|
||||
|
||||
__u32 sctp_start_cksum(__u8 *buffer, __u16 length)
|
||||
{
|
||||
__u32 crc32 = ~(__u32) 0;
|
||||
__u32 i;
|
||||
|
||||
/* Optimize this routine to be SCTP specific, knowing how
|
||||
* to skip the checksum field of the SCTP header.
|
||||
*/
|
||||
|
||||
/* Calculate CRC up to the checksum. */
|
||||
for (i = 0; i < (sizeof(struct sctphdr) - sizeof(__u32)); i++)
|
||||
CRC32C(crc32, buffer[i]);
|
||||
|
||||
/* Skip checksum field of the header. */
|
||||
for (i = 0; i < sizeof(__u32); i++)
|
||||
CRC32C(crc32, 0);
|
||||
|
||||
/* Calculate the rest of the CRC. */
|
||||
for (i = sizeof(struct sctphdr); i < length ; i++)
|
||||
CRC32C(crc32, buffer[i]);
|
||||
|
||||
return crc32;
|
||||
}
|
||||
|
||||
__u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32)
|
||||
{
|
||||
__u32 i;
|
||||
|
||||
for (i = 0; i < length ; i++)
|
||||
CRC32C(crc32, buffer[i]);
|
||||
|
||||
return crc32;
|
||||
}
|
||||
|
||||
__u32 sctp_update_copy_cksum(__u8 *to, __u8 *from, __u16 length, __u32 crc32)
|
||||
{
|
||||
__u32 i;
|
||||
__u32 *_to = (__u32 *)to;
|
||||
__u32 *_from = (__u32 *)from;
|
||||
|
||||
for (i = 0; i < (length/4); i++) {
|
||||
_to[i] = _from[i];
|
||||
CRC32C(crc32, from[i*4]);
|
||||
CRC32C(crc32, from[i*4+1]);
|
||||
CRC32C(crc32, from[i*4+2]);
|
||||
CRC32C(crc32, from[i*4+3]);
|
||||
}
|
||||
|
||||
return crc32;
|
||||
}
|
||||
|
||||
__u32 sctp_end_cksum(__u32 crc32)
|
||||
{
|
||||
__u32 result;
|
||||
__u8 byte0, byte1, byte2, byte3;
|
||||
|
||||
result = ~crc32;
|
||||
|
||||
/* result now holds the negated polynomial remainder;
|
||||
* since the table and algorithm is "reflected" [williams95].
|
||||
* That is, result has the same value as if we mapped the message
|
||||
* to a polyomial, computed the host-bit-order polynomial
|
||||
* remainder, performed final negation, then did an end-for-end
|
||||
* bit-reversal.
|
||||
* Note that a 32-bit bit-reversal is identical to four inplace
|
||||
* 8-bit reversals followed by an end-for-end byteswap.
|
||||
* In other words, the bytes of each bit are in the right order,
|
||||
* but the bytes have been byteswapped. So we now do an explicit
|
||||
* byteswap. On a little-endian machine, this byteswap and
|
||||
* the final ntohl cancel out and could be elided.
|
||||
*/
|
||||
byte0 = result & 0xff;
|
||||
byte1 = (result>>8) & 0xff;
|
||||
byte2 = (result>>16) & 0xff;
|
||||
byte3 = (result>>24) & 0xff;
|
||||
|
||||
crc32 = ((byte0 << 24) |
|
||||
(byte1 << 16) |
|
||||
(byte2 << 8) |
|
||||
byte3);
|
||||
return crc32;
|
||||
}
|
||||
191
net/sctp/debug.c
Normal file
191
net/sctp/debug.c
Normal file
@@ -0,0 +1,191 @@
|
||||
/* 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.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* This file is part of the implementation of the add-IP extension,
|
||||
* based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001,
|
||||
* for the SCTP kernel reference Implementation.
|
||||
*
|
||||
* This file converts numerical ID value to alphabetical names for SCTP
|
||||
* terms such as chunk type, parameter time, event type, etc.
|
||||
*
|
||||
* 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>
|
||||
* Xingang Guo <xingang.guo@intel.com>
|
||||
* Jon Grimm <jgrimm@us.ibm.com>
|
||||
* Daisy Chang <daisyc@us.ibm.com>
|
||||
* Sridhar Samudrala <sri@us.ibm.com>
|
||||
*
|
||||
* Any bugs reported given to us we will try to fix... any fixes shared will
|
||||
* be incorporated into the next SCTP release.
|
||||
*/
|
||||
|
||||
#include <net/sctp/sctp.h>
|
||||
|
||||
#if SCTP_DEBUG
|
||||
int sctp_debug_flag = 1; /* Initially enable DEBUG */
|
||||
#endif /* SCTP_DEBUG */
|
||||
|
||||
/* These are printable forms of Chunk ID's from section 3.1. */
|
||||
static const char *sctp_cid_tbl[SCTP_NUM_BASE_CHUNK_TYPES] = {
|
||||
"DATA",
|
||||
"INIT",
|
||||
"INIT_ACK",
|
||||
"SACK",
|
||||
"HEARTBEAT",
|
||||
"HEARTBEAT_ACK",
|
||||
"ABORT",
|
||||
"SHUTDOWN",
|
||||
"SHUTDOWN_ACK",
|
||||
"ERROR",
|
||||
"COOKIE_ECHO",
|
||||
"COOKIE_ACK",
|
||||
"ECN_ECNE",
|
||||
"ECN_CWR",
|
||||
"SHUTDOWN_COMPLETE",
|
||||
};
|
||||
|
||||
/* Lookup "chunk type" debug name. */
|
||||
const char *sctp_cname(const sctp_subtype_t cid)
|
||||
{
|
||||
if (cid.chunk < 0)
|
||||
return "illegal chunk id";
|
||||
if (cid.chunk <= SCTP_CID_BASE_MAX)
|
||||
return sctp_cid_tbl[cid.chunk];
|
||||
|
||||
switch (cid.chunk) {
|
||||
case SCTP_CID_ASCONF:
|
||||
return "ASCONF";
|
||||
|
||||
case SCTP_CID_ASCONF_ACK:
|
||||
return "ASCONF_ACK";
|
||||
|
||||
case SCTP_CID_FWD_TSN:
|
||||
return "FWD_TSN";
|
||||
|
||||
default:
|
||||
return "unknown chunk";
|
||||
};
|
||||
return "unknown chunk";
|
||||
}
|
||||
|
||||
/* These are printable forms of the states. */
|
||||
const char *sctp_state_tbl[SCTP_STATE_NUM_STATES] = {
|
||||
"STATE_EMPTY",
|
||||
"STATE_CLOSED",
|
||||
"STATE_COOKIE_WAIT",
|
||||
"STATE_COOKIE_ECHOED",
|
||||
"STATE_ESTABLISHED",
|
||||
"STATE_SHUTDOWN_PENDING",
|
||||
"STATE_SHUTDOWN_SENT",
|
||||
"STATE_SHUTDOWN_RECEIVED",
|
||||
"STATE_SHUTDOWN_ACK_SENT",
|
||||
};
|
||||
|
||||
/* Events that could change the state of an association. */
|
||||
const char *sctp_evttype_tbl[] = {
|
||||
"EVENT_T_unknown",
|
||||
"EVENT_T_CHUNK",
|
||||
"EVENT_T_TIMEOUT",
|
||||
"EVENT_T_OTHER",
|
||||
"EVENT_T_PRIMITIVE"
|
||||
};
|
||||
|
||||
/* Return value of a state function */
|
||||
const char *sctp_status_tbl[] = {
|
||||
"DISPOSITION_DISCARD",
|
||||
"DISPOSITION_CONSUME",
|
||||
"DISPOSITION_NOMEM",
|
||||
"DISPOSITION_DELETE_TCB",
|
||||
"DISPOSITION_ABORT",
|
||||
"DISPOSITION_VIOLATION",
|
||||
"DISPOSITION_NOT_IMPL",
|
||||
"DISPOSITION_ERROR",
|
||||
"DISPOSITION_BUG"
|
||||
};
|
||||
|
||||
/* Printable forms of primitives */
|
||||
static const char *sctp_primitive_tbl[SCTP_NUM_PRIMITIVE_TYPES] = {
|
||||
"PRIMITIVE_ASSOCIATE",
|
||||
"PRIMITIVE_SHUTDOWN",
|
||||
"PRIMITIVE_ABORT",
|
||||
"PRIMITIVE_SEND",
|
||||
"PRIMITIVE_REQUESTHEARTBEAT",
|
||||
};
|
||||
|
||||
/* Lookup primitive debug name. */
|
||||
const char *sctp_pname(const sctp_subtype_t id)
|
||||
{
|
||||
if (id.primitive < 0)
|
||||
return "illegal primitive";
|
||||
if (id.primitive <= SCTP_EVENT_PRIMITIVE_MAX)
|
||||
return sctp_primitive_tbl[id.primitive];
|
||||
return "unknown_primitive";
|
||||
}
|
||||
|
||||
static const char *sctp_other_tbl[] = {
|
||||
"NO_PENDING_TSN",
|
||||
"ICMP_PROTO_UNREACH",
|
||||
};
|
||||
|
||||
/* Lookup "other" debug name. */
|
||||
const char *sctp_oname(const sctp_subtype_t id)
|
||||
{
|
||||
if (id.other < 0)
|
||||
return "illegal 'other' event";
|
||||
if (id.other <= SCTP_EVENT_OTHER_MAX)
|
||||
return sctp_other_tbl[id.other];
|
||||
return "unknown 'other' event";
|
||||
}
|
||||
|
||||
static const char *sctp_timer_tbl[] = {
|
||||
"TIMEOUT_NONE",
|
||||
"TIMEOUT_T1_COOKIE",
|
||||
"TIMEOUT_T1_INIT",
|
||||
"TIMEOUT_T2_SHUTDOWN",
|
||||
"TIMEOUT_T3_RTX",
|
||||
"TIMEOUT_T4_RTO",
|
||||
"TIMEOUT_T5_SHUTDOWN_GUARD",
|
||||
"TIMEOUT_HEARTBEAT",
|
||||
"TIMEOUT_SACK",
|
||||
"TIMEOUT_AUTOCLOSE",
|
||||
};
|
||||
|
||||
/* Lookup timer debug name. */
|
||||
const char *sctp_tname(const sctp_subtype_t id)
|
||||
{
|
||||
if (id.timeout < 0)
|
||||
return "illegal 'timer' event";
|
||||
if (id.timeout <= SCTP_EVENT_TIMEOUT_MAX)
|
||||
return sctp_timer_tbl[id.timeout];
|
||||
return "unknown_timer";
|
||||
}
|
||||
382
net/sctp/endpointola.c
Normal file
382
net/sctp/endpointola.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* Copyright (c) 1999-2000 Cisco, Inc.
|
||||
* Copyright (c) 1999-2001 Motorola, Inc.
|
||||
* Copyright (c) 2001-2002 International Business Machines, Corp.
|
||||
* 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
|
||||
*
|
||||
* This abstraction represents an SCTP endpoint.
|
||||
*
|
||||
* This file is part of the implementation of the add-IP extension,
|
||||
* based on <draft-ietf-tsvwg-addip-sctp-02.txt> June 29, 2001,
|
||||
* for the SCTP kernel reference Implementation.
|
||||
*
|
||||
* 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@austin.ibm.com>
|
||||
* Daisy Chang <daisyc@us.ibm.com>
|
||||
* Dajiang Zhang <dajiang.zhang@nokia.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/types.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/in.h>
|
||||
#include <linux/random.h> /* get_random_bytes() */
|
||||
#include <linux/crypto.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/ipv6.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
/* Forward declarations for internal helpers. */
|
||||
static void sctp_endpoint_bh_rcv(struct work_struct *work);
|
||||
|
||||
/*
|
||||
* Initialize the base fields of the endpoint structure.
|
||||
*/
|
||||
static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
|
||||
struct sock *sk,
|
||||
gfp_t gfp)
|
||||
{
|
||||
memset(ep, 0, sizeof(struct sctp_endpoint));
|
||||
|
||||
ep->digest = kzalloc(SCTP_SIGNATURE_SIZE, gfp);
|
||||
if (!ep->digest)
|
||||
return NULL;
|
||||
|
||||
/* Initialize the base structure. */
|
||||
/* What type of endpoint are we? */
|
||||
ep->base.type = SCTP_EP_TYPE_SOCKET;
|
||||
|
||||
/* Initialize the basic object fields. */
|
||||
atomic_set(&ep->base.refcnt, 1);
|
||||
ep->base.dead = 0;
|
||||
ep->base.malloced = 1;
|
||||
|
||||
/* Create an input queue. */
|
||||
sctp_inq_init(&ep->base.inqueue);
|
||||
|
||||
/* Set its top-half handler */
|
||||
sctp_inq_set_th_handler(&ep->base.inqueue, sctp_endpoint_bh_rcv);
|
||||
|
||||
/* Initialize the bind addr area */
|
||||
sctp_bind_addr_init(&ep->base.bind_addr, 0);
|
||||
rwlock_init(&ep->base.addr_lock);
|
||||
|
||||
/* Remember who we are attached to. */
|
||||
ep->base.sk = sk;
|
||||
sock_hold(ep->base.sk);
|
||||
|
||||
/* Create the lists of associations. */
|
||||
INIT_LIST_HEAD(&ep->asocs);
|
||||
|
||||
/* Use SCTP specific send buffer space queues. */
|
||||
ep->sndbuf_policy = sctp_sndbuf_policy;
|
||||
sk->sk_write_space = sctp_write_space;
|
||||
sock_set_flag(sk, SOCK_USE_WRITE_QUEUE);
|
||||
|
||||
/* Get the receive buffer policy for this endpoint */
|
||||
ep->rcvbuf_policy = sctp_rcvbuf_policy;
|
||||
|
||||
/* Initialize the secret key used with cookie. */
|
||||
get_random_bytes(&ep->secret_key[0], SCTP_SECRET_SIZE);
|
||||
ep->last_key = ep->current_key = 0;
|
||||
ep->key_changed_at = jiffies;
|
||||
|
||||
return ep;
|
||||
}
|
||||
|
||||
/* Create a sctp_endpoint with all that boring stuff initialized.
|
||||
* Returns NULL if there isn't enough memory.
|
||||
*/
|
||||
struct sctp_endpoint *sctp_endpoint_new(struct sock *sk, gfp_t gfp)
|
||||
{
|
||||
struct sctp_endpoint *ep;
|
||||
|
||||
/* Build a local endpoint. */
|
||||
ep = t_new(struct sctp_endpoint, gfp);
|
||||
if (!ep)
|
||||
goto fail;
|
||||
if (!sctp_endpoint_init(ep, sk, gfp))
|
||||
goto fail_init;
|
||||
ep->base.malloced = 1;
|
||||
SCTP_DBG_OBJCNT_INC(ep);
|
||||
return ep;
|
||||
|
||||
fail_init:
|
||||
kfree(ep);
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Add an association to an endpoint. */
|
||||
void sctp_endpoint_add_asoc(struct sctp_endpoint *ep,
|
||||
struct sctp_association *asoc)
|
||||
{
|
||||
struct sock *sk = ep->base.sk;
|
||||
|
||||
/* If this is a temporary association, don't bother
|
||||
* since we'll be removing it shortly and don't
|
||||
* want anyone to find it anyway.
|
||||
*/
|
||||
if (asoc->temp)
|
||||
return;
|
||||
|
||||
/* Now just add it to our list of asocs */
|
||||
list_add_tail(&asoc->asocs, &ep->asocs);
|
||||
|
||||
/* Increment the backlog value for a TCP-style listening socket. */
|
||||
if (sctp_style(sk, TCP) && sctp_sstate(sk, LISTENING))
|
||||
sk->sk_ack_backlog++;
|
||||
}
|
||||
|
||||
/* Free the endpoint structure. Delay cleanup until
|
||||
* all users have released their reference count on this structure.
|
||||
*/
|
||||
void sctp_endpoint_free(struct sctp_endpoint *ep)
|
||||
{
|
||||
ep->base.dead = 1;
|
||||
|
||||
ep->base.sk->sk_state = SCTP_SS_CLOSED;
|
||||
|
||||
/* Unlink this endpoint, so we can't find it again! */
|
||||
sctp_unhash_endpoint(ep);
|
||||
|
||||
sctp_endpoint_put(ep);
|
||||
}
|
||||
|
||||
/* Final destructor for endpoint. */
|
||||
static void sctp_endpoint_destroy(struct sctp_endpoint *ep)
|
||||
{
|
||||
SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return);
|
||||
|
||||
/* Free up the HMAC transform. */
|
||||
crypto_free_hash(sctp_sk(ep->base.sk)->hmac);
|
||||
|
||||
/* Free the digest buffer */
|
||||
kfree(ep->digest);
|
||||
|
||||
/* Cleanup. */
|
||||
sctp_inq_free(&ep->base.inqueue);
|
||||
sctp_bind_addr_free(&ep->base.bind_addr);
|
||||
|
||||
/* Remove and free the port */
|
||||
if (sctp_sk(ep->base.sk)->bind_hash)
|
||||
sctp_put_port(ep->base.sk);
|
||||
|
||||
/* Give up our hold on the sock. */
|
||||
if (ep->base.sk)
|
||||
sock_put(ep->base.sk);
|
||||
|
||||
/* Finally, free up our memory. */
|
||||
if (ep->base.malloced) {
|
||||
kfree(ep);
|
||||
SCTP_DBG_OBJCNT_DEC(ep);
|
||||
}
|
||||
}
|
||||
|
||||
/* Hold a reference to an endpoint. */
|
||||
void sctp_endpoint_hold(struct sctp_endpoint *ep)
|
||||
{
|
||||
atomic_inc(&ep->base.refcnt);
|
||||
}
|
||||
|
||||
/* Release a reference to an endpoint and clean up if there are
|
||||
* no more references.
|
||||
*/
|
||||
void sctp_endpoint_put(struct sctp_endpoint *ep)
|
||||
{
|
||||
if (atomic_dec_and_test(&ep->base.refcnt))
|
||||
sctp_endpoint_destroy(ep);
|
||||
}
|
||||
|
||||
/* Is this the endpoint we are looking for? */
|
||||
struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
|
||||
const union sctp_addr *laddr)
|
||||
{
|
||||
struct sctp_endpoint *retval;
|
||||
|
||||
sctp_read_lock(&ep->base.addr_lock);
|
||||
if (htons(ep->base.bind_addr.port) == laddr->v4.sin_port) {
|
||||
if (sctp_bind_addr_match(&ep->base.bind_addr, laddr,
|
||||
sctp_sk(ep->base.sk))) {
|
||||
retval = ep;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
retval = NULL;
|
||||
|
||||
out:
|
||||
sctp_read_unlock(&ep->base.addr_lock);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Find the association that goes with this chunk.
|
||||
* We do a linear search of the associations for this endpoint.
|
||||
* We return the matching transport address too.
|
||||
*/
|
||||
static struct sctp_association *__sctp_endpoint_lookup_assoc(
|
||||
const struct sctp_endpoint *ep,
|
||||
const union sctp_addr *paddr,
|
||||
struct sctp_transport **transport)
|
||||
{
|
||||
int rport;
|
||||
struct sctp_association *asoc;
|
||||
struct list_head *pos;
|
||||
|
||||
rport = ntohs(paddr->v4.sin_port);
|
||||
|
||||
list_for_each(pos, &ep->asocs) {
|
||||
asoc = list_entry(pos, struct sctp_association, asocs);
|
||||
if (rport == asoc->peer.port) {
|
||||
sctp_read_lock(&asoc->base.addr_lock);
|
||||
*transport = sctp_assoc_lookup_paddr(asoc, paddr);
|
||||
sctp_read_unlock(&asoc->base.addr_lock);
|
||||
|
||||
if (*transport)
|
||||
return asoc;
|
||||
}
|
||||
}
|
||||
|
||||
*transport = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lookup association on an endpoint based on a peer address. BH-safe. */
|
||||
struct sctp_association *sctp_endpoint_lookup_assoc(
|
||||
const struct sctp_endpoint *ep,
|
||||
const union sctp_addr *paddr,
|
||||
struct sctp_transport **transport)
|
||||
{
|
||||
struct sctp_association *asoc;
|
||||
|
||||
sctp_local_bh_disable();
|
||||
asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport);
|
||||
sctp_local_bh_enable();
|
||||
|
||||
return asoc;
|
||||
}
|
||||
|
||||
/* Look for any peeled off association from the endpoint that matches the
|
||||
* given peer address.
|
||||
*/
|
||||
int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep,
|
||||
const union sctp_addr *paddr)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct sctp_sockaddr_entry *addr;
|
||||
struct sctp_bind_addr *bp;
|
||||
|
||||
sctp_read_lock(&ep->base.addr_lock);
|
||||
bp = &ep->base.bind_addr;
|
||||
list_for_each(pos, &bp->address_list) {
|
||||
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
if (sctp_has_association(&addr->a, paddr)) {
|
||||
sctp_read_unlock(&ep->base.addr_lock);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
sctp_read_unlock(&ep->base.addr_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Do delayed input processing. This is scheduled by sctp_rcv().
|
||||
* This may be called on BH or task time.
|
||||
*/
|
||||
static void sctp_endpoint_bh_rcv(struct work_struct *work)
|
||||
{
|
||||
struct sctp_endpoint *ep =
|
||||
container_of(work, struct sctp_endpoint,
|
||||
base.inqueue.immediate);
|
||||
struct sctp_association *asoc;
|
||||
struct sock *sk;
|
||||
struct sctp_transport *transport;
|
||||
struct sctp_chunk *chunk;
|
||||
struct sctp_inq *inqueue;
|
||||
sctp_subtype_t subtype;
|
||||
sctp_state_t state;
|
||||
int error = 0;
|
||||
|
||||
if (ep->base.dead)
|
||||
return;
|
||||
|
||||
asoc = NULL;
|
||||
inqueue = &ep->base.inqueue;
|
||||
sk = ep->base.sk;
|
||||
|
||||
while (NULL != (chunk = sctp_inq_pop(inqueue))) {
|
||||
subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type);
|
||||
|
||||
/* We might have grown an association since last we
|
||||
* looked, so try again.
|
||||
*
|
||||
* This happens when we've just processed our
|
||||
* COOKIE-ECHO chunk.
|
||||
*/
|
||||
if (NULL == chunk->asoc) {
|
||||
asoc = sctp_endpoint_lookup_assoc(ep,
|
||||
sctp_source(chunk),
|
||||
&transport);
|
||||
chunk->asoc = asoc;
|
||||
chunk->transport = transport;
|
||||
}
|
||||
|
||||
state = asoc ? asoc->state : SCTP_STATE_CLOSED;
|
||||
|
||||
/* Remember where the last DATA chunk came from so we
|
||||
* know where to send the SACK.
|
||||
*/
|
||||
if (asoc && sctp_chunk_is_data(chunk))
|
||||
asoc->peer.last_data_from = chunk->transport;
|
||||
else
|
||||
SCTP_INC_STATS(SCTP_MIB_INCTRLCHUNKS);
|
||||
|
||||
if (chunk->transport)
|
||||
chunk->transport->last_time_heard = jiffies;
|
||||
|
||||
error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state,
|
||||
ep, asoc, chunk, GFP_ATOMIC);
|
||||
|
||||
if (error && chunk)
|
||||
chunk->pdiscard = 1;
|
||||
|
||||
/* Check to see if the endpoint is freed in response to
|
||||
* the incoming chunk. If so, get out of the while loop.
|
||||
*/
|
||||
if (!sctp_sk(sk)->ep)
|
||||
break;
|
||||
}
|
||||
}
|
||||
988
net/sctp/input.c
Normal file
988
net/sctp/input.c
Normal file
@@ -0,0 +1,988 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* Copyright (c) 1999-2000 Cisco, Inc.
|
||||
* Copyright (c) 1999-2001 Motorola, Inc.
|
||||
* Copyright (c) 2001-2003 International Business Machines, Corp.
|
||||
* 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
|
||||
*
|
||||
* These functions handle all input from the IP layer into SCTP.
|
||||
*
|
||||
* 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>
|
||||
* Xingang Guo <xingang.guo@intel.com>
|
||||
* Jon Grimm <jgrimm@us.ibm.com>
|
||||
* Hui Huang <hui.huang@nokia.com>
|
||||
* Daisy Chang <daisyc@us.ibm.com>
|
||||
* Sridhar Samudrala <sri@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/types.h>
|
||||
#include <linux/list.h> /* For struct list_head */
|
||||
#include <linux/socket.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/time.h> /* For struct timeval */
|
||||
#include <net/ip.h>
|
||||
#include <net/icmp.h>
|
||||
#include <net/snmp.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/xfrm.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
/* Forward declarations for internal helpers. */
|
||||
static int sctp_rcv_ootb(struct sk_buff *);
|
||||
static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb,
|
||||
const union sctp_addr *laddr,
|
||||
const union sctp_addr *paddr,
|
||||
struct sctp_transport **transportp);
|
||||
static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr);
|
||||
static struct sctp_association *__sctp_lookup_association(
|
||||
const union sctp_addr *local,
|
||||
const union sctp_addr *peer,
|
||||
struct sctp_transport **pt);
|
||||
|
||||
static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb);
|
||||
|
||||
|
||||
/* Calculate the SCTP checksum of an SCTP packet. */
|
||||
static inline int sctp_rcv_checksum(struct sk_buff *skb)
|
||||
{
|
||||
struct sctphdr *sh;
|
||||
__u32 cmp, val;
|
||||
struct sk_buff *list = skb_shinfo(skb)->frag_list;
|
||||
|
||||
sh = (struct sctphdr *) skb->h.raw;
|
||||
cmp = ntohl(sh->checksum);
|
||||
|
||||
val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb));
|
||||
|
||||
for (; list; list = list->next)
|
||||
val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list),
|
||||
val);
|
||||
|
||||
val = sctp_end_cksum(val);
|
||||
|
||||
if (val != cmp) {
|
||||
/* CRC failure, dump it. */
|
||||
SCTP_INC_STATS_BH(SCTP_MIB_CHECKSUMERRORS);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sctp_input_cb {
|
||||
union {
|
||||
struct inet_skb_parm h4;
|
||||
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
|
||||
struct inet6_skb_parm h6;
|
||||
#endif
|
||||
} header;
|
||||
struct sctp_chunk *chunk;
|
||||
};
|
||||
#define SCTP_INPUT_CB(__skb) ((struct sctp_input_cb *)&((__skb)->cb[0]))
|
||||
|
||||
/*
|
||||
* This is the routine which IP calls when receiving an SCTP packet.
|
||||
*/
|
||||
int sctp_rcv(struct sk_buff *skb)
|
||||
{
|
||||
struct sock *sk;
|
||||
struct sctp_association *asoc;
|
||||
struct sctp_endpoint *ep = NULL;
|
||||
struct sctp_ep_common *rcvr;
|
||||
struct sctp_transport *transport = NULL;
|
||||
struct sctp_chunk *chunk;
|
||||
struct sctphdr *sh;
|
||||
union sctp_addr src;
|
||||
union sctp_addr dest;
|
||||
int family;
|
||||
struct sctp_af *af;
|
||||
|
||||
if (skb->pkt_type!=PACKET_HOST)
|
||||
goto discard_it;
|
||||
|
||||
SCTP_INC_STATS_BH(SCTP_MIB_INSCTPPACKS);
|
||||
|
||||
if (skb_linearize(skb))
|
||||
goto discard_it;
|
||||
|
||||
sh = (struct sctphdr *) skb->h.raw;
|
||||
|
||||
/* Pull up the IP and SCTP headers. */
|
||||
__skb_pull(skb, skb->h.raw - skb->data);
|
||||
if (skb->len < sizeof(struct sctphdr))
|
||||
goto discard_it;
|
||||
if ((skb->ip_summed != CHECKSUM_UNNECESSARY) &&
|
||||
(sctp_rcv_checksum(skb) < 0))
|
||||
goto discard_it;
|
||||
|
||||
skb_pull(skb, sizeof(struct sctphdr));
|
||||
|
||||
/* Make sure we at least have chunk headers worth of data left. */
|
||||
if (skb->len < sizeof(struct sctp_chunkhdr))
|
||||
goto discard_it;
|
||||
|
||||
family = ipver2af(skb->nh.iph->version);
|
||||
af = sctp_get_af_specific(family);
|
||||
if (unlikely(!af))
|
||||
goto discard_it;
|
||||
|
||||
/* Initialize local addresses for lookups. */
|
||||
af->from_skb(&src, skb, 1);
|
||||
af->from_skb(&dest, skb, 0);
|
||||
|
||||
/* If the packet is to or from a non-unicast address,
|
||||
* silently discard the packet.
|
||||
*
|
||||
* This is not clearly defined in the RFC except in section
|
||||
* 8.4 - OOTB handling. However, based on the book "Stream Control
|
||||
* Transmission Protocol" 2.1, "It is important to note that the
|
||||
* IP address of an SCTP transport address must be a routable
|
||||
* unicast address. In other words, IP multicast addresses and
|
||||
* IP broadcast addresses cannot be used in an SCTP transport
|
||||
* address."
|
||||
*/
|
||||
if (!af->addr_valid(&src, NULL, skb) ||
|
||||
!af->addr_valid(&dest, NULL, skb))
|
||||
goto discard_it;
|
||||
|
||||
asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport);
|
||||
|
||||
if (!asoc)
|
||||
ep = __sctp_rcv_lookup_endpoint(&dest);
|
||||
|
||||
/* Retrieve the common input handling substructure. */
|
||||
rcvr = asoc ? &asoc->base : &ep->base;
|
||||
sk = rcvr->sk;
|
||||
|
||||
/*
|
||||
* If a frame arrives on an interface and the receiving socket is
|
||||
* bound to another interface, via SO_BINDTODEVICE, treat it as OOTB
|
||||
*/
|
||||
if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb)))
|
||||
{
|
||||
if (asoc) {
|
||||
sctp_association_put(asoc);
|
||||
asoc = NULL;
|
||||
} else {
|
||||
sctp_endpoint_put(ep);
|
||||
ep = NULL;
|
||||
}
|
||||
sk = sctp_get_ctl_sock();
|
||||
ep = sctp_sk(sk)->ep;
|
||||
sctp_endpoint_hold(ep);
|
||||
rcvr = &ep->base;
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 2960, 8.4 - Handle "Out of the blue" Packets.
|
||||
* An SCTP packet is called an "out of the blue" (OOTB)
|
||||
* packet if it is correctly formed, i.e., passed the
|
||||
* receiver's checksum check, but the receiver is not
|
||||
* able to identify the association to which this
|
||||
* packet belongs.
|
||||
*/
|
||||
if (!asoc) {
|
||||
if (sctp_rcv_ootb(skb)) {
|
||||
SCTP_INC_STATS_BH(SCTP_MIB_OUTOFBLUES);
|
||||
goto discard_release;
|
||||
}
|
||||
}
|
||||
|
||||
if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb, family))
|
||||
goto discard_release;
|
||||
nf_reset(skb);
|
||||
|
||||
if (sk_filter(sk, skb))
|
||||
goto discard_release;
|
||||
|
||||
/* Create an SCTP packet structure. */
|
||||
chunk = sctp_chunkify(skb, asoc, sk);
|
||||
if (!chunk)
|
||||
goto discard_release;
|
||||
SCTP_INPUT_CB(skb)->chunk = chunk;
|
||||
|
||||
/* Remember what endpoint is to handle this packet. */
|
||||
chunk->rcvr = rcvr;
|
||||
|
||||
/* Remember the SCTP header. */
|
||||
chunk->sctp_hdr = sh;
|
||||
|
||||
/* Set the source and destination addresses of the incoming chunk. */
|
||||
sctp_init_addrs(chunk, &src, &dest);
|
||||
|
||||
/* Remember where we came from. */
|
||||
chunk->transport = transport;
|
||||
|
||||
/* Acquire access to the sock lock. Note: We are safe from other
|
||||
* bottom halves on this lock, but a user may be in the lock too,
|
||||
* so check if it is busy.
|
||||
*/
|
||||
sctp_bh_lock_sock(sk);
|
||||
|
||||
if (sock_owned_by_user(sk)) {
|
||||
SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_BACKLOG);
|
||||
sctp_add_backlog(sk, skb);
|
||||
} else {
|
||||
SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_SOFTIRQ);
|
||||
sctp_inq_push(&chunk->rcvr->inqueue, chunk);
|
||||
}
|
||||
|
||||
sctp_bh_unlock_sock(sk);
|
||||
|
||||
/* Release the asoc/ep ref we took in the lookup calls. */
|
||||
if (asoc)
|
||||
sctp_association_put(asoc);
|
||||
else
|
||||
sctp_endpoint_put(ep);
|
||||
|
||||
return 0;
|
||||
|
||||
discard_it:
|
||||
SCTP_INC_STATS_BH(SCTP_MIB_IN_PKT_DISCARDS);
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
|
||||
discard_release:
|
||||
/* Release the asoc/ep ref we took in the lookup calls. */
|
||||
if (asoc)
|
||||
sctp_association_put(asoc);
|
||||
else
|
||||
sctp_endpoint_put(ep);
|
||||
|
||||
goto discard_it;
|
||||
}
|
||||
|
||||
/* Process the backlog queue of the socket. Every skb on
|
||||
* the backlog holds a ref on an association or endpoint.
|
||||
* We hold this ref throughout the state machine to make
|
||||
* sure that the structure we need is still around.
|
||||
*/
|
||||
int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
|
||||
struct sctp_inq *inqueue = &chunk->rcvr->inqueue;
|
||||
struct sctp_ep_common *rcvr = NULL;
|
||||
int backloged = 0;
|
||||
|
||||
rcvr = chunk->rcvr;
|
||||
|
||||
/* If the rcvr is dead then the association or endpoint
|
||||
* has been deleted and we can safely drop the chunk
|
||||
* and refs that we are holding.
|
||||
*/
|
||||
if (rcvr->dead) {
|
||||
sctp_chunk_free(chunk);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (unlikely(rcvr->sk != sk)) {
|
||||
/* In this case, the association moved from one socket to
|
||||
* another. We are currently sitting on the backlog of the
|
||||
* old socket, so we need to move.
|
||||
* However, since we are here in the process context we
|
||||
* need to take make sure that the user doesn't own
|
||||
* the new socket when we process the packet.
|
||||
* If the new socket is user-owned, queue the chunk to the
|
||||
* backlog of the new socket without dropping any refs.
|
||||
* Otherwise, we can safely push the chunk on the inqueue.
|
||||
*/
|
||||
|
||||
sk = rcvr->sk;
|
||||
sctp_bh_lock_sock(sk);
|
||||
|
||||
if (sock_owned_by_user(sk)) {
|
||||
sk_add_backlog(sk, skb);
|
||||
backloged = 1;
|
||||
} else
|
||||
sctp_inq_push(inqueue, chunk);
|
||||
|
||||
sctp_bh_unlock_sock(sk);
|
||||
|
||||
/* If the chunk was backloged again, don't drop refs */
|
||||
if (backloged)
|
||||
return 0;
|
||||
} else {
|
||||
sctp_inq_push(inqueue, chunk);
|
||||
}
|
||||
|
||||
done:
|
||||
/* Release the refs we took in sctp_add_backlog */
|
||||
if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
|
||||
sctp_association_put(sctp_assoc(rcvr));
|
||||
else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
|
||||
sctp_endpoint_put(sctp_ep(rcvr));
|
||||
else
|
||||
BUG();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb)
|
||||
{
|
||||
struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk;
|
||||
struct sctp_ep_common *rcvr = chunk->rcvr;
|
||||
|
||||
/* Hold the assoc/ep while hanging on the backlog queue.
|
||||
* This way, we know structures we need will not disappear from us
|
||||
*/
|
||||
if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type)
|
||||
sctp_association_hold(sctp_assoc(rcvr));
|
||||
else if (SCTP_EP_TYPE_SOCKET == rcvr->type)
|
||||
sctp_endpoint_hold(sctp_ep(rcvr));
|
||||
else
|
||||
BUG();
|
||||
|
||||
sk_add_backlog(sk, skb);
|
||||
}
|
||||
|
||||
/* Handle icmp frag needed error. */
|
||||
void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc,
|
||||
struct sctp_transport *t, __u32 pmtu)
|
||||
{
|
||||
if (sock_owned_by_user(sk) || !t || (t->pathmtu == pmtu))
|
||||
return;
|
||||
|
||||
if (t->param_flags & SPP_PMTUD_ENABLE) {
|
||||
if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) {
|
||||
printk(KERN_WARNING "%s: Reported pmtu %d too low, "
|
||||
"using default minimum of %d\n",
|
||||
__FUNCTION__, pmtu,
|
||||
SCTP_DEFAULT_MINSEGMENT);
|
||||
/* Use default minimum segment size and disable
|
||||
* pmtu discovery on this transport.
|
||||
*/
|
||||
t->pathmtu = SCTP_DEFAULT_MINSEGMENT;
|
||||
t->param_flags = (t->param_flags & ~SPP_PMTUD) |
|
||||
SPP_PMTUD_DISABLE;
|
||||
} else {
|
||||
t->pathmtu = pmtu;
|
||||
}
|
||||
|
||||
/* Update association pmtu. */
|
||||
sctp_assoc_sync_pmtu(asoc);
|
||||
}
|
||||
|
||||
/* Retransmit with the new pmtu setting.
|
||||
* Normally, if PMTU discovery is disabled, an ICMP Fragmentation
|
||||
* Needed will never be sent, but if a message was sent before
|
||||
* PMTU discovery was disabled that was larger than the PMTU, it
|
||||
* would not be fragmented, so it must be re-transmitted fragmented.
|
||||
*/
|
||||
sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD);
|
||||
}
|
||||
|
||||
/*
|
||||
* SCTP Implementer's Guide, 2.37 ICMP handling procedures
|
||||
*
|
||||
* ICMP8) If the ICMP code is a "Unrecognized next header type encountered"
|
||||
* or a "Protocol Unreachable" treat this message as an abort
|
||||
* with the T bit set.
|
||||
*
|
||||
* This function sends an event to the state machine, which will abort the
|
||||
* association.
|
||||
*
|
||||
*/
|
||||
void sctp_icmp_proto_unreachable(struct sock *sk,
|
||||
struct sctp_association *asoc,
|
||||
struct sctp_transport *t)
|
||||
{
|
||||
SCTP_DEBUG_PRINTK("%s\n", __FUNCTION__);
|
||||
|
||||
sctp_do_sm(SCTP_EVENT_T_OTHER,
|
||||
SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH),
|
||||
asoc->state, asoc->ep, asoc, t,
|
||||
GFP_ATOMIC);
|
||||
|
||||
}
|
||||
|
||||
/* Common lookup code for icmp/icmpv6 error handler. */
|
||||
struct sock *sctp_err_lookup(int family, struct sk_buff *skb,
|
||||
struct sctphdr *sctphdr,
|
||||
struct sctp_association **app,
|
||||
struct sctp_transport **tpp)
|
||||
{
|
||||
union sctp_addr saddr;
|
||||
union sctp_addr daddr;
|
||||
struct sctp_af *af;
|
||||
struct sock *sk = NULL;
|
||||
struct sctp_association *asoc;
|
||||
struct sctp_transport *transport = NULL;
|
||||
|
||||
*app = NULL; *tpp = NULL;
|
||||
|
||||
af = sctp_get_af_specific(family);
|
||||
if (unlikely(!af)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Initialize local addresses for lookups. */
|
||||
af->from_skb(&saddr, skb, 1);
|
||||
af->from_skb(&daddr, skb, 0);
|
||||
|
||||
/* Look for an association that matches the incoming ICMP error
|
||||
* packet.
|
||||
*/
|
||||
asoc = __sctp_lookup_association(&saddr, &daddr, &transport);
|
||||
if (!asoc)
|
||||
return NULL;
|
||||
|
||||
sk = asoc->base.sk;
|
||||
|
||||
if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) {
|
||||
ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sctp_bh_lock_sock(sk);
|
||||
|
||||
/* If too many ICMPs get dropped on busy
|
||||
* servers this needs to be solved differently.
|
||||
*/
|
||||
if (sock_owned_by_user(sk))
|
||||
NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS);
|
||||
|
||||
*app = asoc;
|
||||
*tpp = transport;
|
||||
return sk;
|
||||
|
||||
out:
|
||||
if (asoc)
|
||||
sctp_association_put(asoc);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Common cleanup code for icmp/icmpv6 error handler. */
|
||||
void sctp_err_finish(struct sock *sk, struct sctp_association *asoc)
|
||||
{
|
||||
sctp_bh_unlock_sock(sk);
|
||||
if (asoc)
|
||||
sctp_association_put(asoc);
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine is called by the ICMP module when it gets some
|
||||
* sort of error condition. If err < 0 then the socket should
|
||||
* be closed and the error returned to the user. If err > 0
|
||||
* it's just the icmp type << 8 | icmp code. After adjustment
|
||||
* header points to the first 8 bytes of the sctp header. We need
|
||||
* to find the appropriate port.
|
||||
*
|
||||
* The locking strategy used here is very "optimistic". When
|
||||
* someone else accesses the socket the ICMP is just dropped
|
||||
* and for some paths there is no check at all.
|
||||
* A more general error queue to queue errors for later handling
|
||||
* is probably better.
|
||||
*
|
||||
*/
|
||||
void sctp_v4_err(struct sk_buff *skb, __u32 info)
|
||||
{
|
||||
struct iphdr *iph = (struct iphdr *)skb->data;
|
||||
struct sctphdr *sh = (struct sctphdr *)(skb->data + (iph->ihl <<2));
|
||||
int type = skb->h.icmph->type;
|
||||
int code = skb->h.icmph->code;
|
||||
struct sock *sk;
|
||||
struct sctp_association *asoc = NULL;
|
||||
struct sctp_transport *transport;
|
||||
struct inet_sock *inet;
|
||||
char *saveip, *savesctp;
|
||||
int err;
|
||||
|
||||
if (skb->len < ((iph->ihl << 2) + 8)) {
|
||||
ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Fix up skb to look at the embedded net header. */
|
||||
saveip = skb->nh.raw;
|
||||
savesctp = skb->h.raw;
|
||||
skb->nh.iph = iph;
|
||||
skb->h.raw = (char *)sh;
|
||||
sk = sctp_err_lookup(AF_INET, skb, sh, &asoc, &transport);
|
||||
/* Put back, the original pointers. */
|
||||
skb->nh.raw = saveip;
|
||||
skb->h.raw = savesctp;
|
||||
if (!sk) {
|
||||
ICMP_INC_STATS_BH(ICMP_MIB_INERRORS);
|
||||
return;
|
||||
}
|
||||
/* Warning: The sock lock is held. Remember to call
|
||||
* sctp_err_finish!
|
||||
*/
|
||||
|
||||
switch (type) {
|
||||
case ICMP_PARAMETERPROB:
|
||||
err = EPROTO;
|
||||
break;
|
||||
case ICMP_DEST_UNREACH:
|
||||
if (code > NR_ICMP_UNREACH)
|
||||
goto out_unlock;
|
||||
|
||||
/* PMTU discovery (RFC1191) */
|
||||
if (ICMP_FRAG_NEEDED == code) {
|
||||
sctp_icmp_frag_needed(sk, asoc, transport, info);
|
||||
goto out_unlock;
|
||||
}
|
||||
else {
|
||||
if (ICMP_PROT_UNREACH == code) {
|
||||
sctp_icmp_proto_unreachable(sk, asoc,
|
||||
transport);
|
||||
goto out_unlock;
|
||||
}
|
||||
}
|
||||
err = icmp_err_convert[code].errno;
|
||||
break;
|
||||
case ICMP_TIME_EXCEEDED:
|
||||
/* Ignore any time exceeded errors due to fragment reassembly
|
||||
* timeouts.
|
||||
*/
|
||||
if (ICMP_EXC_FRAGTIME == code)
|
||||
goto out_unlock;
|
||||
|
||||
err = EHOSTUNREACH;
|
||||
break;
|
||||
default:
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
inet = inet_sk(sk);
|
||||
if (!sock_owned_by_user(sk) && inet->recverr) {
|
||||
sk->sk_err = err;
|
||||
sk->sk_error_report(sk);
|
||||
} else { /* Only an error on timeout */
|
||||
sk->sk_err_soft = err;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
sctp_err_finish(sk, asoc);
|
||||
}
|
||||
|
||||
/*
|
||||
* RFC 2960, 8.4 - Handle "Out of the blue" Packets.
|
||||
*
|
||||
* This function scans all the chunks in the OOTB packet to determine if
|
||||
* the packet should be discarded right away. If a response might be needed
|
||||
* for this packet, or, if further processing is possible, the packet will
|
||||
* be queued to a proper inqueue for the next phase of handling.
|
||||
*
|
||||
* Output:
|
||||
* Return 0 - If further processing is needed.
|
||||
* Return 1 - If the packet can be discarded right away.
|
||||
*/
|
||||
int sctp_rcv_ootb(struct sk_buff *skb)
|
||||
{
|
||||
sctp_chunkhdr_t *ch;
|
||||
__u8 *ch_end;
|
||||
sctp_errhdr_t *err;
|
||||
|
||||
ch = (sctp_chunkhdr_t *) skb->data;
|
||||
|
||||
/* Scan through all the chunks in the packet. */
|
||||
do {
|
||||
/* Break out if chunk length is less then minimal. */
|
||||
if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t))
|
||||
break;
|
||||
|
||||
ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
|
||||
if (ch_end > skb->tail)
|
||||
break;
|
||||
|
||||
/* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the
|
||||
* receiver MUST silently discard the OOTB packet and take no
|
||||
* further action.
|
||||
*/
|
||||
if (SCTP_CID_ABORT == ch->type)
|
||||
goto discard;
|
||||
|
||||
/* RFC 8.4, 6) If the packet contains a SHUTDOWN COMPLETE
|
||||
* chunk, the receiver should silently discard the packet
|
||||
* and take no further action.
|
||||
*/
|
||||
if (SCTP_CID_SHUTDOWN_COMPLETE == ch->type)
|
||||
goto discard;
|
||||
|
||||
/* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR
|
||||
* or a COOKIE ACK the SCTP Packet should be silently
|
||||
* discarded.
|
||||
*/
|
||||
if (SCTP_CID_COOKIE_ACK == ch->type)
|
||||
goto discard;
|
||||
|
||||
if (SCTP_CID_ERROR == ch->type) {
|
||||
sctp_walk_errors(err, ch) {
|
||||
if (SCTP_ERROR_STALE_COOKIE == err->cause)
|
||||
goto discard;
|
||||
}
|
||||
}
|
||||
|
||||
ch = (sctp_chunkhdr_t *) ch_end;
|
||||
} while (ch_end < skb->tail);
|
||||
|
||||
return 0;
|
||||
|
||||
discard:
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Insert endpoint into the hash table. */
|
||||
static void __sctp_hash_endpoint(struct sctp_endpoint *ep)
|
||||
{
|
||||
struct sctp_ep_common **epp;
|
||||
struct sctp_ep_common *epb;
|
||||
struct sctp_hashbucket *head;
|
||||
|
||||
epb = &ep->base;
|
||||
|
||||
epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
|
||||
head = &sctp_ep_hashtable[epb->hashent];
|
||||
|
||||
sctp_write_lock(&head->lock);
|
||||
epp = &head->chain;
|
||||
epb->next = *epp;
|
||||
if (epb->next)
|
||||
(*epp)->pprev = &epb->next;
|
||||
*epp = epb;
|
||||
epb->pprev = epp;
|
||||
sctp_write_unlock(&head->lock);
|
||||
}
|
||||
|
||||
/* Add an endpoint to the hash. Local BH-safe. */
|
||||
void sctp_hash_endpoint(struct sctp_endpoint *ep)
|
||||
{
|
||||
sctp_local_bh_disable();
|
||||
__sctp_hash_endpoint(ep);
|
||||
sctp_local_bh_enable();
|
||||
}
|
||||
|
||||
/* Remove endpoint from the hash table. */
|
||||
static void __sctp_unhash_endpoint(struct sctp_endpoint *ep)
|
||||
{
|
||||
struct sctp_hashbucket *head;
|
||||
struct sctp_ep_common *epb;
|
||||
|
||||
epb = &ep->base;
|
||||
|
||||
epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
|
||||
|
||||
head = &sctp_ep_hashtable[epb->hashent];
|
||||
|
||||
sctp_write_lock(&head->lock);
|
||||
|
||||
if (epb->pprev) {
|
||||
if (epb->next)
|
||||
epb->next->pprev = epb->pprev;
|
||||
*epb->pprev = epb->next;
|
||||
epb->pprev = NULL;
|
||||
}
|
||||
|
||||
sctp_write_unlock(&head->lock);
|
||||
}
|
||||
|
||||
/* Remove endpoint from the hash. Local BH-safe. */
|
||||
void sctp_unhash_endpoint(struct sctp_endpoint *ep)
|
||||
{
|
||||
sctp_local_bh_disable();
|
||||
__sctp_unhash_endpoint(ep);
|
||||
sctp_local_bh_enable();
|
||||
}
|
||||
|
||||
/* Look up an endpoint. */
|
||||
static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr)
|
||||
{
|
||||
struct sctp_hashbucket *head;
|
||||
struct sctp_ep_common *epb;
|
||||
struct sctp_endpoint *ep;
|
||||
int hash;
|
||||
|
||||
hash = sctp_ep_hashfn(ntohs(laddr->v4.sin_port));
|
||||
head = &sctp_ep_hashtable[hash];
|
||||
read_lock(&head->lock);
|
||||
for (epb = head->chain; epb; epb = epb->next) {
|
||||
ep = sctp_ep(epb);
|
||||
if (sctp_endpoint_is_match(ep, laddr))
|
||||
goto hit;
|
||||
}
|
||||
|
||||
ep = sctp_sk((sctp_get_ctl_sock()))->ep;
|
||||
epb = &ep->base;
|
||||
|
||||
hit:
|
||||
sctp_endpoint_hold(ep);
|
||||
read_unlock(&head->lock);
|
||||
return ep;
|
||||
}
|
||||
|
||||
/* Insert association into the hash table. */
|
||||
static void __sctp_hash_established(struct sctp_association *asoc)
|
||||
{
|
||||
struct sctp_ep_common **epp;
|
||||
struct sctp_ep_common *epb;
|
||||
struct sctp_hashbucket *head;
|
||||
|
||||
epb = &asoc->base;
|
||||
|
||||
/* Calculate which chain this entry will belong to. */
|
||||
epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port, asoc->peer.port);
|
||||
|
||||
head = &sctp_assoc_hashtable[epb->hashent];
|
||||
|
||||
sctp_write_lock(&head->lock);
|
||||
epp = &head->chain;
|
||||
epb->next = *epp;
|
||||
if (epb->next)
|
||||
(*epp)->pprev = &epb->next;
|
||||
*epp = epb;
|
||||
epb->pprev = epp;
|
||||
sctp_write_unlock(&head->lock);
|
||||
}
|
||||
|
||||
/* Add an association to the hash. Local BH-safe. */
|
||||
void sctp_hash_established(struct sctp_association *asoc)
|
||||
{
|
||||
if (asoc->temp)
|
||||
return;
|
||||
|
||||
sctp_local_bh_disable();
|
||||
__sctp_hash_established(asoc);
|
||||
sctp_local_bh_enable();
|
||||
}
|
||||
|
||||
/* Remove association from the hash table. */
|
||||
static void __sctp_unhash_established(struct sctp_association *asoc)
|
||||
{
|
||||
struct sctp_hashbucket *head;
|
||||
struct sctp_ep_common *epb;
|
||||
|
||||
epb = &asoc->base;
|
||||
|
||||
epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port,
|
||||
asoc->peer.port);
|
||||
|
||||
head = &sctp_assoc_hashtable[epb->hashent];
|
||||
|
||||
sctp_write_lock(&head->lock);
|
||||
|
||||
if (epb->pprev) {
|
||||
if (epb->next)
|
||||
epb->next->pprev = epb->pprev;
|
||||
*epb->pprev = epb->next;
|
||||
epb->pprev = NULL;
|
||||
}
|
||||
|
||||
sctp_write_unlock(&head->lock);
|
||||
}
|
||||
|
||||
/* Remove association from the hash table. Local BH-safe. */
|
||||
void sctp_unhash_established(struct sctp_association *asoc)
|
||||
{
|
||||
if (asoc->temp)
|
||||
return;
|
||||
|
||||
sctp_local_bh_disable();
|
||||
__sctp_unhash_established(asoc);
|
||||
sctp_local_bh_enable();
|
||||
}
|
||||
|
||||
/* Look up an association. */
|
||||
static struct sctp_association *__sctp_lookup_association(
|
||||
const union sctp_addr *local,
|
||||
const union sctp_addr *peer,
|
||||
struct sctp_transport **pt)
|
||||
{
|
||||
struct sctp_hashbucket *head;
|
||||
struct sctp_ep_common *epb;
|
||||
struct sctp_association *asoc;
|
||||
struct sctp_transport *transport;
|
||||
int hash;
|
||||
|
||||
/* Optimize here for direct hit, only listening connections can
|
||||
* have wildcards anyways.
|
||||
*/
|
||||
hash = sctp_assoc_hashfn(ntohs(local->v4.sin_port), ntohs(peer->v4.sin_port));
|
||||
head = &sctp_assoc_hashtable[hash];
|
||||
read_lock(&head->lock);
|
||||
for (epb = head->chain; epb; epb = epb->next) {
|
||||
asoc = sctp_assoc(epb);
|
||||
transport = sctp_assoc_is_match(asoc, local, peer);
|
||||
if (transport)
|
||||
goto hit;
|
||||
}
|
||||
|
||||
read_unlock(&head->lock);
|
||||
|
||||
return NULL;
|
||||
|
||||
hit:
|
||||
*pt = transport;
|
||||
sctp_association_hold(asoc);
|
||||
read_unlock(&head->lock);
|
||||
return asoc;
|
||||
}
|
||||
|
||||
/* Look up an association. BH-safe. */
|
||||
SCTP_STATIC
|
||||
struct sctp_association *sctp_lookup_association(const union sctp_addr *laddr,
|
||||
const union sctp_addr *paddr,
|
||||
struct sctp_transport **transportp)
|
||||
{
|
||||
struct sctp_association *asoc;
|
||||
|
||||
sctp_local_bh_disable();
|
||||
asoc = __sctp_lookup_association(laddr, paddr, transportp);
|
||||
sctp_local_bh_enable();
|
||||
|
||||
return asoc;
|
||||
}
|
||||
|
||||
/* Is there an association matching the given local and peer addresses? */
|
||||
int sctp_has_association(const union sctp_addr *laddr,
|
||||
const union sctp_addr *paddr)
|
||||
{
|
||||
struct sctp_association *asoc;
|
||||
struct sctp_transport *transport;
|
||||
|
||||
if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) {
|
||||
sctp_association_put(asoc);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* SCTP Implementors Guide, 2.18 Handling of address
|
||||
* parameters within the INIT or INIT-ACK.
|
||||
*
|
||||
* D) When searching for a matching TCB upon reception of an INIT
|
||||
* or INIT-ACK chunk the receiver SHOULD use not only the
|
||||
* source address of the packet (containing the INIT or
|
||||
* INIT-ACK) but the receiver SHOULD also use all valid
|
||||
* address parameters contained within the chunk.
|
||||
*
|
||||
* 2.18.3 Solution description
|
||||
*
|
||||
* This new text clearly specifies to an implementor the need
|
||||
* to look within the INIT or INIT-ACK. Any implementation that
|
||||
* does not do this, may not be able to establish associations
|
||||
* in certain circumstances.
|
||||
*
|
||||
*/
|
||||
static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb,
|
||||
const union sctp_addr *laddr, struct sctp_transport **transportp)
|
||||
{
|
||||
struct sctp_association *asoc;
|
||||
union sctp_addr addr;
|
||||
union sctp_addr *paddr = &addr;
|
||||
struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
|
||||
sctp_chunkhdr_t *ch;
|
||||
union sctp_params params;
|
||||
sctp_init_chunk_t *init;
|
||||
struct sctp_transport *transport;
|
||||
struct sctp_af *af;
|
||||
|
||||
ch = (sctp_chunkhdr_t *) skb->data;
|
||||
|
||||
/* If this is INIT/INIT-ACK look inside the chunk too. */
|
||||
switch (ch->type) {
|
||||
case SCTP_CID_INIT:
|
||||
case SCTP_CID_INIT_ACK:
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* The code below will attempt to walk the chunk and extract
|
||||
* parameter information. Before we do that, we need to verify
|
||||
* that the chunk length doesn't cause overflow. Otherwise, we'll
|
||||
* walk off the end.
|
||||
*/
|
||||
if (WORD_ROUND(ntohs(ch->length)) > skb->len)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* This code will NOT touch anything inside the chunk--it is
|
||||
* strictly READ-ONLY.
|
||||
*
|
||||
* RFC 2960 3 SCTP packet Format
|
||||
*
|
||||
* Multiple chunks can be bundled into one SCTP packet up to
|
||||
* the MTU size, except for the INIT, INIT ACK, and SHUTDOWN
|
||||
* COMPLETE chunks. These chunks MUST NOT be bundled with any
|
||||
* other chunk in a packet. See Section 6.10 for more details
|
||||
* on chunk bundling.
|
||||
*/
|
||||
|
||||
/* Find the start of the TLVs and the end of the chunk. This is
|
||||
* the region we search for address parameters.
|
||||
*/
|
||||
init = (sctp_init_chunk_t *)skb->data;
|
||||
|
||||
/* Walk the parameters looking for embedded addresses. */
|
||||
sctp_walk_params(params, init, init_hdr.params) {
|
||||
|
||||
/* Note: Ignoring hostname addresses. */
|
||||
af = sctp_get_af_specific(param_type2af(params.p->type));
|
||||
if (!af)
|
||||
continue;
|
||||
|
||||
af->from_addr_param(paddr, params.addr, sh->source, 0);
|
||||
|
||||
asoc = __sctp_lookup_association(laddr, paddr, &transport);
|
||||
if (asoc)
|
||||
return asoc;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Lookup an association for an inbound skb. */
|
||||
static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb,
|
||||
const union sctp_addr *paddr,
|
||||
const union sctp_addr *laddr,
|
||||
struct sctp_transport **transportp)
|
||||
{
|
||||
struct sctp_association *asoc;
|
||||
|
||||
asoc = __sctp_lookup_association(laddr, paddr, transportp);
|
||||
|
||||
/* Further lookup for INIT/INIT-ACK packets.
|
||||
* SCTP Implementors Guide, 2.18 Handling of address
|
||||
* parameters within the INIT or INIT-ACK.
|
||||
*/
|
||||
if (!asoc)
|
||||
asoc = __sctp_rcv_init_lookup(skb, laddr, transportp);
|
||||
|
||||
return asoc;
|
||||
}
|
||||
212
net/sctp/inqueue.c
Normal file
212
net/sctp/inqueue.c
Normal file
@@ -0,0 +1,212 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* Copyright (c) 1999-2000 Cisco, Inc.
|
||||
* Copyright (c) 1999-2001 Motorola, Inc.
|
||||
* Copyright (c) 2002 International Business Machines, Corp.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* These functions are the methods for accessing the SCTP inqueue.
|
||||
*
|
||||
* An SCTP inqueue is a queue into which you push SCTP packets
|
||||
* (which might be bundles or fragments of chunks) and out of which you
|
||||
* pop SCTP whole chunks.
|
||||
*
|
||||
* 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>
|
||||
*
|
||||
* Any bugs reported given to us we will try to fix... any fixes shared will
|
||||
* be incorporated into the next SCTP release.
|
||||
*/
|
||||
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
/* Initialize an SCTP inqueue. */
|
||||
void sctp_inq_init(struct sctp_inq *queue)
|
||||
{
|
||||
INIT_LIST_HEAD(&queue->in_chunk_list);
|
||||
queue->in_progress = NULL;
|
||||
|
||||
/* Create a task for delivering data. */
|
||||
INIT_WORK(&queue->immediate, NULL);
|
||||
|
||||
queue->malloced = 0;
|
||||
}
|
||||
|
||||
/* Release the memory associated with an SCTP inqueue. */
|
||||
void sctp_inq_free(struct sctp_inq *queue)
|
||||
{
|
||||
struct sctp_chunk *chunk, *tmp;
|
||||
|
||||
/* Empty the queue. */
|
||||
list_for_each_entry_safe(chunk, tmp, &queue->in_chunk_list, list) {
|
||||
list_del_init(&chunk->list);
|
||||
sctp_chunk_free(chunk);
|
||||
}
|
||||
|
||||
/* If there is a packet which is currently being worked on,
|
||||
* free it as well.
|
||||
*/
|
||||
if (queue->in_progress) {
|
||||
sctp_chunk_free(queue->in_progress);
|
||||
queue->in_progress = NULL;
|
||||
}
|
||||
|
||||
if (queue->malloced) {
|
||||
/* Dump the master memory segment. */
|
||||
kfree(queue);
|
||||
}
|
||||
}
|
||||
|
||||
/* Put a new packet in an SCTP inqueue.
|
||||
* We assume that packet->sctp_hdr is set and in host byte order.
|
||||
*/
|
||||
void sctp_inq_push(struct sctp_inq *q, struct sctp_chunk *chunk)
|
||||
{
|
||||
/* Directly call the packet handling routine. */
|
||||
|
||||
/* We are now calling this either from the soft interrupt
|
||||
* or from the backlog processing.
|
||||
* Eventually, we should clean up inqueue to not rely
|
||||
* on the BH related data structures.
|
||||
*/
|
||||
list_add_tail(&chunk->list, &q->in_chunk_list);
|
||||
q->immediate.func(&q->immediate);
|
||||
}
|
||||
|
||||
/* Extract a chunk from an SCTP inqueue.
|
||||
*
|
||||
* WARNING: If you need to put the chunk on another queue, you need to
|
||||
* make a shallow copy (clone) of it.
|
||||
*/
|
||||
struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue)
|
||||
{
|
||||
struct sctp_chunk *chunk;
|
||||
sctp_chunkhdr_t *ch = NULL;
|
||||
|
||||
/* The assumption is that we are safe to process the chunks
|
||||
* at this time.
|
||||
*/
|
||||
|
||||
if ((chunk = queue->in_progress)) {
|
||||
/* There is a packet that we have been working on.
|
||||
* Any post processing work to do before we move on?
|
||||
*/
|
||||
if (chunk->singleton ||
|
||||
chunk->end_of_packet ||
|
||||
chunk->pdiscard) {
|
||||
sctp_chunk_free(chunk);
|
||||
chunk = queue->in_progress = NULL;
|
||||
} else {
|
||||
/* Nothing to do. Next chunk in the packet, please. */
|
||||
ch = (sctp_chunkhdr_t *) chunk->chunk_end;
|
||||
|
||||
/* Force chunk->skb->data to chunk->chunk_end. */
|
||||
skb_pull(chunk->skb,
|
||||
chunk->chunk_end - chunk->skb->data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Do we need to take the next packet out of the queue to process? */
|
||||
if (!chunk) {
|
||||
struct list_head *entry;
|
||||
|
||||
/* Is the queue empty? */
|
||||
if (list_empty(&queue->in_chunk_list))
|
||||
return NULL;
|
||||
|
||||
entry = queue->in_chunk_list.next;
|
||||
chunk = queue->in_progress =
|
||||
list_entry(entry, struct sctp_chunk, list);
|
||||
list_del_init(entry);
|
||||
|
||||
/* This is the first chunk in the packet. */
|
||||
chunk->singleton = 1;
|
||||
ch = (sctp_chunkhdr_t *) chunk->skb->data;
|
||||
chunk->data_accepted = 0;
|
||||
}
|
||||
|
||||
chunk->chunk_hdr = ch;
|
||||
chunk->chunk_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length));
|
||||
/* In the unlikely case of an IP reassembly, the skb could be
|
||||
* non-linear. If so, update chunk_end so that it doesn't go past
|
||||
* the skb->tail.
|
||||
*/
|
||||
if (unlikely(skb_is_nonlinear(chunk->skb))) {
|
||||
if (chunk->chunk_end > chunk->skb->tail)
|
||||
chunk->chunk_end = chunk->skb->tail;
|
||||
}
|
||||
skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t));
|
||||
chunk->subh.v = NULL; /* Subheader is no longer valid. */
|
||||
|
||||
if (chunk->chunk_end < chunk->skb->tail) {
|
||||
/* This is not a singleton */
|
||||
chunk->singleton = 0;
|
||||
} else if (chunk->chunk_end > chunk->skb->tail) {
|
||||
/* RFC 2960, Section 6.10 Bundling
|
||||
*
|
||||
* Partial chunks MUST NOT be placed in an SCTP packet.
|
||||
* If the receiver detects a partial chunk, it MUST drop
|
||||
* the chunk.
|
||||
*
|
||||
* Since the end of the chunk is past the end of our buffer
|
||||
* (which contains the whole packet, we can freely discard
|
||||
* the whole packet.
|
||||
*/
|
||||
sctp_chunk_free(chunk);
|
||||
chunk = queue->in_progress = NULL;
|
||||
|
||||
return NULL;
|
||||
} else {
|
||||
/* We are at the end of the packet, so mark the chunk
|
||||
* in case we need to send a SACK.
|
||||
*/
|
||||
chunk->end_of_packet = 1;
|
||||
}
|
||||
|
||||
SCTP_DEBUG_PRINTK("+++sctp_inq_pop+++ chunk %p[%s],"
|
||||
" length %d, skb->len %d\n",chunk,
|
||||
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type)),
|
||||
ntohs(chunk->chunk_hdr->length), chunk->skb->len);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/* Set a top-half handler.
|
||||
*
|
||||
* Originally, we the top-half handler was scheduled as a BH. We now
|
||||
* call the handler directly in sctp_inq_push() at a time that
|
||||
* we know we are lock safe.
|
||||
* The intent is that this routine will pull stuff out of the
|
||||
* inqueue and process it.
|
||||
*/
|
||||
void sctp_inq_set_th_handler(struct sctp_inq *q, work_func_t callback)
|
||||
{
|
||||
INIT_WORK(&q->immediate, callback);
|
||||
}
|
||||
|
||||
1038
net/sctp/ipv6.c
Normal file
1038
net/sctp/ipv6.c
Normal file
File diff suppressed because it is too large
Load Diff
144
net/sctp/objcnt.c
Normal file
144
net/sctp/objcnt.c
Normal file
@@ -0,0 +1,144 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* (C) Copyright IBM Corp. 2001, 2004
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* Support for memory object debugging. This allows one to monitor the
|
||||
* object allocations/deallocations for types instrumented for this
|
||||
* via the proc fs.
|
||||
*
|
||||
* 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:
|
||||
* Jon Grimm <jgrimm@us.ibm.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/kernel.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
|
||||
/*
|
||||
* Global counters to count raw object allocation counts.
|
||||
* To add new counters, choose a unique suffix for the variable
|
||||
* name as the helper macros key off this suffix to make
|
||||
* life easier for the programmer.
|
||||
*/
|
||||
|
||||
SCTP_DBG_OBJCNT(sock);
|
||||
SCTP_DBG_OBJCNT(ep);
|
||||
SCTP_DBG_OBJCNT(transport);
|
||||
SCTP_DBG_OBJCNT(assoc);
|
||||
SCTP_DBG_OBJCNT(bind_addr);
|
||||
SCTP_DBG_OBJCNT(bind_bucket);
|
||||
SCTP_DBG_OBJCNT(chunk);
|
||||
SCTP_DBG_OBJCNT(addr);
|
||||
SCTP_DBG_OBJCNT(ssnmap);
|
||||
SCTP_DBG_OBJCNT(datamsg);
|
||||
|
||||
/* An array to make it easy to pretty print the debug information
|
||||
* to the proc fs.
|
||||
*/
|
||||
static sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = {
|
||||
SCTP_DBG_OBJCNT_ENTRY(sock),
|
||||
SCTP_DBG_OBJCNT_ENTRY(ep),
|
||||
SCTP_DBG_OBJCNT_ENTRY(assoc),
|
||||
SCTP_DBG_OBJCNT_ENTRY(transport),
|
||||
SCTP_DBG_OBJCNT_ENTRY(chunk),
|
||||
SCTP_DBG_OBJCNT_ENTRY(bind_addr),
|
||||
SCTP_DBG_OBJCNT_ENTRY(bind_bucket),
|
||||
SCTP_DBG_OBJCNT_ENTRY(addr),
|
||||
SCTP_DBG_OBJCNT_ENTRY(ssnmap),
|
||||
SCTP_DBG_OBJCNT_ENTRY(datamsg),
|
||||
};
|
||||
|
||||
/* Callback from procfs to read out objcount information.
|
||||
* Walk through the entries in the sctp_dbg_objcnt array, dumping
|
||||
* the raw object counts for each monitored type.
|
||||
*
|
||||
* This code was modified from similar code in route.c
|
||||
*/
|
||||
static int sctp_dbg_objcnt_read(char *buffer, char **start, off_t offset,
|
||||
int length, int *eof, void *data)
|
||||
{
|
||||
int len = 0;
|
||||
off_t pos = 0;
|
||||
int entries;
|
||||
int i;
|
||||
char temp[128];
|
||||
|
||||
/* How many entries? */
|
||||
entries = ARRAY_SIZE(sctp_dbg_objcnt);
|
||||
|
||||
/* Walk the entries and print out the debug information
|
||||
* for proc fs.
|
||||
*/
|
||||
for (i = 0; i < entries; i++) {
|
||||
pos += 128;
|
||||
|
||||
/* Skip ahead. */
|
||||
if (pos <= offset) {
|
||||
len = 0;
|
||||
continue;
|
||||
}
|
||||
/* Print out each entry. */
|
||||
sprintf(temp, "%s: %d",
|
||||
sctp_dbg_objcnt[i].label,
|
||||
atomic_read(sctp_dbg_objcnt[i].counter));
|
||||
|
||||
sprintf(buffer + len, "%-127s\n", temp);
|
||||
len += 128;
|
||||
if (pos >= offset+length)
|
||||
goto done;
|
||||
}
|
||||
|
||||
done:
|
||||
*start = buffer + len - (pos - offset);
|
||||
len = pos - offset;
|
||||
if (len > length)
|
||||
len = length;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
/* Initialize the objcount in the proc filesystem. */
|
||||
void sctp_dbg_objcnt_init(void)
|
||||
{
|
||||
struct proc_dir_entry *ent;
|
||||
ent = create_proc_read_entry("sctp_dbg_objcnt", 0, proc_net_sctp,
|
||||
sctp_dbg_objcnt_read, NULL);
|
||||
if (!ent)
|
||||
printk(KERN_WARNING
|
||||
"sctp_dbg_objcnt: Unable to create /proc entry.\n");
|
||||
}
|
||||
|
||||
/* Cleanup the objcount entry in the proc filesystem. */
|
||||
void sctp_dbg_objcnt_exit(void)
|
||||
{
|
||||
remove_proc_entry("sctp_dbg_objcnt", proc_net_sctp);
|
||||
}
|
||||
|
||||
|
||||
667
net/sctp/output.c
Normal file
667
net/sctp/output.c
Normal file
@@ -0,0 +1,667 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* (C) Copyright IBM Corp. 2001, 2004
|
||||
* Copyright (c) 1999-2000 Cisco, Inc.
|
||||
* Copyright (c) 1999-2001 Motorola, Inc.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* These functions handle output processing.
|
||||
*
|
||||
* 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@austin.ibm.com>
|
||||
* Sridhar Samudrala <sri@us.ibm.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/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/inet_ecn.h>
|
||||
#include <net/icmp.h>
|
||||
|
||||
#ifndef TEST_FRAME
|
||||
#include <net/tcp.h>
|
||||
#endif /* TEST_FRAME (not defined) */
|
||||
|
||||
#include <linux/socket.h> /* for sa_family_t */
|
||||
#include <net/sock.h>
|
||||
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
/* Forward declarations for private helpers. */
|
||||
static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
|
||||
struct sctp_chunk *chunk);
|
||||
|
||||
/* Config a packet.
|
||||
* This appears to be a followup set of initializations.
|
||||
*/
|
||||
struct sctp_packet *sctp_packet_config(struct sctp_packet *packet,
|
||||
__u32 vtag, int ecn_capable)
|
||||
{
|
||||
struct sctp_chunk *chunk = NULL;
|
||||
|
||||
SCTP_DEBUG_PRINTK("%s: packet:%p vtag:0x%x\n", __FUNCTION__,
|
||||
packet, vtag);
|
||||
|
||||
packet->vtag = vtag;
|
||||
packet->has_cookie_echo = 0;
|
||||
packet->has_sack = 0;
|
||||
packet->ipfragok = 0;
|
||||
|
||||
if (ecn_capable && sctp_packet_empty(packet)) {
|
||||
chunk = sctp_get_ecne_prepend(packet->transport->asoc);
|
||||
|
||||
/* If there a is a prepend chunk stick it on the list before
|
||||
* any other chunks get appended.
|
||||
*/
|
||||
if (chunk)
|
||||
sctp_packet_append_chunk(packet, chunk);
|
||||
}
|
||||
|
||||
return packet;
|
||||
}
|
||||
|
||||
/* Initialize the packet structure. */
|
||||
struct sctp_packet *sctp_packet_init(struct sctp_packet *packet,
|
||||
struct sctp_transport *transport,
|
||||
__u16 sport, __u16 dport)
|
||||
{
|
||||
struct sctp_association *asoc = transport->asoc;
|
||||
size_t overhead;
|
||||
|
||||
SCTP_DEBUG_PRINTK("%s: packet:%p transport:%p\n", __FUNCTION__,
|
||||
packet, transport);
|
||||
|
||||
packet->transport = transport;
|
||||
packet->source_port = sport;
|
||||
packet->destination_port = dport;
|
||||
INIT_LIST_HEAD(&packet->chunk_list);
|
||||
if (asoc) {
|
||||
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
|
||||
overhead = sp->pf->af->net_header_len;
|
||||
} else {
|
||||
overhead = sizeof(struct ipv6hdr);
|
||||
}
|
||||
overhead += sizeof(struct sctphdr);
|
||||
packet->overhead = overhead;
|
||||
packet->size = overhead;
|
||||
packet->vtag = 0;
|
||||
packet->has_cookie_echo = 0;
|
||||
packet->has_sack = 0;
|
||||
packet->ipfragok = 0;
|
||||
packet->malloced = 0;
|
||||
return packet;
|
||||
}
|
||||
|
||||
/* Free a packet. */
|
||||
void sctp_packet_free(struct sctp_packet *packet)
|
||||
{
|
||||
struct sctp_chunk *chunk, *tmp;
|
||||
|
||||
SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet);
|
||||
|
||||
list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {
|
||||
list_del_init(&chunk->list);
|
||||
sctp_chunk_free(chunk);
|
||||
}
|
||||
|
||||
if (packet->malloced)
|
||||
kfree(packet);
|
||||
}
|
||||
|
||||
/* This routine tries to append the chunk to the offered packet. If adding
|
||||
* the chunk causes the packet to exceed the path MTU and COOKIE_ECHO chunk
|
||||
* is not present in the packet, it transmits the input packet.
|
||||
* Data can be bundled with a packet containing a COOKIE_ECHO chunk as long
|
||||
* as it can fit in the packet, but any more data that does not fit in this
|
||||
* packet can be sent only after receiving the COOKIE_ACK.
|
||||
*/
|
||||
sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet,
|
||||
struct sctp_chunk *chunk)
|
||||
{
|
||||
sctp_xmit_t retval;
|
||||
int error = 0;
|
||||
|
||||
SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __FUNCTION__,
|
||||
packet, chunk);
|
||||
|
||||
switch ((retval = (sctp_packet_append_chunk(packet, chunk)))) {
|
||||
case SCTP_XMIT_PMTU_FULL:
|
||||
if (!packet->has_cookie_echo) {
|
||||
error = sctp_packet_transmit(packet);
|
||||
if (error < 0)
|
||||
chunk->skb->sk->sk_err = -error;
|
||||
|
||||
/* If we have an empty packet, then we can NOT ever
|
||||
* return PMTU_FULL.
|
||||
*/
|
||||
retval = sctp_packet_append_chunk(packet, chunk);
|
||||
}
|
||||
break;
|
||||
|
||||
case SCTP_XMIT_RWND_FULL:
|
||||
case SCTP_XMIT_OK:
|
||||
case SCTP_XMIT_NAGLE_DELAY:
|
||||
break;
|
||||
};
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Try to bundle a SACK with the packet. */
|
||||
static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt,
|
||||
struct sctp_chunk *chunk)
|
||||
{
|
||||
sctp_xmit_t retval = SCTP_XMIT_OK;
|
||||
|
||||
/* If sending DATA and haven't aleady bundled a SACK, try to
|
||||
* bundle one in to the packet.
|
||||
*/
|
||||
if (sctp_chunk_is_data(chunk) && !pkt->has_sack &&
|
||||
!pkt->has_cookie_echo) {
|
||||
struct sctp_association *asoc;
|
||||
asoc = pkt->transport->asoc;
|
||||
|
||||
if (asoc->a_rwnd > asoc->rwnd) {
|
||||
struct sctp_chunk *sack;
|
||||
asoc->a_rwnd = asoc->rwnd;
|
||||
sack = sctp_make_sack(asoc);
|
||||
if (sack) {
|
||||
struct timer_list *timer;
|
||||
retval = sctp_packet_append_chunk(pkt, sack);
|
||||
asoc->peer.sack_needed = 0;
|
||||
timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
|
||||
if (timer_pending(timer) && del_timer(timer))
|
||||
sctp_association_put(asoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Append a chunk to the offered packet reporting back any inability to do
|
||||
* so.
|
||||
*/
|
||||
sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet,
|
||||
struct sctp_chunk *chunk)
|
||||
{
|
||||
sctp_xmit_t retval = SCTP_XMIT_OK;
|
||||
__u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length));
|
||||
size_t psize;
|
||||
size_t pmtu;
|
||||
int too_big;
|
||||
|
||||
SCTP_DEBUG_PRINTK("%s: packet:%p chunk:%p\n", __FUNCTION__, packet,
|
||||
chunk);
|
||||
|
||||
retval = sctp_packet_bundle_sack(packet, chunk);
|
||||
psize = packet->size;
|
||||
|
||||
if (retval != SCTP_XMIT_OK)
|
||||
goto finish;
|
||||
|
||||
pmtu = ((packet->transport->asoc) ?
|
||||
(packet->transport->asoc->pathmtu) :
|
||||
(packet->transport->pathmtu));
|
||||
|
||||
too_big = (psize + chunk_len > pmtu);
|
||||
|
||||
/* Decide if we need to fragment or resubmit later. */
|
||||
if (too_big) {
|
||||
/* Both control chunks and data chunks with TSNs are
|
||||
* non-fragmentable.
|
||||
*/
|
||||
if (sctp_packet_empty(packet) || !sctp_chunk_is_data(chunk)) {
|
||||
/* We no longer do re-fragmentation.
|
||||
* Just fragment at the IP layer, if we
|
||||
* actually hit this condition
|
||||
*/
|
||||
packet->ipfragok = 1;
|
||||
goto append;
|
||||
|
||||
} else {
|
||||
retval = SCTP_XMIT_PMTU_FULL;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
append:
|
||||
/* We believe that this chunk is OK to add to the packet (as
|
||||
* long as we have the cwnd for it).
|
||||
*/
|
||||
|
||||
/* DATA is a special case since we must examine both rwnd and cwnd
|
||||
* before we send DATA.
|
||||
*/
|
||||
if (sctp_chunk_is_data(chunk)) {
|
||||
retval = sctp_packet_append_data(packet, chunk);
|
||||
/* Disallow SACK bundling after DATA. */
|
||||
packet->has_sack = 1;
|
||||
if (SCTP_XMIT_OK != retval)
|
||||
goto finish;
|
||||
} else if (SCTP_CID_COOKIE_ECHO == chunk->chunk_hdr->type)
|
||||
packet->has_cookie_echo = 1;
|
||||
else if (SCTP_CID_SACK == chunk->chunk_hdr->type)
|
||||
packet->has_sack = 1;
|
||||
|
||||
/* It is OK to send this chunk. */
|
||||
list_add_tail(&chunk->list, &packet->chunk_list);
|
||||
packet->size += chunk_len;
|
||||
chunk->transport = packet->transport;
|
||||
finish:
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* All packets are sent to the network through this function from
|
||||
* sctp_outq_tail().
|
||||
*
|
||||
* The return value is a normal kernel error return value.
|
||||
*/
|
||||
int sctp_packet_transmit(struct sctp_packet *packet)
|
||||
{
|
||||
struct sctp_transport *tp = packet->transport;
|
||||
struct sctp_association *asoc = tp->asoc;
|
||||
struct sctphdr *sh;
|
||||
__u32 crc32 = 0;
|
||||
struct sk_buff *nskb;
|
||||
struct sctp_chunk *chunk, *tmp;
|
||||
struct sock *sk;
|
||||
int err = 0;
|
||||
int padding; /* How much padding do we need? */
|
||||
__u8 has_data = 0;
|
||||
struct dst_entry *dst = tp->dst;
|
||||
|
||||
SCTP_DEBUG_PRINTK("%s: packet:%p\n", __FUNCTION__, packet);
|
||||
|
||||
/* Do NOT generate a chunkless packet. */
|
||||
if (list_empty(&packet->chunk_list))
|
||||
return err;
|
||||
|
||||
/* Set up convenience variables... */
|
||||
chunk = list_entry(packet->chunk_list.next, struct sctp_chunk, list);
|
||||
sk = chunk->skb->sk;
|
||||
|
||||
/* Allocate the new skb. */
|
||||
nskb = alloc_skb(packet->size + LL_MAX_HEADER, GFP_ATOMIC);
|
||||
if (!nskb)
|
||||
goto nomem;
|
||||
|
||||
/* Make sure the outbound skb has enough header room reserved. */
|
||||
skb_reserve(nskb, packet->overhead + LL_MAX_HEADER);
|
||||
|
||||
/* Set the owning socket so that we know where to get the
|
||||
* destination IP address.
|
||||
*/
|
||||
skb_set_owner_w(nskb, sk);
|
||||
|
||||
/* The 'obsolete' field of dst is set to 2 when a dst is freed. */
|
||||
if (!dst || (dst->obsolete > 1)) {
|
||||
dst_release(dst);
|
||||
sctp_transport_route(tp, NULL, sctp_sk(sk));
|
||||
if (asoc && (asoc->param_flags & SPP_PMTUD_ENABLE)) {
|
||||
sctp_assoc_sync_pmtu(asoc);
|
||||
}
|
||||
}
|
||||
nskb->dst = dst_clone(tp->dst);
|
||||
if (!nskb->dst)
|
||||
goto no_route;
|
||||
dst = nskb->dst;
|
||||
|
||||
/* Build the SCTP header. */
|
||||
sh = (struct sctphdr *)skb_push(nskb, sizeof(struct sctphdr));
|
||||
sh->source = htons(packet->source_port);
|
||||
sh->dest = htons(packet->destination_port);
|
||||
|
||||
/* From 6.8 Adler-32 Checksum Calculation:
|
||||
* After the packet is constructed (containing the SCTP common
|
||||
* header and one or more control or DATA chunks), the
|
||||
* transmitter shall:
|
||||
*
|
||||
* 1) Fill in the proper Verification Tag in the SCTP common
|
||||
* header and initialize the checksum field to 0's.
|
||||
*/
|
||||
sh->vtag = htonl(packet->vtag);
|
||||
sh->checksum = 0;
|
||||
|
||||
/* 2) Calculate the Adler-32 checksum of the whole packet,
|
||||
* including the SCTP common header and all the
|
||||
* chunks.
|
||||
*
|
||||
* Note: Adler-32 is no longer applicable, as has been replaced
|
||||
* by CRC32-C as described in <draft-ietf-tsvwg-sctpcsum-02.txt>.
|
||||
*/
|
||||
if (!(dst->dev->features & NETIF_F_NO_CSUM))
|
||||
crc32 = sctp_start_cksum((__u8 *)sh, sizeof(struct sctphdr));
|
||||
|
||||
/**
|
||||
* 6.10 Bundling
|
||||
*
|
||||
* An endpoint bundles chunks by simply including multiple
|
||||
* chunks in one outbound SCTP packet. ...
|
||||
*/
|
||||
|
||||
/**
|
||||
* 3.2 Chunk Field Descriptions
|
||||
*
|
||||
* The total length of a chunk (including Type, Length and
|
||||
* Value fields) MUST be a multiple of 4 bytes. If the length
|
||||
* of the chunk is not a multiple of 4 bytes, the sender MUST
|
||||
* pad the chunk with all zero bytes and this padding is not
|
||||
* included in the chunk length field. The sender should
|
||||
* never pad with more than 3 bytes.
|
||||
*
|
||||
* [This whole comment explains WORD_ROUND() below.]
|
||||
*/
|
||||
SCTP_DEBUG_PRINTK("***sctp_transmit_packet***\n");
|
||||
list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {
|
||||
list_del_init(&chunk->list);
|
||||
if (sctp_chunk_is_data(chunk)) {
|
||||
|
||||
if (!chunk->has_tsn) {
|
||||
sctp_chunk_assign_ssn(chunk);
|
||||
sctp_chunk_assign_tsn(chunk);
|
||||
|
||||
/* 6.3.1 C4) When data is in flight and when allowed
|
||||
* by rule C5, a new RTT measurement MUST be made each
|
||||
* round trip. Furthermore, new RTT measurements
|
||||
* SHOULD be made no more than once per round-trip
|
||||
* for a given destination transport address.
|
||||
*/
|
||||
|
||||
if (!tp->rto_pending) {
|
||||
chunk->rtt_in_progress = 1;
|
||||
tp->rto_pending = 1;
|
||||
}
|
||||
} else
|
||||
chunk->resent = 1;
|
||||
|
||||
chunk->sent_at = jiffies;
|
||||
has_data = 1;
|
||||
}
|
||||
|
||||
padding = WORD_ROUND(chunk->skb->len) - chunk->skb->len;
|
||||
if (padding)
|
||||
memset(skb_put(chunk->skb, padding), 0, padding);
|
||||
|
||||
if (dst->dev->features & NETIF_F_NO_CSUM)
|
||||
memcpy(skb_put(nskb, chunk->skb->len),
|
||||
chunk->skb->data, chunk->skb->len);
|
||||
else
|
||||
crc32 = sctp_update_copy_cksum(skb_put(nskb,
|
||||
chunk->skb->len),
|
||||
chunk->skb->data,
|
||||
chunk->skb->len, crc32);
|
||||
|
||||
SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %s %d, %s %d\n",
|
||||
"*** Chunk", chunk,
|
||||
sctp_cname(SCTP_ST_CHUNK(
|
||||
chunk->chunk_hdr->type)),
|
||||
chunk->has_tsn ? "TSN" : "No TSN",
|
||||
chunk->has_tsn ?
|
||||
ntohl(chunk->subh.data_hdr->tsn) : 0,
|
||||
"length", ntohs(chunk->chunk_hdr->length),
|
||||
"chunk->skb->len", chunk->skb->len,
|
||||
"rtt_in_progress", chunk->rtt_in_progress);
|
||||
|
||||
/*
|
||||
* If this is a control chunk, this is our last
|
||||
* reference. Free data chunks after they've been
|
||||
* acknowledged or have failed.
|
||||
*/
|
||||
if (!sctp_chunk_is_data(chunk))
|
||||
sctp_chunk_free(chunk);
|
||||
}
|
||||
|
||||
/* Perform final transformation on checksum. */
|
||||
if (!(dst->dev->features & NETIF_F_NO_CSUM))
|
||||
crc32 = sctp_end_cksum(crc32);
|
||||
|
||||
/* 3) Put the resultant value into the checksum field in the
|
||||
* common header, and leave the rest of the bits unchanged.
|
||||
*/
|
||||
sh->checksum = htonl(crc32);
|
||||
|
||||
/* IP layer ECN support
|
||||
* From RFC 2481
|
||||
* "The ECN-Capable Transport (ECT) bit would be set by the
|
||||
* data sender to indicate that the end-points of the
|
||||
* transport protocol are ECN-capable."
|
||||
*
|
||||
* Now setting the ECT bit all the time, as it should not cause
|
||||
* any problems protocol-wise even if our peer ignores it.
|
||||
*
|
||||
* Note: The works for IPv6 layer checks this bit too later
|
||||
* in transmission. See IP6_ECN_flow_xmit().
|
||||
*/
|
||||
INET_ECN_xmit(nskb->sk);
|
||||
|
||||
/* Set up the IP options. */
|
||||
/* BUG: not implemented
|
||||
* For v4 this all lives somewhere in sk->sk_opt...
|
||||
*/
|
||||
|
||||
/* Dump that on IP! */
|
||||
if (asoc && asoc->peer.last_sent_to != tp) {
|
||||
/* Considering the multiple CPU scenario, this is a
|
||||
* "correcter" place for last_sent_to. --xguo
|
||||
*/
|
||||
asoc->peer.last_sent_to = tp;
|
||||
}
|
||||
|
||||
if (has_data) {
|
||||
struct timer_list *timer;
|
||||
unsigned long timeout;
|
||||
|
||||
tp->last_time_used = jiffies;
|
||||
|
||||
/* Restart the AUTOCLOSE timer when sending data. */
|
||||
if (sctp_state(asoc, ESTABLISHED) && asoc->autoclose) {
|
||||
timer = &asoc->timers[SCTP_EVENT_TIMEOUT_AUTOCLOSE];
|
||||
timeout = asoc->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE];
|
||||
|
||||
if (!mod_timer(timer, jiffies + timeout))
|
||||
sctp_association_hold(asoc);
|
||||
}
|
||||
}
|
||||
|
||||
SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb len %d\n",
|
||||
nskb->len);
|
||||
|
||||
if (tp->param_flags & SPP_PMTUD_ENABLE)
|
||||
(*tp->af_specific->sctp_xmit)(nskb, tp, packet->ipfragok);
|
||||
else
|
||||
(*tp->af_specific->sctp_xmit)(nskb, tp, 1);
|
||||
|
||||
out:
|
||||
packet->size = packet->overhead;
|
||||
return err;
|
||||
no_route:
|
||||
kfree_skb(nskb);
|
||||
IP_INC_STATS_BH(IPSTATS_MIB_OUTNOROUTES);
|
||||
|
||||
/* FIXME: Returning the 'err' will effect all the associations
|
||||
* associated with a socket, although only one of the paths of the
|
||||
* association is unreachable.
|
||||
* The real failure of a transport or association can be passed on
|
||||
* to the user via notifications. So setting this error may not be
|
||||
* required.
|
||||
*/
|
||||
/* err = -EHOSTUNREACH; */
|
||||
err:
|
||||
/* Control chunks are unreliable so just drop them. DATA chunks
|
||||
* will get resent or dropped later.
|
||||
*/
|
||||
|
||||
list_for_each_entry_safe(chunk, tmp, &packet->chunk_list, list) {
|
||||
list_del_init(&chunk->list);
|
||||
if (!sctp_chunk_is_data(chunk))
|
||||
sctp_chunk_free(chunk);
|
||||
}
|
||||
goto out;
|
||||
nomem:
|
||||
err = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 2nd Level Abstractions
|
||||
********************************************************************/
|
||||
|
||||
/* This private function handles the specifics of appending DATA chunks. */
|
||||
static sctp_xmit_t sctp_packet_append_data(struct sctp_packet *packet,
|
||||
struct sctp_chunk *chunk)
|
||||
{
|
||||
sctp_xmit_t retval = SCTP_XMIT_OK;
|
||||
size_t datasize, rwnd, inflight;
|
||||
struct sctp_transport *transport = packet->transport;
|
||||
__u32 max_burst_bytes;
|
||||
struct sctp_association *asoc = transport->asoc;
|
||||
struct sctp_sock *sp = sctp_sk(asoc->base.sk);
|
||||
struct sctp_outq *q = &asoc->outqueue;
|
||||
|
||||
/* RFC 2960 6.1 Transmission of DATA Chunks
|
||||
*
|
||||
* A) At any given time, the data sender MUST NOT transmit new data to
|
||||
* any destination transport address if its peer's rwnd indicates
|
||||
* that the peer has no buffer space (i.e. rwnd is 0, see Section
|
||||
* 6.2.1). However, regardless of the value of rwnd (including if it
|
||||
* is 0), the data sender can always have one DATA chunk in flight to
|
||||
* the receiver if allowed by cwnd (see rule B below). This rule
|
||||
* allows the sender to probe for a change in rwnd that the sender
|
||||
* missed due to the SACK having been lost in transit from the data
|
||||
* receiver to the data sender.
|
||||
*/
|
||||
|
||||
rwnd = asoc->peer.rwnd;
|
||||
inflight = asoc->outqueue.outstanding_bytes;
|
||||
|
||||
datasize = sctp_data_size(chunk);
|
||||
|
||||
if (datasize > rwnd) {
|
||||
if (inflight > 0) {
|
||||
/* We have (at least) one data chunk in flight,
|
||||
* so we can't fall back to rule 6.1 B).
|
||||
*/
|
||||
retval = SCTP_XMIT_RWND_FULL;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
/* sctpimpguide-05 2.14.2
|
||||
* D) When the time comes for the sender to
|
||||
* transmit new DATA chunks, the protocol parameter Max.Burst MUST
|
||||
* first be applied to limit how many new DATA chunks may be sent.
|
||||
* The limit is applied by adjusting cwnd as follows:
|
||||
* if ((flightsize + Max.Burst * MTU) < cwnd)
|
||||
* cwnd = flightsize + Max.Burst * MTU
|
||||
*/
|
||||
max_burst_bytes = asoc->max_burst * asoc->pathmtu;
|
||||
if ((transport->flight_size + max_burst_bytes) < transport->cwnd) {
|
||||
transport->cwnd = transport->flight_size + max_burst_bytes;
|
||||
SCTP_DEBUG_PRINTK("%s: cwnd limited by max_burst: "
|
||||
"transport: %p, cwnd: %d, "
|
||||
"ssthresh: %d, flight_size: %d, "
|
||||
"pba: %d\n",
|
||||
__FUNCTION__, transport,
|
||||
transport->cwnd,
|
||||
transport->ssthresh,
|
||||
transport->flight_size,
|
||||
transport->partial_bytes_acked);
|
||||
}
|
||||
|
||||
/* RFC 2960 6.1 Transmission of DATA Chunks
|
||||
*
|
||||
* B) At any given time, the sender MUST NOT transmit new data
|
||||
* to a given transport address if it has cwnd or more bytes
|
||||
* of data outstanding to that transport address.
|
||||
*/
|
||||
/* RFC 7.2.4 & the Implementers Guide 2.8.
|
||||
*
|
||||
* 3) ...
|
||||
* When a Fast Retransmit is being performed the sender SHOULD
|
||||
* ignore the value of cwnd and SHOULD NOT delay retransmission.
|
||||
*/
|
||||
if (chunk->fast_retransmit <= 0)
|
||||
if (transport->flight_size >= transport->cwnd) {
|
||||
retval = SCTP_XMIT_RWND_FULL;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
/* Nagle's algorithm to solve small-packet problem:
|
||||
* Inhibit the sending of new chunks when new outgoing data arrives
|
||||
* if any previously transmitted data on the connection remains
|
||||
* unacknowledged.
|
||||
*/
|
||||
if (!sp->nodelay && sctp_packet_empty(packet) &&
|
||||
q->outstanding_bytes && sctp_state(asoc, ESTABLISHED)) {
|
||||
unsigned len = datasize + q->out_qlen;
|
||||
|
||||
/* Check whether this chunk and all the rest of pending
|
||||
* data will fit or delay in hopes of bundling a full
|
||||
* sized packet.
|
||||
*/
|
||||
if (len < asoc->frag_point) {
|
||||
retval = SCTP_XMIT_NAGLE_DELAY;
|
||||
goto finish;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep track of how many bytes are in flight over this transport. */
|
||||
transport->flight_size += datasize;
|
||||
|
||||
/* Keep track of how many bytes are in flight to the receiver. */
|
||||
asoc->outqueue.outstanding_bytes += datasize;
|
||||
|
||||
/* Update our view of the receiver's rwnd. Include sk_buff overhead
|
||||
* while updating peer.rwnd so that it reduces the chances of a
|
||||
* receiver running out of receive buffer space even when receive
|
||||
* window is still open. This can happen when a sender is sending
|
||||
* sending small messages.
|
||||
*/
|
||||
datasize += sizeof(struct sk_buff);
|
||||
if (datasize < rwnd)
|
||||
rwnd -= datasize;
|
||||
else
|
||||
rwnd = 0;
|
||||
|
||||
asoc->peer.rwnd = rwnd;
|
||||
/* Has been accepted for transmission. */
|
||||
if (!asoc->peer.prsctp_capable)
|
||||
chunk->msg->can_abandon = 0;
|
||||
|
||||
finish:
|
||||
return retval;
|
||||
}
|
||||
1769
net/sctp/outqueue.c
Normal file
1769
net/sctp/outqueue.c
Normal file
File diff suppressed because it is too large
Load Diff
219
net/sctp/primitive.c
Normal file
219
net/sctp/primitive.c
Normal file
@@ -0,0 +1,219 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* Copyright (c) 1999-2000 Cisco, Inc.
|
||||
* Copyright (c) 1999-2001 Motorola, Inc.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* These functions implement the SCTP primitive functions from Section 10.
|
||||
*
|
||||
* Note that the descriptions from the specification are USER level
|
||||
* functions--this file is the functions which populate the struct proto
|
||||
* for SCTP which is the BOTTOM of the sockets interface.
|
||||
*
|
||||
* 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>
|
||||
* Narasimha Budihal <narasimha@refcode.org>
|
||||
* Karl Knutson <karl@athena.chicago.il.us>
|
||||
* Ardelle Fan <ardelle.fan@intel.com>
|
||||
* Kevin Gao <kevin.gao@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/types.h>
|
||||
#include <linux/list.h> /* For struct list_head */
|
||||
#include <linux/socket.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/time.h> /* For struct timeval */
|
||||
#include <net/sock.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
#define DECLARE_PRIMITIVE(name) \
|
||||
/* This is called in the code as sctp_primitive_ ## name. */ \
|
||||
int sctp_primitive_ ## name(struct sctp_association *asoc, \
|
||||
void *arg) { \
|
||||
int error = 0; \
|
||||
sctp_event_t event_type; sctp_subtype_t subtype; \
|
||||
sctp_state_t state; \
|
||||
struct sctp_endpoint *ep; \
|
||||
\
|
||||
event_type = SCTP_EVENT_T_PRIMITIVE; \
|
||||
subtype = SCTP_ST_PRIMITIVE(SCTP_PRIMITIVE_ ## name); \
|
||||
state = asoc ? asoc->state : SCTP_STATE_CLOSED; \
|
||||
ep = asoc ? asoc->ep : NULL; \
|
||||
\
|
||||
error = sctp_do_sm(event_type, subtype, state, ep, asoc, \
|
||||
arg, GFP_KERNEL); \
|
||||
return error; \
|
||||
}
|
||||
|
||||
/* 10.1 ULP-to-SCTP
|
||||
* B) Associate
|
||||
*
|
||||
* Format: ASSOCIATE(local SCTP instance name, destination transport addr,
|
||||
* outbound stream count)
|
||||
* -> association id [,destination transport addr list] [,outbound stream
|
||||
* count]
|
||||
*
|
||||
* This primitive allows the upper layer to initiate an association to a
|
||||
* specific peer endpoint.
|
||||
*
|
||||
* This version assumes that asoc is fully populated with the initial
|
||||
* parameters. We then return a traditional kernel indicator of
|
||||
* success or failure.
|
||||
*/
|
||||
|
||||
/* This is called in the code as sctp_primitive_ASSOCIATE. */
|
||||
|
||||
DECLARE_PRIMITIVE(ASSOCIATE)
|
||||
|
||||
/* 10.1 ULP-to-SCTP
|
||||
* C) Shutdown
|
||||
*
|
||||
* Format: SHUTDOWN(association id)
|
||||
* -> result
|
||||
*
|
||||
* Gracefully closes an association. Any locally queued user data
|
||||
* will be delivered to the peer. The association will be terminated only
|
||||
* after the peer acknowledges all the SCTP packets sent. A success code
|
||||
* will be returned on successful termination of the association. If
|
||||
* attempting to terminate the association results in a failure, an error
|
||||
* code shall be returned.
|
||||
*/
|
||||
|
||||
DECLARE_PRIMITIVE(SHUTDOWN);
|
||||
|
||||
/* 10.1 ULP-to-SCTP
|
||||
* C) Abort
|
||||
*
|
||||
* Format: Abort(association id [, cause code])
|
||||
* -> result
|
||||
*
|
||||
* Ungracefully closes an association. Any locally queued user data
|
||||
* will be discarded and an ABORT chunk is sent to the peer. A success
|
||||
* code will be returned on successful abortion of the association. If
|
||||
* attempting to abort the association results in a failure, an error
|
||||
* code shall be returned.
|
||||
*/
|
||||
|
||||
DECLARE_PRIMITIVE(ABORT);
|
||||
|
||||
/* 10.1 ULP-to-SCTP
|
||||
* E) Send
|
||||
*
|
||||
* Format: SEND(association id, buffer address, byte count [,context]
|
||||
* [,stream id] [,life time] [,destination transport address]
|
||||
* [,unorder flag] [,no-bundle flag] [,payload protocol-id] )
|
||||
* -> result
|
||||
*
|
||||
* This is the main method to send user data via SCTP.
|
||||
*
|
||||
* Mandatory attributes:
|
||||
*
|
||||
* o association id - local handle to the SCTP association
|
||||
*
|
||||
* o buffer address - the location where the user message to be
|
||||
* transmitted is stored;
|
||||
*
|
||||
* o byte count - The size of the user data in number of bytes;
|
||||
*
|
||||
* Optional attributes:
|
||||
*
|
||||
* o context - an optional 32 bit integer that will be carried in the
|
||||
* sending failure notification to the ULP if the transportation of
|
||||
* this User Message fails.
|
||||
*
|
||||
* o stream id - to indicate which stream to send the data on. If not
|
||||
* specified, stream 0 will be used.
|
||||
*
|
||||
* o life time - specifies the life time of the user data. The user data
|
||||
* will not be sent by SCTP after the life time expires. This
|
||||
* parameter can be used to avoid efforts to transmit stale
|
||||
* user messages. SCTP notifies the ULP if the data cannot be
|
||||
* initiated to transport (i.e. sent to the destination via SCTP's
|
||||
* send primitive) within the life time variable. However, the
|
||||
* user data will be transmitted if SCTP has attempted to transmit a
|
||||
* chunk before the life time expired.
|
||||
*
|
||||
* o destination transport address - specified as one of the destination
|
||||
* transport addresses of the peer endpoint to which this packet
|
||||
* should be sent. Whenever possible, SCTP should use this destination
|
||||
* transport address for sending the packets, instead of the current
|
||||
* primary path.
|
||||
*
|
||||
* o unorder flag - this flag, if present, indicates that the user
|
||||
* would like the data delivered in an unordered fashion to the peer
|
||||
* (i.e., the U flag is set to 1 on all DATA chunks carrying this
|
||||
* message).
|
||||
*
|
||||
* o no-bundle flag - instructs SCTP not to bundle this user data with
|
||||
* other outbound DATA chunks. SCTP MAY still bundle even when
|
||||
* this flag is present, when faced with network congestion.
|
||||
*
|
||||
* o payload protocol-id - A 32 bit unsigned integer that is to be
|
||||
* passed to the peer indicating the type of payload protocol data
|
||||
* being transmitted. This value is passed as opaque data by SCTP.
|
||||
*/
|
||||
|
||||
DECLARE_PRIMITIVE(SEND);
|
||||
|
||||
/* 10.1 ULP-to-SCTP
|
||||
* J) Request Heartbeat
|
||||
*
|
||||
* Format: REQUESTHEARTBEAT(association id, destination transport address)
|
||||
*
|
||||
* -> result
|
||||
*
|
||||
* Instructs the local endpoint to perform a HeartBeat on the specified
|
||||
* destination transport address of the given association. The returned
|
||||
* result should indicate whether the transmission of the HEARTBEAT
|
||||
* chunk to the destination address is successful.
|
||||
*
|
||||
* Mandatory attributes:
|
||||
*
|
||||
* o association id - local handle to the SCTP association
|
||||
*
|
||||
* o destination transport address - the transport address of the
|
||||
* association on which a heartbeat should be issued.
|
||||
*/
|
||||
|
||||
DECLARE_PRIMITIVE(REQUESTHEARTBEAT);
|
||||
|
||||
/* ADDIP
|
||||
* 3.1.1 Address Configuration Change Chunk (ASCONF)
|
||||
*
|
||||
* This chunk is used to communicate to the remote endpoint one of the
|
||||
* configuration change requests that MUST be acknowledged. The
|
||||
* information carried in the ASCONF Chunk uses the form of a
|
||||
* Type-Length-Value (TLV), as described in "3.2.1 Optional/
|
||||
* Variable-length Parameter Format" in RFC2960 [5], forall variable
|
||||
* parameters.
|
||||
*/
|
||||
|
||||
DECLARE_PRIMITIVE(ASCONF);
|
||||
402
net/sctp/proc.c
Normal file
402
net/sctp/proc.c
Normal file
@@ -0,0 +1,402 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* Copyright (c) 2003 International Business Machines, Corp.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* 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:
|
||||
* Sridhar Samudrala <sri@us.ibm.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/types.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/init.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
|
||||
static struct snmp_mib sctp_snmp_list[] = {
|
||||
SNMP_MIB_ITEM("SctpCurrEstab", SCTP_MIB_CURRESTAB),
|
||||
SNMP_MIB_ITEM("SctpActiveEstabs", SCTP_MIB_ACTIVEESTABS),
|
||||
SNMP_MIB_ITEM("SctpPassiveEstabs", SCTP_MIB_PASSIVEESTABS),
|
||||
SNMP_MIB_ITEM("SctpAborteds", SCTP_MIB_ABORTEDS),
|
||||
SNMP_MIB_ITEM("SctpShutdowns", SCTP_MIB_SHUTDOWNS),
|
||||
SNMP_MIB_ITEM("SctpOutOfBlues", SCTP_MIB_OUTOFBLUES),
|
||||
SNMP_MIB_ITEM("SctpChecksumErrors", SCTP_MIB_CHECKSUMERRORS),
|
||||
SNMP_MIB_ITEM("SctpOutCtrlChunks", SCTP_MIB_OUTCTRLCHUNKS),
|
||||
SNMP_MIB_ITEM("SctpOutOrderChunks", SCTP_MIB_OUTORDERCHUNKS),
|
||||
SNMP_MIB_ITEM("SctpOutUnorderChunks", SCTP_MIB_OUTUNORDERCHUNKS),
|
||||
SNMP_MIB_ITEM("SctpInCtrlChunks", SCTP_MIB_INCTRLCHUNKS),
|
||||
SNMP_MIB_ITEM("SctpInOrderChunks", SCTP_MIB_INORDERCHUNKS),
|
||||
SNMP_MIB_ITEM("SctpInUnorderChunks", SCTP_MIB_INUNORDERCHUNKS),
|
||||
SNMP_MIB_ITEM("SctpFragUsrMsgs", SCTP_MIB_FRAGUSRMSGS),
|
||||
SNMP_MIB_ITEM("SctpReasmUsrMsgs", SCTP_MIB_REASMUSRMSGS),
|
||||
SNMP_MIB_ITEM("SctpOutSCTPPacks", SCTP_MIB_OUTSCTPPACKS),
|
||||
SNMP_MIB_ITEM("SctpInSCTPPacks", SCTP_MIB_INSCTPPACKS),
|
||||
SNMP_MIB_ITEM("SctpT1InitExpireds", SCTP_MIB_T1_INIT_EXPIREDS),
|
||||
SNMP_MIB_ITEM("SctpT1CookieExpireds", SCTP_MIB_T1_COOKIE_EXPIREDS),
|
||||
SNMP_MIB_ITEM("SctpT2ShutdownExpireds", SCTP_MIB_T2_SHUTDOWN_EXPIREDS),
|
||||
SNMP_MIB_ITEM("SctpT3RtxExpireds", SCTP_MIB_T3_RTX_EXPIREDS),
|
||||
SNMP_MIB_ITEM("SctpT4RtoExpireds", SCTP_MIB_T4_RTO_EXPIREDS),
|
||||
SNMP_MIB_ITEM("SctpT5ShutdownGuardExpireds", SCTP_MIB_T5_SHUTDOWN_GUARD_EXPIREDS),
|
||||
SNMP_MIB_ITEM("SctpDelaySackExpireds", SCTP_MIB_DELAY_SACK_EXPIREDS),
|
||||
SNMP_MIB_ITEM("SctpAutocloseExpireds", SCTP_MIB_AUTOCLOSE_EXPIREDS),
|
||||
SNMP_MIB_ITEM("SctpT3Retransmits", SCTP_MIB_T3_RETRANSMITS),
|
||||
SNMP_MIB_ITEM("SctpPmtudRetransmits", SCTP_MIB_PMTUD_RETRANSMITS),
|
||||
SNMP_MIB_ITEM("SctpFastRetransmits", SCTP_MIB_FAST_RETRANSMITS),
|
||||
SNMP_MIB_ITEM("SctpInPktSoftirq", SCTP_MIB_IN_PKT_SOFTIRQ),
|
||||
SNMP_MIB_ITEM("SctpInPktBacklog", SCTP_MIB_IN_PKT_BACKLOG),
|
||||
SNMP_MIB_ITEM("SctpInPktDiscards", SCTP_MIB_IN_PKT_DISCARDS),
|
||||
SNMP_MIB_ITEM("SctpInDataChunkDiscards", SCTP_MIB_IN_DATA_CHUNK_DISCARDS),
|
||||
SNMP_MIB_SENTINEL
|
||||
};
|
||||
|
||||
/* Return the current value of a particular entry in the mib by adding its
|
||||
* per cpu counters.
|
||||
*/
|
||||
static unsigned long
|
||||
fold_field(void *mib[], int nr)
|
||||
{
|
||||
unsigned long res = 0;
|
||||
int i;
|
||||
|
||||
for_each_possible_cpu(i) {
|
||||
res +=
|
||||
*((unsigned long *) (((void *) per_cpu_ptr(mib[0], i)) +
|
||||
sizeof (unsigned long) * nr));
|
||||
res +=
|
||||
*((unsigned long *) (((void *) per_cpu_ptr(mib[1], i)) +
|
||||
sizeof (unsigned long) * nr));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Display sctp snmp mib statistics(/proc/net/sctp/snmp). */
|
||||
static int sctp_snmp_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; sctp_snmp_list[i].name != NULL; i++)
|
||||
seq_printf(seq, "%-32s\t%ld\n", sctp_snmp_list[i].name,
|
||||
fold_field((void **)sctp_statistics,
|
||||
sctp_snmp_list[i].entry));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize the seq file operations for 'snmp' object. */
|
||||
static int sctp_snmp_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, sctp_snmp_seq_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations sctp_snmp_seq_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = sctp_snmp_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
/* Set up the proc fs entry for 'snmp' object. */
|
||||
int __init sctp_snmp_proc_init(void)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
|
||||
p = create_proc_entry("snmp", S_IRUGO, proc_net_sctp);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->proc_fops = &sctp_snmp_seq_fops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup the proc fs entry for 'snmp' object. */
|
||||
void sctp_snmp_proc_exit(void)
|
||||
{
|
||||
remove_proc_entry("snmp", proc_net_sctp);
|
||||
}
|
||||
|
||||
/* Dump local addresses of an association/endpoint. */
|
||||
static void sctp_seq_dump_local_addrs(struct seq_file *seq, struct sctp_ep_common *epb)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct sctp_association *asoc;
|
||||
struct sctp_sockaddr_entry *laddr;
|
||||
struct sctp_transport *peer;
|
||||
union sctp_addr *addr, *primary = NULL;
|
||||
struct sctp_af *af;
|
||||
|
||||
if (epb->type == SCTP_EP_TYPE_ASSOCIATION) {
|
||||
asoc = sctp_assoc(epb);
|
||||
peer = asoc->peer.primary_path;
|
||||
primary = &peer->saddr;
|
||||
}
|
||||
|
||||
list_for_each(pos, &epb->bind_addr.address_list) {
|
||||
laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
|
||||
addr = &laddr->a;
|
||||
af = sctp_get_af_specific(addr->sa.sa_family);
|
||||
if (primary && af->cmp_addr(addr, primary)) {
|
||||
seq_printf(seq, "*");
|
||||
}
|
||||
af->seq_dump_addr(seq, addr);
|
||||
}
|
||||
}
|
||||
|
||||
/* Dump remote addresses of an association. */
|
||||
static void sctp_seq_dump_remote_addrs(struct seq_file *seq, struct sctp_association *assoc)
|
||||
{
|
||||
struct list_head *pos;
|
||||
struct sctp_transport *transport;
|
||||
union sctp_addr *addr, *primary;
|
||||
struct sctp_af *af;
|
||||
|
||||
primary = &assoc->peer.primary_addr;
|
||||
list_for_each(pos, &assoc->peer.transport_addr_list) {
|
||||
transport = list_entry(pos, struct sctp_transport, transports);
|
||||
addr = &transport->ipaddr;
|
||||
af = sctp_get_af_specific(addr->sa.sa_family);
|
||||
if (af->cmp_addr(addr, primary)) {
|
||||
seq_printf(seq, "*");
|
||||
}
|
||||
af->seq_dump_addr(seq, addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void * sctp_eps_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
if (*pos >= sctp_ep_hashsize)
|
||||
return NULL;
|
||||
|
||||
if (*pos < 0)
|
||||
*pos = 0;
|
||||
|
||||
if (*pos == 0)
|
||||
seq_printf(seq, " ENDPT SOCK STY SST HBKT LPORT UID INODE LADDRS\n");
|
||||
|
||||
return (void *)pos;
|
||||
}
|
||||
|
||||
static void sctp_eps_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void * sctp_eps_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
if (++*pos >= sctp_ep_hashsize)
|
||||
return NULL;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
||||
/* Display sctp endpoints (/proc/net/sctp/eps). */
|
||||
static int sctp_eps_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct sctp_hashbucket *head;
|
||||
struct sctp_ep_common *epb;
|
||||
struct sctp_endpoint *ep;
|
||||
struct sock *sk;
|
||||
int hash = *(loff_t *)v;
|
||||
|
||||
if (hash >= sctp_ep_hashsize)
|
||||
return -ENOMEM;
|
||||
|
||||
head = &sctp_ep_hashtable[hash];
|
||||
sctp_local_bh_disable();
|
||||
read_lock(&head->lock);
|
||||
for (epb = head->chain; epb; epb = epb->next) {
|
||||
ep = sctp_ep(epb);
|
||||
sk = epb->sk;
|
||||
seq_printf(seq, "%8p %8p %-3d %-3d %-4d %-5d %5d %5lu ", ep, sk,
|
||||
sctp_sk(sk)->type, sk->sk_state, hash,
|
||||
epb->bind_addr.port,
|
||||
sock_i_uid(sk), sock_i_ino(sk));
|
||||
|
||||
sctp_seq_dump_local_addrs(seq, epb);
|
||||
seq_printf(seq, "\n");
|
||||
}
|
||||
read_unlock(&head->lock);
|
||||
sctp_local_bh_enable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations sctp_eps_ops = {
|
||||
.start = sctp_eps_seq_start,
|
||||
.next = sctp_eps_seq_next,
|
||||
.stop = sctp_eps_seq_stop,
|
||||
.show = sctp_eps_seq_show,
|
||||
};
|
||||
|
||||
|
||||
/* Initialize the seq file operations for 'eps' object. */
|
||||
static int sctp_eps_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &sctp_eps_ops);
|
||||
}
|
||||
|
||||
static const struct file_operations sctp_eps_seq_fops = {
|
||||
.open = sctp_eps_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
/* Set up the proc fs entry for 'eps' object. */
|
||||
int __init sctp_eps_proc_init(void)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
|
||||
p = create_proc_entry("eps", S_IRUGO, proc_net_sctp);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->proc_fops = &sctp_eps_seq_fops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup the proc fs entry for 'eps' object. */
|
||||
void sctp_eps_proc_exit(void)
|
||||
{
|
||||
remove_proc_entry("eps", proc_net_sctp);
|
||||
}
|
||||
|
||||
|
||||
static void * sctp_assocs_seq_start(struct seq_file *seq, loff_t *pos)
|
||||
{
|
||||
if (*pos >= sctp_assoc_hashsize)
|
||||
return NULL;
|
||||
|
||||
if (*pos < 0)
|
||||
*pos = 0;
|
||||
|
||||
if (*pos == 0)
|
||||
seq_printf(seq, " ASSOC SOCK STY SST ST HBKT ASSOC-ID TX_QUEUE RX_QUEUE UID INODE LPORT "
|
||||
"RPORT LADDRS <-> RADDRS\n");
|
||||
|
||||
return (void *)pos;
|
||||
}
|
||||
|
||||
static void sctp_assocs_seq_stop(struct seq_file *seq, void *v)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
static void * sctp_assocs_seq_next(struct seq_file *seq, void *v, loff_t *pos)
|
||||
{
|
||||
if (++*pos >= sctp_assoc_hashsize)
|
||||
return NULL;
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
/* Display sctp associations (/proc/net/sctp/assocs). */
|
||||
static int sctp_assocs_seq_show(struct seq_file *seq, void *v)
|
||||
{
|
||||
struct sctp_hashbucket *head;
|
||||
struct sctp_ep_common *epb;
|
||||
struct sctp_association *assoc;
|
||||
struct sock *sk;
|
||||
int hash = *(loff_t *)v;
|
||||
|
||||
if (hash >= sctp_assoc_hashsize)
|
||||
return -ENOMEM;
|
||||
|
||||
head = &sctp_assoc_hashtable[hash];
|
||||
sctp_local_bh_disable();
|
||||
read_lock(&head->lock);
|
||||
for (epb = head->chain; epb; epb = epb->next) {
|
||||
assoc = sctp_assoc(epb);
|
||||
sk = epb->sk;
|
||||
seq_printf(seq,
|
||||
"%8p %8p %-3d %-3d %-2d %-4d %4d %8d %8d %7d %5lu %-5d %5d ",
|
||||
assoc, sk, sctp_sk(sk)->type, sk->sk_state,
|
||||
assoc->state, hash, assoc->assoc_id,
|
||||
assoc->sndbuf_used,
|
||||
atomic_read(&assoc->rmem_alloc),
|
||||
sock_i_uid(sk), sock_i_ino(sk),
|
||||
epb->bind_addr.port,
|
||||
assoc->peer.port);
|
||||
|
||||
seq_printf(seq, " ");
|
||||
sctp_seq_dump_local_addrs(seq, epb);
|
||||
seq_printf(seq, "<-> ");
|
||||
sctp_seq_dump_remote_addrs(seq, assoc);
|
||||
seq_printf(seq, "\n");
|
||||
}
|
||||
read_unlock(&head->lock);
|
||||
sctp_local_bh_enable();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct seq_operations sctp_assoc_ops = {
|
||||
.start = sctp_assocs_seq_start,
|
||||
.next = sctp_assocs_seq_next,
|
||||
.stop = sctp_assocs_seq_stop,
|
||||
.show = sctp_assocs_seq_show,
|
||||
};
|
||||
|
||||
/* Initialize the seq file operations for 'assocs' object. */
|
||||
static int sctp_assocs_seq_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return seq_open(file, &sctp_assoc_ops);
|
||||
}
|
||||
|
||||
static const struct file_operations sctp_assocs_seq_fops = {
|
||||
.open = sctp_assocs_seq_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = seq_release,
|
||||
};
|
||||
|
||||
/* Set up the proc fs entry for 'assocs' object. */
|
||||
int __init sctp_assocs_proc_init(void)
|
||||
{
|
||||
struct proc_dir_entry *p;
|
||||
|
||||
p = create_proc_entry("assocs", S_IRUGO, proc_net_sctp);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
p->proc_fops = &sctp_assocs_seq_fops;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Cleanup the proc fs entry for 'assocs' object. */
|
||||
void sctp_assocs_proc_exit(void)
|
||||
{
|
||||
remove_proc_entry("assocs", proc_net_sctp);
|
||||
}
|
||||
1264
net/sctp/protocol.c
Normal file
1264
net/sctp/protocol.c
Normal file
File diff suppressed because it is too large
Load Diff
2818
net/sctp/sm_make_chunk.c
Normal file
2818
net/sctp/sm_make_chunk.c
Normal file
File diff suppressed because it is too large
Load Diff
1500
net/sctp/sm_sideeffect.c
Normal file
1500
net/sctp/sm_sideeffect.c
Normal file
File diff suppressed because it is too large
Load Diff
5432
net/sctp/sm_statefuns.c
Normal file
5432
net/sctp/sm_statefuns.c
Normal file
File diff suppressed because it is too large
Load Diff
980
net/sctp/sm_statetable.c
Normal file
980
net/sctp/sm_statetable.c
Normal file
@@ -0,0 +1,980 @@
|
||||
/* 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.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* These are the state tables for the SCTP state machine.
|
||||
*
|
||||
* 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>
|
||||
* Hui Huang <hui.huang@nokia.com>
|
||||
* Daisy Chang <daisyc@us.ibm.com>
|
||||
* Ardelle Fan <ardelle.fan@intel.com>
|
||||
* Sridhar Samudrala <sri@us.ibm.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/skbuff.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
static const sctp_sm_table_entry_t
|
||||
primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES];
|
||||
static const sctp_sm_table_entry_t
|
||||
other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES];
|
||||
static const sctp_sm_table_entry_t
|
||||
timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES];
|
||||
|
||||
static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid,
|
||||
sctp_state_t state);
|
||||
|
||||
|
||||
static const sctp_sm_table_entry_t bug = {
|
||||
.fn = sctp_sf_bug,
|
||||
.name = "sctp_sf_bug"
|
||||
};
|
||||
|
||||
#define DO_LOOKUP(_max, _type, _table) \
|
||||
if ((event_subtype._type > (_max))) { \
|
||||
printk(KERN_WARNING \
|
||||
"sctp table %p possible attack:" \
|
||||
" event %d exceeds max %d\n", \
|
||||
_table, event_subtype._type, _max); \
|
||||
return &bug; \
|
||||
} \
|
||||
return &_table[event_subtype._type][(int)state];
|
||||
|
||||
const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
|
||||
sctp_state_t state,
|
||||
sctp_subtype_t event_subtype)
|
||||
{
|
||||
switch (event_type) {
|
||||
case SCTP_EVENT_T_CHUNK:
|
||||
return sctp_chunk_event_lookup(event_subtype.chunk, state);
|
||||
break;
|
||||
case SCTP_EVENT_T_TIMEOUT:
|
||||
DO_LOOKUP(SCTP_EVENT_TIMEOUT_MAX, timeout,
|
||||
timeout_event_table);
|
||||
break;
|
||||
|
||||
case SCTP_EVENT_T_OTHER:
|
||||
DO_LOOKUP(SCTP_EVENT_OTHER_MAX, other, other_event_table);
|
||||
break;
|
||||
|
||||
case SCTP_EVENT_T_PRIMITIVE:
|
||||
DO_LOOKUP(SCTP_EVENT_PRIMITIVE_MAX, primitive,
|
||||
primitive_event_table);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Yikes! We got an illegal event type. */
|
||||
return &bug;
|
||||
};
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_FUNC(func) {.fn = func, .name = #func}
|
||||
|
||||
#define TYPE_SCTP_DATA { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_data_6_2), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_data_6_2), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_data_fast_4_4), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_DATA */
|
||||
|
||||
#define TYPE_SCTP_INIT { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_1B_init), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_1_siminit), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_1_siminit), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_2_dupinit), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_2_dupinit), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_2_dupinit), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_2_dupinit), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_2_reshutack), \
|
||||
} /* TYPE_SCTP_INIT */
|
||||
|
||||
#define TYPE_SCTP_INIT_ACK { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_3_initack), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_1C_ack), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_INIT_ACK */
|
||||
|
||||
#define TYPE_SCTP_SACK { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_sack_6_2), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_sack_6_2), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_sack_6_2), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_sack_6_2), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_SACK */
|
||||
|
||||
#define TYPE_SCTP_HEARTBEAT { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_beat_8_3), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_beat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_beat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_beat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_beat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
/* This should not happen, but we are nice. */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_beat_8_3), \
|
||||
} /* TYPE_SCTP_HEARTBEAT */
|
||||
|
||||
#define TYPE_SCTP_HEARTBEAT_ACK { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_violation), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_backbeat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_backbeat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_backbeat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_backbeat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_HEARTBEAT_ACK */
|
||||
|
||||
#define TYPE_SCTP_ABORT { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_pdiscard), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_cookie_wait_abort), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_cookie_echoed_abort), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_1_abort), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_shutdown_pending_abort), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_shutdown_sent_abort), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_1_abort), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_shutdown_ack_sent_abort), \
|
||||
} /* TYPE_SCTP_ABORT */
|
||||
|
||||
#define TYPE_SCTP_SHUTDOWN { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_2_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_2_shutdown_ack), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_SHUTDOWN */
|
||||
|
||||
#define TYPE_SCTP_SHUTDOWN_ACK { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_8_5_1_E_sa), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_8_5_1_E_sa), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_violation), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_violation), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_2_final), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_violation), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_2_final), \
|
||||
} /* TYPE_SCTP_SHUTDOWN_ACK */
|
||||
|
||||
#define TYPE_SCTP_ERROR { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_cookie_echoed_err), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_operr_notify), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_operr_notify), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_operr_notify), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_ERROR */
|
||||
|
||||
#define TYPE_SCTP_COOKIE_ECHO { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_1D_ce), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_4_dupcook), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_4_dupcook), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_4_dupcook), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_4_dupcook), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_4_dupcook), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_4_dupcook), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_2_4_dupcook), \
|
||||
} /* TYPE_SCTP_COOKIE_ECHO */
|
||||
|
||||
#define TYPE_SCTP_COOKIE_ACK { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_5_1E_ca), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_COOKIE_ACK */
|
||||
|
||||
#define TYPE_SCTP_ECN_ECNE { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_ecne), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_ecne), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_ecne), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_ecne), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_ecne), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_ECN_ECNE */
|
||||
|
||||
#define TYPE_SCTP_ECN_CWR { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_ecn_cwr), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_ecn_cwr), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_ecn_cwr), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_ECN_CWR */
|
||||
|
||||
#define TYPE_SCTP_SHUTDOWN_COMPLETE { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_4_C), \
|
||||
} /* TYPE_SCTP_SHUTDOWN_COMPLETE */
|
||||
|
||||
/* The primary index for this table is the chunk type.
|
||||
* The secondary index for this table is the state.
|
||||
*
|
||||
* For base protocol (RFC 2960).
|
||||
*/
|
||||
static const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_DATA,
|
||||
TYPE_SCTP_INIT,
|
||||
TYPE_SCTP_INIT_ACK,
|
||||
TYPE_SCTP_SACK,
|
||||
TYPE_SCTP_HEARTBEAT,
|
||||
TYPE_SCTP_HEARTBEAT_ACK,
|
||||
TYPE_SCTP_ABORT,
|
||||
TYPE_SCTP_SHUTDOWN,
|
||||
TYPE_SCTP_SHUTDOWN_ACK,
|
||||
TYPE_SCTP_ERROR,
|
||||
TYPE_SCTP_COOKIE_ECHO,
|
||||
TYPE_SCTP_COOKIE_ACK,
|
||||
TYPE_SCTP_ECN_ECNE,
|
||||
TYPE_SCTP_ECN_CWR,
|
||||
TYPE_SCTP_SHUTDOWN_COMPLETE,
|
||||
}; /* state_fn_t chunk_event_table[][] */
|
||||
|
||||
#define TYPE_SCTP_ASCONF { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_asconf), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_ASCONF */
|
||||
|
||||
#define TYPE_SCTP_ASCONF_ACK { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_asconf_ack), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_ASCONF_ACK */
|
||||
|
||||
/* The primary index for this table is the chunk type.
|
||||
* The secondary index for this table is the state.
|
||||
*/
|
||||
static const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_ASCONF,
|
||||
TYPE_SCTP_ASCONF_ACK,
|
||||
}; /*state_fn_t addip_chunk_event_table[][] */
|
||||
|
||||
#define TYPE_SCTP_FWD_TSN { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_fwd_tsn), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_fwd_tsn), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_eat_fwd_tsn_fast), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_discard_chunk), \
|
||||
} /* TYPE_SCTP_FWD_TSN */
|
||||
|
||||
/* The primary index for this table is the chunk type.
|
||||
* The secondary index for this table is the state.
|
||||
*/
|
||||
static const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_FWD_TSN,
|
||||
}; /*state_fn_t prsctp_chunk_event_table[][] */
|
||||
|
||||
static const sctp_sm_table_entry_t
|
||||
chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
|
||||
/* SCTP_STATE_EMPTY */
|
||||
TYPE_SCTP_FUNC(sctp_sf_ootb),
|
||||
/* SCTP_STATE_CLOSED */
|
||||
TYPE_SCTP_FUNC(sctp_sf_tabort_8_4_8),
|
||||
/* SCTP_STATE_COOKIE_WAIT */
|
||||
TYPE_SCTP_FUNC(sctp_sf_unk_chunk),
|
||||
/* SCTP_STATE_COOKIE_ECHOED */
|
||||
TYPE_SCTP_FUNC(sctp_sf_unk_chunk),
|
||||
/* SCTP_STATE_ESTABLISHED */
|
||||
TYPE_SCTP_FUNC(sctp_sf_unk_chunk),
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */
|
||||
TYPE_SCTP_FUNC(sctp_sf_unk_chunk),
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */
|
||||
TYPE_SCTP_FUNC(sctp_sf_unk_chunk),
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */
|
||||
TYPE_SCTP_FUNC(sctp_sf_unk_chunk),
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */
|
||||
TYPE_SCTP_FUNC(sctp_sf_unk_chunk),
|
||||
}; /* chunk unknown */
|
||||
|
||||
|
||||
#define TYPE_SCTP_PRIMITIVE_ASSOCIATE { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_asoc), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_not_impl), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_not_impl), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_not_impl), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_not_impl), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_not_impl), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_not_impl), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_not_impl), \
|
||||
} /* TYPE_SCTP_PRIMITIVE_ASSOCIATE */
|
||||
|
||||
#define TYPE_SCTP_PRIMITIVE_SHUTDOWN { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_closed), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_cookie_wait_prm_shutdown), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_cookie_echoed_prm_shutdown),\
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_2_prm_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_primitive), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_primitive), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_primitive), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_primitive), \
|
||||
} /* TYPE_SCTP_PRIMITIVE_SHUTDOWN */
|
||||
|
||||
#define TYPE_SCTP_PRIMITIVE_ABORT { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_closed), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_cookie_wait_prm_abort), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_cookie_echoed_prm_abort), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_1_prm_abort), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_shutdown_pending_prm_abort), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_shutdown_sent_prm_abort), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_1_prm_abort), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_shutdown_ack_sent_prm_abort), \
|
||||
} /* TYPE_SCTP_PRIMITIVE_ABORT */
|
||||
|
||||
#define TYPE_SCTP_PRIMITIVE_SEND { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_closed), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_send), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
|
||||
} /* TYPE_SCTP_PRIMITIVE_SEND */
|
||||
|
||||
#define TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_closed), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_requestheartbeat), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_requestheartbeat), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_requestheartbeat), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_requestheartbeat), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_requestheartbeat), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_requestheartbeat), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_requestheartbeat), \
|
||||
} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
|
||||
|
||||
#define TYPE_SCTP_PRIMITIVE_ASCONF { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_closed), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_closed), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_closed), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_prm_asconf), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_error_shutdown), \
|
||||
} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
|
||||
|
||||
/* The primary index for this table is the primitive type.
|
||||
* The secondary index for this table is the state.
|
||||
*/
|
||||
static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_PRIMITIVE_ASSOCIATE,
|
||||
TYPE_SCTP_PRIMITIVE_SHUTDOWN,
|
||||
TYPE_SCTP_PRIMITIVE_ABORT,
|
||||
TYPE_SCTP_PRIMITIVE_SEND,
|
||||
TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT,
|
||||
TYPE_SCTP_PRIMITIVE_ASCONF,
|
||||
};
|
||||
|
||||
#define TYPE_SCTP_OTHER_NO_PENDING_TSN { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_2_start_shutdown), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_9_2_shutdown_ack), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_OTHER_ICMP_PROTO_UNREACH { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_cookie_wait_icmp_abort), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_ignore_other), \
|
||||
}
|
||||
|
||||
static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_OTHER_NO_PENDING_TSN,
|
||||
TYPE_SCTP_OTHER_ICMP_PROTO_UNREACH,
|
||||
};
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_NONE { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_t1_cookie_timer_expire), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_T1_INIT { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_t1_init_timer_expire), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_t2_timer_expire), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_t2_timer_expire), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_T3_RTX { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_6_3_3_rtx), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_6_3_3_rtx), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_6_3_3_rtx), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_6_3_3_rtx), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_T4_RTO { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_t4_timer_expire), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_t5_timer_expire), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_t5_timer_expire), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_sendbeat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_sendbeat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_sendbeat_8_3), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_SACK { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_bug), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_6_2_sack), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_6_2_sack), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_do_6_2_sack), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
#define TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE { \
|
||||
/* SCTP_STATE_EMPTY */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_CLOSED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_WAIT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_COOKIE_ECHOED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_ESTABLISHED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_autoclose_timer_expire), \
|
||||
/* SCTP_STATE_SHUTDOWN_PENDING */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
|
||||
TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \
|
||||
}
|
||||
|
||||
static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = {
|
||||
TYPE_SCTP_EVENT_TIMEOUT_NONE,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_T1_INIT,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_T3_RTX,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_T4_RTO,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_SACK,
|
||||
TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
|
||||
};
|
||||
|
||||
static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid,
|
||||
sctp_state_t state)
|
||||
{
|
||||
if (state > SCTP_STATE_MAX)
|
||||
return &bug;
|
||||
|
||||
if (cid >= 0 && cid <= SCTP_CID_BASE_MAX)
|
||||
return &chunk_event_table[cid][state];
|
||||
|
||||
if (sctp_prsctp_enable) {
|
||||
if (cid == SCTP_CID_FWD_TSN)
|
||||
return &prsctp_chunk_event_table[0][state];
|
||||
}
|
||||
|
||||
if (sctp_addip_enable) {
|
||||
if (cid == SCTP_CID_ASCONF)
|
||||
return &addip_chunk_event_table[0][state];
|
||||
|
||||
if (cid == SCTP_CID_ASCONF_ACK)
|
||||
return &addip_chunk_event_table[1][state];
|
||||
}
|
||||
|
||||
return &chunk_event_table_unknown[state];
|
||||
}
|
||||
5892
net/sctp/socket.c
Normal file
5892
net/sctp/socket.c
Normal file
File diff suppressed because it is too large
Load Diff
132
net/sctp/ssnmap.c
Normal file
132
net/sctp/ssnmap.c
Normal file
@@ -0,0 +1,132 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* Copyright (c) 2003 International Business Machines, Corp.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* These functions manipulate sctp SSN tracker.
|
||||
*
|
||||
* 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:
|
||||
* Jon Grimm <jgrimm@us.ibm.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/types.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
#define MAX_KMALLOC_SIZE 131072
|
||||
|
||||
static struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in,
|
||||
__u16 out);
|
||||
|
||||
/* Storage size needed for map includes 2 headers and then the
|
||||
* specific needs of in or out streams.
|
||||
*/
|
||||
static inline size_t sctp_ssnmap_size(__u16 in, __u16 out)
|
||||
{
|
||||
return sizeof(struct sctp_ssnmap) + (in + out) * sizeof(__u16);
|
||||
}
|
||||
|
||||
|
||||
/* Create a new sctp_ssnmap.
|
||||
* Allocate room to store at least 'len' contiguous TSNs.
|
||||
*/
|
||||
struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sctp_ssnmap *retval;
|
||||
int size;
|
||||
|
||||
size = sctp_ssnmap_size(in, out);
|
||||
if (size <= MAX_KMALLOC_SIZE)
|
||||
retval = kmalloc(size, gfp);
|
||||
else
|
||||
retval = (struct sctp_ssnmap *)
|
||||
__get_free_pages(gfp, get_order(size));
|
||||
if (!retval)
|
||||
goto fail;
|
||||
|
||||
if (!sctp_ssnmap_init(retval, in, out))
|
||||
goto fail_map;
|
||||
|
||||
retval->malloced = 1;
|
||||
SCTP_DBG_OBJCNT_INC(ssnmap);
|
||||
|
||||
return retval;
|
||||
|
||||
fail_map:
|
||||
if (size <= MAX_KMALLOC_SIZE)
|
||||
kfree(retval);
|
||||
else
|
||||
free_pages((unsigned long)retval, get_order(size));
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize a block of memory as a ssnmap. */
|
||||
static struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in,
|
||||
__u16 out)
|
||||
{
|
||||
memset(map, 0x00, sctp_ssnmap_size(in, out));
|
||||
|
||||
/* Start 'in' stream just after the map header. */
|
||||
map->in.ssn = (__u16 *)&map[1];
|
||||
map->in.len = in;
|
||||
|
||||
/* Start 'out' stream just after 'in'. */
|
||||
map->out.ssn = &map->in.ssn[in];
|
||||
map->out.len = out;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/* Clear out the ssnmap streams. */
|
||||
void sctp_ssnmap_clear(struct sctp_ssnmap *map)
|
||||
{
|
||||
size_t size;
|
||||
|
||||
size = (map->in.len + map->out.len) * sizeof(__u16);
|
||||
memset(map->in.ssn, 0x00, size);
|
||||
}
|
||||
|
||||
/* Dispose of a ssnmap. */
|
||||
void sctp_ssnmap_free(struct sctp_ssnmap *map)
|
||||
{
|
||||
if (map && map->malloced) {
|
||||
int size;
|
||||
|
||||
size = sctp_ssnmap_size(map->in.len, map->out.len);
|
||||
if (size <= MAX_KMALLOC_SIZE)
|
||||
kfree(map);
|
||||
else
|
||||
free_pages((unsigned long)map, get_order(size));
|
||||
SCTP_DBG_OBJCNT_DEC(ssnmap);
|
||||
}
|
||||
}
|
||||
264
net/sctp/sysctl.c
Normal file
264
net/sctp/sysctl.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* (C) Copyright IBM Corp. 2002, 2004
|
||||
* Copyright (c) 2002 Intel Corp.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* Sysctl related interfaces for SCTP.
|
||||
*
|
||||
* 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:
|
||||
* Mingqin Liu <liuming@us.ibm.com>
|
||||
* Jon Grimm <jgrimm@us.ibm.com>
|
||||
* Ardelle Fan <ardelle.fan@intel.com>
|
||||
* Ryan Layer <rmlayer@us.ibm.com>
|
||||
* Sridhar Samudrala <sri@us.ibm.com>
|
||||
*
|
||||
* Any bugs reported given to us we will try to fix... any fixes shared will
|
||||
* be incorporated into the next SCTP release.
|
||||
*/
|
||||
|
||||
#include <net/sctp/structs.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <linux/sysctl.h>
|
||||
|
||||
static int zero = 0;
|
||||
static int one = 1;
|
||||
static int timer_max = 86400000; /* ms in one day */
|
||||
static int int_max = INT_MAX;
|
||||
static long sack_timer_min = 1;
|
||||
static long sack_timer_max = 500;
|
||||
|
||||
static ctl_table sctp_table[] = {
|
||||
{
|
||||
.ctl_name = NET_SCTP_RTO_INITIAL,
|
||||
.procname = "rto_initial",
|
||||
.data = &sctp_rto_initial,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
.extra2 = &timer_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_RTO_MIN,
|
||||
.procname = "rto_min",
|
||||
.data = &sctp_rto_min,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
.extra2 = &timer_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_RTO_MAX,
|
||||
.procname = "rto_max",
|
||||
.data = &sctp_rto_max,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
.extra2 = &timer_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_VALID_COOKIE_LIFE,
|
||||
.procname = "valid_cookie_life",
|
||||
.data = &sctp_valid_cookie_life,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
.extra2 = &timer_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_MAX_BURST,
|
||||
.procname = "max_burst",
|
||||
.data = &sctp_max_burst,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &zero,
|
||||
.extra2 = &int_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_ASSOCIATION_MAX_RETRANS,
|
||||
.procname = "association_max_retrans",
|
||||
.data = &sctp_max_retrans_association,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
.extra2 = &int_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_SNDBUF_POLICY,
|
||||
.procname = "sndbuf_policy",
|
||||
.data = &sctp_sndbuf_policy,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.strategy = &sysctl_intvec
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_RCVBUF_POLICY,
|
||||
.procname = "rcvbuf_policy",
|
||||
.data = &sctp_rcvbuf_policy,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.strategy = &sysctl_intvec
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_PATH_MAX_RETRANS,
|
||||
.procname = "path_max_retrans",
|
||||
.data = &sctp_max_retrans_path,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
.extra2 = &int_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_MAX_INIT_RETRANSMITS,
|
||||
.procname = "max_init_retransmits",
|
||||
.data = &sctp_max_retrans_init,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
.extra2 = &int_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_HB_INTERVAL,
|
||||
.procname = "hb_interval",
|
||||
.data = &sctp_hb_interval,
|
||||
.maxlen = sizeof(unsigned int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &one,
|
||||
.extra2 = &timer_max
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_PRESERVE_ENABLE,
|
||||
.procname = "cookie_preserve_enable",
|
||||
.data = &sctp_cookie_preserve_enable,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.strategy = &sysctl_intvec
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_RTO_ALPHA,
|
||||
.procname = "rto_alpha_exp_divisor",
|
||||
.data = &sctp_rto_alpha,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.strategy = &sysctl_intvec
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_RTO_BETA,
|
||||
.procname = "rto_beta_exp_divisor",
|
||||
.data = &sctp_rto_beta,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0444,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.strategy = &sysctl_intvec
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_ADDIP_ENABLE,
|
||||
.procname = "addip_enable",
|
||||
.data = &sctp_addip_enable,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.strategy = &sysctl_intvec
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_PRSCTP_ENABLE,
|
||||
.procname = "prsctp_enable",
|
||||
.data = &sctp_prsctp_enable,
|
||||
.maxlen = sizeof(int),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec,
|
||||
.strategy = &sysctl_intvec
|
||||
},
|
||||
{
|
||||
.ctl_name = NET_SCTP_SACK_TIMEOUT,
|
||||
.procname = "sack_timeout",
|
||||
.data = &sctp_sack_timeout,
|
||||
.maxlen = sizeof(long),
|
||||
.mode = 0644,
|
||||
.proc_handler = &proc_dointvec_minmax,
|
||||
.strategy = &sysctl_intvec,
|
||||
.extra1 = &sack_timer_min,
|
||||
.extra2 = &sack_timer_max,
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table sctp_net_table[] = {
|
||||
{
|
||||
.ctl_name = NET_SCTP,
|
||||
.procname = "sctp",
|
||||
.mode = 0555,
|
||||
.child = sctp_table
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static ctl_table sctp_root_table[] = {
|
||||
{
|
||||
.ctl_name = CTL_NET,
|
||||
.procname = "net",
|
||||
.mode = 0555,
|
||||
.child = sctp_net_table
|
||||
},
|
||||
{ .ctl_name = 0 }
|
||||
};
|
||||
|
||||
static struct ctl_table_header * sctp_sysctl_header;
|
||||
|
||||
/* Sysctl registration. */
|
||||
void sctp_sysctl_register(void)
|
||||
{
|
||||
sctp_sysctl_header = register_sysctl_table(sctp_root_table);
|
||||
}
|
||||
|
||||
/* Sysctl deregistration. */
|
||||
void sctp_sysctl_unregister(void)
|
||||
{
|
||||
unregister_sysctl_table(sctp_sysctl_header);
|
||||
}
|
||||
560
net/sctp/transport.c
Normal file
560
net/sctp/transport.c
Normal file
@@ -0,0 +1,560 @@
|
||||
/* SCTP kernel reference Implementation
|
||||
* Copyright (c) 1999-2000 Cisco, Inc.
|
||||
* Copyright (c) 1999-2001 Motorola, Inc.
|
||||
* Copyright (c) 2001-2003 International Business Machines Corp.
|
||||
* Copyright (c) 2001 Intel Corp.
|
||||
* Copyright (c) 2001 La Monte H.P. Yarroll
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* This module provides the abstraction for an SCTP tranport representing
|
||||
* a remote transport address. For local transport addresses, we just use
|
||||
* union sctp_addr.
|
||||
*
|
||||
* 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>
|
||||
* Xingang Guo <xingang.guo@intel.com>
|
||||
* Hui Huang <hui.huang@nokia.com>
|
||||
* Sridhar Samudrala <sri@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/types.h>
|
||||
#include <linux/random.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
/* 1st Level Abstractions. */
|
||||
|
||||
/* Initialize a new transport from provided memory. */
|
||||
static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
|
||||
const union sctp_addr *addr,
|
||||
gfp_t gfp)
|
||||
{
|
||||
/* Copy in the address. */
|
||||
peer->ipaddr = *addr;
|
||||
peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
|
||||
peer->asoc = NULL;
|
||||
|
||||
peer->dst = NULL;
|
||||
memset(&peer->saddr, 0, sizeof(union sctp_addr));
|
||||
|
||||
/* From 6.3.1 RTO Calculation:
|
||||
*
|
||||
* C1) Until an RTT measurement has been made for a packet sent to the
|
||||
* given destination transport address, set RTO to the protocol
|
||||
* parameter 'RTO.Initial'.
|
||||
*/
|
||||
peer->rtt = 0;
|
||||
peer->rto = msecs_to_jiffies(sctp_rto_initial);
|
||||
peer->rttvar = 0;
|
||||
peer->srtt = 0;
|
||||
peer->rto_pending = 0;
|
||||
|
||||
peer->last_time_heard = jiffies;
|
||||
peer->last_time_used = jiffies;
|
||||
peer->last_time_ecne_reduced = jiffies;
|
||||
|
||||
peer->init_sent_count = 0;
|
||||
|
||||
peer->param_flags = SPP_HB_DISABLE |
|
||||
SPP_PMTUD_ENABLE |
|
||||
SPP_SACKDELAY_ENABLE;
|
||||
peer->hbinterval = 0;
|
||||
|
||||
/* Initialize the default path max_retrans. */
|
||||
peer->pathmaxrxt = sctp_max_retrans_path;
|
||||
peer->error_count = 0;
|
||||
|
||||
INIT_LIST_HEAD(&peer->transmitted);
|
||||
INIT_LIST_HEAD(&peer->send_ready);
|
||||
INIT_LIST_HEAD(&peer->transports);
|
||||
|
||||
/* Set up the retransmission timer. */
|
||||
init_timer(&peer->T3_rtx_timer);
|
||||
peer->T3_rtx_timer.function = sctp_generate_t3_rtx_event;
|
||||
peer->T3_rtx_timer.data = (unsigned long)peer;
|
||||
|
||||
/* Set up the heartbeat timer. */
|
||||
init_timer(&peer->hb_timer);
|
||||
peer->hb_timer.function = sctp_generate_heartbeat_event;
|
||||
peer->hb_timer.data = (unsigned long)peer;
|
||||
|
||||
/* Initialize the 64-bit random nonce sent with heartbeat. */
|
||||
get_random_bytes(&peer->hb_nonce, sizeof(peer->hb_nonce));
|
||||
|
||||
atomic_set(&peer->refcnt, 1);
|
||||
peer->dead = 0;
|
||||
|
||||
peer->malloced = 0;
|
||||
|
||||
/* Initialize the state information for SFR-CACC */
|
||||
peer->cacc.changeover_active = 0;
|
||||
peer->cacc.cycling_changeover = 0;
|
||||
peer->cacc.next_tsn_at_change = 0;
|
||||
peer->cacc.cacc_saw_newack = 0;
|
||||
|
||||
return peer;
|
||||
}
|
||||
|
||||
/* Allocate and initialize a new transport. */
|
||||
struct sctp_transport *sctp_transport_new(const union sctp_addr *addr,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sctp_transport *transport;
|
||||
|
||||
transport = t_new(struct sctp_transport, gfp);
|
||||
if (!transport)
|
||||
goto fail;
|
||||
|
||||
if (!sctp_transport_init(transport, addr, gfp))
|
||||
goto fail_init;
|
||||
|
||||
transport->malloced = 1;
|
||||
SCTP_DBG_OBJCNT_INC(transport);
|
||||
|
||||
return transport;
|
||||
|
||||
fail_init:
|
||||
kfree(transport);
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This transport is no longer needed. Free up if possible, or
|
||||
* delay until it last reference count.
|
||||
*/
|
||||
void sctp_transport_free(struct sctp_transport *transport)
|
||||
{
|
||||
transport->dead = 1;
|
||||
|
||||
/* Try to delete the heartbeat timer. */
|
||||
if (del_timer(&transport->hb_timer))
|
||||
sctp_transport_put(transport);
|
||||
|
||||
/* Delete the T3_rtx timer if it's active.
|
||||
* There is no point in not doing this now and letting
|
||||
* structure hang around in memory since we know
|
||||
* the tranport is going away.
|
||||
*/
|
||||
if (timer_pending(&transport->T3_rtx_timer) &&
|
||||
del_timer(&transport->T3_rtx_timer))
|
||||
sctp_transport_put(transport);
|
||||
|
||||
|
||||
sctp_transport_put(transport);
|
||||
}
|
||||
|
||||
/* Destroy the transport data structure.
|
||||
* Assumes there are no more users of this structure.
|
||||
*/
|
||||
static void sctp_transport_destroy(struct sctp_transport *transport)
|
||||
{
|
||||
SCTP_ASSERT(transport->dead, "Transport is not dead", return);
|
||||
|
||||
if (transport->asoc)
|
||||
sctp_association_put(transport->asoc);
|
||||
|
||||
sctp_packet_free(&transport->packet);
|
||||
|
||||
dst_release(transport->dst);
|
||||
kfree(transport);
|
||||
SCTP_DBG_OBJCNT_DEC(transport);
|
||||
}
|
||||
|
||||
/* Start T3_rtx timer if it is not already running and update the heartbeat
|
||||
* timer. This routine is called every time a DATA chunk is sent.
|
||||
*/
|
||||
void sctp_transport_reset_timers(struct sctp_transport *transport)
|
||||
{
|
||||
/* RFC 2960 6.3.2 Retransmission Timer Rules
|
||||
*
|
||||
* R1) Every time a DATA chunk is sent to any address(including a
|
||||
* retransmission), if the T3-rtx timer of that address is not running
|
||||
* start it running so that it will expire after the RTO of that
|
||||
* address.
|
||||
*/
|
||||
|
||||
if (!timer_pending(&transport->T3_rtx_timer))
|
||||
if (!mod_timer(&transport->T3_rtx_timer,
|
||||
jiffies + transport->rto))
|
||||
sctp_transport_hold(transport);
|
||||
|
||||
/* When a data chunk is sent, reset the heartbeat interval. */
|
||||
if (!mod_timer(&transport->hb_timer,
|
||||
sctp_transport_timeout(transport)))
|
||||
sctp_transport_hold(transport);
|
||||
}
|
||||
|
||||
/* This transport has been assigned to an association.
|
||||
* Initialize fields from the association or from the sock itself.
|
||||
* Register the reference count in the association.
|
||||
*/
|
||||
void sctp_transport_set_owner(struct sctp_transport *transport,
|
||||
struct sctp_association *asoc)
|
||||
{
|
||||
transport->asoc = asoc;
|
||||
sctp_association_hold(asoc);
|
||||
}
|
||||
|
||||
/* Initialize the pmtu of a transport. */
|
||||
void sctp_transport_pmtu(struct sctp_transport *transport)
|
||||
{
|
||||
struct dst_entry *dst;
|
||||
|
||||
dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL);
|
||||
|
||||
if (dst) {
|
||||
transport->pathmtu = dst_mtu(dst);
|
||||
dst_release(dst);
|
||||
} else
|
||||
transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
|
||||
}
|
||||
|
||||
/* Caches the dst entry and source address for a transport's destination
|
||||
* address.
|
||||
*/
|
||||
void sctp_transport_route(struct sctp_transport *transport,
|
||||
union sctp_addr *saddr, struct sctp_sock *opt)
|
||||
{
|
||||
struct sctp_association *asoc = transport->asoc;
|
||||
struct sctp_af *af = transport->af_specific;
|
||||
union sctp_addr *daddr = &transport->ipaddr;
|
||||
struct dst_entry *dst;
|
||||
|
||||
dst = af->get_dst(asoc, daddr, saddr);
|
||||
|
||||
if (saddr)
|
||||
memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
|
||||
else
|
||||
af->get_saddr(asoc, dst, daddr, &transport->saddr);
|
||||
|
||||
transport->dst = dst;
|
||||
if ((transport->param_flags & SPP_PMTUD_DISABLE) && transport->pathmtu) {
|
||||
return;
|
||||
}
|
||||
if (dst) {
|
||||
transport->pathmtu = dst_mtu(dst);
|
||||
|
||||
/* Initialize sk->sk_rcv_saddr, if the transport is the
|
||||
* association's active path for getsockname().
|
||||
*/
|
||||
if (asoc && (transport == asoc->peer.active_path))
|
||||
opt->pf->af->to_sk_saddr(&transport->saddr,
|
||||
asoc->base.sk);
|
||||
} else
|
||||
transport->pathmtu = SCTP_DEFAULT_MAXSEGMENT;
|
||||
}
|
||||
|
||||
/* Hold a reference to a transport. */
|
||||
void sctp_transport_hold(struct sctp_transport *transport)
|
||||
{
|
||||
atomic_inc(&transport->refcnt);
|
||||
}
|
||||
|
||||
/* Release a reference to a transport and clean up
|
||||
* if there are no more references.
|
||||
*/
|
||||
void sctp_transport_put(struct sctp_transport *transport)
|
||||
{
|
||||
if (atomic_dec_and_test(&transport->refcnt))
|
||||
sctp_transport_destroy(transport);
|
||||
}
|
||||
|
||||
/* Update transport's RTO based on the newly calculated RTT. */
|
||||
void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
|
||||
{
|
||||
/* Check for valid transport. */
|
||||
SCTP_ASSERT(tp, "NULL transport", return);
|
||||
|
||||
/* We should not be doing any RTO updates unless rto_pending is set. */
|
||||
SCTP_ASSERT(tp->rto_pending, "rto_pending not set", return);
|
||||
|
||||
if (tp->rttvar || tp->srtt) {
|
||||
/* 6.3.1 C3) When a new RTT measurement R' is made, set
|
||||
* RTTVAR <- (1 - RTO.Beta) * RTTVAR + RTO.Beta * |SRTT - R'|
|
||||
* SRTT <- (1 - RTO.Alpha) * SRTT + RTO.Alpha * R'
|
||||
*/
|
||||
|
||||
/* Note: The above algorithm has been rewritten to
|
||||
* express rto_beta and rto_alpha as inverse powers
|
||||
* of two.
|
||||
* For example, assuming the default value of RTO.Alpha of
|
||||
* 1/8, rto_alpha would be expressed as 3.
|
||||
*/
|
||||
tp->rttvar = tp->rttvar - (tp->rttvar >> sctp_rto_beta)
|
||||
+ ((abs(tp->srtt - rtt)) >> sctp_rto_beta);
|
||||
tp->srtt = tp->srtt - (tp->srtt >> sctp_rto_alpha)
|
||||
+ (rtt >> sctp_rto_alpha);
|
||||
} else {
|
||||
/* 6.3.1 C2) When the first RTT measurement R is made, set
|
||||
* SRTT <- R, RTTVAR <- R/2.
|
||||
*/
|
||||
tp->srtt = rtt;
|
||||
tp->rttvar = rtt >> 1;
|
||||
}
|
||||
|
||||
/* 6.3.1 G1) Whenever RTTVAR is computed, if RTTVAR = 0, then
|
||||
* adjust RTTVAR <- G, where G is the CLOCK GRANULARITY.
|
||||
*/
|
||||
if (tp->rttvar == 0)
|
||||
tp->rttvar = SCTP_CLOCK_GRANULARITY;
|
||||
|
||||
/* 6.3.1 C3) After the computation, update RTO <- SRTT + 4 * RTTVAR. */
|
||||
tp->rto = tp->srtt + (tp->rttvar << 2);
|
||||
|
||||
/* 6.3.1 C6) Whenever RTO is computed, if it is less than RTO.Min
|
||||
* seconds then it is rounded up to RTO.Min seconds.
|
||||
*/
|
||||
if (tp->rto < tp->asoc->rto_min)
|
||||
tp->rto = tp->asoc->rto_min;
|
||||
|
||||
/* 6.3.1 C7) A maximum value may be placed on RTO provided it is
|
||||
* at least RTO.max seconds.
|
||||
*/
|
||||
if (tp->rto > tp->asoc->rto_max)
|
||||
tp->rto = tp->asoc->rto_max;
|
||||
|
||||
tp->rtt = rtt;
|
||||
|
||||
/* Reset rto_pending so that a new RTT measurement is started when a
|
||||
* new data chunk is sent.
|
||||
*/
|
||||
tp->rto_pending = 0;
|
||||
|
||||
SCTP_DEBUG_PRINTK("%s: transport: %p, rtt: %d, srtt: %d "
|
||||
"rttvar: %d, rto: %ld\n", __FUNCTION__,
|
||||
tp, rtt, tp->srtt, tp->rttvar, tp->rto);
|
||||
}
|
||||
|
||||
/* This routine updates the transport's cwnd and partial_bytes_acked
|
||||
* parameters based on the bytes acked in the received SACK.
|
||||
*/
|
||||
void sctp_transport_raise_cwnd(struct sctp_transport *transport,
|
||||
__u32 sack_ctsn, __u32 bytes_acked)
|
||||
{
|
||||
__u32 cwnd, ssthresh, flight_size, pba, pmtu;
|
||||
|
||||
cwnd = transport->cwnd;
|
||||
flight_size = transport->flight_size;
|
||||
|
||||
/* The appropriate cwnd increase algorithm is performed if, and only
|
||||
* if the cumulative TSN has advanced and the congestion window is
|
||||
* being fully utilized.
|
||||
*/
|
||||
if ((transport->asoc->ctsn_ack_point >= sack_ctsn) ||
|
||||
(flight_size < cwnd))
|
||||
return;
|
||||
|
||||
ssthresh = transport->ssthresh;
|
||||
pba = transport->partial_bytes_acked;
|
||||
pmtu = transport->asoc->pathmtu;
|
||||
|
||||
if (cwnd <= ssthresh) {
|
||||
/* RFC 2960 7.2.1, sctpimpguide-05 2.14.2 When cwnd is less
|
||||
* than or equal to ssthresh an SCTP endpoint MUST use the
|
||||
* slow start algorithm to increase cwnd only if the current
|
||||
* congestion window is being fully utilized and an incoming
|
||||
* SACK advances the Cumulative TSN Ack Point. Only when these
|
||||
* two conditions are met can the cwnd be increased otherwise
|
||||
* the cwnd MUST not be increased. If these conditions are met
|
||||
* then cwnd MUST be increased by at most the lesser of
|
||||
* 1) the total size of the previously outstanding DATA
|
||||
* chunk(s) acknowledged, and 2) the destination's path MTU.
|
||||
*/
|
||||
if (bytes_acked > pmtu)
|
||||
cwnd += pmtu;
|
||||
else
|
||||
cwnd += bytes_acked;
|
||||
SCTP_DEBUG_PRINTK("%s: SLOW START: transport: %p, "
|
||||
"bytes_acked: %d, cwnd: %d, ssthresh: %d, "
|
||||
"flight_size: %d, pba: %d\n",
|
||||
__FUNCTION__,
|
||||
transport, bytes_acked, cwnd,
|
||||
ssthresh, flight_size, pba);
|
||||
} else {
|
||||
/* RFC 2960 7.2.2 Whenever cwnd is greater than ssthresh,
|
||||
* upon each SACK arrival that advances the Cumulative TSN Ack
|
||||
* Point, increase partial_bytes_acked by the total number of
|
||||
* bytes of all new chunks acknowledged in that SACK including
|
||||
* chunks acknowledged by the new Cumulative TSN Ack and by
|
||||
* Gap Ack Blocks.
|
||||
*
|
||||
* When partial_bytes_acked is equal to or greater than cwnd
|
||||
* and before the arrival of the SACK the sender had cwnd or
|
||||
* more bytes of data outstanding (i.e., before arrival of the
|
||||
* SACK, flightsize was greater than or equal to cwnd),
|
||||
* increase cwnd by MTU, and reset partial_bytes_acked to
|
||||
* (partial_bytes_acked - cwnd).
|
||||
*/
|
||||
pba += bytes_acked;
|
||||
if (pba >= cwnd) {
|
||||
cwnd += pmtu;
|
||||
pba = ((cwnd < pba) ? (pba - cwnd) : 0);
|
||||
}
|
||||
SCTP_DEBUG_PRINTK("%s: CONGESTION AVOIDANCE: "
|
||||
"transport: %p, bytes_acked: %d, cwnd: %d, "
|
||||
"ssthresh: %d, flight_size: %d, pba: %d\n",
|
||||
__FUNCTION__,
|
||||
transport, bytes_acked, cwnd,
|
||||
ssthresh, flight_size, pba);
|
||||
}
|
||||
|
||||
transport->cwnd = cwnd;
|
||||
transport->partial_bytes_acked = pba;
|
||||
}
|
||||
|
||||
/* This routine is used to lower the transport's cwnd when congestion is
|
||||
* detected.
|
||||
*/
|
||||
void sctp_transport_lower_cwnd(struct sctp_transport *transport,
|
||||
sctp_lower_cwnd_t reason)
|
||||
{
|
||||
switch (reason) {
|
||||
case SCTP_LOWER_CWND_T3_RTX:
|
||||
/* RFC 2960 Section 7.2.3, sctpimpguide
|
||||
* When the T3-rtx timer expires on an address, SCTP should
|
||||
* perform slow start by:
|
||||
* ssthresh = max(cwnd/2, 4*MTU)
|
||||
* cwnd = 1*MTU
|
||||
* partial_bytes_acked = 0
|
||||
*/
|
||||
transport->ssthresh = max(transport->cwnd/2,
|
||||
4*transport->asoc->pathmtu);
|
||||
transport->cwnd = transport->asoc->pathmtu;
|
||||
break;
|
||||
|
||||
case SCTP_LOWER_CWND_FAST_RTX:
|
||||
/* RFC 2960 7.2.4 Adjust the ssthresh and cwnd of the
|
||||
* destination address(es) to which the missing DATA chunks
|
||||
* were last sent, according to the formula described in
|
||||
* Section 7.2.3.
|
||||
*
|
||||
* RFC 2960 7.2.3, sctpimpguide Upon detection of packet
|
||||
* losses from SACK (see Section 7.2.4), An endpoint
|
||||
* should do the following:
|
||||
* ssthresh = max(cwnd/2, 4*MTU)
|
||||
* cwnd = ssthresh
|
||||
* partial_bytes_acked = 0
|
||||
*/
|
||||
transport->ssthresh = max(transport->cwnd/2,
|
||||
4*transport->asoc->pathmtu);
|
||||
transport->cwnd = transport->ssthresh;
|
||||
break;
|
||||
|
||||
case SCTP_LOWER_CWND_ECNE:
|
||||
/* RFC 2481 Section 6.1.2.
|
||||
* If the sender receives an ECN-Echo ACK packet
|
||||
* then the sender knows that congestion was encountered in the
|
||||
* network on the path from the sender to the receiver. The
|
||||
* indication of congestion should be treated just as a
|
||||
* congestion loss in non-ECN Capable TCP. That is, the TCP
|
||||
* source halves the congestion window "cwnd" and reduces the
|
||||
* slow start threshold "ssthresh".
|
||||
* A critical condition is that TCP does not react to
|
||||
* congestion indications more than once every window of
|
||||
* data (or more loosely more than once every round-trip time).
|
||||
*/
|
||||
if ((jiffies - transport->last_time_ecne_reduced) >
|
||||
transport->rtt) {
|
||||
transport->ssthresh = max(transport->cwnd/2,
|
||||
4*transport->asoc->pathmtu);
|
||||
transport->cwnd = transport->ssthresh;
|
||||
transport->last_time_ecne_reduced = jiffies;
|
||||
}
|
||||
break;
|
||||
|
||||
case SCTP_LOWER_CWND_INACTIVE:
|
||||
/* RFC 2960 Section 7.2.1, sctpimpguide
|
||||
* When the endpoint does not transmit data on a given
|
||||
* transport address, the cwnd of the transport address
|
||||
* should be adjusted to max(cwnd/2, 4*MTU) per RTO.
|
||||
* NOTE: Although the draft recommends that this check needs
|
||||
* to be done every RTO interval, we do it every hearbeat
|
||||
* interval.
|
||||
*/
|
||||
if ((jiffies - transport->last_time_used) > transport->rto)
|
||||
transport->cwnd = max(transport->cwnd/2,
|
||||
4*transport->asoc->pathmtu);
|
||||
break;
|
||||
};
|
||||
|
||||
transport->partial_bytes_acked = 0;
|
||||
SCTP_DEBUG_PRINTK("%s: transport: %p reason: %d cwnd: "
|
||||
"%d ssthresh: %d\n", __FUNCTION__,
|
||||
transport, reason,
|
||||
transport->cwnd, transport->ssthresh);
|
||||
}
|
||||
|
||||
/* What is the next timeout value for this transport? */
|
||||
unsigned long sctp_transport_timeout(struct sctp_transport *t)
|
||||
{
|
||||
unsigned long timeout;
|
||||
timeout = t->rto + sctp_jitter(t->rto);
|
||||
if (t->state != SCTP_UNCONFIRMED)
|
||||
timeout += t->hbinterval;
|
||||
timeout += jiffies;
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/* Reset transport variables to their initial values */
|
||||
void sctp_transport_reset(struct sctp_transport *t)
|
||||
{
|
||||
struct sctp_association *asoc = t->asoc;
|
||||
|
||||
/* RFC 2960 (bis), Section 5.2.4
|
||||
* All the congestion control parameters (e.g., cwnd, ssthresh)
|
||||
* related to this peer MUST be reset to their initial values
|
||||
* (see Section 6.2.1)
|
||||
*/
|
||||
t->cwnd = min(4*asoc->pathmtu, max_t(__u32, 2*asoc->pathmtu, 4380));
|
||||
t->ssthresh = asoc->peer.i.a_rwnd;
|
||||
t->rto = asoc->rto_initial;
|
||||
t->rtt = 0;
|
||||
t->srtt = 0;
|
||||
t->rttvar = 0;
|
||||
|
||||
/* Reset these additional varibles so that we have a clean
|
||||
* slate.
|
||||
*/
|
||||
t->partial_bytes_acked = 0;
|
||||
t->flight_size = 0;
|
||||
t->error_count = 0;
|
||||
t->rto_pending = 0;
|
||||
|
||||
/* Initialize the state information for SFR-CACC */
|
||||
t->cacc.changeover_active = 0;
|
||||
t->cacc.cycling_changeover = 0;
|
||||
t->cacc.next_tsn_at_change = 0;
|
||||
t->cacc.cacc_saw_newack = 0;
|
||||
}
|
||||
418
net/sctp/tsnmap.c
Normal file
418
net/sctp/tsnmap.c
Normal file
@@ -0,0 +1,418 @@
|
||||
/* 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.
|
||||
*
|
||||
* This file is part of the SCTP kernel reference Implementation
|
||||
*
|
||||
* These functions manipulate sctp tsn mapping array.
|
||||
*
|
||||
* 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>
|
||||
* Jon Grimm <jgrimm@us.ibm.com>
|
||||
* Karl Knutson <karl@athena.chicago.il.us>
|
||||
* Sridhar Samudrala <sri@us.ibm.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/types.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
static void sctp_tsnmap_update(struct sctp_tsnmap *map);
|
||||
static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off,
|
||||
__u16 len, __u16 base,
|
||||
int *started, __u16 *start,
|
||||
int *ended, __u16 *end);
|
||||
|
||||
/* Initialize a block of memory as a tsnmap. */
|
||||
struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *map, __u16 len,
|
||||
__u32 initial_tsn)
|
||||
{
|
||||
map->tsn_map = map->raw_map;
|
||||
map->overflow_map = map->tsn_map + len;
|
||||
map->len = len;
|
||||
|
||||
/* Clear out a TSN ack status. */
|
||||
memset(map->tsn_map, 0x00, map->len + map->len);
|
||||
|
||||
/* Keep track of TSNs represented by tsn_map. */
|
||||
map->base_tsn = initial_tsn;
|
||||
map->overflow_tsn = initial_tsn + map->len;
|
||||
map->cumulative_tsn_ack_point = initial_tsn - 1;
|
||||
map->max_tsn_seen = map->cumulative_tsn_ack_point;
|
||||
map->malloced = 0;
|
||||
map->num_dup_tsns = 0;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
/* Test the tracking state of this TSN.
|
||||
* Returns:
|
||||
* 0 if the TSN has not yet been seen
|
||||
* >0 if the TSN has been seen (duplicate)
|
||||
* <0 if the TSN is invalid (too large to track)
|
||||
*/
|
||||
int sctp_tsnmap_check(const struct sctp_tsnmap *map, __u32 tsn)
|
||||
{
|
||||
__s32 gap;
|
||||
int dup;
|
||||
|
||||
/* Calculate the index into the mapping arrays. */
|
||||
gap = tsn - map->base_tsn;
|
||||
|
||||
/* Verify that we can hold this TSN. */
|
||||
if (gap >= (/* base */ map->len + /* overflow */ map->len)) {
|
||||
dup = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Honk if we've already seen this TSN.
|
||||
* We have three cases:
|
||||
* 1. The TSN is ancient or belongs to a previous tsn_map.
|
||||
* 2. The TSN is already marked in the tsn_map.
|
||||
* 3. The TSN is already marked in the tsn_map_overflow.
|
||||
*/
|
||||
if (gap < 0 ||
|
||||
(gap < map->len && map->tsn_map[gap]) ||
|
||||
(gap >= map->len && map->overflow_map[gap - map->len]))
|
||||
dup = 1;
|
||||
else
|
||||
dup = 0;
|
||||
|
||||
out:
|
||||
return dup;
|
||||
}
|
||||
|
||||
|
||||
/* Mark this TSN as seen. */
|
||||
void sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn)
|
||||
{
|
||||
__s32 gap;
|
||||
|
||||
/* Vacuously mark any TSN which precedes the map base or
|
||||
* exceeds the end of the map.
|
||||
*/
|
||||
if (TSN_lt(tsn, map->base_tsn))
|
||||
return;
|
||||
if (!TSN_lt(tsn, map->base_tsn + map->len + map->len))
|
||||
return;
|
||||
|
||||
/* Bump the max. */
|
||||
if (TSN_lt(map->max_tsn_seen, tsn))
|
||||
map->max_tsn_seen = tsn;
|
||||
|
||||
/* Assert: TSN is in range. */
|
||||
gap = tsn - map->base_tsn;
|
||||
|
||||
/* Mark the TSN as received. */
|
||||
if (gap < map->len)
|
||||
map->tsn_map[gap]++;
|
||||
else
|
||||
map->overflow_map[gap - map->len]++;
|
||||
|
||||
/* Go fixup any internal TSN mapping variables including
|
||||
* cumulative_tsn_ack_point.
|
||||
*/
|
||||
sctp_tsnmap_update(map);
|
||||
}
|
||||
|
||||
|
||||
/* Initialize a Gap Ack Block iterator from memory being provided. */
|
||||
SCTP_STATIC void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map,
|
||||
struct sctp_tsnmap_iter *iter)
|
||||
{
|
||||
/* Only start looking one past the Cumulative TSN Ack Point. */
|
||||
iter->start = map->cumulative_tsn_ack_point + 1;
|
||||
}
|
||||
|
||||
/* Get the next Gap Ack Blocks. Returns 0 if there was not another block
|
||||
* to get.
|
||||
*/
|
||||
SCTP_STATIC int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map,
|
||||
struct sctp_tsnmap_iter *iter,
|
||||
__u16 *start, __u16 *end)
|
||||
{
|
||||
int started, ended;
|
||||
__u16 _start, _end, offset;
|
||||
|
||||
/* We haven't found a gap yet. */
|
||||
started = ended = 0;
|
||||
|
||||
/* If there are no more gap acks possible, get out fast. */
|
||||
if (TSN_lte(map->max_tsn_seen, iter->start))
|
||||
return 0;
|
||||
|
||||
/* Search the first mapping array. */
|
||||
if (iter->start - map->base_tsn < map->len) {
|
||||
|
||||
offset = iter->start - map->base_tsn;
|
||||
sctp_tsnmap_find_gap_ack(map->tsn_map, offset, map->len, 0,
|
||||
&started, &_start, &ended, &_end);
|
||||
}
|
||||
|
||||
/* Do we need to check the overflow map? */
|
||||
if (!ended) {
|
||||
/* Fix up where we'd like to start searching in the
|
||||
* overflow map.
|
||||
*/
|
||||
if (iter->start - map->base_tsn < map->len)
|
||||
offset = 0;
|
||||
else
|
||||
offset = iter->start - map->base_tsn - map->len;
|
||||
|
||||
/* Search the overflow map. */
|
||||
sctp_tsnmap_find_gap_ack(map->overflow_map,
|
||||
offset,
|
||||
map->len,
|
||||
map->len,
|
||||
&started, &_start,
|
||||
&ended, &_end);
|
||||
}
|
||||
|
||||
/* The Gap Ack Block happens to end at the end of the
|
||||
* overflow map.
|
||||
*/
|
||||
if (started && !ended) {
|
||||
ended++;
|
||||
_end = map->len + map->len - 1;
|
||||
}
|
||||
|
||||
/* If we found a Gap Ack Block, return the start and end and
|
||||
* bump the iterator forward.
|
||||
*/
|
||||
if (ended) {
|
||||
/* Fix up the start and end based on the
|
||||
* Cumulative TSN Ack offset into the map.
|
||||
*/
|
||||
int gap = map->cumulative_tsn_ack_point -
|
||||
map->base_tsn;
|
||||
|
||||
*start = _start - gap;
|
||||
*end = _end - gap;
|
||||
|
||||
/* Move the iterator forward. */
|
||||
iter->start = map->cumulative_tsn_ack_point + *end + 1;
|
||||
}
|
||||
|
||||
return ended;
|
||||
}
|
||||
|
||||
/* Mark this and any lower TSN as seen. */
|
||||
void sctp_tsnmap_skip(struct sctp_tsnmap *map, __u32 tsn)
|
||||
{
|
||||
__s32 gap;
|
||||
|
||||
/* Vacuously mark any TSN which precedes the map base or
|
||||
* exceeds the end of the map.
|
||||
*/
|
||||
if (TSN_lt(tsn, map->base_tsn))
|
||||
return;
|
||||
if (!TSN_lt(tsn, map->base_tsn + map->len + map->len))
|
||||
return;
|
||||
|
||||
/* Bump the max. */
|
||||
if (TSN_lt(map->max_tsn_seen, tsn))
|
||||
map->max_tsn_seen = tsn;
|
||||
|
||||
/* Assert: TSN is in range. */
|
||||
gap = tsn - map->base_tsn + 1;
|
||||
|
||||
/* Mark the TSNs as received. */
|
||||
if (gap <= map->len)
|
||||
memset(map->tsn_map, 0x01, gap);
|
||||
else {
|
||||
memset(map->tsn_map, 0x01, map->len);
|
||||
memset(map->overflow_map, 0x01, (gap - map->len));
|
||||
}
|
||||
|
||||
/* Go fixup any internal TSN mapping variables including
|
||||
* cumulative_tsn_ack_point.
|
||||
*/
|
||||
sctp_tsnmap_update(map);
|
||||
}
|
||||
|
||||
/********************************************************************
|
||||
* 2nd Level Abstractions
|
||||
********************************************************************/
|
||||
|
||||
/* This private helper function updates the tsnmap buffers and
|
||||
* the Cumulative TSN Ack Point.
|
||||
*/
|
||||
static void sctp_tsnmap_update(struct sctp_tsnmap *map)
|
||||
{
|
||||
__u32 ctsn;
|
||||
|
||||
ctsn = map->cumulative_tsn_ack_point;
|
||||
do {
|
||||
ctsn++;
|
||||
if (ctsn == map->overflow_tsn) {
|
||||
/* Now tsn_map must have been all '1's,
|
||||
* so we swap the map and check the overflow table
|
||||
*/
|
||||
__u8 *tmp = map->tsn_map;
|
||||
memset(tmp, 0, map->len);
|
||||
map->tsn_map = map->overflow_map;
|
||||
map->overflow_map = tmp;
|
||||
|
||||
/* Update the tsn_map boundaries. */
|
||||
map->base_tsn += map->len;
|
||||
map->overflow_tsn += map->len;
|
||||
}
|
||||
} while (map->tsn_map[ctsn - map->base_tsn]);
|
||||
|
||||
map->cumulative_tsn_ack_point = ctsn - 1; /* Back up one. */
|
||||
}
|
||||
|
||||
/* How many data chunks are we missing from our peer?
|
||||
*/
|
||||
__u16 sctp_tsnmap_pending(struct sctp_tsnmap *map)
|
||||
{
|
||||
__u32 cum_tsn = map->cumulative_tsn_ack_point;
|
||||
__u32 max_tsn = map->max_tsn_seen;
|
||||
__u32 base_tsn = map->base_tsn;
|
||||
__u16 pending_data;
|
||||
__s32 gap, start, end, i;
|
||||
|
||||
pending_data = max_tsn - cum_tsn;
|
||||
gap = max_tsn - base_tsn;
|
||||
|
||||
if (gap <= 0 || gap >= (map->len + map->len))
|
||||
goto out;
|
||||
|
||||
start = ((cum_tsn >= base_tsn) ? (cum_tsn - base_tsn + 1) : 0);
|
||||
end = ((gap > map->len ) ? map->len : gap + 1);
|
||||
|
||||
for (i = start; i < end; i++) {
|
||||
if (map->tsn_map[i])
|
||||
pending_data--;
|
||||
}
|
||||
|
||||
if (gap >= map->len) {
|
||||
start = 0;
|
||||
end = gap - map->len + 1;
|
||||
for (i = start; i < end; i++) {
|
||||
if (map->overflow_map[i])
|
||||
pending_data--;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
return pending_data;
|
||||
}
|
||||
|
||||
/* This is a private helper for finding Gap Ack Blocks. It searches a
|
||||
* single array for the start and end of a Gap Ack Block.
|
||||
*
|
||||
* The flags "started" and "ended" tell is if we found the beginning
|
||||
* or (respectively) the end of a Gap Ack Block.
|
||||
*/
|
||||
static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off,
|
||||
__u16 len, __u16 base,
|
||||
int *started, __u16 *start,
|
||||
int *ended, __u16 *end)
|
||||
{
|
||||
int i = off;
|
||||
|
||||
/* Look through the entire array, but break out
|
||||
* early if we have found the end of the Gap Ack Block.
|
||||
*/
|
||||
|
||||
/* Also, stop looking past the maximum TSN seen. */
|
||||
|
||||
/* Look for the start. */
|
||||
if (!(*started)) {
|
||||
for (; i < len; i++) {
|
||||
if (map[i]) {
|
||||
(*started)++;
|
||||
*start = base + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for the end. */
|
||||
if (*started) {
|
||||
/* We have found the start, let's find the
|
||||
* end. If we find the end, break out.
|
||||
*/
|
||||
for (; i < len; i++) {
|
||||
if (!map[i]) {
|
||||
(*ended)++;
|
||||
*end = base + i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Renege that we have seen a TSN. */
|
||||
void sctp_tsnmap_renege(struct sctp_tsnmap *map, __u32 tsn)
|
||||
{
|
||||
__s32 gap;
|
||||
|
||||
if (TSN_lt(tsn, map->base_tsn))
|
||||
return;
|
||||
if (!TSN_lt(tsn, map->base_tsn + map->len + map->len))
|
||||
return;
|
||||
|
||||
/* Assert: TSN is in range. */
|
||||
gap = tsn - map->base_tsn;
|
||||
|
||||
/* Pretend we never saw the TSN. */
|
||||
if (gap < map->len)
|
||||
map->tsn_map[gap] = 0;
|
||||
else
|
||||
map->overflow_map[gap - map->len] = 0;
|
||||
}
|
||||
|
||||
/* How many gap ack blocks do we have recorded? */
|
||||
__u16 sctp_tsnmap_num_gabs(struct sctp_tsnmap *map)
|
||||
{
|
||||
struct sctp_tsnmap_iter iter;
|
||||
int gabs = 0;
|
||||
|
||||
/* Refresh the gap ack information. */
|
||||
if (sctp_tsnmap_has_gap(map)) {
|
||||
__u16 start, end;
|
||||
sctp_tsnmap_iter_init(map, &iter);
|
||||
while (sctp_tsnmap_next_gap_ack(map, &iter,
|
||||
&start,
|
||||
&end)) {
|
||||
|
||||
map->gabs[gabs].start = htons(start);
|
||||
map->gabs[gabs].end = htons(end);
|
||||
gabs++;
|
||||
if (gabs >= SCTP_MAX_GABS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return gabs;
|
||||
}
|
||||
966
net/sctp/ulpevent.c
Normal file
966
net/sctp/ulpevent.c
Normal file
@@ -0,0 +1,966 @@
|
||||
/* 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
|
||||
*
|
||||
* These functions manipulate an sctp event. The struct ulpevent is used
|
||||
* to carry notifications and data to the ULP (sockets).
|
||||
* 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:
|
||||
* Jon Grimm <jgrimm@us.ibm.com>
|
||||
* La Monte H.P. Yarroll <piggy@acm.org>
|
||||
* Ardelle Fan <ardelle.fan@intel.com>
|
||||
* Sridhar Samudrala <sri@us.ibm.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/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sctp/structs.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
|
||||
struct sctp_association *asoc);
|
||||
static void sctp_ulpevent_release_data(struct sctp_ulpevent *event);
|
||||
static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event);
|
||||
|
||||
|
||||
/* Initialize an ULP event from an given skb. */
|
||||
SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event,
|
||||
int msg_flags,
|
||||
unsigned int len)
|
||||
{
|
||||
memset(event, 0, sizeof(struct sctp_ulpevent));
|
||||
event->msg_flags = msg_flags;
|
||||
event->rmem_len = len;
|
||||
}
|
||||
|
||||
/* Create a new sctp_ulpevent. */
|
||||
SCTP_STATIC struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = alloc_skb(size, gfp);
|
||||
if (!skb)
|
||||
goto fail;
|
||||
|
||||
event = sctp_skb2event(skb);
|
||||
sctp_ulpevent_init(event, msg_flags, skb->truesize);
|
||||
|
||||
return event;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Is this a MSG_NOTIFICATION? */
|
||||
int sctp_ulpevent_is_notification(const struct sctp_ulpevent *event)
|
||||
{
|
||||
return MSG_NOTIFICATION == (event->msg_flags & MSG_NOTIFICATION);
|
||||
}
|
||||
|
||||
/* Hold the association in case the msg_name needs read out of
|
||||
* the association.
|
||||
*/
|
||||
static inline void sctp_ulpevent_set_owner(struct sctp_ulpevent *event,
|
||||
const struct sctp_association *asoc)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Cast away the const, as we are just wanting to
|
||||
* bump the reference count.
|
||||
*/
|
||||
sctp_association_hold((struct sctp_association *)asoc);
|
||||
skb = sctp_event2skb(event);
|
||||
event->asoc = (struct sctp_association *)asoc;
|
||||
atomic_add(event->rmem_len, &event->asoc->rmem_alloc);
|
||||
sctp_skb_set_owner_r(skb, asoc->base.sk);
|
||||
}
|
||||
|
||||
/* A simple destructor to give up the reference to the association. */
|
||||
static inline void sctp_ulpevent_release_owner(struct sctp_ulpevent *event)
|
||||
{
|
||||
struct sctp_association *asoc = event->asoc;
|
||||
|
||||
atomic_sub(event->rmem_len, &asoc->rmem_alloc);
|
||||
sctp_association_put(asoc);
|
||||
}
|
||||
|
||||
/* Create and initialize an SCTP_ASSOC_CHANGE event.
|
||||
*
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* Communication notifications inform the ULP that an SCTP association
|
||||
* has either begun or ended. The identifier for a new association is
|
||||
* provided by this notification.
|
||||
*
|
||||
* Note: There is no field checking here. If a field is unused it will be
|
||||
* zero'd out.
|
||||
*/
|
||||
struct sctp_ulpevent *sctp_ulpevent_make_assoc_change(
|
||||
const struct sctp_association *asoc,
|
||||
__u16 flags, __u16 state, __u16 error, __u16 outbound,
|
||||
__u16 inbound, gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_assoc_change *sac;
|
||||
struct sk_buff *skb;
|
||||
|
||||
event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
|
||||
MSG_NOTIFICATION, gfp);
|
||||
if (!event)
|
||||
goto fail;
|
||||
skb = sctp_event2skb(event);
|
||||
sac = (struct sctp_assoc_change *)
|
||||
skb_put(skb, sizeof(struct sctp_assoc_change));
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* sac_type:
|
||||
* It should be SCTP_ASSOC_CHANGE.
|
||||
*/
|
||||
sac->sac_type = SCTP_ASSOC_CHANGE;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* sac_state: 32 bits (signed integer)
|
||||
* This field holds one of a number of values that communicate the
|
||||
* event that happened to the association.
|
||||
*/
|
||||
sac->sac_state = state;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* sac_flags: 16 bits (unsigned integer)
|
||||
* Currently unused.
|
||||
*/
|
||||
sac->sac_flags = 0;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* sac_length: sizeof (__u32)
|
||||
* This field is the total length of the notification data, including
|
||||
* the notification header.
|
||||
*/
|
||||
sac->sac_length = sizeof(struct sctp_assoc_change);
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* sac_error: 32 bits (signed integer)
|
||||
*
|
||||
* If the state was reached due to a error condition (e.g.
|
||||
* COMMUNICATION_LOST) any relevant error information is available in
|
||||
* this field. This corresponds to the protocol error codes defined in
|
||||
* [SCTP].
|
||||
*/
|
||||
sac->sac_error = error;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* sac_outbound_streams: 16 bits (unsigned integer)
|
||||
* sac_inbound_streams: 16 bits (unsigned integer)
|
||||
*
|
||||
* The maximum number of streams allowed in each direction are
|
||||
* available in sac_outbound_streams and sac_inbound streams.
|
||||
*/
|
||||
sac->sac_outbound_streams = outbound;
|
||||
sac->sac_inbound_streams = inbound;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* sac_assoc_id: sizeof (sctp_assoc_t)
|
||||
*
|
||||
* The association id field, holds the identifier for the association.
|
||||
* All notifications for a given association have the same association
|
||||
* identifier. For TCP style socket, this field is ignored.
|
||||
*/
|
||||
sctp_ulpevent_set_owner(event, asoc);
|
||||
sac->sac_assoc_id = sctp_assoc2id(asoc);
|
||||
|
||||
return event;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create and initialize an SCTP_PEER_ADDR_CHANGE event.
|
||||
*
|
||||
* Socket Extensions for SCTP - draft-01
|
||||
* 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
||||
*
|
||||
* When a destination address on a multi-homed peer encounters a change
|
||||
* an interface details event is sent.
|
||||
*/
|
||||
struct sctp_ulpevent *sctp_ulpevent_make_peer_addr_change(
|
||||
const struct sctp_association *asoc,
|
||||
const struct sockaddr_storage *aaddr,
|
||||
int flags, int state, int error, gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_paddr_change *spc;
|
||||
struct sk_buff *skb;
|
||||
|
||||
event = sctp_ulpevent_new(sizeof(struct sctp_paddr_change),
|
||||
MSG_NOTIFICATION, gfp);
|
||||
if (!event)
|
||||
goto fail;
|
||||
|
||||
skb = sctp_event2skb(event);
|
||||
spc = (struct sctp_paddr_change *)
|
||||
skb_put(skb, sizeof(struct sctp_paddr_change));
|
||||
|
||||
/* Sockets API Extensions for SCTP
|
||||
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
||||
*
|
||||
* spc_type:
|
||||
*
|
||||
* It should be SCTP_PEER_ADDR_CHANGE.
|
||||
*/
|
||||
spc->spc_type = SCTP_PEER_ADDR_CHANGE;
|
||||
|
||||
/* Sockets API Extensions for SCTP
|
||||
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
||||
*
|
||||
* spc_length: sizeof (__u32)
|
||||
*
|
||||
* This field is the total length of the notification data, including
|
||||
* the notification header.
|
||||
*/
|
||||
spc->spc_length = sizeof(struct sctp_paddr_change);
|
||||
|
||||
/* Sockets API Extensions for SCTP
|
||||
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
||||
*
|
||||
* spc_flags: 16 bits (unsigned integer)
|
||||
* Currently unused.
|
||||
*/
|
||||
spc->spc_flags = 0;
|
||||
|
||||
/* Sockets API Extensions for SCTP
|
||||
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
||||
*
|
||||
* spc_state: 32 bits (signed integer)
|
||||
*
|
||||
* This field holds one of a number of values that communicate the
|
||||
* event that happened to the address.
|
||||
*/
|
||||
spc->spc_state = state;
|
||||
|
||||
/* Sockets API Extensions for SCTP
|
||||
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
||||
*
|
||||
* spc_error: 32 bits (signed integer)
|
||||
*
|
||||
* If the state was reached due to any error condition (e.g.
|
||||
* ADDRESS_UNREACHABLE) any relevant error information is available in
|
||||
* this field.
|
||||
*/
|
||||
spc->spc_error = error;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.1 SCTP_ASSOC_CHANGE
|
||||
*
|
||||
* spc_assoc_id: sizeof (sctp_assoc_t)
|
||||
*
|
||||
* The association id field, holds the identifier for the association.
|
||||
* All notifications for a given association have the same association
|
||||
* identifier. For TCP style socket, this field is ignored.
|
||||
*/
|
||||
sctp_ulpevent_set_owner(event, asoc);
|
||||
spc->spc_assoc_id = sctp_assoc2id(asoc);
|
||||
|
||||
/* Sockets API Extensions for SCTP
|
||||
* Section 5.3.1.2 SCTP_PEER_ADDR_CHANGE
|
||||
*
|
||||
* spc_aaddr: sizeof (struct sockaddr_storage)
|
||||
*
|
||||
* The affected address field, holds the remote peer's address that is
|
||||
* encountering the change of state.
|
||||
*/
|
||||
memcpy(&spc->spc_aaddr, aaddr, sizeof(struct sockaddr_storage));
|
||||
|
||||
/* Map ipv4 address into v4-mapped-on-v6 address. */
|
||||
sctp_get_pf_specific(asoc->base.sk->sk_family)->addr_v4map(
|
||||
sctp_sk(asoc->base.sk),
|
||||
(union sctp_addr *)&spc->spc_aaddr);
|
||||
|
||||
return event;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create and initialize an SCTP_REMOTE_ERROR notification.
|
||||
*
|
||||
* Note: This assumes that the chunk->skb->data already points to the
|
||||
* operation error payload.
|
||||
*
|
||||
* Socket Extensions for SCTP - draft-01
|
||||
* 5.3.1.3 SCTP_REMOTE_ERROR
|
||||
*
|
||||
* A remote peer may send an Operational Error message to its peer.
|
||||
* This message indicates a variety of error conditions on an
|
||||
* association. The entire error TLV as it appears on the wire is
|
||||
* included in a SCTP_REMOTE_ERROR event. Please refer to the SCTP
|
||||
* specification [SCTP] and any extensions for a list of possible
|
||||
* error formats.
|
||||
*/
|
||||
struct sctp_ulpevent *sctp_ulpevent_make_remote_error(
|
||||
const struct sctp_association *asoc, struct sctp_chunk *chunk,
|
||||
__u16 flags, gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_remote_error *sre;
|
||||
struct sk_buff *skb;
|
||||
sctp_errhdr_t *ch;
|
||||
__be16 cause;
|
||||
int elen;
|
||||
|
||||
ch = (sctp_errhdr_t *)(chunk->skb->data);
|
||||
cause = ch->cause;
|
||||
elen = WORD_ROUND(ntohs(ch->length)) - sizeof(sctp_errhdr_t);
|
||||
|
||||
/* Pull off the ERROR header. */
|
||||
skb_pull(chunk->skb, sizeof(sctp_errhdr_t));
|
||||
|
||||
/* Copy the skb to a new skb with room for us to prepend
|
||||
* notification with.
|
||||
*/
|
||||
skb = skb_copy_expand(chunk->skb, sizeof(struct sctp_remote_error),
|
||||
0, gfp);
|
||||
|
||||
/* Pull off the rest of the cause TLV from the chunk. */
|
||||
skb_pull(chunk->skb, elen);
|
||||
if (!skb)
|
||||
goto fail;
|
||||
|
||||
/* Embed the event fields inside the cloned skb. */
|
||||
event = sctp_skb2event(skb);
|
||||
sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
|
||||
|
||||
sre = (struct sctp_remote_error *)
|
||||
skb_push(skb, sizeof(struct sctp_remote_error));
|
||||
|
||||
/* Trim the buffer to the right length. */
|
||||
skb_trim(skb, sizeof(struct sctp_remote_error) + elen);
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.3 SCTP_REMOTE_ERROR
|
||||
*
|
||||
* sre_type:
|
||||
* It should be SCTP_REMOTE_ERROR.
|
||||
*/
|
||||
sre->sre_type = SCTP_REMOTE_ERROR;
|
||||
|
||||
/*
|
||||
* Socket Extensions for SCTP
|
||||
* 5.3.1.3 SCTP_REMOTE_ERROR
|
||||
*
|
||||
* sre_flags: 16 bits (unsigned integer)
|
||||
* Currently unused.
|
||||
*/
|
||||
sre->sre_flags = 0;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.3 SCTP_REMOTE_ERROR
|
||||
*
|
||||
* sre_length: sizeof (__u32)
|
||||
*
|
||||
* This field is the total length of the notification data,
|
||||
* including the notification header.
|
||||
*/
|
||||
sre->sre_length = skb->len;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.3 SCTP_REMOTE_ERROR
|
||||
*
|
||||
* sre_error: 16 bits (unsigned integer)
|
||||
* This value represents one of the Operational Error causes defined in
|
||||
* the SCTP specification, in network byte order.
|
||||
*/
|
||||
sre->sre_error = cause;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.3 SCTP_REMOTE_ERROR
|
||||
*
|
||||
* sre_assoc_id: sizeof (sctp_assoc_t)
|
||||
*
|
||||
* The association id field, holds the identifier for the association.
|
||||
* All notifications for a given association have the same association
|
||||
* identifier. For TCP style socket, this field is ignored.
|
||||
*/
|
||||
sctp_ulpevent_set_owner(event, asoc);
|
||||
sre->sre_assoc_id = sctp_assoc2id(asoc);
|
||||
|
||||
return event;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create and initialize a SCTP_SEND_FAILED notification.
|
||||
*
|
||||
* Socket Extensions for SCTP - draft-01
|
||||
* 5.3.1.4 SCTP_SEND_FAILED
|
||||
*/
|
||||
struct sctp_ulpevent *sctp_ulpevent_make_send_failed(
|
||||
const struct sctp_association *asoc, struct sctp_chunk *chunk,
|
||||
__u16 flags, __u32 error, gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_send_failed *ssf;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Pull off any padding. */
|
||||
int len = ntohs(chunk->chunk_hdr->length);
|
||||
|
||||
/* Make skb with more room so we can prepend notification. */
|
||||
skb = skb_copy_expand(chunk->skb,
|
||||
sizeof(struct sctp_send_failed), /* headroom */
|
||||
0, /* tailroom */
|
||||
gfp);
|
||||
if (!skb)
|
||||
goto fail;
|
||||
|
||||
/* Pull off the common chunk header and DATA header. */
|
||||
skb_pull(skb, sizeof(struct sctp_data_chunk));
|
||||
len -= sizeof(struct sctp_data_chunk);
|
||||
|
||||
/* Embed the event fields inside the cloned skb. */
|
||||
event = sctp_skb2event(skb);
|
||||
sctp_ulpevent_init(event, MSG_NOTIFICATION, skb->truesize);
|
||||
|
||||
ssf = (struct sctp_send_failed *)
|
||||
skb_push(skb, sizeof(struct sctp_send_failed));
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.4 SCTP_SEND_FAILED
|
||||
*
|
||||
* ssf_type:
|
||||
* It should be SCTP_SEND_FAILED.
|
||||
*/
|
||||
ssf->ssf_type = SCTP_SEND_FAILED;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.4 SCTP_SEND_FAILED
|
||||
*
|
||||
* ssf_flags: 16 bits (unsigned integer)
|
||||
* The flag value will take one of the following values
|
||||
*
|
||||
* SCTP_DATA_UNSENT - Indicates that the data was never put on
|
||||
* the wire.
|
||||
*
|
||||
* SCTP_DATA_SENT - Indicates that the data was put on the wire.
|
||||
* Note that this does not necessarily mean that the
|
||||
* data was (or was not) successfully delivered.
|
||||
*/
|
||||
ssf->ssf_flags = flags;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.4 SCTP_SEND_FAILED
|
||||
*
|
||||
* ssf_length: sizeof (__u32)
|
||||
* This field is the total length of the notification data, including
|
||||
* the notification header.
|
||||
*/
|
||||
ssf->ssf_length = sizeof(struct sctp_send_failed) + len;
|
||||
skb_trim(skb, ssf->ssf_length);
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.4 SCTP_SEND_FAILED
|
||||
*
|
||||
* ssf_error: 16 bits (unsigned integer)
|
||||
* This value represents the reason why the send failed, and if set,
|
||||
* will be a SCTP protocol error code as defined in [SCTP] section
|
||||
* 3.3.10.
|
||||
*/
|
||||
ssf->ssf_error = error;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.4 SCTP_SEND_FAILED
|
||||
*
|
||||
* ssf_info: sizeof (struct sctp_sndrcvinfo)
|
||||
* The original send information associated with the undelivered
|
||||
* message.
|
||||
*/
|
||||
memcpy(&ssf->ssf_info, &chunk->sinfo, sizeof(struct sctp_sndrcvinfo));
|
||||
|
||||
/* Per TSVWG discussion with Randy. Allow the application to
|
||||
* ressemble a fragmented message.
|
||||
*/
|
||||
ssf->ssf_info.sinfo_flags = chunk->chunk_hdr->flags;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.4 SCTP_SEND_FAILED
|
||||
*
|
||||
* ssf_assoc_id: sizeof (sctp_assoc_t)
|
||||
* The association id field, sf_assoc_id, holds the identifier for the
|
||||
* association. All notifications for a given association have the
|
||||
* same association identifier. For TCP style socket, this field is
|
||||
* ignored.
|
||||
*/
|
||||
sctp_ulpevent_set_owner(event, asoc);
|
||||
ssf->ssf_assoc_id = sctp_assoc2id(asoc);
|
||||
return event;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create and initialize a SCTP_SHUTDOWN_EVENT notification.
|
||||
*
|
||||
* Socket Extensions for SCTP - draft-01
|
||||
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
||||
*/
|
||||
struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
|
||||
const struct sctp_association *asoc,
|
||||
__u16 flags, gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_shutdown_event *sse;
|
||||
struct sk_buff *skb;
|
||||
|
||||
event = sctp_ulpevent_new(sizeof(struct sctp_shutdown_event),
|
||||
MSG_NOTIFICATION, gfp);
|
||||
if (!event)
|
||||
goto fail;
|
||||
|
||||
skb = sctp_event2skb(event);
|
||||
sse = (struct sctp_shutdown_event *)
|
||||
skb_put(skb, sizeof(struct sctp_shutdown_event));
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
||||
*
|
||||
* sse_type
|
||||
* It should be SCTP_SHUTDOWN_EVENT
|
||||
*/
|
||||
sse->sse_type = SCTP_SHUTDOWN_EVENT;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
||||
*
|
||||
* sse_flags: 16 bits (unsigned integer)
|
||||
* Currently unused.
|
||||
*/
|
||||
sse->sse_flags = 0;
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
||||
*
|
||||
* sse_length: sizeof (__u32)
|
||||
* This field is the total length of the notification data, including
|
||||
* the notification header.
|
||||
*/
|
||||
sse->sse_length = sizeof(struct sctp_shutdown_event);
|
||||
|
||||
/* Socket Extensions for SCTP
|
||||
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
|
||||
*
|
||||
* sse_assoc_id: sizeof (sctp_assoc_t)
|
||||
* The association id field, holds the identifier for the association.
|
||||
* All notifications for a given association have the same association
|
||||
* identifier. For TCP style socket, this field is ignored.
|
||||
*/
|
||||
sctp_ulpevent_set_owner(event, asoc);
|
||||
sse->sse_assoc_id = sctp_assoc2id(asoc);
|
||||
|
||||
return event;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create and initialize a SCTP_ADAPTATION_INDICATION notification.
|
||||
*
|
||||
* Socket Extensions for SCTP
|
||||
* 5.3.1.6 SCTP_ADAPTATION_INDICATION
|
||||
*/
|
||||
struct sctp_ulpevent *sctp_ulpevent_make_adaptation_indication(
|
||||
const struct sctp_association *asoc, gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_adaptation_event *sai;
|
||||
struct sk_buff *skb;
|
||||
|
||||
event = sctp_ulpevent_new(sizeof(struct sctp_adaptation_event),
|
||||
MSG_NOTIFICATION, gfp);
|
||||
if (!event)
|
||||
goto fail;
|
||||
|
||||
skb = sctp_event2skb(event);
|
||||
sai = (struct sctp_adaptation_event *)
|
||||
skb_put(skb, sizeof(struct sctp_adaptation_event));
|
||||
|
||||
sai->sai_type = SCTP_ADAPTATION_INDICATION;
|
||||
sai->sai_flags = 0;
|
||||
sai->sai_length = sizeof(struct sctp_adaptation_event);
|
||||
sai->sai_adaptation_ind = asoc->peer.adaptation_ind;
|
||||
sctp_ulpevent_set_owner(event, asoc);
|
||||
sai->sai_assoc_id = sctp_assoc2id(asoc);
|
||||
|
||||
return event;
|
||||
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* A message has been received. Package this message as a notification
|
||||
* to pass it to the upper layers. Go ahead and calculate the sndrcvinfo
|
||||
* even if filtered out later.
|
||||
*
|
||||
* Socket Extensions for SCTP
|
||||
* 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
|
||||
*/
|
||||
struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(struct sctp_association *asoc,
|
||||
struct sctp_chunk *chunk,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event = NULL;
|
||||
struct sk_buff *skb;
|
||||
size_t padding, len;
|
||||
|
||||
/* Clone the original skb, sharing the data. */
|
||||
skb = skb_clone(chunk->skb, gfp);
|
||||
if (!skb)
|
||||
goto fail;
|
||||
|
||||
/* First calculate the padding, so we don't inadvertently
|
||||
* pass up the wrong length to the user.
|
||||
*
|
||||
* RFC 2960 - Section 3.2 Chunk Field Descriptions
|
||||
*
|
||||
* The total length of a chunk(including Type, Length and Value fields)
|
||||
* MUST be a multiple of 4 bytes. If the length of the chunk is not a
|
||||
* multiple of 4 bytes, the sender MUST pad the chunk with all zero
|
||||
* bytes and this padding is not included in the chunk length field.
|
||||
* The sender should never pad with more than 3 bytes. The receiver
|
||||
* MUST ignore the padding bytes.
|
||||
*/
|
||||
len = ntohs(chunk->chunk_hdr->length);
|
||||
padding = WORD_ROUND(len) - len;
|
||||
|
||||
/* Fixup cloned skb with just this chunks data. */
|
||||
skb_trim(skb, chunk->chunk_end - padding - skb->data);
|
||||
|
||||
/* Embed the event fields inside the cloned skb. */
|
||||
event = sctp_skb2event(skb);
|
||||
|
||||
/* Initialize event with flags 0 and correct length
|
||||
* Since this is a clone of the original skb, only account for
|
||||
* the data of this chunk as other chunks will be accounted separately.
|
||||
*/
|
||||
sctp_ulpevent_init(event, 0, skb->len + sizeof(struct sk_buff));
|
||||
|
||||
sctp_ulpevent_receive_data(event, asoc);
|
||||
|
||||
event->stream = ntohs(chunk->subh.data_hdr->stream);
|
||||
event->ssn = ntohs(chunk->subh.data_hdr->ssn);
|
||||
event->ppid = chunk->subh.data_hdr->ppid;
|
||||
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
|
||||
event->flags |= SCTP_UNORDERED;
|
||||
event->cumtsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
|
||||
}
|
||||
event->tsn = ntohl(chunk->subh.data_hdr->tsn);
|
||||
event->msg_flags |= chunk->chunk_hdr->flags;
|
||||
event->iif = sctp_chunk_iif(chunk);
|
||||
|
||||
fail:
|
||||
return event;
|
||||
}
|
||||
|
||||
/* Create a partial delivery related event.
|
||||
*
|
||||
* 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
|
||||
*
|
||||
* When a receiver is engaged in a partial delivery of a
|
||||
* message this notification will be used to indicate
|
||||
* various events.
|
||||
*/
|
||||
struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
|
||||
const struct sctp_association *asoc, __u32 indication,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_pdapi_event *pd;
|
||||
struct sk_buff *skb;
|
||||
|
||||
event = sctp_ulpevent_new(sizeof(struct sctp_pdapi_event),
|
||||
MSG_NOTIFICATION, gfp);
|
||||
if (!event)
|
||||
goto fail;
|
||||
|
||||
skb = sctp_event2skb(event);
|
||||
pd = (struct sctp_pdapi_event *)
|
||||
skb_put(skb, sizeof(struct sctp_pdapi_event));
|
||||
|
||||
/* pdapi_type
|
||||
* It should be SCTP_PARTIAL_DELIVERY_EVENT
|
||||
*
|
||||
* pdapi_flags: 16 bits (unsigned integer)
|
||||
* Currently unused.
|
||||
*/
|
||||
pd->pdapi_type = SCTP_PARTIAL_DELIVERY_EVENT;
|
||||
pd->pdapi_flags = 0;
|
||||
|
||||
/* pdapi_length: 32 bits (unsigned integer)
|
||||
*
|
||||
* This field is the total length of the notification data, including
|
||||
* the notification header. It will generally be sizeof (struct
|
||||
* sctp_pdapi_event).
|
||||
*/
|
||||
pd->pdapi_length = sizeof(struct sctp_pdapi_event);
|
||||
|
||||
/* pdapi_indication: 32 bits (unsigned integer)
|
||||
*
|
||||
* This field holds the indication being sent to the application.
|
||||
*/
|
||||
pd->pdapi_indication = indication;
|
||||
|
||||
/* pdapi_assoc_id: sizeof (sctp_assoc_t)
|
||||
*
|
||||
* The association id field, holds the identifier for the association.
|
||||
*/
|
||||
sctp_ulpevent_set_owner(event, asoc);
|
||||
pd->pdapi_assoc_id = sctp_assoc2id(asoc);
|
||||
|
||||
return event;
|
||||
fail:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Return the notification type, assuming this is a notification
|
||||
* event.
|
||||
*/
|
||||
__u16 sctp_ulpevent_get_notification_type(const struct sctp_ulpevent *event)
|
||||
{
|
||||
union sctp_notification *notification;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = sctp_event2skb((struct sctp_ulpevent *)event);
|
||||
notification = (union sctp_notification *) skb->data;
|
||||
return notification->sn_header.sn_type;
|
||||
}
|
||||
|
||||
/* Copy out the sndrcvinfo into a msghdr. */
|
||||
void sctp_ulpevent_read_sndrcvinfo(const struct sctp_ulpevent *event,
|
||||
struct msghdr *msghdr)
|
||||
{
|
||||
struct sctp_sndrcvinfo sinfo;
|
||||
|
||||
if (sctp_ulpevent_is_notification(event))
|
||||
return;
|
||||
|
||||
/* Sockets API Extensions for SCTP
|
||||
* Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
|
||||
*
|
||||
* sinfo_stream: 16 bits (unsigned integer)
|
||||
*
|
||||
* For recvmsg() the SCTP stack places the message's stream number in
|
||||
* this value.
|
||||
*/
|
||||
sinfo.sinfo_stream = event->stream;
|
||||
/* sinfo_ssn: 16 bits (unsigned integer)
|
||||
*
|
||||
* For recvmsg() this value contains the stream sequence number that
|
||||
* the remote endpoint placed in the DATA chunk. For fragmented
|
||||
* messages this is the same number for all deliveries of the message
|
||||
* (if more than one recvmsg() is needed to read the message).
|
||||
*/
|
||||
sinfo.sinfo_ssn = event->ssn;
|
||||
/* sinfo_ppid: 32 bits (unsigned integer)
|
||||
*
|
||||
* In recvmsg() this value is
|
||||
* the same information that was passed by the upper layer in the peer
|
||||
* application. Please note that byte order issues are NOT accounted
|
||||
* for and this information is passed opaquely by the SCTP stack from
|
||||
* one end to the other.
|
||||
*/
|
||||
sinfo.sinfo_ppid = event->ppid;
|
||||
/* sinfo_flags: 16 bits (unsigned integer)
|
||||
*
|
||||
* This field may contain any of the following flags and is composed of
|
||||
* a bitwise OR of these values.
|
||||
*
|
||||
* recvmsg() flags:
|
||||
*
|
||||
* SCTP_UNORDERED - This flag is present when the message was sent
|
||||
* non-ordered.
|
||||
*/
|
||||
sinfo.sinfo_flags = event->flags;
|
||||
/* sinfo_tsn: 32 bit (unsigned integer)
|
||||
*
|
||||
* For the receiving side, this field holds a TSN that was
|
||||
* assigned to one of the SCTP Data Chunks.
|
||||
*/
|
||||
sinfo.sinfo_tsn = event->tsn;
|
||||
/* sinfo_cumtsn: 32 bit (unsigned integer)
|
||||
*
|
||||
* This field will hold the current cumulative TSN as
|
||||
* known by the underlying SCTP layer. Note this field is
|
||||
* ignored when sending and only valid for a receive
|
||||
* operation when sinfo_flags are set to SCTP_UNORDERED.
|
||||
*/
|
||||
sinfo.sinfo_cumtsn = event->cumtsn;
|
||||
/* sinfo_assoc_id: sizeof (sctp_assoc_t)
|
||||
*
|
||||
* The association handle field, sinfo_assoc_id, holds the identifier
|
||||
* for the association announced in the COMMUNICATION_UP notification.
|
||||
* All notifications for a given association have the same identifier.
|
||||
* Ignored for one-to-one style sockets.
|
||||
*/
|
||||
sinfo.sinfo_assoc_id = sctp_assoc2id(event->asoc);
|
||||
|
||||
/* context value that is set via SCTP_CONTEXT socket option. */
|
||||
sinfo.sinfo_context = event->asoc->default_rcv_context;
|
||||
|
||||
/* These fields are not used while receiving. */
|
||||
sinfo.sinfo_timetolive = 0;
|
||||
|
||||
put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV,
|
||||
sizeof(struct sctp_sndrcvinfo), (void *)&sinfo);
|
||||
}
|
||||
|
||||
/* Do accounting for bytes received and hold a reference to the association
|
||||
* for each skb.
|
||||
*/
|
||||
static void sctp_ulpevent_receive_data(struct sctp_ulpevent *event,
|
||||
struct sctp_association *asoc)
|
||||
{
|
||||
struct sk_buff *skb, *frag;
|
||||
|
||||
skb = sctp_event2skb(event);
|
||||
/* Set the owner and charge rwnd for bytes received. */
|
||||
sctp_ulpevent_set_owner(event, asoc);
|
||||
sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb));
|
||||
|
||||
if (!skb->data_len)
|
||||
return;
|
||||
|
||||
/* Note: Not clearing the entire event struct as this is just a
|
||||
* fragment of the real event. However, we still need to do rwnd
|
||||
* accounting.
|
||||
* In general, the skb passed from IP can have only 1 level of
|
||||
* fragments. But we allow multiple levels of fragments.
|
||||
*/
|
||||
for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
|
||||
sctp_ulpevent_receive_data(sctp_skb2event(frag), asoc);
|
||||
}
|
||||
}
|
||||
|
||||
/* Do accounting for bytes just read by user and release the references to
|
||||
* the association.
|
||||
*/
|
||||
static void sctp_ulpevent_release_data(struct sctp_ulpevent *event)
|
||||
{
|
||||
struct sk_buff *skb, *frag;
|
||||
unsigned int len;
|
||||
|
||||
/* Current stack structures assume that the rcv buffer is
|
||||
* per socket. For UDP style sockets this is not true as
|
||||
* multiple associations may be on a single UDP-style socket.
|
||||
* Use the local private area of the skb to track the owning
|
||||
* association.
|
||||
*/
|
||||
|
||||
skb = sctp_event2skb(event);
|
||||
len = skb->len;
|
||||
|
||||
if (!skb->data_len)
|
||||
goto done;
|
||||
|
||||
/* Don't forget the fragments. */
|
||||
for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
|
||||
/* NOTE: skb_shinfos are recursive. Although IP returns
|
||||
* skb's with only 1 level of fragments, SCTP reassembly can
|
||||
* increase the levels.
|
||||
*/
|
||||
sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
|
||||
}
|
||||
|
||||
done:
|
||||
sctp_assoc_rwnd_increase(event->asoc, len);
|
||||
sctp_ulpevent_release_owner(event);
|
||||
}
|
||||
|
||||
static void sctp_ulpevent_release_frag_data(struct sctp_ulpevent *event)
|
||||
{
|
||||
struct sk_buff *skb, *frag;
|
||||
|
||||
skb = sctp_event2skb(event);
|
||||
|
||||
if (!skb->data_len)
|
||||
goto done;
|
||||
|
||||
/* Don't forget the fragments. */
|
||||
for (frag = skb_shinfo(skb)->frag_list; frag; frag = frag->next) {
|
||||
/* NOTE: skb_shinfos are recursive. Although IP returns
|
||||
* skb's with only 1 level of fragments, SCTP reassembly can
|
||||
* increase the levels.
|
||||
*/
|
||||
sctp_ulpevent_release_frag_data(sctp_skb2event(frag));
|
||||
}
|
||||
|
||||
done:
|
||||
sctp_ulpevent_release_owner(event);
|
||||
}
|
||||
|
||||
/* Free a ulpevent that has an owner. It includes releasing the reference
|
||||
* to the owner, updating the rwnd in case of a DATA event and freeing the
|
||||
* skb.
|
||||
*/
|
||||
void sctp_ulpevent_free(struct sctp_ulpevent *event)
|
||||
{
|
||||
if (sctp_ulpevent_is_notification(event))
|
||||
sctp_ulpevent_release_owner(event);
|
||||
else
|
||||
sctp_ulpevent_release_data(event);
|
||||
|
||||
kfree_skb(sctp_event2skb(event));
|
||||
}
|
||||
|
||||
/* Purge the skb lists holding ulpevents. */
|
||||
void sctp_queue_purge_ulpevents(struct sk_buff_head *list)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
while ((skb = skb_dequeue(list)) != NULL)
|
||||
sctp_ulpevent_free(sctp_skb2event(skb));
|
||||
}
|
||||
908
net/sctp/ulpqueue.c
Normal file
908
net/sctp/ulpqueue.c
Normal file
@@ -0,0 +1,908 @@
|
||||
/* 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 abstraction carries sctp events to the ULP (sockets).
|
||||
*
|
||||
* 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:
|
||||
* Jon Grimm <jgrimm@us.ibm.com>
|
||||
* La Monte H.P. Yarroll <piggy@acm.org>
|
||||
* Sridhar Samudrala <sri@us.ibm.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/types.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/sctp/structs.h>
|
||||
#include <net/sctp/sctp.h>
|
||||
#include <net/sctp/sm.h>
|
||||
|
||||
/* Forward declarations for internal helpers. */
|
||||
static struct sctp_ulpevent * sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
|
||||
struct sctp_ulpevent *);
|
||||
static struct sctp_ulpevent * sctp_ulpq_order(struct sctp_ulpq *,
|
||||
struct sctp_ulpevent *);
|
||||
|
||||
/* 1st Level Abstractions */
|
||||
|
||||
/* Initialize a ULP queue from a block of memory. */
|
||||
struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq,
|
||||
struct sctp_association *asoc)
|
||||
{
|
||||
memset(ulpq, 0, sizeof(struct sctp_ulpq));
|
||||
|
||||
ulpq->asoc = asoc;
|
||||
skb_queue_head_init(&ulpq->reasm);
|
||||
skb_queue_head_init(&ulpq->lobby);
|
||||
ulpq->pd_mode = 0;
|
||||
ulpq->malloced = 0;
|
||||
|
||||
return ulpq;
|
||||
}
|
||||
|
||||
|
||||
/* Flush the reassembly and ordering queues. */
|
||||
void sctp_ulpq_flush(struct sctp_ulpq *ulpq)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct sctp_ulpevent *event;
|
||||
|
||||
while ((skb = __skb_dequeue(&ulpq->lobby)) != NULL) {
|
||||
event = sctp_skb2event(skb);
|
||||
sctp_ulpevent_free(event);
|
||||
}
|
||||
|
||||
while ((skb = __skb_dequeue(&ulpq->reasm)) != NULL) {
|
||||
event = sctp_skb2event(skb);
|
||||
sctp_ulpevent_free(event);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Dispose of a ulpqueue. */
|
||||
void sctp_ulpq_free(struct sctp_ulpq *ulpq)
|
||||
{
|
||||
sctp_ulpq_flush(ulpq);
|
||||
if (ulpq->malloced)
|
||||
kfree(ulpq);
|
||||
}
|
||||
|
||||
/* Process an incoming DATA chunk. */
|
||||
int sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sk_buff_head temp;
|
||||
sctp_data_chunk_t *hdr;
|
||||
struct sctp_ulpevent *event;
|
||||
|
||||
hdr = (sctp_data_chunk_t *) chunk->chunk_hdr;
|
||||
|
||||
/* Create an event from the incoming chunk. */
|
||||
event = sctp_ulpevent_make_rcvmsg(chunk->asoc, chunk, gfp);
|
||||
if (!event)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Do reassembly if needed. */
|
||||
event = sctp_ulpq_reasm(ulpq, event);
|
||||
|
||||
/* Do ordering if needed. */
|
||||
if ((event) && (event->msg_flags & MSG_EOR)){
|
||||
/* Create a temporary list to collect chunks on. */
|
||||
skb_queue_head_init(&temp);
|
||||
__skb_queue_tail(&temp, sctp_event2skb(event));
|
||||
|
||||
event = sctp_ulpq_order(ulpq, event);
|
||||
}
|
||||
|
||||
/* Send event to the ULP. 'event' is the sctp_ulpevent for
|
||||
* very first SKB on the 'temp' list.
|
||||
*/
|
||||
if (event)
|
||||
sctp_ulpq_tail_event(ulpq, event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Add a new event for propagation to the ULP. */
|
||||
/* Clear the partial delivery mode for this socket. Note: This
|
||||
* assumes that no association is currently in partial delivery mode.
|
||||
*/
|
||||
int sctp_clear_pd(struct sock *sk)
|
||||
{
|
||||
struct sctp_sock *sp = sctp_sk(sk);
|
||||
|
||||
sp->pd_mode = 0;
|
||||
if (!skb_queue_empty(&sp->pd_lobby)) {
|
||||
struct list_head *list;
|
||||
sctp_skb_list_tail(&sp->pd_lobby, &sk->sk_receive_queue);
|
||||
list = (struct list_head *)&sctp_sk(sk)->pd_lobby;
|
||||
INIT_LIST_HEAD(list);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clear the pd_mode and restart any pending messages waiting for delivery. */
|
||||
static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq)
|
||||
{
|
||||
ulpq->pd_mode = 0;
|
||||
return sctp_clear_pd(ulpq->asoc->base.sk);
|
||||
}
|
||||
|
||||
/* If the SKB of 'event' is on a list, it is the first such member
|
||||
* of that list.
|
||||
*/
|
||||
int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
|
||||
{
|
||||
struct sock *sk = ulpq->asoc->base.sk;
|
||||
struct sk_buff_head *queue, *skb_list;
|
||||
struct sk_buff *skb = sctp_event2skb(event);
|
||||
int clear_pd = 0;
|
||||
|
||||
skb_list = (struct sk_buff_head *) skb->prev;
|
||||
|
||||
/* If the socket is just going to throw this away, do not
|
||||
* even try to deliver it.
|
||||
*/
|
||||
if (sock_flag(sk, SOCK_DEAD) || (sk->sk_shutdown & RCV_SHUTDOWN))
|
||||
goto out_free;
|
||||
|
||||
/* Check if the user wishes to receive this event. */
|
||||
if (!sctp_ulpevent_is_enabled(event, &sctp_sk(sk)->subscribe))
|
||||
goto out_free;
|
||||
|
||||
/* If we are in partial delivery mode, post to the lobby until
|
||||
* partial delivery is cleared, unless, of course _this_ is
|
||||
* the association the cause of the partial delivery.
|
||||
*/
|
||||
|
||||
if (!sctp_sk(sk)->pd_mode) {
|
||||
queue = &sk->sk_receive_queue;
|
||||
} else if (ulpq->pd_mode) {
|
||||
/* If the association is in partial delivery, we
|
||||
* need to finish delivering the partially processed
|
||||
* packet before passing any other data. This is
|
||||
* because we don't truly support stream interleaving.
|
||||
*/
|
||||
if ((event->msg_flags & MSG_NOTIFICATION) ||
|
||||
(SCTP_DATA_NOT_FRAG ==
|
||||
(event->msg_flags & SCTP_DATA_FRAG_MASK)))
|
||||
queue = &sctp_sk(sk)->pd_lobby;
|
||||
else {
|
||||
clear_pd = event->msg_flags & MSG_EOR;
|
||||
queue = &sk->sk_receive_queue;
|
||||
}
|
||||
} else
|
||||
queue = &sctp_sk(sk)->pd_lobby;
|
||||
|
||||
|
||||
/* If we are harvesting multiple skbs they will be
|
||||
* collected on a list.
|
||||
*/
|
||||
if (skb_list)
|
||||
sctp_skb_list_tail(skb_list, queue);
|
||||
else
|
||||
__skb_queue_tail(queue, skb);
|
||||
|
||||
/* Did we just complete partial delivery and need to get
|
||||
* rolling again? Move pending data to the receive
|
||||
* queue.
|
||||
*/
|
||||
if (clear_pd)
|
||||
sctp_ulpq_clear_pd(ulpq);
|
||||
|
||||
if (queue == &sk->sk_receive_queue)
|
||||
sk->sk_data_ready(sk, 0);
|
||||
return 1;
|
||||
|
||||
out_free:
|
||||
if (skb_list)
|
||||
sctp_queue_purge_ulpevents(skb_list);
|
||||
else
|
||||
sctp_ulpevent_free(event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 2nd Level Abstractions */
|
||||
|
||||
/* Helper function to store chunks that need to be reassembled. */
|
||||
static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
|
||||
struct sctp_ulpevent *event)
|
||||
{
|
||||
struct sk_buff *pos;
|
||||
struct sctp_ulpevent *cevent;
|
||||
__u32 tsn, ctsn;
|
||||
|
||||
tsn = event->tsn;
|
||||
|
||||
/* See if it belongs at the end. */
|
||||
pos = skb_peek_tail(&ulpq->reasm);
|
||||
if (!pos) {
|
||||
__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Short circuit just dropping it at the end. */
|
||||
cevent = sctp_skb2event(pos);
|
||||
ctsn = cevent->tsn;
|
||||
if (TSN_lt(ctsn, tsn)) {
|
||||
__skb_queue_tail(&ulpq->reasm, sctp_event2skb(event));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the right place in this list. We store them by TSN. */
|
||||
skb_queue_walk(&ulpq->reasm, pos) {
|
||||
cevent = sctp_skb2event(pos);
|
||||
ctsn = cevent->tsn;
|
||||
|
||||
if (TSN_lt(tsn, ctsn))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Insert before pos. */
|
||||
__skb_insert(sctp_event2skb(event), pos->prev, pos, &ulpq->reasm);
|
||||
|
||||
}
|
||||
|
||||
/* Helper function to return an event corresponding to the reassembled
|
||||
* datagram.
|
||||
* This routine creates a re-assembled skb given the first and last skb's
|
||||
* as stored in the reassembly queue. The skb's may be non-linear if the sctp
|
||||
* payload was fragmented on the way and ip had to reassemble them.
|
||||
* We add the rest of skb's to the first skb's fraglist.
|
||||
*/
|
||||
static struct sctp_ulpevent *sctp_make_reassembled_event(struct sk_buff_head *queue, struct sk_buff *f_frag, struct sk_buff *l_frag)
|
||||
{
|
||||
struct sk_buff *pos;
|
||||
struct sk_buff *new = NULL;
|
||||
struct sctp_ulpevent *event;
|
||||
struct sk_buff *pnext, *last;
|
||||
struct sk_buff *list = skb_shinfo(f_frag)->frag_list;
|
||||
|
||||
/* Store the pointer to the 2nd skb */
|
||||
if (f_frag == l_frag)
|
||||
pos = NULL;
|
||||
else
|
||||
pos = f_frag->next;
|
||||
|
||||
/* Get the last skb in the f_frag's frag_list if present. */
|
||||
for (last = list; list; last = list, list = list->next);
|
||||
|
||||
/* Add the list of remaining fragments to the first fragments
|
||||
* frag_list.
|
||||
*/
|
||||
if (last)
|
||||
last->next = pos;
|
||||
else {
|
||||
if (skb_cloned(f_frag)) {
|
||||
/* This is a cloned skb, we can't just modify
|
||||
* the frag_list. We need a new skb to do that.
|
||||
* Instead of calling skb_unshare(), we'll do it
|
||||
* ourselves since we need to delay the free.
|
||||
*/
|
||||
new = skb_copy(f_frag, GFP_ATOMIC);
|
||||
if (!new)
|
||||
return NULL; /* try again later */
|
||||
|
||||
sctp_skb_set_owner_r(new, f_frag->sk);
|
||||
|
||||
skb_shinfo(new)->frag_list = pos;
|
||||
} else
|
||||
skb_shinfo(f_frag)->frag_list = pos;
|
||||
}
|
||||
|
||||
/* Remove the first fragment from the reassembly queue. */
|
||||
__skb_unlink(f_frag, queue);
|
||||
|
||||
/* if we did unshare, then free the old skb and re-assign */
|
||||
if (new) {
|
||||
kfree_skb(f_frag);
|
||||
f_frag = new;
|
||||
}
|
||||
|
||||
while (pos) {
|
||||
|
||||
pnext = pos->next;
|
||||
|
||||
/* Update the len and data_len fields of the first fragment. */
|
||||
f_frag->len += pos->len;
|
||||
f_frag->data_len += pos->len;
|
||||
|
||||
/* Remove the fragment from the reassembly queue. */
|
||||
__skb_unlink(pos, queue);
|
||||
|
||||
/* Break if we have reached the last fragment. */
|
||||
if (pos == l_frag)
|
||||
break;
|
||||
pos->next = pnext;
|
||||
pos = pnext;
|
||||
};
|
||||
|
||||
event = sctp_skb2event(f_frag);
|
||||
SCTP_INC_STATS(SCTP_MIB_REASMUSRMSGS);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
|
||||
/* Helper function to check if an incoming chunk has filled up the last
|
||||
* missing fragment in a SCTP datagram and return the corresponding event.
|
||||
*/
|
||||
static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *ulpq)
|
||||
{
|
||||
struct sk_buff *pos;
|
||||
struct sctp_ulpevent *cevent;
|
||||
struct sk_buff *first_frag = NULL;
|
||||
__u32 ctsn, next_tsn;
|
||||
struct sctp_ulpevent *retval = NULL;
|
||||
|
||||
/* Initialized to 0 just to avoid compiler warning message. Will
|
||||
* never be used with this value. It is referenced only after it
|
||||
* is set when we find the first fragment of a message.
|
||||
*/
|
||||
next_tsn = 0;
|
||||
|
||||
/* The chunks are held in the reasm queue sorted by TSN.
|
||||
* Walk through the queue sequentially and look for a sequence of
|
||||
* fragmented chunks that complete a datagram.
|
||||
* 'first_frag' and next_tsn are reset when we find a chunk which
|
||||
* is the first fragment of a datagram. Once these 2 fields are set
|
||||
* we expect to find the remaining middle fragments and the last
|
||||
* fragment in order. If not, first_frag is reset to NULL and we
|
||||
* start the next pass when we find another first fragment.
|
||||
*/
|
||||
skb_queue_walk(&ulpq->reasm, pos) {
|
||||
cevent = sctp_skb2event(pos);
|
||||
ctsn = cevent->tsn;
|
||||
|
||||
switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
|
||||
case SCTP_DATA_FIRST_FRAG:
|
||||
first_frag = pos;
|
||||
next_tsn = ctsn + 1;
|
||||
break;
|
||||
|
||||
case SCTP_DATA_MIDDLE_FRAG:
|
||||
if ((first_frag) && (ctsn == next_tsn))
|
||||
next_tsn++;
|
||||
else
|
||||
first_frag = NULL;
|
||||
break;
|
||||
|
||||
case SCTP_DATA_LAST_FRAG:
|
||||
if (first_frag && (ctsn == next_tsn))
|
||||
goto found;
|
||||
else
|
||||
first_frag = NULL;
|
||||
break;
|
||||
};
|
||||
|
||||
}
|
||||
done:
|
||||
return retval;
|
||||
found:
|
||||
retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, pos);
|
||||
if (retval)
|
||||
retval->msg_flags |= MSG_EOR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Retrieve the next set of fragments of a partial message. */
|
||||
static inline struct sctp_ulpevent *sctp_ulpq_retrieve_partial(struct sctp_ulpq *ulpq)
|
||||
{
|
||||
struct sk_buff *pos, *last_frag, *first_frag;
|
||||
struct sctp_ulpevent *cevent;
|
||||
__u32 ctsn, next_tsn;
|
||||
int is_last;
|
||||
struct sctp_ulpevent *retval;
|
||||
|
||||
/* The chunks are held in the reasm queue sorted by TSN.
|
||||
* Walk through the queue sequentially and look for the first
|
||||
* sequence of fragmented chunks.
|
||||
*/
|
||||
|
||||
if (skb_queue_empty(&ulpq->reasm))
|
||||
return NULL;
|
||||
|
||||
last_frag = first_frag = NULL;
|
||||
retval = NULL;
|
||||
next_tsn = 0;
|
||||
is_last = 0;
|
||||
|
||||
skb_queue_walk(&ulpq->reasm, pos) {
|
||||
cevent = sctp_skb2event(pos);
|
||||
ctsn = cevent->tsn;
|
||||
|
||||
switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
|
||||
case SCTP_DATA_MIDDLE_FRAG:
|
||||
if (!first_frag) {
|
||||
first_frag = pos;
|
||||
next_tsn = ctsn + 1;
|
||||
last_frag = pos;
|
||||
} else if (next_tsn == ctsn)
|
||||
next_tsn++;
|
||||
else
|
||||
goto done;
|
||||
break;
|
||||
case SCTP_DATA_LAST_FRAG:
|
||||
if (!first_frag)
|
||||
first_frag = pos;
|
||||
else if (ctsn != next_tsn)
|
||||
goto done;
|
||||
last_frag = pos;
|
||||
is_last = 1;
|
||||
goto done;
|
||||
default:
|
||||
return NULL;
|
||||
};
|
||||
}
|
||||
|
||||
/* We have the reassembled event. There is no need to look
|
||||
* further.
|
||||
*/
|
||||
done:
|
||||
retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, last_frag);
|
||||
if (retval && is_last)
|
||||
retval->msg_flags |= MSG_EOR;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
||||
/* Helper function to reassemble chunks. Hold chunks on the reasm queue that
|
||||
* need reassembling.
|
||||
*/
|
||||
static struct sctp_ulpevent *sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
|
||||
struct sctp_ulpevent *event)
|
||||
{
|
||||
struct sctp_ulpevent *retval = NULL;
|
||||
|
||||
/* Check if this is part of a fragmented message. */
|
||||
if (SCTP_DATA_NOT_FRAG == (event->msg_flags & SCTP_DATA_FRAG_MASK)) {
|
||||
event->msg_flags |= MSG_EOR;
|
||||
return event;
|
||||
}
|
||||
|
||||
sctp_ulpq_store_reasm(ulpq, event);
|
||||
if (!ulpq->pd_mode)
|
||||
retval = sctp_ulpq_retrieve_reassembled(ulpq);
|
||||
else {
|
||||
__u32 ctsn, ctsnap;
|
||||
|
||||
/* Do not even bother unless this is the next tsn to
|
||||
* be delivered.
|
||||
*/
|
||||
ctsn = event->tsn;
|
||||
ctsnap = sctp_tsnmap_get_ctsn(&ulpq->asoc->peer.tsn_map);
|
||||
if (TSN_lte(ctsn, ctsnap))
|
||||
retval = sctp_ulpq_retrieve_partial(ulpq);
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Retrieve the first part (sequential fragments) for partial delivery. */
|
||||
static inline struct sctp_ulpevent *sctp_ulpq_retrieve_first(struct sctp_ulpq *ulpq)
|
||||
{
|
||||
struct sk_buff *pos, *last_frag, *first_frag;
|
||||
struct sctp_ulpevent *cevent;
|
||||
__u32 ctsn, next_tsn;
|
||||
struct sctp_ulpevent *retval;
|
||||
|
||||
/* The chunks are held in the reasm queue sorted by TSN.
|
||||
* Walk through the queue sequentially and look for a sequence of
|
||||
* fragmented chunks that start a datagram.
|
||||
*/
|
||||
|
||||
if (skb_queue_empty(&ulpq->reasm))
|
||||
return NULL;
|
||||
|
||||
last_frag = first_frag = NULL;
|
||||
retval = NULL;
|
||||
next_tsn = 0;
|
||||
|
||||
skb_queue_walk(&ulpq->reasm, pos) {
|
||||
cevent = sctp_skb2event(pos);
|
||||
ctsn = cevent->tsn;
|
||||
|
||||
switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
|
||||
case SCTP_DATA_FIRST_FRAG:
|
||||
if (!first_frag) {
|
||||
first_frag = pos;
|
||||
next_tsn = ctsn + 1;
|
||||
last_frag = pos;
|
||||
} else
|
||||
goto done;
|
||||
break;
|
||||
|
||||
case SCTP_DATA_MIDDLE_FRAG:
|
||||
if (!first_frag)
|
||||
return NULL;
|
||||
if (ctsn == next_tsn) {
|
||||
next_tsn++;
|
||||
last_frag = pos;
|
||||
} else
|
||||
goto done;
|
||||
break;
|
||||
default:
|
||||
return NULL;
|
||||
};
|
||||
}
|
||||
|
||||
/* We have the reassembled event. There is no need to look
|
||||
* further.
|
||||
*/
|
||||
done:
|
||||
retval = sctp_make_reassembled_event(&ulpq->reasm, first_frag, last_frag);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Helper function to gather skbs that have possibly become
|
||||
* ordered by an an incoming chunk.
|
||||
*/
|
||||
static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
|
||||
struct sctp_ulpevent *event)
|
||||
{
|
||||
struct sk_buff_head *event_list;
|
||||
struct sk_buff *pos, *tmp;
|
||||
struct sctp_ulpevent *cevent;
|
||||
struct sctp_stream *in;
|
||||
__u16 sid, csid;
|
||||
__u16 ssn, cssn;
|
||||
|
||||
sid = event->stream;
|
||||
ssn = event->ssn;
|
||||
in = &ulpq->asoc->ssnmap->in;
|
||||
|
||||
event_list = (struct sk_buff_head *) sctp_event2skb(event)->prev;
|
||||
|
||||
/* We are holding the chunks by stream, by SSN. */
|
||||
sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
|
||||
cevent = (struct sctp_ulpevent *) pos->cb;
|
||||
csid = cevent->stream;
|
||||
cssn = cevent->ssn;
|
||||
|
||||
/* Have we gone too far? */
|
||||
if (csid > sid)
|
||||
break;
|
||||
|
||||
/* Have we not gone far enough? */
|
||||
if (csid < sid)
|
||||
continue;
|
||||
|
||||
if (cssn != sctp_ssn_peek(in, sid))
|
||||
break;
|
||||
|
||||
/* Found it, so mark in the ssnmap. */
|
||||
sctp_ssn_next(in, sid);
|
||||
|
||||
__skb_unlink(pos, &ulpq->lobby);
|
||||
|
||||
/* Attach all gathered skbs to the event. */
|
||||
__skb_queue_tail(event_list, pos);
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper function to store chunks needing ordering. */
|
||||
static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *ulpq,
|
||||
struct sctp_ulpevent *event)
|
||||
{
|
||||
struct sk_buff *pos;
|
||||
struct sctp_ulpevent *cevent;
|
||||
__u16 sid, csid;
|
||||
__u16 ssn, cssn;
|
||||
|
||||
pos = skb_peek_tail(&ulpq->lobby);
|
||||
if (!pos) {
|
||||
__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
|
||||
return;
|
||||
}
|
||||
|
||||
sid = event->stream;
|
||||
ssn = event->ssn;
|
||||
|
||||
cevent = (struct sctp_ulpevent *) pos->cb;
|
||||
csid = cevent->stream;
|
||||
cssn = cevent->ssn;
|
||||
if (sid > csid) {
|
||||
__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((sid == csid) && SSN_lt(cssn, ssn)) {
|
||||
__skb_queue_tail(&ulpq->lobby, sctp_event2skb(event));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Find the right place in this list. We store them by
|
||||
* stream ID and then by SSN.
|
||||
*/
|
||||
skb_queue_walk(&ulpq->lobby, pos) {
|
||||
cevent = (struct sctp_ulpevent *) pos->cb;
|
||||
csid = cevent->stream;
|
||||
cssn = cevent->ssn;
|
||||
|
||||
if (csid > sid)
|
||||
break;
|
||||
if (csid == sid && SSN_lt(ssn, cssn))
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* Insert before pos. */
|
||||
__skb_insert(sctp_event2skb(event), pos->prev, pos, &ulpq->lobby);
|
||||
|
||||
}
|
||||
|
||||
static struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *ulpq,
|
||||
struct sctp_ulpevent *event)
|
||||
{
|
||||
__u16 sid, ssn;
|
||||
struct sctp_stream *in;
|
||||
|
||||
/* Check if this message needs ordering. */
|
||||
if (SCTP_DATA_UNORDERED & event->msg_flags)
|
||||
return event;
|
||||
|
||||
/* Note: The stream ID must be verified before this routine. */
|
||||
sid = event->stream;
|
||||
ssn = event->ssn;
|
||||
in = &ulpq->asoc->ssnmap->in;
|
||||
|
||||
/* Is this the expected SSN for this stream ID? */
|
||||
if (ssn != sctp_ssn_peek(in, sid)) {
|
||||
/* We've received something out of order, so find where it
|
||||
* needs to be placed. We order by stream and then by SSN.
|
||||
*/
|
||||
sctp_ulpq_store_ordered(ulpq, event);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Mark that the next chunk has been found. */
|
||||
sctp_ssn_next(in, sid);
|
||||
|
||||
/* Go find any other chunks that were waiting for
|
||||
* ordering.
|
||||
*/
|
||||
sctp_ulpq_retrieve_ordered(ulpq, event);
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
/* Helper function to gather skbs that have possibly become
|
||||
* ordered by forward tsn skipping their dependencies.
|
||||
*/
|
||||
static inline void sctp_ulpq_reap_ordered(struct sctp_ulpq *ulpq)
|
||||
{
|
||||
struct sk_buff *pos, *tmp;
|
||||
struct sctp_ulpevent *cevent;
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_stream *in;
|
||||
struct sk_buff_head temp;
|
||||
__u16 csid, cssn;
|
||||
|
||||
in = &ulpq->asoc->ssnmap->in;
|
||||
|
||||
/* We are holding the chunks by stream, by SSN. */
|
||||
skb_queue_head_init(&temp);
|
||||
event = NULL;
|
||||
sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
|
||||
cevent = (struct sctp_ulpevent *) pos->cb;
|
||||
csid = cevent->stream;
|
||||
cssn = cevent->ssn;
|
||||
|
||||
if (cssn != sctp_ssn_peek(in, csid))
|
||||
break;
|
||||
|
||||
/* Found it, so mark in the ssnmap. */
|
||||
sctp_ssn_next(in, csid);
|
||||
|
||||
__skb_unlink(pos, &ulpq->lobby);
|
||||
if (!event) {
|
||||
/* Create a temporary list to collect chunks on. */
|
||||
event = sctp_skb2event(pos);
|
||||
__skb_queue_tail(&temp, sctp_event2skb(event));
|
||||
} else {
|
||||
/* Attach all gathered skbs to the event. */
|
||||
__skb_queue_tail(&temp, pos);
|
||||
}
|
||||
}
|
||||
|
||||
/* Send event to the ULP. 'event' is the sctp_ulpevent for
|
||||
* very first SKB on the 'temp' list.
|
||||
*/
|
||||
if (event)
|
||||
sctp_ulpq_tail_event(ulpq, event);
|
||||
}
|
||||
|
||||
/* Skip over an SSN. */
|
||||
void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
|
||||
{
|
||||
struct sctp_stream *in;
|
||||
|
||||
/* Note: The stream ID must be verified before this routine. */
|
||||
in = &ulpq->asoc->ssnmap->in;
|
||||
|
||||
/* Is this an old SSN? If so ignore. */
|
||||
if (SSN_lt(ssn, sctp_ssn_peek(in, sid)))
|
||||
return;
|
||||
|
||||
/* Mark that we are no longer expecting this SSN or lower. */
|
||||
sctp_ssn_skip(in, sid, ssn);
|
||||
|
||||
/* Go find any other chunks that were waiting for
|
||||
* ordering and deliver them if needed.
|
||||
*/
|
||||
sctp_ulpq_reap_ordered(ulpq);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Renege 'needed' bytes from the ordering queue. */
|
||||
static __u16 sctp_ulpq_renege_order(struct sctp_ulpq *ulpq, __u16 needed)
|
||||
{
|
||||
__u16 freed = 0;
|
||||
__u32 tsn;
|
||||
struct sk_buff *skb;
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_tsnmap *tsnmap;
|
||||
|
||||
tsnmap = &ulpq->asoc->peer.tsn_map;
|
||||
|
||||
while ((skb = __skb_dequeue_tail(&ulpq->lobby)) != NULL) {
|
||||
freed += skb_headlen(skb);
|
||||
event = sctp_skb2event(skb);
|
||||
tsn = event->tsn;
|
||||
|
||||
sctp_ulpevent_free(event);
|
||||
sctp_tsnmap_renege(tsnmap, tsn);
|
||||
if (freed >= needed)
|
||||
return freed;
|
||||
}
|
||||
|
||||
return freed;
|
||||
}
|
||||
|
||||
/* Renege 'needed' bytes from the reassembly queue. */
|
||||
static __u16 sctp_ulpq_renege_frags(struct sctp_ulpq *ulpq, __u16 needed)
|
||||
{
|
||||
__u16 freed = 0;
|
||||
__u32 tsn;
|
||||
struct sk_buff *skb;
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_tsnmap *tsnmap;
|
||||
|
||||
tsnmap = &ulpq->asoc->peer.tsn_map;
|
||||
|
||||
/* Walk backwards through the list, reneges the newest tsns. */
|
||||
while ((skb = __skb_dequeue_tail(&ulpq->reasm)) != NULL) {
|
||||
freed += skb_headlen(skb);
|
||||
event = sctp_skb2event(skb);
|
||||
tsn = event->tsn;
|
||||
|
||||
sctp_ulpevent_free(event);
|
||||
sctp_tsnmap_renege(tsnmap, tsn);
|
||||
if (freed >= needed)
|
||||
return freed;
|
||||
}
|
||||
|
||||
return freed;
|
||||
}
|
||||
|
||||
/* Partial deliver the first message as there is pressure on rwnd. */
|
||||
void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq,
|
||||
struct sctp_chunk *chunk,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *event;
|
||||
struct sctp_association *asoc;
|
||||
|
||||
asoc = ulpq->asoc;
|
||||
|
||||
/* Are we already in partial delivery mode? */
|
||||
if (!sctp_sk(asoc->base.sk)->pd_mode) {
|
||||
|
||||
/* Is partial delivery possible? */
|
||||
event = sctp_ulpq_retrieve_first(ulpq);
|
||||
/* Send event to the ULP. */
|
||||
if (event) {
|
||||
sctp_ulpq_tail_event(ulpq, event);
|
||||
sctp_sk(asoc->base.sk)->pd_mode = 1;
|
||||
ulpq->pd_mode = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Renege some packets to make room for an incoming chunk. */
|
||||
void sctp_ulpq_renege(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
|
||||
gfp_t gfp)
|
||||
{
|
||||
struct sctp_association *asoc;
|
||||
__u16 needed, freed;
|
||||
|
||||
asoc = ulpq->asoc;
|
||||
|
||||
if (chunk) {
|
||||
needed = ntohs(chunk->chunk_hdr->length);
|
||||
needed -= sizeof(sctp_data_chunk_t);
|
||||
} else
|
||||
needed = SCTP_DEFAULT_MAXWINDOW;
|
||||
|
||||
freed = 0;
|
||||
|
||||
if (skb_queue_empty(&asoc->base.sk->sk_receive_queue)) {
|
||||
freed = sctp_ulpq_renege_order(ulpq, needed);
|
||||
if (freed < needed) {
|
||||
freed += sctp_ulpq_renege_frags(ulpq, needed - freed);
|
||||
}
|
||||
}
|
||||
/* If able to free enough room, accept this chunk. */
|
||||
if (chunk && (freed >= needed)) {
|
||||
__u32 tsn;
|
||||
tsn = ntohl(chunk->subh.data_hdr->tsn);
|
||||
sctp_tsnmap_mark(&asoc->peer.tsn_map, tsn);
|
||||
sctp_ulpq_tail_data(ulpq, chunk, gfp);
|
||||
|
||||
sctp_ulpq_partial_delivery(ulpq, chunk, gfp);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Notify the application if an association is aborted and in
|
||||
* partial delivery mode. Send up any pending received messages.
|
||||
*/
|
||||
void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
|
||||
{
|
||||
struct sctp_ulpevent *ev = NULL;
|
||||
struct sock *sk;
|
||||
|
||||
if (!ulpq->pd_mode)
|
||||
return;
|
||||
|
||||
sk = ulpq->asoc->base.sk;
|
||||
if (sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT,
|
||||
&sctp_sk(sk)->subscribe))
|
||||
ev = sctp_ulpevent_make_pdapi(ulpq->asoc,
|
||||
SCTP_PARTIAL_DELIVERY_ABORTED,
|
||||
gfp);
|
||||
if (ev)
|
||||
__skb_queue_tail(&sk->sk_receive_queue, sctp_event2skb(ev));
|
||||
|
||||
/* If there is data waiting, send it up the socket now. */
|
||||
if (sctp_ulpq_clear_pd(ulpq) || ev)
|
||||
sk->sk_data_ready(sk, 0);
|
||||
}
|
||||
Reference in New Issue
Block a user