Commit c02bf8ef authored by Sridhar Samudrala's avatar Sridhar Samudrala

[SCTP]: merge with 2.5.45

parents b1b782f7 39a990fe
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) 1999-2000 Cisco, Inc. * Copyright (c) 1999-2000 Cisco, Inc.
* Copyright (c) 1999-2001 Motorola, 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 Intel Corp.
* Copyright (c) 2001 Nokia, Inc. * Copyright (c) 2001 Nokia, Inc.
* Copyright (c) 2001 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
* *
* Please send any bug reports or fixes you make to the * Please send any bug reports or fixes you make to the
* email address(es): * 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: * Or submit a bug report through the following website:
* http://www.sf.net/projects/lksctp * http://www.sf.net/projects/lksctp
...@@ -419,10 +419,12 @@ typedef enum { ...@@ -419,10 +419,12 @@ typedef enum {
/* SCTP Implementation Guide: /* SCTP Implementation Guide:
* 11 Restart of an association with new addresses * 11 Restart of an association with new addresses
* 12 User Initiated Abort * 12 User Initiated Abort
* 13 Protocol Violation
*/ */
SCTP_ERROR_RESTART = __constant_htons(0x0b), SCTP_ERROR_RESTART = __constant_htons(0x0b),
SCTP_ERROR_USER_ABORT = __constant_htons(0x0c), SCTP_ERROR_USER_ABORT = __constant_htons(0x0c),
SCTP_ERROR_PROTO_VIOLATION = __constant_htons(0x0d),
/* ADDIP Section 3.3 New Error Causes /* ADDIP Section 3.3 New Error Causes
* *
...@@ -583,7 +585,4 @@ typedef struct { ...@@ -583,7 +585,4 @@ typedef struct {
sctpAsconfAckRsp_t TLV; sctpAsconfAckRsp_t TLV;
} sctpAsconfAckRspNode_t; } sctpAsconfAckRspNode_t;
#endif /* __LINUX_SCTP_H__ */ #endif /* __LINUX_SCTP_H__ */
...@@ -83,6 +83,7 @@ ...@@ -83,6 +83,7 @@
#include <asm/uaccess.h> #include <asm/uaccess.h>
#include <asm/page.h> #include <asm/page.h>
#include <net/sock.h> #include <net/sock.h>
#include <net/snmp.h>
#include <net/sctp/structs.h> #include <net/sctp/structs.h>
#include <net/sctp/constants.h> #include <net/sctp/constants.h>
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
...@@ -201,6 +202,11 @@ extern void sctp_hash_digest(const char *secret, const int secret_len, ...@@ -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_PRE(sk) SOCK_SLEEP_PRE(sk)
#define SCTP_SOCK_SLEEP_POST(sk) SOCK_SLEEP_POST(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. */ /* Determine if this is a valid kernel address. */
static inline int sctp_is_valid_kaddr(unsigned long addr) static inline int sctp_is_valid_kaddr(unsigned long addr)
......
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
* Dajiang Zhang <dajiang.zhang@nokia.com> * Dajiang Zhang <dajiang.zhang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com> * Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@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 * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -253,6 +254,9 @@ sctp_chunk_t *sctp_make_abort(const sctp_association_t *, ...@@ -253,6 +254,9 @@ sctp_chunk_t *sctp_make_abort(const sctp_association_t *,
sctp_chunk_t *sctp_make_abort_no_data(const sctp_association_t *, sctp_chunk_t *sctp_make_abort_no_data(const sctp_association_t *,
const sctp_chunk_t *, const sctp_chunk_t *,
__u32 tsn); __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 *, sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *,
const sctp_transport_t *, const sctp_transport_t *,
const void *payload, const void *payload,
......
...@@ -246,7 +246,10 @@ typedef struct sctp_func { ...@@ -246,7 +246,10 @@ typedef struct sctp_func {
int optname, int optname,
char *optval, char *optval,
int *optlen); 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; __u16 net_header_len;
int sockaddr_len; int sockaddr_len;
sa_family_t sa_family; sa_family_t sa_family;
...@@ -476,6 +479,8 @@ struct SCTP_chunk { ...@@ -476,6 +479,8 @@ struct SCTP_chunk {
/* What is the origin IP address for this chunk? */ /* What is the origin IP address for this chunk? */
sockaddr_storage_t source; 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 inbound chunk, this tells us where it came from.
* For an outbound chunk, it tells us where we'd like it to * For an outbound chunk, it tells us where we'd like it to
...@@ -492,7 +497,7 @@ void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data); ...@@ -492,7 +497,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); 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 *, sctp_chunk_t *sctp_chunkify(struct sk_buff *, const sctp_association_t *,
struct sock *); 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); const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk);
/* This is a structure for holding either an IPv6 or an IPv4 address. */ /* This is a structure for holding either an IPv6 or an IPv4 address. */
...@@ -655,6 +660,9 @@ struct SCTP_transport { ...@@ -655,6 +660,9 @@ struct SCTP_transport {
/* PMTU : The current known path MTU. */ /* PMTU : The current known path MTU. */
__u32 pmtu; __u32 pmtu;
/* Destination */
struct dst_entry *dst;
/* When was the last time(in jiffies) that a data packet was sent on /* 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 * this transport? This is used to adjust the cwnd when the transport
* becomes inactive. * becomes inactive.
...@@ -735,6 +743,7 @@ extern sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *, int); ...@@ -735,6 +743,7 @@ extern sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *, int);
extern sctp_transport_t *sctp_transport_init(sctp_transport_t *, extern sctp_transport_t *sctp_transport_init(sctp_transport_t *,
const sockaddr_storage_t *, int); const sockaddr_storage_t *, int);
extern void sctp_transport_set_owner(sctp_transport_t *, sctp_association_t *); 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_free(sctp_transport_t *);
extern void sctp_transport_destroy(sctp_transport_t *); extern void sctp_transport_destroy(sctp_transport_t *);
extern void sctp_transport_reset_timers(sctp_transport_t *); extern void sctp_transport_reset_timers(sctp_transport_t *);
......
...@@ -189,6 +189,35 @@ struct udp_mib ...@@ -189,6 +189,35 @@ struct udp_mib
unsigned long __pad[0]; unsigned long __pad[0];
} ____cacheline_aligned; } ____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 struct linux_mib
{ {
unsigned long SyncookiesSent; unsigned long SyncookiesSent;
......
...@@ -208,7 +208,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc, ...@@ -208,7 +208,7 @@ sctp_association_t *sctp_association_init(sctp_association_t *asoc,
asoc->ctsn_ack_point = asoc->next_tsn - 1; asoc->ctsn_ack_point = asoc->next_tsn - 1;
asoc->highest_sacked = asoc->ctsn_ack_point; asoc->highest_sacked = asoc->ctsn_ack_point;
asoc->last_cwr_tsn = asoc->ctsn_ack_point;
asoc->unack_data = 0; asoc->unack_data = 0;
SCTP_DEBUG_PRINTK("myctsnap for %s INIT as 0x%x.\n", 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, ...@@ -404,6 +404,9 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
sctp_transport_set_owner(peer, 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, /* If this is the first transport addr on this association,
* initialize the association PMTU to the peer's PMTU. * initialize the association PMTU to the peer's PMTU.
* If not and the current association PMTU is higher than the new * If not and the current association PMTU is higher than the new
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
* Jon Grimm <jgrimm@us.ibm.com> * Jon Grimm <jgrimm@us.ibm.com>
* Hui Huang <hui.huang@nokia.com> * Hui Huang <hui.huang@nokia.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <sri@us.ibm.com>
* *
* Any bugs reported given to us we will try to fix... any fixes shared will * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -217,8 +218,8 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -217,8 +218,8 @@ int sctp_rcv(struct sk_buff *skb)
/* Remember the SCTP header. */ /* Remember the SCTP header. */
chunk->sctp_hdr = sh; chunk->sctp_hdr = sh;
/* Set the source address. */ /* Set the source and destination addresses of the incoming chunk. */
sctp_init_source(chunk); sctp_init_addrs(chunk);
/* Remember where we came from. */ /* Remember where we came from. */
chunk->transport = transport; chunk->transport = transport;
......
...@@ -158,35 +158,60 @@ static inline int sctp_v6_xmit(struct sk_buff *skb) ...@@ -158,35 +158,60 @@ static inline int sctp_v6_xmit(struct sk_buff *skb)
} }
#endif /* TEST_FRAME */ #endif /* TEST_FRAME */
/* Returns the mtu for the given v6 destination address. */ /* FIXME: This macro needs to be moved to a common header file. */
int sctp_v6_get_dst_mtu(const sockaddr_storage_t *address) #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 dst_entry *dst;
struct flowi fl; struct flowi fl = { .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr,
int dst_mtu = SCTP_DEFAULT_MAXSEGMENT; } } };
fl.proto = 0;
fl.fl6_dst = (struct in6_addr *)&address->v6.sin6_addr; SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
fl.fl6_src = NULL; __FUNCTION__, NIP6(fl.fl6_dst));
fl.fl6_flowlabel = 0;
fl.oif = 0; if (saddr) {
fl.uli_u.ports.sport = 0; fl.fl6_src = &saddr->v6.sin6_addr;
fl.uli_u.ports.dport = 0; SCTP_DEBUG_PRINTK(
"SRC=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x - ",
NIP6(fl.fl6_src));
}
dst = ip6_route_output(NULL, &fl); dst = ip6_route_output(NULL, &fl);
if (dst) { if (dst) {
dst_mtu = dst_pmtu(dst); struct rt6_info *rt;
SCTP_DEBUG_PRINTK("sctp_v6_get_dst_mtu: " rt = (struct rt6_info *)dst;
"ip6_route_output: dev:%s pmtu:%d\n", SCTP_DEBUG_PRINTK(
dst->dev->name, dst_mtu); "rt6_dst:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x "
dst_release(dst); "rt6_src:%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
NIP6(&rt->rt6i_dst.addr), NIP6(&rt->rt6i_src.addr));
} else { } else {
SCTP_DEBUG_PRINTK("sctp_v6_get_dst_mtu: " SCTP_DEBUG_PRINTK("NO ROUTE\n");
"ip6_route_output failed, returning " return NULL;
"%d as dst_mtu\n", dst_mtu);
} }
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. */ /* Initialize a PF_INET6 socket msg_name. */
...@@ -301,7 +326,8 @@ static sctp_func_t sctp_ipv6_specific = { ...@@ -301,7 +326,8 @@ static sctp_func_t sctp_ipv6_specific = {
.queue_xmit = sctp_v6_xmit, .queue_xmit = sctp_v6_xmit,
.setsockopt = ipv6_setsockopt, .setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt, .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), .net_header_len = sizeof(struct ipv6hdr),
.sockaddr_len = sizeof(struct sockaddr_in6), .sockaddr_len = sizeof(struct sockaddr_in6),
.sa_family = AF_INET6, .sa_family = AF_INET6,
......
...@@ -245,6 +245,7 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -245,6 +245,7 @@ int sctp_packet_transmit(sctp_packet_t *packet)
int err = 0; int err = 0;
int padding; /* How much padding do we need? */ int padding; /* How much padding do we need? */
__u8 packet_has_data = 0; __u8 packet_has_data = 0;
struct dst_entry *dst;
/* Do NOT generate a chunkless packet... */ /* Do NOT generate a chunkless packet... */
if (skb_queue_empty(&packet->chunks)) if (skb_queue_empty(&packet->chunks))
...@@ -410,13 +411,6 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -410,13 +411,6 @@ int sctp_packet_transmit(sctp_packet_t *packet)
asoc->peer.last_sent_to = transport; 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) { if (packet_has_data) {
struct timer_list *timer; struct timer_list *timer;
unsigned long timeout; unsigned long timeout;
...@@ -434,6 +428,13 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -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", SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n",
nskb->len); nskb->len);
(*transport->af_specific->queue_xmit)(nskb); (*transport->af_specific->queue_xmit)(nskb);
......
...@@ -204,18 +204,43 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk) ...@@ -204,18 +204,43 @@ int sctp_push_outqueue(sctp_outqueue_t *q, sctp_chunk_t *chunk)
return error; 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. */ /* 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(sctp_outqueue_t *q, sctp_transport_t *transport,
__u8 fast_retransmit) __u8 fast_retransmit)
{ {
struct list_head *lchunk; struct list_head *lchunk, *ltemp;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
struct list_head tlist;
INIT_LIST_HEAD(&tlist);
while (!list_empty(&transport->transmitted)) { /* Walk through the specified transmitted queue. */
lchunk = sctp_list_dequeue(&transport->transmitted); list_for_each_safe(lchunk, ltemp, &transport->transmitted) {
chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list);
/* If we are doing retransmission due to a fast retransmit, /* 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, ...@@ -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 * retransmission due to a timeout, only the chunks that are
* not yet acked should be added to the retransmit queue. * not yet acked should be added to the retransmit queue.
*/ */
if ((fast_retransmit && !chunk->fast_retransmit) || if ((fast_retransmit && chunk->fast_retransmit) ||
(!fast_retransmit && chunk->tsn_gap_acked)) { (!fast_retransmit && !chunk->tsn_gap_acked)) {
list_add_tail(lchunk, &tlist);
} else {
/* RFC 2960 6.2.1 Processing a Received SACK /* RFC 2960 6.2.1 Processing a Received SACK
* *
* C) Any time a DATA chunk is marked for * 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, ...@@ -257,15 +280,14 @@ void sctp_retransmit_mark(sctp_outqueue_t *q, sctp_transport_t *transport,
chunk->rtt_in_progress = 0; chunk->rtt_in_progress = 0;
transport->rto_pending = 0; transport->rto_pending = 0;
} }
list_add_tail(lchunk, &q->retransmit);
}
}
/* Reconstruct the transmitted queue with chunks that are not /* Move the chunk to the retransmit queue. The chunks
* eligible for retransmission. * on the retransmit queue is always kept in order.
*/ */
while (NULL != (lchunk = sctp_list_dequeue(&tlist))) list_del(lchunk);
list_add_tail(lchunk, &transport->transmitted); sctp_retransmit_insert(lchunk, q);
}
}
SCTP_DEBUG_PRINTK("%s: transport: %p, fast_retransmit: %d, " SCTP_DEBUG_PRINTK("%s: transport: %p, fast_retransmit: %d, "
"cwnd: %d, ssthresh: %d, flight_size: %d, " "cwnd: %d, ssthresh: %d, flight_size: %d, "
......
...@@ -59,6 +59,7 @@ ...@@ -59,6 +59,7 @@
/* Global data structures. */ /* Global data structures. */
sctp_protocol_t sctp_proto; sctp_protocol_t sctp_proto;
struct proc_dir_entry *proc_net_sctp; 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 /* This is the global socket data structure used for responding to
* the Out-of-the-blue (OOTB) packets. A control sock will be created * 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, ...@@ -255,27 +256,41 @@ int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp,
return error; return error;
} }
/* Returns the mtu for the given v4 destination address. */ /* Returns the dst cache entry for the given source and destination ip
int sctp_v4_get_dst_mtu(const sockaddr_storage_t *address) * 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 rtable *rt;
struct flowi fl = { .nl_u = { .ip4_u = struct flowi fl = { .nl_u = { .ip4_u = { .daddr =
{ .daddr = address->v4.sin_addr.s_addr } } }; 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)) { if (ip_route_output_key(&rt, &fl)) {
SCTP_DEBUG_PRINTK("sctp_v4_get_dst_mtu:ip_route_output_key" SCTP_DEBUG_PRINTK("NO ROUTE\n");
" failed, returning %d as dst_mtu\n", return NULL;
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);
} }
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. /* Event handler for inet device events.
...@@ -437,7 +452,8 @@ sctp_func_t sctp_ipv4_specific = { ...@@ -437,7 +452,8 @@ sctp_func_t sctp_ipv4_specific = {
.queue_xmit = ip_queue_xmit, .queue_xmit = ip_queue_xmit,
.setsockopt = ip_setsockopt, .setsockopt = ip_setsockopt,
.getsockopt = ip_getsockopt, .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), .net_header_len = sizeof(struct iphdr),
.sockaddr_len = sizeof(struct sockaddr_in), .sockaddr_len = sizeof(struct sockaddr_in),
.sa_family = AF_INET, .sa_family = AF_INET,
......
...@@ -47,6 +47,7 @@ ...@@ -47,6 +47,7 @@
* Dajiang Zhang <dajiang.zhang@nokia.com> * Dajiang Zhang <dajiang.zhang@nokia.com>
* Sridhar Samudrala <sri@us.ibm.com> * Sridhar Samudrala <sri@us.ibm.com>
* Daisy Chang <daisyc@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 * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -838,6 +839,53 @@ sctp_chunk_t *sctp_make_abort_no_data(const sctp_association_t *asoc, ...@@ -838,6 +839,53 @@ sctp_chunk_t *sctp_make_abort_no_data(const sctp_association_t *asoc,
return retval; 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. */ /* Make a HEARTBEAT chunk. */
sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc, sctp_chunk_t *sctp_make_heartbeat(const sctp_association_t *asoc,
const sctp_transport_t *transport, const sctp_transport_t *transport,
...@@ -982,16 +1030,17 @@ sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc, ...@@ -982,16 +1030,17 @@ sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc,
return retval; return retval;
} }
/* Set chunk->source based on the IP header in chunk->skb. */ /* Set chunk->source and dest based on the IP header in chunk->skb. */
void sctp_init_source(sctp_chunk_t *chunk) void sctp_init_addrs(sctp_chunk_t *chunk)
{ {
sockaddr_storage_t *source; sockaddr_storage_t *source, *dest;
struct sk_buff *skb; struct sk_buff *skb;
struct sctphdr *sh; struct sctphdr *sh;
struct iphdr *ih4; struct iphdr *ih4;
struct ipv6hdr *ih6; struct ipv6hdr *ih6;
source = &chunk->source; source = &chunk->source;
dest = &chunk->dest;
skb = chunk->skb; skb = chunk->skb;
ih4 = skb->nh.iph; ih4 = skb->nh.iph;
ih6 = skb->nh.ipv6h; ih6 = skb->nh.ipv6h;
...@@ -1002,6 +1051,9 @@ void sctp_init_source(sctp_chunk_t *chunk) ...@@ -1002,6 +1051,9 @@ void sctp_init_source(sctp_chunk_t *chunk)
source->v4.sin_family = AF_INET; source->v4.sin_family = AF_INET;
source->v4.sin_port = ntohs(sh->source); source->v4.sin_port = ntohs(sh->source);
source->v4.sin_addr.s_addr = ih4->saddr; 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; break;
case 6: case 6:
...@@ -1009,6 +1061,9 @@ void sctp_init_source(sctp_chunk_t *chunk) ...@@ -1009,6 +1061,9 @@ void sctp_init_source(sctp_chunk_t *chunk)
source->v6.sin6_family = AF_INET6; source->v6.sin6_family = AF_INET6;
source->v6.sin6_port = ntohs(sh->source); source->v6.sin6_port = ntohs(sh->source);
source->v6.sin6_addr = ih6->saddr; 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. ? */ /* FIXME: What do we do with scope, etc. ? */
break; break;
) )
......
...@@ -66,7 +66,8 @@ static void sctp_do_ecn_cwr_work(sctp_association_t *asoc, ...@@ -66,7 +66,8 @@ static void sctp_do_ecn_cwr_work(sctp_association_t *asoc,
static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
sctp_transport_t *transport); sctp_transport_t *transport);
static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *asoc); 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, static void sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc,
sctp_chunk_t *chunk, sctp_chunk_t *chunk,
sctp_init_chunk_t *peer_init, sctp_init_chunk_t *peer_init,
...@@ -251,7 +252,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -251,7 +252,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
int force; int force;
sctp_cmd_t *command; sctp_cmd_t *command;
sctp_chunk_t *new_obj; sctp_chunk_t *new_obj;
sctp_chunk_t *chunk; sctp_chunk_t *chunk = NULL;
sctp_packet_t *packet; sctp_packet_t *packet;
struct list_head *pos; struct list_head *pos;
struct timer_list *timer; struct timer_list *timer;
...@@ -259,6 +260,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -259,6 +260,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_transport_t *t; sctp_transport_t *t;
sctp_sackhdr_t sackh; sctp_sackhdr_t sackh;
if(SCTP_EVENT_T_TIMEOUT != event_type)
chunk = (sctp_chunk_t *) event_arg; chunk = (sctp_chunk_t *) event_arg;
/* Note: This whole file is a huge candidate for rework. /* Note: This whole file is a huge candidate for rework.
...@@ -504,7 +506,8 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -504,7 +506,8 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break; break;
case SCTP_CMD_ASSOC_FAILED: case SCTP_CMD_ASSOC_FAILED:
sctp_cmd_assoc_failed(commands, asoc); sctp_cmd_assoc_failed(commands, asoc, event_type,
chunk);
break; break;
case SCTP_CMD_COUNTER_INC: case SCTP_CMD_COUNTER_INC:
...@@ -595,10 +598,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -595,10 +598,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. */ /* 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) static void sctp_do_ecn_ce_work(sctp_association_t *asoc, __u32 lowest_tsn)
{ {
/* /* Save the TSN away for comparison when we receive CWR */
* Save the TSN away for comparison when we receive CWR
* Note: dp->TSN is expected in host endian
*/
asoc->last_ecne_tsn = lowest_tsn; asoc->last_ecne_tsn = lowest_tsn;
asoc->need_ecne = 1; asoc->need_ecne = 1;
...@@ -621,7 +621,6 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc, ...@@ -621,7 +621,6 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
sctp_chunk_t *chunk) sctp_chunk_t *chunk)
{ {
sctp_chunk_t *repl; sctp_chunk_t *repl;
sctp_transport_t *transport;
/* Our previously transmitted packet ran into some congestion /* Our previously transmitted packet ran into some congestion
* so we should take action by reducing cwnd and ssthresh * so we should take action by reducing cwnd and ssthresh
...@@ -629,43 +628,28 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc, ...@@ -629,43 +628,28 @@ static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc,
* sending a CWR. * 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 /* Find which transport's congestion variables
* need to be adjusted. * need to be adjusted.
*/ */
transport = sctp_assoc_lookup_tsn(asoc, lowest_tsn); transport = sctp_assoc_lookup_tsn(asoc, lowest_tsn);
/* Update the congestion variables. */ /* Update the congestion variables. */
if (transport) if (transport)
sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_ECNE); sctp_transport_lower_cwnd(transport,
SCTP_LOWER_CWND_ECNE);
/* Save away a rough idea of when we last sent out a CWR. asoc->last_cwr_tsn = lowest_tsn;
* 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;
/* 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); 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 /* If we run out of memory, it will look like a lost CWR. We'll
...@@ -1038,14 +1022,26 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, ...@@ -1038,14 +1022,26 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands,
/* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */ /* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */
static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, 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; 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, event = sctp_ulpevent_make_assoc_change(asoc,
0, 0,
SCTP_COMM_LOST, SCTP_COMM_LOST,
0, 0, 0, error, 0, 0,
GFP_ATOMIC); GFP_ATOMIC);
if (event) if (event)
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
* Hui Huang <hui.huang@nokia.com> * Hui Huang <hui.huang@nokia.com>
* Dajiang Zhang <dajiang.zhang@nokia.com> * Dajiang Zhang <dajiang.zhang@nokia.com>
* Daisy Chang <daisyc@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 * Any bugs reported given to us we will try to fix... any fixes shared will
* be incorporated into the next SCTP release. * be incorporated into the next SCTP release.
...@@ -2098,18 +2099,11 @@ sctp_disposition_t sctp_sf_do_ecne(const sctp_endpoint_t *ep, ...@@ -2098,18 +2099,11 @@ sctp_disposition_t sctp_sf_do_ecne(const sctp_endpoint_t *ep,
ecne = (sctp_ecnehdr_t *) chunk->skb->data; ecne = (sctp_ecnehdr_t *) chunk->skb->data;
skb_pull(chunk->skb, sizeof(sctp_ecnehdr_t)); 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 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_add_cmd_sf(commands, SCTP_CMD_ECN_ECNE,
SCTP_U32(ecne->lowest_tsn)); SCTP_U32(ntohl(ecne->lowest_tsn)));
}
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
} }
...@@ -3401,13 +3395,14 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(const sctp_endpoint_t *ep, ...@@ -3401,13 +3395,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 * from its upper layer, but retransmits data to the far end
* if necessary to fill gaps. * if necessary to fill gaps.
*/ */
struct msghdr *msg = arg;
sctp_chunk_t *abort; sctp_chunk_t *abort;
sctp_disposition_t retval; sctp_disposition_t retval;
retval = SCTP_DISPOSITION_CONSUME; retval = SCTP_DISPOSITION_CONSUME;
/* Generate ABORT chunk to send the peer. */ /* Generate ABORT chunk to send the peer. */
abort = sctp_make_abort(asoc, NULL, 0); abort = sctp_make_abort_user(asoc, NULL, msg);
if (!abort) if (!abort)
retval = SCTP_DISPOSITION_NOMEM; retval = SCTP_DISPOSITION_NOMEM;
else else
...@@ -3525,6 +3520,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep, ...@@ -3525,6 +3520,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep,
void *arg, void *arg,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
struct msghdr *msg = arg;
sctp_chunk_t *abort; sctp_chunk_t *abort;
sctp_disposition_t retval; sctp_disposition_t retval;
...@@ -3534,7 +3530,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep, ...@@ -3534,7 +3530,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep,
retval = SCTP_DISPOSITION_CONSUME; retval = SCTP_DISPOSITION_CONSUME;
/* Generate ABORT chunk to send the peer */ /* Generate ABORT chunk to send the peer */
abort = sctp_make_abort(asoc, NULL, 0); abort = sctp_make_abort_user(asoc, NULL, msg);
if (!abort) if (!abort)
retval = SCTP_DISPOSITION_NOMEM; retval = SCTP_DISPOSITION_NOMEM;
else else
...@@ -4258,6 +4254,11 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, ...@@ -4258,6 +4254,11 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
if (!packet) if (!packet)
goto nomem_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_init(packet, transport, sport, dport);
packet = sctp_packet_config(packet, vtag, 0, NULL); packet = sctp_packet_config(packet, vtag, 0, NULL);
......
...@@ -804,10 +804,12 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -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", SCTP_DEBUG_PRINTK("msg_len: %Zd, sinfo_flags: 0x%x\n",
msg_len, sinfo_flags); msg_len, sinfo_flags);
/* If MSG_EOF|MSG_ABORT is set, no data can be sent. Disallow /* If MSG_EOF is set, no data can be sent. Disallow sending zero
* sending 0-length messages when MSG_EOF|MSG_ABORT is not set. * 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))) { (!(sinfo_flags & (MSG_EOF|MSG_ABORT)) && (msg_len == 0))) {
err = -EINVAL; err = -EINVAL;
goto out_nounlock; goto out_nounlock;
...@@ -879,7 +881,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -879,7 +881,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
} }
if (sinfo_flags & MSG_ABORT) { if (sinfo_flags & MSG_ABORT) {
SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc); SCTP_DEBUG_PRINTK("Aborting association: %p\n", asoc);
sctp_primitive_ABORT(asoc, NULL); sctp_primitive_ABORT(asoc, msg);
err = 0; err = 0;
goto out_unlock; goto out_unlock;
} }
...@@ -1238,6 +1240,9 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, ...@@ -1238,6 +1240,9 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
{ {
sctp_opt_t *sp = sctp_sk(sk); 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)) if (optlen != sizeof(int))
return -EINVAL; return -EINVAL;
if (copy_from_user(&sp->autoclose, optval, optlen)) if (copy_from_user(&sp->autoclose, optval, optlen))
...@@ -1593,6 +1598,9 @@ static inline int sctp_getsockopt_set_events(struct sock *sk, int len, char *opt ...@@ -1593,6 +1598,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) 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)) if (len != sizeof(int))
return -EINVAL; return -EINVAL;
if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len)) if (copy_to_user(optval, &sctp_sk(sk)->autoclose, len))
...@@ -1614,10 +1622,10 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso ...@@ -1614,10 +1622,10 @@ SCTP_STATIC int sctp_do_peeloff(sctp_association_t *assoc, struct socket **newso
int err = 0; int err = 0;
/* An association cannot be branched off from an already peeled-off /* 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) if (SCTP_SOCKET_UDP != sctp_sk(oldsk)->type)
return -EINVAL; return -EOPNOTSUPP;
/* Create a new socket. */ /* Create a new socket. */
err = sock_create(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, &tmpsock); err = sock_create(PF_INET, SOCK_SEQPACKET, IPPROTO_SCTP, &tmpsock);
......
...@@ -87,7 +87,6 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer, ...@@ -87,7 +87,6 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
peer->ipaddr = *addr; peer->ipaddr = *addr;
peer->af_specific = sctp_get_af_specific(addr); peer->af_specific = sctp_get_af_specific(addr);
peer->asoc = NULL; peer->asoc = NULL;
peer->pmtu = peer->af_specific->get_dst_mtu(addr);
/* From 6.3.1 RTO Calculation: /* From 6.3.1 RTO Calculation:
* *
...@@ -161,6 +160,7 @@ void sctp_transport_destroy(sctp_transport_t *transport) ...@@ -161,6 +160,7 @@ void sctp_transport_destroy(sctp_transport_t *transport)
if (transport->asoc) if (transport->asoc)
sctp_association_put(transport->asoc); sctp_association_put(transport->asoc);
dst_release(transport->dst);
kfree(transport); kfree(transport);
SCTP_DBG_OBJCNT_DEC(transport); SCTP_DBG_OBJCNT_DEC(transport);
} }
...@@ -200,6 +200,78 @@ void sctp_transport_set_owner(sctp_transport_t *transport, ...@@ -200,6 +200,78 @@ void sctp_transport_set_owner(sctp_transport_t *transport,
sctp_association_hold(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,
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;
else
transport->pmtu = SCTP_DEFAULT_MAXSEGMENT;
}
/* Hold a reference to a transport. */ /* Hold a reference to a transport. */
void sctp_transport_hold(sctp_transport_t *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