diff options
| author | Linus Torvalds <torvalds@home.transmeta.com> | 2002-08-28 21:20:11 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@home.transmeta.com> | 2002-08-28 21:20:11 -0700 |
| commit | 20c77dc6df3db1dadba222b3ff67048a0deadb2e (patch) | |
| tree | a36b7a7ba12186aaf40c213ab4f7ccceb43354b0 | |
| parent | 8f518465d73fae2b4f750300166dfcc903908374 (diff) | |
| parent | 4f9889dcc50b60ce14c39d5ba12c9ad4205e840d (diff) | |
Merge master.kernel.org:/home/davem/BK/sctp-2.5
into home.transmeta.com:/home/torvalds/v2.5/linux
55 files changed, 24859 insertions, 14 deletions
diff --git a/Documentation/networking/sctp.txt b/Documentation/networking/sctp.txt new file mode 100644 index 000000000000..0c790a76910e --- /dev/null +++ b/Documentation/networking/sctp.txt @@ -0,0 +1,38 @@ +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> + + diff --git a/include/linux/in.h b/include/linux/in.h index f41a61dce932..eebfb0306d1b 100644 --- a/include/linux/in.h +++ b/include/linux/in.h @@ -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 diff --git a/include/linux/net.h b/include/linux/net.h index 8cd440ce36d6..c84c7033fed1 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -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); diff --git a/include/linux/sctp.h b/include/linux/sctp.h new file mode 100644 index 000000000000..b070fc1220e5 --- /dev/null +++ b/include/linux/sctp.h @@ -0,0 +1,590 @@ +/* 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__ */ diff --git a/include/linux/socket.h b/include/linux/socket.h index 74303f129fdd..f8e90303576d 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.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 diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 07eed48ea3f8..4c598d745e2e 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -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: */ diff --git a/include/net/inet_common.h b/include/net/inet_common.h index 87b3fd324347..bbb1fa70099e 100644 --- a/include/net/inet_common.h +++ b/include/net/inet_common.h @@ -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 diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 55ca37370540..1c7a736a2796 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -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 */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h new file mode 100644 index 000000000000..185e39ee064c --- /dev/null +++ b/include/net/sctp/sctp.h @@ -0,0 +1,509 @@ +/* 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__ */ diff --git a/include/net/sctp/sctp_command.h b/include/net/sctp/sctp_command.h new file mode 100644 index 000000000000..4fd50d56fefb --- /dev/null +++ b/include/net/sctp/sctp_command.h @@ -0,0 +1,213 @@ +/* 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__ */ + diff --git a/include/net/sctp/sctp_constants.h b/include/net/sctp/sctp_constants.h new file mode 100644 index 000000000000..58e910dd87da --- /dev/null +++ b/include/net/sctp/sctp_constants.h @@ -0,0 +1,453 @@ +/* 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__ */ + diff --git a/include/net/sctp/sctp_sla1.h b/include/net/sctp/sctp_sla1.h new file mode 100644 index 000000000000..9e88f6748334 --- /dev/null +++ b/include/net/sctp/sctp_sla1.h @@ -0,0 +1,80 @@ +/* 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 diff --git a/include/net/sctp/sctp_sm.h b/include/net/sctp/sctp_sm.h new file mode 100644 index 000000000000..2d1ac5826c89 --- /dev/null +++ b/include/net/sctp/sctp_sm.h @@ -0,0 +1,420 @@ +/* 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__ */ diff --git a/include/net/sctp/sctp_structs.h b/include/net/sctp/sctp_structs.h new file mode 100644 index 000000000000..bb7715795c81 --- /dev/null +++ b/include/net/sctp/sctp_structs.h @@ -0,0 +1,1543 @@ +/* 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__ */ diff --git a/include/net/sctp/sctp_tsnmap.h b/include/net/sctp/sctp_tsnmap.h new file mode 100644 index 000000000000..2d75c3fc4d61 --- /dev/null +++ b/include/net/sctp/sctp_tsnmap.h @@ -0,0 +1,161 @@ +/* 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__ */ + + + diff --git a/include/net/sctp/sctp_ulpevent.h b/include/net/sctp/sctp_ulpevent.h new file mode 100644 index 000000000000..75e8ba9efc05 --- /dev/null +++ b/include/net/sctp/sctp_ulpevent.h @@ -0,0 +1,137 @@ +/* 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__ */ + + + + + + + diff --git a/include/net/sctp/sctp_ulpqueue.h b/include/net/sctp/sctp_ulpqueue.h new file mode 100644 index 000000000000..83c1b9749659 --- /dev/null +++ b/include/net/sctp/sctp_ulpqueue.h @@ -0,0 +1,95 @@ +/* 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__ */ + + + + + + + diff --git a/include/net/sctp/sctp_user.h b/include/net/sctp/sctp_user.h new file mode 100644 index 000000000000..54eaadd802e2 --- /dev/null +++ b/include/net/sctp/sctp_user.h @@ -0,0 +1,609 @@ +/* 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__ */ + + + diff --git a/net/Config.in b/net/Config.in index 7762e7468182..6c9ee39850e6 100644 --- a/net/Config.in +++ b/net/Config.in @@ -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 diff --git a/net/Makefile b/net/Makefile index 2ad255681bf4..4fed0f516ca9 100644 --- a/net/Makefile +++ b/net/Makefile @@ -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 diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 50beeb9a9e32..2dcea8fd874c 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -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 @@ do_err: /* * 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; diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index fbe45634e28b..ee838c63969a 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -274,7 +274,7 @@ do_oom: /* 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, }; diff --git a/net/ipv6/ipv6_syms.c b/net/ipv6/ipv6_syms.c index 4b98f52e6864..e4c491b68c0c 100644 --- a/net/ipv6/ipv6_syms.c +++ b/net/ipv6/ipv6_syms.c @@ -1,5 +1,6 @@ #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); diff --git a/net/netsyms.c b/net/netsyms.c index 654411e005e0..463f6749b027 100644 --- a/net/netsyms.c +++ b/net/netsyms.c @@ -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); diff --git a/net/sctp/Config.help b/net/sctp/Config.help new file mode 100644 index 000000000000..25c1be2550cf --- /dev/null +++ b/net/sctp/Config.help @@ -0,0 +1,49 @@ +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 diff --git a/net/sctp/Config.in b/net/sctp/Config.in new file mode 100644 index 000000000000..ad21aec87db3 --- /dev/null +++ b/net/sctp/Config.in @@ -0,0 +1,20 @@ +# +# 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 diff --git a/net/sctp/Makefile b/net/sctp/Makefile new file mode 100644 index 000000000000..ff293fd2fac4 --- /dev/null +++ b/net/sctp/Makefile @@ -0,0 +1,28 @@ +# +# 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 diff --git a/net/sctp/sctp_adler32.c b/net/sctp/sctp_adler32.c new file mode 100644 index 000000000000..ebacd8e7778e --- /dev/null +++ b/net/sctp/sctp_adler32.c @@ -0,0 +1,150 @@ +/* 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; +} diff --git a/net/sctp/sctp_associola.c b/net/sctp/sctp_associola.c new file mode 100644 index 000000000000..4694f76dfc3c --- /dev/null +++ b/net/sctp/sctp_associola.c @@ -0,0 +1,1036 @@ +/* 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; +} diff --git a/net/sctp/sctp_bind_addr.c b/net/sctp/sctp_bind_addr.c new file mode 100644 index 000000000000..5cfa1af79664 --- /dev/null +++ b/net/sctp/sctp_bind_addr.c @@ -0,0 +1,533 @@ +/* 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; +} diff --git a/net/sctp/sctp_command.c b/net/sctp/sctp_command.c new file mode 100644 index 000000000000..a3f28edbcddf --- /dev/null +++ b/net/sctp/sctp_command.c @@ -0,0 +1,108 @@ +/* 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); +} diff --git a/net/sctp/sctp_crc32c.c b/net/sctp/sctp_crc32c.c new file mode 100644 index 000000000000..300de0c0b5ec --- /dev/null +++ b/net/sctp/sctp_crc32c.c @@ -0,0 +1,190 @@ +/* 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; +} + + diff --git a/net/sctp/sctp_debug.c b/net/sctp/sctp_debug.c new file mode 100644 index 000000000000..b90e0314a04f --- /dev/null +++ b/net/sctp/sctp_debug.c @@ -0,0 +1,218 @@ +/* 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"; +} diff --git a/net/sctp/sctp_endpointola.c b/net/sctp/sctp_endpointola.c new file mode 100644 index 000000000000..1c49ef90ef1e --- /dev/null +++ b/net/sctp/sctp_endpointola.c @@ -0,0 +1,372 @@ +/* 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: +} + + + + diff --git a/net/sctp/sctp_hashdriver.c b/net/sctp/sctp_hashdriver.c new file mode 100644 index 000000000000..6f2a4d00eef7 --- /dev/null +++ b/net/sctp/sctp_hashdriver.c @@ -0,0 +1,129 @@ +/* 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 */ +} + diff --git a/net/sctp/sctp_input.c b/net/sctp/sctp_input.c new file mode 100644 index 000000000000..77ec39d85ae4 --- /dev/null +++ b/net/sctp/sctp_input.c @@ -0,0 +1,665 @@ +/* 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; +} + + + + + diff --git a/net/sctp/sctp_inqueue.c b/net/sctp/sctp_inqueue.c new file mode 100644 index 000000000000..457ed52678b6 --- /dev/null +++ b/net/sctp/sctp_inqueue.c @@ -0,0 +1,202 @@ +/* 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; +} + diff --git a/net/sctp/sctp_ipv6.c b/net/sctp/sctp_ipv6.c new file mode 100644 index 000000000000..f76777e14469 --- /dev/null +++ b/net/sctp/sctp_ipv6.c @@ -0,0 +1,267 @@ +/* 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); +} diff --git a/net/sctp/sctp_objcnt.c b/net/sctp/sctp_objcnt.c new file mode 100644 index 000000000000..c4f3f1774c43 --- /dev/null +++ b/net/sctp/sctp_objcnt.c @@ -0,0 +1,136 @@ +/* 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); +} + + diff --git a/net/sctp/sctp_output.c b/net/sctp/sctp_output.c new file mode 100644 index 000000000000..3a976d544a3b --- /dev/null +++ b/net/sctp/sctp_output.c @@ -0,0 +1,561 @@ +/* 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; +} + + + + diff --git a/net/sctp/sctp_outqueue.c b/net/sctp/sctp_outqueue.c new file mode 100644 index 000000000000..62fbf282ceb5 --- /dev/null +++ b/net/sctp/sctp_outqueue.c @@ -0,0 +1,1441 @@ +/* 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; +} diff --git a/net/sctp/sctp_primitive.c b/net/sctp/sctp_primitive.c new file mode 100644 index 000000000000..71eeeaa93cf7 --- /dev/null +++ b/net/sctp/sctp_primitive.c @@ -0,0 +1,205 @@ +/* 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; +} diff --git a/net/sctp/sctp_protocol.c b/net/sctp/sctp_protocol.c new file mode 100644 index 000000000000..bf39984248b8 --- /dev/null +++ b/net/sctp/sctp_protocol.c @@ -0,0 +1,594 @@ +/* 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"); + diff --git a/net/sctp/sctp_sla1.c b/net/sctp/sctp_sla1.c new file mode 100644 index 000000000000..9151f512c263 --- /dev/null +++ b/net/sctp/sctp_sla1.c @@ -0,0 +1,284 @@ +/* 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); +} diff --git a/net/sctp/sctp_sm_make_chunk.c b/net/sctp/sctp_sm_make_chunk.c new file mode 100644 index 000000000000..51850cbf77bb --- /dev/null +++ b/net/sctp/sctp_sm_make_chunk.c @@ -0,0 +1,1765 @@ +/* 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; +} diff --git a/net/sctp/sctp_sm_sideeffect.c b/net/sctp/sctp_sm_sideeffect.c new file mode 100644 index 000000000000..e206b83675e6 --- /dev/null +++ b/net/sctp/sctp_sm_sideeffect.c @@ -0,0 +1,1170 @@ +/* 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; +} diff --git a/net/sctp/sctp_sm_statefuns.c b/net/sctp/sctp_sm_statefuns.c new file mode 100644 index 000000000000..49cf5c4aa09c --- /dev/null +++ b/net/sctp/sctp_sm_statefuns.c @@ -0,0 +1,3700 @@ +/* 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) 2002 Nokia Corp. + * + * This file is part of the SCTP kernel reference Implementation + * + * $Header: /cvsroot/lksctp/lksctp/sctp_cvs/net/sctp/sctp_sm_statefuns.c,v 1.49 2002/08/21 18:34:04 jgrimm Exp $ + * + * This is part of the SCTP Linux Kernel Reference Implementation. + * + * These are the state functions for 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 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> + * Mathew Kotowsky <kotowsky@sctp.org> + * Sridhar Samudrala <samudrala@us.ibm.com> + * Jon Grimm <jgrimm@us.ibm.com> + * Hui Huang <hui.huang@nokia.com> + * Dajiang Zhang <dajiang.zhang@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_statefuns.c,v 1.49 2002/08/21 18:34:04 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 <net/inet_ecn.h> +#include <linux/skbuff.h> +#include <net/sctp/sctp.h> +#include <net/sctp/sctp_sm.h> +#include <net/sctp/sctp_structs.h> + +/********************************************************** + * These are the state functions for handling chunk events. + **********************************************************/ + +/* + * Process the final SHUTDOWN COMPLETE. + * + * Section: 4 (C) (diagram), 9.2 + * Upon reception of the SHUTDOWN COMPLETE chunk the endpoint will verify + * that it is in SHUTDOWN-ACK-SENT state, if it is not the chunk should be + * discarded. If the endpoint is in the SHUTDOWN-ACK-SENT state the endpoint + * should stop the T2-shutdown timer and remove all knowledge of the + * association (and thus the association enters the CLOSED state). + * + * Verification Tag: 8.5.1(C) + * C) Rules for packet carrying SHUTDOWN COMPLETE: + * ... + * - The receiver of a SHUTDOWN COMPLETE shall accept the packet if the + * Verification Tag field of the packet matches its own tag OR it is + * set to its peer's tag and the T bit is set in the Chunk Flags. + * Otherwise, the receiver MUST silently discard the packet and take + * no further action. An endpoint MUST ignore the SHUTDOWN COMPLETE if + * it is not in the SHUTDOWN-ACK-SENT state. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_4_C(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_ulpevent_t *ev; + + /* RFC 2960 6.10 Bundling + * + * An endpoint MUST NOT bundle INIT, INIT ACK or + * SHUTDOWN COMPLETE with any other chunks. + */ + if (!chunk->singleton) + return SCTP_DISPOSITION_VIOLATION; + + /* RFC 2960 8.5.1 Exceptions in Verification Tag Rules + * + * (C) The receiver of a SHUTDOWN COMPLETE shall accept the + * packet if the Verification Tag field of the packet + * matches its own tag OR it is set to its peer's tag and + * the T bit is set in the Chunk Flags. Otherwise, the + * receiver MUST silently discard the packet and take no + * further action.... + */ + if ((ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) && + !(sctp_test_T_bit(chunk) || + (ntohl(chunk->sctp_hdr->vtag) != asoc->peer.i.init_tag))) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* RFC 2960 10.2 SCTP-to-ULP + * + * H) SHUTDOWN COMPLETE notification + * + * When SCTP completes the shutdown procedures (section 9.2) this + * notification is passed to the upper layer. + */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP, + 0, 0, 0, GFP_ATOMIC); + if (!ev) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + + /* Upon reception of the SHUTDOWN COMPLETE chunk the endpoint + * will verify that it is in SHUTDOWN-ACK-SENT state, if it is + * not the chunk should be discarded. If the endpoint is in + * the SHUTDOWN-ACK-SENT state the endpoint should stop the + * T2-shutdown timer and remove all knowledge of the + * association (and thus the association enters the CLOSED + * state). + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + return SCTP_DISPOSITION_DELETE_TCB; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Discard the whole packet. + * + * Section: 8.4 2) + * + * 2) If the OOTB packet contains an ABORT chunk, the receiver MUST + * silently discard the OOTB packet and take no further action. + * Otherwise, + * + * Verification Tag: No verification necessary + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_pdiscard(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Respond to a normal INIT chunk. + * We are the side that is being asked for an association. + * + * Section: 5.1 Normal Establishment of an Association, B + * B) "Z" shall respond immediately with an INIT ACK chunk. The + * destination IP address of the INIT ACK MUST be set to the source + * IP address of the INIT to which this INIT ACK is responding. In + * the response, besides filling in other parameters, "Z" must set the + * Verification Tag field to Tag_A, and also provide its own + * Verification Tag (Tag_Z) in the Initiate Tag field. + * + * Verification Tag: No checking. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_chunk_t *repl; + sctp_association_t *new_asoc; + + /* If the packet is an OOTB packet which is temporarily on the + * control endpoint, responding with an ABORT. + */ + if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) + return sctp_sf_ootb(ep, asoc, type, arg, commands); + + /* 6.10 Bundling + * An endpoint MUST NOT bundle INIT, INIT ACK or + * SHUTDOWN COMPLETE with any other chunks. + */ + if (!chunk->singleton) + return SCTP_DISPOSITION_VIOLATION; + + /* Grab the INIT header. */ + chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data; + + /* Tag the variable length parameters. */ + chunk->param_hdr.v = + skb_pull(chunk->skb, sizeof(sctp_inithdr_t)); + + new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC); + if (!new_asoc) + goto nomem; + + /* FIXME: sctp_process_init can fail, but there is no + * status nor handling. + */ + sctp_process_init(new_asoc, chunk->chunk_hdr->type, + sctp_source(chunk), + (sctp_init_chunk_t *)chunk->chunk_hdr, + GFP_ATOMIC); + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); + + /* B) "Z" shall respond immediately with an INIT ACK chunk. */ + repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC); + if (!repl) + goto nomem_ack; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* + * Note: After sending out INIT ACK with the State Cookie parameter, + * "Z" MUST NOT allocate any resources, nor keep any states for the + * new association. Otherwise, "Z" will be vulnerable to resource + * attacks. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + return SCTP_DISPOSITION_DELETE_TCB; + +nomem_ack: + sctp_association_free(new_asoc); +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Respond to a normal INIT ACK chunk. + * We are the side that is initiating the association. + * + * Section: 5.1 Normal Establishment of an Association, C + * C) Upon reception of the INIT ACK from "Z", "A" shall stop the T1-init + * timer and leave COOKIE-WAIT state. "A" shall then send the State + * Cookie received in the INIT ACK chunk in a COOKIE ECHO chunk, start + * the T1-cookie timer, and enter the COOKIE-ECHOED state. + * + * Note: The COOKIE ECHO chunk can be bundled with any pending outbound + * DATA chunks, but it MUST be the first chunk in the packet and + * until the COOKIE ACK is returned the sender MUST NOT send any + * other packets to the peer. + * + * Verification Tag: 3.3.3 + * If the value of the Initiate Tag in a received INIT ACK chunk is + * found to be 0, the receiver MUST treat it as an error and close the + * association by transmitting an ABORT. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_1C_ack(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_init_chunk_t *initchunk; + __u32 init_tag; + + /* 6.10 Bundling + * An endpoint MUST NOT bundle INIT, INIT ACK or + * SHUTDOWN COMPLETE with any other chunks. + */ + if (!chunk->singleton) + return SCTP_DISPOSITION_VIOLATION; + + /* Grab the INIT header. */ + chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; + + init_tag = ntohl(chunk->subh.init_hdr->init_tag); + + /* Verification Tag: 3.3.3 + * If the value of the Initiate Tag in a received INIT ACK + * chunk is found to be 0, the receiver MUST treat it as an + * error and close the association by transmitting an ABORT. + */ + if (!init_tag) { + sctp_chunk_t *reply = sctp_make_abort(asoc, chunk, 0); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(reply)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, + SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + } + + /* Tag the variable length paramters. Note that we never + * convert the parameters in an INIT chunk. + */ + chunk->param_hdr.v = + skb_pull(chunk->skb, sizeof(sctp_inithdr_t)); + + initchunk = (sctp_init_chunk_t *) chunk->chunk_hdr; + + sctp_add_cmd_sf(commands, SCTP_CMD_PEER_INIT, + SCTP_PEER_INIT(initchunk)); + + /* 5.1 C) "A" shall stop the T1-init timer and leave + * COOKIE-WAIT state. "A" shall then ... start the T1-cookie + * timer, and enter the COOKIE-ECHOED state. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_RESET, + SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR)); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_COOKIE_ECHOED)); + + /* 5.1 C) "A" shall then send the State Cookie received in the + * INIT ACK chunk in a COOKIE ECHO chunk, ... + */ + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_COOKIE_ECHO, SCTP_NULL()); + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Respond to a normal COOKIE ECHO chunk. + * We are the side that is being asked for an association. + * + * Section: 5.1 Normal Establishment of an Association, D + * D) Upon reception of the COOKIE ECHO chunk, Endpoint "Z" will reply + * with a COOKIE ACK chunk after building a TCB and moving to + * the ESTABLISHED state. A COOKIE ACK chunk may be bundled with + * any pending DATA chunks (and/or SACK chunks), but the COOKIE ACK + * chunk MUST be the first chunk in the packet. + * + * IMPLEMENTATION NOTE: An implementation may choose to send the + * Communication Up notification to the SCTP user upon reception + * of a valid COOKIE ECHO chunk. + * + * Verification Tag: 8.5.1 Exceptions in Verification Tag Rules + * D) Rules for packet carrying a COOKIE ECHO + * + * - When sending a COOKIE ECHO, the endpoint MUST use the value of the + * Initial Tag received in the INIT ACK. + * + * - The receiver of a COOKIE ECHO follows the procedures in Section 5. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_association_t *new_asoc; + sctp_init_chunk_t *peer_init; + sctp_chunk_t *repl; + sctp_ulpevent_t *ev; + int error = 0; + + /* If the packet is an OOTB packet which is temporarily on the + * control endpoint, responding with an ABORT. + */ + if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) + return sctp_sf_ootb(ep, asoc, type, arg, commands); + + /* "Decode" the chunk. We have no optional parameters so we + * are in good shape. + */ + chunk->subh.cookie_hdr = + (sctp_signed_cookie_t *)chunk->skb->data; + skb_pull(chunk->skb, + ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t)); + + /* 5.1 D) Upon reception of the COOKIE ECHO chunk, Endpoint + * "Z" will reply with a COOKIE ACK chunk after building a TCB + * and moving to the ESTABLISHED state. + */ + new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error); + + /* FIXME: + * If the re-build failed, what is the proper error path + * from here? + * + * [We should abort the association. --piggy] + */ + if (!new_asoc) { + /* FIXME: Several errors are possible. A bad cookie should + * be silently discarded, but think about logging it too. + */ + switch (error) { + case -SCTP_IERROR_NOMEM: + goto nomem; + + case -SCTP_IERROR_BAD_SIG: + default: + return sctp_sf_pdiscard(ep, asoc, type, + arg, commands); + }; + } + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_ESTABLISHED)); + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); + + if (new_asoc->autoclose) + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + /* Re-build the bind address for the association is done in + * the sctp_unpack_cookie() already. + */ + /* This is a brand-new association, so these are not yet side + * effects--it is safe to run them here. + */ + peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; + sctp_process_init(new_asoc, chunk->chunk_hdr->type, + &chunk->subh.cookie_hdr->c.peer_addr, peer_init, + GFP_ATOMIC); + + repl = sctp_make_cookie_ack(new_asoc, chunk); + if (!repl) + goto nomem_repl; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * D) IMPLEMENTATION NOTE: An implementation may choose to + * send the Communication Up notification to the SCTP user + * upon reception of a valid COOKIE ECHO chunk. + */ + ev = sctp_ulpevent_make_assoc_change(new_asoc, 0, SCTP_COMM_UP, 0, + new_asoc->c.sinit_num_ostreams, + new_asoc->c.sinit_max_instreams, + GFP_ATOMIC); + if (!ev) + goto nomem_ev; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + + return SCTP_DISPOSITION_CONSUME; + +nomem_ev: + sctp_free_chunk(repl); + +nomem_repl: + sctp_association_free(new_asoc); + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Respond to a normal COOKIE ACK chunk. + * We are the side that is being asked for an association. + * + * RFC 2960 5.1 Normal Establishment of an Association + * + * E) Upon reception of the COOKIE ACK, endpoint "A" will move from the + * COOKIE-ECHOED state to the ESTABLISHED state, stopping the T1-cookie + * timer. It may also notify its ULP about the successful + * establishment of the association with a Communication Up + * notification (see Section 10). + * + * Verification Tag: + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_1E_ca(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_ulpevent_t *ev; + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * E) Upon reception of the COOKIE ACK, endpoint "A" will move + * from the COOKIE-ECHOED state to the ESTABLISHED state, + * stopping the T1-cookie timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_COOKIE)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_ESTABLISHED)); + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); + if (asoc->autoclose) + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + /* It may also notify its ULP about the successful + * establishment of the association with a Communication Up + * notification (see Section 10). + */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, + 0, asoc->c.sinit_num_ostreams, + asoc->c.sinit_max_instreams, + GFP_ATOMIC); + + if (!ev) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* Generate a HEARTBEAT packet on the given transport. */ +sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_transport_t *transport = (sctp_transport_t *) arg; + sctp_chunk_t *reply; + sctp_sender_hb_info_t hbinfo; + size_t paylen = 0; + + if (asoc->overall_error_count >= asoc->overall_error_threshold) { + /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + } + + /* Section 3.3.5. + * 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). + */ + + hbinfo.param_hdr.type = SCTP_PARAM_HEATBEAT_INFO; + hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); + hbinfo.daddr = transport->ipaddr; + hbinfo.sent_at = jiffies; + + /* Set rto_pending indicating that an RTT measurement is started + * with this heartbeat chunk. + */ + transport->rto_pending = 1; + + /* Send a heartbeat to our peer. */ + paylen = sizeof(sctp_sender_hb_info_t); + reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(reply)); + + /* Set transport error counter and association error counter + * when sending heartbeat. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET, + SCTP_TRANSPORT(transport)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process an heartbeat request. + * + * Section: 8.3 Path Heartbeat + * The receiver of the HEARTBEAT should immediately respond with a + * HEARTBEAT ACK that contains the Heartbeat Information field copied + * from the received HEARTBEAT chunk. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * When receiving an SCTP packet, the endpoint MUST ensure that the + * value in the Verification Tag field of the received SCTP packet + * matches its own Tag. If the received Verification Tag value does not + * match the receiver's own tag value, the receiver shall silently + * discard the packet and shall not process it any further except for + * those cases listed in Section 8.5.1 below. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_beat_8_3(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_chunk_t *reply; + size_t paylen = 0; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* 8.3 The receiver of the HEARTBEAT should immediately + * respond with a HEARTBEAT ACK that contains the Heartbeat + * Information field copied from the received HEARTBEAT chunk. + */ + chunk->subh.hb_hdr = (sctp_heartbeathdr_t *) chunk->skb->data; + paylen = ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t); + skb_pull(chunk->skb, paylen); + + reply = sctp_make_heartbeat_ack(asoc, chunk, + chunk->subh.hb_hdr, paylen); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process the returning HEARTBEAT ACK. + * + * Section: 8.3 Path Heartbeat + * 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, and mark the destination + * transport address as active if it is not so marked. The endpoint may + * optionally report to the upper layer when an inactive destination + * address is marked as active due to the reception of the latest + * HEARTBEAT ACK. The receiver of the HEARTBEAT ACK must also + * clear the association overall error count as well (as defined + * in section 8.1). + * + * 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. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sockaddr_storage_t from_addr; + sctp_transport_t *link; + sctp_sender_hb_info_t *hbinfo; + unsigned long max_interval; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. ... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; + from_addr = hbinfo->daddr; + link = sctp_assoc_lookup_paddr(asoc, &from_addr); + + /* This should never happen, but lets log it if so. */ + if (!link) { + printk(KERN_WARNING __FUNCTION__ + ": Could not find address %d.%d.%d.%d\n", + NIPQUAD(from_addr.v4.sin_addr)); + return SCTP_DISPOSITION_DISCARD; + } + + max_interval = link->hb_interval + link->rto; + + /* Check if the timestamp looks valid. */ + if (time_after(hbinfo->sent_at, jiffies) || + time_after(jiffies, hbinfo->sent_at + max_interval)) { + SCTP_DEBUG_PRINTK("%s: HEARTBEAT ACK with invalid timestamp + received for transport: %p\n", + __FUNCTION__, link); + return SCTP_DISPOSITION_DISCARD; + } + + /* 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 and mark the destination transport address as active if + * it is not so marked. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_ON, + SCTP_TRANSPORT(link)); + + return SCTP_DISPOSITION_CONSUME; +} + +/* Populate the verification/tie tags based on overlapping INIT + * scenario. + * + * Note: Do not use in CLOSED or SHUTDOWN-ACK-SENT state. + */ +static void sctp_tietags_populate(sctp_association_t *new_asoc, + const sctp_association_t *asoc) +{ + switch (asoc->state) { + + /* 5.2.1 INIT received in COOKIE-WAIT or COOKIE-ECHOED State */ + + case SCTP_STATE_COOKIE_WAIT: + new_asoc->c.my_vtag = asoc->c.my_vtag; + new_asoc->c.my_ttag = asoc->c.my_vtag; + new_asoc->c.peer_ttag = 0; + break; + + case SCTP_STATE_COOKIE_ECHOED: + new_asoc->c.my_vtag = asoc->c.my_vtag; + new_asoc->c.my_ttag = asoc->c.my_vtag; + new_asoc->c.peer_ttag = asoc->c.peer_vtag; + break; + + /* 5.2.2 Unexpected INIT in States Other than CLOSED, COOKIE-ECHOED, + * COOKIE-WAIT and SHUTDOWN-ACK-SENT + */ + default: + new_asoc->c.my_ttag = asoc->c.my_vtag; + new_asoc->c.peer_ttag = asoc->c.peer_vtag; + break; + }; + + /* Other parameters for the endpoint SHOULD be copied from the + * existing parameters of the association (e.g. number of + * outbound streams) into the INIT ACK and cookie. + */ + new_asoc->rwnd = asoc->rwnd; + new_asoc->c.sinit_num_ostreams = asoc->c.sinit_num_ostreams; + new_asoc->c.sinit_max_instreams = asoc->c.sinit_max_instreams; + new_asoc->c.initial_tsn = asoc->c.initial_tsn; +} + +/* + * Compare vtag/tietag values to determine unexpected COOKIE-ECHO + * handling action. + * + * RFC 2960 5.2.4 Handle a COOKIE ECHO when a TCB exists. + * + * Returns value representing action to be taken. These action values + * correspond to Action/Description values in RFC 2960, Table 2. + */ +static char sctp_tietags_compare(sctp_association_t *new_asoc, + const sctp_association_t *asoc) +{ + /* In this case, the peer may have restarted. */ + if ((asoc->c.my_vtag != new_asoc->c.my_vtag) && + (asoc->c.peer_vtag != new_asoc->c.peer_vtag) && + (asoc->c.my_vtag == new_asoc->c.my_ttag) && + (asoc->c.peer_vtag == new_asoc->c.peer_ttag)) + return 'A'; + + /* Collision case D. + * Note: Test case D first, otherwise it may be incorrectly + * identified as second case of B if the value of the Tie_tag is + * not filled into the state cookie. + */ + if ((asoc->c.my_vtag == new_asoc->c.my_vtag) && + (asoc->c.peer_vtag == new_asoc->c.peer_vtag)) + return 'D'; + + /* Collision case B. */ + if ((asoc->c.my_vtag == new_asoc->c.my_vtag) && + ((asoc->c.peer_vtag != new_asoc->c.peer_vtag) || + (!new_asoc->c.my_ttag && !new_asoc->c.peer_ttag))) + return 'B'; + + /* Collision case C. */ + if ((asoc->c.my_vtag != new_asoc->c.my_vtag) && + (asoc->c.peer_vtag == new_asoc->c.peer_vtag) && + (0 == new_asoc->c.my_ttag) && + (0 == new_asoc->c.peer_ttag)) + return 'C'; + + return 'E'; /* No such case available. */ +} + +/* Common helper routine for both duplicate and simulataneous INIT + * chunk handling. + */ +static sctp_disposition_t sctp_sf_do_unexpected_init(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, const sctp_subtype_t type, + void *arg, sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_chunk_t *repl; + sctp_association_t *new_asoc; + + /* 6.10 Bundling + * An endpoint MUST NOT bundle INIT, INIT ACK or + * SHUTDOWN COMPLETE with any other chunks. + */ + if (!chunk->singleton) + return SCTP_DISPOSITION_VIOLATION; + + /* Grab the INIT header. */ + chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; + + /* Tag the variable length parameters. */ + chunk->param_hdr.v = + skb_pull(chunk->skb, sizeof(sctp_inithdr_t)); + + /* + * Other parameters for the endpoint SHOULD be copied from the + * existing parameters of the association (e.g. number of + * outbound streams) into the INIT ACK and cookie. + * FIXME: We are copying parameters from the endpoint not the + * association. + */ + new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC); + if (!new_asoc) + goto nomem; + + /* In the outbound INIT ACK the endpoint MUST copy its current + * Verification Tag and Peers Verification tag into a reserved + * place (local tie-tag and per tie-tag) within the state cookie. + */ + sctp_process_init(new_asoc, chunk->chunk_hdr->type, + sctp_source(chunk), + (sctp_init_chunk_t *)chunk->chunk_hdr, + GFP_ATOMIC); + sctp_tietags_populate(new_asoc, asoc); + + /* B) "Z" shall respond immediately with an INIT ACK chunk. */ + repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC); + if (!repl) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* + * Note: After sending out INIT ACK with the State Cookie parameter, + * "Z" MUST NOT allocate any resources for this new association. + * Otherwise, "Z" will be vulnerable to resource attacks. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Handle simultanous INIT. + * This means we started an INIT and then we got an INIT request from + * our peer. + * + * Section: 5.2.1 INIT received in COOKIE-WAIT or COOKIE-ECHOED State (Item B) + * This usually indicates an initialization collision, i.e., each + * endpoint is attempting, at about the same time, to establish an + * association with the other endpoint. + * + * Upon receipt of an INIT in the COOKIE-WAIT or COOKIE-ECHOED state, an + * endpoint MUST respond with an INIT ACK using the same parameters it + * sent in its original INIT chunk (including its Verification Tag, + * unchanged). These original parameters are combined with those from the + * newly received INIT chunk. The endpoint shall also generate a State + * Cookie with the INIT ACK. The endpoint uses the parameters sent in its + * INIT to calculate the State Cookie. + * + * After that, the endpoint MUST NOT change its state, the T1-init + * timer shall be left running and the corresponding TCB MUST NOT be + * destroyed. The normal procedures for handling State Cookies when + * a TCB exists will resolve the duplicate INITs to a single association. + * + * For an endpoint that is in the COOKIE-ECHOED state it MUST populate + * its Tie-Tags with the Tag information of itself and its peer (see + * section 5.2.2 for a description of the Tie-Tags). + * + * Verification Tag: Not explicit, but an INIT can not have a valid + * verification tag, so we skip the check. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_2_1_siminit(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Call helper to do the real work for both simulataneous and + * duplicate INIT chunk handling. + */ + return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands); +} + +/* + * Handle duplicated INIT messages. These are usually delayed + * restransmissions. + * + * Section: 5.2.2 Unexpected INIT in States Other than CLOSED, + * COOKIE-ECHOED and COOKIE-WAIT + * + * Unless otherwise stated, upon reception of an unexpected INIT for + * this association, the endpoint shall generate an INIT ACK with a + * State Cookie. In the outbound INIT ACK the endpoint MUST copy its + * current Verification Tag and peer's Verification Tag into a reserved + * place within the state cookie. We shall refer to these locations as + * the Peer's-Tie-Tag and the Local-Tie-Tag. The outbound SCTP packet + * containing this INIT ACK MUST carry a Verification Tag value equal to + * the Initiation Tag found in the unexpected INIT. And the INIT ACK + * MUST contain a new Initiation Tag (randomly generated see Section + * 5.3.1). Other parameters for the endpoint SHOULD be copied from the + * existing parameters of the association (e.g. number of outbound + * streams) into the INIT ACK and cookie. + * + * After sending out the INIT ACK, the endpoint shall take no further + * actions, i.e., the existing association, including its current state, + * and the corresponding TCB MUST NOT be changed. + * + * Note: Only when a TCB exists and the association is not in a COOKIE- + * WAIT state are the Tie-Tags populated. For a normal association INIT + * (i.e. the endpoint is in a COOKIE-WAIT state), the Tie-Tags MUST be + * set to 0 (indicating that no previous TCB existed). The INIT ACK and + * State Cookie are populated as specified in section 5.2.1. + * + * Verification Tag: Not specifed, but an INIT has no way of knowing + * what the verification tag could be, so we ignore it. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Call helper to do the real work for both simulataneous and + * duplicate INIT chunk handling. + */ + return sctp_sf_do_unexpected_init(ep, asoc, type, arg, commands); +} + +/* Unexpected COOKIE-ECHO handlerfor peer restart (Table 2, action 'A') + * + * Section 5.2.4 + * A) In this case, the peer may have restarted. + */ +static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + sctp_association_t *new_asoc) +{ + sctp_init_chunk_t *peer_init; + sctp_ulpevent_t *ev; + sctp_chunk_t *repl; + sctp_transport_t *new_addr, *addr; + list_t *pos, *pos2, *temp; + int found, error; + + /* new_asoc is a brand-new association, so these are not yet + * side effects--it is safe to run them here. + */ + peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; + sctp_process_init(new_asoc, chunk->chunk_hdr->type, + sctp_source(chunk), peer_init, GFP_ATOMIC); + + /* Make sure peer is not adding new addresses. */ + found = 0; + new_addr = NULL; + list_for_each(pos, &new_asoc->peer.transport_addr_list) { + new_addr = list_entry(pos, sctp_transport_t, transports); + found = 1; + list_for_each_safe(pos2, temp, + &asoc->peer.transport_addr_list) { + addr = list_entry(pos2, sctp_transport_t, transports); + if (!sctp_cmp_addr_exact(&new_addr->ipaddr, + &addr->ipaddr)) { + found = 0; + break; + } + } + if (!found) + break; + } + + if (!found) { + sctp_bind_addr_t *bp; + sctpParam_t rawaddr; + int len; + + bp = sctp_bind_addr_new(GFP_ATOMIC); + if (!bp) + goto nomem; + + error = sctp_add_bind_addr(bp, &new_addr->ipaddr, GFP_ATOMIC); + if (error) + goto nomem_add; + + rawaddr = sctp_bind_addrs_to_raw(bp, &len, GFP_ATOMIC); + if (!rawaddr.v) + goto nomem_raw; + + repl = sctp_make_abort(asoc, chunk, len+sizeof(sctp_errhdr_t)); + if (!repl) + goto nomem_abort; + sctp_init_cause(repl, SCTP_ERROR_RESTART, rawaddr.v, len); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + return SCTP_DISPOSITION_CONSUME; + + nomem_abort: + kfree(rawaddr.v); + + nomem_raw: + nomem_add: + sctp_bind_addr_free(bp); + goto nomem; + } + + /* For now, fail any unsent/unacked data. Consider the optional + * choice of resending of this data. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_PURGE_OUTQUEUE, SCTP_NULL()); + + /* Update the content of current association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc)); + + repl = sctp_make_cookie_ack(new_asoc, chunk); + if (!repl) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* Report association restart to upper layer. */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_RESTART, 0, + new_asoc->c.sinit_num_ostreams, + new_asoc->c.sinit_max_instreams, + GFP_ATOMIC); + if (!ev) + goto nomem_ev; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + return SCTP_DISPOSITION_CONSUME; + +nomem_ev: + sctp_free_chunk(repl); + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* Unexpected COOKIE-ECHO handler for setup collision (Table 2, action 'B') + * + * Section 5.2.4 + * B) In this case, both sides may be attempting to start an association + * at about the same time but the peer endpoint started its INIT + * after responding to the local endpoint's INIT + */ +/* This case represents an intialization collision. */ +static sctp_disposition_t sctp_sf_do_dupcook_b(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + sctp_association_t *new_asoc) +{ + sctp_init_chunk_t *peer_init; + sctp_ulpevent_t *ev; + sctp_chunk_t *repl; + + /* new_asoc is a brand-new association, so these are not yet + * side effects--it is safe to run them here. + */ + peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; + sctp_process_init(new_asoc, chunk->chunk_hdr->type, + sctp_source(chunk), peer_init, GFP_ATOMIC); + + /* Update the content of current association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc)); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_ESTABLISHED)); + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, SCTP_NULL()); + + repl = sctp_make_cookie_ack(new_asoc, chunk); + if (!repl) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * D) IMPLEMENTATION NOTE: An implementation may choose to + * send the Communication Up notification to the SCTP user + * upon reception of a valid COOKIE ECHO chunk. + */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_UP, 0, + new_asoc->c.sinit_num_ostreams, + new_asoc->c.sinit_max_instreams, + GFP_ATOMIC); + if (!ev) + goto nomem_ev; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + return SCTP_DISPOSITION_CONSUME; + +nomem_ev: + sctp_free_chunk(repl); +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* Unexpected COOKIE-ECHO handler for setup collision (Table 2, action 'C') + * + * Section 5.2.4 + * C) In this case, the local endpoint's cookie has arrived late. + * Before it arrived, the local endpoint sent an INIT and received an + * INIT-ACK and finally sent a COOKIE ECHO with the peer's same tag + * but a new tag of its own. + */ +/* This case represents an intialization collision. */ +static sctp_disposition_t sctp_sf_do_dupcook_c(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + sctp_association_t *new_asoc) +{ + /* The cookie should be silently discarded. + * The endpoint SHOULD NOT change states and should leave + * any timers running. + */ + return SCTP_DISPOSITION_DISCARD; +} + +/* Unexpected COOKIE-ECHO handler lost chunk (Table 2, action 'D') + * + * Section 5.2.4 + * + * D) When both local and remote tags match the endpoint should always + * enter the ESTABLISHED state, if it has not already done so. + */ +/* This case represents an intialization collision. */ +static sctp_disposition_t sctp_sf_do_dupcook_d(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + sctp_chunk_t *chunk, + sctp_cmd_seq_t *commands, + sctp_association_t *new_asoc) +{ + sctp_ulpevent_t *ev = NULL; + sctp_chunk_t *repl; + + /* The local endpoint cannot use any value from the received + * state cookie and need to immediately resend a COOKIE-ACK + * and move into ESTABLISHED if it hasn't done so. + */ + if (SCTP_STATE_ESTABLISHED != asoc->state) { + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_ESTABLISHED)); + sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_START, + SCTP_NULL()); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * D) IMPLEMENTATION NOTE: An implementation may choose + * to send the Communication Up notification to the + * SCTP user upon reception of a valid COOKIE + * ECHO chunk. + */ + ev = sctp_ulpevent_make_assoc_change(new_asoc, 0, + SCTP_COMM_UP, 0, + new_asoc->c.sinit_num_ostreams, + new_asoc->c.sinit_max_instreams, + GFP_ATOMIC); + if (!ev) + goto nomem; + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(ev)); + } + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + + repl = sctp_make_cookie_ack(new_asoc, chunk); + if (!repl) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + sctp_add_cmd_sf(commands, SCTP_CMD_TRANSMIT, SCTP_NULL()); + return SCTP_DISPOSITION_CONSUME; + +nomem: + if (ev) + sctp_ulpevent_free(ev); + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Handle a duplicate COOKIE-ECHO. This usually means a cookie-carrying + * chunk was retransmitted and then delayed in the network. + * + * Section: 5.2.4 Handle a COOKIE ECHO when a TCB exists + * + * Verification Tag: None. Do cookie validation. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_disposition_t retval; + sctp_chunk_t *chunk = arg; + sctp_association_t *new_asoc; + int error = 0; + char action; + + /* "Decode" the chunk. We have no optional parameters so we + * are in good shape. + */ + chunk->subh.cookie_hdr = + (sctp_signed_cookie_t *) chunk->skb->data; + skb_pull(chunk->skb, + ntohs(chunk->chunk_hdr->length) - sizeof(sctp_chunkhdr_t)); + + /* In RFC 2960 5.2.4 3, if both Verification Tags in the State Cookie + * of a duplicate COOKIE ECHO match the Verification Tags of the + * current association, consider the State Cookie valid even if + * the lifespan is exceeded. + */ + new_asoc = sctp_unpack_cookie(ep, asoc, chunk, GFP_ATOMIC, &error); + + /* FIXME: + * If the re-build failed, what is the proper error path + * from here? + * + * [We should abort the association. --piggy] + */ + if (!new_asoc) { + /* FIXME: Several errors are possible. A bad cookie should + * be silently discarded, but think about logging it too. + */ + switch (error) { + case -SCTP_IERROR_NOMEM: + goto nomem; + + case -SCTP_IERROR_BAD_SIG: + default: + return sctp_sf_pdiscard(ep, asoc, type, + arg, commands); + }; + } + + /* Compare the tie_tag in cookie with the verification tag of + * current association. + */ + action = sctp_tietags_compare(new_asoc, asoc); + + switch (action) { + case 'A': /* Association restart. */ + retval = sctp_sf_do_dupcook_a(ep, asoc, chunk, commands, + new_asoc); + break; + + case 'B': /* Collision case B. */ + retval = sctp_sf_do_dupcook_b(ep, asoc, chunk, commands, + new_asoc); + break; + + case 'C': /* Collisioun case C. */ + retval = sctp_sf_do_dupcook_c(ep, asoc, chunk, commands, + new_asoc); + break; + + case 'D': /* Collision case D. */ + retval = sctp_sf_do_dupcook_d(ep, asoc, chunk, commands, + new_asoc); + break; + + default: /* No such case, discard it. */ + printk(KERN_WARNING __FUNCTION__ ":unknown case\n"); + retval = SCTP_DISPOSITION_DISCARD; + break; + }; + + /* Delete the tempory new association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + return retval; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +#if 0 +/* + * Handle a Stale COOKIE Error + * + * Section: 5.2.6 Handle Stale COOKIE Error + * If the association is in the COOKIE-ECHOED state, the endpoint may elect + * one of the following three alternatives. + * ... + * 3) Send a new INIT chunk to the endpoint, adding a Cookie + * Preservative parameter requesting an extension to the lifetime of + * the State Cookie. When calculating the time extension, an + * implementation SHOULD use the RTT information measured based on the + * previous COOKIE ECHO / ERROR exchange, and should add no more + * than 1 second beyond the measured RTT, due to long State Cookie + * lifetimes making the endpoint more subject to a replay attack. + * + * Verification Tag: Not explicit, but safe to ignore. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t do_5_2_6_stale(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + /* This is not a real chunk type. It is a subtype of the + * ERROR chunk type. The ERROR chunk processing will bring us + * here. + */ + sctp_chunk_t *in_packet; + stp_chunk_t *reply; + sctp_inithdr_t initack; + __u8 *addrs; + int addrs_len; + time_t rtt; + struct sctpCookiePreserve bht; + + /* If we have gotten too many failures, give up. */ + if (1 + asoc->counters[SctpCounterInits] > asoc->max_init_attempts) { + /* FIXME: Move to new ulpevent. */ + retval->event_up = sctp_make_ulp_init_timeout(asoc); + if (!retval->event_up) + goto nomem; + sctp_add_cmd_sf(retval->commands, SCTP_CMD_DELETE_TCB, + SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + } + + retval->counters[0] = SCTP_COUNTER_INCR; + retval->counters[0] = SctpCounterInits; + retval->counters[1] = 0; + retval->counters[1] = 0; + + /* Calculate the RTT in ms. */ + /* BUG--we should get the send time of the HEARTBEAT REQUEST. */ + in_packet = chunk; + rtt = 1000 * timeval_sub(in_packet->skb->stamp, + asoc->c.state_timestamp); + + /* When calculating the time extension, an implementation + * SHOULD use the RTT information measured based on the + * previous COOKIE ECHO / ERROR exchange, and should add no + * more than 1 second beyond the measured RTT, due to long + * State Cookie lifetimes making the endpoint more subject to + * a replay attack. + */ + bht.p = {SCTP_COOKIE_PRESERVE, 8}; + bht.extraTime = htonl(rtt + 1000); + + initack.init_tag = htonl(asoc->c.my_vtag); + initack.a_rwnd = htonl(atomic_read(&asoc->rnwd)); + initack.num_outbound_streams = htons(asoc->streamoutcnt); + initack.num_inbound_streams = htons(asoc->streamincnt); + initack.initial_tsn = htonl(asoc->c.initSeqNumber); + + sctp_get_my_addrs(asoc, &addrs, &addrs_len); + + /* Build that new INIT chunk. */ + reply = sctp_make_chunk(SCTP_INITIATION, 0, + sizeof(initack) + + sizeof(bht) + + addrs_len); + if (!reply) + goto nomem; + sctp_addto_chunk(reply, sizeof(initack), &initack); + sctp_addto_chunk(reply, sizeof(bht), &bht); + sctp_addto_chunk(reply, addrs_len, addrs); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} +#endif /* 0 */ + +/* + * Process an ABORT. + * + * Section: 9.1 + * After checking the Verification Tag, the receiving endpoint shall + * remove the association from its record, and shall report the + * termination to its upper layer. + * + * Verification Tag: 8.5.1 Exceptions in Verification Tag Rules + * B) Rules for packet carrying ABORT: + * + * - The endpoint shall always fill in the Verification Tag field of the + * outbound packet with the destination endpoint's tag value if it + * is known. + * + * - If the ABORT is sent in response to an OOTB packet, the endpoint + * MUST follow the procedure described in Section 8.4. + * + * - The receiver MUST accept the packet if the Verification Tag + * matches either its own tag, OR the tag of its peer. Otherwise, the + * receiver MUST silently discard the packet and take no further + * action. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_9_1_abort(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Check the verification tag. */ + /* BUG: WRITE ME. */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + /* BUG? This does not look complete... */ + return SCTP_DISPOSITION_ABORT; +} + +/* + * Process an ABORT. (COOKIE-WAIT state) + * + * See sctp_sf_do_9_1_abort() above. + */ +sctp_disposition_t sctp_sf_cookie_wait_abort(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + + /* CMD_INIT_FAILED will DELETE_TCB. */ + sctp_add_cmd_sf(commands,SCTP_CMD_INIT_FAILED, SCTP_NULL()); + + return SCTP_DISPOSITION_DELETE_TCB; +} + +/* + * Process an ABORT. (COOKIE-ECHOED state) + * + * See sctp_sf_do_9_1_abort() above. + */ +sctp_disposition_t sctp_sf_cookie_echoed_abort(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* There is a single T1 timer, so we should be able to use + * common function with the COOKIE-WAIT state. + */ + return sctp_sf_cookie_wait_abort(ep, asoc, type, arg, commands); +} + +#if 0 +/* + * Handle a shutdown timeout or INIT during a shutdown phase. + * + * Section: 9.2 + * If an endpoint is in SHUTDOWN-ACK-SENT state and receives an INIT chunk + * (e.g., if the SHUTDOWN COMPLETE was lost) with source and destination + * transport addresses (either in the IP addresses or in the INIT chunk) + * that belong to this association, it should discard the INIT chunk and + * retransmit the SHUTDOWN ACK chunk. + *... + * While in SHUTDOWN-SENT state ... If the timer expires, the endpoint + * must re-send the SHUTDOWN ACK. + * + * Verification Tag: Neither the INIT nor the timeout will have a + * valid verification tag, so it is safe to ignore. + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_do_9_2_reshutack(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + /* If this was a timeout (not an INIT), then do the counter + * work. We might need to just dump the association. + */ + if (!chunk) { + if (1 + asoc->counters[SctpCounterRetran] > + asoc->maxRetrans) { + sctp_add_cmd(commands, SCTP_CMD_DELETE_TCB, + SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + } + retval->counters[0] = SCTP_COUNTER_INCR; + retval->counters[0] = SctpCounterRetran; + retval->counters[1] = 0; + retval->counters[1] = 0; + } + + reply = sctp_make_shutdown_ack(asoc, chunk); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} +#endif /* 0 */ + +/* + * sctp_sf_do_9_2_shut + * + * Section: 9.2 + * Upon the reception of the SHUTDOWN, the peer endpoint shall + * - enter the SHUTDOWN-RECEIVED state, + * + * - stop accepting new data from its SCTP user + * + * - verify, by checking the Cumulative TSN Ack field of the chunk, + * that all its outstanding DATA chunks have been received by the + * SHUTDOWN sender. + * + * Once an endpoint as reached the SHUTDOWN-RECEIVED state it MUST NOT + * send a SHUTDOWN in response to a ULP request. And should discard + * subsequent SHUTDOWN chunks. + * + * If there are still outstanding DATA chunks left, the SHUTDOWN + * receiver shall continue to follow normal data transmission + * procedures defined in Section 6 until all outstanding DATA chunks + * are acknowledged; however, the SHUTDOWN receiver MUST NOT accept + * new data from its SCTP user. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_9_2_shutdown(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_shutdownhdr_t *sdh; + sctp_disposition_t disposition; + + /* Convert the elaborate header. */ + sdh = (sctp_shutdownhdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_shutdownhdr_t)); + chunk->subh.shutdown_hdr = sdh; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Upon the reception of the SHUTDOWN, the peer endpoint shall + * - enter the SHUTDOWN-RECEIVED state, + * - stop accepting new data from its SCTP user + * + * [This is implicit in the new state.] + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_RECEIVED)); + disposition = SCTP_DISPOSITION_CONSUME; + + if (sctp_outqueue_is_empty(&asoc->outqueue)) { + disposition = + sctp_sf_do_9_2_shutdown_ack(ep, asoc, type, + arg, commands); + } + + /* - verify, by checking the Cumulative TSN Ack field of the + * chunk, that all its outstanding DATA chunks have been + * received by the SHUTDOWN sender. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_CTSN, + SCTP_U32(chunk->subh.shutdown_hdr->cum_tsn_ack)); + return disposition; +} + +/* + * sctp_sf_do_ecn_cwr + * + * Section: 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. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_ecn_cwr(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_cwrhdr_t *cwr; + sctp_chunk_t *chunk = arg; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + cwr = (sctp_cwrhdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_cwrhdr_t)); + + cwr->lowest_tsn = ntohl(cwr->lowest_tsn); + + /* Does this CWR ack the last sent congestion notification? */ + if (TSN_lte(asoc->last_ecne_tsn, cwr->lowest_tsn)) { + /* Stop sending ECNE. */ + sctp_add_cmd_sf(commands, + SCTP_CMD_ECN_CWR, + SCTP_U32(cwr->lowest_tsn)); + } + return SCTP_DISPOSITION_CONSUME; +} + +/* + * sctp_sf_do_ecne + * + * Section: Appendix A: Explicit Congestion Notification + * + * ECN-Echo + * + * RFC 2481 details a specific bit for a receiver to send back in its + * TCP acknowledgements to notify the sender of the Congestion + * Experienced (CE) bit having arrived from the network. For SCTP this + * same indication is made by including the ECNE chunk. This chunk + * contains one data element, i.e. the lowest TSN associated with the IP + * datagram marked with the CE bit..... + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_ecne(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_ecnehdr_t *ecne; + sctp_chunk_t *chunk = arg; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. If the received + * Verification Tag value does not match the receiver's own + * tag value, the receiver shall silently discard the packet... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + ecne = (sctp_ecnehdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_ecnehdr_t)); + ecne->lowest_tsn = ntohl(ecne->lowest_tsn); + + /* Casting away the const, as we are just modifying the spinlock, + * not the association itself. This should go away in the near + * future when we move to an endpoint based lock. + */ + + /* If this is a newer ECNE than the last CWR packet we sent out */ + if (TSN_lt(asoc->last_cwr_tsn, ecne->lowest_tsn)) { + sctp_add_cmd_sf(commands, SCTP_CMD_ECN_ECNE, + SCTP_U32(ecne->lowest_tsn)); + } + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Section: 6.2 Acknowledgement on Reception of DATA Chunks + * + * The SCTP endpoint MUST always acknowledge the reception of each valid + * DATA chunk. + * + * The guidelines on delayed acknowledgement algorithm specified in + * 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. In some + * situations it may be beneficial for an SCTP transmitter to be more + * conservative than the algorithms detailed in this document allow. + * However, an SCTP transmitter MUST NOT be more aggressive than the + * following algorithms allow. + * + * A SCTP receiver MUST NOT generate more than one SACK for every + * incoming packet, other than to update the offered window as the + * receiving application consumes new data. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_datahdr_t *data_hdr; + sctp_chunk_t *err; + size_t datalen; + int tmp; + __u32 tsn; + + /* RFC 2960 8.5 Verification Tag + * + * When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. + */ + + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, + SCTP_NULL()); + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + } + + data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *)chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); + + tsn = ntohl(data_hdr->tsn); + + SCTP_DEBUG_PRINTK("eat_data: TSN 0x%x.\n", tsn); + SCTP_DEBUG_PRINTK("eat_data: skb->head %p.\n", chunk->skb->head); + + /* ASSERT: Now skb->data is really the user data. */ + + /* Process ECN based congestion. + * + * Since the chunk structure is reused for all chunks within + * a packet, we use ecn_ce_done to track if we've already + * done CE processing for this packet. + * + * We need to do ECN processing even if we plan to discard the + * chunk later. + */ + + if (!chunk->ecn_ce_done) { + chunk->ecn_ce_done = 1; + if (INET_ECN_is_ce(chunk->skb->nh.iph->tos) && + asoc->peer.ecn_capable) { + /* Do real work as sideffect. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE, + SCTP_U32(tsn)); + } + } + + tmp = sctp_tsnmap_check(&asoc->peer.tsn_map, tsn); + if (tmp < 0) { + /* The TSN is too high--silently discard the chunk and + * count on it getting retransmitted later. + */ + goto discard_noforce; + } else if (tmp > 0) { + /* This is a duplicate. Record it. */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_DUP, SCTP_U32(tsn)); + goto discard_force; + } + + /* This is a new TSN. */ + + /* If we don't have any room in our receive window, discard. + * Actually, allow a little bit of overflow (up to a MTU of + * of overflow). + */ + datalen = ntohs(chunk->chunk_hdr->length); + datalen -= sizeof(sctp_data_chunk_t); + + if (!asoc->rwnd || (datalen > asoc->frag_point)) { + SCTP_DEBUG_PRINTK("Discarding tsn: %u datalen: %Zd, " + "rwnd: %d\n", tsn, datalen, asoc->rwnd); + goto discard_noforce; + } + + /* + * Section 3.3.10.9 No User Data (9) + * + * Cause of error + * --------------- + * No User Data: This error cause is returned to the originator of a + * DATA chunk if a received DATA chunk has no user data. + */ + if (unlikely(0 == datalen)) { + err = sctp_make_abort_no_data(asoc, chunk, tsn); + if (err) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err)); + } + /* We are going to ABORT, so we might as well stop + * processing the rest of the chunks in the packet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + return SCTP_DISPOSITION_CONSUME; + } + + /* We are accepting this DATA chunk. */ + + /* Record the fact that we have received this TSN. */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn)); + + /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number + * + * If an endpoint receive a DATA chunk with an invalid stream + * identifier, it shall acknowledge the reception of the DATA chunk + * following the normal procedure, immediately send an ERROR chunk + * with cause set to "Invalid Stream Identifier" (See Section 3.3.10) + * and discard the DATA chunk. + */ + if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) { + err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM, + &data_hdr->stream, + sizeof(data_hdr->stream)); + if (err) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err)); + } + goto discard_noforce; + } + + /* Send the data up to the user. Note: Schedule the + * SCTP_CMD_CHUNK_ULP cmd before the SCTP_CMD_GEN_SACK, as the SACK + * chunk needs the updated rwnd. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_CHUNK_ULP, SCTP_CHUNK(chunk)); + if (asoc->autoclose) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + } + + /* If this is the last chunk in a packet, we need to count it + * toward sack generation. Note that we need to SACK every + * OTHER packet containing data chunks, EVEN IF WE DISCARD + * THEM. We elect to NOT generate SACK's if the chunk fails + * the verification tag test. + * + * RFC 2960 6.2 Acknowledgement on Reception of DATA Chunks + * + * The SCTP endpoint MUST always acknowledge the reception of + * each valid DATA chunk. + * + * The guidelines on delayed acknowledgement algorithm + * specified in 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. In some + * situations it may be beneficial for an SCTP transmitter to + * be more conservative than the algorithms detailed in this + * document allow. However, an SCTP transmitter MUST NOT be + * more aggressive than the following algorithms allow. + */ + if (chunk->end_of_packet) { + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE()); + + /* Start the SACK timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); + } + + return SCTP_DISPOSITION_CONSUME; + +discard_force: + /* RFC 2960 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. + */ + /* In our case, we split the MAY SACK advice up whether or not + * the last chunk is a duplicate.' + */ + if (chunk->end_of_packet) + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); + return SCTP_DISPOSITION_DISCARD; + +discard_noforce: + if (chunk->end_of_packet) { + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_NOFORCE()); + + /* Start the SACK timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); + } + return SCTP_DISPOSITION_DISCARD; +} + +/* + * sctp_sf_eat_data_fast_4_4 + * + * Section: 4 (4) + * (4) In SHUTDOWN-SENT state the endpoint MUST acknowledge any received + * DATA chunks without delay. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_eat_data_fast_4_4(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_datahdr_t *data_hdr; + sctp_chunk_t *err; + size_t datalen; + int tmp; + __u32 tsn; + + /* RFC 2960 8.5 Verification Tag + * + * When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, + SCTP_NULL()); + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + } + + data_hdr = chunk->subh.data_hdr = (sctp_datahdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_datahdr_t)); + + tsn = ntohl(data_hdr->tsn); + + SCTP_DEBUG_PRINTK("eat_data: TSN 0x%x.\n", tsn); + + /* ASSERT: Now skb->data is really the user data. */ + + /* Process ECN based congestion. + * + * Since the chunk structure is reused for all chunks within + * a packet, we use ecn_ce_done to track if we've already + * done CE processing for this packet. + * + * We need to do ECN processing even if we plan to discard the + * chunk later. + */ + if (!chunk->ecn_ce_done) { + chunk->ecn_ce_done = 1; + if (INET_ECN_is_ce(chunk->skb->nh.iph->tos) && + asoc->peer.ecn_capable) { + /* Do real work as sideffect. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ECN_CE, + SCTP_U32(tsn)); + } + } + + tmp = sctp_tsnmap_check(&asoc->peer.tsn_map, tsn); + if (tmp < 0) { + /* The TSN is too high--silently discard the chunk and + * count on it getting retransmitted later. + */ + goto gen_shutdown; + } else if (tmp > 0) { + /* This is a duplicate. Record it. */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_DUP, SCTP_U32(tsn)); + goto gen_shutdown; + } + + /* This is a new TSN. */ + + datalen = ntohs(chunk->chunk_hdr->length); + datalen -= sizeof(sctp_data_chunk_t); + + /* + * Section 3.3.10.9 No User Data (9) + * + * Cause of error + * --------------- + * No User Data: This error cause is returned to the originator of a + * DATA chunk if a received DATA chunk has no user data. + */ + if (unlikely(0 == datalen)) { + err = sctp_make_abort_no_data(asoc, chunk, tsn); + if (err) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err)); + } + /* We are going to ABORT, so we might as well stop + * processing the rest of the chunks in the packet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + return SCTP_DISPOSITION_CONSUME; + } + + /* We are accepting this DATA chunk. */ + + /* Record the fact that we have received this TSN. */ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_TSN, SCTP_U32(tsn)); + + /* RFC 2960 6.5 Stream Identifier and Stream Sequence Number + * + * If an endpoint receive a DATA chunk with an invalid stream + * identifier, it shall acknowledge the reception of the DATA chunk + * following the normal procedure, immediately send an ERROR chunk + * with cause set to "Invalid Stream Identifier" (See Section 3.3.10) + * and discard the DATA chunk. + */ + if (ntohs(data_hdr->stream) >= asoc->c.sinit_max_instreams) { + err = sctp_make_op_error(asoc, chunk, SCTP_ERROR_INV_STRM, + &data_hdr->stream, + sizeof(data_hdr->stream)); + if (err) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(err)); + } + } + + /* Go a head and force a SACK, since we are shutting down. */ +gen_shutdown: + /* Implementor's Guide. + * + * While in SHUTDOWN-SENT state, the SHUTDOWN sender MUST immediately + * respond to each received packet containing one or more DATA chunk(s) + * with a SACK, a SHUTDOWN chunk, and restart the T2-shutdown timer + */ + if (chunk->end_of_packet) { + /* We must delay the chunk creation since the cumulative + * TSN has not been updated yet. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SHUTDOWN, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + } + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Section: 6.2 Processing a Received SACK + * D) Any time a SACK arrives, the endpoint performs the following: + * + * i) If Cumulative TSN Ack is less than the Cumulative TSN Ack Point, + * then drop the SACK. Since Cumulative TSN Ack is monotonically + * increasing, a SACK whose Cumulative TSN Ack is less than the + * Cumulative TSN Ack Point indicates an out-of-order SACK. + * + * 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. + * + * iii) If the SACK is missing a TSN that was previously + * acknowledged via a Gap Ack Block (e.g., the data receiver + * reneged on the data), then mark the corresponding DATA chunk + * as available for retransmit: Mark it as missing for fast + * retransmit as described in Section 7.2.4 and if no retransmit + * timer is running for the destination address to which the DATA + * chunk was originally transmitted, then T3-rtx is started for + * that destination address. + * + * Verification Tag: 8.5 Verification Tag [Normal verification] + * + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_eat_sack_6_2(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_sackhdr_t *sackh; + __u32 ctsn; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. ... + */ + if (ntohl(chunk->sctp_hdr->vtag) != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Pull the SACK chunk from the data buffer */ + sackh = sctp_sm_pull_sack(chunk); + chunk->subh.sack_hdr = sackh; + ctsn = ntohl(sackh->cum_tsn_ack); + + /* i) If Cumulative TSN Ack is less than the Cumulative TSN + * Ack Point, then drop the SACK. Since Cumulative TSN + * Ack is monotonically increasing, a SACK whose + * Cumulative TSN Ack is less than the Cumulative TSN Ack + * Point indicates an out-of-order SACK. + */ + if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { + SCTP_DEBUG_PRINTK("ctsn %x\n", ctsn); + SCTP_DEBUG_PRINTK("ctsn_ack_point %x\n", + asoc->ctsn_ack_point); + return SCTP_DISPOSITION_DISCARD; + } + + /* Return this SACK for further processing. */ + sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, + SCTP_SACKH(sackh)); + + /* Note: We do the rest of the work on the PROCESS_SACK + * sideeffect. + */ + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Generate an ABORT in response to a packet. + * + * Section: 8.4 Handle "Out of the blue" Packets + * + * 8) The receiver should respond to the sender of the OOTB packet + * with an ABORT. When sending the ABORT, the receiver of the + * OOTB packet MUST fill in the Verification Tag field of the + * outbound packet with the value found in the Verification Tag + * field of the OOTB packet and set the T-bit in the Chunk Flags + * to indicate that no TCB was found. After sending this ABORT, + * the receiver of the OOTB packet shall discard the OOTB packet + * and take no further action. + * + * Verification Tag: + * + * The return value is the disposition of the chunk. +*/ +sctp_disposition_t sctp_sf_tabort_8_4_8(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_packet_t *packet = NULL; + sctp_transport_t *transport = NULL; + sctp_chunk_t *chunk = arg; + sctp_chunk_t *abort; + __u16 sport; + __u16 dport; + __u32 vtag; + + /* Grub in chunk and endpoint for kewl bitz. */ + sport = ntohs(chunk->sctp_hdr->dest); + dport = ntohs(chunk->sctp_hdr->source); + /* -- Make sure the ABORT packet's V-tag is the same as the + * inbound packet if no association exists, otherwise use + * the peer's vtag. + */ + if (asoc) + vtag = asoc->peer.i.init_tag; + else + vtag = ntohl(chunk->sctp_hdr->vtag); + + /* Make a transport for the bucket, Eliza... */ + transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC); + if (!transport) + goto nomem; + + /* Make a packet for the ABORT to go into. */ + packet = t_new(sctp_packet_t, GFP_ATOMIC); + if (!packet) + goto nomem_packet; + + packet = sctp_packet_init(packet, transport, sport, dport); + packet = sctp_packet_config(packet, vtag, 0, NULL); + + /* Make an ABORT. + * This will set the T bit since we have no association. + */ + abort = sctp_make_abort(NULL, chunk, 0); + if (!abort) + goto nomem_chunk; + + /* Set the skb to the belonging sock for accounting. */ + abort->skb->sk = ep->base.sk; + + sctp_packet_append_chunk(packet, abort); + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); + return SCTP_DISPOSITION_DISCARD; + +nomem_chunk: + sctp_packet_free(packet); + +nomem_packet: + sctp_transport_free(transport); + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Received an ERROR chunk from peer. Generate SCTP_REMOTE_ERROR + * event as ULP notification for each cause included in the chunk. + * + * API 5.3.1.3 - SCTP_REMOTE_ERROR + * + * The return value is the disposition of the chunk. +*/ +sctp_disposition_t sctp_sf_operr_notify(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_ulpevent_t *ev; + + while (chunk->chunk_end > chunk->skb->data) { + ev = sctp_ulpevent_make_remote_error(asoc,chunk,0, GFP_ATOMIC); + if (!ev) + goto nomem; + + if (!sctp_add_cmd(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(ev))) { + sctp_ulpevent_free(ev); + goto nomem; + } + } + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process an inbound SHUTDOWN ACK. + * + * From Section 9.2: + * Upon the receipt of the SHUTDOWN ACK, the SHUTDOWN sender shall + * stop the T2-shutdown timer, send a SHUTDOWN COMPLETE chunk to its + * peer, and remove all record of the association. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_2_final(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + sctp_chunk_t *reply; + sctp_ulpevent_t *ev; + + /* 10.2 H) SHUTDOWN COMPLETE notification + * + * When SCTP completes the shutdown procedures (section 9.2) this + * notification is passed to the upper layer. + */ + ev = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_SHUTDOWN_COMP, + 0, 0, 0, GFP_ATOMIC); + if (!ev) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(ev)); + + /* Upon the receipt of the SHUTDOWN ACK, the SHUTDOWN sender shall + * stop the T2-shutdown timer, + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + /* ...send a SHUTDOWN COMPLETE chunk to its peer, */ + reply = sctp_make_shutdown_complete(asoc, chunk); + if (!reply) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + /* ...and remove all record of the association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * RFC 2960, 8.4 - Handle "Out of the blue" Packets + * 5) If the packet contains a SHUTDOWN ACK chunk, the receiver should + * respond to the sender of the OOTB packet with a SHUTDOWN COMPLETE. + * When sending the SHUTDOWN COMPLETE, the receiver of the OOTB + * packet must fill in the Verification Tag field of the outbound + * packet with the Verification Tag received in the SHUTDOWN ACK and + * set the T-bit in the Chunk Flags to indicate that no TCB was + * found. Otherwise, + * + * 8) The receiver should respond to the sender of the OOTB packet with + * an ABORT. When sending the ABORT, the receiver of the OOTB packet + * MUST fill in the Verification Tag field of the outbound packet + * with the value found in the Verification Tag field of the OOTB + * packet and set the T-bit in the Chunk Flags to indicate that no + * TCB was found. After sending this ABORT, the receiver of the OOTB + * packet shall discard the OOTB packet and take no further action. + */ +sctp_disposition_t sctp_sf_ootb(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + struct sk_buff *skb = chunk->skb; + sctp_chunkhdr_t *ch; + __u8 *ch_end; + int ootb_shut_ack = 0; + + ch = (sctp_chunkhdr_t *) chunk->chunk_hdr; + do { + ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); + + if (SCTP_CID_SHUTDOWN_ACK == ch->type) + ootb_shut_ack = 1; + + ch = (sctp_chunkhdr_t *) ch_end; + } while (ch_end < skb->tail); + + if (ootb_shut_ack) + sctp_sf_shut_8_4_5(ep, asoc, type, arg, commands); + else + sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); +} + +/* + * Handle an "Out of the blue" SHUTDOWN ACK. + * + * Section: 8.4 5) + * 5) If the packet contains a SHUTDOWN ACK chunk, the receiver should + * respond to the sender of the OOTB packet with a SHUTDOWN COMPLETE. + * When sending the SHUTDOWN COMPLETE, the receiver of the OOTB packet + * must fill in the Verification Tag field of the outbound packet with + * the Verification Tag received in the SHUTDOWN ACK and set the + * T-bit in the Chunk Flags to indicate that no TCB was found. + * + * Verification Tag: 8.5.1 E) Rules for packet carrying a SHUTDOWN ACK + * If the receiver is in COOKIE-ECHOED or COOKIE-WAIT state the + * procedures in section 8.4 SHOULD be followed, in other words it + * should be treated as an Out Of The Blue packet. + * [This means that we do NOT check the Verification Tag on these + * chunks. --piggy ] + * + * Inputs + * (endpoint, asoc, type, arg, commands) + * + * Outputs + * (sctp_disposition_t) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_shut_8_4_5(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_packet_t *packet = NULL; + sctp_transport_t *transport = NULL; + sctp_chunk_t *chunk = arg; + sctp_chunk_t *shut; + __u16 sport; + __u16 dport; + __u32 vtag; + + /* Grub in chunk and endpoint for kewl bitz. */ + sport = ntohs(chunk->sctp_hdr->dest); + dport = ntohs(chunk->sctp_hdr->source); + + /* Make sure the ABORT packet's V-tag is the same as the + * inbound packet if no association exists, otherwise use + * the peer's vtag. + */ + vtag = ntohl(chunk->sctp_hdr->vtag); + + /* Make a transport for the bucket, Eliza... */ + transport = sctp_transport_new(sctp_source(chunk), GFP_ATOMIC); + if (!transport) + goto nomem; + + /* Make a packet for the ABORT to go into. */ + packet = t_new(sctp_packet_t, GFP_ATOMIC); + if (!packet) + goto nomem_packet; + + packet = sctp_packet_init(packet, transport, sport, dport); + packet = sctp_packet_config(packet, vtag, 0, NULL); + + /* Make an ABORT. + * This will set the T bit since we have no association. + */ + shut = sctp_make_shutdown_complete(NULL, chunk); + if (!shut) + goto nomem_chunk; + + /* Set the skb to the belonging sock for accounting. */ + shut->skb->sk = ep->base.sk; + + sctp_packet_append_chunk(packet, shut); + sctp_add_cmd_sf(commands, SCTP_CMD_SEND_PKT, SCTP_PACKET(packet)); + + return SCTP_DISPOSITION_CONSUME; + +nomem_chunk: + sctp_packet_free(packet); + +nomem_packet: + sctp_transport_free(transport); + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +#if 0 +/* + * We did something stupid but got lucky. Namely, we sent a HEARTBEAT + * before the association was all the way up and we did NOT get an + * ABORT. + * + * Log the fact and then process normally. + * + * Section: Not specified + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t lucky(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. ... + */ + if (chunk->sctp_hdr->vtag != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} +#endif /* 0 */ + +#if 0 +/* + * The other end is doing something very stupid. We'll ignore them + * after logging their idiocy. :-) + * + * Section: Not specified + * Verification Tag: 8.5 Verification Tag [Normal verification] + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t other_stupid(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + /* 8.5 When receiving an SCTP packet, the endpoint MUST ensure + * that the value in the Verification Tag field of the + * received SCTP packet matches its own Tag. ... + */ + if (chunk->sctp_hdr->vtag != asoc->c.my_vtag) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} +#endif /* 0 */ + +/* + * The other end is violating protocol. + * + * Section: Not specified + * Verification Tag: Not specified + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * We simply tag the chunk as a violation. The state machine will log + * the violation and continue. + */ +sctp_disposition_t sctp_sf_violation(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + return SCTP_DISPOSITION_VIOLATION; +} + +/*************************************************************************** + * These are the state functions for handling primitive (Section 10) events. + ***************************************************************************/ +/* + * sctp_sf_do_prm_asoc + * + * Section: 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. + * + * The peer endpoint shall be specified by one of the transport addresses + * which defines the endpoint (see Section 1.4). If the local SCTP + * instance has not been initialized, the ASSOCIATE is considered an + * error. + * [This is not relevant for the kernel implementation since we do all + * initialization at boot time. It we hadn't initialized we wouldn't + * get anywhere near this code.] + * + * An association id, which is a local handle to the SCTP association, + * will be returned on successful establishment of the association. If + * SCTP is not able to open an SCTP association with the peer endpoint, + * an error is returned. + * [In the kernel implementation, the sctp_association_t needs to + * be created BEFORE causing this primitive to run.] + * + * Other association parameters may be returned, including the + * complete destination transport addresses of the peer as well as the + * outbound stream count of the local endpoint. One of the transport + * address from the returned destination addresses will be selected by + * the local endpoint as default primary path for sending SCTP packets + * to this peer. The returned "destination transport addr list" can + * be used by the ULP to change the default primary path or to force + * sending a packet to a specific transport address. [All of this + * stuff happens when the INIT ACK arrives. This is a NON-BLOCKING + * function.] + * + * Mandatory attributes: + * + * o local SCTP instance name - obtained from the INITIALIZE operation. + * [This is the argument asoc.] + * o destination transport addr - specified as one of the transport + * addresses of the peer endpoint with which the association is to be + * established. + * [This is asoc->peer.active_path.] + * o outbound stream count - the number of outbound streams the ULP + * would like to open towards this peer endpoint. + * [BUG: This is not currently implemented.] + * Optional attributes: + * + * None. + * + * The return value is a disposition. + */ +sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *repl; + sctp_bind_addr_t *bp; + sctp_scope_t scope; + int error; + int flags; + + /* The comment below says that we enter COOKIE-WAIT AFTER + * sending the INIT, but that doesn't actually work in our + * implementation... + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_COOKIE_WAIT)); + + /* 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(GFP_ATOMIC); + if (!bp) + goto nomem; + + /* Use scoping rules to determine the subset of addresses from + * the endpoint. + */ + 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, &ep->base.bind_addr, scope, + GFP_ATOMIC, flags); + if (error) + goto nomem; + + /* FIXME: Either move address assignment out of this function + * or else move the association allocation/init into this function. + * The association structure is brand new before calling this + * function, so would not be a sideeffect if the allocation + * moved into this function. --jgrimm + */ + sctp_add_cmd_sf(commands, SCTP_CMD_SET_BIND_ADDR, (sctp_arg_t) bp); + + /* RFC 2960 5.1 Normal Establishment of an Association + * + * A) "A" first sends an INIT chunk to "Z". In the INIT, "A" + * must provide its Verification Tag (Tag_A) in the Initiate + * Tag field. Tag_A SHOULD be a random number in the range of + * 1 to 4294967295 (see 5.3.1 for Tag value selection). ... + */ + + repl = sctp_make_init(asoc, bp, GFP_ATOMIC); + if (!repl) + goto nomem; + + /* Cast away the const modifier, as we want to just + * rerun it through as a sideffect. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, + SCTP_ASOC((sctp_association_t *) asoc)); + + /* After sending the INIT, "A" starts the T1-init timer and + * enters the COOKIE-WAIT state. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + return SCTP_DISPOSITION_CONSUME; + +nomem: + if (bp) + sctp_bind_addr_free(bp); + + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Process the SEND primitive. + * + * Section: 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. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_prm_send(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = arg; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(chunk)); + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Process the SHUTDOWN primitive. + * + * Section: 10.1: + * 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. + * + * Mandatory attributes: + * + * o association id - local handle to the SCTP association + * + * Optional attributes: + * + * None. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_2_prm_shutdown(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + int disposition; + + /* From 9.2 Shutdown of an Association + * Upon receipt of the SHUTDOWN primitive from its upper + * layer, the endpoint enters SHUTDOWN-PENDING state and + * remains there until all outstanding data has been + * acknowledged by its peer. The endpoint accepts no new data + * from its upper layer, but retransmits data to the far end + * if necessary to fill gaps. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_PENDING)); + + disposition = SCTP_DISPOSITION_CONSUME; + if (sctp_outqueue_is_empty(&asoc->outqueue)) { + disposition = + sctp_sf_do_9_2_start_shutdown(ep, asoc, type, + arg, commands); + } + return disposition; +} + +/* + * Process the ABORT primitive. + * + * Section: 10.1: + * 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. + * + * Mandatory attributes: + * + * o association id - local handle to the SCTP association + * + * Optional attributes: + * + * o cause code - reason of the abort to be passed to the peer + * + * None. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_1_prm_abort(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* From 9.1 Abort of an Association + * Upon receipt of the ABORT primitive from its upper + * layer, the endpoint enters CLOSED state and + * discard all outstanding data has been + * acknowledged by its peer. The endpoint accepts no new data + * from its upper layer, but retransmits data to the far end + * if necessary to fill gaps. + */ + sctp_chunk_t *abort; + sctp_disposition_t retval; + + retval = SCTP_DISPOSITION_CONSUME; + + /* Generate ABORT chunk to send the peer. */ + abort = sctp_make_abort(asoc, NULL, 0); + if (!abort) + retval = SCTP_DISPOSITION_NOMEM; + else + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); + + /* Even if we can't send the ABORT due to low memory delete the + * TCB. This is a departure from our typical NOMEM handling. + */ + + /* Change to CLOSED state. */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + + /* Delete the established association. */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + return retval; +} + +/* We tried an illegal operation on an association which is closed. */ +sctp_disposition_t sctp_sf_error_closed(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, SCTP_ERROR(-EINVAL)); + return SCTP_DISPOSITION_CONSUME; +} + +/* We tried an illegal operation on an association which is shutting + * down. + */ +sctp_disposition_t sctp_sf_error_shutdown(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, + SCTP_ERROR(-ESHUTDOWN)); + return SCTP_DISPOSITION_CONSUME; +} + +/* + * sctp_cookie_wait_prm_shutdown + * + * Section: 4 Note: 2 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * The RFC does not explicitly address this issue, but is the route through the + * state table when someone issues a shutdown while in COOKIE_WAIT state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); + + return SCTP_DISPOSITION_DELETE_TCB; +} + +/* + * sctp_cookie_echoed_prm_shutdown + * + * Section: 4 Note: 2 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * The RFC does not explcitly address this issue, but is the route through the + * state table when someone issues a shutdown while in COOKIE_ECHOED state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( + const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, sctp_cmd_seq_t *commands) +{ + /* There is a single T1 timer, so we should be able to use + * common function with the COOKIE-WAIT state. + */ + return sctp_sf_cookie_wait_prm_shutdown(ep, asoc, type, arg, commands); +} + +/* + * sctp_cookie_wait_prm_abort + * + * Section: 4 Note: 2 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * The RFC does not explicitly address this issue, but is the route through the + * state table when someone issues an abort while in COOKIE_WAIT state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* Stop T1-init timer */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + return sctp_sf_do_9_1_prm_abort(ep, asoc, type, arg, commands); +} + +/* + * sctp_cookie_echoed_prm_abort + * + * Section: 4 Note: 3 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * The RFC does not explcitly address this issue, but is the route through the + * state table when someone issues an abort while in COOKIE_ECHOED state. + * + * Outputs + * (timers) + */ +sctp_disposition_t sctp_sf_cookie_echoed_prm_abort(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + /* There is a single T1 timer, so we should be able to use + * common function with the COOKIE-WAIT state. + */ + return sctp_sf_cookie_wait_prm_abort(ep, asoc, type, arg, commands); +} + +/* + * Ignore the primitive event + * + * The return value is the disposition of the primitive. + */ +sctp_disposition_t sctp_sf_ignore_primitive(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + SCTP_DEBUG_PRINTK("Primitive type %d is ignored.\n", type.primitive); + return SCTP_DISPOSITION_DISCARD; +} + +/*************************************************************************** + * These are the state functions for the OTHER events. + ***************************************************************************/ + +/* + * Start the shutdown negotiation. + * + * From Section 9.2: + * Once all its outstanding data has been acknowledged, the endpoint + * shall send a SHUTDOWN chunk to its peer including in the Cumulative + * TSN Ack field the last sequential TSN it has received from the peer. + * It shall then start the T2-shutdown timer and enter the SHUTDOWN-SENT + * state. If the timer expires, the endpoint must re-send the SHUTDOWN + * with the updated last sequential TSN received from its peer. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_2_start_shutdown(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *reply; + + /* Once all its outstanding data has been acknowledged, the + * endpoint shall send a SHUTDOWN chunk to its peer including + * in the Cumulative TSN Ack field the last sequential TSN it + * has received from the peer. + */ + reply = sctp_make_shutdown(asoc); + if (!reply) + goto nomem; + + /* Set the transport for the SHUTDOWN chunk and the timeout for the + * T2-shutdown timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply)); + + /* It shall then start the T2-shutdown timer */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + if (asoc->autoclose) + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + + /* and enter the SHUTDOWN-SENT state. */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_SENT)); + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Generate a SHUTDOWN ACK now that everything is SACK'd. + * + * From Section 9.2: + * + * If it has no more outstanding DATA chunks, the SHUTDOWN receiver + * shall send a SHUTDOWN ACK and start a T2-shutdown timer of its own, + * entering the SHUTDOWN-ACK-SENT state. If the timer expires, the + * endpoint must re-send the SHUTDOWN ACK. + * + * The return value is the disposition. + */ +sctp_disposition_t sctp_sf_do_9_2_shutdown_ack(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *chunk = (sctp_chunk_t *) arg; + sctp_chunk_t *reply; + + /* If it has no more outstanding DATA chunks, the SHUTDOWN receiver + * shall send a SHUTDOWN ACK ... + */ + reply = sctp_make_shutdown_ack(asoc, chunk); + if (!reply) + goto nomem; + + /* Set the transport for the SHUTDOWN ACK chunk and the timeout for + * the T2-shutdown timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply)); + + /* and start/restart a T2-shutdown timer of its own, */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + + if (asoc->autoclose) + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_AUTOCLOSE)); + + /* Enter the SHUTDOWN-ACK-SENT state. */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_ACK_SENT)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* + * Ignore the event defined as other + * + * The return value is the disposition of the event. + */ +sctp_disposition_t sctp_sf_ignore_other(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + SCTP_DEBUG_PRINTK("The event other type %d is ignored\n", + type.other); + return SCTP_DISPOSITION_DISCARD; +} + +/************************************************************ + * These are the state functions for handling timeout events. + ************************************************************/ + +/* + * RTX Timeout + * + * Section: 6.3.3 Handle T3-rtx Expiration + * + * Whenever the retransmission timer T3-rtx expires for a destination + * address, do the following: + * [See below] + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_do_6_3_3_rtx(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_transport_t *transport = arg; + + if (asoc->overall_error_count >= asoc->overall_error_threshold) { + /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + } + + /* E1) For the destination address for which the timer + * expires, adjust its ssthresh with rules defined in Section + * 7.2.3 and set the cwnd <- MTU. + */ + + /* 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. + */ + + /* 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. + * + * Note: Any DATA chunks that were sent to the address for + * which the T3-rtx timer expired but did not fit in one MTU + * (rule E3 above), should be marked for retransmission and + * sent as soon as cwnd allows (normally when a SACK arrives). + */ + + /* NB: Rules E4 and F1 are implicit in R1. */ + sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(transport)); + + /* Do some failure management (Section 8.2). */ + sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, SCTP_TRANSPORT(transport)); + + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Generate delayed SACK on timeout + * + * Section: 6.2 Acknowledgement on Reception of DATA Chunks + * + * The guidelines on delayed acknowledgement algorithm specified in + * 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. In + * some situations it may be beneficial for an SCTP transmitter to be + * more conservative than the algorithms detailed in this document + * allow. However, an SCTP transmitter MUST NOT be more aggressive than + * the following algorithms allow. + */ +sctp_disposition_t sctp_sf_do_6_2_sack(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); + return SCTP_DISPOSITION_CONSUME; +} + +/* + * sctp_sf_t1_timer_expire + * + * Section: 4 Note: 2 + * Verification Tag: + * Inputs + * (endpoint, asoc) + * + * RFC 2960 Section 4 Notes + * 2) If the T1-init timer expires, the endpoint MUST retransmit INIT + * and re-start the T1-init timer without changing state. This MUST + * be repeated up to 'Max.Init.Retransmits' times. After that, the + * endpoint MUST abort the initialization process and report the + * error to SCTP user. + * + * 3) If the T1-cookie timer expires, the endpoint MUST retransmit + * COOKIE ECHO and re-start the T1-cookie timer without changing + * state. This MUST be repeated up to 'Max.Init.Retransmits' times. + * After that, the endpoint MUST abort the initialization process and + * report the error to SCTP user. + * + * Outputs + * (timers, events) + * + */ +sctp_disposition_t sctp_sf_t1_timer_expire(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *repl; + sctp_bind_addr_t *bp; + sctp_event_timeout_t timer = (sctp_event_timeout_t) arg; + int timeout; + int attempts; + + timeout = asoc->timeouts[timer]; + attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1; + repl = NULL; + + SCTP_DEBUG_PRINTK("Timer T1 expired.\n"); + + if ((timeout < asoc->max_init_timeo) && + (attempts < asoc->max_init_attempts)) { + switch (timer) { + case SCTP_EVENT_TIMEOUT_T1_INIT: + bp = (sctp_bind_addr_t *) &asoc->base.bind_addr; + repl = sctp_make_init(asoc, bp, GFP_ATOMIC); + break; + + case SCTP_EVENT_TIMEOUT_T1_COOKIE: + repl = sctp_make_cookie_echo(asoc, NULL); + break; + + default: + BUG(); + break; + }; + + if (!repl) + goto nomem; + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); + + /* Issue a sideeffect to do the needed accounting. */ + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_RESTART, + SCTP_TO(timer)); + } else { + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + } + + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* RFC2960 9.2 If the timer expires, the endpoint must re-send the SHUTDOWN + * with the updated last sequential TSN received from its peer. + * + * An endpoint should limit the number of retransmissions of the + * SHUTDOWN chunk to the protocol parameter 'Association.Max.Retrans'. + * If this threshold is exceeded the endpoint should destroy the TCB and + * MUST report the peer endpoint unreachable to the upper layer (and + * thus the association enters the CLOSED state). The reception of any + * packet from its peer (i.e. as the peer sends all of its queued DATA + * chunks) should clear the endpoint's retransmission count and restart + * the T2-Shutdown timer, giving its peer ample opportunity to transmit + * all of its queued DATA chunks that have not yet been sent. + */ +sctp_disposition_t sctp_sf_t2_timer_expire(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_chunk_t *reply = NULL; + + SCTP_DEBUG_PRINTK("Timer T2 expired.\n"); + if (asoc->overall_error_count >= asoc->overall_error_threshold) { + /* Note: CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + return SCTP_DISPOSITION_DELETE_TCB; + } + + switch (asoc->state) { + case SCTP_STATE_SHUTDOWN_SENT: + reply = sctp_make_shutdown(asoc); + break; + + case SCTP_STATE_SHUTDOWN_ACK_SENT: + reply = sctp_make_shutdown_ack(asoc, NULL); + break; + + default: + BUG(); + break; + }; + + if (!reply) + goto nomem; + + /* Do some failure management (Section 8.2). */ + sctp_add_cmd_sf(commands, SCTP_CMD_STRIKE, + SCTP_TRANSPORT(asoc->shutdown_last_sent_to)); + + /* Set the transport for the SHUTDOWN/ACK chunk and the timeout for + * the T2-shutdown timer. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_SETUP_T2, SCTP_CHUNK(reply)); + + /* Restart the T2-shutdown timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_RESTART, + SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); + return SCTP_DISPOSITION_CONSUME; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + +/* Handle expiration of AUTOCLOSE timer. When the autoclose timer expires, + * the association is automatically closed by starting the shutdown process. + * The work that needs to be done is same as when SHUTDOWN is initiated by + * the user. So this routine looks same as sctp_sf_do_9_2_prm_shutdown(). + */ +sctp_disposition_t sctp_sf_autoclose_timer_expire(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + int disposition; + + /* From 9.2 Shutdown of an Association + * Upon receipt of the SHUTDOWN primitive from its upper + * layer, the endpoint enters SHUTDOWN-PENDING state and + * remains there until all outstanding data has been + * acknowledged by its peer. The endpoint accepts no new data + * from its upper layer, but retransmits data to the far end + * if necessary to fill gaps. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_SHUTDOWN_PENDING)); + + disposition = SCTP_DISPOSITION_CONSUME; + if (sctp_outqueue_is_empty(&asoc->outqueue)) { + disposition = + sctp_sf_do_9_2_start_shutdown(ep, asoc, type, + arg, commands); + } + return disposition; +} + +/***************************************************************************** + * These are sa state functions which could apply to all types of events. + ****************************************************************************/ + +/* + * This table entry is not implemented. + * + * Inputs + * (endpoint, asoc, chunk) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_not_impl(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + return SCTP_DISPOSITION_NOT_IMPL; +} + +/* + * This table entry represents a bug. + * + * Inputs + * (endpoint, asoc, chunk) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_bug(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + return SCTP_DISPOSITION_BUG; +} + +/* + * This table entry represents the firing of a timer in the wrong state. + * Since timer deletion cannot be guaranteed a timer 'may' end up firing + * when the association is in the wrong state. This event should + * be ignored, so as to prevent any rearming of the timer. + * + * Inputs + * (endpoint, asoc, chunk) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_timer_ignore(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + SCTP_DEBUG_PRINTK("Timer %d ignored.\n", type.chunk); + return SCTP_DISPOSITION_CONSUME; +} + +/* + * Discard the chunk. + * + * Section: 0.2, 5.2.3, 5.2.5, 5.2.6, 6.0, 8.4.6, 8.5.1c, 9.2 + * [Too numerous to mention...] + * Verification Tag: No verification needed. + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (asoc, reply_msg, msg_up, timers, counters) + * + * The return value is the disposition of the chunk. + */ +sctp_disposition_t sctp_sf_discard_chunk(const sctp_endpoint_t *ep, + const sctp_association_t *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + SCTP_DEBUG_PRINTK("Chunk %d is discarded\n", type.chunk); + return SCTP_DISPOSITION_DISCARD; +} + +/******************************************************************** + * 2nd Level Abstractions + ********************************************************************/ + +/* Pull the SACK chunk based on the SACK header. */ +sctp_sackhdr_t *sctp_sm_pull_sack(sctp_chunk_t *chunk) +{ + sctp_sackhdr_t *sack; + __u16 num_blocks; + __u16 num_dup_tsns; + + sack = (sctp_sackhdr_t *) chunk->skb->data; + skb_pull(chunk->skb, sizeof(sctp_sackhdr_t)); + + num_blocks = ntohs(sack->num_gap_ack_blocks); + num_dup_tsns = ntohs(sack->num_dup_tsns); + + skb_pull(chunk->skb, (num_blocks + num_dup_tsns) * sizeof(__u32)); + return sack; +} diff --git a/net/sctp/sctp_sm_statetable.c b/net/sctp/sctp_sm_statetable.c new file mode 100644 index 000000000000..89029496cc16 --- /dev/null +++ b/net/sctp/sctp_sm_statetable.c @@ -0,0 +1,1149 @@ +/* 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; +} diff --git a/net/sctp/sctp_socket.c b/net/sctp/sctp_socket.c new file mode 100644 index 000000000000..a619bd4d3cde --- /dev/null +++ b/net/sctp/sctp_socket.c @@ -0,0 +1,2705 @@ +/* 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, +}; diff --git a/net/sctp/sctp_sysctl.c b/net/sctp/sctp_sysctl.c new file mode 100644 index 000000000000..951e66784547 --- /dev/null +++ b/net/sctp/sctp_sysctl.c @@ -0,0 +1,107 @@ +/* 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); +} diff --git a/net/sctp/sctp_transport.c b/net/sctp/sctp_transport.c new file mode 100644 index 000000000000..fcfa7c5ed75f --- /dev/null +++ b/net/sctp/sctp_transport.c @@ -0,0 +1,442 @@ +/* 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); +} diff --git a/net/sctp/sctp_tsnmap.c b/net/sctp/sctp_tsnmap.c new file mode 100644 index 000000000000..c4d1970ad70d --- /dev/null +++ b/net/sctp/sctp_tsnmap.c @@ -0,0 +1,386 @@ +/* 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; + } + } + } +} diff --git a/net/sctp/sctp_ulpevent.c b/net/sctp/sctp_ulpevent.c new file mode 100644 index 000000000000..5bbb91b2c910 --- /dev/null +++ b/net/sctp/sctp_ulpevent.c @@ -0,0 +1,843 @@ +/* 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); +} + + + + + + + + + + + + + + + + diff --git a/net/sctp/sctp_ulpqueue.c b/net/sctp/sctp_ulpqueue.c new file mode 100644 index 000000000000..ed1e700acdfa --- /dev/null +++ b/net/sctp/sctp_ulpqueue.c @@ -0,0 +1,479 @@ +/* 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; +} diff --git a/net/socket.c b/net/socket.c index 53dcf8bf9c83..c9be97e26a5e 100644 --- a/net/socket.c +++ b/net/socket.c @@ -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; |
