Commit 5d068c48 authored by Sridhar Samudrala's avatar Sridhar Samudrala

Merge dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/linux-2.5.46

into dyn9-47-18-140.beaverton.ibm.com:/home/sridhar/BK/lksctp-2.5.46
parents db01fdce 26e2d0a7
/* 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-2002 International Business Machines, Corp.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll
......@@ -29,7 +29,7 @@
*
* Please send any bug reports or fixes you make to the
* email address(es):
* lksctp developers <sctp-developers-list@cig.mot.com>
* lksctp developers <lksctp-developerst@lists.sourceforge.net>
*
* Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp
......@@ -419,10 +419,12 @@ typedef enum {
/* SCTP Implementation Guide:
* 11 Restart of an association with new addresses
* 12 User Initiated Abort
* 13 Protocol Violation
*/
SCTP_ERROR_RESTART = __constant_htons(0x0b),
SCTP_ERROR_USER_ABORT = __constant_htons(0x0c),
SCTP_ERROR_PROTO_VIOLATION = __constant_htons(0x0d),
/* ADDIP Section 3.3 New Error Causes
*
......@@ -583,7 +585,4 @@ typedef struct {
sctpAsconfAckRsp_t TLV;
} sctpAsconfAckRspNode_t;
#endif /* __LINUX_SCTP_H__ */
......@@ -27,6 +27,7 @@
*
* 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.
......@@ -72,6 +73,7 @@ typedef enum {
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_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. */
......
......@@ -83,6 +83,7 @@
#include <asm/uaccess.h>
#include <asm/page.h>
#include <net/sock.h>
#include <net/snmp.h>
#include <net/sctp/structs.h>
#include <net/sctp/constants.h>
#include <net/sctp/sm.h>
......@@ -141,7 +142,7 @@ extern int sctp_primitive_ASSOCIATE(sctp_association_t *, void *arg);
extern int sctp_primitive_SHUTDOWN(sctp_association_t *, void *arg);
extern int sctp_primitive_ABORT(sctp_association_t *, void *arg);
extern int sctp_primitive_SEND(sctp_association_t *, void *arg);
extern int sctp_primitive_REQUESTHEARTBEAT(sctp_association_t *, void *arg);
/*
* sctp_crc32c.c
......@@ -201,6 +202,11 @@ extern void sctp_hash_digest(const char *secret, const int secret_len,
#define SCTP_SOCK_SLEEP_PRE(sk) SOCK_SLEEP_PRE(sk)
#define SCTP_SOCK_SLEEP_POST(sk) SOCK_SLEEP_POST(sk)
/* SCTP SNMP MIB stats handlers */
extern struct sctp_mib sctp_statistics[NR_CPUS * 2];
#define SCTP_INC_STATS(field) SNMP_INC_STATS(sctp_statistics, field)
#define SCTP_INC_STATS_BH(field) SNMP_INC_STATS_BH(sctp_statistics, field)
#define SCTP_INC_STATS_USER(field) SNMP_INC_STATS_USER(sctp_statistics, field)
/* Determine if this is a valid kernel address. */
static inline int sctp_is_valid_kaddr(unsigned long addr)
......@@ -412,6 +418,19 @@ static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
return retval;
}
/* 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
* there is room for a param header too.
*/
#define sctp_walk_params(pos, chunk, member)\
_sctp_walk_params((pos), (chunk), ntohs((chunk)->chunk_hdr.length), member)
#define _sctp_walk_params(pos, chunk, end, member)\
for (pos.v = chunk->member;\
pos.v <= (void *)chunk + end - sizeof(sctp_paramhdr_t) &&\
pos.v <= (void *)chunk + end - WORD_ROUND(ntohs(pos.p->length)); \
pos.v += WORD_ROUND(ntohs(pos.p->length)))
/* Round an int up to the next multiple of 4. */
#define WORD_ROUND(s) (((s)+3)&~3)
......
......@@ -44,6 +44,7 @@
* Dajiang Zhang <dajiang.zhang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -156,6 +157,7 @@ sctp_state_fn_t sctp_sf_shutdown_ack_sent_prm_abort;
sctp_state_fn_t sctp_sf_error_closed;
sctp_state_fn_t sctp_sf_error_shutdown;
sctp_state_fn_t sctp_sf_ignore_primitive;
sctp_state_fn_t sctp_sf_do_prm_requestheartbeat;
/* Prototypes for other event state functions. */
sctp_state_fn_t sctp_sf_do_9_2_start_shutdown;
......@@ -205,9 +207,6 @@ sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *,
sctp_chunk_t *,
const int priority);
__u32 sctp_generate_verification_tag(void);
sctpParam_t sctp_get_my_addrs_raw(const sctp_association_t *,
const int priority, int *addrs_len);
void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag);
/* Prototypes for chunk-building functions. */
......@@ -253,6 +252,9 @@ sctp_chunk_t *sctp_make_abort(const sctp_association_t *,
sctp_chunk_t *sctp_make_abort_no_data(const sctp_association_t *,
const sctp_chunk_t *,
__u32 tsn);
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 void *payload,
......@@ -332,7 +334,7 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *);
/* 4th level prototypes */
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *,
__u16 port);
int sctp_addr2sockaddr(const sctpParam_t, sockaddr_storage_t *);
int sctp_addr2sockaddr(const union sctp_params, sockaddr_storage_t *);
int sockaddr2sctp_addr(const sockaddr_storage_t *, sctp_addr_param_t *);
/* Extern declarations for major data structures. */
......
......@@ -246,7 +246,10 @@ typedef struct sctp_func {
int optname,
char *optval,
int *optlen);
int (*get_dst_mtu) (const sockaddr_storage_t *address);
struct dst_entry *(*get_dst) (sockaddr_storage_t *daddr,
sockaddr_storage_t *saddr);
int (*cmp_saddr) (struct dst_entry *dst,
sockaddr_storage_t *saddr);
__u16 net_header_len;
int sockaddr_len;
sa_family_t sa_family;
......@@ -356,20 +359,6 @@ typedef struct sctp_signed_cookie {
} sctp_signed_cookie_t;
/* This convenience type allows us to avoid casting when walking
* through a parameter list.
*/
typedef union {
__u8 *v;
sctp_paramhdr_t *p;
sctp_cookie_preserve_param_t *bht;
sctp_hostname_param_t *dns;
sctp_cookie_param_t *cookie;
sctp_supported_addrs_param_t *sat;
sctp_ipv4addr_param_t *v4;
sctp_ipv6addr_param_t *v6;
} sctpParam_t;
/* This is another convenience type to allocate memory for address
* params for the maximum size and pass such structures around
......@@ -380,6 +369,21 @@ typedef union {
sctp_ipv6addr_param_t v6;
} sctp_addr_param_t;
/* A convenience type to allow walking through the various
* parameters and avoid casting all over the place.
*/
union sctp_params {
void *v;
sctp_paramhdr_t *p;
sctp_cookie_preserve_param_t *life;
sctp_hostname_param_t *dns;
sctp_cookie_param_t *cookie;
sctp_supported_addrs_param_t *sat;
sctp_ipv4addr_param_t *v4;
sctp_ipv6addr_param_t *v6;
sctp_addr_param_t *addr;
};
/* RFC 2960. Section 3.3.5 Heartbeat.
* Heartbeat Information: variable length
* The Sender-specific Heartbeat Info field should normally include
......@@ -430,7 +434,7 @@ struct SCTP_chunk {
*/
/* We point this at the FIRST TLV parameter to chunk_hdr. */
sctpParam_t param_hdr;
union sctp_params param_hdr;
union {
__u8 *v;
sctp_datahdr_t *data_hdr;
......@@ -476,6 +480,8 @@ struct SCTP_chunk {
/* What is the origin IP address for this chunk? */
sockaddr_storage_t source;
/* Destination address for this chunk. */
sockaddr_storage_t dest;
/* For an inbound chunk, this tells us where it came from.
* For an outbound chunk, it tells us where we'd like it to
......@@ -492,7 +498,7 @@ void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data);
int sctp_user_addto_chunk(sctp_chunk_t *chunk, int len, struct iovec *data);
sctp_chunk_t *sctp_chunkify(struct sk_buff *, const sctp_association_t *,
struct sock *);
void sctp_init_source(sctp_chunk_t *chunk);
void sctp_init_addrs(sctp_chunk_t *chunk);
const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk);
/* This is a structure for holding either an IPv6 or an IPv4 address. */
......@@ -655,6 +661,9 @@ struct SCTP_transport {
/* PMTU : The current known path MTU. */
__u32 pmtu;
/* Destination */
struct dst_entry *dst;
/* When was the last time(in jiffies) that a data packet was sent on
* this transport? This is used to adjust the cwnd when the transport
* becomes inactive.
......@@ -676,13 +685,15 @@ struct SCTP_transport {
*/
unsigned long last_time_ecne_reduced;
/* state : The current state of this destination,
* : i.e. DOWN, UP, ALLOW-HB, NO-HEARTBEAT, etc.
/* active : The current active state of this destination,
* : i.e. DOWN, UP, etc.
*/
struct {
int active;
/* hb_allowed : The current heartbeat state of this destination,
* : i.e. ALLOW-HB, NO-HEARTBEAT, etc.
*/
int hb_allowed;
} state;
/* These are the error stats for this destination. */
......@@ -735,6 +746,7 @@ extern sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *, int);
extern sctp_transport_t *sctp_transport_init(sctp_transport_t *,
const sockaddr_storage_t *, int);
extern void sctp_transport_set_owner(sctp_transport_t *, sctp_association_t *);
extern void sctp_transport_route(sctp_transport_t *, sockaddr_storage_t *);
extern void sctp_transport_free(sctp_transport_t *);
extern void sctp_transport_destroy(sctp_transport_t *);
extern void sctp_transport_reset_timers(sctp_transport_t *);
......@@ -885,7 +897,7 @@ int sctp_add_bind_addr(sctp_bind_addr_t *, sockaddr_storage_t *,
int priority);
int sctp_del_bind_addr(sctp_bind_addr_t *, sockaddr_storage_t *);
int sctp_bind_addr_has_addr(sctp_bind_addr_t *, const sockaddr_storage_t *);
sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
int *addrs_len,
int priority);
int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp,
......@@ -1052,22 +1064,11 @@ int sctp_verify_init(const sctp_association_t *asoc,
sctp_init_chunk_t *peer_init,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk);
int sctp_verify_param(const sctp_association_t *asoc,
sctpParam_t param,
sctp_cid_t cid,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk);
int sctp_process_unk_param(const sctp_association_t *asoc,
sctpParam_t param,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk);
void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
const sockaddr_storage_t *peer_addr,
sctp_init_chunk_t *peer_init, int priority);
int sctp_process_param(sctp_association_t *asoc,
sctpParam_t param,
const sockaddr_storage_t *peer_addr,
sctp_cid_t cid, int priority);
int sctp_process_param(sctp_association_t *asoc, union sctp_params param,
const sockaddr_storage_t *peer_addr, int priority);
__u32 sctp_generate_tag(const sctp_endpoint_t *ep);
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep);
......
......@@ -189,6 +189,35 @@ struct udp_mib
unsigned long __pad[0];
} ____cacheline_aligned;
/* draft-ietf-sigtran-sctp-mib-07.txt */
struct sctp_mib
{
unsigned long SctpCurrEstab;
unsigned long SctpActiveEstabs;
unsigned long SctpPassiveEstabs;
unsigned long SctpAborteds;
unsigned long SctpShutdowns;
unsigned long SctpOutOfBlues;
unsigned long SctpChecksumErrors;
unsigned long SctpOutCtrlChunks;
unsigned long SctpOutOrderChunks;
unsigned long SctpOutUnorderChunks;
unsigned long SctpInCtrlChunks;
unsigned long SctpInOrderChunks;
unsigned long SctpInUnorderChunks;
unsigned long SctpFragUsrMsgs;
unsigned long SctpReasmUsrMsgs;
unsigned long SctpOutSCTPPacks;
unsigned long SctpInSCTPPacks;
unsigned long SctpRtoAlgorithm;
unsigned long SctpRtoMin;
unsigned long SctpRtoMax;
unsigned long SctpRtoInitial;
unsigned long SctpValCookieLife;
unsigned long SctpMaxInitRetr;
unsigned long __pad[0];
} ____cacheline_aligned;
struct linux_mib
{
unsigned long SyncookiesSent;
......
......@@ -208,7 +208,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
asoc->ctsn_ack_point = asoc->next_tsn - 1;
asoc->highest_sacked = asoc->ctsn_ack_point;
asoc->last_cwr_tsn = asoc->ctsn_ack_point;
asoc->unack_data = 0;
SCTP_DEBUG_PRINTK("myctsnap for %s INIT as 0x%x.\n",
......@@ -404,6 +404,9 @@ 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);
/* If this is the first transport addr on this association,
* initialize the association PMTU to the peer's PMTU.
* If not and the current association PMTU is higher than the new
......@@ -458,6 +461,9 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
min(asoc->overall_error_threshold + peer->error_threshold,
asoc->max_retrans);
/* By default, enable heartbeat for peer address. */
peer->hb_allowed = 1;
/* Initialize the peer's heartbeat interval based on the
* sock configured value.
*/
......@@ -520,12 +526,12 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
/* Record the transition on the transport. */
switch (command) {
case SCTP_TRANSPORT_UP:
transport->state.active = 1;
transport->active = 1;
spc_state = ADDRESS_AVAILABLE;
break;
case SCTP_TRANSPORT_DOWN:
transport->state.active = 0;
transport->active = 0;
spc_state = ADDRESS_UNREACHABLE;
break;
......@@ -555,7 +561,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports);
if (!t->state.active)
if (!t->active)
continue;
if (!first || t->last_time_heard > first->last_time_heard) {
second = first;
......@@ -575,7 +581,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
* [If the primary is active but not most recent, bump the most
* recently used transport.]
*/
if (asoc->peer.primary_path->state.active &&
if (asoc->peer.primary_path->active &&
first != asoc->peer.primary_path) {
second = first;
first = asoc->peer.primary_path;
......@@ -1015,7 +1021,7 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
/* Try to find an active transport. */
if (t->state.active) {
if (t->active) {
break;
} else {
/* Keep track of the next transport in case
......
......@@ -196,18 +196,16 @@ int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr)
*
* The second argument is the return value for the length.
*/
sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
int priority)
union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
int *addrs_len, int priority)
{
sctpParam_t addrparms;
sctpParam_t retval;
union sctp_params addrparms;
union sctp_params retval;
int addrparms_len;
sctp_addr_param_t rawaddr;
int len;
struct sockaddr_storage_list *addr;
struct list_head *pos;
retval.v = NULL;
addrparms_len = 0;
len = 0;
......@@ -216,11 +214,11 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len,
len += sizeof(sctp_addr_param_t);
}
addrparms.v = kmalloc(len, priority);
if (!addrparms.v)
retval.v = kmalloc(len, priority);
if (!retval.v)
goto end_raw;
retval = addrparms;
addrparms = retval;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
......
......@@ -41,6 +41,7 @@
* Jon Grimm <jgrimm@us.ibm.com>
* Hui Huang <hui.huang@nokia.com>
* Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -217,8 +218,8 @@ int sctp_rcv(struct sk_buff *skb)
/* Remember the SCTP header. */
chunk->sctp_hdr = sh;
/* Set the source address. */
sctp_init_source(chunk);
/* Set the source and destination addresses of the incoming chunk. */
sctp_init_addrs(chunk);
/* Remember where we came from. */
chunk->transport = transport;
......@@ -613,13 +614,11 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
sockaddr_storage_t *paddr = &addr;
struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
sctp_chunkhdr_t *ch;
__u8 *ch_end, *data;
sctp_paramhdr_t *parm;
union sctp_params params;
sctp_init_chunk_t *init;
ch = (sctp_chunkhdr_t *) skb->data;
ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length));
/* If this is INIT/INIT-ACK look inside the chunk too. */
switch (ch->type) {
case SCTP_CID_INIT:
......@@ -645,24 +644,17 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
/* Find the start of the TLVs and the end of the chunk. This is
* the region we search for address parameters.
*/
data = skb->data + sizeof(sctp_init_chunk_t);
/* See sctp_process_init() for how to go thru TLVs. */
while (data < ch_end) {
parm = (sctp_paramhdr_t *)data;
if (!parm->length)
break;
init = (sctp_init_chunk_t *)skb->data;
data += WORD_ROUND(ntohs(parm->length));
/* Walk the parameters looking for embedded addresses. */
sctp_walk_params(params, init, init_hdr.params) {
/* Note: Ignoring hostname addresses. */
if ((SCTP_PARAM_IPV4_ADDRESS != parm->type) &&
(SCTP_PARAM_IPV6_ADDRESS != parm->type))
if ((SCTP_PARAM_IPV4_ADDRESS != params.p->type) &&
(SCTP_PARAM_IPV6_ADDRESS != params.p->type))
continue;
sctp_param2sockaddr(paddr, (sctp_addr_param_t *)parm,
ntohs(sh->source));
sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source));
asoc = __sctp_lookup_association(laddr, paddr, transportp);
if (asoc)
return asoc;
......
......@@ -158,35 +158,60 @@ static inline int sctp_v6_xmit(struct sk_buff *skb)
}
#endif /* TEST_FRAME */
/* Returns the mtu for the given v6 destination address. */
int sctp_v6_get_dst_mtu(const sockaddr_storage_t *address)
/* FIXME: This macro needs to be moved to a common header file. */
#define NIP6(addr) \
ntohs((addr)->s6_addr16[0]), \
ntohs((addr)->s6_addr16[1]), \
ntohs((addr)->s6_addr16[2]), \
ntohs((addr)->s6_addr16[3]), \
ntohs((addr)->s6_addr16[4]), \
ntohs((addr)->s6_addr16[5]), \
ntohs((addr)->s6_addr16[6]), \
ntohs((addr)->s6_addr16[7])
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
struct dst_entry *sctp_v6_get_dst(sockaddr_storage_t *daddr,
sockaddr_storage_t *saddr)
{
struct dst_entry *dst;
struct flowi fl;
int dst_mtu = SCTP_DEFAULT_MAXSEGMENT;
struct flowi fl = { .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr,
} } };
fl.proto = 0;
fl.fl6_dst = (struct in6_addr *)&address->v6.sin6_addr;
fl.fl6_src = NULL;
fl.fl6_flowlabel = 0;
fl.oif = 0;
fl.uli_u.ports.sport = 0;
fl.uli_u.ports.dport = 0;
SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
__FUNCTION__, NIP6(fl.fl6_dst));
if (saddr) {
fl.fl6_src = &saddr->v6.sin6_addr;
SCTP_DEBUG_PRINTK(
"SRC=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x - ",
NIP6(fl.fl6_src));
}
dst = ip6_route_output(NULL, &fl);
if (dst) {
dst_mtu = dst_pmtu(dst);
SCTP_DEBUG_PRINTK("sctp_v6_get_dst_mtu: "
"ip6_route_output: dev:%s pmtu:%d\n",
dst->dev->name, dst_mtu);
dst_release(dst);
struct rt6_info *rt;
rt = (struct rt6_info *)dst;
SCTP_DEBUG_PRINTK(
"rt6_dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
"rt6_src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
NIP6(&rt->rt6i_dst.addr), NIP6(&rt->rt6i_src.addr));
} else {
SCTP_DEBUG_PRINTK("sctp_v6_get_dst_mtu: "
"ip6_route_output failed, returning "
"%d as dst_mtu\n", dst_mtu);
SCTP_DEBUG_PRINTK("NO ROUTE\n");
return NULL;
}
return dst_mtu;
return dst;
}
/* Check if the dst entry's source addr matches the given source addr. */
int sctp_v6_cmp_saddr(struct dst_entry *dst, sockaddr_storage_t *saddr)
{
struct rt6_info *rt = (struct rt6_info *)dst;
return ipv6_addr_cmp(&rt->rt6i_src.addr, &saddr->v6.sin6_addr);
}
/* Initialize a PF_INET6 socket msg_name. */
......@@ -301,7 +326,8 @@ static sctp_func_t sctp_ipv6_specific = {
.queue_xmit = sctp_v6_xmit,
.setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt,
.get_dst_mtu = sctp_v6_get_dst_mtu,
.get_dst = sctp_v6_get_dst,
.cmp_saddr = sctp_v6_cmp_saddr,
.net_header_len = sizeof(struct ipv6hdr),
.sockaddr_len = sizeof(struct sockaddr_in6),
.sa_family = AF_INET6,
......
......@@ -245,6 +245,7 @@ int sctp_packet_transmit(sctp_packet_t *packet)
int err = 0;
int padding; /* How much padding do we need? */
__u8 packet_has_data = 0;
struct dst_entry *dst;
/* Do NOT generate a chunkless packet... */
if (skb_queue_empty(&packet->chunks))
......@@ -410,13 +411,6 @@ int sctp_packet_transmit(sctp_packet_t *packet)
asoc->peer.last_sent_to = transport;
}
/* Hey, before Linux changes, here's what we have to
* do to force IP routing to recognize the change of
* dest addr. --xguo
*/
if (sk->dst_cache)
sk->dst_cache->obsolete = 1;
if (packet_has_data) {
struct timer_list *timer;
unsigned long timeout;
......@@ -434,6 +428,13 @@ int sctp_packet_transmit(sctp_packet_t *packet)
}
}
dst = transport->dst;
if (!dst || dst->obsolete) {
sctp_transport_route(transport, NULL);
}
nskb->dst = dst_clone(transport->dst);
SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n",
nskb->len);
(*transport->af_specific->queue_xmit)(nskb);
......
......@@ -204,18 +204,43 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
return error;
}
/* 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)
{
struct list_head *rlchunk;
sctp_chunk_t *tchunk, *rchunk;
__u32 ttsn, rtsn;
int done = 0;
tchunk = list_entry(tlchunk, sctp_chunk_t, transmitted_list);
ttsn = ntohl(tchunk->subh.data_hdr->tsn);
list_for_each(rlchunk, &q->retransmit) {
rchunk = list_entry(rlchunk, sctp_chunk_t, transmitted_list);
rtsn = ntohl(rchunk->subh.data_hdr->tsn);
if (TSN_lt(ttsn, rtsn)) {
list_add(tlchunk, rlchunk->prev);
done = 1;
break;
}
}
if (!done) {
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,
__u8 fast_retransmit)
{
struct list_head *lchunk;
struct list_head *lchunk, *ltemp;
sctp_chunk_t *chunk;
struct list_head tlist;
INIT_LIST_HEAD(&tlist);
while (!list_empty(&transport->transmitted)) {
lchunk = sctp_list_dequeue(&transport->transmitted);
/* Walk through the specified transmitted queue. */
list_for_each_safe(lchunk, ltemp, &transport->transmitted) {
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
/* If we are doing retransmission due to a fast retransmit,
......@@ -224,10 +249,8 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
* retransmission due to a timeout, only the chunks that are
* not yet acked should be added to the retransmit queue.
*/
if ((fast_retransmit && !chunk->fast_retransmit) ||
(!fast_retransmit && chunk->tsn_gap_acked)) {
list_add_tail(lchunk, &tlist);
} else {
if ((fast_retransmit && chunk->fast_retransmit) ||
(!fast_retransmit && !chunk->tsn_gap_acked)) {
/* RFC 2960 6.2.1 Processing a Received SACK
*
* C) Any time a DATA chunk is marked for
......@@ -257,15 +280,14 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
chunk->rtt_in_progress = 0;
transport->rto_pending = 0;
}
list_add_tail(lchunk, &q->retransmit);
}
}
/* Reconstruct the transmitted queue with chunks that are not
* eligible for retransmission.
/* Move the chunk to the retransmit queue. The chunks
* on the retransmit queue is always kept in order.
*/
while (NULL != (lchunk = sctp_list_dequeue(&tlist)))
list_add_tail(lchunk, &transport->transmitted);
list_del(lchunk);
sctp_retransmit_insert(lchunk, q);
}
}
SCTP_DEBUG_PRINTK("%s: transport: %p, fast_retransmit: %d, "
"cwnd: %d, ssthresh: %d, flight_size: %d, "
......@@ -656,7 +678,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
if (!new_transport) {
new_transport = asoc->peer.active_path;
} else if (!new_transport->state.active) {
} else if (!new_transport->active) {
/* If the chunk is Heartbeat, send it to
* chunk->transport, even it's inactive.
*/
......@@ -813,7 +835,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
*/
new_transport = chunk->transport;
if (new_transport == NULL ||
!new_transport->state.active)
!new_transport->active)
new_transport = asoc->peer.active_path;
/* Change packets if necessary. */
......@@ -1382,7 +1404,7 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
/* Mark the destination transport address as
* active if it is not so marked.
*/
if (!transport->state.active) {
if (!transport->active) {
sctp_assoc_control_transport(
transport->asoc,
transport,
......
......@@ -38,6 +38,7 @@
* La Monte H.P. Yarroll <piggy@acm.org>
* Narasimha Budihal <narasimha@refcode.org>
* Karl Knutson <karl@athena.chicago.il.us>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -181,6 +182,28 @@ DECLARE_PRIMITIVE(ABORT);
DECLARE_PRIMITIVE(SEND);
/* 10.1 ULP-to-SCTP
* J) Request Heartbeat
*
* Format: REQUESTHEARTBEAT(association id, destination transport address)
*
* -> result
*
* Instructs the local endpoint to perform a HeartBeat on the specified
* destination transport address of the given association. The returned
* result should indicate whether the transmission of the HEARTBEAT
* chunk to the destination address is successful.
*
* Mandatory attributes:
*
* o association id - local handle to the SCTP association
*
* o destination transport address - the transport address of the
* asociation on which a heartbeat should be issued.
*/
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)
{
......
......@@ -59,6 +59,7 @@
/* Global data structures. */
sctp_protocol_t sctp_proto;
struct proc_dir_entry *proc_net_sctp;
struct sctp_mib sctp_statistics[NR_CPUS * 2];
/* This is the global socket data structure used for responding to
* the Out-of-the-blue (OOTB) packets. A control sock will be created
......@@ -255,27 +256,41 @@ int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp,
return error;
}
/* Returns the mtu for the given v4 destination address. */
int sctp_v4_get_dst_mtu(const sockaddr_storage_t *address)
/* Returns the dst cache entry for the given source and destination ip
* addresses.
*/
struct dst_entry *sctp_v4_get_dst(sockaddr_storage_t *daddr,
sockaddr_storage_t *saddr)
{
int dst_mtu = SCTP_DEFAULT_MAXSEGMENT;
struct rtable *rt;
struct flowi fl = { .nl_u = { .ip4_u =
{ .daddr = address->v4.sin_addr.s_addr } } };
struct flowi fl = { .nl_u = { .ip4_u = { .daddr =
daddr->v4.sin_addr.s_addr,
} } };
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("sctp_v4_get_dst_mtu:ip_route_output_key"
" failed, returning %d as dst_mtu\n",
dst_mtu);
} else {
dst_mtu = dst_pmtu(&rt->u.dst);
SCTP_DEBUG_PRINTK("sctp_v4_get_dst_mtu: "
"ip_route_output_key: dev:%s pmtu:%d\n",
rt->u.dst.dev->name, dst_mtu);
ip_rt_put(rt);
SCTP_DEBUG_PRINTK("NO ROUTE\n");
return NULL;
}
return dst_mtu;
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;
}
/* Check if the dst entry's source addr matches the given source addr. */
int sctp_v4_cmp_saddr(struct dst_entry *dst, sockaddr_storage_t *saddr)
{
struct rtable *rt = (struct rtable *)dst;
return (rt->rt_src == saddr->v4.sin_addr.s_addr);
}
/* Event handler for inet device events.
......@@ -437,7 +452,8 @@ sctp_func_t sctp_ipv4_specific = {
.queue_xmit = ip_queue_xmit,
.setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt,
.get_dst_mtu = sctp_v4_get_dst_mtu,
.get_dst = sctp_v4_get_dst,
.cmp_saddr = sctp_v4_cmp_saddr,
.net_header_len = sizeof(struct iphdr),
.sockaddr_len = sizeof(struct sockaddr_in),
.sa_family = AF_INET,
......
/* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, Inc.
* Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 International Business Machines Corp.
* Copyright (c) 2001-2002 Intel Corp.
* Copyright (c) 2001-2002 International Business Machines Corp.
*
* This file is part of the SCTP kernel reference Implementation
*
......@@ -47,6 +47,7 @@
* Dajiang Zhang <dajiang.zhang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -165,7 +166,7 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
int priority)
{
sctp_inithdr_t init;
sctpParam_t addrs;
union sctp_params addrs;
size_t chunksize;
sctp_chunk_t *retval = NULL;
int addrs_len = 0;
......@@ -227,7 +228,7 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
{
sctp_inithdr_t initack;
sctp_chunk_t *retval;
sctpParam_t addrs;
union sctp_params addrs;
int addrs_len;
sctp_cookie_param_t *cookie;
int cookie_len;
......@@ -838,6 +839,53 @@ sctp_chunk_t *sctp_make_abort_no_data(const sctp_association_t *asoc,
return retval;
}
/* Helper to create ABORT with a SCTP_ERROR_USER_ABORT error. */
sctp_chunk_t *sctp_make_abort_user(const sctp_association_t *asoc,
const sctp_chunk_t *chunk,
const struct msghdr *msg)
{
sctp_chunk_t *retval;
void *payload = NULL, *payoff;
size_t paylen;
struct iovec *iov = msg->msg_iov;
int iovlen = msg->msg_iovlen;
paylen = get_user_iov_size(iov, iovlen);
retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen);
if (!retval)
goto err_chunk;
if (paylen) {
/* Put the msg_iov together into payload. */
payload = kmalloc(paylen, GFP_ATOMIC);
if (!payload)
goto err_payload;
payoff = payload;
for (; iovlen > 0; --iovlen) {
if (copy_from_user(payoff, iov->iov_base, iov->iov_len))
goto err_copy;
payoff += iov->iov_len;
iov++;
}
}
sctp_init_cause(retval, SCTP_ERROR_USER_ABORT, payload, paylen);
if (paylen)
kfree(payload);
return retval;
err_copy:
kfree(payload);
err_payload:
sctp_free_chunk(retval);
retval = NULL;
err_chunk:
return retval;
}
/* Make a HEARTBEAT chunk. */
sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc,
const sctp_transport_t *transport,
......@@ -982,16 +1030,17 @@ sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc,
return retval;
}
/* Set chunk->source based on the IP header in chunk->skb. */
void sctp_init_source(sctp_chunk_t *chunk)
/* Set chunk->source and dest based on the IP header in chunk->skb. */
void sctp_init_addrs(sctp_chunk_t *chunk)
{
sockaddr_storage_t *source;
sockaddr_storage_t *source, *dest;
struct sk_buff *skb;
struct sctphdr *sh;
struct iphdr *ih4;
struct ipv6hdr *ih6;
source = &chunk->source;
dest = &chunk->dest;
skb = chunk->skb;
ih4 = skb->nh.iph;
ih6 = skb->nh.ipv6h;
......@@ -1002,6 +1051,9 @@ void sctp_init_source(sctp_chunk_t *chunk)
source->v4.sin_family = AF_INET;
source->v4.sin_port = ntohs(sh->source);
source->v4.sin_addr.s_addr = ih4->saddr;
dest->v4.sin_family = AF_INET;
dest->v4.sin_port = ntohs(sh->dest);
dest->v4.sin_addr.s_addr = ih4->daddr;
break;
case 6:
......@@ -1009,6 +1061,9 @@ void sctp_init_source(sctp_chunk_t *chunk)
source->v6.sin6_family = AF_INET6;
source->v6.sin6_port = ntohs(sh->source);
source->v6.sin6_addr = ih6->saddr;
dest->v6.sin6_family = AF_INET6;
dest->v6.sin6_port = ntohs(sh->dest);
dest->v6.sin6_addr = ih6->daddr;
/* FIXME: What do we do with scope, etc. ? */
break;
)
......@@ -1427,78 +1482,28 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* 3rd Level Abstractions
********************************************************************/
/* Verify the INIT packet before we process it. */
int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid,
sctp_init_chunk_t *peer_init,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
sctpParam_t param;
uint8_t *end;
/* 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. */
/* Find unrecognized parameters. */
end = ((uint8_t *)peer_init + ntohs(peer_init->chunk_hdr.length));
for (param.v = peer_init->init_hdr.params;
param.v < end;
param.v += WORD_ROUND(ntohs(param.p->length))) {
if (!sctp_verify_param(asoc, param, cid, chunk, err_chk_p))
return 0;
} /* for (loop through all parameters) */
return 1;
}
/* Find unrecognized parameters in the chunk.
* Return values:
* 0 - discard the chunk
* 1 - continue with the chunk
/* Do not attempt to handle the HOST_NAME parm. However, do
* send back an indicator to the peer.
*/
int sctp_verify_param(const sctp_association_t *asoc,
sctpParam_t param,
sctp_cid_t cid,
static int sctp_process_hn_param(const sctp_association_t *asoc,
union sctp_params param,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
int retval = 1;
__u16 len = ntohs(param.p->length);
/* FIXME - This routine is not looking at each parameter per the
* chunk type, i.e., unrecognized parameters should be further
* identified based on the chunk id.
/* 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);
switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
case SCTP_PARAM_IPV6_ADDRESS:
case SCTP_PARAM_COOKIE_PRESERVATIVE:
/* FIXME - If we don't support the host name parameter, we should
* generate an error for this - Unresolvable address.
*/
case SCTP_PARAM_HOST_NAME_ADDRESS:
case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
case SCTP_PARAM_STATE_COOKIE:
case SCTP_PARAM_HEARTBEAT_INFO:
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
case SCTP_PARAM_ECN_CAPABLE:
break;
default:
SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
ntohs(param.p->type), cid);
return sctp_process_unk_param(asoc, param, chunk, err_chk_p);
if (*err_chk_p)
sctp_init_cause(*err_chk_p, SCTP_ERROR_DNS_FAILED,
param.v, len);
break;
}
return retval;
/* Stop processing this chunk. */
return 0;
}
/* RFC 3.2.1 & the Implementers Guide 2.2.
......@@ -1527,8 +1532,8 @@ int sctp_verify_param(const sctp_association_t *asoc,
* 0 - discard the chunk
* 1 - continue with the chunk
*/
int sctp_process_unk_param(const sctp_association_t *asoc,
sctpParam_t param,
static int sctp_process_unk_param(const sctp_association_t *asoc,
union sctp_params param,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
......@@ -1549,7 +1554,7 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
if (*err_chk_p)
sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
(const void *)param.p,
param.v,
WORD_ROUND(ntohs(param.p->length)));
break;
......@@ -1565,7 +1570,7 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
if (*err_chk_p) {
sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
(const void *)param.p,
param.v,
WORD_ROUND(ntohs(param.p->length)));
} else {
/* If there is no memory for generating the ERROR
......@@ -1583,17 +1588,84 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
return retval;
}
/* Unpack the parameters in an INIT packet.
* FIXME: There is no return status to allow callers to do
* error handling.
/* Find unrecognized parameters in the chunk.
* Return values:
* 0 - discard the chunk
* 1 - continue with the chunk
*/
void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
static int sctp_verify_param(const sctp_association_t *asoc,
union sctp_params param,
sctp_cid_t cid,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk)
{
int retval = 1;
/* FIXME - This routine is not looking at each parameter per the
* chunk type, i.e., unrecognized parameters should be further
* identified based on the chunk id.
*/
switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
case SCTP_PARAM_IPV6_ADDRESS:
case SCTP_PARAM_COOKIE_PRESERVATIVE:
case SCTP_PARAM_SUPPORTED_ADDRESS_TYPES:
case SCTP_PARAM_STATE_COOKIE:
case SCTP_PARAM_HEARTBEAT_INFO:
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
case SCTP_PARAM_ECN_CAPABLE:
break;
case SCTP_PARAM_HOST_NAME_ADDRESS:
/* Tell the peer, we won't support this param. */
return sctp_process_hn_param(asoc, param, chunk, err_chunk);
default:
SCTP_DEBUG_PRINTK("Unrecognized param: %d for chunk %d.\n",
ntohs(param.p->type), cid);
return sctp_process_unk_param(asoc, param, chunk, err_chunk);
break;
}
return retval;
}
/* Verify the INIT packet before we process it. */
int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid,
sctp_init_chunk_t *peer_init,
sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p)
{
union sctp_params param;
/* 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.
*/
/* Find unrecognized parameters. */
sctp_walk_params(param, peer_init, init_hdr.params) {
if (!sctp_verify_param(asoc, param, cid, chunk, err_chk_p))
return 0;
} /* for (loop through all parameters) */
return 1;
}
/* Unpack the parameters in an INIT packet into an association.
* Returns 0 on failure, else success.
*/
int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
const sockaddr_storage_t *peer_addr,
sctp_init_chunk_t *peer_init,
int priority)
{
sctpParam_t param;
__u8 *end;
union sctp_params param;
sctp_transport_t *transport;
struct list_head *pos, *temp;
char *cookie;
......@@ -1609,15 +1681,14 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* be a a better choice than any of the embedded addresses.
*/
if (peer_addr)
sctp_assoc_add_peer(asoc, peer_addr, priority);
if(!sctp_assoc_add_peer(asoc, peer_addr, priority))
goto nomem;
/* Process the initialization parameters. */
end = ((__u8 *)peer_init + ntohs(peer_init->chunk_hdr.length));
for (param.v = peer_init->init_hdr.params;
param.v < end;
param.v += WORD_ROUND(ntohs(param.p->length))) {
if (!sctp_process_param(asoc, param, peer_addr, cid,
priority))
sctp_walk_params(param, peer_init, init_hdr.params) {
if (!sctp_process_param(asoc, param, peer_addr, priority))
goto clean_up;
}
......@@ -1683,7 +1754,7 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* association to the same value as the Initial TSN.
*/
asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1;
return;
return 1;
clean_up:
/* Release the transport structures. */
......@@ -1692,8 +1763,11 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
list_del(pos);
sctp_transport_free(transport);
}
nomem:
return 0;
}
/* Update asoc with the option described in param.
*
* RFC2960 3.3.2.1 Optional/Variable Length Parameters in INIT
......@@ -1705,14 +1779,12 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* work we do. In particular, we should not build transport
* structures for the addresses.
*/
int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
const sockaddr_storage_t *peer_addr,
sctp_cid_t cid, int priority)
int sctp_process_param(sctp_association_t *asoc, union sctp_params param,
const sockaddr_storage_t *peer_addr, int priority)
{
sockaddr_storage_t addr;
sctp_addr_param_t *addrparm;
int j;
int i;
__u16 sat;
int retval = 1;
sctp_scope_t scope;
......@@ -1721,30 +1793,21 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
* came from a fresh INIT, and INIT ACK, or were stored in a cookie.
*/
switch (param.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
addrparm = (sctp_addr_param_t *)param.v;
sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr, priority);
break;
case SCTP_PARAM_IPV6_ADDRESS:
/* Rethink this as we may need to keep for
* restart considerations.
*/
if (PF_INET6 == asoc->base.sk->family) {
addrparm = (sctp_addr_param_t *)param.v;
sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
if( PF_INET6 != asoc->base.sk->family)
break;
/* Fall through. */
case SCTP_PARAM_IPV4_ADDRESS:
sctp_param2sockaddr(&addr, param.addr, asoc->peer.port);
scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr, priority);
}
if (!sctp_assoc_add_peer(asoc, &addr, priority))
return 0;
break;
case SCTP_PARAM_COOKIE_PRESERVATIVE:
asoc->cookie_preserve =
ntohl(param.bht->lifespan_increment);
ntohl(param.life->lifespan_increment);
break;
case SCTP_PARAM_HOST_NAME_ADDRESS:
......@@ -1758,10 +1821,12 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
asoc->peer.ipv4_address = 0;
asoc->peer.ipv6_address = 0;
j = (ntohs(param.p->length) -
sizeof(sctp_paramhdr_t)) /
sizeof(__u16);
for (i = 0; i < j; ++i) {
/* Cycle through address types; avoid divide by 0. */
sat = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
if (sat)
sat /= sizeof(__u16);
for (i = 0; i < sat; ++i) {
switch (param.sat->types[i]) {
case SCTP_PARAM_IPV4_ADDRESS:
asoc->peer.ipv4_address = 1;
......@@ -1788,13 +1853,11 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
break;
case SCTP_PARAM_HEARTBEAT_INFO:
SCTP_DEBUG_PRINTK("unimplemented "
"SCTP_PARAM_HEARTBEAT_INFO\n");
/* Would be odd to receive, but it causes no problems. */
break;
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
SCTP_DEBUG_PRINTK("unimplemented "
"SCTP_PARAM_UNRECOGNIZED_PARAMETERS\n");
/* Rejected during verify stage. */
break;
case SCTP_PARAM_ECN_CAPABLE:
......@@ -1871,11 +1934,8 @@ void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *param,
/* Convert an IP address in an SCTP param into a sockaddr_in. */
/* Returns true if a valid conversion was possible. */
int sctp_addr2sockaddr(sctpParam_t p, sockaddr_storage_t *sa)
int sctp_addr2sockaddr(union sctp_params p, sockaddr_storage_t *sa)
{
if (!p.v)
return 0;
switch (p.p->type) {
case SCTP_PARAM_IPV4_ADDRESS:
sa->v4.sin_addr = *((struct in_addr *)&p.v4->addr);
......
......@@ -41,6 +41,7 @@
* Dajiang Zhang <dajiang.zhang@nokia.com>
* Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -66,12 +67,15 @@ static void sctp_do_ecn_cwr_work(sctp_association_t *asoc,
static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
sctp_transport_t *transport);
static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *asoc);
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *asoc);
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *asoc,
sctp_event_t event_type, sctp_chunk_t *chunk);
static void sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc,
sctp_chunk_t *chunk,
sctp_init_chunk_t *peer_init,
int priority);
static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *, sctp_association_t *);
static void sctp_cmd_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_transport_reset(sctp_cmd_seq_t *, sctp_association_t *,
......@@ -192,6 +196,7 @@ int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
/* BUG--we should now recover some memory, probably by
* reneging...
*/
error = -ENOMEM;
break;
case SCTP_DISPOSITION_DELETE_TCB:
......@@ -251,7 +256,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
int force;
sctp_cmd_t *command;
sctp_chunk_t *new_obj;
sctp_chunk_t *chunk;
sctp_chunk_t *chunk = NULL;
sctp_packet_t *packet;
struct list_head *pos;
struct timer_list *timer;
......@@ -259,6 +264,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_transport_t *t;
sctp_sackhdr_t sackh;
if(SCTP_EVENT_T_TIMEOUT != event_type)
chunk = (sctp_chunk_t *) event_arg;
/* Note: This whole file is a huge candidate for rework.
......@@ -504,7 +510,8 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break;
case SCTP_CMD_ASSOC_FAILED:
sctp_cmd_assoc_failed(commands, asoc);
sctp_cmd_assoc_failed(commands, asoc, event_type,
chunk);
break;
case SCTP_CMD_COUNTER_INC:
......@@ -558,6 +565,11 @@ 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:
t = command->obj.transport;
sctp_cmd_hb_timers_update(commands, asoc, t);
break;
case SCTP_CMD_REPORT_ERROR:
error = command->obj.error;
break;
......@@ -595,10 +607,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
/* A helper function for delayed processing of INET ECN CE bit. */
static void sctp_do_ecn_ce_work(sctp_association_t *asoc, __u32 lowest_tsn)
{
/*
* Save the TSN away for comparison when we receive CWR
* Note: dp->TSN is expected in host endian
*/
/* Save the TSN away for comparison when we receive CWR */
asoc->last_ecne_tsn = lowest_tsn;
asoc->need_ecne = 1;
......@@ -621,7 +630,6 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
sctp_chunk_t *chunk)
{
sctp_chunk_t *repl;
sctp_transport_t *transport;
/* Our previously transmitted packet ran into some congestion
* so we should take action by reducing cwnd and ssthresh
......@@ -629,43 +637,28 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
* sending a CWR.
*/
/* First, try to determine if we want to actually lower
* our cwnd variables. Only lower them if the ECNE looks more
* recent than the last response.
*/
if (TSN_lt(asoc->last_cwr_tsn, lowest_tsn)) {
sctp_transport_t *transport;
/* Find which transport's congestion variables
* need to be adjusted.
*/
transport = sctp_assoc_lookup_tsn(asoc, lowest_tsn);
/* Update the congestion variables. */
if (transport)
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_ECNE);
/* Save away a rough idea of when we last sent out a CWR.
* We compare against this value (see above) to decide if
* this is a fairly new request.
* Note that this is not a perfect solution. We may
* have moved beyond the window (several times) by the
* next time we get an ECNE. However, it is cute. This idea
* came from Randy's reference code.
*
* Here's what RFC 2960 has to say about CWR. This is NOT
* what we do.
*
* RFC 2960 Appendix A
*
* CWR:
*
* RFC 2481 details a specific bit for a sender to send in
* the header of its next outbound TCP segment to indicate
* to its peer that it has reduced its congestion window.
* This is termed the CWR bit. For SCTP the same
* indication is made by including the CWR chunk. This
* chunk contains one data element, i.e. the TSN number
* that was sent in the ECNE chunk. This element
* represents the lowest TSN number in the datagram that
* was originally marked with the CE bit.
*/
asoc->last_cwr_tsn = asoc->next_tsn - 1;
sctp_transport_lower_cwnd(transport,
SCTP_LOWER_CWND_ECNE);
asoc->last_cwr_tsn = lowest_tsn;
}
/* Always try to quiet the other end. In case of lost CWR,
* resend last_cwr_tsn.
*/
repl = sctp_make_cwr(asoc, asoc->last_cwr_tsn, chunk);
/* If we run out of memory, it will look like a lost CWR. We'll
......@@ -994,7 +987,7 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
*/
asoc->overall_error_count++;
if (transport->state.active &&
if (transport->active &&
(transport->error_count++ >= transport->error_threshold)) {
SCTP_DEBUG_PRINTK("transport_strike: transport "
"IP:%d.%d.%d.%d failed.\n",
......@@ -1038,14 +1031,26 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands,
/* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
sctp_association_t *asoc)
sctp_association_t *asoc,
sctp_event_t event_type,
sctp_chunk_t *chunk)
{
sctp_ulpevent_t *event;
__u16 error = 0;
if (event_type == SCTP_EVENT_T_PRIMITIVE)
error = SCTP_ERROR_USER_ABORT;
if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) &&
(ntohs(chunk->chunk_hdr->length) >= (sizeof(struct sctp_chunkhdr) +
sizeof(struct sctp_errhdr)))) {
error = ((sctp_errhdr_t *)chunk->skb->data)->cause;
}
event = sctp_ulpevent_make_assoc_change(asoc,
0,
SCTP_COMM_LOST,
0, 0, 0,
error, 0, 0,
GFP_ATOMIC);
if (event)
......@@ -1100,6 +1105,16 @@ static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds,
}
}
/* 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)
{
/* Update the heartbeat timer. */
if (!mod_timer(&t->hb_timer, t->hb_interval + t->rto + jiffies))
sctp_transport_hold(t);
}
/* Helper function to break out SCTP_CMD_SET_BIND_ADDR handling. */
void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_bind_addr_t *bp)
......@@ -1135,7 +1150,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
/* Mark the destination transport address as active if it is not so
* marked.
*/
if (!t->state.active)
if (!t->active)
sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
SCTP_HEARTBEAT_SUCCESS);
......@@ -1158,10 +1173,6 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
/* Mark one strike against a transport. */
sctp_do_8_2_transport_strike(asoc, t);
/* Update the heartbeat timer. */
if (!mod_timer(&t->hb_timer, t->hb_interval + t->rto + jiffies))
sctp_transport_hold(t);
}
/* Helper function to process the process SACK command. */
......
......@@ -43,6 +43,7 @@
* Hui Huang <hui.huang@nokia.com>
* Dajiang Zhang <dajiang.zhang@nokia.com>
* Daisy Chang <daisyc@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -235,8 +236,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data;
/* Tag the variable length parameters. */
chunk->param_hdr.v =
skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC);
if (!new_asoc)
......@@ -663,8 +663,8 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_NOMEM;
}
/* Generate a HEARTBEAT packet on the given transport. */
sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
/* Generate and sendout a heartbeat packet. */
sctp_disposition_t sctp_sf_heartbeat(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
const sctp_subtype_t type,
void *arg,
......@@ -675,26 +675,13 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
sctp_sender_hb_info_t hbinfo;
size_t paylen = 0;
if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
return SCTP_DISPOSITION_DELETE_TCB;
}
/* Section 3.3.5.
* The Sender-specific Heartbeat Info field should normally include
* information about the sender's current time when this HEARTBEAT
* chunk is sent and the destination transport address to which this
* HEARTBEAT is sent (see Section 8.3).
*/
hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO;
hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t));
hbinfo.daddr = transport->ipaddr;
hbinfo.sent_at = jiffies;
/* Set rto_pending indicating that an RTT measurement is started
* with this heartbeat chunk.
/* Set rto_pending indicating that an RTT measurement
* is started with this heartbeat chunk.
*/
transport->rto_pending = 1;
......@@ -702,20 +689,49 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
paylen = sizeof(sctp_sender_hb_info_t);
reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen);
if (!reply)
goto nomem;
return SCTP_DISPOSITION_NOMEM;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
return SCTP_DISPOSITION_CONSUME;
}
/* Generate a HEARTBEAT packet on the given transport. */
sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
const sctp_subtype_t type,
void *arg,
sctp_cmd_seq_t *commands)
{
sctp_transport_t *transport = (sctp_transport_t *) arg;
if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
return SCTP_DISPOSITION_DELETE_TCB;
}
/* Section 3.3.5.
* The Sender-specific Heartbeat Info field should normally include
* information about the sender's current time when this HEARTBEAT
* chunk is sent and the destination transport address to which this
* HEARTBEAT is sent (see Section 8.3).
*/
if (transport->hb_allowed) {
if (SCTP_DISPOSITION_NOMEM ==
sctp_sf_heartbeat(ep, asoc, type, arg,
commands))
return SCTP_DISPOSITION_NOMEM;
/* Set transport error counter and association error counter
* when sending heartbeat.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
SCTP_TRANSPORT(transport));
}
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_UPDATE,
SCTP_TRANSPORT(transport));
return SCTP_DISPOSITION_CONSUME;
nomem:
return SCTP_DISPOSITION_NOMEM;
}
/*
......@@ -2098,18 +2114,11 @@ sctp_disposition_t sctp_sf_do_ecne(const sctp_endpoint_t *ep,
ecne = (sctp_ecnehdr_t *) chunk->skb->data;
skb_pull(chunk->skb, sizeof(sctp_ecnehdr_t));
ecne->lowest_tsn = ntohl(ecne->lowest_tsn);
/* Casting away the const, as we are just modifying the spinlock,
* not the association itself. This should go away in the near
* future when we move to an endpoint based lock.
*/
/* If this is a newer ECNE than the last CWR packet we sent out */
if (TSN_lt(asoc->last_cwr_tsn, ecne->lowest_tsn)) {
sctp_add_cmd_sf(commands, SCTP_CMD_ECN_ECNE,
SCTP_U32(ecne->lowest_tsn));
}
SCTP_U32(ntohl(ecne->lowest_tsn)));
return SCTP_DISPOSITION_CONSUME;
}
......@@ -3401,13 +3410,14 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(const sctp_endpoint_t *ep,
* from its upper layer, but retransmits data to the far end
* if necessary to fill gaps.
*/
struct msghdr *msg = arg;
sctp_chunk_t *abort;
sctp_disposition_t retval;
retval = SCTP_DISPOSITION_CONSUME;
/* Generate ABORT chunk to send the peer. */
abort = sctp_make_abort(asoc, NULL, 0);
abort = sctp_make_abort_user(asoc, NULL, msg);
if (!abort)
retval = SCTP_DISPOSITION_NOMEM;
else
......@@ -3525,6 +3535,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep,
void *arg,
sctp_cmd_seq_t *commands)
{
struct msghdr *msg = arg;
sctp_chunk_t *abort;
sctp_disposition_t retval;
......@@ -3534,7 +3545,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep,
retval = SCTP_DISPOSITION_CONSUME;
/* Generate ABORT chunk to send the peer */
abort = sctp_make_abort(asoc, NULL, 0);
abort = sctp_make_abort_user(asoc, NULL, msg);
if (!abort)
retval = SCTP_DISPOSITION_NOMEM;
else
......@@ -3660,6 +3671,39 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort(
return sctp_sf_shutdown_sent_prm_abort(ep, asoc, type, arg, commands);
}
/*
* Process the REQUESTHEARTBEAT primitive
*
* 10.1 ULP-to-SCTP
* J) Request Heartbeat
*
* Format: REQUESTHEARTBEAT(association id, destination transport address)
*
* -> result
*
* Instructs the local endpoint to perform a HeartBeat on the specified
* destination transport address of the given association. The returned
* result should indicate whether the transmission of the HEARTBEAT
* chunk to the destination address is successful.
*
* Mandatory attributes:
*
* o association id - local handle to the SCTP association
*
* o destination transport address - the transport address of the
* asociation on which a heartbeat should be issued.
*/
sctp_disposition_t sctp_sf_do_prm_requestheartbeat(
const sctp_endpoint_t *ep,
const sctp_association_t *asoc,
const sctp_subtype_t type,
void *arg,
sctp_cmd_seq_t *commands)
{
return sctp_sf_heartbeat(ep, asoc, type, (sctp_transport_t *)arg,
commands);
}
/*
* Ignore the primitive event
*
......@@ -4258,6 +4302,11 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
if (!packet)
goto nomem_packet;
/* Cache a route for the transport with the chunk's destination as
* the source address.
*/
sctp_transport_route(transport, (sockaddr_storage_t *)&chunk->dest);
packet = sctp_packet_init(packet, transport, sport, dport);
packet = sctp_packet_config(packet, vtag, 0, NULL);
......
......@@ -39,6 +39,7 @@
* Jon Grimm <jgrimm@us.ibm.com>
* Hui Huang <hui.huang@nokia.com>
* Daisy Chang <daisyc@us.ibm.com>
* Ardelle Fan <ardelle.fan@intel.com>
*
* Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release.
......@@ -706,21 +707,28 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
/* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_COOKIE_WAIT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_do_prm_requestheartbeat, \
.name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_COOKIE_ECHOED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_do_prm_requestheartbeat, \
.name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_ESTABLISHED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_do_prm_requestheartbeat, \
.name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_SHUTDOWN_PENDING */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_do_prm_requestheartbeat, \
.name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_SHUTDOWN_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_do_prm_requestheartbeat, \
.name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_SHUTDOWN_RECEIVED */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_do_prm_requestheartbeat, \
.name = "sctp_sf_do_prm_requestheartbeat"}, \
/* SCTP_STATE_SHUTDOWN_ACK_SENT */ \
{.fn = sctp_sf_not_impl, .name = "sctp_sf_not_impl"}, \
{.fn = sctp_sf_do_prm_requestheartbeat, \
.name = "sctp_sf_do_prm_requestheartbeat"}, \
} /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
#define TYPE_SCTP_PRIMITIVE_GETSRTTREPORT { \
......
......@@ -804,10 +804,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
SCTP_DEBUG_PRINTK("msg_len: %Zd, sinfo_flags: 0x%x\n",
msg_len, sinfo_flags);
/* If MSG_EOF|MSG_ABORT is set, no data can be sent. Disallow
* sending 0-length messages when MSG_EOF|MSG_ABORT is not set.
/* If MSG_EOF is set, no data can be sent. Disallow sending zero
* length messages when MSG_EOF|MSG_ABORT is not set.
* If MSG_ABORT is set, the message length could be non zero with
* the msg_iov set to the user abort reason.
*/
if (((sinfo_flags & (MSG_EOF|MSG_ABORT)) && (msg_len > 0)) ||
if (((sinfo_flags & MSG_EOF) && (msg_len > 0)) ||
(!(sinfo_flags & (MSG_EOF|MSG_ABORT)) && (msg_len == 0))) {
err = -EINVAL;
goto out_nounlock;
......@@ -879,7 +881,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
}
if (sinfo_flags & MSG_ABORT) {
SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc);
sctp_primitive_ABORT(asoc, NULL);
sctp_primitive_ABORT(asoc, msg);
err = 0;
goto out_unlock;
}
......@@ -1238,6 +1240,9 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
{
sctp_opt_t *sp = sctp_sk(sk);
/* Applicable to UDP-style socket only */
if (SCTP_SOCKET_TCP == sp->type)
return -EOPNOTSUPP;
if (optlen != sizeof(int))
return -EINVAL;
if (copy_from_user(&sp->autoclose, optval, optlen))
......@@ -1247,6 +1252,67 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
return 0;
}
static inline int sctp_setsockopt_set_peer_addr_params(struct sock *sk,
char *optval,
int optlen)
{
struct sctp_paddrparams params;
sctp_association_t *asoc;
sockaddr_storage_t *addr;
sctp_transport_t *trans;
int error;
if (optlen != sizeof(struct sctp_paddrparams))
return -EINVAL;
if (copy_from_user(&params, optval, optlen))
return -EFAULT;
asoc = sctp_id2assoc(sk, params.spp_assoc_id);
if (!asoc)
return -EINVAL;
addr = (sockaddr_storage_t *) &(params.spp_address);
trans = sctp_assoc_lookup_paddr(asoc, addr);
if (!trans)
return -ENOENT;
/* Applications can enable or disable heartbeats for any peer address
* of an association, modify an address's heartbeat interval, force a
* heartbeat to be sent immediately, and adjust the address's maximum
* number of retransmissions sent before an address is considered
* unreachable.
*
* The value of the heartbeat interval, in milliseconds. A value of
* UINT32_MAX (4294967295), when modifying the parameter, specifies
* that a heartbeat should be sent immediately to the peer address,
* and the current interval should remain unchanged.
*/
if (0xffffffff == params.spp_hbinterval) {
error = sctp_primitive_REQUESTHEARTBEAT (asoc, trans);
if (error)
return error;
}
else {
/* The value of the heartbeat interval, in milliseconds. A value of 0,
* when modifying the parameter, specifies that the heartbeat on this
* address should be disabled.
*/
if (params.spp_hbinterval) {
trans->hb_allowed = 1;
trans->hb_interval = params.spp_hbinterval * HZ / 1000;
} else
trans->hb_allowed = 0;
}
/* spp_pathmaxrxt contains the maximum number of retransmissions
* before this address shall be considered unreachable.
*/
trans->error_threshold = params.spp_pathmaxrxt;
return 0;
}
/* API 6.2 setsockopt(), getsockopt()
*
* Applications use setsockopt() and getsockopt() to set or retrieve
......@@ -1337,6 +1403,11 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
retval = sctp_setsockopt_autoclose(sk, optval, optlen);
break;
case SCTP_SET_PEER_ADDR_PARAMS:
retval = sctp_setsockopt_set_peer_addr_params(sk, optval,
optlen);
break;
default:
retval = -ENOPROTOOPT;
break;
......@@ -1498,28 +1569,26 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (len != sizeof(status)) {
retval = -EINVAL;
goto out_nounlock;
goto out;
}
if (copy_from_user(&status, optval, sizeof(status))) {
retval = -EFAULT;
goto out_nounlock;
goto out;
}
sctp_lock_sock(sk);
associd = status.sstat_assoc_id;
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sctp_sk(sk)->type) && associd) {
assoc = sctp_id2assoc(sk, associd);
if (!assoc) {
retval = -EINVAL;
goto out_unlock;
goto out;
}
} else {
ep = sctp_sk(sk)->ep;
if (list_empty(&ep->asocs)) {
retval = -EINVAL;
goto out_unlock;
goto out;
}
assoc = list_entry(ep->asocs.next, sctp_association_t, asocs);
......@@ -1538,7 +1607,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address,
&(transport->ipaddr), sizeof(sockaddr_storage_t));
status.sstat_primary.spinfo_state = transport->state.active;
status.sstat_primary.spinfo_state = transport->active;
status.sstat_primary.spinfo_cwnd = transport->cwnd;
status.sstat_primary.spinfo_srtt = transport->srtt;
status.sstat_primary.spinfo_rto = transport->rto;
......@@ -1546,7 +1615,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (put_user(len, optlen)) {
retval = -EFAULT;
goto out_unlock;
goto out;
}
SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %p\n",
......@@ -1555,13 +1624,10 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (copy_to_user(optval, &status, len)) {
retval = -EFAULT;
goto out_unlock;
goto out;
}
out_unlock:
sctp_release_sock(sk);
out_nounlock:
out:
return (retval);
}
......@@ -1593,6 +1659,9 @@ static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *opt
static inline int sctp_getsockopt_autoclose(struct sock *sk, int len, char *optval, int *optlen)
{
/* Applicable to UDP-style socket only */
if (SCTP_SOCKET_TCP == sctp_sk(sk)->type)
return -EOPNOTSUPP;
if (len != sizeof(int))
return -EINVAL;
if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
......@@ -1614,10 +1683,10 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
int err = 0;
/* An association cannot be branched off from an already peeled-off
* socket.
* socket, nor is this supported for tcp style sockets.
*/
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sctp_sk(oldsk)->type)
return -EINVAL;
if (SCTP_SOCKET_UDP != sctp_sk(oldsk)->type)
return -EOPNOTSUPP;
/* Create a new socket. */
err = sock_create(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, &tmpsock);
......@@ -1676,25 +1745,23 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
if (copy_from_user(&peeloff, optval, len))
return -EFAULT;
sctp_lock_sock(sk);
assoc = sctp_id2assoc(sk, peeloff.associd);
if (NULL == assoc) {
retval = -EINVAL;
goto out_unlock;
goto out;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc);
retval = sctp_do_peeloff(assoc, &newsock);
if (retval < 0)
goto out_unlock;
goto out;
/* Map the socket to an unused fd that can be returned to the user. */
retval = sock_map_fd(newsock);
if (retval < 0) {
sock_release(newsock);
goto out_unlock;
goto out;
}
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n",
......@@ -1705,11 +1772,54 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
if (copy_to_user(optval, &peeloff, len))
retval = -EFAULT;
out_unlock:
sctp_release_sock(sk);
out:
return retval;
}
static inline int sctp_getsockopt_get_peer_addr_params(struct sock *sk,
int len, char *optval, int *optlen)
{
struct sctp_paddrparams params;
sctp_association_t *asoc;
sockaddr_storage_t *addr;
sctp_transport_t *trans;
if (len != sizeof(struct sctp_paddrparams))
return -EINVAL;
if (copy_from_user(&params, optval, *optlen))
return -EFAULT;
asoc = sctp_id2assoc(sk, params.spp_assoc_id);
if (!asoc)
return -EINVAL;
addr = (sockaddr_storage_t *) &(params.spp_address);
trans = sctp_assoc_lookup_paddr(asoc, addr);
if (!trans)
return -ENOENT;
/* The value of the heartbeat interval, in milliseconds. A value of 0,
* when modifying the parameter, specifies that the heartbeat on this
* address should be disabled.
*/
if (!trans->hb_allowed)
params.spp_hbinterval = 0;
else
params.spp_hbinterval = trans->hb_interval * 1000 / HZ;
/* spp_pathmaxrxt contains the maximum number of retransmissions
* before this address shall be considered unreachable.
*/
params.spp_pathmaxrxt = trans->error_threshold;
if (copy_to_user(optval, &params, len))
return -EFAULT;
*optlen = len;
return 0;
}
SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen)
{
......@@ -1740,6 +1850,8 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
if (get_user(len, optlen))
return -EFAULT;
sctp_lock_sock(sk);
switch (optname) {
case SCTP_STATUS:
retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen);
......@@ -1762,11 +1874,17 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
break;
case SCTP_GET_PEER_ADDR_PARAMS:
retval = sctp_getsockopt_get_peer_addr_params(sk, len, optval,
optlen);
break;
default:
retval = -ENOPROTOOPT;
break;
};
sctp_release_sock(sk);
return retval;
}
......
......@@ -87,7 +87,6 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
peer->ipaddr = *addr;
peer->af_specific = sctp_get_af_specific(addr);
peer->asoc = NULL;
peer->pmtu = peer->af_specific->get_dst_mtu(addr);
/* From 6.3.1 RTO Calculation:
*
......@@ -105,8 +104,8 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
peer->last_time_used = jiffies;
peer->last_time_ecne_reduced = jiffies;
peer->state.active = 1;
peer->state.hb_allowed = 0;
peer->active = 1;
peer->hb_allowed = 0;
/* Initialize the default path max_retrans. */
peer->max_retrans = proto->max_retrans_path;
......@@ -161,6 +160,7 @@ void sctp_transport_destroy(sctp_transport_t *transport)
if (transport->asoc)
sctp_association_put(transport->asoc);
dst_release(transport->dst);
kfree(transport);
SCTP_DBG_OBJCNT_DEC(transport);
}
......@@ -200,6 +200,78 @@ void sctp_transport_set_owner(sctp_transport_t *transport,
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,
sockaddr_storage_t *saddr)
{
sctp_association_t *asoc = transport->asoc;
sctp_func_t *af = transport->af_specific;
sockaddr_storage_t *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;
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;
}
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);
if (af->cmp_saddr(dst, &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);
}
/* 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);
dst = af->get_dst(daddr, &laddr->a);
if (dst)
goto out_unlock;
}
out_unlock:
sctp_read_unlock(addr_lock);
out:
transport->dst = dst;
if (dst)
transport->pmtu = dst_pmtu(dst);
else
transport->pmtu = SCTP_DEFAULT_MAXSEGMENT;
}
/* Hold a reference to a transport. */
void sctp_transport_hold(sctp_transport_t *transport)
{
......
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