Commit 4f9889dc authored by David S. Miller's avatar David S. Miller

Merge nuts.ninka.net:/home/davem/src/BK/BAK-sctp-2.5

into nuts.ninka.net:/home/davem/src/BK/sctp-2.5
parents 5e051f29 2d00927b
Linux Kernel SCTP
This is the current BETA release of the Linux Kernel SCTP reference
implementation.
SCTP (Stream Control Transmission Protocol) is a IP based, message oriented,
reliable transport protocol, with congestion control, support for
transparent multi-homing, and multiple ordered streams of messages.
RFC2960 defines the core protocol. The IETF SIGTRAN working group originally
developed the SCTP protocol and later handed the protocol over to the
Transport Area (TSVWG) working group for the continued evolvement of SCTP as a
general purpose transport.
See the IETF website (http://www.ietf.org) for further documents on SCTP.
See http://www.ietf.org/rfc/rfc2960.txt
The initial project goal is to create an Linux kernel reference implementation
of SCTP that is RFC 2960 compliant and provides an programming interface
referred to as the UDP-style API of the Sockets Extensions for SCTP, as
proposed in IETF Internet-Drafts.
Caveats:
-lksctp can be built as statically or as a module. However, be aware that
module removal of lksctp is not yet a safe activity.
-There is tentative support for IPv6, but most work has gone towards
implementation and testing lksctp on IPv4.
For more information, please visit the lksctp project website:
http://www.sf.net/projects/lksctp
Or contact the lksctp developers through the mailing list:
<lksctp-developers@lists.sourceforge.net>
......@@ -37,11 +37,12 @@ enum {
IPPROTO_IPV6 = 41, /* IPv6-in-IPv4 tunnelling */
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_ESP = 50, /* Encapsulation Security Payload protocol */
IPPROTO_AH = 51, /* Authentication Header protocol */
IPPROTO_PIM = 103, /* Protocol Independent Multicast */
IPPROTO_COMP = 108, /* Compression Header protocol */
IPPROTO_SCTP = 132, /* Stream Control Transport Protocol */
IPPROTO_RAW = 255, /* Raw IP packets */
IPPROTO_MAX
......
......@@ -137,6 +137,7 @@ extern int sock_sendmsg(struct socket *, struct msghdr *m, int len);
extern int sock_recvmsg(struct socket *, struct msghdr *m, int len, int flags);
extern int sock_readv_writev(int type, struct inode * inode, struct file * file,
const struct iovec * iov, long count, long size);
extern int sock_map_fd(struct socket *sock);
extern int net_ratelimit(void);
extern unsigned long net_random(void);
......
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/linux/sctp.h,v 1.7 2002/07/17 16:13:50 jgrimm Exp $
*
* Various protocol defined structures.
*
* 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 <sctp-developers-list@cig.mot.com>
*
* 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>
* randall@sctp.chicago.il.us
* kmorneau@cisco.com
* qxie1@email.mot.com
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
#ifndef __LINUX_SCTP_H__
#define __LINUX_SCTP_H__
#include <linux/in.h> /* We need in_addr. */
#include <linux/in6.h> /* We need in6_addr. */
/* Section 3.1. SCTP Common Header Format */
typedef struct sctphdr {
__u16 source;
__u16 dest;
__u32 vtag;
__u32 checksum;
} sctp_sctphdr_t __attribute__((packed));
/* Section 3.2. Chunk Field Descriptions. */
typedef struct sctp_chunkhdr {
__u8 type;
__u8 flags;
__u16 length;
} sctp_chunkhdr_t __attribute__((packed));
/* Section 3.2. Chunk Type Values.
* [Chunk Type] identifies the type of information contained in the Chunk
* Value field. It takes a value from 0 to 254. The value of 255 is
* reserved for future use as an extension field.
*/
typedef enum {
SCTP_CID_DATA = 0,
SCTP_CID_INIT = 1,
SCTP_CID_INIT_ACK = 2,
SCTP_CID_SACK = 3,
SCTP_CID_HEARTBEAT = 4,
SCTP_CID_HEARTBEAT_ACK = 5,
SCTP_CID_ABORT = 6,
SCTP_CID_SHUTDOWN = 7,
SCTP_CID_SHUTDOWN_ACK = 8,
SCTP_CID_ERROR = 9,
SCTP_CID_COOKIE_ECHO = 10,
SCTP_CID_COOKIE_ACK = 11,
SCTP_CID_ECN_ECNE = 12,
SCTP_CID_ECN_CWR = 13,
SCTP_CID_SHUTDOWN_COMPLETE = 14,
/* Use hex, as defined in ADDIP sec. 3.1 */
SCTP_CID_ASCONF = 0xC1,
SCTP_CID_ASCONF_ACK = 0x80,
} sctp_cid_t; /* enum */
/* Section 3.2
* Chunk Types are encoded such that the highest-order two bits specify
* the action that must be taken if the processing endpoint does not
* recognize the Chunk Type.
*/
typedef enum {
SCTP_CID_ACTION_DISCARD = 0x00,
SCTP_CID_ACTION_DISCARD_ERR = 0x40,
SCTP_CID_ACTION_SKIP = 0x80,
SCTP_CID_ACTION_SKIP_ERR = 0xc0,
} sctp_cid_action_t;
enum { SCTP_CID_ACTION_MASK = 0xc0, };
/* This flag is used in Chunk Flags for ABORT and SHUTDOWN COMPLETE.
*
* 3.3.7 Abort Association (ABORT) (6):
* The T bit is set to 0 if the sender had a TCB that it destroyed.
* If the sender did not have a TCB it should set this bit to 1.
*/
enum { SCTP_CHUNK_FLAG_T = 0x01 };
/*
* Set the T bit
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 14 |Reserved |T| Length = 4 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Chunk Flags: 8 bits
*
* Reserved: 7 bits
* Set to 0 on transmit and ignored on receipt.
*
* T bit: 1 bit
* The T bit is set to 0 if the sender had a TCB that it destroyed. If
* the sender did NOT have a TCB it should set this bit to 1.
*
* Note: Special rules apply to this chunk for verification, please
* see Section 8.5.1 for details.
*/
#define sctp_test_T_bit(c) ((c)->chunk_hdr->flags & SCTP_CHUNK_FLAG_T)
/* RFC 2960
* Section 3.2.1 Optional/Variable-length Parmaeter Format.
*/
typedef struct sctp_paramhdr {
__u16 type;
__u16 length;
} sctp_paramhdr_t __attribute((packed));
typedef enum {
/* RFC 2960 Section 3.3.5 */
SCTP_PARAM_HEATBEAT_INFO = __constant_htons(1),
/* RFC 2960 Section 3.3.2.1 */
SCTP_PARAM_IPV4_ADDRESS = __constant_htons(5),
SCTP_PARAM_IPV6_ADDRESS = __constant_htons(6),
SCTP_PARAM_STATE_COOKIE = __constant_htons(7),
SCTP_PARAM_UNRECOGNIZED_PARAMETERS = __constant_htons(8),
SCTP_PARAM_COOKIE_PRESERVATIVE = __constant_htons(9),
SCTP_PARAM_HOST_NAME_ADDRESS = __constant_htons(11),
SCTP_PARAM_SUPPORTED_ADDRESS_TYPES = __constant_htons(12),
SCTP_PARAM_ECN_CAPABLE = __constant_htons(0x8000),
/* Add-IP Extension. Section 3.2 */
SCTP_PARAM_ADD_IP = __constant_htons(0xc001),
SCTP_PARAM_DEL_IP = __constant_htons(0xc002),
SCTP_PARAM_ERR_CAUSE = __constant_htons(0xc003),
SCTP_PARAM_SET_PRIMARY = __constant_htons(0xc004),
SCTP_PARAM_SUCCESS_REPORT = __constant_htons(0xc005),
SCTP_PARAM_ADAPTION_LAYER_IND = __constant_htons(0xc006),
} sctp_param_t; /* enum */
/* RFC 2960 Section 3.2.1
* The Parameter Types are encoded such that the highest-order two bits
* specify the action that must be taken if the processing endpoint does
* not recognize the Parameter Type.
*
*/
typedef enum {
SCTP_PARAM_ACTION_DISCARD = __constant_htons(0x0000),
SCTP_PARAM_ACTION_DISCARD_ERR = __constant_htons(0x4000),
SCTP_PARAM_ACTION_SKIP = __constant_htons(0x8000),
SCTP_PARAM_ACTION_SKIP_ERR = __constant_htons(0xc000),
} sctp_param_action_t;
/* RFC 2960 Section 3.3.1 Payload Data (DATA) (0) */
typedef struct sctp_datahdr {
__u32 tsn;
__u16 stream;
__u16 ssn;
__u32 ppid;
__u8 payload[0];
} sctp_datahdr_t __attribute__((packed));
typedef struct sctp_data_chunk {
sctp_chunkhdr_t chunk_hdr;
sctp_datahdr_t data_hdr;
} sctp_data_chunk_t __attribute__((packed));
/* DATA Chuck Specific Flags */
enum {
SCTP_DATA_MIDDLE_FRAG = 0x00,
SCTP_DATA_LAST_FRAG = 0x01,
SCTP_DATA_FIRST_FRAG = 0x02,
SCTP_DATA_NOT_FRAG = 0x03,
SCTP_DATA_UNORDERED = 0x04,
};
enum { SCTP_DATA_FRAG_MASK = 0x03, };
/* RFC 2960 Section 3.3.2 Initiation (INIT) (1)
*
* This chunk is used to initiate a SCTP association between two
* endpoints.
*/
typedef struct sctp_inithdr {
__u32 init_tag;
__u32 a_rwnd;
__u16 num_outbound_streams;
__u16 num_inbound_streams;
__u32 initial_tsn;
__u8 params[0];
} sctp_inithdr_t __attribute__((packed));
typedef struct sctp_init_chunk {
sctp_chunkhdr_t chunk_hdr;
sctp_inithdr_t init_hdr;
} sctp_init_chunk_t __attribute__((packed));
/* Section 3.3.2.1. IPv4 Address Parameter (5) */
typedef struct sctp_ipv4addr_param {
sctp_paramhdr_t param_hdr;
struct in_addr addr;
} sctp_ipv4addr_param_t __attribute__((packed));
/* Section 3.3.2.1. IPv6 Address Parameter (6) */
typedef struct sctp_ipv6addr_param {
sctp_paramhdr_t param_hdr;
struct in6_addr addr;
} sctp_ipv6addr_param_t __attribute__((packed));
/* Section 3.3.2.1 Cookie Preservative (9) */
typedef struct sctp_cookie_preserve_param {
sctp_paramhdr_t param_hdr;
uint32_t lifespan_increment;
} sctp_cookie_preserve_param_t __attribute__((packed));
/* Section 3.3.2.1 Host Name Address (11) */
typedef struct sctp_hostname_param {
sctp_paramhdr_t param_hdr;
uint8_t hostname[0];
} sctp_hostname_param_t __attribute__((packed));
/* Section 3.3.2.1 Supported Address Types (12) */
typedef struct sctp_supported_addrs_param {
sctp_paramhdr_t param_hdr;
uint16_t types[0];
} sctp_supported_addrs_param_t __attribute__((packed));
/* Appendix A. ECN Capable (32768) */
typedef struct sctp_ecn_capable_param {
sctp_paramhdr_t param_hdr;
} sctp_ecn_capable_param_t __attribute__((packed));
/* RFC 2960. Section 3.3.3 Initiation Acknowledgement (INIT ACK) (2):
* The INIT ACK chunk is used to acknowledge the initiation of an SCTP
* association.
*/
typedef sctp_init_chunk_t sctp_initack_chunk_t;
/* Section 3.3.3.1 State Cookie (7) */
typedef struct sctp_cookie_param {
sctp_paramhdr_t p;
__u8 body[0];
} sctp_cookie_param_t __attribute__((packed));
/* Section 3.3.3.1 Unrecognized Parameters (8) */
typedef struct sctp_unrecognized_param {
sctp_paramhdr_t param_hdr;
sctp_paramhdr_t unrecognized;
} sctp_unrecognized_param_t __attribute__((packed));
/*
* 3.3.4 Selective Acknowledgement (SACK) (3):
*
* This chunk is sent to the peer endpoint to acknowledge received DATA
* chunks and to inform the peer endpoint of gaps in the received
* subsequences of DATA chunks as represented by their TSNs.
*/
typedef struct sctp_gap_ack_block {
__u16 start;
__u16 end;
} sctp_gap_ack_block_t __attribute__((packed));
typedef uint32_t sctp_dup_tsn_t;
typedef union {
sctp_gap_ack_block_t gab;
sctp_dup_tsn_t dup;
} sctp_sack_variable_t;
typedef struct sctp_sackhdr {
__u32 cum_tsn_ack;
__u32 a_rwnd;
__u16 num_gap_ack_blocks;
__u16 num_dup_tsns;
sctp_sack_variable_t variable[0];
} sctp_sackhdr_t __attribute__((packed));
typedef struct sctp_sack_chunk {
sctp_chunkhdr_t chunk_hdr;
sctp_sackhdr_t sack_hdr;
} sctp_sack_chunk_t __attribute__((packed));
/* RFC 2960. Section 3.3.5 Heartbeat Request (HEARTBEAT) (4):
*
* An endpoint should send this chunk to its peer endpoint to probe the
* reachability of a particular destination transport address defined in
* the present association.
*/
typedef struct sctp_heartbeathdr {
sctp_paramhdr_t info;
} sctp_heartbeathdr_t __attribute__((packed));
typedef struct sctp_heartbeat_chunk {
sctp_chunkhdr_t chunk_hdr;
sctp_heartbeathdr_t hb_hdr;
} sctp_heartbeat_chunk_t __attribute__((packed));
/* For the abort and shutdown ACK we must carry the init tag in the
* common header. Just the common header is all that is needed with a
* chunk descriptor.
*/
typedef struct sctp_abort_chunk {
sctp_chunkhdr_t uh;
} sctp_abort_chunkt_t __attribute__((packed));
/* For the graceful shutdown we must carry the tag (in common header)
* and the highest consecutive acking value.
*/
typedef struct sctp_shutdownhdr {
__u32 cum_tsn_ack;
} sctp_shutdownhdr_t __attribute__((packed));
struct sctp_shutdown_chunk_t {
sctp_chunkhdr_t chunk_hdr;
sctp_shutdownhdr_t shutdown_hdr;
} __attribute__((packed));
/* RFC 2960. Section 3.3.10 Operation Error (ERROR) (9) */
typedef struct sctp_errhdr {
__u16 cause;
__u16 length;
__u8 variable[0];
} sctp_errhdr_t __attribute__((packed));
typedef struct sctp_operr_chunk {
sctp_chunkhdr_t chunk_hdr;
sctp_errhdr_t err_hdr;
} sctp_operr_chunk_t __attribute__((packed));
/* RFC 2960 3.3.10 - Operation Error
*
* Cause Code: 16 bits (unsigned integer)
*
* Defines the type of error conditions being reported.
* Cause Code
* Value Cause Code
* --------- ----------------
* 1 Invalid Stream Identifier
* 2 Missing Mandatory Parameter
* 3 Stale Cookie Error
* 4 Out of Resource
* 5 Unresolvable Address
* 6 Unrecognized Chunk Type
* 7 Invalid Mandatory Parameter
* 8 Unrecognized Parameters
* 9 No User Data
* 10 Cookie Received While Shutting Down
*/
typedef enum {
SCTP_ERROR_NO_ERROR = __constant_htons(0x00),
SCTP_ERROR_INV_STRM = __constant_htons(0x01),
SCTP_ERROR_MISS_PARAM = __constant_htons(0x02),
SCTP_ERROR_STALE_COOKIE = __constant_htons(0x03),
SCTP_ERROR_NO_RESOURCE = __constant_htons(0x04),
SCTP_ERROR_DNS_FAILED = __constant_htons(0x05),
SCTP_ERROR_UNKNOWN_CHUNK = __constant_htons(0x06),
SCTP_ERROR_INV_PARAM = __constant_htons(0x07),
SCTP_ERROR_UNKNOWN_PARAM = __constant_htons(0x08),
SCTP_ERROR_NO_DATA = __constant_htons(0x09),
SCTP_ERROR_COOKIE_IN_SHUTDOWN = __constant_htons(0x0a),
/* SCTP Implementation Guide:
* 11 Restart of an association with new addresses
* 12 User Initiated Abort
*/
SCTP_ERROR_RESTART = __constant_htons(0x0b),
SCTP_ERROR_USER_ABORT = __constant_htons(0x0c),
/* ADDIP Section 3.3 New Error Causes
*
* Four new Error Causes are added to the SCTP Operational Errors,
* primarily for use in the ASCONF-ACK chunk.
*
* Value Cause Code
* --------- ----------------
* 0x0100 Request to Delete Last Remaining IP Address.
* 0x0101 Operation Refused Due to Resource Shortage.
* 0x0102 Request to Delete Source IP Address.
* 0x0103 Association Aborted due to illegal ASCONF-ACK
*/
SCTP_ERROR_DEL_LAST_IP = __constant_htons(0x0100),
SCTP_ERROR_RSRC_LOW = __constant_htons(0x0101),
SCTP_ERROR_DEL_SRC_IP = __constant_htons(0x0102),
SCTP_ERROR_ASCONF_ACK = __constant_htons(0x0103),
} sctp_error_t;
/* RFC 2960. Appendix A. Explicit Congestion Notification.
* Explicit Congestion Notification Echo (ECNE) (12)
*/
typedef struct sctp_ecnehdr {
__u32 lowest_tsn;
} sctp_ecnehdr_t;
typedef struct sctp_ecne_chunk {
sctp_chunkhdr_t chunk_hdr;
sctp_ecnehdr_t ence_hdr;
} sctp_ecne_chunk_t __attribute__((packed));
/* RFC 2960. Appendix A. Explicit Congestion Notification.
* Congestion Window Reduced (CWR) (13)
*/
typedef struct sctp_cwrhdr {
__u32 lowest_tsn;
} sctp_cwrhdr_t;
typedef struct sctp_cwr_chunk {
sctp_chunkhdr_t chunk_hdr;
sctp_cwrhdr_t cwr_hdr;
} sctp_cwr_chunk_t __attribute__((packed));
/* FIXME: Cleanup needs to continue below this line. */
/*
* ADDIP Section 3.1 New Chunk Types
*/
/* ADDIP Section 3.1.1
*
* ASCONF-Request Correlation ID: 32 bits (unsigned integer)
*
* This is an opaque integer assigned by the sender to identify each
* request parameter. It is in host byte order and is only meaningful
* to the sender. The receiver of the ASCONF Chunk will copy this 32
* bit value into the ASCONF Correlation ID field of the
* ASCONF-ACK. The sender of the ASCONF can use this same value in the
* ASCONF-ACK to find which request the response is for.
*
* ASCONF Parameter: TLV format
*
* Each Address configuration change is represented by a TLV parameter
* as defined in Section 3.2. One or more requests may be present in
* an ASCONF Chunk.
*/
typedef struct {
__u32 correlation;
sctp_paramhdr_t p;
__u8 payload[0];
} sctpAsconfReq_t;
/* ADDIP
* 3.1.1 Address/Stream 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
* Tag-Length-Value (TLV), as described in "3.2.1
* Optional/Variable-length Parameter Format" in [RFC2960], for all
* variable parameters.
*/
typedef struct {
__u32 serial;
__u8 reserved[3];
__u8 addr_type;
__u32 addr[4];
sctpAsconfReq_t requests[0];
} sctpAsconf_t;
/* ADDIP
* 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK)
*
* ASCONF-Request Correlation ID: 32 bits (unsigned integer)
*
* This value is copied from the ASCONF Correlation ID received in the
* ASCONF Chunk. It is used by the receiver of the ASCONF-ACK to identify
* which ASCONF parameter this response is associated with.
*
* ASCONF Parameter Response : TLV format
*
* The ASCONF Parameter Response is used in the ASCONF-ACK to report
* status of ASCONF processing. By default, if a responding endpoint
* does not include any Error Cause, a success is indicated. Thus a
* sender of an ASCONF-ACK MAY indicate complete success of all TLVs in
* an ASCONF by returning only the Chunk Type, Chunk Flags, Chunk Length
* (set to 8) and the Serial Number.
*/
typedef union {
struct {
__u32 correlation;
sctp_paramhdr_t header; /* success report */
} success;
struct {
__u32 correlation;
sctp_paramhdr_t header; /* error cause indication */
sctp_paramhdr_t errcause;
uint8_t request[0]; /* original request from ASCONF */
} error;
#define __correlation success.correlation
#define __header success.header
#define __cause error.errcause
#define __request error.request
} sctpAsconfAckRsp_t;
/* ADDIP
* 3.1.2 Address/Stream Configuration Acknowledgment Chunk (ASCONF-ACK)
*
* This chunk is used by the receiver of an ASCONF Chunk to
* acknowledge the reception. It carries zero or more results for any
* ASCONF Parameters that were processed by the receiver.
*/
typedef struct {
__u32 serial;
sctpAsconfAckRsp_t responses[0];
} sctpAsconfAck_t;
/*********************************************************************
* Internal structures
*
* These are data structures which never go out on the wire.
*********************************************************************/
/* What is this data structure for? The TLV isn't one--it is just a
* value. Perhaps this data structure ought to have a type--otherwise
* it is not unambigiously parseable. --piggy
*/
typedef struct {
struct list_head hook;
int length; /* length of the TLV */
/* the actually TLV to be copied into ASCONF_ACK */
sctpAsconfAckRsp_t TLV;
} sctpAsconfAckRspNode_t;
#endif /* __LINUX_SCTP_H__ */
......@@ -227,6 +227,7 @@ struct ucred {
#define SOL_UDP 17
#define SOL_IPV6 41
#define SOL_ICMPV6 58
#define SOL_SCTP 132
#define SOL_RAW 255
#define SOL_IPX 256
#define SOL_AX25 257
......
......@@ -172,6 +172,7 @@ enum
NET_TR=14,
NET_DECNET=15,
NET_ECONET=16,
NET_SCTP=17,
};
/* /proc/sys/kernel/random */
......@@ -516,6 +517,21 @@ enum {
NET_DECNET_CONF_DEV_STATE = 7
};
/* /proc/sys/net/sctp */
enum {
NET_SCTP_RTO_INITIAL = 1,
NET_SCTP_RTO_MIN = 2,
NET_SCTP_RTO_MAX = 3,
NET_SCTP_RTO_ALPHA = 4,
NET_SCTP_RTO_BETA = 5,
NET_SCTP_VALID_COOKIE_LIFE = 6,
NET_SCTP_ASSOCIATION_MAX_RETRANS = 7,
NET_SCTP_PATH_MAX_RETRANS = 8,
NET_SCTP_MAX_INIT_RETRANSMITS = 9,
NET_SCTP_HB_INTERVAL = 10,
NET_SCTP_MAX_BURST = 11,
};
/* CTL_PROC names: */
/* CTL_FS names: */
......
......@@ -43,6 +43,14 @@ extern void inet_sock_release(struct sock *sk);
extern void inet_sock_destruct(struct sock *sk);
extern atomic_t inet_sock_nr;
extern int inet_bind(struct socket *sock,
struct sockaddr *uaddr, int addr_len);
extern int inet_getname(struct socket *sock,
struct sockaddr *uaddr,
int *uaddr_len, int peer);
extern int inet_ioctl(struct socket *sock,
unsigned int cmd, unsigned long arg);
#endif
......@@ -4,7 +4,7 @@
* Authors:
* Pedro Roque <roque@di.fc.ul.pt>
*
* $Id: ipv6.h,v 1.23 2000/12/13 18:31:48 davem Exp $
* $Id: ipv6.h,v 1.1 2002/05/20 15:13:07 jgrimm Exp $
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -336,6 +336,14 @@ extern void ipv6_icmp_error(struct sock *sk, struct sk_buff *skb, int err, u16
u32 info, u8 *payload);
extern void ipv6_local_error(struct sock *sk, int err, struct flowi *fl, u32 info);
extern int inet6_release(struct socket *sock);
extern int inet6_bind(struct socket *sock, struct sockaddr *uaddr,
int addr_len);
extern int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer);
extern int inet6_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg);
#endif /* __KERNEL__ */
#endif /* _NET_IPV6_H */
......
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Id: sctp.h,v 1.40 2002/08/21 18:34:03 jgrimm Exp $
*
* The base lksctp header.
*
* 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>
* Xingang Guo <xingang.guo@intel.com>
* 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.
*/
#ifndef __net_sctp_h__
#define __net_sctp_h__
/* Header Strategy.
* Start getting some control over the header file depencies:
* includes
* constants
* structs
* prototypes
* macros, externs, and inlines
*
* Move test_frame specific items out of the kernel headers
* and into the test frame headers. This is not perfect in any sense
* and will continue to evolve.
*/
#include <linux/config.h>
#ifdef TEST_FRAME
#undef CONFIG_PROC_FS
#undef CONFIG_SCTP_DBG_OBJCNT
#undef CONFIG_SYSCTL
#endif /* TEST_FRAME */
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/in.h>
#include <linux/tty.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/jiffies.h>
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
#include <net/ipv6.h>
#include <net/ip6_route.h>
#endif
#include <asm/uaccess.h>
#include <asm/page.h>
#include <net/sock.h>
#include <net/sctp/sctp_structs.h>
#include <net/sctp/sctp_constants.h>
#include <net/sctp/sctp_sm.h>
/* Set SCTP_DEBUG flag via config if not already set. */
#ifndef SCTP_DEBUG
#ifdef CONFIG_SCTP_DBG_MSG
#define SCTP_DEBUG 1
#else
#define SCTP_DEBUG 0
#endif /* CONFIG_SCTP_DBG */
#endif /* SCTP_DEBUG */
#ifdef CONFIG_IP_SCTP_MODULE
#define SCTP_PROTOSW_FLAG 0
#else /* static! */
#define SCTP_PROTOSW_FLAG INET_PROTOSW_PERMANENT
#endif
/*
* Function declarations.
*/
/*
* sctp_protocol.c
*/
extern sctp_protocol_t sctp_proto;
extern struct sock *sctp_get_ctl_sock(void);
extern int sctp_copy_local_addr_list(sctp_protocol_t *, sctp_bind_addr_t *,
sctp_scope_t, int priority, int flags);
/*
* sctp_socket.c
*/
extern int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb);
extern int sctp_inet_listen(struct socket *sock, int backlog);
extern void sctp_write_space(struct sock *sk);
extern unsigned int sctp_poll(struct file *file, struct socket *sock,
poll_table *wait);
/*
* sctp_primitive.c
*/
extern int sctp_primitive_ASSOCIATE(sctp_association_t *, void *arg);
extern int sctp_primitive_SHUTDOWN(sctp_association_t *, void *arg);
extern int sctp_primitive_ABORT(sctp_association_t *, void *arg);
extern int sctp_primitive_SEND(sctp_association_t *, void *arg);
/*
* sctp_crc32c.c
*/
extern __u32 count_crc(__u8 *ptr, __u16 count);
/*
* sctp_input.c
*/
extern int sctp_rcv(struct sk_buff *skb);
extern void sctp_v4_err(struct sk_buff *skb, u32 info);
extern void sctp_hash_established(sctp_association_t *);
extern void __sctp_hash_established(sctp_association_t *);
extern void sctp_unhash_established(sctp_association_t *);
extern void __sctp_unhash_established(sctp_association_t *);
extern void sctp_hash_endpoint(sctp_endpoint_t *);
extern void __sctp_hash_endpoint(sctp_endpoint_t *);
extern void sctp_unhash_endpoint(sctp_endpoint_t *);
extern void __sctp_unhash_endpoint(sctp_endpoint_t *);
/*
* sctp_hashdriver.c
*/
extern void sctp_hash_digest(const char *secret, const int secret_len,
const char *text, const int text_len,
__u8 *digest);
/*
* Section: Macros, externs, and inlines
*/
#ifdef TEST_FRAME
#include <test_frame.h>
#else
/* spin lock wrappers. */
#define sctp_spin_lock_irqsave(lock, flags) spin_lock_irqsave(lock, flags)
#define sctp_spin_unlock_irqrestore(lock, flags) \
spin_unlock_irqrestore(lock, flags)
#define sctp_local_bh_disable() local_bh_disable()
#define sctp_local_bh_enable() local_bh_enable()
#define sctp_spin_lock(lock) spin_lock(lock)
#define sctp_spin_unlock(lock) spin_unlock(lock)
#define sctp_write_lock(lock) write_lock(lock)
#define sctp_write_unlock(lock) write_unlock(lock)
#define sctp_read_lock(lock) read_lock(lock)
#define sctp_read_unlock(lock) read_unlock(lock)
/* sock lock wrappers. */
#define sctp_lock_sock(sk) lock_sock(sk)
#define sctp_release_sock(sk) release_sock(sk)
#define sctp_bh_lock_sock(sk) bh_lock_sock(sk)
#define sctp_bh_unlock_sock(sk) bh_unlock_sock(sk)
#define __sctp_sock_busy(sk) ((sk)->lock.users)
#define SCTP_SOCK_SLEEP_PRE(sk) SOCK_SLEEP_PRE(sk)
#define SCTP_SOCK_SLEEP_POST(sk) SOCK_SLEEP_POST(sk)
/* Determine if this is a valid kernel address. */
static inline int sctp_is_valid_kaddr(unsigned long addr)
{
struct page *page;
/* Make sure the address is not in the user address space. */
if (addr < PAGE_OFFSET)
return 0;
page = virt_to_page(addr);
/* Is this page valid? */
if (!virt_addr_valid(addr) || PageReserved(page))
return 0;
return 1;
}
#endif /* !TEST_FRAME */
/* Print debugging messages. */
#if SCTP_DEBUG
extern int sctp_debug_flag;
#define SCTP_DEBUG_PRINTK(whatever...) \
((void) (sctp_debug_flag && printk(KERN_DEBUG whatever)))
#define SCTP_ENABLE_DEBUG { sctp_debug_flag = 1; }
#define SCTP_DISABLE_DEBUG { sctp_debug_flag = 0; }
#define SCTP_ASSERT(expr, str, func) \
if (!(expr)) { \
SCTP_DEBUG_PRINTK("Assertion Failed: %s(%s) at %s:%s:%d\n", \
str, (#expr), __FILE__, __FUNCTION__, __LINE__); \
func; \
}
#else /* SCTP_DEBUG */
#define SCTP_DEBUG_PRINTK(whatever...)
#define SCTP_ENABLE_DEBUG
#define SCTP_DISABLE_DEBUG
#define SCTP_ASSERT(expr, str, func)
#endif /* SCTP_DEBUG */
/*
* Macros for keeping a global reference of object allocations.
*/
#ifdef CONFIG_SCTP_DBG_OBJCNT
extern atomic_t sctp_dbg_objcnt_sock;
extern atomic_t sctp_dbg_objcnt_ep;
extern atomic_t sctp_dbg_objcnt_assoc;
extern atomic_t sctp_dbg_objcnt_transport;
extern atomic_t sctp_dbg_objcnt_chunk;
extern atomic_t sctp_dbg_objcnt_bind_addr;
extern atomic_t sctp_dbg_objcnt_addr;
/* Macros to atomically increment/decrement objcnt counters. */
#define SCTP_DBG_OBJCNT_INC(name) \
atomic_inc(&sctp_dbg_objcnt_## name)
#define SCTP_DBG_OBJCNT_DEC(name) \
atomic_dec(&sctp_dbg_objcnt_## name)
#define SCTP_DBG_OBJCNT(name) \
atomic_t sctp_dbg_objcnt_## name = ATOMIC_INIT(0)
/* Macro to help create new entries in in the global array of
* objcnt counters.
*/
#define SCTP_DBG_OBJCNT_ENTRY(name) \
{.label= #name, .counter= &sctp_dbg_objcnt_## name}
extern void sctp_dbg_objcnt_init(void);
extern void sctp_dbg_objcnt_exit(void);
#else
#define SCTP_DBG_OBJCNT_INC(name)
#define SCTP_DBG_OBJCNT_DEC(name)
static inline void sctp_dbg_objcnt_init(void) { return; }
static inline void sctp_dbg_objcnt_exit(void) { return; }
#endif /* CONFIG_SCTP_DBG_OBJCOUNT */
#if defined CONFIG_SYSCTL
extern void sctp_sysctl_register(void);
extern void sctp_sysctl_unregister(void);
#else
static inline void sctp_sysctl_register(void) { return; }
static inline void sctp_sysctl_unregister(void) { return; }
#endif
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
extern int sctp_v6_init(void);
extern void sctp_v6_exit(void);
static inline int sctp_ipv6_addr_type(const struct in6_addr *addr)
{
return ipv6_addr_type((struct in6_addr*) addr);
}
#define SCTP_SAT_LEN (sizeof(sctp_paramhdr_t) + 2 * sizeof(__u16))
/* Note: These V6 macros are obsolescent. */
/* Use this macro to enclose code fragments which are V6-dependent. */
#define SCTP_V6(m...) m
#define SCTP_V6_SUPPORT 1
#else /* #ifdef defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
#define sctp_ipv6_addr_type(a) 0
#define SCTP_SAT_LEN (sizeof(sctp_paramhdr_t) + 1 * sizeof(__u16))
#define SCTP_V6(m...) /* Do nothing. */
#undef SCTP_V6_SUPPORT
static inline int sctp_v6_init(void) { return 0; }
static inline void sctp_v6_exit(void) { return; }
#endif /* #ifdef defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */
/* Map an association to an assoc_id. */
static inline sctp_assoc_t sctp_assoc2id(const sctp_association_t *asoc)
{
return (sctp_assoc_t) asoc;
}
/* Look up the association by its id. */
static inline sctp_association_t *sctp_id2assoc(const struct sock *sk, sctp_assoc_t id)
{
sctp_association_t *asoc = NULL;
/* First, verify that this is a kernel address. */
if (sctp_is_valid_kaddr((unsigned long) id)) {
sctp_association_t *temp = (sctp_association_t *) id;
/* Verify that this _is_ an sctp_association_t
* data structure and if so, that the socket matches.
*/
if ((SCTP_ASSOC_EYECATCHER == temp->eyecatcher) &&
(temp->base.sk == sk))
asoc = temp;
}
return asoc;
}
/* A macro to walk a list of skbs. */
#define sctp_skb_for_each(pos, head, tmp) \
for (pos = (head)->next;\
tmp = (pos)->next, pos != ((struct sk_buff *)(head));\
pos = tmp)
/* A helper to append an entire skb list (list) to another (head). */
static inline void sctp_skb_list_tail(struct sk_buff_head *list,
struct sk_buff_head *head)
{
int flags __attribute__ ((unused));
sctp_spin_lock_irqsave(&head->lock, flags);
sctp_spin_lock(&list->lock);
list_splice((struct list_head *)list, (struct list_head *)head->prev);
head->qlen += list->qlen;
list->qlen = 0;
sctp_spin_unlock(&list->lock);
sctp_spin_unlock_irqrestore(&head->lock, flags);
}
/**
* sctp_list_dequeue - remove from the head of the queue
* @list: list to dequeue from
*
* Remove the head of the list. The head item is
* returned or %NULL if the list is empty.
*/
static inline struct list_head *sctp_list_dequeue(struct list_head *list)
{
struct list_head *result = NULL;
if (list->next != list) {
result = list->next;
list->next = result->next;
list->next->prev = list;
INIT_LIST_HEAD(result);
}
return result;
}
/* Calculate the size (in bytes) occupied by the data of an iovec. */
static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
{
size_t retval = 0;
for (; iovlen > 0; --iovlen) {
retval += iov->iov_len;
iov++;
}
return retval;
}
/* Round an int up to the next multiple of 4. */
#define WORD_ROUND(s) (((s)+3)&~3)
/* Make a new instance of type. */
#define t_new(type, flags) (type *)kmalloc(sizeof(type), flags)
/* Compare two timevals. */
#define tv_lt(s, t) \
(s.tv_sec < t.tv_sec || (s.tv_sec == t.tv_sec && s.tv_usec < t.tv_usec))
/* Stolen from net/profile.h. Using it from there is more grief than
* it is worth.
*/
static inline void tv_add(const struct timeval *entered, struct timeval *leaved)
{
time_t usecs = leaved->tv_usec + entered->tv_usec;
time_t secs = leaved->tv_sec + entered->tv_sec;
if (usecs >= 1000000) {
usecs -= 1000000;
secs++;
}
leaved->tv_sec = secs;
leaved->tv_usec = usecs;
}
/* External references. */
extern struct proto sctp_prot;
extern struct proc_dir_entry *proc_net_sctp;
extern void sctp_put_port(struct sock *sk);
/* Static inline functions. */
/* Return the SCTP protocol structure. */
static inline sctp_protocol_t *sctp_get_protocol(void)
{
return &sctp_proto;
}
/* Warning: The following hash functions assume a power of two 'size'. */
/* This is the hash function for the SCTP port hash table. */
static inline int sctp_phashfn(__u16 lport)
{
sctp_protocol_t *sctp_proto = sctp_get_protocol();
return (lport & (sctp_proto->port_hashsize - 1));
}
/* This is the hash function for the endpoint hash table. */
static inline int sctp_ep_hashfn(__u16 lport)
{
sctp_protocol_t *sctp_proto = sctp_get_protocol();
return (lport & (sctp_proto->ep_hashsize - 1));
}
/* This is the hash function for the association hash table. */
static inline int sctp_assoc_hashfn(__u16 lport, __u16 rport)
{
sctp_protocol_t *sctp_proto = sctp_get_protocol();
int h = (lport << 16) + rport;
h ^= h>>8;
return (h & (sctp_proto->assoc_hashsize - 1));
}
/* This is the hash function for the association hash table. This is
* not used yet, but could be used as a better hash function when
* we have a vtag.
*/
static inline int sctp_vtag_hashfn(__u16 lport, __u16 rport, __u32 vtag)
{
sctp_protocol_t *sctp_proto = sctp_get_protocol();
int h = (lport << 16) + rport;
h ^= vtag;
return (h & (sctp_proto->assoc_hashsize-1));
}
/* WARNING: Do not change the layout of the members in sctp_sock! */
struct sctp_sock {
struct sock sk;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct ipv6_pinfo *pinet6;
#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
struct inet_opt inet;
struct sctp_opt sctp;
};
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sctp6_sock {
struct sock sk;
struct ipv6_pinfo *pinet6;
struct inet_opt inet;
struct sctp_opt sctp;
struct ipv6_pinfo inet6;
};
#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
#define sctp_sk(__sk) (&((struct sctp_sock *)__sk)->sctp)
#endif /* __net_sctp_h__ */
/* SCTP kernel reference Implementation Copyright (C) 1999-2001
* Cisco, Motorola, and IBM
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/net/sctp/sctp_command.h,v 1.19 2002/08/16 19:30:49 jgrimm Exp $
*
* These are the definitions needed for the command object.
*
* 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 one of the
* following email addresses:
*
* 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.
*/
#ifndef __net_sctp_command_h__
#define __net_sctp_command_h__
#include <net/sctp/sctp_constants.h>
#include <net/sctp/sctp_structs.h>
typedef enum {
SCTP_CMD_NOP = 0, /* Do nothing. */
SCTP_CMD_NEW_ASOC, /* Register a new association. */
SCTP_CMD_DELETE_TCB, /* Delete the current association. */
SCTP_CMD_NEW_STATE, /* Enter a new state. */
SCTP_CMD_REPORT_TSN, /* Record the arrival of a TSN. */
SCTP_CMD_GEN_SACK, /* Send a Selective ACK (maybe). */
SCTP_CMD_PROCESS_SACK, /* Process an inbound SACK. */
SCTP_CMD_GEN_INIT_ACK, /* Generate an INIT ACK chunk. */
SCTP_CMD_PEER_INIT, /* Process a INIT from the peer. */
SCTP_CMD_GEN_COOKIE_ECHO, /* Generate a COOKIE ECHO chunk. */
SCTP_CMD_CHUNK_ULP, /* Send a chunk to the sockets layer. */
SCTP_CMD_EVENT_ULP, /* Send a notification to the sockets layer. */
SCTP_CMD_REPLY, /* Send a chunk to our peer. */
SCTP_CMD_SEND_PKT, /* Send a full packet to our peer. */
SCTP_CMD_RETRAN, /* Mark a transport for retransmission. */
SCTP_CMD_ECN_CE, /* Do delayed CE processing. */
SCTP_CMD_ECN_ECNE, /* Do delayed ECNE processing. */
SCTP_CMD_ECN_CWR, /* Do delayed CWR processing. */
SCTP_CMD_TIMER_START, /* Start a timer. */
SCTP_CMD_TIMER_RESTART, /* Restart a timer. */
SCTP_CMD_TIMER_STOP, /* Stop a timer. */
SCTP_CMD_COUNTER_RESET, /* Reset a counter. */
SCTP_CMD_COUNTER_INC, /* Increment a counter. */
SCTP_CMD_INIT_RESTART, /* High level, do init timer work. */
SCTP_CMD_INIT_FAILED, /* High level, do init failure work. */
SCTP_CMD_REPORT_DUP, /* Report a duplicate TSN. */
SCTP_CMD_REPORT_BIGGAP, /* Narc on a TSN (it was too high). */
SCTP_CMD_SET_BIND_ADDR, /* Set the association bind_addr. */
SCTP_CMD_STRIKE, /* Mark a strike against a transport. */
SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */
SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */
SCTP_CMD_TRANSPORT_RESET, /* Reset the status of a transport. */
SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */
SCTP_CMD_REPORT_ERROR, /* Pass this error back out of the sm. */
SCTP_CMD_REPORT_BAD_TAG, /* Verification tags didn't match. */
SCTP_CMD_PROCESS_CTSN, /* Sideeffect from shutdown. */
SCTP_CMD_ASSOC_FAILED, /* Handle association failure. */
SCTP_CMD_DISCARD_PACKET, /* Discard the whole packet. */
SCTP_CMD_GEN_SHUTDOWN, /* Generate a SHUTDOWN chunk. */
SCTP_CMD_UPDATE_ASSOC, /* Update association information. */
SCTP_CMD_PURGE_OUTQUEUE, /* Purge all data waiting to be sent. */
SCTP_CMD_SETUP_T2, /* Hi-level, setup T2-shutdown parms. */
SCTP_CMD_LAST
} sctp_verb_t;
#define SCTP_CMD_MAX (SCTP_CMD_LAST - 1)
#define SCTP_CMD_NUM_VERBS (SCTP_CMD_MAX + 1)
/* How many commands can you put in an sctp_cmd_seq_t?
* This is a rather arbitrary number, ideally derived from a careful
* analysis of the state functions, but in reality just taken from
* thin air in the hopes othat we don't trigger a kernel panic.
*/
#define SCTP_MAX_NUM_COMMANDS 14
typedef union {
__s32 i32;
__u32 u32;
__u16 u16;
__u8 u8;
int error;
sctp_state_t state;
sctp_event_timeout_t to;
sctp_counter_t counter;
void *ptr;
sctp_chunk_t *chunk;
sctp_association_t *asoc;
sctp_transport_t *transport;
sctp_bind_addr_t *bp;
sctp_init_chunk_t *init;
sctp_ulpevent_t *ulpevent;
sctp_packet_t *packet;
sctp_sackhdr_t *sackh;
} sctp_arg_t;
/* We are simulating ML type constructors here.
*
* SCTP_ARG_CONSTRUCTOR(NAME, TYPE, ELT) builds a function called
* SCTP_NAME() which takes an argument of type TYPE and returns an
* sctp_arg_t. It does this by inserting the sole argument into the
* ELT union element of a local sctp_arg_t.
*
* E.g., SCTP_ARG_CONSTRUCTOR(I32, __s32, i32) builds SCTP_I32(arg),
* which takes an __s32 and returns a sctp_arg_t containing the
* __s32. So, after foo = SCTP_I32(arg), foo.i32 == arg.
*/
static inline sctp_arg_t SCTP_NULL(void)
{
sctp_arg_t retval; retval.ptr = NULL; return retval;
}
static inline sctp_arg_t SCTP_NOFORCE(void)
{
sctp_arg_t retval; retval.i32 = 0; return retval;
}
static inline sctp_arg_t SCTP_FORCE(void)
{
sctp_arg_t retval; retval.i32 = 1; return retval;
}
#define SCTP_ARG_CONSTRUCTOR(name, type, elt) \
static inline sctp_arg_t \
SCTP_## name (type arg) \
{ sctp_arg_t retval; retval.elt = arg; return retval; }
SCTP_ARG_CONSTRUCTOR(I32, __s32, i32)
SCTP_ARG_CONSTRUCTOR(U32, __u32, u32)
SCTP_ARG_CONSTRUCTOR(U16, __u16, u16)
SCTP_ARG_CONSTRUCTOR(U8, __u8, u8)
SCTP_ARG_CONSTRUCTOR(ERROR, int, error)
SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state)
SCTP_ARG_CONSTRUCTOR(COUNTER, sctp_counter_t, counter)
SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to)
SCTP_ARG_CONSTRUCTOR(PTR, void *, ptr)
SCTP_ARG_CONSTRUCTOR(CHUNK, sctp_chunk_t *, chunk)
SCTP_ARG_CONSTRUCTOR(ASOC, sctp_association_t *, asoc)
SCTP_ARG_CONSTRUCTOR(TRANSPORT, sctp_transport_t *, transport)
SCTP_ARG_CONSTRUCTOR(BA, sctp_bind_addr_t *, bp)
SCTP_ARG_CONSTRUCTOR(PEER_INIT, sctp_init_chunk_t *, init)
SCTP_ARG_CONSTRUCTOR(ULPEVENT, sctp_ulpevent_t *, ulpevent)
SCTP_ARG_CONSTRUCTOR(PACKET, sctp_packet_t *, packet)
SCTP_ARG_CONSTRUCTOR(SACKH, sctp_sackhdr_t *, sackh)
typedef struct {
sctp_arg_t obj;
sctp_verb_t verb;
} sctp_cmd_t;
typedef struct {
sctp_cmd_t cmds[SCTP_MAX_NUM_COMMANDS];
__u8 next_free_slot;
__u8 next_cmd;
} sctp_cmd_seq_t;
/* Create a new sctp_command_sequence.
* Return NULL if creating a new sequence fails.
*/
sctp_cmd_seq_t *sctp_new_cmd_seq(int priority);
/* Initialize a block of memory as a command sequence.
* Return 0 if the initialization fails.
*/
int sctp_init_cmd_seq(sctp_cmd_seq_t *seq);
/* Add a command to an sctp_cmd_seq_t.
* Return 0 if the command sequence is full.
*
* Use the SCTP_* constructors defined by SCTP_ARG_CONSTRUCTOR() above
* to wrap data which goes in the obj argument.
*/
int sctp_add_cmd(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj);
/* Rewind an sctp_cmd_seq_t to iterate from the start.
* Return 0 if the rewind fails.
*/
int sctp_rewind_sequence(sctp_cmd_seq_t *seq);
/* Return the next command structure in an sctp_cmd_seq.
* Return NULL at the end of the sequence.
*/
sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq);
/* Dispose of a command sequence. */
void sctp_free_cmd_seq(sctp_cmd_seq_t *seq);
#endif /* __net_sctp_command_h__ */
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001-2002 International Business Machines 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.
*
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/net/sctp/sctp_constants.h,v 1.11 2002/07/26 22:52:32 jgrimm Exp $
*
* 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 one of the following email
* addresses:
*
* La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us>
* Randall Stewart <randall@stewart.chicago.il.us>
* Ken Morneau <kmorneau@cisco.com>
* Qiaobing Xie <qxie1@motorola.com>
* Xingang Guo <xingang.guo@intel.com>
* Sridhar Samudrala <samudrala@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.
*
* There are still LOTS of bugs in this code... I always run on the motto
* "it is a wonder any code ever works :)"
*
*
*/
#ifndef __sctp_constants_h__
#define __sctp_constants_h__
#include <linux/tcp.h> /* For TCP states used in sctp_sock_state_t */
#include <linux/sctp.h>
#include <linux/ipv6.h> /* For ipv6hdr. */
#include <net/sctp/sctp_user.h>
/* What a hack! Jiminy Cricket! */
enum { SCTP_MAX_STREAM = 10 };
/* Define the amount of space to reserve for SCTP, IP, LL.
* There is a little bit of waste that we are always allocating
* for ipv6 headers, but this seems worth the simplicity.
*/
#define SCTP_IP_OVERHEAD ((sizeof(struct sctphdr)\
+ sizeof(struct ipv6hdr)\
+ MAX_HEADER))
/* Define the amount of space to reserve for SCTP, IP, LL.
* There is a little bit of waste that we are always allocating
* for ipv6 headers, but this seems worth the simplicity.
*/
#define SCTP_IP_OVERHEAD ((sizeof(struct sctphdr)\
+ sizeof(struct ipv6hdr)\
+ MAX_HEADER))
/* Since CIDs are sparse, we need all four of the following
* symbols. CIDs are dense through SCTP_CID_BASE_MAX.
*/
#define SCTP_CID_BASE_MAX SCTP_CID_SHUTDOWN_COMPLETE
#define SCTP_CID_MAX SCTP_CID_ASCONF_ACK
#define SCTP_NUM_BASE_CHUNK_TYPES (SCTP_CID_BASE_MAX + 1)
#define SCTP_NUM_CHUNK_TYPES (SCTP_NUM_BASE_CHUNKTYPES + 2)
/* These are the different flavours of event. */
typedef enum {
SCTP_EVENT_T_CHUNK = 1,
SCTP_EVENT_T_TIMEOUT,
SCTP_EVENT_T_OTHER,
SCTP_EVENT_T_PRIMITIVE
} sctp_event_t;
#define SCTP_EVENT_T_MAX SCTP_EVENT_T_PRIMITIVE
#define SCTP_EVENT_T_NUM (SCTP_EVENT_T_MAX + 1)
/* As a convenience for the state machine, we append SCTP_EVENT_* and
* SCTP_ULP_* to the list of possible chunks.
*/
typedef enum {
SCTP_EVENT_TIMEOUT_NONE = 0,
SCTP_EVENT_TIMEOUT_T1_COOKIE,
SCTP_EVENT_TIMEOUT_T1_INIT,
SCTP_EVENT_TIMEOUT_T2_SHUTDOWN,
SCTP_EVENT_TIMEOUT_T3_RTX,
SCTP_EVENT_TIMEOUT_T4_RTO,
SCTP_EVENT_TIMEOUT_HEARTBEAT,
SCTP_EVENT_TIMEOUT_SACK,
SCTP_EVENT_TIMEOUT_AUTOCLOSE,
SCTP_EVENT_TIMEOUT_PMTU_RAISE,
} sctp_event_timeout_t;
#define SCTP_EVENT_TIMEOUT_MAX SCTP_EVENT_TIMEOUT_PMTU_RAISE
#define SCTP_NUM_TIMEOUT_TYPES (SCTP_EVENT_TIMEOUT_MAX + 1)
typedef enum {
SCTP_EVENT_NO_PENDING_TSN = 0,
SCTP_EVENT_ICMP_UNREACHFRAG,
} sctp_event_other_t;
#define SCTP_EVENT_OTHER_MAX SCTP_EVENT_ICMP_UNREACHFRAG
#define SCTP_NUM_OTHER_TYPES (SCTP_EVENT_OTHER_MAX + 1)
/* These are primitive requests from the ULP. */
typedef enum {
SCTP_PRIMITIVE_INITIALIZE = 0,
SCTP_PRIMITIVE_ASSOCIATE,
SCTP_PRIMITIVE_SHUTDOWN,
SCTP_PRIMITIVE_ABORT,
SCTP_PRIMITIVE_SEND,
SCTP_PRIMITIVE_SETPRIMARY,
SCTP_PRIMITIVE_RECEIVE,
SCTP_PRIMITIVE_STATUS,
SCTP_PRIMITIVE_CHANGEHEARTBEAT,
SCTP_PRIMITIVE_REQUESTHEARTBEAT,
SCTP_PRIMITIVE_GETSRTTREPORT,
SCTP_PRIMITIVE_SETFAILURETHRESHOLD,
SCTP_PRIMITIVE_SETPROTOPARAMETERS,
SCTP_PRIMITIVE_RECEIVE_UNSENT,
SCTP_PRIMITIVE_RECEIVE_UNACKED,
SCTP_PRIMITIVE_DESTROY,
} sctp_event_primitive_t;
#define SCTP_EVENT_PRIMITIVE_MAX SCTP_PRIMITIVE_DESTROY
#define SCTP_NUM_PRIMITIVE_TYPES (SCTP_EVENT_PRIMITIVE_MAX + 1)
/* We define here a utility type for manipulating subtypes.
* The subtype constructors all work like this:
*
* sctp_subtype_t foo = SCTP_ST_CHUNK(SCTP_CID_INIT);
*/
typedef union {
sctp_cid_t chunk;
sctp_event_timeout_t timeout;
sctp_event_other_t other;
sctp_event_primitive_t primitive;
} sctp_subtype_t;
#define SCTP_SUBTYPE_CONSTRUCTOR(_name, _type, _elt) \
static inline sctp_subtype_t \
SCTP_ST_## _name (_type _arg) \
{ sctp_subtype_t _retval; _retval._elt = _arg; return _retval; }
SCTP_SUBTYPE_CONSTRUCTOR(CHUNK, sctp_cid_t, chunk)
SCTP_SUBTYPE_CONSTRUCTOR(TIMEOUT, sctp_event_timeout_t, timeout)
SCTP_SUBTYPE_CONSTRUCTOR(OTHER, sctp_event_other_t, other)
SCTP_SUBTYPE_CONSTRUCTOR(PRIMITIVE, sctp_event_primitive_t, primitive)
#define sctp_chunk_is_control(a) (a->chunk_hdr->type != SCTP_CID_DATA)
#define sctp_chunk_is_data(a) (a->chunk_hdr->type == SCTP_CID_DATA)
/* Calculate the actual data size in a data chunk */
#define SCTP_DATA_SNDSIZE(c) ((int)((unsigned long)(c->chunk_end)\
- (unsigned long)(c->chunk_hdr)\
- sizeof(sctp_data_chunk_t)))
/* This is a table of printable names of sctp_param_t's. */
extern const char *sctp_param_tbl[];
#define SCTP_MAX_ERROR_CAUSE SCTP_ERROR_NONEXIST_IP
#define SCTP_NUM_ERROR_CAUSE 10
/* Internal error codes */
typedef enum {
SCTP_IERROR_NO_ERROR = 0,
SCTP_IERROR_BASE = 1000,
SCTP_IERROR_NO_COOKIE,
SCTP_IERROR_BAD_SIG,
SCTP_IERROR_STALE_COOKIE,
SCTP_IERROR_NOMEM,
SCTP_IERROR_MALFORMED,
SCTP_IERROR_BAD_TAG,
SCTP_IERROR_BIG_GAP,
SCTP_IERROR_DUP_TSN,
} sctp_ierror_t;
/* SCTP state defines for internal state machine */
typedef enum {
SCTP_STATE_EMPTY = 0,
SCTP_STATE_CLOSED = 1,
SCTP_STATE_COOKIE_WAIT = 2,
SCTP_STATE_COOKIE_ECHOED = 3,
SCTP_STATE_ESTABLISHED = 4,
SCTP_STATE_SHUTDOWN_PENDING = 5,
SCTP_STATE_SHUTDOWN_SENT = 6,
SCTP_STATE_SHUTDOWN_RECEIVED = 7,
SCTP_STATE_SHUTDOWN_ACK_SENT = 8,
} sctp_state_t;
#define SCTP_STATE_MAX SCTP_STATE_SHUTDOWN_ACK_SENT
#define SCTP_STATE_NUM_STATES (SCTP_STATE_MAX + 1)
/* These are values for sk->state.
* For a UDP-style SCTP socket, the states are defined as follows
* (at this point of time, may change later after more discussions: FIXME)
* A socket in SCTP_SS_UNCONNECTED state indicates that it is not willing
* to accept new associations, but it can initiate the creation of new
* ones.
* A socket in SCTP_SS_LISTENING state indicates that it is willing to
* accept new associations and can initiate the creation of new ones.
* A socket in SCTP_SS_ESTABLISHED state indicates that it is a peeled off
* socket with one association.
*/
typedef enum {
SCTP_SS_CLOSED = TCP_CLOSE,
SCTP_SS_LISTENING = TCP_LISTEN,
SCTP_SS_ESTABLISHING = TCP_SYN_SENT,
SCTP_SS_ESTABLISHED = TCP_ESTABLISHED,
SCTP_SS_DISCONNECTING = TCP_CLOSING,
} sctp_sock_state_t;
/* These functions map various type to printable names. */
const char *sctp_cname(const sctp_subtype_t); /* chunk types */
const char *sctp_oname(const sctp_subtype_t); /* other events */
const char *sctp_tname(const sctp_subtype_t); /* timeouts */
const char *sctp_pname(const sctp_subtype_t); /* primitives */
/* This is a table of printable names of sctp_state_t's. */
extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[];
/* SCTP reachability state for each address */
#define SCTP_ADDR_NOHB 4
#define SCTP_ADDR_REACHABLE 2
#define SCTP_ADDR_NOT_REACHABLE 1
/* Guess at how big to make the TSN mapping array.
* We guarantee that we can handle at least this big a gap between the
* cumulative ACK and the highest TSN. In practice, we can often
* handle up to twice this value.
*
* NEVER make this more than 32767 (2^15-1). The Gap Ack Blocks in a
* SACK (see section 3.3.4) are only 16 bits, so 2*SCTP_TSN_MAP_SIZE
* must be less than 65535 (2^16 - 1), or we will have overflow
* problems creating SACK's.
*/
#define SCTP_TSN_MAP_SIZE 2048
#define SCTP_TSN_MAX_GAP 65535
/* We will not record more than this many duplicate TSNs between two
* SACKs. The minimum PMTU is 576. Remove all the headers and there
* is enough room for 131 duplicate reports. Round down to the
* nearest power of 2.
*/
#define SCTP_MAX_DUP_TSNS 128
typedef enum {
SCTP_COUNTER_INIT_ERROR,
} sctp_counter_t;
/* How many counters does an association need? */
#define SCTP_NUMBER_COUNTERS 5
/* Here we define the default timers. */
/* cookie timer def = ? seconds */
#define SCTP_DEFAULT_TIMEOUT_T1_COOKIE (3 * HZ)
/* init timer def = 3 seconds */
#define SCTP_DEFAULT_TIMEOUT_T1_INIT (3 * HZ)
/* shutdown timer def = 300 ms */
#define SCTP_DEFAULT_TIMEOUT_T2_SHUTDOWN ((300 * HZ) / 1000)
/* 0 seconds + RTO */
#define SCTP_DEFAULT_TIMEOUT_HEARTBEAT (10 * HZ)
/* recv timer def = 200ms (in usec) */
#define SCTP_DEFAULT_TIMEOUT_SACK ((200 * HZ) / 1000)
#define SCTP_DEFAULT_TIMEOUT_SACK_MAX ((500 * HZ) / 1000) /* 500 ms */
/* How long do we wait before attempting to raise the PMTU? */
#define SCTP_DEFAULT_TIMEOUT_PMTU_RAISE (10 * 60 * HZ) /* 10 Minutes */
#define SCTP_DEFAULT_TIMEOUT_PMTU_RAISE_MIN (10 * 60 * HZ) /* 10 Minutes */
/* RTO.Initial - 3 seconds
* RTO.Min - 1 second
* RTO.Max - 60 seconds
* RTO.Alpha - 1/8
* RTO.Beta - 1/4
*/
#define SCTP_RTO_INITIAL (3 * HZ)
#define SCTP_RTO_MIN (1 * HZ)
#define SCTP_RTO_MAX (60 * HZ)
#define SCTP_RTO_ALPHA 3 /* 1/8 when converted to right shifts. */
#define SCTP_RTO_BETA 2 /* 1/4 when converted to right shifts. */
/* Maximum number of new data packets that can be sent in a burst. */
#define SCTP_MAX_BURST 4
#define SCTP_CLOCK_GRANULARITY 1 /* 1 jiffy */
#define SCTP_DEF_MAX_INIT 6
#define SCTP_DEF_MAX_SEND 10
#define SCTP_DEFAULT_COOKIE_LIFE_SEC 60 /* seconds */
#define SCTP_DEFAULT_COOKIE_LIFE_USEC 0 /* microseconds */
#define SCTP_DEFAULT_MINWINDOW 1500 /* default minimum rwnd size */
#define SCTP_DEFAULT_MAXWINDOW 32768 /* default rwnd size */
#define SCTP_DEFAULT_MAXSEGMENT 1500 /* MTU size, this is the limit
* to which we will raise the P-MTU.
*/
#define SCTP_DEFAULT_MINSEGMENT 512 /* MTU size ... if no mtu disc */
#define SCTP_HOW_MANY_SECRETS 2 /* How many secrets I keep */
#define SCTP_HOW_LONG_COOKIE_LIVE 3600 /* How many seconds the current
* secret will live?
*/
#define SCTP_SECRET_SIZE 32 /* Number of octets in a 256 bits. */
#define SCTP_SIGNATURE_SIZE 20 /* size of a SLA-1 signature */
#define SCTP_COOKIE_MULTIPLE 64 /* Pad out our cookie to make our hash
* functions simpler to write.
*/
/* These return values describe the success or failure of a number of
* routines which form the lower interface to SCTP_outqueue.
*/
typedef enum {
SCTP_XMIT_OK,
SCTP_XMIT_PMTU_FULL,
SCTP_XMIT_RWND_FULL,
SCTP_XMIT_MUST_FRAG,
} sctp_xmit_t;
/* These are the commands for manipulating transports. */
typedef enum {
SCTP_TRANSPORT_UP,
SCTP_TRANSPORT_DOWN,
} sctp_transport_cmd_t;
/* These are the address scopes defined mainly for IPv4 addresses
* based on draft of SCTP IPv4 scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>.
* These scopes are hopefully generic enough to be used on scoping both
* IPv4 and IPv6 addresses in SCTP.
* At this point, the IPv6 scopes will be mapped to these internal scopes
* as much as possible.
*/
typedef enum {
SCTP_SCOPE_GLOBAL, /* IPv4 global addresses */
SCTP_SCOPE_PRIVATE, /* IPv4 private addresses */
SCTP_SCOPE_LINK, /* IPv4 link local address */
SCTP_SCOPE_LOOPBACK, /* IPv4 loopback address */
SCTP_SCOPE_UNUSABLE, /* IPv4 unusable addresses */
} sctp_scope_t;
/* Based on IPv4 scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>,
* SCTP IPv4 unusable addresses: 0.0.0.0/8, 224.0.0.0/4, 198.18.0.0/24,
* 192.88.99.0/24.
* Also, RFC 8.4, non-unicast addresses are not considered valid SCTP
* addresses.
*/
#define IS_IPV4_UNUSABLE_ADDRESS(a) \
((INADDR_BROADCAST == *a) || \
(MULTICAST(*a)) || \
(((unsigned char *)(a))[0] == 0) || \
((((unsigned char *)(a))[0] == 198) && \
(((unsigned char *)(a))[1] == 18) && \
(((unsigned char *)(a))[2] == 0)) || \
((((unsigned char *)(a))[0] == 192) && \
(((unsigned char *)(a))[1] == 88) && \
(((unsigned char *)(a))[2] == 99)))
/* IPv4 Link-local addresses: 169.254.0.0/16. */
#define IS_IPV4_LINK_ADDRESS(a) \
((((unsigned char *)(a))[0] == 169) && \
(((unsigned char *)(a))[1] == 254))
/* RFC 1918 "Address Allocation for Private Internets" defines the IPv4
* private address space as the following:
*
* 10.0.0.0 - 10.255.255.255 (10/8 prefix)
* 172.16.0.0.0 - 172.31.255.255 (172.16/12 prefix)
* 192.168.0.0 - 192.168.255.255 (192.168/16 prefix)
*/
#define IS_IPV4_PRIVATE_ADDRESS(a) \
((((unsigned char *)(a))[0] == 10) || \
((((unsigned char *)(a))[0] == 172) && \
(((unsigned char *)(a))[1] >= 16) && \
(((unsigned char *)(a))[1] < 32)) || \
((((unsigned char *)(a))[0] == 192) && \
(((unsigned char *)(a))[1] == 168)))
/* Flags used for the bind address copy functions. */
#define SCTP_ADDR6_ALLOWED 0x00000001 /* IPv6 address is allowed by
local sock family */
#define SCTP_ADDR4_PEERSUPP 0x00000002 /* IPv4 address is supported by
peer */
#define SCTP_ADDR6_PEERSUPP 0x00000004 /* IPv6 address is supported by
peer */
/* Reasons to lower cwnd. */
typedef enum {
SCTP_LOWER_CWND_T3_RTX,
SCTP_LOWER_CWND_FAST_RTX,
SCTP_LOWER_CWND_ECNE,
SCTP_LOWER_CWND_INACTIVE,
} sctp_lower_cwnd_t;
#endif /* __sctp_constants_h__ */
/* SCTP reference Implementation
* Copyright (C) 1999 Cisco, Inc.
* Copyright (C) 1999 Motorola, Inc.
*
* This file originates from Randy Stewart's SCTP reference Implementation.
*
* 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:
* Randy Stewart <rstewar1@email.mot.com>
* Ken Morneau <kmorneau@cisco.com>
* Qiaobing Xie <qxie1@email.mot.com>
*/
#ifndef __SLA1_h__
#define __SLA1_h__
struct SLA_1_Context {
unsigned int A;
unsigned int B;
unsigned int C;
unsigned int D;
unsigned int E;
unsigned int H0;
unsigned int H1;
unsigned int H2;
unsigned int H3;
unsigned int H4;
unsigned int words[80];
unsigned int TEMP;
/* block I am collecting to process */
char SLAblock[64];
/* collected so far */
int howManyInBlock;
unsigned int runningTotal;
};
#define F1(B,C,D) (((B & C) | ((~B) & D))) /* 0 <= t <= 19 */
#define F2(B,C,D) (B ^ C ^ D) /* 20 <= t <= 39 */
#define F3(B,C,D) ((B & C) | (B & D) | (C & D)) /* 40 <= t <= 59 */
#define F4(B,C,D) (B ^ C ^ D) /*600 <= t <= 79 */
/* circular shift */
#define CSHIFT(A,B) ((B << A) | (B >> (32-A)))
#define K1 0x5a827999 /* 0 <= t <= 19 */
#define K2 0x6ed9eba1 /* 20 <= t <= 39 */
#define K3 0x8f1bbcdc /* 40 <= t <= 59 */
#define K4 0xca62c1d6 /* 60 <= t <= 79 */
#define H0INIT 0x67452301
#define H1INIT 0xefcdab89
#define H2INIT 0x98badcfe
#define H3INIT 0x10325476
#define H4INIT 0xc3d2e1f0
extern void SLA1_Init(struct SLA_1_Context *);
extern void SLA1_Process(struct SLA_1_Context *, const unsigned char *, int);
extern void SLA1_Final(struct SLA_1_Context *, unsigned char *);
#endif
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001-2002 International Business Machines 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.
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/net/sctp/sctp_sm.h,v 1.34 2002/08/21 18:34:04 jgrimm Exp $
*
* These are definitions needed by the 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 addresses:
* 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>
* Dajiang Zhang <dajiang.zhang@nokia.com>
* Sridhar Samudrala <sri@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/compiler.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/in.h>
#include <net/sctp/sctp_command.h>
#include <net/sctp/sctp.h>
#ifndef __sctp_sm_h__
#define __sctp_sm_h__
/*
* Possible values for the disposition are:
*/
typedef enum {
SCTP_DISPOSITION_DISCARD, /* No further processing. */
SCTP_DISPOSITION_CONSUME, /* Process return values normally. */
SCTP_DISPOSITION_NOMEM, /* We ran out of memory--recover. */
SCTP_DISPOSITION_DELETE_TCB, /* Close the association. */
SCTP_DISPOSITION_ABORT, /* Close the association NOW. */
SCTP_DISPOSITION_VIOLATION, /* The peer is misbehaving. */
SCTP_DISPOSITION_NOT_IMPL, /* This entry is not implemented. */
SCTP_DISPOSITION_ERROR, /* This is plain old user error. */
SCTP_DISPOSITION_BUG, /* This is a bug. */
} sctp_disposition_t;
typedef struct {
int name;
int action;
} sctp_sm_command_t;
typedef sctp_disposition_t (sctp_state_fn_t) (const sctp_endpoint_t *,
const sctp_association_t *,
const sctp_subtype_t type,
void *arg,
sctp_cmd_seq_t *);
typedef void (sctp_timer_event_t) (unsigned long);
typedef struct {
sctp_state_fn_t *fn;
char *name;
} sctp_sm_table_entry_t;
/* A naming convention of "sctp_sf_xxx" applies to all the state functions
* currently in use.
*/
/* Prototypes for generic state functions. */
sctp_state_fn_t sctp_sf_not_impl;
sctp_state_fn_t sctp_sf_bug;
/* Prototypes for gener timer state functions. */
sctp_state_fn_t sctp_sf_timer_ignore;
/* Prototypes for chunk state functions. */
sctp_state_fn_t sctp_sf_do_9_1_abort;
sctp_state_fn_t sctp_sf_cookie_wait_abort;
sctp_state_fn_t sctp_sf_cookie_echoed_abort;
sctp_state_fn_t sctp_sf_do_5_1B_init;
sctp_state_fn_t sctp_sf_do_5_1C_ack;
sctp_state_fn_t sctp_sf_do_5_1D_ce;
sctp_state_fn_t sctp_sf_do_5_1E_ca;
sctp_state_fn_t sctp_sf_do_4_C;
sctp_state_fn_t sctp_sf_eat_data_6_2;
sctp_state_fn_t sctp_sf_eat_data_fast_4_4;
sctp_state_fn_t sctp_sf_eat_sack_6_2;
sctp_state_fn_t sctp_sf_tabort_8_4_8;
sctp_state_fn_t sctp_sf_operr_notify;
sctp_state_fn_t sctp_sf_t1_timer_expire;
sctp_state_fn_t sctp_sf_t2_timer_expire;
sctp_state_fn_t sctp_sf_sendbeat_8_3;
sctp_state_fn_t sctp_sf_beat_8_3;
sctp_state_fn_t sctp_sf_backbeat_8_3;
sctp_state_fn_t sctp_sf_do_9_2_final;
sctp_state_fn_t sctp_sf_do_9_2_shutdown;
sctp_state_fn_t sctp_sf_do_ecn_cwr;
sctp_state_fn_t sctp_sf_do_ecne;
sctp_state_fn_t sctp_sf_ootb;
sctp_state_fn_t sctp_sf_shut_8_4_5;
sctp_state_fn_t sctp_sf_pdiscard;
sctp_state_fn_t sctp_sf_violation;
sctp_state_fn_t sctp_sf_discard_chunk;
sctp_state_fn_t sctp_sf_do_5_2_1_siminit;
sctp_state_fn_t sctp_sf_do_5_2_2_dupinit;
sctp_state_fn_t sctp_sf_do_5_2_4_dupcook;
/* Prototypes for primitive event state functions. */
sctp_state_fn_t sctp_sf_do_prm_asoc;
sctp_state_fn_t sctp_sf_do_prm_send;
sctp_state_fn_t sctp_sf_do_9_2_prm_shutdown;
sctp_state_fn_t sctp_sf_cookie_wait_prm_shutdown;
sctp_state_fn_t sctp_sf_cookie_echoed_prm_shutdown;
sctp_state_fn_t sctp_sf_do_9_1_prm_abort;
sctp_state_fn_t sctp_sf_cookie_wait_prm_abort;
sctp_state_fn_t sctp_sf_cookie_echoed_prm_abort;
sctp_state_fn_t sctp_sf_error_closed;
sctp_state_fn_t sctp_sf_error_shutdown;
sctp_state_fn_t sctp_sf_ignore_primitive;
/* Prototypes for other event state functions. */
sctp_state_fn_t sctp_sf_do_9_2_start_shutdown;
sctp_state_fn_t sctp_sf_do_9_2_shutdown_ack;
sctp_state_fn_t sctp_sf_ignore_other;
/* Prototypes for timeout event state functions. */
sctp_state_fn_t sctp_sf_do_6_3_3_rtx;
sctp_state_fn_t sctp_sf_do_6_2_sack;
sctp_state_fn_t sctp_sf_autoclose_timer_expire;
/* These are state functions which are either obsolete or not in use yet.
* If any of these functions needs to be revived, it should be renamed with
* the "sctp_sf_xxx" prefix, and be moved to the above prototype groups.
*/
/* Prototypes for chunk state functions. Not in use. */
sctp_state_fn_t sctp_sf_do_5_2_6_stale;
sctp_state_fn_t sctp_sf_do_9_2_reshutack;
sctp_state_fn_t sctp_sf_do_9_2_reshut;
sctp_state_fn_t sctp_sf_do_9_2_shutack;
sctp_state_fn_t lucky;
sctp_state_fn_t other_stupid;
/* Prototypes for timeout event state functions. Not in use. */
sctp_state_fn_t sctp_do_4_2_reinit;
sctp_state_fn_t sctp_do_4_3_reecho;
sctp_state_fn_t sctp_do_9_2_reshut;
sctp_state_fn_t sctp_do_9_2_reshutack;
sctp_state_fn_t sctp_do_8_3_hb_err;
sctp_state_fn_t sctp_heartoff;
/* Prototypes for addip related state functions. Not in use. */
sctp_state_fn_t sctp_addip_do_asconf;
sctp_state_fn_t sctp_addip_do_asconf_ack;
/* Prototypes for utility support functions. */
__u8 sctp_get_chunk_type(sctp_chunk_t *chunk);
sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
sctp_state_t state,
sctp_subtype_t event_subtype);
time_t timeval_sub(struct timeval *, struct timeval *);
sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *,
sctp_chunk_t *,
const int priority);
__u32 sctp_generate_verification_tag(void);
sctpParam_t sctp_get_my_addrs_raw(const sctp_association_t *,
const int priority, int *addrs_len);
void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag);
/* Prototypes for chunk-building functions. */
sctp_chunk_t *sctp_make_init(const sctp_association_t *,
const sctp_bind_addr_t *,
int priority);
sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *,
const sctp_chunk_t *,
const int priority);
sctp_chunk_t *sctp_make_cookie_echo(const sctp_association_t *,
const sctp_chunk_t *);
sctp_chunk_t *sctp_make_cookie_ack(const sctp_association_t *,
const sctp_chunk_t *);
sctp_chunk_t *sctp_make_cwr(const sctp_association_t *,
const __u32 lowest_tsn,
const sctp_chunk_t *);
sctp_chunk_t *sctp_make_datafrag(sctp_association_t *,
const struct sctp_sndrcvinfo *sinfo,
int len, const __u8 *data,
__u8 flags, __u16 ssn);
sctp_chunk_t * sctp_make_datafrag_empty(sctp_association_t *,
const struct sctp_sndrcvinfo *sinfo,
int len, const __u8 flags,
__u16 ssn);
sctp_chunk_t *sctp_make_data(sctp_association_t *,
const struct sctp_sndrcvinfo *sinfo,
int len, const __u8 *data);
sctp_chunk_t *sctp_make_data_empty(sctp_association_t *,
const struct sctp_sndrcvinfo *, int len);
sctp_chunk_t *sctp_make_ecne(const sctp_association_t *,
const __u32);
sctp_chunk_t *sctp_make_sack(const sctp_association_t *);
sctp_chunk_t *sctp_make_shutdown(const sctp_association_t *asoc);
sctp_chunk_t *sctp_make_shutdown_ack(const sctp_association_t *asoc,
const sctp_chunk_t *);
sctp_chunk_t *sctp_make_shutdown_complete(const sctp_association_t *,
const sctp_chunk_t *);
void sctp_init_cause(sctp_chunk_t *, __u16 cause, const void *, size_t);
sctp_chunk_t *sctp_make_abort(const sctp_association_t *,
const sctp_chunk_t *,
const size_t hint);
sctp_chunk_t *sctp_make_abort_no_data(const sctp_association_t *,
const sctp_chunk_t *,
__u32 tsn);
sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *,
const sctp_transport_t *,
const void *payload,
const size_t paylen);
sctp_chunk_t *sctp_make_heartbeat_ack(const sctp_association_t *,
const sctp_chunk_t *,
const void *payload,
const size_t paylen);
sctp_chunk_t *sctp_make_op_error(const sctp_association_t *,
const sctp_chunk_t *chunk,
__u16 cause_code,
const void *payload,
size_t paylen);
void sctp_chunk_assign_tsn(sctp_chunk_t *);
/* Prototypes for statetable processing. */
int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
sctp_endpoint_t *,
sctp_association_t *asoc,
void *event_arg,
int priority);
int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
sctp_endpoint_t *,
sctp_association_t *asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *commands,
int priority);
/* 2nd level prototypes */
int
sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
sctp_endpoint_t *ep,
sctp_association_t *asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *retval,
int priority);
int sctp_gen_sack(sctp_association_t *, int force, sctp_cmd_seq_t *);
void sctp_do_TSNdup(sctp_association_t *, sctp_chunk_t *, long gap);
void sctp_generate_t3_rtx_event(unsigned long peer);
void sctp_generate_heartbeat_event(unsigned long peer);
sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *);
sctp_cookie_param_t *
sctp_pack_cookie(const sctp_endpoint_t *, const sctp_association_t *,
const sctp_chunk_t *, int *cookie_len,
const __u8 *, int addrs_len);
sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *,
const sctp_association_t *,
sctp_chunk_t *, int priority, int *err);
int sctp_addip_addr_config(sctp_association_t *, sctp_param_t,
struct sockaddr_storage*, int);
/* 3rd level prototypes */
__u32 sctp_generate_tag(const sctp_endpoint_t *);
__u32 sctp_generate_tsn(const sctp_endpoint_t *);
/* 4th level prototypes */
void sctp_param2sockaddr(sockaddr_storage_t *addr, const sctpParam_t param,
__u16 port);
int sctp_addr2sockaddr(const sctpParam_t, sockaddr_storage_t *);
int sockaddr2sctp_addr(const sockaddr_storage_t *, sctpParam_t);
/* Extern declarations for major data structures. */
sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t);
extern sctp_sm_table_entry_t
primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES];
extern sctp_sm_table_entry_t
other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES];
extern sctp_sm_table_entry_t
timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES];
extern sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES];
/* These are some handy utility macros... */
/* Get the size of a DATA chunk payload. */
static inline __u16 sctp_data_size(sctp_chunk_t *chunk)
{
__u16 size;
size = ntohs(chunk->chunk_hdr->length);
size -= sizeof(sctp_data_chunk_t);
return size;
}
/* Compare two TSNs */
/* RFC 1982 - Serial Number Arithmetic
*
* 2. Comparison
* Then, s1 is said to be equal to s2 if and only if i1 is equal to i2,
* in all other cases, s1 is not equal to s2.
*
* s1 is said to be less than s2 if, and only if, s1 is not equal to s2,
* and
*
* (i1 < i2 and i2 - i1 < 2^(SERIAL_BITS - 1)) or
* (i1 > i2 and i1 - i2 > 2^(SERIAL_BITS - 1))
*
* s1 is said to be greater than s2 if, and only if, s1 is not equal to
* s2, and
*
* (i1 < i2 and i2 - i1 > 2^(SERIAL_BITS - 1)) or
* (i1 > i2 and i1 - i2 < 2^(SERIAL_BITS - 1))
*/
/*
* RFC 2960
* 1.6 Serial Number Arithmetic
*
* Comparisons and arithmetic on TSNs in this document SHOULD use Serial
* Number Arithmetic as defined in [RFC1982] where SERIAL_BITS = 32.
*/
enum {
TSN_SIGN_BIT = (1<<31)
};
static inline int TSN_lt(__u32 s, __u32 t)
{
return (((s) - (t)) & TSN_SIGN_BIT);
}
static inline int TSN_lte(__u32 s, __u32 t)
{
return (((s) == (t)) || (((s) - (t)) & TSN_SIGN_BIT));
}
/* Compare two SSNs */
/*
* RFC 2960
* 1.6 Serial Number Arithmetic
*
* Comparisons and arithmetic on Stream Sequence Numbers in this document
* SHOULD use Serial Number Arithmetic as defined in [RFC1982] where
* SERIAL_BITS = 16.
*/
enum {
SSN_SIGN_BIT = (1<<15)
};
static inline int SSN_lt(__u16 s, __u16 t)
{
return (((s) - (t)) & SSN_SIGN_BIT);
}
static inline int SSN_lte(__u16 s, __u16 t)
{
return (((s) == (t)) || (((s) - (t)) & SSN_SIGN_BIT));
}
/* Run sctp_add_cmd() generating a BUG() if there is a failure. */
static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj)
{
if (unlikely(!sctp_add_cmd(seq, verb, obj)))
BUG();
}
#endif /* __sctp_sm_h__ */
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/net/sctp/sctp_structs.h,v 1.21 2002/08/16 19:30:49 jgrimm Exp $
*
* 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 addresses:
* 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:
* Randall Stewart <randall@sctp.chicago.il.us>
* Ken Morneau <kmorneau@cisco.com>
* Qiaobing Xie <qxie1@email.mot.com>
* 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>
* 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.
*/
#ifndef __sctp_structs_h__
#define __sctp_structs_h__
#include <linux/time.h> /* We get struct timespec. */
#include <linux/socket.h> /* linux/in.h needs this!! */
#include <linux/in.h> /* We get struct sockaddr_in. */
#include <linux/in6.h> /* We get struct in6_addr */
#include <asm/param.h> /* We get MAXHOSTNAMELEN. */
#include <asm/atomic.h> /* This gets us atomic counters. */
#include <linux/skbuff.h> /* We need sk_buff_head. */
#include <linux/tqueue.h> /* We need tq_struct. */
#include <linux/sctp.h> /* We need sctp* header structs. */
/*
* This is (almost) a direct quote from RFC 2553.
*/
/*
* Desired design of maximum size and alignment
*/
#define _SS_MAXSIZE 128 /* Implementation specific max size */
#define _SS_ALIGNSIZE (sizeof (__s64))
/* Implementation specific desired alignment */
/*
* Definitions used for sockaddr_storage structure paddings design.
*/
#define _SS_PAD1SIZE (_SS_ALIGNSIZE - sizeof (sa_family_t))
#define _SS_PAD2SIZE (_SS_MAXSIZE - (sizeof (sa_family_t)+ \
_SS_PAD1SIZE + _SS_ALIGNSIZE))
struct sockaddr_storage {
sa_family_t __ss_family; /* address family */
/* Following fields are implementation specific */
char __ss_pad1[_SS_PAD1SIZE];
/* 6 byte pad, to make implementation */
/* specific pad up to alignment field that */
/* follows explicit in the data structure */
__s64 __ss_align; /* field to force desired structure */
/* storage alignment */
char __ss_pad2[_SS_PAD2SIZE];
/* 112 byte pad to achieve desired size, */
/* _SS_MAXSIZE value minus size of ss_family */
/* __ss_pad1, __ss_align fields is 112 */
};
/* A convenience structure for handling sockaddr structures.
* We should wean ourselves off this.
*/
typedef union {
struct sockaddr_in v4;
struct sockaddr_in6 v6;
struct sockaddr sa;
} sockaddr_storage_t;
/* Forward declarations for data structures. */
struct SCTP_protocol;
struct SCTP_endpoint;
struct SCTP_association;
struct SCTP_transport;
struct SCTP_packet;
struct SCTP_chunk;
struct SCTP_inqueue;
struct SCTP_outqueue;
struct SCTP_bind_addr;
struct sctp_opt;
struct sctp_endpoint_common;
typedef struct SCTP_protocol sctp_protocol_t;
typedef struct SCTP_endpoint sctp_endpoint_t;
typedef struct SCTP_association sctp_association_t;
typedef struct SCTP_transport sctp_transport_t;
typedef struct SCTP_packet sctp_packet_t;
typedef struct SCTP_chunk sctp_chunk_t;
typedef struct SCTP_inqueue sctp_inqueue_t;
typedef struct SCTP_outqueue sctp_outqueue_t;
typedef struct SCTP_bind_addr sctp_bind_addr_t;
typedef struct sctp_opt sctp_opt_t;
typedef struct sctp_endpoint_common sctp_endpoint_common_t;
#include <net/sctp/sctp_tsnmap.h>
#include <net/sctp/sctp_ulpevent.h>
#include <net/sctp/sctp_ulpqueue.h>
/* Structures useful for managing bind/connect. */
typedef struct sctp_bind_bucket {
unsigned short port;
unsigned short fastreuse;
struct sctp_bind_bucket *next;
struct sctp_bind_bucket **pprev;
struct sock *sk;
} sctp_bind_bucket_t;
typedef struct sctp_bind_hashbucket {
spinlock_t lock;
struct sctp_bind_bucket *chain;
} sctp_bind_hashbucket_t;
/* Used for hashing all associations. */
typedef struct sctp_hashbucket {
rwlock_t lock;
sctp_endpoint_common_t *chain;
} sctp_hashbucket_t __attribute__((__aligned__(8)));
/* The SCTP protocol structure. */
struct SCTP_protocol {
/* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values
*
* The following protocol parameters are RECOMMENDED:
*
* RTO.Initial - 3 seconds
* RTO.Min - 1 second
* RTO.Max - 60 seconds
* RTO.Alpha - 1/8 (3 when converted to right shifts.)
* RTO.Beta - 1/4 (2 when converted to right shifts.)
*/
__u32 rto_initial;
__u32 rto_min;
__u32 rto_max;
/* Note: rto_alpha and rto_beta are really defined as inverse
* powers of two to facilitate integer operations.
*/
int rto_alpha;
int rto_beta;
/* Max.Burst - 4 */
int max_burst;
/* Valid.Cookie.Life - 60 seconds */
int valid_cookie_life;
/* Association.Max.Retrans - 10 attempts
* Path.Max.Retrans - 5 attempts (per destination address)
* Max.Init.Retransmits - 8 attempts
*/
int max_retrans_association;
int max_retrans_path;
int max_retrans_init;
/* HB.interval - 30 seconds */
int hb_interval;
/* The following variables are implementation specific. */
/* Default initialization values to be applied to new associations. */
__u16 max_instreams;
__u16 max_outstreams;
/* This is a list of groups of functions for each address
* family that we support.
*/
list_t address_families;
/* This is the hash of all endpoints. */
int ep_hashsize;
sctp_hashbucket_t *ep_hashbucket;
/* This is the hash of all associations. */
int assoc_hashsize;
sctp_hashbucket_t *assoc_hashbucket;
/* This is the sctp port control hash. */
int port_hashsize;
int port_rover;
spinlock_t port_alloc_lock; /* Protects port_rover. */
sctp_bind_hashbucket_t *port_hashtable;
/* This is the global local address list.
* We actively maintain this complete list of interfaces on
* the system by catching routing events.
*
* It is a list of struct sockaddr_storage_list.
*/
list_t local_addr_list;
spinlock_t local_addr_lock;
};
/*
* Pointers to address related SCTP functions.
* (i.e. things that depend on the address family.)
*/
typedef struct sctp_func {
int (*queue_xmit) (struct sk_buff *skb);
int (*setsockopt) (struct sock *sk,
int level,
int optname,
char *optval,
int optlen);
int (*getsockopt) (struct sock *sk,
int level,
int optname,
char *optval,
int *optlen);
int (*get_dst_mtu) (const sockaddr_storage_t *address);
__u16 net_header_len;
int sockaddr_len;
sa_family_t sa_family;
list_t list;
} sctp_func_t;
sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address);
/* SCTP Socket type: UDP or TCP style. */
typedef enum {
SCTP_SOCKET_UDP = 0,
SCTP_SOCKET_UDP_HIGH_BANDWIDTH,
SCTP_SOCKET_TCP
} sctp_socket_type_t;
/* Per socket SCTP information. */
struct sctp_opt {
/* What kind of a socket is this? */
sctp_socket_type_t type;
/* What is our base endpointer? */
sctp_endpoint_t *ep;
/* Various Socket Options. */
__u16 default_stream;
__u32 default_ppid;
struct sctp_initmsg initmsg;
struct sctp_rtoinfo rtoinfo;
struct sctp_paddrparams paddrparam;
struct sctp_event_subscribe subscribe;
__u32 autoclose;
__u8 nodelay;
__u8 disable_fragments;
};
/* This is our APPLICATION-SPECIFIC state cookie.
* THIS IS NOT DICTATED BY THE SPECIFICATION.
*/
/* These are the parts of an association which we send in the cookie.
* Most of these are straight out of:
* RFC2960 12.2 Parameters necessary per association (i.e. the TCB)
*
*/
typedef struct sctp_cookie {
/* My : Tag expected in every inbound packet and sent
* Verification: in the INIT or INIT ACK chunk.
* Tag :
*/
__u32 my_vtag;
/* Peer's : Tag expected in every outbound packet except
* Verification: in the INIT chunk.
* Tag :
*/
__u32 peer_vtag;
/* The rest of these are not from the spec, but really need to
* be in the cookie.
*/
/* My Tie Tag : Assist in discovering a restarting association. */
__u32 my_ttag;
/* Peer's Tie Tag: Assist in discovering a restarting association. */
__u32 peer_ttag;
/* When does this cookie expire? */
struct timeval expiration;
/* Number of inbound/outbound streams which are set
* and negotiated during the INIT process. */
__u16 sinit_num_ostreams;
__u16 sinit_max_instreams;
/* This is the first sequence number I used. */
__u32 initial_tsn;
/* This holds the originating address of the INIT packet. */
sockaddr_storage_t peer_addr;
/* This is a shim for my peer's INIT packet, followed by
* a copy of the raw address list of the association.
* The length of the raw address list is saved in the
* raw_addr_list_len field, which will be used at the time when
* the association TCB is re-constructed from the cookie.
*/
__u32 raw_addr_list_len;
sctp_init_chunk_t peer_init[0];
} sctp_cookie_t;
/* The format of our cookie that we send to our peer. */
typedef struct sctp_signed_cookie {
__u8 signature[SCTP_SECRET_SIZE];
sctp_cookie_t c;
} sctp_signed_cookie_t;
/* This convenience type allows us to avoid casting when walking
* through a parameter list.
*/
typedef union {
__u8 *v;
sctp_paramhdr_t *p;
sctp_cookie_preserve_param_t *bht;
sctp_hostname_param_t *dns;
sctp_cookie_param_t *cookie;
sctp_supported_addrs_param_t *sat;
sctp_ipv4addr_param_t *v4;
sctp_ipv6addr_param_t *v6;
} sctpParam_t;
/* This is another convenience type to allocate memory for address
* params for the maximum size and pass such structures around
* internally.
*/
typedef union {
sctp_ipv4addr_param_t v4;
sctp_ipv6addr_param_t v6;
} sctpIpAddress_t;
/* RFC 2960. Section 3.3.5 Heartbeat.
* Heartbeat Information: variable length
* The Sender-specific Heartbeat Info field should normally include
* information about the sender's current time when this HEARTBEAT
* chunk is sent and the destination transport address to which this
* HEARTBEAT is sent (see Section 8.3).
*/
typedef struct sctp_sender_hb_info {
sctp_paramhdr_t param_hdr;
sockaddr_storage_t daddr;
unsigned long sent_at;
} sctp_sender_hb_info_t __attribute__((packed));
/* RFC2960 1.4 Key Terms
*
* o Chunk: A unit of information within an SCTP packet, consisting of
* a chunk header and chunk-specific content.
*
* As a matter of convenience, we remember the SCTP common header for
* each chunk as well as a few other header pointers...
*/
struct SCTP_chunk {
/* These first three elements MUST PRECISELY match the first
* three elements of struct sk_buff. This allows us to reuse
* all the skb_* queue management functions.
*/
sctp_chunk_t *next;
sctp_chunk_t *prev;
struct sk_buff_head *list;
/* This is our link to the per-transport transmitted list. */
struct list_head transmitted_list;
/* This field is used by chunks that hold fragmented data.
* For the first fragment this is the list that holds the rest of
* fragments. For the remaining fragments, this is the link to the
* frag_list maintained in the first fragment.
*/
struct list_head frag_list;
/* This points to the sk_buff containing the actual data. */
struct sk_buff *skb;
/* These are the SCTP headers by reverse order in a packet.
* Note that some of these may happen more than once. In that
* case, we point at the "current" one, whatever that means
* for that level of header.
*/
/* We point this at the FIRST TLV parameter to chunk_hdr. */
sctpParam_t param_hdr;
union {
__u8 *v;
sctp_datahdr_t *data_hdr;
sctp_inithdr_t *init_hdr;
sctp_sackhdr_t *sack_hdr;
sctp_heartbeathdr_t *hb_hdr;
sctp_sender_hb_info_t *hbs_hdr;
sctp_shutdownhdr_t *shutdown_hdr;
sctp_signed_cookie_t *cookie_hdr;
sctp_ecnehdr_t *ecne_hdr;
sctp_cwrhdr_t *ecn_cwr_hdr;
sctp_errhdr_t *err_hdr;
} subh;
__u8 *chunk_end;
sctp_chunkhdr_t *chunk_hdr;
sctp_sctphdr_t *sctp_hdr;
/* This needs to be recoverable for SCTP_SEND_FAILED events. */
struct sctp_sndrcvinfo sinfo;
/* Which association does this belong to? */
sctp_association_t *asoc;
/* What endpoint received this chunk? */
sctp_endpoint_common_t *rcvr;
/* We fill this in if we are calculating RTT. */
unsigned long sent_at;
__u8 rtt_in_progress; /* Is this chunk used for RTT calculation? */
__u8 num_times_sent; /* How man times did we send this? */
__u8 has_tsn; /* Does this chunk have a TSN yet? */
__u8 singleton; /* Was this the only chunk in the packet? */
__u8 end_of_packet; /* Was this the last chunk in the packet? */
__u8 ecn_ce_done; /* Have we processed the ECN CE bit? */
__u8 pdiscard; /* Discard the whole packet now? */
__u8 tsn_gap_acked; /* Is this chunk acked by a GAP ACK? */
__u8 fast_retransmit; /* Is this chunk fast retransmitted? */
__u8 tsn_missing_report; /* Data chunk missing counter. */
/* What is the origin IP address for this chunk? */
sockaddr_storage_t source;
/* For an inbound chunk, this tells us where it came from.
* For an outbound chunk, it tells us where we'd like it to
* go. It is NULL if we have no preference.
*/
sctp_transport_t *transport;
};
sctp_chunk_t *sctp_make_chunk(const sctp_association_t *, __u8 type,
__u8 flags, int size);
void sctp_free_chunk(sctp_chunk_t *);
sctp_chunk_t *sctp_copy_chunk(sctp_chunk_t *, int flags);
void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data);
int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data);
sctp_chunk_t *sctp_chunkify(struct sk_buff *, const sctp_association_t *,
struct sock *);
void sctp_init_source(sctp_chunk_t *chunk);
const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk);
/* This is a structure for holding either an IPv6 or an IPv4 address. */
/* sin_family -- AF_INET or AF_INET6
* sin_port -- ordinary port number
* sin_addr -- cast to either (struct in_addr) or (struct in6_addr)
*/
struct sockaddr_storage_list {
list_t list;
sockaddr_storage_t a;
};
typedef sctp_chunk_t *(sctp_packet_phandler_t)(sctp_association_t *);
/* This structure holds lists of chunks as we are assembling for
* transmission.
*/
struct SCTP_packet {
/* These are the SCTP header values (host order) for the packet. */
__u16 source_port;
__u16 destination_port;
__u32 vtag;
/* This contains the payload chunks. */
struct sk_buff_head chunks;
/* This is the total size of all chunks INCLUDING padding. */
size_t size;
/* The packet is destined for this transport address.
* The function we finally use to pass down to the next lower
* layer lives in the transport structure.
*/
sctp_transport_t *transport;
/* Allow a callback for getting a high priority chunk
* bundled early into the packet (This is used for ECNE).
*/
sctp_packet_phandler_t *get_prepend_chunk;
/* This packet should advertise ECN capability to the network
* via the ECT bit.
*/
int ecn_capable;
/* This packet contains a COOKIE-ECHO chunk. */
int has_cookie_echo;
int malloced;
};
typedef int (sctp_outqueue_thandler_t)(sctp_outqueue_t *, void *);
typedef int (sctp_outqueue_ehandler_t)(sctp_outqueue_t *);
typedef sctp_packet_t *(sctp_outqueue_ohandler_init_t)
(sctp_packet_t *,
sctp_transport_t *,
__u16 sport,
__u16 dport);
typedef sctp_packet_t *(sctp_outqueue_ohandler_config_t)
(sctp_packet_t *,
__u32 vtag,
int ecn_capable,
sctp_packet_phandler_t *get_prepend_chunk);
typedef sctp_xmit_t (sctp_outqueue_ohandler_t)(sctp_packet_t *,
sctp_chunk_t *);
typedef int (sctp_outqueue_ohandler_force_t)(sctp_packet_t *);
sctp_outqueue_ohandler_init_t sctp_packet_init;
sctp_outqueue_ohandler_config_t sctp_packet_config;
sctp_outqueue_ohandler_t sctp_packet_append_chunk;
sctp_outqueue_ohandler_t sctp_packet_transmit_chunk;
sctp_outqueue_ohandler_force_t sctp_packet_transmit;
void sctp_packet_free(sctp_packet_t *);
/* This represents a remote transport address.
* For local transport addresses, we just use sockaddr_storage_t.
*
* RFC2960 Section 1.4 Key Terms
*
* o Transport address: A Transport Address is traditionally defined
* by Network Layer address, Transport Layer protocol and Transport
* Layer port number. In the case of SCTP running over IP, a
* transport address is defined by the combination of an IP address
* and an SCTP port number (where SCTP is the Transport protocol).
*
* RFC2960 Section 7.1 SCTP Differences from TCP Congestion control
*
* o The sender keeps a separate congestion control parameter set for
* each of the destination addresses it can send to (not each
* source-destination pair but for each destination). The parameters
* should decay if the address is not used for a long enough time
* period.
*
*/
struct SCTP_transport {
/* A list of transports. */
list_t transports;
/* Reference counting. */
atomic_t refcnt;
int dead;
/* This is the peer's IP address and port. */
sockaddr_storage_t ipaddr;
/* These are the functions we call to handle LLP stuff. */
sctp_func_t *af_specific;
/* Which association do we belong to? */
sctp_association_t *asoc;
/* RFC2960
*
* 12.3 Per Transport Address Data
*
* For each destination transport address in the peer's
* address list derived from the INIT or INIT ACK chunk, a
* number of data elements needs to be maintained including:
*/
__u32 rtt; /* This is the most recent RTT. */
/* RTO : The current retransmission timeout value. */
__u32 rto;
/* RTTVAR : The current RTT variation. */
__u32 rttvar;
/* SRTT : The current smoothed round trip time. */
__u32 srtt;
/* RTO-Pending : A flag used to track if one of the DATA
* chunks sent to this address is currently being
* used to compute a RTT. If this flag is 0,
* the next DATA chunk sent to this destination
* should be used to compute a RTT and this flag
* should be set. Every time the RTT
* calculation completes (i.e. the DATA chunk
* is SACK'd) clear this flag.
*/
int rto_pending;
/*
* These are the congestion stats.
*/
/* cwnd : The current congestion window. */
__u32 cwnd; /* This is the actual cwnd. */
/* ssthresh : The current slow start threshold value. */
__u32 ssthresh;
/* partial : The tracking method for increase of cwnd when in
* bytes acked : congestion avoidance mode (see Section 6.2.2)
*/
__u32 partial_bytes_acked;
/* Data that has been sent, but not acknowledged. */
__u32 flight_size;
/* PMTU : The current known path MTU. */
__u32 pmtu;
/* When was the last time(in jiffies) that a data packet was sent on
* this transport? This is used to adjust the cwnd when the transport
* becomes inactive.
*/
unsigned long last_time_used;
/* Heartbeat interval: The endpoint sends out a Heartbeat chunk to
* the destination address every heartbeat interval.
*/
int hb_interval;
/* When was the last time (in jiffies) that we heard from this
* transport? We use this to pick new active and retran paths.
*/
unsigned long last_time_heard;
/* Last time(in jiffies) when cwnd is reduced due to the congestion
* indication based on ECNE chunk.
*/
unsigned long last_time_ecne_reduced;
/* state : The current state of this destination,
* : i.e. DOWN, UP, ALLOW-HB, NO-HEARTBEAT, etc.
*/
struct {
int active;
int hb_allowed;
} state;
/* These are the error stats for this destination. */
/* Error count : The current error count for this destination. */
unsigned short error_count;
/* Error : Current error threshold for this destination
* Threshold : i.e. what value marks the destination down if
* : errorCount reaches this value.
*/
unsigned short error_threshold;
/* This is the max_retrans value for the transport and will
* be initialized to proto.max_retrans.path. This can be changed
* using SCTP_SET_PEER_ADDR_PARAMS socket option.
*/
int max_retrans;
/* We use this name for debugging output... */
char *debug_name;
/* Per : A timer used by each destination.
* Destination :
* Timer :
*
* [Everywhere else in the text this is called T3-rtx. -ed]
*/
struct timer_list T3_rtx_timer;
/* Heartbeat timer is per destination. */
struct timer_list hb_timer;
/* Since we're using per-destination retransmission timers
* (see above), we're also using per-destination "transmitted"
* queues. This probably ought to be a private struct
* accessible only within the outqueue, but it's not, yet.
*/
struct list_head transmitted;
/* We build bundle-able packets for this transport here. */
sctp_packet_t packet;
/* This is the list of transports that have chunks to send. */
struct list_head send_ready;
int malloced; /* Is this structure kfree()able? */
};
extern sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *, int);
extern sctp_transport_t *sctp_transport_init(sctp_transport_t *,
const sockaddr_storage_t *, int);
extern void sctp_transport_set_owner(sctp_transport_t *, sctp_association_t *);
extern void sctp_transport_free(sctp_transport_t *);
extern void sctp_transport_destroy(sctp_transport_t *);
extern void sctp_transport_reset_timers(sctp_transport_t *);
extern void sctp_transport_hold(sctp_transport_t *);
extern void sctp_transport_put(sctp_transport_t *);
extern void sctp_transport_update_rto(sctp_transport_t *, __u32);
extern void sctp_transport_raise_cwnd(sctp_transport_t *, __u32, __u32);
extern void sctp_transport_lower_cwnd(sctp_transport_t *, sctp_lower_cwnd_t);
/* This is the structure we use to queue packets as they come into
* SCTP. We write packets to it and read chunks from it. It handles
* fragment reassembly and chunk unbundling.
*/
struct SCTP_inqueue {
/* This is actually a queue of sctp_chunk_t each
* containing a partially decoded packet.
*/
struct sk_buff_head in;
/* This is the packet which is currently off the in queue and is
* being worked on through the inbound chunk processing.
*/
sctp_chunk_t *in_progress;
/* This is the delayed task to finish delivering inbound
* messages.
*/
struct tq_struct immediate;
int malloced; /* Is this structure kfree()able? */
};
sctp_inqueue_t *sctp_inqueue_new(void);
void sctp_inqueue_init(sctp_inqueue_t *);
void sctp_inqueue_free(sctp_inqueue_t *);
void sctp_push_inqueue(sctp_inqueue_t *, sctp_chunk_t *packet);
sctp_chunk_t *sctp_pop_inqueue(sctp_inqueue_t *);
void sctp_inqueue_set_th_handler(sctp_inqueue_t *,
void (*)(void *), void *);
/* This is the structure we use to hold outbound chunks. You push
* chunks in and they automatically pop out the other end as bundled
* packets (it calls (*output_handler)()).
*
* This structure covers sections 6.3, 6.4, 6.7, 6.8, 6.10, 7., 8.1,
* and 8.2 of the v13 draft.
*
* It handles retransmissions. The connection to the timeout portion
* of the state machine is through sctp_..._timeout() and timeout_handler.
*
* If you feed it SACKs, it will eat them.
*
* If you give it big chunks, it will fragment them.
*
* It assigns TSN's to data chunks. This happens at the last possible
* instant before transmission.
*
* When free()'d, it empties itself out via output_handler().
*/
struct SCTP_outqueue {
sctp_association_t *asoc;
/* BUG: This really should be an array of streams.
* This really holds a list of chunks (one stream).
* FIXME: If true, why so?
*/
struct sk_buff_head out;
/* These are control chunks we want to send. */
struct sk_buff_head control;
/* These are chunks that have been sacked but are above the
* CTSN, or cumulative tsn ack point.
*/
struct list_head sacked;
/* Put chunks on this list to schedule them for
* retransmission.
*/
struct list_head retransmit;
/* Call these functions to send chunks down to the next lower
* layer. This is always SCTP_packet, but we separate the two
* structures to make testing simpler.
*/
sctp_outqueue_ohandler_init_t *init_output;
sctp_outqueue_ohandler_config_t *config_output;
sctp_outqueue_ohandler_t *append_output;
sctp_outqueue_ohandler_t *build_output;
sctp_outqueue_ohandler_force_t *force_output;
/* How many unackd bytes do we have in-flight? */
__u32 outstanding_bytes;
/* Is this structure empty? */
int empty;
/* Are we kfree()able? */
int malloced;
};
sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *);
void sctp_outqueue_init(sctp_association_t *, sctp_outqueue_t *);
void sctp_outqueue_teardown(sctp_outqueue_t *);
void sctp_outqueue_free(sctp_outqueue_t*);
void sctp_force_outqueue(sctp_outqueue_t *);
int sctp_push_outqueue(sctp_outqueue_t *, sctp_chunk_t *chunk);
int sctp_flush_outqueue(sctp_outqueue_t *, int);
int sctp_sack_outqueue(sctp_outqueue_t *, sctp_sackhdr_t *);
int sctp_outqueue_is_empty(const sctp_outqueue_t *);
int sctp_outqueue_set_output_handlers(sctp_outqueue_t *,
sctp_outqueue_ohandler_init_t init,
sctp_outqueue_ohandler_config_t config,
sctp_outqueue_ohandler_t append,
sctp_outqueue_ohandler_t build,
sctp_outqueue_ohandler_force_t force);
void sctp_outqueue_restart(sctp_outqueue_t *);
void sctp_retransmit(sctp_outqueue_t *, sctp_transport_t *, __u8);
/* These bind address data fields common between endpoints and associations */
struct SCTP_bind_addr {
/* RFC 2960 12.1 Parameters necessary for the SCTP instance
*
* SCTP Port: The local SCTP port number the endpoint is
* bound to.
*/
__u16 port;
/* RFC 2960 12.1 Parameters necessary for the SCTP instance
*
* Address List: The list of IP addresses that this instance
* has bound. This information is passed to one's
* peer(s) in INIT and INIT ACK chunks.
*/
list_t address_list;
int malloced; /* Are we kfree()able? */
};
sctp_bind_addr_t *sctp_bind_addr_new(int gfp_mask);
void sctp_bind_addr_init(sctp_bind_addr_t *, __u16 port);
void sctp_bind_addr_free(sctp_bind_addr_t *);
int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src,
sctp_scope_t scope, int priority,int flags);
int sctp_add_bind_addr(sctp_bind_addr_t *, sockaddr_storage_t *,
int priority);
int sctp_del_bind_addr(sctp_bind_addr_t *, sockaddr_storage_t *);
int sctp_bind_addr_has_addr(sctp_bind_addr_t *, const sockaddr_storage_t *);
sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
int *addrs_len,
int priority);
int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp,
__u8 *raw_addr_list,
int addrs_len,
unsigned short port,
int priority);
sctp_scope_t sctp_scope(const sockaddr_storage_t *);
int sctp_in_scope(const sockaddr_storage_t *addr, const sctp_scope_t scope);
int sctp_is_any(const sockaddr_storage_t *addr);
int sctp_addr_is_valid(const sockaddr_storage_t *addr);
/* What type of sctp_endpoint_common? */
typedef enum {
SCTP_EP_TYPE_SOCKET,
SCTP_EP_TYPE_ASSOCIATION,
} sctp_endpoint_type_t;
/*
* A common base class to bridge the implmentation view of a
* socket (usually listening) endpoint versus an association's
* local endpoint.
* This common structure is useful for several purposes:
* 1) Common interface for lookup routines.
* a) Subfunctions work for either endpoint or association
* b) Single interface to lookup allows hiding the lookup lock rather
* than acquiring it externally.
* 2) Common interface for the inbound chunk handling/state machine.
* 3) Common object handling routines for reference counting, etc.
* 4) Disentangle association lookup from endpoint lookup, where we
* do not have to find our endpoint to find our association.
*
*/
struct sctp_endpoint_common {
/* Fields to help us manage our entries in the hash tables. */
sctp_endpoint_common_t *next;
sctp_endpoint_common_t **pprev;
int hashent;
/* Runtime type information. What kind of endpoint is this? */
sctp_endpoint_type_t type;
/* Some fields to help us manage this object.
* refcnt - Reference count access to this object.
* dead - Do not attempt to use this object.
* malloced - Do we need to kfree this object?
*/
atomic_t refcnt;
char dead;
char malloced;
/* What socket does this endpoint belong to? */
struct sock *sk;
/* This is where we receive inbound chunks. */
sctp_inqueue_t inqueue;
/* This substructure includes the defining parameters of the
* endpoint:
* bind_addr.port is our shared port number.
* bind_addr.address_list is our set of local IP addresses.
*/
sctp_bind_addr_t bind_addr;
/* Protection during address list comparisons. */
rwlock_t addr_lock;
};
/* RFC Section 1.4 Key Terms
*
* o SCTP endpoint: The logical sender/receiver of SCTP packets. On a
* multi-homed host, an SCTP endpoint is represented to its peers as a
* combination of a set of eligible destination transport addresses to
* which SCTP packets can be sent and a set of eligible source
* transport addresses from which SCTP packets can be received.
* All transport addresses used by an SCTP endpoint must use the
* same port number, but can use multiple IP addresses. A transport
* address used by an SCTP endpoint must not be used by another
* SCTP endpoint. In other words, a transport address is unique
* to an SCTP endpoint.
*
* From an implementation perspective, each socket has one of these.
* A TCP-style socket will have exactly one association on one of
* these. An UDP-style socket will have multiple associations hanging
* off one of these.
*/
struct SCTP_endpoint {
/* Common substructure for endpoint and association. */
sctp_endpoint_common_t base;
/* These are the system-wide defaults and other stuff which is
* endpoint-independent.
*/
sctp_protocol_t *proto;
/* Associations: A list of current associations and mappings
* to the data consumers for each association. This
* may be in the form of a hash table or other
* implementation dependent structure. The data
* consumers may be process identification
* information such as file descriptors, named pipe
* pointer, or table pointers dependent on how SCTP
* is implemented.
*/
/* This is really a list of sctp_association_t entries. */
list_t asocs;
/* Secret Key: A secret key used by this endpoint to compute
* the MAC. This SHOULD be a cryptographic quality
* random number with a sufficient length.
* Discussion in [RFC1750] can be helpful in
* selection of the key.
*/
__u8 secret_key[SCTP_HOW_MANY_SECRETS][SCTP_SECRET_SIZE];
int current_key;
int last_key;
int key_changed_at;
/* Default timeouts. */
int timeouts[SCTP_NUM_TIMEOUT_TYPES];
/* Various thresholds. */
/* Name for debugging output... */
char *debug_name;
};
/* Recover the outter endpoint structure. */
static inline sctp_endpoint_t *sctp_ep(sctp_endpoint_common_t *base)
{
sctp_endpoint_t *ep;
/* We are not really a list, but the list_entry() macro is
* really quite generic to find the address of an outter struct.
*/
ep = list_entry(base, sctp_endpoint_t, base);
return ep;
}
/* These are function signatures for manipulating endpoints. */
sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *, struct sock *, int);
sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *, sctp_protocol_t *,
struct sock *, int priority);
void sctp_endpoint_free(sctp_endpoint_t *);
void sctp_endpoint_put(sctp_endpoint_t *);
void sctp_endpoint_hold(sctp_endpoint_t *);
void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc);
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
const sockaddr_storage_t *paddr,
sctp_transport_t **);
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const sockaddr_storage_t *);
void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
const sockaddr_storage_t *peer_addr,
sctp_init_chunk_t *peer_init, int priority);
int sctp_process_param(sctp_association_t *asoc,
sctpParam_t param,
const sockaddr_storage_t *peer_addr,
sctp_cid_t cid, int priority);
__u32 sctp_generate_tag(const sctp_endpoint_t *ep);
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep);
/* RFC2960
*
* 12. Recommended Transmission Control Block (TCB) Parameters
*
* This section details a recommended set of parameters that should
* be contained within the TCB for an implementation. This section is
* for illustrative purposes and should not be deemed as requirements
* on an implementation or as an exhaustive list of all parameters
* inside an SCTP TCB. Each implementation may need its own additional
* parameters for optimization.
*/
/* Here we have information about each individual association. */
struct SCTP_association {
/* A base structure common to endpoint and association.
* In this context, it represents the associations's view
* of the local endpoint of the association.
*/
sctp_endpoint_common_t base;
/* Associations on the same socket. */
list_t asocs;
/* This is a signature that lets us know that this is a
* sctp_association_t data structure. Used for mapping an
* association id to an association.
*/
__u32 eyecatcher;
/* This is our parent endpoint. */
sctp_endpoint_t *ep;
/* These are those association elements needed in the cookie. */
sctp_cookie_t c;
/* This is all information about our peer. */
struct {
/* rwnd
*
* Peer Rwnd : Current calculated value of the peer's rwnd.
*/
__u32 rwnd;
/* transport_addr_list
*
* Peer : A list of SCTP transport addresses that the
* Transport : peer is bound to. This information is derived
* Address : from the INIT or INIT ACK and is used to
* List : associate an inbound packet with a given
* : association. Normally this information is
* : hashed or keyed for quick lookup and access
* : of the TCB.
*
* It is a list of SCTP_transport's.
*/
list_t transport_addr_list;
/* port
* The transport layer port number.
*/
__u16 port;
/* primary_path
*
* Primary : This is the current primary destination
* Path : transport address of the peer endpoint. It
* : may also specify a source transport address
* : on this endpoint.
*
* All of these paths live on transport_addr_list.
*
* At the bakeoffs, we discovered that the intent of
* primaryPath is that it only changes when the ULP
* asks to have it changed. We add the activePath to
* designate the connection we are currently using to
* transmit new data and most control chunks.
*/
sctp_transport_t *primary_path;
/* active_path
* The path that we are currently using to
* transmit new data and most control chunks.
*/
sctp_transport_t *active_path;
/* retran_path
*
* RFC2960 6.4 Multi-homed SCTP Endpoints
* ...
* Furthermore, when its peer is multi-homed, an
* endpoint SHOULD try to retransmit a chunk to an
* active destination transport address that is
* different from the last destination address to
* which the DATA chunk was sent.
*/
sctp_transport_t *retran_path;
/* Pointer to last transport I have sent on. */
sctp_transport_t *last_sent_to;
/* This is the last transport I have recieved DATA on. */
sctp_transport_t *last_data_from;
/*
* Mapping An array of bits or bytes indicating which out of
* Array order TSN's have been received (relative to the
* Last Rcvd TSN). If no gaps exist, i.e. no out of
* order packets have been received, this array
* will be set to all zero. This structure may be
* in the form of a circular buffer or bit array.
*
* Last Rcvd : This is the last TSN received in
* TSN : sequence. This value is set initially by
* : taking the peer's Initial TSN, received in
* : the INIT or INIT ACK chunk, and subtracting
* : one from it.
*
* Throughout most of the specification this is called the
* "Cumulative TSN ACK Point". In this case, we
* ignore the advice in 12.2 in favour of the term
* used in the bulk of the text. This value is hidden
* in tsn_map--we get it by calling sctp_tsnmap_get_ctsn().
*/
sctp_tsnmap_t tsn_map;
__u8 _map[sctp_tsnmap_storage_size(SCTP_TSN_MAP_SIZE)];
/* We record duplicate TSNs here. We clear this after
* every SACK.
* FIXME: We should move this into the tsnmap? --jgrimm
*/
sctp_dup_tsn_t dup_tsns[SCTP_MAX_DUP_TSNS];
int next_dup_tsn;
/* Do we need to sack the peer? */
int sack_needed;
/* These are capabilities which our peer advertised. */
__u8 ecn_capable; /* Can peer do ECN? */
__u8 ipv4_address; /* Peer understands IPv4 addresses? */
__u8 ipv6_address; /* Peer understands IPv6 addresses? */
__u8 hostname_address;/* Peer understands DNS addresses? */
sctp_inithdr_t i;
int cookie_len;
void *cookie;
/* ADDIP Extention (ADDIP) --xguo */
/* <expected peer-serial-number> minus 1 (ADDIP sec. 4.2 C1) */
__u32 addip_serial;
} peer;
/* State : A state variable indicating what state the
* : association is in, i.e. COOKIE-WAIT,
* : COOKIE-ECHOED, ESTABLISHED, SHUTDOWN-PENDING,
* : SHUTDOWN-SENT, SHUTDOWN-RECEIVED, SHUTDOWN-ACK-SENT.
*
* Note: No "CLOSED" state is illustrated since if a
* association is "CLOSED" its TCB SHOULD be removed.
*
* In this implementation we DO have a CLOSED
* state which is used during initiation and shutdown.
*
* State takes values from SCTP_STATE_*.
*/
sctp_state_t state;
/* When did we enter this state? */
int state_timestamp;
/* The cookie life I award for any cookie. */
struct timeval cookie_life;
__u32 cookie_preserve;
/* Overall : The overall association error count.
* Error Count : [Clear this any time I get something.]
*/
int overall_error_count;
/* Overall : The threshold for this association that if
* Error : the Overall Error Count reaches will cause
* Threshold : this association to be torn down.
*/
int overall_error_threshold;
/* These are the association's initial, max, and min RTO values.
* These values will be initialized by system defaults, but can
* be modified via the SCTP_RTOINFO socket option.
*/
__u32 rto_initial;
__u32 rto_max;
__u32 rto_min;
/* Maximum number of new data packets that can be sent in a burst. */
int max_burst;
/* This is the max_retrans value for the association. This value will
* be initialized initialized from system defaults, but can be
* modified by the SCTP_ASSOCINFO socket option.
*/
int max_retrans;
/* Maximum number of times the endpoint will retransmit INIT */
__u16 max_init_attempts;
/* How many times have we resent an INIT? */
__u16 init_retries;
/* The largest timeout or RTO value to use in attempting an INIT */
__u16 max_init_timeo;
int timeouts[SCTP_NUM_TIMEOUT_TYPES];
struct timer_list timers[SCTP_NUM_TIMEOUT_TYPES];
/* Transport to which SHUTDOWN chunk was last sent. */
sctp_transport_t *shutdown_last_sent_to;
/* Next TSN : The next TSN number to be assigned to a new
* : DATA chunk. This is sent in the INIT or INIT
* : ACK chunk to the peer and incremented each
* : time a DATA chunk is assigned a TSN
* : (normally just prior to transmit or during
* : fragmentation).
*/
__u32 next_tsn;
/*
* Last Rcvd : This is the last TSN received in sequence. This value
* TSN : is set initially by taking the peer's Initial TSN,
* : received in the INIT or INIT ACK chunk, and
* : subtracting one from it.
*
* Most of RFC 2960 refers to this as the Cumulative TSN Ack Point.
*/
__u32 ctsn_ack_point;
/* The number of unacknowledged data chunks. Reported through
* the SCTP_STATUS sockopt.
*/
__u16 unack_data;
/* This is the association's receive buffer space. This value is used
* to set a_rwnd field in an INIT or a SACK chunk.
*/
__u32 rwnd;
/* Number of bytes by which the rwnd has slopped. The rwnd is allowed
* to slop over a maximum of the association's frag_point.
*/
__u32 rwnd_over;
/* This is the sndbuf size in use for the association.
* This corresponds to the sndbuf size for the association,
* as specified in the sk->sndbuf.
*/
int sndbuf_used;
/* This is the wait queue head for send requests waiting on
* the association sndbuf space.
*/
wait_queue_head_t wait;
/* Association : The smallest PMTU discovered for all of the
* PMTU : peer's transport addresses.
*/
__u32 pmtu;
/* The message size at which SCTP fragmentation will occur. */
__u32 frag_point;
/* Ack State : This flag indicates if the next received
* : packet is to be responded to with a
* : SACK. This is initializedto 0. When a packet
* : is received it is incremented. If this value
* : reaches 2 or more, a SACK is sent and the
* : value is reset to 0. Note: This is used only
* : when no DATA chunks are received out of
* : order. When DATA chunks are out of order,
* : SACK's are not delayed (see Section 6).
*/
/* Do we need to send an ack?
* When counters[SctpCounterAckState] is above 1 we do!
*/
int counters[SCTP_NUMBER_COUNTERS];
struct {
__u16 stream;
__u32 ppid;
} defaults;
/* This tracks outbound ssn for a given stream. */
__u16 ssn[SCTP_MAX_STREAM];
/* All outbound chunks go through this structure. */
sctp_outqueue_t outqueue;
/* A smart pipe that will handle reordering and fragmentation,
* as well as handle passing events up to the ULP.
* In the future, we should make this at least dynamic, if
* not also some sparse structure.
*/
sctp_ulpqueue_t ulpq;
__u8 _ssnmap[sctp_ulpqueue_storage_size(SCTP_MAX_STREAM)];
/* Need to send an ECNE Chunk? */
int need_ecne;
/* Last TSN that caused an ECNE Chunk to be sent. */
__u32 last_ecne_tsn;
/* Last TSN that caused a CWR Chunk to be sent. */
__u32 last_cwr_tsn;
/* How many duplicated TSNs have we seen? */
int numduptsns;
/* Number of seconds of idle time before an association is closed. */
__u32 autoclose;
/* Name for debugging output... */
char *debug_name;
/* These are to support
* "SCTP Extensions for Dynamic Reconfiguration of IP Addresses
* and Enforcement of Flow and Message Limits"
* <draft-ietf-tsvwg-addip-sctp-02.txt>
* or "ADDIP" for short.
*/
/* Is the ADDIP extension enabled for this association? */
int addip_enable;
/* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
*
* R1) One and only one ASCONF Chunk MAY be in transit and
* unacknowledged at any one time. If a sender, after sending
* an ASCONF chunk, decides it needs to transfer another
* ASCONF Chunk, it MUST wait until the ASCONF-ACK Chunk
* returns from the previous ASCONF Chunk before sending a
* subsequent ASCONF. Note this restriction binds each side,
* so at any time two ASCONF may be in-transit on any given
* association (one sent from each endpoint).
*
* [This is our one-and-only-one ASCONF in flight. If we do
* not have an ASCONF in flight, this is NULL.]
*/
sctp_chunk_t *addip_last_asconf;
/* ADDIP Section 4.2 Upon reception of an ASCONF Chunk.
*
* IMPLEMENTATION NOTE: As an optimization a receiver may wish
* to save the last ASCONF-ACK for some predetermined period
* of time and instead of re-processing the ASCONF (with the
* same serial number) it may just re-transmit the
* ASCONF-ACK. It may wish to use the arrival of a new serial
* number to discard the previously saved ASCONF-ACK or any
* other means it may choose to expire the saved ASCONF-ACK.
*
* [This is our saved ASCONF-ACK. We invalidate it when a new
* ASCONF serial number arrives.]
*/
sctp_chunk_t *addip_last_asconf_ack;
/* These ASCONF chunks are waiting to be sent.
*
* These chunaks can't be pushed to outqueue until receiving
* ASCONF_ACK for the previous ASCONF indicated by
* addip_last_asconf, so as to guarantee that only one ASCONF
* is in flight at any time.
*
* ADDIP Section 4.1.1 Congestion Control of ASCONF Chunks
*
* In defining the ASCONF Chunk transfer procedures, it is
* essential that these transfers MUST NOT cause congestion
* within the network. To achieve this, we place these
* restrictions on the transfer of ASCONF Chunks:
*
* R1) One and only one ASCONF Chunk MAY be in transit and
* unacknowledged at any one time. If a sender, after sending
* an ASCONF chunk, decides it needs to transfer another
* ASCONF Chunk, it MUST wait until the ASCONF-ACK Chunk
* returns from the previous ASCONF Chunk before sending a
* subsequent ASCONF. Note this restriction binds each side,
* so at any time two ASCONF may be in-transit on any given
* association (one sent from each endpoint).
*
*
* [I really think this is EXACTLY the sort of intelligence
* which already resides in SCTP_outqueue. Please move this
* queue and its supporting logic down there. --piggy]
*/
struct sk_buff_head addip_chunks;
/* ADDIP Section 4.1 ASCONF Chunk Procedures
*
* A2) A serial number should be assigned to the Chunk. The
* serial number should be a monotonically increasing
* number. All serial numbers are defined to be initialized at
* the start of the association to the same value as the
* Initial TSN.
*
* [and]
*
* ADDIP
* 3.1.1 Address/Stream Configuration Change Chunk (ASCONF)
*
* Serial Number : 32 bits (unsigned integer)
*
* This value represents a Serial Number for the ASCONF
* Chunk. The valid range of Serial Number is from 0 to
* 4294967295 (2**32 - 1). Serial Numbers wrap back to 0
* after reaching 4294967295.
*/
__u32 addip_serial;
};
/* An eyecatcher for determining if we are really looking at an
* association data structure.
*/
enum {
SCTP_ASSOC_EYECATCHER = 0xa550c123,
};
/* Recover the outter association structure. */
static inline sctp_association_t *sctp_assoc(sctp_endpoint_common_t *base)
{
sctp_association_t *asoc;
/* We are not really a list, but the list_entry() macro is
* really quite generic find the address of an outter struct.
*/
asoc = list_entry(base, sctp_association_t, base);
return asoc;
}
/* These are function signatures for manipulating associations. */
sctp_association_t *
sctp_association_new(const sctp_endpoint_t *, const struct sock *,
sctp_scope_t scope, int priority);
sctp_association_t *
sctp_association_init(sctp_association_t *, const sctp_endpoint_t *,
const struct sock *, sctp_scope_t scope,
int priority);
void sctp_association_free(sctp_association_t *);
void sctp_association_put(sctp_association_t *);
void sctp_association_hold(sctp_association_t *);
sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *);
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *,
const sockaddr_storage_t *);
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *,
const sockaddr_storage_t *address,
const int priority);
void sctp_assoc_control_transport(sctp_association_t *, sctp_transport_t *,
sctp_transport_cmd_t, sctp_sn_error_t);
sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *, __u32);
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *,
const sockaddr_storage_t *,
const sockaddr_storage_t *);
void sctp_assoc_migrate(sctp_association_t *, struct sock *);
void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src);
__u32 __sctp_association_get_next_tsn(sctp_association_t *);
__u32 __sctp_association_get_tsn_block(sctp_association_t *, int);
__u16 __sctp_association_get_next_ssn(sctp_association_t *, __u16 sid);
int sctp_cmp_addr(const sockaddr_storage_t *ss1,
const sockaddr_storage_t *ss2);
int sctp_cmp_addr_exact(const sockaddr_storage_t *ss1,
const sockaddr_storage_t *ss2);
sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc);
sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc);
/* A convenience structure to parse out SCTP specific CMSGs. */
typedef struct sctp_cmsgs {
struct sctp_initmsg *init;
struct sctp_sndrcvinfo *info;
} sctp_cmsgs_t;
/* Structure for tracking memory objects */
typedef struct {
char *label;
atomic_t *counter;
} sctp_dbg_objcnt_entry_t;
#endif /* __sctp_structs_h__ */
/* SCTP kernel reference Implementation Copyright (C) 1999-2001
* Cisco, Motorola, Intel, and International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/net/sctp/sctp_tsnmap.h,v 1.8 2002/07/16 14:51:58 jgrimm Exp $
*
* These are the definitions needed for the tsnmap type. The tsnmap is used
* to track out of order TSNs received.
*
* 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 one of the
* following email addresses:
*
* Jon Grimm <jgrimm@us.ibm.com>
* 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_constants.h>
#ifndef __sctp_tsnmap_h__
#define __sctp_tsnmap_h__
/* RFC 2960 12.2 Parameters necessary per association (i.e. the TCB)
* Mapping An array of bits or bytes indicating which out of
* Array order TSN's have been received (relative to the
* Last Rcvd TSN). If no gaps exist, i.e. no out of
* order packets have been received, this array
* will be set to all zero. This structure may be
* in the form of a circular buffer or bit array.
*/
typedef struct sctp_tsnmap {
/* This array counts the number of chunks with each TSN.
* It points at one of the two buffers with which we will
* ping-pong between.
*/
__u8 *tsn_map;
/* This marks the tsn which overflows the tsn_map, when the
* cumulative ack point reaches this point we know we can switch
* maps (tsn_map and overflow_map swap).
*/
__u32 overflow_tsn;
/* This is the overflow array for tsn_map.
* It points at one of the other ping-pong buffers.
*/
__u8 *overflow_map;
/* This is the TSN at tsn_map[0]. */
__u32 base_tsn;
/* Last Rcvd : This is the last TSN received in
* TSN : sequence. This value is set initially by
* : taking the peer's Initial TSN, received in
* : the INIT or INIT ACK chunk, and subtracting
* : one from it.
*
* Throughout most of the specification this is called the
* "Cumulative TSN ACK Point". In this case, we
* ignore the advice in 12.2 in favour of the term
* used in the bulk of the text.
*/
__u32 cumulative_tsn_ack_point;
/* This is the minimum number of TSNs we can track. This corresponds
* to the size of tsn_map. Note: the overflow_map allows us to
* potentially track more than this quantity.
*/
__u16 len;
/* This is the highest TSN we've marked. */
__u32 max_tsn_seen;
/* No. of data chunks pending receipt. used by SCTP_STATUS sockopt */
__u16 pending_data;
int malloced;
__u8 raw_map[0];
} sctp_tsnmap_t;
typedef struct sctp_tsnmap_iter {
__u32 start;
} sctp_tsnmap_iter_t;
/* Create a new tsnmap. */
sctp_tsnmap_t *sctp_tsnmap_new(__u16 len, __u32 initial_tsn,
int priority);
/* Dispose of a tsnmap. */
void sctp_tsnmap_free(sctp_tsnmap_t *map);
/* This macro assists in creation of external storage for variable length
* internal buffers. We double allocate so the overflow map works.
*/
#define sctp_tsnmap_storage_size(count) (sizeof(__u8) * (count) * 2)
/* Initialize a block of memory as a tsnmap. */
sctp_tsnmap_t *sctp_tsnmap_init(sctp_tsnmap_t *map, __u16 len, __u32 initial_tsn);
/* 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 sctp_tsnmap_t *map, __u32 tsn);
/* Mark this TSN as seen. */
void sctp_tsnmap_mark(sctp_tsnmap_t *map, __u32 tsn);
/* Retrieve the Cumulative TSN ACK Point. */
__u32 sctp_tsnmap_get_ctsn(const sctp_tsnmap_t *map);
/* Retrieve the highest TSN we've seen. */
__u32 sctp_tsnmap_get_max_tsn_seen(const sctp_tsnmap_t *map);
/* Is there a gap in the TSN map? */
int sctp_tsnmap_has_gap(const sctp_tsnmap_t *map);
/* Initialize a gap ack block interator from user-provided memory. */
void sctp_tsnmap_iter_init(const sctp_tsnmap_t *map, sctp_tsnmap_iter_t *iter);
/* Get the next gap ack blocks. We return 0 if there are no more
* gap ack blocks.
*/
int sctp_tsnmap_next_gap_ack(const sctp_tsnmap_t *map, sctp_tsnmap_iter_t *iter,
__u16 *start, __u16 *end);
#endif /* __sctp_tsnmap_h__ */
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/net/sctp/sctp_ulpevent.h,v 1.5 2002/07/12 14:50:25 jgrimm Exp $
*
* These are the definitions needed for the sctp_ulpevent type. The
* sctp_ulpevent type is used to carry information from the state machine
* upwards to the ULP.
*
* 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 one of the
* following email addresses:
*
* Jon Grimm <jgrimm@us.ibm.com>
* 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.
*/
#ifndef __sctp_ulpevent_h__
#define __sctp_ulpevent_h__
/* A structure to carry information to the ULP (e.g. Sockets API) */
/* Warning: This sits inside an skb.cb[] area. Be very careful of
* growing this structure as it is at the maximum limit now.
*/
typedef struct sctp_ulpevent {
int malloced;
sctp_association_t *asoc;
struct sk_buff *parent;
struct sctp_sndrcvinfo sndrcvinfo;
int chunk_flags; /* Temp. until we get a new chunk_t */
int msg_flags;
} sctp_ulpevent_t;
sctp_ulpevent_t *sctp_ulpevent_new(int size, int msg_flags, int priority);
sctp_ulpevent_t *sctp_ulpevent_init(sctp_ulpevent_t *event, struct sk_buff *skb, int msg_flags);
void sctp_ulpevent_free(sctp_ulpevent_t *event);
int sctp_ulpevent_is_notification(const sctp_ulpevent_t *event);
sctp_ulpevent_t *sctp_ulpevent_make_assoc_change(
const struct SCTP_association *asoc,
__u16 flags,
__u16 state,
__u16 error,
__u16 outbound,
__u16 inbound,
int priority);
sctp_ulpevent_t *sctp_ulpevent_make_peer_addr_change(
const struct SCTP_association *asoc,
const struct sockaddr_storage *aaddr,
int flags,
int state,
int error,
int priority);
sctp_ulpevent_t *sctp_ulpevent_make_remote_error(
const struct SCTP_association *asoc,
struct SCTP_chunk *chunk,
__u16 flags,
int priority);
sctp_ulpevent_t *sctp_ulpevent_make_send_failed(
const struct SCTP_association *asoc,
struct SCTP_chunk *chunk,
__u16 flags,
__u32 error,
int priority);
sctp_ulpevent_t *sctp_ulpevent_make_shutdown_event(
const struct SCTP_association *asoc,
__u16 flags,
int priority);
sctp_ulpevent_t *sctp_ulpevent_make_rcvmsg(struct SCTP_association *asoc,
struct SCTP_chunk *chunk,
int priority);
void sctp_ulpevent_read_sndrcvinfo(const sctp_ulpevent_t *event,
struct msghdr *msghdr);
__u16 sctp_ulpevent_get_notification_type(const sctp_ulpevent_t *event);
/* Given an event subscription, is this event enabled? */
static inline int sctp_ulpevent_is_enabled(const sctp_ulpevent_t *event,
const struct sctp_event_subscribe *mask)
{
const char *amask = (const char *) mask;
__u16 sn_type;
int enabled = 1;
if (sctp_ulpevent_is_notification(event)) {
sn_type = sctp_ulpevent_get_notification_type(event);
enabled = amask[sn_type - SCTP_SN_TYPE_BASE];
}
return enabled;
}
#endif /* __sctp_ulpevent_h__ */
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/net/sctp/sctp_ulpqueue.h,v 1.2 2002/07/12 14:50:25 jgrimm Exp $
*
* These are the definitions needed for the sctp_ulpqueue type. The
* sctp_ulpqueue is the interface between the Upper Layer Protocol, or ULP,
* and the core SCTP state machine. This is the component which handles
* reassembly and ordering.
*
* 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 one of the
* following email addresses:
*
* Jon Grimm <jgrimm@us.ibm.com>
* La Monte H.P. Yarroll <piggy@acm.org>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
#ifndef __sctp_ulpqueue_h__
#define __sctp_ulpqueue_h__
/* A structure to carry information to the ULP (e.g. Sockets API) */
typedef struct sctp_ulpqueue {
int malloced;
spinlock_t lock;
sctp_association_t *asoc;
struct sk_buff_head reasm;
struct sk_buff_head lobby;
__u16 ssn[0];
} sctp_ulpqueue_t;
/* This macro assists in creation of external storage for variable length
* internal buffers.
*/
#define sctp_ulpqueue_storage_size(inbound) (sizeof(__u16) * (inbound))
sctp_ulpqueue_t *sctp_ulpqueue_new(sctp_association_t *asoc,
__u16 inbound,
int priority);
sctp_ulpqueue_t *sctp_ulpqueue_init(sctp_ulpqueue_t *ulpq,
sctp_association_t *asoc,
__u16 inbound);
void sctp_ulpqueue_free(sctp_ulpqueue_t *);
/* Add a new DATA chunk for processing. */
int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *,
sctp_chunk_t *chunk,
int priority);
/* Add a new event for propogation to the ULP. */
int sctp_ulpqueue_tail_event(sctp_ulpqueue_t *,
sctp_ulpevent_t *event);
/* Is the ulpqueue empty. */
int sctp_ulpqueue_is_empty(sctp_ulpqueue_t *);
int sctp_ulpqueue_is_data_empty(sctp_ulpqueue_t *);
#endif /* __sctp_ulpqueue_h__ */
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/include/net/sctp/sctp_user.h,v 1.17 2002/07/29 21:04:23 jgrimm Exp $
*
* This header represents the structures and constants needed to support
* the SCTP Extension to the Sockets API.
*
* 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>
* R. Stewart <randall@sctp.chicago.il.us>
* K. Morneau <kmorneau@cisco.com>
* Q. Xie <qxie1@email.mot.com>
* 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/socket.h>
#ifndef __net_sctp_user_h__
#define __net_sctp_user_h__
typedef void * sctp_assoc_t;
/* The following symbols come from the Sockets API Extensions for
* SCTP <draft-ietf-tsvwg-sctpsocket-04.txt>.
*/
enum sctp_optname {
SCTP_RTOINFO,
#define SCTP_RTOINFO SCTP_RTOINFO
SCTP_ASSOCRTXINFO,
#define SCTP_ASSOCRTXINFO SCTP_ASSOCRTXINFO
SCTP_INITMSG,
#define SCTP_INITMSG SCTP_INITMSG
SCTP_AUTO_CLOSE,
#define SCTP_AUTO_CLOSE SCTP_AUTO_CLOSE
SCTP_SET_PRIMARY_ADDR,
#define SCTP_SET_PRIMARY_ADDR SCTP_SET_PRIMARY_ADDR
SCTP_SET_PEER_PRIMARY_ADDR,
#define SCTP_SET_PEER_PRIMARY_ADDR SCTP_SET_PEER_PRIMARY_ADDR
SCTP_SET_ADAPTATION_LAYER,
#define SCTP_SET_ADAPTATION_LAYER SCTP_SET_ADAPTATION_LAYER
SCTP_SET_STREAM_TIMEOUTS,
#define SCTP_SET_STREAM_TIMEOUTS SCTP_SET_STREAM_TIMEOUTS
SCTP_DISABLE_FRAGMENTS,
#define SCTP_DISABLE_FRAGMENTS SCTP_DISABLE_FRAGMENTS
SCTP_SET_PEER_ADDR_PARAMS,
#define SCTP_SET_PEER_ADDR_PARAMS SCTP_SET_PEER_ADDR_PARAMS
SCTP_GET_PEER_ADDR_PARAMS,
#define SCTP_GET_PEER_ADDR_PARAMS SCTP_GET_PEER_ADDR_PARAMS
SCTP_STATUS,
#define SCTP_STATUS SCTP_STATUS
SCTP_GET_PEER_ADDR_INFO,
#define SCTP_GET_PEER_ADDR_INFO SCTP_GET_PEER_ADDR_INFO
SCTP_SET_EVENTS,
#define SCTP_SET_EVENTS SCTP_SET_EVENTS
SCTP_AUTOCLOSE,
#define SCTP_AUTOCLOSE SCTP_AUTOCLOSE
SCTP_SET_DEFAULT_SEND_PARAM,
#define SCTP_SET_DEFAULT_SEND_PARAM SCTP_SET_DEFAULT_SEND_PARAM
SCTP_SOCKOPT_DEBUG_NAME = 42, /* FIXME */
#define SCTP_SOCKOPT_DEBUG_NAME SCTP_SOCKOPT_DEBUG_NAME
SCTP_SOCKOPT_BINDX_ADD, /* BINDX requests for adding addresses. */
#define SCTP_SOCKOPT_BINDX_ADD SCTP_SOCKOPT_BINDX_ADD
SCTP_SOCKOPT_BINDX_REM, /* BINDX requests for removing addresses. */
#define SCTP_SOCKOPT_BINDX_REM SCTP_SOCKOPT_BINDX_REM
SCTP_SOCKOPT_PEELOFF, /* peel off association. */
#define SCTP_SOCKOPT_PEELOFF SCTP_SOCKOPT_PEELOFF
};
/*
* 5.2 SCTP msg_control Structures
*
* A key element of all SCTP-specific socket extensions is the use of
* ancillary data to specify and access SCTP-specific data via the
* struct msghdr's msg_control member used in sendmsg() and recvmsg().
* Fine-grained control over initialization and sending parameters are
* handled with ancillary data.
*
* Each ancillary data item is preceeded by a struct cmsghdr (see
* Section 5.1), which defines the function and purpose of the data
* contained in in the cmsg_data[] member.
*/
/*
* 5.2.1 SCTP Initiation Structure (SCTP_INIT)
*
* This cmsghdr structure provides information for initializing new
* SCTP associations with sendmsg(). The SCTP_INITMSG socket option
* uses this same data structure. This structure is not used for
* recvmsg().
*
* cmsg_level cmsg_type cmsg_data[]
* ------------ ------------ ----------------------
* IPPROTO_SCTP SCTP_INIT struct sctp_initmsg
*
*/
struct sctp_initmsg {
__u16 sinit_num_ostreams;
__u16 sinit_max_instreams;
__u16 sinit_max_attempts;
__u16 sinit_max_init_timeo;
};
/*
* 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
*
* This cmsghdr structure specifies SCTP options for sendmsg() and
* describes SCTP header information about a received message through
* recvmsg().
*
* cmsg_level cmsg_type cmsg_data[]
* ------------ ------------ ----------------------
* IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo
*
*/
struct sctp_sndrcvinfo {
__u16 sinfo_stream;
__u16 sinfo_ssn;
__u16 sinfo_flags;
__u32 sinfo_ppid;
__u32 sinfo_context;
__u32 sinfo_timetolive;
__u32 sinfo_tsn;
sctp_assoc_t sinfo_assoc_id;
};
/*
* 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.
*/
enum sctp_sinfo_flags {
MSG_UNORDERED = 1, /* Send/recieve message unordered. */
MSG_ADDR_OVER = 2, /* Override the primary destination. */
MSG_ABORT=4, /* Send an ABORT message to the peer. */
/* MSG_EOF is already defined per socket.h */
};
typedef union {
__u8 raw;
struct sctp_initmsg init;
struct sctp_sndrcvinfo sndrcv;
} sctp_cmsg_data_t;
/* These are cmsg_types. */
typedef enum sctp_cmsg_type {
SCTP_INIT, /* 5.2.1 SCTP Initiation Structure */
SCTP_SNDRCV, /* 5.2.2 SCTP Header Information Structure */
} sctp_cmsg_t;
/*
* 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 notificaion. The notification information has the
* following format:
*
*/
struct sctp_assoc_change {
__u16 sac_type;
__u16 sac_flags;
__u32 sac_length;
__u16 sac_state;
__u16 sac_error;
__u16 sac_outbound_streams;
__u16 sac_inbound_streams;
sctp_assoc_t sac_assoc_id;
};
/*
* sac_state: 32 bits (signed integer)
*
* This field holds one of a number of values that communicate the
* event that happened to the association. They include:
*
* Note: The following state names deviate from the API draft as
* the names clash too easily with other kernel symbols.
*/
enum sctp_sac_state {
SCTP_COMM_UP,
SCTP_COMM_LOST,
SCTP_RESTART,
SCTP_SHUTDOWN_COMP,
SCTP_CANT_STR_ASSOC,
};
/*
* 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. The information has the
* following structure:
*/
struct sctp_paddr_change {
__u16 spc_type;
__u16 spc_flags;
__u32 spc_length;
struct sockaddr_storage spc_aaddr;
int spc_state;
int spc_error;
sctp_assoc_t spc_assoc_id;
};
/*
* spc_state: 32 bits (signed integer)
*
* This field holds one of a number of values that communicate the
* event that happened to the address. They include:
*/
enum sctp_spc_state {
ADDRESS_AVAILABLE,
ADDRESS_UNREACHABLE,
ADDRESS_REMOVED,
ADDRESS_ADDED,
ADDRESS_MADE_PRIM,
};
/*
* 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. SCTP error TLVs have the format:
*/
struct sctp_remote_error {
__u16 sre_type;
__u16 sre_flags;
__u32 sre_length;
__u16 sre_error;
__u16 sre_len;
sctp_assoc_t sre_assoc_id;
__u8 sre_data[0];
};
/*
* 5.3.1.4 SCTP_SEND_FAILED
*
* If SCTP cannot deliver a message it may return the message as a
* notification.
*/
struct sctp_send_failed {
__u16 ssf_type;
__u16 ssf_flags;
__u32 ssf_length;
__u32 ssf_error;
struct sctp_sndrcvinfo ssf_info;
sctp_assoc_t ssf_assoc_id;
__u8 ssf_data[0];
};
/*
* 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.
*/
enum sctp_ssf_flags {
SCTP_DATA_UNSENT,
SCTP_DATA_SENT,
};
/*
* 5.3.1.5 SCTP_SHUTDOWN_EVENT
*
* When a peer sends a SHUTDOWN, SCTP delivers this notification to
* inform the application that it should cease sending data.
*/
struct sctp_shutdown_event {
__u16 sse_type;
__u16 sse_flags;
__u32 sse_length;
sctp_assoc_t sse_assoc_id;
};
/*
* 5.3.1.6 SCTP_ADAPTION_INDICATION
*
* When a peer sends a Adaption Layer Indication parameter , SCTP
* delivers this notification to inform the application
* that of the peers requested adaption layer.
*/
struct sctp_adaption_event {
__u16 sai_type;
__u16 sai_flags;
__u32 sai_length;
__u32 sai_adaptation_bits;
sctp_assoc_t sse_assoc_id;
};
/*
* 5.3.1.7 SCTP_PARTIAL_DELIVERY_EVENT
*
* When a reciever is engaged in a partial delivery of a
* message this notification will be used to inidicate
* various events.
*/
struct sctp_rcv_pdapi_event {
__u16 pdapi_type;
__u16 pdapi_flags;
__u32 pdapi_length;
__u32 pdapi_indication;
sctp_assoc_t pdapi_assoc_id;
};
/*
* Described in Section 7.3
* Ancillary Data and Notification Interest Options
*/
struct sctp_event_subscribe {
__u8 sctp_data_io_event;
__u8 sctp_association_event;
__u8 sctp_address_event;
__u8 sctp_send_failure_event;
__u8 sctp_peer_error_event;
__u8 sctp_shutdown_event;
__u8 sctp_partial_delivery_event;
__u8 sctp_adaption_layer_event;
};
/*
* 5.3.1 SCTP Notification Structure
*
* The notification structure is defined as the union of all
* notification types.
*
*/
union sctp_notification {
struct {
__u16 sn_type; /* Notification type. */
__u16 sn_flags;
__u32 sn_length;
} h;
struct sctp_assoc_change sn_assoc_change;
struct sctp_paddr_change sn_padr_change;
struct sctp_remote_error sn_remote_error;
struct sctp_send_failed sn_send_failed;
struct sctp_shutdown_event sn_shutdown_event;
struct sctp_adaption_event sn_adaption_event;
struct sctp_rcv_pdapi_event sn_rcv_pdapi_event;
};
/* Section 5.3.1
* All standard values for sn_type flags are greater than 2^15.
* Values from 2^15 and down are reserved.
*/
enum sctp_sn_type {
SCTP_SN_TYPE_BASE = (1<<15),
SCTP_ASSOC_CHANGE,
SCTP_PEER_ADDR_CHANGE,
SCTP_REMOTE_ERROR,
SCTP_SEND_FAILED,
SCTP_SHUTDOWN_EVENT,
SCTP_PARTIAL_DELIVERY_EVENT,
SCTP_ADAPTION_INDICATION,
};
/* Notification error codes used to fill up the error fields in some
* notifications.
* SCTP_PEER_ADDRESS_CHAGE : spc_error
* SCTP_ASSOC_CHANGE : sac_error
* These names should be potentially included in the draft 04 of the SCTP
* sockets API specification.
*/
typedef enum sctp_sn_error {
SCTP_FAILED_THRESHOLD,
SCTP_RECEIVED_SACK,
SCTP_HEARTBEAT_SUCCESS,
SCTP_RESPONSE_TO_USER_REQ,
SCTP_INTERNAL_ERROR,
SCTP_SHUTDOWN_GUARD_EXPIRES,
SCTP_PEER_FAULTY,
} sctp_sn_error_t;
/*
*
* 7.1.14 Peer Address Parameters
*
* Applications can enable or disable heartbeats for any peer address
* of an association, modify an address's heartbeat interval, force a
* heartbeat to be sent immediately, and adjust the address's maximum
* number of retransmissions sent before an address is considered
* unreachable. The following structure is used to access and modify an
* address's parameters:
*/
struct sctp_paddrparams {
struct sockaddr_storage spp_address;
__u32 spp_hbinterval;
__u16 spp_pathmaxrxt;
sctp_assoc_t spp_assoc_id;
};
/*
* 7.2.2 Peer Address Information
*
* Applications can retrieve information about a specific peer address
* of an association, including its reachability state, congestion
* window, and retransmission timer values. This information is
* read-only. The following structure is used to access this
* information:
*/
struct sctp_paddrinfo {
sctp_assoc_t spinfo_assoc_id;
struct sockaddr_storage spinfo_address;
__s32 spinfo_state;
__u32 spinfo_cwnd;
__u32 spinfo_srtt;
__u32 spinfo_rto;
__u32 spinfo_mtu;
};
/*
* 7.1.1 Retransmission Timeout Parameters (SCTP_RTOINFO)
*
* The protocol parameters used to initialize and bound retransmission
* timeout (RTO) are tunable. See [SCTP] for more information on how
* these parameters are used in RTO calculation. The peer address
* parameter is ignored for TCP style socket.
*/
struct sctp_rtoinfo {
__u32 srto_initial;
__u32 srto_max;
__u32 srto_min;
sctp_assoc_t srto_assoc_id;
};
/*
* 7.1.2 Association Retransmission Parameter (SCTP_ASSOCRTXINFO)
*
* The protocol parameter used to set the number of retransmissions
* sent before an association is considered unreachable.
* See [SCTP] for more information on how this parameter is used. The
* peer address parameter is ignored for TCP style socket.
*/
struct sctp_assocparams {
__u16 sasoc_asocmaxrxt;
sctp_assoc_t sasoc_assoc_id;
};
/*
* 7.1.9 Set Primary Address (SCTP_SET_PRIMARY_ADDR)
*
* Requests that the peer mark the enclosed address as the association
* primary. The enclosed address must be one of the association's
* locally bound addresses. The following structure is used to make a
* set primary request:
*/
struct sctp_setprim {
struct sockaddr_storage ssp_addr;
sctp_assoc_t ssp_assoc_id;
};
/*
* 7.1.10 Set Peer Primary Address (SCTP_SET_PEER_PRIMARY_ADDR)
*
* Requests that the local SCTP stack use the enclosed peer address as
* the association primary. The enclosed address must be one of the
* association peer's addresses. The following structure is used to
* make a set peer primary request:
*/
struct sctp_setpeerprim {
struct sockaddr_storage sspp_addr;
sctp_assoc_t sspp_assoc_id;
};
/*
* 7.2.1 Association Status (SCTP_STATUS)
*
* Applications can retrieve current status information about an
* association, including association state, peer receiver window size,
* number of unacked data chunks, and number of data chunks pending
* receipt. This information is read-only. The following structure is
* used to access this information:
*/
struct sctp_status {
sctp_assoc_t sstat_assoc_id;
__s32 sstat_state;
__u32 sstat_rwnd;
__u16 sstat_unackdata;
__u16 sstat_penddata;
__u16 sstat_instrms;
__u16 sstat_outstrms;
__u32 sstat_fragmentation_point;
struct sctp_paddrinfo sstat_primary;
};
/*
* 7.1.12 Set Adaption Layer Indicator
*
* Requests that the local endpoint set the specified Adaption Layer
* Indication parameter for all future
* INIT and INIT-ACK exchanges.
*/
struct sctp_setadaption {
__u32 ssb_adaption_ind;
};
/*
* 7.1.12 Set default message time outs (SCTP_SET_STREAM_TIMEOUTS)
*
* This option requests that the requested stream apply a
* default time-out for messages in queue.
*/
struct sctp_setstrm_timeout {
sctp_assoc_t ssto_assoc_id;
__u32 ssto_timeout;
__u16 ssto_streamid_start;
__u16 ssto_streamid_end;
};
/* These are bit fields for msghdr->msg_flags. See section 5.1. */
/* On user space Linux, these live in <bits/socket.h> as an enum. */
enum sctp_msg_flags {
MSG_NOTIFICATION = 0x8000,
#define MSG_NOTIFICATION MSG_NOTIFICATION
};
/*
* 8.1 sctp_bindx()
*
* The flags parameter is formed from the bitwise OR of zero or more of the
* following currently defined flags:
*/
#define BINDX_ADD_ADDR 0x01
#define BINDX_REM_ADDR 0x02
/* This is the structure that is passed as an argument(optval) to
* getsockopt(SCTP_SOCKOPT_PEELOFF).
*/
typedef struct {
sctp_assoc_t associd;
int sd;
} sctp_peeloff_arg_t;
#endif /* __net_sctp_user_h__ */
......@@ -26,6 +26,9 @@ if [ "$CONFIG_INET" = "y" ]; then
source net/ipv6/Config.in
fi
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
source net/sctp/Config.in
fi
fi
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
bool 'Asynchronous Transfer Mode (ATM) (EXPERIMENTAL)' CONFIG_ATM
......
......@@ -35,6 +35,7 @@ obj-$(CONFIG_DECNET) += decnet/
obj-$(CONFIG_ECONET) += econet/
obj-$(CONFIG_VLAN_8021Q) += 8021q/
obj-$(CONFIG_LLC) += llc/
obj-$(CONFIG_IP_SCTP) += sctp/
ifeq ($(CONFIG_NET),y)
obj-$(CONFIG_MODULES) += netsyms.o
......
......@@ -493,7 +493,7 @@ int inet_release(struct socket *sock)
/* It is off by default, see below. */
int sysctl_ip_nonlocal_bind;
static int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;
struct sock *sk = sock->sk;
......@@ -729,7 +729,7 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags)
/*
* This does both peername and sockname.
*/
static int inet_getname(struct socket *sock, struct sockaddr *uaddr,
int inet_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct sock *sk = sock->sk;
......@@ -846,7 +846,7 @@ int inet_shutdown(struct socket *sock, int how)
* There's a good 20K of config code hanging around the kernel.
*/
static int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
int inet_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
int err = 0;
......
......@@ -274,7 +274,7 @@ static int inet6_create(struct socket *sock, int protocol)
/* bind for INET6 API */
static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
{
struct sockaddr_in6 *addr=(struct sockaddr_in6 *)uaddr;
struct sock *sk = sock->sk;
......@@ -370,7 +370,7 @@ static int inet6_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
return 0;
}
static int inet6_release(struct socket *sock)
int inet6_release(struct socket *sock)
{
struct sock *sk = sock->sk;
......@@ -415,7 +415,7 @@ int inet6_destroy_sock(struct sock *sk)
* This does both peername and sockname.
*/
static int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
int *uaddr_len, int peer)
{
struct sockaddr_in6 *sin=(struct sockaddr_in6 *)uaddr;
......@@ -451,7 +451,7 @@ static int inet6_getname(struct socket *sock, struct sockaddr *uaddr,
return(0);
}
static int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
int inet6_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
{
struct sock *sk = sock->sk;
int err = -EINVAL;
......@@ -547,7 +547,7 @@ struct proto_ops inet6_dgram_ops = {
.sendpage = sock_no_sendpage,
};
static struct net_proto_family inet6_family_ops = {
struct net_proto_family inet6_family_ops = {
.family =PF_INET6,
.create =inet6_create,
};
......
#include <linux/module.h>
#include <net/protocol.h>
#include <net/ipv6.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
......@@ -10,3 +11,16 @@ EXPORT_SYMBOL(ndisc_mc_map);
EXPORT_SYMBOL(register_inet6addr_notifier);
EXPORT_SYMBOL(unregister_inet6addr_notifier);
EXPORT_SYMBOL(ip6_route_output);
EXPORT_SYMBOL(addrconf_lock);
EXPORT_SYMBOL(ipv6_setsockopt);
EXPORT_SYMBOL(ipv6_getsockopt);
EXPORT_SYMBOL(inet6_register_protosw);
EXPORT_SYMBOL(inet6_unregister_protosw);
EXPORT_SYMBOL(inet6_add_protocol);
EXPORT_SYMBOL(inet6_del_protocol);
EXPORT_SYMBOL(ip6_xmit);
EXPORT_SYMBOL(inet6_release);
EXPORT_SYMBOL(inet6_bind);
EXPORT_SYMBOL(inet6_getname);
EXPORT_SYMBOL(inet6_ioctl);
EXPORT_SYMBOL(ipv6_get_saddr);
......@@ -56,7 +56,8 @@ extern __u32 sysctl_rmem_max;
extern struct net_proto_family inet_family_ops;
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE)
#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) \
|| defined (CONFIG_IP_SCTP_MODULE)
#include <linux/in6.h>
#include <linux/icmpv6.h>
#include <net/ipv6.h>
......@@ -159,6 +160,7 @@ EXPORT_SYMBOL(datagram_poll);
EXPORT_SYMBOL(put_cmsg);
EXPORT_SYMBOL(sock_kmalloc);
EXPORT_SYMBOL(sock_kfree_s);
EXPORT_SYMBOL(sock_map_fd);
#ifdef CONFIG_FILTER
EXPORT_SYMBOL(sk_run_filter);
......@@ -286,7 +288,7 @@ EXPORT_SYMBOL(dlci_ioctl_hook);
#endif
#if defined (CONFIG_IPV6_MODULE)
#if defined (CONFIG_IPV6_MODULE) || defined (CONFIG_IP_SCTP_MODULE)
/* inet functions common to v4 and v6 */
EXPORT_SYMBOL(inet_release);
EXPORT_SYMBOL(inet_stream_connect);
......@@ -397,6 +399,14 @@ EXPORT_SYMBOL(sysctl_max_syn_backlog);
EXPORT_SYMBOL(tcp_read_sock);
#ifdef CONFIG_IP_SCTP_MODULE
EXPORT_SYMBOL(ip_setsockopt);
EXPORT_SYMBOL(ip_getsockopt);
EXPORT_SYMBOL(inet_ioctl);
EXPORT_SYMBOL(inet_bind);
EXPORT_SYMBOL(inet_getname);
#endif /* CONFIG_IP_SCTP_MODULE */
EXPORT_SYMBOL(netlink_set_err);
EXPORT_SYMBOL(netlink_broadcast);
EXPORT_SYMBOL(netlink_unicast);
......
CONFIG_IP_SCTP
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."
This protocol support is also available as a module ( = code which
can be inserted in and removed from the running kernel whenever you
want). The module will be called sctp.o. If you want to compile it
as a module, say M here and read <file:Documentation/modules.txt>.
If in doubt, say N.
CONFIG_SCTP_ADLER32
RCF2960 currently specifies the Adler-32 checksum algorithm for SCTP.
This has been deprecated and replaced by an algorithm now referred
to as crc32c.
If you say Y, this will use the Adler-32 algorithm, this might be useful
for interoperation with downlevel peers.
If unsure, say N.
CONFIG_SCTP_DBG_MSG
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
If you say Y, this will enable debugging support for counting the types
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
#
# SCTP configuration
#
mainmenu_option next_comment
comment ' SCTP Configuration (EXPERIMENTAL)'
if [ "$CONFIG_IPV6" != "n" ]; then
define_bool CONFIG_IPV6_SCTP__ $CONFIG_IPV6
else
define_bool CONFIG_IPV6_SCTP__ y
fi
dep_tristate ' The SCTP Protocol (EXPERIMENTAL)' CONFIG_IP_SCTP $CONFIG_IPV6_SCTP__
if [ "$CONFIG_IP_SCTP" != "n" ]; then
bool ' SCTP: Use old checksum (Adler-32)' CONFIG_SCTP_ADLER32
bool ' SCTP: Debug messages' CONFIG_SCTP_DBG_MSG
bool ' SCTP: Debug object counts' CONFIG_SCTP_DBG_OBJCNT
fi
endmenu
#
# Makefile for SCTP support code.
#
obj-$(CONFIG_IP_SCTP) += sctp.o
sctp-y := sctp_sm_statetable.o sctp_sm_statefuns.o sctp_sm_sideeffect.o \
sctp_protocol.o sctp_endpointola.o sctp_associola.o \
sctp_transport.o sctp_sm_make_chunk.o sctp_ulpevent.o \
sctp_inqueue.o sctp_outqueue.o sctp_ulpqueue.o sctp_command.o \
sctp_tsnmap.o sctp_bind_addr.o sctp_socket.o sctp_primitive.o \
sctp_output.o sctp_input.o sctp_hashdriver.o sctp_sla1.o \
sctp_debug.o
ifeq ($(CONFIG_SCTP_ADLER32), y)
sctp-y += sctp_adler32.o
else
sctp-y += sctp_crc32c.o
endif
sctp-$(CONFIG_SCTP_DBG_OBJCNT) += sctp_objcnt.o
sctp-$(CONFIG_SYSCTL) += sctp_sysctl.o
sctp-$(subst m,y,$(CONFIG_IPV6)) += sctp_ipv6.o
sctp-objs := $(sctp-y)
include $(TOPDIR)/Rules.make
/* 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_adler32.c,v 1.5 2002/06/13 16:03:38 jgrimm Exp $
*
* This file has direct heritage from the SCTP user-level reference
* implementation by R. Stewart, et al. These functions implement the
* Adler-32 algorithm as specified by RFC 2960.
*
* 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:
* Randall Stewart <rstewar1@email.mot.com>
* Ken Morneau <kmorneau@cisco.com>
* Qiaobing Xie <qxie1@email.mot.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_adler32.c,v 1.5 2002/06/13 16:03:38 jgrimm Exp $";
/* This is an entry point for external calls
* Define this function in the header file. This is
* direct from rfc1950, ...
*
* The following C code computes the Adler-32 checksum of a data buffer.
* It is written for clarity, not for speed. The sample code is in the
* ANSI C programming language. Non C users may find it easier to read
* with these hints:
*
* & Bitwise AND operator.
* >> Bitwise right shift operator. When applied to an
* unsigned quantity, as here, right shift inserts zero bit(s)
* at the left.
* << Bitwise left shift operator. Left shift inserts zero
* bit(s) at the right.
* ++ "n++" increments the variable n.
* % modulo operator: a % b is the remainder of a divided by b.
*
* Well, the above is a bit of a lie, I have optimized this a small
* tad, but I have commented the original lines below
*/
#include <linux/types.h>
#include <net/sctp/sctp.h>
#define BASE 65521 /* largest prime smaller than 65536 */
/* Performance work as shown this pig to be the
* worst CPU wise guy. I have done what I could think
* of on my flight to Austrialia but I am sure some
* clever assembly could speed this up, but of
* course this would require the dreaded #ifdef's for
* architecture. If you can speed this up more, pass
* it back and we will incorporate it :-)
*/
unsigned long update_adler32(unsigned long adler,
unsigned char *buf, int len)
{
__u32 s1 = adler & 0xffff;
__u32 s2 = (adler >> 16) & 0xffff;
int n;
for (n = 0; n < len; n++,buf++) {
/* s1 = (s1 + buf[n]) % BASE */
/* first we add */
s1 = (s1 + *buf);
/* Now if we need to, we do a mod by
* subtracting. It seems a bit faster
* since I really will only ever do
* one subtract at the MOST, since buf[n]
* is a max of 255.
*/
if(s1 >= BASE)
s1 -= BASE;
/* s2 = (s2 + s1) % BASE */
/* first we add */
s2 = (s2 + s1);
/* again, it is more efficent (it seems) to
* subtract since the most s2 will ever be
* is (BASE-1 + BASE-1) in the worse case.
* This would then be (2 * BASE) - 2, which
* will still only do one subtract. On Intel
* this is much better to do this way and
* avoid the divide. Have not -pg'd on
* sparc.
*/
if (s2 >= BASE) {
/* s2 %= BASE;*/
s2 -= BASE;
}
}
/* Return the adler32 of the bytes buf[0..len-1] */
return (s2 << 16) + s1;
}
__u32 count_crc(__u8 *ptr, __u16 count)
{
/*
* Update a running Adler-32 checksum with the bytes
* buf[0..len-1] and return the updated checksum. The Adler-32
* checksum should be initialized to 1.
*/
__u32 adler = 1L;
__u32 zero = 0L;
/* Calculate the CRC up to the checksum field. */
adler = update_adler32(adler, ptr,
sizeof(struct sctphdr) - sizeof(__u32));
/* Skip over the checksum field. */
adler = update_adler32(adler, &zero, sizeof(__u32));
ptr += sizeof(struct sctphdr);
count -= sizeof(struct sctphdr);
/* Calculate the rest of the Adler-32. */
adler = update_adler32(adler, ptr, count);
return adler;
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_associola.c,v 1.48 2002/08/16 19:30:49 jgrimm Exp $
*
* This module provides the abstraction for an SCTP association.
*
* 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>
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_associola.c,v 1.48 2002/08/16 19:30:49 jgrimm Exp $";
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/in.h>
#include <net/ipv6.h>
#include <net/sctp/sctp.h>
/* Forward declarations for internal functions. */
static void sctp_assoc_bh_rcv(sctp_association_t *asoc);
/* 1st Level Abstractions. */
/* Allocate and initialize a new association */
sctp_association_t *sctp_association_new(const sctp_endpoint_t *ep,
const struct sock *sk,
sctp_scope_t scope, int priority)
{
sctp_association_t *asoc;
asoc = t_new(sctp_association_t, priority);
if (!asoc)
goto fail;
if (!sctp_association_init(asoc, ep, sk, scope, priority))
goto fail_init;
asoc->base.malloced = 1;
SCTP_DBG_OBJCNT_INC(assoc);
return asoc;
fail_init:
kfree(asoc);
fail:
return NULL;
}
/* Intialize a new association from provided memory. */
sctp_association_t *sctp_association_init(sctp_association_t *asoc,
const sctp_endpoint_t *ep,
const struct sock *sk,
sctp_scope_t scope,
int priority)
{
sctp_opt_t *sp;
int i;
/* Retrieve the SCTP per socket area. */
sp = sctp_sk((struct sock *)sk);
/* Init all variables to a known value. */
memset(asoc, 0, sizeof(sctp_association_t));
/* Discarding const is appropriate here. */
asoc->ep = (sctp_endpoint_t *)ep;
sctp_endpoint_hold(asoc->ep);
/* Hold the sock. */
asoc->base.sk = (struct sock *)sk;
sock_hold(asoc->base.sk);
/* Initialize the common base substructure. */
asoc->base.type = SCTP_EP_TYPE_ASSOCIATION;
/* Initialize the object handling fields. */
atomic_set(&asoc->base.refcnt, 1);
asoc->base.dead = 0;
asoc->base.malloced = 0;
/* Initialize the bind addr area. */
sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port);
asoc->base.addr_lock = RW_LOCK_UNLOCKED;
asoc->state = SCTP_STATE_CLOSED;
asoc->state_timestamp = jiffies;
/* Set things that have constant value. */
asoc->cookie_life.tv_sec = SCTP_DEFAULT_COOKIE_LIFE_SEC;
asoc->cookie_life.tv_usec = SCTP_DEFAULT_COOKIE_LIFE_USEC;
asoc->pmtu = 0;
asoc->frag_point = 0;
/* Initialize the default association max_retrans and RTO values. */
asoc->max_retrans = ep->proto->max_retrans_association;
asoc->rto_initial = ep->proto->rto_initial;
asoc->rto_max = ep->proto->rto_max;
asoc->rto_min = ep->proto->rto_min;
asoc->overall_error_threshold = 0;
asoc->overall_error_count = 0;
/* Initialize the maximum mumber of new data packets that can be sent
* in a burst.
*/
asoc->max_burst = ep->proto->max_burst;
/* Copy things from the endpoint. */
for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) {
asoc->timeouts[i] = ep->timeouts[i];
init_timer(&asoc->timers[i]);
asoc->timers[i].function = sctp_timer_events[i];
asoc->timers[i].data = (unsigned long) asoc;
}
/* Pull default initialization values from the sock options.
* Note: This assumes that the values have already been
* validated in the sock.
*/
asoc->c.sinit_max_instreams = sp->initmsg.sinit_max_instreams;
asoc->c.sinit_num_ostreams = sp->initmsg.sinit_num_ostreams;
asoc->max_init_attempts = sp->initmsg.sinit_max_attempts;
asoc->max_init_timeo = sp->initmsg.sinit_max_init_timeo * HZ;
/* RFC 2960 6.5 Stream Identifier and Stream Sequence Number
*
* The stream sequence number in all the streams shall start
* from 0 when the association is established. Also, when the
* stream sequence number reaches the value 65535 the next
* stream sequence number shall be set to 0.
*/
for (i = 0; i < SCTP_MAX_STREAM; i++)
asoc->ssn[i] = 0;
/* Set the local window size for receive.
* This is also the rcvbuf space per association.
* RFC 6 - A SCTP receiver MUST be able to receive a minimum of
* 1500 bytes in one SCTP packet.
*/
if (sk->rcvbuf < SCTP_DEFAULT_MINWINDOW)
asoc->rwnd = SCTP_DEFAULT_MINWINDOW;
else
asoc->rwnd = sk->rcvbuf;
asoc->rwnd_over = 0;
/* Use my own max window until I learn something better. */
asoc->peer.rwnd = SCTP_DEFAULT_MAXWINDOW;
/* Set the sndbuf size for transmit. */
asoc->sndbuf_used = 0;
init_waitqueue_head(&asoc->wait);
asoc->c.my_vtag = sctp_generate_tag(ep);
asoc->peer.i.init_tag = 0; /* INIT needs a vtag of 0. */
asoc->c.peer_vtag = 0;
asoc->c.my_ttag = 0;
asoc->c.peer_ttag = 0;
asoc->c.initial_tsn = sctp_generate_tsn(ep);
asoc->next_tsn = asoc->c.initial_tsn;
asoc->ctsn_ack_point = asoc->next_tsn - 1;
asoc->unack_data = 0;
SCTP_DEBUG_PRINTK("myctsnap for %s INIT as 0x%x.\n",
asoc->ep->debug_name,
asoc->ctsn_ack_point);
/* ADDIP Section 4.1 Asconf Chunk Procedures
*
* When an endpoint has an ASCONF signaled change to be sent to the
* remote endpoint it should do the following:
* ...
* A2) a serial number should be assigned to the chunk. The serial
* number should be a monotonically increasing number. All serial
* numbers are defined to be initialized at the start of the
* association to the same value as the initial TSN.
*/
asoc->addip_serial = asoc->c.initial_tsn;
/* Make an empty list of remote transport addresses. */
INIT_LIST_HEAD(&asoc->peer.transport_addr_list);
/* RFC 2960 5.1 Normal Establishment of an Association
*
* After the reception of the first data chunk in an
* association the endpoint must immediately respond with a
* sack to acknowledge the data chunk. Subsequent
* acknowledgements should be done as described in Section
* 6.2.
*
* [We implement this by telling a new association that it
* already received one packet.]
*/
asoc->peer.sack_needed = 1;
/* Create an input queue. */
sctp_inqueue_init(&asoc->base.inqueue);
sctp_inqueue_set_th_handler(&asoc->base.inqueue,
(void (*)(void *))sctp_assoc_bh_rcv,
asoc);
/* Create an output queue. */
sctp_outqueue_init(asoc, &asoc->outqueue);
sctp_outqueue_set_output_handlers(&asoc->outqueue,
sctp_packet_init,
sctp_packet_config,
sctp_packet_append_chunk,
sctp_packet_transmit_chunk,
sctp_packet_transmit);
if (NULL == sctp_ulpqueue_init(&asoc->ulpq, asoc, SCTP_MAX_STREAM))
goto fail_init;
/* Set up the tsn tracking. */
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE, 0);
asoc->peer.next_dup_tsn = 0;
skb_queue_head_init(&asoc->addip_chunks);
asoc->need_ecne = 0;
asoc->debug_name = "unnamedasoc";
asoc->eyecatcher = SCTP_ASSOC_EYECATCHER;
/* Assume that peer would support both address types unless we are
* told otherwise.
*/
asoc->peer.ipv4_address = 1;
asoc->peer.ipv6_address = 1;
INIT_LIST_HEAD(&asoc->asocs);
asoc->autoclose = sp->autoclose;
return asoc;
fail_init:
sctp_endpoint_put(asoc->ep);
sock_put(asoc->base.sk);
return NULL;
}
/* Free this association if possible. There may still be users, so
* the actual deallocation may be delayed.
*/
void sctp_association_free(sctp_association_t *asoc)
{
sctp_transport_t *transport;
sctp_endpoint_t *ep;
list_t *pos, *temp;
int i;
ep = asoc->ep;
list_del(&asoc->asocs);
/* Mark as dead, so other users can know this structure is
* going away.
*/
asoc->base.dead = 1;
/* Dispose of any data lying around in the outqueue. */
sctp_outqueue_free(&asoc->outqueue);
/* Dispose of any pending messages for the upper layer. */
sctp_ulpqueue_free(&asoc->ulpq);
/* Dispose of any pending chunks on the inqueue. */
sctp_inqueue_free(&asoc->base.inqueue);
/* Clean up the bound address list. */
sctp_bind_addr_free(&asoc->base.bind_addr);
/* Do we need to go through all of our timers and
* delete them? To be safe we will try to delete all, but we
* should be able to go through and make a guess based
* on our state.
*/
for (i = SCTP_EVENT_TIMEOUT_NONE; i < SCTP_NUM_TIMEOUT_TYPES; ++i) {
if (timer_pending(&asoc->timers[i]) &&
del_timer(&asoc->timers[i]))
sctp_association_put(asoc);
}
/* Release the transport structures. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports);
list_del(pos);
sctp_transport_free(transport);
}
asoc->eyecatcher = 0;
sctp_association_put(asoc);
}
/* Cleanup and free up an association. */
static void sctp_association_destroy(sctp_association_t *asoc)
{
SCTP_ASSERT(asoc->base.dead, "Assoc is not dead", return);
sctp_endpoint_put(asoc->ep);
sock_put(asoc->base.sk);
if (asoc->base.malloced) {
kfree(asoc);
SCTP_DBG_OBJCNT_DEC(assoc);
}
}
/* Add a transport address to an association. */
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
const sockaddr_storage_t *addr,
int priority)
{
sctp_transport_t *peer;
sctp_opt_t *sp;
const __u16 *port;
switch (addr->sa.sa_family) {
case AF_INET:
port = &addr->v4.sin_port;
break;
case AF_INET6:
SCTP_V6(
port = &addr->v6.sin6_port;
break;
);
default:
return NULL;
};
/* Set the port if it has not been set yet. */
if (0 == asoc->peer.port) {
asoc->peer.port = *port;
}
SCTP_ASSERT(*port == asoc->peer.port, ":Invalid port\n",
return NULL);
/* Check to see if this is a duplicate. */
peer = sctp_assoc_lookup_paddr(asoc, addr);
if (peer)
return peer;
peer = sctp_transport_new(addr, priority);
if (NULL == peer)
return NULL;
sctp_transport_set_owner(peer, asoc);
/* If this is the first transport addr on this association,
* initialize the association PMTU to the peer's PMTU.
* If not and the current association PMTU is higher than the new
* peer's PMTU, reset the association PMTU to the new peer's PMTU.
*/
if (asoc->pmtu) {
asoc->pmtu = min_t(int, peer->pmtu, asoc->pmtu);
} else {
asoc->pmtu = peer->pmtu;
}
SCTP_DEBUG_PRINTK("sctp_assoc_add_peer:association %p PMTU set to "
"%d\n", asoc, asoc->pmtu);
asoc->frag_point = asoc->pmtu -
(SCTP_IP_OVERHEAD + sizeof(sctp_data_chunk_t));
/* The asoc->peer.port might not be meaningful as of now, but
* initialize the packet structure anyway.
*/
(asoc->outqueue.init_output)(&peer->packet,
peer,
asoc->base.bind_addr.port,
asoc->peer.port);
/* 7.2.1 Slow-Start
*
* o The initial cwnd before data transmission or after a
* sufficiently long idle period MUST be <= 2*MTU.
*
* o The initial value of ssthresh MAY be arbitrarily high
* (for example, implementations MAY use the size of the
* receiver advertised window).
*/
peer->cwnd = asoc->pmtu * 2;
/* At this point, we may not have the receiver's advertised window,
* so initialize ssthresh to the default value and it will be set
* later when we process the INIT.
*/
peer->ssthresh = SCTP_DEFAULT_MAXWINDOW;
peer->partial_bytes_acked = 0;
peer->flight_size = 0;
peer->error_threshold = peer->max_retrans;
/* Update the overall error threshold value of the association
* taking the new peer's error threshold into account.
*/
asoc->overall_error_threshold =
min(asoc->overall_error_threshold + peer->error_threshold,
asoc->max_retrans);
/* Initialize the peer's heartbeat interval based on the
* sock configured value.
*/
sp = sctp_sk(asoc->base.sk);
peer->hb_interval = sp->paddrparam.spp_hbinterval * HZ;
/* Attach the remote transport to our asoc. */
list_add_tail(&peer->transports, &asoc->peer.transport_addr_list);
/* If we do not yet have a primary path, set one. */
if (NULL == asoc->peer.primary_path) {
asoc->peer.primary_path = peer;
asoc->peer.active_path = peer;
asoc->peer.retran_path = peer;
}
if (asoc->peer.active_path == asoc->peer.retran_path)
asoc->peer.retran_path = peer;
return peer;
}
/* Lookup a transport by address. */
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc,
const sockaddr_storage_t *address)
{
sctp_transport_t *t;
list_t *pos;
/* Cycle through all transports searching for a peer address. */
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports);
if (sctp_cmp_addr_exact(address, &t->ipaddr))
return t;
}
return NULL;
}
/* Engage in transport control operations.
* Mark the transport up or down and send a notification to the user.
* Select and update the new active and retran paths.
*/
void sctp_assoc_control_transport(sctp_association_t *asoc,
sctp_transport_t *transport,
sctp_transport_cmd_t command,
sctp_sn_error_t error)
{
sctp_transport_t *t = NULL;
sctp_transport_t *first;
sctp_transport_t *second;
sctp_ulpevent_t *event;
list_t *pos;
int spc_state = 0;
/* Record the transition on the transport. */
switch (command) {
case SCTP_TRANSPORT_UP:
transport->state.active = 1;
spc_state = ADDRESS_AVAILABLE;
break;
case SCTP_TRANSPORT_DOWN:
transport->state.active = 0;
spc_state = ADDRESS_UNREACHABLE;
break;
default:
BUG();
};
/* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the
* user.
*/
event = sctp_ulpevent_make_peer_addr_change(asoc,
(struct sockaddr_storage *) &transport->ipaddr,
0, spc_state, error, GFP_ATOMIC);
if (event)
sctp_ulpqueue_tail_event(&asoc->ulpq, event);
/* Select new active and retran paths. */
/* Look for the two most recently used active transports.
*
* This code produces the wrong ordering whenever jiffies
* rolls over, but we still get usable transports, so we don't
* worry about it.
*/
first = NULL; second = NULL;
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports);
if (!t->state.active)
continue;
if (!first || t->last_time_heard > first->last_time_heard) {
second = first;
first = t;
}
if (!second || t->last_time_heard > second->last_time_heard)
second = t;
}
/* RFC 2960 6.4 Multi-Homed SCTP Endpoints
*
* By default, an endpoint should always transmit to the
* primary path, unless the SCTP user explicitly specifies the
* destination transport address (and possibly source
* transport address) to use.
*
* [If the primary is active but not most recent, bump the most
* recently used transport.]
*/
if (asoc->peer.primary_path->state.active &&
first != asoc->peer.primary_path) {
second = first;
first = asoc->peer.primary_path;
}
/* If we failed to find a usable transport, just camp on the
* primary, even if it is inactive.
*/
if (NULL == first) {
first = asoc->peer.primary_path;
second = asoc->peer.primary_path;
}
/* Set the active and retran transports. */
asoc->peer.active_path = first;
asoc->peer.retran_path = second;
}
/* Hold a reference to an association. */
void sctp_association_hold(sctp_association_t *asoc)
{
atomic_inc(&asoc->base.refcnt);
}
/* Release a reference to an association and cleanup
* if there are no more references.
*/
void sctp_association_put(sctp_association_t *asoc)
{
if (atomic_dec_and_test(&asoc->base.refcnt))
sctp_association_destroy(asoc);
}
/* Allocate the next TSN, Transmission Sequence Number, for the given
* association.
*/
__u32 __sctp_association_get_next_tsn(sctp_association_t *asoc)
{
/* From Section 1.6 Serial Number Arithmetic:
* Transmission Sequence Numbers wrap around when they reach
* 2**32 - 1. That is, the next TSN a DATA chunk MUST use
* after transmitting TSN = 2*32 - 1 is TSN = 0.
*/
__u32 retval = asoc->next_tsn;
asoc->next_tsn++;
asoc->unack_data++;
return retval;
}
/* Allocate 'num' TSNs by incrementing the association's TSN by num. */
__u32 __sctp_association_get_tsn_block(sctp_association_t *asoc, int num)
{
__u32 retval = asoc->next_tsn;
asoc->next_tsn += num;
asoc->unack_data += num;
return retval;
}
/* Fetch the next Stream Sequence Number for stream number 'sid'. */
__u16 __sctp_association_get_next_ssn(sctp_association_t *asoc, __u16 sid)
{
return asoc->ssn[sid]++;
}
/* Compare two addresses to see if they match. Wildcard addresses
* always match within their address family.
*
* FIXME: We do not match address scopes correctly.
*/
int sctp_cmp_addr(const sockaddr_storage_t *ss1, const sockaddr_storage_t *ss2)
{
int len;
const void *base1;
const void *base2;
if (ss1->sa.sa_family != ss2->sa.sa_family)
return 0;
if (ss1->v4.sin_port != ss2->v4.sin_port)
return 0;
switch (ss1->sa.sa_family) {
case AF_INET:
if (INADDR_ANY == ss1->v4.sin_addr.s_addr ||
INADDR_ANY == ss2->v4.sin_addr.s_addr)
goto match;
len = sizeof(struct in_addr);
base1 = &ss1->v4.sin_addr;
base2 = &ss2->v4.sin_addr;
break;
case AF_INET6:
SCTP_V6(
if (IPV6_ADDR_ANY ==
sctp_ipv6_addr_type(&ss1->v6.sin6_addr))
goto match;
if (IPV6_ADDR_ANY ==
sctp_ipv6_addr_type(&ss2->v6.sin6_addr))
goto match;
len = sizeof(struct in6_addr);
base1 = &ss1->v6.sin6_addr;
base2 = &ss2->v6.sin6_addr;
break;
)
default:
printk(KERN_WARNING
"WARNING, bogus socket address family %d\n",
ss1->sa.sa_family);
return 0;
};
return (0 == memcmp(base1, base2, len));
match:
return 1;
}
/* Compare two addresses to see if they match. Wildcard addresses
* only match themselves.
*
* FIXME: We do not match address scopes correctly.
*/
int sctp_cmp_addr_exact(const sockaddr_storage_t *ss1,
const sockaddr_storage_t *ss2)
{
int len;
const void *base1;
const void *base2;
if (ss1->sa.sa_family != ss2->sa.sa_family)
return 0;
if (ss1->v4.sin_port != ss2->v4.sin_port)
return 0;
switch (ss1->sa.sa_family) {
case AF_INET:
len = sizeof(struct in_addr);
base1 = &ss1->v4.sin_addr;
base2 = &ss2->v4.sin_addr;
break;
case AF_INET6:
SCTP_V6(
len = sizeof(struct in6_addr);
base1 = &ss1->v6.sin6_addr;
base2 = &ss2->v6.sin6_addr;
break;
)
default:
printk(KERN_WARNING
"WARNING, bogus socket address family %d\n",
ss1->sa.sa_family);
return 0;
};
return (0 == memcmp(base1, base2, len));
}
/* Return an ecne chunk to get prepended to a packet.
* Note: We are sly and return a shared, prealloced chunk.
*/
sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc)
{
sctp_chunk_t *chunk;
int need_ecne;
__u32 lowest_tsn;
/* Can be called from task or bh. Both need_ecne and
* last_ecne_tsn are written during bh.
*/
need_ecne = asoc->need_ecne;
lowest_tsn = asoc->last_ecne_tsn;
if (need_ecne) {
chunk = sctp_make_ecne(asoc, lowest_tsn);
/* ECNE is not mandatory to the flow. Being unable to
* alloc mem is not deadly. We are just unable to help
* out the network. If we run out of memory, just return
* NULL.
*/
} else {
chunk = NULL;
}
return chunk;
}
/* Use this function for the packet prepend callback when no ECNE
* packet is desired (e.g. some packets don't like to be bundled).
*/
sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc)
{
return NULL;
}
/*
* Find which transport this TSN was sent on.
*/
sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
{
sctp_transport_t *active;
sctp_transport_t *match;
list_t *entry, *pos;
sctp_transport_t *transport;
sctp_chunk_t *chunk;
__u32 key = htonl(tsn);
match = NULL;
/*
* FIXME: In general, find a more efficient data structure for
* searching.
*/
/*
* The general strategy is to search each transport's transmitted
* list. Return which transport this TSN lives on.
*
* Let's be hopeful and check the active_path first.
* Another optimization would be to know if there is only one
* outbound path and not have to look for the TSN at all.
*
*/
active = asoc->peer.active_path;
list_for_each(entry, &active->transmitted) {
chunk = list_entry(entry, sctp_chunk_t, transmitted_list);
if (key == chunk->subh.data_hdr->tsn) {
match = active;
goto out;
}
}
/* If not found, go search all the other transports. */
list_for_each(pos, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports);
if (transport == active)
break;
list_for_each(entry, &transport->transmitted) {
chunk = list_entry(entry, sctp_chunk_t,
transmitted_list);
if (key == chunk->subh.data_hdr->tsn) {
match = transport;
goto out;
}
}
}
out:
return match;
}
/* Is this the association we are looking for? */
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *asoc,
const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr)
{
sctp_transport_t *transport;
sctp_read_lock(&asoc->base.addr_lock);
if ((asoc->base.bind_addr.port == laddr->v4.sin_port) &&
(asoc->peer.port == paddr->v4.sin_port)) {
transport = sctp_assoc_lookup_paddr(asoc, paddr);
if (!transport)
goto out;
if (sctp_bind_addr_has_addr(&asoc->base.bind_addr, laddr))
goto out;
}
transport = NULL;
out:
sctp_read_unlock(&asoc->base.addr_lock);
return transport;
}
/* Do delayed input processing. This is scheduled by sctp_rcv(). */
static void sctp_assoc_bh_rcv(sctp_association_t *asoc)
{
sctp_endpoint_t *ep;
sctp_chunk_t *chunk;
struct sock *sk;
sctp_inqueue_t *inqueue;
int state, subtype;
sctp_assoc_t associd = sctp_assoc2id(asoc);
int error = 0;
/* The association should be held so we should be safe. */
ep = asoc->ep;
sk = asoc->base.sk;
inqueue = &asoc->base.inqueue;
while (NULL != (chunk = sctp_pop_inqueue(inqueue))) {
state = asoc->state;
subtype = chunk->chunk_hdr->type;
/* Remember where the last DATA chunk came from so we
* know where to send the SACK.
*/
if (sctp_chunk_is_data(chunk))
asoc->peer.last_data_from = chunk->transport;
if (chunk->transport)
chunk->transport->last_time_heard = jiffies;
/* Run through the state machine. */
error = sctp_do_sm(SCTP_EVENT_T_CHUNK, SCTP_ST_CHUNK(subtype),
state, ep, asoc, chunk, GFP_ATOMIC);
/* Check to see if the association is freed in response to
* the incoming chunk. If so, get out of the while loop.
*/
if (!sctp_id2assoc(sk, associd))
goto out;
if (error != 0)
goto err_out;
}
err_out:
/* Is this the right way to pass errors up to the ULP? */
if (error)
sk->err = -error;
out:
}
/* This routine moves an association from its old sk to a new sk. */
void sctp_assoc_migrate(sctp_association_t *assoc, struct sock *newsk)
{
sctp_opt_t *newsp = sctp_sk(newsk);
/* Delete the association from the old endpoint's list of
* associations.
*/
list_del(&assoc->asocs);
/* Release references to the old endpoint and the sock. */
sctp_endpoint_put(assoc->ep);
sock_put(assoc->base.sk);
/* Get a reference to the new endpoint. */
assoc->ep = newsp->ep;
sctp_endpoint_hold(assoc->ep);
/* Get a reference to the new sock. */
assoc->base.sk = newsk;
sock_hold(assoc->base.sk);
/* Add the association to the new endpoint's list of associations. */
sctp_endpoint_add_asoc(newsp->ep, assoc);
}
/* Update an association (possibly from unexpected COOKIE-ECHO processing). */
void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new)
{
int i;
/* Copy in new parameters of peer. */
asoc->c = new->c;
asoc->peer.rwnd = new->peer.rwnd;
asoc->peer.next_dup_tsn = new->peer.next_dup_tsn;
asoc->peer.sack_needed = new->peer.sack_needed;
asoc->peer.i = new->peer.i;
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,
asoc->peer.i.initial_tsn);
/* FIXME:
* Do we need to copy primary_path etc?
*
* More explicitly, addresses may have been removed and
* this needs accounting for.
*/
/* If the case is A (association restart), use
* initial_tsn as next_tsn. If the case is B, use
* current next_tsn in case there is data sent to peer
* has been discarded and needs retransmission.
*/
if (SCTP_STATE_ESTABLISHED == asoc->state) {
asoc->next_tsn = new->next_tsn;
asoc->ctsn_ack_point = new->ctsn_ack_point;
/* Reinitialize SSN for both local streams
* and peer's streams.
*/
for (i = 0; i < SCTP_MAX_STREAM; i++) {
asoc->ssn[i] = 0;
asoc->ulpq.ssn[i] = 0;
}
} else {
asoc->ctsn_ack_point = asoc->next_tsn - 1;
}
}
/* Choose the transport for sending a shutdown packet.
* Round-robin through the active transports, else round-robin
* through the inactive transports as this is the next best thing
* we can try.
*/
sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
{
sctp_transport_t *t, *next;
list_t *head = &asoc->peer.transport_addr_list;
list_t *pos;
/* If this is the first time SHUTDOWN is sent, use the active
* path.
*/
if (!asoc->shutdown_last_sent_to)
return asoc->peer.active_path;
/* Otherwise, find the next transport in a round-robin fashion. */
t = asoc->shutdown_last_sent_to;
pos = &t->transports;
next = NULL;
while (1) {
/* Skip the head. */
if (pos->next == head)
pos = head->next;
else
pos = pos->next;
t = list_entry(pos, sctp_transport_t, transports);
/* Try to find an active transport. */
if (t->state.active) {
break;
} else {
/* Keep track of the next transport in case
* we don't find any active transport.
*/
if (!next)
next = t;
}
/* We have exhausted the list, but didn't find any
* other active transports. If so, use the next
* transport.
*/
if (t == asoc->shutdown_last_sent_to) {
t = next;
break;
}
}
return t;
}
/* SCTP kernel reference Implementation
* Copyright (c) Cisco 1999,2000
* Copyright (c) Motorola 1999,2000,2001
* Copyright (c) International Business Machines Corp., 2001
* Copyright (c) La Monte H.P. Yarroll 2001
*
* This file is part of the SCTP kernel reference implementation.
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_bind_addr.c,v 1.16 2002/07/12 15:15:45 jgrimm Exp $
*
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_bind_addr.c,v 1.16 2002/07/12 15:15:45 jgrimm Exp $";
#include <linux/types.h>
#include <linux/sched.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/sctp_sm.h>
/* Forward declarations for internal helpers. */
static int sctp_copy_one_addr(sctp_bind_addr_t *, sockaddr_storage_t *,
sctp_scope_t scope, int priority, int flags);
static void sctp_bind_addr_clean(sctp_bind_addr_t *);
/* 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(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src,
sctp_scope_t scope, int priority, int flags)
{
struct sockaddr_storage_list *addr;
list_t *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 sockaddr_storage_list, list);
error = sctp_copy_one_addr(dest, &addr->a, scope,
priority, flags);
if (error < 0)
goto out;
}
out:
if (error)
sctp_bind_addr_clean(dest);
return error;
}
/* Create a new SCTP_bind_addr from nothing. */
sctp_bind_addr_t *sctp_bind_addr_new(int priority)
{
sctp_bind_addr_t *retval;
retval = t_new(sctp_bind_addr_t, priority);
if (!retval)
goto nomem;
sctp_bind_addr_init(retval, 0);
retval->malloced = 1;
SCTP_DBG_OBJCNT_INC(bind_addr);
nomem:
return retval;
}
/* Initialize the SCTP_bind_addr structure for either an endpoint or
* an association.
*/
void sctp_bind_addr_init(sctp_bind_addr_t *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(sctp_bind_addr_t *bp)
{
struct sockaddr_storage_list *addr;
list_t *pos, *temp;
/* Empty the bind address list. */
list_for_each_safe(pos, temp, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
list_del(pos);
kfree(addr);
SCTP_DBG_OBJCNT_DEC(addr);
}
}
/* Dispose of an SCTP_bind_addr structure */
void sctp_bind_addr_free(sctp_bind_addr_t *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(sctp_bind_addr_t *bp, sockaddr_storage_t *new,
int priority)
{
struct sockaddr_storage_list *addr;
/* Add the address to the bind address list. */
addr = t_new(struct sockaddr_storage_list, priority);
if (!addr)
return -ENOMEM;
addr->a = *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 = bp->port;
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(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr)
{
list_t *pos, *temp;
struct sockaddr_storage_list *addr;
list_for_each_safe(pos, temp, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, 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.
*/
sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int priority)
{
sctpParam_t rawaddr;
sctpParam_t addrparms;
sctpParam_t retval;
int addrparms_len;
sctpIpAddress_t rawaddr_space;
int len;
struct sockaddr_storage_list *addr;
list_t *pos;
retval.v = NULL;
addrparms_len = 0;
len = 0;
/* Allocate enough memory at once. */
list_for_each(pos, &bp->address_list) {
len += sizeof(sctp_ipv6addr_param_t);
}
addrparms.v = kmalloc(len, priority);
if (!addrparms.v)
goto end_raw;
retval = addrparms;
rawaddr.v4 = &rawaddr_space.v4;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
len = sockaddr2sctp_addr(&addr->a, rawaddr);
memcpy(addrparms.v, rawaddr.v, 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(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
int addrs_len, __u16 port, int priority)
{
sctpParam_t rawaddr;
sockaddr_storage_t addr;
int retval = 0;
int len;
/* Convert the raw address to standard address format */
while (addrs_len) {
rawaddr.v = raw_addr_list;
if (SCTP_PARAM_IPV4_ADDRESS==rawaddr.p->type
|| SCTP_PARAM_IPV6_ADDRESS==rawaddr.p->type) {
sctp_param2sockaddr(&addr, rawaddr, port);
retval = sctp_add_bind_addr(bp, &addr, priority);
if (retval) {
/* Can't finish building the list, clean up. */
sctp_bind_addr_clean(bp);
break;
}
len = ntohs(rawaddr.p->length);
addrs_len -= len;
raw_addr_list += len;
} else {
/* Corrupted raw addr list! */
retval = -EINVAL;
sctp_bind_addr_clean(bp);
break;
}
}
return retval;
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
/* Does this contain a specified address? */
int sctp_bind_addr_has_addr(sctp_bind_addr_t *bp, const sockaddr_storage_t *addr)
{
struct sockaddr_storage_list *laddr;
list_t *pos;
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, list);
if (sctp_cmp_addr(&laddr->a, addr))
return 1;
}
return 0;
}
/* Copy out addresses from the global local address list. */
static int sctp_copy_one_addr(sctp_bind_addr_t *dest, sockaddr_storage_t *addr,
sctp_scope_t scope, int priority, int flags)
{
sctp_protocol_t *proto = sctp_get_protocol();
int error = 0;
if (sctp_is_any(addr)) {
error = sctp_copy_local_addr_list(proto, dest, scope,
priority, 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, priority);
}
return error;
}
/* Is addr one of the wildcards? */
int sctp_is_any(const sockaddr_storage_t *addr)
{
int retval = 0;
switch (addr->sa.sa_family) {
case AF_INET:
if (INADDR_ANY == addr->v4.sin_addr.s_addr)
retval = 1;
break;
case AF_INET6:
SCTP_V6(
if (IPV6_ADDR_ANY ==
sctp_ipv6_addr_type(&addr->v6.sin6_addr))
retval = 1;
);
break;
default:
break;
};
return retval;
}
/* Is 'addr' valid for 'scope'? */
int sctp_in_scope(const sockaddr_storage_t *addr, sctp_scope_t scope)
{
sctp_scope_t addr_scope = sctp_scope(addr);
switch (addr->sa.sa_family) {
case AF_INET:
/* According to the SCTP IPv4 address scoping document -
* <draft-stewart-tsvwg-sctp-ipv4-00.txt>, the scope has
* a heirarchy of 5 levels:
* Level 0 - unusable SCTP addresses
* Level 1 - loopback address
* Level 2 - link-local addresses
* Level 3 - private addresses.
* Level 4 - global addresses
* For INIT and INIT-ACK address list, let L be the level of
* of requested destination address, sender and receiver
* SHOULD include all of its addresses with level greater
* than or equal to L.
*/
/* The unusable SCTP addresses will not be considered with
* any defined scopes.
*/
if (SCTP_SCOPE_UNUSABLE == addr_scope)
return 0;
/* Note that we are assuming that the scoping are the same
* for both IPv4 addresses and IPv6 addresses, i.e., if the
* scope is link local, both IPv4 link local addresses and
* IPv6 link local addresses would be treated as in the
* scope. There is no filtering for IPv4 vs. IPv6 addresses
* based on scoping alone.
*/
if (addr_scope <= scope)
return 1;
break;
case AF_INET6:
/* FIXME:
* This is almost certainly wrong since scopes have an
* heirarchy. I don't know what RFC to look at.
* There may be some guidance in the SCTP implementors
* guide (an Internet Draft as of October 2001).
*
* Further verification on the correctness of the IPv6
* scoping is needed. According to the IPv6 scoping draft,
* the link local and site local address may require
* further scoping.
*
* Is the heirachy of the IPv6 scoping the same as what's
* defined for IPv4?
* If the same heirarchy indeed applies to both famiies,
* this function can be simplified with one set of code.
* (see the comments for IPv4 above)
*/
if (addr_scope <= scope)
return 1;
break;
default:
return 0;
};
return 0;
}
/********************************************************************
* 3rd Level Abstractions
********************************************************************/
/* What is the scope of 'addr'? */
sctp_scope_t sctp_scope(const sockaddr_storage_t *addr)
{
sctp_scope_t retval = SCTP_SCOPE_GLOBAL;
switch (addr->sa.sa_family) {
case AF_INET:
/* We are checking the loopback, private and other address
* scopes as defined in RFC 1918.
* The IPv4 scoping is based on the draft for SCTP IPv4
* scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>.
* The set of SCTP address scope hopefully can cover both
* types of addresses.
*/
/* Should IPv4 scoping be a sysctl configurable option
* so users can turn it off (default on) for certain
* unconventional networking environments?
*/
/* Check for unusable SCTP addresses. */
if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_UNUSABLE;
} else if (LOOPBACK(addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_LOOPBACK;
} else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_LINK;
} else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_PRIVATE;
} else {
retval = SCTP_SCOPE_GLOBAL;
}
break;
case AF_INET6:
{
SCTP_V6(
int v6scope;
v6scope = ipv6_addr_scope((struct in6_addr *)
&addr->v6.sin6_addr);
/* The IPv6 scope is really a set of bit
* fields. See IFA_* in <net/if_inet6.h>.
* Mapping them to the generic SCTP scope
* set is an attempt to have code
* consistencies with the IPv4 scoping.
*/
switch (v6scope) {
case IFA_HOST:
retval = SCTP_SCOPE_LOOPBACK;
break;
case IFA_LINK:
retval = SCTP_SCOPE_LINK;
break;
case IFA_SITE:
retval = SCTP_SCOPE_PRIVATE;
break;
default:
retval = SCTP_SCOPE_GLOBAL;
break;
};
);
break;
}
default:
retval = SCTP_SCOPE_GLOBAL;
break;
};
return retval;
}
/* This function checks if the address is a valid address to be used for
* SCTP.
*
* Output:
* Return 0 - If the address is a non-unicast or an illegal address.
* Return 1 - If the address is a unicast.
*/
int sctp_addr_is_valid(const sockaddr_storage_t *addr)
{
unsigned short sa_family = addr->sa.sa_family;
switch (sa_family) {
case AF_INET:
/* Is this a non-unicast address or a unusable SCTP address? */
if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
return 0;
break;
case AF_INET6:
SCTP_V6(
{
int ret = sctp_ipv6_addr_type(&addr->v6.sin6_addr);
/* Is this a non-unicast address */
if (!(ret & IPV6_ADDR_UNICAST))
return 0;
break;
});
default:
return 0;
};
return 1;
}
/* 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_command.c,v 1.4 2002/04/24 16:33:39 jgrimm Exp $
*
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_command.c,v 1.4 2002/04/24 16:33:39 jgrimm Exp $";
#include <linux/types.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
/* Create a new sctp_command_sequence. */
sctp_cmd_seq_t *sctp_new_cmd_seq(int priority)
{
sctp_cmd_seq_t *retval = t_new(sctp_cmd_seq_t, priority);
/* XXX Check for NULL? -DaveM */
sctp_init_cmd_seq(retval);
return retval;
}
/* 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;
}
/* Rewind an sctp_cmd_seq_t to iterate from the start. */
int sctp_rewind_sequence(sctp_cmd_seq_t *seq)
{
seq->next_cmd = 0;
return 1; /* We always succeed. */
}
/* 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;
}
/* Dispose of a command sequence. */
void sctp_free_cmd_seq(sctp_cmd_seq_t *seq)
{
kfree(seq);
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_crc32c.c,v 1.9 2002/07/12 14:50:25 jgrimm Exp $
*
* 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_crc32c.c,v 1.9 2002/07/12 14:50:25 jgrimm Exp $";
/* 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 */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
__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 count_crc(__u8 *buffer, __u16 length)
{
__u32 crc32 = ~(__u32) 0;
__u32 i, result;
__u8 byte0, byte1, byte2, byte3;
/* 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]);
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;
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 International Business Machines 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.
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_debug.c,v 1.10 2002/07/12 14:50:25 jgrimm Exp $
*
* 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_debug.c,v 1.10 2002/07/12 14:50:25 jgrimm Exp $";
#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";
default:
return "unknown chunk";
};
return "unknown chunk";
}
/* These are printable form of variable-length parameters. */
const char *sctp_param_tbl[SCTP_PARAM_ECN_CAPABLE + 1] = {
"",
"PARAM_HEATBEAT_INFO",
"",
"",
"",
"PARAM_IPV4_ADDRESS",
"PARAM_IPV6_ADDRESS",
"PARAM_STATE_COOKIE",
"PARAM_UNRECOGNIZED_PARAMETERS",
"PARAM_COOKIE_PRESERVATIVE",
"",
"PARAM_HOST_NAME_ADDRESS",
"PARAM_SUPPORTED_ADDRESS_TYPES",
};
/* 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_INITIALIZE",
"PRIMITIVE_ASSOCIATE",
"PRIMITIVE_SHUTDOWN",
"PRIMITIVE_ABORT",
"PRIMITIVE_SEND",
"PRIMITIVE_SETPRIMARY",
"PRIMITIVE_RECEIVE",
"PRIMITIVE_STATUS",
"PRIMITIVE_CHANGEHEARTBEAT",
"PRIMITIVE_REQUESTHEARTBEAT",
"PRIMITIVE_GETSRTTREPORT",
"PRIMITIVE_SETFAILURETHRESHOLD",
"PRIMITIVE_SETPROTOPARAMETERS",
"PRIMITIVE_RECEIVE_UNSENT",
"PRIMITIVE_RECEIVE_UNACKED",
"PRIMITIVE_DESTROY"
};
/* 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_UNREACHFRAG"
};
/* 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_HEARTBEAT",
"TIMEOUT_SACK",
"TIMEOUT_AUTOCLOSE",
"TIMEOUT_PMTU_RAISE",
};
/* 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";
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_endpointola.c,v 1.26 2002/08/16 19:30:49 jgrimm Exp $
*
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_endpointola.c,v 1.26 2002/08/16 19:30:49 jgrimm Exp $";
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/in.h>
#include <linux/random.h> /* get_random_bytes() */
#include <net/sock.h>
#include <net/ipv6.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
/* Forward declarations for internal helpers. */
static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep);
/* Create a sctp_endpoint_t with all that boring stuff initialized.
* Returns NULL if there isn't enough memory.
*/
sctp_endpoint_t *sctp_endpoint_new(sctp_protocol_t *proto,
struct sock *sk, int priority)
{
sctp_endpoint_t *ep;
/* Build a local endpoint. */
ep = t_new(sctp_endpoint_t, priority);
if (!ep)
goto fail;
if (!sctp_endpoint_init(ep, proto, sk, priority))
goto fail_init;
ep->base.malloced = 1;
SCTP_DBG_OBJCNT_INC(ep);
return ep;
fail_init:
kfree(ep);
fail:
return NULL;
}
/*
* Initialize the base fields of the endpoint structure.
*/
sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto,
struct sock *sk, int priority)
{
memset(ep, 0, sizeof(sctp_endpoint_t));
/* 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_inqueue_init(&ep->base.inqueue);
/* Set its top-half handler */
sctp_inqueue_set_th_handler(&ep->base.inqueue,
(void (*)(void *))sctp_endpoint_bh_rcv,
ep);
/* Initialize the bind addr area */
sctp_bind_addr_init(&ep->base.bind_addr, 0);
ep->base.addr_lock = RW_LOCK_UNLOCKED;
/* Remember who we are attached to. */
ep->base.sk = sk;
sock_hold(ep->base.sk);
/* This pointer is useful to access the default protocol parameter
* values.
*/
ep->proto = proto;
/* Create the lists of associations. */
INIT_LIST_HEAD(&ep->asocs);
/* Set up the base timeout information. */
ep->timeouts[SCTP_EVENT_TIMEOUT_NONE] = 0;
ep->timeouts[SCTP_EVENT_TIMEOUT_T1_COOKIE]
= SCTP_DEFAULT_TIMEOUT_T1_COOKIE;
ep->timeouts[SCTP_EVENT_TIMEOUT_T1_INIT]
= SCTP_DEFAULT_TIMEOUT_T1_INIT;
ep->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN]
= sctp_sk(sk)->rtoinfo.srto_initial;
ep->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] = 0;
ep->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = 0;
ep->timeouts[SCTP_EVENT_TIMEOUT_HEARTBEAT]
= SCTP_DEFAULT_TIMEOUT_HEARTBEAT;
ep->timeouts[SCTP_EVENT_TIMEOUT_SACK]
= SCTP_DEFAULT_TIMEOUT_SACK;
ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE]
= sctp_sk(sk)->autoclose * HZ;
ep->timeouts[SCTP_EVENT_TIMEOUT_PMTU_RAISE]
= SCTP_DEFAULT_TIMEOUT_PMTU_RAISE;
/* Set up the default send/receive buffer space. */
/* FIXME - Should the min and max window size be configurable
* sysctl parameters as opposed to be constants?
*/
sk->rcvbuf = SCTP_DEFAULT_MAXWINDOW;
sk->sndbuf = SCTP_DEFAULT_MAXWINDOW * 2;
/* Use SCTP specific send buffer space queues. */
sk->write_space = sctp_write_space;
sk->use_write_queue = 1;
/* 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;
ep->debug_name = "unnamedEndpoint";
return ep;
}
/* Add an association to an endpoint. */
void sctp_endpoint_add_asoc(sctp_endpoint_t *ep, sctp_association_t *asoc)
{
/* Now just add it to our list of asocs */
list_add_tail(&asoc->asocs, &ep->asocs);
}
/* Free the endpoint structure. Delay cleanup until
* all users have released their reference count on this structure.
*/
void sctp_endpoint_free(sctp_endpoint_t *ep)
{
ep->base.dead = 1;
sctp_endpoint_put(ep);
}
/* Final destructor for endpoint. */
void sctp_endpoint_destroy(sctp_endpoint_t *ep)
{
SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return);
/* Unlink this endpoint, so we can't find it again! */
sctp_unhash_endpoint(ep);
/* Cleanup the inqueue. */
sctp_inqueue_free(&ep->base.inqueue);
sctp_bind_addr_free(&ep->base.bind_addr);
/* Remove and free the port */
if (ep->base.sk->prev != NULL)
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(sctp_endpoint_t *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(sctp_endpoint_t *ep)
{
if (atomic_dec_and_test(&ep->base.refcnt))
sctp_endpoint_destroy(ep);
}
/* Is this the endpoint we are looking for? */
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep,
const sockaddr_storage_t *laddr)
{
sctp_endpoint_t *retval;
sctp_read_lock(&ep->base.addr_lock);
if (ep->base.bind_addr.port == laddr->v4.sin_port) {
if (sctp_bind_addr_has_addr(&ep->base.bind_addr, laddr)) {
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.
*/
sctp_association_t *__sctp_endpoint_lookup_assoc(const sctp_endpoint_t *endpoint,
const sockaddr_storage_t *paddr,
sctp_transport_t **transport)
{
int rport;
sctp_association_t *asoc;
list_t *pos;
rport = paddr->v4.sin_port;
list_for_each(pos, &endpoint->asocs) {
asoc = list_entry(pos, sctp_association_t, 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. */
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
const sockaddr_storage_t *paddr,
sctp_transport_t **transport)
{
sctp_association_t *asoc;
sctp_local_bh_disable();
asoc = __sctp_endpoint_lookup_assoc(ep, paddr, transport);
sctp_local_bh_enable();
return asoc;
}
/* 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(sctp_endpoint_t *ep)
{
sctp_association_t *asoc;
struct sock *sk;
sctp_transport_t *transport;
sctp_chunk_t *chunk;
sctp_inqueue_t *inqueue;
sctp_subtype_t subtype;
sctp_state_t state;
int error = 0;
if (ep->base.dead)
goto out;
asoc = NULL;
inqueue = &ep->base.inqueue;
sk = ep->base.sk;
while (NULL != (chunk = sctp_pop_inqueue(inqueue))) {
subtype.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;
if (chunk->transport)
chunk->transport->last_time_heard = jiffies;
/* FIX ME We really would rather NOT have to use
* GFP_ATOMIC.
*/
error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state,
ep, asoc, chunk, GFP_ATOMIC);
if (error != 0)
goto err_out;
/* 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)
goto out;
}
err_out:
/* Is this the right way to pass errors up to the ULP? */
if (error)
ep->base.sk->err = -error;
out:
}
/* SCTP reference Implementation Copyright (C) 1999 Cisco And Motorola
*
* This file origiantes from Randy Stewart's SCTP 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:
* Randy Stewart rstewar1@email.mot.com
* Ken Morneau kmorneau@cisco.com
* Qiaobing Xie qxie1@email.mot.com
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorperated into the next SCTP release.
*
* There are still LOTS of bugs in this code... I always run on the motto
* "it is a wonder any code ever works :)"
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_hashdriver.c,v 1.2 2002/07/19 22:00:33 jgrimm Exp $";
#include <linux/types.h>
#include <asm/string.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sla1.h>
/* SCTP Main driver.
* passing a two pointers and two lengths,
* returning a digest pointer filled. The md5 code
* was taken directly from the RFC (2104) so to understand it
* you may want to go look at the RFC referenced in the
* SCTP spec. We did modify this code to either user OUR
* implementation of SLA1 or the MD5 that comes from its
* RFC. SLA1 may have IPR issues so you need to check in
* to this if you wish to use it... Or at least that is
* what the FIP-180.1 web page says.
*/
void sctp_hash_digest(const char *key, const int in_key_len,
const char *text, const int text_len,
__u8 *digest)
{
int key_len = in_key_len;
struct SLA_1_Context context;
__u8 k_ipad[65]; /* inner padding -
* key XORd with ipad
*/
__u8 k_opad[65]; /* outer padding -
* key XORd with opad
*/
__u8 tk[20];
int i;
/* if key is longer than 64 bytes reset it to key=MD5(key) */
if (key_len > 64) {
struct SLA_1_Context tctx;
SLA1_Init(&tctx);
SLA1_Process(&tctx, key, key_len);
SLA1_Final(&tctx,tk);
key = tk;
key_len = 20;
}
/*
* the HMAC_MD5 transform looks like:
*
* MD5(K XOR opad, MD5(K XOR ipad, text))
*
* where K is an n byte key
* ipad is the byte 0x36 repeated 64 times
* opad is the byte 0x5c repeated 64 times
* and text is the data being protected
*/
/* start out by storing key in pads */
memset(k_ipad, 0, sizeof k_ipad);
memset(k_opad, 0, sizeof k_opad);
memcpy(k_ipad, key, key_len);
memcpy(k_opad, key, key_len);
/* XOR key with ipad and opad values */
for (i = 0; i < 64; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/* perform inner hash */
SLA1_Init(&context); /* init context for 1st
* pass
*/
SLA1_Process(&context, k_ipad, 64); /* start with inner pad */
SLA1_Process(&context, text, text_len); /* then text of datagram */
SLA1_Final(&context,digest); /* finish up 1st pass */
/*
* perform outer hash
*/
SLA1_Init(&context); /* init context for 2nd
* pass
*/
SLA1_Process(&context, k_opad, 64); /* start with outer pad */
SLA1_Process(&context, digest, 20); /* then results of 1st
* hash
*/
SLA1_Final(&context, digest); /* finish up 2nd pass */
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_input.c,v 1.24 2002/07/24 12:26:20 jgrimm Exp $
*
* 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_input.c,v 1.24 2002/07/24 12:26:20 jgrimm Exp $";
#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 <linux/ipsec.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
/* Forward declarations for internal helpers. */
static int sctp_rcv_ootb(struct sk_buff *);
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr,
sctp_transport_t **transportp);
sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const sockaddr_storage_t *laddr);
/* Initialize a sockaddr_storage from in incoming skb.
* FIXME: This belongs with AF specific sctp_func_t. --jgrimm
*/
static sockaddr_storage_t *sctp_sockaddr_storage_init(sockaddr_storage_t *addr,
const struct sk_buff *skb,
int is_saddr)
{
sockaddr_storage_t *ret = NULL;
void *to, *saddr, *daddr;
__u16 *port;
size_t len;
struct sctphdr *sh;
switch (skb->nh.iph->version) {
case 4:
to = &addr->v4.sin_addr.s_addr;
port = &addr->v4.sin_port;
saddr = &skb->nh.iph->saddr;
daddr = &skb->nh.iph->daddr;
len = sizeof(struct in_addr);
addr->v4.sin_family = AF_INET;
break;
case 6:
SCTP_V6(
to = &addr->v6.sin6_addr;
port = &addr->v6.sin6_port;
saddr = &skb->nh.ipv6h->saddr;
daddr = &skb->nh.ipv6h->daddr;
len = sizeof(struct in6_addr);
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_flowinfo = 0; /* FIXME */
addr->v6.sin6_scope_id = 0; /* FIXME */
break;
)
default:
goto out;
};
sh = (struct sctphdr *) skb->h.raw;
if (is_saddr) {
*port = ntohs(sh->source);
memcpy(to, saddr, len);
} else {
*port = ntohs(sh->dest);
memcpy(to, daddr, len);
}
ret = addr;
out:
return ret;
}
/* Calculate the SCTP checksum of an SCTP packet. */
static inline int sctp_rcv_checksum(struct sk_buff *skb)
{
struct sctphdr *sh;
__u32 cmp, val;
sh = (struct sctphdr *) skb->h.raw;
cmp = ntohl(sh->checksum);
val = count_crc((__u8 *)sh, skb->len);
if (val != cmp) {
/* CRC failure, dump it. */
return -1;
}
return 0;
}
/*
* This is the routine which IP calls when receiving an SCTP packet.
*/
int sctp_rcv(struct sk_buff *skb)
{
struct sock *sk;
sctp_association_t *asoc;
sctp_endpoint_t *ep = NULL;
sctp_endpoint_common_t *rcvr;
sctp_transport_t *transport = NULL;
sctp_chunk_t *chunk;
struct sctphdr *sh;
sockaddr_storage_t src;
sockaddr_storage_t dest;
int ret = 0;
if (skb->pkt_type!=PACKET_HOST)
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 bad_packet;
if (sctp_rcv_checksum(skb) < 0)
goto bad_packet;
skb_pull(skb, sizeof(struct sctphdr));
sctp_sockaddr_storage_init(&src, skb, 1);
sctp_sockaddr_storage_init(&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 (!sctp_addr_is_valid(&src) || !sctp_addr_is_valid(&dest))
goto discard_it;
asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport);
/*
* 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) {
ep = __sctp_rcv_lookup_endpoint(&dest);
if (sctp_rcv_ootb(skb))
goto discard_release;
}
/* Retrieve the common input handling substructure. */
rcvr = asoc ? &asoc->base : &ep->base;
sk = rcvr->sk;
if (!ipsec_sk_policy(sk, skb))
goto discard_release;
/* Create an SCTP packet structure. */
chunk = sctp_chunkify(skb, asoc, sk);
if (!chunk) {
ret = -ENOMEM;
goto discard_release;
}
/* Remember what endpoint is to handle this packet. */
chunk->rcvr = rcvr;
/* Remember the SCTP header. */
chunk->sctp_hdr = sh;
/* Set the source address. */
sctp_init_source(chunk);
/* 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 (__sctp_sock_busy(sk)) {
sk_add_backlog(sk, (struct sk_buff *) chunk);
} else {
sctp_backlog_rcv(sk, (struct sk_buff *) chunk);
}
/* Release the sock and any reference counts we took in the
* lookup calls.
*/
sctp_bh_unlock_sock(sk);
if (asoc) {
sctp_association_put(asoc);
} else {
sctp_endpoint_put(ep);
}
sock_put(sk);
return ret;
bad_packet:
#if 0 /* FIXME */
SCTP_INC_STATS(SctpInErrs);
#endif /* FIXME*/
discard_it:
kfree_skb(skb);
return ret;
discard_release:
/* Release any structures we may be holding. */
if (asoc) {
sock_put(asoc->base.sk);
sctp_association_put(asoc);
} else {
sock_put(ep->base.sk);
sctp_endpoint_put(ep);
}
goto discard_it;
}
/* Handle second half of inbound skb processing. If the sock was busy,
* we may have need to delay processing until later when the sock is
* released (on the backlog). If not busy, we call this routine
* directly from the bottom half.
*/
int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
{
sctp_chunk_t *chunk;
sctp_inqueue_t *inqueue;
/* One day chunk will live inside the skb, but for
* now this works.
*/
chunk = (sctp_chunk_t *) skb;
inqueue = &chunk->rcvr->inqueue;
sctp_push_inqueue(inqueue, chunk);
return 0;
}
/*
* 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)
{
/* This should probably involve a call to SCTPhandleICMP(). */
}
/*
* 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;
ch = (sctp_chunkhdr_t *) skb->data;
/* Scan through all the chunks in the packet. */
do {
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
/* 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 (ch->type == SCTP_CID_SHUTDOWN_COMPLETE)
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 (ch->type == SCTP_CID_COOKIE_ACK)
goto discard;
if (ch->type == SCTP_CID_ERROR) {
/* FIXME - Need to check the "Stale cookie" ERROR. */
goto discard;
}
ch = (sctp_chunkhdr_t *) ch_end;
} while (ch_end < skb->tail);
return 0;
discard:
return 1;
}
/* Insert endpoint into the hash table. */
void __sctp_hash_endpoint(sctp_endpoint_t *ep)
{
sctp_endpoint_common_t **epp;
sctp_endpoint_common_t *epb;
sctp_hashbucket_t *head;
epb = &ep->base;
epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
head = &sctp_proto.ep_hashbucket[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(sctp_endpoint_t *ep)
{
sctp_local_bh_disable();
__sctp_hash_endpoint(ep);
sctp_local_bh_enable();
}
/* Remove endpoint from the hash table. */
void __sctp_unhash_endpoint(sctp_endpoint_t *ep)
{
sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb;
epb = &ep->base;
epb->hashent = sctp_ep_hashfn(epb->bind_addr.port);
head = &sctp_proto.ep_hashbucket[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(sctp_endpoint_t *ep)
{
sctp_local_bh_disable();
__sctp_unhash_endpoint(ep);
sctp_local_bh_enable();
}
/* Look up an endpoint. */
sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const sockaddr_storage_t *laddr)
{
sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb;
sctp_endpoint_t *ep;
int hash;
hash = sctp_ep_hashfn(laddr->v4.sin_port);
head = &sctp_proto.ep_hashbucket[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);
sock_hold(epb->sk);
read_unlock(&head->lock);
return ep;
}
/* Add an association to the hash. Local BH-safe. */
void sctp_hash_established(sctp_association_t *asoc)
{
sctp_local_bh_disable();
__sctp_hash_established(asoc);
sctp_local_bh_enable();
}
/* Insert association into the hash table. */
void __sctp_hash_established(sctp_association_t *asoc)
{
sctp_endpoint_common_t **epp;
sctp_endpoint_common_t *epb;
sctp_hashbucket_t *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_proto.assoc_hashbucket[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);
}
/* Remove association from the hash table. Local BH-safe. */
void sctp_unhash_established(sctp_association_t *asoc)
{
sctp_local_bh_disable();
__sctp_unhash_established(asoc);
sctp_local_bh_enable();
}
/* Remove association from the hash table. */
void __sctp_unhash_established(sctp_association_t *asoc)
{
sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb;
epb = &asoc->base;
epb->hashent = sctp_assoc_hashfn(epb->bind_addr.port,
asoc->peer.port);
head = &sctp_proto.assoc_hashbucket[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);
}
/* Look up an association. */
sctp_association_t *__sctp_rcv_lookup_association(const sockaddr_storage_t *laddr,
const sockaddr_storage_t *paddr,
sctp_transport_t **transportp)
{
sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb;
sctp_association_t *asoc;
sctp_transport_t *transport;
int hash;
/* Optimize here for direct hit, only listening connections can
* have wildcards anyways.
*/
hash = sctp_assoc_hashfn(laddr->v4.sin_port, paddr->v4.sin_port);
head = &sctp_proto.assoc_hashbucket[hash];
read_lock(&head->lock);
for (epb = head->chain; epb; epb = epb->next) {
asoc = sctp_assoc(epb);
transport = sctp_assoc_is_match(asoc, laddr, paddr);
if (transport)
goto hit;
}
read_unlock(&head->lock);
return NULL;
hit:
*transportp = transport;
sctp_association_hold(asoc);
sock_hold(epb->sk);
read_unlock(&head->lock);
return asoc;
}
/*
* 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 sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
const sockaddr_storage_t *laddr, sctp_transport_t **transportp)
{
sctp_association_t *asoc;
sockaddr_storage_t addr;
sockaddr_storage_t *paddr = &addr;
struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
sctp_chunkhdr_t *ch;
__u8 *ch_end, *data;
sctpParam_t parm;
ch = (sctp_chunkhdr_t *) skb->data;
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
if (SCTP_CID_INIT_ACK != ch->type)
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.
*/
data = skb->data + sizeof(sctp_init_chunk_t);
/* See sctp_process_init() for how to go thru TLVs. */
while (data < ch_end) {
parm.v = data;
if (!parm.p->length)
break;
data += WORD_ROUND(ntohs(parm.p->length));
/* Note: Ignoring hostname addresses. */
if ((SCTP_PARAM_IPV4_ADDRESS != parm.p->type) &&
(SCTP_PARAM_IPV6_ADDRESS != parm.p->type))
continue;
sctp_param2sockaddr(paddr, parm, ntohs(sh->source));
asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp);
if (asoc)
return asoc;
}
return NULL;
}
/* Lookup an association for an inbound skb. */
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
const sockaddr_storage_t *paddr,
const sockaddr_storage_t *laddr,
sctp_transport_t **transportp)
{
sctp_association_t *asoc;
asoc = __sctp_rcv_lookup_association(laddr, paddr, transportp);
/* Further lookup for INIT-ACK packet.
* SCTP Implementors Guide, 2.18 Handling of address
* parameters within the INIT or INIT-ACK.
*/
if (!asoc)
asoc = __sctp_rcv_initack_lookup(skb, laddr, transportp);
return asoc;
}
/* 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_inqueue.c,v 1.10 2002/05/20 22:05:54 jgrimm Exp $
*
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_inqueue.c,v 1.10 2002/05/20 22:05:54 jgrimm Exp $";
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
#include <linux/interrupt.h>
/* Initialize an SCTP_inqueue. */
void sctp_inqueue_init(sctp_inqueue_t *queue)
{
skb_queue_head_init(&queue->in);
queue->in_progress = NULL;
/* Create a task for delivering data. */
INIT_LIST_HEAD(&queue->immediate.list);
queue->immediate.sync = 0;
queue->immediate.routine = NULL;
queue->immediate.data = NULL;
queue->malloced = 0;
}
/* Create an initialized SCTP_inqueue. */
sctp_inqueue_t *sctp_inqueue_new(void)
{
sctp_inqueue_t *retval;
retval = t_new(sctp_inqueue_t, GFP_ATOMIC);
if (retval) {
sctp_inqueue_init(retval);
retval->malloced = 1;
}
return retval;
}
/* Release the memory associated with an SCTP inqueue. */
void sctp_inqueue_free(sctp_inqueue_t *queue)
{
sctp_chunk_t *chunk;
/* Empty the queue. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&queue->in)) != NULL)
sctp_free_chunk(chunk);
/* If there is a packet which is currently being worked on,
* free it as well.
*/
if (queue->in_progress)
sctp_free_chunk(queue->in_progress);
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_push_inqueue(sctp_inqueue_t *q, sctp_chunk_t *packet)
{
/* 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.
*/
skb_queue_tail(&(q->in), (struct sk_buff *) packet);
q->immediate.routine(q->immediate.data);
}
/* 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.
*/
sctp_chunk_t *sctp_pop_inqueue(sctp_inqueue_t *queue)
{
sctp_chunk_t *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) != NULL) {
/* 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_free_chunk(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) {
/* Is the queue empty? */
if (skb_queue_empty(&queue->in))
return NULL;
chunk = queue->in_progress =
(sctp_chunk_t *) skb_dequeue(&queue->in);
/* This is the first chunk in the packet. */
chunk->singleton = 1;
ch = (sctp_chunkhdr_t *) chunk->skb->data;
}
chunk->chunk_hdr = ch;
chunk->chunk_end = ((__u8 *) ch)
+ WORD_ROUND(ntohs(ch->length));
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 {
/* 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_pop_inqueue+++ 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_push_inqueue() 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_inqueue_set_th_handler(sctp_inqueue_t *q,
void (*callback)(void *), void *arg)
{
q->immediate.routine = callback;
q->immediate.data = arg;
}
/* SCTP kernel reference Implementation
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
* Copyright (c) 2002 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_ipv6.c,v 1.12 2002/08/16 19:30:49 jgrimm Exp $
*
* SCTP over IPv6.
*
* 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:
* Le Yanqun <yanqun.le@nokia.com>
* Hui Huang <hui.huang@nokia.com>
* La Monte H.P. Yarroll <piggy@acm.org>
* Sridhar Samudrala <sri@us.ibm.com>
* Jon Grimm <jgrimm@us.ibm.com>
*
* Based on:
* linux/net/ipv6/tcp_ipv6.c
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_ipv6.c,v 1.12 2002/08/16 19:30:49 jgrimm Exp $";
#define __NO_VERSION__
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/sockios.h>
#include <linux/net.h>
#include <linux/sched.h>
#include <linux/in.h>
#include <linux/in6.h>
#include <linux/netdevice.h>
#include <linux/init.h>
#include <linux/ipsec.h>
#include <linux/ipv6.h>
#include <linux/icmpv6.h>
#include <linux/random.h>
#include <net/protocol.h>
#include <net/tcp.h>
#include <net/ndisc.h>
#include <net/ipv6.h>
#include <net/transp_v6.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
#include <net/inet_common.h>
#include <net/inet_ecn.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp.h>
#include <asm/uaccess.h>
/* FIXME: Cleanup so we don't need TEST_FRAME here. */
#ifndef TEST_FRAME
/* FIXME: Comments. */
static inline void sctp_v6_err(struct sk_buff *skb,
struct inet6_skb_parm *opt,
int type, int code, int offset, __u32 info)
{
/* BUG. WRITE ME. */
}
/* Based on tcp_v6_xmit() in tcp_ipv6.c. */
static inline int sctp_v6_xmit(struct sk_buff *skb)
{
struct sock *sk = skb->sk;
struct ipv6_pinfo *np = inet6_sk(sk);
struct flowi fl;
struct dst_entry *dst;
struct in6_addr saddr;
int err = 0;
fl.proto = sk->protocol;
fl.fl6_dst = &np->daddr;
fl.fl6_src = NULL;
fl.fl6_flowlabel = np->flow_label;
IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel);
fl.oif = sk->bound_dev_if;
fl.uli_u.ports.sport = inet_sk(sk)->sport;
fl.uli_u.ports.dport = inet_sk(sk)->dport;
if (np->opt && np->opt->srcrt) {
struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt;
fl.nl_u.ip6_u.daddr = rt0->addr;
}
dst = __sk_dst_check(sk, np->dst_cookie);
if (dst == NULL) {
dst = ip6_route_output(sk, &fl);
if (dst->error) {
sk->err_soft = -dst->error;
dst_release(dst);
return -sk->err_soft;
}
ip6_dst_store(sk, dst, NULL);
}
skb->dst = dst_clone(dst);
/* FIXME: This is all temporary until real source address
* selection is done.
*/
if (ipv6_addr_any(&np->saddr)) {
err = ipv6_get_saddr(dst, fl.fl6_dst, &saddr);
if (err)
printk(KERN_ERR "sctp_v6_xmit: no saddr available\n");
/* FIXME: This is a workaround until we get
* real source address selection done. This is here
* to disallow loopback when the scoping rules have
* not bound loopback to the endpoint.
*/
if (sctp_ipv6_addr_type(&saddr) & IPV6_ADDR_LOOPBACK) {
if (!(sctp_ipv6_addr_type(&np->daddr) &
IPV6_ADDR_LOOPBACK)) {
ipv6_addr_copy(&saddr, &np->daddr);
}
}
fl.fl6_src = &saddr;
} else {
fl.fl6_src = &np->saddr;
}
/* Restore final destination back after routing done */
fl.nl_u.ip6_u.daddr = &np->daddr;
return ip6_xmit(sk, skb, &fl, np->opt);
}
#endif /* TEST_FRAME */
/* Returns the mtu for the given v6 destination address. */
int sctp_v6_get_dst_mtu(const sockaddr_storage_t *address)
{
struct dst_entry *dst;
struct flowi fl;
int dst_mtu = SCTP_DEFAULT_MAXSEGMENT;
fl.proto = 0;
fl.fl6_dst = (struct in6_addr *)&address->v6.sin6_addr;
fl.fl6_src = NULL;
fl.fl6_flowlabel = 0;
fl.oif = 0;
fl.uli_u.ports.sport = 0;
fl.uli_u.ports.dport = 0;
dst = ip6_route_output(NULL, &fl);
if (dst) {
dst_mtu = dst->pmtu;
SCTP_DEBUG_PRINTK("sctp_v6_get_dst_mtu: "
"ip6_route_output: dev:%s pmtu:%d\n",
dst->dev->name, dst_mtu);
dst_release(dst);
} else {
SCTP_DEBUG_PRINTK("sctp_v6_get_dst_mtu: "
"ip6_route_output failed, returning "
"%d as dst_mtu\n", dst_mtu);
}
return dst_mtu;
}
static struct proto_ops inet6_seqpacket_ops = {
.family = PF_INET6,
.release = inet6_release,
.bind = inet6_bind,
.connect = inet_dgram_connect,
.socketpair = sock_no_socketpair,
.accept = inet_accept,
.getname = inet6_getname,
.poll = sctp_poll,
.ioctl = inet6_ioctl,
.listen = sctp_inet_listen,
.shutdown = inet_shutdown,
.setsockopt = inet_setsockopt,
.getsockopt = inet_getsockopt,
.sendmsg = inet_sendmsg,
.recvmsg = inet_recvmsg,
.mmap = sock_no_mmap,
};
static struct inet_protosw sctpv6_protosw = {
.type = SOCK_SEQPACKET,
.protocol = IPPROTO_SCTP,
.prot = &sctp_prot,
.ops = &inet6_seqpacket_ops,
.capability = -1,
.no_check = 0,
.flags = SCTP_PROTOSW_FLAG
};
static struct inet6_protocol sctpv6_protocol = {
.handler = sctp_rcv,
.err_handler = sctp_v6_err,
.next = NULL,
.protocol = IPPROTO_SCTP,
.copy = 0,
.data = NULL,
.name = "SCTPv6"
};
static sctp_func_t sctp_ipv6_specific = {
.queue_xmit = sctp_v6_xmit,
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
.get_dst_mtu = sctp_v6_get_dst_mtu,
.net_header_len = sizeof(struct ipv6hdr),
.sockaddr_len = sizeof(struct sockaddr_in6),
.sa_family = AF_INET6,
};
/* Initialize IPv6 support and register with inet6 stack. */
int sctp_v6_init(void)
{
/* Add SCTPv6 to inetsw6 linked list. */
inet6_register_protosw(&sctpv6_protosw);
/* Register inet6 protocol. */
inet6_add_protocol(&sctpv6_protocol);
/* Fill in address family info. */
INIT_LIST_HEAD(&sctp_ipv6_specific.list);
list_add_tail(&sctp_ipv6_specific.list, &sctp_proto.address_families);
return 0;
}
/* IPv6 specific exit support. */
void sctp_v6_exit(void)
{
list_del(&sctp_ipv6_specific.list);
inet6_del_protocol(&sctpv6_protocol);
inet6_unregister_protosw(&sctpv6_protosw);
}
/* SCTP kernel reference Implementation
* Copyright (c) 2001 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_objcnt.c,v 1.5 2002/07/12 14:50:25 jgrimm Exp $
*
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_objcnt.c,v 1.5 2002/07/12 14:50:25 jgrimm Exp $";
#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(chunk);
SCTP_DBG_OBJCNT(addr);
/* An array to make it easy to pretty print the debug information
* to the proc fs.
*/
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(addr),
};
/* 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 = sizeof(sctp_dbg_objcnt)/sizeof(sctp_dbg_objcnt[0]);
/* 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)
{
create_proc_read_entry("sctp_dbg_objcnt", 0, proc_net_sctp,
sctp_dbg_objcnt_read, NULL);
}
/* Cleanup the objcount entry in the proc filesystem. */
void sctp_dbg_objcnt_exit(void)
{
remove_proc_entry("sctp_dbg_objcount", proc_net_sctp);
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_output.c,v 1.22 2002/07/12 14:39:05 jgrimm Exp $
*
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_output.c,v 1.22 2002/07/12 14:39:05 jgrimm Exp $";
#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 <linux/types.h> /* For NULL, etc... */
#include <net/sock.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
/* Forward declarations for private helpers. */
__u32 count_crc(__u8 *ptr, __u16 count);
static void sctp_packet_reset(sctp_packet_t *packet);
static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
sctp_chunk_t *chunk);
/* Config a packet.
* This appears to be a followup set of initializations.)
*/
sctp_packet_t *sctp_packet_config(sctp_packet_t *packet,
__u32 vtag,
int ecn_capable,
sctp_packet_phandler_t *prepend_handler)
{
int packet_empty = (packet->size == SCTP_IP_OVERHEAD);
packet->vtag = vtag;
packet->ecn_capable = ecn_capable;
packet->get_prepend_chunk = prepend_handler;
packet->has_cookie_echo = 0;
/* We might need to call the prepend_handler right away. */
if (packet_empty)
sctp_packet_reset(packet);
return packet;
}
/* Initialize the packet structure. */
sctp_packet_t *sctp_packet_init(sctp_packet_t *packet,
sctp_transport_t *transport,
__u16 sport,
__u16 dport)
{
packet->transport = transport;
packet->source_port = sport;
packet->destination_port = dport;
skb_queue_head_init(&packet->chunks);
packet->vtag = 0;
packet->ecn_capable = 0;
packet->get_prepend_chunk = NULL;
packet->has_cookie_echo = 0;
packet->malloced = 0;
sctp_packet_reset(packet);
return packet;
}
/* Free a packet. */
void sctp_packet_free(sctp_packet_t *packet)
{
sctp_chunk_t *chunk;
while (NULL !=
(chunk = (sctp_chunk_t *)skb_dequeue(&packet->chunks))) {
sctp_free_chunk(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(sctp_packet_t *packet, sctp_chunk_t *chunk)
{
sctp_xmit_t retval;
int error = 0;
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->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_MUST_FRAG:
case SCTP_XMIT_RWND_FULL:
case SCTP_XMIT_OK:
break;
};
return retval;
}
/* Append a chunk to the offered packet reporting back any inability to do
* so.
*/
sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk)
{
sctp_xmit_t retval = SCTP_XMIT_OK;
__u16 chunk_len = WORD_ROUND(ntohs(chunk->chunk_hdr->length));
size_t psize = packet->size;
size_t pmtu;
int too_big;
pmtu = ((packet->transport->asoc) ?
(packet->transport->asoc->pmtu) :
(packet->transport->pmtu));
too_big = (psize + chunk_len > pmtu);
/* Decide if we need to fragment or resubmit later. */
if (too_big) {
int packet_empty = (packet->size == SCTP_IP_OVERHEAD);
/* Both control chunks and data chunks with TSNs are
* non-fragmentable.
*/
int fragmentable = sctp_chunk_is_data(chunk)
&& (!chunk->has_tsn);
if (packet_empty) {
if (fragmentable) {
retval = SCTP_XMIT_MUST_FRAG;
goto finish;
} else {
/* The packet is too big but we can
* not fragment it--we have to just
* transmit and rely on IP
* fragmentation.
*/
goto append;
}
} else { /* !packet_empty */
retval = SCTP_XMIT_PMTU_FULL;
goto finish;
}
} else {
/* The chunk fits in the packet. */
goto append;
}
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);
if (SCTP_XMIT_OK != retval)
goto finish;
} else if (SCTP_CID_COOKIE_ECHO == chunk->chunk_hdr->type) {
packet->has_cookie_echo = 1;
}
/* It is OK to send this chunk. */
skb_queue_tail(&packet->chunks,
(struct sk_buff *)chunk);
packet->size += chunk_len;
finish:
return retval;
}
/* All packets are sent to the network through this function from
* sctp_push_outqueue().
*
* The return value is a normal kernel error return value.
*/
int sctp_packet_transmit(sctp_packet_t *packet)
{
sctp_transport_t *transport = packet->transport;
sctp_association_t *asoc = transport->asoc;
struct sctphdr *sh;
__u32 crc32;
struct sk_buff *nskb;
sctp_chunk_t *chunk;
struct sock *sk;
int err = 0;
int padding; /* How much padding do we need? */
__u8 packet_has_data = 0;
/* Do NOT generate a chunkless packet... */
if (skb_queue_empty(&packet->chunks))
return err;
/* Set up convenience variables... */
chunk = (sctp_chunk_t *) (packet->chunks.next);
sk = chunk->skb->sk;
/* Allocate the new skb. */
nskb = dev_alloc_skb(packet->size);
if (!nskb) {
err = -ENOMEM;
goto out;
}
/* Make sure the outbound skb has enough header room reserved. */
skb_reserve(nskb, SCTP_IP_OVERHEAD);
/* Set the owning socket so that we know where to get the
* destination IP address.
*/
skb_set_owner_w(nskb, sk);
/**
* 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");
while (NULL != (chunk = (sctp_chunk_t *)
skb_dequeue(&packet->chunks))) {
chunk->num_times_sent++;
chunk->sent_at = jiffies;
if (sctp_chunk_is_data(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 ((1 == chunk->num_times_sent) &&
(!transport->rto_pending)) {
chunk->rtt_in_progress = 1;
transport->rto_pending = 1;
}
packet_has_data = 1;
}
memcpy(skb_put(nskb, chunk->skb->len),
chunk->skb->data, chunk->skb->len);
padding = WORD_ROUND(chunk->skb->len) - chunk->skb->len;
memset(skb_put(nskb, padding), 0, padding);
SCTP_DEBUG_PRINTK("%s %p[%s] %s 0x%x, %s %d, %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,
"num_times_sent", chunk->num_times_sent,
"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_free_chunk(chunk);
}
/* 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>.
*/
crc32 = count_crc((__u8 *)sh, nskb->len);
/* 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);
switch (transport->ipaddr.sa.sa_family) {
case AF_INET:
inet_sk(sk)->daddr = transport->ipaddr.v4.sin_addr.s_addr;
break;
case AF_INET6:
SCTP_V6(inet6_sk(sk)->daddr = transport->ipaddr.v6.sin6_addr;)
break;
default:
/* This is bogus address type, just bail. */
break;
};
/* 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."
*
* If ECN capable && negotiated && it makes sense for
* this packet to support it (e.g. post ECN negotiation)
* then lets set the ECT bit
*
* FIXME: Need to do something else for IPv6
*/
if (packet->ecn_capable) {
INET_ECN_xmit(nskb->sk);
} else {
INET_ECN_dontxmit(nskb->sk);
}
/* Set up the IP options. */
/* BUG: not implemented
* For v4 this all lives somewhere in sk->opt...
*/
/* Dump that on IP! */
if (asoc && asoc->peer.last_sent_to != transport) {
/* Considering the multiple CPU scenario, this is a
* "correcter" place for last_sent_to. --xguo
*/
asoc->peer.last_sent_to = transport;
}
/* Hey, before Linux changes, here's what we have to
* do to force IP routing to recognize the change of
* dest addr. --xguo
*/
if (sk->dst_cache)
sk->dst_cache->obsolete = 1;
if (packet_has_data) {
struct timer_list *timer;
unsigned long timeout;
transport->last_time_used = jiffies;
/* Restart the AUTOCLOSE timer when sending data. */
if ((SCTP_STATE_ESTABLISHED == asoc->state) &&
(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 length %d\n",
nskb->len);
(*transport->af_specific->queue_xmit)(nskb);
out:
packet->size = SCTP_IP_OVERHEAD;
return err;
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
/*
* This private function resets the packet to a fresh state.
*/
static void sctp_packet_reset(sctp_packet_t *packet)
{
sctp_chunk_t *chunk = NULL;
packet->size = SCTP_IP_OVERHEAD;
if (packet->get_prepend_chunk)
chunk = packet->get_prepend_chunk(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);
}
/* This private function handles the specifics of appending DATA chunks. */
static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet, sctp_chunk_t *chunk)
{
sctp_xmit_t retval = SCTP_XMIT_OK;
size_t datasize, rwnd, inflight;
sctp_transport_t *transport = packet->transport;
__u32 max_burst_bytes;
/* 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 = transport->asoc->peer.rwnd;
inflight = transport->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 = transport->asoc->max_burst * transport->asoc->pmtu;
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.
*/
if (transport->flight_size >= transport->cwnd) {
retval = SCTP_XMIT_RWND_FULL;
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. */
transport->asoc->outqueue.outstanding_bytes += datasize;
/* Update our view of the receiver's rwnd. */
if (datasize < rwnd) {
rwnd -= datasize;
} else {
rwnd = 0;
}
transport->asoc->peer.rwnd = rwnd;
finish:
return retval;
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001-2002 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_outqueue.c,v 1.35 2002/08/05 02:58:05 jgrimm Exp $
*
* These functions implement the outqueue class. The outqueue handles
* bundling and queueing of outgoing SCTP 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>
* Perry Melange <pmelange@null.cc.uic.edu>
* Xingang Guo <xingang.guo@intel.com>
* Hui Huang <hui.huang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com>
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_outqueue.c,v 1.35 2002/08/05 02:58:05 jgrimm Exp $";
#include <linux/types.h>
#include <linux/list.h> /* For struct list_head */
#include <linux/socket.h>
#include <linux/ip.h>
#include <net/sock.h> /* For skb_set_owner_w */
#include <net/sctp/sctp.h>
/* Declare internal functions here. */
static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn);
static void sctp_check_transmitted(sctp_outqueue_t *q,
struct list_head *transmitted_queue,
sctp_transport_t *transport,
sctp_sackhdr_t *sack);
/* Generate a new outqueue. */
sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *asoc)
{
sctp_outqueue_t *q;
q = t_new(sctp_outqueue_t, GFP_KERNEL);
if (q) {
sctp_outqueue_init(asoc, q);
q->malloced = 1;
}
return q;
}
/* Initialize an existing SCTP_outqueue. This does the boring stuff.
* You still need to define handlers if you really want to DO
* something with this structure...
*/
void sctp_outqueue_init(sctp_association_t *asoc, sctp_outqueue_t *q)
{
q->asoc = asoc;
skb_queue_head_init(&q->out);
skb_queue_head_init(&q->control);
INIT_LIST_HEAD(&q->retransmit);
INIT_LIST_HEAD(&q->sacked);
q->init_output = NULL;
q->config_output = NULL;
q->append_output = NULL;
q->build_output = NULL;
q->force_output = NULL;
q->outstanding_bytes = 0;
q->empty = 1;
q->malloced = 0;
}
/* Free the outqueue structure and any related pending chunks.
* FIXME: Add SEND_FAILED support.
*/
void sctp_outqueue_teardown(sctp_outqueue_t *q)
{
sctp_transport_t *transport;
list_t *lchunk, *pos;
sctp_chunk_t *chunk;
/* Throw away unacknowledged chunks. */
list_for_each(pos, &q->asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports);
while ((lchunk = sctp_list_dequeue(&transport->transmitted))) {
chunk = list_entry(lchunk, sctp_chunk_t,
transmitted_list);
sctp_free_chunk(chunk);
}
}
/* Throw away any leftover chunks. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->out)))
sctp_free_chunk(chunk);
}
/* Free the outqueue structure and any related pending chunks. */
void sctp_outqueue_free(sctp_outqueue_t *q)
{
/* Throw away leftover chunks. */
sctp_outqueue_teardown(q);
/* If we were kmalloc()'d, free the memory. */
if (q->malloced)
kfree(q);
}
/* Transmit any pending partial chunks. */
void sctp_force_outqueue(sctp_outqueue_t *q)
{
/* Do we really need this? */
/* BUG */
}
/* Put a new chunk in an SCTP_outqueue. */
int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
{
int error = 0;
SCTP_DEBUG_PRINTK("sctp_push_outqueue(%p, %p[%s])\n",
q, chunk, chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
: "Illegal Chunk");
/* If it is data, queue it up, otherwise, send it
* immediately.
*/
if (SCTP_CID_DATA == chunk->chunk_hdr->type) {
/* Is it OK to queue data chunks? */
/* From 9. Termination of Association
*
* When either endpoint performs a shutdown, the
* association on each peer will stop accepting new
* data from its user and only deliver data in queue
* at the time of sending or receiving the SHUTDOWN
* chunk.
*/
switch (q->asoc->state) {
case SCTP_STATE_EMPTY:
case SCTP_STATE_CLOSED:
case SCTP_STATE_SHUTDOWN_PENDING:
case SCTP_STATE_SHUTDOWN_SENT:
case SCTP_STATE_SHUTDOWN_RECEIVED:
case SCTP_STATE_SHUTDOWN_ACK_SENT:
/* Cannot send after transport endpoint shutdown */
error = -ESHUTDOWN;
break;
default:
SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n",
q, chunk,
chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
: "Illegal Chunk");
skb_queue_tail(&q->out, (struct sk_buff *) chunk);
q->empty = 0;
break;
};
} else {
skb_queue_tail(&q->control, (struct sk_buff *) chunk);
}
if (error < 0)
return error;
error = sctp_flush_outqueue(q, 0);
return error;
}
/* Mark all the eligible packets on a transport for retransmission and force
* one packet out.
*/
void sctp_retransmit(sctp_outqueue_t *q, sctp_transport_t *transport,
__u8 fast_retransmit)
{
struct list_head *lchunk;
sctp_chunk_t *chunk;
int error = 0;
struct list_head tlist;
if (fast_retransmit) {
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
} else {
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX);
}
INIT_LIST_HEAD(&tlist);
while (!list_empty(&transport->transmitted)) {
lchunk = sctp_list_dequeue(&transport->transmitted);
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
/* If we are doing retransmission due to a fast retransmit,
* only the chunk's that are marked for fast retransmit
* should be added to the retransmit queue. If we are doing
* retransmission due to a timeout, only the chunks that are
* not yet acked should be added to the retransmit queue.
*/
if ((fast_retransmit && !chunk->fast_retransmit) ||
(!fast_retransmit && chunk->tsn_gap_acked)) {
list_add_tail(lchunk, &tlist);
} else {
/* RFC 2960 6.2.1 Processing a Received SACK
*
* C) Any time a DATA chunk is marked for
* retransmission (via either T3-rtx timer expiration
* (Section 6.3.3) or via fast retransmit
* (Section 7.2.4)), add the data size of those
* chunks to the rwnd.
*/
q->asoc->peer.rwnd += sctp_data_size(chunk);
q->outstanding_bytes -= sctp_data_size(chunk);
transport->flight_size -= sctp_data_size(chunk);
/* sctpimpguide-05 Section 2.8.2
* M5) If a T3-rtx timer expires, the
* 'TSN.Missing.Report' of all affected TSNs is set
* to 0.
*/
chunk->tsn_missing_report = 0;
/* If a chunk that is being used for RTT measurement
* has to be retransmitted, we cannot use this chunk
* anymore for RTT measurements. Reset rto_pending so
* that a new RTT measurement is started when a new
* data chunk is sent.
*/
if (chunk->rtt_in_progress) {
chunk->rtt_in_progress = 0;
transport->rto_pending = 0;
}
list_add_tail(lchunk, &q->retransmit);
}
}
/* Reconstruct the transmitted queue with chunks that are not
* eligible for retransmission.
*/
while (NULL != (lchunk = sctp_list_dequeue(&tlist)))
list_add_tail(lchunk, &transport->transmitted);
SCTP_DEBUG_PRINTK(__FUNCTION__": transport: %p, fast_retransmit: %d, "
"cwnd: %d, ssthresh: %d, flight_size: %d, "
"pba: %d\n",transport, fast_retransmit,
transport->cwnd, transport->ssthresh,
transport->flight_size,
transport->partial_bytes_acked);
error = sctp_flush_outqueue(q, /* rtx_timeout */ 1);
if (error)
q->asoc->base.sk->err = -error;
}
/*
* Transmit DATA chunks on the retransmit queue. Upon return from
* sctp_flush_retran_queue() the packet 'pkt' may contain chunks which
* need to be transmitted by the caller.
* We assume that pkt->transport has already been set.
*
* The return value is a normal kernel error return value.
*/
static int sctp_flush_retran_queue(sctp_outqueue_t *q, sctp_packet_t *pkt,
int rtx_timeout, int *start_timer)
{
struct list_head *lqueue;
struct list_head *lchunk;
sctp_transport_t *transport = pkt->transport;
sctp_xmit_t status;
sctp_chunk_t *chunk;
sctp_association_t *asoc;
int error = 0;
asoc = q->asoc;
lqueue = &q->retransmit;
/* RFC 2960 6.3.3 Handle T3-rtx Expiration
*
* E3) Determine how many of the earliest (i.e., lowest TSN)
* outstanding DATA chunks for the address for which the
* T3-rtx has expired will fit into a single packet, subject
* to the MTU constraint for the path corresponding to the
* destination transport address to which the retransmission
* is being sent (this may be different from the address for
* which the timer expires [see Section 6.4]). Call this value
* K. Bundle and retransmit those K DATA chunks in a single
* packet to the destination endpoint.
*
* [Just to be painfully clear, if we are retransmitting
* because a timeout just happened, we should send only ONE
* packet of retransmitted data.]
*/
lchunk = sctp_list_dequeue(lqueue);
while (lchunk) {
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
#if 0
/* If a chunk has been tried for more than SCTP_DEF_MAX_SEND
* times, discard it, and check the empty flag of the outqueue.
*
* --xguo
*/
if (chunk->snd_count > SCTP_DEF_MAX_SEND) {
sctp_free_chunk(chunk);
continue;
}
#endif
/* Attempt to append this chunk to the packet. */
status = (*q->append_output)(pkt, chunk);
switch (status) {
case SCTP_XMIT_PMTU_FULL:
/* Send this packet. */
if ((error = (*q->force_output)(pkt)) == 0)
*start_timer = 1;
/* If we are retransmitting, we should only
* send a single packet.
*/
if (rtx_timeout) {
list_add(lchunk, lqueue);
lchunk = NULL;
}
/* Bundle lchunk in the next round. */
break;
case SCTP_XMIT_RWND_FULL:
/* Send this packet. */
if ((error = (*q->force_output)(pkt)) == 0)
*start_timer = 1;
/* Stop sending DATA as there is no more room
* at the reciever.
*/
list_add(lchunk, lqueue);
lchunk = NULL;
break;
default:
/* The append was successful, so add this chunk to
* the transmitted list.
*/
list_add_tail(lchunk,
&transport->transmitted);
*start_timer = 1;
q->empty = 0;
/* Retrieve a new chunk to bundle. */
lchunk = sctp_list_dequeue(lqueue);
break;
};
}
return error;
}
/* This routine either transmits the fragment or puts it on the output
* queue. 'pos' points to the next chunk in the output queue after the
* chunk that is currently in the process of fragmentation.
*/
void sctp_xmit_frag(sctp_outqueue_t *q, struct sk_buff *pos,
sctp_packet_t *packet,
sctp_chunk_t *frag, __u32 tsn)
{
sctp_transport_t *transport = packet->transport;
struct sk_buff_head *queue = &q->out;
sctp_xmit_t status;
int error;
frag->subh.data_hdr->tsn = htonl(tsn);
frag->has_tsn = 1;
/* An inner fragment may be smaller than the earlier one and may get
* in if we call q->build_output. This ensures that all the fragments
* are sent in order.
*/
if (!skb_queue_empty(queue)) {
SCTP_DEBUG_PRINTK("sctp_xmit_frag: q not empty. "
"adding 0x%x to outqueue\n",
ntohl(frag->subh.data_hdr->tsn));
if (pos) {
skb_insert(pos, (struct sk_buff *) frag);
} else {
skb_queue_tail(queue, (struct sk_buff *) frag);
}
return;
}
/* Add the chunk fragment to the packet. */
status = (*q->build_output)(packet, frag);
switch (status) {
case SCTP_XMIT_RWND_FULL:
/* RWND is full, so put the chunk in the output queue. */
SCTP_DEBUG_PRINTK("sctp_xmit_frag: rwnd full. "
"adding 0x%x to outqueue\n",
ntohl(frag->subh.data_hdr->tsn));
if (pos) {
skb_insert(pos, (struct sk_buff *) frag);
} else {
skb_queue_tail(queue, (struct sk_buff *) frag);
}
break;
case SCTP_XMIT_OK:
error = (*q->force_output)(packet);
if (error < 0) {
/* Packet could not be transmitted, put the chunk in
* the output queue
*/
SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output "
"failed. adding 0x%x to outqueue\n",
ntohl(frag->subh.data_hdr->tsn));
if (pos) {
skb_insert(pos, (struct sk_buff *) frag);
} else {
skb_queue_tail(queue, (struct sk_buff *) frag);
}
} else {
SCTP_DEBUG_PRINTK("sctp_xmit_frag: force output "
"success. 0x%x sent\n",
ntohl(frag->subh.data_hdr->tsn));
list_add_tail(&frag->transmitted_list,
&transport->transmitted);
sctp_transport_reset_timers(transport);
}
break;
default:
BUG();
};
}
/* This routine calls sctp_xmit_frag() for all the fragments of a message.
* The argument 'frag' point to the first fragment and it holds the list
* of all the other fragments in the 'frag_list' field.
*/
void sctp_xmit_fragmented_chunks(sctp_outqueue_t *q, sctp_packet_t *packet,
sctp_chunk_t *frag)
{
sctp_association_t *asoc = frag->asoc;
struct list_head *lfrag, *frag_list;
__u32 tsn;
int nfrags = 1;
struct sk_buff *pos;
/* Count the number of fragments. */
frag_list = &frag->frag_list;
list_for_each(lfrag, frag_list) {
nfrags++;
}
/* Get a TSN block of nfrags TSNs. */
tsn = __sctp_association_get_tsn_block(asoc, nfrags);
pos = skb_peek(&q->out);
/* Transmit the first fragment. */
sctp_xmit_frag(q, pos, packet, frag, tsn++);
/* Transmit the rest of fragments. */
frag_list = &frag->frag_list;
list_for_each(lfrag, frag_list) {
frag = list_entry(lfrag, sctp_chunk_t, frag_list);
sctp_xmit_frag(q, pos, packet, frag, tsn++);
}
}
/* This routine breaks the given chunk into 'max_frag_data_len' size
* fragments. It returns the first fragment with the frag_list field holding
* the remaining fragments.
*/
sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk, size_t max_frag_data_len)
{
sctp_association_t *asoc = chunk->asoc;
void *data_ptr = chunk->subh.data_hdr;
struct sctp_sndrcvinfo *sinfo = &chunk->sinfo;
__u16 chunk_data_len = sctp_data_size(chunk);
__u16 ssn = ntohs(chunk->subh.data_hdr->ssn);
sctp_chunk_t *first_frag, *frag;
struct list_head *frag_list;
int nfrags;
/* nfrags = no. of max size fragments + any smaller last fragment. */
nfrags = ((chunk_data_len / max_frag_data_len) +
((chunk_data_len % max_frag_data_len) ? 1 : 0));
/* Start of the data in the chunk. */
data_ptr += sizeof(sctp_datahdr_t);
/* Make the first fragment. */
first_frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len,
data_ptr, SCTP_DATA_FIRST_FRAG, ssn);
if (!first_frag)
goto err;
/* All the fragments are added to the frag_list of the first chunk. */
frag_list = &first_frag->frag_list;
chunk_data_len -= max_frag_data_len;
data_ptr += max_frag_data_len;
/* Make the middle fragments. */
while (chunk_data_len > max_frag_data_len) {
frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len,
data_ptr, SCTP_DATA_MIDDLE_FRAG, ssn);
if (!frag)
goto err;
/* Add the middle fragment to the first fragment's frag_list. */
list_add_tail(&frag->frag_list, frag_list);
chunk_data_len -= max_frag_data_len;
data_ptr += max_frag_data_len;
}
/* Make the last fragment. */
frag = sctp_make_datafrag(asoc, sinfo, chunk_data_len, data_ptr,
SCTP_DATA_LAST_FRAG, ssn);
if (!frag)
goto err;
/* Add the last fragment to the first fragment's frag_list. */
list_add_tail(&frag->frag_list, frag_list);
/* Free the original chunk. */
sctp_free_chunk(chunk);
return first_frag;
err:
/* Free any fragments that are created before the failure. */
if (first_frag) {
struct list_head *flist, *lfrag;
/* Free all the fragments off the first one. */
flist = &first_frag->frag_list;
while (NULL != (lfrag = sctp_list_dequeue(flist))) {
frag = list_entry(lfrag, sctp_chunk_t, frag_list);
sctp_free_chunk(frag);
}
/* Free the first fragment. */
sctp_free_chunk(first_frag);
}
return NULL;
}
/*
* sctp_flush_outqueue - Try to flush an outqueue.
*
* Description: Send everything in q which we legally can, subject to
* congestion limitations.
*
* Note: This function can be called from multiple contexts so appropriate
* locking concerns must be made. Today we use the sock lock to protect
* this function.
*/
int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
{
sctp_packet_t *packet;
sctp_packet_t singleton;
sctp_association_t *asoc = q->asoc;
int ecn_capable = asoc->peer.ecn_capable;
__u16 sport = asoc->base.bind_addr.port;
__u16 dport = asoc->peer.port;
__u32 vtag = asoc->peer.i.init_tag;
/* This is the ECNE handler for singleton packets. */
sctp_packet_phandler_t *s_ecne_handler = NULL;
sctp_packet_phandler_t *ecne_handler = NULL;
struct sk_buff_head *queue;
sctp_transport_t *transport = NULL;
sctp_transport_t *new_transport;
sctp_chunk_t *chunk;
sctp_xmit_t status;
int error = 0;
int start_timer = 0;
sctp_ulpevent_t *event;
/* These transports have chunks to send. */
struct list_head transport_list;
struct list_head *ltransport;
INIT_LIST_HEAD(&transport_list);
packet = NULL;
/*
* 6.10 Bundling
* ...
* When bundling control chunks with DATA chunks, an
* endpoint MUST place control chunks first in the outbound
* SCTP packet. The transmitter MUST transmit DATA chunks
* within a SCTP packet in increasing order of TSN.
* ...
*/
if (ecn_capable) {
s_ecne_handler = &sctp_get_no_prepend;
ecne_handler = &sctp_get_ecne_prepend;
}
queue = &q->control;
while (NULL != (chunk = (sctp_chunk_t *)skb_dequeue(queue))) {
/* Pick the right transport to use. */
new_transport = chunk->transport;
if (!new_transport) {
new_transport = asoc->peer.active_path;
} else if (!new_transport->state.active) {
/* If the chunk is Heartbeat, send it to
* chunk->transport, even it's inactive.
*/
if (chunk->chunk_hdr->type != SCTP_CID_HEARTBEAT)
new_transport = asoc->peer.active_path;
}
/* Are we switching transports?
* Take care of transport locks.
*/
if (new_transport != transport) {
transport = new_transport;
if (list_empty(&transport->send_ready)) {
list_add_tail(&transport->send_ready,
&transport_list);
}
packet = &transport->packet;
(*q->config_output)(packet, vtag,
ecn_capable, ecne_handler);
}
switch (chunk->chunk_hdr->type) {
/*
* 6.10 Bundling
* ...
* An endpoint MUST NOT bundle INIT, INIT ACK or SHUTDOWN
* COMPLETE with any other chunks. [Send them immediately.]
*/
case SCTP_CID_INIT:
case SCTP_CID_INIT_ACK:
case SCTP_CID_SHUTDOWN_COMPLETE:
(*q->init_output)(&singleton, transport, sport, dport);
(*q->config_output)(&singleton, vtag, ecn_capable,
s_ecne_handler);
(void) (*q->build_output)(&singleton, chunk);
error = (*q->force_output)(&singleton);
if (error < 0)
return(error);
break;
case SCTP_CID_ABORT:
case SCTP_CID_SACK:
case SCTP_CID_HEARTBEAT:
case SCTP_CID_HEARTBEAT_ACK:
case SCTP_CID_SHUTDOWN:
case SCTP_CID_SHUTDOWN_ACK:
case SCTP_CID_ERROR:
case SCTP_CID_COOKIE_ECHO:
case SCTP_CID_COOKIE_ACK:
case SCTP_CID_ECN_ECNE:
case SCTP_CID_ECN_CWR:
(void) (*q->build_output)(packet, chunk);
break;
case SCTP_CID_ASCONF:
case SCTP_CID_ASCONF_ACK:
(void) (*q->build_output)(packet, chunk);
break;
default:
/* We built a chunk with an illegal type! */
BUG();
};
}
/* Is it OK to send data chunks? */
switch (asoc->state) {
case SCTP_STATE_COOKIE_ECHOED:
/* Only allow bundling, if this packet has a COOKIE-ECHO
* chunk.
*/
if (packet && !packet->has_cookie_echo)
break;
/* fallthru */
case SCTP_STATE_ESTABLISHED:
case SCTP_STATE_SHUTDOWN_PENDING:
case SCTP_STATE_SHUTDOWN_RECEIVED:
/*
* RFC 2960 6.1 Transmission of DATA Chunks
*
* C) When the time comes for the sender to transmit,
* before sending new DATA chunks, the sender MUST
* first transmit any outstanding DATA chunks which
* are marked for retransmission (limited by the
* current cwnd).
*/
if (!list_empty(&q->retransmit)) {
if (transport == asoc->peer.retran_path)
goto retran;
/* Switch transports & prepare the packet. */
transport = asoc->peer.retran_path;
if (list_empty(&transport->send_ready)) {
list_add_tail(&transport->send_ready,
&transport_list);
}
packet = &transport->packet;
(*q->config_output)(packet, vtag,
ecn_capable, ecne_handler);
retran:
error = sctp_flush_retran_queue(q,
packet,
rtx_timeout,
&start_timer);
if (start_timer)
sctp_transport_reset_timers(transport);
}
/* Finally, transmit new packets. */
start_timer = 0;
queue = &q->out;
while (NULL != (chunk = (sctp_chunk_t *) skb_dequeue(queue))) {
/* RFC 2960 6.5 Every DATA chunk MUST carry a valid
* stream identifier.
*/
if (chunk->sinfo.sinfo_stream >=
asoc->c.sinit_num_ostreams) {
/* Generate a SEND FAILED event. */
event = sctp_ulpevent_make_send_failed(asoc,
chunk, SCTP_DATA_UNSENT,
SCTP_ERROR_INV_STRM,
GFP_ATOMIC);
if (event) {
sctp_ulpqueue_tail_event(&asoc->ulpq,
event);
}
/* Free the chunk. This chunk is not on any
* list yet, just free it.
*/
sctp_free_chunk(chunk);
continue;
}
/* If there is a specified transport, use it.
* Otherwise, we want to use the active path.
*/
new_transport = chunk->transport;
if (new_transport == NULL ||
!new_transport->state.active)
new_transport = asoc->peer.active_path;
/* Change packets if necessary. */
if (new_transport != transport) {
transport = new_transport;
/* Schedule to have this transport's
* packet flushed.
*/
if (list_empty(&transport->send_ready)) {
list_add_tail(&transport->send_ready,
&transport_list);
}
packet = &transport->packet;
(*q->config_output)(packet, vtag,
ecn_capable, ecne_handler);
}
SCTP_DEBUG_PRINTK("sctp_transmit_packet(%p, %p[%s]), ",
q, chunk,
chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(
chunk->chunk_hdr->type))
: "Illegal Chunk");
SCTP_DEBUG_PRINTK("TX TSN 0x%x skb->head "
"%p skb->users %d.\n",
ntohl(chunk->subh.data_hdr->tsn),
chunk->skb ?chunk->skb->head : 0,
chunk->skb ?
atomic_read(&chunk->skb->users) :
-1);
/* Add the chunk to the packet. */
status = (*q->build_output)(packet, chunk);
switch (status) {
case SCTP_XMIT_PMTU_FULL:
case SCTP_XMIT_RWND_FULL:
/* We could not append this chunk, so put
* the chunk back on the output queue.
*/
SCTP_DEBUG_PRINTK("sctp_flush_outqueue: could"
"not transmit TSN: 0x%x, status: %d\n",
ntohl(chunk->subh.data_hdr->tsn), status);
skb_queue_head(queue, (struct sk_buff *)chunk);
goto sctp_flush_out;
break;
case SCTP_XMIT_MUST_FRAG: {
sctp_chunk_t *frag;
frag = sctp_fragment_chunk(chunk,
packet->transport->asoc->frag_point);
if (!frag) {
/* We could not fragment due to out of
* memory condition. Free the original
* chunk and return ENOMEM.
*/
sctp_free_chunk(chunk);
error = -ENOMEM;
return error;
}
sctp_xmit_fragmented_chunks(q, packet, frag);
goto sctp_flush_out;
break;
}
case SCTP_XMIT_OK:
break;
default:
BUG();
};
/* BUG: We assume that the (*q->force_output())
* call below will succeed all the time and add the
* chunk to the transmitted list and restart the
* timers.
* It is possible that the call can fail under OOM
* conditions.
*
* Is this really a problem? Won't this behave
* like a lost TSN?
*/
list_add_tail(&chunk->transmitted_list,
&transport->transmitted);
sctp_transport_reset_timers(transport);
q->empty = 0;
}
break;
default:
/* Do nothing. */
break;
};
sctp_flush_out:
/* Before returning, examine all the transports touched in
* this call. Right now, we bluntly force clear all the
* transports. Things might change after we implement Nagle.
* But such an examination is still required.
*
* --xguo
*/
while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) {
sctp_transport_t *t = list_entry(ltransport,
sctp_transport_t, send_ready);
if (t != transport)
transport = t;
packet = &transport->packet;
if (packet->size != SCTP_IP_OVERHEAD)
error = (*q->force_output)(packet);
}
return error;
}
/* Set the various output handling callbacks. */
int sctp_outqueue_set_output_handlers(sctp_outqueue_t *q,
sctp_outqueue_ohandler_init_t init,
sctp_outqueue_ohandler_config_t config,
sctp_outqueue_ohandler_t append,
sctp_outqueue_ohandler_t build,
sctp_outqueue_ohandler_force_t force)
{
q->init_output = init;
q->config_output = config;
q->append_output = append;
q->build_output = build;
q->force_output = force;
return 0;
}
/* Update unack_data based on the incoming SACK chunk */
static void sctp_sack_update_unack_data(sctp_association_t *assoc,
sctp_sackhdr_t *sack)
{
sctp_sack_variable_t *frags;
__u16 unack_data;
int i;
unack_data = assoc->next_tsn - assoc->ctsn_ack_point - 1;
frags = sack->variable;
for (i = 0; i < ntohs(sack->num_gap_ack_blocks); i++) {
unack_data -= ((ntohs(frags[i].gab.end) -
ntohs(frags[i].gab.start) + 1));
}
assoc->unack_data = unack_data;
}
/* This is where we REALLY process a SACK.
*
* Process the sack against the outqueue. Mostly, this just frees
* things off the transmitted queue.
*/
int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
{
sctp_chunk_t *tchunk;
list_t *lchunk, *transport_list, *pos;
__u32 tsn;
__u32 sack_ctsn;
__u32 ctsn;
sctp_transport_t *transport;
int outstanding;
__u32 sack_a_rwnd;
/* Grab the association's destination address list. */
transport_list = &q->asoc->peer.transport_addr_list;
/* Run through the retransmit queue. Credit bytes received
* and free those chunks that we can.
*/
sctp_check_transmitted(q, &q->retransmit, NULL, sack);
/* Run through the transmitted queue.
* Credit bytes received and free those chunks which we can.
*
* This is a MASSIVE candidate for optimization.
*/
list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports);
sctp_check_transmitted(q, &transport->transmitted,
transport, sack);
}
/* Move the Cumulative TSN Ack Point if appropriate. */
sack_ctsn = ntohl(sack->cum_tsn_ack);
if (TSN_lt(q->asoc->ctsn_ack_point, sack_ctsn))
q->asoc->ctsn_ack_point = sack_ctsn;
/* Update unack_data field in the assoc. */
sctp_sack_update_unack_data(q->asoc, sack);
ctsn = q->asoc->ctsn_ack_point;
SCTP_DEBUG_PRINTK(__FUNCTION__ ": sack Cumulative TSN Ack is 0x%x.\n",
sack_ctsn);
SCTP_DEBUG_PRINTK(__FUNCTION__ ": Cumulative TSN Ack of association "
"%p is 0x%x.\n",q->asoc, ctsn);
/* Throw away stuff rotting on the sack queue. */
list_for_each(lchunk, &q->sacked) {
tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
tsn = ntohl(tchunk->subh.data_hdr->tsn);
if (TSN_lte(tsn, ctsn)) {
lchunk = lchunk->prev;
sctp_free_chunk(tchunk);
}
}
/* ii) Set rwnd equal to the newly received a_rwnd minus the
* number of bytes still outstanding after processing the
* Cumulative TSN Ack and the Gap Ack Blocks.
*/
sack_a_rwnd = ntohl(sack->a_rwnd);
outstanding = q->outstanding_bytes;
if (outstanding < sack_a_rwnd) {
sack_a_rwnd -= outstanding;
} else {
sack_a_rwnd = 0;
}
q->asoc->peer.rwnd = sack_a_rwnd;
/* See if all chunks are acked.
* Make sure the empty queue handler will get run later.
*/
q->empty = skb_queue_empty(&q->out) && list_empty(&q->retransmit);
if (!q->empty)
goto finish;
list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports);
q->empty = q->empty && list_empty(&transport->transmitted);
if (!q->empty)
goto finish;
}
SCTP_DEBUG_PRINTK("sack queue is empty.\n");
finish:
return q->empty;
}
/* Is the outqueue empty? */
int sctp_outqueue_is_empty(const sctp_outqueue_t *q)
{
return q->empty;
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
/* Go through a transport's transmitted list or the assocication's retransmit
* list and move chunks that are acked by the Cumulative TSN Ack to q->sacked.
* The retransmit list will not have an associated transport. In case of a
* transmitted list with a transport, the transport's congestion, rto and fast
* retransmit parameters are also updated and if needed a fast retransmit
* process is started.
*
* I added coherent debug information output. --xguo
*
* Instead of printing 'sacked' or 'kept' for each TSN on the
* transmitted_queue, we print a range: SACKED: TSN1-TSN2, TSN3, TSN4-TSN5.
* KEPT TSN6-TSN7, etc.
*/
static void sctp_check_transmitted(sctp_outqueue_t *q,
struct list_head *transmitted_queue,
sctp_transport_t *transport,
sctp_sackhdr_t *sack)
{
struct list_head *lchunk;
sctp_chunk_t *tchunk;
struct list_head tlist;
__u32 tsn;
__u32 sack_ctsn;
__u32 rtt;
__u32 highest_new_tsn_in_sack;
__u8 restart_timer = 0;
__u8 do_fast_retransmit = 0;
int bytes_acked = 0;
/* These state variables are for coherent debug output. --xguo */
#if SCTP_DEBUG
__u32 dbg_ack_tsn = 0; /* An ACKed TSN range starts here... */
__u32 dbg_last_ack_tsn = 0; /* ...and finishes here. */
__u32 dbg_kept_tsn = 0; /* An un-ACKed range starts here... */
__u32 dbg_last_kept_tsn = 0; /* ...and finishes here. */
/* 0 : The last TSN was ACKed.
* 1 : The last TSN was NOT ACKed (i.e. KEPT).
* -1: We need to initialize.
*/
int dbg_prt_state = -1;
#endif /* SCTP_DEBUG */
sack_ctsn = ntohl(sack->cum_tsn_ack);
highest_new_tsn_in_sack = sack_ctsn;
INIT_LIST_HEAD(&tlist);
/* The while loop will skip empty transmitted queues. */
while (NULL != (lchunk = sctp_list_dequeue(transmitted_queue))) {
tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
tsn = ntohl(tchunk->subh.data_hdr->tsn);
if (sctp_acked(sack, tsn)) {
/* If this queue is the retransmit queue, the
* retransmit timer has already reclaimed
* the outstanding bytes for this chunk, so only
* count bytes associated with a transport.
*/
if (transport) {
/* If this chunk is being used for RTT
* measurement, calculate the RTT and update
* the RTO using this value.
*
* 6.3.1 C5) Karn's algorithm: RTT measurements
* MUST NOT be made using packets that were
* retransmitted (and thus for which it is
* ambiguous whether the reply was for the first
* instance of the packet or a later instance).
*/
if ((!tchunk->tsn_gap_acked) &&
(1 == tchunk->num_times_sent) &&
(tchunk->rtt_in_progress)) {
rtt = jiffies - tchunk->sent_at;
sctp_transport_update_rto(transport,
rtt);
}
}
if (TSN_lte(tsn, sack_ctsn)) {
/* RFC 2960 6.3.2 Retransmission Timer Rules
*
* R3) Whenever a SACK is received
* that acknowledges the DATA chunk
* with the earliest outstanding TSN
* for that address, restart T3-rtx
* timer for that address with its
* current RTO.
*/
restart_timer = 1;
if (!tchunk->tsn_gap_acked) {
tchunk->tsn_gap_acked = 1;
bytes_acked += sctp_data_size(tchunk);
}
list_add_tail(&tchunk->transmitted_list,
&q->sacked);
} else {
/* RFC2960 7.2.4, sctpimpguide-05 2.8.2
* M2) Each time a SACK arrives reporting
* 'Stray DATA chunk(s)' record the highest TSN
* reported as newly acknowledged, call this
* value 'HighestTSNinSack'. A newly
* acknowledged DATA chunk is one not previously
* acknowledged in a SACK.
*
* When the SCTP sender of data receives a SACK
* chunk that acknowledges, for the first time,
* the receipt of a DATA chunk, all the still
* unacknowledged DATA chunks whose TSN is older
* than that newly acknowledged DATA chunk, are
* qualified as 'Stray DATA chunks'.
*/
if (!tchunk->tsn_gap_acked) {
tchunk->tsn_gap_acked = 1;
bytes_acked += sctp_data_size(tchunk);
if (TSN_lt(highest_new_tsn_in_sack,
tsn)) {
highest_new_tsn_in_sack = tsn;
}
}
list_add_tail(lchunk, &tlist);
}
#if SCTP_DEBUG
switch (dbg_prt_state) {
case 0: /* last TSN was ACKed */
if (dbg_last_ack_tsn + 1 == tsn) {
/* This TSN belongs to the
* current ACK range.
*/
break;
}
if (dbg_last_ack_tsn != dbg_ack_tsn) {
/* Display the end of the
* current range.
*/
SCTP_DEBUG_PRINTK("-%08x",
dbg_last_ack_tsn);
}
/* Start a new range. */
SCTP_DEBUG_PRINTK(",%08x", tsn);
dbg_ack_tsn = tsn;
break;
case 1: /* The last TSN was NOT ACKed. */
if (dbg_last_kept_tsn != dbg_kept_tsn) {
/* Display the end of current range. */
SCTP_DEBUG_PRINTK("-%08x",
dbg_last_kept_tsn);
}
SCTP_DEBUG_PRINTK("\n");
/* FALL THROUGH... */
default:
/* This is the first-ever TSN we examined. */
/* Start a new range of ACK-ed TSNs. */
SCTP_DEBUG_PRINTK("ACKed: %08x", tsn);
dbg_prt_state = 0;
dbg_ack_tsn = tsn;
};
dbg_last_ack_tsn = tsn;
#endif /* SCTP_DEBUG */
} else {
if (tchunk->tsn_gap_acked) {
SCTP_DEBUG_PRINTK(__FUNCTION__
": Receiver reneged on data "
"TSN: 0x%x\n", tsn);
tchunk->tsn_gap_acked = 0;
bytes_acked -= sctp_data_size(tchunk);
/* RFC 2960 6.3.2 Retransmission Timer Rules
*
* R4) Whenever a SACK is received missing a TSN
* that was previously acknowledged via a Gap Ack
* Block, start T3-rtx for the destination
* address to which the DATA chunk was originally
* transmitted if it is not already running.
*/
restart_timer = 1;
}
list_add_tail(lchunk, &tlist);
#if SCTP_DEBUG
/* See the above comments on ACK-ed TSNs. */
switch (dbg_prt_state) {
case 1:
if (dbg_last_kept_tsn + 1 == tsn)
break;
if (dbg_last_kept_tsn != dbg_kept_tsn)
SCTP_DEBUG_PRINTK("-%08x",
dbg_last_kept_tsn);
SCTP_DEBUG_PRINTK(",%08x", tsn);
dbg_kept_tsn = tsn;
break;
case 0:
if (dbg_last_ack_tsn != dbg_ack_tsn)
SCTP_DEBUG_PRINTK("-%08x",
dbg_last_ack_tsn);
SCTP_DEBUG_PRINTK("\n");
/* FALL THROUGH... */
default:
SCTP_DEBUG_PRINTK("KEPT: %08x",tsn);
dbg_prt_state = 1;
dbg_kept_tsn = tsn;
};
dbg_last_kept_tsn = tsn;
#endif /* SCTP_DEBUG */
}
}
#if SCTP_DEBUG
/* Finish off the last range, displaying its ending TSN. */
switch (dbg_prt_state) {
case 0:
if (dbg_last_ack_tsn != dbg_ack_tsn) {
SCTP_DEBUG_PRINTK("-%08x\n", dbg_last_ack_tsn);
} else {
SCTP_DEBUG_PRINTK("\n");
}
break;
case 1:
if (dbg_last_kept_tsn != dbg_kept_tsn) {
SCTP_DEBUG_PRINTK("-%08x\n", dbg_last_kept_tsn);
} else {
SCTP_DEBUG_PRINTK("\n");
}
};
#endif /* SCTP_DEBUG */
if (transport) {
if (bytes_acked) {
/* 8.2. When an outstanding TSN is acknowledged,
* the endpoint shall clear the error counter of
* the destination transport address to which the
* DATA chunk was last sent.
* The association's overall error counter is
* also cleared.
*/
transport->error_count = 0;
transport->asoc->overall_error_count = 0;
/* Mark the destination transport address as
* active if it is not so marked.
*/
if (!transport->state.active) {
sctp_assoc_control_transport(transport->asoc,
transport,
SCTP_TRANSPORT_UP,
SCTP_RECEIVED_SACK);
}
sctp_transport_raise_cwnd(transport, sack_ctsn,
bytes_acked);
transport->flight_size -= bytes_acked;
q->outstanding_bytes -= bytes_acked;
} else {
/* RFC 2960 6.1, sctpimpguide-06 2.15.2
* When a sender is doing zero window probing, it
* should not timeout the association if it continues
* to receive new packets from the receiver. The
* reason is that the receiver MAY keep its window
* closed for an indefinite time.
* A sender is doing zero window probing when the
* receiver's advertised window is zero, and there is
* only one data chunk in flight to the receiver.
*/
if ((0 == q->asoc->peer.rwnd) &&
(!list_empty(&tlist)) &&
(sack_ctsn+2 == q->asoc->next_tsn)) {
SCTP_DEBUG_PRINTK("%s: SACK received for zero "
"window probe: %u\n",
__FUNCTION__, sack_ctsn);
q->asoc->overall_error_count = 0;
transport->error_count = 0;
}
}
/* RFC 2960 6.3.2 Retransmission Timer Rules
*
* R2) Whenever all outstanding data sent to an address have
* been acknowledged, turn off the T3-rtx timer of that
* address.
*/
if (!transport->flight_size) {
if (timer_pending(&transport->T3_rtx_timer) &&
del_timer(&transport->T3_rtx_timer)) {
sctp_transport_put(transport);
}
} else if (restart_timer) {
if (!mod_timer(&transport->T3_rtx_timer,
jiffies + transport->rto))
sctp_transport_hold(transport);
}
}
/* Reconstruct the transmitted list with chunks that are not yet
* acked by the Cumulative TSN Ack.
*/
while (NULL != (lchunk = sctp_list_dequeue(&tlist))) {
tchunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
tsn = ntohl(tchunk->subh.data_hdr->tsn);
/* RFC 2960 7.2.4, sctpimpguide-05 2.8.2 M3) Examine all
* 'Unacknowledged TSN's', if the TSN number of an
* 'Unacknowledged TSN' is smaller than the 'HighestTSNinSack'
* value, increment the 'TSN.Missing.Report' count on that
* chunk if it has NOT been fast retransmitted or marked for
* fast retransmit already.
*
* M4) If any DATA chunk is found to have a
* 'TSN.Missing.Report'
* value larger than or equal to 4, mark that chunk for
* retransmission and start the fast retransmit procedure.
*/
if ((!tchunk->fast_retransmit) &&
(!tchunk->tsn_gap_acked) &&
(TSN_lt(tsn, highest_new_tsn_in_sack))) {
tchunk->tsn_missing_report++;
SCTP_DEBUG_PRINTK("%s: TSN 0x%x missing counter: %d\n",
__FUNCTION__, tsn,
tchunk->tsn_missing_report);
}
if (tchunk->tsn_missing_report >= 4) {
tchunk->fast_retransmit = 1;
do_fast_retransmit = 1;
}
list_add_tail(lchunk, transmitted_queue);
}
if (transport) {
if (do_fast_retransmit)
sctp_retransmit(q, transport, do_fast_retransmit);
SCTP_DEBUG_PRINTK(__FUNCTION__ ": transport: %p, cwnd: %d, "
"ssthresh: %d, flight_size: %d, pba: %d\n",
transport, transport->cwnd,
transport->ssthresh, transport->flight_size,
transport->partial_bytes_acked);
}
}
/* Is the given TSN acked by this packet? */
static int sctp_acked(sctp_sackhdr_t *sack, __u32 tsn)
{
int i;
sctp_sack_variable_t *frags;
__u16 gap;
__u32 ctsn = ntohl(sack->cum_tsn_ack);
if (TSN_lte(tsn, ctsn))
goto pass;
/* 3.3.4 Selective Acknowledgement (SACK) (3):
*
* Gap Ack Blocks:
* These fields contain the Gap Ack Blocks. They are repeated
* for each Gap Ack Block up to the number of Gap Ack Blocks
* defined in the Number of Gap Ack Blocks field. All DATA
* chunks with TSNs greater than or equal to (Cumulative TSN
* Ack + Gap Ack Block Start) and less than or equal to
* (Cumulative TSN Ack + Gap Ack Block End) of each Gap Ack
* Block are assumed to have been received correctly.
*/
frags = sack->variable;
gap = tsn - ctsn;
for (i = 0; i < ntohs(sack->num_gap_ack_blocks); ++i) {
if (TSN_lte(ntohs(frags[i].gab.start), gap) &&
TSN_lte(gap, ntohs(frags[i].gab.end)))
goto pass;
}
return 0;
pass:
return 1;
}
/* 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_primitive.c,v 1.6 2002/08/21 18:34:04 jgrimm Exp $
*
* 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_primitive.c,v 1.6 2002/08/21 18:34:04 jgrimm Exp $";
#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/sctp_sm.h>
#define DECLARE_PRIMITIVE(name) \
/* This is called in the code as sctp_primitive_ ## name. */ \
int sctp_primitive_ ## name(sctp_association_t *asoc, \
void *arg) { \
int error = 0; \
sctp_event_t event_type; sctp_subtype_t subtype; \
sctp_state_t state; \
sctp_endpoint_t *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);
/* COMMENT BUG. Find out where this is mentioned in the spec. */
int sctp_other_icmp_unreachfrag(sctp_association_t *asoc, void *arg)
{
int error = 0;
sctp_event_t event_type;
sctp_subtype_t subtype;
sctp_state_t state;
sctp_endpoint_t *ep;
event_type = SCTP_EVENT_T_OTHER;
subtype = SCTP_ST_OTHER(SCTP_EVENT_ICMP_UNREACHFRAG);
state = asoc ? asoc->state : SCTP_STATE_CLOSED;
ep = asoc ? asoc->ep : NULL;
error = sctp_do_sm(event_type, subtype, state, ep,
asoc, arg, GFP_ATOMIC);
return error;
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_protocol.c,v 1.35 2002/08/16 19:30:49 jgrimm Exp $
*
* Initialization/cleanup for SCTP protocol support.
*
* The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net>
*
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
* Written or modified by:
* La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us>
* Jon Grimm <jgrimm@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_protocol.c,v 1.35 2002/08/16 19:30:49 jgrimm Exp $";
#include <linux/module.h>
#include <linux/init.h>
#include <linux/netdevice.h>
#include <linux/inetdevice.h>
#include <net/protocol.h>
#include <net/ip.h>
#include <net/ipv6.h>
#include <net/sctp/sctp.h>
#include <net/addrconf.h>
#include <net/inet_common.h>
/* Global data structures. */
sctp_protocol_t sctp_proto;
struct proc_dir_entry *proc_net_sctp;
/* This is the global socket data structure used for responding to
* the Out-of-the-blue (OOTB) packets. A control sock will be created
* for this socket at the initialization time.
*/
static struct socket *sctp_ctl_socket;
extern struct net_proto_family inet_family_ops;
/* Return the address of the control sock. */
struct sock *sctp_get_ctl_sock(void)
{
return sctp_ctl_socket->sk;
}
/* Set up the proc fs entry for the SCTP protocol. */
void sctp_proc_init(void)
{
if (!proc_net_sctp) {
struct proc_dir_entry *ent;
ent = proc_mkdir("net/sctp", 0);
if (ent) {
ent->owner = THIS_MODULE;
proc_net_sctp = ent;
}
}
}
/* Clean up the proc fs entry for the SCTP protocol. */
void sctp_proc_exit(void)
{
if (proc_net_sctp) {
proc_net_sctp= NULL;
remove_proc_entry("net/sctp", 0);
}
}
/* Private helper to extract ipv4 address and stash them in
* the protocol structure.
*/
static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto,
struct net_device *dev)
{
struct in_device *in_dev;
struct in_ifaddr *ifa;
struct sockaddr_storage_list *addr;
read_lock(&inetdev_lock);
if ((in_dev = __in_dev_get(dev)) == NULL) {
read_unlock(&inetdev_lock);
return;
}
read_lock(&in_dev->lock);
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
/* Add the address to the local list. */
/* XXX BUG: sleeping allocation with lock held -DaveM */
addr = t_new(struct sockaddr_storage_list, GFP_KERNEL);
if (addr) {
INIT_LIST_HEAD(&addr->list);
addr->a.v4.sin_family = AF_INET;
addr->a.v4.sin_port = 0;
addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
list_add_tail(&addr->list, &proto->local_addr_list);
}
}
read_unlock(&in_dev->lock);
read_unlock(&inetdev_lock);
}
/* Private helper to extract ipv6 address and stash them in
* the protocol structure.
* FIXME: Make this an address family function.
*/
static inline void sctp_v6_get_local_addr_list(sctp_protocol_t *proto, struct net_device *dev)
{
#ifdef SCTP_V6_SUPPORT
/* FIXME: The testframe doesn't support this function. */
#ifndef TEST_FRAME
struct inet6_dev *in6_dev;
struct inet6_ifaddr *ifp;
struct sockaddr_storage_list *addr;
read_lock(&addrconf_lock);
if ((in6_dev = __in6_dev_get(dev)) == NULL) {
read_unlock(&addrconf_lock);
return;
}
read_lock_bh(&in6_dev->lock);
for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) {
/* Add the address to the local list. */
/* XXX BUG: sleeping allocation with lock held -DaveM */
addr = t_new(struct sockaddr_storage_list, GFP_KERNEL);
if (addr) {
addr->a.v6.sin6_family = AF_INET6;
addr->a.v6.sin6_port = 0;
addr->a.v6.sin6_addr = ifp->addr;
INIT_LIST_HEAD(&addr->list);
list_add_tail(&addr->list, &proto->local_addr_list);
}
}
read_unlock_bh(&in6_dev->lock);
read_unlock(&addrconf_lock);
#endif /* TEST_FRAME */
#endif /* SCTP_V6_SUPPORT */
}
/* Extract our IP addresses from the system and stash them in the
* protocol structure.
*/
static void __sctp_get_local_addr_list(sctp_protocol_t *proto)
{
struct net_device *dev;
read_lock(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) {
sctp_v4_get_local_addr_list(proto, dev);
sctp_v6_get_local_addr_list(proto, dev);
}
read_unlock(&dev_base_lock);
}
static void sctp_get_local_addr_list(sctp_protocol_t *proto)
{
long flags __attribute__ ((unused));
sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags);
__sctp_get_local_addr_list(&sctp_proto);
sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags);
}
/* Free the existing local addresses. */
static void __sctp_free_local_addr_list(sctp_protocol_t *proto)
{
struct sockaddr_storage_list *addr;
list_t *pos, *temp;
list_for_each_safe(pos, temp, &proto->local_addr_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
list_del(pos);
kfree(addr);
}
}
/* Free the existing local addresses. */
static void sctp_free_local_addr_list(sctp_protocol_t *proto)
{
long flags __attribute__ ((unused));
sctp_spin_lock_irqsave(&proto->local_addr_lock, flags);
__sctp_free_local_addr_list(proto);
sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags);
}
/* Copy the local addresses which are valid for 'scope' into 'bp'. */
int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp,
sctp_scope_t scope, int priority, int copy_flags)
{
struct sockaddr_storage_list *addr;
int error = 0;
list_t *pos;
long flags __attribute__ ((unused));
sctp_spin_lock_irqsave(&proto->local_addr_lock, flags);
list_for_each(pos, &proto->local_addr_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
if (sctp_in_scope(&addr->a, scope)) {
/* Now that the address is in scope, check to see if
* the address type is really supported by the local
* sock as well as the remote peer.
*/
if ((((AF_INET == addr->a.sa.sa_family) &&
(copy_flags & SCTP_ADDR4_PEERSUPP))) ||
(((AF_INET6 == addr->a.sa.sa_family) &&
(copy_flags & SCTP_ADDR6_ALLOWED) &&
(copy_flags & SCTP_ADDR6_PEERSUPP)))) {
error = sctp_add_bind_addr(bp,
&addr->a,
priority);
if (error)
goto end_copy;
}
}
}
end_copy:
sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags);
return error;
}
/* Returns the mtu for the given v4 destination address. */
int sctp_v4_get_dst_mtu(const sockaddr_storage_t *address)
{
int dst_mtu = SCTP_DEFAULT_MAXSEGMENT;
struct rtable *rt;
struct rt_key key = {
.dst = address->v4.sin_addr.s_addr,
.src = 0,
.iif = 0,
.oif = 0,
.tos = 0,
.scope = 0
};
if (ip_route_output_key(&rt, &key)) {
SCTP_DEBUG_PRINTK("sctp_v4_get_dst_mtu:ip_route_output_key"
" failed, returning %d as dst_mtu\n",
dst_mtu);
} else {
dst_mtu = rt->u.dst.pmtu;
SCTP_DEBUG_PRINTK("sctp_v4_get_dst_mtu: "
"ip_route_output_key: dev:%s pmtu:%d\n",
rt->u.dst.dev->name, dst_mtu);
ip_rt_put(rt);
}
return dst_mtu;
}
/* Event handler for inet device events.
* Basically, whenever there is an event, we re-build our local address list.
*/
static int sctp_netdev_event(struct notifier_block *this, unsigned long event, void *ptr)
{
long flags __attribute__ ((unused));
sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags);
__sctp_free_local_addr_list(&sctp_proto);
__sctp_get_local_addr_list(&sctp_proto);
sctp_spin_unlock_irqrestore(&sctp_proto.local_addr_lock, flags);
return NOTIFY_DONE;
}
/*
* Initialize the control inode/socket with a control endpoint data
* structure. This endpoint is reserved exclusively for the OOTB processing.
*/
int sctp_ctl_sock_init(void)
{
int err = 0;
int family = PF_INET;
SCTP_V6(family = PF_INET6;)
err = sock_create(family, SOCK_SEQPACKET, IPPROTO_SCTP,
&sctp_ctl_socket);
if (err < 0) {
printk(KERN_ERR
"SCTP: Failed to create the SCTP control socket.\n");
return err;
}
sctp_ctl_socket->sk->allocation = GFP_ATOMIC;
inet_sk(sctp_ctl_socket->sk)->ttl = MAXTTL;
return 0;
}
/* Get the table of functions for manipulating a particular address
* family.
*/
sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address)
{
list_t *pos;
sctp_protocol_t *proto = sctp_get_protocol();
sctp_func_t *retval, *af;
retval = NULL;
/* Cycle through all AF specific functions looking for a
* match.
*/
list_for_each(pos, &proto->address_families) {
af = list_entry(pos, sctp_func_t, list);
if (address->sa.sa_family == af->sa_family) {
retval = af;
break;
}
}
return retval;
}
/* Registration for netdev events. */
struct notifier_block sctp_netdev_notifier = {
.notifier_call = sctp_netdev_event,
};
/* Socket operations. */
struct proto_ops inet_seqpacket_ops = {
.family = PF_INET,
.release = inet_release, /* Needs to be wrapped... */
.bind = inet_bind,
.connect = inet_dgram_connect,
.socketpair = sock_no_socketpair,
.accept = inet_accept,
.getname = inet_getname, /* Semantics are different. */
.poll = sctp_poll,
.ioctl = inet_ioctl,
.listen = sctp_inet_listen,
.shutdown = inet_shutdown, /* Looks harmless. */
.setsockopt = inet_setsockopt, /* IP_SOL IP_OPTION is a problem. */
.getsockopt = inet_getsockopt,
.sendmsg = inet_sendmsg,
.recvmsg = inet_recvmsg,
.mmap = sock_no_mmap,
.sendpage = sock_no_sendpage,
};
/* Registration with AF_INET family. */
struct inet_protosw sctp_protosw = {
.type = SOCK_SEQPACKET,
.protocol = IPPROTO_SCTP,
.prot = &sctp_prot,
.ops = &inet_seqpacket_ops,
.capability = -1,
.no_check = 0,
.flags = SCTP_PROTOSW_FLAG
};
/* Register with IP layer. */
static struct inet_protocol sctp_protocol = {
.handler = sctp_rcv, /* SCTP input handler. */
.err_handler = sctp_v4_err, /* SCTP error control */
.protocol = IPPROTO_SCTP, /* protocol ID */
.name = "SCTP" /* name */
};
/* IPv4 address related functions. */
sctp_func_t sctp_ipv4_specific = {
.queue_xmit = ip_queue_xmit,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
.get_dst_mtu = sctp_v4_get_dst_mtu,
.net_header_len = sizeof(struct iphdr),
.sockaddr_len = sizeof(struct sockaddr_in),
.sa_family = AF_INET,
};
/* Initialize the universe into something sensible. */
int sctp_init(void)
{
int i;
int status = 0;
/* Add SCTP to inetsw linked list. */
inet_register_protosw(&sctp_protosw);
/* Add SCTP to inet_protos hash table. */
inet_add_protocol(&sctp_protocol);
/* Initialize proc fs directory. */
sctp_proc_init();
/* Initialize object count debugging. */
sctp_dbg_objcnt_init();
/*
* 14. Suggested SCTP Protocol Parameter Values
*/
/* The following protocol parameters are RECOMMENDED: */
/* RTO.Initial - 3 seconds */
sctp_proto.rto_initial = SCTP_RTO_INITIAL;
/* RTO.Min - 1 second */
sctp_proto.rto_min = SCTP_RTO_MIN;
/* RTO.Max - 60 seconds */
sctp_proto.rto_max = SCTP_RTO_MAX;
/* RTO.Alpha - 1/8 */
sctp_proto.rto_alpha = SCTP_RTO_ALPHA;
/* RTO.Beta - 1/4 */
sctp_proto.rto_beta = SCTP_RTO_BETA;
/* Valid.Cookie.Life - 60 seconds */
sctp_proto.valid_cookie_life = 60 * HZ;
/* Max.Burst - 4 */
sctp_proto.max_burst = SCTP_MAX_BURST;
/* Association.Max.Retrans - 10 attempts
* Path.Max.Retrans - 5 attempts (per destination address)
* Max.Init.Retransmits - 8 attempts
*/
sctp_proto.max_retrans_association = 10;
sctp_proto.max_retrans_path = 5;
sctp_proto.max_retrans_init = 8;
/* HB.interval - 30 seconds */
sctp_proto.hb_interval = 30 * HZ;
/* Implementation specific variables. */
/* Initialize default stream count setup information.
* Note: today the stream accounting data structures are very
* fixed size, so one really does need to make sure that these have
* upper/lower limits when changing.
*/
sctp_proto.max_instreams = SCTP_MAX_STREAM;
sctp_proto.max_outstreams = SCTP_MAX_STREAM;
/* Allocate and initialize the association hash table. */
sctp_proto.assoc_hashsize = 4096;
sctp_proto.assoc_hashbucket = (sctp_hashbucket_t *)
kmalloc(4096 * sizeof(sctp_hashbucket_t), GFP_KERNEL);
if (!sctp_proto.assoc_hashbucket) {
printk (KERN_ERR "SCTP: Failed association hash alloc.\n");
status = -ENOMEM;
goto err_ahash_alloc;
}
for (i = 0; i < sctp_proto.assoc_hashsize; i++) {
sctp_proto.assoc_hashbucket[i].lock = RW_LOCK_UNLOCKED;
sctp_proto.assoc_hashbucket[i].chain = NULL;
}
/* Allocate and initialize the endpoint hash table. */
sctp_proto.ep_hashsize = 64;
sctp_proto.ep_hashbucket = (sctp_hashbucket_t *)
kmalloc(64 * sizeof(sctp_hashbucket_t), GFP_KERNEL);
if (!sctp_proto.ep_hashbucket) {
printk (KERN_ERR "SCTP: Failed endpoint_hash alloc.\n");
status = -ENOMEM;
goto err_ehash_alloc;
}
for (i = 0; i < sctp_proto.ep_hashsize; i++) {
sctp_proto.ep_hashbucket[i].lock = RW_LOCK_UNLOCKED;
sctp_proto.ep_hashbucket[i].chain = NULL;
}
/* Allocate and initialize the SCTP port hash table. */
sctp_proto.port_hashsize = 4096;
sctp_proto.port_hashtable = (sctp_bind_hashbucket_t *)
kmalloc(4096 * sizeof(sctp_bind_hashbucket_t), GFP_KERNEL);
if (!sctp_proto.port_hashtable) {
printk (KERN_ERR "SCTP: Failed bind hash alloc.");
status = -ENOMEM;
goto err_bhash_alloc;
}
sctp_proto.port_alloc_lock = SPIN_LOCK_UNLOCKED;
sctp_proto.port_rover = sysctl_local_port_range[0] - 1;
for (i = 0; i < sctp_proto.port_hashsize; i++) {
sctp_proto.port_hashtable[i].lock = SPIN_LOCK_UNLOCKED;
sctp_proto.port_hashtable[i].chain = NULL;
}
sctp_sysctl_register();
INIT_LIST_HEAD(&sctp_proto.address_families);
INIT_LIST_HEAD(&sctp_ipv4_specific.list);
list_add_tail(&sctp_ipv4_specific.list, &sctp_proto.address_families);
status = sctp_v6_init();
if (status)
goto err_v6_init;
/* Initialize the control inode/socket for handling OOTB packets. */
if ((status = sctp_ctl_sock_init())) {
printk (KERN_ERR
"SCTP: Failed to initialize the SCTP control sock.\n");
goto err_ctl_sock_init;
}
/* Initialize the local address list. */
INIT_LIST_HEAD(&sctp_proto.local_addr_list);
sctp_proto.local_addr_lock = SPIN_LOCK_UNLOCKED;
register_inetaddr_notifier(&sctp_netdev_notifier);
sctp_get_local_addr_list(&sctp_proto);
return 0;
err_ctl_sock_init:
sctp_v6_exit();
err_v6_init:
sctp_sysctl_unregister();
list_del(&sctp_ipv4_specific.list);
kfree(sctp_proto.port_hashtable);
err_bhash_alloc:
kfree(sctp_proto.ep_hashbucket);
err_ehash_alloc:
kfree(sctp_proto.assoc_hashbucket);
err_ahash_alloc:
sctp_dbg_objcnt_exit();
sctp_proc_exit();
inet_del_protocol(&sctp_protocol);
inet_unregister_protosw(&sctp_protosw);
return status;
}
/* Exit handler for the SCTP protocol. */
void sctp_exit(void)
{
/* BUG. This should probably do something useful like clean
* up all the remaining associations and all that memory.
*/
/* Free the local address list. */
unregister_inetaddr_notifier(&sctp_netdev_notifier);
sctp_free_local_addr_list(&sctp_proto);
/* Free the control endpoint. */
sock_release(sctp_ctl_socket);
sctp_v6_exit();
sctp_sysctl_unregister();
list_del(&sctp_ipv4_specific.list);
kfree(sctp_proto.assoc_hashbucket);
kfree(sctp_proto.ep_hashbucket);
kfree(sctp_proto.port_hashtable);
sctp_dbg_objcnt_exit();
sctp_proc_exit();
inet_del_protocol(&sctp_protocol);
inet_unregister_protosw(&sctp_protosw);
}
module_init(sctp_init);
module_exit(sctp_exit);
MODULE_AUTHOR("Linux Kernel SCTP developers <lksctp-developers@lists.sourceforge.net>");
MODULE_DESCRIPTION("Support for the SCTP protocol (RFC2960)");
MODULE_LICENSE("GPL");
/* 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_sla1.c,v 1.4 2002/07/19 22:00:33 jgrimm Exp $
*
* (It's really SHA-1 but Hey I was tired when I created this
* file, and on a plane to France :-)
*
* 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:
* Randall Stewart <rstewar1@email.mot.com>
* kmorneau@cisco.com
* qxie1@email.mot.com
*
* Based on:
* Randy Stewart, et al. SCTP Reference Implementation which is licenced
* under the GPL.
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_sla1.c,v 1.4 2002/07/19 22:00:33 jgrimm Exp $";
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/fcntl.h>
#include <asm/string.h> /* for memcpy */
#include <linux/sched.h> /* dead chicken for in.h */
#include <linux/in.h> /* for htonl and ntohl */
#include <net/sctp/sctp_sla1.h>
void SLA1_Init(struct SLA_1_Context *ctx)
{
/* Init the SLA-1 context structure. */
ctx->A = 0;
ctx->B = 0;
ctx->C = 0;
ctx->D = 0;
ctx->E = 0;
ctx->H0 = H0INIT;
ctx->H1 = H1INIT;
ctx->H2 = H2INIT;
ctx->H3 = H3INIT;
ctx->H4 = H4INIT;
ctx->TEMP = 0;
memset(ctx->words, 0, sizeof(ctx->words));
ctx->howManyInBlock = 0;
ctx->runningTotal = 0;
}
void SLA1processABlock(struct SLA_1_Context *ctx,unsigned int *block)
{
int i;
/* init the W0-W15 to the block of words being
* hashed.
*/
/* step a) */
for (i = 0; i < 16; i++)
ctx->words[i] = ntohl(block[i]);
/* now init the rest based on the SLA-1 formula, step b) */
for (i = 16; i < 80; i++)
ctx->words[i] =
CSHIFT(1, ((ctx->words[(i-3)]) ^
(ctx->words[(i-8)]) ^
(ctx->words[(i-14)]) ^
(ctx->words[(i-16)])));
/* step c) */
ctx->A = ctx->H0;
ctx->B = ctx->H1;
ctx->C = ctx->H2;
ctx->D = ctx->H3;
ctx->E = ctx->H4;
/* step d) */
for (i = 0; i < 80; i++) {
if (i < 20) {
ctx->TEMP = ((CSHIFT(5, ctx->A)) +
(F1(ctx->B, ctx->C, ctx->D)) +
(ctx->E) +
ctx->words[i] +
K1
);
} else if (i < 40) {
ctx->TEMP = ((CSHIFT(5, ctx->A)) +
(F2(ctx->B, ctx->C, ctx->D)) +
(ctx->E) +
(ctx->words[i]) +
K2
);
} else if (i < 60) {
ctx->TEMP = ((CSHIFT(5, ctx->A)) +
(F3(ctx->B, ctx->C, ctx->D)) +
(ctx->E) +
(ctx->words[i]) +
K3
);
} else {
ctx->TEMP = ((CSHIFT(5, ctx->A)) +
(F4(ctx->B, ctx->C, ctx->D)) +
(ctx->E) +
(ctx->words[i]) +
K4
);
}
ctx->E = ctx->D;
ctx->D = ctx->C;
ctx->C = CSHIFT(30, ctx->B);
ctx->B = ctx->A;
ctx->A = ctx->TEMP;
}
/* step e) */
ctx->H0 = (ctx->H0) + (ctx->A);
ctx->H1 = (ctx->H1) + (ctx->B);
ctx->H2 = (ctx->H2) + (ctx->C);
ctx->H3 = (ctx->H3) + (ctx->D);
ctx->H4 = (ctx->H4) + (ctx->E);
}
void SLA1_Process(struct SLA_1_Context *ctx, const unsigned char *ptr, int siz)
{
int numberLeft, leftToFill;
numberLeft = siz;
while (numberLeft > 0) {
leftToFill = sizeof(ctx->SLAblock) - ctx->howManyInBlock;
if (leftToFill > numberLeft) {
/* can only partially fill up this one */
memcpy(&ctx->SLAblock[ctx->howManyInBlock],
ptr, numberLeft);
ctx->howManyInBlock += siz;
ctx->runningTotal += siz;
break;
} else {
/* block is now full, process it */
memcpy(&ctx->SLAblock[ctx->howManyInBlock],
ptr, leftToFill);
SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
numberLeft -= leftToFill;
ctx->runningTotal += leftToFill;
ctx->howManyInBlock = 0;
}
}
}
void SLA1_Final(struct SLA_1_Context *ctx, unsigned char *digestBuf)
{
/* if any left in block fill with padding
* and process. Then transfer the digest to
* the pointer. At the last block some special
* rules need to apply. We must add a 1 bit
* following the message, then we pad with
* 0's. The total size is encoded as a 64 bit
* number at the end. Now if the last buffer has
* more than 55 octets in it we cannot fit
* the 64 bit number + 10000000 pad on the end
* and must add the 10000000 pad, pad the rest
* of the message with 0's and then create a
* all 0 message with just the 64 bit size
* at the end and run this block through by itself.
* Also the 64 bit int must be in network byte
* order.
*/
int i, leftToFill;
unsigned int *ptr;
if (ctx->howManyInBlock > 55) {
/* special case, we need to process two
* blocks here. One for the current stuff
* plus possibly the pad. The other for
* the size.
*/
leftToFill = sizeof(ctx->SLAblock) - ctx->howManyInBlock;
if (leftToFill == 0) {
/* Should not really happen but I am paranoid */
/* Not paranoid enough! It is possible for leftToFill
* to become negative! AAA!!!! This is another reason
* to pick MD5 :-)...
*/
SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
/* init last block, a bit different then the rest :-) */
ctx->SLAblock[0] = 0x80;
for (i = 1; i < sizeof(ctx->SLAblock); i++) {
ctx->SLAblock[i] = 0x0;
}
} else if (leftToFill == 1) {
ctx->SLAblock[ctx->howManyInBlock] = 0x80;
SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
/* init last block */
memset(ctx->SLAblock, 0, sizeof(ctx->SLAblock));
} else {
ctx->SLAblock[ctx->howManyInBlock] = 0x80;
for (i = (ctx->howManyInBlock + 1);
i < sizeof(ctx->SLAblock);
i++) {
ctx->SLAblock[i] = 0x0;
}
SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
/* init last block */
memset(ctx->SLAblock, 0, sizeof(ctx->SLAblock));
}
/* This is in bits so multiply by 8 */
ctx->runningTotal *= 8;
ptr = (unsigned int *) &ctx->SLAblock[60];
*ptr = htonl(ctx->runningTotal);
SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
} else {
/* easy case, we just pad this
* message to size - end with 0
* add the magic 0x80 to the next
* word and then put the network byte
* order size in the last spot and
* process the block.
*/
ctx->SLAblock[ctx->howManyInBlock] = 0x80;
for (i = (ctx->howManyInBlock + 1);
i < sizeof(ctx->SLAblock);
i++) {
ctx->SLAblock[i] = 0x0;
}
/* get last int spot */
ctx->runningTotal *= 8;
ptr = (unsigned int *) &ctx->SLAblock[60];
*ptr = htonl(ctx->runningTotal);
SLA1processABlock(ctx, (unsigned int *) ctx->SLAblock);
}
/* Now at this point all we need do is transfer the
* digest back to the user
*/
digestBuf[3] = (ctx->H0 & 0xff);
digestBuf[2] = ((ctx->H0 >> 8) & 0xff);
digestBuf[1] = ((ctx->H0 >> 16) & 0xff);
digestBuf[0] = ((ctx->H0 >> 24) & 0xff);
digestBuf[7] = (ctx->H1 & 0xff);
digestBuf[6] = ((ctx->H1 >> 8) & 0xff);
digestBuf[5] = ((ctx->H1 >> 16) & 0xff);
digestBuf[4] = ((ctx->H1 >> 24) & 0xff);
digestBuf[11] = (ctx->H2 & 0xff);
digestBuf[10] = ((ctx->H2 >> 8) & 0xff);
digestBuf[9] = ((ctx->H2 >> 16) & 0xff);
digestBuf[8] = ((ctx->H2 >> 24) & 0xff);
digestBuf[15] = (ctx->H3 & 0xff);
digestBuf[14] = ((ctx->H3 >> 8) & 0xff);
digestBuf[13] = ((ctx->H3 >> 16) & 0xff);
digestBuf[12] = ((ctx->H3 >> 24) & 0xff);
digestBuf[19] = (ctx->H4 & 0xff);
digestBuf[18] = ((ctx->H4 >> 8) & 0xff);
digestBuf[17] = ((ctx->H4 >> 16) & 0xff);
digestBuf[16] = ((ctx->H4 >> 24) & 0xff);
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_sm_make_chunk.c,v 1.38 2002/07/26 22:52:32 jgrimm Exp $
*
* This file includes 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.
*
* These functions work with the state functions in sctp_sm_statefuns.c
* to implement the state operations. These functions implement the
* steps which require modifying existing data structures.
*
* 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>
* C. Robin <chris@hundredacre.ac.uk>
* Jon Grimm <jgrimm@us.ibm.com>
* Xingang Guo <xingang.guo@intel.com>
* Dajiang Zhang <dajiang.zhang@nokia.com>
* Sridhar Samudrala <sri@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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_sm_make_chunk.c,v 1.38 2002/07/26 22:52:32 jgrimm Exp $";
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/ip.h>
#include <linux/ipv6.h>
#include <linux/net.h>
#include <linux/inet.h>
#include <net/sock.h>
#include <linux/skbuff.h>
#include <linux/random.h> /* for get_random_bytes */
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
* Note 4: This parameter, when present, specifies all the
* address types the sending endpoint can support. The absence
* of this parameter indicates that the sending endpoint can
* support any address type.
*/
static const sctp_supported_addrs_param_t sat_param = {
{
SCTP_PARAM_SUPPORTED_ADDRESS_TYPES,
__constant_htons(SCTP_SAT_LEN),
},
{ /* types[] */
SCTP_PARAM_IPV4_ADDRESS,
SCTP_V6(SCTP_PARAM_IPV6_ADDRESS,)
}
};
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
* Note 2: The ECN capable field is reserved for future use of
* Explicit Congestion Notification.
*/
static const sctp_ecn_capable_param_t ecap_param = {
{
SCTP_PARAM_ECN_CAPABLE,
__constant_htons(sizeof(sctp_ecn_capable_param_t)),
}
};
/* A helper to initilize to initilize an op error inside a
* provided chunk, as most cause codes will be embedded inside an
* abort chunk.
*/
void sctp_init_cause(sctp_chunk_t *chunk, __u16 cause_code,
const void *payload, size_t paylen)
{
sctp_errhdr_t err;
int padlen;
__u16 len;
/* Cause code constants are now defined in network order. */
err.cause = cause_code;
len = sizeof(sctp_errhdr_t) + paylen;
padlen = len % 4;
len += padlen;
err.length = htons(len);
sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err);
chunk->subh.err_hdr = sctp_addto_chunk(chunk, paylen, payload);
}
/* 3.3.2 Initiation (INIT) (1)
*
* This chunk is used to initiate a SCTP association between two
* endpoints. The format of the INIT chunk is shown below:
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 1 | Chunk Flags | Chunk Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Initiate Tag |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Advertised Receiver Window Credit (a_rwnd) |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Number of Outbound Streams | Number of Inbound Streams |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Initial TSN |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* \ \
* / Optional/Variable-Length Parameters /
* \ \
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
*
* The INIT chunk contains the following parameters. Unless otherwise
* noted, each parameter MUST only be included once in the INIT chunk.
*
* Fixed Parameters Status
* ----------------------------------------------
* Initiate Tag Mandatory
* Advertised Receiver Window Credit Mandatory
* Number of Outbound Streams Mandatory
* Number of Inbound Streams Mandatory
* Initial TSN Mandatory
*
* Variable Parameters Status Type Value
* -------------------------------------------------------------
* IPv4 Address (Note 1) Optional 5
* IPv6 Address (Note 1) Optional 6
* Cookie Preservative Optional 9
* Reserved for ECN Capable (Note 2) Optional 32768 (0x8000)
* Host Name Address (Note 3) Optional 11
* Supported Address Types (Note 4) Optional 12
*/
sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
const sctp_bind_addr_t *bp,
int priority)
{
sctp_inithdr_t init;
sctpParam_t addrs;
size_t chunksize;
sctp_chunk_t *retval = NULL;
int addrs_len = 0;
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
* Note 1: The INIT chunks can contain multiple addresses that
* can be IPv4 and/or IPv6 in any combination.
*/
retval = NULL;
addrs.v = NULL;
/* Convert the provided bind address list to raw format */
addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, priority);
if (!addrs.v)
goto nodata;
init.init_tag = htonl(asoc->c.my_vtag);
init.a_rwnd = htonl(asoc->rwnd);
init.num_outbound_streams = htons(asoc->c.sinit_num_ostreams);
init.num_inbound_streams = htons(asoc->c.sinit_max_instreams);
init.initial_tsn = htonl(asoc->c.initial_tsn);
chunksize = sizeof(init) + addrs_len + SCTP_SAT_LEN;
chunksize += sizeof(ecap_param);
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
* Note 3: An INIT chunk MUST NOT contain more than one Host
* Name address parameter. Moreover, the sender of the INIT
* MUST NOT combine any other address types with the Host Name
* address in the INIT. The receiver of INIT MUST ignore any
* other address types if the Host Name address parameter is
* present in the received INIT chunk.
*
* PLEASE DO NOT FIXME [This version does not support Host Name.]
*/
retval = sctp_make_chunk(asoc, SCTP_CID_INIT, 0, chunksize);
if (!retval)
goto nodata;
retval->subh.init_hdr =
sctp_addto_chunk(retval, sizeof(init), &init);
retval->param_hdr.v =
sctp_addto_chunk(retval, addrs_len, addrs.v);
sctp_addto_chunk(retval, SCTP_SAT_LEN, &sat_param);
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
nodata:
if (addrs.v)
kfree(addrs.v);
return retval;
}
sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
const sctp_chunk_t *chunk,
int priority)
{
sctp_inithdr_t initack;
sctp_chunk_t *retval;
sctpParam_t addrs;
int addrs_len;
sctp_cookie_param_t *cookie;
int cookie_len;
size_t chunksize;
int error;
sctp_scope_t scope;
sctp_bind_addr_t *bp = NULL;
int flags;
retval = NULL;
/* Build up the bind address list for the association based on
* info from the local endpoint and the remote peer.
*/
bp = sctp_bind_addr_new(priority);
if (!bp)
goto nomem_bindaddr;
/* Look for supported address types parameter and then build
* our address list based on that.
*/
scope = sctp_scope(&asoc->peer.active_path->ipaddr);
flags = (PF_INET6 == asoc->base.sk->family) ? SCTP_ADDR6_ALLOWED : 0;
if (asoc->peer.ipv4_address)
flags |= SCTP_ADDR4_PEERSUPP;
if (asoc->peer.ipv6_address)
flags |= SCTP_ADDR6_PEERSUPP;
error = sctp_bind_addr_copy(bp, &asoc->ep->base.bind_addr,
scope, priority, flags);
if (error)
goto nomem_copyaddr;
addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, priority);
if (!addrs.v)
goto nomem_rawaddr;
initack.init_tag = htonl(asoc->c.my_vtag);
initack.a_rwnd = htonl(asoc->rwnd);
initack.num_outbound_streams = htons(asoc->c.sinit_num_ostreams);
initack.num_inbound_streams = htons(asoc->c.sinit_max_instreams);
initack.initial_tsn = htonl(asoc->c.initial_tsn);
/* FIXME: We really ought to build the cookie right
* into the packet instead of allocating more fresh memory.
*/
cookie = sctp_pack_cookie(asoc->ep, asoc, chunk, &cookie_len,
addrs.v, addrs_len);
if (!cookie)
goto nomem_cookie;
chunksize = sizeof(initack) + addrs_len + cookie_len;
/* Tell peer that we'll do ECN only if peer advertised such cap. */
if (asoc->peer.ecn_capable)
chunksize += sizeof(ecap_param);
/* Now allocate and fill out the chunk. */
retval = sctp_make_chunk(asoc, SCTP_CID_INIT_ACK, 0, chunksize);
if (!retval)
goto nomem_chunk;
/* Per the advice in RFC 2960 6.4, send this reply to
* the source of the INIT packet.
*/
retval->transport = chunk->transport;
retval->subh.init_hdr =
sctp_addto_chunk(retval, sizeof(initack), &initack);
retval->param_hdr.v = sctp_addto_chunk(retval, addrs_len, addrs.v);
sctp_addto_chunk(retval, cookie_len, cookie);
if (asoc->peer.ecn_capable)
sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param);
/* We need to remove the const qualifier at this point. */
retval->asoc = (sctp_association_t *) asoc;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it received the DATA or control chunk
* to which it is replying.
*
* [INIT ACK back to where the INIT came from.]
*/
if (chunk)
retval->transport = chunk->transport;
nomem_chunk:
kfree(cookie);
nomem_cookie:
kfree(addrs.v);
nomem_rawaddr:
nomem_copyaddr:
sctp_bind_addr_free(bp);
nomem_bindaddr:
return retval;
}
/* 3.3.11 Cookie Echo (COOKIE ECHO) (10):
*
* This chunk is used only during the initialization of an association.
* It is sent by the initiator of an association to its peer to complete
* the initialization process. This chunk MUST precede any DATA chunk
* sent within the association, but MAY be bundled with one or more DATA
* chunks in the same packet.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 10 |Chunk Flags | Length |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* / Cookie /
* \ \
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Chunk Flags: 8 bit
*
* Set to zero on transmit and ignored on receipt.
*
* Length: 16 bits (unsigned integer)
*
* Set to the size of the chunk in bytes, including the 4 bytes of
* the chunk header and the size of the Cookie.
*
* Cookie: variable size
*
* This field must contain the exact cookie received in the
* State Cookie parameter from the previous INIT ACK.
*
* An implementation SHOULD make the cookie as small as possible
* to insure interoperability.
*/
sctp_chunk_t *sctp_make_cookie_echo(const sctp_association_t *asoc,
const sctp_chunk_t *chunk)
{
sctp_chunk_t *retval;
void *cookie;
int cookie_len;
cookie = asoc->peer.cookie;
cookie_len = asoc->peer.cookie_len;
/* Build a cookie echo chunk. */
retval = sctp_make_chunk(asoc, SCTP_CID_COOKIE_ECHO, 0, cookie_len);
if (!retval)
goto nodata;
retval->subh.cookie_hdr =
sctp_addto_chunk(retval, cookie_len, cookie);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*
* [COOKIE ECHO back to where the INIT ACK came from.]
*/
if (chunk)
retval->transport = chunk->transport;
nodata:
return retval;
}
/* 3.3.12 Cookie Acknowledgement (COOKIE ACK) (11):
*
* This chunk is used only during the initialization of an
* association. It is used to acknowledge the receipt of a COOKIE
* ECHO chunk. This chunk MUST precede any DATA or SACK chunk sent
* within the association, but MAY be bundled with one or more DATA
* chunks or SACK chunk in the same SCTP packet.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Type = 11 |Chunk Flags | Length = 4 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Chunk Flags: 8 bits
*
* Set to zero on transmit and ignored on receipt.
*/
sctp_chunk_t *sctp_make_cookie_ack(const sctp_association_t *asoc,
const sctp_chunk_t *chunk)
{
sctp_chunk_t *retval;
retval = sctp_make_chunk(asoc, SCTP_CID_COOKIE_ACK, 0, 0);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*
* [COOKIE ACK back to where the COOKIE ECHO came from.]
*/
if (retval && chunk)
retval->transport = chunk->transport;
return retval;
}
/*
* Appendix A: Explicit Congestion Notification:
* CWR:
*
* RFC 2481 details a specific bit for a sender to send in the header of
* its next outbound TCP segment to indicate to its peer that it has
* reduced its congestion window. This is termed the CWR bit. For
* SCTP the same indication is made by including the CWR chunk.
* This chunk contains one data element, i.e. the TSN number that
* was sent in the ECNE chunk. This element represents the lowest
* TSN number in the datagram that was originally marked with the
* CE bit.
*
* 0 1 2 3
* 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Chunk Type=13 | Flags=00000000| Chunk Length = 8 |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
* | Lowest TSN Number |
* +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*
* Note: The CWR is considered a Control chunk.
*/
sctp_chunk_t *sctp_make_cwr(const sctp_association_t *asoc,
const __u32 lowest_tsn,
const sctp_chunk_t *chunk)
{
sctp_chunk_t *retval;
sctp_cwrhdr_t cwr;
cwr.lowest_tsn = htonl(lowest_tsn);
retval = sctp_make_chunk(asoc, SCTP_CID_ECN_CWR, 0,
sizeof(sctp_cwrhdr_t));
if (!retval)
goto nodata;
retval->subh.ecn_cwr_hdr =
sctp_addto_chunk(retval, sizeof(cwr), &cwr);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*
* [Report a reduced congestion window back to where the ECNE
* came from.]
*/
if (chunk)
retval->transport = chunk->transport;
nodata:
return retval;
}
/* Make an ECNE chunk. This is a congestion experienced report. */
sctp_chunk_t *sctp_make_ecne(const sctp_association_t *asoc,
const __u32 lowest_tsn)
{
sctp_chunk_t *retval;
sctp_ecnehdr_t ecne;
ecne.lowest_tsn = htonl(lowest_tsn);
retval = sctp_make_chunk(asoc, SCTP_CID_ECN_ECNE, 0,
sizeof(sctp_ecnehdr_t));
if (!retval)
goto nodata;
retval->subh.ecne_hdr =
sctp_addto_chunk(retval, sizeof(ecne), &ecne);
nodata:
return retval;
}
/* Make a DATA chunk for the given association from the provided
* parameters. However, do not populate the data payload.
*/
sctp_chunk_t *sctp_make_datafrag_empty(sctp_association_t *asoc,
const struct sctp_sndrcvinfo *sinfo,
int data_len, __u8 flags, __u16 ssn)
{
sctp_chunk_t *retval;
sctp_datahdr_t dp;
int chunk_len;
/* We assign the TSN as LATE as possible, not here when
* creating the chunk.
*/
dp.tsn= 1000000; /* This marker is a debugging aid. */
dp.stream = htons(sinfo->sinfo_stream);
dp.ppid = htonl(sinfo->sinfo_ppid);
dp.ssn = htons(ssn);
/* Set the flags for an unordered send. */
if (sinfo->sinfo_flags & MSG_UNORDERED)
flags |= SCTP_DATA_UNORDERED;
chunk_len = sizeof(dp) + data_len;
retval = sctp_make_chunk(asoc, SCTP_CID_DATA, flags, chunk_len);
if (!retval)
goto nodata;
retval->subh.data_hdr = sctp_addto_chunk(retval, sizeof(dp), &dp);
memcpy(&retval->sinfo, sinfo, sizeof(struct sctp_sndrcvinfo));
nodata:
return retval;
}
/* Make a DATA chunk for the given association. Populate the data
* payload.
*/
sctp_chunk_t *sctp_make_datafrag(sctp_association_t *asoc,
const struct sctp_sndrcvinfo *sinfo,
int data_len, const __u8 *data,
__u8 flags, __u16 ssn)
{
sctp_chunk_t *retval;
retval = sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, ssn);
if (retval)
sctp_addto_chunk(retval, data_len, data);
return retval;
}
/* Make a DATA chunk for the given association to ride on stream id
* 'stream', with a payload id of 'payload', and a body of 'data'.
*/
sctp_chunk_t *sctp_make_data(sctp_association_t *asoc,
const struct sctp_sndrcvinfo *sinfo,
int data_len, const __u8 *data)
{
sctp_chunk_t *retval = NULL;
retval = sctp_make_data_empty(asoc, sinfo, data_len);
if (retval)
sctp_addto_chunk(retval, data_len, data);
return retval;
}
/* Make a DATA chunk for the given association to ride on stream id
* 'stream', with a payload id of 'payload', and a body big enough to
* hold 'data_len' octets of data. We use this version when we need
* to build the message AFTER allocating memory.
*/
sctp_chunk_t *sctp_make_data_empty(sctp_association_t *asoc,
const struct sctp_sndrcvinfo *sinfo,
int data_len)
{
__u16 ssn;
__u8 flags = SCTP_DATA_NOT_FRAG;
/* Sockets API Extensions for SCTP 5.2.2
* MSG_UNORDERED - This flag requests the un-ordered delivery of the
* message. If this flag is clear, the datagram is considered an
* ordered send and a new ssn is generated. The flags field is set
* in the inner routine - sctp_make_datafrag_empty().
*/
if (sinfo->sinfo_flags & MSG_UNORDERED) {
ssn = 0;
} else {
ssn = __sctp_association_get_next_ssn(asoc,
sinfo->sinfo_stream);
}
return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, ssn);
}
/* Create a selective ackowledgement (SACK) for the given
* association. This reports on which TSN's we've seen to date,
* including duplicates and gaps.
*/
sctp_chunk_t *sctp_make_sack(const sctp_association_t *asoc)
{
sctp_chunk_t *retval;
sctp_sackhdr_t sack;
sctp_gap_ack_block_t gab;
int length;
__u32 ctsn;
sctp_tsnmap_iter_t iter;
__u16 num_gabs;
__u16 num_dup_tsns = asoc->peer.next_dup_tsn;
const sctp_tsnmap_t *map = &asoc->peer.tsn_map;
ctsn = sctp_tsnmap_get_ctsn(map);
SCTP_DEBUG_PRINTK("make_sack: sackCTSNAck sent is 0x%x.\n",
ctsn);
/* Count the number of Gap Ack Blocks. */
sctp_tsnmap_iter_init(map, &iter);
for (num_gabs = 0;
sctp_tsnmap_next_gap_ack(map, &iter, &gab.start, &gab.end);
num_gabs++) {
/* Do nothing. */
}
/* Initialize the SACK header. */
sack.cum_tsn_ack = htonl(ctsn);
sack.a_rwnd = htonl(asoc->rwnd);
sack.num_gap_ack_blocks = htons(num_gabs);
sack.num_dup_tsns = htons(num_dup_tsns);
length = sizeof(sack)
+ sizeof(sctp_gap_ack_block_t) * num_gabs
+ sizeof(sctp_dup_tsn_t) * num_dup_tsns;
/* Create the chunk. */
retval = sctp_make_chunk(asoc, SCTP_CID_SACK, 0, length);
if (!retval)
goto nodata;
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, etc.) to the same destination transport
* address from which it received the DATA or control chunk to
* which it is replying. This rule should also be followed if
* the endpoint is bundling DATA chunks together with the
* reply chunk.
*
* However, when acknowledging multiple DATA chunks received
* in packets from different source addresses in a single
* SACK, the SACK chunk may be transmitted to one of the
* destination transport addresses from which the DATA or
* control chunks being acknowledged were received.
*
* [BUG: We do not implement the following paragraph.
* Perhaps we should remember the last transport we used for a
* SACK and avoid that (if possible) if we have seen any
* duplicates. --piggy]
*
* When a receiver of a duplicate DATA chunk sends a SACK to a
* multi- homed endpoint it MAY be beneficial to vary the
* destination address and not use the source address of the
* DATA chunk. The reason being that receiving a duplicate
* from a multi-homed endpoint might indicate that the return
* path (as specified in the source address of the DATA chunk)
* for the SACK is broken.
*
* [Send to the address from which we last received a DATA chunk.]
*/
retval->transport = asoc->peer.last_data_from;
retval->subh.sack_hdr =
sctp_addto_chunk(retval, sizeof(sack), &sack);
/* Put the Gap Ack Blocks into the chunk. */
sctp_tsnmap_iter_init(map, &iter);
while(sctp_tsnmap_next_gap_ack(map, &iter, &gab.start, &gab.end)) {
gab.start = htons(gab.start);
gab.end = htons(gab.end);
sctp_addto_chunk(retval,
sizeof(sctp_gap_ack_block_t),
&gab);
}
/* Register the duplicates. */
sctp_addto_chunk(retval,
sizeof(sctp_dup_tsn_t) * num_dup_tsns,
&asoc->peer.dup_tsns);
nodata:
return retval;
}
sctp_chunk_t *sctp_make_shutdown(const sctp_association_t *asoc)
{
sctp_chunk_t *retval;
sctp_shutdownhdr_t shut;
__u32 ctsn;
ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
shut.cum_tsn_ack = htonl(ctsn);
retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN, 0,
sizeof(sctp_shutdownhdr_t));
if (!retval)
goto nodata;
retval->subh.shutdown_hdr =
sctp_addto_chunk(retval, sizeof(shut), &shut);
nodata:
return retval;
}
sctp_chunk_t *sctp_make_shutdown_ack(const sctp_association_t *asoc,
const sctp_chunk_t *chunk)
{
sctp_chunk_t *retval;
retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_ACK, 0, 0);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*
* [ACK back to where the SHUTDOWN came from.]
*/
if (retval && chunk)
retval->transport = chunk->transport;
return retval;
}
sctp_chunk_t *sctp_make_shutdown_complete(const sctp_association_t *asoc,
const sctp_chunk_t *chunk)
{
sctp_chunk_t *retval;
__u8 flags = 0;
/* Maybe set the T-bit if we have no association. */
flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T;
retval = sctp_make_chunk(asoc, SCTP_CID_SHUTDOWN_COMPLETE, flags, 0);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*
* [Report SHUTDOWN COMPLETE back to where the SHUTDOWN ACK
* came from.]
*/
if (retval && chunk)
retval->transport = chunk->transport;
return retval;
}
/* Create an ABORT. Note that we set the T bit if we have no
* association.
*/
sctp_chunk_t *sctp_make_abort(const sctp_association_t *asoc,
const sctp_chunk_t *chunk,
const size_t hint)
{
sctp_chunk_t *retval;
__u8 flags = 0;
/* Maybe set the T-bit if we have no association. */
flags |= asoc ? 0 : SCTP_CHUNK_FLAG_T;
retval = sctp_make_chunk(asoc, SCTP_CID_ABORT, flags, hint);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*
* [ABORT back to where the offender came from.]
*/
if (retval && chunk)
retval->transport = chunk->transport;
return retval;
}
/* Helper to create ABORT with a NO_USER_DATA error. */
sctp_chunk_t *sctp_make_abort_no_data(const sctp_association_t *asoc,
const sctp_chunk_t *chunk, __u32 tsn)
{
sctp_chunk_t *retval;
__u32 payload;
retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t)
+ sizeof(tsn));
if (!retval)
goto no_mem;
/* Put the tsn back into network byte order. */
payload = htonl(tsn);
sctp_init_cause(retval, SCTP_ERROR_NO_DATA, (const void *)&payload,
sizeof(payload));
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*
* [ABORT back to where the offender came from.]
*/
if (chunk)
retval->transport = chunk->transport;
no_mem:
return retval;
}
/* Make a HEARTBEAT chunk. */
sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc,
const sctp_transport_t *transport,
const void *payload, const size_t paylen)
{
sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT,
0, paylen);
if (!retval)
goto nodata;
/* Cast away the 'const', as this is just telling the chunk
* what transport it belongs to.
*/
retval->transport = (sctp_transport_t *) transport;
retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload);
nodata:
return retval;
}
sctp_chunk_t *sctp_make_heartbeat_ack(const sctp_association_t *asoc,
const sctp_chunk_t *chunk,
const void *payload, const size_t paylen)
{
sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT_ACK,
0, paylen);
if (!retval)
goto nodata;
retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*
* [HBACK back to where the HEARTBEAT came from.]
*/
if (chunk)
retval->transport = chunk->transport;
nodata:
return retval;
}
/* Create an Operation Error chunk. */
sctp_chunk_t *sctp_make_op_error(const sctp_association_t *asoc,
const sctp_chunk_t *chunk,
__u16 cause_code, const void *payload,
size_t paylen)
{
sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_ERROR, 0,
sizeof(sctp_errhdr_t) + paylen);
if (!retval)
goto nodata;
sctp_init_cause(retval, cause_code, payload, paylen);
/* RFC 2960 6.4 Multi-homed SCTP Endpoints
*
* An endpoint SHOULD transmit reply chunks (e.g., SACK,
* HEARTBEAT ACK, * etc.) to the same destination transport
* address from which it * received the DATA or control chunk
* to which it is replying.
*/
if (chunk)
retval->transport = chunk->transport;
nodata:
return retval;
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
/* Turn an skb into a chunk.
* FIXME: Eventually move the structure directly inside the skb->cb[].
*/
sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc,
struct sock *sk)
{
sctp_chunk_t *retval = t_new(sctp_chunk_t, GFP_ATOMIC);
if (!retval)
goto nodata;
memset(retval, 0, sizeof(sctp_chunk_t));
if (!sk) {
SCTP_DEBUG_PRINTK("chunkifying skb %p w/o an sk\n", skb);
}
retval->skb = skb;
retval->asoc = (sctp_association_t *) asoc;
retval->num_times_sent = 0;
retval->has_tsn = 0;
retval->rtt_in_progress = 0;
retval->sent_at = jiffies;
retval->singleton = 1;
retval->end_of_packet = 0;
retval->ecn_ce_done = 0;
retval->pdiscard = 0;
/* sctpimpguide-05.txt Section 2.8.2
* M1) Each time a new DATA chunk is transmitted
* set the 'TSN.Missing.Report' count for that TSN to 0. The
* 'TSN.Missing.Report' count will be used to determine missing chunks
* and when to fast retransmit.
*/
retval->tsn_missing_report = 0;
retval->tsn_gap_acked = 0;
retval->fast_retransmit = 0;
/* Polish the bead hole. */
INIT_LIST_HEAD(&retval->transmitted_list);
INIT_LIST_HEAD(&retval->frag_list);
SCTP_DBG_OBJCNT_INC(chunk);
nodata:
return retval;
}
/* Set chunk->source based on the IP header in chunk->skb. */
void sctp_init_source(sctp_chunk_t *chunk)
{
sockaddr_storage_t *source;
struct sk_buff *skb;
struct sctphdr *sh;
struct iphdr *ih4;
struct ipv6hdr *ih6;
source = &chunk->source;
skb = chunk->skb;
ih4 = skb->nh.iph;
ih6 = skb->nh.ipv6h;
sh = chunk->sctp_hdr;
switch (ih4->version) {
case 4:
source->v4.sin_family = AF_INET;
source->v4.sin_port = ntohs(sh->source);
source->v4.sin_addr.s_addr = ih4->saddr;
break;
case 6:
SCTP_V6(
source->v6.sin6_family = AF_INET6;
source->v6.sin6_port = ntohs(sh->source);
source->v6.sin6_addr = ih6->saddr;
/* FIXME: What do we do with scope, etc. ? */
break;
)
default:
/* This is a bogus address type, just bail. */
break;
};
}
/* Extract the source address from a chunk. */
const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk)
{
/* If we have a known transport, use that. */
if (chunk->transport) {
return &chunk->transport->ipaddr;
} else {
/* Otherwise, extract it from the IP header. */
return &chunk->source;
}
}
/* Create a new chunk, setting the type and flags headers from the
* arguments, reserving enough space for a 'paylen' byte payload.
*/
sctp_chunk_t *sctp_make_chunk(const sctp_association_t *asoc,
__u8 type, __u8 flags, int paylen)
{
sctp_chunk_t *retval;
sctp_chunkhdr_t *chunk_hdr;
struct sk_buff *skb;
struct sock *sk;
skb = dev_alloc_skb(WORD_ROUND(sizeof(sctp_chunkhdr_t) + paylen));
if (!skb)
goto nodata;
/* Make room for the chunk header. */
chunk_hdr = (sctp_chunkhdr_t *)skb_put(skb, sizeof(sctp_chunkhdr_t));
skb_pull(skb, sizeof(sctp_chunkhdr_t));
chunk_hdr->type = type;
chunk_hdr->flags = flags;
chunk_hdr->length = htons(sizeof(sctp_chunkhdr_t));
/* Move the data pointer back up to the start of the chunk. */
skb_push(skb, sizeof(sctp_chunkhdr_t));
sk = asoc ? asoc->base.sk : NULL;
retval = sctp_chunkify(skb, asoc, sk);
if (!retval) {
dev_kfree_skb(skb);
goto nodata;
}
retval->chunk_hdr = chunk_hdr;
retval->chunk_end = ((__u8 *)chunk_hdr) + sizeof(sctp_chunkhdr_t);
/* Set the skb to the belonging sock for accounting. */
skb->sk = sk;
return retval;
nodata:
return NULL;
}
/* Release the memory occupied by a chunk. */
void sctp_free_chunk(sctp_chunk_t *chunk)
{
/* Make sure that we are not on any list. */
skb_unlink((struct sk_buff *) chunk);
list_del(&chunk->transmitted_list);
/* Free the chunk skb data and the SCTP_chunk stub itself. */
dev_kfree_skb(chunk->skb);
kfree(chunk);
SCTP_DBG_OBJCNT_DEC(chunk);
}
/* Do a deep copy of a chunk. */
sctp_chunk_t *sctp_copy_chunk(sctp_chunk_t *chunk, const int priority)
{
sctp_chunk_t *retval;
long offset;
retval = t_new(sctp_chunk_t, priority);
if (!retval)
goto nodata;
/* Do the shallow copy. */
*retval = *chunk;
/* Make sure that the copy does NOT think it is on any lists. */
retval->next = NULL;
retval->prev = NULL;
retval->list = NULL;
INIT_LIST_HEAD(&retval->transmitted_list);
INIT_LIST_HEAD(&retval->frag_list);
/* Now we copy the deep structure. */
retval->skb = skb_copy(chunk->skb, priority);
if (!retval->skb) {
kfree(retval);
goto nodata;
}
/* Move the copy headers to point into the new skb. */
offset = ((__u8 *)retval->skb->head)
- ((__u8 *)chunk->skb->head);
if (retval->param_hdr.v)
retval->param_hdr.v += offset;
if (retval->subh.v)
retval->subh.v += offset;
if (retval->chunk_end)
((__u8 *) retval->chunk_end) += offset;
if (retval->chunk_hdr)
((__u8 *) retval->chunk_hdr) += offset;
if (retval->sctp_hdr)
((__u8 *) retval->sctp_hdr) += offset;
SCTP_DBG_OBJCNT_INC(chunk);
return retval;
nodata:
return NULL;
}
/* Append bytes to the end of a chunk. Will panic if chunk is not big
* enough.
*/
void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data)
{
void *target;
void *padding;
int chunklen = ntohs(chunk->chunk_hdr->length);
int padlen = chunklen % 4;
padding = skb_put(chunk->skb, padlen);
target = skb_put(chunk->skb, len);
memset(padding, 0, padlen);
memcpy(target, data, len);
/* Adjust the chunk length field. */
chunk->chunk_hdr->length = htons(chunklen + padlen + len);
chunk->chunk_end = chunk->skb->tail;
return target;
}
/* Append bytes from user space to the end of a chunk. Will panic if
* chunk is not big enough.
* Returns a kernel err value.
*/
int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data)
{
__u8 *target;
int err = 0;
/* Make room in chunk for data. */
target = skb_put(chunk->skb, len);
/* Copy data (whole iovec) into chunk */
if ((err = memcpy_fromiovec(target, data, len)))
goto out;
/* Adjust the chunk length field. */
chunk->chunk_hdr->length =
htons(ntohs(chunk->chunk_hdr->length) + len);
chunk->chunk_end = chunk->skb->tail;
out:
return err;
}
/* Helper function to assign a TSN if needed. This assumes that both
* the data_hdr and association have already been assigned.
*/
void sctp_chunk_assign_tsn(sctp_chunk_t *chunk)
{
if (!chunk->has_tsn) {
/* This is the last possible instant to
* assign a TSN.
*/
chunk->subh.data_hdr->tsn =
htonl(__sctp_association_get_next_tsn(chunk->asoc));
chunk->has_tsn = 1;
}
}
/* Create a CLOSED association to use with an incoming packet. */
sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *ep,
sctp_chunk_t *chunk,
int priority)
{
sctp_association_t *asoc;
sctp_scope_t scope;
/* Create the bare association. */
scope = sctp_scope(sctp_source(chunk));
asoc = sctp_association_new(ep, ep->base.sk, scope, priority);
if (!asoc)
goto nodata;
/* Create an entry for the source address of the packet. */
switch (chunk->skb->nh.iph->version) {
case 4:
asoc->c.peer_addr.v4.sin_family = AF_INET;
asoc->c.peer_addr.v4.sin_port = ntohs(chunk->sctp_hdr->source);
asoc->c.peer_addr.v4.sin_addr.s_addr =
chunk->skb->nh.iph->saddr;
break;
case 6:
asoc->c.peer_addr.v6.sin6_family = AF_INET6;
asoc->c.peer_addr.v6.sin6_port
= ntohs(chunk->sctp_hdr->source);
asoc->c.peer_addr.v6.sin6_flowinfo = 0; /* BUG BUG BUG */
asoc->c.peer_addr.v6.sin6_addr = chunk->skb->nh.ipv6h->saddr;
asoc->c.peer_addr.v6.sin6_scope_id = 0; /* BUG BUG BUG */
break;
default:
/* Yikes! I never heard of this kind of address. */
goto fail;
};
nodata:
return asoc;
fail:
sctp_association_free(asoc);
return NULL;
}
/* Build a cookie representing asoc.
* This INCLUDES the param header needed to put the cookie in the INIT ACK.
*/
sctp_cookie_param_t *sctp_pack_cookie(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
const sctp_chunk_t *init_chunk,
int *cookie_len,
const __u8 *raw_addrs, int addrs_len)
{
sctp_cookie_param_t *retval;
sctp_signed_cookie_t *cookie;
int headersize, bodysize;
headersize = sizeof(sctp_paramhdr_t) + SCTP_SECRET_SIZE;
bodysize = sizeof(sctp_cookie_t)
+ ntohs(init_chunk->chunk_hdr->length) + addrs_len;
/* Pad out the cookie to a multiple to make the signature
* functions simpler to write.
*/
if (bodysize % SCTP_COOKIE_MULTIPLE)
bodysize += SCTP_COOKIE_MULTIPLE
- (bodysize % SCTP_COOKIE_MULTIPLE);
*cookie_len = headersize + bodysize;
retval = (sctp_cookie_param_t *)
kmalloc(*cookie_len, GFP_ATOMIC);
if (!retval) {
*cookie_len = 0;
goto nodata;
}
/* Clear this memory since we are sending this data structure
* out on the network.
*/
memset(retval, 0x00, *cookie_len);
cookie = (sctp_signed_cookie_t *) retval->body;
/* Set up the parameter header. */
retval->p.type = SCTP_PARAM_STATE_COOKIE;
retval->p.length = htons(*cookie_len);
/* Copy the cookie part of the association itself. */
cookie->c = asoc->c;
/* Save the raw address list length in the cookie. */
cookie->c.raw_addr_list_len = addrs_len;
/* Set an expiration time for the cookie. */
do_gettimeofday(&cookie->c.expiration);
tv_add(&asoc->cookie_life, &cookie->c.expiration);
/* Copy the peer's init packet. */
memcpy(&cookie->c.peer_init[0], init_chunk->chunk_hdr,
ntohs(init_chunk->chunk_hdr->length));
/* Copy the raw local address list of the association. */
memcpy((__u8 *)&cookie->c.peer_init[0] +
ntohs(init_chunk->chunk_hdr->length), raw_addrs,
addrs_len);
/* Sign the message. */
sctp_hash_digest(ep->secret_key[ep->current_key], SCTP_SECRET_SIZE,
(__u8 *) &cookie->c, bodysize, cookie->signature);
nodata:
return retval;
}
/* Unpack the cookie from COOKIE ECHO chunk, recreating the association. */
sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
sctp_chunk_t *chunk, int priority,
int *error)
{
sctp_association_t *retval = NULL;
sctp_signed_cookie_t *cookie;
sctp_cookie_t *bear_cookie;
int headersize, bodysize;
int fixed_size, var_size1, var_size2, var_size3;
__u8 digest_buf[SCTP_SIGNATURE_SIZE];
int secret;
sctp_scope_t scope;
__u8 *raw_addr_list;
headersize = sizeof(sctp_chunkhdr_t) + SCTP_SECRET_SIZE;
bodysize = ntohs(chunk->chunk_hdr->length) - headersize;
fixed_size = headersize + sizeof(sctp_cookie_t);
/* Verify that the chunk looks like it even has a cookie.
* There must be enough room for our cookie and our peer's
* INIT chunk.
*/
if (ntohs(chunk->chunk_hdr->length) <
(fixed_size + sizeof(sctp_chunkhdr_t)))
goto malformed;
/* Verify that the cookie has been padded out. */
if (bodysize % SCTP_COOKIE_MULTIPLE)
goto malformed;
/* Process the cookie. */
cookie = chunk->subh.cookie_hdr;
bear_cookie = &cookie->c;
var_size1 = ntohs(chunk->chunk_hdr->length) - fixed_size;
var_size2 = ntohs(bear_cookie->peer_init->chunk_hdr.length);
var_size3 = bear_cookie->raw_addr_list_len;
/* Check the signature. */
secret = ep->current_key;
sctp_hash_digest(ep->secret_key[secret], SCTP_SECRET_SIZE,
(__u8 *) bear_cookie, bodysize,
digest_buf);
if (memcmp(digest_buf, cookie->signature, SCTP_SIGNATURE_SIZE)) {
/* Try the previous key. */
secret = ep->last_key;
sctp_hash_digest(ep->secret_key[secret], SCTP_SECRET_SIZE,
(__u8 *) bear_cookie, bodysize, digest_buf);
if (memcmp(digest_buf, cookie->signature, SCTP_SIGNATURE_SIZE)) {
/* Yikes! Still bad signature! */
*error = -SCTP_IERROR_BAD_SIG;
goto fail;
}
}
/* Check to see if the cookie is stale. If there is already
* an association, there is no need to check cookie's expiration
* for init collision case of lost COOKIE ACK.
*/
if (!asoc && tv_lt(bear_cookie->expiration, chunk->skb->stamp)) {
*error = -SCTP_IERROR_STALE_COOKIE;
goto fail;
}
/* Make a new base association. */
scope = sctp_scope(sctp_source(chunk));
retval = sctp_association_new(ep, ep->base.sk, scope, priority);
if (!retval) {
*error = -SCTP_IERROR_NOMEM;
goto fail;
}
/* Set up our peer's port number. */
retval->peer.port = ntohs(chunk->sctp_hdr->source);
/* Populate the association from the cookie. */
retval->c = *bear_cookie;
/* Build the bind address list based on the cookie. */
raw_addr_list = (__u8 *) bear_cookie +
sizeof(sctp_cookie_t) + var_size2;
if (sctp_raw_to_bind_addrs(&retval->base.bind_addr, raw_addr_list,
var_size3, retval->base.bind_addr.port,
priority)) {
*error = -SCTP_IERROR_NOMEM;
goto fail;
}
retval->next_tsn = retval->c.initial_tsn;
retval->ctsn_ack_point = retval->next_tsn - 1;
/* The INIT stuff will be done by the side effects. */
return retval;
fail:
if (retval)
sctp_association_free(retval);
return NULL;
malformed:
/* Yikes! The packet is either corrupt or deliberately
* malformed.
*/
*error = -SCTP_IERROR_MALFORMED;
goto fail;
}
/********************************************************************
* 3rd Level Abstractions
********************************************************************/
/* Unpack the parameters in an INIT packet.
* FIXME: There is no return status to allow callers to do
* error handling.
*/
void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
const sockaddr_storage_t *peer_addr,
sctp_init_chunk_t *peer_init,
int priority)
{
sctpParam_t param;
__u8 *end;
sctp_transport_t *transport;
list_t *pos, *temp;
/* We must include the address that the INIT packet came from.
* This is the only address that matters for an INIT packet.
* When processing a COOKIE ECHO, we retrieve the from address
* of the INIT from the cookie.
*/
/* This implementation defaults to making the first transport
* added as the primary transport. The source address seems to
* be a a better choice than any of the embedded addresses.
*/
if (peer_addr)
sctp_assoc_add_peer(asoc, peer_addr, priority);
/* Process the initialization parameters. */
end = ((__u8 *)peer_init + ntohs(peer_init->chunk_hdr.length));
for (param.v = peer_init->init_hdr.params;
param.v < end;
param.v += WORD_ROUND(ntohs(param.p->length))) {
if (!sctp_process_param(asoc, param, peer_addr, cid,
priority))
goto clean_up;
}
/* The fixed INIT headers are always in network byte
* order.
*/
asoc->peer.i.init_tag =
ntohl(peer_init->init_hdr.init_tag);
asoc->peer.i.a_rwnd =
ntohl(peer_init->init_hdr.a_rwnd);
asoc->peer.i.num_outbound_streams =
ntohs(peer_init->init_hdr.num_outbound_streams);
asoc->peer.i.num_inbound_streams =
ntohs(peer_init->init_hdr.num_inbound_streams);
asoc->peer.i.initial_tsn =
ntohl(peer_init->init_hdr.initial_tsn);
/* Apply the upper bounds for output streams based on peer's
* number of inbound streams.
*/
if (asoc->c.sinit_num_ostreams >
ntohs(peer_init->init_hdr.num_inbound_streams)) {
asoc->c.sinit_num_ostreams =
ntohs(peer_init->init_hdr.num_inbound_streams);
}
/* Copy Initiation tag from INIT to VT_peer in cookie. */
asoc->c.peer_vtag = asoc->peer.i.init_tag;
/* Peer Rwnd : Current calculated value of the peer's rwnd. */
asoc->peer.rwnd = asoc->peer.i.a_rwnd;
/* RFC 2960 7.2.1 The initial value of ssthresh MAY be arbitrarily
* high (for example, implementations MAY use the size of the receiver
* advertised window).
*/
list_for_each(pos, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports);
transport->ssthresh = asoc->peer.i.a_rwnd;
}
/* Set up the TSN tracking pieces. */
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,
asoc->peer.i.initial_tsn);
/* ADDIP Section 4.1 ASCONF Chunk Procedures
*
* When an endpoint has an ASCONF signaled change to be sent to the
* remote endpoint it should do the following:
* ...
* A2) A serial number should be assigned to the Chunk. The serial
* number should be a monotonically increasing number. All serial
* numbers are defined to be initialized at the start of the
* association to the same value as the Initial TSN.
*/
asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1;
return;
clean_up:
/* Release the transport structures. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports);
list_del(pos);
sctp_transport_free(transport);
}
}
/* Update asoc with the option described in param.
*
* RFC2960 3.3.2.1 Optional/Variable Length Parameters in INIT
*
* asoc is the association to update.
* param is the variable length parameter to use for update.
* cid tells us if this is an INIT, INIT ACK or COOKIE ECHO.
* If the current packet is an INIT we want to minimize the amount of
* work we do. In particular, we should not build transport
* structures for the addresses.
*/
int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
const sockaddr_storage_t *peer_addr,
sctp_cid_t cid, int priority)
{
sockaddr_storage_t addr;
int j;
int i;
int retval = 1;
sctp_scope_t scope;
/* We maintain all INIT parameters in network byte order all the
* time. This allows us to not worry about whether the parameters
* came from a fresh INIT, and INIT ACK, or were stored in a cookie.
*/
switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
if (SCTP_CID_INIT != cid) {
sctp_param2sockaddr(&addr, param, asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr, priority);
}
break;
case SCTP_PARAM_IPV6_ADDRESS:
if (SCTP_CID_INIT != cid) {
if (PF_INET6 == asoc->base.sk->family) {
sctp_param2sockaddr(&addr, param,
asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr,
priority);
}
}
break;
case SCTP_PARAM_COOKIE_PRESERVATIVE:
asoc->cookie_preserve =
ntohl(param.bht->lifespan_increment);
break;
case SCTP_PARAM_HOST_NAME_ADDRESS:
SCTP_DEBUG_PRINTK("unimplmented SCTP_HOST_NAME_ADDRESS\n");
break;
case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
/* Turn off the default values first so we'll know which
* ones are really set by the peer.
*/
asoc->peer.ipv4_address = 0;
asoc->peer.ipv6_address = 0;
j = (ntohs(param.p->length) -
sizeof(sctp_paramhdr_t)) /
sizeof(__u16);
for (i = 0; i < j; ++i) {
switch (param.sat->types[i]) {
case SCTP_PARAM_IPV4_ADDRESS:
asoc->peer.ipv4_address = 1;
break;
case SCTP_PARAM_IPV6_ADDRESS:
asoc->peer.ipv6_address = 1;
break;
case SCTP_PARAM_HOST_NAME_ADDRESS:
asoc->peer.hostname_address = 1;
break;
default: /* Just ignore anything else. */
break;
};
}
break;
case SCTP_PARAM_STATE_COOKIE:
asoc->peer.cookie_len =
ntohs(param.p->length) =
sizeof(sctp_paramhdr_t);
asoc->peer.cookie = param.cookie->body;
break;
case SCTP_PARAM_HEATBEAT_INFO:
SCTP_DEBUG_PRINTK("unimplmented "
"SCTP_PARAM_HEATBEAT_INFO\n");
break;
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
SCTP_DEBUG_PRINTK("unimplemented "
"SCTP_PARAM_UNRECOGNIZED_PARAMETERS\n");
break;
case SCTP_PARAM_ECN_CAPABLE:
asoc->peer.ecn_capable = 1;
break;
default:
SCTP_DEBUG_PRINTK("Ignoring param: %d for association %p.\n",
ntohs(param.p->type), asoc);
/* FIXME: The entire parameter processing really needs
* redesigned. For now, always return success as doing
* otherwise craters the system.
*/
retval = 1;
break;
};
return retval;
}
/* Select a new verification tag. */
__u32 sctp_generate_tag(const sctp_endpoint_t *ep)
{
/* I believe that this random number generator complies with RFC1750.
* A tag of 0 is reserved for special cases (e.g. INIT).
*/
__u32 x;
do {
get_random_bytes(&x, sizeof(__u32));
} while (x == 0);
return x;
}
/* Select an initial TSN to send during startup. */
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
{
/* I believe that this random number generator complies with RFC1750. */
__u32 retval;
get_random_bytes(&retval, sizeof(__u32));
return retval;
}
/********************************************************************
* 4th Level Abstractions
********************************************************************/
/* Convert from an SCTP IP parameter to a sockaddr_storage_t. */
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctpParam_t param, __u16 port)
{
switch(param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
addr->v4.sin_family = AF_INET;
addr->v4.sin_port = port;
addr->v4.sin_addr.s_addr = param.v4->addr.s_addr;
break;
case SCTP_PARAM_IPV6_ADDRESS:
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = port;
addr->v6.sin6_flowinfo = 0; /* BUG */
addr->v6.sin6_addr = param.v6->addr;
addr->v6.sin6_scope_id = 0; /* BUG */
break;
default:
SCTP_DEBUG_PRINTK("Illegal address type %d\n",
ntohs(param.p->type));
break;
};
}
/* Convert an IP address in an SCTP param into a sockaddr_in. */
/* Returns true if a valid conversion was possible. */
int sctp_addr2sockaddr(sctpParam_t p, sockaddr_storage_t *sa)
{
if (!p.v)
return 0;
switch (p.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
sa->v4.sin_addr = *((struct in_addr *)&p.v4->addr);
sa->v4.sin_family = AF_INET;
break;
case SCTP_PARAM_IPV6_ADDRESS:
*((struct in6_addr *)&sa->v4.sin_addr)
= p.v6->addr;
sa->v4.sin_family = AF_INET6;
break;
default:
return 0;
};
return 1;
}
/* Convert from an IP version number to an Address Family symbol. */
int ipver2af(__u8 ipver)
{
int family;
switch (ipver) {
case 4:
family = AF_INET;
break;
case 6:
family = AF_INET6;
break;
default:
family = 0;
break;
};
return family;
}
/* Convert a sockaddr_in to IP address in an SCTP para. */
/* Returns true if a valid conversion was possible. */
int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctpParam_t p)
{
int len = 0;
switch (sa->v4.sin_family) {
case AF_INET:
p.p->type = SCTP_PARAM_IPV4_ADDRESS;
p.p->length = ntohs(sizeof(sctp_ipv4addr_param_t));
len = sizeof(sctp_ipv4addr_param_t);
p.v4->addr.s_addr = sa->v4.sin_addr.s_addr;
break;
case AF_INET6:
p.p->type = SCTP_PARAM_IPV6_ADDRESS;
p.p->length = ntohs(sizeof(sctp_ipv6addr_param_t));
len = sizeof(sctp_ipv6addr_param_t);
p.v6->addr = *(&sa->v6.sin6_addr);
break;
default:
printk(KERN_WARNING "sockaddr2sctp_addr: Illegal family %d.\n",
sa->v4.sin_family);
return 0;
};
return len;
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_sm_sideeffect.c,v 1.44 2002/08/16 19:30:50 jgrimm Exp $
*
* These functions work with the state functions in sctp_sm_statefuns.c
* to implement that state operations. These functions implement the
* steps which require modifying existing data structures.
*
* 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>
* Hui Huang <hui.huang@nokia.com>
* Dajiang Zhang <dajiang.zhang@nokia.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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_sm_sideeffect.c,v 1.44 2002/08/16 19:30:50 jgrimm Exp $";
#include <linux/skbuff.h>
#include <linux/types.h>
#include <linux/socket.h>
#include <linux/ip.h>
#include <net/sock.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
/* Do forward declarations of static functions. */
static void sctp_do_ecn_ce_work(sctp_association_t *asoc,
__u32 lowest_tsn);
static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
__u32 lowest_tsn,
sctp_chunk_t *);
static void sctp_do_ecn_cwr_work(sctp_association_t *asoc,
__u32 lowest_tsn);
static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
sctp_transport_t *transport);
static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *asoc);
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *asoc);
static void sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc,
sctp_chunk_t *chunk,
sctp_init_chunk_t *peer_init,
int priority);
static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *, sctp_association_t *);
static void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *, sctp_association_t *,
sctp_bind_addr_t *);
static void sctp_cmd_transport_reset(sctp_cmd_seq_t *, sctp_association_t *,
sctp_transport_t *);
static void sctp_cmd_transport_on(sctp_cmd_seq_t *, sctp_association_t *,
sctp_transport_t *, sctp_chunk_t *);
static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *,
sctp_sackhdr_t *);
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *,
sctp_chunk_t *);
/* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real
* functionality there.
*/
#define DEBUG_PRE \
SCTP_DEBUG_PRINTK("sctp_do_sm prefn: " \
"ep %p, %s, %s, asoc %p[%s], %s\n", \
ep, sctp_evttype_tbl[event_type], \
(*debug_fn)(subtype), asoc, \
sctp_state_tbl[state], state_fn->name)
#define DEBUG_POST \
SCTP_DEBUG_PRINTK("sctp_do_sm postfn: " \
"asoc %p, status: %s\n", \
asoc, sctp_status_tbl[status])
#define DEBUG_POST_SFX \
SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \
error, asoc, \
sctp_state_tbl[sctp_id2assoc(ep->base.sk, \
sctp_assoc2id(asoc))?asoc->state:SCTP_STATE_CLOSED])
/*
* This is the master state machine processing function.
*
* If you want to understand all of lksctp, this is a
* good place to start.
*/
int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
sctp_endpoint_t *ep,
sctp_association_t *asoc,
void *event_arg,
int priority)
{
sctp_cmd_seq_t commands;
sctp_sm_table_entry_t *state_fn;
sctp_disposition_t status;
int error = 0;
typedef const char *(printfn_t)(sctp_subtype_t);
static printfn_t *table[] = {
NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname,
};
printfn_t *debug_fn __attribute__ ((unused)) = table[event_type];
/* Look up the state function, run it, and then process the
* side effects. These three steps are the heart of lksctp.
*/
state_fn = sctp_sm_lookup_event(event_type, state, subtype);
sctp_init_cmd_seq(&commands);
DEBUG_PRE;
status = (*state_fn->fn)(ep, asoc, subtype, event_arg, &commands);
DEBUG_POST;
error = sctp_side_effects(event_type, subtype, state,
ep, asoc, event_arg,
status, &commands,
priority);
DEBUG_POST_SFX;
return error;
}
#undef DEBUG_PRE
#undef DEBUG_POST
/*****************************************************************
* This the master state function side effect processing function.
*****************************************************************/
int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state,
sctp_endpoint_t *ep,
sctp_association_t *asoc,
void *event_arg,
sctp_disposition_t status,
sctp_cmd_seq_t *commands,
int priority)
{
int error;
/* FIXME - Most of the dispositions left today would be categorized
* as "exceptional" dispositions. For those dispositions, it
* may not be proper to run through any of the commands at all.
* For example, the command interpreter might be run only with
* disposition SCTP_DISPOSITION_CONSUME.
*/
if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state,
ep, asoc,
event_arg, status,
commands, priority)))
goto bail;
switch (status) {
case SCTP_DISPOSITION_DISCARD:
SCTP_DEBUG_PRINTK("Ignored sctp protocol event - state %d, "
"event_type %d, event_id %d\n",
state, event_type, subtype.chunk);
break;
case SCTP_DISPOSITION_NOMEM:
/* We ran out of memory, so we need to discard this
* packet.
*/
/* BUG--we should now recover some memory, probably by
* reneging...
*/
break;
case SCTP_DISPOSITION_DELETE_TCB:
/* This should now be a command. */
break;
case SCTP_DISPOSITION_CONSUME:
case SCTP_DISPOSITION_ABORT:
/*
* We should no longer have much work to do here as the
* real work has been done as explicit commands above.
*/
break;
case SCTP_DISPOSITION_VIOLATION:
printk(KERN_ERR "sctp protocol violation state %d "
"chunkid %d\n", state, subtype.chunk);
break;
case SCTP_DISPOSITION_NOT_IMPL:
printk(KERN_WARNING "sctp unimplemented feature in state %d, "
"event_type %d, event_id %d\n",
state, event_type, subtype.chunk);
break;
case SCTP_DISPOSITION_BUG:
printk(KERN_ERR "sctp bug in state %d, "
"event_type %d, event_id %d\n",
state, event_type, subtype.chunk);
BUG();
break;
default:
printk(KERN_ERR "sctp impossible disposition %d "
"in state %d, event_type %d, event_id %d\n",
status, state, event_type, subtype.chunk);
BUG();
break;
};
bail:
return error;
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
/* This is the side-effect interpreter. */
int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_state_t state, sctp_endpoint_t *ep,
sctp_association_t *asoc, void *event_arg,
sctp_disposition_t status, sctp_cmd_seq_t *commands,
int priority)
{
int error = 0;
int force;
sctp_cmd_t *command;
sctp_chunk_t *new_obj;
sctp_chunk_t *chunk;
sctp_packet_t *packet;
struct timer_list *timer;
unsigned long timeout;
sctp_transport_t *t;
sctp_sackhdr_t sackh;
chunk = (sctp_chunk_t *) event_arg;
/* Note: This whole file is a huge candidate for rework.
* For example, each command could either have its own handler, so
* the loop would look like:
* while (cmds)
* cmd->handle(x, y, z)
* --jgrimm
*/
while (NULL != (command = sctp_next_cmd(commands))) {
switch (command->verb) {
case SCTP_CMD_NOP:
/* Do nothing. */
break;
case SCTP_CMD_NEW_ASOC:
/* Register a new association. */
asoc = command->obj.ptr;
/* Register with the endpoint. */
sctp_endpoint_add_asoc(ep, asoc);
sctp_hash_established(asoc);
break;
case SCTP_CMD_UPDATE_ASSOC:
sctp_assoc_update(asoc, command->obj.ptr);
break;
case SCTP_CMD_PURGE_OUTQUEUE:
sctp_outqueue_teardown(&asoc->outqueue);
break;
case SCTP_CMD_DELETE_TCB:
/* Delete the current association. */
sctp_unhash_established(asoc);
sctp_association_free(asoc);
asoc = NULL;
break;
case SCTP_CMD_NEW_STATE:
/* Enter a new state. */
asoc->state = command->obj.state;
asoc->state_timestamp = jiffies;
break;
case SCTP_CMD_REPORT_TSN:
/* Record the arrival of a TSN. */
sctp_tsnmap_mark(&asoc->peer.tsn_map,
command->obj.u32);
break;
case SCTP_CMD_GEN_SACK:
/* Generate a Selective ACK.
* The argument tells us whether to just count
* the packet and MAYBE generate a SACK, or
* force a SACK out.
*/
force = command->obj.i32;
error = sctp_gen_sack(asoc, force, commands);
break;
case SCTP_CMD_PROCESS_SACK:
/* Process an inbound SACK. */
error = sctp_cmd_process_sack(commands, asoc,
command->obj.ptr);
break;
case SCTP_CMD_GEN_INIT_ACK:
/* Generate an INIT ACK chunk. */
new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC);
if (!new_obj)
goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj));
break;
case SCTP_CMD_PEER_INIT:
/* Process a unified INIT from the peer. */
sctp_cmd_process_init(commands,
asoc, chunk, command->obj.ptr,
priority);
break;
case SCTP_CMD_GEN_COOKIE_ECHO:
/* Generate a COOKIE ECHO chunk. */
new_obj = sctp_make_cookie_echo(asoc, chunk);
if (!new_obj)
goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj));
break;
case SCTP_CMD_GEN_SHUTDOWN:
/* Generate SHUTDOWN when in SHUTDOWN_SENT state.
* Reset error counts.
*/
asoc->overall_error_count = 0;
/* Generate a SHUTDOWN chunk. */
new_obj = sctp_make_shutdown(asoc);
if (!new_obj)
goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj));
break;
case SCTP_CMD_CHUNK_ULP:
/* Send a chunk to the sockets layer. */
SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
"chunk_up:",
command->obj.ptr,
"ulpq:",
&asoc->ulpq);
sctp_ulpqueue_tail_data(&asoc->ulpq,
command->obj.ptr,
GFP_ATOMIC);
break;
case SCTP_CMD_EVENT_ULP:
/* Send a notification to the sockets layer. */
SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n",
"event_up:",
command->obj.ptr,
"ulpq:",
&asoc->ulpq);
sctp_ulpqueue_tail_event(&asoc->ulpq,
command->obj.ptr);
break;
case SCTP_CMD_REPLY:
/* Send a chunk to our peer. */
error = sctp_push_outqueue(&asoc->outqueue,
command->obj.ptr);
break;
case SCTP_CMD_SEND_PKT:
/* Send a full packet to our peer. */
packet = command->obj.ptr;
sctp_packet_transmit(packet);
sctp_transport_free(packet->transport);
sctp_packet_free(packet);
break;
case SCTP_CMD_RETRAN:
/* Mark a transport for retransmission. */
sctp_retransmit(&asoc->outqueue,
command->obj.transport, 0);
break;
case SCTP_CMD_TRANSMIT:
/* Kick start transmission. */
error = sctp_flush_outqueue(&asoc->outqueue, 0);
break;
case SCTP_CMD_ECN_CE:
/* Do delayed CE processing. */
sctp_do_ecn_ce_work(asoc, command->obj.u32);
break;
case SCTP_CMD_ECN_ECNE:
/* Do delayed ECNE processing. */
new_obj = sctp_do_ecn_ecne_work(asoc,
command->obj.u32,
chunk);
if (new_obj) {
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY,
SCTP_CHUNK(new_obj));
}
break;
case SCTP_CMD_ECN_CWR:
/* Do delayed CWR processing. */
sctp_do_ecn_cwr_work(asoc, command->obj.u32);
break;
case SCTP_CMD_SETUP_T2:
sctp_cmd_setup_t2(commands, asoc, command->obj.ptr);
break;
case SCTP_CMD_TIMER_START:
timer = &asoc->timers[command->obj.to];
timeout = asoc->timeouts[command->obj.to];
if (!timeout)
BUG();
timer->expires = jiffies + timeout;
sctp_association_hold(asoc);
add_timer(timer);
break;
case SCTP_CMD_TIMER_RESTART:
timer = &asoc->timers[command->obj.to];
timeout = asoc->timeouts[command->obj.to];
if (!mod_timer(timer, jiffies + timeout))
sctp_association_hold(asoc);
break;
case SCTP_CMD_TIMER_STOP:
timer = &asoc->timers[command->obj.to];
if (timer_pending(timer) && del_timer(timer))
sctp_association_put(asoc);
break;
case SCTP_CMD_INIT_RESTART:
/* Do the needed accounting and updates
* associated with restarting an initialization
* timer.
*/
asoc->counters[SCTP_COUNTER_INIT_ERROR]++;
asoc->timeouts[command->obj.to] *= 2;
if (asoc->timeouts[command->obj.to] >
asoc->max_init_timeo) {
asoc->timeouts[command->obj.to] =
asoc->max_init_timeo;
}
sctp_add_cmd_sf(commands,
SCTP_CMD_TIMER_RESTART,
SCTP_TO(command->obj.to));
break;
case SCTP_CMD_INIT_FAILED:
sctp_cmd_init_failed(commands, asoc);
break;
case SCTP_CMD_ASSOC_FAILED:
sctp_cmd_assoc_failed(commands, asoc);
break;
case SCTP_CMD_COUNTER_INC:
asoc->counters[command->obj.counter]++;
break;
case SCTP_CMD_COUNTER_RESET:
asoc->counters[command->obj.counter] = 0;
break;
case SCTP_CMD_REPORT_DUP:
if (asoc->peer.next_dup_tsn < SCTP_MAX_DUP_TSNS) {
asoc->peer.dup_tsns[asoc->peer.next_dup_tsn++] =
ntohl(command->obj.u32);
}
break;
case SCTP_CMD_REPORT_BIGGAP:
SCTP_DEBUG_PRINTK("Big gap: %x to %x\n",
sctp_tsnmap_get_ctsn(
&asoc->peer.tsn_map),
command->obj.u32);
break;
case SCTP_CMD_REPORT_BAD_TAG:
SCTP_DEBUG_PRINTK("vtag mismatch!\n");
break;
case SCTP_CMD_SET_BIND_ADDR:
sctp_cmd_set_bind_addrs(commands, asoc,
command->obj.bp);
break;
case SCTP_CMD_STRIKE:
/* Mark one strike against a transport. */
sctp_do_8_2_transport_strike(asoc,
command->obj.transport);
break;
case SCTP_CMD_TRANSPORT_RESET:
t = command->obj.transport;
sctp_cmd_transport_reset(commands, asoc, t);
break;
case SCTP_CMD_TRANSPORT_ON:
t = command->obj.transport;
sctp_cmd_transport_on(commands, asoc, t, chunk);
break;
case SCTP_CMD_HB_TIMERS_START:
sctp_cmd_hb_timers_start(commands, asoc);
break;
case SCTP_CMD_REPORT_ERROR:
error = command->obj.error;
break;
case SCTP_CMD_PROCESS_CTSN:
/* Dummy up a SACK for processing. */
sackh.cum_tsn_ack = command->obj.u32;
sackh.a_rwnd = 0;
sackh.num_gap_ack_blocks = 0;
sackh.num_dup_tsns = 0;
sctp_add_cmd_sf(commands,
SCTP_CMD_PROCESS_SACK,
SCTP_SACKH(&sackh));
break;
case SCTP_CMD_DISCARD_PACKET:
/* We need to discard the whole packet. */
chunk->pdiscard = 1;
break;
default:
printk(KERN_WARNING "Impossible command: %u, %p\n",
command->verb, command->obj.ptr);
break;
};
}
return error;
nomem:
error = -ENOMEM;
return error;
}
/* A helper function for delayed processing of INET ECN CE bit. */
static void sctp_do_ecn_ce_work(sctp_association_t *asoc, __u32 lowest_tsn)
{
/*
* Save the TSN away for comparison when we receive CWR
* Note: dp->TSN is expected in host endian
*/
asoc->last_ecne_tsn = lowest_tsn;
asoc->need_ecne = 1;
}
/* Helper function for delayed processing of SCTP ECNE chunk. */
/* RFC 2960 Appendix A
*
* RFC 2481 details a specific bit for a sender to send in
* the header of its next outbound TCP segment to indicate to
* its peer that it has reduced its congestion window. This
* is termed the CWR bit. For SCTP the same indication is made
* by including the CWR chunk. This chunk contains one data
* element, i.e. the TSN number that was sent in the ECNE chunk.
* This element represents the lowest TSN number in the datagram
* that was originally marked with the CE bit.
*/
static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
__u32 lowest_tsn,
sctp_chunk_t *chunk)
{
sctp_chunk_t *repl;
sctp_transport_t *transport;
/* Our previously transmitted packet ran into some congestion
* so we should take action by reducing cwnd and ssthresh
* and then ACK our peer that we we've done so by
* sending a CWR.
*/
/* Find which transport's congestion variables
* need to be adjusted.
*/
transport = sctp_assoc_lookup_tsn(asoc, lowest_tsn);
/* Update the congestion variables. */
if (transport)
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_ECNE);
/* Save away a rough idea of when we last sent out a CWR.
* We compare against this value (see above) to decide if
* this is a fairly new request.
* Note that this is not a perfect solution. We may
* have moved beyond the window (several times) by the
* next time we get an ECNE. However, it is cute. This idea
* came from Randy's reference code.
*
* Here's what RFC 2960 has to say about CWR. This is NOT
* what we do.
*
* RFC 2960 Appendix A
*
* CWR:
*
* RFC 2481 details a specific bit for a sender to send in
* the header of its next outbound TCP segment to indicate
* to its peer that it has reduced its congestion window.
* This is termed the CWR bit. For SCTP the same
* indication is made by including the CWR chunk. This
* chunk contains one data element, i.e. the TSN number
* that was sent in the ECNE chunk. This element
* represents the lowest TSN number in the datagram that
* was originally marked with the CE bit.
*/
asoc->last_cwr_tsn = asoc->next_tsn - 1;
repl = sctp_make_cwr(asoc, asoc->last_cwr_tsn, chunk);
/* If we run out of memory, it will look like a lost CWR. We'll
* get back in sync eventually.
*/
return repl;
}
/* Helper function to do delayed processing of ECN CWR chunk. */
static void sctp_do_ecn_cwr_work(sctp_association_t *asoc,
__u32 lowest_tsn)
{
/* Turn off ECNE getting auto-prepended to every outgoing
* packet
*/
asoc->need_ecne = 0;
}
/* This macro is to compress the text a bit... */
#define AP(v) asoc->peer.v
/* Generate SACK if necessary. We call this at the end of a packet. */
int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands)
{
__u32 ctsn, max_tsn_seen;
sctp_chunk_t *sack;
int error = 0;
if (force)
asoc->peer.sack_needed = 1;
ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map);
max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map);
/* From 12.2 Parameters necessary per association (i.e. the TCB):
*
* Ack State : This flag indicates if the next received packet
* : is to be responded to with a SACK. ...
* : When DATA chunks are out of order, SACK's
* : are not delayed (see Section 6).
*
* [This is actually not mentioned in Section 6, but we
* implement it here anyway. --piggy]
*/
if (max_tsn_seen != ctsn)
asoc->peer.sack_needed = 1;
/* From 6.2 Acknowledgement on Reception of DATA Chunks:
*
* Section 4.2 of [RFC2581] SHOULD be followed. Specifically,
* an acknowledgement SHOULD be generated for at least every
* second packet (not every second DATA chunk) received, and
* SHOULD be generated within 200 ms of the arrival of any
* unacknowledged DATA chunk. ...
*/
if (!asoc->peer.sack_needed) {
/* We will need a SACK for the next packet. */
asoc->peer.sack_needed = 1;
goto out;
} else {
sack = sctp_make_sack(asoc);
if (!sack)
goto nomem;
asoc->peer.sack_needed = 0;
asoc->peer.next_dup_tsn = 0;
error = sctp_push_outqueue(&asoc->outqueue, sack);
/* Stop the SACK timer. */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
SCTP_TO(SCTP_EVENT_TIMEOUT_SACK));
}
out:
return error;
nomem:
error = -ENOMEM;
return error;
}
/* Handle a duplicate TSN. */
void sctp_do_TSNdup(sctp_association_t *asoc, sctp_chunk_t *chunk, long gap)
{
#if 0
sctp_chunk_t *sack;
/* Caution: gap < 2 * SCTP_TSN_MAP_SIZE
* so gap can be negative.
*
* --xguo
*/
/* Count this TSN. */
if (gap < SCTP_TSN_MAP_SIZE) {
asoc->peer.tsn_map[gap]++;
} else {
asoc->peer.tsn_map_overflow[gap - SCTP_TSN_MAP_SIZE]++;
}
/* From 6.2 Acknowledgement on Reception of DATA Chunks
*
* When a packet arrives with duplicate DATA chunk(s)
* and with no new DATA chunk(s), the endpoint MUST
* immediately send a SACK with no delay. If a packet
* arrives with duplicate DATA chunk(s) bundled with
* new DATA chunks, the endpoint MAY immediately send a
* SACK. Normally receipt of duplicate DATA chunks
* will occur when the original SACK chunk was lost and
* the peer's RTO has expired. The duplicate TSN
* number(s) SHOULD be reported in the SACK as
* duplicate.
*/
asoc->counters[SctpCounterAckState] = 2;
#endif /* 0 */
} /* sctp_do_TSNdup() */
#undef AP
/* When the T3-RTX timer expires, it calls this function to create the
* relevant state machine event.
*/
void sctp_generate_t3_rtx_event(unsigned long peer)
{
int error;
sctp_transport_t *transport = (sctp_transport_t *) peer;
sctp_association_t *asoc = transport->asoc;
/* Check whether a task is in the sock. */
sctp_bh_lock_sock(asoc->base.sk);
if (__sctp_sock_busy(asoc->base.sk)) {
SCTP_DEBUG_PRINTK(__FUNCTION__ ":Sock is busy.\n");
/* Try again later. */
if (!mod_timer(&transport->T3_rtx_timer, jiffies + (HZ/20)))
sctp_transport_hold(transport);
goto out_unlock;
}
/* Is this transport really dead and just waiting around for
* the timer to let go of the reference?
*/
if (transport->dead)
goto out_unlock;
/* Run through the state machine. */
error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_T3_RTX),
asoc->state,
asoc->ep, asoc,
transport, GFP_ATOMIC);
if (error)
asoc->base.sk->err = -error;
out_unlock:
sctp_bh_unlock_sock(asoc->base.sk);
sctp_transport_put(transport);
}
/* This is a sa interface for producing timeout events. It works
* for timeouts which use the association as their parameter.
*/
static void sctp_generate_timeout_event(sctp_association_t *asoc,
sctp_event_timeout_t timeout_type)
{
int error = 0;
sctp_bh_lock_sock(asoc->base.sk);
if (__sctp_sock_busy(asoc->base.sk)) {
SCTP_DEBUG_PRINTK(__FUNCTION__ "Sock is busy: timer %d\n",
timeout_type);
/* Try again later. */
if (!mod_timer(&asoc->timers[timeout_type], jiffies + (HZ/20)))
sctp_association_hold(asoc);
goto out_unlock;
}
/* Is this association really dead and just waiting around for
* the timer to let go of the reference?
*/
if (asoc->base.dead)
goto out_unlock;
/* Run through the state machine. */
error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
SCTP_ST_TIMEOUT(timeout_type),
asoc->state, asoc->ep, asoc,
(void *)timeout_type,
GFP_ATOMIC);
if (error)
asoc->base.sk->err = -error;
out_unlock:
sctp_bh_unlock_sock(asoc->base.sk);
sctp_association_put(asoc);
}
void sctp_generate_t1_cookie_event(unsigned long data)
{
sctp_association_t *asoc = (sctp_association_t *) data;
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_COOKIE);
}
void sctp_generate_t1_init_event(unsigned long data)
{
sctp_association_t *asoc = (sctp_association_t *) data;
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_INIT);
}
void sctp_generate_t2_shutdown_event(unsigned long data)
{
sctp_association_t *asoc = (sctp_association_t *) data;
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN);
}
void sctp_generate_autoclose_event(unsigned long data)
{
sctp_association_t *asoc = (sctp_association_t *) data;
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE);
}
/* Generate a heart beat event. If the sock is busy, reschedule. Make
* sure that the transport is still valid.
*/
void sctp_generate_heartbeat_event(unsigned long data)
{
int error = 0;
sctp_transport_t *transport = (sctp_transport_t *) data;
sctp_association_t *asoc = transport->asoc;
sctp_bh_lock_sock(asoc->base.sk);
if (__sctp_sock_busy(asoc->base.sk)) {
SCTP_DEBUG_PRINTK(__FUNCTION__ ":Sock is busy.\n");
/* Try again later. */
if (!mod_timer(&transport->hb_timer, jiffies + (HZ/20)))
sctp_transport_hold(transport);
goto out_unlock;
}
/* Is this structure just waiting around for us to actually
* get destroyed?
*/
if (transport->dead)
goto out_unlock;
error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT,
SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_HEARTBEAT),
asoc->state,
asoc->ep, asoc,
transport, GFP_ATOMIC);
if (error)
asoc->base.sk->err = -error;
out_unlock:
sctp_bh_unlock_sock(asoc->base.sk);
sctp_transport_put(transport);
}
/* Inject a SACK Timeout event into the state machine. */
void sctp_generate_sack_event(unsigned long data)
{
sctp_association_t *asoc = (sctp_association_t *) data;
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK);
}
void sctp_generate_pmtu_raise_event(unsigned long data)
{
sctp_association_t *asoc = (sctp_association_t *) data;
sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_PMTU_RAISE);
}
sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
NULL,
sctp_generate_t1_cookie_event,
sctp_generate_t1_init_event,
sctp_generate_t2_shutdown_event,
NULL,
NULL,
sctp_generate_heartbeat_event,
sctp_generate_sack_event,
sctp_generate_autoclose_event,
sctp_generate_pmtu_raise_event,
};
/********************************************************************
* 3rd Level Abstractions
********************************************************************/
/* RFC 2960 8.2 Path Failure Detection
*
* When its peer endpoint is multi-homed, an endpoint should keep a
* error counter for each of the destination transport addresses of the
* peer endpoint.
*
* Each time the T3-rtx timer expires on any address, or when a
* HEARTBEAT sent to an idle address is not acknowledged within a RTO,
* the error counter of that destination address will be incremented.
* When the value in the error counter exceeds the protocol parameter
* 'Path.Max.Retrans' of that destination address, the endpoint should
* mark the destination transport address as inactive, and a
* notification SHOULD be sent to the upper layer.
*
*/
static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
sctp_transport_t *transport)
{
/* The check for association's overall error counter exceeding the
* threshold is done in the state function.
*/
asoc->overall_error_count++;
if (transport->state.active &&
(transport->error_count++ >= transport->error_threshold)) {
SCTP_DEBUG_PRINTK("transport_strike: transport "
"IP:%d.%d.%d.%d failed.\n",
NIPQUAD(transport->ipaddr.v4.sin_addr));
sctp_assoc_control_transport(asoc, transport,
SCTP_TRANSPORT_DOWN,
SCTP_FAILED_THRESHOLD);
}
/* E2) For the destination address for which the timer
* expires, set RTO <- RTO * 2 ("back off the timer"). The
* maximum value discussed in rule C7 above (RTO.max) may be
* used to provide an upper bound to this doubling operation.
*/
transport->rto = min((transport->rto * 2), transport->asoc->rto_max);
}
/* Worker routine to handle INIT command failure. */
static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands,
sctp_association_t *asoc)
{
sctp_ulpevent_t *event;
event = sctp_ulpevent_make_assoc_change(asoc,
0,
SCTP_CANT_STR_ASSOC,
0, 0, 0,
GFP_ATOMIC);
if (event)
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
SCTP_ULPEVENT(event));
/* FIXME: We need to handle data possibly either
* sent via COOKIE-ECHO bundling or just waiting in
* the transmit queue, if the user has enabled
* SEND_FAILED notifications.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
}
/* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
sctp_association_t *asoc)
{
sctp_ulpevent_t *event;
event = sctp_ulpevent_make_assoc_change(asoc,
0,
SCTP_COMM_LOST,
0, 0, 0,
GFP_ATOMIC);
if (event)
sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP,
SCTP_ULPEVENT(event));
/* FIXME: We need to handle data that could not be sent or was not
* acked, if the user has enabled SEND_FAILED notifications.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL());
}
/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT
* inside the cookie.
*/
static void sctp_cmd_process_init(sctp_cmd_seq_t *commands,
sctp_association_t *asoc,
sctp_chunk_t *chunk,
sctp_init_chunk_t *peer_init,
int priority)
{
/* The command sequence holds commands assuming that the
* processing will happen successfully. If this is not the
* case, rewind the sequence and add appropriate error handling
* to the sequence.
*/
sctp_process_init(asoc, chunk->chunk_hdr->type,
sctp_source(chunk), peer_init,
priority);
}
/* Helper function to break out starting up of heartbeat timers. */
static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc)
{
sctp_transport_t *t;
list_t *pos;
/* Start a heartbeat timer for each transport on the association.
* hold a reference on the transport to make sure none of
* the needed data structures go away.
*/
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports);
if (!mod_timer(&t->hb_timer,
t->hb_interval + t->rto + jiffies)) {
sctp_transport_hold(t);
}
}
}
/* Helper function to break out SCTP_CMD_SET_BIND_ADDR handling. */
void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_bind_addr_t *bp)
{
list_t *pos, *temp;
list_for_each_safe(pos, temp, &bp->address_list) {
list_del_init(pos);
list_add_tail(pos, &asoc->base.bind_addr.address_list);
}
/* Free the temporary bind addr header, otherwise
* there will a memory leak.
*/
sctp_bind_addr_free(bp);
}
/* Helper function to handle the reception of an HEARTBEAT ACK. */
static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_transport_t *t, sctp_chunk_t *chunk)
{
sctp_sender_hb_info_t *hbinfo;
/* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of the
* HEARTBEAT should clear the error counter of the destination
* transport address to which the HEARTBEAT was sent.
* The association's overall error count is also cleared.
*/
t->error_count = 0;
t->asoc->overall_error_count = 0;
/* Mark the destination transport address as active if it is not so
* marked.
*/
if (!t->state.active)
sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
SCTP_HEARTBEAT_SUCCESS);
/* The receiver of the HEARTBEAT ACK should also perform an
* RTT measurement for that destination transport address
* using the time value carried in the HEARTBEAT ACK chunk.
*/
hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data;
sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at));
}
/* Helper function to do a transport reset at the expiry of the hearbeat
* timer.
*/
static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc,
sctp_transport_t *t)
{
sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE);
/* Mark one strike against a transport. */
sctp_do_8_2_transport_strike(asoc, t);
/* Update the heartbeat timer. */
if (!mod_timer(&t->hb_timer, t->hb_interval + t->rto + jiffies))
sctp_transport_hold(t);
}
/* Helper function to process the process SACK command. */
static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_sackhdr_t *sackh)
{
int err;
if (sctp_sack_outqueue(&asoc->outqueue, sackh)) {
/* There are no more TSNs awaiting SACK. */
err = sctp_do_sm(SCTP_EVENT_T_OTHER,
SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN),
asoc->state, asoc->ep, asoc, NULL,
GFP_ATOMIC);
} else {
/* Windows may have opened, so we need
* to check if we have DATA to transmit
*/
err = sctp_flush_outqueue(&asoc->outqueue, 0);
}
return err;
}
/* Helper function to set the timeout value for T2-SHUTDOWN timer and to set
* the transport for a shutdown chunk.
*/
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_chunk_t *chunk)
{
sctp_transport_t *t;
t = sctp_assoc_choose_shutdown_transport(asoc);
asoc->shutdown_last_sent_to = t;
asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
chunk->transport = t;
}
This source diff could not be displayed because it is too large. You can view the blob instead.
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_sm_statetable.c,v 1.21 2002/08/22 02:25:33 jgrimm Exp $
*
* 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_sm_statetable.c,v 1.21 2002/08/22 02:25:33 jgrimm Exp $";
#include <linux/skbuff.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
sctp_sm_table_entry_t nop = {fn: sctp_sf_discard_chunk,
name: "sctp_sf_discard_chunk"};
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]);
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_DATA { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_tabort_8_4_8, name: "sctp_sf_tabort_8_4_8"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_eat_data_6_2, name: "sctp_sf_eat_data_6_2"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_eat_data_6_2, name: "sctp_sf_eat_data_6_2"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_eat_data_fast_4_4, name: "sctp_sf_eat_data_fast_4_4"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_DATA */
#define TYPE_SCTP_INIT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_do_5_1B_init, name: "sctp_sf_do_5_1B_init"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_do_5_2_1_siminit, name: "sctp_sf_do_5_2_1_siminit"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_do_5_2_1_siminit, name: "sctp_sf_do_5_2_1_siminit"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_5_2_2_dupinit, name: "sctp_sf_do_5_2_2_dupinit"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_5_2_2_dupinit, name: "sctp_sf_do_5_2_2_dupinit"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_5_2_2_dupinit, name: "sctp_sf_do_5_2_2_dupinit"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_do_5_2_2_dupinit, name: "sctp_sf_do_5_2_2_dupinit"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_INIT */
#define TYPE_SCTP_INIT_ACK { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_do_5_1C_ack, name: "sctp_sf_do_5_1C_ack"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_INIT_ACK */
#define TYPE_SCTP_SACK { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_tabort_8_4_8, name: "sctp_sf_tabort_8_4_8"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_eat_sack_6_2, name: "sctp_sf_eat_sack_6_2"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_eat_sack_6_2, name: "sctp_sf_eat_sack_6_2"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_eat_sack_6_2, name: "sctp_sf_eat_sack_6_2"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_eat_sack_6_2, name: "sctp_sf_eat_sack_6_2"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_SACK */
#define TYPE_SCTP_HEARTBEAT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_tabort_8_4_8, name: "sctp_sf_tabort_8_4_8"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_beat_8_3, name: "sctp_sf_beat_8_3"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_beat_8_3, name: "sctp_sf_beat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_beat_8_3, name: "sctp_sf_beat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_beat_8_3, name: "sctp_sf_beat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_beat_8_3, name: "sctp_sf_beat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
/* This should not happen, but we are nice. */ \
{fn: sctp_sf_beat_8_3, name: "sctp_sf_beat_8_3"}, \
} /* TYPE_SCTP_HEARTBEAT */
#define TYPE_SCTP_HEARTBEAT_ACK { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_tabort_8_4_8, name: "sctp_sf_tabort_8_4_8"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_violation, name: "sctp_sf_violation"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_backbeat_8_3, name: "sctp_sf_backbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_backbeat_8_3, name: "sctp_sf_backbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_backbeat_8_3, name: "sctp_sf_backbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_backbeat_8_3, name: "sctp_sf_backbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_HEARTBEAT_ACK */
#define TYPE_SCTP_ABORT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_pdiscard, name: "sctp_sf_pdiscard"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_cookie_wait_abort, name: "sctp_sf_cookie_wait_abort"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_cookie_echoed_abort, \
name: "sctp_sf_cookie_echoed_abort"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_9_1_abort, name: "sctp_sf_do_9_1_abort"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_9_1_abort, name: "sctp_sf_do_9_1_abort"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_9_1_abort, name: "sctp_sf_do_9_1_abort"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_do_9_1_abort, name: "sctp_sf_do_9_1_abort"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_do_9_1_abort, name: "sctp_sf_do_9_1_abort"}, \
} /* TYPE_SCTP_ABORT */
#define TYPE_SCTP_SHUTDOWN { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_tabort_8_4_8, name: "sctp_sf_tabort_8_4_8"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_9_2_shutdown, name: "sctp_sf_do_9_2_shutdown"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_9_2_shutdown_ack, \
name: "sctp_sf_do_9_2_shutdown_ack"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_SHUTDOWN */
#define TYPE_SCTP_SHUTDOWN_ACK { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_violation, name: "sctp_sf_violation"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_violation, name: "sctp_sf_violation"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_9_2_final, name: "sctp_sf_do_9_2_final"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_violation, name: "sctp_sf_violation"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_do_9_2_final, name: "sctp_sf_do_9_2_final"}, \
} /* TYPE_SCTP_SHUTDOWN_ACK */
#define TYPE_SCTP_ERROR { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_tabort_8_4_8, name: "sctp_sf_tabort_8_4_8"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_operr_notify, name: "sctp_sf_operr_notify"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_ERROR */
#define TYPE_SCTP_COOKIE_ECHO { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_do_5_1D_ce, name: "sctp_sf_do_5_1D_ce"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_do_5_2_4_dupcook, name: "sctp_sf_do_5_2_4_dupcook"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_do_5_2_4_dupcook, name: "sctp_sf_do_5_2_4_dupcook"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_5_2_4_dupcook, name: "sctp_sf_do_5_2_4_dupcook"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_5_2_4_dupcook, name: "sctp_sf_do_5_2_4_dupcook"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_5_2_4_dupcook, name: "sctp_sf_do_5_2_4_dupcook"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_do_5_2_4_dupcook, name: "sctp_sf_do_5_2_4_dupcook"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_do_5_2_4_dupcook, name: "sctp_sf_do_5_2_4_dupcook"}, \
} /* TYPE_SCTP_COOKIE_ECHO */
#define TYPE_SCTP_COOKIE_ACK { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_do_5_1E_ca, name: "sctp_sf_do_5_1E_ca"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_COOKIE_ACK */
#define TYPE_SCTP_ECN_ECNE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_do_ecne, name: "sctp_sf_do_ecne"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_ecne, name: "sctp_sf_do_ecne"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_ecne, name: "sctp_sf_do_ecne"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_ecne, name: "sctp_sf_do_ecne"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_do_ecne, name: "sctp_sf_do_ecne"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
} /* TYPE_SCTP_ECN_ECNE */
#define TYPE_SCTP_ECN_CWR { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_ecn_cwr, name: "sctp_sf_do_ecn_cwr"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_ecn_cwr, name: "sctp_sf_do_ecn_cwr"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_ecn_cwr, name: "sctp_sf_do_ecn_cwr"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
} /* TYPE_SCTP_ECN_CWR */
#define TYPE_SCTP_SHUTDOWN_COMPLETE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_ootb, name: "sctp_sf_ootb"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_do_4_C, name: "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).
*/
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[][] */
static sctp_sm_table_entry_t
chunk_event_table_asconf[SCTP_STATE_NUM_STATES] = {
/* SCTP_STATE_EMPTY */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_CLOSED */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_COOKIE_WAIT */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_COOKIE_ECHOED */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_ESTABLISHED */
{fn: sctp_sf_discard_chunk,
name: "sctp_sf_discard_chunk (will be sctp_addip_do_asconf)"},
/* SCTP_STATE_SHUTDOWN_PENDING */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_SHUTDOWN_SENT */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_SHUTDOWN_RECEIVED */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_SHUTDOWN_ACK_SENT */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
}; /* chunk asconf */
static sctp_sm_table_entry_t
chunk_event_table_asconf_ack[SCTP_STATE_NUM_STATES] = {
/* SCTP_STATE_EMPTY */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_CLOSED */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_COOKIE_WAIT */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_COOKIE_ECHOED */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_ESTABLISHED */
{fn: sctp_sf_discard_chunk,
name: "sctp_sf_discard_chunk (will be sctp_addip_do_asconf_ack)"},
/* SCTP_STATE_SHUTDOWN_PENDING */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_SHUTDOWN_SENT */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_SHUTDOWN_RECEIVED */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
/* SCTP_STATE_SHUTDOWN_ACK_SENT */
{fn: sctp_sf_discard_chunk, name: "sctp_sf_discard_chunk"},
}; /* chunk asconf_ack */
#define TYPE_SCTP_PRIMITIVE_INITIALIZE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_INITIALIZE */
#define TYPE_SCTP_PRIMITIVE_ASSOCIATE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_do_prm_asoc, name: "sctp_sf_do_prm_asoc"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_ASSOCIATE */
#define TYPE_SCTP_PRIMITIVE_SHUTDOWN { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_error_closed, name: "sctp_sf_error_closed"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_cookie_wait_prm_shutdown, \
name: "sctp_sf_cookie_wait_prm_shutdown"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_cookie_echoed_prm_shutdown, \
name:"sctp_sf_cookie_echoed_prm_shutdown"},\
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_9_2_prm_shutdown, \
name: "sctp_sf_do_9_2_prm_shutdown"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_ignore_primitive, name: "sctp_sf_ignore_primitive"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_ignore_primitive, name: "sctp_sf_ignore_primitive"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_ignore_primitive, name: "sctp_sf_ignore_primitive"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_ignore_primitive, name: "sctp_sf_ignore_primitive"}, \
} /* TYPE_SCTP_PRIMITIVE_SHUTDOWN */
#define TYPE_SCTP_PRIMITIVE_ABORT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_error_closed, name: "sctp_sf_error_closed"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_cookie_wait_prm_abort, \
name: "sctp_sf_cookie_wait_prm_abort"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_cookie_echoed_prm_abort, \
name: "sctp_sf_cookie_echoed_prm_abort"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_9_1_prm_abort, \
name: "sctp_sf_do_9_1_prm_abort"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_9_1_prm_abort, \
name: "sctp_sf_do_9_1_prm_abort"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_9_1_prm_abort, \
name: "sctp_sf_do_9_1_prm_abort"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_do_9_1_prm_abort, \
name: "sctp_sf_do_9_1_prm_abort"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_do_9_1_prm_abort, \
name: "sctp_sf_do_9_1_prm_abort"}, \
} /* TYPE_SCTP_PRIMITIVE_ABORT */
#define TYPE_SCTP_PRIMITIVE_SEND { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_error_closed, name: "sctp_sf_error_closed"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_do_prm_send, name: "sctp_sf_do_prm_send"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_do_prm_send, name: "sctp_sf_do_prm_send"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_prm_send, name: "sctp_sf_do_prm_send"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_error_shutdown, name: "sctp_sf_error_shutdown"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_error_shutdown, name: "sctp_sf_error_shutdown"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_error_shutdown, name: "sctp_sf_error_shutdown"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_error_shutdown, name: "sctp_sf_error_shutdown"}, \
} /* TYPE_SCTP_PRIMITIVE_SEND */
#define TYPE_SCTP_PRIMITIVE_SETPRIMARY { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_SETPRIMARY */
#define TYPE_SCTP_PRIMITIVE_RECEIVE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_RECEIVE */
#define TYPE_SCTP_PRIMITIVE_STATUS { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_STATUS */
#define TYPE_SCTP_PRIMITIVE_CHANGEHEARTBEAT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_CHANGEHEARTBEAT */
#define TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
#define TYPE_SCTP_PRIMITIVE_GETSRTTREPORT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_GETSRTTREPORT */
#define TYPE_SCTP_PRIMITIVE_SETFAILURETHRESHOLD { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_SETFAILURETHRESHOLD */
#define TYPE_SCTP_PRIMITIVE_SETPROTOPARAMETERS { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_SETPROTOPARAMETERS */
#define TYPE_SCTP_PRIMITIVE_RECEIVE_UNSENT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_RECEIVE_UNSENT */
#define TYPE_SCTP_PRIMITIVE_RECEIVE_UNACKED { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_RECEIVE_UNACKED */
#define TYPE_SCTP_PRIMITIVE_DESTROY { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
} /* TYPE_SCTP_PRIMITIVE_DESTROY */
/* The primary index for this table is the primitive type.
* The secondary index for this table is the state.
*/
sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = {
TYPE_SCTP_PRIMITIVE_INITIALIZE,
TYPE_SCTP_PRIMITIVE_ASSOCIATE,
TYPE_SCTP_PRIMITIVE_SHUTDOWN,
TYPE_SCTP_PRIMITIVE_ABORT,
TYPE_SCTP_PRIMITIVE_SEND,
TYPE_SCTP_PRIMITIVE_SETPRIMARY,
TYPE_SCTP_PRIMITIVE_RECEIVE,
TYPE_SCTP_PRIMITIVE_STATUS,
TYPE_SCTP_PRIMITIVE_CHANGEHEARTBEAT,
TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT,
TYPE_SCTP_PRIMITIVE_GETSRTTREPORT,
TYPE_SCTP_PRIMITIVE_SETFAILURETHRESHOLD,
TYPE_SCTP_PRIMITIVE_SETPROTOPARAMETERS,
TYPE_SCTP_PRIMITIVE_RECEIVE_UNSENT,
TYPE_SCTP_PRIMITIVE_RECEIVE_UNACKED,
TYPE_SCTP_PRIMITIVE_DESTROY,
};
#define TYPE_SCTP_OTHER_NO_PENDING_TSN { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_ignore_other, name: "sctp_sf_ignore_other"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_ignore_other, name: "sctp_sf_ignore_other"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_ignore_other, name: "sctp_sf_ignore_other"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_ignore_other, name: "sctp_sf_ignore_other"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_9_2_start_shutdown, \
name: "sctp_do_9_2_start_shutdown"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_ignore_other, name: "sctp_sf_ignore_other"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_do_9_2_shutdown_ack, \
name: "sctp_sf_do_9_2_shutdown_ack"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_ignore_other, name: "sctp_sf_ignore_other"}, \
}
#define TYPE_SCTP_OTHER_ICMP_UNREACHFRAG { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
}
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_UNREACHFRAG,
};
#define TYPE_SCTP_EVENT_TIMEOUT_NONE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_t1_timer_expire, name: "sctp_sf_t1_timer_expire"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_T1_INIT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_t1_timer_expire, name: "sctp_sf_t1_timer_expire"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_T2_SHUTDOWN { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_t2_timer_expire, name: "sctp_sf_t2_timer_expire"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_t2_timer_expire, name: "sctp_sf_t2_timer_expire"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_T3_RTX { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_do_6_3_3_rtx, name: "sctp_sf_do_6_3_3_rtx"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_6_3_3_rtx, name: "sctp_sf_do_6_3_3_rtx"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_6_3_3_rtx, name: "sctp_sf_do_6_3_3_rtx"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_do_6_3_3_rtx, name: "sctp_sf_do_6_3_3_rtx"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_T4_RTO { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_HEARTBEAT { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_sendbeat_8_3, name: "sctp_sf_sendbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_sendbeat_8_3, name: "sctp_sf_sendbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_SACK { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_do_6_2_sack, name: "sctp_sf_do_6_2_sack"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_do_6_2_sack, name: "sctp_sf_do_6_2_sack"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_do_6_2_sack, name: "sctp_sf_do_6_2_sack"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_autoclose_timer_expire, \
name: "sctp_sf_autoclose_timer_expire"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_timer_ignore, name: "sctp_sf_timer_ignore"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_PMTU_RAISE { \
/* SCTP_STATE_EMPTY */ \
{fn: sctp_sf_bug, name: "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_ESTABLISHED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{fn: sctp_sf_not_impl, name: "sctp_sf_not_impl"}, \
}
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_HEARTBEAT,
TYPE_SCTP_EVENT_TIMEOUT_SACK,
TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE,
TYPE_SCTP_EVENT_TIMEOUT_PMTU_RAISE,
};
sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, sctp_state_t state)
{
if (state > SCTP_STATE_MAX)
BUG();
if (cid < 0)
return &nop;
if (cid <= SCTP_CID_BASE_MAX)
return &chunk_event_table[cid][state];
switch (cid) {
case SCTP_CID_ASCONF:
return &chunk_event_table_asconf[state];
case SCTP_CID_ASCONF_ACK:
return &chunk_event_table_asconf_ack[state];
default:
return &nop;
};
return &nop;
}
/* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_socket.c,v 1.64 2002/08/21 23:06:28 jgrimm Exp $
*
* These functions interface with the sockets layer to implement the
* SCTP Extensions for the Sockets API.
*
* 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 <narsi@refcode.org>
* Karl Knutson <karl@athena.chicago.il.us>
* Jon Grimm <jgrimm@us.ibm.com>
* Xingang Guo <xingang.guo@intel.com>
* Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <samudrala@us.ibm.com>
* Inaky Perez-Gonzalez <inaky.gonzalez@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_socket.c,v 1.64 2002/08/21 23:06:28 jgrimm Exp $";
#include <linux/config.h>
#include <linux/types.h>
#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/time.h>
#include <linux/ip.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
#include <linux/init.h>
#include <net/ip.h>
#include <net/icmp.h>
#include <net/route.h>
#include <net/ipv6.h>
#include <net/inet_common.h>
#include <linux/socket.h> /* for sa_family_t */
#include <net/sock.h>
#include <net/sctp/sctp.h>
/* Forward declarations for internal helper functions. */
static void __sctp_write_space(sctp_association_t *asoc);
static int sctp_writeable(struct sock *sk);
static inline int sctp_wspace(sctp_association_t *asoc);
static inline void sctp_set_owner_w(sctp_chunk_t *chunk);
static void sctp_wfree(struct sk_buff *skb);
static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p,
int msg_len);
static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
static inline void sctp_sk_memcpy_msgname(struct sock *sk, char * msgname,
int *addr_len, struct sk_buff *skb);
static inline void sctp_sk_addr_set(struct sock *,
const sockaddr_storage_t *newaddr,
sockaddr_storage_t *saveaddr);
static inline void sctp_sk_addr_restore(struct sock *,
const sockaddr_storage_t *);
static inline int sctp_sendmsg_verify_name(struct sock *, struct msghdr *);
static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int);
static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int);
static int sctp_do_bind(struct sock *, sockaddr_storage_t *, int);
static int sctp_autobind(struct sock *sk);
static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head,
unsigned short snum);
/* API 3.1.2 bind() - UDP Style Syntax
* The syntax of bind() is,
*
* ret = bind(int sd, struct sockaddr *addr, int addrlen);
*
* sd - the socket descriptor returned by socket().
* addr - the address structure (struct sockaddr_in or struct
* sockaddr_in6 [RFC 2553]),
* addrlen - the size of the address structure.
*
* The caller should use struct sockaddr_storage described in RFC 2553
* to represent addr for portability reason.
*/
int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
int retval = 0;
sctp_lock_sock(sk);
SCTP_DEBUG_PRINTK("sctp_bind(sk: %p, uaddr: %p, addr_len: %d)\n",
sk, uaddr, addr_len);
/* Disallow binding twice. */
if (!sctp_sk(sk)->ep->base.bind_addr.port)
retval = sctp_do_bind(sk, (sockaddr_storage_t *)uaddr,
addr_len);
else
retval = -EINVAL;
sctp_release_sock(sk);
return retval;
}
static long sctp_get_port_local(struct sock *, unsigned short);
/* Bind a local address either to an endpoint or to an association. */
static int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr, int addr_len)
{
sctp_opt_t *sp = sctp_sk(sk);
sctp_endpoint_t *ep = sp->ep;
sctp_bind_addr_t *bp = &ep->base.bind_addr;
unsigned short sa_family = newaddr->sa.sa_family;
sockaddr_storage_t tmpaddr, saveaddr;
unsigned short *snum;
int ret = 0;
SCTP_DEBUG_PRINTK("sctp_do_bind(sk: %p, newaddr: %p, addr_len: %d)\n",
sk, newaddr, addr_len);
/* FIXME: This function needs to handle v4-mapped-on-v6
* addresses!
*/
if (PF_INET == sk->family) {
if (sa_family != AF_INET)
return -EINVAL;
}
/* Make a local copy of the new address. */
tmpaddr = *newaddr;
switch (sa_family) {
case AF_INET:
if (addr_len < sizeof(struct sockaddr_in))
return -EINVAL;
ret = inet_addr_type(newaddr->v4.sin_addr.s_addr);
/* FIXME:
* Should we allow apps to bind to non-local addresses by
* checking the IP sysctl parameter "ip_nonlocal_bind"?
*/
if (newaddr->v4.sin_addr.s_addr != INADDR_ANY &&
ret != RTN_LOCAL)
return -EADDRNOTAVAIL;
tmpaddr.v4.sin_port = htons(tmpaddr.v4.sin_port);
snum = &tmpaddr.v4.sin_port;
break;
case AF_INET6:
SCTP_V6(
/* FIXME: Hui, please verify this. Looking at
* the ipv6 code I see a SIN6_LEN_RFC2133 check.
* I'm guessing that scope_id is a newer addition.
*/
if (addr_len < sizeof(struct sockaddr_in6))
return -EINVAL;
/* FIXME - The support for IPv6 multiple types
* of addresses need to be added later.
*/
ret = sctp_ipv6_addr_type(&newaddr->v6.sin6_addr);
tmpaddr.v6.sin6_port = htons(tmpaddr.v6.sin6_port);
snum = &tmpaddr.v6.sin6_port;
break;
)
default:
return -EINVAL;
};
SCTP_DEBUG_PRINTK("sctp_do_bind: port: %d, new port: %d\n",
bp->port, *snum);
/* We must either be unbound, or bind to the same port. */
if (bp->port && (*snum != bp->port)) {
SCTP_DEBUG_PRINTK("sctp_do_bind:"
" New port %d does not match existing port "
"%d.\n", *snum, bp->port);
return -EINVAL;
}
if (*snum && *snum < PROT_SOCK && !capable(CAP_NET_BIND_SERVICE))
return -EACCES;
/* FIXME - Make socket understand that there might be multiple bind
* addresses and there will be multiple source addresses involved in
* routing and failover decisions.
*/
sctp_sk_addr_set(sk, &tmpaddr, &saveaddr);
/* Make sure we are allowed to bind here.
* The function sctp_get_port_local() does duplicate address
* detection.
*/
if ((ret = sctp_get_port_local(sk, *snum))) {
sctp_sk_addr_restore(sk, &saveaddr);
if (ret == (long) sk) {
/* This endpoint has a conflicting address. */
return -EINVAL;
} else {
return -EADDRINUSE;
}
}
/* Refresh ephemeral port. */
if (!*snum)
*snum = inet_sk(sk)->num;
/* The getsockname() API depends on 'sport' being set. */
inet_sk(sk)->sport = htons(inet_sk(sk)->num);
/* Add the address to the bind address list. */
sctp_local_bh_disable();
sctp_write_lock(&ep->base.addr_lock);
/* Use GFP_ATOMIC since BHs are disabled. */
if ((ret = sctp_add_bind_addr(bp, &tmpaddr, GFP_ATOMIC))) {
sctp_sk_addr_restore(sk, &saveaddr);
} else if (!bp->port) {
bp->port = *snum;
}
sctp_write_unlock(&ep->base.addr_lock);
sctp_local_bh_enable();
return ret;
}
/* API 8.1 sctp_bindx()
*
* The syntax of sctp_bindx() is,
*
* ret = sctp_bindx(int sd,
* struct sockaddr_storage *addrs,
* int addrcnt,
* int flags);
*
* If sd is an IPv4 socket, the addresses passed must be IPv4 addresses.
* If the sd is an IPv6 socket, the addresses passed can either be IPv4
* or IPv6 addresses.
*
* A single address may be specified as INADDR_ANY or IPV6_ADDR_ANY, see
* section 3.1.2 for this usage.
*
* addrs is a pointer to an array of one or more socket addresses. Each
* address is contained in a struct sockaddr_storage, so each address is
* fixed length. The caller specifies the number of addresses in the
* array with addrcnt.
*
* On success, sctp_bindx() returns 0. On failure, sctp_bindx() returns -1,
* and sets errno to the appropriate error code. [ Editor's note: need
* to fill in all error code? ]
*
* For SCTP, the port given in each socket address must be the same, or
* sctp_bindx() will fail, setting errno to EINVAL .
*
* The flags parameter is formed from the bitwise OR of zero or
* more of the following currently defined flags:
*
* SCTP_BINDX_ADD_ADDR
* SCTP_BINDX_REM_ADDR
*
* SCTP_BIND_ADD_ADDR directs SCTP to add the given addresses to the
* association, and SCTP_BIND_REM_ADDR directs SCTP to remove the given
* addresses from the association. The two flags are mutually exclusive;
* if both are given, sctp_bindx() will fail with EINVAL. A caller may not
* remove all addresses from an association; sctp_bindx() will reject such
* an attempt with EINVAL.
*
* An application can use sctp_bindx(SCTP_BINDX_ADD_ADDR) to associate
* additional addresses with an endpoint after calling bind(). Or use
* sctp_bindx(SCTP_BINDX_REM_ADDR) to remove some addresses a listening
* socket is associated with so that no new association accepted will be
* associated with those addresses.
*
* SCTP_BIND_ADD_ADDR is defined as 0, so that it becomes the default
* behavior for sctp_bindx() when no flags are given.
*
* Adding and removing addresses from a connected association is optional
* functionality. Implementations that do not support this functionality
* should return EOPNOTSUPP.
*
* NOTE: This could be integrated into sctp_setsockopt_bindx(),
* but keeping it this way makes it easier if sometime sys_bindx is
* added.
*/
/* Unprotected by locks. Call only with socket lock sk->lock held! See
* sctp_bindx() for a lock-protected call.
*/
static int __sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs,
int addrcnt, int flags)
{
int retval = 0;
SCTP_DEBUG_PRINTK("__sctp_bindx(sk: %p, addrs: %p, addrcnt: %d, "
"flags: %s)\n", sk, addrs, addrcnt,
(BINDX_ADD_ADDR == flags)?"ADD":
((BINDX_REM_ADDR == flags)?"REM":"BOGUS"));
switch (flags) {
case BINDX_ADD_ADDR:
retval = sctp_bindx_add(sk, addrs, addrcnt);
break;
case BINDX_REM_ADDR:
retval = sctp_bindx_rem(sk, addrs, addrcnt);
break;
default:
retval = -EINVAL;
break;
};
return retval;
}
/* BINDX with locks.
*
* NOTE: Currently unused at all ...
*/
int sctp_bindx(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt,
int flags)
{
int retval;
sctp_lock_sock(sk);
retval = __sctp_bindx(sk, addrs, addrcnt, flags);
sctp_release_sock(sk);
return retval;
}
/* Add a list of addresses as bind addresses to local endpoint or
* association.
*
* Basically run through each address specified in the addrs/addrcnt
* array/length pair, determine if it is IPv6 or IPv4 and call
* sctp_do_bind() on it.
*
* If any of them fails, then the operation will be reversed and the
* ones that were added will be removed.
*
* Only __sctp_bindx() is supposed to call this function.
*/
int sctp_bindx_add(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
{
int cnt;
int retval = 0;
int addr_len;
SCTP_DEBUG_PRINTK("sctp_bindx_add (sk: %p, addrs: %p, addrcnt: %d)\n",
sk, addrs, addrcnt);
for (cnt = 0; cnt < addrcnt; cnt++) {
/* The list may contain either IPv4 or IPv6 address;
* determine the address length for walking thru the list.
*/
switch (((struct sockaddr *)&addrs[cnt])->sa_family) {
case AF_INET:
addr_len = sizeof(struct sockaddr_in);
break;
case AF_INET6:
addr_len = sizeof(struct sockaddr_in6);
break;
default:
retval = -EINVAL;
goto err_bindx_add;
};
retval = sctp_do_bind(sk, (sockaddr_storage_t *)&addrs[cnt],
addr_len);
err_bindx_add:
if (retval < 0) {
/* Failed. Cleanup the ones that has been added */
if (cnt > 0)
sctp_bindx_rem(sk, addrs, cnt);
return retval;
}
}
/* Notify the peer(s), assuming we have (an) association(s).
* FIXME: for UDP, we have a 1-1-many mapping amongst sk, ep and asoc,
* so we don't have to do much work on locating associations.
*
* However, when the separation of ep and asoc kicks in, especially
* for TCP style connection, it becomes n-1-n mapping. We will need
* to do more fine work. Until then, hold my peace.
* --xguo
*
* Really, I don't think that will be a problem. The bind()
* call on a socket will either know the endpoint
* (e.g. TCP-style listen()ing socket, or UDP-style socket),
* or exactly one association. The former case is EXACTLY
* what we have now. In the former case we know the
* association already. --piggy
*
* This code will be working on either a UDP style or a TCP style
* socket, or say either an endpoint or an association. The socket
* type verification code need to be added later before calling the
* ADDIP code.
* --daisy
*/
#if CONFIG_IP_SCTP_ADDIP
/* Add these addresses to all associations on this endpoint. */
if (retval >= 0) {
list_t *pos;
sctp_endpoint_t *ep;
sctp_association_t *asoc;
ep = sctp_sk(sk)->ep;
list_for_each(pos, &ep->asocs) {
asoc = list_entry(pos, sctp_association_t, asocs);
sctp_addip_addr_config(asoc,
SCTP_PARAM_ADD_IP,
addrs, addrcnt);
}
}
#endif
return retval;
}
/* Remove a list of addresses from bind addresses list. Do not remove the
* last address.
*
* Basically run through each address specified in the addrs/addrcnt
* array/length pair, determine if it is IPv6 or IPv4 and call
* sctp_del_bind() on it.
*
* If any of them fails, then the operation will be reversed and the
* ones that were removed will be added back.
*
* At least one address has to be left; if only one address is
* available, the operation will return -EBUSY.
*
* Only __sctp_bindx() is supposed to call this function.
*/
int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
{
sctp_opt_t *sp = sctp_sk(sk);
sctp_endpoint_t *ep = sp->ep;
int cnt;
sctp_bind_addr_t *bp = &ep->base.bind_addr;
int retval = 0;
sockaddr_storage_t saveaddr;
SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n",
sk, addrs, addrcnt);
for (cnt = 0; cnt < addrcnt; cnt++) {
/* If there is only one bind address, there is nothing more
* to be removed (we need at least one address here).
*/
if (list_empty(&bp->address_list)) {
retval = -EBUSY;
goto err_bindx_rem;
}
/* The list may contain either IPv4 or IPv6 address;
* determine the address length for walking thru the list.
*/
switch (((struct sockaddr *)&addrs[cnt])->sa_family) {
case AF_INET:
saveaddr = *((sockaddr_storage_t *)
&addrs[cnt]);
saveaddr.v4.sin_port =
ntohs(saveaddr.v4.sin_port);
/* verify the port */
if (saveaddr.v4.sin_port != bp->port) {
retval = -EINVAL;
goto err_bindx_rem;
}
break;
case AF_INET6:
saveaddr = *((sockaddr_storage_t *)
&addrs[cnt]);
saveaddr.v6.sin6_port =
ntohs(saveaddr.v6.sin6_port);
/* verify the port */
if (saveaddr.v6.sin6_port != bp->port) {
retval = -EINVAL;
goto err_bindx_rem;
}
break;
default:
retval = -EINVAL;
goto err_bindx_rem;
};
/* FIXME - There is probably a need to check if sk->saddr and
* sk->rcv_addr are currently set to one of the addresses to
* be removed. This is something which needs to be looked into
* when we are fixing the outstanding issues with multi-homing
* socket routing and failover schemes. Refer to comments in
* sctp_do_bind(). -daisy
*/
sctp_local_bh_disable();
sctp_write_lock(&ep->base.addr_lock);
retval = sctp_del_bind_addr(bp, &saveaddr);
sctp_write_unlock(&ep->base.addr_lock);
sctp_local_bh_enable();
err_bindx_rem:
if (retval < 0) {
/* Failed. Add the ones that has been removed back */
if (cnt > 0)
sctp_bindx_add(sk, addrs, cnt);
return retval;
}
}
/*
* This code will be working on either a UDP style or a TCP style
* socket, * or say either an endpoint or an association. The socket
* type verification code need to be added later before calling the
* ADDIP code.
* --daisy
*/
#if CONFIG_IP_SCTP_ADDIP
/* Remove these addresses from all associations on this endpoint. */
if (retval >= 0) {
list_t *pos;
sctp_endpoint_t *ep;
sctp_association_t *asoc;
ep = sctp_sk(sk)->ep;
list_for_each(pos, &ep->asocs) {
asoc = list_entry(pos, sctp_association_t, asocs);
sctp_addip_addr_config(asoc, SCTP_PARAM_DEL_IP,
addrs, addrcnt);
}
}
#endif
return retval;
}
/* Helper for tunneling sys_bindx() requests through sctp_setsockopt()
*
* Basically do nothing but copying the addresses from user to kernel
* land and invoking sctp_bindx on the sk. This is used for tunneling
* the sctp_bindx() [sys_bindx()] request through sctp_setsockopt()
* from userspace.
*
* Note I don't use move_addr_to_kernel(): the reason is we would be
* iterating over an array of struct sockaddr_storage passing always
* what we know is a good size (sizeof (struct sock...)), so it is
* pointless. Instead check the whole area for read access and copy
* it.
*
* We don't use copy_from_user() for optimization: we first do the
* sanity checks (buffer size -fast- and access check-healthy
* pointer); if all of those succeed, then we can alloc the memory
* (expensive operation) needed to copy the data to kernel. Then we do
* the copying without checking the user space area
* (__copy_from_user()).
*
* On exit there is no need to do sockfd_put(), sys_setsockopt() does
* it.
*
* sk The sk of the socket
* addrs The pointer to the addresses in user land
* addrssize Size of the addrs buffer
* op Operation to perform (add or remove, see the flags of
* sctp_bindx)
*
* Returns 0 if ok, <0 errno code on error.
*/
static int sctp_setsockopt_bindx(struct sock* sk, struct sockaddr_storage *addrs,
int addrssize, int op)
{
struct sockaddr_storage *kaddrs;
int err;
size_t addrcnt;
SCTP_DEBUG_PRINTK("sctp_do_setsocktopt_bindx: sk %p addrs %p"
" addrssize %d opt %d\n", sk, addrs,
addrssize, op);
/* Do we have an integer number of structs sockaddr_storage? */
if (unlikely(addrssize <= 0 ||
addrssize % sizeof(struct sockaddr_storage) != 0))
return -EINVAL;
/* Check the user passed a healthy pointer. */
if (unlikely(!access_ok(VERIFY_READ, addrs, addrssize)))
return -EFAULT;
/* Alloc space for the address array in kernel memory. */
kaddrs = (struct sockaddr_storage *) kmalloc(addrssize, GFP_KERNEL);
if (unlikely(NULL == kaddrs))
return -ENOMEM;
if (copy_from_user(kaddrs, addrs, addrssize)) {
kfree(kaddrs);
return -EFAULT;
}
addrcnt = addrssize / sizeof(struct sockaddr_storage);
err = __sctp_bindx(sk, kaddrs, addrcnt, op); /* Do the work. */
kfree(kaddrs);
return err;
}
/* API 3.1.4 close() - UDP Style Syntax
* Applications use close() to perform graceful shutdown (as described in
* Section 10.1 of [SCTP]) on ALL the associations currently represented
* by a UDP-style socket.
*
* The syntax is
*
* ret = close(int sd);
*
* sd - the socket descriptor of the associations to be closed.
*
* To gracefully shutdown a specific association represented by the
* UDP-style socket, an application should use the sendmsg() call,
* passing no user data, but including the appropriate flag in the
* ancillary data (see Section xxxx).
*
* If sd in the close() call is a branched-off socket representing only
* one association, the shutdown is performed on that association only.
*/
static void sctp_close(struct sock *sk, long timeout)
{
sctp_endpoint_t *ep;
sctp_association_t *asoc;
list_t *pos, *temp;
SCTP_DEBUG_PRINTK("sctp_close(sk: 0x%p...)\n", sk);
sctp_lock_sock(sk);
sk->shutdown = SHUTDOWN_MASK;
ep = sctp_sk(sk)->ep;
/* Walk all associations on a socket, not on an endpoint. */
list_for_each_safe(pos, temp, &ep->asocs) {
asoc = list_entry(pos, sctp_association_t, asocs);
sctp_primitive_SHUTDOWN(asoc, NULL);
}
/* Clean up any skbs sitting on the receive queue. */
skb_queue_purge(&sk->receive_queue);
/* This will run the backlog queue. */
sctp_release_sock(sk);
/* Supposedly, no process has access to the socket, but
* the net layers still may.
*/
sctp_local_bh_disable();
sctp_bh_lock_sock(sk);
/* Hold the sock, since inet_sock_release() will put sock_put()
* and we have just a little more cleanup.
*/
sock_hold(sk);
inet_sock_release(sk);
sctp_bh_unlock_sock(sk);
sctp_local_bh_enable();
sock_put(sk);
SCTP_DBG_OBJCNT_DEC(sock);
}
/* API 3.1.3 sendmsg() - UDP Style Syntax
*
* An application uses sendmsg() and recvmsg() calls to transmit data to
* and receive data from its peer.
*
* ssize_t sendmsg(int socket, const struct msghdr *message,
* int flags);
*
* socket - the socket descriptor of the endpoint.
* message - pointer to the msghdr structure which contains a single
* user message and possibly some ancillary data.
*
* See Section 5 for complete description of the data
* structures.
*
* flags - flags sent or received with the user message, see Section
* 5 for complete description of the flags.
*
* NB: The argument 'msg' is a user space address.
*/
/* BUG: We do not implement timeouts. */
/* BUG: We do not implement the equivalent of wait_for_tcp_memory(). */
static int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *);
static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, int size)
{
sctp_opt_t *sp;
sctp_endpoint_t *ep;
sctp_association_t *asoc = NULL;
sctp_transport_t *transport;
sctp_chunk_t *chunk = NULL;
sockaddr_storage_t to;
struct sockaddr *msg_name = NULL;
struct sctp_sndrcvinfo default_sinfo = { 0 };
struct sctp_sndrcvinfo *sinfo;
struct sctp_initmsg *sinit;
sctp_assoc_t associd = NULL;
sctp_cmsgs_t cmsgs = { 0 };
int err;
size_t msg_len;
sctp_scope_t scope;
long timeo;
__u16 sinfo_flags = 0;
SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, "
"size: %d)\n", sk, msg, size);
err = 0;
sp = sctp_sk(sk);
ep = sp->ep;
SCTP_DEBUG_PRINTK("Using endpoint: %s.\n", ep->debug_name);
/* Parse out the SCTP CMSGs. */
err = sctp_msghdr_parse(msg, &cmsgs);
if (err) {
SCTP_DEBUG_PRINTK("msghdr parse err = %x\n", err);
goto out_nounlock;
}
/* Fetch the destination address for this packet. This
* address only selects the association--it is not necessarily
* the address we will send to.
* For a peeled-off socket, msg_name is ignored.
*/
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) {
err = sctp_sendmsg_verify_name(sk, msg);
if (err)
return err;
memcpy(&to, msg->msg_name, msg->msg_namelen);
SCTP_DEBUG_PRINTK("Just memcpy'd. msg_name is "
"0x%x:%u.\n",
to.v4.sin_addr.s_addr, to.v4.sin_port);
to.v4.sin_port = ntohs(to.v4.sin_port);
msg_name = msg->msg_name;
}
msg_len = get_user_iov_size(msg->msg_iov, msg->msg_iovlen);
sinfo = cmsgs.info;
sinit = cmsgs.init;
/* Did the user specify SNDRCVINFO? */
if (sinfo) {
sinfo_flags = sinfo->sinfo_flags;
associd = sinfo->sinfo_assoc_id;
}
SCTP_DEBUG_PRINTK("msg_len: %Zd, sinfo_flags: 0x%x\n",
msg_len, sinfo_flags);
/* If MSG_EOF|MSG_ABORT is set, no data can be sent. Disallow
* sending 0-length messages when MSG_EOF|MSG_ABORT is not set.
*/
if (((sinfo_flags & (MSG_EOF|MSG_ABORT)) && (msg_len > 0)) ||
(!(sinfo_flags & (MSG_EOF|MSG_ABORT)) && (msg_len == 0))) {
err = -EINVAL;
goto out_nounlock;
}
sctp_lock_sock(sk);
transport = NULL;
SCTP_DEBUG_PRINTK("About to look up association.\n");
/* If a msg_name has been specified, assume this is to be used. */
if (msg_name) {
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
} else {
/* For a peeled-off socket, ignore any associd specified by
* the user with SNDRCVINFO.
*/
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) {
if (list_empty(&ep->asocs)) {
err = -EINVAL;
goto out_unlock;
}
asoc = list_entry(ep->asocs.next, sctp_association_t,
asocs);
} else if (associd) {
asoc = sctp_id2assoc(sk, associd);
}
if (!asoc) {
err = -EINVAL;
goto out_unlock;
}
}
if (asoc) {
SCTP_DEBUG_PRINTK("Just looked up association: "
"%s. \n", asoc->debug_name);
if (sinfo_flags & MSG_EOF) {
SCTP_DEBUG_PRINTK("Shutting down association: %p\n",
asoc);
sctp_primitive_SHUTDOWN(asoc, NULL);
err = 0;
goto out_unlock;
}
if (sinfo_flags & MSG_ABORT) {
SCTP_DEBUG_PRINTK("Aborting association: %p\n",asoc);
sctp_primitive_ABORT(asoc, NULL);
err = 0;
goto out_unlock;
}
}
/* Do we need to create the association? */
if (!asoc) {
SCTP_DEBUG_PRINTK("There is no association yet.\n");
/* Check for invalid stream against the stream counts,
* either the default or the user specified stream counts.
*/
if (sinfo) {
if (!sinit ||
(sinit && !sinit->sinit_num_ostreams)) {
/* Check against the defaults. */
if (sinfo->sinfo_stream >=
sp->initmsg.sinit_num_ostreams) {
err = -EINVAL;
goto out_unlock;
}
} else {
/* Check against the defaults. */
if (sinfo->sinfo_stream >=
sp->initmsg.sinit_num_ostreams) {
err = -EINVAL;
goto out_unlock;
}
/* Check against the requested. */
if (sinfo->sinfo_stream >=
sinit->sinit_num_ostreams) {
err = -EINVAL;
goto out_unlock;
}
}
}
/*
* API 3.1.2 bind() - UDP Style Syntax
* If a bind() or sctp_bindx() is not called prior to a
* sendmsg() call that initiates a new association, the
* system picks an ephemeral port and will choose an address
* set equivalent to binding with a wildcard address.
*/
if (!ep->base.bind_addr.port) {
if (sctp_autobind(sk)) {
err = -EAGAIN;
goto out_unlock;
}
}
scope = sctp_scope(&to);
asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
if (!asoc) {
err = -ENOMEM;
goto out_unlock;
}
/* If the SCTP_INIT ancillary data is specified, set all
* the association init values accordingly.
*/
if (sinit) {
if (sinit->sinit_num_ostreams) {
asoc->c.sinit_num_ostreams =
sinit->sinit_num_ostreams;
}
if (sinit->sinit_max_instreams) {
if (sinit->sinit_max_instreams <=
SCTP_MAX_STREAM) {
asoc->c.sinit_max_instreams =
sinit->sinit_max_instreams;
} else {
asoc->c.sinit_max_instreams =
SCTP_MAX_STREAM;
}
}
if (sinit->sinit_max_attempts) {
asoc->max_init_attempts
= sinit->sinit_max_attempts;
}
if (sinit->sinit_max_init_timeo) {
asoc->max_init_timeo
= sinit->sinit_max_init_timeo * HZ;
}
}
/* Prime the peer's transport structures. */
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
}
/* ASSERT: we have a valid association at this point. */
SCTP_DEBUG_PRINTK("We have a valid association. \n");
/* API 7.1.7, the sndbuf size per association bounds the
* maximum size of data that can be sent in a single send call.
*/
if (msg_len > sk->sndbuf) {
err = -EMSGSIZE;
goto out_free;
}
/* FIXME: In the current implementation, a single chunk is created
* for the entire message initially, even if it has to be fragmented
* later. As the length field in the chunkhdr is used to set
* the chunk length, the maximum size of the chunk and hence the
* message is limited by its type(__u16).
* The real fix is to fragment the message before creating the chunks.
*/
if (msg_len > ((__u16)(~(__u16)0) -
WORD_ROUND(sizeof(sctp_data_chunk_t)+1))) {
err = -EMSGSIZE;
goto out_free;
}
/* If fragmentation is disabled and the message length exceeds the
* association fragmentation point, return EMSGSIZE. The I-D
* does not specify what this error is, but this looks like
* a great fit.
*/
if (sctp_sk(sk)->disable_fragments && (msg_len > asoc->frag_point)) {
err = -EMSGSIZE;
goto out_free;
}
if (sinfo) {
/* Check for invalid stream. */
if (sinfo->sinfo_stream >= asoc->c.sinit_num_ostreams) {
err = -EINVAL;
goto out_free;
}
} else {
/* If the user didn't specify SNDRCVINFO, make up one with
* some defaults.
*/
default_sinfo.sinfo_stream = asoc->defaults.stream;
default_sinfo.sinfo_ppid = asoc->defaults.ppid;
sinfo = &default_sinfo;
}
timeo = sock_sndtimeo(sk, msg->msg_flags & MSG_DONTWAIT);
if (!sctp_wspace(asoc)) {
err = sctp_wait_for_sndbuf(asoc, &timeo, msg_len);
if (err)
goto out_free;
}
/* Get enough memory for the whole message. */
chunk = sctp_make_data_empty(asoc, sinfo, msg_len);
if (!chunk) {
err = -ENOMEM;
goto out_free;
}
#if 0
/* FIXME: This looks wrong so I'll comment out.
* We should be able to use this same technique for
* primary address override! --jgrimm
*/
/* If the user gave us an address, copy it in. */
if (msg->msg_name) {
chunk->transport = sctp_assoc_lookup_paddr(asoc, &to);
if (!chunk->transport) {
err = -EINVAL;
goto out_free;
}
}
#endif /* 0 */
/* Copy the message from the user. */
err = sctp_user_addto_chunk(chunk, msg_len, msg->msg_iov);
if (err < 0)
goto out_free;
SCTP_DEBUG_PRINTK("Copied message to chunk: %p.\n", chunk);
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
/* Do accounting for the write space. */
sctp_set_owner_w(chunk);
if (SCTP_STATE_CLOSED == asoc->state) {
err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0)
goto out_free;
SCTP_DEBUG_PRINTK("We associated primitively.\n");
}
/* Send it to the lower layers. */
err = sctp_primitive_SEND(asoc, chunk);
SCTP_DEBUG_PRINTK("We sent primitively.\n");
/* BUG: SCTP_CHECK_TIMER(sk); */
if (!err) {
err = msg_len;
goto out_unlock;
}
out_free:
if (SCTP_STATE_CLOSED == asoc->state)
sctp_association_free(asoc);
if (chunk)
sctp_free_chunk(chunk);
out_unlock:
sctp_release_sock(sk);
out_nounlock:
return err;
#if 0
do_sock_err:
if (msg_len)
err = msg_len;
else
err = sock_error(sk);
goto out;
do_interrupted:
if (msg_len)
err = msg_len;
goto out;
#endif /* 0 */
}
/* API 3.1.3 recvmsg() - UDP Style Syntax
*
* ssize_t recvmsg(int socket, struct msghdr *message,
* int flags);
*
* socket - the socket descriptor of the endpoint.
* message - pointer to the msghdr structure which contains a single
* user message and possibly some ancillary data.
*
* See Section 5 for complete description of the data
* structures.
*
* flags - flags sent or received with the user message, see Section
* 5 for complete description of the flags.
*/
static struct sk_buff * sctp_skb_recv_datagram(struct sock *, int, int, int *);
static int sctp_recvmsg(struct sock *sk, struct msghdr *msg, int len,
int noblock, int flags, int *addr_len)
{
sctp_ulpevent_t *event = NULL;
struct sk_buff *skb;
int copied;
int err = 0;
SCTP_DEBUG_PRINTK("sctp_recvmsg("
"%s: %p, %s: %p, %s: %d, %s: %d, %s: "
"0x%x, %s: %p)\n",
"sk", sk,
"msghdr", msg,
"len", len,
"knoblauch", noblock,
"flags", flags,
"addr_len", addr_len);
sctp_lock_sock(sk);
skb = sctp_skb_recv_datagram(sk, flags, noblock, &err);
if (!skb)
goto out;
copied = skb->len;
if (skb_shinfo(skb)->frag_list) {
struct sk_buff *list;
for (list = skb_shinfo(skb)->frag_list;
list;
list = list->next)
copied += list->len;
}
if (copied > len) {
copied = len;
msg->msg_flags |= MSG_TRUNC;
}
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
event = (sctp_ulpevent_t *) skb->cb;
if (err)
goto out_free;
sock_recv_timestamp(msg, sk, skb);
if (sctp_ulpevent_is_notification(event)) {
msg->msg_flags |= MSG_NOTIFICATION;
} else {
/* Copy the address. */
if (addr_len && msg->msg_name)
sctp_sk_memcpy_msgname(sk, msg->msg_name,
addr_len, skb);
}
/* Check if we allow SCTP_SNDRCVINFO. */
if (sctp_sk(sk)->subscribe.sctp_data_io_event)
sctp_ulpevent_read_sndrcvinfo(event, msg);
#if 0
/* FIXME: we should be calling IP layer too. */
if (sk->protinfo.af_inet.cmsg_flags)
ip_cmsg_recv(msg, skb);
#endif
err = copied;
/* FIXME: We need to support MSG_EOR correctly. */
msg->msg_flags |= MSG_EOR;
out_free:
sctp_ulpevent_free(event); /* Free the skb. */
out:
sctp_release_sock(sk);
return err;
}
static inline int sctp_setsockopt_disable_fragments(struct sock *sk, char *optval, int optlen)
{
int val;
if (optlen < sizeof(int))
return -EINVAL;
if (get_user(val, (int *)optval))
return -EFAULT;
sctp_sk(sk)->disable_fragments = (val == 0) ? 0 : 1;
return 0;
}
static inline int sctp_setsockopt_set_events(struct sock *sk, char *optval, int optlen)
{
if (optlen != sizeof(struct sctp_event_subscribe))
return -EINVAL;
if (copy_from_user(&sctp_sk(sk)->subscribe, optval, optlen))
return -EFAULT;
return 0;
}
static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, int optlen)
{
sctp_opt_t *sp = sctp_sk(sk);
if (optlen != sizeof(int))
return -EINVAL;
if (copy_from_user(&sp->autoclose, optval, optlen))
return -EFAULT;
sp->ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] = sp->autoclose * HZ;
return 0;
}
/* API 6.2 setsockopt(), getsockopt()
*
* Applications use setsockopt() and getsockopt() to set or retrieve
* socket options. Socket options are used to change the default
* behavior of sockets calls. They are described in Section 7.
*
* The syntax is:
*
* ret = getsockopt(int sd, int level, int optname, void *optval,
* int *optlen);
* ret = setsockopt(int sd, int level, int optname, const void *optval,
* int optlen);
*
* sd - the socket descript.
* level - set to IPPROTO_SCTP for all SCTP options.
* optname - the option name.
* optval - the buffer to store the value of the option.
* optlen - the size of the buffer.
*/
static int sctp_setsockopt(struct sock *sk, int level, int optname,
char *optval, int optlen)
{
int retval = 0;
char * tmp;
sctp_protocol_t *proto = sctp_get_protocol();
list_t *pos;
sctp_func_t *af;
SCTP_DEBUG_PRINTK("sctp_setsockopt(sk: %p... optname: %d)\n",
sk, optname);
/* I can hardly begin to describe how wrong this is. This is
* so broken as to be worse than useless. The API draft
* REALLY is NOT helpful here... I am not convinced that the
* semantics of setsockopt() with a level OTHER THAN SOL_SCTP
* are at all well-founded.
*/
if (level != SOL_SCTP) {
list_for_each(pos, &proto->address_families) {
af = list_entry(pos, sctp_func_t, list);
retval = af->setsockopt(sk, level, optname, optval,
optlen);
if (retval < 0)
goto out_nounlock;
}
}
sctp_lock_sock(sk);
switch (optname) {
case SCTP_SOCKOPT_DEBUG_NAME:
/* BUG! we don't ever seem to free this memory. --jgrimm */
if (NULL == (tmp = kmalloc(optlen + 1, GFP_KERNEL))) {
retval = -ENOMEM;
goto out_unlock;
}
if (copy_from_user(tmp, optval, optlen)) {
retval = -EFAULT;
goto out_unlock;
}
tmp[optlen] = '\000';
sctp_sk(sk)->ep->debug_name = tmp;
break;
case SCTP_SOCKOPT_BINDX_ADD:
/* 'optlen' is the size of the addresses buffer. */
retval = sctp_setsockopt_bindx(sk, (struct sockaddr_storage *)
optval, optlen, BINDX_ADD_ADDR);
break;
case SCTP_SOCKOPT_BINDX_REM:
/* 'optlen' is the size of the addresses buffer. */
retval = sctp_setsockopt_bindx(sk, (struct sockaddr_storage *)
optval, optlen, BINDX_REM_ADDR);
break;
case SCTP_DISABLE_FRAGMENTS:
retval = sctp_setsockopt_disable_fragments(sk, optval, optlen);
break;
case SCTP_SET_EVENTS:
retval = sctp_setsockopt_set_events(sk, optval, optlen);
break;
case SCTP_AUTOCLOSE:
retval = sctp_setsockopt_autoclose(sk, optval, optlen);
break;
default:
retval = -ENOPROTOOPT;
break;
};
out_unlock:
sctp_release_sock(sk);
out_nounlock:
return retval;
}
/* FIXME: Write comments. */
static int sctp_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len)
{
return -EOPNOTSUPP; /* STUB */
}
/* FIXME: Write comments. */
static int sctp_disconnect(struct sock *sk, int flags)
{
return -EOPNOTSUPP; /* STUB */
}
/* FIXME: Write comments. */
static struct sock *sctp_accept(struct sock *sk, int flags, int *err)
{
int error = -EOPNOTSUPP;
*err = error;
return NULL;
}
/* FIXME: Write Comments. */
static int sctp_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
return -EOPNOTSUPP; /* STUB */
}
/* This is the function which gets called during socket creation to
* initialized the SCTP-specific portion of the sock.
* The sock structure should already be zero-filled memory.
*/
static int sctp_init_sock(struct sock *sk)
{
sctp_endpoint_t *ep;
sctp_protocol_t *proto;
sctp_opt_t *sp;
SCTP_DEBUG_PRINTK("sctp_init_sock(sk: %p)\n", sk);
proto = sctp_get_protocol();
/* Create a per socket endpoint structure. Even if we
* change the data structure relationships, this may still
* be useful for storing pre-connect address information.
*/
ep = sctp_endpoint_new(proto, sk, GFP_KERNEL);
if (!ep)
return -ENOMEM;
sp = sctp_sk(sk);
/* Initialize the SCTP per socket area. */
sp->ep = ep;
sp->type = SCTP_SOCKET_UDP;
/* FIXME: The next draft (04) of the SCTP Sockets Extensions
* should include a socket option for manipulating these
* message parameters (and a few others).
*/
sp->default_stream = 0;
sp->default_ppid = 0;
/* Initialize default setup parameters. These parameters
* can be modified with the SCTP_INITMSG socket option or
* overridden by the SCTP_INIT CMSG.
*/
sp->initmsg.sinit_num_ostreams = proto->max_outstreams;
sp->initmsg.sinit_max_instreams = proto->max_instreams;
sp->initmsg.sinit_max_attempts = proto->max_retrans_init;
sp->initmsg.sinit_max_init_timeo = proto->rto_max / HZ;
/* Initialize default RTO related parameters. These parameters can
* be modified for with the SCTP_RTOINFO socket option.
* FIXME: This are not used yet.
*/
sp->rtoinfo.srto_initial = proto->rto_initial;
sp->rtoinfo.srto_max = proto->rto_max;
sp->rtoinfo.srto_min = proto->rto_min;
/* Initialize default event subscriptions.
* the struct sock is initialized to zero, so only
* enable the events needed. By default, UDP-style
* sockets enable io and association change notifications.
*/
if (SCTP_SOCKET_UDP == sp->type) {
sp->subscribe.sctp_data_io_event = 1;
sp->subscribe.sctp_association_event = 1;
}
/* Default Peer Address Parameters. These defaults can
* be modified via SCTP_SET_PEER_ADDR_PARAMS
*/
sp->paddrparam.spp_hbinterval = proto->hb_interval / HZ;
sp->paddrparam.spp_pathmaxrxt = proto->max_retrans_path;
/* If enabled no SCTP message fragmentation will be performed.
* Configure through SCTP_DISABLE_FRAGMENTS socket option.
*/
sp->disable_fragments = 0;
/* Turn on/off any Nagle-like algorithm. */
sp->nodelay = 0;
/* Auto-close idle associations after the configured
* number of seconds. A value of 0 disables this
* feature. Configure through the SCTP_AUTOCLOSE socket option,
* for UDP-style sockets only.
*/
sp->autoclose = 0;
SCTP_DBG_OBJCNT_INC(sock);
return 0;
}
/* Cleanup any SCTP per socket resources. */
static int sctp_destroy_sock(struct sock *sk)
{
sctp_endpoint_t *ep;
SCTP_DEBUG_PRINTK("sctp_destroy_sock(sk: %p)\n", sk);
/* Release our hold on the endpoint. */
ep = sctp_sk(sk)->ep;
sctp_endpoint_free(ep);
return 0;
}
/* FIXME: Comments needed. */
static void sctp_shutdown(struct sock *sk, int how)
{
/* UDP-style sockets do not support shutdown. */
/* STUB */
}
static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
int *optlen)
{
struct sctp_status status;
sctp_endpoint_t *ep;
sctp_association_t *assoc = NULL;
sctp_transport_t *transport;
sctp_assoc_t associd;
int retval = 0;
if (len != sizeof(status)) {
retval = -EINVAL;
goto out_nounlock;
}
if (copy_from_user(&status, optval, sizeof(status))) {
retval = -EFAULT;
goto out_nounlock;
}
sctp_lock_sock(sk);
associd = status.sstat_assoc_id;
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sctp_sk(sk)->type) && associd) {
assoc = sctp_id2assoc(sk, associd);
if (!assoc) {
retval = -EINVAL;
goto out_unlock;
}
} else {
ep = sctp_sk(sk)->ep;
if (list_empty(&ep->asocs)) {
retval = -EINVAL;
goto out_unlock;
}
assoc = list_entry(ep->asocs.next, sctp_association_t, asocs);
}
transport = assoc->peer.primary_path;
status.sstat_assoc_id = sctp_assoc2id(assoc);
status.sstat_state = assoc->state;
status.sstat_rwnd = assoc->peer.rwnd;
status.sstat_unackdata = assoc->unack_data;
status.sstat_penddata = assoc->peer.tsn_map.pending_data;
status.sstat_instrms = assoc->c.sinit_max_instreams;
status.sstat_outstrms = assoc->c.sinit_num_ostreams;
status.sstat_fragmentation_point = assoc->frag_point;
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address,
&(transport->ipaddr), sizeof(sockaddr_storage_t));
status.sstat_primary.spinfo_state = transport->state.active;
status.sstat_primary.spinfo_cwnd = transport->cwnd;
status.sstat_primary.spinfo_srtt = transport->srtt;
status.sstat_primary.spinfo_rto = transport->rto;
status.sstat_primary.spinfo_mtu = transport->pmtu;
if (put_user(len, optlen)) {
retval = -EFAULT;
goto out_unlock;
}
SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %p\n",
len, status.sstat_state, status.sstat_rwnd,
status.sstat_assoc_id);
if (copy_to_user(optval, &status, len)) {
retval = -EFAULT;
goto out_unlock;
}
out_unlock:
sctp_release_sock(sk);
out_nounlock:
return (retval);
}
static inline int sctp_getsockopt_disable_fragments(struct sock *sk, int len,
char *optval, int *optlen)
{
int val;
if (len < sizeof(int))
return -EINVAL;
len = sizeof(int);
val = (sctp_sk(sk)->disable_fragments == 1);
if (put_user(len, optlen))
return -EFAULT;
if (copy_to_user(optval, &val, len))
return -EFAULT;
return 0;
}
static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *optval, int *optlen)
{
if (len != sizeof(struct sctp_event_subscribe))
return -EINVAL;
if (copy_to_user(optval, &sctp_sk(sk)->subscribe, len))
return -EFAULT;
return 0;
}
static inline int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen)
{
if (len != sizeof(int))
return -EINVAL;
if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
return -EFAULT;
return 0;
}
/* Helper routine to branch off an association to a new socket. */
static int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newsock)
{
struct sock *oldsk = assoc->base.sk;
struct sock *newsk;
struct socket *tmpsock;
sctp_endpoint_t *newep;
sctp_opt_t *oldsp = sctp_sk(oldsk);
sctp_opt_t *newsp;
int err = 0;
/* An association cannot be branched off from an already peeled-off
* socket.
*/
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sctp_sk(oldsk)->type)
return -EINVAL;
/* Create a new socket. */
err = sock_create(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, &tmpsock);
if (err < 0)
return err;
newsk = tmpsock->sk;
newsp = sctp_sk(newsk);
newep = newsp->ep;
/* Migrate socket buffer sizes and all the socket level options to the
* new socket.
*/
newsk->sndbuf = oldsk->sndbuf;
newsk->rcvbuf = oldsk->rcvbuf;
*newsp = *oldsp;
/* Restore the ep value that was overwritten with the above structure
* copy.
*/
newsp->ep = newep;
/* Set the type of socket to indicate that it is peeled off from the
* original socket.
*/
newsp->type = SCTP_SOCKET_UDP_HIGH_BANDWIDTH;
/* Migrate the association to the new socket. */
sctp_assoc_migrate(assoc, newsk);
*newsock = tmpsock;
return err;
}
static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval, int *optlen)
{
sctp_peeloff_arg_t peeloff;
struct socket *newsock;
int err, sd;
sctp_association_t *assoc;
if (len != sizeof(sctp_peeloff_arg_t))
return -EINVAL;
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
assoc = sctp_id2assoc(sk, peeloff.associd);
if (NULL == assoc)
return -EINVAL;
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc);
err = sctp_do_peeloff(assoc, &newsock);
if (err < 0)
return err;
/* Map the socket to an unused fd that can be returned to the user. */
sd = sock_map_fd(newsock);
if (sd < 0) {
sock_release(newsock);
return sd;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n",
__FUNCTION__, sk, assoc, newsock->sk, sd);
/* Return the fd mapped to the new socket. */
peeloff.sd = sd;
if (copy_to_user(optval, &peeloff, len))
return -EFAULT;
return 0;
}
static int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen)
{
int retval = 0;
sctp_protocol_t *proto = sctp_get_protocol();
sctp_func_t *af;
list_t *pos;
int len;
SCTP_DEBUG_PRINTK("sctp_getsockopt(sk: %p, ...)\n", sk);
/* I can hardly begin to describe how wrong this is. This is
* so broken as to be worse than useless. The API draft
* REALLY is NOT helpful here... I am not convinced that the
* semantics of getsockopt() with a level OTHER THAN SOL_SCTP
* are at all well-founded.
*/
if (level != SOL_SCTP) {
list_for_each(pos, &proto->address_families) {
af = list_entry(pos, sctp_func_t, list);
retval = af->getsockopt(sk, level, optname,
optval, optlen);
if (retval < 0)
return retval;
}
}
if (get_user(len, optlen))
return -EFAULT;
switch (optname) {
case SCTP_STATUS:
retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen);
break;
case SCTP_DISABLE_FRAGMENTS:
retval = sctp_getsockopt_disable_fragments(sk, len, optval,
optlen);
break;
case SCTP_SET_EVENTS:
retval = sctp_getsockopt_set_events(sk, len, optval, optlen);
break;
case SCTP_AUTOCLOSE:
retval = sctp_getsockopt_autoclose(sk, len, optval, optlen);
break;
case SCTP_SOCKOPT_PEELOFF:
retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
break;
default:
retval = -ENOPROTOOPT;
break;
};
return retval;
}
static void sctp_hash(struct sock *sk)
{
/* STUB */
}
static void sctp_unhash(struct sock *sk)
{
/* STUB */
}
/* Check if port is acceptable. Possibly find first available port.
*
* The port hash table (contained in the 'global' SCTP protocol storage
* returned by sctp_protocol_t * sctp_get_protocol()). The hash
* table is an array of 4096 lists (sctp_bind_hashbucket_t). Each
* list (the list number is the port number hashed out, so as you
* would expect from a hash function, all the ports in a given list have
* such a number that hashes out to the same list number; you were
* expecting that, right?); so each list has a set of ports, with a
* link to the socket (struct sock) that uses it, the port number and
* a fastreuse flag (FIXME: NPI ipg).
*/
static long sctp_get_port_local(struct sock *sk, unsigned short snum)
{
sctp_bind_hashbucket_t *head; /* hash list */
sctp_bind_bucket_t *pp; /* hash list port iterator */
sctp_protocol_t *sctp = sctp_get_protocol();
int ret;
SCTP_DEBUG_PRINTK("sctp_get_port() begins, snum=%d\n", snum);
sctp_local_bh_disable();
if (snum == 0) {
/* Search for an available port.
*
* 'sctp->port_rover' was the last port assigned, so
* we start to search from 'sctp->port_rover +
* 1'. What we do is first check if port 'rover' is
* already in the hash table; if not, we use that; if
* it is, we try next.
*/
int low = sysctl_local_port_range[0];
int high = sysctl_local_port_range[1];
int remaining = (high - low) + 1;
int rover;
int index;
sctp_spin_lock(&sctp->port_alloc_lock);
rover = sctp->port_rover;
do {
rover++;
if ((rover < low) || (rover > high))
rover = low;
index = sctp_phashfn(rover);
head = &sctp->port_hashtable[index];
sctp_spin_lock(&head->lock);
for (pp = head->chain; pp; pp = pp->next)
if (pp->port == rover)
goto next;
break;
next:
sctp_spin_unlock(&head->lock);
} while (--remaining > 0);
sctp->port_rover = rover;
sctp_spin_unlock(&sctp->port_alloc_lock);
/* Exhausted local port range during search? */
ret = 1;
if (remaining <= 0)
goto fail;
/* OK, here is the one we will use. HEAD (the port
* hash table list entry) is non-NULL and we hold it's
* mutex.
*/
snum = rover;
pp = NULL;
} else {
/* We are given an specific port number; we verify
* that it is not being used. If it is used, we will
* exahust the search in the hash list corresponding
* to the port number (snum) - we detect that with the
* port iterator, pp being NULL.
*/
head = &sctp->port_hashtable[sctp_phashfn(snum)];
sctp_spin_lock(&head->lock);
for (pp = head->chain; pp; pp = pp->next) {
if (pp->port == snum)
break;
}
}
if (pp != NULL && pp->sk != NULL) {
/* We had a port hash table hit - there is an
* available port (pp != NULL) and it is being
* used by other socket (pp->sk != NULL); that other
* socket is going to be sk2.
*/
int sk_reuse = sk->reuse;
sockaddr_storage_t tmpaddr;
struct sock *sk2 = pp->sk;
SCTP_DEBUG_PRINTK("sctp_get_port() found a "
"possible match\n");
if (pp->fastreuse != 0 && sk->reuse != 0)
goto success;
/* FIXME - multiple addresses need to be supported
* later.
*/
switch (sk->family) {
case PF_INET:
tmpaddr.v4.sin_family = AF_INET;
tmpaddr.v4.sin_port = snum;
tmpaddr.v4.sin_addr.s_addr = inet_sk(sk)->rcv_saddr;
break;
case PF_INET6:
SCTP_V6(tmpaddr.v6.sin6_family = AF_INET6;
tmpaddr.v6.sin6_port = snum;
tmpaddr.v6.sin6_addr =
inet6_sk(sk)->rcv_saddr;
)
break;
default:
break;
};
/* Run through the list of sockets bound to the port
* (pp->port) [via the pointers bind_next and
* bind_pprev in the struct sock *sk2 (pp->sk)]. On each one,
* we get the endpoint they describe and run through
* the endpoint's list of IP (v4 or v6) addresses,
* comparing each of the addresses with the address of
* the socket sk. If we find a match, then that means
* that this port/socket (sk) combination are already
* in an endpoint.
*/
for( ; sk2 != NULL; sk2 = sk2->bind_next) {
sctp_endpoint_t *ep2;
ep2 = sctp_sk(sk2)->ep;
if (!sk_reuse || !sk2->reuse) {
if (sctp_bind_addr_has_addr(
&ep2->base.bind_addr, &tmpaddr)) {
goto found;
}
}
}
found:
/* If we found a conflict, fail. */
if (sk2 != NULL) {
ret = (long) sk2;
goto fail_unlock;
}
SCTP_DEBUG_PRINTK("sctp_get_port(): Found a match\n");
}
/* If there was a hash table miss, create a new port. */
ret = 1;
if (pp == NULL && (pp = sctp_bucket_create(head, snum)) == NULL)
goto fail_unlock;
/* In either case (hit or miss), make sure fastreuse is 1 only
* if sk->reuse is too (that is, if the caller requested
* SO_REUSEADDR on this socket -sk-).
*/
if (pp->sk == NULL) {
pp->fastreuse = sk->reuse? 1 : 0;
} else if (pp->fastreuse && sk->reuse == 0) {
pp->fastreuse = 0;
}
/* We are set, so fill up all the data in the hash table
* entry, tie the socket list information with the rest of the
* sockets FIXME: Blurry, NPI (ipg).
*/
success:
inet_sk(sk)->num = snum;
if (sk->prev == NULL) {
if ((sk->bind_next = pp->sk) != NULL)
pp->sk->bind_pprev = &sk->bind_next;
pp->sk = sk;
sk->bind_pprev = &pp->sk;
sk->prev = (struct sock *) pp;
}
ret = 0;
fail_unlock:
sctp_spin_unlock(&head->lock);
fail:
sctp_local_bh_enable();
SCTP_DEBUG_PRINTK("sctp_get_port() ends, ret=%d\n", ret);
return ret;
}
static int sctp_get_port(struct sock *sk, unsigned short snum)
{
long ret = sctp_get_port_local(sk, snum);
return (ret ? 1 : 0);
}
/*
* 3.1.3 listen() - UDP Style Syntax
*
* By default, new associations are not accepted for UDP style sockets.
* An application uses listen() to mark a socket as being able to
* accept new associations.
*/
static int sctp_seqpacket_listen(struct sock *sk, int backlog)
{
sctp_opt_t *sp = sctp_sk(sk);
sctp_endpoint_t *ep = sp->ep;
/* Only UDP style sockets that are not peeled off are allowed to
* listen().
*/
if (SCTP_SOCKET_UDP != sp->type)
return -EINVAL;
/*
* If a bind() or sctp_bindx() is not called prior to a listen()
* call that allows new associations to be accepted, the system
* picks an ephemeral port and will choose an address set equivalent
* to binding with a wildcard address.
*
* This is not currently spelled out in the SCTP sockets
* extensions draft, but follows the practice as seen in TCP
* sockets.
*/
if (!ep->base.bind_addr.port) {
if (sctp_autobind(sk))
return -EAGAIN;
}
sk->state = SCTP_SS_LISTENING;
sctp_hash_endpoint(ep);
return 0;
}
/*
* Move a socket to LISTENING state.
*/
int sctp_inet_listen(struct socket *sock, int backlog)
{
struct sock *sk = sock->sk;
int err;
sctp_lock_sock(sk);
err = -EINVAL;
if (sock->state != SS_UNCONNECTED)
goto out;
switch (sock->type) {
case SOCK_SEQPACKET:
err = sctp_seqpacket_listen(sk, backlog);
break;
case SOCK_STREAM:
/* FIXME for TCP-style sockets. */
err = -EOPNOTSUPP;
default:
goto out;
};
out:
sctp_release_sock(sk);
return err;
}
/*
* This function is done by modeling the current datagram_poll() and the
* tcp_poll(). Note that, based on these implementations, we don't
* lock the socket in this function, even though it seems that,
* ideally, locking or some other mechanisms can be used to ensure
* the integrity of the counters (sndbuf and wmem_queued) used
* in this place. We assume that we don't need locks either until proven
* otherwise.
*
* Another thing to note is that we include the Async I/O support
* here, again, by modeling the current TCP/UDP code. We don't have
* a good way to test with it yet.
*/
unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait)
{
struct sock *sk = sock->sk;
unsigned int mask;
poll_wait(file, sk->sleep, wait);
mask = 0;
/* Is there any exceptional events? */
if (sk->err || !skb_queue_empty(&sk->error_queue))
mask |= POLLERR;
if (sk->shutdown == SHUTDOWN_MASK)
mask |= POLLHUP;
/* Is it readable? Reconsider this code with TCP-style support. */
if (!skb_queue_empty(&sk->receive_queue) ||
(sk->shutdown & RCV_SHUTDOWN))
mask |= POLLIN | POLLRDNORM;
/*
* FIXME: We need to set SCTP_SS_DISCONNECTING for TCP-style and
* peeled off sockets. Additionally, TCP-style needs to consider
* other establishment conditions.
*/
if (SCTP_SOCKET_UDP != sctp_sk(sk)->type) {
/* The association is going away. */
if (SCTP_SS_DISCONNECTING == sk->state)
mask |= POLLHUP;
/* The association is either gone or not ready. */
if (SCTP_SS_CLOSED == sk->state)
return mask;
}
/* Is it writable? */
if (sctp_writeable(sk)) {
mask |= POLLOUT | POLLWRNORM;
} else {
set_bit(SOCK_ASYNC_NOSPACE, &sk->socket->flags);
/*
* Since the socket is not locked, the buffer
* might be made available after the writeable check and
* before the bit is set. This could cause a lost I/O
* signal. tcp_poll() has a race breaker for this race
* condition. Based on their implementation, we put
* in the following code to cover it as well.
*/
if (sctp_writeable(sk))
mask |= POLLOUT | POLLWRNORM;
}
return mask;
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
static sctp_bind_bucket_t *sctp_bucket_create(sctp_bind_hashbucket_t *head, unsigned short snum)
{
sctp_bind_bucket_t *pp;
SCTP_DEBUG_PRINTK( "sctp_bucket_create() begins, snum=%d\n", snum);
pp = kmalloc(sizeof(sctp_bind_bucket_t), GFP_ATOMIC);
if (pp) {
pp->port = snum;
pp->fastreuse = 0;
pp->sk = NULL;
if ((pp->next = head->chain) != NULL)
pp->next->pprev = &pp->next;
head->chain= pp;
pp->pprev = &head->chain;
}
SCTP_DEBUG_PRINTK("sctp_bucket_create() ends, pp=%p\n", pp);
return pp;
}
/* FIXME: Commments! */
static __inline__ void __sctp_put_port(struct sock *sk)
{
sctp_protocol_t *sctp_proto = sctp_get_protocol();
sctp_bind_hashbucket_t *head =
&sctp_proto->port_hashtable[sctp_phashfn(inet_sk(sk)->num)];
sctp_bind_bucket_t *pp;
sctp_spin_lock(&head->lock);
pp = (sctp_bind_bucket_t *) sk->prev;
if (sk->bind_next)
sk->bind_next->bind_pprev = sk->bind_pprev;
*(sk->bind_pprev) = sk->bind_next;
sk->prev = NULL;
inet_sk(sk)->num = 0;
if (pp->sk) {
if (pp->next)
pp->next->pprev = pp->pprev;
*(pp->pprev) = pp->next;
kfree(pp);
}
sctp_spin_unlock(&head->lock);
}
void sctp_put_port(struct sock *sk)
{
sctp_local_bh_disable();
__sctp_put_port(sk);
sctp_local_bh_enable();
}
/*
* The system picks an ephemeral port and choose an address set equivalent
* to binding with a wildcard address.
* One of those addresses will be the primary address for the association.
* This automatically enables the multihoming capability of SCTP.
*/
static int sctp_autobind(struct sock *sk)
{
sockaddr_storage_t autoaddr;
int addr_len = 0;
memset(&autoaddr, 0, sizeof(sockaddr_storage_t));
switch (sk->family) {
case PF_INET:
autoaddr.v4.sin_family = AF_INET;
autoaddr.v4.sin_addr.s_addr = INADDR_ANY;
autoaddr.v4.sin_port = htons(inet_sk(sk)->num);
addr_len = sizeof(struct sockaddr_in);
break;
case PF_INET6:
SCTP_V6(
/* FIXME: Write me for v6! */
BUG();
autoaddr.v6.sin6_family = AF_INET6;
autoaddr.v6.sin6_port = htons(inet_sk(sk)->num);
addr_len = sizeof(struct sockaddr_in6);
);
break;
default: /* This should not happen. */
break;
};
return sctp_do_bind(sk, &autoaddr, addr_len);
}
/* Parse out IPPROTO_SCTP CMSG headers. Perform only minimal validation.
*
* From RFC 2292
* 4.2 The cmsghdr Structure *
*
* When ancillary data is sent or received, any number of ancillary data
* objects can be specified by the msg_control and msg_controllen members of
* the msghdr structure, because each object is preceded by
* a cmsghdr structure defining the object's length (the cmsg_len member).
* Historically Berkeley-derived implementations have passed only one object
* at a time, but this API allows multiple objects to be
* passed in a single call to sendmsg() or recvmsg(). The following example
* shows two ancillary data objects in a control buffer.
*
* |<--------------------------- msg_controllen -------------------------->|
* | |
*
* |<----- ancillary data object ----->|<----- ancillary data object ----->|
*
* |<---------- CMSG_SPACE() --------->|<---------- CMSG_SPACE() --------->|
* | | |
*
* |<---------- cmsg_len ---------->| |<--------- cmsg_len ----------->| |
*
* |<--------- CMSG_LEN() --------->| |<-------- CMSG_LEN() ---------->| |
* | | | | |
*
* +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
* |cmsg_|cmsg_|cmsg_|XX| |XX|cmsg_|cmsg_|cmsg_|XX| |XX|
*
* |len |level|type |XX|cmsg_data[]|XX|len |level|type |XX|cmsg_data[]|XX|
*
* +-----+-----+-----+--+-----------+--+-----+-----+-----+--+-----------+--+
* ^
* |
*
* msg_control
* points here
*/
static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs)
{
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(msg);
cmsg != NULL;
cmsg = CMSG_NXTHDR((struct msghdr*)msg, cmsg)) {
/* Check for minimum length. The SCM code has this check. */
if (cmsg->cmsg_len < sizeof(struct cmsghdr) ||
(unsigned long)(((char*)cmsg - (char*)msg->msg_control)
+ cmsg->cmsg_len) > msg->msg_controllen) {
return -EINVAL;
}
/* Should we parse this header or ignore? */
if (cmsg->cmsg_level != IPPROTO_SCTP)
continue;
/* Strictly check lengths following example in SCM code. */
switch (cmsg->cmsg_type) {
case SCTP_INIT:
/* SCTP Socket API Extension (draft 1)
* 5.2.1 SCTP Initiation Structure (SCTP_INIT)
*
* This cmsghdr structure provides information for
* initializing new SCTP associations with sendmsg().
* The SCTP_INITMSG socket option uses this same data
* structure. This structure is not used for
* recvmsg().
*
* cmsg_level cmsg_type cmsg_data[]
* ------------ ------------ ----------------------
* IPPROTO_SCTP SCTP_INIT struct sctp_initmsg
*/
if (cmsg->cmsg_len !=
CMSG_LEN(sizeof(struct sctp_initmsg)))
return -EINVAL;
cmsgs->init = (struct sctp_initmsg *)CMSG_DATA(cmsg);
break;
case SCTP_SNDRCV:
/* SCTP Socket API Extension (draft 1)
* 5.2.2 SCTP Header Information Structure(SCTP_SNDRCV)
*
* This cmsghdr structure specifies SCTP options for
* sendmsg() and describes SCTP header information
* about a received message through recvmsg().
*
* cmsg_level cmsg_type cmsg_data[]
* ------------ ------------ ----------------------
* IPPROTO_SCTP SCTP_SNDRCV struct sctp_sndrcvinfo
*/
if (cmsg->cmsg_len !=
CMSG_LEN(sizeof(struct sctp_sndrcvinfo)))
return -EINVAL;
cmsgs->info = (struct sctp_sndrcvinfo *)CMSG_DATA(cmsg);
/* Minimally, validate the sinfo_flags. */
if (cmsgs->info->sinfo_flags &
~(MSG_UNORDERED | MSG_ADDR_OVER |
MSG_ABORT | MSG_EOF))
return -EINVAL;
break;
default:
return -EINVAL;
};
}
return 0;
}
/* Setup sk->rcv_saddr before calling get_port(). */
static inline void sctp_sk_addr_set(struct sock *sk,
const sockaddr_storage_t *newaddr,
sockaddr_storage_t *saveaddr)
{
struct inet_opt *inet = inet_sk(sk);
saveaddr->sa.sa_family = newaddr->sa.sa_family;
switch (newaddr->sa.sa_family) {
case AF_INET:
saveaddr->v4.sin_addr.s_addr = inet->rcv_saddr;
inet->rcv_saddr = inet->saddr = newaddr->v4.sin_addr.s_addr;
break;
case AF_INET6:
SCTP_V6({
struct ipv6_pinfo *np = inet6_sk(sk);
saveaddr->v6.sin6_addr = np->rcv_saddr;
np->rcv_saddr = np->saddr = newaddr->v6.sin6_addr;
break;
})
default:
break;
};
}
/* Restore sk->rcv_saddr after failing get_port(). */
static inline void sctp_sk_addr_restore(struct sock *sk, const sockaddr_storage_t *addr)
{
struct inet_opt *inet = inet_sk(sk);
switch (addr->sa.sa_family) {
case AF_INET:
inet->rcv_saddr = inet->saddr = addr->v4.sin_addr.s_addr;
break;
case AF_INET6:
SCTP_V6({
struct ipv6_pinfo *np = inet6_sk(sk);
np->rcv_saddr = np->saddr = addr->v6.sin6_addr;
break;
})
default:
break;
};
}
/*
* Wait for a packet..
* Note: This function is the same function as in core/datagram.c
* with a few modifications to make lksctp work.
*/
static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p)
{
int error;
DECLARE_WAITQUEUE(wait, current);
__set_current_state(TASK_INTERRUPTIBLE);
add_wait_queue_exclusive(sk->sleep, &wait);
/* Socket errors? */
error = sock_error(sk);
if (error)
goto out;
if (!skb_queue_empty(&sk->receive_queue))
goto ready;
/* Socket shut down? */
if (sk->shutdown & RCV_SHUTDOWN)
goto out;
/* Sequenced packets can come disconnected. If so we report the
* problem.
*/
error = -ENOTCONN;
/* Is there a good reason to think that we may receive some data? */
if ((list_empty(&sctp_sk(sk)->ep->asocs)) &&
(sk->state != SCTP_SS_LISTENING))
goto out;
/* Handle signals. */
if (signal_pending(current))
goto interrupted;
/* Let another process have a go. Since we are going to sleep
* anyway. Note: This may cause odd behaviors if the message
* does not fit in the user's buffer, but this seems to be the
* only way to honor MSG_DONTWAIT realistically.
*/
sctp_release_sock(sk);
*timeo_p = schedule_timeout(*timeo_p);
sctp_lock_sock(sk);
ready:
remove_wait_queue(sk->sleep, &wait);
__set_current_state(TASK_RUNNING);
return 0;
interrupted:
error = sock_intr_errno(*timeo_p);
out:
remove_wait_queue(sk->sleep, &wait);
__set_current_state(TASK_RUNNING);
*err = error;
return error;
}
/* Receive a datagram.
* Note: This is pretty much the same routine as in core/datagram.c
* with a few changes to make lksctp work.
*/
static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int noblock, int *err)
{
int error;
struct sk_buff *skb;
long timeo;
/* Caller is allowed not to check sk->err before skb_recv_datagram() */
error = sock_error(sk);
if (error)
goto no_packet;
timeo = sock_rcvtimeo(sk, noblock);
SCTP_DEBUG_PRINTK("Timeout: timeo: %ld, MAX: %ld.\n",
timeo, MAX_SCHEDULE_TIMEOUT);
do {
/* Again only user level code calls this function,
* so nothing interrupt level
* will suddenly eat the receive_queue.
*
* Look at current nfs client by the way...
* However, this function was corrent in any case. 8)
*/
if (flags & MSG_PEEK) {
unsigned long cpu_flags;
sctp_spin_lock_irqsave(&sk->receive_queue.lock,
cpu_flags);
skb = skb_peek(&sk->receive_queue);
if (skb)
atomic_inc(&skb->users);
sctp_spin_unlock_irqrestore(&sk->receive_queue.lock,
cpu_flags);
} else {
skb = skb_dequeue(&sk->receive_queue);
}
if (skb)
return skb;
/* User doesn't want to wait. */
error = -EAGAIN;
if (!timeo)
goto no_packet;
} while (sctp_wait_for_packet(sk, err, &timeo) == 0);
return NULL;
no_packet:
*err = error;
return NULL;
}
/* Copy an approriately formatted address for msg_name. */
static inline void sctp_sk_memcpy_msgname(struct sock *sk, char * msgname,
int *addr_len, struct sk_buff *skb)
{
struct sockaddr_in *sin;
struct sockaddr_in6 *sin6 __attribute__ ((unused));
struct sctphdr *sh;
/* The sockets layer handles copying this out to user space. */
switch (sk->family) {
case PF_INET:
sin = (struct sockaddr_in *)msgname;
if (addr_len)
*addr_len = sizeof(struct sockaddr_in);
sin->sin_family = AF_INET;
sh = (struct sctphdr *) skb->h.raw;
sin->sin_port = sh->source;
sin->sin_addr.s_addr = skb->nh.iph->saddr;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
break;
case PF_INET6:
SCTP_V6(
/* FIXME: Need v6 code here. We should convert
* V4 addresses to PF_INET6 format. See ipv6/udp.c
* for an example. --jgrimm
*/
);
break;
default: /* Should not get here. */
break;
};
}
static inline int sctp_sendmsg_verify_name(struct sock *sk, struct msghdr *msg)
{
sockaddr_storage_t *sa;
if (msg->msg_namelen < sizeof (struct sockaddr))
return -EINVAL;
sa = (sockaddr_storage_t *) msg->msg_name;
switch (sa->sa.sa_family) {
case AF_INET:
if (msg->msg_namelen < sizeof(struct sockaddr_in))
return -EINVAL;
break;
case AF_INET6:
if (PF_INET == sk->family)
return -EINVAL;
SCTP_V6(
if (msg->msg_namelen < sizeof(struct sockaddr_in6))
return -EINVAL;
break;
);
default:
return -EINVAL;
};
/* Disallow any illegal addresses to be used as destinations. */
if (!sctp_addr_is_valid(sa))
return -EINVAL;
return 0;
}
/* Get the sndbuf space available at the time on the association. */
static inline int sctp_wspace(sctp_association_t *asoc)
{
struct sock *sk = asoc->base.sk;
int amt = 0;
amt = sk->sndbuf - asoc->sndbuf_used;
if (amt < 0)
amt = 0;
return amt;
}
/* Increment the used sndbuf space count of the corresponding association by
* the size of the outgoing data chunk.
* Also, set the skb destructor for sndbuf accounting later.
*
* Since it is always 1-1 between chunk and skb, and also a new skb is always
* allocated for chunk bundling in sctp_packet_transmit(), we can use the
* destructor in the data chunk skb for the purpose of the sndbuf space
* tracking.
*/
static inline void sctp_set_owner_w(sctp_chunk_t *chunk)
{
sctp_association_t *asoc = chunk->asoc;
struct sock *sk = asoc->base.sk;
/* The sndbuf space is tracked per association. */
sctp_association_hold(asoc);
chunk->skb->destructor = sctp_wfree;
/* Save the chunk pointer in skb for sctp_wfree to use later. */
*((sctp_chunk_t **)(chunk->skb->cb)) = chunk;
asoc->sndbuf_used += SCTP_DATA_SNDSIZE(chunk);
sk->wmem_queued += SCTP_DATA_SNDSIZE(chunk);
}
/* Do accounting for the sndbuf space.
* Decrement the used sndbuf space of the corresponding association by the
* data size which was just transmitted(freed).
*/
static void sctp_wfree(struct sk_buff *skb)
{
sctp_association_t *asoc;
sctp_chunk_t *chunk;
struct sock *sk;
/* Get the saved chunk pointer. */
chunk = *((sctp_chunk_t **)(skb->cb));
asoc = chunk->asoc;
sk = asoc->base.sk;
asoc->sndbuf_used -= SCTP_DATA_SNDSIZE(chunk);
sk->wmem_queued -= SCTP_DATA_SNDSIZE(chunk);
__sctp_write_space(asoc);
sctp_association_put(asoc);
}
/* Helper function to wait for space in the sndbuf. */
static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p, int msg_len)
{
struct sock *sk = asoc->base.sk;
int err = 0;
long current_timeo = *timeo_p;
DECLARE_WAITQUEUE(wait, current);
SCTP_DEBUG_PRINTK("wait_for_sndbuf: asoc=%p, timeo=%ld, msg_len=%d\n",
asoc, (long)(*timeo_p), msg_len);
/* Wait on the association specific sndbuf space. */
add_wait_queue_exclusive(&asoc->wait, &wait);
/* Increment the association's refcnt. */
sctp_association_hold(asoc);
for (;;) {
set_current_state(TASK_INTERRUPTIBLE);
if (!*timeo_p)
goto do_nonblock;
if (sk->err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
asoc->base.dead)
goto do_error;
if (signal_pending(current))
goto do_interrupted;
if (msg_len <= sctp_wspace(asoc))
break;
/* Let another process have a go. Since we are going
* to sleep anyway.
*/
sctp_release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
sctp_lock_sock(sk);
*timeo_p = current_timeo;
}
out:
remove_wait_queue(&asoc->wait, &wait);
/* Release the association's refcnt. */
sctp_association_put(asoc);
__set_current_state(TASK_RUNNING);
return err;
do_error:
err = -EPIPE;
goto out;
do_interrupted:
err = sock_intr_errno(*timeo_p);
goto out;
do_nonblock:
err = -EAGAIN;
goto out;
}
/* If sndbuf has changed, wake up per association sndbuf waiters. */
static void __sctp_write_space(sctp_association_t *asoc)
{
struct sock *sk = asoc->base.sk;
struct socket *sock = sk->socket;
if ((sctp_wspace(asoc) > 0) && sock) {
if (waitqueue_active(&asoc->wait))
wake_up_interruptible(&asoc->wait);
if (sctp_writeable(sk)) {
if (sk->sleep && waitqueue_active(sk->sleep))
wake_up_interruptible(sk->sleep);
/* Note that we try to include the Async I/O support
* here by modeling from the current TCP/UDP code.
* We have not tested with it yet.
*/
if (sock->fasync_list &&
!(sk->shutdown & SEND_SHUTDOWN))
sock_wake_async(sock, 2, POLL_OUT);
}
}
}
/* If socket sndbuf has changed, wake up all per association waiters. */
void sctp_write_space(struct sock *sk)
{
sctp_association_t *asoc;
list_t *pos;
/* Wake up the tasks in each wait queue. */
list_for_each(pos, &((sctp_sk(sk))->ep->asocs)) {
asoc = list_entry(pos, sctp_association_t, asocs);
__sctp_write_space(asoc);
}
}
/* Is there any sndbuf space available on the socket?
*
* Note that wmem_queued is the sum of the send buffers on all of the
* associations on the same socket. For a UDP-style socket with
* multiple associations, it is possible for it to be "unwriteable"
* prematurely. I assume that this is acceptable because
* a premature "unwriteable" is better than an accidental "writeable" which
* would cause an unwanted block under certain circumstances. For the 1-1
* UDP-style sockets or TCP-style sockets, this code should work.
* - Daisy
*/
static int sctp_writeable(struct sock *sk)
{
int amt = 0;
amt = sk->sndbuf - sk->wmem_queued;
if (amt < 0)
amt = 0;
return amt;
}
/* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = {
.name = "SCTP",
.close = sctp_close,
.connect = sctp_connect,
.disconnect = sctp_disconnect,
.accept = sctp_accept,
.ioctl = sctp_ioctl,
.init = sctp_init_sock,
.destroy = sctp_destroy_sock,
.shutdown = sctp_shutdown,
.setsockopt = sctp_setsockopt,
.getsockopt = sctp_getsockopt,
.sendmsg = sctp_sendmsg,
.recvmsg = sctp_recvmsg,
.bind = sctp_bind,
.backlog_rcv = sctp_backlog_rcv,
.hash = sctp_hash,
.unhash = sctp_unhash,
.get_port = sctp_get_port,
};
/* SCTP kernel reference Implementation
* Copyright (c) 2002 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_sysctl.c,v 1.2 2002/07/12 14:50:25 jgrimm Exp $
*
* 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_sysctl.c,v 1.2 2002/07/12 14:50:25 jgrimm Exp $";
#include <net/sctp/sctp_structs.h>
#include <linux/sysctl.h>
extern sctp_protocol_t sctp_proto;
static ctl_table sctp_table[] = {
{ NET_SCTP_RTO_INITIAL, "rto_initial",
&sctp_proto.rto_initial, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies },
{ NET_SCTP_RTO_MIN, "rto_min",
&sctp_proto.rto_min, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies },
{ NET_SCTP_RTO_MAX, "rto_max",
&sctp_proto.rto_max, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies },
{ NET_SCTP_VALID_COOKIE_LIFE, "valid_cookie_life",
&sctp_proto.valid_cookie_life, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies },
{ NET_SCTP_MAX_BURST, "max_burst",
&sctp_proto.max_burst, sizeof(int), 0644, NULL,
&proc_dointvec },
{ NET_SCTP_ASSOCIATION_MAX_RETRANS, "association_max_retrans",
&sctp_proto.max_retrans_association, sizeof(int), 0644, NULL,
&proc_dointvec },
{ NET_SCTP_PATH_MAX_RETRANS, "path_max_retrans",
&sctp_proto.max_retrans_path, sizeof(int), 0644, NULL,
&proc_dointvec },
{ NET_SCTP_MAX_INIT_RETRANSMITS, "max_init_retransmits",
&sctp_proto.max_retrans_init, sizeof(int), 0644, NULL,
&proc_dointvec },
{ NET_SCTP_HB_INTERVAL, "hb_interval",
&sctp_proto.hb_interval, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies },
{ NET_SCTP_RTO_ALPHA, "rto_alpha_exp_divisor",
&sctp_proto.rto_alpha, sizeof(int), 0644, NULL,
&proc_dointvec },
{ NET_SCTP_RTO_BETA, "rto_beta_exp_divisor",
&sctp_proto.rto_beta, sizeof(int), 0644, NULL,
&proc_dointvec },
{ 0 }
};
static ctl_table sctp_net_table[] = {
{ NET_SCTP, "sctp", NULL, 0, 0555, sctp_table },
{ 0 }
};
static ctl_table sctp_root_table[] = {
{ CTL_NET, "net", NULL, 0, 0555, sctp_net_table },
{ 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, 0);
}
/* Sysctl deregistration. */
void sctp_sysctl_unregister(void)
{
unregister_sysctl_table(sctp_sysctl_header);
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_transport.c,v 1.11 2002/06/20 05:57:01 samudrala Exp $
*
* This module provides the abstraction for an SCTP tranport representing
* a remote transport address. For local transport addresses, we just use
* sockaddr_storage_t.
*
* 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_transport.c,v 1.11 2002/06/20 05:57:01 samudrala Exp $";
#include <linux/types.h>
#include <net/sctp/sctp.h>
/* 1st Level Abstractions. */
/* Allocate and initialize a new transport. */
sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *addr, int priority)
{
sctp_transport_t *transport;
transport = t_new(sctp_transport_t, priority);
if (!transport)
goto fail;
if (!sctp_transport_init(transport, addr, priority))
goto fail_init;
transport->malloced = 1;
SCTP_DBG_OBJCNT_INC(transport);
return transport;
fail_init:
kfree(transport);
fail:
return NULL;
}
/* Intialize a new transport from provided memory. */
sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
const sockaddr_storage_t *addr,
int priority)
{
sctp_protocol_t *proto = sctp_get_protocol();
/* Copy in the address. */
peer->ipaddr = *addr;
peer->af_specific = sctp_get_af_specific(addr);
peer->asoc = NULL;
peer->pmtu = peer->af_specific->get_dst_mtu(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 = proto->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->state.active = 1;
peer->state.hb_allowed = 0;
/* Initialize the default path max_retrans. */
peer->max_retrans = proto->max_retrans_path;
peer->error_threshold = 0;
peer->error_count = 0;
peer->debug_name = "unnamedtransport";
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_interval = SCTP_DEFAULT_TIMEOUT_HEARTBEAT;
peer->hb_timer.function = sctp_generate_heartbeat_event;
peer->hb_timer.data = (unsigned long)peer;
atomic_set(&peer->refcnt, 1);
peer->dead = 0;
peer->malloced = 0;
return peer;
}
/* This transport is no longer needed. Free up if possible, or
* delay until it last reference count.
*/
void sctp_transport_free(sctp_transport_t *transport)
{
transport->dead = 1;
/* Try to delete the heartbeat timer. */
if (del_timer(&transport->hb_timer))
sctp_transport_put(transport);
sctp_transport_put(transport);
}
/* Destroy the transport data structure.
* Assumes there are no more users of this structure.
*/
void sctp_transport_destroy(sctp_transport_t *transport)
{
SCTP_ASSERT(transport->dead, "Transport is not dead", return);
if (transport->asoc)
sctp_association_put(transport->asoc);
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 everytime a DATA chunk is sent.
*/
void sctp_transport_reset_timers(sctp_transport_t *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,
transport->hb_interval + transport->rto + jiffies))
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(sctp_transport_t *transport,
sctp_association_t *asoc)
{
transport->asoc = asoc;
sctp_association_hold(asoc);
}
/* Hold a reference to a transport. */
void sctp_transport_hold(sctp_transport_t *transport)
{
atomic_inc(&transport->refcnt);
}
/* Release a reference to a transport and clean up
* if there are no more references.
*/
void sctp_transport_put(sctp_transport_t *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(sctp_transport_t *tp, __u32 rtt)
{
sctp_protocol_t *proto = sctp_get_protocol();
/* 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 >> proto->rto_beta)
+ ((abs(tp->srtt - rtt)) >> proto->rto_beta);
tp->srtt = tp->srtt - (tp->srtt >> proto->rto_alpha)
+ (rtt >> proto->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(__FUNCTION__ ": transport: %p, rtt: %d, srtt: %d "
"rttvar: %d, rto: %d\n",
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(sctp_transport_t *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->pmtu;
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(__FUNCTION__ ": SLOW START: transport: %p, "
"bytes_acked: %d, cwnd: %d, ssthresh: %d, "
"flight_size: %d, pba: %d\n",
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(__FUNCTION__ ": CONGESTION AVOIDANCE: "
"transport: %p, bytes_acked: %d, cwnd: %d, "
"ssthresh: %d, flight_size: %d, pba: %d\n",
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(sctp_transport_t *transport,
sctp_lower_cwnd_t reason)
{
switch (reason) {
case SCTP_LOWER_CWND_T3_RTX:
/* RFC 2960 Section 7.2.3, sctpimpguide-05 Section 2.9.2
* When the T3-rtx timer expires on an address, SCTP should
* perform slow start by:
* ssthresh = max(cwnd/2, 2*MTU)
* cwnd = 1*MTU
* partial_bytes_acked = 0
*/
transport->ssthresh = max(transport->cwnd/2,
2*transport->asoc->pmtu);
transport->cwnd = transport->asoc->pmtu;
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-05 2.9.2 Upon detection of
* packet losses from SACK (see Section 7.2.4), An endpoint
* should do the following:
* ssthresh = max(cwnd/2, 2*MTU)
* cwnd = ssthresh
* partial_bytes_acked = 0
*/
transport->ssthresh = max(transport->cwnd/2,
2*transport->asoc->pmtu);
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,
2*transport->asoc->pmtu);
transport->cwnd = transport->ssthresh;
transport->last_time_ecne_reduced = jiffies;
}
break;
case SCTP_LOWER_CWND_INACTIVE:
/* RFC 2960 Section 7.2.1, sctpimpguide-05 Section 2.14.2
* When the association does not transmit data on a given
* transport address within an RTO, the cwnd of the transport
* address should be adjusted to 2*MTU.
* 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 = 2*transport->asoc->pmtu;
break;
};
transport->partial_bytes_acked = 0;
SCTP_DEBUG_PRINTK(__FUNCTION__ ": transport: %p reason: %d cwnd: "
"%d ssthresh: %d\n", transport, reason,
transport->cwnd, transport->ssthresh);
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_tsnmap.c,v 1.8 2002/07/26 22:52:32 jgrimm Exp $
*
* 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_tsnmap.c,v 1.8 2002/07/26 22:52:32 jgrimm Exp $";
#include <linux/types.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
static void _sctp_tsnmap_update(sctp_tsnmap_t *map);
static void _sctp_tsnmap_update_pending_data(sctp_tsnmap_t *map);
static void _sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off,
__u16 len, __u16 base,
int *started, __u16 *start,
int *ended, __u16 *end);
/* Create a new sctp_tsnmap.
* Allocate room to store at least 'len' contiguous TSNs.
*/
sctp_tsnmap_t *sctp_tsnmap_new(__u16 len, __u32 initial_tsn, int priority)
{
sctp_tsnmap_t *retval;
retval = kmalloc(sizeof(sctp_tsnmap_t) +
sctp_tsnmap_storage_size(len),
priority);
if (!retval)
goto fail;
if (!sctp_tsnmap_init(retval, len, initial_tsn))
goto fail_map;
retval->malloced = 1;
return retval;
fail_map:
kfree(retval);
fail:
return NULL;
}
/* Initialize a block of memory as a tsnmap. */
sctp_tsnmap_t *sctp_tsnmap_init(sctp_tsnmap_t *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->pending_data = 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 sctp_tsnmap_t *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;
}
/* Is there a gap in the TSN map? */
int sctp_tsnmap_has_gap(const sctp_tsnmap_t *map)
{
int has_gap;
has_gap = (map->cumulative_tsn_ack_point != map->max_tsn_seen);
return has_gap;
}
/* Mark this TSN as seen. */
void sctp_tsnmap_mark(sctp_tsnmap_t *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);
}
/* Retrieve the Cumulative TSN Ack Point. */
__u32 sctp_tsnmap_get_ctsn(const sctp_tsnmap_t *map)
{
return map->cumulative_tsn_ack_point;
}
/* Retrieve the highest TSN we've seen. */
__u32 sctp_tsnmap_get_max_tsn_seen(const sctp_tsnmap_t *map)
{
return map->max_tsn_seen;
}
/* Dispose of a tsnmap. */
void sctp_tsnmap_free(sctp_tsnmap_t *map)
{
if (map->malloced)
kfree(map);
}
/* Initialize a Gap Ack Block iterator from memory being provided. */
void sctp_tsnmap_iter_init(const sctp_tsnmap_t *map, sctp_tsnmap_iter_t *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.
*/
int sctp_tsnmap_next_gap_ack(const sctp_tsnmap_t *map, sctp_tsnmap_iter_t *iter,
__u16 *start, __u16 *end)
{
int started, ended;
__u16 _start, _end, offset;
/* We haven't found a gap yet. */
started = ended = 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;
}
/********************************************************************
* 2nd Level Abstractions
********************************************************************/
/* This private helper function updates the tsnmap buffers and
* the Cumulative TSN Ack Point.
*/
static void _sctp_tsnmap_update(sctp_tsnmap_t *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. */
_sctp_tsnmap_update_pending_data(map);
}
static void _sctp_tsnmap_update_pending_data(sctp_tsnmap_t *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:
map->pending_data = 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;
/* Let's look through the entire array, but break out
* early if we have found the end of the Gap Ack Block.
*/
/* 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;
}
}
}
}
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_ulpevent.c,v 1.16 2002/08/21 18:34:04 jgrimm Exp $
*
* These functions manipulate an sctp event. The sctp_ulpevent_t 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>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_ulpevent.c,v 1.16 2002/08/21 18:34:04 jgrimm Exp $";
#include <linux/types.h>
#include <linux/skbuff.h>
#include <net/sctp/sctp_structs.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
static void sctp_rcvmsg_rfree(struct sk_buff *skb);
static void sctp_ulpevent_set_owner_r(struct sk_buff *skb,
sctp_association_t *asoc);
/* Create a new sctp_ulpevent. */
sctp_ulpevent_t *sctp_ulpevent_new(int size, int msg_flags, int priority)
{
sctp_ulpevent_t *event;
struct sk_buff *skb;
skb = alloc_skb(size, priority);
if (!skb)
goto fail;
event = (sctp_ulpevent_t *) skb->cb;
event = sctp_ulpevent_init(event, skb, msg_flags);
if (!event)
goto fail_init;
event->malloced = 1;
return event;
fail_init:
kfree_skb(event->parent);
fail:
return NULL;
}
/* Initialize an ULP event from an given skb. */
sctp_ulpevent_t *sctp_ulpevent_init(sctp_ulpevent_t *event,
struct sk_buff *parent,
int msg_flags)
{
memset(event, sizeof(sctp_ulpevent_t), 0x00);
event->msg_flags = msg_flags;
event->parent = parent;
event->malloced = 0;
return event;
}
/* Dispose of an event. */
void sctp_ulpevent_free(sctp_ulpevent_t *event)
{
if (event->malloced)
kfree_skb(event->parent);
}
/* Is this a MSG_NOTIFICATION? */
int sctp_ulpevent_is_notification(const sctp_ulpevent_t *event)
{
return event->msg_flags & MSG_NOTIFICATION;
}
/* 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.
*/
sctp_ulpevent_t *sctp_ulpevent_make_assoc_change(const sctp_association_t *asoc,
__u16 flags,
__u16 state,
__u16 error,
__u16 outbound,
__u16 inbound,
int priority)
{
sctp_ulpevent_t *event;
struct sctp_assoc_change *sac;
event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
MSG_NOTIFICATION,
priority);
if (!event)
goto fail;
sac = (struct sctp_assoc_change *)
skb_put(event->parent,
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.
*/
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.
*/
sctp_ulpevent_t *sctp_ulpevent_make_peer_addr_change(
const sctp_association_t *asoc,
const struct sockaddr_storage *aaddr,
int flags,
int state,
int error,
int priority)
{
sctp_ulpevent_t *event;
struct sctp_paddr_change *spc;
event = sctp_ulpevent_new(sizeof(struct sctp_paddr_change),
MSG_NOTIFICATION,
priority);
if (!event)
goto fail;
spc = (struct sctp_paddr_change *)
skb_put(event->parent,
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
*
* 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.
*/
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));
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.
*/
sctp_ulpevent_t *sctp_ulpevent_make_remote_error(const sctp_association_t *asoc,
sctp_chunk_t *chunk,
__u16 flags,
int priority)
{
sctp_ulpevent_t *event;
struct sctp_remote_error *sre;
struct sk_buff *skb;
sctp_errhdr_t *ch;
__u16 cause;
int elen;
ch = (sctp_errhdr_t *)(chunk->skb->data);
cause = ch->cause;
elen = 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), /* headroom */
0, /* tailroom */
priority);
/* 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_ulpevent_t *) skb->cb;
event = sctp_ulpevent_init(event,
skb,
MSG_NOTIFICATION);
if (!event)
goto fail;
event->malloced = 1;
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.
*/
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
*/
sctp_ulpevent_t *sctp_ulpevent_make_send_failed(const sctp_association_t *asoc,
sctp_chunk_t *chunk,
__u16 flags,
__u32 error,
int priority)
{
sctp_ulpevent_t *event;
struct sctp_send_failed *ssf;
struct sk_buff *skb;
/* Make skb with more room so we can prepend notification. */
skb = skb_copy_expand(chunk->skb,
sizeof(struct sctp_send_failed), /* headroom */
0, /* tailroom */
priority);
if (!skb)
goto fail;
/* Pull off the common chunk header and DATA header. */
skb_pull(skb, sizeof(sctp_data_chunk_t));
/* Embed the event fields inside the cloned skb. */
event = (sctp_ulpevent_t *) skb->cb;
event = sctp_ulpevent_init(event, skb, MSG_NOTIFICATION);
if (!event)
goto fail;
/* Mark as malloced, even though the constructor was not
* called.
*/
event->malloced = 1;
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 = skb->len;
/* 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));
/* 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.
*/
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
*/
sctp_ulpevent_t *sctp_ulpevent_make_shutdown_event(const sctp_association_t *asoc,
__u16 flags,
int priority)
{
sctp_ulpevent_t *event;
struct sctp_shutdown_event *sse;
event = sctp_ulpevent_new(sizeof(struct sctp_assoc_change),
MSG_NOTIFICATION,
priority);
if (!event)
goto fail;
sse = (struct sctp_shutdown_event *)
skb_put(event->parent,
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.
*/
sse->sse_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 - draft-01
* 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
*/
sctp_ulpevent_t *sctp_ulpevent_make_rcvmsg(sctp_association_t *asoc,
sctp_chunk_t *chunk,
int priority)
{
sctp_ulpevent_t *event;
struct sctp_sndrcvinfo *info;
struct sk_buff *skb;
size_t padding, len;
/* Clone the original skb, sharing the data. */
skb = skb_clone(chunk->skb, priority);
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);
/* Set up a destructor to do rwnd accounting. */
sctp_ulpevent_set_owner_r(skb, asoc);
/* Embed the event fields inside the cloned skb. */
event = (sctp_ulpevent_t *) skb->cb;
/* Initialize event with flags 0. */
event = sctp_ulpevent_init(event, skb, 0);
if (!event)
goto fail_init;
event->malloced = 1;
info = (struct sctp_sndrcvinfo *) &event->sndrcvinfo;
/* 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.
*/
info->sinfo_stream = ntohs(chunk->subh.data_hdr->stream);
/* Sockets API Extensions for SCTP
* Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
*
* 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).
*/
info->sinfo_ssn = ntohs(chunk->subh.data_hdr->ssn);
/* Sockets API Extensions for SCTP
* Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
*
* 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.
*/
info->sinfo_ppid = ntohl(chunk->subh.data_hdr->ppid);
/* Sockets API Extensions for SCTP
* Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
*
* 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:
*
* MSG_UNORDERED - This flag is present when the message was sent
* non-ordered.
*/
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
info->sinfo_flags |= MSG_UNORDERED;
/* FIXME: For reassembly, we need to have the fragmentation bits.
* This really does not belong in the event structure, but
* its difficult to fix everything at the same time. Eventually,
* we should create and skb based chunk structure. This structure
* storage can be converted to an event. --jgrimm
*/
event->chunk_flags = chunk->chunk_hdr->flags;
/* With -04 draft, tsn moves into sndrcvinfo. */
info->sinfo_tsn = ntohl(chunk->subh.data_hdr->tsn);
/* Context is not used on receive. */
info->sinfo_context = 0;
/* Sockets API Extensions for SCTP
* Section 5.2.2 SCTP Header Information Structure (SCTP_SNDRCV)
*
* 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 TCP-style sockets.
*/
info->sinfo_assoc_id = sctp_assoc2id(asoc);
return event;
fail_init:
kfree_skb(skb);
fail:
return NULL;
}
/* Return the notification type, assuming this is a notification
* event.
*/
__u16 sctp_ulpevent_get_notification_type(const sctp_ulpevent_t *event)
{
union sctp_notification *notification;
notification = (union sctp_notification *) event->parent->data;
return notification->h.sn_type;
}
/* Copy out the sndrcvinfo into a msghdr. */
void sctp_ulpevent_read_sndrcvinfo(const sctp_ulpevent_t *event,
struct msghdr *msghdr)
{
if (!sctp_ulpevent_is_notification(event)) {
put_cmsg(msghdr, IPPROTO_SCTP, SCTP_SNDRCV,
sizeof(struct sctp_sndrcvinfo),
(void *) &event->sndrcvinfo);
}
}
/* Do accounting for bytes just read by user. */
static void sctp_rcvmsg_rfree(struct sk_buff *skb)
{
sctp_association_t *asoc;
sctp_ulpevent_t *event;
/* 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.
*/
event = (sctp_ulpevent_t *) skb->cb;
asoc = event->asoc;
if (asoc->rwnd_over) {
if (asoc->rwnd_over >= skb->len) {
asoc->rwnd_over -= skb->len;
} else {
asoc->rwnd += (skb->len - asoc->rwnd_over);
asoc->rwnd_over = 0;
}
} else {
asoc->rwnd += skb->len;
}
SCTP_DEBUG_PRINTK("rwnd increased by %d to (%u, %u)\n",
skb->len, asoc->rwnd, asoc->rwnd_over);
sctp_association_put(asoc);
}
/* Charge receive window for bytes recieved. */
static void sctp_ulpevent_set_owner_r(struct sk_buff *skb, sctp_association_t *asoc)
{
sctp_ulpevent_t *event;
/* The 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.
* We use the local private area of the skb to track the owning
* association.
*/
sctp_association_hold(asoc);
skb->sk = asoc->base.sk;
event = (sctp_ulpevent_t *) skb->cb;
event->asoc = asoc;
skb->destructor = sctp_rcvmsg_rfree;
SCTP_ASSERT(asoc->rwnd, "rwnd zero", return);
SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);
if (asoc->rwnd >= skb->len) {
asoc->rwnd -= skb->len;
} else {
asoc->rwnd_over = skb->len - asoc->rwnd;
asoc->rwnd = 0;
}
SCTP_DEBUG_PRINTK("rwnd decreased by %d to (%u, %u)\n",
skb->len, asoc->rwnd, asoc->rwnd_over);
}
/* 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
*
* $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_ulpqueue.c,v 1.14 2002/08/21 18:34:04 jgrimm Exp $
*
* 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.
*/
static char *cvs_id __attribute__ ((unused)) = "$Id: sctp_ulpqueue.c,v 1.14 2002/08/21 18:34:04 jgrimm Exp $";
#include <linux/types.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <net/sctp/sctp_structs.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sctp_sm.h>
/* Forward declarations for internal helpers. */
static inline sctp_ulpevent_t * sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq,
sctp_ulpevent_t *event);
static inline sctp_ulpevent_t *sctp_ulpqueue_order(sctp_ulpqueue_t *ulpq,
sctp_ulpevent_t *event);
/* 1st Level Abstractions */
/* Create a new ULP queue. */
sctp_ulpqueue_t *sctp_ulpqueue_new(sctp_association_t *asoc,
__u16 inbound, int priority)
{
sctp_ulpqueue_t *ulpq;
size_t size;
/* Today, there is only a fixed size of storage needed for
* stream support, but make the interfaces acceptable for
* the future.
*/
size = sizeof(sctp_ulpqueue_t)+sctp_ulpqueue_storage_size(inbound);
ulpq = kmalloc(size, priority);
if (!ulpq)
goto fail;
if (!sctp_ulpqueue_init(ulpq, asoc, inbound))
goto fail_init;
ulpq->malloced = 1;
return ulpq;
fail_init:
kfree(ulpq);
fail:
return NULL;
}
/* Initialize a ULP queue from a block of memory. */
sctp_ulpqueue_t *sctp_ulpqueue_init(sctp_ulpqueue_t *ulpq,
sctp_association_t *asoc,
__u16 inbound)
{
memset(ulpq,
sizeof(sctp_ulpqueue_t) + sctp_ulpqueue_storage_size(inbound),
0x00);
ulpq->asoc = asoc;
spin_lock_init(&ulpq->lock);
skb_queue_head_init(&ulpq->reasm);
skb_queue_head_init(&ulpq->lobby);
ulpq->malloced = 0;
return ulpq;
}
/* Flush the reassembly and ordering queues. */
void sctp_ulpqueue_flush(sctp_ulpqueue_t *ulpq)
{
struct sk_buff *skb;
sctp_ulpevent_t *event;
while ((skb = skb_dequeue(&ulpq->lobby))) {
event = (sctp_ulpevent_t *) skb->cb;
sctp_ulpevent_free(event);
}
while ((skb = skb_dequeue(&ulpq->reasm))) {
event = (sctp_ulpevent_t *) skb->cb;
sctp_ulpevent_free(event);
}
}
/* Dispose of a ulpqueue. */
void sctp_ulpqueue_free(sctp_ulpqueue_t *ulpq)
{
sctp_ulpqueue_flush(ulpq);
if (ulpq->malloced)
kfree(ulpq);
}
/* Process an incoming DATA chunk. */
int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *ulpq, sctp_chunk_t *chunk,
int priority)
{
struct sk_buff_head temp;
sctp_data_chunk_t *hdr;
sctp_ulpevent_t *event;
hdr = (sctp_data_chunk_t *) chunk->chunk_hdr;
/* FIXME: Instead of event being the skb clone, we really should
* have a new skb based chunk structure that we can convert to
* an event. Temporarily, I'm carrying a few chunk fields in
* the event to allow reassembly. Its too painful to change
* everything at once. --jgrimm
*/
event = sctp_ulpevent_make_rcvmsg(chunk->asoc, chunk, priority);
if (!event)
return -ENOMEM;
/* Do reassembly if needed. */
event = sctp_ulpqueue_reasm(ulpq, event);
/* Do ordering if needed. */
if (event) {
/* Create a temporary list to collect chunks on. */
skb_queue_head_init(&temp);
skb_queue_tail(&temp, event->parent);
event = sctp_ulpqueue_order(ulpq, event);
}
/* Send event to the ULP. */
if (event)
sctp_ulpqueue_tail_event(ulpq, event);
return 0;
}
/* Add a new event for propogation to the ULP. */
int sctp_ulpqueue_tail_event(sctp_ulpqueue_t *ulpq, sctp_ulpevent_t *event)
{
struct sock *sk = ulpq->asoc->base.sk;
/* If the socket is just going to throw this away, do not
* even try to deliver it.
*/
if (sk->dead || (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 harvesting multiple skbs they will be
* collected on a list.
*/
if (event->parent->list)
sctp_skb_list_tail(event->parent->list, &sk->receive_queue);
else
skb_queue_tail(&sk->receive_queue, event->parent);
wake_up_interruptible(sk->sleep);
return 1;
out_free:
if (event->parent->list)
skb_queue_purge(event->parent->list);
else
kfree_skb(event->parent);
return 0;
}
/* 2nd Level Abstractions */
/* Helper function to store chunks that need to be reassembled. */
static inline void sctp_ulpqueue_store_reasm(sctp_ulpqueue_t *ulpq, sctp_ulpevent_t *event)
{
struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent;
__u32 tsn, ctsn;
unsigned long flags __attribute ((unused));
tsn = event->sndrcvinfo.sinfo_tsn;
sctp_spin_lock_irqsave(&ulpq->reasm.lock, flags);
/* Find the right place in this list. We store them by TSN. */
sctp_skb_for_each(pos, &ulpq->reasm, tmp) {
cevent = (sctp_ulpevent_t *)pos->cb;
ctsn = cevent->sndrcvinfo.sinfo_tsn;
if (TSN_lt(tsn, ctsn))
break;
}
/* If the queue is empty, we have a different function to call. */
if (skb_peek(&ulpq->reasm))
__skb_insert(event->parent, pos->prev, pos, &ulpq->reasm);
else
__skb_queue_tail(&ulpq->reasm, event->parent);
sctp_spin_unlock_irqrestore(&ulpq->reasm.lock, flags);
}
/* Helper function to return an event corresponding to the reassembled
* datagram.
*/
static inline sctp_ulpevent_t *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag)
{
struct sk_buff *pos;
sctp_ulpevent_t *event;
struct sk_buff *pnext;
pos = f_frag->next;
/* Set the first fragment's frag_list to point to the 2nd fragment. */
skb_shinfo(f_frag)->frag_list = pos;
/* Remove the first fragment from the reassembly queue. */
__skb_unlink(f_frag, f_frag->list);
do {
pnext = pos->next;
/* Remove the fragment from the reassembly queue. */
__skb_unlink(pos, pos->list);
/* Break if we have reached the last fragment. */
if (pos == l_frag)
break;
pos->next = pnext;
pos = pnext;
} while (1);
event = (sctp_ulpevent_t *) f_frag->cb;
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 sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_t *ulpq)
{
struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent;
struct sk_buff *first_frag = NULL;
__u32 ctsn, next_tsn;
unsigned long flags __attribute ((unused));
sctp_ulpevent_t *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;
sctp_spin_lock_irqsave(&ulpq->reasm.lock, flags);
/* 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.
*/
sctp_skb_for_each(pos, &ulpq->reasm, tmp) {
cevent = (sctp_ulpevent_t *) pos->cb;
ctsn = cevent->sndrcvinfo.sinfo_tsn;
switch (cevent->chunk_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))
retval = sctp_make_reassembled_event(
first_frag, pos);
else
first_frag = NULL;
break;
};
/* We have the reassembled event. There is no need to look
* further.
*/
if (retval)
break;
}
sctp_spin_unlock_irqrestore(&ulpq->reasm.lock, flags);
return retval;
}
/* Helper function to reassemble chunks. Hold chunks on the reasm queue that
* need reassembling.
*/
static inline sctp_ulpevent_t *sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq,
sctp_ulpevent_t *event)
{
sctp_ulpevent_t *retval = NULL;
/* FIXME: We should be using some new chunk structure here
* instead of carrying chunk fields in the event structure.
* This is temporary as it is too painful to change everything
* at once.
*/
/* Check if this is part of a fragmented message. */
if (SCTP_DATA_NOT_FRAG == (event->chunk_flags & SCTP_DATA_FRAG_MASK))
return event;
sctp_ulpqueue_store_reasm(ulpq, event);
retval = sctp_ulpqueue_retrieve_reassembled(ulpq);
return retval;
}
/* Helper function to gather skbs that have possibly become
* ordered by an an incoming chunk.
*/
static inline void sctp_ulpqueue_retrieve_ordered(sctp_ulpqueue_t *ulpq,
sctp_ulpevent_t *event)
{
struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent;
__u16 sid, csid;
__u16 ssn, cssn;
unsigned long flags __attribute ((unused));
sid = event->sndrcvinfo.sinfo_stream;
ssn = event->sndrcvinfo.sinfo_ssn;
/* We are holding the chunks by stream, by SSN. */
sctp_spin_lock_irqsave(&ulpq->lobby.lock, flags);
sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
cevent = (sctp_ulpevent_t *) pos->cb;
csid = cevent->sndrcvinfo.sinfo_stream;
cssn = cevent->sndrcvinfo.sinfo_ssn;
/* Have we gone too far? */
if (csid > sid)
break;
/* Have we not gone far enough? */
if (csid < sid)
continue;
if (cssn != ulpq->ssn[sid])
break;
ulpq->ssn[sid]++;
__skb_unlink(pos, pos->list);
/* Attach all gathered skbs to the event. */
__skb_queue_tail(event->parent->list, pos);
}
sctp_spin_unlock_irqrestore(&ulpq->lobby.lock, flags);
}
/* Helper function to store chunks needing ordering. */
static inline void sctp_ulpqueue_store_ordered(sctp_ulpqueue_t *ulpq,
sctp_ulpevent_t *event)
{
struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent;
__u16 sid, csid;
__u16 ssn, cssn;
unsigned long flags __attribute ((unused));
sid = event->sndrcvinfo.sinfo_stream;
ssn = event->sndrcvinfo.sinfo_ssn;
sctp_spin_lock_irqsave(&ulpq->lobby.lock, flags);
/* Find the right place in this list. We store them by
* stream ID and then by SSN.
*/
sctp_skb_for_each(pos, &ulpq->lobby, tmp) {
cevent = (sctp_ulpevent_t *) pos->cb;
csid = cevent->sndrcvinfo.sinfo_stream;
cssn = cevent->sndrcvinfo.sinfo_ssn;
if (csid > sid)
break;
if (csid == sid && SSN_lt(ssn, cssn))
break;
}
/* If the queue is empty, we have a different function to call. */
if (skb_peek(&ulpq->lobby))
__skb_insert(event->parent, pos->prev, pos, &ulpq->lobby);
else
__skb_queue_tail(&ulpq->lobby, event->parent);
sctp_spin_unlock_irqrestore(&ulpq->lobby.lock, flags);
}
static inline sctp_ulpevent_t *sctp_ulpqueue_order(sctp_ulpqueue_t *ulpq,
sctp_ulpevent_t *event)
{
__u16 sid, ssn;
/* FIXME: We should be using some new chunk structure here
* instead of carrying chunk fields in the event structure.
* This is temporary as it is too painful to change everything
* at once.
*/
/* Check if this message needs ordering. */
if (SCTP_DATA_UNORDERED & event->chunk_flags)
return event;
/* Note: The stream ID must be verified before this routine. */
sid = event->sndrcvinfo.sinfo_stream;
ssn = event->sndrcvinfo.sinfo_ssn;
/* Is this the expected SSN for this stream ID? */
if (ssn != ulpq->ssn[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_ulpqueue_store_ordered(ulpq, event);
return NULL;
}
/* Mark that the next chunk has been found. */
ulpq->ssn[sid]++;
/* Go find any other chunks that were waiting for
* ordering.
*/
sctp_ulpqueue_retrieve_ordered(ulpq, event);
return event;
}
......@@ -355,7 +355,7 @@ static struct dentry_operations sockfs_dentry_operations = {
* but we take care of internal coherence yet.
*/
static int sock_map_fd(struct socket *sock)
int sock_map_fd(struct socket *sock)
{
int fd;
struct qstr this;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment