X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=net%2Fsctp%2Finput.c;h=70d6606e2812e3eaaa9e39206b9c967c5e5e3d7e;hb=43bc926fffd92024b46cafaf7350d669ba9ca884;hp=0268f75c41e58b7b17ec18fdcbd75e3fc8074c6c;hpb=9bf4aaab3e101692164d49b7ca357651eb691cb6;p=linux-2.6.git diff --git a/net/sctp/input.c b/net/sctp/input.c index 0268f75c4..70d6606e2 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -63,11 +63,17 @@ /* 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); + +static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb); /* Calculate the SCTP checksum of an SCTP packet. */ @@ -96,6 +102,17 @@ static inline int sctp_rcv_checksum(struct sk_buff *skb) return 0; } +struct sctp_input_cb { + union { + struct inet_skb_parm h4; +#if defined(CONFIG_IPV6) || defined (CONFIG_IPV6_MODULE) + struct inet6_skb_parm h6; +#endif + } header; + struct sctp_chunk *chunk; +}; +#define SCTP_INPUT_CB(__skb) ((struct sctp_input_cb *)&((__skb)->cb[0])) + /* * This is the routine which IP calls when receiving an SCTP packet. */ @@ -112,7 +129,6 @@ int sctp_rcv(struct sk_buff *skb) union sctp_addr dest; int family; struct sctp_af *af; - int ret = 0; if (skb->pkt_type!=PACKET_HOST) goto discard_it; @@ -130,6 +146,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)) @@ -150,11 +170,38 @@ int sctp_rcv(struct sk_buff *skb) * IP broadcast addresses cannot be used in an SCTP transport * address." */ - if (!af->addr_valid(&src, NULL) || !af->addr_valid(&dest, NULL)) + if (!af->addr_valid(&src, NULL, skb) || + !af->addr_valid(&dest, NULL, skb)) goto discard_it; asoc = __sctp_rcv_lookup(skb, &src, &dest, &transport); + if (!asoc) + ep = __sctp_rcv_lookup_endpoint(&dest); + + /* Retrieve the common input handling substructure. */ + rcvr = asoc ? &asoc->base : &ep->base; + sk = rcvr->sk; + + /* + * If a frame arrives on an interface and the receiving socket is + * bound to another interface, via SO_BINDTODEVICE, treat it as OOTB + */ + if (sk->sk_bound_dev_if && (sk->sk_bound_dev_if != af->skb_iif(skb))) + { + if (asoc) { + sctp_association_put(asoc); + asoc = NULL; + } else { + sctp_endpoint_put(ep); + ep = NULL; + } + sk = sctp_get_ctl_sock(); + ep = sctp_sk(sk)->ep; + sctp_endpoint_hold(ep); + rcvr = &ep->base; + } + /* * RFC 2960, 8.4 - Handle "Out of the blue" Packets. * An SCTP packet is called an "out of the blue" (OOTB) @@ -164,36 +211,30 @@ int sctp_rcv(struct sk_buff *skb) * packet belongs. */ if (!asoc) { - ep = __sctp_rcv_lookup_endpoint(&dest); if (sctp_rcv_ootb(skb)) { SCTP_INC_STATS_BH(SCTP_MIB_OUTOFBLUES); goto discard_release; } } - /* Retrieve the common input handling substructure. */ - rcvr = asoc ? &asoc->base : &ep->base; - sk = rcvr->sk; - /* SCTP seems to always need a timestamp right now (FIXME) */ - if (skb->stamp.tv_sec == 0) { - do_gettimeofday(&skb->stamp); + if (skb->tstamp.off_sec == 0) { + __net_timestamp(skb); sock_enable_timestamp(sk); } if (!xfrm_policy_check(sk, XFRM_POLICY_IN, skb, family)) goto discard_release; + nf_reset(skb); - ret = sk_filter(sk, skb, 1); - if (ret) + if (sk_filter(sk, skb, 1)) goto discard_release; /* Create an SCTP packet structure. */ chunk = sctp_chunkify(skb, asoc, sk); - if (!chunk) { - ret = -ENOMEM; + if (!chunk) goto discard_release; - } + SCTP_INPUT_CB(skb)->chunk = chunk; /* Remember what endpoint is to handle this packet. */ chunk->rcvr = rcvr; @@ -214,80 +255,180 @@ int sctp_rcv(struct sk_buff *skb) sctp_bh_lock_sock(sk); if (sock_owned_by_user(sk)) - sk_add_backlog(sk, (struct sk_buff *) chunk); + sctp_add_backlog(sk, skb); else - sctp_backlog_rcv(sk, (struct sk_buff *) chunk); + sctp_inq_push(&chunk->rcvr->inqueue, chunk); - /* Release the sock and any reference counts we took in the - * lookup calls. - */ sctp_bh_unlock_sock(sk); + + /* Release the asoc/ep ref we took in the lookup calls. */ if (asoc) sctp_association_put(asoc); else sctp_endpoint_put(ep); - sock_put(sk); - return ret; + + return 0; discard_it: kfree_skb(skb); - return ret; + return 0; discard_release: - /* Release any structures we may be holding. */ - if (asoc) { - sock_put(asoc->base.sk); + /* Release the asoc/ep ref we took in the lookup calls. */ + if (asoc) sctp_association_put(asoc); - } else { - sock_put(ep->base.sk); + else sctp_endpoint_put(ep); - } goto discard_it; } -/* Handle second half of inbound skb processing. If the sock was busy, - * we may have need to delay processing until later when the sock is - * released (on the backlog). If not busy, we call this routine - * directly from the bottom half. +/* Process the backlog queue of the socket. Every skb on + * the backlog holds a ref on an association or endpoint. + * We hold this ref throughout the state machine to make + * sure that the structure we need is still around. */ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) { - struct sctp_chunk *chunk; - struct sctp_inq *inqueue; + struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; + struct sctp_inq *inqueue = &chunk->rcvr->inqueue; + struct sctp_ep_common *rcvr = NULL; + int backloged = 0; + + rcvr = chunk->rcvr; - /* One day chunk will live inside the skb, but for - * now this works. + /* If the rcvr is dead then the association or endpoint + * has been deleted and we can safely drop the chunk + * and refs that we are holding. */ - chunk = (struct sctp_chunk *) skb; - inqueue = &chunk->rcvr->inqueue; + if (rcvr->dead) { + sctp_chunk_free(chunk); + goto done; + } + + if (unlikely(rcvr->sk != sk)) { + /* In this case, the association moved from one socket to + * another. We are currently sitting on the backlog of the + * old socket, so we need to move. + * However, since we are here in the process context we + * need to take make sure that the user doesn't own + * the new socket when we process the packet. + * If the new socket is user-owned, queue the chunk to the + * backlog of the new socket without dropping any refs. + * Otherwise, we can safely push the chunk on the inqueue. + */ + + sk = rcvr->sk; + sctp_bh_lock_sock(sk); + + if (sock_owned_by_user(sk)) { + sk_add_backlog(sk, skb); + backloged = 1; + } else + sctp_inq_push(inqueue, chunk); + + sctp_bh_unlock_sock(sk); + + /* If the chunk was backloged again, don't drop refs */ + if (backloged) + return 0; + } else { + sctp_inq_push(inqueue, chunk); + } + +done: + /* Release the refs we took in sctp_add_backlog */ + if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type) + sctp_association_put(sctp_assoc(rcvr)); + else if (SCTP_EP_TYPE_SOCKET == rcvr->type) + sctp_endpoint_put(sctp_ep(rcvr)); + else + BUG(); - sctp_inq_push(inqueue, chunk); return 0; } +static void sctp_add_backlog(struct sock *sk, struct sk_buff *skb) +{ + struct sctp_chunk *chunk = SCTP_INPUT_CB(skb)->chunk; + struct sctp_ep_common *rcvr = chunk->rcvr; + + /* Hold the assoc/ep while hanging on the backlog queue. + * This way, we know structures we need will not disappear from us + */ + if (SCTP_EP_TYPE_ASSOCIATION == rcvr->type) + sctp_association_hold(sctp_assoc(rcvr)); + else if (SCTP_EP_TYPE_SOCKET == rcvr->type) + sctp_endpoint_hold(sctp_ep(rcvr)); + else + BUG(); + + sk_add_backlog(sk, skb); +} + /* Handle icmp frag needed error. */ void sctp_icmp_frag_needed(struct sock *sk, struct sctp_association *asoc, struct sctp_transport *t, __u32 pmtu) { - if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) { - printk(KERN_WARNING "%s: Reported pmtu %d too low, " - "using default minimum of %d\n", __FUNCTION__, pmtu, - SCTP_DEFAULT_MINSEGMENT); - pmtu = SCTP_DEFAULT_MINSEGMENT; - } + if (sock_owned_by_user(sk) || !t || (t->pathmtu == pmtu)) + return; - if (!sock_owned_by_user(sk) && t && (t->pmtu != pmtu)) { - t->pmtu = pmtu; + if (t->param_flags & SPP_PMTUD_ENABLE) { + if (unlikely(pmtu < SCTP_DEFAULT_MINSEGMENT)) { + printk(KERN_WARNING "%s: Reported pmtu %d too low, " + "using default minimum of %d\n", + __FUNCTION__, pmtu, + SCTP_DEFAULT_MINSEGMENT); + /* Use default minimum segment size and disable + * pmtu discovery on this transport. + */ + t->pathmtu = SCTP_DEFAULT_MINSEGMENT; + t->param_flags = (t->param_flags & ~SPP_HB) | + SPP_PMTUD_DISABLE; + } else { + t->pathmtu = pmtu; + } + + /* Update association pmtu. */ sctp_assoc_sync_pmtu(asoc); - sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD); } + + /* Retransmit with the new pmtu setting. + * Normally, if PMTU discovery is disabled, an ICMP Fragmentation + * Needed will never be sent, but if a message was sent before + * PMTU discovery was disabled that was larger than the PMTU, it + * would not be fragmented, so it must be re-transmitted fragmented. + */ + sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD); +} + +/* + * 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_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, t, + GFP_ATOMIC); + } /* Common lookup code for icmp/icmpv6 error handler. */ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, struct sctphdr *sctphdr, - struct sctp_endpoint **epp, struct sctp_association **app, struct sctp_transport **tpp) { @@ -295,11 +436,10 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, union sctp_addr daddr; struct sctp_af *af; struct sock *sk = NULL; - struct sctp_endpoint *ep = NULL; - struct sctp_association *asoc = NULL; + struct sctp_association *asoc; struct sctp_transport *transport = NULL; - *app = NULL; *epp = NULL; *tpp = NULL; + *app = NULL; *tpp = NULL; af = sctp_get_af_specific(family); if (unlikely(!af)) { @@ -314,25 +454,15 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, * packet. */ asoc = __sctp_lookup_association(&saddr, &daddr, &transport); - if (!asoc) { - /* If there is no matching association, see if it matches any - * endpoint. This may happen for an ICMP error generated in - * response to an INIT_ACK. - */ - ep = __sctp_rcv_lookup_endpoint(&daddr); - if (!ep) { - return NULL; - } - } + if (!asoc) + return NULL; - if (asoc) { - 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; + sk = asoc->base.sk; + + if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) { + ICMP_INC_STATS_BH(ICMP_MIB_INERRORS); + goto out; + } sctp_bh_lock_sock(sk); @@ -342,30 +472,22 @@ struct sock *sctp_err_lookup(int family, struct sk_buff *skb, if (sock_owned_by_user(sk)) NET_INC_STATS_BH(LINUX_MIB_LOCKDROPPEDICMPS); - *epp = ep; *app = asoc; *tpp = transport; return sk; out: - sock_put(sk); if (asoc) sctp_association_put(asoc); - if (ep) - sctp_endpoint_put(ep); return NULL; } /* Common cleanup code for icmp/icmpv6 error handler. */ -void sctp_err_finish(struct sock *sk, struct sctp_endpoint *ep, - struct sctp_association *asoc) +void sctp_err_finish(struct sock *sk, struct sctp_association *asoc) { sctp_bh_unlock_sock(sk); - sock_put(sk); if (asoc) sctp_association_put(asoc); - if (ep) - sctp_endpoint_put(ep); } /* @@ -390,10 +512,9 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) int type = skb->h.icmph->type; int code = skb->h.icmph->code; struct sock *sk; - struct sctp_endpoint *ep; - struct sctp_association *asoc; + struct sctp_association *asoc = NULL; struct sctp_transport *transport; - struct inet_opt *inet; + struct inet_sock *inet; char *saveip, *savesctp; int err; @@ -407,7 +528,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) savesctp = skb->h.raw; skb->nh.iph = iph; skb->h.raw = (char *)sh; - sk = sctp_err_lookup(AF_INET, skb, sh, &ep, &asoc, &transport); + sk = sctp_err_lookup(AF_INET, skb, sh, &asoc, &transport); /* Put back, the original pointers. */ skb->nh.raw = saveip; skb->h.raw = savesctp; @@ -432,7 +553,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, asoc, + transport); + goto out_unlock; + } + } err = icmp_err_convert[code].errno; break; case ICMP_TIME_EXCEEDED: @@ -457,7 +584,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) } out_unlock: - sctp_err_finish(sk, ep, asoc); + sctp_err_finish(sk, asoc); } /* @@ -482,7 +609,13 @@ int sctp_rcv_ootb(struct sk_buff *skb) /* Scan through all the chunks in the packet. */ do { - ch_end = ((__u8 *) ch) + WORD_ROUND(ntohs(ch->length)); + /* 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) + break; /* RFC 8.4, 2) If the OOTB packet contains an ABORT chunk, the * receiver MUST silently discard the OOTB packet and take no @@ -522,7 +655,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 +685,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 +717,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; @@ -605,21 +738,12 @@ struct sctp_endpoint *__sctp_rcv_lookup_endpoint(const union sctp_addr *laddr) hit: sctp_endpoint_hold(ep); - sock_hold(epb->sk); read_unlock(&head->lock); 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 +766,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 +799,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) @@ -707,14 +839,14 @@ struct sctp_association *__sctp_lookup_association( hit: *pt = transport; sctp_association_hold(asoc); - sock_hold(epb->sk); read_unlock(&head->lock); return asoc; } /* 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; @@ -734,7 +866,6 @@ int sctp_has_association(const union sctp_addr *laddr, struct sctp_transport *transport; if ((asoc = sctp_lookup_association(laddr, paddr, &transport))) { - sock_put(asoc->base.sk); sctp_association_put(asoc); return 1; } @@ -784,6 +915,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 +960,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)