Commit 180fb54b authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://kernel.bkbits.net/davem/net-2.5

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 8f493285 0b7f6302
/* SCTP kernel reference Implementation Copyright (C) 1999-2001
* Cisco, Motorola, and IBM
*
*
* This file is part of the SCTP kernel reference Implementation
*
*
* 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 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
*
* 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.
*
* 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>
* Ardelle Fan <ardelle.fan@intel.com>
*
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
......@@ -69,11 +69,11 @@ typedef enum {
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_HB_TIMERS_UPDATE, /* Update the heartbeat timers. */
SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */
SCTP_CMD_HB_TIMER_UPDATE, /* Update a heartbeat timers. */
SCTP_CMD_HB_TIMERS_STOP, /* Stop 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. */
......@@ -112,7 +112,7 @@ typedef union {
void *ptr;
sctp_chunk_t *chunk;
sctp_association_t *asoc;
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_bind_addr_t *bp;
sctp_init_chunk_t *init;
sctp_ulpevent_t *ulpevent;
......@@ -160,7 +160,7 @@ 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(TRANSPORT, struct sctp_transport *, 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)
......
......@@ -3,33 +3,33 @@
* 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.
*
* The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of
*
* 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
*
* 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.
*
* 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>
......@@ -38,14 +38,14 @@
* 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__
......@@ -56,17 +56,10 @@
#include <linux/ipv6.h> /* For ipv6hdr. */
#include <net/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))
/* Value used for stream negotiation. */
enum { SCTP_MAX_STREAM = 0xffff };
enum { SCTP_DEFAULT_OUTSTREAMS = 10 };
enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM };
/* Define the amount of space to reserve for SCTP, IP, LL.
* There is a little bit of waste that we are always allocating
......@@ -105,57 +98,37 @@ typedef enum {
*/
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_T5_SHUTDOWN_GUARD,
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_EVENT_TIMEOUT_MAX SCTP_EVENT_TIMEOUT_AUTOCLOSE
#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_EVENT_OTHER_MAX SCTP_EVENT_NO_PENDING_TSN
#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_ASSOCIATE = 0,
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_EVENT_PRIMITIVE_MAX SCTP_PRIMITIVE_REQUESTHEARTBEAT
#define SCTP_NUM_PRIMITIVE_TYPES (SCTP_EVENT_PRIMITIVE_MAX + 1)
/* We define here a utility type for manipulating subtypes.
......@@ -268,8 +241,13 @@ extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[];
#define SCTP_ADDR_REACHABLE 2
#define SCTP_ADDR_NOT_REACHABLE 1
/* Maximum chunk length considering padding requirements. */
enum { SCTP_MAX_CHUNK_LEN = ((1<<16) - sizeof(__u32)) };
/* Encourage Cookie-Echo bundling by pre-fragmenting chunks a little
* harder (until reaching ESTABLISHED state).
*/
enum { SCTP_ARBITRARY_COOKIE_ECHO_LEN = 200 };
/* Guess at how big to make the TSN mapping array.
* We guarantee that we can handle at least this big a gap between the
......@@ -289,7 +267,8 @@ extern const char *sctp_state_tbl[], *sctp_evttype_tbl[], *sctp_status_tbl[];
* is enough room for 131 duplicate reports. Round down to the
* nearest power of 2.
*/
#define SCTP_MAX_DUP_TSNS 128
enum { SCTP_MIN_PMTU = 576 };
enum { SCTP_MAX_DUP_TSNS = 128 };
typedef enum {
SCTP_COUNTER_INIT_ERROR,
......@@ -298,7 +277,6 @@ typedef enum {
/* How many counters does an association need? */
#define SCTP_NUMBER_COUNTERS 5
/* Here we define the default timers. */
/* cookie timer def = ? seconds */
......@@ -317,10 +295,6 @@ typedef enum {
#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
......@@ -439,6 +413,13 @@ typedef enum {
#define SCTP_ADDR6_PEERSUPP 0x00000004 /* IPv6 address is supported by
peer */
/* Reasons to retransmit. */
typedef enum {
SCTP_RETRANSMIT_T3_RTX,
SCTP_RETRANSMIT_FAST_RTX,
SCTP_RETRANSMIT_PMTU_DISCOVERY,
} sctp_retransmit_reason_t;
/* Reasons to lower cwnd. */
typedef enum {
SCTP_LOWER_CWND_T3_RTX,
......@@ -448,4 +429,3 @@ typedef enum {
} sctp_lower_cwnd_t;
#endif /* __sctp_constants_h__ */
/* 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-2003 International Business Machines, Corp.
* Copyright (c) 2001-2003 Intel Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -36,7 +36,9 @@
* 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>
* Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -147,7 +149,9 @@ extern int sctp_primitive_REQUESTHEARTBEAT(sctp_association_t *, void *arg);
/*
* sctp_crc32c.c
*/
extern __u32 count_crc(__u8 *ptr, __u16 count);
extern __u32 sctp_start_cksum(__u8 *ptr, __u16 count);
extern __u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 cksum);
extern __u32 sctp_end_cksum(__u32 cksum);
/*
* sctp_input.c
......@@ -162,6 +166,9 @@ 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 *);
extern sctp_association_t *__sctp_lookup_association(const union sctp_addr *,
const union sctp_addr *,
struct sctp_transport **);
/*
* sctp_hashdriver.c
......@@ -266,6 +273,7 @@ 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;
extern atomic_t sctp_dbg_objcnt_ssnmap;
/* Macros to atomically increment/decrement objcnt counters. */
#define SCTP_DBG_OBJCNT_INC(name) \
......@@ -418,6 +426,23 @@ static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
return retval;
}
/* Generate a random jitter in the range of -50% ~ +50% of input RTO. */
static inline __s32 sctp_jitter(__u32 rto)
{
static __u32 sctp_rand;
__s32 ret;
sctp_rand += jiffies;
sctp_rand ^= (sctp_rand << 12);
sctp_rand ^= (sctp_rand >> 20);
/* Choose random number from 0 to rto, then move to -50% ~ +50%
* of rto.
*/
ret = sctp_rand % rto - (rto >> 1);
return ret;
}
/* Walk through a list of TLV parameters. Don't trust the
* individual parameter lengths and instead depend on
* the chunk length to indicate when to stop. Make sure
......
......@@ -256,7 +256,7 @@ sctp_chunk_t *sctp_make_abort_user(const sctp_association_t *,
const sctp_chunk_t *,
const struct msghdr *);
sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *,
const sctp_transport_t *,
const struct sctp_transport *,
const void *payload,
const size_t paylen);
sctp_chunk_t *sctp_make_heartbeat_ack(const sctp_association_t *,
......@@ -269,6 +269,11 @@ sctp_chunk_t *sctp_make_op_error(const sctp_association_t *,
const void *payload,
size_t paylen);
void sctp_chunk_assign_tsn(sctp_chunk_t *);
void sctp_chunk_assign_ssn(sctp_chunk_t *);
int sctp_datachunks_from_user(sctp_association_t *,
const struct sctp_sndrcvinfo *,
struct msghdr *, int len,
struct sk_buff_head *);
/* Prototypes for statetable processing. */
......
......@@ -42,7 +42,7 @@
* Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
* Dajiang Zhang <dajiang.zhang@nokia.com>
* Ardelle Fan <ardelle.fan@intel.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -104,27 +104,26 @@ union sctp_addr {
/* Forward declarations for data structures. */
struct SCTP_protocol;
struct sctp_protocol;
struct SCTP_endpoint;
struct SCTP_association;
struct SCTP_transport;
struct sctp_transport;
struct SCTP_packet;
struct SCTP_chunk;
struct SCTP_inqueue;
struct SCTP_outqueue;
struct sctp_outq;
struct SCTP_bind_addr;
struct sctp_ulpq;
struct sctp_opt;
struct sctp_endpoint_common;
struct sctp_ssnmap;
typedef struct SCTP_protocol sctp_protocol_t;
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;
......@@ -133,7 +132,6 @@ typedef struct sctp_endpoint_common sctp_endpoint_common_t;
#include <net/sctp/ulpevent.h>
#include <net/sctp/ulpqueue.h>
/* Structures useful for managing bind/connect. */
typedef struct sctp_bind_bucket {
......@@ -157,7 +155,7 @@ typedef struct sctp_hashbucket {
/* The SCTP protocol structure. */
struct SCTP_protocol {
struct sctp_protocol {
/* RFC2960 Section 14. Suggested SCTP Protocol Parameter Values
*
* The following protocol parameters are RECOMMENDED:
......@@ -183,8 +181,8 @@ struct SCTP_protocol {
/* Valid.Cookie.Life - 60 seconds */
int valid_cookie_life;
/* Whether Cookie Preservative is enabled(1) or not(0) */
/* Whether Cookie Preservative is enabled(1) or not(0) */
int cookie_preserve_enable;
/* Association.Max.Retrans - 10 attempts
......@@ -239,7 +237,9 @@ struct SCTP_protocol {
* (i.e. things that depend on the address family.)
*/
struct sctp_af {
int (*queue_xmit) (struct sk_buff *skb);
int (*sctp_xmit) (struct sk_buff *skb,
struct sctp_transport *,
int ipfragok);
int (*setsockopt) (struct sock *sk,
int level,
int optname,
......@@ -250,12 +250,18 @@ struct sctp_af {
int optname,
char *optval,
int *optlen);
struct dst_entry *(*get_dst) (union sctp_addr *daddr,
struct dst_entry *(*get_dst) (sctp_association_t *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr);
void (*get_saddr) (sctp_association_t *asoc,
struct dst_entry *dst,
union sctp_addr *daddr,
union sctp_addr *saddr);
void (*copy_addrlist) (struct list_head *,
struct net_device *);
void (*dst_saddr) (union sctp_addr *saddr,
struct dst_entry *dst);
struct dst_entry *dst,
unsigned short port);
int (*cmp_addr) (const union sctp_addr *addr1,
const union sctp_addr *addr2);
void (*addr_copy) (union sctp_addr *dst,
......@@ -282,7 +288,7 @@ struct sctp_af *sctp_get_af_specific(sa_family_t);
int sctp_register_af(struct sctp_af *);
/* Protocol family functions. */
typedef struct sctp_pf {
struct sctp_pf {
void (*event_msgname)(sctp_ulpevent_t *, char *, int *);
void (*skb_msgname) (struct sk_buff *, char *, int *);
int (*af_supported) (sa_family_t);
......@@ -291,7 +297,7 @@ typedef struct sctp_pf {
struct sctp_opt *);
int (*bind_verify) (struct sctp_opt *, union sctp_addr *);
struct sctp_af *af;
} sctp_pf_t;
};
/* SCTP Socket type: UDP or TCP style. */
typedef enum {
......@@ -318,7 +324,7 @@ struct sctp_opt {
__u32 autoclose;
__u8 nodelay;
__u8 disable_fragments;
sctp_pf_t *pf;
struct sctp_pf *pf;
};
......@@ -360,7 +366,8 @@ typedef struct sctp_cookie {
struct timeval expiration;
/* Number of inbound/outbound streams which are set
* and negotiated during the INIT process. */
* and negotiated during the INIT process.
*/
__u16 sinit_num_ostreams;
__u16 sinit_max_instreams;
......@@ -426,6 +433,49 @@ typedef struct sctp_sender_hb_info {
unsigned long sent_at;
} sctp_sender_hb_info_t __attribute__((packed));
/*
* RFC 2960 1.3.2 Sequenced Delivery within Streams
*
* The term "stream" is used in SCTP to refer to a sequence of user
* messages that are to be delivered to the upper-layer protocol in
* order with respect to other messages within the same stream. This is
* in contrast to its usage in TCP, where it refers to a sequence of
* bytes (in this document a byte is assumed to be eight bits).
* ...
*
* This is the structure we use to track both our outbound and inbound
* SSN, or Stream Sequence Numbers.
*/
struct sctp_stream {
__u16 *ssn;
unsigned int len;
};
struct sctp_ssnmap {
struct sctp_stream in;
struct sctp_stream out;
int malloced;
};
struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *, __u16, __u16);
struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int priority);
void sctp_ssnmap_free(struct sctp_ssnmap *map);
void sctp_ssnmap_clear(struct sctp_ssnmap *map);
/* What is the current SSN number for this stream? */
static inline __u16 sctp_ssn_peek(struct sctp_stream *stream, __u16 id)
{
return stream->ssn[id];
}
/* Return the next SSN number for this stream. */
static inline __u16 sctp_ssn_next(struct sctp_stream *stream, __u16 id)
{
return stream->ssn[id]++;
}
/* RFC2960 1.4 Key Terms
*
* o Chunk: A unit of information within an SCTP packet, consisting of
......@@ -499,6 +549,7 @@ struct SCTP_chunk {
__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 has_ssn; /* Does this chunk have a SSN 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? */
......@@ -516,15 +567,13 @@ struct SCTP_chunk {
* 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;
struct sctp_transport *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_addrs(sctp_chunk_t *, union sctp_addr *, union sctp_addr *);
......@@ -560,7 +609,7 @@ struct SCTP_packet {
* The function we finally use to pass down to the next lower
* layer lives in the transport structure.
*/
sctp_transport_t *transport;
struct sctp_transport *transport;
/* Allow a callback for getting a high priority chunk
* bundled early into the packet (This is used for ECNE).
......@@ -575,30 +624,33 @@ struct SCTP_packet {
/* This packet contains a COOKIE-ECHO chunk. */
int has_cookie_echo;
/* SCTP cannot fragment this packet. So let ip fragment it. */
int ipfragok;
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)
typedef int (sctp_outq_thandler_t)(struct sctp_outq *, void *);
typedef int (sctp_outq_ehandler_t)(struct sctp_outq *);
typedef sctp_packet_t *(sctp_outq_ohandler_init_t)
(sctp_packet_t *,
sctp_transport_t *,
struct sctp_transport *,
__u16 sport,
__u16 dport);
typedef sctp_packet_t *(sctp_outqueue_ohandler_config_t)
typedef sctp_packet_t *(sctp_outq_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 *,
typedef sctp_xmit_t (sctp_outq_ohandler_t)(sctp_packet_t *,
sctp_chunk_t *);
typedef int (sctp_outqueue_ohandler_force_t)(sctp_packet_t *);
typedef int (sctp_outq_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;
sctp_outq_ohandler_init_t sctp_packet_init;
sctp_outq_ohandler_config_t sctp_packet_config;
sctp_outq_ohandler_t sctp_packet_append_chunk;
sctp_outq_ohandler_t sctp_packet_transmit_chunk;
sctp_outq_ohandler_force_t sctp_packet_transmit;
void sctp_packet_free(sctp_packet_t *);
......@@ -622,7 +674,7 @@ void sctp_packet_free(sctp_packet_t *);
* period.
*
*/
struct SCTP_transport {
struct sctp_transport {
/* A list of transports. */
struct list_head transports;
......@@ -692,6 +744,8 @@ struct SCTP_transport {
/* Destination */
struct dst_entry *dst;
/* Source address. */
union sctp_addr saddr;
/* 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
......@@ -771,24 +825,26 @@ struct SCTP_transport {
int malloced; /* Is this structure kfree()able? */
};
extern sctp_transport_t *sctp_transport_new(const union sctp_addr *, int);
extern sctp_transport_t *sctp_transport_init(sctp_transport_t *,
const union sctp_addr *, int);
extern void sctp_transport_set_owner(sctp_transport_t *, sctp_association_t *);
extern void sctp_transport_route(sctp_transport_t *, union sctp_addr *,
struct sctp_opt *);
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);
struct sctp_transport *sctp_transport_new(const union sctp_addr *, int);
struct sctp_transport *sctp_transport_init(struct sctp_transport *,
const union sctp_addr *, int);
void sctp_transport_set_owner(struct sctp_transport *, sctp_association_t *);
void sctp_transport_route(struct sctp_transport *, union sctp_addr *,
struct sctp_opt *);
void sctp_transport_pmtu(struct sctp_transport *);
void sctp_transport_free(struct sctp_transport *);
void sctp_transport_destroy(struct sctp_transport *);
void sctp_transport_reset_timers(struct sctp_transport *);
void sctp_transport_hold(struct sctp_transport *);
void sctp_transport_put(struct sctp_transport *);
void sctp_transport_update_rto(struct sctp_transport *, __u32);
void sctp_transport_raise_cwnd(struct sctp_transport *, __u32, __u32);
void sctp_transport_lower_cwnd(struct sctp_transport *, sctp_lower_cwnd_t);
unsigned long sctp_transport_timeout(struct sctp_transport *);
/* 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.
* SCTP. We write packets to it and read chunks from it.
*/
struct SCTP_inqueue {
/* This is actually a queue of sctp_chunk_t each
......@@ -835,7 +891,7 @@ void sctp_inqueue_set_th_handler(sctp_inqueue_t *,
*
* When free()'d, it empties itself out via output_handler().
*/
struct SCTP_outqueue {
struct sctp_outq {
sctp_association_t *asoc;
/* BUG: This really should be an array of streams.
......@@ -861,11 +917,11 @@ struct SCTP_outqueue {
* 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;
sctp_outq_ohandler_init_t *init_output;
sctp_outq_ohandler_config_t *config_output;
sctp_outq_ohandler_t *append_output;
sctp_outq_ohandler_t *build_output;
sctp_outq_ohandler_force_t *force_output;
/* How many unackd bytes do we have in-flight? */
__u32 outstanding_bytes;
......@@ -877,24 +933,24 @@ struct SCTP_outqueue {
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);
void sctp_retransmit_mark(sctp_outqueue_t *, sctp_transport_t *, __u8);
struct sctp_outq *sctp_outq_new(sctp_association_t *);
void sctp_outq_init(sctp_association_t *, struct sctp_outq *);
void sctp_outq_teardown(struct sctp_outq *);
void sctp_outq_free(struct sctp_outq*);
int sctp_outq_tail(struct sctp_outq *, sctp_chunk_t *chunk);
int sctp_outq_flush(struct sctp_outq *, int);
int sctp_outq_sack(struct sctp_outq *, sctp_sackhdr_t *);
int sctp_outq_is_empty(const struct sctp_outq *);
int sctp_outq_set_output_handlers(struct sctp_outq *,
sctp_outq_ohandler_init_t init,
sctp_outq_ohandler_config_t config,
sctp_outq_ohandler_t append,
sctp_outq_ohandler_t build,
sctp_outq_ohandler_force_t force);
void sctp_outq_restart(struct sctp_outq *);
void sctp_retransmit(struct sctp_outq *, struct sctp_transport *,
sctp_retransmit_reason_t);
void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8);
/* These bind address data fields common between endpoints and associations */
......@@ -1027,7 +1083,7 @@ struct SCTP_endpoint {
/* These are the system-wide defaults and other stuff which is
* endpoint-independent.
*/
sctp_protocol_t *proto;
struct sctp_protocol *proto;
/* Associations: A list of current associations and mappings
* to the data consumers for each association. This
......@@ -1066,10 +1122,7 @@ 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);
ep = container_of(base, sctp_endpoint_t, base);
return ep;
}
......@@ -1083,7 +1136,7 @@ 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 union sctp_addr *paddr,
sctp_transport_t **);
struct sctp_transport **);
int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *);
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const union sctp_addr *);
......@@ -1184,7 +1237,7 @@ struct SCTP_association {
* designate the connection we are currently using to
* transmit new data and most control chunks.
*/
sctp_transport_t *primary_path;
struct sctp_transport *primary_path;
/* Cache the primary path address here, when we
* need a an address for msg_name.
......@@ -1195,7 +1248,7 @@ struct SCTP_association {
* The path that we are currently using to
* transmit new data and most control chunks.
*/
sctp_transport_t *active_path;
struct sctp_transport *active_path;
/* retran_path
*
......@@ -1207,13 +1260,13 @@ struct SCTP_association {
* different from the last destination address to
* which the DATA chunk was sent.
*/
sctp_transport_t *retran_path;
struct sctp_transport *retran_path;
/* Pointer to last transport I have sent on. */
sctp_transport_t *last_sent_to;
struct sctp_transport *last_sent_to;
/* This is the last transport I have recieved DATA on. */
sctp_transport_t *last_data_from;
struct sctp_transport *last_data_from;
/*
* Mapping An array of bits or bytes indicating which out of
......@@ -1325,7 +1378,7 @@ struct SCTP_association {
struct timer_list timers[SCTP_NUM_TIMEOUT_TYPES];
/* Transport to which SHUTDOWN chunk was last sent. */
sctp_transport_t *shutdown_last_sent_to;
struct sctp_transport *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
......@@ -1408,18 +1461,15 @@ struct SCTP_association {
} defaults;
/* This tracks outbound ssn for a given stream. */
__u16 ssn[SCTP_MAX_STREAM];
struct sctp_ssnmap *ssnmap;
/* All outbound chunks go through this structure. */
sctp_outqueue_t outqueue;
struct sctp_outq 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)];
struct sctp_ulpq ulpq;
/* Need to send an ECNE Chunk? */
int need_ecne;
......@@ -1505,7 +1555,7 @@ struct SCTP_association {
*
*
* [I really think this is EXACTLY the sort of intelligence
* which already resides in SCTP_outqueue. Please move this
* which already resides in sctp_outq. Please move this
* queue and its supporting logic down there. --piggy]
*/
struct sk_buff_head addip_chunks;
......@@ -1546,10 +1596,7 @@ 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);
asoc = container_of(base, sctp_association_t, base);
return asoc;
}
......@@ -1567,18 +1614,19 @@ 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 *,
struct sctp_transport *sctp_assoc_choose_shutdown_transport(sctp_association_t *);
struct sctp_transport *sctp_assoc_lookup_paddr(const sctp_association_t *,
const union sctp_addr *);
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *,
struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *,
const union sctp_addr *address,
const int priority);
void sctp_assoc_control_transport(sctp_association_t *, sctp_transport_t *,
void sctp_assoc_control_transport(sctp_association_t *,
struct sctp_transport *,
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 union sctp_addr *,
const union sctp_addr *);
struct sctp_transport *sctp_assoc_lookup_tsn(sctp_association_t *, __u32);
struct sctp_transport *sctp_assoc_is_match(sctp_association_t *,
const union sctp_addr *,
const union sctp_addr *);
void sctp_assoc_migrate(sctp_association_t *, struct sock *);
void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src);
......@@ -1586,6 +1634,14 @@ __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);
void sctp_assoc_sync_pmtu(sctp_association_t *);
void sctp_assoc_rwnd_increase(sctp_association_t *, int);
void sctp_assoc_rwnd_decrease(sctp_association_t *, int);
int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *, int);
int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *,
sctp_cookie_t *, int);
int sctp_cmp_addr_exact(const union sctp_addr *ss1,
const union sctp_addr *ss2);
sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc);
......
/* 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-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
* These are the definitions needed for the sctp_ulpqueue type. The
* sctp_ulpqueue is the interface between the Upper Layer Protocol, or ULP,
*
* These are the definitions needed for the sctp_ulpq type. The
* sctp_ulpq 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
* 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
*
* 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>
*
* 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:
* 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.
*/
......@@ -42,46 +47,26 @@
#define __sctp_ulpqueue_h__
/* A structure to carry information to the ULP (e.g. Sockets API) */
typedef struct sctp_ulpqueue {
struct sctp_ulpq {
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 *);
};
/* Prototypes. */
struct sctp_ulpq *sctp_ulpq_new(sctp_association_t *asoc, int priority);
struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *, sctp_association_t *);
void sctp_ulpq_free(struct sctp_ulpq *);
/* Add a new DATA chunk for processing. */
int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *,
sctp_chunk_t *chunk,
int priority);
int sctp_ulpq_tail_data(struct sctp_ulpq *, 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);
int sctp_ulpq_tail_event(struct sctp_ulpq *, struct sctp_ulpevent *ev);
/* Is the ulpqueue empty. */
int sctp_ulpqueue_is_empty(sctp_ulpqueue_t *);
int sctp_ulpqueue_is_data_empty(sctp_ulpqueue_t *);
int sctp_ulpqueue_is_empty(struct sctp_ulpq *);
#endif /* __sctp_ulpqueue_h__ */
......@@ -90,4 +75,4 @@ int sctp_ulpqueue_is_data_empty(sctp_ulpqueue_t *);
......@@ -100,6 +100,14 @@ enum sctp_optname {
#define SCTP_SOCKOPT_BINDX_REM SCTP_SOCKOPT_BINDX_REM
SCTP_SOCKOPT_PEELOFF, /* peel off association. */
#define SCTP_SOCKOPT_PEELOFF SCTP_SOCKOPT_PEELOFF
SCTP_GET_PEER_ADDRS_NUM, /* Get number of peer addresss. */
#define SCTP_GET_PEER_ADDRS_NUM SCTP_GET_PEER_ADDRS_NUM
SCTP_GET_PEER_ADDRS, /* Get all peer addresss. */
#define SCTP_GET_PEER_ADDRS SCTP_GET_PEER_ADDRS
SCTP_GET_LOCAL_ADDRS_NUM, /* Get number of local addresss. */
#define SCTP_GET_LOCAL_ADDRS_NUM SCTP_GET_LOCAL_ADDRS_NUM
SCTP_GET_LOCAL_ADDRS, /* Get all local addresss. */
#define SCTP_GET_LOCAL_ADDRS SCTP_GET_LOCAL_ADDRS
};
......@@ -576,6 +584,15 @@ struct sctp_setstrm_timeout {
__u16 ssto_streamid_end;
};
/*
* 8.3 8.5 get all peer/local addresses on a socket
* This parameter struct is for getsockopt
*/
struct sctp_getaddrs {
sctp_assoc_t assoc_id;
int addr_num;
struct sockaddr_storage *addrs;
};
/* 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. */
......
......@@ -8,6 +8,7 @@
#include <linux/netdevice.h>
#include <linux/crypto.h>
#include <linux/pfkeyv2.h>
#include <linux/in6.h>
#include <net/dst.h>
#include <net/route.h>
......@@ -424,4 +425,23 @@ extern struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id);
extern struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name);
extern struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name);
static inline int
xfrm6_selector_match(struct xfrm_selector *sel, struct flowi *fl)
{
return !memcmp(fl->fl6_dst, sel->daddr.a6, sizeof(struct in6_addr)) &&
!((fl->uli_u.ports.dport^sel->dport)&sel->dport_mask) &&
!((fl->uli_u.ports.sport^sel->sport)&sel->sport_mask) &&
(fl->proto == sel->proto || !sel->proto) &&
(fl->oif == sel->ifindex || !sel->ifindex) &&
!memcmp(fl->fl6_src, sel->saddr.a6, sizeof(struct in6_addr));
}
extern int xfrm6_register_type(struct xfrm_type *type);
extern int xfrm6_unregister_type(struct xfrm_type *type);
extern struct xfrm_type *xfrm6_get_type(u8 proto);
extern struct xfrm_state *xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto);
struct xfrm_state * xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, struct in6_addr *saddr, int create);
void xfrm6_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi);
#endif /* _NET_XFRM_H */
......@@ -33,73 +33,168 @@ static ctl_table *ax25_table;
static int ax25_table_size;
static ctl_table ax25_dir_table[] = {
{NET_AX25, "ax25", NULL, 0, 0555, NULL},
{0}
{
.ctl_name = NET_AX25,
.procname = "ax25",
.maxlen = 0,
.mode = 0555,
},
{ .ctl_name = 0 }
};
static ctl_table ax25_root_table[] = {
{CTL_NET, "net", NULL, 0, 0555, ax25_dir_table},
{0}
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = ax25_dir_table
},
{ .ctl_name = 0 }
};
static const ctl_table ax25_param_table[] = {
{NET_AX25_IP_DEFAULT_MODE, "ip_default_mode",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_ipdefmode, &max_ipdefmode},
{NET_AX25_DEFAULT_MODE, "ax25_default_mode",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_axdefmode, &max_axdefmode},
{NET_AX25_BACKOFF_TYPE, "backoff_type",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_backoff, &max_backoff},
{NET_AX25_CONNECT_MODE, "connect_mode",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_conmode, &max_conmode},
{NET_AX25_STANDARD_WINDOW, "standard_window_size",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_window, &max_window},
{NET_AX25_EXTENDED_WINDOW, "extended_window_size",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_ewindow, &max_ewindow},
{NET_AX25_T1_TIMEOUT, "t1_timeout",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_t1, &max_t1},
{NET_AX25_T2_TIMEOUT, "t2_timeout",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_t2, &max_t2},
{NET_AX25_T3_TIMEOUT, "t3_timeout",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_t3, &max_t3},
{NET_AX25_IDLE_TIMEOUT, "idle_timeout",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_idle, &max_idle},
{NET_AX25_N2, "maximum_retry_count",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_n2, &max_n2},
{NET_AX25_PACLEN, "maximum_packet_length",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_paclen, &max_paclen},
{NET_AX25_PROTOCOL, "protocol",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_proto, &max_proto},
{NET_AX25_DAMA_SLAVE_TIMEOUT, "dama_slave_timeout",
NULL, sizeof(int), 0644, NULL,
&proc_dointvec_minmax, &sysctl_intvec, NULL,
&min_ds_timeout, &max_ds_timeout},
{0} /* that's all, folks! */
{
.ctl_name = NET_AX25_IP_DEFAULT_MODE,
.procname = "ip_default_mode",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_ipdefmode,
.extra2 = &max_ipdefmode
},
{
.ctl_name = NET_AX25_DEFAULT_MODE,
.procname = "ax25_default_mode",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_axdefmode,
.extra2 = &max_axdefmode
},
{
.ctl_name = NET_AX25_BACKOFF_TYPE,
.procname = "backoff_type",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_backoff,
.extra2 = &max_backoff
},
{
.ctl_name = NET_AX25_CONNECT_MODE,
.procname = "connect_mode",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_conmode,
.extra2 = &max_conmode
},
{
.ctl_name = NET_AX25_STANDARD_WINDOW,
.procname = "standard_window_size",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_window,
.extra2 = &max_window
},
{
.ctl_name = NET_AX25_EXTENDED_WINDOW,
.procname = "extended_window_size",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_ewindow,
.extra2 = &max_ewindow
},
{
.ctl_name = NET_AX25_T1_TIMEOUT,
.procname = "t1_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_t1,
.extra2 = &max_t1
},
{
.ctl_name = NET_AX25_T2_TIMEOUT,
.procname = "t2_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_t2,
.extra2 = &max_t2
},
{
.ctl_name = NET_AX25_T3_TIMEOUT,
.procname = "t3_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_t3,
.extra2 = &max_t3
},
{
.ctl_name = NET_AX25_IDLE_TIMEOUT,
.procname = "idle_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_idle,
.extra2 = &max_idle
},
{
.ctl_name = NET_AX25_N2,
.procname = "maximum_retry_count",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_n2,
.extra2 = &max_n2
},
{
.ctl_name = NET_AX25_PACLEN,
.procname = "maximum_packet_length",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_paclen,
.extra2 = &max_paclen
},
{
.ctl_name = NET_AX25_PROTOCOL,
.procname = "protocol",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_proto,
.extra2 = &max_proto
},
{
.ctl_name = NET_AX25_DAMA_SLAVE_TIMEOUT,
.procname = "dama_slave_timeout",
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_ds_timeout,
.extra2 = &max_ds_timeout
},
{ .ctl_name = 0 } /* that's all, folks! */
};
void ax25_register_sysctl(void)
......
......@@ -36,59 +36,139 @@ extern char sysctl_divert_version[];
ctl_table core_table[] = {
#ifdef CONFIG_NET
{NET_CORE_WMEM_MAX, "wmem_max",
&sysctl_wmem_max, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_RMEM_MAX, "rmem_max",
&sysctl_rmem_max, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_WMEM_DEFAULT, "wmem_default",
&sysctl_wmem_default, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_RMEM_DEFAULT, "rmem_default",
&sysctl_rmem_default, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_DEV_WEIGHT, "dev_weight",
&weight_p, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_MAX_BACKLOG, "netdev_max_backlog",
&netdev_max_backlog, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_NO_CONG_THRESH, "no_cong_thresh",
&no_cong, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_NO_CONG, "no_cong",
&no_cong, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_LO_CONG, "lo_cong",
&lo_cong, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_MOD_CONG, "mod_cong",
&mod_cong, sizeof(int), 0644, NULL,
&proc_dointvec},
{
.ctl_name = NET_CORE_WMEM_MAX,
.procname = "wmem_max",
.data = &sysctl_wmem_max,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_RMEM_MAX,
.procname = "rmem_max",
.data = &sysctl_rmem_max,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_WMEM_DEFAULT,
.procname = "wmem_default",
.data = &sysctl_wmem_default,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_RMEM_DEFAULT,
.procname = "rmem_default",
.data = &sysctl_rmem_default,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_DEV_WEIGHT,
.procname = "dev_weight",
.data = &weight_p,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_MAX_BACKLOG,
.procname = "netdev_max_backlog",
.data = &netdev_max_backlog,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_NO_CONG_THRESH,
.procname = "no_cong_thresh",
.data = &no_cong,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_NO_CONG,
.procname = "no_cong",
.data = &no_cong,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_LO_CONG,
.procname = "lo_cong",
.data = &lo_cong,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_MOD_CONG,
.procname = "mod_cong",
.data = &mod_cong,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#ifdef CONFIG_NET_FASTROUTE
{NET_CORE_FASTROUTE, "netdev_fastroute",
&netdev_fastroute, sizeof(int), 0644, NULL,
&proc_dointvec},
{
.ctl_name = NET_CORE_FASTROUTE,
.procname = "netdev_fastroute",
.data = &netdev_fastroute,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#endif
{NET_CORE_MSG_COST, "message_cost",
&net_msg_cost, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies},
{NET_CORE_MSG_BURST, "message_burst",
&net_msg_burst, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies},
{NET_CORE_OPTMEM_MAX, "optmem_max",
&sysctl_optmem_max, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_CORE_HOT_LIST_LENGTH, "hot_list_length",
&sysctl_hot_list_len, sizeof(int), 0644, NULL,
&proc_dointvec},
{
.ctl_name = NET_CORE_MSG_COST,
.procname = "message_cost",
.data = &net_msg_cost,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies
},
{
.ctl_name = NET_CORE_MSG_BURST,
.procname = "message_burst",
.data = &net_msg_burst,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies
},
{
.ctl_name = NET_CORE_OPTMEM_MAX,
.procname = "optmem_max",
.data = &sysctl_optmem_max,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_CORE_HOT_LIST_LENGTH,
.procname = "hot_list_length",
.data = &sysctl_hot_list_len,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#ifdef CONFIG_NET_DIVERT
{NET_CORE_DIVERT_VERSION, "divert_version",
(void *)sysctl_divert_version, 32, 0444, NULL,
&proc_dostring},
{
.ctl_name = NET_CORE_DIVERT_VERSION,
.procname = "divert_version",
.data = (void *)sysctl_divert_version,
.maxlen = 32,
.mode = 0444,
.proc_handler = &proc_dostring
},
#endif /* CONFIG_NET_DIVERT */
#endif /* CONFIG_NET */
{ 0 }
{ .ctl_name = 0 }
};
#endif
......@@ -1361,19 +1361,37 @@ static struct nf_sockopt_ops so_getorigdst
static struct ctl_table_header *ip_conntrack_sysctl_header;
static ctl_table ip_conntrack_table[] = {
{ NET_IP_CONNTRACK_MAX, NET_IP_CONNTRACK_MAX_NAME, &ip_conntrack_max,
sizeof(ip_conntrack_max), 0644, NULL, proc_dointvec },
{ 0 }
{
.ctl_name = NET_IP_CONNTRACK_MAX,
.procname = NET_IP_CONNTRACK_MAX_NAME,
.data = &ip_conntrack_max,
.maxlen = sizeof(ip_conntrack_max),
.mode = 0644,
.proc_handler = proc_dointvec
},
{ .ctl_name = 0 }
};
static ctl_table ip_conntrack_dir_table[] = {
{NET_IPV4, "ipv4", NULL, 0, 0555, ip_conntrack_table, 0, 0, 0, 0, 0},
{ 0 }
{
.ctl_name = NET_IPV4,
.procname = "ipv4",
.maxlen = 0,
.mode = 0555,
.child = ip_conntrack_table
},
{ .ctl_name = 0 }
};
static ctl_table ip_conntrack_root_table[] = {
{CTL_NET, "net", NULL, 0, 0555, ip_conntrack_dir_table, 0, 0, 0, 0, 0},
{ 0 }
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = ip_conntrack_dir_table
},
{ .ctl_name = 0 }
};
#endif /*CONFIG_SYSCTL*/
......
......@@ -586,19 +586,37 @@ static int sysctl_maxlen = IPQ_QMAX_DEFAULT;
static struct ctl_table_header *ipq_sysctl_header;
static ctl_table ipq_table[] = {
{ NET_IPQ_QMAX, NET_IPQ_QMAX_NAME, &sysctl_maxlen,
sizeof(sysctl_maxlen), 0644, NULL, proc_dointvec },
{ 0 }
{
.ctl_name = NET_IPQ_QMAX,
.procname = NET_IPQ_QMAX_NAME,
.data = &sysctl_maxlen,
.maxlen = sizeof(sysctl_maxlen),
.mode = 0644,
.proc_handler = proc_dointvec
},
{ .ctl_name = 0 }
};
static ctl_table ipq_dir_table[] = {
{NET_IPV4, "ipv4", NULL, 0, 0555, ipq_table, 0, 0, 0, 0, 0},
{ 0 }
{
.ctl_name = NET_IPV4,
.procname = "ipv4",
.maxlen = 0,
.mode = 0555,
.child = ipq_table
},
{ .ctl_name = 0 }
};
static ctl_table ipq_root_table[] = {
{CTL_NET, "net", NULL, 0, 0555, ipq_dir_table, 0, 0, 0, 0, 0},
{ 0 }
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = ipq_dir_table
},
{ .ctl_name = 0 }
};
static int
......
......@@ -88,144 +88,465 @@ static int ipv4_sysctl_forward_strategy(ctl_table *table, int *name, int nlen,
}
ctl_table ipv4_table[] = {
{NET_IPV4_TCP_TIMESTAMPS, "tcp_timestamps",
&sysctl_tcp_timestamps, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_TCP_WINDOW_SCALING, "tcp_window_scaling",
&sysctl_tcp_window_scaling, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_TCP_SACK, "tcp_sack",
&sysctl_tcp_sack, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_TCP_RETRANS_COLLAPSE, "tcp_retrans_collapse",
&sysctl_tcp_retrans_collapse, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_FORWARD, "ip_forward",
&ipv4_devconf.forwarding, sizeof(int), 0644, NULL,
&ipv4_sysctl_forward,&ipv4_sysctl_forward_strategy},
{NET_IPV4_DEFAULT_TTL, "ip_default_ttl",
&sysctl_ip_default_ttl, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_AUTOCONFIG, "ip_autoconfig",
&ipv4_config.autoconfig, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_NO_PMTU_DISC, "ip_no_pmtu_disc",
&ipv4_config.no_pmtu_disc, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_NONLOCAL_BIND, "ip_nonlocal_bind",
&sysctl_ip_nonlocal_bind, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_TCP_SYN_RETRIES, "tcp_syn_retries",
&sysctl_tcp_syn_retries, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_SYNACK_RETRIES, "tcp_synack_retries",
&sysctl_tcp_synack_retries, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_MAX_ORPHANS, "tcp_max_orphans",
&sysctl_tcp_max_orphans, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_MAX_TW_BUCKETS, "tcp_max_tw_buckets",
&sysctl_tcp_max_tw_buckets, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_IPFRAG_HIGH_THRESH, "ipfrag_high_thresh",
&sysctl_ipfrag_high_thresh, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_IPFRAG_LOW_THRESH, "ipfrag_low_thresh",
&sysctl_ipfrag_low_thresh, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_DYNADDR, "ip_dynaddr",
&sysctl_ip_dynaddr, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_IPFRAG_TIME, "ipfrag_time",
&sysctl_ipfrag_time, sizeof(int), 0644, NULL, &proc_dointvec_jiffies,
&sysctl_jiffies},
{NET_IPV4_TCP_KEEPALIVE_TIME, "tcp_keepalive_time",
&sysctl_tcp_keepalive_time, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies},
{NET_IPV4_TCP_KEEPALIVE_PROBES, "tcp_keepalive_probes",
&sysctl_tcp_keepalive_probes, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_TCP_KEEPALIVE_INTVL, "tcp_keepalive_intvl",
&sysctl_tcp_keepalive_intvl, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies},
{NET_IPV4_TCP_RETRIES1, "tcp_retries1",
&sysctl_tcp_retries1, sizeof(int), 0644, NULL, &proc_dointvec_minmax,
&sysctl_intvec, NULL, NULL, &tcp_retr1_max},
{NET_IPV4_TCP_RETRIES2, "tcp_retries2",
&sysctl_tcp_retries2, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_TCP_FIN_TIMEOUT, "tcp_fin_timeout",
&sysctl_tcp_fin_timeout, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies},
{
.ctl_name = NET_IPV4_TCP_TIMESTAMPS,
.procname = "tcp_timestamps",
.data = &sysctl_tcp_timestamps,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_WINDOW_SCALING,
.procname = "tcp_window_scaling",
.data = &sysctl_tcp_window_scaling,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_SACK,
.procname = "tcp_sack",
.data = &sysctl_tcp_sack,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_RETRANS_COLLAPSE,
.procname = "tcp_retrans_collapse",
.data = &sysctl_tcp_retrans_collapse,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_FORWARD,
.procname = "ip_forward",
.data = &ipv4_devconf.forwarding,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &ipv4_sysctl_forward,
.strategy = &ipv4_sysctl_forward_strategy
},
{
.ctl_name = NET_IPV4_DEFAULT_TTL,
.procname = "ip_default_ttl",
.data = &sysctl_ip_default_ttl,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_AUTOCONFIG,
.procname = "ip_autoconfig",
.data = &ipv4_config.autoconfig,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_NO_PMTU_DISC,
.procname = "ip_no_pmtu_disc",
.data = &ipv4_config.no_pmtu_disc,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_NONLOCAL_BIND,
.procname = "ip_nonlocal_bind",
.data = &sysctl_ip_nonlocal_bind,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_SYN_RETRIES,
.procname = "tcp_syn_retries",
.data = &sysctl_tcp_syn_retries,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_SYNACK_RETRIES,
.procname = "tcp_synack_retries",
.data = &sysctl_tcp_synack_retries,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_MAX_ORPHANS,
.procname = "tcp_max_orphans",
.data = &sysctl_tcp_max_orphans,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_MAX_TW_BUCKETS,
.procname = "tcp_max_tw_buckets",
.data = &sysctl_tcp_max_tw_buckets,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_IPFRAG_HIGH_THRESH,
.procname = "ipfrag_high_thresh",
.data = &sysctl_ipfrag_high_thresh,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_IPFRAG_LOW_THRESH,
.procname = "ipfrag_low_thresh",
.data = &sysctl_ipfrag_low_thresh,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_DYNADDR,
.procname = "ip_dynaddr",
.data = &sysctl_ip_dynaddr,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_IPFRAG_TIME,
.procname = "ipfrag_time",
.data = &sysctl_ipfrag_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_IPV4_TCP_KEEPALIVE_TIME,
.procname = "tcp_keepalive_time",
.data = &sysctl_tcp_keepalive_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_IPV4_TCP_KEEPALIVE_PROBES,
.procname = "tcp_keepalive_probes",
.data = &sysctl_tcp_keepalive_probes,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_KEEPALIVE_INTVL,
.procname = "tcp_keepalive_intvl",
.data = &sysctl_tcp_keepalive_intvl,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_IPV4_TCP_RETRIES1,
.procname = "tcp_retries1",
.data = &sysctl_tcp_retries1,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra2 = &tcp_retr1_max
},
{
.ctl_name = NET_IPV4_TCP_RETRIES2,
.procname = "tcp_retries2",
.data = &sysctl_tcp_retries2,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_TCP_FIN_TIMEOUT,
.procname = "tcp_fin_timeout",
.data = &sysctl_tcp_fin_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
#ifdef CONFIG_SYN_COOKIES
{NET_TCP_SYNCOOKIES, "tcp_syncookies",
&sysctl_tcp_syncookies, sizeof(int), 0644, NULL, &proc_dointvec},
{
.ctl_name = NET_TCP_SYNCOOKIES,
.procname = "tcp_syncookies",
.data = &sysctl_tcp_syncookies,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#endif
{NET_TCP_TW_RECYCLE, "tcp_tw_recycle",
&sysctl_tcp_tw_recycle, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_ABORT_ON_OVERFLOW, "tcp_abort_on_overflow",
&sysctl_tcp_abort_on_overflow, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_STDURG, "tcp_stdurg", &sysctl_tcp_stdurg,
sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_RFC1337, "tcp_rfc1337", &sysctl_tcp_rfc1337,
sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_MAX_SYN_BACKLOG, "tcp_max_syn_backlog", &sysctl_max_syn_backlog,
sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_LOCAL_PORT_RANGE, "ip_local_port_range",
&sysctl_local_port_range, sizeof(sysctl_local_port_range), 0644,
NULL, &proc_dointvec_minmax, &sysctl_intvec, NULL,
ip_local_port_range_min, ip_local_port_range_max },
{NET_IPV4_ICMP_ECHO_IGNORE_ALL, "icmp_echo_ignore_all",
&sysctl_icmp_echo_ignore_all, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_ICMP_ECHO_IGNORE_BROADCASTS, "icmp_echo_ignore_broadcasts",
&sysctl_icmp_echo_ignore_broadcasts, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES, "icmp_ignore_bogus_error_responses",
&sysctl_icmp_ignore_bogus_error_responses, sizeof(int), 0644, NULL,
&proc_dointvec},
{NET_IPV4_ROUTE, "route", NULL, 0, 0555, ipv4_route_table},
{
.ctl_name = NET_TCP_TW_RECYCLE,
.procname = "tcp_tw_recycle",
.data = &sysctl_tcp_tw_recycle,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_ABORT_ON_OVERFLOW,
.procname = "tcp_abort_on_overflow",
.data = &sysctl_tcp_abort_on_overflow,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_STDURG,
.procname = "tcp_stdurg",
.data = &sysctl_tcp_stdurg,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_RFC1337,
.procname = "tcp_rfc1337",
.data = &sysctl_tcp_rfc1337,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_MAX_SYN_BACKLOG,
.procname = "tcp_max_syn_backlog",
.data = &sysctl_max_syn_backlog,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_LOCAL_PORT_RANGE,
.procname = "ip_local_port_range",
.data = &sysctl_local_port_range,
.maxlen = sizeof(sysctl_local_port_range),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = ip_local_port_range_min,
.extra2 = ip_local_port_range_max
},
{
.ctl_name = NET_IPV4_ICMP_ECHO_IGNORE_ALL,
.procname = "icmp_echo_ignore_all",
.data = &sysctl_icmp_echo_ignore_all,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_ICMP_ECHO_IGNORE_BROADCASTS,
.procname = "icmp_echo_ignore_broadcasts",
.data = &sysctl_icmp_echo_ignore_broadcasts,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_ICMP_IGNORE_BOGUS_ERROR_RESPONSES,
.procname = "icmp_ignore_bogus_error_responses",
.data = &sysctl_icmp_ignore_bogus_error_responses,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_ROUTE,
.procname = "route",
.maxlen = 0,
.mode = 0555,
.child = ipv4_route_table
},
#ifdef CONFIG_IP_MULTICAST
{NET_IPV4_IGMP_MAX_MEMBERSHIPS, "igmp_max_memberships",
&sysctl_igmp_max_memberships, sizeof(int), 0644, NULL, &proc_dointvec},
{
.ctl_name = NET_IPV4_IGMP_MAX_MEMBERSHIPS,
.procname = "igmp_max_memberships",
.data = &sysctl_igmp_max_memberships,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#endif
{NET_IPV4_INET_PEER_THRESHOLD, "inet_peer_threshold",
&inet_peer_threshold, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_INET_PEER_MINTTL, "inet_peer_minttl",
&inet_peer_minttl, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies},
{NET_IPV4_INET_PEER_MAXTTL, "inet_peer_maxttl",
&inet_peer_maxttl, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies},
{NET_IPV4_INET_PEER_GC_MINTIME, "inet_peer_gc_mintime",
&inet_peer_gc_mintime, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies},
{NET_IPV4_INET_PEER_GC_MAXTIME, "inet_peer_gc_maxtime",
&inet_peer_gc_maxtime, sizeof(int), 0644, NULL,
&proc_dointvec_jiffies, &sysctl_jiffies},
{NET_TCP_ORPHAN_RETRIES, "tcp_orphan_retries",
&sysctl_tcp_orphan_retries, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_FACK, "tcp_fack",
&sysctl_tcp_fack, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_REORDERING, "tcp_reordering",
&sysctl_tcp_reordering, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_ECN, "tcp_ecn",
&sysctl_tcp_ecn, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_DSACK, "tcp_dsack",
&sysctl_tcp_dsack, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_MEM, "tcp_mem",
&sysctl_tcp_mem, sizeof(sysctl_tcp_mem), 0644, NULL, &proc_dointvec},
{NET_TCP_WMEM, "tcp_wmem",
&sysctl_tcp_wmem, sizeof(sysctl_tcp_wmem), 0644, NULL, &proc_dointvec},
{NET_TCP_RMEM, "tcp_rmem",
&sysctl_tcp_rmem, sizeof(sysctl_tcp_rmem), 0644, NULL, &proc_dointvec},
{NET_TCP_APP_WIN, "tcp_app_win",
&sysctl_tcp_app_win, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_ADV_WIN_SCALE, "tcp_adv_win_scale",
&sysctl_tcp_adv_win_scale, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_ICMP_RATELIMIT, "icmp_ratelimit",
&sysctl_icmp_ratelimit, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_IPV4_ICMP_RATEMASK, "icmp_ratemask",
&sysctl_icmp_ratemask, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_TW_REUSE, "tcp_tw_reuse",
&sysctl_tcp_tw_reuse, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_FRTO, "tcp_frto",
&sysctl_tcp_frto, sizeof(int), 0644, NULL, &proc_dointvec},
{NET_TCP_LOW_LATENCY, "tcp_low_latency",
&sysctl_tcp_low_latency, sizeof(int), 0644, NULL, &proc_dointvec},
{0}
{
.ctl_name = NET_IPV4_INET_PEER_THRESHOLD,
.procname = "inet_peer_threshold",
.data = &inet_peer_threshold,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_INET_PEER_MINTTL,
.procname = "inet_peer_minttl",
.data = &inet_peer_minttl,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_IPV4_INET_PEER_MAXTTL,
.procname = "inet_peer_maxttl",
.data = &inet_peer_maxttl,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_IPV4_INET_PEER_GC_MINTIME,
.procname = "inet_peer_gc_mintime",
.data = &inet_peer_gc_mintime,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_IPV4_INET_PEER_GC_MAXTIME,
.procname = "inet_peer_gc_maxtime",
.data = &inet_peer_gc_maxtime,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_TCP_ORPHAN_RETRIES,
.procname = "tcp_orphan_retries",
.data = &sysctl_tcp_orphan_retries,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_FACK,
.procname = "tcp_fack",
.data = &sysctl_tcp_fack,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_REORDERING,
.procname = "tcp_reordering",
.data = &sysctl_tcp_reordering,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_ECN,
.procname = "tcp_ecn",
.data = &sysctl_tcp_ecn,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_DSACK,
.procname = "tcp_dsack",
.data = &sysctl_tcp_dsack,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_MEM,
.procname = "tcp_mem",
.data = &sysctl_tcp_mem,
.maxlen = sizeof(sysctl_tcp_mem),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_WMEM,
.procname = "tcp_wmem",
.data = &sysctl_tcp_wmem,
.maxlen = sizeof(sysctl_tcp_wmem),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_RMEM,
.procname = "tcp_rmem",
.data = &sysctl_tcp_rmem,
.maxlen = sizeof(sysctl_tcp_rmem),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_APP_WIN,
.procname = "tcp_app_win",
.data = &sysctl_tcp_app_win,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_ADV_WIN_SCALE,
.procname = "tcp_adv_win_scale",
.data = &sysctl_tcp_adv_win_scale,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_ICMP_RATELIMIT,
.procname = "icmp_ratelimit",
.data = &sysctl_icmp_ratelimit,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_IPV4_ICMP_RATEMASK,
.procname = "icmp_ratemask",
.data = &sysctl_icmp_ratemask,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_TW_REUSE,
.procname = "tcp_tw_reuse",
.data = &sysctl_tcp_tw_reuse,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_FRTO,
.procname = "tcp_frto",
.data = &sysctl_tcp_frto,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_TCP_LOW_LATENCY,
.procname = "tcp_low_latency",
.data = &sysctl_tcp_low_latency,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{ .ctl_name = 0 }
};
#endif /* CONFIG_SYSCTL */
#include <net/xfrm.h>
#include <linux/pfkeyv2.h>
#include <linux/ipsec.h>
#include <net/ipv6.h>
/* Each xfrm_state may be linked to two tables:
......@@ -219,7 +220,8 @@ xfrm_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
if (daddr == x->id.daddr.xfrm4_addr &&
if (x->props.family == AF_INET &&
daddr == x->id.daddr.xfrm4_addr &&
x->props.reqid == tmpl->reqid &&
(saddr == x->props.saddr.xfrm4_addr || !saddr || !x->props.saddr.xfrm4_addr) &&
tmpl->mode == x->props.mode &&
......@@ -282,6 +284,7 @@ xfrm_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
x->id = tmpl->id;
if (x->id.daddr.xfrm4_addr == 0)
x->id.daddr.xfrm4_addr = daddr;
x->props.family = AF_INET;
x->props.saddr = tmpl->saddr;
if (x->props.saddr.xfrm4_addr == 0)
x->props.saddr.xfrm4_addr = saddr;
......@@ -317,7 +320,12 @@ xfrm_state_find(u32 daddr, u32 saddr, struct flowi *fl, struct xfrm_tmpl *tmpl,
void xfrm_state_insert(struct xfrm_state *x)
{
unsigned h = ntohl(x->id.daddr.xfrm4_addr);
unsigned h = 0;
if (x->props.family == AF_INET)
h = ntohl(x->id.daddr.xfrm4_addr);
else if (x->props.family == AF_INET6)
h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]);
h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
......@@ -325,7 +333,10 @@ void xfrm_state_insert(struct xfrm_state *x)
list_add(&x->bydst, xfrm_state_bydst+h);
atomic_inc(&x->refcnt);
h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
if (x->props.family == AF_INET)
h = ntohl(x->id.daddr.xfrm4_addr^x->id.spi^x->id.proto);
else
h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
......@@ -382,7 +393,8 @@ xfrm_state_lookup(u32 daddr, u32 spi, u8 proto)
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_byspi+h, byspi) {
if (spi == x->id.spi &&
if (x->props.family == AF_INET &&
spi == x->id.spi &&
daddr == x->id.daddr.xfrm4_addr &&
proto == x->id.proto) {
atomic_inc(&x->refcnt);
......@@ -405,7 +417,8 @@ xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create)
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
if (daddr == x->id.daddr.xfrm4_addr &&
if (x->props.family == AF_INET &&
daddr == x->id.daddr.xfrm4_addr &&
mode == x->props.mode &&
proto == x->id.proto &&
saddr == x->props.saddr.xfrm4_addr &&
......@@ -432,6 +445,7 @@ xfrm_find_acq(u8 mode, u16 reqid, u8 proto, u32 daddr, u32 saddr, int create)
x0->km.state = XFRM_STATE_ACQ;
x0->id.daddr.xfrm4_addr = daddr;
x0->id.proto = proto;
x0->props.family = AF_INET;
x0->props.mode = mode;
x0->props.reqid = reqid;
x0->lft.hard_add_expires_seconds = ACQ_EXPIRES;
......@@ -591,8 +605,14 @@ int xfrm_check_selectors(struct xfrm_state **x, int n, struct flowi *fl)
int i;
for (i=0; i<n; i++) {
if (!xfrm4_selector_match(&x[i]->sel, fl))
if (x[i]->props.family == AF_INET &&
!xfrm4_selector_match(&x[i]->sel, fl))
return -EINVAL;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
if (x[i]->props.family == AF_INET6 &&
!xfrm6_selector_match(&x[i]->sel, fl))
return -EINVAL;
#endif
}
return 0;
}
......@@ -701,3 +721,119 @@ void __init xfrm_state_init(void)
INIT_LIST_HEAD(&xfrm_state_byspi[i]);
}
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct xfrm_state *
xfrm6_state_lookup(struct in6_addr *daddr, u32 spi, u8 proto)
{
unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]^spi^proto);
struct xfrm_state *x;
h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_byspi+h, byspi) {
if (x->props.family == AF_INET6 &&
spi == x->id.spi &&
!ipv6_addr_cmp(daddr, (struct in6_addr *)x->id.daddr.a6) &&
proto == x->id.proto) {
atomic_inc(&x->refcnt);
spin_unlock_bh(&xfrm_state_lock);
return x;
}
}
spin_unlock_bh(&xfrm_state_lock);
return NULL;
}
struct xfrm_state *
xfrm6_find_acq(u8 mode, u16 reqid, u8 proto, struct in6_addr *daddr, struct in6_addr *saddr, int create)
{
struct xfrm_state *x, *x0;
unsigned h = ntohl(daddr->s6_addr32[2]^daddr->s6_addr32[3]);
h = (h ^ (h>>16)) % XFRM_DST_HSIZE;
x0 = NULL;
spin_lock_bh(&xfrm_state_lock);
list_for_each_entry(x, xfrm_state_bydst+h, bydst) {
if (x->props.family == AF_INET6 &&
!memcmp(daddr, x->id.daddr.a6, sizeof(struct in6_addr)) &&
mode == x->props.mode &&
proto == x->id.proto &&
!memcmp(saddr, x->props.saddr.a6, sizeof(struct in6_addr)) &&
reqid == x->props.reqid &&
x->km.state == XFRM_STATE_ACQ) {
if (!x0)
x0 = x;
if (x->id.spi)
continue;
x0 = x;
break;
}
}
if (x0) {
atomic_inc(&x0->refcnt);
} else if (create && (x0 = xfrm_state_alloc()) != NULL) {
memcpy(x0->sel.daddr.a6, daddr, sizeof(struct in6_addr));
memcpy(x0->sel.saddr.a6, saddr, sizeof(struct in6_addr));
x0->sel.prefixlen_d = 128;
x0->sel.prefixlen_s = 128;
memcpy(x0->props.saddr.a6, saddr, sizeof(struct in6_addr));
x0->km.state = XFRM_STATE_ACQ;
memcpy(x0->id.daddr.a6, daddr, sizeof(struct in6_addr));
x0->id.proto = proto;
x0->props.family = AF_INET6;
x0->props.mode = mode;
x0->props.reqid = reqid;
x0->lft.hard_add_expires_seconds = ACQ_EXPIRES;
atomic_inc(&x0->refcnt);
mod_timer(&x0->timer, jiffies + ACQ_EXPIRES*HZ);
atomic_inc(&x0->refcnt);
list_add_tail(&x0->bydst, xfrm_state_bydst+h);
wake_up(&km_waitq);
}
spin_unlock_bh(&xfrm_state_lock);
return x0;
}
void
xfrm6_alloc_spi(struct xfrm_state *x, u32 minspi, u32 maxspi)
{
u32 h;
struct xfrm_state *x0;
if (x->id.spi)
return;
if (minspi == maxspi) {
x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, minspi, x->id.proto);
if (x0) {
xfrm_state_put(x0);
return;
}
x->id.spi = minspi;
} else {
u32 spi = 0;
minspi = ntohl(minspi);
maxspi = ntohl(maxspi);
for (h=0; h<maxspi-minspi+1; h++) {
spi = minspi + net_random()%(maxspi-minspi+1);
x0 = xfrm6_state_lookup((struct in6_addr*)x->id.daddr.a6, htonl(spi), x->id.proto);
if (x0 == NULL)
break;
xfrm_state_put(x0);
}
x->id.spi = htonl(spi);
}
if (x->id.spi) {
spin_lock_bh(&xfrm_state_lock);
h = ntohl(x->id.daddr.a6[2]^x->id.daddr.a6[3]^x->id.spi^x->id.proto);
h = (h ^ (h>>10) ^ (h>>20)) % XFRM_DST_HSIZE;
list_add(&x->byspi, xfrm_state_byspi+h);
atomic_inc(&x->refcnt);
spin_unlock_bh(&xfrm_state_lock);
wake_up(&km_waitq);
}
}
#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */
......@@ -11,3 +11,5 @@ ipv6-objs := af_inet6.o ip6_output.o ip6_input.o addrconf.o sit.o \
ip6_flowlabel.o ipv6_syms.o
obj-$(CONFIG_NETFILTER) += netfilter/
obj-y += xfrm_policy.o
......@@ -4,6 +4,7 @@
#include <net/ipv6.h>
#include <net/addrconf.h>
#include <net/ip6_route.h>
#include <net/xfrm.h>
EXPORT_SYMBOL(ipv6_addr_type);
EXPORT_SYMBOL(icmpv6_send);
......@@ -25,3 +26,6 @@ EXPORT_SYMBOL(inet6_getname);
EXPORT_SYMBOL(inet6_ioctl);
EXPORT_SYMBOL(ipv6_get_saddr);
EXPORT_SYMBOL(ipv6_chk_addr);
EXPORT_SYMBOL(xfrm6_register_type);
EXPORT_SYMBOL(xfrm6_unregister_type);
EXPORT_SYMBOL(xfrm6_get_type);
......@@ -628,19 +628,37 @@ static int sysctl_maxlen = IPQ_QMAX_DEFAULT;
static struct ctl_table_header *ipq_sysctl_header;
static ctl_table ipq_table[] = {
{ NET_IPQ_QMAX, NET_IPQ_QMAX_NAME, &sysctl_maxlen,
sizeof(sysctl_maxlen), 0644, NULL, proc_dointvec },
{ 0 }
{
.ctl_name = NET_IPQ_QMAX,
.procname = NET_IPQ_QMAX_NAME,
.data = &sysctl_maxlen,
.maxlen = sizeof(sysctl_maxlen),
.mode = 0644,
.proc_handler = proc_dointvec
},
{ .ctl_name = 0 }
};
static ctl_table ipq_dir_table[] = {
{NET_IPV6, "ipv6", NULL, 0, 0555, ipq_table, 0, 0, 0, 0, 0},
{ 0 }
{
.ctl_name = NET_IPV6,
.procname = "ipv6",
.maxlen = 0,
.mode = 0555,
.child = ipq_table
},
{ .ctl_name = 0 }
};
static ctl_table ipq_root_table[] = {
{CTL_NET, "net", NULL, 0, 0555, ipq_dir_table, 0, 0, 0, 0, 0},
{ 0 }
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = ipq_dir_table
},
{ .ctl_name = 0 }
};
static int
......
......@@ -20,24 +20,54 @@ extern ctl_table ipv6_icmp_table[];
#ifdef CONFIG_SYSCTL
ctl_table ipv6_table[] = {
{NET_IPV6_ROUTE, "route", NULL, 0, 0555, ipv6_route_table},
{NET_IPV6_ICMP, "icmp", NULL, 0, 0500, ipv6_icmp_table},
{NET_IPV6_BINDV6ONLY, "bindv6only",
&sysctl_ipv6_bindv6only, sizeof(int), 0644, NULL, &proc_dointvec},
{0}
{
.ctl_name = NET_IPV6_ROUTE,
.procname = "route",
.maxlen = 0,
.mode = 0555,
.child = ipv6_route_table
},
{
.ctl_name = NET_IPV6_ICMP,
.procname = "icmp",
.maxlen = 0,
.mode = 0500,
.child = ipv6_icmp_table
},
{
.ctl_name = NET_IPV6_BINDV6ONLY,
.procname = "bindv6only",
.data = &sysctl_ipv6_bindv6only,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{ .ctl_name = 0 }
};
#ifdef MODULE
static struct ctl_table_header *ipv6_sysctl_header;
static ctl_table ipv6_net_table[] = {
{NET_IPV6, "ipv6", NULL, 0, 0555, ipv6_table},
{0}
{
.ctl_name = NET_IPV6,
.procname = "ipv6",
.maxlen = 0,
.mode = 0555,
.child = ipv6_table
},
{ .ctl_name = 0 }
};
static ctl_table ipv6_root_table[] = {
{CTL_NET, "net", NULL, 0, 0555, ipv6_net_table},
{0}
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = ipv6_net_table
},
{ .ctl_name = 0 }
};
void ipv6_sysctl_register(void)
......
#include <net/xfrm.h>
#include <net/ip.h>
static struct xfrm_type *xfrm6_type_map[256];
static rwlock_t xfrm6_type_lock = RW_LOCK_UNLOCKED;
int xfrm6_register_type(struct xfrm_type *type)
{
int err = 0;
write_lock(&xfrm6_type_lock);
if (xfrm6_type_map[type->proto] == NULL)
xfrm6_type_map[type->proto] = type;
else
err = -EEXIST;
write_unlock(&xfrm6_type_lock);
return err;
}
int xfrm6_unregister_type(struct xfrm_type *type)
{
int err = 0;
write_lock(&xfrm6_type_lock);
if (xfrm6_type_map[type->proto] != type)
err = -ENOENT;
else
xfrm6_type_map[type->proto] = NULL;
write_unlock(&xfrm6_type_lock);
return err;
}
struct xfrm_type *xfrm6_get_type(u8 proto)
{
struct xfrm_type *type;
read_lock(&xfrm6_type_lock);
type = xfrm6_type_map[proto];
if (type && !try_module_get(type->owner))
type = NULL;
read_unlock(&xfrm6_type_lock);
return type;
}
......@@ -99,60 +99,175 @@ static int do_devname(ctl_table *table, int write, struct file *filp,
/* One file */
static ctl_table irda_table[] = {
{ DISCOVERY, "discovery", &sysctl_discovery,
sizeof(int), 0644, NULL, &proc_dointvec },
{ DEVNAME, "devname", sysctl_devname,
65, 0644, NULL, &do_devname, &sysctl_string},
{
.ctl_name = DISCOVERY,
.procname = "discovery",
.data = &sysctl_discovery,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = DEVNAME,
.procname = "devname",
.data = sysctl_devname,
.maxlen = 65,
.mode = 0644,
.proc_handler = &do_devname,
.strategy = &sysctl_string
},
#ifdef CONFIG_IRDA_DEBUG
{ DEBUG, "debug", &irda_debug,
sizeof(int), 0644, NULL, &proc_dointvec },
{
.ctl_name = DEBUG,
.procname = "debug",
.data = &irda_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#endif
#ifdef CONFIG_IRDA_FAST_RR
{ FAST_POLL, "fast_poll_increase", &sysctl_fast_poll_increase,
sizeof(int), 0644, NULL, &proc_dointvec },
{
.ctl_name = FAST_POLL,
.procname = "fast_poll_increase",
.data = &sysctl_fast_poll_increase,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
#endif
{ DISCOVERY_SLOTS, "discovery_slots", &sysctl_discovery_slots,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_discovery_slots, &max_discovery_slots },
{ DISCOVERY_TIMEOUT, "discovery_timeout", &sysctl_discovery_timeout,
sizeof(int), 0644, NULL, &proc_dointvec },
{ SLOT_TIMEOUT, "slot_timeout", &sysctl_slot_timeout,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_slot_timeout, &max_slot_timeout },
{ MAX_BAUD_RATE, "max_baud_rate", &sysctl_max_baud_rate,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_max_baud_rate, &max_max_baud_rate },
{ MIN_TX_TURN_TIME, "min_tx_turn_time", &sysctl_min_tx_turn_time,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_min_tx_turn_time, &max_min_tx_turn_time },
{ MAX_TX_DATA_SIZE, "max_tx_data_size", &sysctl_max_tx_data_size,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_max_tx_data_size, &max_max_tx_data_size },
{ MAX_TX_WINDOW, "max_tx_window", &sysctl_max_tx_window,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_max_tx_window, &max_max_tx_window },
{ MAX_NOREPLY_TIME, "max_noreply_time", &sysctl_max_noreply_time,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_max_noreply_time, &max_max_noreply_time },
{ WARN_NOREPLY_TIME, "warn_noreply_time", &sysctl_warn_noreply_time,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_warn_noreply_time, &max_warn_noreply_time },
{ LAP_KEEPALIVE_TIME, "lap_keepalive_time", &sysctl_lap_keepalive_time,
sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec,
NULL, &min_lap_keepalive_time, &max_lap_keepalive_time },
{ 0 }
{
.ctl_name = DISCOVERY_SLOTS,
.procname = "discovery_slots",
.data = &sysctl_discovery_slots,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_discovery_slots,
.extra2 = &max_discovery_slots
},
{
.ctl_name = DISCOVERY_TIMEOUT,
.procname = "discovery_timeout",
.data = &sysctl_discovery_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = SLOT_TIMEOUT,
.procname = "slot_timeout",
.data = &sysctl_slot_timeout,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_slot_timeout,
.extra2 = &max_slot_timeout
},
{
.ctl_name = MAX_BAUD_RATE,
.procname = "max_baud_rate",
.data = &sysctl_max_baud_rate,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_max_baud_rate,
.extra2 = &max_max_baud_rate
},
{
.ctl_name = MIN_TX_TURN_TIME,
.procname = "min_tx_turn_time",
.data = &sysctl_min_tx_turn_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_min_tx_turn_time,
.extra2 = &max_min_tx_turn_time
},
{
.ctl_name = MAX_TX_DATA_SIZE,
.procname = "max_tx_data_size",
.data = &sysctl_max_tx_data_size,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_max_tx_data_size,
.extra2 = &max_max_tx_data_size
},
{
.ctl_name = MAX_TX_WINDOW,
.procname = "max_tx_window",
.data = &sysctl_max_tx_window,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_max_tx_window,
.extra2 = &max_max_tx_window
},
{
.ctl_name = MAX_NOREPLY_TIME,
.procname = "max_noreply_time",
.data = &sysctl_max_noreply_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_max_noreply_time,
.extra2 = &max_max_noreply_time
},
{
.ctl_name = WARN_NOREPLY_TIME,
.procname = "warn_noreply_time",
.data = &sysctl_warn_noreply_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_warn_noreply_time,
.extra2 = &max_warn_noreply_time
},
{
.ctl_name = LAP_KEEPALIVE_TIME,
.procname = "lap_keepalive_time",
.data = &sysctl_lap_keepalive_time,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_minmax,
.strategy = &sysctl_intvec,
.extra1 = &min_lap_keepalive_time,
.extra2 = &max_lap_keepalive_time
},
{ .ctl_name = 0 }
};
/* One directory */
static ctl_table irda_net_table[] = {
{ NET_IRDA, "irda", NULL, 0, 0555, irda_table },
{ 0 }
{
.ctl_name = NET_IRDA,
.procname = "irda",
.maxlen = 0,
.mode = 0555,
.child = irda_table
},
{ .ctl_name = 0 }
};
/* The parent directory */
static ctl_table irda_root_table[] = {
{ CTL_NET, "net", NULL, 0, 0555, irda_net_table },
{ 0 }
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = irda_net_table
},
{ .ctl_name = 0 }
};
static struct ctl_table_header *irda_table_header;
......
......@@ -9,6 +9,7 @@
* Authors: Maxim Giryaev <gem@asplinux.ru>
* David S. Miller <davem@redhat.com>
* Alexey Kuznetsov <kuznet@ms2.inr.ac.ru>
* Kunihiro Ishiguro <kunihiro@ipinfusion.com>
*/
#include <linux/config.h>
......@@ -351,7 +352,9 @@ static int verify_address_len(void *p)
struct sadb_address *sp = p;
struct sockaddr *addr = (struct sockaddr *)(sp + 1);
struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
int len;
switch (addr->sa_family) {
......@@ -362,7 +365,7 @@ static int verify_address_len(void *p)
sp->sadb_address_prefixlen > 32)
return -EINVAL;
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
len = sizeof(*sp) + sizeof(*sin6) + (sizeof(uint64_t) - 1);
len /= sizeof(uint64_t);
......@@ -370,7 +373,7 @@ static int verify_address_len(void *p)
sp->sadb_address_prefixlen > 128)
return -EINVAL;
break;
#endif
default:
/* It is user using kernel to keep track of security
* associations for another protocol, such as
......@@ -400,7 +403,11 @@ static int present_and_same_family(struct sadb_address *src,
d_addr = (struct sockaddr *)(dst + 1);
if (s_addr->sa_family != d_addr->sa_family)
return 0;
if (s_addr->sa_family != AF_INET)
if (s_addr->sa_family != AF_INET
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
&& s_addr->sa_family != AF_INET6
#endif
)
return 0;
return 1;
......@@ -497,25 +504,27 @@ static uint8_t pfkey_proto_from_xfrm(uint8_t proto)
return (proto ? proto : IPSEC_PROTO_ANY);
}
static xfrm_address_t *pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
xfrm_address_t *xaddr)
static int pfkey_sadb_addr2xfrm_addr(struct sadb_address *addr,
xfrm_address_t *xaddr)
{
switch (((struct sockaddr*)(addr + 1))->sa_family) {
case AF_INET:
xaddr->xfrm4_addr =
((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr;
((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr;
if (addr->sadb_address_prefixlen)
xaddr->xfrm4_mask = htonl(~0 << (32 - addr->sadb_address_prefixlen));
break;
return AF_INET;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
memcpy(xaddr->a6,
&((struct sockaddr_in6*)(addr + 1))->sin6_addr,
sizeof(xaddr->a6));
&((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
sizeof(struct in6_addr));
return AF_INET6;
#endif
default:
return NULL;
return 0;
}
return xaddr;
/* NOTREACHED */
}
static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **ext_hdrs)
......@@ -540,11 +549,15 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **
switch (((struct sockaddr *)(addr + 1))->sa_family) {
case AF_INET:
x = xfrm_state_lookup(((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr,
x = xfrm_state_lookup(((struct sockaddr_in *)(addr + 1))->sin_addr.s_addr,
sa->sadb_sa_spi, proto);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
/* XXX handle IPv6 */
x = xfrm6_state_lookup(&((struct sockaddr_in6 *)(addr + 1))->sin6_addr,
sa->sadb_sa_spi, proto);
break;
#endif
default:
x = NULL;
break;
......@@ -554,6 +567,22 @@ static struct xfrm_state *pfkey_xfrm_state_lookup(struct sadb_msg *hdr, void **
}
#define PFKEY_ALIGN8(a) (1 + (((a) - 1) | (8 - 1)))
static int
pfkey_sockaddr_size(sa_family_t family)
{
switch (family) {
case AF_INET:
return PFKEY_ALIGN8(sizeof(struct sockaddr_in));
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
return PFKEY_ALIGN8(sizeof(struct sockaddr_in6));
#endif
default:
return 0;
}
/* NOTREACHED */
}
static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys, int hsc)
{
struct sk_buff *skb;
......@@ -563,9 +592,19 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
struct sadb_address *addr;
struct sadb_key *key;
struct sadb_x_sa2 *sa2;
struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
int size;
int auth_key_size = 0;
int encrypt_key_size = 0;
int sockaddr_size;
/* address family check */
sockaddr_size = pfkey_sockaddr_size(x->props.family);
if (!sockaddr_size)
ERR_PTR(-EINVAL);
/* base, SA, (lifetime (HSC),) address(SD), (address(P),)
key(AE), (identity(SD),) (sensitivity)> */
......@@ -574,13 +613,18 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
((hsc & 1) ? sizeof(struct sadb_lifetime) : 0) +
((hsc & 2) ? sizeof(struct sadb_lifetime) : 0) +
sizeof(struct sadb_address)*2 +
sizeof(struct sockaddr_in)*2 + /* XXX */
sockaddr_size*2 +
sizeof(struct sadb_x_sa2);
/* XXX identity & sensitivity */
/* identity & sensitivity */
if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr)
size += sizeof(struct sadb_address) +
sizeof(struct sockaddr_in); /* XXX */
if ((x->props.family == AF_INET &&
x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr)
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|| (x->props.family == AF_INET6 &&
memcmp (x->sel.saddr.a6, x->props.saddr.a6, sizeof (struct in6_addr)))
#endif
)
size += sizeof(struct sadb_address) + sockaddr_size;
if (add_keys) {
if (x->aalg && x->aalg->alg_key_len) {
......@@ -664,50 +708,113 @@ static struct sk_buff * pfkey_xfrm_state2msg(struct xfrm_state *x, int add_keys,
lifetime->sadb_lifetime_usetime = x->curlft.use_time;
/* src address */
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
/* "if the ports are non-zero, then the sadb_address_proto field,
normally zero, MUST be filled in with the transport
protocol's number." - RFC2367 */
addr->sadb_address_proto = 0;
addr->sadb_address_prefixlen = 32; /* XXX */
addr->sadb_address_reserved = 0;
((struct sockaddr_in*)(addr + 1))->sin_family = AF_INET;
((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr =
x->props.saddr.xfrm4_addr;
if (x->props.family == AF_INET) {
addr->sadb_address_prefixlen = 32;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
addr->sadb_address_prefixlen = 128;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, x->props.saddr.a6,
sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();
/* dst address */
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = 0;
addr->sadb_address_prefixlen = 32; /* XXX */
addr->sadb_address_reserved = 0;
((struct sockaddr_in*)(addr + 1))->sin_family = AF_INET;
((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr =
x->id.daddr.xfrm4_addr;
if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) {
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
addr->sadb_address_len =
(sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
if (x->props.family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
if (x->sel.saddr.xfrm4_addr != x->props.saddr.xfrm4_addr) {
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
addr->sadb_address_proto =
pfkey_proto_from_xfrm(x->sel.proto);
addr->sadb_address_prefixlen = x->sel.prefixlen_s;
addr->sadb_address_reserved = 0;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->sel.saddr.xfrm4_addr;
sin->sin_port = x->sel.sport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
addr->sadb_address_prefixlen = 128;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, x->id.daddr.a6, sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
if (memcmp (x->sel.saddr.a6, x->props.saddr.a6,
sizeof(struct in6_addr))) {
addr = (struct sadb_address *) skb_put(skb,
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
addr->sadb_address_proto = pfkey_proto_from_xfrm(x->sel.proto);
addr->sadb_address_prefixlen = x->sel.prefixlen_s;
addr->sadb_address_reserved = 0;
((struct sockaddr_in*)(addr + 1))->sin_family = AF_INET;
((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr =
x->sel.saddr.xfrm4_addr;
((struct sockaddr_in*)(addr + 1))->sin_port =
x->sel.sport;
addr->sadb_address_exttype = SADB_EXT_ADDRESS_PROXY;
addr->sadb_address_proto =
pfkey_proto_from_xfrm(x->sel.proto);
addr->sadb_address_prefixlen = x->sel.prefixlen_s;
addr->sadb_address_reserved = 0;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = x->sel.sport;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, x->sel.saddr.a6,
sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
}
#endif
else
BUG();
/* auth key */
if (add_keys && auth_key_size) {
......@@ -797,6 +904,7 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
return ERR_PTR(-EINVAL);
key = ext_hdrs[SADB_EXT_KEY_ENCRYPT-1];
if (key != NULL &&
sa->sadb_sa_encrypt != SADB_EALG_NULL &&
((key->sadb_key_bits+7) / 8 == 0 ||
(key->sadb_key_bits+7) / 8 > key->sadb_key_len * sizeof(uint64_t)))
return ERR_PTR(-EINVAL);
......@@ -864,8 +972,10 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
}
/* x->algo.flags = sa->sadb_sa_flags; */
pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
&x->props.saddr);
x->props.family = pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
&x->props.saddr);
if (!x->props.family)
goto out;
pfkey_sadb_addr2xfrm_addr((struct sadb_address *) ext_hdrs[SADB_EXT_ADDRESS_DST-1],
&x->id.daddr);
......@@ -885,7 +995,20 @@ static struct xfrm_state * pfkey_msg2xfrm_state(struct sadb_msg *hdr,
x->sel.prefixlen_s = addr->sadb_address_prefixlen;
}
x->type = xfrm_get_type(proto);
switch (x->props.family) {
case AF_INET:
x->type = xfrm_get_type(proto);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
x->type = xfrm6_get_type(proto);
break;
#endif
default:
x->type = NULL;
break;
}
if (x->type == NULL)
goto out;
if (x->type->init_state(x, NULL))
......@@ -912,8 +1035,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
{
struct sk_buff *resp_skb;
struct sadb_x_sa2 *sa2;
struct sadb_address *addr;
struct sockaddr_in *saddr, *daddr;
struct sadb_address *saddr, *daddr;
struct sadb_msg *out_hdr;
struct xfrm_state *x;
u8 mode;
......@@ -936,13 +1058,27 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
reqid = 0;
}
addr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
saddr = (struct sockaddr_in*)(addr + 1);
addr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
daddr = (struct sockaddr_in*)(addr + 1);
saddr = ext_hdrs[SADB_EXT_ADDRESS_SRC-1];
daddr = ext_hdrs[SADB_EXT_ADDRESS_DST-1];
switch (((struct sockaddr *)(saddr + 1))->sa_family) {
case AF_INET:
x = xfrm_find_acq(mode, reqid, proto,
((struct sockaddr_in *)(daddr + 1))->sin_addr.s_addr,
((struct sockaddr_in *)(saddr + 1))->sin_addr.s_addr, 1);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
x = xfrm6_find_acq(mode, reqid, proto,
&((struct sockaddr_in6 *)(daddr + 1))->sin6_addr,
&((struct sockaddr_in6 *)(saddr + 1))->sin6_addr, 1);
break;
#endif
default:
x = NULL;
break;
}
x = xfrm_find_acq(mode, reqid, proto, daddr->sin_addr.s_addr,
saddr->sin_addr.s_addr, 1);
if (x == NULL)
return -ENOENT;
......@@ -960,7 +1096,18 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
min_spi = htonl(0x100);
max_spi = htonl(0x0fffffff);
}
xfrm_alloc_spi(x, min_spi, max_spi);
switch (x->props.family) {
case AF_INET:
xfrm_alloc_spi(x, min_spi, max_spi);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
xfrm6_alloc_spi(x, min_spi, max_spi);
break;
#endif
default:
break;
}
if (x->id.spi)
resp_skb = pfkey_xfrm_state2msg(x, 0, 3);
}
......@@ -1028,9 +1175,23 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr,
/* XXX there is race condition */
x1 = pfkey_xfrm_state_lookup(hdr, ext_hdrs);
if (!x1) {
x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
x->id.daddr.xfrm4_addr,
x->props.saddr.xfrm4_addr, 0);
switch (x->props.family) {
case AF_INET:
x1 = xfrm_find_acq(x->props.mode, x->props.reqid, x->id.proto,
x->id.daddr.xfrm4_addr,
x->props.saddr.xfrm4_addr, 0);
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
x1 = xfrm6_find_acq(x->props.mode, x->props.reqid, x->id.proto,
(struct in6_addr*)x->id.daddr.a6,
(struct in6_addr*)x->props.saddr.a6, 0);
break;
#endif
default:
x1 = NULL;
break;
}
if (x1 && x1->id.spi != x->id.spi && x1->id.spi) {
xfrm_state_put(x1);
x1 = NULL;
......@@ -1339,7 +1500,10 @@ static int
parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
{
struct xfrm_tmpl *t = xp->xfrm_vec + xp->xfrm_nr;
struct sockaddr_in *addr;
struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
if (xp->xfrm_nr >= XFRM_MAX_DEPTH)
return -ELOOP;
......@@ -1361,10 +1525,32 @@ parse_ipsecrequest(struct xfrm_policy *xp, struct sadb_x_ipsecrequest *rq)
/* addresses present only in tunnel mode */
if (t->mode) {
addr = (void*)(rq+1);
t->saddr.xfrm4_addr = addr->sin_addr.s_addr;
addr++;
t->id.daddr.xfrm4_addr = addr->sin_addr.s_addr;
switch (xp->family) {
case AF_INET:
sin = (void*)(rq+1);
if (sin->sin_family != AF_INET)
return -EINVAL;
t->saddr.xfrm4_addr = sin->sin_addr.s_addr;
sin++;
if (sin->sin_family != AF_INET)
return -EINVAL;
t->id.daddr.xfrm4_addr = sin->sin_addr.s_addr;
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
sin6 = (void *)(rq+1);
if (sin6->sin6_family != AF_INET6)
return -EINVAL;
memcpy(t->saddr.a6, &sin6->sin6_addr, sizeof(struct in6_addr));
sin6++;
if (sin6->sin6_family != AF_INET6)
return -EINVAL;
memcpy(t->id.daddr.a6, &sin6->sin6_addr, sizeof(struct in6_addr));
break;
#endif
default:
return -EINVAL;
}
}
/* No way to set this via kame pfkey */
t->aalgos = t->ealgos = t->calgos = ~0;
......@@ -1388,28 +1574,54 @@ parse_ipsecrequests(struct xfrm_policy *xp, struct sadb_x_policy *pol)
return 0;
}
static struct sk_buff * pfkey_xfrm_policy2msg(struct xfrm_policy *xp, int dir)
static int pfkey_xfrm_policy2msg_size(struct xfrm_policy *xp)
{
int sockaddr_size = pfkey_sockaddr_size(xp->family);
int socklen = (xp->family == AF_INET ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6));
return sizeof(struct sadb_msg) +
(sizeof(struct sadb_lifetime) * 3) +
(sizeof(struct sadb_address) * 2) +
(sockaddr_size * 2) +
sizeof(struct sadb_x_policy) +
(xp->xfrm_nr * (sizeof(struct sadb_x_ipsecrequest) +
(socklen * 2)));
}
static struct sk_buff * pfkey_xfrm_policy2msg_prep(struct xfrm_policy *xp)
{
struct sk_buff *skb;
int size;
size = pfkey_xfrm_policy2msg_size(xp);
skb = alloc_skb(size + 16, GFP_ATOMIC);
if (skb == NULL)
return ERR_PTR(-ENOBUFS);
return skb;
}
static void pfkey_xfrm_policy2msg(struct sk_buff *skb, struct xfrm_policy *xp, int dir)
{
struct sadb_msg *hdr;
struct sadb_address *addr;
struct sadb_lifetime *lifetime;
struct sadb_x_policy *pol;
struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
int i;
int size;
int sockaddr_size = pfkey_sockaddr_size(xp->family);
int socklen = (xp->family == AF_INET ?
sizeof(struct sockaddr_in) :
sizeof(struct sockaddr_in6));
size = sizeof(struct sadb_msg) +
sizeof(struct sadb_lifetime) * 3 +
sizeof(struct sadb_address)*2 +
sizeof(struct sockaddr_in)*2 + /* XXX */
sizeof(struct sadb_x_policy) +
xp->xfrm_nr*(sizeof(struct sadb_x_ipsecrequest) +
sizeof(struct sockaddr_in)*2);
skb = alloc_skb(size + 16, GFP_ATOMIC);
if (skb == NULL)
return ERR_PTR(-ENOBUFS);
size = pfkey_xfrm_policy2msg_size(xp);
/* call should fill header later */
hdr = (struct sadb_msg *) skb_put(skb, sizeof(struct sadb_msg));
......@@ -1417,35 +1629,66 @@ static struct sk_buff * pfkey_xfrm_policy2msg(struct xfrm_policy *xp, int dir)
/* src address */
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
addr->sadb_address_prefixlen = xp->selector.prefixlen_s;
addr->sadb_address_reserved = 0;
/* src address */
sin = (struct sockaddr_in*)(addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = xp->selector.saddr.xfrm4_addr;
sin->sin_port = xp->selector.sport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
if (xp->family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = xp->selector.saddr.xfrm4_addr;
sin->sin_port = xp->selector.sport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (xp->family == AF_INET6) {
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = xp->selector.sport;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, xp->selector.saddr.a6,
sizeof(struct in6_addr));;
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();
/* dst address */
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = pfkey_proto_from_xfrm(xp->selector.proto);
addr->sadb_address_prefixlen = xp->selector.prefixlen_d;
addr->sadb_address_reserved = 0;
sin = (struct sockaddr_in*)(addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = xp->selector.daddr.xfrm4_addr;
sin->sin_port = xp->selector.dport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
if (xp->family == AF_INET) {
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = xp->selector.daddr.xfrm4_addr;
sin->sin_port = xp->selector.dport;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (xp->family == AF_INET6) {
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = xp->selector.dport;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, xp->selector.daddr.a6,
sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();
/* hard time */
lifetime = (struct sadb_lifetime *) skb_put(skb,
......@@ -1498,9 +1741,9 @@ static struct sk_buff * pfkey_xfrm_policy2msg(struct xfrm_policy *xp, int dir)
req_size = sizeof(struct sadb_x_ipsecrequest);
if (t->mode)
req_size += 2*sizeof(struct sockaddr_in);
else
size -= 2*sizeof(struct sockaddr_in);
req_size += 2*socklen;
else
size -= 2*socklen;
rq = (void*)skb_put(skb, req_size);
pol->sadb_x_policy_len += req_size/8;
rq->sadb_x_ipsecrequest_len = req_size;
......@@ -1513,21 +1756,45 @@ static struct sk_buff * pfkey_xfrm_policy2msg(struct xfrm_policy *xp, int dir)
rq->sadb_x_ipsecrequest_level = IPSEC_LEVEL_USE;
rq->sadb_x_ipsecrequest_reqid = t->reqid;
if (t->mode) {
sin = (void*)(rq+1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = t->saddr.xfrm4_addr;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
sin++;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = t->id.daddr.xfrm4_addr;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
switch (xp->family) {
case AF_INET:
sin = (void*)(rq+1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = t->saddr.xfrm4_addr;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
sin++;
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = t->id.daddr.xfrm4_addr;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
break;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
case AF_INET6:
sin6 = (void*)(rq+1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, t->saddr.a6,
sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
sin6++;
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr, t->id.daddr.a6,
sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
break;
#endif
default:
break;
}
}
}
hdr->sadb_msg_len = size / sizeof(uint64_t);
hdr->sadb_msg_reserved = atomic_read(&xp->refcnt);
return skb;
}
static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, void **ext_hdrs)
......@@ -1559,10 +1826,14 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
XFRM_POLICY_BLOCK : XFRM_POLICY_ALLOW);
sa = ext_hdrs[SADB_EXT_ADDRESS_SRC-1],
pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
xp->family = pfkey_sadb_addr2xfrm_addr(sa, &xp->selector.saddr);
if (!xp->family) {
err = -EINVAL;
goto out;
}
xp->selector.prefixlen_s = sa->sadb_address_prefixlen;
xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
xp->selector.sport = ((struct sockaddr_in*)(sa+1))->sin_port;
xp->selector.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (xp->selector.sport)
xp->selector.sport_mask = ~0;
......@@ -1575,7 +1846,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
*/
xp->selector.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
xp->selector.dport = ((struct sockaddr_in*)(sa+1))->sin_port;
xp->selector.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (xp->selector.dport)
xp->selector.dport_mask = ~0;
......@@ -1600,7 +1871,7 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
(err = parse_ipsecrequests(xp, pol)) < 0)
goto out;
out_skb = pfkey_xfrm_policy2msg(xp, pol->sadb_x_policy_dir-1);
out_skb = pfkey_xfrm_policy2msg_prep(xp);
if (IS_ERR(out_skb)) {
err = PTR_ERR(out_skb);
goto out;
......@@ -1613,6 +1884,8 @@ static int pfkey_spdadd(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
goto out;
}
pfkey_xfrm_policy2msg(out_skb, xp, pol->sadb_x_policy_dir-1);
xfrm_pol_put(xp);
out_hdr = (struct sadb_msg *) out_skb->data;
......@@ -1655,7 +1928,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
pfkey_sadb_addr2xfrm_addr(sa, &sel.saddr);
sel.prefixlen_s = sa->sadb_address_prefixlen;
sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
sel.sport = ((struct sockaddr_in*)(sa+1))->sin_port;
sel.sport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (sel.sport)
sel.sport_mask = ~0;
......@@ -1663,7 +1936,7 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
pfkey_sadb_addr2xfrm_addr(sa, &sel.daddr);
sel.prefixlen_d = sa->sadb_address_prefixlen;
sel.proto = pfkey_proto_to_xfrm(sa->sadb_address_proto);
sel.dport = ((struct sockaddr_in*)(sa+1))->sin_port;
sel.dport = ((struct sockaddr_in *)(sa+1))->sin_port;
if (sel.dport)
sel.dport_mask = ~0;
......@@ -1673,11 +1946,12 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, struct sadb_msg
err = 0;
out_skb = pfkey_xfrm_policy2msg(xp, pol->sadb_x_policy_dir-1);
out_skb = pfkey_xfrm_policy2msg_prep(xp);
if (IS_ERR(out_skb)) {
err = PTR_ERR(out_skb);
goto out;
}
pfkey_xfrm_policy2msg(out_skb, xp, pol->sadb_x_policy_dir-1);
out_hdr = (struct sadb_msg *) out_skb->data;
out_hdr->sadb_msg_version = hdr->sadb_msg_version;
......@@ -1715,11 +1989,12 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, struct sadb_msg *h
err = 0;
out_skb = pfkey_xfrm_policy2msg(xp, pol->sadb_x_policy_dir-1);
out_skb = pfkey_xfrm_policy2msg_prep(xp);
if (IS_ERR(out_skb)) {
err = PTR_ERR(out_skb);
goto out;
}
pfkey_xfrm_policy2msg(out_skb, xp, pol->sadb_x_policy_dir-1);
out_hdr = (struct sadb_msg *) out_skb->data;
out_hdr->sadb_msg_version = hdr->sadb_msg_version;
......@@ -1746,10 +2021,12 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr)
struct sk_buff *out_skb;
struct sadb_msg *out_hdr;
out_skb = pfkey_xfrm_policy2msg(xp, dir);
out_skb = pfkey_xfrm_policy2msg_prep(xp);
if (IS_ERR(out_skb))
return PTR_ERR(out_skb);
pfkey_xfrm_policy2msg(out_skb, xp, dir);
out_hdr = (struct sadb_msg *) out_skb->data;
out_hdr->sadb_msg_version = data->hdr->sadb_msg_version;
out_hdr->sadb_msg_type = SADB_X_SPDDUMP;
......@@ -2023,12 +2300,21 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
struct sadb_msg *hdr;
struct sadb_address *addr;
struct sadb_x_policy *pol;
struct sockaddr_in *sin;
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
struct sockaddr_in6 *sin6;
#endif
int sockaddr_size;
int size;
sockaddr_size = pfkey_sockaddr_size(x->props.family);
if (!sockaddr_size)
return -EINVAL;
size = sizeof(struct sadb_msg) +
sizeof(struct sadb_address)*2 +
sizeof(struct sockaddr_in)*2 + /* XXX */
sizeof(struct sadb_x_policy);
(sizeof(struct sadb_address) * 2) +
(sockaddr_size * 2) +
sizeof(struct sadb_x_policy);
if (x->id.proto == IPPROTO_AH)
size += count_ah_combs(t);
......@@ -2051,33 +2337,71 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct
/* src address */
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_SRC;
addr->sadb_address_proto = 0;
addr->sadb_address_prefixlen = 32;
addr->sadb_address_reserved = 0;
((struct sockaddr_in*)(addr + 1))->sin_family = AF_INET;
((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr =
x->props.saddr.xfrm4_addr;
((struct sockaddr_in*)(addr + 1))->sin_port = 0;
if (x->props.family == AF_INET) {
addr->sadb_address_prefixlen = 32;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->props.saddr.xfrm4_addr;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
addr->sadb_address_prefixlen = 128;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr,
x->props.saddr.a6, sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();
/* dst address */
addr = (struct sadb_address*) skb_put(skb,
sizeof(struct sadb_address)+sizeof(struct sockaddr_in));
sizeof(struct sadb_address)+sockaddr_size);
addr->sadb_address_len =
(sizeof(struct sadb_address)+sizeof(struct sockaddr_in))/
(sizeof(struct sadb_address)+sockaddr_size)/
sizeof(uint64_t);
addr->sadb_address_exttype = SADB_EXT_ADDRESS_DST;
addr->sadb_address_proto = 0;
addr->sadb_address_prefixlen = 32;
addr->sadb_address_reserved = 0;
((struct sockaddr_in*)(addr + 1))->sin_family = AF_INET;
((struct sockaddr_in*)(addr + 1))->sin_addr.s_addr =
x->id.daddr.xfrm4_addr;
((struct sockaddr_in*)(addr + 1))->sin_port = 0;
if (x->props.family == AF_INET) {
addr->sadb_address_prefixlen = 32;
sin = (struct sockaddr_in *) (addr + 1);
sin->sin_family = AF_INET;
sin->sin_addr.s_addr = x->id.daddr.xfrm4_addr;
sin->sin_port = 0;
memset(sin->sin_zero, 0, sizeof(sin->sin_zero));
}
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
else if (x->props.family == AF_INET6) {
addr->sadb_address_prefixlen = 128;
sin6 = (struct sockaddr_in6 *) (addr + 1);
sin6->sin6_family = AF_INET6;
sin6->sin6_port = 0;
sin6->sin6_flowinfo = 0;
memcpy(&sin6->sin6_addr,
x->id.daddr.a6, sizeof(struct in6_addr));
sin6->sin6_scope_id = 0;
}
#endif
else
BUG();
pol = (struct sadb_x_policy *) skb_put(skb, sizeof(struct sadb_x_policy));
pol->sadb_x_policy_len = sizeof(struct sadb_x_policy)/sizeof(uint64_t);
......
......@@ -324,6 +324,11 @@ EXPORT_SYMBOL(xfrm_policy_walk);
EXPORT_SYMBOL(xfrm_policy_flush);
EXPORT_SYMBOL(xfrm_policy_byid);
EXPORT_SYMBOL(xfrm_policy_list);
#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
EXPORT_SYMBOL(xfrm6_state_lookup);
EXPORT_SYMBOL(xfrm6_find_acq);
EXPORT_SYMBOL(xfrm6_alloc_spi);
#endif
EXPORT_SYMBOL_GPL(xfrm_probe_algs);
EXPORT_SYMBOL_GPL(xfrm_count_auth_supported);
......
......@@ -6,7 +6,7 @@ menu "SCTP Configuration (EXPERIMENTAL)"
depends on INET && EXPERIMENTAL
config IPV6_SCTP__
bool
tristate
default y if IPV6=n
default IPV6 if IPV6
......
......@@ -10,7 +10,7 @@ sctp-y := sm_statetable.o sm_statefuns.o sm_sideeffect.o \
inqueue.o outqueue.o ulpqueue.o command.o \
tsnmap.o bind_addr.o socket.o primitive.o \
output.o input.o hashdriver.o sla1.o \
debug.o
debug.o ssnmap.o
ifeq ($(CONFIG_SCTP_ADLER32), y)
sctp-y += adler32.o
......
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2003 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -36,6 +37,7 @@
* Randall Stewart <rstewar1@email.mot.com>
* Ken Morneau <kmorneau@cisco.com>
* Qiaobing Xie <qxie1@email.mot.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.
......@@ -122,7 +124,7 @@ unsigned long update_adler32(unsigned long adler,
return (s2 << 16) + s1;
}
__u32 count_crc(__u8 *ptr, __u16 count)
__u32 sctp_start_cksum(__u8 *ptr, __u16 count)
{
/*
* Update a running Adler-32 checksum with the bytes
......@@ -146,3 +148,15 @@ __u32 count_crc(__u8 *ptr, __u16 count)
return adler;
}
__u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 adler)
{
adler = update_adler32(adler, ptr, count);
return adler;
}
__u32 sctp_end_cksum(__u32 adler)
{
return adler;
}
/* 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-2003 International Business Machines Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
......@@ -166,15 +166,10 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
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.
/* Allocate storage for the ssnmap after the inbound and outbound
* streams have been negotiated during Init.
*/
for (i = 0; i < SCTP_MAX_STREAM; i++)
asoc->ssn[i] = 0;
asoc->ssnmap = NULL;
/* Set the local window size for receive.
* This is also the rcvbuf space per association.
......@@ -252,15 +247,15 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
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))
sctp_outq_init(asoc, &asoc->outqueue);
sctp_outq_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_ulpq_init(&asoc->ulpq, asoc))
goto fail_init;
/* Set up the tsn tracking. */
......@@ -296,7 +291,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
*/
void sctp_association_free(sctp_association_t *asoc)
{
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_endpoint_t *ep;
struct list_head *pos, *temp;
int i;
......@@ -310,14 +305,17 @@ void sctp_association_free(sctp_association_t *asoc)
asoc->base.dead = 1;
/* Dispose of any data lying around in the outqueue. */
sctp_outqueue_free(&asoc->outqueue);
sctp_outq_free(&asoc->outqueue);
/* Dispose of any pending messages for the upper layer. */
sctp_ulpqueue_free(&asoc->ulpq);
sctp_ulpq_free(&asoc->ulpq);
/* Dispose of any pending chunks on the inqueue. */
sctp_inqueue_free(&asoc->base.inqueue);
/* Free ssnmap storage. */
sctp_ssnmap_free(asoc->ssnmap);
/* Clean up the bound address list. */
sctp_bind_addr_free(&asoc->base.bind_addr);
......@@ -339,7 +337,7 @@ void sctp_association_free(sctp_association_t *asoc)
/* Release the transport structures. */
list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports);
transport = list_entry(pos, struct sctp_transport, transports);
list_del(pos);
sctp_transport_free(transport);
}
......@@ -365,11 +363,11 @@ static void sctp_association_destroy(sctp_association_t *asoc)
/* Add a transport address to an association. */
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
const union sctp_addr *addr,
int priority)
struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *asoc,
const union sctp_addr *addr,
int priority)
{
sctp_transport_t *peer;
struct sctp_transport *peer;
sctp_opt_t *sp;
unsigned short port;
......@@ -392,8 +390,8 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
sctp_transport_set_owner(peer, asoc);
/* Cache a route for the transport. */
sctp_transport_route(peer, NULL, sctp_sk(asoc->base.sk));
/* Initialize the pmtu of the transport. */
sctp_transport_pmtu(peer);
/* If this is the first transport addr on this association,
* initialize the association PMTU to the peer's PMTU.
......@@ -478,16 +476,16 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
}
/* Lookup a transport by address. */
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc,
struct sctp_transport *sctp_assoc_lookup_paddr(const sctp_association_t *asoc,
const union sctp_addr *address)
{
sctp_transport_t *t;
struct sctp_transport *t;
struct list_head *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);
t = list_entry(pos, struct sctp_transport, transports);
if (sctp_cmp_addr_exact(address, &t->ipaddr))
return t;
}
......@@ -500,13 +498,13 @@ sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc,
* Select and update the new active and retran paths.
*/
void sctp_assoc_control_transport(sctp_association_t *asoc,
sctp_transport_t *transport,
struct sctp_transport *transport,
sctp_transport_cmd_t command,
sctp_sn_error_t error)
{
sctp_transport_t *t = NULL;
sctp_transport_t *first;
sctp_transport_t *second;
struct sctp_transport *t = NULL;
struct sctp_transport *first;
struct sctp_transport *second;
sctp_ulpevent_t *event;
struct list_head *pos;
int spc_state = 0;
......@@ -524,7 +522,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
break;
default:
BUG();
return;
};
/* Generate and send a SCTP_PEER_ADDR_CHANGE notification to the
......@@ -534,7 +532,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
(struct sockaddr_storage *) &transport->ipaddr,
0, spc_state, error, GFP_ATOMIC);
if (event)
sctp_ulpqueue_tail_event(&asoc->ulpq, event);
sctp_ulpq_tail_event(&asoc->ulpq, event);
/* Select new active and retran paths. */
......@@ -547,7 +545,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
first = NULL; second = NULL;
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports);
t = list_entry(pos, struct sctp_transport, transports);
if (!t->active)
continue;
......@@ -631,11 +629,6 @@ __u32 __sctp_association_get_tsn_block(sctp_association_t *asoc, int 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
* only match themselves.
......@@ -695,12 +688,12 @@ sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc)
/*
* Find which transport this TSN was sent on.
*/
sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
struct sctp_transport *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
{
sctp_transport_t *active;
sctp_transport_t *match;
struct sctp_transport *active;
struct sctp_transport *match;
struct list_head *entry, *pos;
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_chunk_t *chunk;
__u32 key = htonl(tsn);
......@@ -734,7 +727,7 @@ sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
/* 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);
transport = list_entry(pos, struct sctp_transport, transports);
if (transport == active)
break;
......@@ -752,11 +745,11 @@ sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
}
/* Is this the association we are looking for? */
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *asoc,
const union sctp_addr *laddr,
const union sctp_addr *paddr)
struct sctp_transport *sctp_assoc_is_match(sctp_association_t *asoc,
const union sctp_addr *laddr,
const union sctp_addr *paddr)
{
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_read_lock(&asoc->base.addr_lock);
......@@ -852,8 +845,6 @@ void sctp_assoc_migrate(sctp_association_t *assoc, struct sock *newsk)
/* 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;
......@@ -872,23 +863,28 @@ void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new)
/* 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
* current next_tsn in case 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;
}
sctp_ssnmap_clear(asoc->ssnmap);
} else {
asoc->ctsn_ack_point = asoc->next_tsn - 1;
if (!asoc->ssnmap) {
/* Move the ssnmap. */
asoc->ssnmap = new->ssnmap;
new->ssnmap = NULL;
}
}
}
/* Choose the transport for sending a shutdown packet.
......@@ -896,9 +892,9 @@ void sctp_assoc_update(sctp_association_t *asoc, sctp_association_t *new)
* 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)
struct sctp_transport *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
{
sctp_transport_t *t, *next;
struct sctp_transport *t, *next;
struct list_head *head = &asoc->peer.transport_addr_list;
struct list_head *pos;
......@@ -921,7 +917,7 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
else
pos = pos->next;
t = list_entry(pos, sctp_transport_t, transports);
t = list_entry(pos, struct sctp_transport, transports);
/* Try to find an active transport. */
......@@ -947,3 +943,136 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
return t;
}
/* Update the association's pmtu and frag_point by going through all the
* transports. This routine is called when a transport's PMTU has changed.
*/
void sctp_assoc_sync_pmtu(sctp_association_t *asoc)
{
struct sctp_transport *t;
struct list_head *pos;
__u32 pmtu = 0;
if (!asoc)
return;
/* Get the lowest pmtu of all the transports. */
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, struct sctp_transport, transports);
if (!pmtu || (t->pmtu < pmtu))
pmtu = t->pmtu;
}
if (pmtu) {
asoc->pmtu = pmtu;
asoc->frag_point = pmtu - (SCTP_IP_OVERHEAD +
sizeof(sctp_data_chunk_t));
}
SCTP_DEBUG_PRINTK("%s: asoc:%p, pmtu:%d, frag_point:%d\n",
__FUNCTION__, asoc, asoc->pmtu, asoc->frag_point);
}
/* Increase asoc's rwnd by len and send any window update SACK if needed. */
void sctp_assoc_rwnd_increase(sctp_association_t *asoc, int len)
{
sctp_chunk_t *sack;
struct timer_list *timer;
if (asoc->rwnd_over) {
if (asoc->rwnd_over >= len) {
asoc->rwnd_over -= len;
} else {
asoc->rwnd += (len - asoc->rwnd_over);
asoc->rwnd_over = 0;
}
} else {
asoc->rwnd += len;
}
SCTP_DEBUG_PRINTK("%s: asoc %p rwnd increased by %d to (%u, %u) - %u\n",
__FUNCTION__, asoc, len, asoc->rwnd, asoc->rwnd_over,
asoc->a_rwnd);
/* Send a window update SACK if the rwnd has increased by at least the
* minimum of the association's PMTU and half of the receive buffer.
* The algorithm used is similar to the one described in
* Section 4.2.3.3 of RFC 1122.
*/
if ((asoc->state == SCTP_STATE_ESTABLISHED) &&
(asoc->rwnd > asoc->a_rwnd) &&
((asoc->rwnd - asoc->a_rwnd) >=
min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) {
SCTP_DEBUG_PRINTK("%s: Sending window update SACK- asoc: %p "
"rwnd: %u a_rwnd: %u\n",
__FUNCTION__, asoc, asoc->rwnd, asoc->a_rwnd);
sack = sctp_make_sack(asoc);
if (!sack)
return;
/* Update the last advertised rwnd value. */
asoc->a_rwnd = asoc->rwnd;
asoc->peer.sack_needed = 0;
asoc->peer.next_dup_tsn = 0;
sctp_outq_tail(&asoc->outqueue, sack);
/* Stop the SACK timer. */
timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
if (timer_pending(timer) && del_timer(timer))
sctp_association_put(asoc);
}
}
/* Decrease asoc's rwnd by len. */
void sctp_assoc_rwnd_decrease(sctp_association_t *asoc, int len)
{
SCTP_ASSERT(asoc->rwnd, "rwnd zero", return);
SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);
if (asoc->rwnd >= len) {
asoc->rwnd -= len;
} else {
asoc->rwnd_over = len - asoc->rwnd;
asoc->rwnd = 0;
}
SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n",
__FUNCTION__, asoc, len, asoc->rwnd, asoc->rwnd_over);
}
/* Build the bind address list for the association based on info from the
* local endpoint and the remote peer.
*/
int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *asoc, int priority)
{
sctp_scope_t scope;
int flags;
/* 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;
return sctp_bind_addr_copy(&asoc->base.bind_addr,
&asoc->ep->base.bind_addr,
scope, priority, flags);
}
/* Build the association's bind address list from the cookie. */
int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *asoc,
sctp_cookie_t *cookie, int priority)
{
int var_size2 = ntohs(cookie->peer_init->chunk_hdr.length);
int var_size3 = cookie->raw_addr_list_len;
__u8 *raw_addr_list = (__u8 *)cookie + sizeof(sctp_cookie_t) +
var_size2;
return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw_addr_list,
var_size3, asoc->ep->base.bind_addr.port,
priority);
}
......@@ -47,8 +47,8 @@ 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);
if (retval)
sctp_init_cmd_seq(retval);
return retval;
}
......
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines, Corp.
* Copyright (c) 2001-2003 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -33,6 +33,7 @@
* Written or modified by:
* Dinakaran Joseph
* Jon Grimm <jgrimm@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -135,11 +136,10 @@ __u32 crc_c[256] = {
0xBE2DA0A5, 0x4C4623A6, 0x5F16D052, 0xAD7D5351,
};
__u32 count_crc(__u8 *buffer, __u16 length)
__u32 sctp_start_cksum(__u8 *buffer, __u16 length)
{
__u32 crc32 = ~(__u32) 0;
__u32 i, result;
__u8 byte0, byte1, byte2, byte3;
__u32 i;
/* Optimize this routine to be SCTP specific, knowing how
* to skip the checksum field of the SCTP header.
......@@ -157,6 +157,24 @@ __u32 count_crc(__u8 *buffer, __u16 length)
for (i = sizeof(struct sctphdr); i < length ; i++)
CRC32C(crc32, buffer[i]);
return crc32;
}
__u32 sctp_update_cksum(__u8 *buffer, __u16 length, __u32 crc32)
{
__u32 i;
for (i = 0; i < length ; i++)
CRC32C(crc32, buffer[i]);
return crc32;
}
__u32 sctp_end_cksum(__u32 crc32)
{
__u32 result;
__u8 byte0, byte1, byte2, byte3;
result = ~crc32;
/* result now holds the negated polynomial remainder;
......@@ -183,5 +201,3 @@ __u32 count_crc(__u8 *buffer, __u16 length)
byte3);
return crc32;
}
......@@ -148,22 +148,11 @@ const char *sctp_status_tbl[] = {
/* 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. */
......@@ -178,7 +167,6 @@ const char *sctp_pname(const sctp_subtype_t id)
static const char *sctp_other_tbl[] = {
"NO_PENDING_TSN",
"ICMP_UNREACHFRAG"
};
/* Lookup "other" debug name. */
......@@ -197,12 +185,10 @@ static const char *sctp_timer_tbl[] = {
"TIMEOUT_T1_INIT",
"TIMEOUT_T2_SHUTDOWN",
"TIMEOUT_T3_RTX",
"TIMEOUT_T4_RTO",
"TIMEOUT_T5_SHUTDOWN_GUARD",
"TIMEOUT_HEARTBEAT",
"TIMEOUT_SACK",
"TIMEOUT_AUTOCLOSE",
"TIMEOUT_PMTU_RAISE",
};
/* Lookup timer debug name. */
......
......@@ -137,7 +137,6 @@ sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto,
ep->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] =
sp->rtoinfo.srto_initial;
ep->timeouts[SCTP_EVENT_TIMEOUT_T3_RTX] = 0;
ep->timeouts[SCTP_EVENT_TIMEOUT_T4_RTO] = 0;
/* sctpimpguide-05 Section 2.12.2
* If the 'T5-shutdown-guard' timer is used, it SHOULD be set to the
......@@ -152,8 +151,6 @@ sctp_endpoint_t *sctp_endpoint_init(sctp_endpoint_t *ep, sctp_protocol_t *proto,
SCTP_DEFAULT_TIMEOUT_SACK;
ep->timeouts[SCTP_EVENT_TIMEOUT_AUTOCLOSE] =
sp->autoclose * HZ;
ep->timeouts[SCTP_EVENT_TIMEOUT_PMTU_RAISE] =
SCTP_DEFAULT_TIMEOUT_PMTU_RAISE;
/* Set up the default send/receive buffer space. */
......@@ -264,7 +261,7 @@ sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep,
sctp_association_t *__sctp_endpoint_lookup_assoc(
const sctp_endpoint_t *endpoint,
const union sctp_addr *paddr,
sctp_transport_t **transport)
struct sctp_transport **transport)
{
int rport;
sctp_association_t *asoc;
......@@ -289,9 +286,10 @@ sctp_association_t *__sctp_endpoint_lookup_assoc(
}
/* 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 union sctp_addr *paddr,
sctp_transport_t **transport)
sctp_association_t *sctp_endpoint_lookup_assoc(
const sctp_endpoint_t *ep,
const union sctp_addr *paddr,
struct sctp_transport **transport)
{
sctp_association_t *asoc;
......@@ -333,7 +331,7 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep)
{
sctp_association_t *asoc;
struct sock *sk;
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_chunk_t *chunk;
sctp_inqueue_t *inqueue;
sctp_subtype_t subtype;
......
/* 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-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
......@@ -53,6 +53,9 @@
#include <linux/socket.h>
#include <linux/ip.h>
#include <linux/time.h> /* For struct timeval */
#include <net/ip.h>
#include <net/icmp.h>
#include <net/snmp.h>
#include <net/sock.h>
#include <net/xfrm.h>
#include <net/sctp/sctp.h>
......@@ -63,7 +66,7 @@ static int sctp_rcv_ootb(struct sk_buff *);
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
const union sctp_addr *laddr,
const union sctp_addr *paddr,
sctp_transport_t **transportp);
struct sctp_transport **transportp);
sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr);
......@@ -72,10 +75,19 @@ static inline int sctp_rcv_checksum(struct sk_buff *skb)
{
struct sctphdr *sh;
__u32 cmp, val;
struct sk_buff *list = skb_shinfo(skb)->frag_list;
sh = (struct sctphdr *) skb->h.raw;
cmp = ntohl(sh->checksum);
val = count_crc((__u8 *)sh, skb->len);
val = sctp_start_cksum((__u8 *)sh, skb_headlen(skb));
for (; list; list = list->next)
val = sctp_update_cksum((__u8 *)list->data, skb_headlen(list),
val);
val = sctp_end_cksum(val);
if (val != cmp) {
/* CRC failure, dump it. */
return -1;
......@@ -92,7 +104,7 @@ int sctp_rcv(struct sk_buff *skb)
sctp_association_t *asoc;
sctp_endpoint_t *ep = NULL;
sctp_endpoint_common_t *rcvr;
sctp_transport_t *transport = NULL;
struct sctp_transport *transport = NULL;
sctp_chunk_t *chunk;
struct sctphdr *sh;
union sctp_addr src;
......@@ -248,6 +260,27 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
return 0;
}
/* Handle icmp frag needed error. */
static inline void sctp_icmp_frag_needed(struct sock *sk,
sctp_association_t *asoc,
struct sctp_transport *transport,
__u32 pmtu)
{
if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) {
printk(KERN_WARNING "%s: Reported pmtu %d too low, "
"using default minimum of %d\n", __FUNCTION__, pmtu,
SCTP_DEFAULT_MINSEGMENT);
pmtu = SCTP_DEFAULT_MINSEGMENT;
}
if (!sock_owned_by_user(sk) && transport && (transport->pmtu != pmtu)) {
transport->pmtu = pmtu;
sctp_assoc_sync_pmtu(asoc);
sctp_retransmit(&asoc->outqueue, transport,
SCTP_RETRANSMIT_PMTU_DISCOVERY );
}
}
/*
* This routine is called by the ICMP module when it gets some
* sort of error condition. If err < 0 then the socket should
......@@ -263,9 +296,109 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb)
* is probably better.
*
*/
void sctp_v4_err(struct sk_buff *skb, u32 info)
void sctp_v4_err(struct sk_buff *skb, __u32 info)
{
/* This should probably involve a call to SCTPhandleICMP(). */
struct iphdr *iph = (struct iphdr *)skb->data;
struct sctphdr *sh = (struct sctphdr *)(skb->data + (iph->ihl <<2));
int type = skb->h.icmph->type;
int code = skb->h.icmph->code;
union sctp_addr saddr, daddr;
struct inet_opt *inet;
struct sock *sk = NULL;
sctp_endpoint_t *ep = NULL;
sctp_association_t *asoc = NULL;
struct sctp_transport *transport;
int err;
if (skb->len < ((iph->ihl << 2) + 8)) {
ICMP_INC_STATS_BH(IcmpInErrors);
return;
}
saddr.v4.sin_family = AF_INET;
saddr.v4.sin_port = ntohs(sh->source);
memcpy(&saddr.v4.sin_addr.s_addr, &iph->saddr, sizeof(struct in_addr));
daddr.v4.sin_family = AF_INET;
daddr.v4.sin_port = ntohs(sh->dest);
memcpy(&daddr.v4.sin_addr.s_addr, &iph->daddr, sizeof(struct in_addr));
/* Look for an association that matches the incoming ICMP error
* packet.
*/
asoc = __sctp_lookup_association(&saddr, &daddr, &transport);
if (!asoc) {
/* If there is no matching association, see if it matches any
* endpoint. This may happen for an ICMP error generated in
* response to an INIT_ACK.
*/
ep = __sctp_rcv_lookup_endpoint(&daddr);
if (!ep) {
ICMP_INC_STATS_BH(IcmpInErrors);
return;
}
}
if (asoc) {
if (ntohl(sh->vtag) != asoc->c.peer_vtag) {
ICMP_INC_STATS_BH(IcmpInErrors);
goto out;
}
sk = asoc->base.sk;
} else
sk = ep->base.sk;
sctp_bh_lock_sock(sk);
/* If too many ICMPs get dropped on busy
* servers this needs to be solved differently.
*/
if (sock_owned_by_user(sk))
NET_INC_STATS_BH(LockDroppedIcmps);
switch (type) {
case ICMP_PARAMETERPROB:
err = EPROTO;
break;
case ICMP_DEST_UNREACH:
if (code > NR_ICMP_UNREACH)
goto out_unlock;
/* PMTU discovery (RFC1191) */
if (ICMP_FRAG_NEEDED == code) {
sctp_icmp_frag_needed(sk, asoc, transport, info);
goto out_unlock;
}
err = icmp_err_convert[code].errno;
break;
case ICMP_TIME_EXCEEDED:
/* Ignore any time exceeded errors due to fragment reassembly
* timeouts.
*/
if (ICMP_EXC_FRAGTIME == code)
goto out_unlock;
err = EHOSTUNREACH;
break;
default:
goto out_unlock;
}
inet = inet_sk(sk);
if (!sock_owned_by_user(sk) && inet->recverr) {
sk->err = err;
sk->error_report(sk);
} else { /* Only an error on timeout */
sk->err_soft = err;
}
out_unlock:
sctp_bh_unlock_sock(sk);
out:
sock_put(sk);
if (asoc)
sctp_association_put(asoc);
if (ep)
sctp_endpoint_put(ep);
}
/*
......@@ -303,17 +436,17 @@ int sctp_rcv_ootb(struct sk_buff *skb)
* chunk, the receiver should silently discard the packet
* and take no further action.
*/
if (ch->type == SCTP_CID_SHUTDOWN_COMPLETE)
if (SCTP_CID_SHUTDOWN_COMPLETE == ch->type)
goto discard;
/* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR
* or a COOKIE ACK the SCTP Packet should be silently
* discarded.
*/
if (ch->type == SCTP_CID_COOKIE_ACK)
if (SCTP_CID_COOKIE_ACK == ch->type)
goto discard;
if (ch->type == SCTP_CID_ERROR) {
if (SCTP_CID_ERROR == ch->type) {
err = (sctp_errhdr_t *)(ch + sizeof(sctp_chunkhdr_t));
if (SCTP_ERROR_STALE_COOKIE == err->cause)
goto discard;
......@@ -485,12 +618,12 @@ void __sctp_unhash_established(sctp_association_t *asoc)
/* Look up an association. */
sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr,
const union sctp_addr *paddr,
sctp_transport_t **transportp)
struct sctp_transport **transportp)
{
sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb;
sctp_association_t *asoc;
sctp_transport_t *transport;
struct sctp_transport *transport;
int hash;
/* Optimize here for direct hit, only listening connections can
......@@ -521,7 +654,7 @@ sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr,
/* Look up an association. BH-safe. */
sctp_association_t *sctp_lookup_association(const union sctp_addr *laddr,
const union sctp_addr *paddr,
sctp_transport_t **transportp)
struct sctp_transport **transportp)
{
sctp_association_t *asoc;
......@@ -537,7 +670,7 @@ int sctp_has_association(const union sctp_addr *laddr,
const union sctp_addr *paddr)
{
sctp_association_t *asoc;
sctp_transport_t *transport;
struct sctp_transport *transport;
if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) {
sock_put(asoc->base.sk);
......@@ -567,7 +700,7 @@ int sctp_has_association(const union sctp_addr *laddr,
*
*/
static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb,
const union sctp_addr *laddr, sctp_transport_t **transportp)
const union sctp_addr *laddr, struct sctp_transport **transportp)
{
sctp_association_t *asoc;
union sctp_addr addr;
......@@ -627,7 +760,7 @@ static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb,
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
const union sctp_addr *paddr,
const union sctp_addr *laddr,
sctp_transport_t **transportp)
struct sctp_transport **transportp)
{
sctp_association_t *asoc;
......
......@@ -98,43 +98,22 @@ static inline void sctp_v6_err(struct sk_buff *skb,
}
/* Based on tcp_v6_xmit() in tcp_ipv6.c. */
static inline int sctp_v6_xmit(struct sk_buff *skb)
static inline int sctp_v6_xmit(struct sk_buff *skb,
struct sctp_transport *transport, int ipfragok)
{
struct sock *sk = skb->sk;
struct ipv6_pinfo *np = inet6_sk(sk);
struct flowi fl;
struct dst_entry *dst = skb->dst;
struct rt6_info *rt6 = (struct rt6_info *)dst;
struct in6_addr saddr;
int err;
fl.proto = sk->protocol;
fl.fl6_dst = &rt6->rt6i_dst.addr;
/* FIXME: Currently, ip6_route_output() doesn't fill in the source
* address in the returned route entry. So we call ipv6_get_saddr()
* to get an appropriate source address. It is possible that this address
* may not be part of the bind address list of the association.
* Once ip6_route_ouput() is fixed so that it returns a route entry
* with an appropriate source address, the following if condition can
* be removed. With ip6_route_output() returning a source address filled
* route entry, sctp_transport_route() can do real source address
* selection for v6.
/* Fill in the dest address from the route entry passed with the skb
* and the source address from the transport.
*/
if (ipv6_addr_any(&rt6->rt6i_src.addr)) {
err = ipv6_get_saddr(dst, fl.fl6_dst, &saddr);
if (err) {
printk(KERN_ERR "%s: No saddr available for "
"DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
__FUNCTION__, NIP6(fl.fl6_src));
return err;
}
fl.fl6_src = &saddr;
} else {
fl.fl6_src = &rt6->rt6i_src.addr;
}
fl.fl6_dst = &rt6->rt6i_dst.addr;
fl.fl6_src = &transport->saddr.v6.sin6_addr;
fl.fl6_flowlabel = np->flow_label;
IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel);
......@@ -147,20 +126,26 @@ static inline int sctp_v6_xmit(struct sk_buff *skb)
fl.nl_u.ip6_u.daddr = rt0->addr;
}
SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
"src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
"dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
__FUNCTION__, skb, skb->len, NIP6(fl.fl6_src),
NIP6(fl.fl6_dst));
return ip6_xmit(sk, skb, &fl, np->opt);
}
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr,
struct dst_entry *sctp_v6_get_dst(sctp_association_t *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr)
{
struct dst_entry *dst;
struct flowi fl = {
.nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, } } };
SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
__FUNCTION__, NIP6(fl.fl6_dst));
......@@ -181,12 +166,96 @@ struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr,
NIP6(&rt->rt6i_dst.addr), NIP6(&rt->rt6i_src.addr));
} else {
SCTP_DEBUG_PRINTK("NO ROUTE\n");
return NULL;
}
return dst;
}
/* Returns the number of consecutive initial bits that match in the 2 ipv6
* addresses.
*/
static inline int sctp_v6_addr_match_len(union sctp_addr *s1,
union sctp_addr *s2)
{
struct in6_addr *a1 = &s1->v6.sin6_addr;
struct in6_addr *a2 = &s2->v6.sin6_addr;
int i, j;
for (i = 0; i < 4 ; i++) {
__u32 a1xora2;
a1xora2 = a1->s6_addr32[i] ^ a2->s6_addr32[i];
if ((j = fls(ntohl(a1xora2))))
return (i * 32 + 32 - j);
}
return (i*32);
}
/* Fills in the source address(saddr) based on the destination address(daddr)
* and asoc's bind address list.
*/
void sctp_v6_get_saddr(sctp_association_t *asoc, struct dst_entry *dst,
union sctp_addr *daddr, union sctp_addr *saddr)
{
sctp_bind_addr_t *bp;
rwlock_t *addr_lock;
struct sockaddr_storage_list *laddr;
struct list_head *pos;
sctp_scope_t scope;
union sctp_addr *baddr = NULL;
__u8 matchlen = 0;
__u8 bmatchlen;
SCTP_DEBUG_PRINTK("%s: asoc:%p dst:%p "
"daddr:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
__FUNCTION__, asoc, dst, NIP6(&daddr->v6.sin6_addr));
if (!asoc) {
ipv6_get_saddr(dst, &daddr->v6.sin6_addr, &saddr->v6.sin6_addr);
SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: "
"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
NIP6(&saddr->v6.sin6_addr));
return;
}
scope = sctp_scope(daddr);
bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
/* Go through the bind address list and find the best source address
* that matches the scope of the destination address.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, list);
if ((laddr->a.sa.sa_family == AF_INET6) &&
(scope <= sctp_scope(&laddr->a))) {
bmatchlen = sctp_v6_addr_match_len(daddr, &laddr->a);
if (!baddr || (matchlen < bmatchlen)) {
baddr = &laddr->a;
matchlen = bmatchlen;
}
}
}
if (baddr) {
memcpy(saddr, baddr, sizeof(union sctp_addr));
SCTP_DEBUG_PRINTK("saddr: "
"%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
NIP6(&saddr->v6.sin6_addr));
} else {
printk(KERN_ERR "%s: asoc:%p Could not find a valid source "
"address for the "
"dest:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
__FUNCTION__, asoc, NIP6(&daddr->v6.sin6_addr));
}
sctp_read_unlock(addr_lock);
}
/* Make a copy of all potential local addresses. */
static void sctp_v6_copy_addrlist(struct list_head *addrlist,
struct net_device *dev)
......@@ -257,10 +326,12 @@ static void sctp_v6_to_sk(union sctp_addr *addr, struct sock *sk)
}
/* Initialize a sctp_addr from a dst_entry. */
static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst)
static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst,
unsigned short port)
{
struct rt6_info *rt = (struct rt6_info *)dst;
addr->sa.sa_family = AF_INET6;
addr->v6.sin6_port = port;
ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr);
}
......@@ -527,10 +598,11 @@ static struct inet6_protocol sctpv6_protocol = {
};
static struct sctp_af sctp_ipv6_specific = {
.queue_xmit = sctp_v6_xmit,
.setsockopt = ipv6_setsockopt,
.sctp_xmit = sctp_v6_xmit,
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
.get_dst = sctp_v6_get_dst,
.get_saddr = sctp_v6_get_saddr,
.copy_addrlist = sctp_v6_copy_addrlist,
.from_skb = sctp_v6_from_skb,
.from_sk = sctp_v6_from_sk,
......@@ -572,7 +644,7 @@ int sctp_v6_init(void)
/* Register the SCTP specfic AF_INET6 functions. */
sctp_register_af(&sctp_ipv6_specific);
/* Register notifier for inet6 address additions/deletions. */
/* Register notifier for inet6 address additions/deletions. */
register_inet6addr_notifier(&sctp_inetaddr_notifier);
return 0;
......
......@@ -54,6 +54,7 @@ SCTP_DBG_OBJCNT(assoc);
SCTP_DBG_OBJCNT(bind_addr);
SCTP_DBG_OBJCNT(chunk);
SCTP_DBG_OBJCNT(addr);
SCTP_DBG_OBJCNT(ssnmap);
/* An array to make it easy to pretty print the debug information
* to the proc fs.
......@@ -66,6 +67,7 @@ sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = {
SCTP_DBG_OBJCNT_ENTRY(chunk),
SCTP_DBG_OBJCNT_ENTRY(bind_addr),
SCTP_DBG_OBJCNT_ENTRY(addr),
SCTP_DBG_OBJCNT_ENTRY(ssnmap),
};
/* Callback from procfs to read out objcount information.
......
/* 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-2003 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -62,7 +62,6 @@
#include <net/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);
......@@ -81,6 +80,7 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet,
packet->ecn_capable = ecn_capable;
packet->get_prepend_chunk = prepend_handler;
packet->has_cookie_echo = 0;
packet->ipfragok = 0;
/* We might need to call the prepend_handler right away. */
if (packet_empty)
......@@ -90,7 +90,7 @@ sctp_packet_t *sctp_packet_config(sctp_packet_t *packet,
/* Initialize the packet structure. */
sctp_packet_t *sctp_packet_init(sctp_packet_t *packet,
sctp_transport_t *transport,
struct sctp_transport *transport,
__u16 sport,
__u16 dport)
{
......@@ -102,6 +102,7 @@ sctp_packet_t *sctp_packet_init(sctp_packet_t *packet,
packet->ecn_capable = 0;
packet->get_prepend_chunk = NULL;
packet->has_cookie_echo = 0;
packet->ipfragok = 0;
packet->malloced = 0;
sctp_packet_reset(packet);
return packet;
......@@ -193,6 +194,7 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk)
* transmit and rely on IP
* fragmentation.
*/
packet->ipfragok = 1;
goto append;
}
} else { /* !packet_empty */
......@@ -228,13 +230,13 @@ sctp_xmit_t sctp_packet_append_chunk(sctp_packet_t *packet, sctp_chunk_t *chunk)
}
/* All packets are sent to the network through this function from
* sctp_push_outqueue().
* sctp_outq_tail().
*
* The return value is a normal kernel error return value.
*/
int sctp_packet_transmit(sctp_packet_t *packet)
{
sctp_transport_t *transport = packet->transport;
struct sctp_transport *transport = packet->transport;
sctp_association_t *asoc = transport->asoc;
struct sctphdr *sh;
__u32 crc32;
......@@ -358,22 +360,14 @@ int sctp_packet_transmit(sctp_packet_t *packet)
* 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);
crc32 = sctp_start_cksum((__u8 *)sh, nskb->len);
crc32 = sctp_end_cksum(crc32);
/* 3) Put the resultant value into the checksum field in the
* common header, and leave the rest of the bits unchanged.
*/
sh->checksum = htonl(crc32);
/* FIXME: Delete the rest of this switch statement once phase 2
* of address selection (ipv6 support) drops in.
*/
switch (transport->ipaddr.sa.sa_family) {
case AF_INET6:
SCTP_V6(inet6_sk(sk)->daddr = transport->ipaddr.v6.sin6_addr;)
break;
};
/* IP layer ECN support
* From RFC 2481
* "The ECN-Capable Transport (ECT) bit would be set by the
......@@ -423,8 +417,10 @@ int sctp_packet_transmit(sctp_packet_t *packet)
}
dst = transport->dst;
if (!dst || dst->obsolete) {
/* The 'obsolete' field of dst is set to 2 when a dst is freed. */
if (!dst || (dst->obsolete > 1)) {
sctp_transport_route(transport, NULL, sctp_sk(sk));
sctp_assoc_sync_pmtu(asoc);
}
nskb->dst = dst_clone(transport->dst);
......@@ -433,14 +429,22 @@ int sctp_packet_transmit(sctp_packet_t *packet)
SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n",
nskb->len);
(*transport->af_specific->queue_xmit)(nskb);
(*transport->af_specific->sctp_xmit)(nskb, transport, packet->ipfragok);
out:
packet->size = SCTP_IP_OVERHEAD;
return err;
no_route:
kfree_skb(nskb);
IP_INC_STATS_BH(IpOutNoRoutes);
err = -EHOSTUNREACH;
/* FIXME: Returning the 'err' will effect all the associations
* associated with a socket, although only one of the paths of the
* association is unreachable.
* The real failure of a transport or association can be passed on
* to the user via notifications. So setting this error may not be
* required.
*/
/* err = -EHOSTUNREACH; */
goto out;
}
......@@ -473,7 +477,7 @@ static sctp_xmit_t sctp_packet_append_data(sctp_packet_t *packet,
{
sctp_xmit_t retval = SCTP_XMIT_OK;
size_t datasize, rwnd, inflight;
sctp_transport_t *transport = packet->transport;
struct sctp_transport *transport = packet->transport;
__u32 max_burst_bytes;
/* RFC 2960 6.1 Transmission of DATA Chunks
......
......@@ -2,11 +2,11 @@
* 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.
* Copyright (c) 2001-2003 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* These functions implement the outqueue class. The outqueue handles
* These functions implement the sctp_outq class. The outqueue handles
* bundling and queueing of outgoing SCTP chunks.
*
* The SCTP reference implementation is free software;
......@@ -47,39 +47,39 @@
*/
#include <linux/types.h>
#include <linux/list.h> /* For struct list_head */
#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/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,
static void sctp_check_transmitted(struct sctp_outq *q,
struct list_head *transmitted_queue,
sctp_transport_t *transport,
struct sctp_transport *transport,
sctp_sackhdr_t *sack,
__u32 highest_new_tsn);
/* Generate a new outqueue. */
sctp_outqueue_t *sctp_outqueue_new(sctp_association_t *asoc)
struct sctp_outq *sctp_outq_new(sctp_association_t *asoc)
{
sctp_outqueue_t *q;
struct sctp_outq *q;
q = t_new(sctp_outqueue_t, GFP_KERNEL);
q = t_new(struct sctp_outq, GFP_KERNEL);
if (q) {
sctp_outqueue_init(asoc, q);
sctp_outq_init(asoc, q);
q->malloced = 1;
}
return q;
}
/* Initialize an existing SCTP_outqueue. This does the boring stuff.
/* Initialize an existing sctp_outq. 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)
void sctp_outq_init(sctp_association_t *asoc, struct sctp_outq *q)
{
q->asoc = asoc;
skb_queue_head_init(&q->out);
......@@ -102,15 +102,15 @@ void sctp_outqueue_init(sctp_association_t *asoc, sctp_outqueue_t *q)
/* Free the outqueue structure and any related pending chunks.
* FIXME: Add SEND_FAILED support.
*/
void sctp_outqueue_teardown(sctp_outqueue_t *q)
void sctp_outq_teardown(struct sctp_outq *q)
{
sctp_transport_t *transport;
struct sctp_transport *transport;
struct list_head *lchunk, *pos, *temp;
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);
transport = list_entry(pos, struct sctp_transport, transports);
while ((lchunk = sctp_list_dequeue(&transport->transmitted))) {
chunk = list_entry(lchunk, sctp_chunk_t,
transmitted_list);
......@@ -125,35 +125,39 @@ void sctp_outqueue_teardown(sctp_outqueue_t *q)
sctp_free_chunk(chunk);
}
/* Throw away any leftover chunks. */
/* Throw away any chunks in the retransmit queue. */
list_for_each_safe(lchunk, temp, &q->retransmit) {
list_del(lchunk);
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
sctp_free_chunk(chunk);
}
/* Throw away any leftover data chunks. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->out)))
sctp_free_chunk(chunk);
/* Throw away any leftover control chunks. */
while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->control)))
sctp_free_chunk(chunk);
}
/* Free the outqueue structure and any related pending chunks. */
void sctp_outqueue_free(sctp_outqueue_t *q)
void sctp_outq_free(struct sctp_outq *q)
{
/* Throw away leftover chunks. */
sctp_outqueue_teardown(q);
sctp_outq_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)
/* Put a new chunk in an sctp_outq. */
int sctp_outq_tail(struct sctp_outq *q, sctp_chunk_t *chunk)
{
int error = 0;
SCTP_DEBUG_PRINTK("sctp_push_outqueue(%p, %p[%s])\n",
SCTP_DEBUG_PRINTK("sctp_outq_tail(%p, %p[%s])\n",
q, chunk, chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
: "Illegal Chunk");
......@@ -184,8 +188,7 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
default:
SCTP_DEBUG_PRINTK("outqueueing (%p, %p[%s])\n",
q, chunk,
chunk && chunk->chunk_hdr ?
q, chunk, chunk && chunk->chunk_hdr ?
sctp_cname(SCTP_ST_CHUNK(chunk->chunk_hdr->type))
: "Illegal Chunk");
......@@ -193,13 +196,13 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
q->empty = 0;
break;
};
} else {
} else
skb_queue_tail(&q->control, (struct sk_buff *) chunk);
}
if (error < 0)
return error;
error = sctp_flush_outqueue(q, 0);
error = sctp_outq_flush(q, 0);
return error;
}
......@@ -207,7 +210,7 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
/* Insert a chunk into the retransmit queue. Chunks on the retransmit
* queue are kept in order, based on the TSNs.
*/
void sctp_retransmit_insert(struct list_head *tlchunk, sctp_outqueue_t *q)
void sctp_retransmit_insert(struct list_head *tlchunk, struct sctp_outq *q)
{
struct list_head *rlchunk;
sctp_chunk_t *tchunk, *rchunk;
......@@ -230,15 +233,15 @@ void sctp_retransmit_insert(struct list_head *tlchunk, sctp_outqueue_t *q)
list_add_tail(tlchunk, &q->retransmit);
}
}
/* Mark all the eligible packets on a transport for retransmission. */
void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
void sctp_retransmit_mark(struct sctp_outq *q,
struct sctp_transport *transport,
__u8 fast_retransmit)
{
struct list_head *lchunk, *ltemp;
sctp_chunk_t *chunk;
/* Walk through the specified transmitted queue. */
list_for_each_safe(lchunk, ltemp, &transport->transmitted) {
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
......@@ -246,8 +249,9 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
/* 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.
* retransmission due to a timeout or pmtu discovery, 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)) {
......@@ -302,20 +306,27 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
/* 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)
void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport,
sctp_retransmit_reason_t reason)
{
int error = 0;
__u8 fast_retransmit = 0;
if (fast_retransmit) {
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
} else {
switch(reason) {
case SCTP_RETRANSMIT_T3_RTX:
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX);
break;
case SCTP_RETRANSMIT_FAST_RTX:
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX);
fast_retransmit = 1;
break;
default:
break;
}
sctp_retransmit_mark(q, transport, fast_retransmit);
error = sctp_flush_outqueue(q, /* rtx_timeout */ 1);
error = sctp_outq_flush(q, /* rtx_timeout */ 1);
if (error)
q->asoc->base.sk->err = -error;
......@@ -323,18 +334,18 @@ void sctp_retransmit(sctp_outqueue_t *q, sctp_transport_t *transport,
/*
* Transmit DATA chunks on the retransmit queue. Upon return from
* sctp_flush_retran_queue() the packet 'pkt' may contain chunks which
* sctp_outq_flush_rtx() 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)
static int sctp_outq_flush_rtx(struct sctp_outq *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;
struct sctp_transport *transport = pkt->transport;
sctp_xmit_t status;
sctp_chunk_t *chunk;
sctp_association_t *asoc;
......@@ -374,6 +385,18 @@ static int sctp_flush_retran_queue(sctp_outqueue_t *q, sctp_packet_t *pkt,
continue;
}
#endif
/* Make sure that Gap Acked TSNs are not retransmitted. A
* simple approach is just to move such TSNs out of the
* way and into a 'transmitted' queue and skip to the
* next chunk.
*/
if (chunk->tsn_gap_acked) {
list_add_tail(lchunk, &transport->transmitted);
lchunk = sctp_list_dequeue(lqueue);
continue;
}
/* Attempt to append this chunk to the packet. */
status = (*q->append_output)(pkt, chunk);
......@@ -427,10 +450,10 @@ static int sctp_flush_retran_queue(sctp_outqueue_t *q, sctp_packet_t *pkt,
* 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,
void sctp_xmit_frag(struct sctp_outq *q, struct sk_buff *pos,
sctp_packet_t *packet, sctp_chunk_t *frag, __u32 tsn)
{
sctp_transport_t *transport = packet->transport;
struct sctp_transport *transport = packet->transport;
struct sk_buff_head *queue = &q->out;
sctp_xmit_t status;
int error;
......@@ -503,7 +526,7 @@ void sctp_xmit_frag(sctp_outqueue_t *q, struct sk_buff *pos,
* 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,
void sctp_xmit_fragmented_chunks(struct sctp_outq *q, sctp_packet_t *packet,
sctp_chunk_t *frag)
{
sctp_association_t *asoc = frag->asoc;
......@@ -548,6 +571,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
sctp_chunk_t *first_frag, *frag;
struct list_head *frag_list;
int nfrags;
__u8 old_flags, flags;
/* nfrags = no. of max size fragments + any smaller last fragment. */
nfrags = ((chunk_data_len / max_frag_data_len) +
......@@ -556,13 +580,20 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
/* Start of the data in the chunk. */
data_ptr += sizeof(sctp_datahdr_t);
/* Are we fragmenting an already fragmented large message? */
old_flags = chunk->chunk_hdr->flags;
if (old_flags & SCTP_DATA_FIRST_FRAG)
flags = SCTP_DATA_FIRST_FRAG;
else
flags = SCTP_DATA_MIDDLE_FRAG;
/* Make the first fragment. */
first_frag = sctp_make_datafrag(asoc, sinfo, max_frag_data_len,
data_ptr, SCTP_DATA_FIRST_FRAG, ssn);
data_ptr, flags, ssn);
if (!first_frag)
goto err;
first_frag->has_ssn = 1;
/* All the fragments are added to the frag_list of the first chunk. */
frag_list = &first_frag->frag_list;
......@@ -576,7 +607,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
ssn);
if (!frag)
goto err;
frag->has_ssn = 1;
/* Add the middle fragment to the first fragment's
* frag_list.
*/
......@@ -586,11 +617,17 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
data_ptr += max_frag_data_len;
}
if (old_flags & SCTP_DATA_LAST_FRAG)
flags = SCTP_DATA_LAST_FRAG;
else
flags = SCTP_DATA_MIDDLE_FRAG;
/* Make the last fragment. */
frag = sctp_make_datafrag(asoc, sinfo, chunk_data_len, data_ptr,
SCTP_DATA_LAST_FRAG, ssn);
flags, ssn);
if (!frag)
goto err;
frag->has_ssn = 1;
/* Add the last fragment to the first fragment's frag_list. */
list_add_tail(&frag->frag_list, frag_list);
......@@ -620,7 +657,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
}
/*
* sctp_flush_outqueue - Try to flush an outqueue.
* sctp_outq_flush - Try to flush an outqueue.
*
* Description: Send everything in q which we legally can, subject to
* congestion limitations.
......@@ -629,7 +666,7 @@ sctp_chunk_t *sctp_fragment_chunk(sctp_chunk_t *chunk,
* 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)
int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
{
sctp_packet_t *packet;
sctp_packet_t singleton;
......@@ -642,13 +679,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
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;
struct sctp_transport *transport = NULL;
struct sctp_transport *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;
......@@ -783,10 +819,8 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
(*q->config_output)(packet, vtag,
ecn_capable, ecne_handler);
retran:
error = sctp_flush_retran_queue(q,
packet,
rtx_timeout,
&start_timer);
error = sctp_outq_flush_rtx(q, packet,
rtx_timeout, &start_timer);
if (start_timer)
sctp_transport_reset_timers(transport);
......@@ -813,15 +847,14 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
*/
if (chunk->sinfo.sinfo_stream >=
asoc->c.sinit_num_ostreams) {
struct sctp_ulpevent *ev;
/* 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);
}
ev = sctp_ulpevent_make_send_failed(asoc,
chunk, SCTP_DATA_UNSENT,
SCTP_ERROR_INV_STRM, GFP_ATOMIC);
if (ev)
sctp_ulpq_tail_event(&asoc->ulpq, ev);
/* Free the chunk. This chunk is not on any
* list yet, just free it.
......@@ -830,6 +863,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
continue;
}
/* Now do delayed assignment of SSN. This will
* probably change again when we start supporting
* large (> approximately 2^16) size messages.
*/
sctp_chunk_assign_ssn(chunk);
/* If there is a specified transport, use it.
* Otherwise, we want to use the active path.
*/
......@@ -878,7 +917,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
/* We could not append this chunk, so put
* the chunk back on the output queue.
*/
SCTP_DEBUG_PRINTK("sctp_flush_outqueue: could "
SCTP_DEBUG_PRINTK("sctp_outq_flush: could "
"not transmit TSN: 0x%x, status: %d\n",
ntohl(chunk->subh.data_hdr->tsn),
status);
......@@ -952,8 +991,9 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
* --xguo
*/
while ((ltransport = sctp_list_dequeue(&transport_list)) != NULL ) {
sctp_transport_t *t = list_entry(ltransport,
sctp_transport_t, send_ready);
struct sctp_transport *t = list_entry(ltransport,
struct sctp_transport,
send_ready);
if (t != transport)
transport = t;
......@@ -966,12 +1006,12 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
}
/* 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)
int sctp_outq_set_output_handlers(struct sctp_outq *q,
sctp_outq_ohandler_init_t init,
sctp_outq_ohandler_config_t config,
sctp_outq_ohandler_t append,
sctp_outq_ohandler_t build,
sctp_outq_ohandler_force_t force)
{
q->init_output = init;
q->config_output = config;
......@@ -1005,7 +1045,7 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
sctp_association_t *asoc)
{
struct list_head *ltransport, *lchunk;
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_chunk_t *chunk;
__u32 highest_new_tsn, tsn;
struct list_head *transport_list = &asoc->peer.transport_addr_list;
......@@ -1013,7 +1053,7 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
highest_new_tsn = ntohl(sack->cum_tsn_ack);
list_for_each(ltransport, transport_list) {
transport = list_entry(ltransport, sctp_transport_t,
transport = list_entry(ltransport, struct sctp_transport,
transports);
list_for_each(lchunk, &transport->transmitted) {
chunk = list_entry(lchunk, sctp_chunk_t,
......@@ -1028,17 +1068,17 @@ static __u32 sctp_highest_new_tsn(sctp_sackhdr_t *sack,
}
return highest_new_tsn;
}
}
/* This is where we REALLY process a SACK.
*
* Process the sack against the outqueue. Mostly, this just frees
* 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)
int sctp_outq_sack(struct sctp_outq *q, sctp_sackhdr_t *sack)
{
sctp_association_t *asoc = q->asoc;
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_chunk_t *tchunk;
struct list_head *lchunk, *transport_list, *pos;
sctp_sack_variable_t *frags = sack->variable;
......@@ -1053,7 +1093,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
sack_ctsn = ntohl(sack->cum_tsn_ack);
/* Get the highest TSN in the sack. */
highest_tsn = sack_ctsn +
highest_tsn = sack_ctsn +
ntohs(frags[ntohs(sack->num_gap_ack_blocks) - 1].gab.end);
if (TSN_lt(asoc->highest_sacked, highest_tsn)) {
......@@ -1074,7 +1114,8 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
* This is a MASSIVE candidate for optimization.
*/
list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports);
transport = list_entry(pos, struct sctp_transport,
transports);
sctp_check_transmitted(q, &transport->transmitted,
transport, sack, highest_new_tsn);
}
......@@ -1127,7 +1168,8 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
goto finish;
list_for_each(pos, transport_list) {
transport = list_entry(pos, sctp_transport_t, transports);
transport = list_entry(pos, struct sctp_transport,
transports);
q->empty = q->empty && list_empty(&transport->transmitted);
if (!q->empty)
goto finish;
......@@ -1139,7 +1181,7 @@ int sctp_sack_outqueue(sctp_outqueue_t *q, sctp_sackhdr_t *sack)
}
/* Is the outqueue empty? */
int sctp_outqueue_is_empty(const sctp_outqueue_t *q)
int sctp_outq_is_empty(const struct sctp_outq *q)
{
return q->empty;
}
......@@ -1161,9 +1203,9 @@ int sctp_outqueue_is_empty(const sctp_outqueue_t *q)
* 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,
static void sctp_check_transmitted(struct sctp_outq *q,
struct list_head *transmitted_queue,
sctp_transport_t *transport,
struct sctp_transport *transport,
sctp_sackhdr_t *sack,
__u32 highest_new_tsn_in_sack)
{
......@@ -1494,7 +1536,7 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
if (transport) {
if (do_fast_retransmit)
sctp_retransmit(q, transport, do_fast_retransmit);
sctp_retransmit(q, transport, SCTP_RETRANSMIT_FAST_RTX);
SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, "
"ssthresh: %d, flight_size: %d, pba: %d\n",
......
......@@ -203,23 +203,3 @@ DECLARE_PRIMITIVE(SEND);
*/
DECLARE_PRIMITIVE(REQUESTHEARTBEAT);
/* 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;
}
......@@ -82,7 +82,7 @@ struct sock *sctp_get_ctl_sock(void)
}
/* Set up the proc fs entry for the SCTP protocol. */
void sctp_proc_init(void)
__init void sctp_proc_init(void)
{
if (!proc_net_sctp) {
struct proc_dir_entry *ent;
......@@ -223,37 +223,6 @@ int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp,
return error;
}
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
struct dst_entry *sctp_v4_get_dst(union sctp_addr *daddr,
union sctp_addr *saddr)
{
struct rtable *rt;
struct flowi fl;
memset(&fl, 0x0, sizeof(struct flowi));
fl.fl4_dst = daddr->v4.sin_addr.s_addr;
fl.proto = IPPROTO_SCTP;
if (saddr)
fl.fl4_src = saddr->v4.sin_addr.s_addr;
SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",
__FUNCTION__, NIPQUAD(fl.fl4_dst),
NIPQUAD(fl.fl4_src));
if (ip_route_output_key(&rt, &fl)) {
SCTP_DEBUG_PRINTK("NO ROUTE\n");
return NULL;
}
SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",
NIPQUAD(rt->rt_src), NIPQUAD(rt->rt_dst));
return &rt->u.dst;
}
/* Initialize a sctp_addr from in incoming skb. */
static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
int is_saddr)
......@@ -292,10 +261,12 @@ static void sctp_v4_to_sk(union sctp_addr *addr, struct sock *sk)
/* Initialize a sctp_addr from a dst_entry. */
static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst)
static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst,
unsigned short port)
{
struct rtable *rt = (struct rtable *)dst;
saddr->v4.sin_family = AF_INET;
saddr->v4.sin_port = port;
saddr->v4.sin_addr.s_addr = rt->rt_src;
}
......@@ -394,6 +365,108 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
return retval;
}
/* Returns a valid dst cache entry for the given source and destination ip
* addresses. If an association is passed, trys to get a dst entry with a
* source adddress that matches an address in the bind address list.
*/
struct dst_entry *sctp_v4_get_dst(sctp_association_t *asoc,
union sctp_addr *daddr,
union sctp_addr *saddr)
{
struct rtable *rt;
struct flowi fl;
sctp_bind_addr_t *bp;
rwlock_t *addr_lock;
struct sockaddr_storage_list *laddr;
struct list_head *pos;
struct dst_entry *dst = NULL;
union sctp_addr dst_saddr;
memset(&fl, 0x0, sizeof(struct flowi));
fl.fl4_dst = daddr->v4.sin_addr.s_addr;
fl.proto = IPPROTO_SCTP;
if (saddr)
fl.fl4_src = saddr->v4.sin_addr.s_addr;
SCTP_DEBUG_PRINTK("%s: DST:%u.%u.%u.%u, SRC:%u.%u.%u.%u - ",
__FUNCTION__, NIPQUAD(fl.fl4_dst),
NIPQUAD(fl.fl4_src));
if (!ip_route_output_key(&rt, &fl)) {
dst = &rt->u.dst;
}
/* If there is no association or if a source address is passed, no
* more validation is required.
*/
if (!asoc || saddr)
goto out;
bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
if (dst) {
/* Walk through the bind address list and look for a bind
* address that matches the source address of the returned dst.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list,
list);
sctp_v4_dst_saddr(&dst_saddr, dst, bp->port);
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
goto out_unlock;
}
sctp_read_unlock(addr_lock);
/* None of the bound addresses match the source address of the
* dst. So release it.
*/
dst_release(dst);
dst = NULL;
}
/* Walk through the bind address list and try to get a dst that
* matches a bind address as the source address.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, list);
if (AF_INET == laddr->a.sa.sa_family) {
fl.fl4_src = laddr->a.v4.sin_addr.s_addr;
dst = sctp_v4_get_dst(asoc, daddr, &laddr->a);
if (!ip_route_output_key(&rt, &fl)) {
dst = &rt->u.dst;
goto out_unlock;
}
}
}
out_unlock:
sctp_read_unlock(addr_lock);
out:
if (dst)
SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",
NIPQUAD(rt->rt_dst), NIPQUAD(rt->rt_src));
else
SCTP_DEBUG_PRINTK("NO ROUTE\n");
return dst;
}
/* For v4, the source address is cached in the route entry(dst). So no need
* to cache it separately and hence this is an empty routine.
*/
void sctp_v4_get_saddr(sctp_association_t *asoc,
struct dst_entry *dst,
union sctp_addr *daddr,
union sctp_addr *saddr)
{
}
/* Event handler for inet address addition/deletion events.
* Basically, whenever there is an event, we re-build our local address list.
*/
......@@ -545,6 +618,19 @@ static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr)
return sctp_v4_available(addr);
}
/* Wrapper routine that calls the ip transmit routine. */
static inline int sctp_v4_xmit(struct sk_buff *skb,
struct sctp_transport *transport, int ipfragok)
{
SCTP_DEBUG_PRINTK("%s: skb:%p, len:%d, "
"src:%u.%u.%u.%u, dst:%u.%u.%u.%u\n",
__FUNCTION__, skb, skb->len,
NIPQUAD(((struct rtable *)skb->dst)->rt_src),
NIPQUAD(((struct rtable *)skb->dst)->rt_dst));
return ip_queue_xmit(skb, ipfragok);
}
struct sctp_af sctp_ipv4_specific;
static struct sctp_pf sctp_pf_inet = {
......@@ -601,10 +687,11 @@ static struct inet_protocol sctp_protocol = {
/* IPv4 address related functions. */
struct sctp_af sctp_ipv4_specific = {
.queue_xmit = ip_queue_xmit,
.sctp_xmit = sctp_v4_xmit,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
.get_dst = sctp_v4_get_dst,
.get_saddr = sctp_v4_get_saddr,
.copy_addrlist = sctp_v4_copy_addrlist,
.from_skb = sctp_v4_from_skb,
.from_sk = sctp_v4_from_sk,
......@@ -688,7 +775,7 @@ static void cleanup_sctp_mibs(void)
}
/* Initialize the universe into something sensible. */
int sctp_init(void)
__init int sctp_init(void)
{
int i;
int status = 0;
......@@ -750,13 +837,9 @@ int sctp_init(void)
/* 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;
/* Initialize default stream count setup information. */
sctp_proto.max_instreams = SCTP_DEFAULT_INSTREAMS;
sctp_proto.max_outstreams = SCTP_DEFAULT_OUTSTREAMS;
/* Allocate and initialize the association hash table. */
sctp_proto.assoc_hashsize = 4096;
......@@ -829,6 +912,7 @@ int sctp_init(void)
sctp_get_local_addr_list(&sctp_proto);
__unsafe(THIS_MODULE);
return 0;
err_ctl_sock_init:
......@@ -852,7 +936,7 @@ int sctp_init(void)
}
/* Exit handler for the SCTP protocol. */
void sctp_exit(void)
__exit void sctp_exit(void)
{
/* BUG. This should probably do something useful like clean
* up all the remaining associations and all that memory.
......@@ -889,4 +973,3 @@ 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");
......@@ -82,12 +82,12 @@ static const sctp_supported_addrs_param_t sat_param = {
/* gcc 3.2 doesn't allow initialization of zero-length arrays. So the above
* structure is split and the address types array is initialized using a
* fixed length array.
* fixed length array.
*/
static const __u16 sat_addr_types[2] = {
SCTP_PARAM_IPV4_ADDRESS,
SCTP_V6(SCTP_PARAM_IPV6_ADDRESS,)
};
};
/* RFC 2960 3.3.2 Initiation (INIT) (1)
*
......@@ -116,8 +116,8 @@ void sctp_init_cause(sctp_chunk_t *chunk, __u16 cause_code,
err.cause = cause_code;
len = sizeof(sctp_errhdr_t) + paylen;
padlen = len % 4;
len += padlen;
err.length = htons(len);
len += padlen;
sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err);
chunk->subh.err_hdr = sctp_addto_chunk(chunk, paylen, payload);
}
......@@ -242,35 +242,10 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
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);
addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, priority);
if (!addrs.v)
goto nomem_rawaddr;
......@@ -333,9 +308,6 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
nomem_cookie:
kfree(addrs.v);
nomem_rawaddr:
nomem_copyaddr:
sctp_bind_addr_free(bp);
nomem_bindaddr:
return retval;
}
......@@ -598,23 +570,9 @@ 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);
return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, 0);
}
/* Create a selective ackowledgement (SACK) for the given
......@@ -714,6 +672,7 @@ sctp_chunk_t *sctp_make_sack(const sctp_association_t *asoc)
return retval;
}
/* FIXME: Comments. */
sctp_chunk_t *sctp_make_shutdown(const sctp_association_t *asoc)
{
sctp_chunk_t *retval;
......@@ -897,7 +856,7 @@ sctp_chunk_t *sctp_make_abort_user(const sctp_association_t *asoc,
/* Make a HEARTBEAT chunk. */
sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc,
const sctp_transport_t *transport,
const struct sctp_transport *transport,
const void *payload, const size_t paylen)
{
sctp_chunk_t *retval = sctp_make_chunk(asoc, SCTP_CID_HEARTBEAT,
......@@ -909,7 +868,7 @@ sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc,
/* Cast away the 'const', as this is just telling the chunk
* what transport it belongs to.
*/
retval->transport = (sctp_transport_t *) transport;
retval->transport = (struct sctp_transport *) transport;
retval->subh.hbs_hdr = sctp_addto_chunk(retval, paylen, payload);
nodata:
......@@ -1013,6 +972,7 @@ sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc,
retval->asoc = (sctp_association_t *) asoc;
retval->num_times_sent = 0;
retval->has_tsn = 0;
retval->has_ssn = 0;
retval->rtt_in_progress = 0;
retval->sent_at = jiffies;
retval->singleton = 1;
......@@ -1118,53 +1078,6 @@ void sctp_free_chunk(sctp_chunk_t *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.
......@@ -1193,7 +1106,8 @@ void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data)
* chunk is not big enough.
* Returns a kernel err value.
*/
int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data)
static int sctp_user_addto_chunk(sctp_chunk_t *chunk, int off, int len,
struct iovec *data)
{
__u8 *target;
int err = 0;
......@@ -1202,7 +1116,7 @@ int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data)
target = skb_put(chunk->skb, len);
/* Copy data (whole iovec) into chunk */
if ((err = memcpy_fromiovec(target, data, len)))
if ((err = memcpy_fromiovecend(target, data, off, len)))
goto out;
/* Adjust the chunk length field. */
......@@ -1214,6 +1128,152 @@ int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data)
return err;
}
/* A data chunk can have a maximum payload of (2^16 - 20). Break
* down any such message into smaller chunks. Opportunistically, fragment
* the chunks down to the current MTU constraints. We may get refragmented
* later if the PMTU changes, but it is _much better_ to fragment immediately
* with a reasonable guess than always doing our fragmentation on the
* soft-interrupt.
*/
int sctp_datachunks_from_user(sctp_association_t *asoc,
const struct sctp_sndrcvinfo *sinfo,
struct msghdr *msg, int msg_len,
struct sk_buff_head *chunks)
{
int max, whole, i, offset, over, err;
int len, first_len;
sctp_chunk_t *chunk;
__u8 frag;
/* What is a reasonable fragmentation point right now? */
max = asoc->pmtu;
if (max < SCTP_MIN_PMTU)
max = SCTP_MIN_PMTU;
max -= SCTP_IP_OVERHEAD;
/* Make sure not beyond maximum chunk size. */
if (max > SCTP_MAX_CHUNK_LEN)
max = SCTP_MAX_CHUNK_LEN;
/* Subtract out the overhead of a data chunk header. */
max -= sizeof(struct sctp_data_chunk);
whole = 0;
first_len = max;
/* Encourage Cookie-ECHO bundling. */
if (asoc->state < SCTP_STATE_ESTABLISHED) {
whole = msg_len / (max - SCTP_ARBITRARY_COOKIE_ECHO_LEN);
/* Account for the DATA to be bundled with the COOKIE-ECHO. */
if (whole) {
first_len = max - SCTP_ARBITRARY_COOKIE_ECHO_LEN;
msg_len -= first_len;
whole = 1;
}
}
/* How many full sized? How many bytes leftover? */
whole += msg_len / max;
over = msg_len % max;
offset = 0;
/* Create chunks for all the full sized DATA chunks. */
for (i=0, len=first_len; i < whole; i++) {
frag = SCTP_DATA_MIDDLE_FRAG;
if (0 == i)
frag |= SCTP_DATA_FIRST_FRAG;
if ((i == (whole - 1)) && !over)
frag |= SCTP_DATA_LAST_FRAG;
chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0);
if (!chunk)
goto nomem;
err = sctp_user_addto_chunk(chunk, offset, len, msg->msg_iov);
if (err < 0)
goto errout;
offset += len;
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
__skb_queue_tail(chunks, (struct sk_buff *)chunk);
/* The first chunk, the first chunk was likely short
* to allow bundling, so reset to full size.
*/
if (0 == i)
len = max;
}
/* .. now the leftover bytes. */
if (over) {
if (!whole)
frag = SCTP_DATA_NOT_FRAG;
else
frag = SCTP_DATA_LAST_FRAG;
chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0);
if (!chunk)
goto nomem;
err = sctp_user_addto_chunk(chunk, offset, over, msg->msg_iov);
/* Put the chunk->skb back into the form expected by send. */
__skb_pull(chunk->skb, (__u8 *)chunk->chunk_hdr
- (__u8 *)chunk->skb->data);
if (err < 0)
goto errout;
__skb_queue_tail(chunks, (struct sk_buff *)chunk);
}
err = 0;
goto out;
nomem:
err = -ENOMEM;
errout:
while ((chunk = (sctp_chunk_t *)__skb_dequeue(chunks)))
sctp_free_chunk(chunk);
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_ssn(sctp_chunk_t *chunk)
{
__u16 ssn;
__u16 sid;
if (chunk->has_ssn)
return;
/* This is the last possible instant to assign a SSN. */
if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
ssn = 0;
} else {
sid = htons(chunk->subh.data_hdr->stream);
if (chunk->chunk_hdr->flags & SCTP_DATA_LAST_FRAG)
ssn = sctp_ssn_next(&chunk->asoc->ssnmap->out, sid);
else
ssn = sctp_ssn_peek(&chunk->asoc->ssnmap->out, sid);
ssn = htons(ssn);
}
chunk->subh.data_hdr->ssn = ssn;
chunk->has_ssn = 1;
}
/* Helper function to assign a TSN if needed. This assumes that both
* the data_hdr and association have already been assigned.
*/
......@@ -1352,11 +1412,10 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
sctp_signed_cookie_t *cookie;
sctp_cookie_t *bear_cookie;
int headersize, bodysize;
int fixed_size, var_size1, var_size2, var_size3;
int fixed_size;
__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;
......@@ -1377,9 +1436,6 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
/* 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;
......@@ -1403,6 +1459,7 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* for init collision case of lost COOKIE ACK.
*/
if (!asoc && tv_lt(bear_cookie->expiration, chunk->skb->stamp)) {
__u16 len;
/*
* Section 3.3.10.3 Stale Cookie Error (3)
*
......@@ -1411,8 +1468,8 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* Stale Cookie Error: Indicates the receipt of a valid State
* Cookie that has expired.
*/
*err_chk_p = sctp_make_op_error_space(asoc, chunk,
ntohs(chunk->chunk_hdr->length));
len = ntohs(chunk->chunk_hdr->length);
*err_chk_p = sctp_make_op_error_space(asoc, chunk, len);
if (*err_chk_p) {
suseconds_t usecs = (chunk->skb->stamp.tv_sec -
bear_cookie->expiration.tv_sec) * 1000000L +
......@@ -1443,12 +1500,8 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
/* 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)) {
if (sctp_assoc_set_bind_addr_from_cookie(retval, bear_cookie,
GFP_ATOMIC) < 0) {
*error = -SCTP_IERROR_NOMEM;
goto fail;
}
......@@ -1477,6 +1530,57 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* 3rd Level Abstractions
********************************************************************/
/*
* Report a missing mandatory parameter.
*/
struct __sctp_missing {
__u32 num_missing;
__u16 type;
} __attribute__((packed));;
static int sctp_process_missing_param(const sctp_association_t *asoc,
sctp_param_t paramtype,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
struct __sctp_missing report;
__u16 len;
len = WORD_ROUND(sizeof(report));
/* Make an ERROR chunk, preparing enough room for
* returning multiple unknown parameters.
*/
if (!*err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk, len);
if (*err_chk_p) {
report.num_missing = htonl(1);
report.type = paramtype;
sctp_init_cause(*err_chk_p, SCTP_ERROR_INV_PARAM,
&report, sizeof(report));
}
/* Stop processing this chunk. */
return 0;
}
/* Report an Invalid Mandatory Parameter. */
static int sctp_process_inv_mandatory(const sctp_association_t *asoc,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
/* Invalid Mandatory Parameter Error has no payload. */
if (!*err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk, 0);
if (*err_chk_p)
sctp_init_cause(*err_chk_p, SCTP_ERROR_INV_PARAM, NULL, 0);
/* Stop processing this chunk. */
return 0;
}
/* Do not attempt to handle the HOST_NAME parm. However, do
* send back an indicator to the peer.
*/
......@@ -1487,9 +1591,7 @@ static int sctp_process_hn_param(const sctp_association_t *asoc,
{
__u16 len = ntohs(param.p->length);
/* Make an ERROR chunk, preparing enough room for
* returning multiple unknown parameters.
*/
/* Make an ERROR chunk. */
if (!*err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk, len);
......@@ -1570,7 +1672,8 @@ static int sctp_process_unk_param(const sctp_association_t *asoc,
} else {
/* If there is no memory for generating the ERROR
* report as specified, an ABORT will be triggered
* to the peer and the association won't be established.
* to the peer and the association won't be
* established.
*/
retval = 0;
}
......@@ -1633,12 +1736,33 @@ int sctp_verify_init(const sctp_association_t *asoc,
sctp_chunk_t **err_chk_p)
{
union sctp_params param;
int has_cookie = 0;
/* Verify stream values are non-zero. */
if ((0 == peer_init->init_hdr.num_outbound_streams) ||
(0 == peer_init->init_hdr.num_inbound_streams)) {
sctp_process_inv_mandatory(asoc, chunk, err_chk_p);
return 0;
}
/* Check for missing mandatory parameters. */
sctp_walk_params(param, peer_init, init_hdr.params) {
/* FIXME - Verify the fixed fields of the INIT chunk. Also, verify
* the mandatory parameters somewhere here and generate either the
* "Missing mandatory parameter" error or the "Invalid mandatory
* parameter" error.
if (SCTP_PARAM_STATE_COOKIE == param.p->type)
has_cookie = 1;
} /* for (loop through all parameters) */
/* The only missing mandatory param possible today is
* the state cookie for an INIT-ACK chunk.
*/
if ((SCTP_CID_INIT_ACK == cid) && !has_cookie) {
sctp_process_missing_param(asoc, SCTP_PARAM_STATE_COOKIE,
chunk, err_chk_p);
return 0;
}
/* Find unrecognized parameters. */
......@@ -1654,6 +1778,7 @@ int sctp_verify_init(const sctp_association_t *asoc,
/* Unpack the parameters in an INIT packet into an association.
* Returns 0 on failure, else success.
* FIXME: This is an association method.
*/
int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
const union sctp_addr *peer_addr,
......@@ -1661,7 +1786,7 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
int priority)
{
union sctp_params param;
sctp_transport_t *transport;
struct sctp_transport *transport;
struct list_head *pos, *temp;
char *cookie;
......@@ -1710,6 +1835,12 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
ntohs(peer_init->init_hdr.num_inbound_streams);
}
if (asoc->c.sinit_max_instreams >
ntohs(peer_init->init_hdr.num_outbound_streams)) {
asoc->c.sinit_max_instreams =
ntohs(peer_init->init_hdr.num_outbound_streams);
}
/* Copy Initiation tag from INIT to VT_peer in cookie. */
asoc->c.peer_vtag = asoc->peer.i.init_tag;
......@@ -1730,7 +1861,7 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* advertised window).
*/
list_for_each(pos, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, sctp_transport_t, transports);
transport = list_entry(pos, struct sctp_transport, transports);
transport->ssthresh = asoc->peer.i.a_rwnd;
}
......@@ -1738,6 +1869,21 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
sctp_tsnmap_init(&asoc->peer.tsn_map, SCTP_TSN_MAP_SIZE,
asoc->peer.i.initial_tsn);
/* 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.
*/
/* Allocate storage for the negotiated streams. */
asoc->ssnmap = sctp_ssnmap_new(asoc->peer.i.num_outbound_streams,
asoc->c.sinit_num_ostreams,
priority);
if (!asoc->ssnmap)
goto nomem_ssnmap;
/* ADDIP Section 4.1 ASCONF Chunk Procedures
*
* When an endpoint has an ASCONF signaled change to be sent to the
......@@ -1751,10 +1897,11 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1;
return 1;
nomem_ssnmap:
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);
transport = list_entry(pos, struct sctp_transport, transports);
list_del(pos);
sctp_transport_free(transport);
}
......
......@@ -56,33 +56,29 @@
#include <net/sctp/sm.h>
/* Do forward declarations of static functions. */
static void sctp_do_ecn_ce_work(sctp_association_t *asoc,
__u32 lowest_tsn);
static void sctp_do_ecn_ce_work(sctp_association_t *,__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,
sctp_event_t event_type, sctp_subtype_t stype,
static void sctp_do_ecn_cwr_work(sctp_association_t *,__u32 lowest_tsn);
static void sctp_do_8_2_transport_strike(sctp_association_t *,
struct sctp_transport *);
static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *);
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *,
sctp_event_t, sctp_subtype_t,
sctp_chunk_t *chunk);
static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc,
static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *,
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_hb_timers_update(sctp_cmd_seq_t *, sctp_association_t *,
sctp_transport_t *);
static void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *, sctp_association_t *,
sctp_bind_addr_t *);
static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *, sctp_association_t *);
static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *, sctp_association_t *,
struct sctp_transport *);
static void sctp_cmd_transport_reset(sctp_cmd_seq_t *, sctp_association_t *,
sctp_transport_t *);
struct sctp_transport *);
static void sctp_cmd_transport_on(sctp_cmd_seq_t *, sctp_association_t *,
sctp_transport_t *, sctp_chunk_t *);
struct sctp_transport *, 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 *,
......@@ -264,7 +260,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
struct list_head *pos;
struct timer_list *timer;
unsigned long timeout;
sctp_transport_t *t;
struct sctp_transport *t;
sctp_sackhdr_t sackh;
if(SCTP_EVENT_T_TIMEOUT != event_type)
......@@ -296,7 +292,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break;
case SCTP_CMD_PURGE_OUTQUEUE:
sctp_outqueue_teardown(&asoc->outqueue);
sctp_outq_teardown(&asoc->outqueue);
break;
case SCTP_CMD_DELETE_TCB:
......@@ -395,9 +391,9 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
command->obj.ptr,
"ulpq:",
&asoc->ulpq);
sctp_ulpqueue_tail_data(&asoc->ulpq,
command->obj.ptr,
GFP_ATOMIC);
sctp_ulpq_tail_data(&asoc->ulpq,
command->obj.ptr,
GFP_ATOMIC);
break;
case SCTP_CMD_EVENT_ULP:
......@@ -407,14 +403,14 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
command->obj.ptr,
"ulpq:",
&asoc->ulpq);
sctp_ulpqueue_tail_event(&asoc->ulpq,
command->obj.ptr);
sctp_ulpq_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);
error = sctp_outq_tail(&asoc->outqueue,
command->obj.ptr);
break;
case SCTP_CMD_SEND_PKT:
......@@ -427,12 +423,13 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_RETRAN:
/* Mark a transport for retransmission. */
sctp_retransmit(&asoc->outqueue,
command->obj.transport, 0);
command->obj.transport,
SCTP_RETRANSMIT_T3_RTX);
break;
case SCTP_CMD_TRANSMIT:
/* Kick start transmission. */
error = sctp_flush_outqueue(&asoc->outqueue, 0);
error = sctp_outq_flush(&asoc->outqueue, 0);
break;
case SCTP_CMD_ECN_CE:
......@@ -502,7 +499,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
* COOKIE-ECHO we need to resend.
*/
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t,
t = list_entry(pos, struct sctp_transport,
transports);
sctp_retransmit_mark(&asoc->outqueue, t, 0);
}
......@@ -547,11 +544,6 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
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,
......@@ -572,9 +564,13 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_cmd_hb_timers_start(commands, asoc);
break;
case SCTP_CMD_HB_TIMERS_UPDATE:
case SCTP_CMD_HB_TIMER_UPDATE:
t = command->obj.transport;
sctp_cmd_hb_timers_update(commands, asoc, t);
sctp_cmd_hb_timer_update(commands, asoc, t);
break;
case SCTP_CMD_HB_TIMERS_STOP:
sctp_cmd_hb_timers_stop(commands, asoc);
break;
case SCTP_CMD_REPORT_ERROR:
......@@ -599,7 +595,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_RTO_PENDING:
t = command->obj.transport;
t->rto_pending = 1;
t->rto_pending = 1;
break;
default:
......@@ -656,7 +652,7 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
* recent than the last response.
*/
if (TSN_lt(asoc->last_cwr_tsn, lowest_tsn)) {
sctp_transport_t *transport;
struct sctp_transport *transport;
/* Find which transport's congestion variables
* need to be adjusted.
......@@ -743,7 +739,7 @@ int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands)
asoc->peer.sack_needed = 0;
asoc->peer.next_dup_tsn = 0;
error = sctp_push_outqueue(&asoc->outqueue, sack);
error = sctp_outq_tail(&asoc->outqueue, sack);
/* Stop the SACK timer. */
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP,
......@@ -802,7 +798,7 @@ void sctp_do_TSNdup(sctp_association_t *asoc, sctp_chunk_t *chunk, long gap)
void sctp_generate_t3_rtx_event(unsigned long peer)
{
int error;
sctp_transport_t *transport = (sctp_transport_t *) peer;
struct sctp_transport *transport = (struct sctp_transport *) peer;
sctp_association_t *asoc = transport->asoc;
/* Check whether a task is in the sock. */
......@@ -917,7 +913,7 @@ void sctp_generate_autoclose_event(unsigned long data)
void sctp_generate_heartbeat_event(unsigned long data)
{
int error = 0;
sctp_transport_t *transport = (sctp_transport_t *) data;
struct sctp_transport *transport = (struct sctp_transport *) data;
sctp_association_t *asoc = transport->asoc;
sctp_bh_lock_sock(asoc->base.sk);
......@@ -957,24 +953,16 @@ void sctp_generate_sack_event(unsigned long 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_t5_shutdown_guard_event,
sctp_generate_heartbeat_event,
sctp_generate_sack_event,
sctp_generate_autoclose_event,
sctp_generate_pmtu_raise_event,
};
/********************************************************************
......@@ -997,7 +985,7 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = {
*
*/
static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
sctp_transport_t *transport)
struct sctp_transport *transport)
{
/* The check for association's overall error counter exceeding the
* threshold is done in the state function.
......@@ -1095,7 +1083,7 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT
* inside the cookie. In reality, this is only used for INIT-ACK processing
* since all other cases use "temporary" associations and can do all
* their work in statefuns directly.
* their work in statefuns directly.
*/
static int sctp_cmd_process_init(sctp_cmd_seq_t *commands,
sctp_association_t *asoc,
......@@ -1125,7 +1113,7 @@ static int sctp_cmd_process_init(sctp_cmd_seq_t *commands,
static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc)
{
sctp_transport_t *t;
struct sctp_transport *t;
struct list_head *pos;
/* Start a heartbeat timer for each transport on the association.
......@@ -1133,45 +1121,43 @@ static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds,
* 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)) {
t = list_entry(pos, struct sctp_transport, transports);
if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t)))
sctp_transport_hold(t);
}
}
}
/* Helper function to update the heartbeat timer. */
static void sctp_cmd_hb_timers_update(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc,
sctp_transport_t *t)
static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc)
{
/* Update the heartbeat timer. */
if (!mod_timer(&t->hb_timer, t->hb_interval + t->rto + jiffies))
sctp_transport_hold(t);
}
struct sctp_transport *t;
struct list_head *pos;
/* 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)
{
struct list_head *pos, *temp;
/* Stop all heartbeat timers. */
list_for_each_safe(pos, temp, &bp->address_list) {
list_del_init(pos);
list_add_tail(pos, &asoc->base.bind_addr.address_list);
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, struct sctp_transport, transports);
if (del_timer(&t->hb_timer))
sctp_transport_put(t);
}
}
/* Free the temporary bind addr header, otherwise
* there will a memory leak.
*/
sctp_bind_addr_free(bp);
/* Helper function to update the heartbeat timer. */
static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc,
struct sctp_transport *t)
{
/* Update the heartbeat timer. */
if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t)))
sctp_transport_hold(t);
}
/* 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)
struct sctp_transport *t,
sctp_chunk_t *chunk)
{
sctp_sender_hb_info_t *hbinfo;
......@@ -1203,7 +1189,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
*/
static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
sctp_association_t *asoc,
sctp_transport_t *t)
struct sctp_transport *t)
{
sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE);
......@@ -1218,7 +1204,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
{
int err;
if (sctp_sack_outqueue(&asoc->outqueue, sackh)) {
if (sctp_outq_sack(&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),
......@@ -1228,7 +1214,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
/* Windows may have opened, so we need
* to check if we have DATA to transmit
*/
err = sctp_flush_outqueue(&asoc->outqueue, 0);
err = sctp_outq_flush(&asoc->outqueue, 0);
}
return err;
......@@ -1240,7 +1226,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds,
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_chunk_t *chunk)
{
sctp_transport_t *t;
struct sctp_transport *t;
t = sctp_assoc_choose_shutdown_transport(asoc);
asoc->shutdown_last_sent_to = t;
......
......@@ -191,7 +191,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
int len;
/* If the packet is an OOTB packet which is temporarily on the
* control endpoint, responding with an ABORT.
* control endpoint, respond with an ABORT.
*/
if (ep == sctp_sk((sctp_get_ctl_sock()))->ep)
return sctp_sf_ootb(ep, asoc, type, arg, commands);
......@@ -262,6 +262,9 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
len = ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t);
if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0)
goto nomem_ack;
repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
if (!repl)
goto nomem_ack;
......@@ -506,7 +509,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
sctp_chunk_t *err_chk_p;
/* If the packet is an OOTB packet which is temporarily on the
* control endpoint, responding with an ABORT.
* control endpoint, respond with an ABORT.
*/
if (ep == sctp_sk((sctp_get_ctl_sock()))->ep)
return sctp_sf_ootb(ep, asoc, type, arg, commands);
......@@ -678,7 +681,7 @@ sctp_disposition_t sctp_sf_heartbeat(const sctp_endpoint_t *ep,
void *arg,
sctp_cmd_seq_t *commands)
{
sctp_transport_t *transport = (sctp_transport_t *) arg;
struct sctp_transport *transport = (struct sctp_transport *) arg;
sctp_chunk_t *reply;
sctp_sender_hb_info_t hbinfo;
size_t paylen = 0;
......@@ -711,7 +714,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
void *arg,
sctp_cmd_seq_t *commands)
{
sctp_transport_t *transport = (sctp_transport_t *) arg;
struct sctp_transport *transport = (struct sctp_transport *) arg;
if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
......@@ -737,7 +740,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
SCTP_TRANSPORT(transport));
}
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_UPDATE,
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMER_UPDATE,
SCTP_TRANSPORT(transport));
return SCTP_DISPOSITION_CONSUME;
......@@ -842,7 +845,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
{
sctp_chunk_t *chunk = arg;
union sctp_addr from_addr;
sctp_transport_t *link;
struct sctp_transport *link;
sctp_sender_hb_info_t *hbinfo;
unsigned long max_interval;
......@@ -944,7 +947,7 @@ static int sctp_sf_check_restart_addrs(const sctp_association_t *new_asoc,
sctp_chunk_t *init,
sctp_cmd_seq_t *commands)
{
sctp_transport_t *new_addr, *addr;
struct sctp_transport *new_addr, *addr;
struct list_head *pos, *pos2;
int found;
......@@ -963,10 +966,11 @@ static int sctp_sf_check_restart_addrs(const sctp_association_t *new_asoc,
found = 0;
list_for_each(pos, &new_asoc->peer.transport_addr_list) {
new_addr = list_entry(pos, sctp_transport_t, transports);
new_addr = list_entry(pos, struct sctp_transport, transports);
found = 0;
list_for_each(pos2, &asoc->peer.transport_addr_list) {
addr = list_entry(pos2, sctp_transport_t, transports);
addr = list_entry(pos2, struct sctp_transport,
transports);
if (sctp_cmp_addr_exact(&new_addr->ipaddr,
&addr->ipaddr)) {
found = 1;
......@@ -1048,20 +1052,17 @@ static char sctp_tietags_compare(sctp_association_t *new_asoc,
(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)))
(0 == asoc->c.peer_vtag))) {
return 'B';
}
/* Collision case D. */
if ((asoc->c.my_vtag == new_asoc->c.my_vtag) &&
(asoc->c.peer_vtag == new_asoc->c.peer_vtag))
return 'D';
/* Collision case C. */
if ((asoc->c.my_vtag != new_asoc->c.my_vtag) &&
......@@ -1070,7 +1071,8 @@ static char sctp_tietags_compare(sctp_association_t *new_asoc,
(0 == new_asoc->c.peer_ttag))
return 'C';
return 'E'; /* No such case available. */
/* No match to any of the special cases; discard this packet. */
return 'E';
}
/* Common helper routine for both duplicate and simulataneous INIT
......@@ -1182,6 +1184,10 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
len = ntohs(err_chunk->chunk_hdr->length) -
sizeof(sctp_chunkhdr_t);
}
if (sctp_assoc_set_bind_addr_from_ep(new_asoc, GFP_ATOMIC) < 0)
goto nomem;
repl = sctp_make_init_ack(new_asoc, chunk, GFP_ATOMIC, len);
if (!repl)
goto nomem;
......@@ -1337,7 +1343,7 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(const sctp_endpoint_t *ep,
/* Unexpected COOKIE-ECHO handlerfor peer restart (Table 2, action 'A')
/* Unexpected COOKIE-ECHO handler for peer restart (Table 2, action 'A')
*
* Section 5.2.4
* A) In this case, the peer may have restarted.
......@@ -1500,11 +1506,17 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const sctp_endpoint_t *ep,
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.
/* Clarification from Implementor's Guide:
* D) When both local and remote tags match the endpoint should
* enter the ESTABLISHED state, if it is in the COOKIE-ECHOED state.
* It should stop any cookie timer that may be running and send
* a COOKIE ACK.
*/
if (SCTP_STATE_ESTABLISHED != asoc->state) {
/* Don't accidentally move back into established state. */
if (asoc->state < SCTP_STATE_ESTABLISHED) {
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,
......@@ -1528,13 +1540,14 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(const sctp_endpoint_t *ep,
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:
......@@ -1605,8 +1618,6 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
sctp_send_stale_cookie_err(ep, asoc, chunk, commands,
err_chk_p);
return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
break;
case -SCTP_IERROR_BAD_SIG:
default:
return sctp_sf_pdiscard(ep, asoc, type, arg, commands);
......@@ -1629,7 +1640,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
new_asoc);
break;
case 'C': /* Collisioun case C. */
case 'C': /* Collision case C. */
retval = sctp_sf_do_dupcook_c(ep, asoc, chunk, commands,
new_asoc);
break;
......@@ -1639,9 +1650,8 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const sctp_endpoint_t *ep,
new_asoc);
break;
default: /* No such case, discard it. */
printk(KERN_WARNING "%s:unknown case\n", __FUNCTION__);
retval = SCTP_DISPOSITION_DISCARD;
default: /* Discard packet for all others. */
retval = sctp_sf_pdiscard(ep, asoc, type, arg, commands);
break;
};
......@@ -1799,7 +1809,7 @@ sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep,
sctp_cookie_preserve_param_t bht;
sctp_errhdr_t *err;
struct list_head *pos;
sctp_transport_t *t;
struct sctp_transport *t;
sctp_chunk_t *reply;
sctp_bind_addr_t *bp;
int attempts;
......@@ -1848,9 +1858,11 @@ sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_COUNTER_INC,
SCTP_COUNTER(SCTP_COUNTER_INIT_ERROR));
/* If we've sent any data bundled with COOKIE-ECHO we need to resend. */
/* If we've sent any data bundled with COOKIE-ECHO we need to
* resend.
*/
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports);
t = list_entry(pos, struct sctp_transport, transports);
sctp_add_cmd_sf(commands, SCTP_CMD_RETRAN, SCTP_TRANSPORT(t));
}
......@@ -2030,7 +2042,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const sctp_endpoint_t *ep,
SCTP_STATE(SCTP_STATE_SHUTDOWN_RECEIVED));
disposition = SCTP_DISPOSITION_CONSUME;
if (sctp_outqueue_is_empty(&asoc->outqueue)) {
if (sctp_outq_is_empty(&asoc->outqueue)) {
disposition = sctp_sf_do_9_2_shutdown_ack(ep, asoc, type,
arg, commands);
}
......@@ -3229,10 +3241,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
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
......@@ -3241,35 +3249,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
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"
......@@ -3278,7 +3257,7 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
* 1 to 4294967295 (see 5.3.1 for Tag value selection). ...
*/
repl = sctp_make_init(asoc, bp, GFP_ATOMIC, 0);
repl = sctp_make_init(asoc, &asoc->base.bind_addr, GFP_ATOMIC, 0);
if (!repl)
goto nomem;
......@@ -3297,9 +3276,6 @@ sctp_disposition_t sctp_sf_do_prm_asoc(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_CONSUME;
nomem:
if (bp)
sctp_bind_addr_free(bp);
return SCTP_DISPOSITION_NOMEM;
}
......@@ -3429,7 +3405,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown(const sctp_endpoint_t *ep,
SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
disposition = SCTP_DISPOSITION_CONSUME;
if (sctp_outqueue_is_empty(&asoc->outqueue)) {
if (sctp_outq_is_empty(&asoc->outqueue)) {
disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type,
arg, commands);
}
......@@ -3767,7 +3743,7 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat(
void *arg,
sctp_cmd_seq_t *commands)
{
return sctp_sf_heartbeat(ep, asoc, type, (sctp_transport_t *)arg,
return sctp_sf_heartbeat(ep, asoc, type, (struct sctp_transport *)arg,
commands);
}
......@@ -3837,6 +3813,13 @@ sctp_disposition_t sctp_sf_do_9_2_start_shutdown(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_SHUTDOWN_SENT));
/* sctp-implguide 2.10 Issues with Heartbeating and failover
*
* HEARTBEAT ... is discontinued after sending either SHUTDOWN
* or SHUTDOWN-ACK.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_STOP, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
return SCTP_DISPOSITION_CONSUME;
......@@ -3889,6 +3872,14 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack(const sctp_endpoint_t *ep,
/* Enter the SHUTDOWN-ACK-SENT state. */
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE,
SCTP_STATE(SCTP_STATE_SHUTDOWN_ACK_SENT));
/* sctp-implguide 2.10 Issues with Heartbeating and failover
*
* HEARTBEAT ... is discontinued after sending either SHUTDOWN
* or SHUTDOWN-ACK.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_STOP, SCTP_NULL());
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
return SCTP_DISPOSITION_CONSUME;
......@@ -3933,7 +3924,7 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const sctp_endpoint_t *ep,
void *arg,
sctp_cmd_seq_t *commands)
{
sctp_transport_t *transport = arg;
struct sctp_transport *transport = arg;
if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
......@@ -4203,7 +4194,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire(const sctp_endpoint_t *ep,
sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_START,
SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD));
disposition = SCTP_DISPOSITION_CONSUME;
if (sctp_outqueue_is_empty(&asoc->outqueue)) {
if (sctp_outq_is_empty(&asoc->outqueue)) {
disposition = sctp_sf_do_9_2_start_shutdown(ep, asoc, type,
arg, commands);
}
......@@ -4333,7 +4324,7 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
const sctp_chunk_t *chunk)
{
sctp_packet_t *packet;
sctp_transport_t *transport;
struct sctp_transport *transport;
__u16 sport;
__u16 dport;
__u32 vtag;
......
/* 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-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
*
......@@ -49,7 +49,7 @@
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
sctp_sm_table_entry_t bug = {
static sctp_sm_table_entry_t bug = {
.fn = sctp_sf_bug,
.name = "sctp_sf_bug"
};
......@@ -206,7 +206,7 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* 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"}, \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_backbeat_8_3, .name = "sctp_sf_backbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
......@@ -216,7 +216,7 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* 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"}, \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_HEARTBEAT_ACK */
#define TYPE_SCTP_ABORT { \
......@@ -293,19 +293,19 @@ sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type,
/* 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"}, \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_cookie_echoed_err, .name = "sctp_sf_cookie_echoed_err"}, \
/* 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"}, \
{.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_operr_notify, .name = "sctp_sf_operr_notify"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_discard_chunk, .name = "sctp_sf_discard_chunk"}, \
} /* TYPE_SCTP_ERROR */
#define TYPE_SCTP_COOKIE_ECHO { \
......@@ -504,26 +504,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
{.fn = sctp_sf_unk_chunk, .name = "sctp_sf_unk_chunk"},
}; /* chunk unknown */
#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 */ \
......@@ -619,90 +599,6 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
{.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"}, \
......@@ -731,152 +627,16 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
.name = "sctp_sf_do_prm_requestheartbeat"}, \
} /* 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 { \
......@@ -902,30 +662,8 @@ sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE
{.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 { \
......@@ -1033,27 +771,6 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA
{.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_T5_SHUTDOWN_GUARD { \
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
......@@ -1089,11 +806,11 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA
/* 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"}, \
{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_sendbeat_8_3, .name = "sctp_sf_sendbeat_8_3"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \
}
#define TYPE_SCTP_EVENT_TIMEOUT_SACK { \
......@@ -1139,39 +856,16 @@ sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STA
{.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_T5_SHUTDOWN_GUARD,
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)
......
/* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001-2002 International Business Machines, Corp.
* Copyright (c) 2001-2002 Intel Corp.
* Copyright (c) 2001-2003 International Business Machines, Corp.
* Copyright (c) 2001-2003 Intel Corp.
* Copyright (c) 2001-2002 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
......@@ -47,6 +47,7 @@
* Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <samudrala@us.ibm.com>
* Inaky Perez-Gonzalez <inaky.gonzalez@intel.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -131,7 +132,7 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
static long sctp_get_port_local(struct sock *, union sctp_addr *);
/* Verify this is a valid sockaddr. */
static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt,
static struct sctp_af *sctp_sockaddr_af(struct sctp_opt *opt,
union sctp_addr *addr, int len)
{
struct sctp_af *af;
......@@ -716,7 +717,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_opt_t *sp;
sctp_endpoint_t *ep;
sctp_association_t *new_asoc=NULL, *asoc=NULL;
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_chunk_t *chunk = NULL;
union sctp_addr to;
struct sockaddr *msg_name = NULL;
......@@ -729,6 +730,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_scope_t scope;
long timeo;
__u16 sinfo_flags = 0;
struct sk_buff_head chunks;
SCTP_DEBUG_PRINTK("sctp_sendmsg(sk: %p, msg: %p, msg_len: %d)\n",
sk, msg, msg_len);
......@@ -754,8 +756,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
*/
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) {
int msg_namelen = msg->msg_namelen;
err = sctp_verify_addr(sk, (union sctp_addr *)msg->msg_name,
err = sctp_verify_addr(sk, (union sctp_addr *)msg->msg_name,
msg_namelen);
if (err)
return err;
......@@ -806,7 +808,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (!asoc) {
/* If we could not find a matching association on the
* endpoint, make sure that there is no peeled-off
* endpoint, make sure that there is no peeled-off
* association on another socket.
*/
if (sctp_endpoint_is_peeled_off(ep, &to)) {
......@@ -868,13 +870,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
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) {
......@@ -915,14 +910,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
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;
}
asoc->c.sinit_max_instreams =
sinit->sinit_max_instreams;
}
if (sinit->sinit_max_attempts) {
asoc->max_init_attempts
......@@ -936,6 +925,15 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* Prime the peer's transport structures. */
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
if (!transport) {
err = -ENOMEM;
goto out_free;
}
err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL);
if (err < 0) {
err = -ENOMEM;
goto out_free;
}
}
/* ASSERT: we have a valid association at this point. */
......@@ -949,19 +947,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
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
......@@ -994,13 +979,6 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
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
......@@ -1016,20 +994,13 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
#endif /* 0 */
/* Copy the message from the user. */
err = sctp_user_addto_chunk(chunk, msg_len, msg->msg_iov);
if (err < 0)
/* Break the message into multiple chunks of maximum size. */
skb_queue_head_init(&chunks);
err = sctp_datachunks_from_user(asoc, sinfo, msg, msg_len, &chunks);
if (err)
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);
/* Auto-connect, if we aren't connected already. */
if (SCTP_STATE_CLOSED == asoc->state) {
err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0)
......@@ -1037,18 +1008,22 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
SCTP_DEBUG_PRINTK("We associated primitively.\n");
}
/* Send it to the lower layers. */
err = sctp_primitive_SEND(asoc, chunk);
/* Now send the (possibly) fragmented message. */
while ((chunk = (sctp_chunk_t *)__skb_dequeue(&chunks))) {
SCTP_DEBUG_PRINTK("We sent primitively.\n");
/* Do accounting for the write space. */
sctp_set_owner_w(chunk);
/* Send it to the lower layers. */
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;
}
/* If we are already past ASSOCIATE, the lower
* layers are responsible for its cleanup.
* layers are responsible for association cleanup.
*/
goto out_free_chunk;
......@@ -1086,23 +1061,30 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
* frag_list. len specifies the total amount of data that needs to be removed.
* when 'len' bytes could be removed from the skb, it returns 0.
* If 'len' exceeds the total skb length, it returns the no. of bytes that
* could not be removed.
*/
* could not be removed.
*/
static int sctp_skb_pull(struct sk_buff *skb, int len)
{
struct sk_buff *list;
int skb_len = skb_headlen(skb);
int rlen;
if (len <= skb->len) {
if (len <= skb_len) {
__skb_pull(skb, len);
return 0;
}
len -= skb->len;
__skb_pull(skb, skb->len);
len -= skb_len;
__skb_pull(skb, skb_len);
for (list = skb_shinfo(skb)->frag_list; list; list = list->next) {
len = sctp_skb_pull(list, len);
if (!len)
rlen = sctp_skb_pull(list, len);
skb->len -= (len-rlen);
skb->data_len -= (len-rlen);
if (!rlen)
return 0;
len = rlen;
}
return len;
......@@ -1130,7 +1112,7 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
{
sctp_ulpevent_t *event = NULL;
sctp_opt_t *sp = sctp_sk(sk);
struct sk_buff *skb, *list;
struct sk_buff *skb;
int copied;
int err = 0;
int skb_len;
......@@ -1152,10 +1134,8 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
/* Get the total length of the skb including any skb's in the
* frag_list.
*/
*/
skb_len = skb->len;
for (list = skb_shinfo(skb)->frag_list; list; list = list->next)
skb_len += list->len;
copied = skb_len;
if (copied > len)
......@@ -1190,14 +1170,20 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
/* If skb's length exceeds the user's buffer, update the skb and
* push it back to the receive_queue so that the next call to
* recvmsg() will return the remaining data. Don't set MSG_EOR.
* Otherwise, set MSG_EOR indicating the end of a message.
* Otherwise, set MSG_EOR indicating the end of a message.
*/
if (skb_len > copied) {
msg->msg_flags &= ~MSG_EOR;
if (flags & MSG_PEEK)
goto out_free;
goto out_free;
sctp_skb_pull(skb, copied);
skb_queue_head(&sk->receive_queue, skb);
/* When only partial message is copied to the user, increase
* rwnd by that amount. If all the data in the skb is read,
* rwnd is updated when the skb's destructor is called via
* sctp_ulpevent_free().
*/
sctp_assoc_rwnd_increase(event->asoc, copied);
goto out;
} else {
msg->msg_flags |= MSG_EOR;
......@@ -1260,7 +1246,7 @@ static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk,
struct sctp_paddrparams params;
sctp_association_t *asoc;
union sctp_addr *addr;
sctp_transport_t *trans;
struct sctp_transport *trans;
int error;
if (optlen != sizeof(struct sctp_paddrparams))
......@@ -1449,7 +1435,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
sctp_opt_t *sp;
sctp_endpoint_t *ep;
sctp_association_t *asoc;
sctp_transport_t *transport;
struct sctp_transport *transport;
union sctp_addr to;
sctp_scope_t scope;
long timeo;
......@@ -1463,7 +1449,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
sp = sctp_sk(sk);
ep = sp->ep;
/* connect() cannot be done on a peeled-off socket. */
/* connect() cannot be done on a peeled-off socket. */
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) {
err = -EISCONN;
goto out_unlock;
......@@ -1471,7 +1457,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
err = sctp_verify_addr(sk, (union sctp_addr *)uaddr, addr_len);
if (err)
goto out_unlock;
goto out_unlock;
memcpy(&to, uaddr, addr_len);
to.v4.sin_port = ntohs(to.v4.sin_port);
......@@ -1479,7 +1465,7 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (asoc) {
if (asoc->state >= SCTP_STATE_ESTABLISHED)
err = -EISCONN;
err = -EISCONN;
else
err = -EALREADY;
goto out_unlock;
......@@ -1514,10 +1500,19 @@ SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
/* Prime the peer's transport structures. */
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
if (!transport) {
sctp_association_free(asoc);
goto out_unlock;
}
err = sctp_assoc_set_bind_addr_from_ep(asoc, GFP_KERNEL);
if (err < 0) {
sctp_association_free(asoc);
goto out_unlock;
}
err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0) {
sctp_association_free(asoc);
sctp_association_free(asoc);
goto out_unlock;
}
......@@ -1666,7 +1661,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
struct sctp_status status;
sctp_endpoint_t *ep;
sctp_association_t *assoc = NULL;
sctp_transport_t *transport;
struct sctp_transport *transport;
sctp_assoc_t associd;
int retval = 0;
......@@ -1885,7 +1880,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk,
struct sctp_paddrparams params;
sctp_association_t *asoc;
union sctp_addr *addr;
sctp_transport_t *trans;
struct sctp_transport *trans;
if (len != sizeof(struct sctp_paddrparams))
return -EINVAL;
......@@ -1915,7 +1910,7 @@ static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk,
* before this address shall be considered unreachable.
*/
params.spp_pathmaxrxt = trans->error_threshold;
if (copy_to_user(optval, &params, len))
return -EFAULT;
*optlen = len;
......@@ -1932,6 +1927,166 @@ static inline int sctp_getsockopt_initmsg(struct sock *sk, int len, char *optval
return 0;
}
static inline int sctp_getsockopt_get_peer_addrs_num(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_assoc_t id;
sctp_association_t *asoc;
struct list_head *pos;
int cnt = 0;
if (len != sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
return -EFAULT;
/*
* For UDP-style sockets, id specifies the association to query.
*/
asoc = sctp_id2assoc(sk, id);
if (!asoc)
return -EINVAL;
list_for_each(pos, &asoc->peer.transport_addr_list) {
cnt ++;
}
if (copy_to_user(optval, &cnt, sizeof(int)))
return -EFAULT;
return 0;
}
static inline int sctp_getsockopt_get_peer_addrs(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_association_t *asoc;
struct list_head *pos;
int cnt = 0;
struct sctp_getaddrs getaddrs;
struct sctp_transport *from;
struct sockaddr_storage *to;
if (len != sizeof(struct sctp_getaddrs))
return -EINVAL;
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
return -EFAULT;
if (getaddrs.addr_num <= 0) return -EINVAL;
/*
* For UDP-style sockets, id specifies the association to query.
*/
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
to = getaddrs.addrs;
list_for_each(pos, &asoc->peer.transport_addr_list) {
from = list_entry(pos, struct sctp_transport, transports);
if (copy_to_user(to, &from->ipaddr, sizeof(from->ipaddr)))
return -EFAULT;
to ++;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
}
getaddrs.addr_num = cnt;
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
return -EFAULT;
return 0;
}
static inline int sctp_getsockopt_get_local_addrs_num(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_assoc_t id;
sctp_bind_addr_t *bp;
sctp_association_t *asoc;
struct list_head *pos;
int cnt = 0;
if (len != sizeof(sctp_assoc_t))
return -EINVAL;
if (copy_from_user(&id, optval, sizeof(sctp_assoc_t)))
return -EFAULT;
/*
* For UDP-style sockets, id specifies the association to query.
* If the id field is set to the value '0' then the locally bound
* addresses are returned without regard to any particular
* association.
*/
if (0 == id) {
bp = &sctp_sk(sk)->ep->base.bind_addr;
} else {
asoc = sctp_id2assoc(sk, id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
}
list_for_each(pos, &bp->address_list) {
cnt ++;
}
if (copy_to_user(optval, &cnt, sizeof(int)))
return -EFAULT;
return 0;
}
static inline int sctp_getsockopt_get_local_addrs(struct sock *sk, int len,
char *optval, int *optlen)
{
sctp_bind_addr_t *bp;
sctp_association_t *asoc;
struct list_head *pos;
int cnt = 0;
struct sctp_getaddrs getaddrs;
struct sockaddr_storage_list *from;
struct sockaddr_storage *to;
if (len != sizeof(struct sctp_getaddrs))
return -EINVAL;
if (copy_from_user(&getaddrs, optval, sizeof(struct sctp_getaddrs)))
return -EFAULT;
if (getaddrs.addr_num <= 0) return -EINVAL;
/*
* For UDP-style sockets, id specifies the association to query.
* If the id field is set to the value '0' then the locally bound
* addresses are returned without regard to any particular
* association.
*/
if (0 == getaddrs.assoc_id) {
bp = &sctp_sk(sk)->ep->base.bind_addr;
} else {
asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc)
return -EINVAL;
bp = &asoc->base.bind_addr;
}
to = getaddrs.addrs;
list_for_each(pos, &bp->address_list) {
from = list_entry(pos,
struct sockaddr_storage_list,
list);
if (copy_to_user(to, &from->a, sizeof(from->a)))
return -EFAULT;
to ++;
cnt ++;
if (cnt >= getaddrs.addr_num) break;
}
getaddrs.addr_num = cnt;
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs)))
return -EFAULT;
return 0;
}
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen)
{
......@@ -1989,6 +2144,26 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_initmsg(sk, len, optval, optlen);
break;
case SCTP_GET_PEER_ADDRS_NUM:
retval = sctp_getsockopt_get_peer_addrs_num(sk, len, optval,
optlen);
break;
case SCTP_GET_LOCAL_ADDRS_NUM:
retval = sctp_getsockopt_get_local_addrs_num(sk, len, optval,
optlen);
break;
case SCTP_GET_PEER_ADDRS:
retval = sctp_getsockopt_get_peer_addrs(sk, len, optval,
optlen);
break;
case SCTP_GET_LOCAL_ADDRS:
retval = sctp_getsockopt_get_local_addrs(sk, len, optval,
optlen);
break;
default:
retval = -ENOPROTOOPT;
break;
......@@ -2029,7 +2204,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
sctp_protocol_t *sctp = sctp_get_protocol();
unsigned short snum;
int ret;
/* NOTE: Remember to put this back to net order. */
addr->v4.sin_port = ntohs(addr->v4.sin_port);
snum = addr->v4.sin_port;
......@@ -2098,7 +2273,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
}
}
if (pp != NULL && pp->sk != NULL) {
/* We had a port hash table hit - there is an
* available port (pp != NULL) and it is being
......@@ -2129,7 +2304,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
if (sk_reuse && sk2->reuse)
continue;
if (sctp_bind_addr_match(&ep2->base.bind_addr, addr,
sctp_sk(sk)))
goto found;
......@@ -2187,7 +2362,7 @@ static long sctp_get_port_local(struct sock *sk, union sctp_addr *addr)
}
/* Assign a 'snum' port to the socket. If snum == 0, an ephemeral
* port is requested.
* port is requested.
*/
static int sctp_get_port(struct sock *sk, unsigned short snum)
{
......@@ -2657,10 +2832,10 @@ static int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, int len)
return -EINVAL;
/* Is this a valid SCTP address? */
if (!af->addr_valid((union sctp_addr *)addr))
if (!af->addr_valid((union sctp_addr *)addr))
return -EINVAL;
return 0;
return 0;
}
/* Get the sndbuf space available at the time on the association. */
......
/* SCTP kernel reference Implementation
* Copyright (c) 2003 International Business Machines, Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
* These functions manipulate sctp SSN tracker.
*
* The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* The SCTP reference implementation is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied
* ************************
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GNU CC; see the file COPYING. If not, write to
* the Free Software Foundation, 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <lksctp-developers@lists.sourceforge.net>
*
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
*
* Written or modified by:
* Jon Grimm <jgrimm@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
*/
#include <linux/types.h>
#include <net/sctp/sctp.h>
#include <net/sctp/sm.h>
/* Storage size needed for map includes 2 headers and then the
* specific needs of in or out streams.
*/
static inline size_t sctp_ssnmap_size(__u16 in, __u16 out)
{
return sizeof(struct sctp_ssnmap) + (in + out) * sizeof(__u16);
}
/* Create a new sctp_ssnmap.
* Allocate room to store at least 'len' contiguous TSNs.
*/
struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int priority)
{
struct sctp_ssnmap *retval;
retval = kmalloc(sctp_ssnmap_size(in, out), priority);
if (!retval)
goto fail;
if (!sctp_ssnmap_init(retval, in, out))
goto fail_map;
retval->malloced = 1;
SCTP_DBG_OBJCNT_INC(ssnmap);
return retval;
fail_map:
kfree(retval);
fail:
return NULL;
}
/* Initialize a block of memory as a ssnmap. */
struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in,
__u16 out)
{
memset(map, 0x00, sctp_ssnmap_size(in, out));
/* Start 'in' stream just after the map header. */
map->in.ssn = (__u16 *)&map[1];
map->in.len = in;
/* Start 'out' stream just after 'in'. */
map->out.ssn = &map->in.ssn[in];
map->out.len = out;
return map;
}
/* Clear out the ssnmap streams. */
void sctp_ssnmap_clear(struct sctp_ssnmap *map)
{
size_t size;
size = (map->in.len + map->out.len) * sizeof(__u16);
memset(map->in.ssn, 0x00, size);
}
/* Dispose of a ssnmap. */
void sctp_ssnmap_free(struct sctp_ssnmap *map)
{
if (map && map->malloced) {
kfree(map);
SCTP_DBG_OBJCNT_DEC(ssnmap);
}
}
......@@ -45,53 +45,131 @@
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_PRESERVE_ENABLE, "cookie_preserve_enable",
&sctp_proto.cookie_preserve_enable, 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 }
{
.ctl_name = NET_SCTP_RTO_INITIAL,
.procname = "rto_initial",
.data = &sctp_proto.rto_initial,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_SCTP_RTO_MIN,
.procname = "rto_min",
.data = &sctp_proto.rto_min,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_SCTP_RTO_MAX,
.procname = "rto_max",
.data = &sctp_proto.rto_max,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_SCTP_VALID_COOKIE_LIFE,
.procname = "valid_cookie_life",
.data = &sctp_proto.valid_cookie_life,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_SCTP_MAX_BURST,
.procname = "max_burst",
.data = &sctp_proto.max_burst,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_SCTP_ASSOCIATION_MAX_RETRANS,
.procname = "association_max_retrans",
.data = &sctp_proto.max_retrans_association,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_SCTP_PATH_MAX_RETRANS,
.procname = "path_max_retrans",
.data = &sctp_proto.max_retrans_path,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_SCTP_MAX_INIT_RETRANSMITS,
.procname = "max_init_retransmits",
.data = &sctp_proto.max_retrans_init,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_SCTP_HB_INTERVAL,
.procname = "hb_interval",
.data = &sctp_proto.hb_interval,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_SCTP_PRESERVE_ENABLE,
.procname = "cookie_preserve_enable",
.data = &sctp_proto.cookie_preserve_enable,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec_jiffies,
.strategy = &sysctl_jiffies
},
{
.ctl_name = NET_SCTP_RTO_ALPHA,
.procname = "rto_alpha_exp_divisor",
.data = &sctp_proto.rto_alpha,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{
.ctl_name = NET_SCTP_RTO_BETA,
.procname = "rto_beta_exp_divisor",
.data = &sctp_proto.rto_beta,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dointvec
},
{ .ctl_name = 0 }
};
static ctl_table sctp_net_table[] = {
{ NET_SCTP, "sctp", NULL, 0, 0555, sctp_table },
{ 0 }
{
.ctl_name = NET_SCTP,
.procname = "sctp",
.maxlen = 0,
.mode = 0555,
.child = sctp_table
},
{ .ctl_name = 0 }
};
static ctl_table sctp_root_table[] = {
{ CTL_NET, "net", NULL, 0, 0555, sctp_net_table },
{ 0 }
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = sctp_net_table
},
{ .ctl_name = 0 }
};
static struct ctl_table_header * sctp_sysctl_header;
......
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 International Business Machines Corp.
* Copyright (c) 2001-2003 International Business Machines Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 La Monte H.P. Yarroll
*
......@@ -42,6 +42,7 @@
* Xingang Guo <xingang.guo@intel.com>
* Hui Huang <hui.huang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -53,11 +54,12 @@
/* 1st Level Abstractions. */
/* Allocate and initialize a new transport. */
sctp_transport_t *sctp_transport_new(const union sctp_addr *addr, int priority)
struct sctp_transport *sctp_transport_new(const union sctp_addr *addr,
int priority)
{
sctp_transport_t *transport;
struct sctp_transport *transport;
transport = t_new(sctp_transport_t, priority);
transport = t_new(struct sctp_transport, priority);
if (!transport)
goto fail;
......@@ -77,9 +79,9 @@ sctp_transport_t *sctp_transport_new(const union sctp_addr *addr, int priority)
}
/* Intialize a new transport from provided memory. */
sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
const union sctp_addr *addr,
int priority)
struct sctp_transport *sctp_transport_init(struct sctp_transport *peer,
const union sctp_addr *addr,
int priority)
{
sctp_protocol_t *proto = sctp_get_protocol();
......@@ -88,6 +90,9 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
peer->asoc = NULL;
peer->dst = NULL;
memset(&peer->saddr, 0, sizeof(union sctp_addr));
/* From 6.3.1 RTO Calculation:
*
* C1) Until an RTT measurement has been made for a packet sent to the
......@@ -139,7 +144,7 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *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)
void sctp_transport_free(struct sctp_transport *transport)
{
transport->dead = 1;
......@@ -153,7 +158,7 @@ void sctp_transport_free(sctp_transport_t *transport)
/* Destroy the transport data structure.
* Assumes there are no more users of this structure.
*/
void sctp_transport_destroy(sctp_transport_t *transport)
void sctp_transport_destroy(struct sctp_transport *transport)
{
SCTP_ASSERT(transport->dead, "Transport is not dead", return);
......@@ -168,7 +173,7 @@ void sctp_transport_destroy(sctp_transport_t *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)
void sctp_transport_reset_timers(struct sctp_transport *transport)
{
/* RFC 2960 6.3.2 Retransmission Timer Rules
*
......@@ -177,96 +182,61 @@ void sctp_transport_reset_timers(sctp_transport_t *transport)
* start it running so that it will expire after the RTO of that
* address.
*/
if (!timer_pending(&transport->T3_rtx_timer)) {
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);
sctp_transport_timeout(transport)))
sctp_transport_hold(transport);
}
/* This transport has been assigned to an association.
* Initialize fields from the association or from the sock itself.
* Register the reference count in the association.
*/
void sctp_transport_set_owner(sctp_transport_t *transport,
void sctp_transport_set_owner(struct sctp_transport *transport,
sctp_association_t *asoc)
{
transport->asoc = asoc;
sctp_association_hold(asoc);
}
/* Caches the dst entry for a transport's destination address and an optional
* souce address.
*/
void sctp_transport_route(sctp_transport_t *transport, union sctp_addr *saddr,
struct sctp_opt *opt)
/* Initialize the pmtu of a transport. */
void sctp_transport_pmtu(struct sctp_transport *transport)
{
sctp_association_t *asoc = transport->asoc;
struct sctp_af *af = transport->af_specific;
union sctp_addr *daddr = &transport->ipaddr;
sctp_bind_addr_t *bp;
rwlock_t *addr_lock;
struct sockaddr_storage_list *laddr;
struct list_head *pos;
struct dst_entry *dst;
union sctp_addr dst_saddr;
dst = af->get_dst(daddr, saddr);
/* If there is no association or if a source address is passed,
* no more validation is required.
*/
if (!asoc || saddr)
goto out;
if (SCTP_STATE_ESTABLISHED == asoc->state) {
bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
} else {
bp = &asoc->ep->base.bind_addr;
addr_lock = &asoc->ep->base.addr_lock;
}
dst = transport->af_specific->get_dst(NULL, &transport->ipaddr, NULL);
if (dst) {
/* Walk through the bind address list and look for a bind
* address that matches the source address of the returned dst.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list,
list);
af->dst_saddr(&dst_saddr, dst);
if (opt->pf->cmp_addr(&dst_saddr, &laddr->a, opt))
goto out_unlock;
}
sctp_read_unlock(addr_lock);
/* None of the bound addresses match the source address of the
* dst. So release it.
*/
transport->pmtu = dst_pmtu(dst);
dst_release(dst);
}
} else
transport->pmtu = SCTP_DEFAULT_MAXSEGMENT;
}
/* Walk through the bind address list and try to get a dst that
* matches a bind address as the source address.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, list);
/* Caches the dst entry and source address for a transport's destination
* address.
*/
void sctp_transport_route(struct sctp_transport *transport,
union sctp_addr *saddr, struct sctp_opt *opt)
{
sctp_association_t *asoc = transport->asoc;
struct sctp_af *af = transport->af_specific;
union sctp_addr *daddr = &transport->ipaddr;
struct dst_entry *dst;
dst = af->get_dst(daddr, &laddr->a);
if (dst)
goto out_unlock;
}
dst = af->get_dst(asoc, daddr, saddr);
if (saddr)
memcpy(&transport->saddr, saddr, sizeof(union sctp_addr));
else
af->get_saddr(asoc, dst, daddr, &transport->saddr);
out_unlock:
sctp_read_unlock(addr_lock);
out:
transport->dst = dst;
if (dst)
transport->pmtu = dst_pmtu(dst);
......@@ -275,7 +245,7 @@ void sctp_transport_route(sctp_transport_t *transport, union sctp_addr *saddr,
}
/* Hold a reference to a transport. */
void sctp_transport_hold(sctp_transport_t *transport)
void sctp_transport_hold(struct sctp_transport *transport)
{
atomic_inc(&transport->refcnt);
}
......@@ -283,14 +253,14 @@ void sctp_transport_hold(sctp_transport_t *transport)
/* Release a reference to a transport and clean up
* if there are no more references.
*/
void sctp_transport_put(sctp_transport_t *transport)
void sctp_transport_put(struct sctp_transport *transport)
{
if (atomic_dec_and_test(&transport->refcnt))
sctp_transport_destroy(transport);
}
/* Update transport's RTO based on the newly calculated RTT. */
void sctp_transport_update_rto(sctp_transport_t *tp, __u32 rtt)
void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
{
sctp_protocol_t *proto = sctp_get_protocol();
......@@ -330,7 +300,7 @@ void sctp_transport_update_rto(sctp_transport_t *tp, __u32 rtt)
if (tp->rttvar == 0)
tp->rttvar = SCTP_CLOCK_GRANULARITY;
/* 6.3.1 C3) After the computation, update RTO <- SRTT + 4 * RTTVAR. */
/* 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
......@@ -360,8 +330,8 @@ void sctp_transport_update_rto(sctp_transport_t *tp, __u32 rtt)
/* 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)
void sctp_transport_raise_cwnd(struct sctp_transport *transport,
__u32 sack_ctsn, __u32 bytes_acked)
{
__u32 cwnd, ssthresh, flight_size, pba, pmtu;
......@@ -389,8 +359,8 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn,
* 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.
* 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;
......@@ -403,18 +373,18 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn,
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.
/* 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
* 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;
......@@ -437,7 +407,7 @@ void sctp_transport_raise_cwnd(sctp_transport_t *transport, __u32 sack_ctsn,
/* This routine is used to lower the transport's cwnd when congestion is
* detected.
*/
void sctp_transport_lower_cwnd(sctp_transport_t *transport,
void sctp_transport_lower_cwnd(struct sctp_transport *transport,
sctp_lower_cwnd_t reason)
{
switch (reason) {
......@@ -514,3 +484,12 @@ void sctp_transport_lower_cwnd(sctp_transport_t *transport,
transport, reason,
transport->cwnd, transport->ssthresh);
}
/* What is the next timeout value for this transport? */
unsigned long sctp_transport_timeout(struct sctp_transport *t)
{
unsigned long timeout;
timeout = t->hb_interval + t->rto + sctp_jitter(t->rto);
timeout += jiffies;
return timeout;
}
......@@ -606,9 +606,9 @@ sctp_ulpevent_t *sctp_ulpevent_make_shutdown_event(
sctp_ulpevent_t *sctp_ulpevent_make_rcvmsg(sctp_association_t *asoc,
sctp_chunk_t *chunk, int priority)
{
sctp_ulpevent_t *event;
sctp_ulpevent_t *event, *levent;
struct sctp_sndrcvinfo *info;
struct sk_buff *skb;
struct sk_buff *skb, *list;
size_t padding, len;
/* Clone the original skb, sharing the data. */
......@@ -647,6 +647,16 @@ sctp_ulpevent_t *sctp_ulpevent_make_rcvmsg(sctp_association_t *asoc,
event->malloced = 1;
for (list = skb_shinfo(skb)->frag_list; list; list = list->next) {
sctp_ulpevent_set_owner_r(list, asoc);
/* Initialize event with flags 0. */
levent = sctp_ulpevent_init(event, skb, 0);
if (!levent)
goto fail_init;
levent->malloced = 1;
}
info = (struct sctp_sndrcvinfo *) &event->sndrcvinfo;
/* Sockets API Extensions for SCTP
......@@ -762,8 +772,6 @@ static void sctp_rcvmsg_rfree(struct sk_buff *skb)
{
sctp_association_t *asoc;
sctp_ulpevent_t *event;
sctp_chunk_t *sack;
struct timer_list *timer;
/* Current stack structures assume that the rcv buffer is
* per socket. For UDP style sockets this is not true as
......@@ -773,50 +781,7 @@ static void sctp_rcvmsg_rfree(struct sk_buff *skb)
*/
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) - %u\n",
skb->len, asoc->rwnd, asoc->rwnd_over, asoc->a_rwnd);
/* Send a window update SACK if the rwnd has increased by at least the
* minimum of the association's PMTU and half of the receive buffer.
* The algorithm used is similar to the one described in Section 4.2.3.3
* of RFC 1122.
*/
if ((asoc->state == SCTP_STATE_ESTABLISHED) &&
(asoc->rwnd > asoc->a_rwnd) &&
((asoc->rwnd - asoc->a_rwnd) >=
min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) {
SCTP_DEBUG_PRINTK("Sending window update SACK- rwnd: %u "
"a_rwnd: %u\n", asoc->rwnd, asoc->a_rwnd);
sack = sctp_make_sack(asoc);
if (!sack)
goto out;
/* Update the last advertised rwnd value. */
asoc->a_rwnd = asoc->rwnd;
asoc->peer.sack_needed = 0;
asoc->peer.next_dup_tsn = 0;
sctp_push_outqueue(&asoc->outqueue, sack);
/* Stop the SACK timer. */
timer = &asoc->timers[SCTP_EVENT_TIMEOUT_SACK];
if (timer_pending(timer) && del_timer(timer))
sctp_association_put(asoc);
}
out:
sctp_assoc_rwnd_increase(asoc, skb_headlen(skb));
sctp_association_put(asoc);
}
......@@ -838,16 +803,7 @@ static void sctp_ulpevent_set_owner_r(struct sk_buff *skb, sctp_association_t *a
skb->destructor = sctp_rcvmsg_rfree;
SCTP_ASSERT(asoc->rwnd, "rwnd zero", return);
SCTP_ASSERT(!asoc->rwnd_over, "rwnd_over not zero", return);
if (asoc->rwnd >= skb->len) {
asoc->rwnd -= skb->len;
} else {
asoc->rwnd_over = skb->len - asoc->rwnd;
asoc->rwnd = 0;
}
SCTP_DEBUG_PRINTK("rwnd decreased by %d to (%u, %u)\n",
skb->len, asoc->rwnd, asoc->rwnd_over);
sctp_assoc_rwnd_decrease(asoc, skb_headlen(skb));
}
/* A simple destructor to give up the reference to the association. */
......
/* 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-2003 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
......@@ -49,51 +49,39 @@
#include <net/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);
static inline struct sctp_ulpevent * sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
struct sctp_ulpevent *);
static inline struct sctp_ulpevent *sctp_ulpq_order(struct sctp_ulpq *,
struct sctp_ulpevent *);
/* 1st Level Abstractions */
/* Create a new ULP queue. */
sctp_ulpqueue_t *sctp_ulpqueue_new(sctp_association_t *asoc,
__u16 inbound, int priority)
struct sctp_ulpq *sctp_ulpq_new(sctp_association_t *asoc, int priority)
{
sctp_ulpqueue_t *ulpq;
size_t size;
struct sctp_ulpq *ulpq;
/* 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);
ulpq = kmalloc(sizeof(struct sctp_ulpq), priority);
if (!ulpq)
goto fail;
if (!sctp_ulpqueue_init(ulpq, asoc, inbound))
if (!sctp_ulpq_init(ulpq, asoc))
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)
struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq,
sctp_association_t *asoc)
{
memset(ulpq,
sizeof(sctp_ulpqueue_t) + sctp_ulpqueue_storage_size(inbound),
0x00);
memset(ulpq, sizeof(struct sctp_ulpq), 0x00);
ulpq->asoc = asoc;
spin_lock_init(&ulpq->lock);
skb_queue_head_init(&ulpq->reasm);
skb_queue_head_init(&ulpq->lobby);
ulpq->malloced = 0;
......@@ -101,38 +89,39 @@ sctp_ulpqueue_t *sctp_ulpqueue_init(sctp_ulpqueue_t *ulpq,
return ulpq;
}
/* Flush the reassembly and ordering queues. */
void sctp_ulpqueue_flush(sctp_ulpqueue_t *ulpq)
void sctp_ulpq_flush(struct sctp_ulpq *ulpq)
{
struct sk_buff *skb;
sctp_ulpevent_t *event;
struct sctp_ulpevent *event;
while ((skb = skb_dequeue(&ulpq->lobby))) {
event = (sctp_ulpevent_t *) skb->cb;
event = (struct sctp_ulpevent *) skb->cb;
sctp_ulpevent_free(event);
}
while ((skb = skb_dequeue(&ulpq->reasm))) {
event = (sctp_ulpevent_t *) skb->cb;
event = (struct sctp_ulpevent *) skb->cb;
sctp_ulpevent_free(event);
}
}
/* Dispose of a ulpqueue. */
void sctp_ulpqueue_free(sctp_ulpqueue_t *ulpq)
void sctp_ulpq_free(struct sctp_ulpq *ulpq)
{
sctp_ulpqueue_flush(ulpq);
sctp_ulpq_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 sctp_ulpq_tail_data(struct sctp_ulpq *ulpq, sctp_chunk_t *chunk,
int priority)
{
struct sk_buff_head temp;
sctp_data_chunk_t *hdr;
sctp_ulpevent_t *event;
struct sctp_ulpevent *event;
hdr = (sctp_data_chunk_t *) chunk->chunk_hdr;
......@@ -147,7 +136,7 @@ int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *ulpq, sctp_chunk_t *chunk,
return -ENOMEM;
/* Do reassembly if needed. */
event = sctp_ulpqueue_reasm(ulpq, event);
event = sctp_ulpq_reasm(ulpq, event);
/* Do ordering if needed. */
if (event) {
......@@ -155,18 +144,18 @@ int sctp_ulpqueue_tail_data(sctp_ulpqueue_t *ulpq, sctp_chunk_t *chunk,
skb_queue_head_init(&temp);
skb_queue_tail(&temp, event->parent);
event = sctp_ulpqueue_order(ulpq, event);
event = sctp_ulpq_order(ulpq, event);
}
/* Send event to the ULP. */
if (event)
sctp_ulpqueue_tail_event(ulpq, event);
sctp_ulpq_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)
int sctp_ulpq_tail_event(struct sctp_ulpq *ulpq, struct sctp_ulpevent *event)
{
struct sock *sk = ulpq->asoc->base.sk;
......@@ -202,20 +191,18 @@ int sctp_ulpqueue_tail_event(sctp_ulpqueue_t *ulpq, sctp_ulpevent_t *event)
/* 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)
static inline void sctp_ulpq_store_reasm(struct sctp_ulpq *ulpq,
struct sctp_ulpevent *event)
{
struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent;
struct sctp_ulpevent *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;
cevent = (struct sctp_ulpevent *)pos->cb;
ctsn = cevent->sndrcvinfo.sinfo_tsn;
if (TSN_lt(tsn, ctsn))
......@@ -227,29 +214,45 @@ static inline void sctp_ulpqueue_store_reasm(sctp_ulpqueue_t *ulpq, sctp_ulpeven
__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.
* This routine creates a re-assembled skb given the first and last skb's
* as stored in the reassembly queue. The skb's may be non-linear if the sctp
* payload was fragmented on the way and ip had to reassemble them.
* We add the rest of skb's to the first skb's fraglist.
*/
static inline sctp_ulpevent_t *sctp_make_reassembled_event(struct sk_buff *f_frag, struct sk_buff *l_frag)
static inline struct sctp_ulpevent *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;
struct sctp_ulpevent *event;
struct sk_buff *pnext, *last;
struct sk_buff *list = skb_shinfo(f_frag)->frag_list;
/* Store the pointer to the 2nd skb */
pos = f_frag->next;
/* Set the first fragment's frag_list to point to the 2nd fragment. */
skb_shinfo(f_frag)->frag_list = pos;
/* Get the last skb in the f_frag's frag_list if present. */
for (last = list; list; last = list, list = list->next);
/* Add the list of remaining fragments to the first fragments
* frag_list.
*/
if (last)
last->next = pos;
else
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;
/* Update the len and data_len fields of the first fragment. */
f_frag->len += pos->len;
f_frag->data_len += pos->len;
/* Remove the fragment from the reassembly queue. */
__skb_unlink(pos, pos->list);
......@@ -269,13 +272,12 @@ static inline sctp_ulpevent_t *sctp_make_reassembled_event(struct sk_buff *f_fra
/* 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)
static inline sctp_ulpevent_t *sctp_ulpq_retrieve_reassembled(struct sctp_ulpq *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
......@@ -284,8 +286,6 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_
*/
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.
......@@ -327,7 +327,6 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_
if (retval)
break;
}
sctp_spin_unlock_irqrestore(&ulpq->reasm.lock, flags);
return retval;
}
......@@ -335,7 +334,7 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_retrieve_reassembled(sctp_ulpqueue_
/* 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,
static inline sctp_ulpevent_t *sctp_ulpq_reasm(struct sctp_ulpq *ulpq,
sctp_ulpevent_t *event)
{
sctp_ulpevent_t *retval = NULL;
......@@ -350,8 +349,8 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq,
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);
sctp_ulpq_store_reasm(ulpq, event);
retval = sctp_ulpq_retrieve_reassembled(ulpq);
return retval;
}
......@@ -359,20 +358,20 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_reasm(sctp_ulpqueue_t *ulpq,
/* 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)
static inline void sctp_ulpq_retrieve_ordered(struct sctp_ulpq *ulpq,
sctp_ulpevent_t *event)
{
struct sk_buff *pos, *tmp;
sctp_ulpevent_t *cevent;
struct sctp_ulpevent *cevent;
struct sctp_stream *in;
__u16 sid, csid;
__u16 ssn, cssn;
unsigned long flags __attribute ((unused));
sid = event->sndrcvinfo.sinfo_stream;
ssn = event->sndrcvinfo.sinfo_ssn;
in = &ulpq->asoc->ssnmap->in;
/* 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;
......@@ -386,32 +385,31 @@ static inline void sctp_ulpqueue_retrieve_ordered(sctp_ulpqueue_t *ulpq,
if (csid < sid)
continue;
if (cssn != ulpq->ssn[sid])
if (cssn != sctp_ssn_peek(in, sid))
break;
ulpq->ssn[sid]++;
/* Found it, so mark in the ssnmap. */
sctp_ssn_next(in, 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)
static inline void sctp_ulpq_store_ordered(struct sctp_ulpq *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.
......@@ -432,14 +430,13 @@ static inline void sctp_ulpqueue_store_ordered(sctp_ulpqueue_t *ulpq,
__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)
static inline sctp_ulpevent_t *sctp_ulpq_order(struct sctp_ulpq *ulpq,
sctp_ulpevent_t *event)
{
__u16 sid, ssn;
struct sctp_stream *in;
/* FIXME: We should be using some new chunk structure here
* instead of carrying chunk fields in the event structure.
......@@ -454,23 +451,24 @@ static inline sctp_ulpevent_t *sctp_ulpqueue_order(sctp_ulpqueue_t *ulpq,
/* Note: The stream ID must be verified before this routine. */
sid = event->sndrcvinfo.sinfo_stream;
ssn = event->sndrcvinfo.sinfo_ssn;
in = &ulpq->asoc->ssnmap->in;
/* Is this the expected SSN for this stream ID? */
if (ssn != ulpq->ssn[sid]) {
if (ssn != sctp_ssn_peek(in, sid)) {
/* We've received something out of order, so find where it
* needs to be placed. We order by stream and then by SSN.
*/
sctp_ulpqueue_store_ordered(ulpq, event);
sctp_ulpq_store_ordered(ulpq, event);
return NULL;
}
/* Mark that the next chunk has been found. */
ulpq->ssn[sid]++;
sctp_ssn_next(in, sid);
/* Go find any other chunks that were waiting for
* ordering.
*/
sctp_ulpqueue_retrieve_ordered(ulpq, event);
sctp_ulpq_retrieve_ordered(ulpq, event);
return event;
}
......@@ -116,23 +116,51 @@ proc_dodebug(ctl_table *table, int write, struct file *file,
return 0;
}
#define DIRENTRY(nam1, nam2, child) \
{CTL_##nam1, #nam2, NULL, 0, 0555, child }
#define DBGENTRY(nam1, nam2) \
{CTL_##nam1##DEBUG, #nam2 "_debug", &nam2##_debug, sizeof(int),\
0644, NULL, &proc_dodebug}
static ctl_table debug_table[] = {
DBGENTRY(RPC, rpc),
DBGENTRY(NFS, nfs),
DBGENTRY(NFSD, nfsd),
DBGENTRY(NLM, nlm),
{0}
static ctl_table debug_table[] = {
{
.ctl_name = CTL_RPCDEBUG,
.procname = "rpc_debug",
.data = &rpc_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dodebug
},
{
.ctl_name = CTL_NFSDEBUG,
.procname = "nfs_debug",
.data = &nfs_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dodebug
},
{
.ctl_name = CTL_NFSDDEBUG,
.procname = "nfsd_debug",
.data = &nfsd_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dodebug
},
{
.ctl_name = CTL_NLMDEBUG,
.procname = "nlm_debug",
.data = &nlm_debug,
.maxlen = sizeof(int),
.mode = 0644,
.proc_handler = &proc_dodebug
},
{ .ctl_name = 0 }
};
static ctl_table sunrpc_table[] = {
DIRENTRY(SUNRPC, sunrpc, debug_table),
{0}
static ctl_table sunrpc_table[] = {
{
.ctl_name = CTL_SUNRPC,
.procname = "sunrpc",
.maxlen = 0,
.mode = 0555,
.child = debug_table
},
{ .ctl_name = 0 }
};
#endif
......@@ -15,20 +15,37 @@
extern int sysctl_unix_max_dgram_qlen;
ctl_table unix_table[] = {
{NET_UNIX_MAX_DGRAM_QLEN, "max_dgram_qlen",
&sysctl_unix_max_dgram_qlen, sizeof(int), 0600, NULL,
&proc_dointvec },
{0}
{
.ctl_name = NET_UNIX_MAX_DGRAM_QLEN,
.procname = "max_dgram_qlen",
.data = &sysctl_unix_max_dgram_qlen,
.maxlen = sizeof(int),
.mode = 0600,
.proc_handler = &proc_dointvec
},
{ .ctl_name = 0 }
};
static ctl_table unix_net_table[] = {
{NET_UNIX, "unix", NULL, 0, 0555, unix_table},
{0}
{
.ctl_name = NET_UNIX,
.procname = "unix",
.maxlen = 0,
.mode = 0555,
.child = unix_table
},
{ .ctl_name = 0 }
};
static ctl_table unix_root_table[] = {
{CTL_NET, "net", NULL, 0, 0555, unix_net_table},
{0}
{
.ctl_name = CTL_NET,
.procname = "net",
.maxlen = 0,
.mode = 0555,
.child = unix_net_table
},
{ .ctl_name = 0 }
};
static struct ctl_table_header * unix_sysctl_header;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment