X-Git-Url: http://git.cascardo.info/?a=blobdiff_plain;f=net%2Fsctp%2Finput.c;h=d695f710fc77d0e986f599674873c6e6975603bd;hb=7a90e8cc21ad80529b3a3371dc97acc8832cc592;hp=47e56017f4ce887716e0dee8a4ba87d3ece37ac9;hpb=0ae842eaf9512d9c9479797083750c077fc7d1f8;p=cascardo%2Flinux.git diff --git a/net/sctp/input.c b/net/sctp/input.c index 47e56017f4ce..d695f710fc77 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -60,6 +60,7 @@ #include #include #include +#include /* Forward declarations for internal helpers. */ static int sctp_rcv_ootb(struct sk_buff *); @@ -622,6 +623,14 @@ static int sctp_rcv_ootb(struct sk_buff *skb) if (SCTP_CID_SHUTDOWN_COMPLETE == ch->type) goto discard; + /* RFC 4460, 2.11.2 + * This will discard packets with INIT chunk bundled as + * subsequent chunks in the packet. When INIT is first, + * the normal INIT processing will discard the chunk. + */ + if (SCTP_CID_INIT == ch->type && (void *)ch != skb->data) + goto discard; + /* RFC 8.4, 7) If the packet contains a "Stale cookie" ERROR * or a COOKIE ACK the SCTP Packet should be silently * discarded. @@ -648,7 +657,6 @@ discard: /* Insert endpoint into the hash table. */ static void __sctp_hash_endpoint(struct sctp_endpoint *ep) { - struct sctp_ep_common **epp; struct sctp_ep_common *epb; struct sctp_hashbucket *head; @@ -658,12 +666,7 @@ static void __sctp_hash_endpoint(struct sctp_endpoint *ep) head = &sctp_ep_hashtable[epb->hashent]; sctp_write_lock(&head->lock); - epp = &head->chain; - epb->next = *epp; - if (epb->next) - (*epp)->pprev = &epb->next; - *epp = epb; - epb->pprev = epp; + hlist_add_head(&epb->node, &head->chain); sctp_write_unlock(&head->lock); } @@ -683,19 +686,15 @@ static void __sctp_unhash_endpoint(struct sctp_endpoint *ep) epb = &ep->base; + if (hlist_unhashed(&epb->node)) + return; + epb->hashent = sctp_ep_hashfn(epb->bind_addr.port); head = &sctp_ep_hashtable[epb->hashent]; sctp_write_lock(&head->lock); - - if (epb->pprev) { - if (epb->next) - epb->next->pprev = epb->pprev; - *epb->pprev = epb->next; - epb->pprev = NULL; - } - + __hlist_del(&epb->node); sctp_write_unlock(&head->lock); } @@ -713,12 +712,13 @@ static struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *l struct sctp_hashbucket *head; struct sctp_ep_common *epb; struct sctp_endpoint *ep; + struct hlist_node *node; int hash; hash = sctp_ep_hashfn(ntohs(laddr->v4.sin_port)); head = &sctp_ep_hashtable[hash]; read_lock(&head->lock); - for (epb = head->chain; epb; epb = epb->next) { + sctp_for_each_hentry(epb, node, &head->chain) { ep = sctp_ep(epb); if (sctp_endpoint_is_match(ep, laddr)) goto hit; @@ -736,7 +736,6 @@ hit: /* Insert association into the hash table. */ static void __sctp_hash_established(struct sctp_association *asoc) { - struct sctp_ep_common **epp; struct sctp_ep_common *epb; struct sctp_hashbucket *head; @@ -748,12 +747,7 @@ static void __sctp_hash_established(struct sctp_association *asoc) head = &sctp_assoc_hashtable[epb->hashent]; sctp_write_lock(&head->lock); - epp = &head->chain; - epb->next = *epp; - if (epb->next) - (*epp)->pprev = &epb->next; - *epp = epb; - epb->pprev = epp; + hlist_add_head(&epb->node, &head->chain); sctp_write_unlock(&head->lock); } @@ -782,14 +776,7 @@ static void __sctp_unhash_established(struct sctp_association *asoc) head = &sctp_assoc_hashtable[epb->hashent]; sctp_write_lock(&head->lock); - - if (epb->pprev) { - if (epb->next) - epb->next->pprev = epb->pprev; - *epb->pprev = epb->next; - epb->pprev = NULL; - } - + __hlist_del(&epb->node); sctp_write_unlock(&head->lock); } @@ -814,6 +801,7 @@ static struct sctp_association *__sctp_lookup_association( struct sctp_ep_common *epb; struct sctp_association *asoc; struct sctp_transport *transport; + struct hlist_node *node; int hash; /* Optimize here for direct hit, only listening connections can @@ -822,7 +810,7 @@ static struct sctp_association *__sctp_lookup_association( hash = sctp_assoc_hashfn(ntohs(local->v4.sin_port), ntohs(peer->v4.sin_port)); head = &sctp_assoc_hashtable[hash]; read_lock(&head->lock); - for (epb = head->chain; epb; epb = epb->next) { + sctp_for_each_hentry(epb, node, &head->chain) { asoc = sctp_assoc(epb); transport = sctp_assoc_is_match(asoc, local, peer); if (transport) @@ -903,23 +891,6 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, ch = (sctp_chunkhdr_t *) skb->data; - /* If this is INIT/INIT-ACK look inside the chunk too. */ - switch (ch->type) { - case SCTP_CID_INIT: - case SCTP_CID_INIT_ACK: - break; - default: - 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. @@ -956,6 +927,150 @@ static struct sctp_association *__sctp_rcv_init_lookup(struct sk_buff *skb, return NULL; } +/* ADD-IP, Section 5.2 + * When an endpoint receives an ASCONF Chunk from the remote peer + * special procedures may be needed to identify the association the + * ASCONF Chunk is associated with. To properly find the association + * the following procedures SHOULD be followed: + * + * D2) If the association is not found, use the address found in the + * Address Parameter TLV combined with the port number found in the + * SCTP common header. If found proceed to rule D4. + * + * D2-ext) If more than one ASCONF Chunks are packed together, use the + * address found in the ASCONF Address Parameter TLV of each of the + * subsequent ASCONF Chunks. If found, proceed to rule D4. + */ +static struct sctp_association *__sctp_rcv_asconf_lookup( + sctp_chunkhdr_t *ch, + const union sctp_addr *laddr, + __be32 peer_port, + struct sctp_transport **transportp) +{ + sctp_addip_chunk_t *asconf = (struct sctp_addip_chunk *)ch; + struct sctp_af *af; + union sctp_addr_param *param; + union sctp_addr paddr; + + /* Skip over the ADDIP header and find the Address parameter */ + param = (union sctp_addr_param *)(asconf + 1); + + af = sctp_get_af_specific(param_type2af(param->v4.param_hdr.type)); + if (unlikely(!af)) + return NULL; + + af->from_addr_param(&paddr, param, peer_port, 0); + + return __sctp_lookup_association(laddr, &paddr, transportp); +} + + +/* SCTP-AUTH, Section 6.3: +* If the receiver does not find a STCB for a packet containing an AUTH +* chunk as the first chunk and not a COOKIE-ECHO chunk as the second +* chunk, it MUST use the chunks after the AUTH chunk to look up an existing +* association. +* +* This means that any chunks that can help us identify the association need +* to be looked at to find this assocation. +*/ +static struct sctp_association *__sctp_rcv_walk_lookup(struct sk_buff *skb, + const union sctp_addr *laddr, + struct sctp_transport **transportp) +{ + struct sctp_association *asoc = NULL; + sctp_chunkhdr_t *ch; + int have_auth = 0; + unsigned int chunk_num = 1; + __u8 *ch_end; + + /* Walk through the chunks looking for AUTH or ASCONF chunks + * to help us find the association. + */ + ch = (sctp_chunkhdr_t *) skb->data; + 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 (ch_end > skb_tail_pointer(skb)) + break; + + switch(ch->type) { + case SCTP_CID_AUTH: + have_auth = chunk_num; + break; + + case SCTP_CID_COOKIE_ECHO: + /* If a packet arrives containing an AUTH chunk as + * a first chunk, a COOKIE-ECHO chunk as the second + * chunk, and possibly more chunks after them, and + * the receiver does not have an STCB for that + * packet, then authentication is based on + * the contents of the COOKIE- ECHO chunk. + */ + if (have_auth == 1 && chunk_num == 2) + return NULL; + break; + + case SCTP_CID_ASCONF: + if (have_auth || sctp_addip_noauth) + asoc = __sctp_rcv_asconf_lookup(ch, laddr, + sctp_hdr(skb)->source, + transportp); + default: + break; + } + + if (asoc) + break; + + ch = (sctp_chunkhdr_t *) ch_end; + chunk_num++; + } while (ch_end < skb_tail_pointer(skb)); + + return asoc; +} + +/* + * There are circumstances when we need to look inside the SCTP packet + * for information to help us find the association. Examples + * include looking inside of INIT/INIT-ACK chunks or after the AUTH + * chunks. + */ +static struct sctp_association *__sctp_rcv_lookup_harder(struct sk_buff *skb, + const union sctp_addr *laddr, + struct sctp_transport **transportp) +{ + sctp_chunkhdr_t *ch; + + ch = (sctp_chunkhdr_t *) skb->data; + + /* 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; + + /* If this is INIT/INIT-ACK look inside the chunk too. */ + switch (ch->type) { + case SCTP_CID_INIT: + case SCTP_CID_INIT_ACK: + return __sctp_rcv_init_lookup(skb, laddr, transportp); + break; + + default: + return __sctp_rcv_walk_lookup(skb, laddr, transportp); + break; + } + + + return NULL; +} + /* Lookup an association for an inbound skb. */ static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, const union sctp_addr *paddr, @@ -971,7 +1086,7 @@ static struct sctp_association *__sctp_rcv_lookup(struct sk_buff *skb, * parameters within the INIT or INIT-ACK. */ if (!asoc) - asoc = __sctp_rcv_init_lookup(skb, laddr, transportp); + asoc = __sctp_rcv_lookup_harder(skb, laddr, transportp); return asoc; }