diff options
author | David S. Miller <davem@nuts.davemloft.net> | 2005-01-09 19:45:14 -0800 |
---|---|---|
committer | David S. Miller <davem@nuts.davemloft.net> | 2005-01-09 19:45:14 -0800 |
commit | cabbb4ded926b76d0e6a71ee7c901532b829b40a (patch) | |
tree | fc799f5900ec238d0b1e153d4830a5485ca4edd6 /net | |
parent | c7e670c0a1fb8ff5552e461437b4944af289bd90 (diff) | |
parent | a271fa8bb7ea2eed7d6199d94acb479862fb0d91 (diff) | |
download | history-cabbb4ded926b76d0e6a71ee7c901532b829b40a.tar.gz |
Merge http://linux-lksctp.bkbits.net/lksctp-2.5.work
into nuts.davemloft.net:/disk1/BK/net-2.6
Diffstat (limited to 'net')
-rw-r--r-- | net/sctp/associola.c | 83 | ||||
-rw-r--r-- | net/sctp/bind_addr.c | 17 | ||||
-rw-r--r-- | net/sctp/chunk.c | 8 | ||||
-rw-r--r-- | net/sctp/command.c | 23 | ||||
-rw-r--r-- | net/sctp/debug.c | 20 | ||||
-rw-r--r-- | net/sctp/endpointola.c | 56 | ||||
-rw-r--r-- | net/sctp/input.c | 102 | ||||
-rw-r--r-- | net/sctp/inqueue.c | 39 | ||||
-rw-r--r-- | net/sctp/ipv6.c | 26 | ||||
-rw-r--r-- | net/sctp/objcnt.c | 2 | ||||
-rw-r--r-- | net/sctp/outqueue.c | 15 | ||||
-rw-r--r-- | net/sctp/proc.c | 2 | ||||
-rw-r--r-- | net/sctp/protocol.c | 34 | ||||
-rw-r--r-- | net/sctp/sm_make_chunk.c | 159 | ||||
-rw-r--r-- | net/sctp/sm_sideeffect.c | 68 | ||||
-rw-r--r-- | net/sctp/sm_statefuns.c | 475 | ||||
-rw-r--r-- | net/sctp/sm_statetable.c | 50 | ||||
-rw-r--r-- | net/sctp/socket.c | 142 | ||||
-rw-r--r-- | net/sctp/ssnmap.c | 7 | ||||
-rw-r--r-- | net/sctp/transport.c | 67 | ||||
-rw-r--r-- | net/sctp/tsnmap.c | 39 | ||||
-rw-r--r-- | net/sctp/ulpevent.c | 17 | ||||
-rw-r--r-- | net/sctp/ulpqueue.c | 21 |
23 files changed, 1000 insertions, 472 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 498a71367cebbc..fda3bc435c7c7a 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -66,33 +66,8 @@ static void sctp_assoc_bh_rcv(struct sctp_association *asoc); /* 1st Level Abstractions. */ -/* Allocate and initialize a new association */ -struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep, - const struct sock *sk, - sctp_scope_t scope, int gfp) -{ - struct sctp_association *asoc; - - asoc = t_new(struct sctp_association, gfp); - if (!asoc) - goto fail; - - if (!sctp_association_init(asoc, ep, sk, scope, gfp)) - goto fail_init; - - asoc->base.malloced = 1; - SCTP_DBG_OBJCNT_INC(assoc); - - return asoc; - -fail_init: - kfree(asoc); -fail: - return NULL; -} - /* Initialize a new association from provided memory. */ -struct sctp_association *sctp_association_init(struct sctp_association *asoc, +static struct sctp_association *sctp_association_init(struct sctp_association *asoc, const struct sctp_endpoint *ep, const struct sock *sk, sctp_scope_t scope, @@ -204,6 +179,7 @@ struct sctp_association *sctp_association_init(struct sctp_association *asoc, asoc->c.peer_vtag = 0; asoc->c.my_ttag = 0; asoc->c.peer_ttag = 0; + asoc->c.my_port = ep->base.bind_addr.port; asoc->c.initial_tsn = sctp_generate_tsn(ep); @@ -296,6 +272,31 @@ fail_init: return NULL; } +/* Allocate and initialize a new association */ +struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep, + const struct sock *sk, + sctp_scope_t scope, int gfp) +{ + struct sctp_association *asoc; + + asoc = t_new(struct sctp_association, gfp); + if (!asoc) + goto fail; + + if (!sctp_association_init(asoc, ep, sk, scope, gfp)) + goto fail_init; + + asoc->base.malloced = 1; + SCTP_DBG_OBJCNT_INC(assoc); + + return asoc; + +fail_init: + kfree(asoc); +fail: + return NULL; +} + /* Free this association if possible. There may still be users, so * the actual deallocation may be delayed. */ @@ -500,7 +501,6 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, peer->partial_bytes_acked = 0; peer->flight_size = 0; - peer->error_threshold = peer->max_retrans; /* By default, enable heartbeat for peer address. */ peer->hb_allowed = 1; @@ -511,7 +511,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc, peer->hb_interval = msecs_to_jiffies(sp->paddrparam.spp_hbinterval); /* Set the path max_retrans. */ - peer->max_retrans = asoc->max_retrans; + peer->max_retrans = sp->paddrparam.spp_pathmaxrxt; /* Set the transport's RTO.initial value */ peer->rto = asoc->rto_initial; @@ -714,18 +714,6 @@ __u32 sctp_association_get_next_tsn(struct sctp_association *asoc) return retval; } -/* Allocate 'num' TSNs by incrementing the association's TSN by num. */ -__u32 sctp_association_get_tsn_block(struct sctp_association *asoc, int num) -{ - __u32 retval = asoc->next_tsn; - - asoc->next_tsn += num; - asoc->unack_data += num; - - return retval; -} - - /* Compare two addresses to see if they match. Wildcard addresses * only match themselves. */ @@ -760,14 +748,6 @@ struct sctp_chunk *sctp_get_ecne_prepend(struct sctp_association *asoc) return chunk; } -/* Use this function for the packet prepend callback when no ECNE - * packet is desired (e.g. some packets don't like to be bundled). - */ -struct sctp_chunk *sctp_get_no_prepend(struct sctp_association *asoc) -{ - return NULL; -} - /* * Find which transport this TSN was sent on. */ @@ -861,7 +841,8 @@ static void sctp_assoc_bh_rcv(struct sctp_association *asoc) struct sctp_chunk *chunk; struct sock *sk; struct sctp_inq *inqueue; - int state, subtype; + int state; + sctp_subtype_t subtype; int error = 0; /* The association should be held so we should be safe. */ @@ -872,7 +853,7 @@ static void sctp_assoc_bh_rcv(struct sctp_association *asoc) sctp_association_hold(asoc); while (NULL != (chunk = sctp_inq_pop(inqueue))) { state = asoc->state; - subtype = chunk->chunk_hdr->type; + subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type); /* Remember where the last DATA chunk came from so we * know where to send the SACK. @@ -886,7 +867,7 @@ static void sctp_assoc_bh_rcv(struct sctp_association *asoc) chunk->transport->last_time_heard = jiffies; /* Run through the state machine. */ - error = sctp_do_sm(SCTP_EVENT_T_CHUNK, SCTP_ST_CHUNK(subtype), + error = sctp_do_sm(SCTP_EVENT_T_CHUNK, subtype, state, ep, asoc, chunk, GFP_ATOMIC); /* Check to see if the association is freed in response to diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index b484b4afd7301e..ec2d7450fb18a9 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -104,23 +104,6 @@ out: return error; } -/* Create a new SCTP_bind_addr from nothing. */ -struct sctp_bind_addr *sctp_bind_addr_new(int gfp) -{ - struct sctp_bind_addr *retval; - - retval = t_new(struct sctp_bind_addr, gfp); - if (!retval) - goto nomem; - - sctp_bind_addr_init(retval, 0); - retval->malloced = 1; - SCTP_DBG_OBJCNT_INC(bind_addr); - -nomem: - return retval; -} - /* Initialize the SCTP_bind_addr structure for either an endpoint or * an association. */ diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index cdcd427e4bac68..6e500f8ca47fb4 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -51,7 +51,7 @@ */ /* Initialize datamsg from memory. */ -void sctp_datamsg_init(struct sctp_datamsg *msg) +static void sctp_datamsg_init(struct sctp_datamsg *msg) { atomic_set(&msg->refcnt, 1); msg->send_failed = 0; @@ -62,7 +62,7 @@ void sctp_datamsg_init(struct sctp_datamsg *msg) } /* Allocate and initialize datamsg. */ -struct sctp_datamsg *sctp_datamsg_new(int gfp) +SCTP_STATIC struct sctp_datamsg *sctp_datamsg_new(int gfp) { struct sctp_datamsg *msg; msg = kmalloc(sizeof(struct sctp_datamsg), gfp); @@ -124,7 +124,7 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg) } /* Hold a reference. */ -void sctp_datamsg_hold(struct sctp_datamsg *msg) +static void sctp_datamsg_hold(struct sctp_datamsg *msg) { atomic_inc(&msg->refcnt); } @@ -151,7 +151,7 @@ void sctp_datamsg_track(struct sctp_chunk *chunk) } /* Assign a chunk to this datamsg. */ -void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chunk) +static void sctp_datamsg_assign(struct sctp_datamsg *msg, struct sctp_chunk *chunk) { sctp_datamsg_hold(msg); chunk->msg = msg; diff --git a/net/sctp/command.c b/net/sctp/command.c index 637fadee95a866..3ff804757f4a59 100644 --- a/net/sctp/command.c +++ b/net/sctp/command.c @@ -42,17 +42,6 @@ #include <net/sctp/sctp.h> #include <net/sctp/sm.h> -/* Create a new sctp_command_sequence. */ -sctp_cmd_seq_t *sctp_new_cmd_seq(int gfp) -{ - sctp_cmd_seq_t *retval = t_new(sctp_cmd_seq_t, gfp); - - if (retval) - sctp_init_cmd_seq(retval); - - return retval; -} - /* Initialize a block of memory as a command sequence. */ int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) { @@ -77,13 +66,6 @@ fail: return 0; } -/* Rewind an sctp_cmd_seq_t to iterate from the start. */ -int sctp_rewind_sequence(sctp_cmd_seq_t *seq) -{ - seq->next_cmd = 0; - return 1; /* We always succeed. */ -} - /* Return the next command structure in a sctp_cmd_seq. * Returns NULL at the end of the sequence. */ @@ -97,8 +79,3 @@ sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq) return retval; } -/* Dispose of a command sequence. */ -void sctp_free_cmd_seq(sctp_cmd_seq_t *seq) -{ - kfree(seq); -} diff --git a/net/sctp/debug.c b/net/sctp/debug.c index bb1301a9defac3..aa8340373af7f1 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -98,23 +98,6 @@ const char *sctp_cname(const sctp_subtype_t cid) return "unknown chunk"; } -/* These are printable form of variable-length parameters. */ -const char *sctp_param_tbl[SCTP_PARAM_ECN_CAPABLE + 1] = { - "", - "PARAM_HEARTBEAT_INFO", - "", - "", - "", - "PARAM_IPV4_ADDRESS", - "PARAM_IPV6_ADDRESS", - "PARAM_STATE_COOKIE", - "PARAM_UNRECOGNIZED_PARAMETERS", - "PARAM_COOKIE_PRESERVATIVE", - "", - "PARAM_HOST_NAME_ADDRESS", - "PARAM_SUPPORTED_ADDRESS_TYPES", -}; - /* These are printable forms of the states. */ const char *sctp_state_tbl[SCTP_STATE_NUM_STATES] = { "STATE_EMPTY", @@ -171,6 +154,7 @@ const char *sctp_pname(const sctp_subtype_t id) static const char *sctp_other_tbl[] = { "NO_PENDING_TSN", + "ICMP_PROTO_UNREACH", }; /* Lookup "other" debug name. */ @@ -178,7 +162,7 @@ const char *sctp_oname(const sctp_subtype_t id) { if (id.other < 0) return "illegal 'other' event"; - if (id.other < SCTP_EVENT_OTHER_MAX) + if (id.other <= SCTP_EVENT_OTHER_MAX) return sctp_other_tbl[id.other]; return "unknown 'other' event"; } diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index edc4291a67f4ac..cfeb033841dc06 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -63,34 +63,11 @@ /* Forward declarations for internal helpers. */ static void sctp_endpoint_bh_rcv(struct sctp_endpoint *ep); -/* Create a sctp_endpoint with all that boring stuff initialized. - * Returns NULL if there isn't enough memory. - */ -struct sctp_endpoint *sctp_endpoint_new(struct sock *sk, int gfp) -{ - struct sctp_endpoint *ep; - - /* Build a local endpoint. */ - ep = t_new(struct sctp_endpoint, gfp); - if (!ep) - goto fail; - if (!sctp_endpoint_init(ep, sk, gfp)) - goto fail_init; - ep->base.malloced = 1; - SCTP_DBG_OBJCNT_INC(ep); - return ep; - -fail_init: - kfree(ep); -fail: - return NULL; -} - /* * Initialize the base fields of the endpoint structure. */ -struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, - struct sock *sk, int gfp) +static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, + struct sock *sk, int gfp) { struct sctp_opt *sp = sctp_sk(sk); memset(ep, 0, sizeof(struct sctp_endpoint)); @@ -160,6 +137,29 @@ struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, return ep; } +/* Create a sctp_endpoint with all that boring stuff initialized. + * Returns NULL if there isn't enough memory. + */ +struct sctp_endpoint *sctp_endpoint_new(struct sock *sk, int gfp) +{ + struct sctp_endpoint *ep; + + /* Build a local endpoint. */ + ep = t_new(struct sctp_endpoint, gfp); + if (!ep) + goto fail; + if (!sctp_endpoint_init(ep, sk, gfp)) + goto fail_init; + ep->base.malloced = 1; + SCTP_DBG_OBJCNT_INC(ep); + return ep; + +fail_init: + kfree(ep); +fail: + return NULL; +} + /* Add an association to an endpoint. */ void sctp_endpoint_add_asoc(struct sctp_endpoint *ep, struct sctp_association *asoc) @@ -184,7 +184,7 @@ void sctp_endpoint_free(struct sctp_endpoint *ep) } /* Final destructor for endpoint. */ -void sctp_endpoint_destroy(struct sctp_endpoint *ep) +static void sctp_endpoint_destroy(struct sctp_endpoint *ep) { SCTP_ASSERT(ep->base.dead, "Endpoint is not dead", return); @@ -257,7 +257,7 @@ out: * We do a linear search of the associations for this endpoint. * We return the matching transport address too. */ -struct sctp_association *__sctp_endpoint_lookup_assoc( +static struct sctp_association *__sctp_endpoint_lookup_assoc( const struct sctp_endpoint *ep, const union sctp_addr *paddr, struct sctp_transport **transport) @@ -345,7 +345,7 @@ static void sctp_endpoint_bh_rcv(struct sctp_endpoint *ep) sk = ep->base.sk; while (NULL != (chunk = sctp_inq_pop(inqueue))) { - subtype.chunk = chunk->chunk_hdr->type; + subtype = SCTP_ST_CHUNK(chunk->chunk_hdr->type); /* We might have grown an association since last we * looked, so try again. diff --git a/net/sctp/input.c b/net/sctp/input.c index cab833371a54b0..688e714f909ffa 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -63,11 +63,15 @@ /* Forward declarations for internal helpers. */ static int sctp_rcv_ootb(struct sk_buff *); -struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, +static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, const union sctp_addr *laddr, const union sctp_addr *paddr, struct sctp_transport **transportp); -struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr); +static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr); +static struct sctp_association *__sctp_lookup_association( + const union sctp_addr *local, + const union sctp_addr *peer, + struct sctp_transport **pt); /* Calculate the SCTP checksum of an SCTP packet. */ @@ -130,6 +134,10 @@ int sctp_rcv(struct sk_buff *skb) skb_pull(skb, sizeof(struct sctphdr)); + /* Make sure we at least have chunk headers worth of data left. */ + if (skb->len < sizeof(struct sctp_chunkhdr)) + goto discard_it; + family = ipver2af(skb->nh.iph->version); af = sctp_get_af_specific(family); if (unlikely(!af)) @@ -284,6 +292,31 @@ void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc, } } +/* + * SCTP Implementer's Guide, 2.37 ICMP handling procedures + * + * ICMP8) If the ICMP code is a "Unrecognized next header type encountered" + * or a "Protocol Unreachable" treat this message as an abort + * with the T bit set. + * + * This function sends an event to the state machine, which will abort the + * association. + * + */ +void sctp_icmp_proto_unreachable(struct sock *sk, + struct sctp_endpoint *ep, + struct sctp_association *asoc, + struct sctp_transport *t) +{ + SCTP_DEBUG_PRINTK("%s\n", __FUNCTION__); + + sctp_do_sm(SCTP_EVENT_T_OTHER, + SCTP_ST_OTHER(SCTP_EVENT_ICMP_PROTO_UNREACH), + asoc->state, asoc->ep, asoc, NULL, + GFP_ATOMIC); + +} + /* Common lookup code for icmp/icmpv6 error handler. */ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, struct sctphdr *sctphdr, @@ -326,11 +359,12 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, } if (asoc) { + sk = asoc->base.sk; + if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) { ICMP_INC_STATS_BH(ICMP_MIB_INERRORS); goto out; } - sk = asoc->base.sk; } else sk = ep->base.sk; @@ -432,7 +466,13 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) sctp_icmp_frag_needed(sk, asoc, transport, info); goto out_unlock; } - + else { + if (ICMP_PROT_UNREACH == code) { + sctp_icmp_proto_unreachable(sk, ep, asoc, + transport); + goto out_unlock; + } + } err = icmp_err_convert[code].errno; break; case ICMP_TIME_EXCEEDED: @@ -479,10 +519,10 @@ int sctp_rcv_ootb(struct sk_buff *skb) sctp_errhdr_t *err; ch = (sctp_chunkhdr_t *) skb->data; + ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length)); /* Scan through all the chunks in the packet. */ - do { - ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length)); + while (ch_end > (__u8 *)ch && ch_end < skb->tail) { /* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the * receiver MUST silently discard the OOTB packet and take no @@ -513,7 +553,8 @@ int sctp_rcv_ootb(struct sk_buff *skb) } ch = (sctp_chunkhdr_t *) ch_end; - } while (ch_end < skb->tail); + ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length)); + } return 0; @@ -522,7 +563,7 @@ discard: } /* Insert endpoint into the hash table. */ -void __sctp_hash_endpoint(struct sctp_endpoint *ep) +static void __sctp_hash_endpoint(struct sctp_endpoint *ep) { struct sctp_ep_common **epp; struct sctp_ep_common *epb; @@ -552,7 +593,7 @@ void sctp_hash_endpoint(struct sctp_endpoint *ep) } /* Remove endpoint from the hash table. */ -void __sctp_unhash_endpoint(struct sctp_endpoint *ep) +static void __sctp_unhash_endpoint(struct sctp_endpoint *ep) { struct sctp_hashbucket *head; struct sctp_ep_common *epb; @@ -584,7 +625,7 @@ void sctp_unhash_endpoint(struct sctp_endpoint *ep) } /* Look up an endpoint. */ -struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr) +static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr) { struct sctp_hashbucket *head; struct sctp_ep_common *epb; @@ -610,16 +651,8 @@ hit: return ep; } -/* Add an association to the hash. Local BH-safe. */ -void sctp_hash_established(struct sctp_association *asoc) -{ - sctp_local_bh_disable(); - __sctp_hash_established(asoc); - sctp_local_bh_enable(); -} - /* Insert association into the hash table. */ -void __sctp_hash_established(struct sctp_association *asoc) +static void __sctp_hash_established(struct sctp_association *asoc) { struct sctp_ep_common **epp; struct sctp_ep_common *epb; @@ -642,16 +675,16 @@ void __sctp_hash_established(struct sctp_association *asoc) sctp_write_unlock(&head->lock); } -/* Remove association from the hash table. Local BH-safe. */ -void sctp_unhash_established(struct sctp_association *asoc) +/* Add an association to the hash. Local BH-safe. */ +void sctp_hash_established(struct sctp_association *asoc) { sctp_local_bh_disable(); - __sctp_unhash_established(asoc); + __sctp_hash_established(asoc); sctp_local_bh_enable(); } /* Remove association from the hash table. */ -void __sctp_unhash_established(struct sctp_association *asoc) +static void __sctp_unhash_established(struct sctp_association *asoc) { struct sctp_hashbucket *head; struct sctp_ep_common *epb; @@ -675,8 +708,16 @@ void __sctp_unhash_established(struct sctp_association *asoc) sctp_write_unlock(&head->lock); } +/* Remove association from the hash table. Local BH-safe. */ +void sctp_unhash_established(struct sctp_association *asoc) +{ + sctp_local_bh_disable(); + __sctp_unhash_established(asoc); + sctp_local_bh_enable(); +} + /* Look up an association. */ -struct sctp_association *__sctp_lookup_association( +static struct sctp_association *__sctp_lookup_association( const union sctp_addr *local, const union sctp_addr *peer, struct sctp_transport **pt) @@ -713,8 +754,9 @@ hit: } /* Look up an association. BH-safe. */ +SCTP_STATIC struct sctp_association *sctp_lookup_association(const union sctp_addr *laddr, - const union sctp_addr *paddr, + const union sctp_addr *paddr, struct sctp_transport **transportp) { struct sctp_association *asoc; @@ -784,6 +826,14 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, return NULL; } + /* The code below will attempt to walk the chunk and extract + * parameter information. Before we do that, we need to verify + * that the chunk length doesn't cause overflow. Otherwise, we'll + * walk off the end. + */ + if (WORD_ROUND(ntohs(ch->length)) > skb->len) + return NULL; + /* * This code will NOT touch anything inside the chunk--it is * strictly READ-ONLY. @@ -821,7 +871,7 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, } /* Lookup an association for an inbound skb. */ -struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, +static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, const union sctp_addr *paddr, const union sctp_addr *laddr, struct sctp_transport **transportp) diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c index 2e83405b8be7c6..cedf4351556cc0 100644 --- a/net/sctp/inqueue.c +++ b/net/sctp/inqueue.c @@ -59,19 +59,6 @@ void sctp_inq_init(struct sctp_inq *queue) queue->malloced = 0; } -/* Create an initialized sctp_inq. */ -struct sctp_inq *sctp_inq_new(void) -{ - struct sctp_inq *retval; - - retval = t_new(struct sctp_inq, GFP_ATOMIC); - if (retval) { - sctp_inq_init(retval); - retval->malloced = 1; - } - return retval; -} - /* Release the memory associated with an SCTP inqueue. */ void sctp_inq_free(struct sctp_inq *queue) { @@ -157,14 +144,36 @@ struct sctp_chunk *sctp_inq_pop(struct sctp_inq *queue) } chunk->chunk_hdr = ch; - chunk->chunk_end = ((__u8 *) ch) - + WORD_ROUND(ntohs(ch->length)); + chunk->chunk_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); + /* In the unlikely case of an IP reassembly, the skb could be + * non-linear. If so, update chunk_end so that it doesn't go past + * the skb->tail. + */ + if (unlikely(skb_is_nonlinear(chunk->skb))) { + if (chunk->chunk_end > chunk->skb->tail) + chunk->chunk_end = chunk->skb->tail; + } skb_pull(chunk->skb, sizeof(sctp_chunkhdr_t)); chunk->subh.v = NULL; /* Subheader is no longer valid. */ if (chunk->chunk_end < chunk->skb->tail) { /* This is not a singleton */ chunk->singleton = 0; + } else if (chunk->chunk_end > chunk->skb->tail) { + /* RFC 2960, Section 6.10 Bundling + * + * Partial chunks MUST NOT be placed in an SCTP packet. + * If the receiver detects a partial chunk, it MUST drop + * the chunk. + * + * Since the end of the chunk is past the end of our buffer + * (which contains the whole packet, we can freely discard + * the whole packet. + */ + sctp_chunk_free(chunk); + chunk = queue->in_progress = NULL; + + return NULL; } else { /* We are at the end of the packet, so mark the chunk * in case we need to send a SACK. diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index e142681824ca88..f23de6bd5b52fa 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -84,8 +84,8 @@ static struct notifier_block sctp_inet6addr_notifier = { }; /* ICMP error handler. */ -void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, - int type, int code, int offset, __u32 info) +SCTP_STATIC void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) { struct inet6_dev *idev; struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; @@ -122,6 +122,12 @@ void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, case ICMPV6_PKT_TOOBIG: sctp_icmp_frag_needed(sk, asoc, transport, ntohl(info)); goto out_unlock; + case ICMPV6_PARAMPROB: + if (ICMPV6_UNK_NEXTHDR == code) { + sctp_icmp_proto_unreachable(sk, ep, asoc, transport); + goto out_unlock; + } + break; default: break; } @@ -188,9 +194,9 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport, /* Returns the dst cache entry for the given source and destination ip * addresses. */ -struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, - union sctp_addr *daddr, - union sctp_addr *saddr) +static struct dst_entry *sctp_v6_get_dst(struct sctp_association *asoc, + union sctp_addr *daddr, + union sctp_addr *saddr) { struct dst_entry *dst; struct flowi fl; @@ -251,8 +257,10 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, /* Fills in the source address(saddr) based on the destination address(daddr) * and asoc's bind address list. */ -void sctp_v6_get_saddr(struct sctp_association *asoc, struct dst_entry *dst, - union sctp_addr *daddr, union sctp_addr *saddr) +static void sctp_v6_get_saddr(struct sctp_association *asoc, + struct dst_entry *dst, + union sctp_addr *daddr, + union sctp_addr *saddr) { struct sctp_bind_addr *bp; rwlock_t *addr_lock; @@ -577,8 +585,8 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) } /* Create and initialize a new sk for the socket to be returned by accept(). */ -struct sock *sctp_v6_create_accept_sk(struct sock *sk, - struct sctp_association *asoc) +static struct sock *sctp_v6_create_accept_sk(struct sock *sk, + struct sctp_association *asoc) { struct inet_sock *inet = inet_sk(sk); struct sock *newsk; diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c index 23288c8d3d6ed7..0781e5d509fddc 100644 --- a/net/sctp/objcnt.c +++ b/net/sctp/objcnt.c @@ -62,7 +62,7 @@ SCTP_DBG_OBJCNT(datamsg); /* An array to make it easy to pretty print the debug information * to the proc fs. */ -sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = { +static sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = { SCTP_DBG_OBJCNT_ENTRY(sock), SCTP_DBG_OBJCNT_ENTRY(ep), SCTP_DBG_OBJCNT_ENTRY(assoc), diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 36ddfb78d41e0c..1b2d4adc4ddbaf 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -190,19 +190,6 @@ static inline int sctp_cacc_skip(struct sctp_transport *primary, return 0; } -/* Generate a new outqueue. */ -struct sctp_outq *sctp_outq_new(struct sctp_association *asoc) -{ - struct sctp_outq *q; - - q = t_new(struct sctp_outq, GFP_KERNEL); - if (q) { - sctp_outq_init(asoc, q); - q->malloced = 1; - } - return q; -} - /* Initialize an existing sctp_outq. This does the boring stuff. * You still need to define handlers if you really want to DO * something with this structure... @@ -362,7 +349,7 @@ int sctp_outq_tail(struct sctp_outq *q, struct sctp_chunk *chunk) /* Insert a chunk into the sorted list based on the TSNs. The retransmit list * and the abandoned list are in ascending order. */ -void sctp_insert_list(struct list_head *head, struct list_head *new) +static void sctp_insert_list(struct list_head *head, struct list_head *new) { struct list_head *pos; struct sctp_chunk *nchunk, *lchunk; diff --git a/net/sctp/proc.c b/net/sctp/proc.c index 9c7d2e9d8cc221..e42fd8c2916b63 100644 --- a/net/sctp/proc.c +++ b/net/sctp/proc.c @@ -39,7 +39,7 @@ #include <linux/init.h> #include <net/sctp/sctp.h> -struct snmp_mib sctp_snmp_list[] = { +static struct snmp_mib sctp_snmp_list[] = { SNMP_MIB_ITEM("SctpCurrEstab", SCTP_MIB_CURRESTAB), SNMP_MIB_ITEM("SctpActiveEstabs", SCTP_MIB_ACTIVEESTABS), SNMP_MIB_ITEM("SctpPassiveEstabs", SCTP_MIB_PASSIVEESTABS), diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index a8a3f3d37efc3e..7b3ed411f1a3b5 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -95,7 +95,7 @@ struct sock *sctp_get_ctl_sock(void) } /* Set up the proc fs entry for the SCTP protocol. */ -__init int sctp_proc_init(void) +static __init int sctp_proc_init(void) { if (!proc_net_sctp) { struct proc_dir_entry *ent; @@ -124,7 +124,7 @@ out_nomem: * Note: Do not make this __exit as it is used in the init error * path. */ -void sctp_proc_exit(void) +static void sctp_proc_exit(void) { sctp_snmp_proc_exit(); sctp_eps_proc_exit(); @@ -428,9 +428,9 @@ static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) * addresses. If an association is passed, trys to get a dst entry with a * source address that matches an address in the bind address list. */ -struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, - union sctp_addr *daddr, - union sctp_addr *saddr) +static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, + union sctp_addr *daddr, + union sctp_addr *saddr) { struct rtable *rt; struct flowi fl; @@ -520,10 +520,10 @@ out: /* For v4, the source address is cached in the route entry(dst). So no need * to cache it separately and hence this is an empty routine. */ -void sctp_v4_get_saddr(struct sctp_association *asoc, - struct dst_entry *dst, - union sctp_addr *daddr, - union sctp_addr *saddr) +static void sctp_v4_get_saddr(struct sctp_association *asoc, + struct dst_entry *dst, + union sctp_addr *daddr, + union sctp_addr *saddr) { struct rtable *rt = (struct rtable *)dst; @@ -547,8 +547,8 @@ static int sctp_v4_is_ce(const struct sk_buff *skb) } /* Create and initialize a new sk for the socket returned by accept(). */ -struct sock *sctp_v4_create_accept_sk(struct sock *sk, - struct sctp_association *asoc) +static struct sock *sctp_v4_create_accept_sk(struct sock *sk, + struct sctp_association *asoc) { struct sock *newsk; struct inet_sock *inet = inet_sk(sk); @@ -639,7 +639,7 @@ int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, * Initialize the control inode/socket with a control endpoint data * structure. This endpoint is reserved exclusively for the OOTB processing. */ -int sctp_ctl_sock_init(void) +static int sctp_ctl_sock_init(void) { int err; sa_family_t family; @@ -808,7 +808,7 @@ static inline int sctp_v4_xmit(struct sk_buff *skb, return ip_queue_xmit(skb, ipfragok); } -struct sctp_af sctp_ipv4_specific; +static struct sctp_af sctp_ipv4_specific; static struct sctp_pf sctp_pf_inet = { .event_msgname = sctp_inet_event_msgname, @@ -829,7 +829,7 @@ static struct notifier_block sctp_inetaddr_notifier = { }; /* Socket operations. */ -struct proto_ops inet_seqpacket_ops = { +static struct proto_ops inet_seqpacket_ops = { .family = PF_INET, .owner = THIS_MODULE, .release = inet_release, /* Needs to be wrapped... */ @@ -878,7 +878,7 @@ static struct net_protocol sctp_protocol = { }; /* IPv4 address related functions. */ -struct sctp_af sctp_ipv4_specific = { +static struct sctp_af sctp_ipv4_specific = { .sctp_xmit = sctp_v4_xmit, .setsockopt = ip_setsockopt, .getsockopt = ip_getsockopt, @@ -959,7 +959,7 @@ static void cleanup_sctp_mibs(void) } /* Initialize the universe into something sensible. */ -__init int sctp_init(void) +SCTP_STATIC __init int sctp_init(void) { int i; int status = -EINVAL; @@ -1196,7 +1196,7 @@ err_bucket_cachep: } /* Exit handler for the SCTP protocol. */ -__exit void sctp_exit(void) +SCTP_STATIC __exit void sctp_exit(void) { /* BUG. This should probably do something useful like clean * up all the remaining associations and all that memory. diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 1f0676d1ffbc29..7a0c6fa4c33c11 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -67,6 +67,19 @@ extern kmem_cache_t *sctp_chunk_cachep; +SCTP_STATIC +struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc, + __u8 type, __u8 flags, int paylen); +static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const struct sctp_chunk *init_chunk, + int *cookie_len, + const __u8 *raw_addrs, int addrs_len); +static int sctp_process_param(struct sctp_association *asoc, + union sctp_params param, + const union sctp_addr *peer_addr, + int gfp); + /* What was the inbound interface for this chunk? */ int sctp_chunk_iif(const struct sctp_chunk *chunk) { @@ -559,52 +572,6 @@ nodata: return retval; } -/* Make a DATA chunk for the given association. Populate the data - * payload. - */ -struct sctp_chunk *sctp_make_datafrag(struct sctp_association *asoc, - const struct sctp_sndrcvinfo *sinfo, - int data_len, const __u8 *data, - __u8 flags, __u16 ssn) -{ - struct sctp_chunk *retval; - - retval = sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, ssn); - if (retval) - sctp_addto_chunk(retval, data_len, data); - - return retval; -} - -/* Make a DATA chunk for the given association to ride on stream id - * 'stream', with a payload id of 'payload', and a body of 'data'. - */ -struct sctp_chunk *sctp_make_data(struct sctp_association *asoc, - const struct sctp_sndrcvinfo *sinfo, - int data_len, const __u8 *data) -{ - struct sctp_chunk *retval = NULL; - - retval = sctp_make_data_empty(asoc, sinfo, data_len); - if (retval) - sctp_addto_chunk(retval, data_len, data); - return retval; -} - -/* Make a DATA chunk for the given association to ride on stream id - * 'stream', with a payload id of 'payload', and a body big enough to - * hold 'data_len' octets of data. We use this version when we need - * to build the message AFTER allocating memory. - */ -struct sctp_chunk *sctp_make_data_empty(struct sctp_association *asoc, - const struct sctp_sndrcvinfo *sinfo, - int data_len) -{ - __u8 flags = SCTP_DATA_NOT_FRAG; - - return sctp_make_datafrag_empty(asoc, sinfo, data_len, flags, 0); -} - /* Create a selective ackowledgement (SACK) for the given * association. This reports on which TSN's we've seen to date, * including duplicates and gaps. @@ -881,6 +848,31 @@ err_chunk: return retval; } +/* Make an ABORT chunk with a PROTOCOL VIOLATION cause code. */ +struct sctp_chunk *sctp_make_abort_violation( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + const __u8 *payload, + const size_t paylen) +{ + struct sctp_chunk *retval; + struct sctp_paramhdr phdr; + + retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen + + sizeof(sctp_chunkhdr_t)); + if (!retval) + goto end; + + sctp_init_cause(retval, SCTP_ERROR_PROTO_VIOLATION, payload, paylen); + + phdr.type = htons(chunk->chunk_hdr->type); + phdr.length = chunk->chunk_hdr->length; + sctp_addto_chunk(retval, sizeof(sctp_paramhdr_t), &phdr); + +end: + return retval; +} + /* Make a HEARTBEAT chunk. */ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, const struct sctp_transport *transport, @@ -933,7 +925,7 @@ nodata: /* Create an Operation Error chunk with the specified space reserved. * This routine can be used for containing multiple causes in the chunk. */ -struct sctp_chunk *sctp_make_op_error_space( +static struct sctp_chunk *sctp_make_op_error_space( const struct sctp_association *asoc, const struct sctp_chunk *chunk, size_t size) @@ -1034,7 +1026,6 @@ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, SCTP_DBG_OBJCNT_INC(chunk); atomic_set(&retval->refcnt, 1); - nodata: return retval; } @@ -1062,6 +1053,7 @@ const union sctp_addr *sctp_source(const struct sctp_chunk *chunk) /* Create a new chunk, setting the type and flags headers from the * arguments, reserving enough space for a 'paylen' byte payload. */ +SCTP_STATIC struct sctp_chunk *sctp_make_chunk(const struct sctp_association *asoc, __u8 type, __u8 flags, int paylen) { @@ -1261,7 +1253,7 @@ fail: /* Build a cookie representing asoc. * This INCLUDES the param header needed to put the cookie in the INIT ACK. */ -sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, +static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *init_chunk, int *cookie_len, @@ -1409,6 +1401,24 @@ struct sctp_association *sctp_unpack_cookie( } no_hmac: + /* IG Section 2.35.2: + * 3) Compare the port numbers and the verification tag contained + * within the COOKIE ECHO chunk to the actual port numbers and the + * verification tag within the SCTP common header of the received + * packet. If these values do not match the packet MUST be silently + * discarded, + */ + if (ntohl(chunk->sctp_hdr->vtag) != bear_cookie->my_vtag) { + *error = -SCTP_IERROR_BAD_TAG; + goto fail; + } + + if (ntohs(chunk->sctp_hdr->source) != bear_cookie->peer_addr.v4.sin_port || + ntohs(chunk->sctp_hdr->dest) != bear_cookie->my_port) { + *error = -SCTP_IERROR_BAD_PORTS; + goto fail; + } + /* Check to see if the cookie is stale. If there is already * an association, there is no need to check cookie's expiration * for init collision case of lost COOKIE ACK. @@ -1547,6 +1557,30 @@ static int sctp_process_inv_mandatory(const struct sctp_association *asoc, return 0; } +static int sctp_process_inv_paramlength(const struct sctp_association *asoc, + struct sctp_paramhdr *param, + const struct sctp_chunk *chunk, + struct sctp_chunk **errp) +{ + char error[] = "The following parameter had invalid length:"; + size_t payload_len = WORD_ROUND(sizeof(error)) + + sizeof(sctp_paramhdr_t); + + + /* Create an error chunk and fill it in with our payload. */ + if (!*errp) + *errp = sctp_make_op_error_space(asoc, chunk, payload_len); + + if (*errp) { + sctp_init_cause(*errp, SCTP_ERROR_PROTO_VIOLATION, error, + sizeof(error)); + sctp_addto_chunk(*errp, sizeof(sctp_paramhdr_t), param); + } + + return 0; +} + + /* Do not attempt to handle the HOST_NAME parm. However, do * send back an indicator to the peer. */ @@ -1725,6 +1759,18 @@ int sctp_verify_init(const struct sctp_association *asoc, } /* for (loop through all parameters) */ + /* There is a possibility that a parameter length was bad and + * in that case we would have stoped walking the parameters. + * The current param.p would point at the bad one. + * Current consensus on the mailing list is to generate a PROTOCOL + * VIOLATION error. We build the ERROR chunk here and let the normal + * error handling code build and send the packet. + */ + if (param.v < (void*)chunk->chunk_end - sizeof(sctp_paramhdr_t)) { + sctp_process_inv_paramlength(asoc, param.p, chunk, errp); + return 0; + } + /* The only missing mandatory param possible today is * the state cookie for an INIT-ACK chunk. */ @@ -1912,8 +1958,10 @@ nomem: * work we do. In particular, we should not build transport * structures for the addresses. */ -int sctp_process_param(struct sctp_association *asoc, union sctp_params param, - const union sctp_addr *peer_addr, int gfp) +static int sctp_process_param(struct sctp_association *asoc, + union sctp_params param, + const union sctp_addr *peer_addr, + int gfp) { union sctp_addr addr; int i; @@ -2078,8 +2126,9 @@ __u32 sctp_generate_tsn(const struct sctp_endpoint *ep) * * Address Parameter and other parameter will not be wrapped in this function */ -struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, - union sctp_addr *addr, int vparam_len) +static struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, + union sctp_addr *addr, + int vparam_len) { sctp_addiphdr_t asconf; struct sctp_chunk *retval; @@ -2248,8 +2297,8 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, * * Create an ASCONF_ACK chunk with enough space for the parameter responses. */ -struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, - __u32 serial, int vparam_len) +static struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, + __u32 serial, int vparam_len) { sctp_addiphdr_t asconf; struct sctp_chunk *retval; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index c9705de466bcca..f65fa441952f7b 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -55,6 +55,24 @@ #include <net/sctp/sctp.h> #include <net/sctp/sm.h> +static int sctp_cmd_interpreter(sctp_event_t event_type, + sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *ep, + struct sctp_association *asoc, + void *event_arg, + sctp_disposition_t status, + sctp_cmd_seq_t *commands, + int gfp); +static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *ep, + struct sctp_association *asoc, + void *event_arg, + sctp_disposition_t status, + sctp_cmd_seq_t *commands, + int gfp); + /******************************************************************** * Helper functions ********************************************************************/ @@ -134,8 +152,8 @@ static void sctp_do_ecn_cwr_work(struct sctp_association *asoc, } /* Generate SACK if necessary. We call this at the end of a packet. */ -int sctp_gen_sack(struct sctp_association *asoc, int force, - sctp_cmd_seq_t *commands) +static int sctp_gen_sack(struct sctp_association *asoc, int force, + sctp_cmd_seq_t *commands) { __u32 ctsn, max_tsn_seen; struct sctp_chunk *sack; @@ -276,31 +294,31 @@ out_unlock: sctp_association_put(asoc); } -void sctp_generate_t1_cookie_event(unsigned long data) +static void sctp_generate_t1_cookie_event(unsigned long data) { struct sctp_association *asoc = (struct sctp_association *) data; sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_COOKIE); } -void sctp_generate_t1_init_event(unsigned long data) +static void sctp_generate_t1_init_event(unsigned long data) { struct sctp_association *asoc = (struct sctp_association *) data; sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_INIT); } -void sctp_generate_t2_shutdown_event(unsigned long data) +static void sctp_generate_t2_shutdown_event(unsigned long data) { struct sctp_association *asoc = (struct sctp_association *) data; sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN); } -void sctp_generate_t4_rto_event(unsigned long data) +static void sctp_generate_t4_rto_event(unsigned long data) { struct sctp_association *asoc = (struct sctp_association *) data; sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T4_RTO); } -void sctp_generate_t5_shutdown_guard_event(unsigned long data) +static void sctp_generate_t5_shutdown_guard_event(unsigned long data) { struct sctp_association *asoc = (struct sctp_association *)data; sctp_generate_timeout_event(asoc, @@ -308,7 +326,7 @@ void sctp_generate_t5_shutdown_guard_event(unsigned long data) } /* sctp_generate_t5_shutdown_guard_event() */ -void sctp_generate_autoclose_event(unsigned long data) +static void sctp_generate_autoclose_event(unsigned long data) { struct sctp_association *asoc = (struct sctp_association *) data; sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE); @@ -353,7 +371,7 @@ out_unlock: } /* Inject a SACK Timeout event into the state machine. */ -void sctp_generate_sack_event(unsigned long data) +static void sctp_generate_sack_event(unsigned long data) { struct sctp_association *asoc = (struct sctp_association *) data; sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK); @@ -397,7 +415,7 @@ static void sctp_do_8_2_transport_strike(struct sctp_association *asoc, asoc->overall_error_count++; if (transport->active && - (transport->error_count++ >= transport->error_threshold)) { + (transport->error_count++ >= transport->max_retrans)) { SCTP_DEBUG_PRINTK("transport_strike: transport " "IP:%d.%d.%d.%d failed.\n", NIPQUAD(transport->ipaddr.v4.sin_addr)); @@ -857,14 +875,14 @@ int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, /***************************************************************** * This the master state function side effect processing function. *****************************************************************/ -int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, - struct sctp_endpoint *ep, - struct sctp_association *asoc, - void *event_arg, - sctp_disposition_t status, - sctp_cmd_seq_t *commands, - int gfp) +static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *ep, + struct sctp_association *asoc, + void *event_arg, + sctp_disposition_t status, + sctp_cmd_seq_t *commands, + int gfp) { int error; @@ -944,11 +962,15 @@ bail: ********************************************************************/ /* This is the side-effect interpreter. */ -int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, struct sctp_endpoint *ep, - struct sctp_association *asoc, void *event_arg, - sctp_disposition_t status, sctp_cmd_seq_t *commands, - int gfp) +static int sctp_cmd_interpreter(sctp_event_t event_type, + sctp_subtype_t subtype, + sctp_state_t state, + struct sctp_endpoint *ep, + struct sctp_association *asoc, + void *event_arg, + sctp_disposition_t status, + sctp_cmd_seq_t *commands, + int gfp) { int error = 0; int force; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7871faa083788f..9b1b95939b62aa 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -65,6 +65,53 @@ #include <net/sctp/sm.h> #include <net/sctp/structs.h> +static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + struct sctp_chunk *chunk, + const void *payload, + size_t paylen); +static int sctp_eat_data(const struct sctp_association *asoc, + struct sctp_chunk *chunk, + sctp_cmd_seq_t *commands); +static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc, + const struct sctp_chunk *chunk); +static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + sctp_cmd_seq_t *commands, + struct sctp_chunk *err_chunk); +static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands); +static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands); +static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); + + +/* Small helper function that checks if the chunk length + * is of the appropriate length. The 'required_length' argument + * is set to be the size of a specific chunk we are testing. + * Return Values: 1 = Valid length + * 0 = Invalid length + * + */ +static inline int +sctp_chunk_length_valid(struct sctp_chunk *chunk, + __u16 required_length) +{ + __u16 chunk_length = ntohs(chunk->chunk_hdr->length); + + if (unlikely(chunk_length < required_length)) + return 0; + + return 1; +} + /********************************************************** * These are the state functions for handling chunk events. **********************************************************/ @@ -199,9 +246,14 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. + * + * IG Section 2.11.2 + * Furthermore, we require that the receiver of an INIT chunk MUST + * enforce these rules by silently discarding an arriving packet + * with an INIT chunk that is bundled with other chunks. */ if (!chunk->singleton) - return SCTP_DISPOSITION_VIOLATION; + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); /* If the packet is an OOTB packet which is temporarily on the * control endpoint, respond with an ABORT. @@ -225,6 +277,14 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const struct sctp_endpoint *ep, if (chunk->sctp_hdr->vtag != 0) return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + /* Make sure that the INIT chunk has a valid length. + * Normally, this would cause an ABORT with a Protocol Violation + * error, but since we don't have an association, we'll + * just discard the packet. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t))) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Verify the INIT chunk before processing it. */ err_chunk = NULL; if (!sctp_verify_init(asoc, chunk->chunk_hdr->type, @@ -376,6 +436,13 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, struct sctp_packet *packet; sctp_disposition_t ret; + if (!sctp_vtag_verify(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Make sure that the INIT-ACK chunk has a valid length */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_initack_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. @@ -383,9 +450,6 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(const struct sctp_endpoint *ep, if (!chunk->singleton) return SCTP_DISPOSITION_VIOLATION; - if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - /* Grab the INIT header. */ chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; @@ -542,6 +606,14 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(const struct sctp_endpoint *ep, if (ep == sctp_sk((sctp_get_ctl_sock()))->ep) return sctp_sf_ootb(ep, asoc, type, arg, commands); + /* Make sure that the COOKIE_ECHO chunk has a valid length. + * In this case, we check that we have enough for at least a + * chunk header. More detailed verification is done + * in sctp_unpack_cookie(). + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* "Decode" the chunk. We have no optional parameters so we * are in good shape. */ @@ -687,6 +759,13 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Verify that the chunk length for the COOKIE-ACK is OK. + * If we don't do this, any bundled chunks may be junked. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* Reset init error count upon receipt of COOKIE-ACK, * to avoid problems with the managemement of this * counter in stale cookie situations when a transition back @@ -748,11 +827,11 @@ nomem: } /* Generate and sendout a heartbeat packet. */ -sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, - void *arg, - sctp_cmd_seq_t *commands) +static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) { struct sctp_transport *transport = (struct sctp_transport *) arg; struct sctp_chunk *reply; @@ -859,6 +938,11 @@ sctp_disposition_t sctp_sf_beat_8_3(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Make sure that the HEARTBEAT chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_heartbeat_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* 8.3 The receiver of the HEARTBEAT should immediately * respond with a HEARTBEAT ACK that contains the Heartbeat * Information field copied from the received HEARTBEAT chunk. @@ -922,6 +1006,11 @@ sctp_disposition_t sctp_sf_backbeat_8_3(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Make sure that the HEARTBEAT-ACK chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_heartbeat_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; from_addr = hbinfo->daddr; link = sctp_assoc_lookup_paddr(asoc, &from_addr); @@ -1165,9 +1254,14 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( /* 6.10 Bundling * An endpoint MUST NOT bundle INIT, INIT ACK or * SHUTDOWN COMPLETE with any other chunks. + * + * IG Section 2.11.2 + * Furthermore, we require that the receiver of an INIT chunk MUST + * enforce these rules by silently discarding an arriving packet + * with an INIT chunk that is bundled with other chunks. */ if (!chunk->singleton) - return SCTP_DISPOSITION_VIOLATION; + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); /* 3.1 A packet containing an INIT chunk MUST have a zero Verification * Tag. @@ -1175,6 +1269,13 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( if (chunk->sctp_hdr->vtag != 0) return sctp_sf_tabort_8_4_8(ep, asoc, type, arg, commands); + /* Make sure that the INIT chunk has a valid length. + * In this case, we generate a protocol violation since we have + * an association established. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_init_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); /* Grab the INIT header. */ chunk->subh.init_hdr = (sctp_inithdr_t *) chunk->skb->data; @@ -1718,6 +1819,15 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(const struct sctp_endpoint *ep, char action; struct sctp_chunk *err_chk_p; + /* Make sure that the chunk has a valid length from the protocol + * perspective. In this case check to make sure we have at least + * enough for the chunk header. Cookie length verification is + * done later. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* "Decode" the chunk. We have no optional parameters so we * are in good shape. */ @@ -1815,6 +1925,19 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Make sure that the ABORT chunk has a valid length. + * Since this is an ABORT chunk, we have to discard it + * because of the following text: + * RFC 2960, Section 3.3.7 + * If an endpoint receives an ABORT with a format error or for an + * association that doesn't exist, it MUST silently discard it. + * Becasue the length is "invalid", we can't really discard just + * as we do not know its true length. So, to be safe, discard the + * packet. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Stop the T5-shutdown guard timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD)); @@ -1838,6 +1961,19 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(const struct sctp_endpoint *ep, if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Make sure that the ABORT chunk has a valid length. + * Since this is an ABORT chunk, we have to discard it + * because of the following text: + * RFC 2960, Section 3.3.7 + * If an endpoint receives an ABORT with a format error or for an + * association that doesn't exist, it MUST silently discard it. + * Becasue the length is "invalid", we can't really discard just + * as we do not know its true length. So, to be safe, discard the + * packet. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Stop the T2-shutdown timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T2_SHUTDOWN)); @@ -1890,6 +2026,16 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; sctp_errhdr_t *err; + if (!sctp_vtag_verify(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Make sure that the ERROR chunk has a valid length. + * The parameter walking depends on this as well. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* Process the error here */ /* FUTURE FIXME: When PR-SCTP related and other optional * parms are emitted, this will have to change to handle multiple @@ -1900,6 +2046,12 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, return sctp_sf_do_5_2_6_stale(ep, asoc, type, arg, commands); } + + /* It is possible to have malformed error causes, and that + * will cause us to end the walk early. However, since + * we are discarding the packet, there should be no adverse + * affects. + */ return sctp_sf_pdiscard(ep, asoc, type, arg, commands); } @@ -1928,11 +2080,11 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, - void *arg, - sctp_cmd_seq_t *commands) +static sctp_disposition_t sctp_sf_do_5_2_6_stale(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; time_t stale; @@ -2064,12 +2216,24 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const struct sctp_endpoint *ep, if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - /* Check that chunk header looks valid. */ + /* Make sure that the ABORT chunk has a valid length. + * Since this is an ABORT chunk, we have to discard it + * because of the following text: + * RFC 2960, Section 3.3.7 + * If an endpoint receives an ABORT with a format error or for an + * association that doesn't exist, it MUST silently discard it. + * Becasue the length is "invalid", we can't really discard just + * as we do not know its true length. So, to be safe, discard the + * packet. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) error = ((sctp_errhdr_t *)chunk->skb->data)->cause; - /* ASSOC_FAILED will DELETE_TCB. */ sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error)); SCTP_INC_STATS(SCTP_MIB_ABORTEDS); @@ -2096,27 +2260,43 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const struct sctp_endpoint *ep, if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, - SCTP_STATE(SCTP_STATE_CLOSED)); - SCTP_INC_STATS(SCTP_MIB_ABORTEDS); - sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, - SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + /* Make sure that the ABORT chunk has a valid length. + * Since this is an ABORT chunk, we have to discard it + * because of the following text: + * RFC 2960, Section 3.3.7 + * If an endpoint receives an ABORT with a format error or for an + * association that doesn't exist, it MUST silently discard it. + * Becasue the length is "invalid", we can't really discard just + * as we do not know its true length. So, to be safe, discard the + * packet. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - /* Check that chunk header looks valid. */ + /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) error = ((sctp_errhdr_t *)chunk->skb->data)->cause; - /* CMD_INIT_FAILED will DELETE_TCB. */ - sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(error)); - + sctp_stop_t1_and_abort(commands, error); return SCTP_DISPOSITION_ABORT; } /* + * Process an incoming ICMP as an ABORT. (COOKIE-WAIT state) + */ +sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + sctp_stop_t1_and_abort(commands, SCTP_ERROR_NO_ERROR); + return SCTP_DISPOSITION_ABORT; +} + +/* * Process an ABORT. (COOKIE-ECHOED state) - * - * See sctp_sf_do_9_1_abort() above. */ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, const struct sctp_association *asoc, @@ -2131,6 +2311,23 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(const struct sctp_endpoint *ep, } /* + * Stop T1 timer and abort association with "INIT failed". + * + * This is common code called by several sctp_sf_*_abort() functions above. + */ +void sctp_stop_t1_and_abort(sctp_cmd_seq_t *commands, __u16 error) +{ + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); + SCTP_INC_STATS(SCTP_MIB_ABORTEDS); + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + /* CMD_INIT_FAILED will DELETE_TCB. */ + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(error)); +} + +/* * sctp_sf_do_9_2_shut * * Section: 9.2 @@ -2174,14 +2371,20 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(const struct sctp_endpoint *ep, sctp_disposition_t disposition; struct sctp_ulpevent *ev; + if (!sctp_vtag_verify(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Make sure that the SHUTDOWN chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, + sizeof(struct sctp_shutdown_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* Convert the elaborate header. */ sdh = (sctp_shutdownhdr_t *)chunk->skb->data; skb_pull(chunk->skb, sizeof(sctp_shutdownhdr_t)); chunk->subh.shutdown_hdr = sdh; - if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(ep, asoc, type, arg, commands); - /* Upon the reception of the SHUTDOWN, the peer endpoint shall * - enter the SHUTDOWN-RECEIVED state, * - stop accepting new data from its SCTP user @@ -2238,6 +2441,10 @@ sctp_disposition_t sctp_sf_do_9_2_reshutack(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = (struct sctp_chunk *) arg; struct sctp_chunk *reply; + /* Since we are not going to really process this INIT, there + * is no point in verifying chunk boundries. Just generate + * the SHUTDOWN ACK. + */ reply = sctp_make_shutdown_ack(asoc, chunk); if (NULL == reply) goto nomem; @@ -2295,6 +2502,10 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_ecne_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + cwr = (sctp_cwrhdr_t *) chunk->skb->data; skb_pull(chunk->skb, sizeof(sctp_cwrhdr_t)); @@ -2345,6 +2556,10 @@ sctp_disposition_t sctp_sf_do_ecne(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_ecne_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + ecne = (sctp_ecnehdr_t *) chunk->skb->data; skb_pull(chunk->skb, sizeof(sctp_ecnehdr_t)); @@ -2400,6 +2615,10 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const struct sctp_endpoint *ep, return sctp_sf_pdiscard(ep, asoc, type, arg, commands); } + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_data_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + error = sctp_eat_data(asoc, chunk, commands ); switch (error) { case SCTP_IERROR_NO_ERROR: @@ -2517,6 +2736,10 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const struct sctp_endpoint *ep, return sctp_sf_pdiscard(ep, asoc, type, arg, commands); } + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_data_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + error = sctp_eat_data(asoc, chunk, commands ); switch (error) { case SCTP_IERROR_NO_ERROR: @@ -2598,6 +2821,11 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Make sure that the SACK chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_sack_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* Pull the SACK chunk from the data buffer */ sackh = sctp_sm_pull_sack(chunk); /* Was this a bogus SACK? */ @@ -2700,6 +2928,14 @@ sctp_disposition_t sctp_sf_operr_notify(const struct sctp_endpoint *ep, struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; + if (!sctp_vtag_verify(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Make sure that the ERROR chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + while (chunk->chunk_end > chunk->skb->data) { ev = sctp_ulpevent_make_remote_error(asoc, chunk, 0, GFP_ATOMIC); @@ -2744,6 +2980,11 @@ sctp_disposition_t sctp_sf_do_9_2_final(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Make sure that the SHUTDOWN_ACK chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + /* 10.2 H) SHUTDOWN COMPLETE notification * * When SCTP completes the shutdown procedures (section 9.2) this @@ -2818,11 +3059,23 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, ch = (sctp_chunkhdr_t *) chunk->chunk_hdr; do { + /* Break out if chunk length is less then minimal. */ + if (ntohs(ch->length) < sizeof(sctp_chunkhdr_t)) + break; + ch_end = ((__u8 *)ch) + WORD_ROUND(ntohs(ch->length)); if (SCTP_CID_SHUTDOWN_ACK == ch->type) ootb_shut_ack = 1; + /* RFC 2960, Section 3.3.7 + * Moreover, under any circumstances, an endpoint that + * receives an ABORT MUST NOT respond to that ABORT by + * sending an ABORT of its own. + */ + if (SCTP_CID_ABORT == ch->type) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + ch = (sctp_chunkhdr_t *) ch_end; } while (ch_end < skb->tail); @@ -2853,11 +3106,11 @@ sctp_disposition_t sctp_sf_ootb(const struct sctp_endpoint *ep, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, - void *arg, - sctp_cmd_seq_t *commands) +static sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) { struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; @@ -2885,6 +3138,12 @@ sctp_disposition_t sctp_sf_shut_8_4_5(const struct sctp_endpoint *ep, SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + /* If the chunk length is invalid, we don't want to process + * the reset of the packet. + */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + return SCTP_DISPOSITION_CONSUME; } @@ -2927,6 +3186,17 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, sctp_addiphdr_t *hdr; __u32 serial; + if (!sctp_vtag_verify(chunk, asoc)) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, + SCTP_NULL()); + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + } + + /* Make sure that the ASCONF ADDIP chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(sctp_addip_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + hdr = (sctp_addiphdr_t *)chunk->skb->data; serial = ntohl(hdr->serial); @@ -2947,7 +3217,8 @@ sctp_disposition_t sctp_sf_do_asconf(const struct sctp_endpoint *ep, /* ADDIP 4.2 C3) If the value found in the serial number is * equal to the value stored in the 'Peer-Serial-Number' * IMPLEMENTATION NOTE: As an optimization a receiver may wish - * to save the last ASCONF-ACK for some predetermined period of * time and instead of re-processing the ASCONF (with the same + * to save the last ASCONF-ACK for some predetermined period of + * time and instead of re-processing the ASCONF (with the same * serial number) it may just re-transmit the ASCONF-ACK. */ if (asoc->addip_last_asconf_ack) @@ -2986,6 +3257,17 @@ sctp_disposition_t sctp_sf_do_asconf_ack(const struct sctp_endpoint *ep, sctp_addiphdr_t *addip_hdr; __u32 sent_serial, rcvd_serial; + if (!sctp_vtag_verify(asconf_ack, asoc)) { + sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, + SCTP_NULL()); + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + } + + /* Make sure that the ADDIP chunk has a valid length. */ + if (!sctp_chunk_length_valid(asconf_ack, sizeof(sctp_addip_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + addip_hdr = (sctp_addiphdr_t *)asconf_ack->skb->data; rcvd_serial = ntohl(addip_hdr->serial); @@ -3084,6 +3366,11 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(const struct sctp_endpoint *ep, return sctp_sf_pdiscard(ep, asoc, type, arg, commands); } + /* Make sure that the FORWARD_TSN chunk has valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_fwdtsn_chunk))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + fwdtsn_hdr = (struct sctp_fwdtsn_hdr *)chunk->skb->data; chunk->subh.fwdtsn_hdr = fwdtsn_hdr; len = ntohs(chunk->chunk_hdr->length); @@ -3142,6 +3429,11 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast( return sctp_sf_pdiscard(ep, asoc, type, arg, commands); } + /* Make sure that the FORWARD_TSN chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_fwdtsn_chunk))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + fwdtsn_hdr = (struct sctp_fwdtsn_hdr *)chunk->skb->data; chunk->subh.fwdtsn_hdr = fwdtsn_hdr; len = ntohs(chunk->chunk_hdr->length); @@ -3216,6 +3508,14 @@ sctp_disposition_t sctp_sf_unk_chunk(const struct sctp_endpoint *ep, if (!sctp_vtag_verify(unk_chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + /* Make sure that the chunk has a valid length. + * Since we don't know the chunk type, we use a general + * chunkhdr structure to make a comparison. + */ + if (!sctp_chunk_length_valid(unk_chunk, sizeof(sctp_chunkhdr_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + switch (type.chunk & SCTP_CID_ACTION_MASK) { case SCTP_CID_ACTION_DISCARD: /* Discard the packet. */ @@ -3338,6 +3638,66 @@ sctp_disposition_t sctp_sf_violation(const struct sctp_endpoint *ep, return SCTP_DISPOSITION_VIOLATION; } + +/* + * Handle a protocol violation when the chunk length is invalid. + * "Invalid" length is identified as smaller then the minimal length a + * given chunk can be. For example, a SACK chunk has invalid length + * if it's length is set to be smaller then the size of sctp_sack_chunk_t. + * + * We inform the other end by sending an ABORT with a Protocol Violation + * error code. + * + * Section: Not specified + * Verification Tag: Nothing to do + * Inputs + * (endpoint, asoc, chunk) + * + * Outputs + * (reply_msg, msg_up, counters) + * + * Generate an ABORT chunk and terminate the association. + */ +sctp_disposition_t sctp_sf_violation_chunklen(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + void *arg, + sctp_cmd_seq_t *commands) +{ + struct sctp_chunk *chunk = arg; + struct sctp_chunk *abort = NULL; + char err_str[]="The following chunk had invalid length:"; + + /* Make the abort chunk. */ + abort = sctp_make_abort_violation(asoc, chunk, err_str, + sizeof(err_str)); + if (!abort) + goto nomem; + + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); + SCTP_INC_STATS(SCTP_MIB_OUTCTRLCHUNKS); + + if (asoc->state <= SCTP_STATE_COOKIE_ECHOED) { + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(SCTP_ERROR_PROTO_VIOLATION)); + } else { + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_PROTO_VIOLATION)); + SCTP_DEC_STATS(SCTP_MIB_CURRESTAB); + } + + sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); + + SCTP_INC_STATS(SCTP_MIB_ABORTEDS); + + return SCTP_DISPOSITION_ABORT; + +nomem: + return SCTP_DISPOSITION_NOMEM; +} + /*************************************************************************** * These are the state functions for handling primitive (Section 10) events. ***************************************************************************/ @@ -4050,6 +4410,23 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack( struct sctp_chunk *chunk = (struct sctp_chunk *) arg; struct sctp_chunk *reply; + /* There are 2 ways of getting here: + * 1) called in response to a SHUTDOWN chunk + * 2) called when SCTP_EVENT_NO_PENDING_TSN event is issued. + * + * For the case (2), the arg parameter is set to NULL. We need + * to check that we have a chunk before accessing it's fields. + */ + if (chunk) { + if (!sctp_vtag_verify(chunk, asoc)) + return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + + /* Make sure that the SHUTDOWN chunk has a valid length. */ + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_shutdown_chunk_t))) + return sctp_sf_violation_chunklen(ep, asoc, type, arg, + commands); + } + /* If it has no more outstanding DATA chunks, the SHUTDOWN receiver * shall send a SHUTDOWN ACK ... */ @@ -4537,7 +4914,7 @@ sctp_disposition_t sctp_sf_timer_ignore(const struct sctp_endpoint *ep, ********************************************************************/ /* Pull the SACK chunk based on the SACK header. */ -struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk) +static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk) { struct sctp_sackhdr *sack; unsigned int len; @@ -4564,7 +4941,7 @@ struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk) /* Create an ABORT packet to be sent as a response, with the specified * error causes. */ -struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, +static struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, const void *payload, @@ -4600,8 +4977,8 @@ struct sctp_packet *sctp_abort_pkt_new(const struct sctp_endpoint *ep, } /* Allocate a packet for responding in the OOTB conditions. */ -struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc, - const struct sctp_chunk *chunk) +static struct sctp_packet *sctp_ootb_pkt_new(const struct sctp_association *asoc, + const struct sctp_chunk *chunk) { struct sctp_packet *packet; struct sctp_transport *transport; @@ -4664,11 +5041,11 @@ void sctp_ootb_pkt_free(struct sctp_packet *packet) } /* Send a stale cookie error when a invalid COOKIE ECHO chunk is found */ -void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands, - struct sctp_chunk *err_chunk) +static void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + sctp_cmd_seq_t *commands, + struct sctp_chunk *err_chunk) { struct sctp_packet *packet; @@ -4694,9 +5071,9 @@ void sctp_send_stale_cookie_err(const struct sctp_endpoint *ep, /* Process a data chunk */ -int sctp_eat_data(const struct sctp_association *asoc, - struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands) +static int sctp_eat_data(const struct sctp_association *asoc, + struct sctp_chunk *chunk, + sctp_cmd_seq_t *commands) { sctp_datahdr_t *data_hdr; struct sctp_chunk *err; diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 516dc7914366ac..8967846f69e888 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -50,6 +50,17 @@ #include <net/sctp/sctp.h> #include <net/sctp/sm.h> +static const sctp_sm_table_entry_t +primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES]; +static const sctp_sm_table_entry_t +other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES]; +static const sctp_sm_table_entry_t +timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES]; + +static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, + sctp_state_t state); + + static const sctp_sm_table_entry_t bug = { .fn = sctp_sf_bug, .name = "sctp_sf_bug" @@ -419,7 +430,7 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, * * For base protocol (RFC 2960). */ -const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { +static const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_DATA, TYPE_SCTP_INIT, TYPE_SCTP_INIT_ACK, @@ -482,7 +493,7 @@ const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_ST /* The primary index for this table is the chunk type. * The secondary index for this table is the state. */ -const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { +static const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_ASCONF, TYPE_SCTP_ASCONF_ACK, }; /*state_fn_t addip_chunk_event_table[][] */ @@ -511,7 +522,7 @@ const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_TYPES][ /* The primary index for this table is the chunk type. * The secondary index for this table is the state. */ -const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { +static const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_FWD_TSN, }; /*state_fn_t prsctp_chunk_event_table[][] */ @@ -684,7 +695,7 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { /* The primary index for this table is the primitive type. * The secondary index for this table is the state. */ -const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = { +static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_PRIMITIVE_ASSOCIATE, TYPE_SCTP_PRIMITIVE_SHUTDOWN, TYPE_SCTP_PRIMITIVE_ABORT, @@ -716,8 +727,31 @@ const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ } -const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = { +#define TYPE_SCTP_OTHER_ICMP_PROTO_UNREACH { \ + /* SCTP_STATE_EMPTY */ \ + {.fn = sctp_sf_bug, .name = "sctp_sf_bug"}, \ + /* SCTP_STATE_CLOSED */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_COOKIE_WAIT */ \ + {.fn = sctp_sf_cookie_wait_icmp_abort, \ + .name = "sctp_sf_cookie_wait_icmp_abort"}, \ + /* SCTP_STATE_COOKIE_ECHOED */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_ESTABLISHED */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_SHUTDOWN_PENDING */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_SHUTDOWN_SENT */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_SHUTDOWN_RECEIVED */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ + /* SCTP_STATE_SHUTDOWN_ACK_SENT */ \ + {.fn = sctp_sf_ignore_other, .name = "sctp_sf_ignore_other"}, \ +} + +static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_OTHER_NO_PENDING_TSN, + TYPE_SCTP_OTHER_ICMP_PROTO_UNREACH, }; #define TYPE_SCTP_EVENT_TIMEOUT_NONE { \ @@ -931,7 +965,7 @@ const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_N {.fn = sctp_sf_timer_ignore, .name = "sctp_sf_timer_ignore"}, \ } -const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = { +static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_EVENT_TIMEOUT_NONE, TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE, TYPE_SCTP_EVENT_TIMEOUT_T1_INIT, @@ -944,8 +978,8 @@ const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STA TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE, }; -const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, - sctp_state_t state) +static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(sctp_cid_t cid, + sctp_state_t state) { if (state > SCTP_STATE_MAX) return &bug; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index d9967aaefecf65..30b921b90d008a 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -208,7 +208,7 @@ struct sctp_association *sctp_id2assoc(struct sock *sk, sctp_assoc_t id) * id are specified, the associations matching the address and the id should be * the same. */ -struct sctp_transport *sctp_addr_id2transport(struct sock *sk, +static struct sctp_transport *sctp_addr_id2transport(struct sock *sk, struct sockaddr_storage *addr, sctp_assoc_t id) { @@ -245,7 +245,7 @@ struct sctp_transport *sctp_addr_id2transport(struct sock *sk, * sockaddr_in6 [RFC 2553]), * addr_len - the size of the address structure. */ -int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) +SCTP_STATIC int sctp_bind(struct sock *sk, struct sockaddr *uaddr, int addr_len) { int retval = 0; @@ -343,8 +343,8 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) } /* Refresh ephemeral port. */ - if (!snum) - snum = inet_sk(sk)->num; + if (!bp->port) + bp->port = inet_sk(sk)->num; /* Add the address to the bind address list. */ sctp_local_bh_disable(); @@ -354,8 +354,6 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) addr->v4.sin_port = ntohs(addr->v4.sin_port); ret = sctp_add_bind_addr(bp, addr, GFP_ATOMIC); addr->v4.sin_port = htons(addr->v4.sin_port); - if (!ret && !bp->port) - bp->port = snum; sctp_write_unlock(&ep->base.addr_lock); sctp_local_bh_enable(); @@ -1713,10 +1711,13 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk, /* update default value for endpoint (all future associations) */ if (!params.spp_assoc_id && sctp_is_any(( union sctp_addr *)¶ms.spp_address)) { - if (params.spp_hbinterval) + /* Manual heartbeat on an endpoint is invalid. */ + if (0xffffffff == params.spp_hbinterval) + return -EINVAL; + else if (params.spp_hbinterval) sctp_sk(sk)->paddrparam.spp_hbinterval = params.spp_hbinterval; - if (sctp_max_retrans_path) + if (params.spp_pathmaxrxt) sctp_sk(sk)->paddrparam.spp_pathmaxrxt = params.spp_pathmaxrxt; return 0; @@ -1758,7 +1759,8 @@ static int sctp_setsockopt_peer_addr_params(struct sock *sk, /* spp_pathmaxrxt contains the maximum number of retransmissions * before this address shall be considered unreachable. */ - trans->error_threshold = params.spp_pathmaxrxt; + if (params.spp_pathmaxrxt) + trans->max_retrans = params.spp_pathmaxrxt; return 0; } @@ -2937,7 +2939,7 @@ static int sctp_getsockopt_peer_addr_params(struct sock *sk, int len, /* spp_pathmaxrxt contains the maximum number of retransmissions * before this address shall be considered unreachable. */ - params.spp_pathmaxrxt = trans->error_threshold; + params.spp_pathmaxrxt = trans->max_retrans; done: if (copy_to_user(optval, ¶ms, len)) @@ -3049,6 +3051,9 @@ static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len, struct sctp_bind_addr *bp; struct sctp_association *asoc; struct list_head *pos; + struct sctp_sockaddr_entry *addr; + rwlock_t *addr_lock; + unsigned long flags; int cnt = 0; if (len != sizeof(sctp_assoc_t)) @@ -3065,33 +3070,104 @@ static int sctp_getsockopt_local_addrs_num(struct sock *sk, int len, */ if (0 == id) { bp = &sctp_sk(sk)->ep->base.bind_addr; + addr_lock = &sctp_sk(sk)->ep->base.addr_lock; } else { asoc = sctp_id2assoc(sk, id); if (!asoc) return -EINVAL; bp = &asoc->base.bind_addr; + addr_lock = &asoc->base.addr_lock; + } + + sctp_read_lock(addr_lock); + + /* If the endpoint is bound to 0.0.0.0 or ::0, count the valid + * addresses from the global local address list. + */ + if (sctp_list_single_entry(&bp->address_list)) { + addr = list_entry(bp->address_list.next, + struct sctp_sockaddr_entry, list); + if (sctp_is_any(&addr->a)) { + sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); + list_for_each(pos, &sctp_local_addr_list) { + addr = list_entry(pos, + struct sctp_sockaddr_entry, + list); + if ((PF_INET == sk->sk_family) && + (AF_INET6 == addr->a.sa.sa_family)) + continue; + cnt++; + } + sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, + flags); + } else { + cnt = 1; + } + goto done; } list_for_each(pos, &bp->address_list) { cnt ++; } +done: + sctp_read_unlock(addr_lock); + return cnt; +} + +/* Helper function that copies local addresses to user and returns the number + * of addresses copied. + */ +static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, int max_addrs, + void __user *to) +{ + struct list_head *pos; + struct sctp_sockaddr_entry *addr; + unsigned long flags; + union sctp_addr temp; + int cnt = 0; + int addrlen; + + sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); + list_for_each(pos, &sctp_local_addr_list) { + addr = list_entry(pos, struct sctp_sockaddr_entry, list); + if ((PF_INET == sk->sk_family) && + (AF_INET6 == addr->a.sa.sa_family)) + continue; + memcpy(&temp, &addr->a, sizeof(temp)); + sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), + &temp); + addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; + temp.v4.sin_port = htons(port); + if (copy_to_user(to, &temp, addrlen)) { + sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, + flags); + return -EFAULT; + } + to += addrlen; + cnt ++; + if (cnt >= max_addrs) break; + } + sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); + return cnt; } static int sctp_getsockopt_local_addrs(struct sock *sk, int len, - char __user *optval, int __user *optlen) + char __user *optval, int __user *optlen) { struct sctp_bind_addr *bp; struct sctp_association *asoc; struct list_head *pos; int cnt = 0; struct sctp_getaddrs getaddrs; - struct sctp_sockaddr_entry *from; + struct sctp_sockaddr_entry *addr; void __user *to; union sctp_addr temp; struct sctp_opt *sp = sctp_sk(sk); int addrlen; + rwlock_t *addr_lock; + int err = 0; if (len != sizeof(struct sctp_getaddrs)) return -EINVAL; @@ -3108,33 +3184,59 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, */ if (0 == getaddrs.assoc_id) { bp = &sctp_sk(sk)->ep->base.bind_addr; + addr_lock = &sctp_sk(sk)->ep->base.addr_lock; } else { asoc = sctp_id2assoc(sk, getaddrs.assoc_id); if (!asoc) return -EINVAL; bp = &asoc->base.bind_addr; + addr_lock = &asoc->base.addr_lock; } to = getaddrs.addrs; + + sctp_read_lock(addr_lock); + + /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid + * addresses from the global local address list. + */ + if (sctp_list_single_entry(&bp->address_list)) { + addr = list_entry(bp->address_list.next, + struct sctp_sockaddr_entry, list); + if (sctp_is_any(&addr->a)) { + cnt = sctp_copy_laddrs_to_user(sk, bp->port, + getaddrs.addr_num, to); + if (cnt < 0) { + err = cnt; + goto unlock; + } + goto copy_getaddrs; + } + } + list_for_each(pos, &bp->address_list) { - from = list_entry(pos, - struct sctp_sockaddr_entry, - list); - memcpy(&temp, &from->a, sizeof(temp)); + addr = list_entry(pos, struct sctp_sockaddr_entry, list); + memcpy(&temp, &addr->a, sizeof(temp)); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; temp.v4.sin_port = htons(temp.v4.sin_port); - if (copy_to_user(to, &temp, addrlen)) - return -EFAULT; + if (copy_to_user(to, &temp, addrlen)) { + err = -EFAULT; + goto unlock; + } to += addrlen; cnt ++; if (cnt >= getaddrs.addr_num) break; } + +copy_getaddrs: getaddrs.addr_num = cnt; if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs))) - return -EFAULT; + err = -EFAULT; - return 0; +unlock: + sctp_read_unlock(addr_lock); + return err; } /* 7.1.10 Set Primary Address (SCTP_PRIMARY_ADDR) diff --git a/net/sctp/ssnmap.c b/net/sctp/ssnmap.c index 847ba4ca235f24..e627d2b451b608 100644 --- a/net/sctp/ssnmap.c +++ b/net/sctp/ssnmap.c @@ -42,6 +42,9 @@ #define MAX_KMALLOC_SIZE 131072 +static struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in, + __u16 out); + /* Storage size needed for map includes 2 headers and then the * specific needs of in or out streams. */ @@ -87,8 +90,8 @@ fail: /* Initialize a block of memory as a ssnmap. */ -struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in, - __u16 out) +static struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *map, __u16 in, + __u16 out) { memset(map, 0x00, sctp_ssnmap_size(in, out)); diff --git a/net/sctp/transport.c b/net/sctp/transport.c index c4a2370beb1286..3fcca5ec314b06 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -54,34 +54,10 @@ /* 1st Level Abstractions. */ -/* Allocate and initialize a new transport. */ -struct sctp_transport *sctp_transport_new(const union sctp_addr *addr, int gfp) -{ - struct sctp_transport *transport; - - transport = t_new(struct sctp_transport, gfp); - if (!transport) - goto fail; - - if (!sctp_transport_init(transport, addr, gfp)) - goto fail_init; - - transport->malloced = 1; - SCTP_DBG_OBJCNT_INC(transport); - - return transport; - -fail_init: - kfree(transport); - -fail: - return NULL; -} - /* Initialize a new transport from provided memory. */ -struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, - const union sctp_addr *addr, - int gfp) +static struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, + const union sctp_addr *addr, + int gfp) { /* Copy in the address. */ peer->ipaddr = *addr; @@ -112,7 +88,6 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, /* Initialize the default path max_retrans. */ peer->max_retrans = sctp_max_retrans_path; - peer->error_threshold = 0; peer->error_count = 0; INIT_LIST_HEAD(&peer->transmitted); @@ -144,6 +119,30 @@ struct sctp_transport *sctp_transport_init(struct sctp_transport *peer, return peer; } +/* Allocate and initialize a new transport. */ +struct sctp_transport *sctp_transport_new(const union sctp_addr *addr, int gfp) +{ + struct sctp_transport *transport; + + transport = t_new(struct sctp_transport, gfp); + if (!transport) + goto fail; + + if (!sctp_transport_init(transport, addr, gfp)) + goto fail_init; + + transport->malloced = 1; + SCTP_DBG_OBJCNT_INC(transport); + + return transport; + +fail_init: + kfree(transport); + +fail: + return NULL; +} + /* This transport is no longer needed. Free up if possible, or * delay until it last reference count. */ @@ -155,13 +154,23 @@ void sctp_transport_free(struct sctp_transport *transport) if (del_timer(&transport->hb_timer)) sctp_transport_put(transport); + /* Delete the T3_rtx timer if it's active. + * There is no point in not doing this now and letting + * structure hang around in memory since we know + * the tranport is going away. + */ + if (timer_pending(&transport->T3_rtx_timer) && + del_timer(&transport->T3_rtx_timer)) + sctp_transport_put(transport); + + sctp_transport_put(transport); } /* Destroy the transport data structure. * Assumes there are no more users of this structure. */ -void sctp_transport_destroy(struct sctp_transport *transport) +static void sctp_transport_destroy(struct sctp_transport *transport) { SCTP_ASSERT(transport->dead, "Transport is not dead", return); diff --git a/net/sctp/tsnmap.c b/net/sctp/tsnmap.c index b1e334cde61ffb..ac4fae161bc727 100644 --- a/net/sctp/tsnmap.c +++ b/net/sctp/tsnmap.c @@ -52,29 +52,6 @@ static void sctp_tsnmap_find_gap_ack(__u8 *map, __u16 off, int *started, __u16 *start, int *ended, __u16 *end); -/* Create a new sctp_tsnmap. - * Allocate room to store at least 'len' contiguous TSNs. - */ -struct sctp_tsnmap *sctp_tsnmap_new(__u16 len, __u32 initial_tsn, int gfp) -{ - struct sctp_tsnmap *retval; - - retval = kmalloc(sizeof(struct sctp_tsnmap) + - sctp_tsnmap_storage_size(len), gfp); - if (!retval) - goto fail; - - if (!sctp_tsnmap_init(retval, len, initial_tsn)) - goto fail_map; - retval->malloced = 1; - return retval; - -fail_map: - kfree(retval); -fail: - return NULL; -} - /* Initialize a block of memory as a tsnmap. */ struct sctp_tsnmap *sctp_tsnmap_init(struct sctp_tsnmap *map, __u16 len, __u32 initial_tsn) @@ -168,16 +145,9 @@ void sctp_tsnmap_mark(struct sctp_tsnmap *map, __u32 tsn) } -/* Dispose of a tsnmap. */ -void sctp_tsnmap_free(struct sctp_tsnmap *map) -{ - if (map->malloced) - kfree(map); -} - /* Initialize a Gap Ack Block iterator from memory being provided. */ -void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map, - struct sctp_tsnmap_iter *iter) +SCTP_STATIC void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map, + struct sctp_tsnmap_iter *iter) { /* Only start looking one past the Cumulative TSN Ack Point. */ iter->start = map->cumulative_tsn_ack_point + 1; @@ -186,8 +156,9 @@ void sctp_tsnmap_iter_init(const struct sctp_tsnmap *map, /* Get the next Gap Ack Blocks. Returns 0 if there was not another block * to get. */ -int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map, - struct sctp_tsnmap_iter *iter, __u16 *start, __u16 *end) +SCTP_STATIC int sctp_tsnmap_next_gap_ack(const struct sctp_tsnmap *map, + struct sctp_tsnmap_iter *iter, + __u16 *start, __u16 *end) { int started, ended; __u16 _start, _end, offset; diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index b4f2e95ae7cb7e..17d0ff5347356f 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -65,8 +65,16 @@ static void sctp_stub_rfree(struct sk_buff *skb) */ } +/* Initialize an ULP event from an given skb. */ +SCTP_STATIC void sctp_ulpevent_init(struct sctp_ulpevent *event, int msg_flags) +{ + memset(event, 0, sizeof(struct sctp_ulpevent)); + event->msg_flags = msg_flags; +} + /* Create a new sctp_ulpevent. */ -struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, int gfp) +SCTP_STATIC struct sctp_ulpevent *sctp_ulpevent_new(int size, int msg_flags, + int gfp) { struct sctp_ulpevent *event; struct sk_buff *skb; @@ -84,13 +92,6 @@ fail: return NULL; } -/* Initialize an ULP event from an given skb. */ -void sctp_ulpevent_init(struct sctp_ulpevent *event, int msg_flags) -{ - memset(event, 0, sizeof(struct sctp_ulpevent)); - event->msg_flags = msg_flags; -} - /* Is this a MSG_NOTIFICATION? */ int sctp_ulpevent_is_notification(const struct sctp_ulpevent *event) { diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index 350a97ec7c622d..47a43580f05b33 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c @@ -56,25 +56,6 @@ static struct sctp_ulpevent * sctp_ulpq_order(struct sctp_ulpq *, /* 1st Level Abstractions */ -/* Create a new ULP queue. */ -struct sctp_ulpq *sctp_ulpq_new(struct sctp_association *asoc, int gfp) -{ - struct sctp_ulpq *ulpq; - - ulpq = kmalloc(sizeof(struct sctp_ulpq), gfp); - if (!ulpq) - goto fail; - if (!sctp_ulpq_init(ulpq, asoc)) - goto fail_init; - ulpq->malloced = 1; - return ulpq; - -fail_init: - kfree(ulpq); -fail: - return NULL; -} - /* Initialize a ULP queue from a block of memory. */ struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq, struct sctp_association *asoc) @@ -92,7 +73,7 @@ struct sctp_ulpq *sctp_ulpq_init(struct sctp_ulpq *ulpq, /* Flush the reassembly and ordering queues. */ -void sctp_ulpq_flush(struct sctp_ulpq *ulpq) +static void sctp_ulpq_flush(struct sctp_ulpq *ulpq) { struct sk_buff *skb; struct sctp_ulpevent *event; |