From d6b6fece20021fee9579323fa094f4bf86be6ef2 Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Tue, 18 Mar 2003 23:03:41 -0600 Subject: [SCTP] Bundle SACK with outgoing DATA. If we are sending DATA, bundle a SACK. Otherwise, we end up always waiting for the delayed SACK rules (timer or every other packet rule) to kick in. --- include/net/sctp/sctp.h | 15 ++++++++++++--- include/net/sctp/structs.h | 9 ++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 27a69518c2f9..7ad60bcd5581 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -123,7 +123,7 @@ */ extern struct sctp_protocol sctp_proto; extern struct sock *sctp_get_ctl_sock(void); -extern int sctp_copy_local_addr_list(struct sctp_protocol *, +extern int sctp_copy_local_addr_list(struct sctp_protocol *, struct sctp_bind_addr *, sctp_scope_t, int priority, int flags); extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); @@ -414,13 +414,22 @@ static inline __s32 sctp_jitter(__u32 rto) sctp_rand ^= (sctp_rand << 12); sctp_rand ^= (sctp_rand >> 20); - /* Choose random number from 0 to rto, then move to -50% ~ +50% - * of rto. + /* Choose random number from 0 to rto, then move to -50% ~ +50% + * of rto. */ ret = sctp_rand % rto - (rto >> 1); return ret; } +/* Break down data chunks at this point. */ +static inline int sctp_frag_point(int pmtu) +{ + pmtu -= SCTP_IP_OVERHEAD + sizeof(struct sctp_data_chunk); + pmtu -= sizeof(struct sctp_sack_chunk); + + return pmtu; +} + /* Walk through a list of TLV parameters. Don't trust the * individual parameter lengths and instead depend on * the chunk length to indicate when to stop. Make sure diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 913c4769b343..e22b333a94b2 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -590,13 +590,16 @@ struct sctp_packet { /* This packet should advertise ECN capability to the network * via the ECT bit. */ - int ecn_capable; + char ecn_capable; /* This packet contains a COOKIE-ECHO chunk. */ - int has_cookie_echo; + char has_cookie_echo; + + /* This packet containsa SACK chunk. */ + char has_sack; /* SCTP cannot fragment this packet. So let ip fragment it. */ - int ipfragok; + char ipfragok; int malloced; }; -- cgit v1.2.3 From a33b43999f950d56dba711d914bff965ebd1394e Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Mon, 24 Mar 2003 22:00:54 -0600 Subject: [SCTP] Add icmpv6 handler to SCTP. --- include/net/sctp/constants.h | 8 +- include/net/sctp/sctp.h | 38 +- net/sctp/input.c | 174 ++-- net/sctp/ipv6.c | 81 +- net/sctp/outqueue.c | 9 +- net/sctp/sm_sideeffect.c | 1936 ++++++++++++++++++++---------------------- 6 files changed, 1131 insertions(+), 1115 deletions(-) (limited to 'include') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 8ddc88ed69fd..0d6a20a7390e 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -138,12 +138,10 @@ typedef enum { */ typedef union { - sctp_cid_t chunk; sctp_event_timeout_t timeout; sctp_event_other_t other; sctp_event_primitive_t primitive; - } sctp_subtype_t; #define SCTP_SUBTYPE_CONSTRUCTOR(_name, _type, _elt) \ @@ -421,9 +419,9 @@ typedef enum { /* Reasons to retransmit. */ typedef enum { - SCTP_RETRANSMIT_T3_RTX, - SCTP_RETRANSMIT_FAST_RTX, - SCTP_RETRANSMIT_PMTU_DISCOVERY, + SCTP_RTXR_T3_RTX, + SCTP_RTXR_FAST_RTX, + SCTP_RTXR_PMTUD, } sctp_retransmit_reason_t; /* Reasons to lower cwnd. */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 7ad60bcd5581..5c003845721c 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -130,7 +130,7 @@ extern struct sctp_pf *sctp_get_pf_specific(sa_family_t family); extern int sctp_register_pf(struct sctp_pf *, sa_family_t); /* - * sctp_socket.c + * sctp/socket.c */ extern int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb); extern int sctp_inet_listen(struct socket *sock, int backlog); @@ -139,7 +139,7 @@ extern unsigned int sctp_poll(struct file *file, struct socket *sock, poll_table *wait); /* - * sctp_primitive.c + * sctp/primitive.c */ extern int sctp_primitive_ASSOCIATE(sctp_association_t *, void *arg); extern int sctp_primitive_SHUTDOWN(sctp_association_t *, void *arg); @@ -148,14 +148,14 @@ extern int sctp_primitive_SEND(sctp_association_t *, void *arg); extern int sctp_primitive_REQUESTHEARTBEAT(sctp_association_t *, void *arg); /* - * sctp_crc32c.c + * sctp/crc32c.c */ extern __u32 sctp_start_cksum(__u8 *ptr, __u16 count); extern __u32 sctp_update_cksum(__u8 *ptr, __u16 count, __u32 cksum); extern __u32 sctp_end_cksum(__u32 cksum); /* - * sctp_input.c + * sctp/input.c */ extern int sctp_rcv(struct sk_buff *skb); extern void sctp_v4_err(struct sk_buff *skb, u32 info); @@ -170,9 +170,16 @@ extern void __sctp_unhash_endpoint(sctp_endpoint_t *); extern sctp_association_t *__sctp_lookup_association(const union sctp_addr *, const union sctp_addr *, struct sctp_transport **); - +extern struct sock *sctp_err_lookup(int family, struct sk_buff *, + struct sctphdr *, struct sctp_endpoint **, + struct sctp_association **, + struct sctp_transport **); +extern void sctp_err_finish(struct sock *, struct sctp_endpoint *, + struct sctp_association *); +extern void sctp_icmp_frag_needed(struct sock *, struct sctp_association *, + struct sctp_transport *t, __u32 pmtu); /* - * sctp_hashdriver.c + * sctp/hashdriver.c */ extern void sctp_hash_digest(const char *secret, const int secret_len, const char *text, const int text_len, @@ -184,9 +191,7 @@ extern void sctp_hash_digest(const char *secret, const int secret_len, #ifdef TEST_FRAME - #include - #else /* spin lock wrappers. */ @@ -312,7 +317,6 @@ static inline void sctp_sysctl_register(void) { return; } static inline void sctp_sysctl_unregister(void) { return; } #endif - /* Size of Supported Address Parameter for 'x' address types. */ #define SCTP_SAT_LEN(x) (sizeof(struct sctp_paramhdr) + (x) * sizeof(__u16)) @@ -320,19 +324,15 @@ static inline void sctp_sysctl_unregister(void) { return; } extern int sctp_v6_init(void); extern void sctp_v6_exit(void); -static inline int sctp_ipv6_addr_type(const struct in6_addr *addr) -{ - return ipv6_addr_type((struct in6_addr*) addr); -} +extern void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info); -#else /* #ifdef defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ +#else /* #ifdef defined(CONFIG_IPV6) */ -#define sctp_ipv6_addr_type(a) 0 static inline int sctp_v6_init(void) { return 0; } static inline void sctp_v6_exit(void) { return; } -#endif /* #ifdef defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ - +#endif /* #if defined(CONFIG_IPV6) */ /* Map an association to an assoc_id. */ static inline sctp_assoc_t sctp_assoc2id(const sctp_association_t *asoc) @@ -546,7 +546,7 @@ struct sctp_sock { struct sock sk; #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct ipv6_pinfo *pinet6; -#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */ +#endif /* CONFIG_IPV6 */ struct inet_opt inet; struct sctp_opt sctp; }; @@ -559,7 +559,7 @@ struct sctp6_sock { struct sctp_opt sctp; struct ipv6_pinfo inet6; }; -#endif /* CONFIG_IPV6 || CONFIG_IPV6_MODULE */ +#endif /* CONFIG_IPV6 */ #define sctp_sk(__sk) (&((struct sctp_sock *)__sk)->sctp) diff --git a/net/sctp/input.c b/net/sctp/input.c index 8e67351f419d..5eff70c91708 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -205,21 +205,19 @@ int sctp_rcv(struct sk_buff *skb) */ sctp_bh_lock_sock(sk); - if (sock_owned_by_user(sk)) { + if (sock_owned_by_user(sk)) sk_add_backlog(sk, (struct sk_buff *) chunk); - } else { + else sctp_backlog_rcv(sk, (struct sk_buff *) chunk); - } /* Release the sock and any reference counts we took in the * lookup calls. */ sctp_bh_unlock_sock(sk); - if (asoc) { + if (asoc) sctp_association_put(asoc); - } else { + else sctp_endpoint_put(ep); - } sock_put(sk); return ret; @@ -266,10 +264,8 @@ int sctp_backlog_rcv(struct sock *sk, struct sk_buff *skb) } /* Handle icmp frag needed error. */ -static inline void sctp_icmp_frag_needed(struct sock *sk, - sctp_association_t *asoc, - struct sctp_transport *transport, - __u32 pmtu) +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, " @@ -278,54 +274,38 @@ static inline void sctp_icmp_frag_needed(struct sock *sk, pmtu = SCTP_DEFAULT_MINSEGMENT; } - if (!sock_owned_by_user(sk) && transport && (transport->pmtu != pmtu)) { - transport->pmtu = pmtu; + if (!sock_owned_by_user(sk) && t && (t->pmtu != pmtu)) { + t->pmtu = pmtu; sctp_assoc_sync_pmtu(asoc); - sctp_retransmit(&asoc->outqueue, transport, - SCTP_RETRANSMIT_PMTU_DISCOVERY ); + sctp_retransmit(&asoc->outqueue, t, SCTP_RTXR_PMTUD); } } -/* - * This routine is called by the ICMP module when it gets some - * sort of error condition. If err < 0 then the socket should - * be closed and the error returned to the user. If err > 0 - * it's just the icmp type << 8 | icmp code. After adjustment - * header points to the first 8 bytes of the sctp header. We need - * to find the appropriate port. - * - * The locking strategy used here is very "optimistic". When - * someone else accesses the socket the ICMP is just dropped - * and for some paths there is no check at all. - * A more general error queue to queue errors for later handling - * is probably better. - * - */ -void sctp_v4_err(struct sk_buff *skb, __u32 info) +/* 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) { - struct iphdr *iph = (struct iphdr *)skb->data; - struct sctphdr *sh = (struct sctphdr *)(skb->data + (iph->ihl <<2)); - int type = skb->h.icmph->type; - int code = skb->h.icmph->code; - union sctp_addr saddr, daddr; - struct inet_opt *inet; + union sctp_addr saddr; + union sctp_addr daddr; + struct sctp_af *af; struct sock *sk = NULL; - sctp_endpoint_t *ep = NULL; - sctp_association_t *asoc = NULL; - struct sctp_transport *transport; - int err; + struct sctp_endpoint *ep = NULL; + struct sctp_association *asoc = NULL; + struct sctp_transport *transport = NULL; - if (skb->len < ((iph->ihl << 2) + 8)) { - ICMP_INC_STATS_BH(IcmpInErrors); - return; + *app = NULL; *epp = NULL; *tpp = NULL; + + af = sctp_get_af_specific(family); + if (unlikely(!af)) { + return NULL; } - saddr.v4.sin_family = AF_INET; - saddr.v4.sin_port = ntohs(sh->source); - memcpy(&saddr.v4.sin_addr.s_addr, &iph->saddr, sizeof(struct in_addr)); - daddr.v4.sin_family = AF_INET; - daddr.v4.sin_port = ntohs(sh->dest); - memcpy(&daddr.v4.sin_addr.s_addr, &iph->daddr, sizeof(struct in_addr)); + /* Initialize local addresses for lookups. */ + af->from_skb(&saddr, skb, 1); + af->from_skb(&daddr, skb, 0); /* Look for an association that matches the incoming ICMP error * packet. @@ -338,13 +318,12 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) */ ep = __sctp_rcv_lookup_endpoint(&daddr); if (!ep) { - ICMP_INC_STATS_BH(IcmpInErrors); - return; + return NULL; } } if (asoc) { - if (ntohl(sh->vtag) != asoc->c.peer_vtag) { + if (ntohl(sctphdr->vtag) != asoc->c.peer_vtag) { ICMP_INC_STATS_BH(IcmpInErrors); goto out; } @@ -353,12 +332,90 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) sk = ep->base.sk; sctp_bh_lock_sock(sk); + /* If too many ICMPs get dropped on busy * servers this needs to be solved differently. */ if (sock_owned_by_user(sk)) NET_INC_STATS_BH(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) +{ + sctp_bh_unlock_sock(sk); + sock_put(sk); + if (asoc) + sctp_association_put(asoc); + if (ep) + sctp_endpoint_put(ep); +} + +/* + * This routine is called by the ICMP module when it gets some + * sort of error condition. If err < 0 then the socket should + * be closed and the error returned to the user. If err > 0 + * it's just the icmp type << 8 | icmp code. After adjustment + * header points to the first 8 bytes of the sctp header. We need + * to find the appropriate port. + * + * The locking strategy used here is very "optimistic". When + * someone else accesses the socket the ICMP is just dropped + * and for some paths there is no check at all. + * A more general error queue to queue errors for later handling + * is probably better. + * + */ +void sctp_v4_err(struct sk_buff *skb, __u32 info) +{ + struct iphdr *iph = (struct iphdr *)skb->data; + struct sctphdr *sh = (struct sctphdr *)(skb->data + (iph->ihl <<2)); + int type = skb->h.icmph->type; + int code = skb->h.icmph->code; + struct sock *sk; + sctp_endpoint_t *ep; + sctp_association_t *asoc; + struct sctp_transport *transport; + struct inet_opt *inet; + char *saveip, *savesctp; + int err; + + if (skb->len < ((iph->ihl << 2) + 8)) { + ICMP_INC_STATS_BH(IcmpInErrors); + return; + } + + /* Fix up skb to look at the embedded net header. */ + saveip = skb->nh.raw; + savesctp = skb->h.raw; + skb->nh.iph = iph; + skb->h.raw = (char *)sh; + sk = sctp_err_lookup(AF_INET, skb, sh, &ep, &asoc, &transport); + /* Put back, the original pointers. */ + skb->nh.raw = saveip; + skb->h.raw = savesctp; + if (!sk) { + ICMP_INC_STATS_BH(IcmpInErrors); + return; + } + /* Warning: The sock lock is held. Remember to call + * sctp_err_finish! + */ + switch (type) { case ICMP_PARAMETERPROB: err = EPROTO; @@ -397,13 +454,7 @@ void sctp_v4_err(struct sk_buff *skb, __u32 info) } out_unlock: - sctp_bh_unlock_sock(sk); -out: - sock_put(sk); - if (asoc) - sctp_association_put(asoc); - if (ep) - sctp_endpoint_put(ep); + sctp_err_finish(sk, ep, asoc); } /* @@ -780,8 +831,3 @@ sctp_association_t *__sctp_rcv_lookup(struct sk_buff *skb, return asoc; } - - - - - diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 1133d3fd93bb..3c36fbf6070f 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -1,7 +1,7 @@ /* SCTP kernel reference Implementation * Copyright (c) 2001 Nokia, Inc. * Copyright (c) 2001 La Monte H.P. Yarroll - * Copyright (c) 2002 International Business Machines, Corp. + * Copyright (c) 2002-2003 International Business Machines, Corp. * * This file is part of the SCTP kernel reference Implementation * @@ -88,17 +88,62 @@ extern struct notifier_block sctp_inetaddr_notifier; ntohs((addr)->s6_addr16[6]), \ ntohs((addr)->s6_addr16[7]) -/* FIXME: Comments. */ -static inline void sctp_v6_err(struct sk_buff *skb, - struct inet6_skb_parm *opt, - int type, int code, int offset, __u32 info) +/* ICMP error handler. */ +void sctp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, + int type, int code, int offset, __u32 info) { - /* BUG. WRITE ME. */ + struct ipv6hdr *iph = (struct ipv6hdr *)skb->data; + struct sctphdr *sh = (struct sctphdr *)(skb->data + offset); + struct sock *sk; + sctp_endpoint_t *ep; + sctp_association_t *asoc; + struct sctp_transport *transport; + struct ipv6_pinfo *np; + char *saveip, *savesctp; + int err; + + /* Fix up skb to look at the embedded net header. */ + saveip = skb->nh.raw; + savesctp = skb->h.raw; + skb->nh.ipv6h = iph; + skb->h.raw = (char *)sh; + sk = sctp_err_lookup(AF_INET6, skb, sh, &ep, &asoc, &transport); + /* Put back, the original pointers. */ + skb->nh.raw = saveip; + skb->h.raw = savesctp; + if (!sk) { + ICMP6_INC_STATS_BH(Icmp6InErrors); + return; + } + + /* Warning: The sock lock is held. Remember to call + * sctp_err_finish! + */ + + switch (type) { + case ICMPV6_PKT_TOOBIG: + sctp_icmp_frag_needed(sk, asoc, transport, ntohl(info)); + goto out_unlock; + default: + break; + } + + np = inet6_sk(sk); + icmpv6_err_convert(type, code, &err); + if (!sock_owned_by_user(sk) && np->recverr) { + sk->err = err; + sk->error_report(sk); + } else { /* Only an error on timeout */ + sk->err_soft = err; + } + +out_unlock: + sctp_err_finish(sk, ep, asoc); } /* Based on tcp_v6_xmit() in tcp_ipv6.c. */ -static inline int sctp_v6_xmit(struct sk_buff *skb, - struct sctp_transport *transport, int ipfragok) +static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport, + int ipfragok) { struct sock *sk = skb->sk; struct ipv6_pinfo *np = inet6_sk(sk); @@ -110,9 +155,9 @@ static inline int sctp_v6_xmit(struct sk_buff *skb, /* Fill in the dest address from the route entry passed with the skb * and the source address from the transport. - */ + */ fl.fl6_dst = &rt6->rt6i_dst.addr; - fl.fl6_src = &transport->saddr.v6.sin6_addr; + fl.fl6_src = &transport->saddr.v6.sin6_addr; fl.fl6_flowlabel = np->flow_label; IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel); @@ -174,7 +219,7 @@ struct dst_entry *sctp_v6_get_dst(sctp_association_t *asoc, /* Returns the number of consecutive initial bits that match in the 2 ipv6 * addresses. - */ + */ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, union sctp_addr *s2) { @@ -186,7 +231,7 @@ static inline int sctp_v6_addr_match_len(union sctp_addr *s1, __u32 a1xora2; a1xora2 = a1->s6_addr32[i] ^ a2->s6_addr32[i]; - + if ((j = fls(ntohl(a1xora2)))) return (i * 32 + 32 - j); } @@ -196,7 +241,7 @@ 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(sctp_association_t *asoc, struct dst_entry *dst, union sctp_addr *daddr, union sctp_addr *saddr) { @@ -432,7 +477,7 @@ static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) return retval; } -/* Create and initialize a new sk for the socket to be returned by accept(). */ +/* 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) { @@ -469,11 +514,11 @@ struct sock *sctp_v6_create_accept_sk(struct sock *sk, memcpy(newnp, np, sizeof(struct ipv6_pinfo)); - ipv6_addr_copy(&newnp->daddr, &asoc->peer.primary_addr.v6.sin6_addr); + ipv6_addr_copy(&newnp->daddr, &asoc->peer.primary_addr.v6.sin6_addr); newinet->sport = inet->sport; newinet->dport = asoc->peer.port; - + #ifdef INET_REFCNT_DEBUG atomic_inc(&inet6_sock_nr); atomic_inc(&inet_sock_nr); @@ -623,11 +668,11 @@ static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) /* Fill in Supported Address Type information for INIT and INIT-ACK * chunks. Note: In the future, we may want to look at sock options * to determine whether a PF_INET6 socket really wants to have IPV4 - * addresses. + * addresses. * Returns number of addresses supported. */ static int sctp_inet6_supported_addrs(const struct sctp_opt *opt, - __u16 *types) + __u16 *types) { types[0] = SCTP_PARAM_IPV4_ADDRESS; types[1] = SCTP_PARAM_IPV6_ADDRESS; diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index e2b2295a678c..87bad9667e66 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -357,7 +357,7 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, __u8 fast_retransmit = 0; switch(reason) { - case SCTP_RETRANSMIT_T3_RTX: + case SCTP_RTXR_T3_RTX: sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_T3_RTX); /* Update the retran path if the T3-rtx timer has expired for * the current retran path. @@ -365,10 +365,11 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, if (transport == transport->asoc->peer.retran_path) sctp_assoc_update_retran_path(transport->asoc); break; - case SCTP_RETRANSMIT_FAST_RTX: + case SCTP_RTXR_FAST_RTX: sctp_transport_lower_cwnd(transport, SCTP_LOWER_CWND_FAST_RTX); fast_retransmit = 1; break; + case SCTP_RTXR_PMTUD: default: break; } @@ -876,7 +877,7 @@ int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout) start_timer = 0; queue = &q->out; - while (chunk = sctp_outq_dequeue_data(q)) { + while ((chunk = sctp_outq_dequeue_data(q))) { /* RFC 2960 6.5 Every DATA chunk MUST carry a valid * stream identifier. */ @@ -1570,7 +1571,7 @@ static void sctp_check_transmitted(struct sctp_outq *q, if (transport) { if (do_fast_retransmit) - sctp_retransmit(q, transport, SCTP_RETRANSMIT_FAST_RTX); + sctp_retransmit(q, transport, SCTP_RTXR_FAST_RTX); SCTP_DEBUG_PRINTK("%s: transport: %p, cwnd: %d, " "ssthresh: %d, flight_size: %d, pba: %d\n", diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 004b6d2f0b03..63cad2d13e07 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -55,1202 +55,1128 @@ #include #include -/* Do forward declarations of static functions. */ -static void sctp_do_ecn_ce_work(sctp_association_t *,__u32 lowest_tsn); -static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc, - __u32 lowest_tsn, - sctp_chunk_t *); -static void sctp_do_ecn_cwr_work(sctp_association_t *,__u32 lowest_tsn); -static void sctp_do_8_2_transport_strike(sctp_association_t *, - struct sctp_transport *); -static void sctp_cmd_init_failed(sctp_cmd_seq_t *, sctp_association_t *); -static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *, sctp_association_t *, - sctp_event_t, sctp_subtype_t, - sctp_chunk_t *chunk); -static int sctp_cmd_process_init(sctp_cmd_seq_t *, sctp_association_t *, - sctp_chunk_t *chunk, - sctp_init_chunk_t *peer_init, - int priority); -static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *, sctp_association_t *); -static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *, sctp_association_t *); -static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *, sctp_association_t *, - struct sctp_transport *); -static void sctp_cmd_transport_reset(sctp_cmd_seq_t *, sctp_association_t *, - struct sctp_transport *); -static void sctp_cmd_transport_on(sctp_cmd_seq_t *, sctp_association_t *, - struct sctp_transport *, sctp_chunk_t *); -static int sctp_cmd_process_sack(sctp_cmd_seq_t *, sctp_association_t *, - sctp_sackhdr_t *); -static void sctp_cmd_setup_t2(sctp_cmd_seq_t *, sctp_association_t *, - sctp_chunk_t *); -static void sctp_cmd_new_state(sctp_cmd_seq_t *, sctp_association_t *, - sctp_state_t); - -/* These three macros allow us to pull the debugging code out of the - * main flow of sctp_do_sm() to keep attention focused on the real - * functionality there. - */ -#define DEBUG_PRE \ - SCTP_DEBUG_PRINTK("sctp_do_sm prefn: " \ - "ep %p, %s, %s, asoc %p[%s], %s\n", \ - ep, sctp_evttype_tbl[event_type], \ - (*debug_fn)(subtype), asoc, \ - sctp_state_tbl[state], state_fn->name) +/******************************************************************** + * Helper functions + ********************************************************************/ -#define DEBUG_POST \ - SCTP_DEBUG_PRINTK("sctp_do_sm postfn: " \ - "asoc %p, status: %s\n", \ - asoc, sctp_status_tbl[status]) +/* A helper function for delayed processing of INET ECN CE bit. */ +static void sctp_do_ecn_ce_work(sctp_association_t *asoc, __u32 lowest_tsn) +{ + /* Save the TSN away for comparison when we receive CWR */ -#define DEBUG_POST_SFX \ - SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ - error, asoc, \ - sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \ - sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED]) + asoc->last_ecne_tsn = lowest_tsn; + asoc->need_ecne = 1; +} -/* - * This is the master state machine processing function. +/* Helper function for delayed processing of SCTP ECNE chunk. */ +/* RFC 2960 Appendix A * - * If you want to understand all of lksctp, this is a - * good place to start. + * RFC 2481 details a specific bit for a sender to send in + * the header of its next outbound TCP segment to indicate to + * its peer that it has reduced its congestion window. This + * is termed the CWR bit. For SCTP the same indication is made + * by including the CWR chunk. This chunk contains one data + * element, i.e. the TSN number that was sent in the ECNE chunk. + * This element represents the lowest TSN number in the datagram + * that was originally marked with the CE bit. */ -int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, - sctp_endpoint_t *ep, - sctp_association_t *asoc, - void *event_arg, - int priority) +static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc, + __u32 lowest_tsn, + sctp_chunk_t *chunk) { - sctp_cmd_seq_t commands; - sctp_sm_table_entry_t *state_fn; - sctp_disposition_t status; - int error = 0; - typedef const char *(printfn_t)(sctp_subtype_t); + sctp_chunk_t *repl; - static printfn_t *table[] = { - NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname, - }; - printfn_t *debug_fn __attribute__ ((unused)) = table[event_type]; + /* Our previously transmitted packet ran into some congestion + * so we should take action by reducing cwnd and ssthresh + * and then ACK our peer that we we've done so by + * sending a CWR. + */ - /* Look up the state function, run it, and then process the - * side effects. These three steps are the heart of lksctp. + /* First, try to determine if we want to actually lower + * our cwnd variables. Only lower them if the ECNE looks more + * recent than the last response. */ - state_fn = sctp_sm_lookup_event(event_type, state, subtype); + if (TSN_lt(asoc->last_cwr_tsn, lowest_tsn)) { + struct sctp_transport *transport; - sctp_init_cmd_seq(&commands); + /* Find which transport's congestion variables + * need to be adjusted. + */ + transport = sctp_assoc_lookup_tsn(asoc, lowest_tsn); - DEBUG_PRE; - status = (*state_fn->fn)(ep, asoc, subtype, event_arg, &commands); - DEBUG_POST; + /* Update the congestion variables. */ + if (transport) + sctp_transport_lower_cwnd(transport, + SCTP_LOWER_CWND_ECNE); + asoc->last_cwr_tsn = lowest_tsn; + } - error = sctp_side_effects(event_type, subtype, state, - ep, asoc, event_arg, - status, &commands, - priority); - DEBUG_POST_SFX; + /* Always try to quiet the other end. In case of lost CWR, + * resend last_cwr_tsn. + */ + repl = sctp_make_cwr(asoc, asoc->last_cwr_tsn, chunk); - return error; + /* If we run out of memory, it will look like a lost CWR. We'll + * get back in sync eventually. + */ + return repl; } -#undef DEBUG_PRE -#undef DEBUG_POST - -/***************************************************************** - * 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, - sctp_endpoint_t *ep, - sctp_association_t *asoc, - void *event_arg, - sctp_disposition_t status, - sctp_cmd_seq_t *commands, - int priority) +/* Helper function to do delayed processing of ECN CWR chunk. */ +static void sctp_do_ecn_cwr_work(sctp_association_t *asoc, + __u32 lowest_tsn) { - int error; - - /* FIXME - Most of the dispositions left today would be categorized - * as "exceptional" dispositions. For those dispositions, it - * may not be proper to run through any of the commands at all. - * For example, the command interpreter might be run only with - * disposition SCTP_DISPOSITION_CONSUME. + /* Turn off ECNE getting auto-prepended to every outgoing + * packet */ - if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state, - ep, asoc, - event_arg, status, - commands, priority))) - goto bail; - - switch (status) { - case SCTP_DISPOSITION_DISCARD: - SCTP_DEBUG_PRINTK("Ignored sctp protocol event - state %d, " - "event_type %d, event_id %d\n", - state, event_type, subtype.chunk); - break; + asoc->need_ecne = 0; +} - case SCTP_DISPOSITION_NOMEM: - /* We ran out of memory, so we need to discard this - * packet. - */ - /* BUG--we should now recover some memory, probably by - * reneging... - */ - error = -ENOMEM; - break; +/* 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) +{ + __u32 ctsn, max_tsn_seen; + struct sctp_chunk *sack; + int error = 0; - case SCTP_DISPOSITION_DELETE_TCB: - /* This should now be a command. */ - break; + if (force) + asoc->peer.sack_needed = 1; - case SCTP_DISPOSITION_CONSUME: - case SCTP_DISPOSITION_ABORT: - /* - * We should no longer have much work to do here as the - * real work has been done as explicit commands above. - */ - break; + ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map); + max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); - case SCTP_DISPOSITION_VIOLATION: - printk(KERN_ERR "sctp protocol violation state %d " - "chunkid %d\n", state, subtype.chunk); - break; + /* From 12.2 Parameters necessary per association (i.e. the TCB): + * + * Ack State : This flag indicates if the next received packet + * : is to be responded to with a SACK. ... + * : When DATA chunks are out of order, SACK's + * : are not delayed (see Section 6). + * + * [This is actually not mentioned in Section 6, but we + * implement it here anyway. --piggy] + */ + if (max_tsn_seen != ctsn) + asoc->peer.sack_needed = 1; - case SCTP_DISPOSITION_NOT_IMPL: - printk(KERN_WARNING "sctp unimplemented feature in state %d, " - "event_type %d, event_id %d\n", - state, event_type, subtype.chunk); - break; + /* From 6.2 Acknowledgement on Reception of DATA Chunks: + * + * Section 4.2 of [RFC2581] SHOULD be followed. Specifically, + * an acknowledgement SHOULD be generated for at least every + * second packet (not every second DATA chunk) received, and + * SHOULD be generated within 200 ms of the arrival of any + * unacknowledged DATA chunk. ... + */ + if (!asoc->peer.sack_needed) { + /* We will need a SACK for the next packet. */ + asoc->peer.sack_needed = 1; + goto out; + } else { + if (asoc->a_rwnd > asoc->rwnd) + asoc->a_rwnd = asoc->rwnd; + sack = sctp_make_sack(asoc); + if (!sack) + goto nomem; - case SCTP_DISPOSITION_BUG: - printk(KERN_ERR "sctp bug in state %d, " - "event_type %d, event_id %d\n", - state, event_type, subtype.chunk); - BUG(); - break; + asoc->peer.sack_needed = 0; - default: - printk(KERN_ERR "sctp impossible disposition %d " - "in state %d, event_type %d, event_id %d\n", - status, state, event_type, subtype.chunk); - BUG(); - break; - }; + error = sctp_outq_tail(&asoc->outqueue, sack); -bail: + /* Stop the SACK timer. */ + sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, + SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); + } +out: + return error; +nomem: + error = -ENOMEM; return error; } -/******************************************************************** - * 2nd Level Abstractions - ********************************************************************/ - -/* This is the side-effect interpreter. */ -int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, sctp_endpoint_t *ep, - sctp_association_t *asoc, void *event_arg, - sctp_disposition_t status, sctp_cmd_seq_t *commands, - int priority) +/* When the T3-RTX timer expires, it calls this function to create the + * relevant state machine event. + */ +void sctp_generate_t3_rtx_event(unsigned long peer) { - int error = 0; - int force; - sctp_cmd_t *cmd; - sctp_chunk_t *new_obj; - sctp_chunk_t *chunk = NULL; - struct sctp_packet *packet; - struct list_head *pos; - struct timer_list *timer; - unsigned long timeout; - struct sctp_transport *t; - sctp_sackhdr_t sackh; + int error; + struct sctp_transport *transport = (struct sctp_transport *) peer; + sctp_association_t *asoc = transport->asoc; - if(SCTP_EVENT_T_TIMEOUT != event_type) - chunk = (sctp_chunk_t *) event_arg; + /* Check whether a task is in the sock. */ - /* Note: This whole file is a huge candidate for rework. - * For example, each command could either have its own handler, so - * the loop would look like: - * while (cmds) - * cmd->handle(x, y, z) - * --jgrimm - */ - while (NULL != (cmd = sctp_next_cmd(commands))) { - switch (cmd->verb) { - case SCTP_CMD_NOP: - /* Do nothing. */ - break; + sctp_bh_lock_sock(asoc->base.sk); + if (sock_owned_by_user(asoc->base.sk)) { + SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __FUNCTION__); - case SCTP_CMD_NEW_ASOC: - /* Register a new association. */ - asoc = cmd->obj.ptr; - /* Register with the endpoint. */ - sctp_endpoint_add_asoc(ep, asoc); - sctp_hash_established(asoc); - break; + /* Try again later. */ + if (!mod_timer(&transport->T3_rtx_timer, jiffies + (HZ/20))) + sctp_transport_hold(transport); + goto out_unlock; + } - case SCTP_CMD_UPDATE_ASSOC: - sctp_assoc_update(asoc, cmd->obj.ptr); - break; + /* Is this transport really dead and just waiting around for + * the timer to let go of the reference? + */ + if (transport->dead) + goto out_unlock; - case SCTP_CMD_PURGE_OUTQUEUE: - sctp_outq_teardown(&asoc->outqueue); - break; + /* Run through the state machine. */ + error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_T3_RTX), + asoc->state, + asoc->ep, asoc, + transport, GFP_ATOMIC); - case SCTP_CMD_DELETE_TCB: - /* Delete the current association. */ - sctp_unhash_established(asoc); - sctp_association_free(asoc); - asoc = NULL; - break; + if (error) + asoc->base.sk->err = -error; - case SCTP_CMD_NEW_STATE: - /* Enter a new state. */ - sctp_cmd_new_state(commands, asoc, cmd->obj.state); - break; +out_unlock: + sctp_bh_unlock_sock(asoc->base.sk); + sctp_transport_put(transport); +} - case SCTP_CMD_REPORT_TSN: - /* Record the arrival of a TSN. */ - sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32); - break; +/* This is a sa interface for producing timeout events. It works + * for timeouts which use the association as their parameter. + */ +static void sctp_generate_timeout_event(sctp_association_t *asoc, + sctp_event_timeout_t timeout_type) +{ + int error = 0; - case SCTP_CMD_GEN_SACK: - /* Generate a Selective ACK. - * The argument tells us whether to just count - * the packet and MAYBE generate a SACK, or - * force a SACK out. - */ - force = cmd->obj.i32; - error = sctp_gen_sack(asoc, force, commands); - break; + sctp_bh_lock_sock(asoc->base.sk); + if (sock_owned_by_user(asoc->base.sk)) { + SCTP_DEBUG_PRINTK("%s:Sock is busy: timer %d\n", + __FUNCTION__, + timeout_type); - case SCTP_CMD_PROCESS_SACK: - /* Process an inbound SACK. */ - error = sctp_cmd_process_sack(commands, asoc, - cmd->obj.ptr); - break; + /* Try again later. */ + if (!mod_timer(&asoc->timers[timeout_type], jiffies + (HZ/20))) + sctp_association_hold(asoc); + goto out_unlock; + } - case SCTP_CMD_GEN_INIT_ACK: - /* Generate an INIT ACK chunk. */ - new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC, - 0); - if (!new_obj) - goto nomem; + /* Is this association really dead and just waiting around for + * the timer to let go of the reference? + */ + if (asoc->base.dead) + goto out_unlock; - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, - SCTP_CHUNK(new_obj)); - break; + /* Run through the state machine. */ + error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + SCTP_ST_TIMEOUT(timeout_type), + asoc->state, asoc->ep, asoc, + (void *)timeout_type, + GFP_ATOMIC); - case SCTP_CMD_PEER_INIT: - /* Process a unified INIT from the peer. - * Note: Only used during INIT-ACK processing. If - * there is an error just return to the outter - * layer which will bail. - */ - error = sctp_cmd_process_init(commands, asoc, chunk, - cmd->obj.ptr, priority); - break; + if (error) + asoc->base.sk->err = -error; - case SCTP_CMD_GEN_COOKIE_ECHO: - /* Generate a COOKIE ECHO chunk. */ - new_obj = sctp_make_cookie_echo(asoc, chunk); - if (!new_obj) { - if (cmd->obj.ptr) - sctp_free_chunk(cmd->obj.ptr); - goto nomem; - } - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, - SCTP_CHUNK(new_obj)); +out_unlock: + sctp_bh_unlock_sock(asoc->base.sk); + sctp_association_put(asoc); +} - /* If there is an ERROR chunk to be sent along with - * the COOKIE_ECHO, send it, too. - */ - if (cmd->obj.ptr) - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, - SCTP_CHUNK(cmd->obj.ptr)); - break; +void sctp_generate_t1_cookie_event(unsigned long data) +{ + sctp_association_t *asoc = (sctp_association_t *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_COOKIE); +} - case SCTP_CMD_GEN_SHUTDOWN: - /* Generate SHUTDOWN when in SHUTDOWN_SENT state. - * Reset error counts. - */ - asoc->overall_error_count = 0; +void sctp_generate_t1_init_event(unsigned long data) +{ + sctp_association_t *asoc = (sctp_association_t *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_INIT); +} - /* Generate a SHUTDOWN chunk. */ - new_obj = sctp_make_shutdown(asoc); - if (!new_obj) - goto nomem; - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, - SCTP_CHUNK(new_obj)); - break; +void sctp_generate_t2_shutdown_event(unsigned long data) +{ + sctp_association_t *asoc = (sctp_association_t *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN); +} - case SCTP_CMD_CHUNK_ULP: - /* Send a chunk to the sockets layer. */ - SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", - "chunk_up:", cmd->obj.ptr, - "ulpq:", &asoc->ulpq); - sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.ptr, - GFP_ATOMIC); - break; +void sctp_generate_t5_shutdown_guard_event(unsigned long data) +{ + sctp_association_t *asoc = (sctp_association_t *)data; + sctp_generate_timeout_event(asoc, + SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD); - case SCTP_CMD_EVENT_ULP: - /* Send a notification to the sockets layer. */ - SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", - "event_up:",cmd->obj.ptr, - "ulpq:",&asoc->ulpq); - sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ptr); - break; +} /* sctp_generate_t5_shutdown_guard_event() */ - case SCTP_CMD_REPLY: - /* Send a chunk to our peer. */ - error = sctp_outq_tail(&asoc->outqueue, - cmd->obj.ptr); - break; +void sctp_generate_autoclose_event(unsigned long data) +{ + sctp_association_t *asoc = (sctp_association_t *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE); +} - case SCTP_CMD_SEND_PKT: - /* Send a full packet to our peer. */ - packet = cmd->obj.ptr; - sctp_packet_transmit(packet); - sctp_ootb_pkt_free(packet); - break; +/* Generate a heart beat event. If the sock is busy, reschedule. Make + * sure that the transport is still valid. + */ +void sctp_generate_heartbeat_event(unsigned long data) +{ + int error = 0; + struct sctp_transport *transport = (struct sctp_transport *) data; + sctp_association_t *asoc = transport->asoc; - case SCTP_CMD_RETRAN: - /* Mark a transport for retransmission. */ - sctp_retransmit(&asoc->outqueue, cmd->obj.transport, - SCTP_RETRANSMIT_T3_RTX); - break; + sctp_bh_lock_sock(asoc->base.sk); + if (sock_owned_by_user(asoc->base.sk)) { + SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __FUNCTION__); - case SCTP_CMD_TRANSMIT: - /* Kick start transmission. */ - error = sctp_outq_flush(&asoc->outqueue, 0); - break; + /* Try again later. */ + if (!mod_timer(&transport->hb_timer, jiffies + (HZ/20))) + sctp_transport_hold(transport); + goto out_unlock; + } - case SCTP_CMD_ECN_CE: - /* Do delayed CE processing. */ - sctp_do_ecn_ce_work(asoc, cmd->obj.u32); - break; + /* Is this structure just waiting around for us to actually + * get destroyed? + */ + if (transport->dead) + goto out_unlock; - case SCTP_CMD_ECN_ECNE: - /* Do delayed ECNE processing. */ - new_obj = sctp_do_ecn_ecne_work(asoc, cmd->obj.u32, - chunk); - if (new_obj) - sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, - SCTP_CHUNK(new_obj)); - break; + error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, + SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_HEARTBEAT), + asoc->state, + asoc->ep, asoc, + transport, GFP_ATOMIC); - case SCTP_CMD_ECN_CWR: - /* Do delayed CWR processing. */ - sctp_do_ecn_cwr_work(asoc, cmd->obj.u32); - break; + if (error) + asoc->base.sk->err = -error; - case SCTP_CMD_SETUP_T2: - sctp_cmd_setup_t2(commands, asoc, cmd->obj.ptr); - break; +out_unlock: + sctp_bh_unlock_sock(asoc->base.sk); + sctp_transport_put(transport); +} - case SCTP_CMD_TIMER_START: - timer = &asoc->timers[cmd->obj.to]; - timeout = asoc->timeouts[cmd->obj.to]; - if (!timeout) - BUG(); +/* Inject a SACK Timeout event into the state machine. */ +void sctp_generate_sack_event(unsigned long data) +{ + sctp_association_t *asoc = (sctp_association_t *) data; + sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK); +} - timer->expires = jiffies + timeout; - sctp_association_hold(asoc); - add_timer(timer); - break; +sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { + NULL, + sctp_generate_t1_cookie_event, + sctp_generate_t1_init_event, + sctp_generate_t2_shutdown_event, + NULL, + sctp_generate_t5_shutdown_guard_event, + sctp_generate_heartbeat_event, + sctp_generate_sack_event, + sctp_generate_autoclose_event, +}; - case SCTP_CMD_TIMER_RESTART: - timer = &asoc->timers[cmd->obj.to]; - timeout = asoc->timeouts[cmd->obj.to]; - if (!mod_timer(timer, jiffies + timeout)) - sctp_association_hold(asoc); - break; - case SCTP_CMD_TIMER_STOP: - timer = &asoc->timers[cmd->obj.to]; - if (timer_pending(timer) && del_timer(timer)) - sctp_association_put(asoc); - break; +/* RFC 2960 8.2 Path Failure Detection + * + * When its peer endpoint is multi-homed, an endpoint should keep a + * error counter for each of the destination transport addresses of the + * peer endpoint. + * + * Each time the T3-rtx timer expires on any address, or when a + * HEARTBEAT sent to an idle address is not acknowledged within a RTO, + * the error counter of that destination address will be incremented. + * When the value in the error counter exceeds the protocol parameter + * 'Path.Max.Retrans' of that destination address, the endpoint should + * mark the destination transport address as inactive, and a + * notification SHOULD be sent to the upper layer. + * + */ +static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, + struct sctp_transport *transport) +{ + /* The check for association's overall error counter exceeding the + * threshold is done in the state function. + */ + asoc->overall_error_count++; - case SCTP_CMD_INIT_RESTART: - /* Do the needed accounting and updates - * associated with restarting an initialization - * timer. - */ - asoc->counters[SCTP_COUNTER_INIT_ERROR]++; - asoc->timeouts[cmd->obj.to] *= 2; - if (asoc->timeouts[cmd->obj.to] > - asoc->max_init_timeo) { - asoc->timeouts[cmd->obj.to] = - asoc->max_init_timeo; - } + if (transport->active && + (transport->error_count++ >= transport->error_threshold)) { + SCTP_DEBUG_PRINTK("transport_strike: transport " + "IP:%d.%d.%d.%d failed.\n", + NIPQUAD(transport->ipaddr.v4.sin_addr)); + sctp_assoc_control_transport(asoc, transport, + SCTP_TRANSPORT_DOWN, + SCTP_FAILED_THRESHOLD); + } - /* If we've sent any data bundled with - * COOKIE-ECHO we need to resend. - */ - list_for_each(pos, &asoc->peer.transport_addr_list) { - t = list_entry(pos, struct sctp_transport, - transports); - sctp_retransmit_mark(&asoc->outqueue, t, 0); - } + /* E2) For the destination address for which the timer + * expires, set RTO <- RTO * 2 ("back off the timer"). The + * maximum value discussed in rule C7 above (RTO.max) may be + * used to provide an upper bound to this doubling operation. + */ + transport->rto = min((transport->rto * 2), transport->asoc->rto_max); +} - sctp_add_cmd_sf(commands, - SCTP_CMD_TIMER_RESTART, - SCTP_TO(cmd->obj.to)); - break; +/* Worker routine to handle INIT command failure. */ +static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, + sctp_association_t *asoc) +{ + struct sctp_ulpevent *event; - case SCTP_CMD_INIT_FAILED: - sctp_cmd_init_failed(commands, asoc); - break; + event = sctp_ulpevent_make_assoc_change(asoc,0, SCTP_CANT_STR_ASSOC, + 0, 0, 0, GFP_ATOMIC); - case SCTP_CMD_ASSOC_FAILED: - sctp_cmd_assoc_failed(commands, asoc, event_type, - subtype, chunk); - break; + if (event) + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(event)); - case SCTP_CMD_COUNTER_INC: - asoc->counters[cmd->obj.counter]++; - break; + /* FIXME: We need to handle data possibly either + * sent via COOKIE-ECHO bundling or just waiting in + * the transmit queue, if the user has enabled + * SEND_FAILED notifications. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); +} - case SCTP_CMD_COUNTER_RESET: - asoc->counters[cmd->obj.counter] = 0; - break; +/* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */ +static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, + sctp_association_t *asoc, + sctp_event_t event_type, + sctp_subtype_t subtype, + sctp_chunk_t *chunk) +{ + struct sctp_ulpevent *event; + __u16 error = 0; - case SCTP_CMD_REPORT_DUP: - sctp_tsnmap_mark_dup(&asoc->peer.tsn_map, - cmd->obj.u32); - break; + switch(event_type) { + case SCTP_EVENT_T_PRIMITIVE: + if (SCTP_PRIMITIVE_ABORT == subtype.primitive) + error = SCTP_ERROR_USER_ABORT; + break; + case SCTP_EVENT_T_CHUNK: + if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) && + (ntohs(chunk->chunk_hdr->length) >= + (sizeof(struct sctp_chunkhdr) + + sizeof(struct sctp_errhdr)))) { + error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + } + break; + default: + break; + } - case SCTP_CMD_REPORT_BAD_TAG: - SCTP_DEBUG_PRINTK("vtag mismatch!\n"); - break; + /* Cancel any partial delivery in progress. */ + sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); - case SCTP_CMD_STRIKE: - /* Mark one strike against a transport. */ - sctp_do_8_2_transport_strike(asoc, cmd->obj.transport); - break; + event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST, + error, 0, 0, GFP_ATOMIC); + if (event) + sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, + SCTP_ULPEVENT(event)); - case SCTP_CMD_TRANSPORT_RESET: - t = cmd->obj.transport; - sctp_cmd_transport_reset(commands, asoc, t); - break; + sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, + SCTP_STATE(SCTP_STATE_CLOSED)); - case SCTP_CMD_TRANSPORT_ON: - t = cmd->obj.transport; - sctp_cmd_transport_on(commands, asoc, t, chunk); - break; + /* FIXME: We need to handle data that could not be sent or was not + * acked, if the user has enabled SEND_FAILED notifications. + */ + sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); +} - case SCTP_CMD_HB_TIMERS_START: - sctp_cmd_hb_timers_start(commands, asoc); - break; +/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT + * inside the cookie. In reality, this is only used for INIT-ACK processing + * since all other cases use "temporary" associations and can do all + * their work in statefuns directly. + */ +static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, + sctp_association_t *asoc, + sctp_chunk_t *chunk, + sctp_init_chunk_t *peer_init, + int priority) +{ + int error; - case SCTP_CMD_HB_TIMER_UPDATE: - t = cmd->obj.transport; - sctp_cmd_hb_timer_update(commands, asoc, t); - break; + /* We only process the init as a sideeffect in a single + * case. This is when we process the INIT-ACK. If we + * fail during INIT processing (due to malloc problems), + * just return the error and stop processing the stack. + */ - case SCTP_CMD_HB_TIMERS_STOP: - sctp_cmd_hb_timers_stop(commands, asoc); - break; + if (!sctp_process_init(asoc, chunk->chunk_hdr->type, + sctp_source(chunk), peer_init, + priority)) + error = -ENOMEM; + else + error = 0; - case SCTP_CMD_REPORT_ERROR: - error = cmd->obj.error; - break; + return error; +} - case SCTP_CMD_PROCESS_CTSN: - /* Dummy up a SACK for processing. */ - sackh.cum_tsn_ack = cmd->obj.u32; - sackh.a_rwnd = 0; - sackh.num_gap_ack_blocks = 0; - sackh.num_dup_tsns = 0; - sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, - SCTP_SACKH(&sackh)); - break; +/* Helper function to break out starting up of heartbeat timers. */ +static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, + sctp_association_t *asoc) +{ + struct sctp_transport *t; + struct list_head *pos; - case SCTP_CMD_DISCARD_PACKET: - /* We need to discard the whole packet. */ - chunk->pdiscard = 1; - break; + /* Start a heartbeat timer for each transport on the association. + * hold a reference on the transport to make sure none of + * the needed data structures go away. + */ + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, transports); - case SCTP_CMD_RTO_PENDING: - t = cmd->obj.transport; - t->rto_pending = 1; - break; + if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) + sctp_transport_hold(t); + } +} - case SCTP_CMD_PART_DELIVER: - sctp_ulpq_partial_delivery(&asoc->ulpq, cmd->obj.ptr, - GFP_ATOMIC); - break; +static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds, + sctp_association_t *asoc) +{ + struct sctp_transport *t; + struct list_head *pos; - case SCTP_CMD_RENEGE: - sctp_ulpq_renege(&asoc->ulpq, cmd->obj.ptr, - GFP_ATOMIC); - break; + /* Stop all heartbeat timers. */ - default: - printk(KERN_WARNING "Impossible command: %u, %p\n", - cmd->verb, cmd->obj.ptr); - break; - }; - if (error) - return error; + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, transports); + if (del_timer(&t->hb_timer)) + sctp_transport_put(t); } - - return error; - -nomem: - error = -ENOMEM; - return error; } -/* A helper function for delayed processing of INET ECN CE bit. */ -static void sctp_do_ecn_ce_work(sctp_association_t *asoc, __u32 lowest_tsn) +/* Helper function to update the heartbeat timer. */ +static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds, + sctp_association_t *asoc, + struct sctp_transport *t) { - /* Save the TSN away for comparison when we receive CWR */ - - asoc->last_ecne_tsn = lowest_tsn; - asoc->need_ecne = 1; + /* Update the heartbeat timer. */ + if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) + sctp_transport_hold(t); } -/* Helper function for delayed processing of SCTP ECNE chunk. */ -/* RFC 2960 Appendix A - * - * RFC 2481 details a specific bit for a sender to send in - * the header of its next outbound TCP segment to indicate to - * its peer that it has reduced its congestion window. This - * is termed the CWR bit. For SCTP the same indication is made - * by including the CWR chunk. This chunk contains one data - * element, i.e. the TSN number that was sent in the ECNE chunk. - * This element represents the lowest TSN number in the datagram - * that was originally marked with the CE bit. - */ -static sctp_chunk_t *sctp_do_ecn_ecne_work(sctp_association_t *asoc, - __u32 lowest_tsn, - sctp_chunk_t *chunk) +/* Helper function to handle the reception of an HEARTBEAT ACK. */ +static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, + sctp_association_t *asoc, + struct sctp_transport *t, + sctp_chunk_t *chunk) { - sctp_chunk_t *repl; - - /* Our previously transmitted packet ran into some congestion - * so we should take action by reducing cwnd and ssthresh - * and then ACK our peer that we we've done so by - * sending a CWR. - */ + sctp_sender_hb_info_t *hbinfo; - /* First, try to determine if we want to actually lower - * our cwnd variables. Only lower them if the ECNE looks more - * recent than the last response. + /* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of the + * HEARTBEAT should clear the error counter of the destination + * transport address to which the HEARTBEAT was sent. + * The association's overall error count is also cleared. */ - if (TSN_lt(asoc->last_cwr_tsn, lowest_tsn)) { - struct sctp_transport *transport; - - /* Find which transport's congestion variables - * need to be adjusted. - */ - transport = sctp_assoc_lookup_tsn(asoc, lowest_tsn); - - /* Update the congestion variables. */ - if (transport) - sctp_transport_lower_cwnd(transport, - SCTP_LOWER_CWND_ECNE); - asoc->last_cwr_tsn = lowest_tsn; - } + t->error_count = 0; + t->asoc->overall_error_count = 0; - /* Always try to quiet the other end. In case of lost CWR, - * resend last_cwr_tsn. + /* Mark the destination transport address as active if it is not so + * marked. */ - repl = sctp_make_cwr(asoc, asoc->last_cwr_tsn, chunk); + if (!t->active) + sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, + SCTP_HEARTBEAT_SUCCESS); - /* If we run out of memory, it will look like a lost CWR. We'll - * get back in sync eventually. + /* The receiver of the HEARTBEAT ACK should also perform an + * RTT measurement for that destination transport address + * using the time value carried in the HEARTBEAT ACK chunk. */ - return repl; + hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; + sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at)); } -/* Helper function to do delayed processing of ECN CWR chunk. */ -static void sctp_do_ecn_cwr_work(sctp_association_t *asoc, - __u32 lowest_tsn) +/* Helper function to do a transport reset at the expiry of the hearbeat + * timer. + */ +static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, + sctp_association_t *asoc, + struct sctp_transport *t) { - /* Turn off ECNE getting auto-prepended to every outgoing - * packet - */ - asoc->need_ecne = 0; -} + sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE); -/* This macro is to compress the text a bit... */ -#define AP(v) asoc->peer.v + /* Mark one strike against a transport. */ + sctp_do_8_2_transport_strike(asoc, t); +} -/* Generate SACK if necessary. We call this at the end of a packet. */ -int sctp_gen_sack(sctp_association_t *asoc, int force, sctp_cmd_seq_t *commands) +/* Helper function to process the process SACK command. */ +static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, + sctp_association_t *asoc, + sctp_sackhdr_t *sackh) { - __u32 ctsn, max_tsn_seen; - sctp_chunk_t *sack; - int error = 0; - - if (force) - asoc->peer.sack_needed = 1; - - ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map); - max_tsn_seen = sctp_tsnmap_get_max_tsn_seen(&asoc->peer.tsn_map); - - /* From 12.2 Parameters necessary per association (i.e. the TCB): - * - * Ack State : This flag indicates if the next received packet - * : is to be responded to with a SACK. ... - * : When DATA chunks are out of order, SACK's - * : are not delayed (see Section 6). - * - * [This is actually not mentioned in Section 6, but we - * implement it here anyway. --piggy] - */ - if (max_tsn_seen != ctsn) - asoc->peer.sack_needed = 1; + int err; - /* From 6.2 Acknowledgement on Reception of DATA Chunks: - * - * Section 4.2 of [RFC2581] SHOULD be followed. Specifically, - * an acknowledgement SHOULD be generated for at least every - * second packet (not every second DATA chunk) received, and - * SHOULD be generated within 200 ms of the arrival of any - * unacknowledged DATA chunk. ... - */ - if (!asoc->peer.sack_needed) { - /* We will need a SACK for the next packet. */ - asoc->peer.sack_needed = 1; - goto out; + if (sctp_outq_sack(&asoc->outqueue, sackh)) { + /* There are no more TSNs awaiting SACK. */ + err = sctp_do_sm(SCTP_EVENT_T_OTHER, + SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN), + asoc->state, asoc->ep, asoc, NULL, + GFP_ATOMIC); } else { - if (asoc->a_rwnd > asoc->rwnd) - asoc->a_rwnd = asoc->rwnd; - sack = sctp_make_sack(asoc); - if (!sack) - goto nomem; - - asoc->peer.sack_needed = 0; - - error = sctp_outq_tail(&asoc->outqueue, sack); - - /* Stop the SACK timer. */ - sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, - SCTP_TO(SCTP_EVENT_TIMEOUT_SACK)); + /* Windows may have opened, so we need + * to check if we have DATA to transmit + */ + err = sctp_outq_flush(&asoc->outqueue, 0); } -out: - return error; - -nomem: - error = -ENOMEM; - return error; + return err; } -/* Handle a duplicate TSN. */ -void sctp_do_TSNdup(sctp_association_t *asoc, sctp_chunk_t *chunk, long gap) +/* Helper function to set the timeout value for T2-SHUTDOWN timer and to set + * the transport for a shutdown chunk. + */ +static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, + sctp_chunk_t *chunk) { -#if 0 - sctp_chunk_t *sack; + struct sctp_transport *t; - /* Caution: gap < 2 * SCTP_TSN_MAP_SIZE - * so gap can be negative. - * - * --xguo - */ + t = sctp_assoc_choose_shutdown_transport(asoc); + asoc->shutdown_last_sent_to = t; + asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto; + chunk->transport = t; +} - /* Count this TSN. */ - if (gap < SCTP_TSN_MAP_SIZE) { - asoc->peer.tsn_map[gap]++; - } else { - asoc->peer.tsn_map_overflow[gap - SCTP_TSN_MAP_SIZE]++; - } +/* Helper function to change the state of an association. */ +static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, + sctp_state_t state) +{ - /* From 6.2 Acknowledgement on Reception of DATA Chunks - * - * When a packet arrives with duplicate DATA chunk(s) - * and with no new DATA chunk(s), the endpoint MUST - * immediately send a SACK with no delay. If a packet - * arrives with duplicate DATA chunk(s) bundled with - * new DATA chunks, the endpoint MAY immediately send a - * SACK. Normally receipt of duplicate DATA chunks - * will occur when the original SACK chunk was lost and - * the peer's RTO has expired. The duplicate TSN - * number(s) SHOULD be reported in the SACK as - * duplicate. - */ - asoc->counters[SctpCounterAckState] = 2; -#endif /* 0 */ -} /* sctp_do_TSNdup() */ + struct sock *sk = asoc->base.sk; + struct sctp_opt *sp = sctp_sk(sk); -#undef AP + asoc->state = state; + asoc->state_timestamp = jiffies; -/* When the T3-RTX timer expires, it calls this function to create the - * relevant state machine event. - */ -void sctp_generate_t3_rtx_event(unsigned long peer) -{ - int error; - struct sctp_transport *transport = (struct sctp_transport *) peer; - sctp_association_t *asoc = transport->asoc; - - /* Check whether a task is in the sock. */ - - sctp_bh_lock_sock(asoc->base.sk); - if (sock_owned_by_user(asoc->base.sk)) { - SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __FUNCTION__); + if ((SCTP_STATE_ESTABLISHED == asoc->state) || + (SCTP_STATE_CLOSED == asoc->state)) { + /* Wake up any processes waiting in the asoc's wait queue in + * sctp_wait_for_connect() or sctp_wait_for_sndbuf(). + */ + if (waitqueue_active(&asoc->wait)) + wake_up_interruptible(&asoc->wait); - /* Try again later. */ - if (!mod_timer(&transport->T3_rtx_timer, jiffies + (HZ/20))) - sctp_transport_hold(transport); - goto out_unlock; + /* Wake up any processes waiting in the sk's sleep queue of + * a TCP-style or UDP-style peeled-off socket in + * sctp_wait_for_accept() or sctp_wait_for_packet(). + * For a UDP-style socket, the waiters are woken up by the + * notifications. + */ + if (SCTP_SOCKET_UDP != sp->type) + sk->state_change(sk); } - /* Is this transport really dead and just waiting around for - * the timer to let go of the reference? + /* Change the sk->state of a TCP-style socket that has sucessfully + * completed a connect() call. */ - if (transport->dead) - goto out_unlock; + if ((SCTP_STATE_ESTABLISHED == asoc->state) && + (SCTP_SOCKET_TCP == sp->type) && (SCTP_SS_CLOSED == sk->state)) + sk->state = SCTP_SS_ESTABLISHED; +} - /* Run through the state machine. */ - error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, - SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_T3_RTX), - asoc->state, - asoc->ep, asoc, - transport, GFP_ATOMIC); +/* These three macros allow us to pull the debugging code out of the + * main flow of sctp_do_sm() to keep attention focused on the real + * functionality there. + */ +#define DEBUG_PRE \ + SCTP_DEBUG_PRINTK("sctp_do_sm prefn: " \ + "ep %p, %s, %s, asoc %p[%s], %s\n", \ + ep, sctp_evttype_tbl[event_type], \ + (*debug_fn)(subtype), asoc, \ + sctp_state_tbl[state], state_fn->name) - if (error) - asoc->base.sk->err = -error; +#define DEBUG_POST \ + SCTP_DEBUG_PRINTK("sctp_do_sm postfn: " \ + "asoc %p, status: %s\n", \ + asoc, sctp_status_tbl[status]) -out_unlock: - sctp_bh_unlock_sock(asoc->base.sk); - sctp_transport_put(transport); -} +#define DEBUG_POST_SFX \ + SCTP_DEBUG_PRINTK("sctp_do_sm post sfx: error %d, asoc %p[%s]\n", \ + error, asoc, \ + sctp_state_tbl[(asoc && sctp_id2assoc(ep->base.sk, \ + sctp_assoc2id(asoc)))?asoc->state:SCTP_STATE_CLOSED]) -/* This is a sa interface for producing timeout events. It works - * for timeouts which use the association as their parameter. +/* + * This is the master state machine processing function. + * + * If you want to understand all of lksctp, this is a + * good place to start. */ -static void sctp_generate_timeout_event(sctp_association_t *asoc, - sctp_event_timeout_t timeout_type) +int sctp_do_sm(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, + sctp_endpoint_t *ep, + sctp_association_t *asoc, + void *event_arg, + int priority) { + sctp_cmd_seq_t commands; + sctp_sm_table_entry_t *state_fn; + sctp_disposition_t status; int error = 0; + typedef const char *(printfn_t)(sctp_subtype_t); - sctp_bh_lock_sock(asoc->base.sk); - if (sock_owned_by_user(asoc->base.sk)) { - SCTP_DEBUG_PRINTK("%s:Sock is busy: timer %d\n", - __FUNCTION__, - timeout_type); - - /* Try again later. */ - if (!mod_timer(&asoc->timers[timeout_type], jiffies + (HZ/20))) - sctp_association_hold(asoc); - goto out_unlock; - } + static printfn_t *table[] = { + NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname, + }; + printfn_t *debug_fn __attribute__ ((unused)) = table[event_type]; - /* Is this association really dead and just waiting around for - * the timer to let go of the reference? + /* Look up the state function, run it, and then process the + * side effects. These three steps are the heart of lksctp. */ - if (asoc->base.dead) - goto out_unlock; + state_fn = sctp_sm_lookup_event(event_type, state, subtype); - /* Run through the state machine. */ - error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, - SCTP_ST_TIMEOUT(timeout_type), - asoc->state, asoc->ep, asoc, - (void *)timeout_type, - GFP_ATOMIC); + sctp_init_cmd_seq(&commands); - if (error) - asoc->base.sk->err = -error; + DEBUG_PRE; + status = (*state_fn->fn)(ep, asoc, subtype, event_arg, &commands); + DEBUG_POST; -out_unlock: - sctp_bh_unlock_sock(asoc->base.sk); - sctp_association_put(asoc); -} + error = sctp_side_effects(event_type, subtype, state, + ep, asoc, event_arg, + status, &commands, + priority); + DEBUG_POST_SFX; -void sctp_generate_t1_cookie_event(unsigned long data) -{ - sctp_association_t *asoc = (sctp_association_t *) data; - sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_COOKIE); + return error; } -void sctp_generate_t1_init_event(unsigned long data) -{ - sctp_association_t *asoc = (sctp_association_t *) data; - sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T1_INIT); -} +#undef DEBUG_PRE +#undef DEBUG_POST -void sctp_generate_t2_shutdown_event(unsigned long data) +/***************************************************************** + * 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, + sctp_endpoint_t *ep, + sctp_association_t *asoc, + void *event_arg, + sctp_disposition_t status, + sctp_cmd_seq_t *commands, + int priority) { - sctp_association_t *asoc = (sctp_association_t *) data; - sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_T2_SHUTDOWN); -} + int error; -void sctp_generate_t5_shutdown_guard_event(unsigned long data) -{ - sctp_association_t *asoc = (sctp_association_t *)data; - sctp_generate_timeout_event(asoc, - SCTP_EVENT_TIMEOUT_T5_SHUTDOWN_GUARD); + /* FIXME - Most of the dispositions left today would be categorized + * as "exceptional" dispositions. For those dispositions, it + * may not be proper to run through any of the commands at all. + * For example, the command interpreter might be run only with + * disposition SCTP_DISPOSITION_CONSUME. + */ + if (0 != (error = sctp_cmd_interpreter(event_type, subtype, state, + ep, asoc, + event_arg, status, + commands, priority))) + goto bail; -} /* sctp_generate_t5_shutdown_guard_event() */ + switch (status) { + case SCTP_DISPOSITION_DISCARD: + SCTP_DEBUG_PRINTK("Ignored sctp protocol event - state %d, " + "event_type %d, event_id %d\n", + state, event_type, subtype.chunk); + break; -void sctp_generate_autoclose_event(unsigned long data) -{ - sctp_association_t *asoc = (sctp_association_t *) data; - sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_AUTOCLOSE); -} + case SCTP_DISPOSITION_NOMEM: + /* We ran out of memory, so we need to discard this + * packet. + */ + /* BUG--we should now recover some memory, probably by + * reneging... + */ + error = -ENOMEM; + break; -/* Generate a heart beat event. If the sock is busy, reschedule. Make - * sure that the transport is still valid. - */ -void sctp_generate_heartbeat_event(unsigned long data) -{ - int error = 0; - struct sctp_transport *transport = (struct sctp_transport *) data; - sctp_association_t *asoc = transport->asoc; + case SCTP_DISPOSITION_DELETE_TCB: + /* This should now be a command. */ + break; - sctp_bh_lock_sock(asoc->base.sk); - if (sock_owned_by_user(asoc->base.sk)) { - SCTP_DEBUG_PRINTK("%s:Sock is busy.\n", __FUNCTION__); + case SCTP_DISPOSITION_CONSUME: + case SCTP_DISPOSITION_ABORT: + /* + * We should no longer have much work to do here as the + * real work has been done as explicit commands above. + */ + break; - /* Try again later. */ - if (!mod_timer(&transport->hb_timer, jiffies + (HZ/20))) - sctp_transport_hold(transport); - goto out_unlock; - } + case SCTP_DISPOSITION_VIOLATION: + printk(KERN_ERR "sctp protocol violation state %d " + "chunkid %d\n", state, subtype.chunk); + break; - /* Is this structure just waiting around for us to actually - * get destroyed? - */ - if (transport->dead) - goto out_unlock; + case SCTP_DISPOSITION_NOT_IMPL: + printk(KERN_WARNING "sctp unimplemented feature in state %d, " + "event_type %d, event_id %d\n", + state, event_type, subtype.chunk); + break; - error = sctp_do_sm(SCTP_EVENT_T_TIMEOUT, - SCTP_ST_TIMEOUT(SCTP_EVENT_TIMEOUT_HEARTBEAT), - asoc->state, - asoc->ep, asoc, - transport, GFP_ATOMIC); + case SCTP_DISPOSITION_BUG: + printk(KERN_ERR "sctp bug in state %d, " + "event_type %d, event_id %d\n", + state, event_type, subtype.chunk); + BUG(); + break; - if (error) - asoc->base.sk->err = -error; + default: + printk(KERN_ERR "sctp impossible disposition %d " + "in state %d, event_type %d, event_id %d\n", + status, state, event_type, subtype.chunk); + BUG(); + break; + }; -out_unlock: - sctp_bh_unlock_sock(asoc->base.sk); - sctp_transport_put(transport); -} - -/* Inject a SACK Timeout event into the state machine. */ -void sctp_generate_sack_event(unsigned long data) -{ - sctp_association_t *asoc = (sctp_association_t *) data; - sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK); +bail: + return error; } -sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { - NULL, - sctp_generate_t1_cookie_event, - sctp_generate_t1_init_event, - sctp_generate_t2_shutdown_event, - NULL, - sctp_generate_t5_shutdown_guard_event, - sctp_generate_heartbeat_event, - sctp_generate_sack_event, - sctp_generate_autoclose_event, -}; - /******************************************************************** - * 3rd Level Abstractions + * 2nd Level Abstractions ********************************************************************/ -/* RFC 2960 8.2 Path Failure Detection - * - * When its peer endpoint is multi-homed, an endpoint should keep a - * error counter for each of the destination transport addresses of the - * peer endpoint. - * - * Each time the T3-rtx timer expires on any address, or when a - * HEARTBEAT sent to an idle address is not acknowledged within a RTO, - * the error counter of that destination address will be incremented. - * When the value in the error counter exceeds the protocol parameter - * 'Path.Max.Retrans' of that destination address, the endpoint should - * mark the destination transport address as inactive, and a - * notification SHOULD be sent to the upper layer. - * - */ -static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, - struct sctp_transport *transport) +/* This is the side-effect interpreter. */ +int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, + sctp_state_t state, sctp_endpoint_t *ep, + sctp_association_t *asoc, void *event_arg, + sctp_disposition_t status, sctp_cmd_seq_t *commands, + int priority) { - /* The check for association's overall error counter exceeding the - * threshold is done in the state function. - */ - asoc->overall_error_count++; + int error = 0; + int force; + sctp_cmd_t *cmd; + sctp_chunk_t *new_obj; + sctp_chunk_t *chunk = NULL; + struct sctp_packet *packet; + struct list_head *pos; + struct timer_list *timer; + unsigned long timeout; + struct sctp_transport *t; + sctp_sackhdr_t sackh; - if (transport->active && - (transport->error_count++ >= transport->error_threshold)) { - SCTP_DEBUG_PRINTK("transport_strike: transport " - "IP:%d.%d.%d.%d failed.\n", - NIPQUAD(transport->ipaddr.v4.sin_addr)); - sctp_assoc_control_transport(asoc, transport, - SCTP_TRANSPORT_DOWN, - SCTP_FAILED_THRESHOLD); - } + if(SCTP_EVENT_T_TIMEOUT != event_type) + chunk = (sctp_chunk_t *) event_arg; - /* E2) For the destination address for which the timer - * expires, set RTO <- RTO * 2 ("back off the timer"). The - * maximum value discussed in rule C7 above (RTO.max) may be - * used to provide an upper bound to this doubling operation. + /* Note: This whole file is a huge candidate for rework. + * For example, each command could either have its own handler, so + * the loop would look like: + * while (cmds) + * cmd->handle(x, y, z) + * --jgrimm */ - transport->rto = min((transport->rto * 2), transport->asoc->rto_max); -} + while (NULL != (cmd = sctp_next_cmd(commands))) { + switch (cmd->verb) { + case SCTP_CMD_NOP: + /* Do nothing. */ + break; -/* Worker routine to handle INIT command failure. */ -static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, - sctp_association_t *asoc) -{ - struct sctp_ulpevent *event; + case SCTP_CMD_NEW_ASOC: + /* Register a new association. */ + asoc = cmd->obj.ptr; + /* Register with the endpoint. */ + sctp_endpoint_add_asoc(ep, asoc); + sctp_hash_established(asoc); + break; - event = sctp_ulpevent_make_assoc_change(asoc, - 0, - SCTP_CANT_STR_ASSOC, - 0, 0, 0, - GFP_ATOMIC); + case SCTP_CMD_UPDATE_ASSOC: + sctp_assoc_update(asoc, cmd->obj.ptr); + break; - if (event) - sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, - SCTP_ULPEVENT(event)); + case SCTP_CMD_PURGE_OUTQUEUE: + sctp_outq_teardown(&asoc->outqueue); + break; - /* FIXME: We need to handle data possibly either - * sent via COOKIE-ECHO bundling or just waiting in - * the transmit queue, if the user has enabled - * SEND_FAILED notifications. - */ - sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); -} + case SCTP_CMD_DELETE_TCB: + /* Delete the current association. */ + sctp_unhash_established(asoc); + sctp_association_free(asoc); + asoc = NULL; + break; -/* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */ -static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, - sctp_association_t *asoc, - sctp_event_t event_type, - sctp_subtype_t subtype, - sctp_chunk_t *chunk) -{ - struct sctp_ulpevent *event; - __u16 error = 0; + case SCTP_CMD_NEW_STATE: + /* Enter a new state. */ + sctp_cmd_new_state(commands, asoc, cmd->obj.state); + break; - switch(event_type) { - case SCTP_EVENT_T_PRIMITIVE: - if (SCTP_PRIMITIVE_ABORT == subtype.primitive) - error = SCTP_ERROR_USER_ABORT; - break; - case SCTP_EVENT_T_CHUNK: - if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) && - (ntohs(chunk->chunk_hdr->length) >= - (sizeof(struct sctp_chunkhdr) + - sizeof(struct sctp_errhdr)))) { - error = ((sctp_errhdr_t *)chunk->skb->data)->cause; - } - break; - default: - break; - } + case SCTP_CMD_REPORT_TSN: + /* Record the arrival of a TSN. */ + sctp_tsnmap_mark(&asoc->peer.tsn_map, cmd->obj.u32); + break; - /* Cancel any partial delivery in progress. */ - sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); + case SCTP_CMD_GEN_SACK: + /* Generate a Selective ACK. + * The argument tells us whether to just count + * the packet and MAYBE generate a SACK, or + * force a SACK out. + */ + force = cmd->obj.i32; + error = sctp_gen_sack(asoc, force, commands); + break; - event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST, - error, 0, 0, GFP_ATOMIC); - if (event) - sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, - SCTP_ULPEVENT(event)); + case SCTP_CMD_PROCESS_SACK: + /* Process an inbound SACK. */ + error = sctp_cmd_process_sack(commands, asoc, + cmd->obj.ptr); + break; - sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, - SCTP_STATE(SCTP_STATE_CLOSED)); + case SCTP_CMD_GEN_INIT_ACK: + /* Generate an INIT ACK chunk. */ + new_obj = sctp_make_init_ack(asoc, chunk, GFP_ATOMIC, + 0); + if (!new_obj) + goto nomem; - /* FIXME: We need to handle data that could not be sent or was not - * acked, if the user has enabled SEND_FAILED notifications. - */ - sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); -} + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(new_obj)); + break; -/* Process an init chunk (may be real INIT/INIT-ACK or an embedded INIT - * inside the cookie. In reality, this is only used for INIT-ACK processing - * since all other cases use "temporary" associations and can do all - * their work in statefuns directly. - */ -static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, - sctp_association_t *asoc, - sctp_chunk_t *chunk, - sctp_init_chunk_t *peer_init, - int priority) -{ - int error; + case SCTP_CMD_PEER_INIT: + /* Process a unified INIT from the peer. + * Note: Only used during INIT-ACK processing. If + * there is an error just return to the outter + * layer which will bail. + */ + error = sctp_cmd_process_init(commands, asoc, chunk, + cmd->obj.ptr, priority); + break; - /* We only process the init as a sideeffect in a single - * case. This is when we process the INIT-ACK. If we - * fail during INIT processing (due to malloc problems), - * just return the error and stop processing the stack. - */ + case SCTP_CMD_GEN_COOKIE_ECHO: + /* Generate a COOKIE ECHO chunk. */ + new_obj = sctp_make_cookie_echo(asoc, chunk); + if (!new_obj) { + if (cmd->obj.ptr) + sctp_free_chunk(cmd->obj.ptr); + goto nomem; + } + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(new_obj)); - if (!sctp_process_init(asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, - priority)) - error = -ENOMEM; - else - error = 0; + /* If there is an ERROR chunk to be sent along with + * the COOKIE_ECHO, send it, too. + */ + if (cmd->obj.ptr) + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(cmd->obj.ptr)); + break; + + case SCTP_CMD_GEN_SHUTDOWN: + /* Generate SHUTDOWN when in SHUTDOWN_SENT state. + * Reset error counts. + */ + asoc->overall_error_count = 0; + + /* Generate a SHUTDOWN chunk. */ + new_obj = sctp_make_shutdown(asoc); + if (!new_obj) + goto nomem; + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(new_obj)); + break; + + case SCTP_CMD_CHUNK_ULP: + /* Send a chunk to the sockets layer. */ + SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", + "chunk_up:", cmd->obj.ptr, + "ulpq:", &asoc->ulpq); + sctp_ulpq_tail_data(&asoc->ulpq, cmd->obj.ptr, + GFP_ATOMIC); + break; + + case SCTP_CMD_EVENT_ULP: + /* Send a notification to the sockets layer. */ + SCTP_DEBUG_PRINTK("sm_sideff: %s %p, %s %p.\n", + "event_up:",cmd->obj.ptr, + "ulpq:",&asoc->ulpq); + sctp_ulpq_tail_event(&asoc->ulpq, cmd->obj.ptr); + break; + + case SCTP_CMD_REPLY: + /* Send a chunk to our peer. */ + error = sctp_outq_tail(&asoc->outqueue, + cmd->obj.ptr); + break; + + case SCTP_CMD_SEND_PKT: + /* Send a full packet to our peer. */ + packet = cmd->obj.ptr; + sctp_packet_transmit(packet); + sctp_ootb_pkt_free(packet); + break; + + case SCTP_CMD_RETRAN: + /* Mark a transport for retransmission. */ + sctp_retransmit(&asoc->outqueue, cmd->obj.transport, + SCTP_RTXR_T3_RTX); + break; + + case SCTP_CMD_TRANSMIT: + /* Kick start transmission. */ + error = sctp_outq_flush(&asoc->outqueue, 0); + break; + + case SCTP_CMD_ECN_CE: + /* Do delayed CE processing. */ + sctp_do_ecn_ce_work(asoc, cmd->obj.u32); + break; + + case SCTP_CMD_ECN_ECNE: + /* Do delayed ECNE processing. */ + new_obj = sctp_do_ecn_ecne_work(asoc, cmd->obj.u32, + chunk); + if (new_obj) + sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, + SCTP_CHUNK(new_obj)); + break; + + case SCTP_CMD_ECN_CWR: + /* Do delayed CWR processing. */ + sctp_do_ecn_cwr_work(asoc, cmd->obj.u32); + break; + + case SCTP_CMD_SETUP_T2: + sctp_cmd_setup_t2(commands, asoc, cmd->obj.ptr); + break; + + case SCTP_CMD_TIMER_START: + timer = &asoc->timers[cmd->obj.to]; + timeout = asoc->timeouts[cmd->obj.to]; + if (!timeout) + BUG(); + + timer->expires = jiffies + timeout; + sctp_association_hold(asoc); + add_timer(timer); + break; - return error; -} + case SCTP_CMD_TIMER_RESTART: + timer = &asoc->timers[cmd->obj.to]; + timeout = asoc->timeouts[cmd->obj.to]; + if (!mod_timer(timer, jiffies + timeout)) + sctp_association_hold(asoc); + break; -/* Helper function to break out starting up of heartbeat timers. */ -static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, - sctp_association_t *asoc) -{ - struct sctp_transport *t; - struct list_head *pos; + case SCTP_CMD_TIMER_STOP: + timer = &asoc->timers[cmd->obj.to]; + if (timer_pending(timer) && del_timer(timer)) + sctp_association_put(asoc); + break; - /* Start a heartbeat timer for each transport on the association. - * hold a reference on the transport to make sure none of - * the needed data structures go away. - */ - list_for_each(pos, &asoc->peer.transport_addr_list) { - t = list_entry(pos, struct sctp_transport, transports); + case SCTP_CMD_INIT_RESTART: + /* Do the needed accounting and updates + * associated with restarting an initialization + * timer. + */ + asoc->counters[SCTP_COUNTER_INIT_ERROR]++; + asoc->timeouts[cmd->obj.to] *= 2; + if (asoc->timeouts[cmd->obj.to] > + asoc->max_init_timeo) { + asoc->timeouts[cmd->obj.to] = + asoc->max_init_timeo; + } - if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) - sctp_transport_hold(t); - } -} + /* If we've sent any data bundled with + * COOKIE-ECHO we need to resend. + */ + list_for_each(pos, &asoc->peer.transport_addr_list) { + t = list_entry(pos, struct sctp_transport, + transports); + sctp_retransmit_mark(&asoc->outqueue, t, 0); + } -static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds, - sctp_association_t *asoc) -{ - struct sctp_transport *t; - struct list_head *pos; + sctp_add_cmd_sf(commands, + SCTP_CMD_TIMER_RESTART, + SCTP_TO(cmd->obj.to)); + break; - /* Stop all heartbeat timers. */ + case SCTP_CMD_INIT_FAILED: + sctp_cmd_init_failed(commands, asoc); + break; - list_for_each(pos, &asoc->peer.transport_addr_list) { - t = list_entry(pos, struct sctp_transport, transports); - if (del_timer(&t->hb_timer)) - sctp_transport_put(t); - } -} + case SCTP_CMD_ASSOC_FAILED: + sctp_cmd_assoc_failed(commands, asoc, event_type, + subtype, chunk); + break; -/* Helper function to update the heartbeat timer. */ -static void sctp_cmd_hb_timer_update(sctp_cmd_seq_t *cmds, - sctp_association_t *asoc, - struct sctp_transport *t) -{ - /* Update the heartbeat timer. */ - if (!mod_timer(&t->hb_timer, sctp_transport_timeout(t))) - sctp_transport_hold(t); -} + case SCTP_CMD_COUNTER_INC: + asoc->counters[cmd->obj.counter]++; + break; -/* Helper function to handle the reception of an HEARTBEAT ACK. */ -static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, - sctp_association_t *asoc, - struct sctp_transport *t, - sctp_chunk_t *chunk) -{ - sctp_sender_hb_info_t *hbinfo; + case SCTP_CMD_COUNTER_RESET: + asoc->counters[cmd->obj.counter] = 0; + break; - /* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of the - * HEARTBEAT should clear the error counter of the destination - * transport address to which the HEARTBEAT was sent. - * The association's overall error count is also cleared. - */ - t->error_count = 0; - t->asoc->overall_error_count = 0; + case SCTP_CMD_REPORT_DUP: + sctp_tsnmap_mark_dup(&asoc->peer.tsn_map, + cmd->obj.u32); + break; - /* Mark the destination transport address as active if it is not so - * marked. - */ - if (!t->active) - sctp_assoc_control_transport(asoc, t, SCTP_TRANSPORT_UP, - SCTP_HEARTBEAT_SUCCESS); + case SCTP_CMD_REPORT_BAD_TAG: + SCTP_DEBUG_PRINTK("vtag mismatch!\n"); + break; - /* The receiver of the HEARTBEAT ACK should also perform an - * RTT measurement for that destination transport address - * using the time value carried in the HEARTBEAT ACK chunk. - */ - hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; - sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at)); -} + case SCTP_CMD_STRIKE: + /* Mark one strike against a transport. */ + sctp_do_8_2_transport_strike(asoc, cmd->obj.transport); + break; -/* Helper function to do a transport reset at the expiry of the hearbeat - * timer. - */ -static void sctp_cmd_transport_reset(sctp_cmd_seq_t *cmds, - sctp_association_t *asoc, - struct sctp_transport *t) -{ - sctp_transport_lower_cwnd(t, SCTP_LOWER_CWND_INACTIVE); + case SCTP_CMD_TRANSPORT_RESET: + t = cmd->obj.transport; + sctp_cmd_transport_reset(commands, asoc, t); + break; - /* Mark one strike against a transport. */ - sctp_do_8_2_transport_strike(asoc, t); -} + case SCTP_CMD_TRANSPORT_ON: + t = cmd->obj.transport; + sctp_cmd_transport_on(commands, asoc, t, chunk); + break; -/* Helper function to process the process SACK command. */ -static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, - sctp_association_t *asoc, - sctp_sackhdr_t *sackh) -{ - int err; + case SCTP_CMD_HB_TIMERS_START: + sctp_cmd_hb_timers_start(commands, asoc); + break; - if (sctp_outq_sack(&asoc->outqueue, sackh)) { - /* There are no more TSNs awaiting SACK. */ - err = sctp_do_sm(SCTP_EVENT_T_OTHER, - SCTP_ST_OTHER(SCTP_EVENT_NO_PENDING_TSN), - asoc->state, asoc->ep, asoc, NULL, - GFP_ATOMIC); - } else { - /* Windows may have opened, so we need - * to check if we have DATA to transmit - */ - err = sctp_outq_flush(&asoc->outqueue, 0); - } + case SCTP_CMD_HB_TIMER_UPDATE: + t = cmd->obj.transport; + sctp_cmd_hb_timer_update(commands, asoc, t); + break; - return err; -} + case SCTP_CMD_HB_TIMERS_STOP: + sctp_cmd_hb_timers_stop(commands, asoc); + break; -/* Helper function to set the timeout value for T2-SHUTDOWN timer and to set - * the transport for a shutdown chunk. - */ -static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, - sctp_chunk_t *chunk) -{ - struct sctp_transport *t; + case SCTP_CMD_REPORT_ERROR: + error = cmd->obj.error; + break; - t = sctp_assoc_choose_shutdown_transport(asoc); - asoc->shutdown_last_sent_to = t; - asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto; - chunk->transport = t; -} + case SCTP_CMD_PROCESS_CTSN: + /* Dummy up a SACK for processing. */ + sackh.cum_tsn_ack = cmd->obj.u32; + sackh.a_rwnd = 0; + sackh.num_gap_ack_blocks = 0; + sackh.num_dup_tsns = 0; + sctp_add_cmd_sf(commands, SCTP_CMD_PROCESS_SACK, + SCTP_SACKH(&sackh)); + break; -/* Helper function to change the state of an association. */ -static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, - sctp_state_t state) -{ + case SCTP_CMD_DISCARD_PACKET: + /* We need to discard the whole packet. */ + chunk->pdiscard = 1; + break; - struct sock *sk = asoc->base.sk; - struct sctp_opt *sp = sctp_sk(sk); + case SCTP_CMD_RTO_PENDING: + t = cmd->obj.transport; + t->rto_pending = 1; + break; - asoc->state = state; - asoc->state_timestamp = jiffies; + case SCTP_CMD_PART_DELIVER: + sctp_ulpq_partial_delivery(&asoc->ulpq, cmd->obj.ptr, + GFP_ATOMIC); + break; - if ((SCTP_STATE_ESTABLISHED == asoc->state) || - (SCTP_STATE_CLOSED == asoc->state)) { - /* Wake up any processes waiting in the asoc's wait queue in - * sctp_wait_for_connect() or sctp_wait_for_sndbuf(). - */ - if (waitqueue_active(&asoc->wait)) - wake_up_interruptible(&asoc->wait); + case SCTP_CMD_RENEGE: + sctp_ulpq_renege(&asoc->ulpq, cmd->obj.ptr, + GFP_ATOMIC); + break; - /* Wake up any processes waiting in the sk's sleep queue of - * a TCP-style or UDP-style peeled-off socket in - * sctp_wait_for_accept() or sctp_wait_for_packet(). - * For a UDP-style socket, the waiters are woken up by the - * notifications. - */ - if (SCTP_SOCKET_UDP != sp->type) - sk->state_change(sk); + default: + printk(KERN_WARNING "Impossible command: %u, %p\n", + cmd->verb, cmd->obj.ptr); + break; + }; + if (error) + return error; } - /* Change the sk->state of a TCP-style socket that has sucessfully - * completed a connect() call. - */ - if ((SCTP_STATE_ESTABLISHED == asoc->state) && - (SCTP_SOCKET_TCP == sp->type) && (SCTP_SS_CLOSED == sk->state)) - sk->state = SCTP_SS_ESTABLISHED; + return error; + +nomem: + error = -ENOMEM; + return error; } + -- cgit v1.2.3 From 1bafb09e6ebf31151b7c9b62e67f1f3bcb419337 Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Tue, 25 Mar 2003 04:11:45 -0600 Subject: [SCTP] Add SEND_FAILED support. (Ardelle Fan) --- include/net/sctp/structs.h | 3 +++ net/sctp/outqueue.c | 23 ++++++++++++++++++-- net/sctp/sm_sideeffect.c | 53 +++++++++++++++------------------------------- net/sctp/sm_statefuns.c | 53 +++++++++++++++++++++++++++++++++------------- 4 files changed, 79 insertions(+), 53 deletions(-) (limited to 'include') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index e22b333a94b2..44c81f5c3020 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -872,6 +872,9 @@ struct sctp_outq { unsigned out_qlen; /* Total length of queued data chunks. */ + /* Error of send failed, may used in SCTP_SEND_FAILED event. */ + unsigned error; + /* These are control chunks we want to send. */ struct sk_buff_head control; diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 87bad9667e66..ae964d48c1db 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -138,13 +138,13 @@ void sctp_outq_init(sctp_association_t *asoc, struct sctp_outq *q) } /* Free the outqueue structure and any related pending chunks. - * FIXME: Add SEND_FAILED support. */ void sctp_outq_teardown(struct sctp_outq *q) { struct sctp_transport *transport; struct list_head *lchunk, *pos, *temp; sctp_chunk_t *chunk; + struct sctp_ulpevent *ev; /* Throw away unacknowledged chunks. */ list_for_each(pos, &q->asoc->peer.transport_addr_list) { @@ -152,6 +152,14 @@ void sctp_outq_teardown(struct sctp_outq *q) while ((lchunk = sctp_list_dequeue(&transport->transmitted))) { chunk = list_entry(lchunk, sctp_chunk_t, transmitted_list); + + /* Generate a SEND FAILED event. */ + ev = sctp_ulpevent_make_send_failed(q->asoc, + chunk, SCTP_DATA_SENT, + q->error, GFP_ATOMIC); + if (ev) + sctp_ulpq_tail_event(&q->asoc->ulpq, ev); + sctp_free_chunk(chunk); } } @@ -171,8 +179,19 @@ void sctp_outq_teardown(struct sctp_outq *q) } /* Throw away any leftover data chunks. */ - while ((chunk = sctp_outq_dequeue_data(q))) + while ((chunk = sctp_outq_dequeue_data(q))) { + + /* Generate a SEND FAILED event. */ + ev = sctp_ulpevent_make_send_failed(q->asoc, + chunk, SCTP_DATA_UNSENT, + q->error, GFP_ATOMIC); + if (ev) + sctp_ulpq_tail_event(&q->asoc->ulpq, ev); + sctp_free_chunk(chunk); + } + + q->error = 0; /* Throw away any leftover control chunks. */ while ((chunk = (sctp_chunk_t *) skb_dequeue(&q->control))) diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 63cad2d13e07..2a794ada7247 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -133,7 +133,7 @@ static void sctp_do_ecn_cwr_work(sctp_association_t *asoc, } /* Generate SACK if necessary. We call this at the end of a packet. */ -int sctp_gen_sack(struct sctp_association *asoc, int force, +int sctp_gen_sack(struct sctp_association *asoc, int force, sctp_cmd_seq_t *commands) { __u32 ctsn, max_tsn_seen; @@ -410,7 +410,8 @@ static void sctp_do_8_2_transport_strike(sctp_association_t *asoc, /* Worker routine to handle INIT command failure. */ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, - sctp_association_t *asoc) + sctp_association_t *asoc, + unsigned error) { struct sctp_ulpevent *event; @@ -421,46 +422,27 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(event)); - /* FIXME: We need to handle data possibly either - * sent via COOKIE-ECHO bundling or just waiting in - * the transmit queue, if the user has enabled - * SEND_FAILED notifications. - */ + /* SEND_FAILED sent later when cleaning up the association. */ + asoc->outqueue.error = error; sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); } /* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, - sctp_association_t *asoc, + struct sctp_association *asoc, sctp_event_t event_type, sctp_subtype_t subtype, - sctp_chunk_t *chunk) + struct sctp_chunk *chunk, + unsigned error) { struct sctp_ulpevent *event; - __u16 error = 0; - - switch(event_type) { - case SCTP_EVENT_T_PRIMITIVE: - if (SCTP_PRIMITIVE_ABORT == subtype.primitive) - error = SCTP_ERROR_USER_ABORT; - break; - case SCTP_EVENT_T_CHUNK: - if (chunk && (SCTP_CID_ABORT == chunk->chunk_hdr->type) && - (ntohs(chunk->chunk_hdr->length) >= - (sizeof(struct sctp_chunkhdr) + - sizeof(struct sctp_errhdr)))) { - error = ((sctp_errhdr_t *)chunk->skb->data)->cause; - } - break; - default: - break; - } /* Cancel any partial delivery in progress. */ sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST, - error, 0, 0, GFP_ATOMIC); + (__u16)error, 0, 0, + GFP_ATOMIC); if (event) sctp_add_cmd_sf(commands, SCTP_CMD_EVENT_ULP, SCTP_ULPEVENT(event)); @@ -468,9 +450,8 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, sctp_add_cmd_sf(commands, SCTP_CMD_NEW_STATE, SCTP_STATE(SCTP_STATE_CLOSED)); - /* FIXME: We need to handle data that could not be sent or was not - * acked, if the user has enabled SEND_FAILED notifications. - */ + /* SEND_FAILED sent later when cleaning up the association. */ + asoc->outqueue.error = error; sctp_add_cmd_sf(commands, SCTP_CMD_DELETE_TCB, SCTP_NULL()); } @@ -640,9 +621,9 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, sctp_association_t *asoc, asoc->state_timestamp = jiffies; if ((SCTP_STATE_ESTABLISHED == asoc->state) || - (SCTP_STATE_CLOSED == asoc->state)) { + (SCTP_STATE_CLOSED == asoc->state)) { /* Wake up any processes waiting in the asoc's wait queue in - * sctp_wait_for_connect() or sctp_wait_for_sndbuf(). + * sctp_wait_for_connect() or sctp_wait_for_sndbuf(). */ if (waitqueue_active(&asoc->wait)) wake_up_interruptible(&asoc->wait); @@ -1077,12 +1058,12 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, break; case SCTP_CMD_INIT_FAILED: - sctp_cmd_init_failed(commands, asoc); + sctp_cmd_init_failed(commands, asoc, cmd->obj.u32); break; case SCTP_CMD_ASSOC_FAILED: sctp_cmd_assoc_failed(commands, asoc, event_type, - subtype, chunk); + subtype, chunk, cmd->obj.u32); break; case SCTP_CMD_COUNTER_INC: @@ -1094,7 +1075,7 @@ int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, break; case SCTP_CMD_REPORT_DUP: - sctp_tsnmap_mark_dup(&asoc->peer.tsn_map, + sctp_tsnmap_mark_dup(&asoc->peer.tsn_map, cmd->obj.u32); break; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9e2862892013..cec75eb93946 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -729,7 +729,8 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(const sctp_endpoint_t *ep, if (asoc->overall_error_count >= asoc->overall_error_threshold) { /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ - sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_INC_STATS(SctpAborteds); SCTP_DEC_STATS(SctpCurrEstab); return SCTP_DISPOSITION_DELETE_TCB; @@ -1772,14 +1773,16 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(const sctp_endpoint_t *ep, sctp_chunk_t *chunk = arg; sctp_errhdr_t *err; + err = (sctp_errhdr_t *)(chunk->skb->data); + /* If we have gotten too many failures, give up. */ if (1 + asoc->counters[SCTP_COUNTER_INIT_ERROR] > asoc->max_init_attempts) { /* INIT_FAILED will issue an ulpevent. */ - sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(err->cause)); return SCTP_DISPOSITION_DELETE_TCB; } - err = (sctp_errhdr_t *)(chunk->skb->data); /* Process the error here */ switch (err->cause) { @@ -1834,7 +1837,8 @@ sctp_disposition_t sctp_sf_do_5_2_6_stale(const sctp_endpoint_t *ep, attempts = asoc->counters[SCTP_COUNTER_INIT_ERROR] + 1; if (attempts >= asoc->max_init_attempts) { - sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(SCTP_ERROR_STALE_COOKIE)); return SCTP_DISPOSITION_DELETE_TCB; } @@ -1936,12 +1940,18 @@ sctp_disposition_t sctp_sf_do_9_1_abort(const sctp_endpoint_t *ep, sctp_cmd_seq_t *commands) { sctp_chunk_t *chunk = arg; + __u16 error = SCTP_ERROR_NO_ERROR; if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); + if (chunk && (ntohs(chunk->chunk_hdr->length) >= + (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_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_U32(error)); SCTP_INC_STATS(SctpAborteds); SCTP_DEC_STATS(SctpCurrEstab); @@ -1961,6 +1971,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const sctp_endpoint_t *ep, sctp_cmd_seq_t *commands) { sctp_chunk_t *chunk = arg; + __u16 error = SCTP_ERROR_NO_ERROR; if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(ep, asoc, type, arg, commands); @@ -1971,10 +1982,14 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(const sctp_endpoint_t *ep, sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); + if (chunk && (ntohs(chunk->chunk_hdr->length) >= + (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_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_U32(error)); - /* BUG? This does not look complete... */ return SCTP_DISPOSITION_ABORT; } @@ -2381,7 +2396,8 @@ sctp_disposition_t sctp_sf_eat_data_6_2(const sctp_endpoint_t *ep, * processing the rest of the chunks in the packet. */ sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); - sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_DATA)); SCTP_INC_STATS(SctpAborteds); SCTP_DEC_STATS(SctpCurrEstab); return SCTP_DISPOSITION_CONSUME; @@ -2596,7 +2612,8 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(const sctp_endpoint_t *ep, * processing the rest of the chunks in the packet. */ sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET,SCTP_NULL()); - sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_DATA)); SCTP_INC_STATS(SctpAborteds); SCTP_DEC_STATS(SctpCurrEstab); return SCTP_DISPOSITION_CONSUME; @@ -3547,7 +3564,8 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort(const sctp_endpoint_t *ep, */ /* Delete the established association. */ - sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_USER_ABORT)); SCTP_INC_STATS(SctpAborteds); SCTP_DEC_STATS(SctpCurrEstab); @@ -3686,7 +3704,8 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort(const sctp_endpoint_t *ep, */ /* Delete the established association. */ - sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(SCTP_ERROR_USER_ABORT)); return retval; } @@ -4012,7 +4031,8 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(const sctp_endpoint_t *ep, if (asoc->overall_error_count >= asoc->overall_error_threshold) { /* CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ - sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_INC_STATS(SctpAborteds); SCTP_DEC_STATS(SctpCurrEstab); return SCTP_DISPOSITION_DELETE_TCB; @@ -4147,7 +4167,8 @@ sctp_disposition_t sctp_sf_t1_timer_expire(const sctp_endpoint_t *ep, SCTP_TO(timer)); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(repl)); } else { - sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_INIT_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); return SCTP_DISPOSITION_DELETE_TCB; } @@ -4181,7 +4202,8 @@ sctp_disposition_t sctp_sf_t2_timer_expire(const sctp_endpoint_t *ep, SCTP_DEBUG_PRINTK("Timer T2 expired.\n"); if (asoc->overall_error_count >= asoc->overall_error_threshold) { /* Note: CMD_ASSOC_FAILED calls CMD_DELETE_TCB. */ - sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); SCTP_INC_STATS(SctpAborteds); SCTP_DEC_STATS(SctpCurrEstab); return SCTP_DISPOSITION_DELETE_TCB; @@ -4244,7 +4266,8 @@ sctp_disposition_t sctp_sf_t5_timer_expire(const sctp_endpoint_t *ep, goto nomem; sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, SCTP_CHUNK(reply)); - sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, SCTP_NULL()); + sctp_add_cmd_sf(commands, SCTP_CMD_ASSOC_FAILED, + SCTP_U32(SCTP_ERROR_NO_ERROR)); return SCTP_DISPOSITION_DELETE_TCB; nomem: -- cgit v1.2.3 From c425b68c35c0d90223b5e8f235f389d3213fcb0b Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Fri, 28 Mar 2003 02:49:50 -0600 Subject: [SCTP] Add LINKLOCAL/sin6_scope_id support. --- include/net/sctp/sctp.h | 51 +++++++++--------- include/net/sctp/sm.h | 13 +++-- include/net/sctp/structs.h | 127 ++++++++++++++++++++++---------------------- include/net/sctp/ulpevent.h | 6 +-- net/sctp/associola.c | 30 +++++------ net/sctp/bind_addr.c | 30 +++++------ net/sctp/input.c | 14 ++--- net/sctp/ipv6.c | 108 +++++++++++++++++++++++++++++-------- net/sctp/protocol.c | 23 +++++++- net/sctp/sm_make_chunk.c | 87 +++++++++++++++++------------- net/sctp/sm_sideeffect.c | 11 ++-- net/sctp/sm_statefuns.c | 10 ++-- net/sctp/socket.c | 9 ++-- net/sctp/ulpevent.c | 1 + 14 files changed, 306 insertions(+), 214 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 5c003845721c..5c537d9e7499 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -199,28 +199,28 @@ extern void sctp_hash_digest(const char *secret, const int secret_len, #define sctp_spin_unlock_irqrestore(lock, flags) \ spin_unlock_irqrestore(lock, flags) #define sctp_local_bh_disable() local_bh_disable() -#define sctp_local_bh_enable() local_bh_enable() -#define sctp_spin_lock(lock) spin_lock(lock) -#define sctp_spin_unlock(lock) spin_unlock(lock) -#define sctp_write_lock(lock) write_lock(lock) +#define sctp_local_bh_enable() local_bh_enable() +#define sctp_spin_lock(lock) spin_lock(lock) +#define sctp_spin_unlock(lock) spin_unlock(lock) +#define sctp_write_lock(lock) write_lock(lock) #define sctp_write_unlock(lock) write_unlock(lock) -#define sctp_read_lock(lock) read_lock(lock) -#define sctp_read_unlock(lock) read_unlock(lock) +#define sctp_read_lock(lock) read_lock(lock) +#define sctp_read_unlock(lock) read_unlock(lock) /* sock lock wrappers. */ -#define sctp_lock_sock(sk) lock_sock(sk) -#define sctp_release_sock(sk) release_sock(sk) -#define sctp_bh_lock_sock(sk) bh_lock_sock(sk) -#define sctp_bh_unlock_sock(sk) bh_unlock_sock(sk) -#define SCTP_SOCK_SLEEP_PRE(sk) SOCK_SLEEP_PRE(sk) +#define sctp_lock_sock(sk) lock_sock(sk) +#define sctp_release_sock(sk) release_sock(sk) +#define sctp_bh_lock_sock(sk) bh_lock_sock(sk) +#define sctp_bh_unlock_sock(sk) bh_unlock_sock(sk) +#define SCTP_SOCK_SLEEP_PRE(sk) SOCK_SLEEP_PRE(sk) #define SCTP_SOCK_SLEEP_POST(sk) SOCK_SLEEP_POST(sk) /* SCTP SNMP MIB stats handlers */ DECLARE_SNMP_STAT(struct sctp_mib, sctp_statistics); -#define SCTP_INC_STATS(field) SNMP_INC_STATS(sctp_statistics, field) -#define SCTP_INC_STATS_BH(field) SNMP_INC_STATS_BH(sctp_statistics, field) -#define SCTP_INC_STATS_USER(field) SNMP_INC_STATS_USER(sctp_statistics, field) -#define SCTP_DEC_STATS(field) SNMP_DEC_STATS(sctp_statistics, field) +#define SCTP_INC_STATS(field) SNMP_INC_STATS(sctp_statistics, field) +#define SCTP_INC_STATS_BH(field) SNMP_INC_STATS_BH(sctp_statistics, field) +#define SCTP_INC_STATS_USER(field) SNMP_INC_STATS_USER(sctp_statistics, field) +#define SCTP_DEC_STATS(field) SNMP_DEC_STATS(sctp_statistics, field) /* Determine if this is a valid kernel address. */ static inline int sctp_is_valid_kaddr(unsigned long addr) @@ -488,21 +488,24 @@ static inline struct sctp_protocol *sctp_get_protocol(void) /* Convert from an IP version number to an Address Family symbol. */ static inline int ipver2af(__u8 ipver) { - int family; - switch (ipver) { case 4: - family = AF_INET; - break; + return AF_INET; case 6: - family = AF_INET6; - break; + return AF_INET6; default: - family = 0; - break; + return 0; }; +} - return family; +/* Perform some sanity checks. */ +static inline int sctp_sanity_check(void) +{ + SCTP_ASSERT(sizeof(struct sctp_ulpevent) <= + sizeof(((struct sk_buff *)0)->cb), + "SCTP: ulpevent does not fit in skb!\n", return 0); + + return 1; } /* Warning: The following hash functions assume a power of two 'size'. */ diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 16737eda8d8c..6b9a30330630 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -197,15 +197,14 @@ sctp_state_fn_t sctp_addip_do_asconf; sctp_state_fn_t sctp_addip_do_asconf_ack; /* Prototypes for utility support functions. */ -__u8 sctp_get_chunk_type(sctp_chunk_t *chunk); +__u8 sctp_get_chunk_type(struct sctp_chunk *chunk); sctp_sm_table_entry_t *sctp_sm_lookup_event(sctp_event_t event_type, sctp_state_t state, sctp_subtype_t event_subtype); - -time_t timeval_sub(struct timeval *, struct timeval *); -sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *, - sctp_chunk_t *, - const int priority); +int sctp_chunk_iif(const struct sctp_chunk *); +struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *, + struct sctp_chunk *, + int gfp); __u32 sctp_generate_verification_tag(void); void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag); @@ -344,7 +343,7 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *); /* 4th level prototypes */ void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *, - __u16 port); + __u16 port, int iif); int sctp_addr2sockaddr(const union sctp_params, union sctp_addr *); int sockaddr2sctp_addr(const union sctp_addr *, sctp_addr_param_t *); diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 44c81f5c3020..5cf74d3c2882 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -242,6 +242,7 @@ struct sctp_af { void (*inaddr_any) (union sctp_addr *, unsigned short); int (*is_any) (const union sctp_addr *); int (*available) (const union sctp_addr *); + int (*skb_iif) (const struct sk_buff *sk); __u16 net_header_len; int sockaddr_len; sa_family_t sa_family; @@ -260,6 +261,7 @@ struct sctp_pf { const union sctp_addr *, struct sctp_opt *); int (*bind_verify) (struct sctp_opt *, union sctp_addr *); + int (*send_verify) (struct sctp_opt *, union sctp_addr *); int (*supported_addrs)(const struct sctp_opt *, __u16 *); struct sock *(*create_accept_sk) (struct sock *sk, struct sctp_association *asoc); @@ -430,7 +432,7 @@ struct sctp_ssnmap { }; struct sctp_ssnmap *sctp_ssnmap_init(struct sctp_ssnmap *, __u16, __u16); -struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int priority); +struct sctp_ssnmap *sctp_ssnmap_new(__u16 in, __u16 out, int gfp); void sctp_ssnmap_free(struct sctp_ssnmap *map); void sctp_ssnmap_clear(struct sctp_ssnmap *map); @@ -509,7 +511,7 @@ struct sctp_chunk { struct sctp_sndrcvinfo sinfo; /* Which association does this belong to? */ - sctp_association_t *asoc; + struct sctp_association *asoc; /* What endpoint received this chunk? */ sctp_endpoint_common_t *rcvr; @@ -541,11 +543,11 @@ struct sctp_chunk { struct sctp_transport *transport; }; -sctp_chunk_t *sctp_make_chunk(const sctp_association_t *, __u8 type, +sctp_chunk_t *sctp_make_chunk(const struct sctp_association *, __u8 type, __u8 flags, int size); void sctp_free_chunk(sctp_chunk_t *); void *sctp_addto_chunk(sctp_chunk_t *chunk, int len, const void *data); -sctp_chunk_t *sctp_chunkify(struct sk_buff *, const sctp_association_t *, +sctp_chunk_t *sctp_chunkify(struct sk_buff *, const struct sctp_association *, struct sock *); void sctp_init_addrs(sctp_chunk_t *, union sctp_addr *, union sctp_addr *); const union sctp_addr *sctp_source(const sctp_chunk_t *chunk); @@ -560,7 +562,7 @@ struct sockaddr_storage_list { union sctp_addr a; }; -typedef sctp_chunk_t *(sctp_packet_phandler_t)(sctp_association_t *); +typedef sctp_chunk_t *(sctp_packet_phandler_t)(struct sctp_association *); /* This structure holds lists of chunks as we are assembling for * transmission. @@ -596,7 +598,7 @@ struct sctp_packet { char has_cookie_echo; /* This packet containsa SACK chunk. */ - char has_sack; + char has_sack; /* SCTP cannot fragment this packet. So let ip fragment it. */ char ipfragok; @@ -663,7 +665,7 @@ struct sctp_transport { struct sctp_af *af_specific; /* Which association do we belong to? */ - sctp_association_t *asoc; + struct sctp_association *asoc; /* RFC2960 * @@ -802,7 +804,8 @@ struct sctp_transport { struct sctp_transport *sctp_transport_new(const union sctp_addr *, int); struct sctp_transport *sctp_transport_init(struct sctp_transport *, const union sctp_addr *, int); -void sctp_transport_set_owner(struct sctp_transport *, sctp_association_t *); +void sctp_transport_set_owner(struct sctp_transport *, + struct sctp_association *); void sctp_transport_route(struct sctp_transport *, union sctp_addr *, struct sctp_opt *); void sctp_transport_pmtu(struct sctp_transport *); @@ -865,7 +868,7 @@ void sctp_inq_set_th_handler(struct sctp_inq *, void (*)(void *), void *); * When free()'d, it empties itself out via output_handler(). */ struct sctp_outq { - sctp_association_t *asoc; + struct sctp_association *asoc; /* Data pending that has never been transmitted. */ struct sk_buff_head out; @@ -908,8 +911,8 @@ struct sctp_outq { int malloced; }; -struct sctp_outq *sctp_outq_new(sctp_association_t *); -void sctp_outq_init(sctp_association_t *, struct sctp_outq *); +struct sctp_outq *sctp_outq_new(struct sctp_association *); +void sctp_outq_init(struct sctp_association *, struct sctp_outq *); void sctp_outq_teardown(struct sctp_outq *); void sctp_outq_free(struct sctp_outq*); int sctp_outq_tail(struct sctp_outq *, sctp_chunk_t *chunk); @@ -953,20 +956,16 @@ sctp_bind_addr_t *sctp_bind_addr_new(int gfp_mask); void sctp_bind_addr_init(sctp_bind_addr_t *, __u16 port); void sctp_bind_addr_free(sctp_bind_addr_t *); int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src, - sctp_scope_t scope, int priority,int flags); + sctp_scope_t scope, int gfp,int flags); int sctp_add_bind_addr(sctp_bind_addr_t *, union sctp_addr *, - int priority); + int gfp); int sctp_del_bind_addr(sctp_bind_addr_t *, union sctp_addr *); int sctp_bind_addr_match(sctp_bind_addr_t *, const union sctp_addr *, struct sctp_opt *); -union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, - int *addrs_len, - int priority); -int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, - __u8 *raw_addr_list, - int addrs_len, - unsigned short port, - int priority); +union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp, + int *addrs_len, int gfp); +int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw, int len, + __u16 port, int gfp); sctp_scope_t sctp_scope(const union sctp_addr *); int sctp_in_scope(const union sctp_addr *addr, const sctp_scope_t scope); @@ -1069,7 +1068,7 @@ struct sctp_endpoint { * pointer, or table pointers dependent on how SCTP * is implemented. */ - /* This is really a list of sctp_association_t entries. */ + /* This is really a list of struct sctp_association entries. */ struct list_head asocs; /* Secret Key: A secret key used by this endpoint to compute @@ -1105,12 +1104,12 @@ static inline sctp_endpoint_t *sctp_ep(sctp_endpoint_common_t *base) sctp_endpoint_t *sctp_endpoint_new(struct sctp_protocol *, struct sock *, int); sctp_endpoint_t *sctp_endpoint_init(struct sctp_endpoint *, struct sctp_protocol *, - struct sock *, int priority); + struct sock *, int gfp); void sctp_endpoint_free(sctp_endpoint_t *); void sctp_endpoint_put(sctp_endpoint_t *); void sctp_endpoint_hold(sctp_endpoint_t *); -void sctp_endpoint_add_asoc(sctp_endpoint_t *, sctp_association_t *asoc); -sctp_association_t *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, +void sctp_endpoint_add_asoc(sctp_endpoint_t *, struct sctp_association *asoc); +struct sctp_association *sctp_endpoint_lookup_assoc(const sctp_endpoint_t *ep, const union sctp_addr *paddr, struct sctp_transport **); int sctp_endpoint_is_peeled_off(sctp_endpoint_t *, const union sctp_addr *); @@ -1119,18 +1118,16 @@ sctp_endpoint_t *sctp_endpoint_is_match(sctp_endpoint_t *, int sctp_has_association(const union sctp_addr *laddr, const union sctp_addr *paddr); -int sctp_verify_init(const sctp_association_t *asoc, - sctp_cid_t cid, - sctp_init_chunk_t *peer_init, - sctp_chunk_t *chunk, - sctp_chunk_t **err_chunk); -int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, - const union sctp_addr *peer_addr, - sctp_init_chunk_t *peer_init, int priority); -int sctp_process_param(sctp_association_t *asoc, union sctp_params param, - const union sctp_addr *peer_addr, int priority); -__u32 sctp_generate_tag(const sctp_endpoint_t *ep); -__u32 sctp_generate_tsn(const sctp_endpoint_t *ep); +int sctp_verify_init(const struct sctp_association *asoc, sctp_cid_t, + sctp_init_chunk_t *peer_init, struct sctp_chunk *chunk, + struct sctp_chunk **err_chunk); +int sctp_process_init(struct sctp_association *, sctp_cid_t cid, + const union sctp_addr *peer, + sctp_init_chunk_t *init, int gfp); +int sctp_process_param(struct sctp_association *, union sctp_params param, + const union sctp_addr *from, int gfp); +__u32 sctp_generate_tag(const sctp_endpoint_t *); +__u32 sctp_generate_tsn(const sctp_endpoint_t *); /* RFC2960 @@ -1159,7 +1156,7 @@ struct sctp_association { struct list_head asocs; /* This is a signature that lets us know that this is a - * sctp_association_t data structure. Used for mapping an + * struct sctp_association data structure. Used for mapping an * association id to an association. */ __u32 eyecatcher; @@ -1562,44 +1559,46 @@ enum { }; /* Recover the outter association structure. */ -static inline sctp_association_t *sctp_assoc(sctp_endpoint_common_t *base) +static inline struct sctp_association *sctp_assoc(sctp_endpoint_common_t *base) { - sctp_association_t *asoc; + struct sctp_association *asoc; - asoc = container_of(base, sctp_association_t, base); + asoc = container_of(base, struct sctp_association, base); return asoc; } /* These are function signatures for manipulating associations. */ -sctp_association_t * +struct sctp_association * sctp_association_new(const sctp_endpoint_t *, const struct sock *, - sctp_scope_t scope, int priority); -sctp_association_t * -sctp_association_init(sctp_association_t *, const sctp_endpoint_t *, + sctp_scope_t scope, int gfp); +struct sctp_association * +sctp_association_init(struct sctp_association *, const sctp_endpoint_t *, const struct sock *, sctp_scope_t scope, - int priority); -void sctp_association_free(sctp_association_t *); -void sctp_association_put(sctp_association_t *); -void sctp_association_hold(sctp_association_t *); - -struct sctp_transport *sctp_assoc_choose_shutdown_transport(sctp_association_t *); -void sctp_assoc_update_retran_path(sctp_association_t *); -struct sctp_transport *sctp_assoc_lookup_paddr(const sctp_association_t *, + int gfp); +void sctp_association_free(struct sctp_association *); +void sctp_association_put(struct sctp_association *); +void sctp_association_hold(struct sctp_association *); + +struct sctp_transport *sctp_assoc_choose_shutdown_transport( + struct sctp_association *); +void sctp_assoc_update_retran_path(struct sctp_association *); +struct sctp_transport *sctp_assoc_lookup_paddr(const struct sctp_association *, const union sctp_addr *); -struct sctp_transport *sctp_assoc_add_peer(sctp_association_t *, +struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *, const union sctp_addr *address, - const int priority); + const int gfp); void sctp_assoc_control_transport(struct sctp_association *, struct sctp_transport *, sctp_transport_cmd_t, sctp_sn_error_t); -struct sctp_transport *sctp_assoc_lookup_tsn(sctp_association_t *, __u32); -struct sctp_transport *sctp_assoc_is_match(sctp_association_t *, +struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *, __u32); +struct sctp_transport *sctp_assoc_is_match(struct sctp_association *, const union sctp_addr *, const union sctp_addr *); -void sctp_assoc_migrate(sctp_association_t *, struct sock *); -void sctp_assoc_update(sctp_association_t *dst, sctp_association_t *src); +void sctp_assoc_migrate(struct sctp_association *, struct sock *); +void sctp_assoc_update(struct sctp_association *old, + struct sctp_association *new); __u32 sctp_association_get_next_tsn(struct sctp_association *); __u32 sctp_association_get_tsn_block(struct sctp_association *, int); @@ -1609,14 +1608,14 @@ void sctp_assoc_rwnd_increase(struct sctp_association *, int); void sctp_assoc_rwnd_decrease(struct sctp_association *, int); void sctp_assoc_set_primary(struct sctp_association *, struct sctp_transport *); -int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *, int); -int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *, - sctp_cookie_t *, int); +int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *, int); +int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *, + sctp_cookie_t *, int gfp); int sctp_cmp_addr_exact(const union sctp_addr *ss1, const union sctp_addr *ss2); -sctp_chunk_t *sctp_get_ecne_prepend(sctp_association_t *asoc); -sctp_chunk_t *sctp_get_no_prepend(sctp_association_t *asoc); +sctp_chunk_t *sctp_get_ecne_prepend(struct sctp_association *asoc); +sctp_chunk_t *sctp_get_no_prepend(struct sctp_association *asoc); /* A convenience structure to parse out SCTP specific CMSGs. */ typedef struct sctp_cmsgs { diff --git a/include/net/sctp/ulpevent.h b/include/net/sctp/ulpevent.h index 8d0edaf22025..d508ca47ea4d 100644 --- a/include/net/sctp/ulpevent.h +++ b/include/net/sctp/ulpevent.h @@ -38,7 +38,6 @@ * be incorporated into the next SCTP release. */ - #ifndef __sctp_ulpevent_h__ #define __sctp_ulpevent_h__ @@ -50,6 +49,7 @@ struct sctp_ulpevent { struct sctp_association *asoc; struct sctp_sndrcvinfo sndrcvinfo; int msg_flags; + int iif; }; /* Retrieve the skb this event sits inside of. */ @@ -61,9 +61,9 @@ static inline struct sk_buff *sctp_event2skb(struct sctp_ulpevent *ev) /* Retrieve & cast the event sitting inside the skb. */ static inline struct sctp_ulpevent *sctp_skb2event(struct sk_buff *skb) { - return (struct sctp_ulpevent *)skb->cb; + return (struct sctp_ulpevent *)skb->cb; } - + struct sctp_ulpevent *sctp_ulpevent_new(int size, int flags, int priority); struct sctp_ulpevent *sctp_ulpevent_init(struct sctp_ulpevent *, int flags); void sctp_ulpevent_free(struct sctp_ulpevent *); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index ff0483975528..b283b1079c38 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -641,8 +641,6 @@ __u32 sctp_association_get_tsn_block(sctp_association_t *asoc, int num) /* Compare two addresses to see if they match. Wildcard addresses * only match themselves. - * - * FIXME: We do not match address scopes correctly. */ int sctp_cmp_addr_exact(const union sctp_addr *ss1, const union sctp_addr *ss2) @@ -650,7 +648,7 @@ int sctp_cmp_addr_exact(const union sctp_addr *ss1, struct sctp_af *af; af = sctp_get_af_specific(ss1->sa.sa_family); - if (!af) + if (unlikely(!af)) return 0; return af->cmp_addr(ss1, ss2); @@ -664,14 +662,14 @@ sctp_chunk_t *sctp_get_ecne_prepend(struct sctp_association *asoc) { struct sctp_chunk *chunk; - /* Send ECNE if needed. - * Not being able to allocate a chunk here is not deadly. + /* Send ECNE if needed. + * Not being able to allocate a chunk here is not deadly. */ if (asoc->need_ecne) chunk = sctp_make_ecne(asoc, asoc->last_ecne_tsn); else chunk = NULL; - + return chunk; } @@ -988,9 +986,9 @@ static inline int sctp_peer_needs_update(struct sctp_association *asoc) case SCTP_STATE_ESTABLISHED: case SCTP_STATE_SHUTDOWN_PENDING: case SCTP_STATE_SHUTDOWN_RECEIVED: - if ((asoc->rwnd > asoc->a_rwnd) && + if ((asoc->rwnd > asoc->a_rwnd) && ((asoc->rwnd - asoc->a_rwnd) >= - min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) + min_t(__u32, (asoc->base.sk->rcvbuf >> 1), asoc->pmtu))) return 1; break; default: @@ -1057,14 +1055,14 @@ void sctp_assoc_rwnd_decrease(sctp_association_t *asoc, int len) asoc->rwnd = 0; } SCTP_DEBUG_PRINTK("%s: asoc %p rwnd decreased by %d to (%u, %u)\n", - __FUNCTION__, asoc, len, asoc->rwnd, + __FUNCTION__, asoc, len, asoc->rwnd, asoc->rwnd_over); } /* Build the bind address list for the association based on info from the * local endpoint and the remote peer. */ -int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *asoc, int priority) +int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *asoc, int gfp) { sctp_scope_t scope; int flags; @@ -1081,19 +1079,17 @@ int sctp_assoc_set_bind_addr_from_ep(sctp_association_t *asoc, int priority) return sctp_bind_addr_copy(&asoc->base.bind_addr, &asoc->ep->base.bind_addr, - scope, priority, flags); + scope, gfp, flags); } /* Build the association's bind address list from the cookie. */ int sctp_assoc_set_bind_addr_from_cookie(sctp_association_t *asoc, - sctp_cookie_t *cookie, int priority) + sctp_cookie_t *cookie, int gfp) { int var_size2 = ntohs(cookie->peer_init->chunk_hdr.length); int var_size3 = cookie->raw_addr_list_len; - __u8 *raw_addr_list = (__u8 *)cookie + sizeof(sctp_cookie_t) + - var_size2; + __u8 *raw = (__u8 *)cookie + sizeof(sctp_cookie_t) + var_size2; - return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw_addr_list, - var_size3, asoc->ep->base.bind_addr.port, - priority); + return sctp_raw_to_bind_addrs(&asoc->base.bind_addr, raw, var_size3, + asoc->ep->base.bind_addr.port, gfp); } diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 26c62125226b..5052ce6969ef 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -53,7 +53,7 @@ /* Forward declarations for internal helpers. */ static int sctp_copy_one_addr(sctp_bind_addr_t *, union sctp_addr *, - sctp_scope_t scope, int priority, int flags); + sctp_scope_t scope, int gfp, int flags); static void sctp_bind_addr_clean(sctp_bind_addr_t *); /* First Level Abstractions. */ @@ -62,7 +62,7 @@ static void sctp_bind_addr_clean(sctp_bind_addr_t *); * in 'src' which have a broader scope than 'scope'. */ int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src, - sctp_scope_t scope, int priority, int flags) + sctp_scope_t scope, int gfp, int flags) { struct sockaddr_storage_list *addr; struct list_head *pos; @@ -75,7 +75,7 @@ int sctp_bind_addr_copy(sctp_bind_addr_t *dest, const sctp_bind_addr_t *src, list_for_each(pos, &src->address_list) { addr = list_entry(pos, struct sockaddr_storage_list, list); error = sctp_copy_one_addr(dest, &addr->a, scope, - priority, flags); + gfp, flags); if (error < 0) goto out; } @@ -88,11 +88,11 @@ out: } /* Create a new SCTP_bind_addr from nothing. */ -sctp_bind_addr_t *sctp_bind_addr_new(int priority) +sctp_bind_addr_t *sctp_bind_addr_new(int gfp) { sctp_bind_addr_t *retval; - retval = t_new(sctp_bind_addr_t, priority); + retval = t_new(sctp_bind_addr_t, gfp); if (!retval) goto nomem; @@ -144,12 +144,12 @@ void sctp_bind_addr_free(sctp_bind_addr_t *bp) /* Add an address to the bind address list in the SCTP_bind_addr structure. */ int sctp_add_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *new, - int priority) + int gfp) { struct sockaddr_storage_list *addr; /* Add the address to the bind address list. */ - addr = t_new(struct sockaddr_storage_list, priority); + addr = t_new(struct sockaddr_storage_list, gfp); if (!addr) return -ENOMEM; @@ -197,7 +197,7 @@ int sctp_del_bind_addr(sctp_bind_addr_t *bp, union sctp_addr *del_addr) * The second argument is the return value for the length. */ union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, - int *addrs_len, int priority) + int *addrs_len, int gfp) { union sctp_params addrparms; union sctp_params retval; @@ -214,7 +214,7 @@ union sctp_params sctp_bind_addrs_to_raw(const sctp_bind_addr_t *bp, len += sizeof(sctp_addr_param_t); } - retval.v = kmalloc(len, priority); + retval.v = kmalloc(len, gfp); if (!retval.v) goto end_raw; @@ -238,7 +238,7 @@ end_raw: * address parameters). */ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list, - int addrs_len, __u16 port, int priority) + int addrs_len, __u16 port, int gfp) { sctp_addr_param_t *rawaddr; sctp_paramhdr_t *param; @@ -254,8 +254,8 @@ int sctp_raw_to_bind_addrs(sctp_bind_addr_t *bp, __u8 *raw_addr_list, switch (param->type) { case SCTP_PARAM_IPV4_ADDRESS: case SCTP_PARAM_IPV6_ADDRESS: - sctp_param2sockaddr(&addr, rawaddr, port); - retval = sctp_add_bind_addr(bp, &addr, priority); + sctp_param2sockaddr(&addr, rawaddr, port, 0); + retval = sctp_add_bind_addr(bp, &addr, gfp); if (retval) { /* Can't finish building the list, clean up. */ sctp_bind_addr_clean(bp); @@ -300,14 +300,14 @@ int sctp_bind_addr_match(sctp_bind_addr_t *bp, const union sctp_addr *addr, /* Copy out addresses from the global local address list. */ static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr, - sctp_scope_t scope, int priority, int flags) + sctp_scope_t scope, int gfp, int flags) { struct sctp_protocol *proto = sctp_get_protocol(); int error = 0; if (sctp_is_any(addr)) { error = sctp_copy_local_addr_list(proto, dest, scope, - priority, flags); + gfp, flags); } else if (sctp_in_scope(addr, scope)) { /* Now that the address is in scope, check to see if * the address type is supported by local sock as @@ -318,7 +318,7 @@ static int sctp_copy_one_addr(sctp_bind_addr_t *dest, union sctp_addr *addr, (((AF_INET6 == addr->sa.sa_family) && (flags & SCTP_ADDR6_ALLOWED) && (flags & SCTP_ADDR6_PEERSUPP)))) - error = sctp_add_bind_addr(dest, addr, priority); + error = sctp_add_bind_addr(dest, addr, gfp); } return error; diff --git a/net/sctp/input.c b/net/sctp/input.c index cd9f6ad38d71..a6ad80beed98 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -674,9 +674,9 @@ void __sctp_unhash_established(sctp_association_t *asoc) } /* Look up an association. */ -sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr, - const union sctp_addr *paddr, - struct sctp_transport **transportp) +sctp_association_t *__sctp_lookup_association(const union sctp_addr *local, + const union sctp_addr *peer, + struct sctp_transport **pt) { sctp_hashbucket_t *head; sctp_endpoint_common_t *epb; @@ -687,12 +687,12 @@ sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr, /* Optimize here for direct hit, only listening connections can * have wildcards anyways. */ - hash = sctp_assoc_hashfn(laddr->v4.sin_port, paddr->v4.sin_port); + hash = sctp_assoc_hashfn(local->v4.sin_port, peer->v4.sin_port); head = &sctp_proto.assoc_hashbucket[hash]; read_lock(&head->lock); for (epb = head->chain; epb; epb = epb->next) { asoc = sctp_assoc(epb); - transport = sctp_assoc_is_match(asoc, laddr, paddr); + transport = sctp_assoc_is_match(asoc, local, peer); if (transport) goto hit; } @@ -702,7 +702,7 @@ sctp_association_t *__sctp_lookup_association(const union sctp_addr *laddr, return NULL; hit: - *transportp = transport; + *pt = transport; sctp_association_hold(asoc); sock_hold(epb->sk); read_unlock(&head->lock); @@ -805,7 +805,7 @@ static sctp_association_t *__sctp_rcv_init_lookup(struct sk_buff *skb, (SCTP_PARAM_IPV6_ADDRESS != params.p->type)) continue; - sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source)); + sctp_param2sockaddr(paddr, params.addr, ntohs(sh->source), 0); asoc = __sctp_lookup_association(laddr, paddr, transportp); if (asoc) return asoc; diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index a7ea483c615f..4256371eba35 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -161,9 +161,12 @@ static int sctp_v6_xmit(struct sk_buff *skb, struct sctp_transport *transport, fl.fl6_flowlabel = np->flow_label; IP6_ECN_flow_xmit(sk, fl.fl6_flowlabel); - fl.oif = sk->bound_dev_if; + if (ipv6_addr_type(fl.fl6_src) & IPV6_ADDR_LINKLOCAL) + fl.oif = transport->saddr.v6.sin6_scope_id; + else + fl.oif = sk->bound_dev_if; fl.uli_u.ports.sport = inet_sk(sk)->sport; - fl.uli_u.ports.dport = inet_sk(sk)->dport; + fl.uli_u.ports.dport = transport->ipaddr.v6.sin6_port; if (np->opt && np->opt->srcrt) { struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; @@ -259,7 +262,7 @@ void sctp_v6_get_saddr(sctp_association_t *asoc, struct dst_entry *dst, __FUNCTION__, asoc, dst, NIP6(&daddr->v6.sin6_addr)); if (!asoc) { - ipv6_get_saddr(dst, &daddr->v6.sin6_addr, &saddr->v6.sin6_addr); + ipv6_get_saddr(dst, &daddr->v6.sin6_addr,&saddr->v6.sin6_addr); SCTP_DEBUG_PRINTK("saddr from ipv6_get_saddr: " "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n", NIP6(&saddr->v6.sin6_addr)); @@ -324,6 +327,7 @@ static void sctp_v6_copy_addrlist(struct list_head *addrlist, addr->a.v6.sin6_family = AF_INET6; addr->a.v6.sin6_port = 0; addr->a.v6.sin6_addr = ifp->addr; + addr->a.v6.sin6_scope_id = dev->ifindex; INIT_LIST_HEAD(&addr->list); list_add_tail(&addr->list, addrlist); } @@ -344,7 +348,7 @@ static void sctp_v6_from_skb(union sctp_addr *addr,struct sk_buff *skb, port = &addr->v6.sin6_port; addr->v6.sin6_family = AF_INET6; addr->v6.sin6_flowinfo = 0; /* FIXME */ - addr->v6.sin6_scope_id = 0; /* FIXME */ + addr->v6.sin6_scope_id = ((struct inet6_skb_parm *)skb->cb)->iif; sh = (struct sctphdr *) skb->h.raw; if (is_saddr) { @@ -381,19 +385,25 @@ static void sctp_v6_dst_saddr(union sctp_addr *addr, struct dst_entry *dst, ipv6_addr_copy(&addr->v6.sin6_addr, &rt->rt6i_src.addr); } -/* Compare addresses exactly. Well.. almost exactly; ignore scope_id - * for now. FIXME: v4-mapped-v6. +/* Compare addresses exactly. + * FIXME: v4-mapped-v6. */ static int sctp_v6_cmp_addr(const union sctp_addr *addr1, const union sctp_addr *addr2) { - int match; if (addr1->sa.sa_family != addr2->sa.sa_family) return 0; - match = !ipv6_addr_cmp((struct in6_addr *)&addr1->v6.sin6_addr, - (struct in6_addr *)&addr2->v6.sin6_addr); + if (ipv6_addr_cmp(&addr1->v6.sin6_addr, &addr2->v6.sin6_addr)) + return 0; + /* If this is a linklocal address, compare the scope_id. */ + if (ipv6_addr_type(&addr1->v6.sin6_addr) & IPV6_ADDR_LINKLOCAL) { + if (addr1->v6.sin6_scope_id && addr2->v6.sin6_scope_id && + (addr1->v6.sin6_scope_id != addr2->v6.sin6_scope_id)) { + return 0; + } + } - return match; + return 1; } /* Initialize addr struct to INADDR_ANY. */ @@ -427,7 +437,6 @@ static int sctp_v6_available(const union sctp_addr *addr) return ipv6_chk_addr(in6, NULL); } - /* This function checks if the address is a valid address to be used for * SCTP. * @@ -533,6 +542,13 @@ out: return newsk; } +/* Where did this skb come from? */ +static int sctp_v6_skb_iif(const struct sk_buff *skb) +{ + struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; + return opt->iif; +} + /* Initialize a PF_INET6 socket msg_name. */ static void sctp_inet6_msgname(char *msgname, int *addr_len) { @@ -541,13 +557,13 @@ static void sctp_inet6_msgname(char *msgname, int *addr_len) sin6 = (struct sockaddr_in6 *)msgname; sin6->sin6_family = AF_INET6; sin6->sin6_flowinfo = 0; - sin6->sin6_scope_id = 0; + sin6->sin6_scope_id = 0; /*FIXME */ *addr_len = sizeof(struct sockaddr_in6); } /* Initialize a PF_INET msgname from a ulpevent. */ -static void sctp_inet6_event_msgname(struct sctp_ulpevent *event, char *msgname, - int *addrlen) +static void sctp_inet6_event_msgname(struct sctp_ulpevent *event, + char *msgname, int *addrlen) { struct sockaddr_in6 *sin6, *sin6from; @@ -573,6 +589,8 @@ static void sctp_inet6_event_msgname(struct sctp_ulpevent *event, char *msgname, sin6from = &event->asoc->peer.primary_addr.v6; ipv6_addr_copy(&sin6->sin6_addr, &sin6from->sin6_addr); + if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) + sin6->sin6_scope_id = sin6from->sin6_scope_id; } } @@ -591,8 +609,8 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname, /* FIXME: Map ipv4 address into v4-mapped-on-v6 address. */ if (__constant_htons(ETH_P_IP) == skb->protocol) { - /* FIXME: Easy, but there was no way to test this - * yet. + /* FIXME: The latest I-D added options for two + * behaviors. */ return; } @@ -601,9 +619,8 @@ static void sctp_inet6_skb_msgname(struct sk_buff *skb, char *msgname, ipv6_addr_copy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr); if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) { - struct inet6_skb_parm *opt = - (struct inet6_skb_parm *) skb->cb; - sin6->sin6_scope_id = opt->iif; + struct sctp_ulpevent *ev = sctp_skb2event(skb); + sin6->sin6_scope_id = ev->iif; } } } @@ -657,14 +674,59 @@ static int sctp_inet6_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) struct sctp_af *af; /* ASSERT: address family has already been verified. */ - if (addr->sa.sa_family != AF_INET6) { + if (addr->sa.sa_family != AF_INET6) af = sctp_get_af_specific(addr->sa.sa_family); - } else - af = opt->pf->af; + else { + struct sock *sk; + int type = ipv6_addr_type(&addr->v6.sin6_addr); + sk = &container_of(opt, struct sctp6_sock, sctp)->sk; + if (type & IPV6_ADDR_LINKLOCAL) { + /* Note: Behavior similar to af_inet6.c: + * 1) Overrides previous bound_dev_if + * 2) Destructive even if bind isn't successful. + */ + if (addr->v6.sin6_scope_id) + sk->bound_dev_if = addr->v6.sin6_scope_id; + if (!sk->bound_dev_if) + return 0; + } + af = opt->pf->af; + } return af->available(addr); } +/* Verify that the provided sockaddr looks bindable. Common verification, + * has already been taken care of. + */ +static int sctp_inet6_send_verify(struct sctp_opt *opt, union sctp_addr *addr) +{ + struct sctp_af *af = NULL; + + /* ASSERT: address family has already been verified. */ + if (addr->sa.sa_family != AF_INET6) + af = sctp_get_af_specific(addr->sa.sa_family); + else { + struct sock *sk; + int type = ipv6_addr_type(&addr->v6.sin6_addr); + sk = &container_of(opt, struct sctp6_sock, sctp)->sk; + if (type & IPV6_ADDR_LINKLOCAL) { + /* Note: Behavior similar to af_inet6.c: + * 1) Overrides previous bound_dev_if + * 2) Destructive even if bind isn't successful. + */ + + if (addr->v6.sin6_scope_id) + sk->bound_dev_if = addr->v6.sin6_scope_id; + if (!sk->bound_dev_if) + return 0; + } + af = opt->pf->af; + } + + return af != NULL; +} + /* Fill in Supported Address Type information for INIT and INIT-ACK * chunks. Note: In the future, we may want to look at sock options * to determine whether a PF_INET6 socket really wants to have IPV4 @@ -744,6 +806,7 @@ static struct sctp_af sctp_ipv6_specific = { .inaddr_any = sctp_v6_inaddr_any, .is_any = sctp_v6_is_any, .available = sctp_v6_available, + .skb_iif = sctp_v6_skb_iif, .net_header_len = sizeof(struct ipv6hdr), .sockaddr_len = sizeof(struct sockaddr_in6), .sa_family = AF_INET6, @@ -755,6 +818,7 @@ static struct sctp_pf sctp_pf_inet6_specific = { .af_supported = sctp_inet6_af_supported, .cmp_addr = sctp_inet6_cmp_addr, .bind_verify = sctp_inet6_bind_verify, + .send_verify = sctp_inet6_send_verify, .supported_addrs = sctp_inet6_supported_addrs, .create_accept_sk = sctp_v6_create_accept_sk, .af = &sctp_ipv6_specific, diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index a1c98e3618cc..c4e03b9f74a9 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -233,7 +233,6 @@ int sctp_copy_local_addr_list(struct sctp_protocol *proto, end_copy: sctp_spin_unlock_irqrestore(&proto->local_addr_lock, flags); - return error; } @@ -383,7 +382,7 @@ 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 adddress that matches an address in the bind address list. */ -struct dst_entry *sctp_v4_get_dst(sctp_association_t *asoc, +struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc, union sctp_addr *daddr, union sctp_addr *saddr) { @@ -480,6 +479,12 @@ void sctp_v4_get_saddr(sctp_association_t *asoc, } +/* What interface did this skb arrive on? */ +int sctp_v4_skb_iif(const struct sk_buff *skb) +{ + return ((struct rtable *)skb->dst)->rt_iif; +} + /* 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) @@ -689,6 +694,14 @@ static int sctp_inet_bind_verify(struct sctp_opt *opt, union sctp_addr *addr) return sctp_v4_available(addr); } +/* Verify that sockaddr looks sendable. Common verification has already + * been taken care of. + */ +static int sctp_inet_send_verify(struct sctp_opt *opt, union sctp_addr *addr) +{ + return 1; +} + /* Fill in Supported Address Type information for INIT and INIT-ACK * chunks. Returns number of addresses supported. */ @@ -721,6 +734,7 @@ static struct sctp_pf sctp_pf_inet = { .af_supported = sctp_inet_af_supported, .cmp_addr = sctp_inet_cmp_addr, .bind_verify = sctp_inet_bind_verify, + .send_verify = sctp_inet_send_verify, .supported_addrs = sctp_inet_supported_addrs, .create_accept_sk = sctp_v4_create_accept_sk, .af = &sctp_ipv4_specific, @@ -796,6 +810,7 @@ struct sctp_af sctp_ipv4_specific = { .is_any = sctp_v4_is_any, .available = sctp_v4_available, .scope = sctp_v4_scope, + .skb_iif = sctp_v4_skb_iif, .net_header_len = sizeof(struct iphdr), .sockaddr_len = sizeof(struct sockaddr_in), .sa_family = AF_INET, @@ -873,6 +888,10 @@ __init int sctp_init(void) int i; int status = 0; + /* SCTP_DEBUG sanity check. */ + if (!sctp_sanity_check()) + return -EINVAL; + /* Add SCTP to inet_protos hash table. */ if (inet_add_protocol(&sctp_protocol, IPPROTO_SCTP) < 0) return -EAGAIN; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0f3f35affe8e..50c21b5d9093 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -66,6 +66,19 @@ #include #include +/* What was the inbound interface for this chunk? */ +int sctp_chunk_iif(const struct sctp_chunk *chunk) +{ + struct sctp_af *af; + int iif = 0; + + af = sctp_get_af_specific(ipver2af(chunk->skb->nh.iph->version)); + if (af) + iif = af->skb_iif(chunk->skb); + + return iif; +} + /* RFC 2960 3.3.2 Initiation (INIT) (1) * * Note 2: The ECN capable field is reserved for future use of @@ -145,7 +158,7 @@ void sctp_init_cause(sctp_chunk_t *chunk, __u16 cause_code, */ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, const sctp_bind_addr_t *bp, - int priority, int vparam_len) + int gfp, int vparam_len) { sctp_inithdr_t init; union sctp_params addrs; @@ -165,7 +178,7 @@ sctp_chunk_t *sctp_make_init(const sctp_association_t *asoc, addrs.v = NULL; /* Convert the provided bind address list to raw format */ - addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, priority); + addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, gfp); if (!addrs.v) goto nodata; @@ -225,7 +238,7 @@ nodata: sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc, const sctp_chunk_t *chunk, - int priority, int unkparam_len) + int gfp, int unkparam_len) { sctp_inithdr_t initack; sctp_chunk_t *retval; @@ -237,8 +250,7 @@ sctp_chunk_t *sctp_make_init_ack(const sctp_association_t *asoc, retval = NULL; - addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, - priority); + addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, gfp); if (!addrs.v) goto nomem_rawaddr; @@ -1162,7 +1174,7 @@ int sctp_datachunks_from_user(sctp_association_t *asoc, msg_len -= first_len; whole = 1; } - } + } /* How many full sized? How many bytes leftover? */ whole += msg_len / max; @@ -1198,7 +1210,7 @@ int sctp_datachunks_from_user(sctp_association_t *asoc, __skb_queue_tail(chunks, (struct sk_buff *)chunk); - /* The first chunk, the first chunk was likely short + /* The first chunk, the first chunk was likely short * to allow bundling, so reset to full size. */ if (0 == i) @@ -1282,26 +1294,26 @@ void sctp_chunk_assign_tsn(sctp_chunk_t *chunk) } /* Create a CLOSED association to use with an incoming packet. */ -sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *ep, - sctp_chunk_t *chunk, - int priority) +sctp_association_t *sctp_make_temp_asoc(const struct sctp_endpoint *ep, + struct sctp_chunk *chunk, int gfp) { sctp_association_t *asoc; + struct sk_buff *skb; sctp_scope_t scope; /* Create the bare association. */ scope = sctp_scope(sctp_source(chunk)); - asoc = sctp_association_new(ep, ep->base.sk, scope, priority); + asoc = sctp_association_new(ep, ep->base.sk, scope, gfp); if (!asoc) goto nodata; - + skb = chunk->skb; /* Create an entry for the source address of the packet. */ - switch (chunk->skb->nh.iph->version) { + /* FIXME: Use the af specific helpers. */ + switch (skb->nh.iph->version) { case 4: asoc->c.peer_addr.v4.sin_family = AF_INET; asoc->c.peer_addr.v4.sin_port = ntohs(chunk->sctp_hdr->source); - asoc->c.peer_addr.v4.sin_addr.s_addr = - chunk->skb->nh.iph->saddr; + asoc->c.peer_addr.v4.sin_addr.s_addr = skb->nh.iph->saddr; break; case 6: @@ -1309,8 +1321,9 @@ sctp_association_t *sctp_make_temp_asoc(const sctp_endpoint_t *ep, asoc->c.peer_addr.v6.sin6_port = ntohs(chunk->sctp_hdr->source); asoc->c.peer_addr.v6.sin6_flowinfo = 0; /* BUG BUG BUG */ - asoc->c.peer_addr.v6.sin6_addr = chunk->skb->nh.ipv6h->saddr; - asoc->c.peer_addr.v6.sin6_scope_id = 0; /* BUG BUG BUG */ + asoc->c.peer_addr.v6.sin6_addr = skb->nh.ipv6h->saddr; + asoc->c.peer_addr.v6.sin6_scope_id = + ((struct inet6_skb_parm *)skb->cb)->iif; break; default: @@ -1397,7 +1410,7 @@ nodata: /* Unpack the cookie from COOKIE ECHO chunk, recreating the association. */ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, const sctp_association_t *asoc, - sctp_chunk_t *chunk, int priority, + sctp_chunk_t *chunk, int gfp, int *error, sctp_chunk_t **err_chk_p) { sctp_association_t *retval = NULL; @@ -1408,6 +1421,7 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, __u8 digest_buf[SCTP_SIGNATURE_SIZE]; int secret; sctp_scope_t scope; + struct sk_buff *skb = chunk->skb; headersize = sizeof(sctp_chunkhdr_t) + SCTP_SECRET_SIZE; bodysize = ntohs(chunk->chunk_hdr->length) - headersize; @@ -1450,7 +1464,7 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, * an association, there is no need to check cookie's expiration * for init collision case of lost COOKIE ACK. */ - if (!asoc && tv_lt(bear_cookie->expiration, chunk->skb->stamp)) { + if (!asoc && tv_lt(bear_cookie->expiration, skb->stamp)) { __u16 len; /* * Section 3.3.10.3 Stale Cookie Error (3) @@ -1463,9 +1477,9 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, len = ntohs(chunk->chunk_hdr->length); *err_chk_p = sctp_make_op_error_space(asoc, chunk, len); if (*err_chk_p) { - suseconds_t usecs = (chunk->skb->stamp.tv_sec - + suseconds_t usecs = (skb->stamp.tv_sec - bear_cookie->expiration.tv_sec) * 1000000L + - chunk->skb->stamp.tv_usec - + skb->stamp.tv_usec - bear_cookie->expiration.tv_usec; usecs = htonl(usecs); @@ -1480,7 +1494,7 @@ sctp_association_t *sctp_unpack_cookie(const sctp_endpoint_t *ep, /* Make a new base association. */ scope = sctp_scope(sctp_source(chunk)); - retval = sctp_association_new(ep, ep->base.sk, scope, priority); + retval = sctp_association_new(ep, ep->base.sk, scope, gfp); if (!retval) { *error = -SCTP_IERROR_NOMEM; goto fail; @@ -1522,13 +1536,14 @@ malformed: * 3rd Level Abstractions ********************************************************************/ -/* - * Report a missing mandatory parameter. - */ struct __sctp_missing { __u32 num_missing; __u16 type; } __attribute__((packed));; + +/* + * Report a missing mandatory parameter. + */ static int sctp_process_missing_param(const sctp_association_t *asoc, sctp_param_t paramtype, sctp_chunk_t *chunk, @@ -1774,8 +1789,7 @@ int sctp_verify_init(const sctp_association_t *asoc, */ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, const union sctp_addr *peer_addr, - sctp_init_chunk_t *peer_init, - int priority) + sctp_init_chunk_t *peer_init, int gfp) { union sctp_params param; struct sctp_transport *transport; @@ -1793,14 +1807,14 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, * be a a better choice than any of the embedded addresses. */ if (peer_addr) - if(!sctp_assoc_add_peer(asoc, peer_addr, priority)) + if(!sctp_assoc_add_peer(asoc, peer_addr, gfp)) goto nomem; /* Process the initialization parameters. */ sctp_walk_params(param, peer_init, init_hdr.params) { - if (!sctp_process_param(asoc, param, peer_addr, priority)) + if (!sctp_process_param(asoc, param, peer_addr, gfp)) goto clean_up; } @@ -1842,7 +1856,7 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, /* Copy cookie in case we need to resend COOKIE-ECHO. */ cookie = asoc->peer.cookie; if (cookie) { - asoc->peer.cookie = kmalloc(asoc->peer.cookie_len, priority); + asoc->peer.cookie = kmalloc(asoc->peer.cookie_len, gfp); if (!asoc->peer.cookie) goto clean_up; memcpy(asoc->peer.cookie, cookie, asoc->peer.cookie_len); @@ -1871,8 +1885,7 @@ int sctp_process_init(sctp_association_t *asoc, sctp_cid_t cid, /* Allocate storage for the negotiated streams. */ asoc->ssnmap = sctp_ssnmap_new(asoc->peer.i.num_outbound_streams, - asoc->c.sinit_num_ostreams, - priority); + asoc->c.sinit_num_ostreams, gfp); if (!asoc->ssnmap) goto nomem_ssnmap; @@ -1914,7 +1927,7 @@ nomem: * structures for the addresses. */ int sctp_process_param(sctp_association_t *asoc, union sctp_params param, - const union sctp_addr *peer_addr, int priority) + const union sctp_addr *peer_addr, int gfp) { union sctp_addr addr; int i; @@ -1933,10 +1946,10 @@ int sctp_process_param(sctp_association_t *asoc, union sctp_params param, break; /* Fall through. */ case SCTP_PARAM_IPV4_ADDRESS: - sctp_param2sockaddr(&addr, param.addr, asoc->peer.port); + sctp_param2sockaddr(&addr, param.addr, asoc->peer.port, 0); scope = sctp_scope(peer_addr); if (sctp_in_scope(&addr, scope)) - if (!sctp_assoc_add_peer(asoc, &addr, priority)) + if (!sctp_assoc_add_peer(asoc, &addr, gfp)) return 0; break; @@ -2051,7 +2064,7 @@ __u32 sctp_generate_tsn(const sctp_endpoint_t *ep) /* Convert from an SCTP IP parameter to a union sctp_addr. */ void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *param, - __u16 port) + __u16 port, int iif) { switch(param->v4.param_hdr.type) { case SCTP_PARAM_IPV4_ADDRESS: @@ -2065,7 +2078,7 @@ void sctp_param2sockaddr(union sctp_addr *addr, sctp_addr_param_t *param, addr->v6.sin6_port = port; addr->v6.sin6_flowinfo = 0; /* BUG */ addr->v6.sin6_addr = param->v6.addr; - addr->v6.sin6_scope_id = 0; /* BUG */ + addr->v6.sin6_scope_id = iif; break; default: diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 2a794ada7247..4024fec3dc9f 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -461,10 +461,9 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, * their work in statefuns directly. */ static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, - sctp_association_t *asoc, - sctp_chunk_t *chunk, - sctp_init_chunk_t *peer_init, - int priority) + struct sctp_association *asoc, + struct sctp_chunk *chunk, + sctp_init_chunk_t *peer_init, int gfp) { int error; @@ -473,10 +472,8 @@ static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, * fail during INIT processing (due to malloc problems), * just return the error and stop processing the stack. */ - if (!sctp_process_init(asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, - priority)) + sctp_source(chunk), peer_init, gfp)) error = -ENOMEM; else error = 0; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index cec75eb93946..49ec19e1061e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -249,8 +249,8 @@ sctp_disposition_t sctp_sf_do_5_1B_init(const sctp_endpoint_t *ep, /* The call, sctp_process_init(), can fail on memory allocation. */ if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), - (sctp_init_chunk_t *)chunk->chunk_hdr, + sctp_source(chunk), + (sctp_init_chunk_t *)chunk->chunk_hdr, GFP_ATOMIC)) goto nomem_init; @@ -1380,7 +1380,8 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(const sctp_endpoint_t *ep, peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, GFP_ATOMIC)) + sctp_source(chunk), peer_init, + GFP_ATOMIC)) goto nomem; /* Make sure no new addresses are being added during the @@ -1445,7 +1446,8 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(const sctp_endpoint_t *ep, */ peer_init = &chunk->subh.cookie_hdr->c.peer_init[0]; if (!sctp_process_init(new_asoc, chunk->chunk_hdr->type, - sctp_source(chunk), peer_init, GFP_ATOMIC)) + sctp_source(chunk), peer_init, + GFP_ATOMIC)) goto nomem; /* Update the content of current association. */ diff --git a/net/sctp/socket.c b/net/sctp/socket.c index bb91784b0c68..0a79cebcd7d0 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -244,9 +244,6 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) if (!snum) snum = inet_sk(sk)->num; - - - /* Add the address to the bind address list. */ sctp_local_bh_disable(); sctp_write_lock(&ep->base.addr_lock); @@ -257,7 +254,6 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len) 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(); @@ -3152,7 +3148,10 @@ static inline int sctp_verify_addr(struct sock *sk, union sctp_addr *addr, return -EINVAL; /* Is this a valid SCTP address? */ - if (!af->addr_valid((union sctp_addr *)addr)) + if (!af->addr_valid(addr)) + return -EINVAL; + + if (!sctp_sk(sk)->pf->send_verify(sctp_sk(sk), (addr))) return -EINVAL; return 0; diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 295fb6029d12..4e4947ef3c65 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -628,6 +628,7 @@ struct sctp_ulpevent *sctp_ulpevent_make_rcvmsg(sctp_association_t *asoc, if (!event) goto fail_init; + event->iif = sctp_chunk_iif(chunk); /* Note: Not clearing the entire event struct as * this is just a fragment of the real event. However, * we still need to do rwnd accounting. -- cgit v1.2.3 From 3436449540fbfad5b8887a9dd900a1f37a1eec96 Mon Sep 17 00:00:00 2001 From: Jon Grimm Date: Tue, 1 Apr 2003 21:23:53 -0600 Subject: [SCTP] Fix warning and unused (sfr@canb.auug.org.au) Patch submitted by Stephen Rothwell to change 'flags' to unsigned long and remove 'unused' attribute. --- include/net/sctp/sctp.h | 2 +- net/sctp/protocol.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 5c537d9e7499..f964badf7062 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -356,7 +356,7 @@ for (pos = (head)->next;\ static inline void sctp_skb_list_tail(struct sk_buff_head *list, struct sk_buff_head *head) { - int flags __attribute__ ((unused)); + unsigned long flags; sctp_spin_lock_irqsave(&head->lock, flags); sctp_spin_lock(&list->lock); diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index c4e03b9f74a9..5773728dd1fe 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -170,7 +170,7 @@ static void __sctp_get_local_addr_list(struct sctp_protocol *proto) static void sctp_get_local_addr_list(struct sctp_protocol *proto) { - long flags __attribute__ ((unused)); + unsigned long flags; sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags); __sctp_get_local_addr_list(&sctp_proto); @@ -193,7 +193,7 @@ static void __sctp_free_local_addr_list(struct sctp_protocol *proto) /* Free the existing local addresses. */ static void sctp_free_local_addr_list(struct sctp_protocol *proto) { - long flags __attribute__ ((unused)); + unsigned long flags; sctp_spin_lock_irqsave(&proto->local_addr_lock, flags); __sctp_free_local_addr_list(proto); @@ -208,7 +208,7 @@ int sctp_copy_local_addr_list(struct sctp_protocol *proto, struct sockaddr_storage_list *addr; int error = 0; struct list_head *pos; - long flags __attribute__ ((unused)); + unsigned long flags; sctp_spin_lock_irqsave(&proto->local_addr_lock, flags); list_for_each(pos, &proto->local_addr_list) { @@ -543,10 +543,10 @@ out: /* Event handler for inet address addition/deletion events. * Basically, whenever there is an event, we re-build our local address list. */ -static int sctp_inetaddr_event(struct notifier_block *this, unsigned long event, +static int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, void *ptr) { - long flags __attribute__ ((unused)); + unsigned long flags; sctp_spin_lock_irqsave(&sctp_proto.local_addr_lock, flags); __sctp_free_local_addr_list(&sctp_proto); -- cgit v1.2.3