Commit 28b9daad authored by David S. Miller's avatar David S. Miller

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

into nuts.ninka.net:/home/davem/src/BK/net-2.5
parents f7efec4a 89de669a
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
* *
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* Karl Knutson <karl@athena.chicago.il.us> * 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 * 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.
...@@ -72,6 +73,7 @@ typedef enum { ...@@ -72,6 +73,7 @@ typedef enum {
SCTP_CMD_STRIKE, /* Mark a strike against a transport. */ SCTP_CMD_STRIKE, /* Mark a strike against a transport. */
SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */ SCTP_CMD_TRANSMIT, /* Transmit the outqueue. */
SCTP_CMD_HB_TIMERS_START, /* Start the heartbeat timers. */ 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_RESET, /* Reset the status of a transport. */
SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */ SCTP_CMD_TRANSPORT_ON, /* Mark the transport as active. */
SCTP_CMD_REPORT_ERROR, /* Pass this error back out of the sm. */ SCTP_CMD_REPORT_ERROR, /* Pass this error back out of the sm. */
...@@ -83,6 +85,7 @@ typedef enum { ...@@ -83,6 +85,7 @@ typedef enum {
SCTP_CMD_UPDATE_ASSOC, /* Update association information. */ SCTP_CMD_UPDATE_ASSOC, /* Update association information. */
SCTP_CMD_PURGE_OUTQUEUE, /* Purge all data waiting to be sent. */ SCTP_CMD_PURGE_OUTQUEUE, /* Purge all data waiting to be sent. */
SCTP_CMD_SETUP_T2, /* Hi-level, setup T2-shutdown parms. */ SCTP_CMD_SETUP_T2, /* Hi-level, setup T2-shutdown parms. */
SCTP_CMD_RTO_PENDING, /* Set transport's rto_pending. */
SCTP_CMD_LAST SCTP_CMD_LAST
} sctp_verb_t; } sctp_verb_t;
......
/* 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.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
...@@ -142,7 +142,7 @@ extern int sctp_primitive_ASSOCIATE(sctp_association_t *, void *arg); ...@@ -142,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_SHUTDOWN(sctp_association_t *, void *arg);
extern int sctp_primitive_ABORT(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_SEND(sctp_association_t *, void *arg);
extern int sctp_primitive_REQUESTHEARTBEAT(sctp_association_t *, void *arg);
/* /*
* sctp_crc32c.c * sctp_crc32c.c
...@@ -418,6 +418,19 @@ static inline size_t get_user_iov_size(struct iovec *iov, int iovlen) ...@@ -418,6 +418,19 @@ static inline size_t get_user_iov_size(struct iovec *iov, int iovlen)
return retval; 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. */ /* Round an int up to the next multiple of 4. */
#define WORD_ROUND(s) (((s)+3)&~3) #define WORD_ROUND(s) (((s)+3)&~3)
...@@ -460,6 +473,26 @@ static inline sctp_protocol_t *sctp_get_protocol(void) ...@@ -460,6 +473,26 @@ static inline sctp_protocol_t *sctp_get_protocol(void)
return &sctp_proto; return &sctp_proto;
} }
/* Convert from an IP version number to an Address Family symbol. */
static inline int ipver2af(__u8 ipver)
{
int family;
switch (ipver) {
case 4:
family = AF_INET;
break;
case 6:
family = AF_INET6;
break;
default:
family = 0;
break;
};
return family;
}
/* Warning: The following hash functions assume a power of two 'size'. */ /* Warning: The following hash functions assume a power of two 'size'. */
/* This is the hash function for the SCTP port hash table. */ /* This is the hash function for the SCTP port hash table. */
static inline int sctp_phashfn(__u16 lport) static inline int sctp_phashfn(__u16 lport)
......
...@@ -157,6 +157,7 @@ sctp_state_fn_t sctp_sf_shutdown_ack_sent_prm_abort; ...@@ -157,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_closed;
sctp_state_fn_t sctp_sf_error_shutdown; sctp_state_fn_t sctp_sf_error_shutdown;
sctp_state_fn_t sctp_sf_ignore_primitive; sctp_state_fn_t sctp_sf_ignore_primitive;
sctp_state_fn_t sctp_sf_do_prm_requestheartbeat;
/* Prototypes for other event state functions. */ /* Prototypes for other event state functions. */
sctp_state_fn_t sctp_sf_do_9_2_start_shutdown; sctp_state_fn_t sctp_sf_do_9_2_start_shutdown;
...@@ -206,9 +207,6 @@ sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *, ...@@ -206,9 +207,6 @@ sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *,
sctp_chunk_t *, sctp_chunk_t *,
const int priority); const int priority);
__u32 sctp_generate_verification_tag(void); __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); void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag);
/* Prototypes for chunk-building functions. */ /* Prototypes for chunk-building functions. */
...@@ -334,10 +332,10 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *); ...@@ -334,10 +332,10 @@ __u32 sctp_generate_tag(const sctp_endpoint_t *);
__u32 sctp_generate_tsn(const sctp_endpoint_t *); __u32 sctp_generate_tsn(const sctp_endpoint_t *);
/* 4th level prototypes */ /* 4th level prototypes */
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *, void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *,
__u16 port); __u16 port);
int sctp_addr2sockaddr(const sctpParam_t, sockaddr_storage_t *); int sctp_addr2sockaddr(const union sctp_params, union sctp_addr *);
int sockaddr2sctp_addr(const sockaddr_storage_t *, sctp_addr_param_t *); int sockaddr2sctp_addr(const union sctp_addr *, sctp_addr_param_t *);
/* Extern declarations for major data structures. */ /* Extern declarations for major data structures. */
sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t); sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t, sctp_state_t);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
* 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 Intel Corp. * Copyright (c) 2001 Intel Corp.
* Copyright (c) 2001 International Business Machines Corp. * Copyright (c) 2001-2002 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -95,11 +95,11 @@ struct sockaddr_storage { ...@@ -95,11 +95,11 @@ struct sockaddr_storage {
/* A convenience structure for handling sockaddr structures. /* A convenience structure for handling sockaddr structures.
* We should wean ourselves off this. * We should wean ourselves off this.
*/ */
typedef union { union sctp_addr {
struct sockaddr_in v4; struct sockaddr_in v4;
struct sockaddr_in6 v6; struct sockaddr_in6 v6;
struct sockaddr sa; struct sockaddr sa;
} sockaddr_storage_t; };
/* Forward declarations for data structures. */ /* Forward declarations for data structures. */
...@@ -246,22 +246,40 @@ typedef struct sctp_func { ...@@ -246,22 +246,40 @@ typedef struct sctp_func {
int optname, int optname,
char *optval, char *optval,
int *optlen); int *optlen);
struct dst_entry *(*get_dst) (sockaddr_storage_t *daddr, struct dst_entry *(*get_dst) (union sctp_addr *daddr,
sockaddr_storage_t *saddr); union sctp_addr *saddr);
int (*cmp_saddr) (struct dst_entry *dst, void (*copy_addrlist) (struct list_head *,
sockaddr_storage_t *saddr); struct net_device *);
void (*dst_saddr) (union sctp_addr *saddr,
struct dst_entry *dst);
int (*cmp_addr) (const union sctp_addr *addr1,
const union sctp_addr *addr2);
void (*addr_copy) (union sctp_addr *dst,
union sctp_addr *src);
void (*from_skb) (union sctp_addr *,
struct sk_buff *skb,
int saddr);
int (*addr_valid) (union sctp_addr *);
sctp_scope_t (*scope) (union sctp_addr *);
void (*inaddr_any) (union sctp_addr *, unsigned short);
int (*is_any) (const union sctp_addr *);
__u16 net_header_len; __u16 net_header_len;
int sockaddr_len; int sockaddr_len;
sa_family_t sa_family; sa_family_t sa_family;
struct list_head list; struct list_head list;
} sctp_func_t; } sctp_func_t;
sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address); sctp_func_t *sctp_get_af_specific(sa_family_t);
/* Protocol family functions. */ /* Protocol family functions. */
typedef struct sctp_pf { typedef struct sctp_pf {
void (*event_msgname)(sctp_ulpevent_t *, char *, int *); void (*event_msgname)(sctp_ulpevent_t *, char *, int *);
void (*skb_msgname)(struct sk_buff *, char *, int *); void (*skb_msgname)(struct sk_buff *, char *, int *);
int (*af_supported)(sa_family_t);
int (*cmp_addr) (const union sctp_addr *,
const union sctp_addr *,
struct sctp_opt *);
struct sctp_func *af;
} sctp_pf_t; } sctp_pf_t;
/* SCTP Socket type: UDP or TCP style. */ /* SCTP Socket type: UDP or TCP style. */
...@@ -339,7 +357,7 @@ typedef struct sctp_cookie { ...@@ -339,7 +357,7 @@ typedef struct sctp_cookie {
__u32 initial_tsn; __u32 initial_tsn;
/* This holds the originating address of the INIT packet. */ /* This holds the originating address of the INIT packet. */
sockaddr_storage_t peer_addr; union sctp_addr peer_addr;
/* This is a shim for my peer's INIT packet, followed by /* This is a shim for my peer's INIT packet, followed by
* a copy of the raw address list of the association. * a copy of the raw address list of the association.
...@@ -359,20 +377,6 @@ typedef struct sctp_signed_cookie { ...@@ -359,20 +377,6 @@ typedef struct sctp_signed_cookie {
} sctp_signed_cookie_t; } 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 /* This is another convenience type to allocate memory for address
* params for the maximum size and pass such structures around * params for the maximum size and pass such structures around
...@@ -383,6 +387,21 @@ typedef union { ...@@ -383,6 +387,21 @@ typedef union {
sctp_ipv6addr_param_t v6; sctp_ipv6addr_param_t v6;
} sctp_addr_param_t; } 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. /* RFC 2960. Section 3.3.5 Heartbeat.
* Heartbeat Information: variable length * Heartbeat Information: variable length
* The Sender-specific Heartbeat Info field should normally include * The Sender-specific Heartbeat Info field should normally include
...@@ -392,7 +411,7 @@ typedef union { ...@@ -392,7 +411,7 @@ typedef union {
*/ */
typedef struct sctp_sender_hb_info { typedef struct sctp_sender_hb_info {
sctp_paramhdr_t param_hdr; sctp_paramhdr_t param_hdr;
sockaddr_storage_t daddr; union sctp_addr daddr;
unsigned long sent_at; unsigned long sent_at;
} sctp_sender_hb_info_t __attribute__((packed)); } sctp_sender_hb_info_t __attribute__((packed));
...@@ -433,7 +452,7 @@ struct SCTP_chunk { ...@@ -433,7 +452,7 @@ struct SCTP_chunk {
*/ */
/* We point this at the FIRST TLV parameter to chunk_hdr. */ /* We point this at the FIRST TLV parameter to chunk_hdr. */
sctpParam_t param_hdr; union sctp_params param_hdr;
union { union {
__u8 *v; __u8 *v;
sctp_datahdr_t *data_hdr; sctp_datahdr_t *data_hdr;
...@@ -478,9 +497,9 @@ struct SCTP_chunk { ...@@ -478,9 +497,9 @@ struct SCTP_chunk {
__u8 tsn_missing_report; /* Data chunk missing counter. */ __u8 tsn_missing_report; /* Data chunk missing counter. */
/* What is the origin IP address for this chunk? */ /* What is the origin IP address for this chunk? */
sockaddr_storage_t source; union sctp_addr source;
/* Destination address for this chunk. */ /* Destination address for this chunk. */
sockaddr_storage_t dest; union sctp_addr 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
...@@ -497,8 +516,8 @@ void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data); ...@@ -497,8 +516,8 @@ 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_addrs(sctp_chunk_t *chunk); void sctp_init_addrs(sctp_chunk_t *, union sctp_addr *, union sctp_addr *);
const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk); const union sctp_addr *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. */
/* sin_family -- AF_INET or AF_INET6 /* sin_family -- AF_INET or AF_INET6
...@@ -507,7 +526,7 @@ const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk); ...@@ -507,7 +526,7 @@ const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk);
*/ */
struct sockaddr_storage_list { struct sockaddr_storage_list {
struct list_head list; struct list_head list;
sockaddr_storage_t a; union sctp_addr a;
}; };
typedef sctp_chunk_t *(sctp_packet_phandler_t)(sctp_association_t *); typedef sctp_chunk_t *(sctp_packet_phandler_t)(sctp_association_t *);
...@@ -573,7 +592,7 @@ void sctp_packet_free(sctp_packet_t *); ...@@ -573,7 +592,7 @@ void sctp_packet_free(sctp_packet_t *);
/* This represents a remote transport address. /* This represents a remote transport address.
* For local transport addresses, we just use sockaddr_storage_t. * For local transport addresses, we just use union sctp_addr.
* *
* RFC2960 Section 1.4 Key Terms * RFC2960 Section 1.4 Key Terms
* *
...@@ -601,7 +620,7 @@ struct SCTP_transport { ...@@ -601,7 +620,7 @@ struct SCTP_transport {
int dead; int dead;
/* This is the peer's IP address and port. */ /* This is the peer's IP address and port. */
sockaddr_storage_t ipaddr; union sctp_addr ipaddr;
/* These are the functions we call to handle LLP stuff. */ /* These are the functions we call to handle LLP stuff. */
sctp_func_t *af_specific; sctp_func_t *af_specific;
...@@ -684,13 +703,15 @@ struct SCTP_transport { ...@@ -684,13 +703,15 @@ struct SCTP_transport {
*/ */
unsigned long last_time_ecne_reduced; unsigned long last_time_ecne_reduced;
/* state : The current state of this destination, /* active : The current active state of this destination,
* : i.e. DOWN, UP, ALLOW-HB, NO-HEARTBEAT, etc. * : i.e. DOWN, UP, etc.
*/ */
struct {
int active; int active;
/* hb_allowed : The current heartbeat state of this destination,
* : i.e. ALLOW-HB, NO-HEARTBEAT, etc.
*/
int hb_allowed; int hb_allowed;
} state;
/* These are the error stats for this destination. */ /* These are the error stats for this destination. */
...@@ -739,11 +760,12 @@ struct SCTP_transport { ...@@ -739,11 +760,12 @@ struct SCTP_transport {
int malloced; /* Is this structure kfree()able? */ int malloced; /* Is this structure kfree()able? */
}; };
extern sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *, int); extern sctp_transport_t *sctp_transport_new(const union sctp_addr *, 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 union sctp_addr *, 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_route(sctp_transport_t *, union sctp_addr *,
struct sctp_opt *);
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 *);
...@@ -890,11 +912,12 @@ void sctp_bind_addr_init(sctp_bind_addr_t *, __u16 port); ...@@ -890,11 +912,12 @@ void sctp_bind_addr_init(sctp_bind_addr_t *, __u16 port);
void sctp_bind_addr_free(sctp_bind_addr_t *); void sctp_bind_addr_free(sctp_bind_addr_t *);
int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src, int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src,
sctp_scope_t scope, int priority,int flags); sctp_scope_t scope, int priority,int flags);
int sctp_add_bind_addr(sctp_bind_addr_t *, sockaddr_storage_t *, int sctp_add_bind_addr(sctp_bind_addr_t *, union sctp_addr *,
int priority); int priority);
int sctp_del_bind_addr(sctp_bind_addr_t *, sockaddr_storage_t *); int sctp_del_bind_addr(sctp_bind_addr_t *, union sctp_addr *);
int sctp_bind_addr_has_addr(sctp_bind_addr_t *, const sockaddr_storage_t *); int sctp_bind_addr_match(sctp_bind_addr_t *, const union sctp_addr *,
sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, struct sctp_opt *);
union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
int *addrs_len, int *addrs_len,
int priority); int priority);
int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp,
...@@ -903,10 +926,10 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, ...@@ -903,10 +926,10 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp,
unsigned short port, unsigned short port,
int priority); int priority);
sctp_scope_t sctp_scope(const sockaddr_storage_t *); sctp_scope_t sctp_scope(const union sctp_addr *);
int sctp_in_scope(const sockaddr_storage_t *addr, const sctp_scope_t scope); int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope);
int sctp_is_any(const sockaddr_storage_t *addr); int sctp_is_any(const union sctp_addr *addr);
int sctp_addr_is_valid(const sockaddr_storage_t *addr); int sctp_addr_is_valid(const union sctp_addr *addr);
/* What type of sctp_endpoint_common? */ /* What type of sctp_endpoint_common? */
...@@ -1048,35 +1071,25 @@ void sctp_endpoint_put(sctp_endpoint_t *); ...@@ -1048,35 +1071,25 @@ void sctp_endpoint_put(sctp_endpoint_t *);
void sctp_endpoint_hold(sctp_endpoint_t *); void sctp_endpoint_hold(sctp_endpoint_t *);
void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc); void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc);
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
const sockaddr_storage_t *paddr, const union sctp_addr *paddr,
sctp_transport_t **); sctp_transport_t **);
int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *);
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *,
const sockaddr_storage_t *); const union sctp_addr *);
int sctp_has_association(const sockaddr_storage_t *laddr, int sctp_has_association(const union sctp_addr *laddr,
const sockaddr_storage_t *paddr); const union sctp_addr *paddr);
int sctp_verify_init(const sctp_association_t *asoc, int sctp_verify_init(const sctp_association_t *asoc,
sctp_cid_t cid, sctp_cid_t cid,
sctp_init_chunk_t *peer_init, sctp_init_chunk_t *peer_init,
sctp_chunk_t *chunk, sctp_chunk_t *chunk,
sctp_chunk_t **err_chunk); sctp_chunk_t **err_chunk);
int sctp_verify_param(const sctp_association_t *asoc, int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
sctpParam_t param, const union sctp_addr *peer_addr,
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,
const sockaddr_storage_t *peer_addr,
sctp_init_chunk_t *peer_init, int priority); sctp_init_chunk_t *peer_init, int priority);
int sctp_process_param(sctp_association_t *asoc, int sctp_process_param(sctp_association_t *asoc, union sctp_params param,
sctpParam_t param, const union sctp_addr *peer_addr, int priority);
const sockaddr_storage_t *peer_addr,
sctp_cid_t cid, int priority);
__u32 sctp_generate_tag(const sctp_endpoint_t *ep); __u32 sctp_generate_tag(const sctp_endpoint_t *ep);
__u32 sctp_generate_tsn(const sctp_endpoint_t *ep); __u32 sctp_generate_tsn(const sctp_endpoint_t *ep);
...@@ -1165,7 +1178,7 @@ struct SCTP_association { ...@@ -1165,7 +1178,7 @@ struct SCTP_association {
/* Cache the primary path address here, when we /* Cache the primary path address here, when we
* need a an address for msg_name. * need a an address for msg_name.
*/ */
sockaddr_storage_t primary_addr; union sctp_addr primary_addr;
/* active_path /* active_path
* The path that we are currently using to * The path that we are currently using to
...@@ -1543,16 +1556,16 @@ void sctp_association_hold(sctp_association_t *); ...@@ -1543,16 +1556,16 @@ void sctp_association_hold(sctp_association_t *);
sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *); sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *);
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *, sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *,
const sockaddr_storage_t *); const union sctp_addr *);
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *, sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *,
const sockaddr_storage_t *address, const union sctp_addr *address,
const int priority); const int priority);
void sctp_assoc_control_transport(sctp_association_t *, sctp_transport_t *, void sctp_assoc_control_transport(sctp_association_t *, sctp_transport_t *,
sctp_transport_cmd_t, sctp_sn_error_t); sctp_transport_cmd_t, sctp_sn_error_t);
sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *, __u32); sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *, __u32);
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *, sctp_transport_t *sctp_assoc_is_match(sctp_association_t *,
const sockaddr_storage_t *, const union sctp_addr *,
const sockaddr_storage_t *); const union sctp_addr *);
void sctp_assoc_migrate(sctp_association_t *, struct sock *); void sctp_assoc_migrate(sctp_association_t *, struct sock *);
void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src); void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src);
...@@ -1560,14 +1573,11 @@ __u32 __sctp_association_get_next_tsn(sctp_association_t *); ...@@ -1560,14 +1573,11 @@ __u32 __sctp_association_get_next_tsn(sctp_association_t *);
__u32 __sctp_association_get_tsn_block(sctp_association_t *, int); __u32 __sctp_association_get_tsn_block(sctp_association_t *, int);
__u16 __sctp_association_get_next_ssn(sctp_association_t *, __u16 sid); __u16 __sctp_association_get_next_ssn(sctp_association_t *, __u16 sid);
int sctp_cmp_addr(const sockaddr_storage_t *ss1, int sctp_cmp_addr_exact(const union sctp_addr *ss1,
const sockaddr_storage_t *ss2); const union sctp_addr *ss2);
int sctp_cmp_addr_exact(const sockaddr_storage_t *ss1,
const sockaddr_storage_t *ss2);
sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc); sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc);
sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc); sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc);
/* A convenience structure to parse out SCTP specific CMSGs. */ /* A convenience structure to parse out SCTP specific CMSGs. */
typedef struct sctp_cmsgs { typedef struct sctp_cmsgs {
struct sctp_initmsg *init; struct sctp_initmsg *init;
......
/* 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 La Monte H.P. Yarroll * Copyright (c) 2001 La Monte H.P. Yarroll
* *
...@@ -363,48 +363,34 @@ static void sctp_association_destroy(sctp_association_t *asoc) ...@@ -363,48 +363,34 @@ static void sctp_association_destroy(sctp_association_t *asoc)
/* Add a transport address to an association. */ /* Add a transport address to an association. */
sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
const sockaddr_storage_t *addr, const union sctp_addr *addr,
int priority) int priority)
{ {
sctp_transport_t *peer; sctp_transport_t *peer;
sctp_opt_t *sp; sctp_opt_t *sp;
const __u16 *port; unsigned short port;
switch (addr->sa.sa_family) { /* AF_INET and AF_INET6 share common port field. */
case AF_INET: port = addr->v4.sin_port;
port = &addr->v4.sin_port;
break;
case AF_INET6:
SCTP_V6(
port = &addr->v6.sin6_port;
break;
);
default:
return NULL;
};
/* Set the port if it has not been set yet. */ /* Set the port if it has not been set yet. */
if (0 == asoc->peer.port) { if (0 == asoc->peer.port) {
asoc->peer.port = *port; asoc->peer.port = port;
} }
SCTP_ASSERT(*port == asoc->peer.port, ":Invalid port\n", return NULL);
/* Check to see if this is a duplicate. */ /* Check to see if this is a duplicate. */
peer = sctp_assoc_lookup_paddr(asoc, addr); peer = sctp_assoc_lookup_paddr(asoc, addr);
if (peer) if (peer)
return peer; return peer;
peer = sctp_transport_new(addr, priority); peer = sctp_transport_new(addr, priority);
if (NULL == peer) if (!peer)
return NULL; return NULL;
sctp_transport_set_owner(peer, asoc); sctp_transport_set_owner(peer, asoc);
/* Cache a route for the transport. */ /* Cache a route for the transport. */
sctp_transport_route(peer, NULL); sctp_transport_route(peer, NULL, sctp_sk(asoc->base.sk));
/* 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.
...@@ -423,7 +409,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -423,7 +409,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
asoc->frag_point = asoc->pmtu - asoc->frag_point = asoc->pmtu -
(SCTP_IP_OVERHEAD + sizeof(sctp_data_chunk_t)); (SCTP_IP_OVERHEAD + sizeof(sctp_data_chunk_t));
/* The asoc->peer.port might not be meaningful as of now, but /* The asoc->peer.port might not be meaningful yet, but
* initialize the packet structure anyway. * initialize the packet structure anyway.
*/ */
(asoc->outqueue.init_output)(&peer->packet, (asoc->outqueue.init_output)(&peer->packet,
...@@ -460,6 +446,9 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -460,6 +446,9 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
min(asoc->overall_error_threshold + peer->error_threshold, min(asoc->overall_error_threshold + peer->error_threshold,
asoc->max_retrans); asoc->max_retrans);
/* By default, enable heartbeat for peer address. */
peer->hb_allowed = 1;
/* Initialize the peer's heartbeat interval based on the /* Initialize the peer's heartbeat interval based on the
* sock configured value. * sock configured value.
*/ */
...@@ -474,7 +463,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -474,7 +463,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
asoc->peer.primary_path = peer; asoc->peer.primary_path = peer;
/* Set a default msg_name for events. */ /* Set a default msg_name for events. */
memcpy(&asoc->peer.primary_addr, &peer->ipaddr, memcpy(&asoc->peer.primary_addr, &peer->ipaddr,
sizeof(sockaddr_storage_t)); sizeof(union sctp_addr));
asoc->peer.active_path = peer; asoc->peer.active_path = peer;
asoc->peer.retran_path = peer; asoc->peer.retran_path = peer;
} }
...@@ -487,7 +476,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc, ...@@ -487,7 +476,7 @@ sctp_transport_t *sctp_assoc_add_peer(sctp_association_t *asoc,
/* Lookup a transport by address. */ /* Lookup a transport by address. */
sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc, sctp_transport_t *sctp_assoc_lookup_paddr(const sctp_association_t *asoc,
const sockaddr_storage_t *address) const union sctp_addr *address)
{ {
sctp_transport_t *t; sctp_transport_t *t;
struct list_head *pos; struct list_head *pos;
...@@ -522,12 +511,12 @@ void sctp_assoc_control_transport(sctp_association_t *asoc, ...@@ -522,12 +511,12 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
/* Record the transition on the transport. */ /* Record the transition on the transport. */
switch (command) { switch (command) {
case SCTP_TRANSPORT_UP: case SCTP_TRANSPORT_UP:
transport->state.active = 1; transport->active = 1;
spc_state = ADDRESS_AVAILABLE; spc_state = ADDRESS_AVAILABLE;
break; break;
case SCTP_TRANSPORT_DOWN: case SCTP_TRANSPORT_DOWN:
transport->state.active = 0; transport->active = 0;
spc_state = ADDRESS_UNREACHABLE; spc_state = ADDRESS_UNREACHABLE;
break; break;
...@@ -557,7 +546,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc, ...@@ -557,7 +546,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
t = list_entry(pos, sctp_transport_t, transports); t = list_entry(pos, sctp_transport_t, transports);
if (!t->state.active) if (!t->active)
continue; continue;
if (!first || t->last_time_heard > first->last_time_heard) { if (!first || t->last_time_heard > first->last_time_heard) {
second = first; second = first;
...@@ -577,7 +566,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc, ...@@ -577,7 +566,7 @@ void sctp_assoc_control_transport(sctp_association_t *asoc,
* [If the primary is active but not most recent, bump the most * [If the primary is active but not most recent, bump the most
* recently used transport.] * recently used transport.]
*/ */
if (asoc->peer.primary_path->state.active && if (asoc->peer.primary_path->active &&
first != asoc->peer.primary_path) { first != asoc->peer.primary_path) {
second = first; second = first;
first = asoc->peer.primary_path; first = asoc->peer.primary_path;
...@@ -645,102 +634,21 @@ __u16 __sctp_association_get_next_ssn(sctp_association_t *asoc, __u16 sid) ...@@ -645,102 +634,21 @@ __u16 __sctp_association_get_next_ssn(sctp_association_t *asoc, __u16 sid)
return asoc->ssn[sid]++; return asoc->ssn[sid]++;
} }
/* Compare two addresses to see if they match. Wildcard addresses
* always match within their address family.
*
* FIXME: We do not match address scopes correctly.
*/
int sctp_cmp_addr(const sockaddr_storage_t *ss1, const sockaddr_storage_t *ss2)
{
int len;
const void *base1;
const void *base2;
if (ss1->sa.sa_family != ss2->sa.sa_family)
return 0;
if (ss1->v4.sin_port != ss2->v4.sin_port)
return 0;
switch (ss1->sa.sa_family) {
case AF_INET:
if (INADDR_ANY == ss1->v4.sin_addr.s_addr ||
INADDR_ANY == ss2->v4.sin_addr.s_addr)
goto match;
len = sizeof(struct in_addr);
base1 = &ss1->v4.sin_addr;
base2 = &ss2->v4.sin_addr;
break;
case AF_INET6:
SCTP_V6(
if (IPV6_ADDR_ANY ==
sctp_ipv6_addr_type(&ss1->v6.sin6_addr))
goto match;
if (IPV6_ADDR_ANY ==
sctp_ipv6_addr_type(&ss2->v6.sin6_addr))
goto match;
len = sizeof(struct in6_addr);
base1 = &ss1->v6.sin6_addr;
base2 = &ss2->v6.sin6_addr;
break;
)
default:
printk(KERN_WARNING
"WARNING, bogus socket address family %d\n",
ss1->sa.sa_family);
return 0;
};
return (0 == memcmp(base1, base2, len));
match:
return 1;
}
/* Compare two addresses to see if they match. Wildcard addresses /* Compare two addresses to see if they match. Wildcard addresses
* only match themselves. * only match themselves.
* *
* FIXME: We do not match address scopes correctly. * FIXME: We do not match address scopes correctly.
*/ */
int sctp_cmp_addr_exact(const sockaddr_storage_t *ss1, int sctp_cmp_addr_exact(const union sctp_addr *ss1,
const sockaddr_storage_t *ss2) const union sctp_addr *ss2)
{ {
int len; struct sctp_func *af;
const void *base1;
const void *base2;
if (ss1->sa.sa_family != ss2->sa.sa_family)
return 0;
if (ss1->v4.sin_port != ss2->v4.sin_port)
return 0;
switch (ss1->sa.sa_family) {
case AF_INET:
len = sizeof(struct in_addr);
base1 = &ss1->v4.sin_addr;
base2 = &ss2->v4.sin_addr;
break;
case AF_INET6:
SCTP_V6(
len = sizeof(struct in6_addr);
base1 = &ss1->v6.sin6_addr;
base2 = &ss2->v6.sin6_addr;
break;
)
default: af = sctp_get_af_specific(ss1->sa.sa_family);
printk(KERN_WARNING if (!af)
"WARNING, bogus socket address family %d\n",
ss1->sa.sa_family);
return 0; return 0;
};
return (0 == memcmp(base1, base2, len)); return af->cmp_addr(ss1, ss2);
} }
/* Return an ecne chunk to get prepended to a packet. /* Return an ecne chunk to get prepended to a packet.
...@@ -842,8 +750,8 @@ sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn) ...@@ -842,8 +750,8 @@ sctp_transport_t *sctp_assoc_lookup_tsn(sctp_association_t *asoc, __u32 tsn)
/* Is this the association we are looking for? */ /* Is this the association we are looking for? */
sctp_transport_t *sctp_assoc_is_match(sctp_association_t *asoc, sctp_transport_t *sctp_assoc_is_match(sctp_association_t *asoc,
const sockaddr_storage_t *laddr, const union sctp_addr *laddr,
const sockaddr_storage_t *paddr) const union sctp_addr *paddr)
{ {
sctp_transport_t *transport; sctp_transport_t *transport;
...@@ -855,7 +763,8 @@ sctp_transport_t *sctp_assoc_is_match(sctp_association_t *asoc, ...@@ -855,7 +763,8 @@ sctp_transport_t *sctp_assoc_is_match(sctp_association_t *asoc,
if (!transport) if (!transport)
goto out; goto out;
if (sctp_bind_addr_has_addr(&asoc->base.bind_addr, laddr)) if (sctp_bind_addr_match(&asoc->base.bind_addr, laddr,
sctp_sk(asoc->base.sk)))
goto out; goto out;
} }
transport = NULL; transport = NULL;
...@@ -902,17 +811,13 @@ static void sctp_assoc_bh_rcv(sctp_association_t *asoc) ...@@ -902,17 +811,13 @@ static void sctp_assoc_bh_rcv(sctp_association_t *asoc)
* the incoming chunk. If so, get out of the while loop. * the incoming chunk. If so, get out of the while loop.
*/ */
if (!sctp_id2assoc(sk, associd)) if (!sctp_id2assoc(sk, associd))
goto out; break;
if (error != 0) /* If there is an error on chunk, discard this packet. */
goto err_out; if (error && chunk)
chunk->pdiscard = 1;
} }
err_out:
/* Is this the right way to pass errors up to the ULP? */
if (error)
sk->err = -error;
out:
} }
/* This routine moves an association from its old sk to a new sk. */ /* This routine moves an association from its old sk to a new sk. */
...@@ -1017,7 +922,7 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc) ...@@ -1017,7 +922,7 @@ sctp_transport_t *sctp_assoc_choose_shutdown_transport(sctp_association_t *asoc)
/* Try to find an active transport. */ /* Try to find an active transport. */
if (t->state.active) { if (t->active) {
break; break;
} else { } else {
/* Keep track of the next transport in case /* Keep track of the next transport in case
......
/* SCTP kernel reference Implementation /* SCTP kernel reference Implementation
* Copyright (c) Cisco 1999,2000 * Copyright (c) Cisco 1999,2000
* Copyright (c) Motorola 1999,2000,2001 * Copyright (c) Motorola 1999,2000,2001
* Copyright (c) International Business Machines Corp., 2001 * Copyright (c) International Business Machines Corp., 2001,2002
* Copyright (c) La Monte H.P. Yarroll 2001 * Copyright (c) La Monte H.P. Yarroll 2001
* *
* This file is part of the SCTP kernel reference implementation. * This file is part of the SCTP kernel reference implementation.
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
#include <net/sctp/sm.h> #include <net/sctp/sm.h>
/* Forward declarations for internal helpers. */ /* Forward declarations for internal helpers. */
static int sctp_copy_one_addr(sctp_bind_addr_t *, sockaddr_storage_t *, static int sctp_copy_one_addr(sctp_bind_addr_t *, union sctp_addr *,
sctp_scope_t scope, int priority, int flags); sctp_scope_t scope, int priority, int flags);
static void sctp_bind_addr_clean(sctp_bind_addr_t *); static void sctp_bind_addr_clean(sctp_bind_addr_t *);
...@@ -143,7 +143,7 @@ void sctp_bind_addr_free(sctp_bind_addr_t *bp) ...@@ -143,7 +143,7 @@ void sctp_bind_addr_free(sctp_bind_addr_t *bp)
} }
/* Add an address to the bind address list in the SCTP_bind_addr structure. */ /* Add an address to the bind address list in the SCTP_bind_addr structure. */
int sctp_add_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *new, int sctp_add_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *new,
int priority) int priority)
{ {
struct sockaddr_storage_list *addr; struct sockaddr_storage_list *addr;
...@@ -171,7 +171,7 @@ int sctp_add_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *new, ...@@ -171,7 +171,7 @@ int sctp_add_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *new,
/* Delete an address from the bind address list in the SCTP_bind_addr /* Delete an address from the bind address list in the SCTP_bind_addr
* structure. * structure.
*/ */
int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr) int sctp_del_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *del_addr)
{ {
struct list_head *pos, *temp; struct list_head *pos, *temp;
struct sockaddr_storage_list *addr; struct sockaddr_storage_list *addr;
...@@ -196,18 +196,16 @@ int sctp_del_bind_addr(sctp_bind_addr_t *bp, sockaddr_storage_t *del_addr) ...@@ -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. * 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, union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp,
int priority) int *addrs_len, int priority)
{ {
sctpParam_t addrparms; union sctp_params addrparms;
sctpParam_t retval; union sctp_params retval;
int addrparms_len; int addrparms_len;
sctp_addr_param_t rawaddr; sctp_addr_param_t rawaddr;
int len; int len;
struct sockaddr_storage_list *addr; struct sockaddr_storage_list *addr;
struct list_head *pos; struct list_head *pos;
retval.v = NULL;
addrparms_len = 0; addrparms_len = 0;
len = 0; len = 0;
...@@ -216,11 +214,11 @@ sctpParam_t sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, int *addrs_len, ...@@ -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); len += sizeof(sctp_addr_param_t);
} }
addrparms.v = kmalloc(len, priority); retval.v = kmalloc(len, priority);
if (!addrparms.v) if (!retval.v)
goto end_raw; goto end_raw;
retval = addrparms; addrparms = retval;
list_for_each(pos, &bp->address_list) { list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list); addr = list_entry(pos, struct sockaddr_storage_list, list);
...@@ -244,7 +242,7 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list, ...@@ -244,7 +242,7 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
{ {
sctp_addr_param_t *rawaddr; sctp_addr_param_t *rawaddr;
sctp_paramhdr_t *param; sctp_paramhdr_t *param;
sockaddr_storage_t addr; union sctp_addr addr;
int retval = 0; int retval = 0;
int len; int len;
...@@ -284,15 +282,16 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list, ...@@ -284,15 +282,16 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list,
* 2nd Level Abstractions * 2nd Level Abstractions
********************************************************************/ ********************************************************************/
/* Does this contain a specified address? */ /* Does this contain a specified address? Allow wildcarding. */
int sctp_bind_addr_has_addr(sctp_bind_addr_t *bp, const sockaddr_storage_t *addr) int sctp_bind_addr_match(sctp_bind_addr_t *bp, const union sctp_addr *addr,
struct sctp_opt *opt)
{ {
struct sockaddr_storage_list *laddr; struct sockaddr_storage_list *laddr;
struct list_head *pos; struct list_head *pos;
list_for_each(pos, &bp->address_list) { list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, list); laddr = list_entry(pos, struct sockaddr_storage_list, list);
if (sctp_cmp_addr(&laddr->a, addr)) if (opt->pf->cmp_addr(&laddr->a, addr, opt))
return 1; return 1;
} }
...@@ -300,7 +299,7 @@ int sctp_bind_addr_has_addr(sctp_bind_addr_t *bp, const sockaddr_storage_t *addr ...@@ -300,7 +299,7 @@ int sctp_bind_addr_has_addr(sctp_bind_addr_t *bp, const sockaddr_storage_t *addr
} }
/* Copy out addresses from the global local address list. */ /* Copy out addresses from the global local address list. */
static int sctp_copy_one_addr(sctp_bind_addr_t *dest, sockaddr_storage_t *addr, static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr,
sctp_scope_t scope, int priority, int flags) sctp_scope_t scope, int priority, int flags)
{ {
sctp_protocol_t *proto = sctp_get_protocol(); sctp_protocol_t *proto = sctp_get_protocol();
...@@ -325,94 +324,33 @@ static int sctp_copy_one_addr(sctp_bind_addr_t *dest, sockaddr_storage_t *addr, ...@@ -325,94 +324,33 @@ static int sctp_copy_one_addr(sctp_bind_addr_t *dest, sockaddr_storage_t *addr,
return error; return error;
} }
/* Is addr one of the wildcards? */ /* Is this a wildcard address? */
int sctp_is_any(const sockaddr_storage_t *addr) int sctp_is_any(const union sctp_addr *addr)
{ {
int retval = 0; struct sctp_func *af = sctp_get_af_specific(addr->sa.sa_family);
if (!af)
switch (addr->sa.sa_family) { return 0;
case AF_INET: return af->is_any(addr);
if (INADDR_ANY == addr->v4.sin_addr.s_addr)
retval = 1;
break;
case AF_INET6:
SCTP_V6(
if (IPV6_ADDR_ANY ==
sctp_ipv6_addr_type(&addr->v6.sin6_addr))
retval = 1;
);
break;
default:
break;
};
return retval;
} }
/* Is 'addr' valid for 'scope'? */ /* Is 'addr' valid for 'scope'? */
int sctp_in_scope(const sockaddr_storage_t *addr, sctp_scope_t scope) int sctp_in_scope(const union sctp_addr *addr, sctp_scope_t scope)
{ {
sctp_scope_t addr_scope = sctp_scope(addr); sctp_scope_t addr_scope = sctp_scope(addr);
switch (addr->sa.sa_family) {
case AF_INET:
/* According to the SCTP IPv4 address scoping document -
* <draft-stewart-tsvwg-sctp-ipv4-00.txt>, the scope has
* a heirarchy of 5 levels:
* Level 0 - unusable SCTP addresses
* Level 1 - loopback address
* Level 2 - link-local addresses
* Level 3 - private addresses.
* Level 4 - global addresses
* For INIT and INIT-ACK address list, let L be the level of
* of requested destination address, sender and receiver
* SHOULD include all of its addresses with level greater
* than or equal to L.
*/
/* The unusable SCTP addresses will not be considered with /* The unusable SCTP addresses will not be considered with
* any defined scopes. * any defined scopes.
*/ */
if (SCTP_SCOPE_UNUSABLE == addr_scope) if (SCTP_SCOPE_UNUSABLE == addr_scope)
return 0; return 0;
/*
/* Note that we are assuming that the scoping are the same * For INIT and INIT-ACK address list, let L be the level of
* for both IPv4 addresses and IPv6 addresses, i.e., if the * of requested destination address, sender and receiver
* scope is link local, both IPv4 link local addresses and * SHOULD include all of its addresses with level greater
* IPv6 link local addresses would be treated as in the * than or equal to L.
* scope. There is no filtering for IPv4 vs. IPv6 addresses
* based on scoping alone.
*/
if (addr_scope <= scope)
return 1;
break;
case AF_INET6:
/* FIXME:
* This is almost certainly wrong since scopes have an
* heirarchy. I don't know what RFC to look at.
* There may be some guidance in the SCTP implementors
* guide (an Internet Draft as of October 2001).
*
* Further verification on the correctness of the IPv6
* scoping is needed. According to the IPv6 scoping draft,
* the link local and site local address may require
* further scoping.
*
* Is the heirachy of the IPv6 scoping the same as what's
* defined for IPv4?
* If the same heirarchy indeed applies to both famiies,
* this function can be simplified with one set of code.
* (see the comments for IPv4 above)
*/ */
if (addr_scope <= scope) if (addr_scope <= scope)
return 1; return 1;
break;
default:
return 0;
};
return 0; return 0;
} }
...@@ -422,112 +360,13 @@ int sctp_in_scope(const sockaddr_storage_t *addr, sctp_scope_t scope) ...@@ -422,112 +360,13 @@ int sctp_in_scope(const sockaddr_storage_t *addr, sctp_scope_t scope)
********************************************************************/ ********************************************************************/
/* What is the scope of 'addr'? */ /* What is the scope of 'addr'? */
sctp_scope_t sctp_scope(const sockaddr_storage_t *addr) sctp_scope_t sctp_scope(const union sctp_addr *addr)
{
sctp_scope_t retval = SCTP_SCOPE_GLOBAL;
switch (addr->sa.sa_family) {
case AF_INET:
/* We are checking the loopback, private and other address
* scopes as defined in RFC 1918.
* The IPv4 scoping is based on the draft for SCTP IPv4
* scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>.
* The set of SCTP address scope hopefully can cover both
* types of addresses.
*/
/* Should IPv4 scoping be a sysctl configurable option
* so users can turn it off (default on) for certain
* unconventional networking environments?
*/
/* Check for unusable SCTP addresses. */
if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_UNUSABLE;
} else if (LOOPBACK(addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_LOOPBACK;
} else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_LINK;
} else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_PRIVATE;
} else {
retval = SCTP_SCOPE_GLOBAL;
}
break;
case AF_INET6:
{
SCTP_V6(
int v6scope;
v6scope = ipv6_addr_scope((struct in6_addr *)
&addr->v6.sin6_addr);
/* The IPv6 scope is really a set of bit
* fields. See IFA_* in <net/if_inet6.h>.
* Mapping them to the generic SCTP scope
* set is an attempt to have code
* consistencies with the IPv4 scoping.
*/
switch (v6scope) {
case IFA_HOST:
retval = SCTP_SCOPE_LOOPBACK;
break;
case IFA_LINK:
retval = SCTP_SCOPE_LINK;
break;
case IFA_SITE:
retval = SCTP_SCOPE_PRIVATE;
break;
default:
retval = SCTP_SCOPE_GLOBAL;
break;
};
);
break;
}
default:
retval = SCTP_SCOPE_GLOBAL;
break;
};
return retval;
}
/* This function checks if the address is a valid address to be used for
* SCTP.
*
* Output:
* Return 0 - If the address is a non-unicast or an illegal address.
* Return 1 - If the address is a unicast.
*/
int sctp_addr_is_valid(const sockaddr_storage_t *addr)
{ {
unsigned short sa_family = addr->sa.sa_family; struct sctp_func *af;
switch (sa_family) {
case AF_INET:
/* Is this a non-unicast address or a unusable SCTP address? */
if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
return 0;
break;
case AF_INET6:
SCTP_V6(
{
int ret = sctp_ipv6_addr_type(&addr->v6.sin6_addr);
/* Is this a non-unicast address */ af = sctp_get_af_specific(addr->sa.sa_family);
if (!(ret & IPV6_ADDR_UNICAST)) if (!af)
return 0; return SCTP_SCOPE_UNUSABLE;
break;
});
default: return af->scope((union sctp_addr *)addr);
return 0;
};
return 1;
} }
/* 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
...@@ -237,13 +237,14 @@ void sctp_endpoint_put(sctp_endpoint_t *ep) ...@@ -237,13 +237,14 @@ void sctp_endpoint_put(sctp_endpoint_t *ep)
/* Is this the endpoint we are looking for? */ /* Is this the endpoint we are looking for? */
sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep, sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep,
const sockaddr_storage_t *laddr) const union sctp_addr *laddr)
{ {
sctp_endpoint_t *retval; sctp_endpoint_t *retval;
sctp_read_lock(&ep->base.addr_lock); sctp_read_lock(&ep->base.addr_lock);
if (ep->base.bind_addr.port == laddr->v4.sin_port) { if (ep->base.bind_addr.port == laddr->v4.sin_port) {
if (sctp_bind_addr_has_addr(&ep->base.bind_addr, laddr)) { if (sctp_bind_addr_match(&ep->base.bind_addr, laddr,
sctp_sk(ep->base.sk))) {
retval = ep; retval = ep;
goto out; goto out;
} }
...@@ -262,7 +263,7 @@ sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep, ...@@ -262,7 +263,7 @@ sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *ep,
*/ */
sctp_association_t *__sctp_endpoint_lookup_assoc( sctp_association_t *__sctp_endpoint_lookup_assoc(
const sctp_endpoint_t *endpoint, const sctp_endpoint_t *endpoint,
const sockaddr_storage_t *paddr, const union sctp_addr *paddr,
sctp_transport_t **transport) sctp_transport_t **transport)
{ {
int rport; int rport;
...@@ -289,7 +290,7 @@ sctp_association_t *__sctp_endpoint_lookup_assoc( ...@@ -289,7 +290,7 @@ sctp_association_t *__sctp_endpoint_lookup_assoc(
/* Lookup association on an endpoint based on a peer address. BH-safe. */ /* Lookup association on an endpoint based on a peer address. BH-safe. */
sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
const sockaddr_storage_t *paddr, const union sctp_addr *paddr,
sctp_transport_t **transport) sctp_transport_t **transport)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -301,6 +302,30 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, ...@@ -301,6 +302,30 @@ sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep,
return asoc; return asoc;
} }
/* Look for any peeled off association from the endpoint that matches the
* given peer address.
*/
int sctp_endpoint_is_peeled_off(sctp_endpoint_t *ep,
const union sctp_addr *paddr)
{
struct list_head *pos;
struct sockaddr_storage_list *addr;
sctp_bind_addr_t *bp;
sctp_read_lock(&ep->base.addr_lock);
bp = &ep->base.bind_addr;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sockaddr_storage_list, list);
if (sctp_has_association(&addr->a, paddr)) {
sctp_read_unlock(&ep->base.addr_lock);
return 1;
}
}
sctp_read_unlock(&ep->base.addr_lock);
return 0;
}
/* Do delayed input processing. This is scheduled by sctp_rcv(). /* Do delayed input processing. This is scheduled by sctp_rcv().
* This may be called on BH or task time. * This may be called on BH or task time.
*/ */
...@@ -316,7 +341,7 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep) ...@@ -316,7 +341,7 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep)
int error = 0; int error = 0;
if (ep->base.dead) if (ep->base.dead)
goto out; return;
asoc = NULL; asoc = NULL;
inqueue = &ep->base.inqueue; inqueue = &ep->base.inqueue;
...@@ -350,25 +375,16 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep) ...@@ -350,25 +375,16 @@ static void sctp_endpoint_bh_rcv(sctp_endpoint_t *ep)
if (chunk->transport) if (chunk->transport)
chunk->transport->last_time_heard = jiffies; chunk->transport->last_time_heard = jiffies;
/* FIX ME We really would rather NOT have to use
* GFP_ATOMIC.
*/
error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state, error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state,
ep, asoc, chunk, GFP_ATOMIC); ep, asoc, chunk, GFP_ATOMIC);
if (error != 0) if (error && chunk)
goto err_out; chunk->pdiscard = 1;
/* Check to see if the endpoint is freed in response to /* Check to see if the endpoint is freed in response to
* the incoming chunk. If so, get out of the while loop. * the incoming chunk. If so, get out of the while loop.
*/ */
if (!sctp_sk(sk)->ep) if (!sctp_sk(sk)->ep)
goto out; break;
} }
err_out:
/* Is this the right way to pass errors up to the ULP? */
if (error)
ep->base.sk->err = -error;
out:
} }
...@@ -60,64 +60,11 @@ ...@@ -60,64 +60,11 @@
/* Forward declarations for internal helpers. */ /* Forward declarations for internal helpers. */
static int sctp_rcv_ootb(struct sk_buff *); static int sctp_rcv_ootb(struct sk_buff *);
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
const sockaddr_storage_t *laddr, const union sctp_addr *laddr,
const sockaddr_storage_t *paddr, const union sctp_addr *paddr,
sctp_transport_t **transportp); sctp_transport_t **transportp);
sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const sockaddr_storage_t *laddr); sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr);
/* Initialize a sockaddr_storage from in incoming skb.
* FIXME: This belongs with AF specific sctp_func_t. --jgrimm
*/
static sockaddr_storage_t *sctp_sockaddr_storage_init(sockaddr_storage_t *addr,
const struct sk_buff *skb,
int is_saddr)
{
sockaddr_storage_t *ret = NULL;
void *to, *saddr, *daddr;
__u16 *port;
size_t len;
struct sctphdr *sh;
switch (skb->nh.iph->version) {
case 4:
to = &addr->v4.sin_addr.s_addr;
port = &addr->v4.sin_port;
saddr = &skb->nh.iph->saddr;
daddr = &skb->nh.iph->daddr;
len = sizeof(struct in_addr);
addr->v4.sin_family = AF_INET;
break;
case 6:
SCTP_V6(
to = &addr->v6.sin6_addr;
port = &addr->v6.sin6_port;
saddr = &skb->nh.ipv6h->saddr;
daddr = &skb->nh.ipv6h->daddr;
len = sizeof(struct in6_addr);
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_flowinfo = 0; /* FIXME */
addr->v6.sin6_scope_id = 0; /* FIXME */
break;
)
default:
goto out;
};
sh = (struct sctphdr *) skb->h.raw;
if (is_saddr) {
*port = ntohs(sh->source);
memcpy(to, saddr, len);
} else {
*port = ntohs(sh->dest);
memcpy(to, daddr, len);
}
ret = addr;
out:
return ret;
}
/* Calculate the SCTP checksum of an SCTP packet. */ /* Calculate the SCTP checksum of an SCTP packet. */
static inline int sctp_rcv_checksum(struct sk_buff *skb) static inline int sctp_rcv_checksum(struct sk_buff *skb)
...@@ -147,8 +94,9 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -147,8 +94,9 @@ int sctp_rcv(struct sk_buff *skb)
sctp_transport_t *transport = NULL; sctp_transport_t *transport = NULL;
sctp_chunk_t *chunk; sctp_chunk_t *chunk;
struct sctphdr *sh; struct sctphdr *sh;
sockaddr_storage_t src; union sctp_addr src;
sockaddr_storage_t dest; union sctp_addr dest;
struct sctp_func *af;
int ret = 0; int ret = 0;
if (skb->pkt_type!=PACKET_HOST) if (skb->pkt_type!=PACKET_HOST)
...@@ -165,8 +113,13 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -165,8 +113,13 @@ int sctp_rcv(struct sk_buff *skb)
skb_pull(skb, sizeof(struct sctphdr)); skb_pull(skb, sizeof(struct sctphdr));
sctp_sockaddr_storage_init(&src, skb, 1); af = sctp_get_af_specific(ipver2af(skb->nh.iph->version));
sctp_sockaddr_storage_init(&dest, skb, 0); if (unlikely(!af))
goto bad_packet;
/* Initialize local addresses for lookups. */
af->from_skb(&src, skb, 1);
af->from_skb(&dest, skb, 0);
/* If the packet is to or from a non-unicast address, /* If the packet is to or from a non-unicast address,
* silently discard the packet. * silently discard the packet.
...@@ -179,7 +132,7 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -179,7 +132,7 @@ int sctp_rcv(struct sk_buff *skb)
* IP broadcast addresses cannot be used in an SCTP transport * IP broadcast addresses cannot be used in an SCTP transport
* address." * address."
*/ */
if (!sctp_addr_is_valid(&src) || !sctp_addr_is_valid(&dest)) if (!af->addr_valid(&src) || !af->addr_valid(&dest))
goto discard_it; goto discard_it;
asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport); asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport);
...@@ -219,7 +172,7 @@ int sctp_rcv(struct sk_buff *skb) ...@@ -219,7 +172,7 @@ int sctp_rcv(struct sk_buff *skb)
chunk->sctp_hdr = sh; chunk->sctp_hdr = sh;
/* Set the source and destination addresses of the incoming chunk. */ /* Set the source and destination addresses of the incoming chunk. */
sctp_init_addrs(chunk); sctp_init_addrs(chunk, &src, &dest);
/* Remember where we came from. */ /* Remember where we came from. */
chunk->transport = transport; chunk->transport = transport;
...@@ -431,7 +384,7 @@ void sctp_unhash_endpoint(sctp_endpoint_t *ep) ...@@ -431,7 +384,7 @@ void sctp_unhash_endpoint(sctp_endpoint_t *ep)
} }
/* Look up an endpoint. */ /* Look up an endpoint. */
sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const sockaddr_storage_t *laddr) sctp_endpoint_t *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr)
{ {
sctp_hashbucket_t *head; sctp_hashbucket_t *head;
sctp_endpoint_common_t *epb; sctp_endpoint_common_t *epb;
...@@ -523,8 +476,8 @@ void __sctp_unhash_established(sctp_association_t *asoc) ...@@ -523,8 +476,8 @@ void __sctp_unhash_established(sctp_association_t *asoc)
} }
/* Look up an association. */ /* Look up an association. */
sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr, sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr,
const sockaddr_storage_t *paddr, const union sctp_addr *paddr,
sctp_transport_t **transportp) sctp_transport_t **transportp)
{ {
sctp_hashbucket_t *head; sctp_hashbucket_t *head;
...@@ -559,8 +512,8 @@ sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr, ...@@ -559,8 +512,8 @@ sctp_association_t *__sctp_lookup_association(const sockaddr_storage_t *laddr,
} }
/* Look up an association. BH-safe. */ /* Look up an association. BH-safe. */
sctp_association_t *sctp_lookup_association(const sockaddr_storage_t *laddr, sctp_association_t *sctp_lookup_association(const union sctp_addr *laddr,
const sockaddr_storage_t *paddr, const union sctp_addr *paddr,
sctp_transport_t **transportp) sctp_transport_t **transportp)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
...@@ -573,8 +526,8 @@ sctp_association_t *sctp_lookup_association(const sockaddr_storage_t *laddr, ...@@ -573,8 +526,8 @@ sctp_association_t *sctp_lookup_association(const sockaddr_storage_t *laddr,
} }
/* Is there an association matching the given local and peer addresses? */ /* Is there an association matching the given local and peer addresses? */
int sctp_has_association(const sockaddr_storage_t *laddr, int sctp_has_association(const union sctp_addr *laddr,
const sockaddr_storage_t *paddr) const union sctp_addr *paddr)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
sctp_transport_t *transport; sctp_transport_t *transport;
...@@ -606,21 +559,19 @@ int sctp_has_association(const sockaddr_storage_t *laddr, ...@@ -606,21 +559,19 @@ int sctp_has_association(const sockaddr_storage_t *laddr,
* in certain circumstances. * in certain circumstances.
* *
*/ */
static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb, static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb,
const sockaddr_storage_t *laddr, sctp_transport_t **transportp) const union sctp_addr *laddr, sctp_transport_t **transportp)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
sockaddr_storage_t addr; union sctp_addr addr;
sockaddr_storage_t *paddr = &addr; union sctp_addr *paddr = &addr;
struct sctphdr *sh = (struct sctphdr *) skb->h.raw; struct sctphdr *sh = (struct sctphdr *) skb->h.raw;
sctp_chunkhdr_t *ch; sctp_chunkhdr_t *ch;
__u8 *ch_end, *data; union sctp_params params;
sctp_paramhdr_t *parm; sctp_init_chunk_t *init;
ch = (sctp_chunkhdr_t *) skb->data; 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. */ /* If this is INIT/INIT-ACK look inside the chunk too. */
switch (ch->type) { switch (ch->type) {
case SCTP_CID_INIT: case SCTP_CID_INIT:
...@@ -646,24 +597,17 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb, ...@@ -646,24 +597,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 /* Find the start of the TLVs and the end of the chunk. This is
* the region we search for address parameters. * the region we search for address parameters.
*/ */
data = skb->data + sizeof(sctp_init_chunk_t); init = (sctp_init_chunk_t *)skb->data;
/* See sctp_process_init() for how to go thru TLVs. */
while (data < ch_end) {
parm = (sctp_paramhdr_t *)data;
if (!parm->length)
break;
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. */ /* Note: Ignoring hostname addresses. */
if ((SCTP_PARAM_IPV4_ADDRESS != parm->type) && if ((SCTP_PARAM_IPV4_ADDRESS != params.p->type) &&
(SCTP_PARAM_IPV6_ADDRESS != parm->type)) (SCTP_PARAM_IPV6_ADDRESS != params.p->type))
continue; continue;
sctp_param2sockaddr(paddr, (sctp_addr_param_t *)parm, sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source));
ntohs(sh->source));
asoc = __sctp_lookup_association(laddr, paddr, transportp); asoc = __sctp_lookup_association(laddr, paddr, transportp);
if (asoc) if (asoc)
return asoc; return asoc;
...@@ -674,20 +618,20 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb, ...@@ -674,20 +618,20 @@ static sctp_association_t *__sctp_rcv_initack_lookup(struct sk_buff *skb,
/* Lookup an association for an inbound skb. */ /* Lookup an association for an inbound skb. */
sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb,
const sockaddr_storage_t *paddr, const union sctp_addr *paddr,
const sockaddr_storage_t *laddr, const union sctp_addr *laddr,
sctp_transport_t **transportp) sctp_transport_t **transportp)
{ {
sctp_association_t *asoc; sctp_association_t *asoc;
asoc = __sctp_lookup_association(laddr, paddr, transportp); asoc = __sctp_lookup_association(laddr, paddr, transportp);
/* Further lookup for INIT-ACK packet. /* Further lookup for INIT/INIT-ACK packets.
* SCTP Implementors Guide, 2.18 Handling of address * SCTP Implementors Guide, 2.18 Handling of address
* parameters within the INIT or INIT-ACK. * parameters within the INIT or INIT-ACK.
*/ */
if (!asoc) if (!asoc)
asoc = __sctp_rcv_initack_lookup(skb, laddr, transportp); asoc = __sctp_rcv_init_lookup(skb, laddr, transportp);
return asoc; return asoc;
} }
......
...@@ -172,12 +172,12 @@ static inline int sctp_v6_xmit(struct sk_buff *skb) ...@@ -172,12 +172,12 @@ static inline int sctp_v6_xmit(struct sk_buff *skb)
/* Returns the dst cache entry for the given source and destination ip /* Returns the dst cache entry for the given source and destination ip
* addresses. * addresses.
*/ */
struct dst_entry *sctp_v6_get_dst(sockaddr_storage_t *daddr, struct dst_entry *sctp_v6_get_dst(union sctp_addr *daddr,
sockaddr_storage_t *saddr) union sctp_addr *saddr)
{ {
struct dst_entry *dst; struct dst_entry *dst;
struct flowi fl = { .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, struct flowi fl = {
} } }; .nl_u = { .ip6_u = { .daddr = &daddr->v6.sin6_addr, } } };
SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ", SCTP_DEBUG_PRINTK("%s: DST=%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x ",
...@@ -206,12 +206,147 @@ struct dst_entry *sctp_v6_get_dst(sockaddr_storage_t *daddr, ...@@ -206,12 +206,147 @@ struct dst_entry *sctp_v6_get_dst(sockaddr_storage_t *daddr,
return dst; return dst;
} }
/* Check if the dst entry's source addr matches the given source addr. */ /* Make a copy of all potential local addresses. */
int sctp_v6_cmp_saddr(struct dst_entry *dst, sockaddr_storage_t *saddr) static void sctp_v6_copy_addrlist(struct list_head *addrlist,
struct net_device *dev)
{
struct inet6_dev *in6_dev;
struct inet6_ifaddr *ifp;
struct sockaddr_storage_list *addr;
read_lock(&addrconf_lock);
if ((in6_dev = __in6_dev_get(dev)) == NULL) {
read_unlock(&addrconf_lock);
return;
}
read_lock(&in6_dev->lock);
for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) {
/* Add the address to the local list. */
addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
if (addr) {
addr->a.v6.sin6_family = AF_INET6;
addr->a.v6.sin6_port = 0;
addr->a.v6.sin6_addr = ifp->addr;
INIT_LIST_HEAD(&addr->list);
list_add_tail(&addr->list, addrlist);
}
}
read_unlock(&in6_dev->lock);
read_unlock(&addrconf_lock);
}
/* Initialize a sockaddr_storage from in incoming skb. */
static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb,
int is_saddr)
{
void *from;
__u16 *port;
struct sctphdr *sh;
port = &addr->v6.sin6_port;
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_flowinfo = 0; /* FIXME */
addr->v6.sin6_scope_id = 0; /* FIXME */
sh = (struct sctphdr *) skb->h.raw;
if (is_saddr) {
*port = ntohs(sh->source);
from = &skb->nh.ipv6h->saddr;
} else {
*port = ntohs(sh->dest);
from = &skb->nh.ipv6h->daddr;
}
ipv6_addr_copy(&addr->v6.sin6_addr, from);
}
/* Initialize a sctp_addr from a dst_entry. */
static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst)
{ {
struct rt6_info *rt = (struct rt6_info *)dst; struct rt6_info *rt = (struct rt6_info *)dst;
addr->sa.sa_family = AF_INET6;
ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr);
}
/* Compare addresses exactly. Well.. almost exactly; ignore scope_id
* for now. FIXME.
*/
static int sctp_v6_cmp_addr(const union sctp_addr *addr1,
const union sctp_addr *addr2)
{
int match;
if (addr1->sa.sa_family != addr2->sa.sa_family)
return 0;
match = !ipv6_addr_cmp((struct in6_addr *)&addr1->v6.sin6_addr,
(struct in6_addr *)&addr2->v6.sin6_addr);
return match;
}
/* Initialize addr struct to INADDR_ANY. */
static void sctp_v6_inaddr_any(union sctp_addr *addr, unsigned short port)
{
memset(addr, 0x00, sizeof(union sctp_addr));
addr->v6.sin6_family = AF_INET6;
addr->v6.sin6_port = port;
}
/* Is this a wildcard address? */
static int sctp_v6_is_any(const union sctp_addr *addr)
{
int type;
type = ipv6_addr_type((struct in6_addr *)&addr->v6.sin6_addr);
return IPV6_ADDR_ANY == type;
}
return ipv6_addr_cmp(&rt->rt6i_src.addr, &saddr->v6.sin6_addr); /* This function checks if the address is a valid address to be used for
* SCTP.
*
* Output:
* Return 0 - If the address is a non-unicast or an illegal address.
* Return 1 - If the address is a unicast.
*/
static int sctp_v6_addr_valid(union sctp_addr *addr)
{
int ret = sctp_ipv6_addr_type(&addr->v6.sin6_addr);
/* FIXME: v4-mapped-v6 address support. */
/* Is this a non-unicast address */
if (!(ret & IPV6_ADDR_UNICAST))
return 0;
return 1;
}
/* What is the scope of 'addr'? */
static sctp_scope_t sctp_v6_scope(union sctp_addr *addr)
{
int v6scope;
sctp_scope_t retval;
/* The IPv6 scope is really a set of bit fields.
* See IFA_* in <net/if_inet6.h>. Map to a generic SCTP scope.
*/
v6scope = ipv6_addr_scope(&addr->v6.sin6_addr);
switch (v6scope) {
case IFA_HOST:
retval = SCTP_SCOPE_LOOPBACK;
break;
case IFA_LINK:
retval = SCTP_SCOPE_LINK;
break;
case IFA_SITE:
retval = SCTP_SCOPE_PRIVATE;
break;
default:
retval = SCTP_SCOPE_GLOBAL;
break;
};
return retval;
} }
/* Initialize a PF_INET6 socket msg_name. */ /* Initialize a PF_INET6 socket msg_name. */
...@@ -227,12 +362,13 @@ static void sctp_inet6_msgname(char *msgname, int *addr_len) ...@@ -227,12 +362,13 @@ static void sctp_inet6_msgname(char *msgname, int *addr_len)
} }
/* Initialize a PF_INET msgname from a ulpevent. */ /* Initialize a PF_INET msgname from a ulpevent. */
static void sctp_inet6_event_msgname(sctp_ulpevent_t *event, char *msgname, int *addrlen) static void sctp_inet6_event_msgname(sctp_ulpevent_t *event, char *msgname,
int *addrlen)
{ {
struct sockaddr_in6 *sin6, *sin6from; struct sockaddr_in6 *sin6, *sin6from;
if (msgname) { if (msgname) {
sockaddr_storage_t *addr; union sctp_addr *addr;
sctp_inet6_msgname(msgname, addrlen); sctp_inet6_msgname(msgname, addrlen);
sin6 = (struct sockaddr_in6 *)msgname; sin6 = (struct sockaddr_in6 *)msgname;
...@@ -288,6 +424,49 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname, ...@@ -288,6 +424,49 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname,
} }
} }
/* Do we support this AF? */
static int sctp_inet6_af_supported(sa_family_t family)
{
/* FIXME: v4-mapped-v6 addresses. The I-D is still waffling
* on what to do with sockaddr formats for PF_INET6 sockets.
* For now assume we'll support both.
*/
switch (family) {
case AF_INET6:
case AF_INET:
return 1;
default:
return 0;
}
}
/* Address matching with wildcards allowed. This extra level
* of indirection lets us choose whether a PF_INET6 should
* disallow any v4 addresses if we so choose.
*/
static int sctp_inet6_cmp_addr(const union sctp_addr *addr1,
const union sctp_addr *addr2,
struct sctp_opt *opt)
{
struct sctp_func *af1, *af2;
af1 = sctp_get_af_specific(addr1->sa.sa_family);
af2 = sctp_get_af_specific(addr2->sa.sa_family);
if (!af1 || !af2)
return 0;
/* Today, wildcard AF_INET/AF_INET6. */
if (sctp_is_any(addr1) || sctp_is_any(addr2))
return 1;
if (addr1->sa.sa_family != addr2->sa.sa_family)
return 0;
return af1->cmp_addr(addr1, addr2);
}
static struct proto_ops inet6_seqpacket_ops = { static struct proto_ops inet6_seqpacket_ops = {
.family = PF_INET6, .family = PF_INET6,
.release = inet6_release, .release = inet6_release,
...@@ -327,7 +506,14 @@ static sctp_func_t sctp_ipv6_specific = { ...@@ -327,7 +506,14 @@ static sctp_func_t sctp_ipv6_specific = {
.setsockopt = ipv6_setsockopt, .setsockopt = ipv6_setsockopt,
.getsockopt = ipv6_getsockopt, .getsockopt = ipv6_getsockopt,
.get_dst = sctp_v6_get_dst, .get_dst = sctp_v6_get_dst,
.cmp_saddr = sctp_v6_cmp_saddr, .copy_addrlist = sctp_v6_copy_addrlist,
.from_skb = sctp_v6_from_skb,
.dst_saddr = sctp_v6_dst_saddr,
.cmp_addr = sctp_v6_cmp_addr,
.scope = sctp_v6_scope,
.addr_valid = sctp_v6_addr_valid,
.inaddr_any = sctp_v6_inaddr_any,
.is_any = sctp_v6_is_any,
.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,
...@@ -336,6 +522,9 @@ static sctp_func_t sctp_ipv6_specific = { ...@@ -336,6 +522,9 @@ static sctp_func_t sctp_ipv6_specific = {
static sctp_pf_t sctp_pf_inet6_specific = { static sctp_pf_t sctp_pf_inet6_specific = {
.event_msgname = sctp_inet6_event_msgname, .event_msgname = sctp_inet6_event_msgname,
.skb_msgname = sctp_inet6_skb_msgname, .skb_msgname = sctp_inet6_skb_msgname,
.af_supported = sctp_inet6_af_supported,
.cmp_addr = sctp_inet6_cmp_addr,
.af = &sctp_ipv6_specific,
}; };
/* Initialize IPv6 support and register with inet6 stack. */ /* Initialize IPv6 support and register with inet6 stack. */
......
...@@ -366,18 +366,13 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -366,18 +366,13 @@ int sctp_packet_transmit(sctp_packet_t *packet)
*/ */
sh->checksum = htonl(crc32); sh->checksum = htonl(crc32);
/* FIXME: Delete the rest of this switch statement once phase 2
* of address selection (ipv6 support) drops in.
*/
switch (transport->ipaddr.sa.sa_family) { switch (transport->ipaddr.sa.sa_family) {
case AF_INET:
inet_sk(sk)->daddr = transport->ipaddr.v4.sin_addr.s_addr;
break;
case AF_INET6: case AF_INET6:
SCTP_V6(inet6_sk(sk)->daddr = transport->ipaddr.v6.sin6_addr;) SCTP_V6(inet6_sk(sk)->daddr = transport->ipaddr.v6.sin6_addr;)
break; break;
default:
/* This is bogus address type, just bail. */
break;
}; };
/* IP layer ECN support /* IP layer ECN support
...@@ -430,10 +425,12 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -430,10 +425,12 @@ int sctp_packet_transmit(sctp_packet_t *packet)
dst = transport->dst; dst = transport->dst;
if (!dst || dst->obsolete) { if (!dst || dst->obsolete) {
sctp_transport_route(transport, NULL); sctp_transport_route(transport, NULL, sctp_sk(sk));
} }
nskb->dst = dst_clone(transport->dst); nskb->dst = dst_clone(transport->dst);
if (!nskb->dst)
goto no_route;
SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n", SCTP_DEBUG_PRINTK("***sctp_transmit_packet*** skb length %d\n",
nskb->len); nskb->len);
...@@ -441,6 +438,11 @@ int sctp_packet_transmit(sctp_packet_t *packet) ...@@ -441,6 +438,11 @@ int sctp_packet_transmit(sctp_packet_t *packet)
out: out:
packet->size = SCTP_IP_OVERHEAD; packet->size = SCTP_IP_OVERHEAD;
return err; return err;
no_route:
kfree_skb(nskb);
IP_INC_STATS_BH(IpOutNoRoutes);
err = -EHOSTUNREACH;
goto out;
} }
/******************************************************************** /********************************************************************
......
...@@ -678,7 +678,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -678,7 +678,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
if (!new_transport) { if (!new_transport) {
new_transport = asoc->peer.active_path; 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 /* If the chunk is Heartbeat, send it to
* chunk->transport, even it's inactive. * chunk->transport, even it's inactive.
*/ */
...@@ -835,7 +835,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout) ...@@ -835,7 +835,7 @@ int sctp_flush_outqueue(sctp_outqueue_t *q, int rtx_timeout)
*/ */
new_transport = chunk->transport; new_transport = chunk->transport;
if (new_transport == NULL || if (new_transport == NULL ||
!new_transport->state.active) !new_transport->active)
new_transport = asoc->peer.active_path; new_transport = asoc->peer.active_path;
/* Change packets if necessary. */ /* Change packets if necessary. */
...@@ -1404,7 +1404,7 @@ static void sctp_check_transmitted(sctp_outqueue_t *q, ...@@ -1404,7 +1404,7 @@ static void sctp_check_transmitted(sctp_outqueue_t *q,
/* Mark the destination transport address as /* Mark the destination transport address as
* active if it is not so marked. * active if it is not so marked.
*/ */
if (!transport->state.active) { if (!transport->active) {
sctp_assoc_control_transport( sctp_assoc_control_transport(
transport->asoc, transport->asoc,
transport, transport,
......
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
* La Monte H.P. Yarroll <piggy@acm.org> * La Monte H.P. Yarroll <piggy@acm.org>
* Narasimha Budihal <narasimha@refcode.org> * Narasimha Budihal <narasimha@refcode.org>
* Karl Knutson <karl@athena.chicago.il.us> * 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 * 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.
...@@ -181,6 +182,28 @@ DECLARE_PRIMITIVE(ABORT); ...@@ -181,6 +182,28 @@ DECLARE_PRIMITIVE(ABORT);
DECLARE_PRIMITIVE(SEND); 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. */ /* COMMENT BUG. Find out where this is mentioned in the spec. */
int sctp_other_icmp_unreachfrag(sctp_association_t *asoc, void *arg) int sctp_other_icmp_unreachfrag(sctp_association_t *asoc, void *arg)
{ {
......
/* 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
...@@ -103,7 +103,7 @@ void sctp_proc_exit(void) ...@@ -103,7 +103,7 @@ void sctp_proc_exit(void)
/* Private helper to extract ipv4 address and stash them in /* Private helper to extract ipv4 address and stash them in
* the protocol structure. * the protocol structure.
*/ */
static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto, static void sctp_v4_copy_addrlist(struct list_head *addrlist,
struct net_device *dev) struct net_device *dev)
{ {
struct in_device *in_dev; struct in_device *in_dev;
...@@ -117,7 +117,6 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto, ...@@ -117,7 +117,6 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto,
} }
read_lock(&in_dev->lock); read_lock(&in_dev->lock);
for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) { for (ifa = in_dev->ifa_list; ifa; ifa = ifa->ifa_next) {
/* Add the address to the local list. */ /* Add the address to the local list. */
addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC); addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
...@@ -126,7 +125,7 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto, ...@@ -126,7 +125,7 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto,
addr->a.v4.sin_family = AF_INET; addr->a.v4.sin_family = AF_INET;
addr->a.v4.sin_port = 0; addr->a.v4.sin_port = 0;
addr->a.v4.sin_addr.s_addr = ifa->ifa_local; addr->a.v4.sin_addr.s_addr = ifa->ifa_local;
list_add_tail(&addr->list, &proto->local_addr_list); list_add_tail(&addr->list, addrlist);
} }
} }
...@@ -134,56 +133,21 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto, ...@@ -134,56 +133,21 @@ static inline void sctp_v4_get_local_addr_list(sctp_protocol_t *proto,
read_unlock(&inetdev_lock); read_unlock(&inetdev_lock);
} }
/* Private helper to extract ipv6 address and stash them in
* the protocol structure.
* FIXME: Make this an address family function.
*/
static inline void sctp_v6_get_local_addr_list(sctp_protocol_t *proto,
struct net_device *dev)
{
#ifdef SCTP_V6_SUPPORT
/* FIXME: The testframe doesn't support this function. */
#ifndef TEST_FRAME
struct inet6_dev *in6_dev;
struct inet6_ifaddr *ifp;
struct sockaddr_storage_list *addr;
read_lock(&addrconf_lock);
if ((in6_dev = __in6_dev_get(dev)) == NULL) {
read_unlock(&addrconf_lock);
return;
}
read_lock_bh(&in6_dev->lock);
for (ifp = in6_dev->addr_list; ifp; ifp = ifp->if_next) {
/* Add the address to the local list. */
addr = t_new(struct sockaddr_storage_list, GFP_ATOMIC);
if (addr) {
addr->a.v6.sin6_family = AF_INET6;
addr->a.v6.sin6_port = 0;
addr->a.v6.sin6_addr = ifp->addr;
INIT_LIST_HEAD(&addr->list);
list_add_tail(&addr->list, &proto->local_addr_list);
}
}
read_unlock_bh(&in6_dev->lock);
read_unlock(&addrconf_lock);
#endif /* TEST_FRAME */
#endif /* SCTP_V6_SUPPORT */
}
/* Extract our IP addresses from the system and stash them in the /* Extract our IP addresses from the system and stash them in the
* protocol structure. * protocol structure.
*/ */
static void __sctp_get_local_addr_list(sctp_protocol_t *proto) static void __sctp_get_local_addr_list(sctp_protocol_t *proto)
{ {
struct net_device *dev; struct net_device *dev;
struct list_head *pos;
struct sctp_func *af;
read_lock(&dev_base_lock); read_lock(&dev_base_lock);
for (dev = dev_base; dev; dev = dev->next) { for (dev = dev_base; dev; dev = dev->next) {
sctp_v4_get_local_addr_list(proto, dev); list_for_each(pos, &proto->address_families) {
sctp_v6_get_local_addr_list(proto, dev); af = list_entry(pos, sctp_func_t, list);
af->copy_addrlist(&proto->local_addr_list, dev);
}
} }
read_unlock(&dev_base_lock); read_unlock(&dev_base_lock);
} }
...@@ -259,13 +223,15 @@ int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp, ...@@ -259,13 +223,15 @@ int sctp_copy_local_addr_list(sctp_protocol_t *proto, sctp_bind_addr_t *bp,
/* Returns the dst cache entry for the given source and destination ip /* Returns the dst cache entry for the given source and destination ip
* addresses. * addresses.
*/ */
struct dst_entry *sctp_v4_get_dst(sockaddr_storage_t *daddr, struct dst_entry *sctp_v4_get_dst(union sctp_addr *daddr,
sockaddr_storage_t *saddr) union sctp_addr *saddr)
{ {
struct rtable *rt; struct rtable *rt;
struct flowi fl = { .nl_u = { .ip4_u = { .daddr = struct flowi fl;
daddr->v4.sin_addr.s_addr,
} } }; memset(&fl, 0x0, sizeof(struct flowi));
fl.fl4_dst = daddr->v4.sin_addr.s_addr;
fl.proto = IPPROTO_SCTP;
if (saddr) if (saddr)
fl.fl4_src = saddr->v4.sin_addr.s_addr; fl.fl4_src = saddr->v4.sin_addr.s_addr;
...@@ -285,12 +251,118 @@ struct dst_entry *sctp_v4_get_dst(sockaddr_storage_t *daddr, ...@@ -285,12 +251,118 @@ struct dst_entry *sctp_v4_get_dst(sockaddr_storage_t *daddr,
return &rt->u.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) /* Initialize a sctp_addr from in incoming skb. */
static void sctp_v4_from_skb(union sctp_addr *addr, struct sk_buff *skb,
int is_saddr)
{
void *from;
__u16 *port;
struct sctphdr *sh;
port = &addr->v4.sin_port;
addr->v4.sin_family = AF_INET;
sh = (struct sctphdr *) skb->h.raw;
if (is_saddr) {
*port = ntohs(sh->source);
from = &skb->nh.iph->saddr;
} else {
*port = ntohs(sh->dest);
from = &skb->nh.iph->daddr;
}
memcpy(&addr->v4.sin_addr.s_addr, from, sizeof(struct in_addr));
}
/* Initialize a sctp_addr from a dst_entry. */
static void sctp_v4_dst_saddr(union sctp_addr *saddr, struct dst_entry *dst)
{ {
struct rtable *rt = (struct rtable *)dst; struct rtable *rt = (struct rtable *)dst;
saddr->v4.sin_family = AF_INET;
saddr->v4.sin_addr.s_addr = rt->rt_src;
}
/* Compare two addresses exactly. */
static int sctp_v4_cmp_addr(const union sctp_addr *addr1,
const union sctp_addr *addr2)
{
if (addr1->sa.sa_family != addr2->sa.sa_family)
return 0;
if (addr1->v4.sin_port != addr2->v4.sin_port)
return 0;
if (addr1->v4.sin_addr.s_addr != addr2->v4.sin_addr.s_addr)
return 0;
return 1;
}
/* Initialize addr struct to INADDR_ANY. */
static void sctp_v4_inaddr_any(union sctp_addr *addr, unsigned short port)
{
addr->v4.sin_family = AF_INET;
addr->v4.sin_addr.s_addr = INADDR_ANY;
addr->v4.sin_port = port;
}
return (rt->rt_src == saddr->v4.sin_addr.s_addr); /* Is this a wildcard address? */
static int sctp_v4_is_any(const union sctp_addr *addr)
{
return INADDR_ANY == addr->v4.sin_addr.s_addr;
}
/* This function checks if the address is a valid address to be used for
* SCTP.
*
* Output:
* Return 0 - If the address is a non-unicast or an illegal address.
* Return 1 - If the address is a unicast.
*/
static int sctp_v4_addr_valid(union sctp_addr *addr)
{
/* Is this a non-unicast address or a unusable SCTP address? */
if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr))
return 0;
return 1;
}
/* Checking the loopback, private and other address scopes as defined in
* RFC 1918. The IPv4 scoping is based on the draft for SCTP IPv4
* scoping <draft-stewart-tsvwg-sctp-ipv4-00.txt>.
*
* Level 0 - unusable SCTP addresses
* Level 1 - loopback address
* Level 2 - link-local addresses
* Level 3 - private addresses.
* Level 4 - global addresses
* For INIT and INIT-ACK address list, let L be the level of
* of requested destination address, sender and receiver
* SHOULD include all of its addresses with level greater
* than or equal to L.
*/
static sctp_scope_t sctp_v4_scope(union sctp_addr *addr)
{
sctp_scope_t retval;
/* Should IPv4 scoping be a sysctl configurable option
* so users can turn it off (default on) for certain
* unconventional networking environments?
*/
/* Check for unusable SCTP addresses. */
if (IS_IPV4_UNUSABLE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_UNUSABLE;
} else if (LOOPBACK(addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_LOOPBACK;
} else if (IS_IPV4_LINK_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_LINK;
} else if (IS_IPV4_PRIVATE_ADDRESS(&addr->v4.sin_addr.s_addr)) {
retval = SCTP_SCOPE_PRIVATE;
} else {
retval = SCTP_SCOPE_GLOBAL;
}
return retval;
} }
/* Event handler for inet device events. /* Event handler for inet device events.
...@@ -336,11 +408,11 @@ int sctp_ctl_sock_init(void) ...@@ -336,11 +408,11 @@ int sctp_ctl_sock_init(void)
/* Get the table of functions for manipulating a particular address /* Get the table of functions for manipulating a particular address
* family. * family.
*/ */
sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address) sctp_func_t *sctp_get_af_specific(sa_family_t family)
{ {
struct list_head *pos; struct list_head *pos;
sctp_protocol_t *proto = sctp_get_protocol(); sctp_protocol_t *proto = sctp_get_protocol();
sctp_func_t *retval, *af; struct sctp_func *retval, *af;
retval = NULL; retval = NULL;
...@@ -349,7 +421,7 @@ sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address) ...@@ -349,7 +421,7 @@ sctp_func_t *sctp_get_af_specific(const sockaddr_storage_t *address)
*/ */
list_for_each(pos, &proto->address_families) { list_for_each(pos, &proto->address_families) {
af = list_entry(pos, sctp_func_t, list); af = list_entry(pos, sctp_func_t, list);
if (address->sa.sa_family == af->sa_family) { if (family == af->sa_family) {
retval = af; retval = af;
break; break;
} }
...@@ -370,7 +442,8 @@ static void sctp_inet_msgname(char *msgname, int *addr_len) ...@@ -370,7 +442,8 @@ static void sctp_inet_msgname(char *msgname, int *addr_len)
} }
/* Copy the primary address of the peer primary address as the msg_name. */ /* Copy the primary address of the peer primary address as the msg_name. */
static void sctp_inet_event_msgname(sctp_ulpevent_t *event, char *msgname, int *addr_len) static void sctp_inet_event_msgname(sctp_ulpevent_t *event, char *msgname,
int *addr_len)
{ {
struct sockaddr_in *sin, *sinfrom; struct sockaddr_in *sin, *sinfrom;
...@@ -384,13 +457,13 @@ static void sctp_inet_event_msgname(sctp_ulpevent_t *event, char *msgname, int * ...@@ -384,13 +457,13 @@ static void sctp_inet_event_msgname(sctp_ulpevent_t *event, char *msgname, int *
} }
/* Initialize and copy out a msgname from an inbound skb. */ /* Initialize and copy out a msgname from an inbound skb. */
static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *addr_len) static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *len)
{ {
struct sctphdr *sh; struct sctphdr *sh;
struct sockaddr_in *sin; struct sockaddr_in *sin;
if (msgname) { if (msgname) {
sctp_inet_msgname(msgname, addr_len); sctp_inet_msgname(msgname, len);
sin = (struct sockaddr_in *)msgname; sin = (struct sockaddr_in *)msgname;
sh = (struct sctphdr *)skb->h.raw; sh = (struct sctphdr *)skb->h.raw;
sin->sin_port = sh->source; sin->sin_port = sh->source;
...@@ -398,9 +471,39 @@ static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *addr_ ...@@ -398,9 +471,39 @@ static void sctp_inet_skb_msgname(struct sk_buff *skb, char *msgname, int *addr_
} }
} }
/* Do we support this AF? */
static int sctp_inet_af_supported(sa_family_t family)
{
/* PF_INET only supports AF_INET addresses. */
return (AF_INET == family);
}
/* Address matching with wildcards allowed. */
static int sctp_inet_cmp_addr(const union sctp_addr *addr1,
const union sctp_addr *addr2,
struct sctp_opt *opt)
{
/* PF_INET only supports AF_INET addresses. */
if (addr1->sa.sa_family != addr2->sa.sa_family)
return 0;
if (INADDR_ANY == addr1->v4.sin_addr.s_addr ||
INADDR_ANY == addr2->v4.sin_addr.s_addr)
return 1;
if (addr1->v4.sin_addr.s_addr == addr2->v4.sin_addr.s_addr)
return 1;
return 0;
}
struct sctp_func sctp_ipv4_specific;
static sctp_pf_t sctp_pf_inet = { static sctp_pf_t sctp_pf_inet = {
.event_msgname = sctp_inet_event_msgname, .event_msgname = sctp_inet_event_msgname,
.skb_msgname = sctp_inet_skb_msgname, .skb_msgname = sctp_inet_skb_msgname,
.af_supported = sctp_inet_af_supported,
.cmp_addr = sctp_inet_cmp_addr,
.af = &sctp_ipv4_specific,
}; };
...@@ -448,12 +551,19 @@ static struct inet_protocol sctp_protocol = { ...@@ -448,12 +551,19 @@ static struct inet_protocol sctp_protocol = {
}; };
/* IPv4 address related functions. */ /* IPv4 address related functions. */
sctp_func_t sctp_ipv4_specific = { struct sctp_func 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 = sctp_v4_get_dst, .get_dst = sctp_v4_get_dst,
.cmp_saddr = sctp_v4_cmp_saddr, .copy_addrlist = sctp_v4_copy_addrlist,
.from_skb = sctp_v4_from_skb,
.dst_saddr = sctp_v4_dst_saddr,
.cmp_addr = sctp_v4_cmp_addr,
.addr_valid = sctp_v4_addr_valid,
.inaddr_any = sctp_v4_inaddr_any,
.is_any = sctp_v4_is_any,
.scope = sctp_v4_scope,
.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,
......
/* 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 Intel Corp. * Copyright (c) 2001-2002 Intel Corp.
* Copyright (c) 2001 International Business Machines Corp. * Copyright (c) 2001-2002 International Business Machines Corp.
* *
* This file is part of the SCTP kernel reference Implementation * This file is part of the SCTP kernel reference Implementation
* *
...@@ -166,7 +166,7 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, ...@@ -166,7 +166,7 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc,
int priority) int priority)
{ {
sctp_inithdr_t init; sctp_inithdr_t init;
sctpParam_t addrs; union sctp_params addrs;
size_t chunksize; size_t chunksize;
sctp_chunk_t *retval = NULL; sctp_chunk_t *retval = NULL;
int addrs_len = 0; int addrs_len = 0;
...@@ -228,7 +228,7 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc, ...@@ -228,7 +228,7 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc,
{ {
sctp_inithdr_t initack; sctp_inithdr_t initack;
sctp_chunk_t *retval; sctp_chunk_t *retval;
sctpParam_t addrs; union sctp_params addrs;
int addrs_len; int addrs_len;
sctp_cookie_param_t *cookie; sctp_cookie_param_t *cookie;
int cookie_len; int cookie_len;
...@@ -1031,51 +1031,15 @@ sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc, ...@@ -1031,51 +1031,15 @@ sctp_chunk_t *sctp_chunkify(struct sk_buff *skb, const sctp_association_t *asoc,
} }
/* Set chunk->source and dest based on the IP header in chunk->skb. */ /* Set chunk->source and dest based on the IP header in chunk->skb. */
void sctp_init_addrs(sctp_chunk_t *chunk) void sctp_init_addrs(sctp_chunk_t *chunk, union sctp_addr *src,
union sctp_addr *dest)
{ {
sockaddr_storage_t *source, *dest; memcpy(&chunk->source, src, sizeof(union sctp_addr));
struct sk_buff *skb; memcpy(&chunk->dest, dest, sizeof(union sctp_addr));
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;
sh = chunk->sctp_hdr;
switch (ih4->version) {
case 4:
source->v4.sin_family = AF_INET;
source->v4.sin_port = ntohs(sh->source);
source->v4.sin_addr.s_addr = ih4->saddr;
dest->v4.sin_family = AF_INET;
dest->v4.sin_port = ntohs(sh->dest);
dest->v4.sin_addr.s_addr = ih4->daddr;
break;
case 6:
SCTP_V6(
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;
)
default:
/* This is a bogus address type, just bail. */
break;
};
} }
/* Extract the source address from a chunk. */ /* Extract the source address from a chunk. */
const sockaddr_storage_t *sctp_source(const sctp_chunk_t *chunk) const union sctp_addr *sctp_source(const sctp_chunk_t *chunk)
{ {
/* If we have a known transport, use that. */ /* If we have a known transport, use that. */
if (chunk->transport) { if (chunk->transport) {
...@@ -1482,78 +1446,28 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, ...@@ -1482,78 +1446,28 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep,
* 3rd Level Abstractions * 3rd Level Abstractions
********************************************************************/ ********************************************************************/
/* Verify the INIT packet before we process it. */ /* Do not attempt to handle the HOST_NAME parm. However, do
int sctp_verify_init(const sctp_association_t *asoc, * send back an indicator to the peer.
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
*/ */
int sctp_verify_param(const sctp_association_t *asoc, static int sctp_process_hn_param(const sctp_association_t *asoc,
sctpParam_t param, union sctp_params param,
sctp_cid_t cid,
sctp_chunk_t *chunk, sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p) 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 /* Make an ERROR chunk, preparing enough room for
* chunk type, i.e., unrecognized parameters should be further * returning multiple unknown parameters.
* identified based on the chunk id.
*/ */
if (!*err_chk_p)
*err_chk_p = sctp_make_op_error_space(asoc, chunk, len);
switch (param.p->type) { if (*err_chk_p)
case SCTP_PARAM_IPV4_ADDRESS: sctp_init_cause(*err_chk_p, SCTP_ERROR_DNS_FAILED,
case SCTP_PARAM_IPV6_ADDRESS: param.v, len);
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);
break; /* Stop processing this chunk. */
} return 0;
return retval;
} }
/* RFC 3.2.1 & the Implementers Guide 2.2. /* RFC 3.2.1 & the Implementers Guide 2.2.
...@@ -1582,8 +1496,8 @@ int sctp_verify_param(const sctp_association_t *asoc, ...@@ -1582,8 +1496,8 @@ int sctp_verify_param(const sctp_association_t *asoc,
* 0 - discard the chunk * 0 - discard the chunk
* 1 - continue with the chunk * 1 - continue with the chunk
*/ */
int sctp_process_unk_param(const sctp_association_t *asoc, static int sctp_process_unk_param(const sctp_association_t *asoc,
sctpParam_t param, union sctp_params param,
sctp_chunk_t *chunk, sctp_chunk_t *chunk,
sctp_chunk_t **err_chk_p) sctp_chunk_t **err_chk_p)
{ {
...@@ -1604,7 +1518,7 @@ int sctp_process_unk_param(const sctp_association_t *asoc, ...@@ -1604,7 +1518,7 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
if (*err_chk_p) if (*err_chk_p)
sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM, sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
(const void *)param.p, param.v,
WORD_ROUND(ntohs(param.p->length))); WORD_ROUND(ntohs(param.p->length)));
break; break;
...@@ -1620,7 +1534,7 @@ int sctp_process_unk_param(const sctp_association_t *asoc, ...@@ -1620,7 +1534,7 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
if (*err_chk_p) { if (*err_chk_p) {
sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM, sctp_init_cause(*err_chk_p, SCTP_ERROR_UNKNOWN_PARAM,
(const void *)param.p, param.v,
WORD_ROUND(ntohs(param.p->length))); WORD_ROUND(ntohs(param.p->length)));
} else { } else {
/* If there is no memory for generating the ERROR /* If there is no memory for generating the ERROR
...@@ -1638,17 +1552,84 @@ int sctp_process_unk_param(const sctp_association_t *asoc, ...@@ -1638,17 +1552,84 @@ int sctp_process_unk_param(const sctp_association_t *asoc,
return retval; return retval;
} }
/* Unpack the parameters in an INIT packet. /* Find unrecognized parameters in the chunk.
* FIXME: There is no return status to allow callers to do * Return values:
* error handling. * 0 - discard the chunk
* 1 - continue with the chunk
*/
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.
*/ */
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, const union sctp_addr *peer_addr,
sctp_init_chunk_t *peer_init, sctp_init_chunk_t *peer_init,
int priority) int priority)
{ {
sctpParam_t param; union sctp_params param;
__u8 *end;
sctp_transport_t *transport; sctp_transport_t *transport;
struct list_head *pos, *temp; struct list_head *pos, *temp;
char *cookie; char *cookie;
...@@ -1664,15 +1645,14 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1664,15 +1645,14 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* be a a better choice than any of the embedded addresses. * be a a better choice than any of the embedded addresses.
*/ */
if (peer_addr) 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. */ /* Process the initialization parameters. */
end = ((__u8 *)peer_init + ntohs(peer_init->chunk_hdr.length));
for (param.v = peer_init->init_hdr.params; sctp_walk_params(param, peer_init, init_hdr.params) {
param.v < end;
param.v += WORD_ROUND(ntohs(param.p->length))) { if (!sctp_process_param(asoc, param, peer_addr, priority))
if (!sctp_process_param(asoc, param, peer_addr, cid,
priority))
goto clean_up; goto clean_up;
} }
...@@ -1738,7 +1718,7 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1738,7 +1718,7 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* association to the same value as the Initial TSN. * association to the same value as the Initial TSN.
*/ */
asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1; asoc->peer.addip_serial = asoc->peer.i.initial_tsn - 1;
return; return 1;
clean_up: clean_up:
/* Release the transport structures. */ /* Release the transport structures. */
...@@ -1747,8 +1727,11 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1747,8 +1727,11 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
list_del(pos); list_del(pos);
sctp_transport_free(transport); sctp_transport_free(transport);
} }
nomem:
return 0;
} }
/* Update asoc with the option described in param. /* Update asoc with the option described in param.
* *
* RFC2960 3.3.2.1 Optional/Variable Length Parameters in INIT * RFC2960 3.3.2.1 Optional/Variable Length Parameters in INIT
...@@ -1760,14 +1743,12 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, ...@@ -1760,14 +1743,12 @@ void sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid,
* work we do. In particular, we should not build transport * work we do. In particular, we should not build transport
* structures for the addresses. * structures for the addresses.
*/ */
int sctp_process_param(sctp_association_t *asoc, sctpParam_t param, int sctp_process_param(sctp_association_t *asoc, union sctp_params param,
const sockaddr_storage_t *peer_addr, const union sctp_addr *peer_addr, int priority)
sctp_cid_t cid, int priority)
{ {
sockaddr_storage_t addr; union sctp_addr addr;
sctp_addr_param_t *addrparm;
int j;
int i; int i;
__u16 sat;
int retval = 1; int retval = 1;
sctp_scope_t scope; sctp_scope_t scope;
...@@ -1776,30 +1757,21 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param, ...@@ -1776,30 +1757,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. * came from a fresh INIT, and INIT ACK, or were stored in a cookie.
*/ */
switch (param.p->type) { 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: case SCTP_PARAM_IPV6_ADDRESS:
/* Rethink this as we may need to keep for if( PF_INET6 != asoc->base.sk->family)
* restart considerations. break;
*/ /* Fall through. */
if (PF_INET6 == asoc->base.sk->family) { case SCTP_PARAM_IPV4_ADDRESS:
addrparm = (sctp_addr_param_t *)param.v; sctp_param2sockaddr(&addr, param.addr, asoc->peer.port);
sctp_param2sockaddr(&addr, addrparm, asoc->peer.port);
scope = sctp_scope(peer_addr); scope = sctp_scope(peer_addr);
if (sctp_in_scope(&addr, scope)) if (sctp_in_scope(&addr, scope))
sctp_assoc_add_peer(asoc, &addr, priority); if (!sctp_assoc_add_peer(asoc, &addr, priority))
} return 0;
break; break;
case SCTP_PARAM_COOKIE_PRESERVATIVE: case SCTP_PARAM_COOKIE_PRESERVATIVE:
asoc->cookie_preserve = asoc->cookie_preserve =
ntohl(param.bht->lifespan_increment); ntohl(param.life->lifespan_increment);
break; break;
case SCTP_PARAM_HOST_NAME_ADDRESS: case SCTP_PARAM_HOST_NAME_ADDRESS:
...@@ -1813,10 +1785,12 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param, ...@@ -1813,10 +1785,12 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
asoc->peer.ipv4_address = 0; asoc->peer.ipv4_address = 0;
asoc->peer.ipv6_address = 0; asoc->peer.ipv6_address = 0;
j = (ntohs(param.p->length) - /* Cycle through address types; avoid divide by 0. */
sizeof(sctp_paramhdr_t)) / sat = ntohs(param.p->length) - sizeof(sctp_paramhdr_t);
sizeof(__u16); if (sat)
for (i = 0; i < j; ++i) { sat /= sizeof(__u16);
for (i = 0; i < sat; ++i) {
switch (param.sat->types[i]) { switch (param.sat->types[i]) {
case SCTP_PARAM_IPV4_ADDRESS: case SCTP_PARAM_IPV4_ADDRESS:
asoc->peer.ipv4_address = 1; asoc->peer.ipv4_address = 1;
...@@ -1843,13 +1817,11 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param, ...@@ -1843,13 +1817,11 @@ int sctp_process_param(sctp_association_t *asoc, sctpParam_t param,
break; break;
case SCTP_PARAM_HEARTBEAT_INFO: case SCTP_PARAM_HEARTBEAT_INFO:
SCTP_DEBUG_PRINTK("unimplemented " /* Would be odd to receive, but it causes no problems. */
"SCTP_PARAM_HEARTBEAT_INFO\n");
break; break;
case SCTP_PARAM_UNRECOGNIZED_PARAMETERS: case SCTP_PARAM_UNRECOGNIZED_PARAMETERS:
SCTP_DEBUG_PRINTK("unimplemented " /* Rejected during verify stage. */
"SCTP_PARAM_UNRECOGNIZED_PARAMETERS\n");
break; break;
case SCTP_PARAM_ECN_CAPABLE: case SCTP_PARAM_ECN_CAPABLE:
...@@ -1898,8 +1870,8 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep) ...@@ -1898,8 +1870,8 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep)
* 4th Level Abstractions * 4th Level Abstractions
********************************************************************/ ********************************************************************/
/* Convert from an SCTP IP parameter to a sockaddr_storage_t. */ /* Convert from an SCTP IP parameter to a union sctp_addr. */
void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *param, void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *param,
__u16 port) __u16 port)
{ {
switch(param->v4.param_hdr.type) { switch(param->v4.param_hdr.type) {
...@@ -1926,11 +1898,8 @@ void sctp_param2sockaddr(sockaddr_storage_t *addr, sctp_addr_param_t *param, ...@@ -1926,11 +1898,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. */ /* Convert an IP address in an SCTP param into a sockaddr_in. */
/* Returns true if a valid conversion was possible. */ /* 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, union sctp_addr *sa)
{ {
if (!p.v)
return 0;
switch (p.p->type) { switch (p.p->type) {
case SCTP_PARAM_IPV4_ADDRESS: case SCTP_PARAM_IPV4_ADDRESS:
sa->v4.sin_addr = *((struct in_addr *)&p.v4->addr); sa->v4.sin_addr = *((struct in_addr *)&p.v4->addr);
...@@ -1950,30 +1919,10 @@ int sctp_addr2sockaddr(sctpParam_t p, sockaddr_storage_t *sa) ...@@ -1950,30 +1919,10 @@ int sctp_addr2sockaddr(sctpParam_t p, sockaddr_storage_t *sa)
return 1; return 1;
} }
/* Convert from an IP version number to an Address Family symbol. */
int ipver2af(__u8 ipver)
{
int family;
switch (ipver) {
case 4:
family = AF_INET;
break;
case 6:
family = AF_INET6;
break;
default:
family = 0;
break;
};
return family;
}
/* Convert a sockaddr_in to an IP address in an SCTP param. /* Convert a sockaddr_in to an IP address in an SCTP param.
* Returns len if a valid conversion was possible. * Returns len if a valid conversion was possible.
*/ */
int sockaddr2sctp_addr(const sockaddr_storage_t *sa, sctp_addr_param_t *p) int sockaddr2sctp_addr(const union sctp_addr *sa, sctp_addr_param_t *p)
{ {
int len = 0; int len = 0;
......
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
* Dajiang Zhang <dajiang.zhang@nokia.com> * Dajiang Zhang <dajiang.zhang@nokia.com>
* Daisy Chang <daisyc@us.ibm.com> * Daisy Chang <daisyc@us.ibm.com>
* Sridhar Samudrala <sri@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 * 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.
...@@ -68,11 +69,13 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, ...@@ -68,11 +69,13 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
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); sctp_event_t event_type, sctp_chunk_t *chunk);
static void sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc, static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *asoc,
sctp_chunk_t *chunk, sctp_chunk_t *chunk,
sctp_init_chunk_t *peer_init, sctp_init_chunk_t *peer_init,
int priority); int priority);
static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *, sctp_association_t *); 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 *, static void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *, sctp_association_t *,
sctp_bind_addr_t *); sctp_bind_addr_t *);
static void sctp_cmd_transport_reset(sctp_cmd_seq_t *, sctp_association_t *, static void sctp_cmd_transport_reset(sctp_cmd_seq_t *, sctp_association_t *,
...@@ -83,6 +86,8 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *, ...@@ -83,6 +86,8 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *,
sctp_sackhdr_t *); sctp_sackhdr_t *);
static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *, static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *,
sctp_chunk_t *); sctp_chunk_t *);
static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *,
sctp_state_t);
/* These three macros allow us to pull the debugging code out of the /* These three macros allow us to pull the debugging code out of the
* main flow of sctp_do_sm() to keep attention focused on the real * main flow of sctp_do_sm() to keep attention focused on the real
...@@ -193,6 +198,7 @@ int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -193,6 +198,7 @@ int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype,
/* BUG--we should now recover some memory, probably by /* BUG--we should now recover some memory, probably by
* reneging... * reneging...
*/ */
error = -ENOMEM;
break; break;
case SCTP_DISPOSITION_DELETE_TCB: case SCTP_DISPOSITION_DELETE_TCB:
...@@ -301,8 +307,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -301,8 +307,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
case SCTP_CMD_NEW_STATE: case SCTP_CMD_NEW_STATE:
/* Enter a new state. */ /* Enter a new state. */
asoc->state = command->obj.state; sctp_cmd_new_state(commands, asoc, command->obj.state);
asoc->state_timestamp = jiffies;
break; break;
case SCTP_CMD_REPORT_TSN: case SCTP_CMD_REPORT_TSN:
...@@ -339,9 +344,14 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -339,9 +344,14 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
break; break;
case SCTP_CMD_PEER_INIT: case SCTP_CMD_PEER_INIT:
/* Process a unified INIT from the peer. */ /* Process a unified INIT from the peer.
sctp_cmd_process_init(commands, asoc, chunk, * Note: Only used during INIT-ACK processing. If
command->obj.ptr, priority); * there is an error just return to the outter
* layer which will bail.
*/
error = sctp_cmd_process_init(commands, asoc, chunk,
command->obj.ptr,
priority);
break; break;
case SCTP_CMD_GEN_COOKIE_ECHO: case SCTP_CMD_GEN_COOKIE_ECHO:
...@@ -561,6 +571,11 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -561,6 +571,11 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
sctp_cmd_hb_timers_start(commands, asoc); sctp_cmd_hb_timers_start(commands, asoc);
break; 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: case SCTP_CMD_REPORT_ERROR:
error = command->obj.error; error = command->obj.error;
break; break;
...@@ -581,11 +596,18 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, ...@@ -581,11 +596,18 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype,
chunk->pdiscard = 1; chunk->pdiscard = 1;
break; break;
case SCTP_CMD_RTO_PENDING:
t = command->obj.transport;
t->rto_pending = 1;
break;
default: default:
printk(KERN_WARNING "Impossible command: %u, %p\n", printk(KERN_WARNING "Impossible command: %u, %p\n",
command->verb, command->obj.ptr); command->verb, command->obj.ptr);
break; break;
}; };
if (error)
return error;
} }
return error; return error;
...@@ -978,7 +1000,7 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, ...@@ -978,7 +1000,7 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc,
*/ */
asoc->overall_error_count++; asoc->overall_error_count++;
if (transport->state.active && if (transport->active &&
(transport->error_count++ >= transport->error_threshold)) { (transport->error_count++ >= transport->error_threshold)) {
SCTP_DEBUG_PRINTK("transport_strike: transport " SCTP_DEBUG_PRINTK("transport_strike: transport "
"IP:%d.%d.%d.%d failed.\n", "IP:%d.%d.%d.%d failed.\n",
...@@ -1058,22 +1080,32 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, ...@@ -1058,22 +1080,32 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands,
} }
/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT /* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT
* inside the cookie. * inside the cookie. In reality, this is only used for INIT-ACK processing
* since all other cases use "temporary" associations and can do all
* their work in statefuns directly.
*/ */
static void sctp_cmd_process_init(sctp_cmd_seq_t *commands, static int sctp_cmd_process_init(sctp_cmd_seq_t *commands,
sctp_association_t *asoc, sctp_association_t *asoc,
sctp_chunk_t *chunk, sctp_chunk_t *chunk,
sctp_init_chunk_t *peer_init, sctp_init_chunk_t *peer_init,
int priority) int priority)
{ {
/* The command sequence holds commands assuming that the int error;
* processing will happen successfully. If this is not the
* case, rewind the sequence and add appropriate error handling /* We only process the init as a sideeffect in a single
* to the sequence. * case. This is when we process the INIT-ACK. If we
* fail during INIT processing (due to malloc problems),
* just return the error and stop processing the stack.
*/ */
sctp_process_init(asoc, chunk->chunk_hdr->type,
if (!sctp_process_init(asoc, chunk->chunk_hdr->type,
sctp_source(chunk), peer_init, sctp_source(chunk), peer_init,
priority); priority))
error = -ENOMEM;
else
error = 0;
return error;
} }
/* Helper function to break out starting up of heartbeat timers. */ /* Helper function to break out starting up of heartbeat timers. */
...@@ -1096,6 +1128,16 @@ static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, ...@@ -1096,6 +1128,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. */ /* 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, void sctp_cmd_set_bind_addrs(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_bind_addr_t *bp) sctp_bind_addr_t *bp)
...@@ -1131,7 +1173,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, ...@@ -1131,7 +1173,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds,
/* Mark the destination transport address as active if it is not so /* Mark the destination transport address as active if it is not so
* marked. * marked.
*/ */
if (!t->state.active) if (!t->active)
sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP,
SCTP_HEARTBEAT_SUCCESS); SCTP_HEARTBEAT_SUCCESS);
...@@ -1154,10 +1196,6 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, ...@@ -1154,10 +1196,6 @@ static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds,
/* Mark one strike against a transport. */ /* Mark one strike against a transport. */
sctp_do_8_2_transport_strike(asoc, t); 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. */ /* Helper function to process the process SACK command. */
...@@ -1196,3 +1234,18 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, ...@@ -1196,3 +1234,18 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto; asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto;
chunk->transport = t; chunk->transport = t;
} }
/* Helper function to change the state of an association. */
static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc,
sctp_state_t state)
{
asoc->state = state;
asoc->state_timestamp = jiffies;
/* Wake up any process waiting for the association to
* get established.
*/
if ((SCTP_STATE_ESTABLISHED == asoc->state) &&
(waitqueue_active(&asoc->wait)))
wake_up_interruptible(&asoc->wait);
}
...@@ -236,20 +236,18 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -236,20 +236,18 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data; chunk->subh.init_hdr = (sctp_inithdr_t *)chunk->skb->data;
/* Tag the variable length parameters. */ /* Tag the variable length parameters. */
chunk->param_hdr.v = chunk->param_hdr.v = skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
skb_pull(chunk->skb, sizeof(sctp_inithdr_t));
new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC); new_asoc = sctp_make_temp_asoc(ep, chunk, GFP_ATOMIC);
if (!new_asoc) if (!new_asoc)
goto nomem; goto nomem;
/* FIXME: sctp_process_init can fail, but there is no /* The call, sctp_process_init(), can fail on memory allocation. */
* status nor handling. if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
*/
sctp_process_init(new_asoc, chunk->chunk_hdr->type,
sctp_source(chunk), sctp_source(chunk),
(sctp_init_chunk_t *)chunk->chunk_hdr, (sctp_init_chunk_t *)chunk->chunk_hdr,
GFP_ATOMIC); GFP_ATOMIC))
goto nomem_init;
sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc)); sctp_add_cmd_sf(commands, SCTP_CMD_NEW_ASOC, SCTP_ASOC(new_asoc));
...@@ -302,10 +300,10 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, ...@@ -302,10 +300,10 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_DELETE_TCB; return SCTP_DISPOSITION_DELETE_TCB;
nomem_ack: nomem_ack:
sctp_association_free(new_asoc);
if (err_chunk) if (err_chunk)
sctp_free_chunk(err_chunk); sctp_free_chunk(err_chunk);
nomem_init:
sctp_association_free(new_asoc);
nomem: nomem:
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
...@@ -563,9 +561,11 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep, ...@@ -563,9 +561,11 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
* effects--it is safe to run them here. * effects--it is safe to run them here.
*/ */
peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
sctp_process_init(new_asoc, chunk->chunk_hdr->type,
&chunk->subh.cookie_hdr->c.peer_addr, peer_init, if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
GFP_ATOMIC); &chunk->subh.cookie_hdr->c.peer_addr,
peer_init, GFP_ATOMIC))
goto nomem_init;
repl = sctp_make_cookie_ack(new_asoc, chunk); repl = sctp_make_cookie_ack(new_asoc, chunk);
if (!repl) if (!repl)
...@@ -592,10 +592,9 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep, ...@@ -592,10 +592,9 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const sctp_endpoint_t *ep,
nomem_ev: nomem_ev:
sctp_free_chunk(repl); sctp_free_chunk(repl);
nomem_repl: nomem_repl:
nomem_init:
sctp_association_free(new_asoc); sctp_association_free(new_asoc);
nomem: nomem:
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
...@@ -664,8 +663,8 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const sctp_endpoint_t *ep, ...@@ -664,8 +663,8 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const sctp_endpoint_t *ep,
return SCTP_DISPOSITION_NOMEM; return SCTP_DISPOSITION_NOMEM;
} }
/* Generate a HEARTBEAT packet on the given transport. */ /* Generate and sendout a heartbeat packet. */
sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep, sctp_disposition_t sctp_sf_heartbeat(const sctp_endpoint_t *ep,
const sctp_association_t *asoc, const sctp_association_t *asoc,
const sctp_subtype_t type, const sctp_subtype_t type,
void *arg, void *arg,
...@@ -676,6 +675,36 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep, ...@@ -676,6 +675,36 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
sctp_sender_hb_info_t hbinfo; sctp_sender_hb_info_t hbinfo;
size_t paylen = 0; size_t paylen = 0;
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;
/* Send a heartbeat to our peer. */
paylen = sizeof(sctp_sender_hb_info_t);
reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen);
if (!reply)
return SCTP_DISPOSITION_NOMEM;
/* Set rto_pending indicating that an RTT measurement
* is started with this heartbeat chunk.
*/
sctp_add_cmd_sf(commands, SCTP_CMD_RTO_PENDING,
SCTP_TRANSPORT(transport));
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) { if (asoc->overall_error_count >= asoc->overall_error_threshold) {
/* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */
sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL());
...@@ -689,34 +718,21 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep, ...@@ -689,34 +718,21 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep,
* HEARTBEAT is sent (see Section 8.3). * HEARTBEAT is sent (see Section 8.3).
*/ */
hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO; if (transport->hb_allowed) {
hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); if (SCTP_DISPOSITION_NOMEM ==
hbinfo.daddr = transport->ipaddr; sctp_sf_heartbeat(ep, asoc, type, arg,
hbinfo.sent_at = jiffies; commands))
return SCTP_DISPOSITION_NOMEM;
/* Set rto_pending indicating that an RTT measurement is started
* with this heartbeat chunk.
*/
transport->rto_pending = 1;
/* Send a heartbeat to our peer. */
paylen = sizeof(sctp_sender_hb_info_t);
reply = sctp_make_heartbeat(asoc, transport, &hbinfo, paylen);
if (!reply)
goto nomem;
sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply));
/* Set transport error counter and association error counter /* Set transport error counter and association error counter
* when sending heartbeat. * when sending heartbeat.
*/ */
sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET, sctp_add_cmd_sf(commands, SCTP_CMD_TRANSPORT_RESET,
SCTP_TRANSPORT(transport)); SCTP_TRANSPORT(transport));
}
sctp_add_cmd_sf(commands, SCTP_CMD_HB_TIMERS_UPDATE,
SCTP_TRANSPORT(transport));
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
nomem:
return SCTP_DISPOSITION_NOMEM;
} }
/* /*
...@@ -817,7 +833,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep, ...@@ -817,7 +833,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
sctp_chunk_t *chunk = arg; sctp_chunk_t *chunk = arg;
sockaddr_storage_t from_addr; union sctp_addr from_addr;
sctp_transport_t *link; sctp_transport_t *link;
sctp_sender_hb_info_t *hbinfo; sctp_sender_hb_info_t *hbinfo;
unsigned long max_interval; unsigned long max_interval;
...@@ -866,7 +882,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep, ...@@ -866,7 +882,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const sctp_endpoint_t *ep,
/* Helper function to send out an abort for the restart /* Helper function to send out an abort for the restart
* condition. * condition.
*/ */
static int sctp_sf_send_restart_abort(sockaddr_storage_t *ssa, static int sctp_sf_send_restart_abort(union sctp_addr *ssa,
sctp_chunk_t *init, sctp_chunk_t *init,
sctp_cmd_seq_t *commands) sctp_cmd_seq_t *commands)
{ {
...@@ -1125,8 +1141,13 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -1125,8 +1141,13 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
* Verification Tag and Peers Verification tag into a reserved * Verification Tag and Peers Verification tag into a reserved
* place (local tie-tag and per tie-tag) within the state cookie. * place (local tie-tag and per tie-tag) within the state cookie.
*/ */
sctp_process_init(new_asoc, chunk->chunk_hdr->type, sctp_source(chunk), if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
(sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC); sctp_source(chunk),
(sctp_init_chunk_t *)chunk->chunk_hdr,
GFP_ATOMIC)) {
retval = SCTP_DISPOSITION_NOMEM;
goto nomem_init;
}
/* Make sure no new addresses are being added during the /* Make sure no new addresses are being added during the
* restart. Do not do this check for COOKIE-WAIT state, * restart. Do not do this check for COOKIE-WAIT state,
...@@ -1197,6 +1218,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( ...@@ -1197,6 +1218,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init(
nomem: nomem:
retval = SCTP_DISPOSITION_NOMEM; retval = SCTP_DISPOSITION_NOMEM;
goto cleanup; goto cleanup;
nomem_init:
cleanup_asoc: cleanup_asoc:
sctp_association_free(new_asoc); sctp_association_free(new_asoc);
goto cleanup; goto cleanup;
...@@ -1326,15 +1348,16 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep, ...@@ -1326,15 +1348,16 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep,
* side effects--it is safe to run them here. * side effects--it is safe to run them here.
*/ */
peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
sctp_process_init(new_asoc, chunk->chunk_hdr->type,
sctp_source(chunk), peer_init, GFP_ATOMIC); if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
sctp_source(chunk), peer_init, GFP_ATOMIC))
goto nomem;
/* Make sure no new addresses are being added during the /* Make sure no new addresses are being added during the
* restart. Though this is a pretty complicated attack * restart. Though this is a pretty complicated attack
* since you'd have to get inside the cookie. * since you'd have to get inside the cookie.
*/ */
if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) { if (!sctp_sf_check_restart_addrs(new_asoc, asoc, chunk, commands)) {
printk("cookie echo check\n");
return SCTP_DISPOSITION_CONSUME; return SCTP_DISPOSITION_CONSUME;
} }
...@@ -1391,8 +1414,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const sctp_endpoint_t *ep, ...@@ -1391,8 +1414,9 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const sctp_endpoint_t *ep,
* side effects--it is safe to run them here. * side effects--it is safe to run them here.
*/ */
peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; peer_init = &chunk->subh.cookie_hdr->c.peer_init[0];
sctp_process_init(new_asoc, chunk->chunk_hdr->type, if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type,
sctp_source(chunk), peer_init, GFP_ATOMIC); sctp_source(chunk), peer_init, GFP_ATOMIC))
goto nomem;
/* Update the content of current association. */ /* Update the content of current association. */
sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc)); sctp_add_cmd_sf(commands, SCTP_CMD_UPDATE_ASSOC, SCTP_ASOC(new_asoc));
...@@ -3656,6 +3680,39 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( ...@@ -3656,6 +3680,39 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort(
return sctp_sf_shutdown_sent_prm_abort(ep, asoc, type, arg, commands); 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 * Ignore the primitive event
* *
...@@ -4257,7 +4314,8 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc, ...@@ -4257,7 +4314,8 @@ sctp_packet_t *sctp_ootb_pkt_new(const sctp_association_t *asoc,
/* Cache a route for the transport with the chunk's destination as /* Cache a route for the transport with the chunk's destination as
* the source address. * the source address.
*/ */
sctp_transport_route(transport, (sockaddr_storage_t *)&chunk->dest); sctp_transport_route(transport, (union sctp_addr *)&chunk->dest,
sctp_sk(sctp_get_ctl_sock()));
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);
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,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>
* 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.
...@@ -706,21 +707,28 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { ...@@ -706,21 +707,28 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = {
/* SCTP_STATE_EMPTY */ \ /* SCTP_STATE_EMPTY */ \
{.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \
/* SCTP_STATE_CLOSED */ \ /* 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 */ \ /* 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 */ \ /* 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 */ \ /* 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 */ \ /* 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 */ \ /* 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 */ \ /* 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 */ \ /* 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 */ } /* TYPE_SCTP_PRIMITIVE_REQUESTHEARTBEAT */
#define TYPE_SCTP_PRIMITIVE_GETSRTTREPORT { \ #define TYPE_SCTP_PRIMITIVE_GETSRTTREPORT { \
......
...@@ -86,15 +86,16 @@ static void sctp_wfree(struct sk_buff *skb); ...@@ -86,15 +86,16 @@ static void sctp_wfree(struct sk_buff *skb);
static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p, static int sctp_wait_for_sndbuf(sctp_association_t *asoc, long *timeo_p,
int msg_len); int msg_len);
static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p); static int sctp_wait_for_packet(struct sock * sk, int *err, long *timeo_p);
static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p);
static inline void sctp_sk_addr_set(struct sock *, static inline void sctp_sk_addr_set(struct sock *,
const sockaddr_storage_t *newaddr, const union sctp_addr *newaddr,
sockaddr_storage_t *saveaddr); union sctp_addr *saveaddr);
static inline void sctp_sk_addr_restore(struct sock *, static inline void sctp_sk_addr_restore(struct sock *,
const sockaddr_storage_t *); const union sctp_addr *);
static inline int sctp_sendmsg_verify_name(struct sock *, struct msghdr *); static inline int sctp_verify_addr(struct sock *, struct sockaddr *, int);
static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_add(struct sock *, struct sockaddr_storage *, int);
static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int); static int sctp_bindx_rem(struct sock *, struct sockaddr_storage *, int);
static int sctp_do_bind(struct sock *, sockaddr_storage_t *, int); static int sctp_do_bind(struct sock *, union sctp_addr *, int);
static int sctp_autobind(struct sock *sk); static int sctp_autobind(struct sock *sk);
...@@ -122,7 +123,7 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -122,7 +123,7 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
/* Disallow binding twice. */ /* Disallow binding twice. */
if (!sctp_sk(sk)->ep->base.bind_addr.port) if (!sctp_sk(sk)->ep->base.bind_addr.port)
retval = sctp_do_bind(sk, (sockaddr_storage_t *)uaddr, retval = sctp_do_bind(sk, (union sctp_addr *)uaddr,
addr_len); addr_len);
else else
retval = -EINVAL; retval = -EINVAL;
...@@ -135,14 +136,14 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) ...@@ -135,14 +136,14 @@ int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len)
static long sctp_get_port_local(struct sock *, unsigned short); static long sctp_get_port_local(struct sock *, unsigned short);
/* Bind a local address either to an endpoint or to an association. */ /* Bind a local address either to an endpoint or to an association. */
SCTP_STATIC int sctp_do_bind(struct sock *sk, sockaddr_storage_t *newaddr, SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *newaddr,
int addr_len) int addr_len)
{ {
sctp_opt_t *sp = sctp_sk(sk); sctp_opt_t *sp = sctp_sk(sk);
sctp_endpoint_t *ep = sp->ep; sctp_endpoint_t *ep = sp->ep;
sctp_bind_addr_t *bp = &ep->base.bind_addr; sctp_bind_addr_t *bp = &ep->base.bind_addr;
unsigned short sa_family = newaddr->sa.sa_family; unsigned short sa_family = newaddr->sa.sa_family;
sockaddr_storage_t tmpaddr, saveaddr; union sctp_addr tmpaddr, saveaddr;
unsigned short *snum; unsigned short *snum;
int ret = 0; int ret = 0;
...@@ -403,7 +404,7 @@ int sctp_bindx_add(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt) ...@@ -403,7 +404,7 @@ int sctp_bindx_add(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
goto err_bindx_add; goto err_bindx_add;
}; };
retval = sctp_do_bind(sk, (sockaddr_storage_t *)&addrs[cnt], retval = sctp_do_bind(sk, (union sctp_addr *)&addrs[cnt],
addr_len); addr_len);
err_bindx_add: err_bindx_add:
...@@ -481,7 +482,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt) ...@@ -481,7 +482,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
int cnt; int cnt;
sctp_bind_addr_t *bp = &ep->base.bind_addr; sctp_bind_addr_t *bp = &ep->base.bind_addr;
int retval = 0; int retval = 0;
sockaddr_storage_t saveaddr; union sctp_addr saveaddr;
SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n", SCTP_DEBUG_PRINTK("sctp_bindx_rem (sk: %p, addrs: %p, addrcnt: %d)\n",
sk, addrs, addrcnt); sk, addrs, addrcnt);
...@@ -500,7 +501,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt) ...@@ -500,7 +501,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
*/ */
switch (((struct sockaddr *)&addrs[cnt])->sa_family) { switch (((struct sockaddr *)&addrs[cnt])->sa_family) {
case AF_INET: case AF_INET:
saveaddr = *((sockaddr_storage_t *) saveaddr = *((union sctp_addr *)
&addrs[cnt]); &addrs[cnt]);
saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port); saveaddr.v4.sin_port = ntohs(saveaddr.v4.sin_port);
/* Verify the port. */ /* Verify the port. */
...@@ -511,7 +512,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt) ...@@ -511,7 +512,7 @@ int sctp_bindx_rem(struct sock *sk, struct sockaddr_storage *addrs, int addrcnt)
break; break;
case AF_INET6: case AF_INET6:
saveaddr = *((sockaddr_storage_t *) saveaddr = *((union sctp_addr *)
&addrs[cnt]); &addrs[cnt]);
saveaddr.v6.sin6_port = saveaddr.v6.sin6_port =
ntohs(saveaddr.v6.sin6_port); ntohs(saveaddr.v6.sin6_port);
...@@ -741,7 +742,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -741,7 +742,7 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
sctp_association_t *new_asoc=NULL, *asoc=NULL; sctp_association_t *new_asoc=NULL, *asoc=NULL;
sctp_transport_t *transport; sctp_transport_t *transport;
sctp_chunk_t *chunk = NULL; sctp_chunk_t *chunk = NULL;
sockaddr_storage_t to; union sctp_addr to;
struct sockaddr *msg_name = NULL; struct sockaddr *msg_name = NULL;
struct sctp_sndrcvinfo default_sinfo = { 0 }; struct sctp_sndrcvinfo default_sinfo = { 0 };
struct sctp_sndrcvinfo *sinfo; struct sctp_sndrcvinfo *sinfo;
...@@ -777,7 +778,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -777,7 +778,8 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
* For a peeled-off socket, msg_name is ignored. * For a peeled-off socket, msg_name is ignored.
*/ */
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) { if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sp->type) && msg->msg_name) {
err = sctp_sendmsg_verify_name(sk, msg); err = sctp_verify_addr(sk, (struct sockaddr *)msg->msg_name,
msg->msg_namelen);
if (err) if (err)
return err; return err;
...@@ -826,29 +828,15 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -826,29 +828,15 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
/* Look for a matching association on the endpoint. */ /* Look for a matching association on the endpoint. */
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport); asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (!asoc) { if (!asoc) {
struct list_head *pos; /* If we could not find a matching association on the
struct sockaddr_storage_list *addr; * endpoint, make sure that there is no peeled-off
sctp_bind_addr_t *bp = &ep->base.bind_addr; * association on another socket.
sctp_read_lock(&ep->base.addr_lock);
/* If we could not find a matching association on
* the endpoint, make sure that there is no peeled-
* off association.
*/ */
list_for_each(pos, &bp->address_list) { if (sctp_endpoint_is_peeled_off(ep, &to)) {
addr = list_entry(pos, err = -EADDRNOTAVAIL;
struct sockaddr_storage_list,
list);
if (sctp_has_association(&addr->a, &to)) {
err = -EINVAL;
sctp_read_unlock(&ep->base.addr_lock);
goto out_unlock; goto out_unlock;
} }
} }
sctp_read_unlock(&ep->base.addr_lock);
}
} else { } else {
/* For a peeled-off socket, ignore any associd specified by /* For a peeled-off socket, ignore any associd specified by
* the user with SNDRCVINFO. * the user with SNDRCVINFO.
...@@ -1116,6 +1104,33 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk, ...@@ -1116,6 +1104,33 @@ SCTP_STATIC int sctp_sendmsg(struct kiocb *iocb, struct sock *sk,
#endif /* 0 */ #endif /* 0 */
} }
/* This is an extended version of skb_pull() that removes the data from the
* start of a skb even when data is spread across the list of skb's in the
* frag_list. len specifies the total amount of data that needs to be removed.
* when 'len' bytes could be removed from the skb, it returns 0.
* If 'len' exceeds the total skb length, it returns the no. of bytes that
* could not be removed.
*/
static int sctp_skb_pull(struct sk_buff *skb, int len)
{
struct sk_buff *list;
if (len <= skb->len) {
__skb_pull(skb, len);
return 0;
}
len -= skb->len;
__skb_pull(skb, skb->len);
for (list = skb_shinfo(skb)->frag_list; list; list = list->next) {
len = sctp_skb_pull(list, len);
if (!len)
return 0;
}
return len;
}
/* API 3.1.3 recvmsg() - UDP Style Syntax /* API 3.1.3 recvmsg() - UDP Style Syntax
* *
* ssize_t recvmsg(int socket, struct msghdr *message, * ssize_t recvmsg(int socket, struct msghdr *message,
...@@ -1138,9 +1153,10 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1138,9 +1153,10 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
{ {
sctp_ulpevent_t *event = NULL; sctp_ulpevent_t *event = NULL;
sctp_opt_t *sp = sctp_sk(sk); sctp_opt_t *sp = sctp_sk(sk);
struct sk_buff *skb; struct sk_buff *skb, *list;
int copied; int copied;
int err = 0; int err = 0;
int skb_len;
SCTP_DEBUG_PRINTK("sctp_recvmsg(" SCTP_DEBUG_PRINTK("sctp_recvmsg("
"%s: %p, %s: %p, %s: %d, %s: %d, %s: " "%s: %p, %s: %p, %s: %d, %s: %d, %s: "
...@@ -1157,21 +1173,16 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1157,21 +1173,16 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
if (!skb) if (!skb)
goto out; goto out;
copied = skb->len; /* Get the total length of the skb including any skb's in the
* frag_list.
if (skb_shinfo(skb)->frag_list) { */
struct sk_buff *list; skb_len = skb->len;
for (list = skb_shinfo(skb)->frag_list; list; list = list->next)
for (list = skb_shinfo(skb)->frag_list; skb_len += list->len;
list;
list = list->next)
copied += list->len;
}
if (copied > len) { copied = skb_len;
if (copied > len)
copied = len; copied = len;
msg->msg_flags |= MSG_TRUNC;
}
err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied); err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
...@@ -1199,8 +1210,19 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr ...@@ -1199,8 +1210,19 @@ SCTP_STATIC int sctp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr
err = copied; err = copied;
/* FIXME: We need to support MSG_EOR correctly. */ /* If skb's length exceeds the user's buffer, update the skb and
* push it back to the receive_queue so that the next call to
* recvmsg() will return the remaining data. Don't set MSG_EOR.
* Otherwise, set MSG_EOR indicating the end of a message.
*/
if (skb_len > copied) {
sctp_skb_pull(skb, copied);
skb_queue_head(&sk->receive_queue, skb);
msg->msg_flags &= ~MSG_EOR;
goto out;
} else {
msg->msg_flags |= MSG_EOR; msg->msg_flags |= MSG_EOR;
}
out_free: out_free:
sctp_ulpevent_free(event); /* Free the skb. */ sctp_ulpevent_free(event); /* Free the skb. */
...@@ -1252,6 +1274,67 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval, ...@@ -1252,6 +1274,67 @@ static inline int sctp_setsockopt_autoclose(struct sock *sk, char *optval,
return 0; 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;
union sctp_addr *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 = (union sctp_addr *) &(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() /* API 6.2 setsockopt(), getsockopt()
* *
* Applications use setsockopt() and getsockopt() to set or retrieve * Applications use setsockopt() and getsockopt() to set or retrieve
...@@ -1342,6 +1425,11 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1342,6 +1425,11 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
retval = sctp_setsockopt_autoclose(sk, optval, optlen); retval = sctp_setsockopt_autoclose(sk, optval, optlen);
break; break;
case SCTP_SET_PEER_ADDR_PARAMS:
retval = sctp_setsockopt_set_peer_addr_params(sk, optval,
optlen);
break;
default: default:
retval = -ENOPROTOOPT; retval = -ENOPROTOOPT;
break; break;
...@@ -1354,11 +1442,107 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, ...@@ -1354,11 +1442,107 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname,
return retval; return retval;
} }
/* FIXME: Write comments. */ /* API 3.1.6 connect() - UDP Style Syntax
*
* An application may use the connect() call in the UDP model to initiate an
* association without sending data.
*
* The syntax is:
*
* ret = connect(int sd, const struct sockaddr *nam, socklen_t len);
*
* sd: the socket descriptor to have a new association added to.
*
* nam: the address structure (either struct sockaddr_in or struct
* sockaddr_in6 defined in RFC2553 [7]).
*
* len: the size of the address.
*/
SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr, SCTP_STATIC int sctp_connect(struct sock *sk, struct sockaddr *uaddr,
int addr_len) int addr_len)
{ {
return -EOPNOTSUPP; /* STUB */ sctp_opt_t *sp;
sctp_endpoint_t *ep;
sctp_association_t *asoc;
sctp_transport_t *transport;
union sctp_addr to;
sctp_scope_t scope;
long timeo;
int err = 0;
sctp_lock_sock(sk);
SCTP_DEBUG_PRINTK("%s - sk: %p, sockaddr: %p, addr_len: %d)\n",
__FUNCTION__, sk, uaddr, addr_len);
sp = sctp_sk(sk);
ep = sp->ep;
/* connect() cannot be done on a peeled-off socket. */
if (SCTP_SOCKET_UDP_HIGH_BANDWIDTH == sp->type) {
err = -EISCONN;
goto out_unlock;
}
err = sctp_verify_addr(sk, uaddr, addr_len);
if (err)
goto out_unlock;
memcpy(&to, uaddr, addr_len);
to.v4.sin_port = ntohs(to.v4.sin_port);
asoc = sctp_endpoint_lookup_assoc(ep, &to, &transport);
if (asoc) {
if (asoc->state >= SCTP_STATE_ESTABLISHED)
err = -EISCONN;
else
err = -EALREADY;
goto out_unlock;
}
/* If we could not find a matching association on the endpoint,
* make sure that there is no peeled-off association matching the
* peer address even on another socket.
*/
if (sctp_endpoint_is_peeled_off(ep, &to)) {
err = -EADDRNOTAVAIL;
goto out_unlock;
}
/* If a bind() or sctp_bindx() is not called prior to a connect()
* call, the system picks an ephemeral port and will choose an address
* set equivalent to binding with a wildcard address.
*/
if (!ep->base.bind_addr.port) {
if (sctp_autobind(sk)) {
err = -EAGAIN;
goto out_unlock;
}
}
scope = sctp_scope(&to);
asoc = sctp_association_new(ep, sk, scope, GFP_KERNEL);
if (!asoc) {
err = -ENOMEM;
goto out_unlock;
}
/* Prime the peer's transport structures. */
transport = sctp_assoc_add_peer(asoc, &to, GFP_KERNEL);
err = sctp_primitive_ASSOCIATE(asoc, NULL);
if (err < 0) {
sctp_association_free(asoc);
goto out_unlock;
}
timeo = sock_sndtimeo(sk, sk->socket->file->f_flags & O_NONBLOCK);
err = sctp_wait_for_connect(asoc, &timeo);
out_unlock:
sctp_release_sock(sk);
return err;
} }
/* FIXME: Write comments. */ /* FIXME: Write comments. */
...@@ -1503,28 +1687,26 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1503,28 +1687,26 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (len != sizeof(status)) { if (len != sizeof(status)) {
retval = -EINVAL; retval = -EINVAL;
goto out_nounlock; goto out;
} }
if (copy_from_user(&status, optval, sizeof(status))) { if (copy_from_user(&status, optval, sizeof(status))) {
retval = -EFAULT; retval = -EFAULT;
goto out_nounlock; goto out;
} }
sctp_lock_sock(sk);
associd = status.sstat_assoc_id; associd = status.sstat_assoc_id;
if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sctp_sk(sk)->type) && associd) { if ((SCTP_SOCKET_UDP_HIGH_BANDWIDTH != sctp_sk(sk)->type) && associd) {
assoc = sctp_id2assoc(sk, associd); assoc = sctp_id2assoc(sk, associd);
if (!assoc) { if (!assoc) {
retval = -EINVAL; retval = -EINVAL;
goto out_unlock; goto out;
} }
} else { } else {
ep = sctp_sk(sk)->ep; ep = sctp_sk(sk)->ep;
if (list_empty(&ep->asocs)) { if (list_empty(&ep->asocs)) {
retval = -EINVAL; retval = -EINVAL;
goto out_unlock; goto out;
} }
assoc = list_entry(ep->asocs.next, sctp_association_t, asocs); assoc = list_entry(ep->asocs.next, sctp_association_t, asocs);
...@@ -1542,8 +1724,8 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1542,8 +1724,8 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
status.sstat_fragmentation_point = assoc->frag_point; status.sstat_fragmentation_point = assoc->frag_point;
status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc); status.sstat_primary.spinfo_assoc_id = sctp_assoc2id(transport->asoc);
memcpy(&status.sstat_primary.spinfo_address, memcpy(&status.sstat_primary.spinfo_address,
&(transport->ipaddr), sizeof(sockaddr_storage_t)); &(transport->ipaddr), sizeof(union sctp_addr));
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_cwnd = transport->cwnd;
status.sstat_primary.spinfo_srtt = transport->srtt; status.sstat_primary.spinfo_srtt = transport->srtt;
status.sstat_primary.spinfo_rto = transport->rto; status.sstat_primary.spinfo_rto = transport->rto;
...@@ -1551,7 +1733,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1551,7 +1733,7 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (put_user(len, optlen)) { if (put_user(len, optlen)) {
retval = -EFAULT; retval = -EFAULT;
goto out_unlock; goto out;
} }
SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %p\n", SCTP_DEBUG_PRINTK("sctp_getsockopt_sctp_status(%d): %d %d %p\n",
...@@ -1560,13 +1742,10 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval, ...@@ -1560,13 +1742,10 @@ static int sctp_getsockopt_sctp_status(struct sock *sk, int len, char *optval,
if (copy_to_user(optval, &status, len)) { if (copy_to_user(optval, &status, len)) {
retval = -EFAULT; retval = -EFAULT;
goto out_unlock; goto out;
} }
out_unlock: out:
sctp_release_sock(sk);
out_nounlock:
return (retval); return (retval);
} }
...@@ -1684,25 +1863,23 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval ...@@ -1684,25 +1863,23 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
if (copy_from_user(&peeloff, optval, len)) if (copy_from_user(&peeloff, optval, len))
return -EFAULT; return -EFAULT;
sctp_lock_sock(sk);
assoc = sctp_id2assoc(sk, peeloff.associd); assoc = sctp_id2assoc(sk, peeloff.associd);
if (NULL == assoc) { if (NULL == assoc) {
retval = -EINVAL; retval = -EINVAL;
goto out_unlock; goto out;
} }
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc); SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p\n", __FUNCTION__, sk, assoc);
retval = sctp_do_peeloff(assoc, &newsock); retval = sctp_do_peeloff(assoc, &newsock);
if (retval < 0) if (retval < 0)
goto out_unlock; goto out;
/* Map the socket to an unused fd that can be returned to the user. */ /* Map the socket to an unused fd that can be returned to the user. */
retval = sock_map_fd(newsock); retval = sock_map_fd(newsock);
if (retval < 0) { if (retval < 0) {
sock_release(newsock); sock_release(newsock);
goto out_unlock; goto out;
} }
SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n", SCTP_DEBUG_PRINTK("%s: sk: %p assoc: %p newsk: %p sd: %d\n",
...@@ -1713,11 +1890,54 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval ...@@ -1713,11 +1890,54 @@ static inline int sctp_getsockopt_peeloff(struct sock *sk, int len, char *optval
if (copy_to_user(optval, &peeloff, len)) if (copy_to_user(optval, &peeloff, len))
retval = -EFAULT; retval = -EFAULT;
out_unlock: out:
sctp_release_sock(sk);
return retval; 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;
union sctp_addr *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 = (union sctp_addr *) &(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, SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen) char *optval, int *optlen)
{ {
...@@ -1748,6 +1968,8 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, ...@@ -1748,6 +1968,8 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
if (get_user(len, optlen)) if (get_user(len, optlen))
return -EFAULT; return -EFAULT;
sctp_lock_sock(sk);
switch (optname) { switch (optname) {
case SCTP_STATUS: case SCTP_STATUS:
retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen); retval = sctp_getsockopt_sctp_status(sk, len, optval, optlen);
...@@ -1770,11 +1992,17 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, ...@@ -1770,11 +1992,17 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
retval = sctp_getsockopt_peeloff(sk, len, optval, optlen); retval = sctp_getsockopt_peeloff(sk, len, optval, optlen);
break; break;
case SCTP_GET_PEER_ADDR_PARAMS:
retval = sctp_getsockopt_get_peer_addr_params(sk, len, optval,
optlen);
break;
default: default:
retval = -ENOPROTOOPT; retval = -ENOPROTOOPT;
break; break;
}; };
sctp_release_sock(sk);
return retval; return retval;
} }
...@@ -1880,7 +2108,7 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum) ...@@ -1880,7 +2108,7 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum)
* socket is going to be sk2. * socket is going to be sk2.
*/ */
int sk_reuse = sk->reuse; int sk_reuse = sk->reuse;
sockaddr_storage_t tmpaddr; union sctp_addr tmpaddr;
struct sock *sk2 = pp->sk; struct sock *sk2 = pp->sk;
SCTP_DEBUG_PRINTK("sctp_get_port() found a " SCTP_DEBUG_PRINTK("sctp_get_port() found a "
...@@ -1923,13 +2151,14 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum) ...@@ -1923,13 +2151,14 @@ static long sctp_get_port_local(struct sock *sk, unsigned short snum)
sctp_endpoint_t *ep2; sctp_endpoint_t *ep2;
ep2 = sctp_sk(sk2)->ep; ep2 = sctp_sk(sk2)->ep;
if (!sk_reuse || !sk2->reuse) { if (sk_reuse && sk2->reuse)
if (sctp_bind_addr_has_addr( continue;
&ep2->base.bind_addr, &tmpaddr)) {
if (sctp_bind_addr_match(&ep2->base.bind_addr,
&tmpaddr,
sctp_sk(sk)))
goto found; goto found;
} }
}
}
found: found:
/* If we found a conflict, fail. */ /* If we found a conflict, fail. */
...@@ -2183,34 +2412,17 @@ void sctp_put_port(struct sock *sk) ...@@ -2183,34 +2412,17 @@ void sctp_put_port(struct sock *sk)
*/ */
static int sctp_autobind(struct sock *sk) static int sctp_autobind(struct sock *sk)
{ {
sockaddr_storage_t autoaddr; union sctp_addr autoaddr;
int addr_len = 0; struct sctp_func *af;
unsigned short port;
memset(&autoaddr, 0, sizeof(sockaddr_storage_t));
switch (sk->family) {
case PF_INET:
autoaddr.v4.sin_family = AF_INET;
autoaddr.v4.sin_addr.s_addr = INADDR_ANY;
autoaddr.v4.sin_port = htons(inet_sk(sk)->num);
addr_len = sizeof(struct sockaddr_in);
break;
case PF_INET6: /* Initialize a local sockaddr structure to INADDR_ANY. */
SCTP_V6( af = sctp_sk(sk)->pf->af;
/* FIXME: Write me for v6! */
BUG();
autoaddr.v6.sin6_family = AF_INET6;
autoaddr.v6.sin6_port = htons(inet_sk(sk)->num);
addr_len = sizeof(struct sockaddr_in6);
);
break;
default: /* This should not happen. */ port = htons(inet_sk(sk)->num);
break; af->inaddr_any(&autoaddr, port);
};
return sctp_do_bind(sk, &autoaddr, addr_len); return sctp_do_bind(sk, &autoaddr, af->sockaddr_len);
} }
/* Parse out IPPROTO_SCTP CMSG headers. Perform only minimal validation. /* Parse out IPPROTO_SCTP CMSG headers. Perform only minimal validation.
...@@ -2327,8 +2539,8 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg, ...@@ -2327,8 +2539,8 @@ SCTP_STATIC int sctp_msghdr_parse(const struct msghdr *msg,
/* Setup sk->rcv_saddr before calling get_port(). */ /* Setup sk->rcv_saddr before calling get_port(). */
static inline void sctp_sk_addr_set(struct sock *sk, static inline void sctp_sk_addr_set(struct sock *sk,
const sockaddr_storage_t *newaddr, const union sctp_addr *newaddr,
sockaddr_storage_t *saveaddr) union sctp_addr *saveaddr)
{ {
struct inet_opt *inet = inet_sk(sk); struct inet_opt *inet = inet_sk(sk);
...@@ -2355,7 +2567,7 @@ static inline void sctp_sk_addr_set(struct sock *sk, ...@@ -2355,7 +2567,7 @@ static inline void sctp_sk_addr_set(struct sock *sk,
} }
/* Restore sk->rcv_saddr after failing get_port(). */ /* Restore sk->rcv_saddr after failing get_port(). */
static inline void sctp_sk_addr_restore(struct sock *sk, const sockaddr_storage_t *addr) static inline void sctp_sk_addr_restore(struct sock *sk, const union sctp_addr *addr)
{ {
struct inet_opt *inet = inet_sk(sk); struct inet_opt *inet = inet_sk(sk);
...@@ -2498,35 +2710,30 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int no ...@@ -2498,35 +2710,30 @@ static struct sk_buff *sctp_skb_recv_datagram(struct sock *sk, int flags, int no
return NULL; return NULL;
} }
static inline int sctp_sendmsg_verify_name(struct sock *sk, struct msghdr *msg) /* Verify that this is a valid address. */
static int sctp_verify_addr(struct sock *sk, struct sockaddr *addr, int len)
{ {
sockaddr_storage_t *sa; struct sctp_func *af;
if (msg->msg_namelen < sizeof (struct sockaddr)) /* Check minimum size. */
if (len < sizeof (struct sockaddr))
return -EINVAL; return -EINVAL;
sa = (sockaddr_storage_t *) msg->msg_name; /* Do we support this address family in general? */
switch (sa->sa.sa_family) { af = sctp_get_af_specific(addr->sa_family);
case AF_INET: if (!af)
if (msg->msg_namelen < sizeof(struct sockaddr_in))
return -EINVAL; return -EINVAL;
break;
case AF_INET6: /* Does this PF support this AF? */
if (PF_INET == sk->family) if (!sctp_sk(sk)->pf->af_supported(addr->sa_family))
return -EINVAL; return -EINVAL;
SCTP_V6(
if (msg->msg_namelen < sizeof(struct sockaddr_in6))
return -EINVAL;
break;
);
default: /* Verify the minimum for this AF sockaddr. */
if (len < af->sockaddr_len)
return -EINVAL; return -EINVAL;
};
/* Disallow any illegal addresses to be used as destinations. */ /* Is this a valid SCTP address? */
if (!sctp_addr_is_valid(sa)) if (!af->addr_valid((union sctp_addr *)addr))
return -EINVAL; return -EINVAL;
return 0; return 0;
...@@ -2710,6 +2917,70 @@ static int sctp_writeable(struct sock *sk) ...@@ -2710,6 +2917,70 @@ static int sctp_writeable(struct sock *sk)
return amt; return amt;
} }
/* Wait for an association to go into ESTABLISHED state. If timeout is 0,
* returns immediately with EINPROGRESS.
*/
static int sctp_wait_for_connect(sctp_association_t *asoc, long *timeo_p)
{
struct sock *sk = asoc->base.sk;
int err = 0;
long current_timeo = *timeo_p;
DECLARE_WAITQUEUE(wait, current);
SCTP_DEBUG_PRINTK("%s: asoc=%p, timeo=%ld\n", __FUNCTION__, asoc,
(long)(*timeo_p));
add_wait_queue_exclusive(&asoc->wait, &wait);
/* Increment the association's refcnt. */
sctp_association_hold(asoc);
for (;;) {
__set_current_state(TASK_INTERRUPTIBLE);
if (!*timeo_p)
goto do_nonblock;
if (sk->err || asoc->state >= SCTP_STATE_SHUTDOWN_PENDING ||
asoc->base.dead)
goto do_error;
if (signal_pending(current))
goto do_interrupted;
if (asoc->state == SCTP_STATE_ESTABLISHED)
break;
/* Let another process have a go. Since we are going
* to sleep anyway.
*/
sctp_release_sock(sk);
current_timeo = schedule_timeout(current_timeo);
sctp_lock_sock(sk);
*timeo_p = current_timeo;
}
out:
remove_wait_queue(&asoc->wait, &wait);
/* Release the association's refcnt. */
sctp_association_put(asoc);
__set_current_state(TASK_RUNNING);
return err;
do_error:
err = -ECONNABORTED;
goto out;
do_interrupted:
err = sock_intr_errno(*timeo_p);
goto out;
do_nonblock:
err = -EINPROGRESS;
goto out;
}
/* This proto struct describes the ULP interface for SCTP. */ /* This proto struct describes the ULP interface for SCTP. */
struct proto sctp_prot = { struct proto sctp_prot = {
.name = "SCTP", .name = "SCTP",
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* This module provides the abstraction for an SCTP tranport representing * This module provides the abstraction for an SCTP tranport representing
* a remote transport address. For local transport addresses, we just use * a remote transport address. For local transport addresses, we just use
* sockaddr_storage_t. * union sctp_addr.
* *
* The SCTP reference implementation is free software; * The SCTP reference implementation is free software;
* you can redistribute it and/or modify it under the terms of * you can redistribute it and/or modify it under the terms of
...@@ -53,7 +53,7 @@ ...@@ -53,7 +53,7 @@
/* 1st Level Abstractions. */ /* 1st Level Abstractions. */
/* Allocate and initialize a new transport. */ /* Allocate and initialize a new transport. */
sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *addr, int priority) sctp_transport_t *sctp_transport_new(const union sctp_addr *addr, int priority)
{ {
sctp_transport_t *transport; sctp_transport_t *transport;
...@@ -78,14 +78,14 @@ sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *addr, int priorit ...@@ -78,14 +78,14 @@ sctp_transport_t *sctp_transport_new(const sockaddr_storage_t *addr, int priorit
/* Intialize a new transport from provided memory. */ /* Intialize a new transport from provided memory. */
sctp_transport_t *sctp_transport_init(sctp_transport_t *peer, sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
const sockaddr_storage_t *addr, const union sctp_addr *addr,
int priority) int priority)
{ {
sctp_protocol_t *proto = sctp_get_protocol(); sctp_protocol_t *proto = sctp_get_protocol();
/* Copy in the address. */ /* Copy in the address. */
peer->ipaddr = *addr; peer->ipaddr = *addr;
peer->af_specific = sctp_get_af_specific(addr); peer->af_specific = sctp_get_af_specific(addr->sa.sa_family);
peer->asoc = NULL; peer->asoc = NULL;
/* From 6.3.1 RTO Calculation: /* From 6.3.1 RTO Calculation:
...@@ -104,8 +104,8 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer, ...@@ -104,8 +104,8 @@ sctp_transport_t *sctp_transport_init(sctp_transport_t *peer,
peer->last_time_used = jiffies; peer->last_time_used = jiffies;
peer->last_time_ecne_reduced = jiffies; peer->last_time_ecne_reduced = jiffies;
peer->state.active = 1; peer->active = 1;
peer->state.hb_allowed = 0; peer->hb_allowed = 0;
/* Initialize the default path max_retrans. */ /* Initialize the default path max_retrans. */
peer->max_retrans = proto->max_retrans_path; peer->max_retrans = proto->max_retrans_path;
...@@ -203,17 +203,18 @@ void sctp_transport_set_owner(sctp_transport_t *transport, ...@@ -203,17 +203,18 @@ void sctp_transport_set_owner(sctp_transport_t *transport,
/* Caches the dst entry for a transport's destination address and an optional /* Caches the dst entry for a transport's destination address and an optional
* souce address. * souce address.
*/ */
void sctp_transport_route(sctp_transport_t *transport, void sctp_transport_route(sctp_transport_t *transport, union sctp_addr *saddr,
sockaddr_storage_t *saddr) struct sctp_opt *opt)
{ {
sctp_association_t *asoc = transport->asoc; sctp_association_t *asoc = transport->asoc;
sctp_func_t *af = transport->af_specific; struct sctp_func *af = transport->af_specific;
sockaddr_storage_t *daddr = &transport->ipaddr; union sctp_addr *daddr = &transport->ipaddr;
sctp_bind_addr_t *bp; sctp_bind_addr_t *bp;
rwlock_t *addr_lock; rwlock_t *addr_lock;
struct sockaddr_storage_list *laddr; struct sockaddr_storage_list *laddr;
struct list_head *pos; struct list_head *pos;
struct dst_entry *dst; struct dst_entry *dst;
union sctp_addr dst_saddr;
dst = af->get_dst(daddr, saddr); dst = af->get_dst(daddr, saddr);
...@@ -239,7 +240,8 @@ void sctp_transport_route(sctp_transport_t *transport, ...@@ -239,7 +240,8 @@ void sctp_transport_route(sctp_transport_t *transport,
list_for_each(pos, &bp->address_list) { list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sockaddr_storage_list, laddr = list_entry(pos, struct sockaddr_storage_list,
list); list);
if (af->cmp_saddr(dst, &laddr->a)) af->dst_saddr(&dst_saddr, dst);
if (opt->pf->cmp_addr(&dst_saddr, &laddr->a, opt))
goto out_unlock; goto out_unlock;
} }
sctp_read_unlock(addr_lock); sctp_read_unlock(addr_lock);
......
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