OpenSS7 SS7 for the Common Man |
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |
||||||||||||||||||||||||||
Home | Overview | Status | News | Documentation | Resources | About | |||||||||||||||||||||
File /code/strss7/drivers/sctp/sctp_msg.c#ident "@(#) $RCSfile: sctp_msg.c,v $ $Name: $($Revision: 0.8.2.14 $) $Date: 2003/05/30 21:15:52 $" static char const ident[] = "$RCSfile: sctp_msg.c,v $ $Name: $($Revision: 0.8.2.14 $) $Date: 2003/05/30 21:15:52 $"; #define __NO_VERSION__ #include <linux/config.h> #include <linux/version.h> #ifdef MODVERSIONS #include <linux/modversions.h> #endif #include <linux/module.h> #include <sys/stream.h> #include <sys/stropts.h> #include <sys/cmn_err.h> #include <sys/dki.h> #include "../debug.h" #include "../bufq.h" #include "sctp.h" #include "sctp_defs.h" #include "sctp_hash.h" #include "sctp_cache.h" #include "sctp_route.h" #include "sctp_cookie.h" #include "sctp_output.h" #undef min /* LiS should not have defined these */ #undef max /* LiS should not have defined these */ #define sctp_daddr sctp_daddr__ #define sctp_saddr sctp_saddr__ #define sctp_strm sctp_strm__ #define sctp_dup sctp_dup__ #ifdef ASSERT #undef ASSERT #endif #include <net/ip.h> #include <net/icmp.h> #include <net/route.h> #undef sctp_daddr #undef sctp_saddr #undef sctp_strm #undef sctp_dup #ifdef ASSERT #undef ASSERT #endif #include "sctp_msg.h" #include <linux/random.h> #undef min #define min lis_min #undef max #define max lis_min STATIC uint32_t sctp_get_vtag(uint32_t daddr, uint32_t saddr, uint16_t dport, uint16_t sport) { uint32_t ret; ret = secure_tcp_sequence_number(daddr, saddr, dport, sport); usual(ret); return (ret); } static inline void set_timeout(sctp_t * sp, int *tidp, timo_fcn_t * fnc, void *data, long ticks) { int flags = 0; assert(tidp); assert(data); lis_spin_lock_irqsave(&sp->lock, &flags); { if (*tidp) { abnormal(*tidp); untimeout(xchg(tidp, 0)); } *tidp = timeout(fnc, data, ticks ? ticks : 1); } lis_spin_unlock_irqrestore(&sp->lock, &flags); } static inline void mod_timeout(sctp_t * sp, int *tidp, timo_fcn_t * fnc, void *data, long ticks) { int flags = 0; assert(tidp); assert(data); lis_spin_lock_irqsave(&sp->lock, &flags); { if (*tidp) { untimeout(xchg(tidp, 0)); } *tidp = timeout(fnc, data, ticks ? ticks : 1); } lis_spin_unlock_irqrestore(&sp->lock, &flags); } /* * ========================================================================== * * SCTP --> SCTP Peer Messages (Send Messages) * * ========================================================================== */ /* * BUNDLING FUNCTIONS * ------------------------------------------------------------------------- * * BUNDLE SACK * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC void sctp_bundle_sack(sp, sd, dmps, amps, dpp, mrem, mlen) sctp_t *sp; /* association */ sctp_daddr_t *sd; /* destination */ size_t dmps; /* destination maximum payload size */ size_t amps; /* association maximum payload size */ mblk_t ***dpp; /* place to link buffer */ size_t *mrem; /* remaining payload */ size_t *mlen; /* current message length */ { mblk_t *mp; sctp_tcb_t *cb; struct sctp_sack *m; size_t ngaps, ndups, glen, dlen, clen, plen; ngaps = sp->ngaps; ndups = sp->ndups; glen = ngaps * sizeof(uint32_t); dlen = ndups * sizeof(uint32_t); clen = sizeof(*m) + glen + dlen; plen = PADC(clen); if (clen > dmps) { size_t too_many_dups; rare(); /* trim down sack */ too_many_dups = (clen - *mrem + 3) / sizeof(uint32_t); ndups = ndups > too_many_dups ? ndups - too_many_dups : 0; clen = sizeof(*m) + glen + ndups * sizeof(uint32_t); if (*mrem < clen) { size_t too_many_gaps; rare(); /* trim some more */ too_many_gaps = (clen - *mrem + 3) / sizeof(uint32_t); ngaps = ngaps > too_many_gaps ? ngaps - too_many_gaps : 0; clen = sizeof(*m) + ngaps * sizeof(uint32_t); } } if (plen > *mrem && plen <= dmps) { rare(); return; /* wait for next packet */ } if ((mp = allocb(sizeof(*cb) + plen, BPRI_HI))) { sctp_tcb_t *gap = sp->gaps; sctp_tcb_t *dup = sp->dups; size_t arwnd = sp->a_rwnd; size_t count = bufq_size(&sp->oooq) + bufq_size(&sp->dupq) + bufq_size(&sp->rcvq); arwnd = count < arwnd ? arwnd - count : 0; /* __ptrace(("oooq = %u:%u, dupq = %u:%u, rcvq = %u:%u\n", bufq_size(&sp->oooq),bufq_length(&sp->oooq), bufq_size(&sp->dupq),bufq_length(&sp->dupq), bufq_size(&sp->rcvq),bufq_length(&sp->rcvq))); */ // if ( !canputnext(sp->rq) ) arwnd = 0; mp->b_datap->db_type = M_DATA; cb = (sctp_tcb_t *) mp->b_wptr; bzero(cb, sizeof(*cb)); cb->mp = mp; cb->dlen = clen; /* XXX */ mp->b_rptr += sizeof(*cb); /* hide control block */ mp->b_wptr = mp->b_rptr; bzero(mp->b_wptr + clen, plen - clen); m = (struct sctp_sack *) mp->b_wptr; m->ch.type = SCTP_CTYPE_SACK; m->ch.flags = 0; m->ch.len = htons(clen); m->c_tsn = htonl(sp->r_ack); m->a_rwnd = htonl(arwnd); m->ngaps = htons(ngaps); m->ndups = htons(ndups); mp->b_wptr += sizeof(*m); for (; gap && ngaps; gap = gap->next, ngaps--) { *((uint16_t *) mp->b_wptr)++ = htons(gap->tsn - sp->r_ack); gap = gap->tail; *((uint16_t *) mp->b_wptr)++ = htons(gap->tsn - sp->r_ack); } for (; dup && ndups; dup = dup->next, ndups--) *((uint32_t *) mp->b_wptr)++ = htonl(dup->tsn); bufq_purge(&sp->dupq); sp->ndups = 0; sp->dups = NULL; sp->sackf = 0; if (sp->timer_sack) untimeout(xchg(&sp->timer_sack, 0)); *mrem = *mrem > plen ? *mrem - plen : 0; *mlen += plen; **dpp = mp; *dpp = &(mp->b_next); mp->b_next = NULL; return; } rare(); } /* * BUNDLE ERROR * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC void sctp_bundle_error(sp, sd, dmps, amps, dpp, mrem, mlen) sctp_t *sp; /* association */ sctp_daddr_t *sd; /* destination */ size_t dmps; /* destination maximum payload size */ size_t amps; /* association maximum payload size */ mblk_t ***dpp; /* place to link buffer */ size_t *mrem; /* remaining payload */ size_t *mlen; /* current message length */ { mblk_t *mp; while (*mrem && (mp = bufq_head(&sp->errq))) { size_t clen = mp->b_wptr - mp->b_rptr; size_t plen = PADC(clen); if (plen > *mrem && plen <= dmps) { rare(); return; /* wait for next packet */ } *mrem = *mrem > plen ? *mrem - plen : 0; *mlen += plen; mp = bufq_dequeue(&sp->errq); **dpp = mp; *dpp = &(mp->b_next); mp->b_next = NULL; continue; } } /* * BUNDLE DATA * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ static inline int trimhead(mblk_t *mp, int len) { for (; len && mp; mp = mp->b_cont) { if (mp->b_datap->db_type == M_DATA) { int size = mp->b_wptr - mp->b_rptr; if (size > len) { mp->b_rptr += len; len = 0; } else if (size >= 0) { mp->b_rptr = mp->b_wptr; len -= size; } } } assure(!len); return (len); } static inline int trimtail(mblk_t *mp, int len) { for (; len && mp; mp = mp->b_cont) { if (mp->b_datap->db_type == M_DATA) { int size = mp->b_wptr - mp->b_rptr; if (size > len) { mp->b_wptr = mp->b_rptr + len; len = 0; } else if (size >= 0) { mp->b_wptr = mp->b_rptr; len -= size; } } } for (; mp; mp = mp->b_cont) if (mp->b_datap->db_type == M_DATA) if (mp->b_wptr > mp->b_rptr) mp->b_wptr = mp->b_rptr; assure(!len); return (len); } /* * FRAGMENT DATA CHUNKS * * Try to fragment a DATA chunk which has not been transmitted yet into * two chunks, the first small enough to fit into the pmtu and the second one * containing the remainder of the data in a chunk. This is called * iteratively, so the reminaing data may also be further fragmented * according to the pmtu experienced at the time that it is further * fragmented. */ #if defined(_DEBUG)||defined(_SAFE) STATIC void sctp_frag_chunk(bq, mp, mps) bufq_t *bq; mblk_t *mp; size_t mps; { mblk_t *dp; assert(bq); assert(mp); rare(); /* * This should be extremely rare, now that we are fragmenting in * sctp_send_data. This fragmentation only occurs if the path MTU * has dropped since we buffered data for transmission. It is * probably not necessary any more. */ { mblk_t *bp; /* copy the transmission control block and data header */ if (!(dp = copyb(mp))) { rare(); return; } /* copyb does not copy the hiddle control block */ bcopy(mp->b_datap->db_base, dp->b_datap->db_base, mp->b_datap->db_lim - mp->b_datap->db_base); /* duplicate the message blocks which form the data */ if (!(bp = dupmsg(mp->b_cont))) { rare(); freeb(dp); return; } dp->b_cont = bp; } { sctp_tcb_t *cb1 = SCTP_TCB(mp); sctp_tcb_t *cb2 = SCTP_TCB(dp); struct sctp_data *m1 = (struct sctp_data *) mp->b_rptr; struct sctp_data *m2 = (struct sctp_data *) dp->b_rptr; size_t dlen = (mps - sizeof(struct sctp_data)); cb1->dlen = dlen; cb1->flags &= ~SCTPCB_FLAG_LAST_FRAG; m1->ch.len = htons(cb1->dlen + sizeof(*m1)); m1->ch.flags = cb1->flags & 0x7; cb2->dlen -= dlen; cb2->flags &= ~SCTPCB_FLAG_FIRST_FRAG; m2->ch.len = htons(cb2->dlen + sizeof(*m2)); m2->ch.flags = cb2->flags & 0x7; #if 1 { int ret; ret = trimhead(mp, dlen); /* trim originai */ ensure(ret, freemsg(dp); return; ); ret = trimtail(dp, dlen); /* trim fragment */ ensure(ret, freemsg(dp); return; ); } #else fixme(("Should consider multiple mblks\n")); mp->b_cont->b_wptr = mp->b_cont->b_rptr + dlen; /* trim original */ dp->b_cont->b_rptr = dp->b_cont->b_rptr + dlen; /* trim fragment */ #endif } /* insert the fresh copy after the existing copy in the bufq */ __ctrace(bufq_append(bq, mp, dp)); } #endif /* * CONGESTION/RECEIVE WINDOW AVAILABILITY * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * * Calculate of the remaining space in the current packet, how much is * available for use by data according to the current peer receive window, * the current destination congestion window, and the current outstanding * data bytes in flight. * * This is called iteratively as each data chunk is tested for bundling into * the current message. The usable length returned does not include the data * chunk header. */ STATIC size_t sctp_avail(sp, sd, dmps, mrem) sctp_t *sp; sctp_daddr_t *sd; size_t dmps; size_t mrem; { size_t ulen; ulen = mrem > sizeof(struct sctp_data) ? mrem - sizeof(struct sctp_data) : 0; if (ulen) { size_t cwnd, rwnd, swnd, awnd; cwnd = sd->cwnd + dmps; cwnd = cwnd > sd->in_flight ? cwnd - sd->in_flight : 0; rwnd = sp->p_rwnd; rwnd = rwnd > sp->in_flight ? rwnd - sp->in_flight : 0; swnd = cwnd < rwnd ? cwnd : rwnd; awnd = sp->in_flight ? swnd : cwnd; ulen = awnd < ulen ? awnd : ulen; } return (ulen); } /* * BUNDLE DATA for RETRANSMISSION */ STATIC void sctp_bundle_data_retrans(sp, sd, dmps, amps, dpp, mrem, mlen) sctp_t *sp; /* association */ sctp_daddr_t *sd; /* destination */ size_t dmps; /* destination maximum payload size */ size_t amps; /* association maximum payload size */ mblk_t ***dpp; /* place to link buffer */ size_t *mrem; /* remaining payload */ size_t *mlen; /* current message length */ { mblk_t *mp = bufq_head(&sp->rtxq); size_t swnd = sctp_avail(sp, sd, dmps, *mrem); for (; mp && *mrem && swnd; mp = mp->b_next) { if ((SCTP_TCB(mp)->flags & SCTPCB_FLAG_RETRANS)) { mblk_t *db; sctp_tcb_t *cb = SCTP_TCB(mp); size_t dlen = cb->dlen; size_t plen = PADC(sizeof(struct sctp_data) + dlen); if (dlen > swnd) { rare(); return; /* congested */ } if (plen > *mrem && plen <= dmps) { rare(); return; /* wait for next packet */ } if ((db = dupmsg(mp))) { cb->flags &= ~SCTPCB_FLAG_RETRANS; sp->nrtxs--; // cb->trans += 1; done by sctp_send_msg */ cb->sacks = 0; cb->when = jiffies; cb->daddr = sd; sd->in_flight += dlen; sp->in_flight += dlen; *mrem = *mrem > plen ? *mrem - plen : 0; *mlen += plen; **dpp = db; *dpp = &(db->b_next); db->b_next = NULL; swnd = sctp_avail(sp, sd, dmps, *mrem); continue; } rare(); return; } } return; } /* * BUNDLE NEW EXPEDITED (OUT OF ORDER) DATA */ STATIC void sctp_bundle_data_urgent(sp, sd, dmps, amps, dpp, mrem, mlen) sctp_t *sp; /* association */ sctp_daddr_t *sd; /* destination */ size_t dmps; /* destination maximum payload size */ size_t amps; /* association maximum payload size */ mblk_t ***dpp; /* place to link buffer */ size_t *mrem; /* remaining payload */ size_t *mlen; /* current message length */ { mblk_t *mp; size_t swnd; while (*mrem && (swnd = sctp_avail(sp, sd, dmps, *mrem)) && (mp = bufq_head(&sp->urgq))) { mblk_t *db; sctp_tcb_t *cb = SCTP_TCB(mp); size_t dlen = cb->dlen; size_t plen = PADC(sizeof(struct sctp_data) + dlen); ensure(cb->st, continue); #if defined(_DEBUG)||defined(_SAFE) /* this should only occur if the pmtu is falling */ if (amps <= *mrem && plen > amps) { rare(); sctp_frag_chunk(&sp->urgq, mp, amps); dlen = cb->dlen; plen = PADC(sizeof(struct sctp_data) + dlen); } #endif if (dlen > swnd) { rare(); return; /* congested */ } if (plen > *mrem && plen <= dmps) { rare(); return; /* wait for next packet */ } if ((mp == cb->st->x.head)) { rare(); cb->st->x.head = NULL; /* steal partial */ } if ((db = dupmsg(mp))) { uint32_t tsn = sp->t_tsn++; struct sctp_data *m = (struct sctp_data *) db->b_rptr; cb->tsn = tsn; cb->flags |= SCTPCB_FLAG_SENT; cb->trans = 0; cb->sacks = 0; cb->when = jiffies; cb->daddr = sd; m->tsn = htonl(tsn); sd->in_flight += dlen; sp->in_flight += dlen; *mrem = *mrem > plen ? *mrem - plen : 0; *mlen += plen; **dpp = db; *dpp = &(db->b_next); db->b_next = NULL; bufq_queue(&sp->rtxq, bufq_dequeue(&sp->urgq)); continue; } rare(); return; } return; } /* * BUNDLE NEW NORMAL (ORDERED) DATA */ STATIC void sctp_bundle_data_normal(sp, sd, dmps, amps, dpp, mrem, mlen) sctp_t *sp; /* association */ sctp_daddr_t *sd; /* destination */ size_t dmps; /* destination maximum payload size */ size_t amps; /* association maximum payload size */ mblk_t ***dpp; /* place to link buffer */ size_t *mrem; /* remaining payload */ size_t *mlen; /* current message length */ { mblk_t *mp; size_t swnd; /* don't bundle normal data without more to send (like a SACK) */ if (sp->options & SCTP_OPTION_CORK || (sp->options & SCTP_OPTION_NAGLE && sp->in_flight && *mlen == sizeof(struct sctphdr) && bufq_size(&sp->sndq) < *mrem >> 1)) return; while (*mrem && (swnd = sctp_avail(sp, sd, dmps, *mrem)) && (mp = bufq_head(&sp->sndq))) { mblk_t *db; sctp_tcb_t *cb = SCTP_TCB(mp); size_t dlen = cb->dlen; size_t plen = PADC(sizeof(struct sctp_data) + dlen); ensure(cb->st, continue); #if defined(_DEBUG)||defined(_SAFE) /* this should only occur if the pmtu is falling */ if (amps <= *mrem && plen > amps) { rare(); sctp_frag_chunk(&sp->sndq, mp, amps); dlen = cb->dlen; plen = PADC(sizeof(struct sctp_data) + dlen); } #endif if (dlen > swnd) { seldom(); return; /* congested */ } if (plen > *mrem && plen <= dmps) { seldom(); return; /* wait for next packet */ } if ((mp == cb->st->n.head)) { seldom(); cb->st->n.head = NULL; /* steal partial */ } if ((db = dupmsg(mp))) { uint32_t tsn = sp->t_tsn++; cb->tsn = tsn; cb->flags |= SCTPCB_FLAG_SENT; // cb->trans = 1; done by sctp_send_msg */ cb->sacks = 0; cb->when = jiffies; cb->daddr = sd; ((struct sctp_data *) db->b_rptr)->tsn = htonl(tsn); sd->in_flight += dlen; sp->in_flight += dlen; *mrem = *mrem > plen ? *mrem - plen : 0; *mlen += plen; **dpp = db; *dpp = &(db->b_next); db->b_next = NULL; bufq_queue(&sp->rtxq, bufq_dequeue(&sp->sndq)); continue; } rare(); return; } return; } /* * BUNDLE CHUNKS * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Tack on SACK, ERROR, and DATA chunks up to the destination MTU considering * congestion windows and fragmentation sizes. */ STATIC void sctp_retrans_timeout(caddr_t data); STATIC void sctp_bundle_more(sp, sd, mp) sctp_t *sp; sctp_daddr_t *sd; mblk_t *mp; { mblk_t **dpp; size_t mrem, dmps, amps, mlen; size_t htax = sizeof(struct iphdr) + sizeof(struct sctphdr); size_t in_flight; ensure(mp, return); assert(sp); assert(sd); in_flight = sd->in_flight; ensure(sd->mtu > htax, return); /* FIXME: this is worse... */ ensure(sp->pmtu > htax, return); /* FIXME: this is worse... */ dmps = sd->mtu - htax; /* destintaion max payload size */ amps = sp->pmtu - htax; /* association max payload size */ mlen = msgdsize(mp); mrem = sd->mtu - sizeof(struct iphdr); mrem = (mlen < mrem) ? mrem - mlen : 0; dpp = &(mp->b_next); mp->b_next = NULL; if (mrem && (sp->sackf & SCTP_SACKF_NOW)) sctp_bundle_sack(sp, sd, dmps, amps, &dpp, &mrem, &mlen); if (mrem && bufq_head(&sp->errq)) sctp_bundle_error(sp, sd, dmps, amps, &dpp, &mrem, &mlen); if (mrem && bufq_head(&sp->urgq)) sctp_bundle_data_urgent(sp, sd, dmps, amps, &dpp, &mrem, &mlen); if (mrem && sp->nrtxs) sctp_bundle_data_retrans(sp, sd, dmps, amps, &dpp, &mrem, &mlen); if (mrem && bufq_head(&sp->sndq)) sctp_bundle_data_normal(sp, sd, dmps, amps, &dpp, &mrem, &mlen); SCTP_TCB(mp)->dlen = mlen; if (sd->in_flight && !sd->timer_retrans) { set_timeout(sp, &sd->timer_retrans, &sctp_retrans_timeout, sd, sd->rto); } return; } /* * ALLOC MSG * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Allocates a new message block with a hidden transmission control block, an * SCTP message header, and the first chunk of a message. */ STATIC mblk_t *sctp_alloc_msg(sp, clen) sctp_t *sp; size_t clen; { mblk_t *mp; struct sctphdr *sh; sctp_tcb_t *cb; size_t plen = PADC(clen); assert(sp); if ((mp = allocb(sizeof(*cb) + sizeof(*sh) + plen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; cb = (sctp_tcb_t *) mp->b_wptr; bzero(cb, sizeof(*cb)); cb->mp = mp; cb->dlen = sizeof(*sh) + plen; mp->b_rptr += sizeof(*cb); /* hide control block */ mp->b_wptr += sizeof(*cb); sh = (struct sctphdr *) mp->b_wptr; sh->srce = sp->sport; sh->dest = sp->dport; sh->v_tag = sp->p_tag; sh->check = 0; mp->b_wptr += sizeof(*sh); bzero(mp->b_wptr + clen, plen - clen); mp->b_next = NULL; } return (mp); } /* * ALLOC REPLY * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * This is for allocating a message block with a hidden transmission control * block for a message sith an STCP header. This is for out of the blue * replies and all that is required is the SCTP header of the message to * which this is a reply. */ STATIC mblk_t *sctp_alloc_reply(rh, clen) struct sctphdr *rh; size_t clen; { mblk_t *mp; struct sctphdr *sh; sctp_tcb_t *cb; size_t plen = PADC(clen); assert(rh); if ((mp = allocb(sizeof(*cb) + sizeof(*sh) + plen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; cb = (sctp_tcb_t *) mp->b_wptr; bzero(cb, sizeof(*cb)); cb->mp = mp; cb->dlen = sizeof(*sh) + plen; mp->b_rptr += sizeof(*cb); /* hide control block */ mp->b_wptr += sizeof(*cb); sh = (struct sctphdr *) mp->b_wptr; sh->srce = rh->dest; sh->dest = rh->srce; sh->v_tag = rh->v_tag; sh->check = 0; mp->b_wptr += sizeof(*sh); bzero(mp->b_wptr + clen, plen - clen); mp->b_next = NULL; } return (mp); } /* * ROUTE SELECTION * ------------------------------------------------------------------------- * * ROUTE NORMAL * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Send to the normal transmit (primary) address. If that has timedout, we * use the retransmit (secondary) address. We always use the secondary * address if we have retransmit chunks to bundle or if we have be receiving * duplicates (our SACKs are not getting through on the primary address). * */ STATIC sctp_daddr_t *sctp_route_normal(sp) sctp_t *sp; { sctp_daddr_t *sd; assert(sp); if (sctp_update_routes(sp, 1)) { rare(); /* we have no viable route */ if ((1 << sp->s_state) & (SCTPF_HAVEUSER)) { sp->ops->sctp_discon_ind(sp, SCTP_ORIG_PROVIDER, -EHOSTUNREACH, NULL); } else rare(); __sctp_disconnect(sp); return (NULL); } sd = sp->taddr; normal(sd); #if 0 if ((!sd || ((sd->retransmits || sp->nrtxs) && sd->max_retrans)) && sp->raddr) sd = sp->raddr; // if ( !sd || ((sd->retransmits || sp->nrtxs) && sd->max_retrans) || (sp->sackf & SCTP_SACKF_DUP) ) if (!sd || (sp->nrtxs && sd->max_retrans) || (sp->sackf & SCTP_SACKF_DUP)) sd = sp->raddr; /* might be same address */ #endif return (sd); } /* * ROUTE RESPONSE * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * In response to control chunks we normally send back to the address that * the control chunk came from. If that address is unusable or wasn't * provided we send as normal. */ STATIC sctp_daddr_t *sctp_route_response(sp) sctp_t *sp; { sctp_daddr_t *sd; assert(sp); sd = sp->caddr; if (!sd || !sd->dst_cache || sd->retransmits) sd = sctp_route_normal(sp); normal(sd); return (sd); } /* * WAKEUP (Send SACK, ERROR, DATA) * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ void sctp_transmit_wakeup(sp) sctp_t *sp; { int i; mblk_t *mp; sctp_daddr_t *sd; int loop_max = 3000; assert(sp); if ((1 << sp->s_state) & SCTPF_SENDING) { for (i = 0; i < loop_max; i++) { /* placed in order of probability */ if (bufq_head(&sp->sndq) || (sp->sackf & SCTP_SACKF_NOW) || sp->nrtxs || bufq_head(&sp->urgq) || bufq_head(&sp->errq) ) { if (!(sd = sctp_route_normal(sp))) { rare(); break; } if (!(mp = sctp_alloc_msg(sp, 0))) { rare(); break; } sctp_bundle_more(sp, sd, mp); if (mp->b_next) { sctp_send_msg(sp, sd, mp); freechunks(mp); continue; } unusual(bufq_head(&sp->sndq)); unusual(bufq_head(&sp->urgq)); unusual(bufq_head(&sp->errq)); unusual(sp->sackf & SCTP_SACKF_NOD); unusual(sp->nrtxs); if (sp->nrtxs) ptrace(("sp->nrtxs = %u\n", sp->nrtxs)); rare(); freechunks(mp); #if _DEBUG for (mp = bufq_head(&sp->sndq); mp; mp = mp->b_next) { sctp_tcb_t *cb = SCTP_TCB(mp); printk("sndq: mp = %08x, dlen = %u\n", (uint) mp, cb->dlen); } for (mp = bufq_head(&sp->urgq); mp; mp = mp->b_next) { sctp_tcb_t *cb = SCTP_TCB(mp); printk("urgq: mp = %08x, dlen = %u\n", (uint) mp, cb->dlen); } if (sp->nrtxs) for (mp = bufq_head(&sp->rtxq); mp; mp = mp->b_next) { sctp_tcb_t *cb = SCTP_TCB(mp); printk("rtxq: mp = %08x, dlen = %u, tsn = %u\n", (uint) mp, cb->dlen, cb->tsn); } #endif } break; } assure(i < loop_max); } return; } /* * ========================================================================= * * SCTP State Machine TIMEOUTS * * ========================================================================= */ STATIC void sctp_send_heartbeat(sctp_t * sp, sctp_daddr_t * sd); STATIC void sctp_reset_idle(sctp_daddr_t * sd); /* * ASSOCIATION TIMEOUT FUNCTION * ------------------------------------------------------------------------- */ STATIC int sctp_assoc_timedout(sp, sd, rmax) sctp_t *sp; sctp_daddr_t *sd; size_t rmax; { assert(sp); assert(sd); /* RFC 2960 6.3.3 E1 and 7.2.3, E2, E3 and 8.3 */ sd->ssthresh = sd->cwnd >> 1 > sd->mtu << 1 ? sd->cwnd >> 1 : sd->mtu << 1; sd->cwnd = sd->mtu; sd->rto = sd->rto ? sd->rto << 1 : 1; sd->rto = sd->rto_min > sd->rto ? sd->rto_min : sd->rto; sd->rto = sd->rto_max < sd->rto ? sd->rto_max : sd->rto; /* See RFC 2960 Section 8.3 */ if (sd->retransmits++ >= sd->max_retrans) { if (sd->dst_cache) dst_negative_advice(&sd->dst_cache); if (sd->retransmits == sd->max_retrans + 1) { /* * IMPLEMENTATION NOTE:- When max_retrans and * rto_max are set to zero, we are cruel on * destinations that drop a single packet due to * noise. This forces an immediate heartbeat on the * destination so that it can be made available again * quickly it if passes the heartbeat. */ if ((1 << sp->s_state) & (SCTPF_CONNECTED | SCTPF_CLOSING)) { if (sd->timer_idle) untimeout(xchg(&sd->timer_idle, 0)); sctp_send_heartbeat(sp, sd); } } else { if ((1 << sp->s_state) & (SCTPF_CONNECTED)) return (0); } } /* See RFC 2960 Section 8.2 */ if (rmax && sp->retransmits++ >= rmax) { seldom(); if ((1 << sp->s_state) & (SCTPF_HAVEUSER)) { sp->ops->sctp_discon_ind(sp, SCTP_ORIG_PROVIDER, -ETIMEDOUT, NULL); } else rare(); __sctp_disconnect(sp); return (-ETIMEDOUT); } return (0); } #define SCTP_TIMER_BACKOFF 1 /* * INIT TIMEOUT (T1-init) * ------------------------------------------------------------------------- * The init timer has expired indicating that we have not received an INIT * ACK within timer T1-init. This means that we should attempt to retransmit * the INIT until we have attempted Max.Init.Retrans times. */ STATIC void sctp_init_timeout(data) caddr_t data; { sctp_t *sp; sctp_daddr_t *sd; sd = (sctp_daddr_t *) data; assert(sd); sp = sd->sp; assert(sp); sctp_bh_lock(sp); do { if (!sp->timer_init) { rare(); break; } if (sctp_locked(sp)) { seldom(); sp->timer_init = timeout(sctp_init_timeout, (caddr_t) sd, SCTP_TIMER_BACKOFF); break; } sp->timer_init = 0; if (sp->s_state != SCTP_COOKIE_WAIT) { rare(); break; } if (sctp_assoc_timedout(sp, sd, sp->max_inits)) { seldom(); break; } sd = sp->taddr; /* might have new primary */ ensure(sd, break); set_timeout(sp, &sp->timer_init, sctp_init_timeout, (caddr_t) sd, sd->rto); usual(sp->retry); sctp_send_msg(sp, sd, sp->retry); } while (0); sctp_bh_unlock(sp); } /* * COOKIE TIMEOUT * ------------------------------------------------------------------------- * The cookie timer has expired indicating that we have not yet received a * COOKIE ACK within timer T1-cookie. This means that we should attempt to * retransmit the COOKIE ECHO until we have attempted Path.Max.Retrans times. */ STATIC void sctp_cookie_timeout(data) caddr_t data; { mblk_t *mp; sctp_t *sp; sctp_daddr_t *sd; sd = (sctp_daddr_t *) data; assert(sd); sp = sd->sp; assert(sp); sctp_bh_lock(sp); do { if (!sp->timer_cookie) { rare(); break; } if (sctp_locked(sp)) { seldom(); sp->timer_cookie = timeout(sctp_cookie_timeout, (caddr_t) sd, SCTP_TIMER_BACKOFF); break; } sp->timer_cookie = 0; if (sp->s_state != SCTP_COOKIE_ECHOED) { rare(); break; } if (sctp_assoc_timedout(sp, sd, sp->max_retrans)) { seldom(); break; } /* See RFC 2960 6.3.3 E3 */ for (mp = bufq_head(&sp->rtxq); mp; mp = mp->b_next) { sctp_tcb_t *cb = SCTP_TCB(mp); seldom(); if (cb->daddr == sd && (cb->flags & SCTPCB_FLAG_SENT) && !(cb->flags & SCTPCB_FLAG_RETRANS)) { cb->flags |= SCTPCB_FLAG_RETRANS; sp->nrtxs++; cb->sacks = 0; } } sd = sp->taddr; /* might have new primary */ ensure(sd, break); set_timeout(sp, &sp->timer_cookie, sctp_cookie_timeout, sd, sd->rto); usual(sp->retry); sctp_send_msg(sp, sd, sp->retry); } while (0); sctp_bh_unlock(sp); } /* * RETRANS TIMEOUT (T3-rtx) * ------------------------------------------------------------------------- * This means that we have not received an ack for a DATA chunk within timer * T3-rtx. This means that we should mark all outstanding DATA chunks for * retransmission and start a retransmission cycle. */ STATIC void sctp_retrans_timeout(data) caddr_t data; { mblk_t *mp; sctp_t *sp; sctp_daddr_t *sd; int retransmits = 0; sd = (sctp_daddr_t *) data; assert(sd); sp = sd->sp; assert(sp); sctp_bh_lock(sp); do { if (!sd->timer_retrans) { rare(); break; } if (sctp_locked(sp)) { seldom(); sd->timer_retrans = timeout(sctp_retrans_timeout, (caddr_t) sd, SCTP_TIMER_BACKOFF); break; } sd->timer_retrans = 0; if (!((1 << sp->s_state) & (SCTPF_CONNECTED))) { rare(); break; } if (sctp_assoc_timedout(sp, sd, sp->max_retrans)) { seldom(); break; } /* See RFC 2960 6.3.3 E3 */ for (mp = bufq_head(&sp->rtxq); mp; mp = mp->b_next) { sctp_tcb_t *cb = SCTP_TCB(mp); size_t dlen = cb->dlen; if (cb->daddr == sd && (cb->flags & SCTPCB_FLAG_SENT) && !(cb->flags & SCTPCB_FLAG_RETRANS) && !(cb->flags & SCTPCB_FLAG_SACKED)) { cb->flags |= SCTPCB_FLAG_RETRANS; sp->nrtxs++; cb->sacks = 0; normal(sd->in_flight >= dlen); normal(sp->in_flight >= dlen); sd->in_flight = sd->in_flight > dlen ? sd->in_flight - dlen : 0; /* credit dest */ sp->in_flight = sp->in_flight > dlen ? sp->in_flight - dlen : 0; /* credit assoc */ retransmits++; } } normal(retransmits); sctp_transmit_wakeup(sp); } while (0); sctp_bh_unlock(sp); } /* * SACK TIMEOUT * ------------------------------------------------------------------------- * This timer is the 200ms timer which ensures that a SACK is sent within * 200ms of the receipt of an unacknoweldged DATA chunk. When an * unacknowledged DATA chunks i receive and the timer is not running, the * timer is set. Whenever a DATA chunks(s) are acknowledged, the timer is * stopped. */ STATIC void sctp_sack_timeout(data) caddr_t data; { sctp_t *sp; sp = (sctp_t *) data; assert(sp); sctp_bh_lock(sp); do { if (!sp->timer_sack) { rare(); break; } if (sctp_locked(sp)) { seldom(); sp->timer_sack = timeout(sctp_sack_timeout, (caddr_t) sp, SCTP_TIMER_BACKOFF); break; } sp->timer_sack = 0; if (!((1 << sp->s_state) & (SCTPF_RECEIVING))) { rare(); break; } sp->sackf |= SCTP_SACKF_TIM; /* RFC 2960 6.2 */ sctp_transmit_wakeup(sp); } while (0); sctp_bh_unlock(sp); } /* * IDLE TIMEOUT * ------------------------------------------------------------------------- * This means that a destination has been idle for longer than the hb.itvl or * the interval for which we must send heartbeats. This timer is reset every * time we do an RTT calculation for a destination. It is stopped while * sending heartbeats and started again whenever an RTT calculation is done. * While this timer is stopped, heartbeats will be sent until they are * acknowledged. */ STATIC void sctp_idle_timeout(data) caddr_t data; { sctp_t *sp; sctp_daddr_t *sd; sd = (sctp_daddr_t *) data; assert(sd); sp = sd->sp; assert(sp); sctp_bh_lock(sp); do { if (!sd->timer_idle) { rare(); break; } if (sctp_locked(sp)) { seldom(); sd->timer_idle = timeout(&sctp_idle_timeout, (caddr_t) sd, SCTP_TIMER_BACKOFF); break; } sd->timer_idle = 0; if (!((1 << sp->s_state) & (SCTPF_CONNECTED | SCTPF_CLOSING))) { rare(); break; } sctp_send_heartbeat(sp, sd); } while (0); sctp_bh_unlock(sp); } /* * HEARTBEAT TIMEOUT * ------------------------------------------------------------------------- * If we get a heartbeat timeout we adjust RTO the same as we do for * retransmit (and the congestion window) and resend the heartbeat. Once we * have sent Path.Max.Retrans heartbeats unsuccessfully, we mark the * destination as unusable, but continue heartbeating until they get * acknowledged. (Well! That's not really true, is it?) */ STATIC void sctp_heartbeat_timeout(data) caddr_t data; { sctp_t *sp; sctp_daddr_t *sd; sd = (sctp_daddr_t *) data; assert(sd); sp = sd->sp; assert(sp); sctp_bh_lock(sp); do { if (!sd->timer_heartbeat) { rare(); break; } if (sctp_locked(sp)) { seldom(); sd->timer_heartbeat = timeout(sctp_heartbeat_timeout, (caddr_t) sd, SCTP_TIMER_BACKOFF); break; } sd->timer_heartbeat = 0; if (!((1 << sp->s_state) & (SCTPF_CONNECTED | SCTPF_CLOSING))) { rare(); break; } if (sctp_assoc_timedout(sp, sd, 0)) { seldom(); break; } sctp_send_heartbeat(sp, sd); } while (0); sctp_bh_unlock(sp); } /* * SHUTDOWN TIMEOUT * ------------------------------------------------------------------------- * This means that we have timedout on sending a SHUTDOWN or a SHUTDOWN ACK * message. We simply resend the message. */ STATIC void sctp_shutdown_timeout(data) caddr_t data; { sctp_t *sp; sctp_daddr_t *sd; sd = (sctp_daddr_t *) data; assert(sd); sp = sd->sp; assert(sp); sctp_bh_lock(sp); do { if (!sp->timer_shutdown) { rare(); break; } if (sctp_locked(sp)) { seldom(); sp->timer_shutdown = timeout(sctp_shutdown_timeout, (caddr_t) sd, SCTP_TIMER_BACKOFF); break; } sp->timer_shutdown = 0; if (!((1 << sp->s_state) & (SCTPF_CLOSING))) { rare(); break; } if (sctp_assoc_timedout(sp, sd, sp->max_retrans)) { seldom(); break; } sd = sp->taddr; ensure(sd, break); set_timeout(sp, &sp->timer_shutdown, sctp_shutdown_timeout, (caddr_t) sd, sd->rto); usual(sp->retry); sctp_send_msg(sp, sd, sp->retry); } while (0); sctp_bh_unlock(sp); } /* * SEND DATA * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * This function slaps a chunk header onto the M_DATA blocks which form the * data and places it onto the stream's write queue. The message blocks * input to this function already have a chunk control block prepended. */ STATIC int sctp_send_data(sp, st, flags, dp) sctp_t *sp; sctp_strm_t *st; ulong flags; mblk_t *dp; { uint *more; mblk_t **head; uint32_t ppi; size_t mlen, dlen; ulong dflags = flags; uint urg = (dflags & SCTPCB_FLAG_URG); assert(sp); assert(st); assert(dp); if (urg) { more = &st->x.more; head = &st->x.head; ppi = st->x.ppi; } else { more = &st->n.more; head = &st->n.head; ppi = st->n.ppi; } for (mlen = msgdsize(dp); mlen; mlen -= dlen, dflags &= ~SCTPCB_FLAG_FIRST_FRAG) { mblk_t *bp; struct sctp_daddr *sd; if (!(sd = sctp_route_normal(sp))) return (-EHOSTUNREACH); /* * If there is not enough room in the current send window to handle all or at * least 1/2 MTU of the data and the current send backlog then return (-EBUSY) * and put the message back on the queue so that backpressure will result. We * only do this separately for normal data and urgent data (urgent data will be * expedited ahead of even retransmissions). */ { size_t cwnd, rwnd, swnd, awnd, plen, amps, dmps, used; plen = PADC(sizeof(struct sctp_data) + mlen); amps = sp->pmtu - sizeof(struct iphdr) - sizeof(struct sctphdr); dmps = sd->mtu - sizeof(struct iphdr) - sizeof(struct sctphdr); used = urg ? sp->urgq.q_count : sp->sndq.q_count; cwnd = sd->cwnd + dmps; cwnd = cwnd > sd->in_flight ? cwnd - sd->in_flight : 0; rwnd = sp->p_rwnd; rwnd = rwnd > sp->in_flight ? rwnd - sp->in_flight : 0; swnd = cwnd < rwnd ? cwnd : rwnd; awnd = sp->in_flight ? swnd : cwnd; awnd = awnd > used ? awnd - used : 0; if (plen > awnd || plen > amps) { if (plen > amps && awnd >= amps >> 1) { /* SWS avoidance */ if ((bp = dupmsg(dp))) { dlen = awnd < amps ? amps >> 1 : amps; ensure(dlen > sizeof(struct sctp_data), freemsg(bp); return (-EFAULT)); dlen -= sizeof(struct sctp_data); ensure(dlen < mlen, freemsg(bp); return (-EFAULT)); #if 1 { int ret; ret = trimhead(dp, dlen); /* trim original */ unless(ret, freemsg(bp); return (-EFAULT)); ret = trimtail(bp, dlen); /* trim fragment */ unless(ret, freemsg(bp); return (-EFAULT)); } #else fixme(("Should consider multiple mblks\n")); dp->b_rptr = dp->b_rptr + dlen; /* trim original */ bp->b_wptr = bp->b_rptr + dlen; /* trim fragment */ #endif dflags &= ~SCTPCB_FLAG_LAST_FRAG; } else { rare(); return (-ENOBUFS); } } else { rare(); return (-EBUSY); } } else { bp = dp; dlen = mlen; /* use entire */ dflags |= flags & SCTPCB_FLAG_LAST_FRAG; /* * If we have an existing SDU being built that hasn't * been transmitted yet, we just tack data onto it. We * concatenate only to an MTU. */ if (*more && *head && plen + SCTP_TCB(*head)->dlen <= amps) { struct sctp_data *m; sctp_tcb_t *cb = SCTP_TCB(*head); cb->flags |= (dflags & 0x7); cb->dlen += dlen; m = (struct sctp_data *) (*head)->b_rptr; m->ch.flags = cb->flags & 0x7; m->ch.len = htons(sizeof(*m) + cb->dlen); linkb(*head, bp); normal(cb->dlen == msgdsize((*head)->b_cont)); if (dflags & SCTPCB_FLAG_LAST_FRAG) { *head = NULL; *more = 0; } return (0); } } } { mblk_t *mp; sctp_tcb_t *cb; struct sctp_data *m; if ((mp = allocb(sizeof(*cb) + sizeof(*m), BPRI_MED))) { mp->b_datap->db_type = M_DATA; cb = (sctp_tcb_t *) mp->b_wptr; bzero(cb, sizeof(*cb)); cb->mp = mp; cb->dlen = dlen; cb->flags = dflags; cb->st = st; cb->when = jiffies; cb->daddr = NULL; /* set when transmitted */ cb->ppi = ppi; cb->sid = st->sid; cb->ssn = urg ? 0 : st->ssn; mp->b_rptr += sizeof(*cb); /* hide control block */ mp->b_wptr += sizeof(*cb); m = (struct sctp_data *) mp->b_wptr; m->ch.type = SCTP_CTYPE_DATA; m->ch.flags = dflags; m->ch.len = htons(sizeof(*m) + dlen); m->tsn = 0; /* assign before transmission */ m->sid = htons(cb->sid); m->ssn = htons(cb->ssn); m->ppi = htonl(cb->ppi); mp->b_wptr += sizeof(*m); mp->b_cont = bp; normal(cb->dlen == msgdsize(mp->b_cont)); /* * Remember where we can add more data in case data * completing a SDU comes before we are forced to bundle the * DATA. */ *more = (dflags & SCTPCB_FLAG_LAST_FRAG) ? 0 : 1; *head = (dflags & SCTPCB_FLAG_LAST_FRAG) ? NULL : mp; if (urg) { bufq_queue(&sp->urgq, mp); } else { if (dflags & SCTPCB_FLAG_LAST_FRAG) st->ssn = (st->ssn + 1) & 0xffff; bufq_queue(&sp->sndq, mp); } } else { rare(); return (-ENOBUFS); } } } return (0); } /* * SEND SACK * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC void sctp_send_sack(sp) sctp_t *sp; { sp->sackf |= SCTP_SACKF_NOD; } /* * SEND ERROR * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * We just queue the error, we don't send it out... It gets bundled with * other things. */ STATIC int sctp_send_error(sp, ecode, eptr, elen) sctp_t *sp; uint ecode; caddr_t eptr; size_t elen; { mblk_t *mp; sctp_tcb_t *cb; struct sctp_error *m; struct sctpehdr *eh; size_t clen = sizeof(*m) + sizeof(*eh) + elen; size_t plen = PADC(clen); assert(sp); if ((mp = allocb(sizeof(*cb) + plen, BPRI_MED))) { mp->b_datap->db_type = M_DATA; cb = (sctp_tcb_t *) mp->b_wptr; bzero(cb, sizeof(*cb)); cb->mp = mp; mp->b_rptr += sizeof(*cb); /* hide control block */ mp->b_wptr = mp->b_rptr; bzero(mp->b_wptr + clen, plen - clen); m = (struct sctp_error *) mp->b_wptr; m->ch.type = SCTP_CTYPE_ERROR; m->ch.flags = 0; m->ch.len = htons(clen); eh = (struct sctpehdr *) (m + 1); eh->code = htons(ecode); eh->len = htons(sizeof(*eh) + elen); bcopy(eptr, (eh + 1), elen); mp->b_wptr += plen; bufq_queue(&sp->errq, mp); return (0); } rare(); return (-ENOBUFS); } /* * SEND INIT * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * If we fail to launch the INIT and get timers started, we must return an * error to the user interface calling this function. */ STATIC int sctp_send_init(sp) sctp_t *sp; { sctp_daddr_t *sd; assert(sp); if ((sd = sp->daddr)) { mblk_t *mp; struct sctp_init *m; struct sctp_addr_type *at; struct sctp_ipv4_addr *ap; struct sctp_cookie_psrv *cp; size_t sanum = sp->sanum; size_t clen = sizeof(*m) + (sanum * PADC(sizeof(*ap))) + (sp->ck_inc ? PADC(sizeof(*cp)) : 0) + (sizeof(*at) + sizeof(at->type[0])); if ((mp = sctp_alloc_msg(sp, clen))) { sctp_saddr_t *ss; m = (struct sctp_init *) mp->b_wptr; m->ch.type = SCTP_CTYPE_INIT; m->ch.flags = 0; m->ch.len = htons(clen); m->i_tag = sp->v_tag; m->a_rwnd = htonl(sp->a_rwnd); m->n_istr = htons(sp->max_istr); m->n_ostr = htons(sp->req_ostr); m->i_tsn = htonl(sp->v_tag); mp->b_wptr += sizeof(*m); for (ss = sp->saddr; ss && sanum; ss = ss->next, sanum--) { ap = (struct sctp_ipv4_addr *) mp->b_wptr; ap->ph.type = SCTP_PTYPE_IPV4_ADDR; ap->ph.len = htons(sizeof(*ap)); ap->addr = ss->saddr; mp->b_wptr += PADC(sizeof(*ap)); } unusual(ss); unusual(sanum); if (sp->ck_inc) { cp = (struct sctp_cookie_psrv *) mp->b_wptr; cp->ph.type = SCTP_PTYPE_COOKIE_PSRV; cp->ph.len = htons(sizeof(*cp));; cp->ck_inc = htonl(sp->ck_inc); mp->b_wptr += PADC(sizeof(*cp)); } at = (struct sctp_addr_type *) mp->b_wptr; at->ph.type = SCTP_PTYPE_ADDR_TYPE; at->ph.len = htons(sizeof(*at) + sizeof(at->type[0])); at->type[0] = SCTP_PTYPE_IPV4_ADDR; mp->b_wptr += PADC(sizeof(*at) + sizeof(at->type[0])); sctp_send_msg(sp, sd, mp); mod_timeout(sp, &sp->timer_init, &sctp_init_timeout, sd, sd->rto); unusual(sp->retry); freechunks(xchg(&sp->retry, mp)); sp->s_state = SCTP_COOKIE_WAIT; return (0); } rare(); return (-ENOBUFS); } rare(); return (-EFAULT); } /* * SEND INIT ACK * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * No s_state change results from replying to an INIT. INIT ACKs are sent * without a TCB but a STREAM is referenced. INIT ACK chunks cannot have any * other chunks bundled with them. (RFC 2960 6.10). */ STATIC void sctp_send_init_ack(sp, daddr, sh, ck) sctp_t *sp; uint32_t daddr; struct sctphdr *sh; struct sctp_cookie *ck; { mblk_t *mp; struct sctp_init_ack *m; struct sctpphdr *ph; struct sctp_cookie *cp; struct sctp_ipv4_addr *ap; struct sctp_init *im = (struct sctp_init *) (sh + 1); unsigned char *init = (unsigned char *) im; int anum = ck->danum; int snum = ck->sanum; size_t klen = sizeof(*ph) + raw_cookie_size(ck) + HMAC_SIZE; size_t dlen = sp->sanum * PADC(sizeof(*ap)); size_t clen = sizeof(*m) + dlen + klen; int arem, alen; assert(sp); if ((mp = sctp_alloc_reply(sh, clen))) { sctp_saddr_t *ss; struct sctphdr *rh; rh = ((struct sctphdr *) mp->b_wptr) - 1; rh->v_tag = im->i_tag; m = (struct sctp_init_ack *) mp->b_wptr; m->ch.type = SCTP_CTYPE_INIT_ACK; m->ch.flags = 0; m->ch.len = htons(clen); m->i_tag = ck->v_tag; m->a_rwnd = htonl(sp->a_rwnd); m->n_istr = htons(ck->n_istr); m->n_ostr = htons(ck->n_ostr); m->i_tsn = htonl(ck->v_tag); mp->b_wptr += sizeof(*m); for (ss = sp->saddr; ss; ss = ss->next) { ap = (struct sctp_ipv4_addr *) mp->b_wptr; ap->ph.type = SCTP_PTYPE_IPV4_ADDR; ap->ph.len = __constant_htons(sizeof(*ap)); ap->addr = ss->saddr; mp->b_wptr += PADC(sizeof(*ap)); } ph = (struct sctpphdr *) mp->b_wptr; ph->type = SCTP_PTYPE_STATE_COOKIE; ph->len = htons(klen); mp->b_wptr += sizeof(*ph); cp = (struct sctp_cookie *) mp->b_wptr; bcopy(ck, cp, sizeof(*cp)); mp->b_wptr += sizeof(*cp); #if 0 /* copy in IP reply options */ if (ck->opt_len) { assure(opt); bcopy(opt, mp->b_wptr, optlength(opt)); kfree_s(opt, optlength(opt)); sp->opt = (struct ip_options *) mp->b_wptr; mp->b_wptr += ck->opt_len; } #endif for (ap = (struct sctp_ipv4_addr *) (init + sizeof(struct sctp_init)), arem = PADC(htons(((struct sctpchdr *) init)->len)) - sizeof(struct sctp_init); anum && arem >= sizeof(struct sctpphdr); arem -= PADC(alen), ap = (struct sctp_ipv4_addr *) (((uint8_t *) ap) + PADC(alen))) { if ((alen = ntohs(ap->ph.len)) > arem) { assure(alen <= arem); freemsg(mp); rare(); return; } if (ap->ph.type == SCTP_PTYPE_IPV4_ADDR) { /* skip primary */ if (ap->addr != ck->daddr) { *((uint32_t *) mp->b_wptr)++ = ap->addr; anum--; } } } for (ss = sp->saddr; ss; ss = ss->next) { if (ss->saddr != ck->saddr) { *((uint32_t *) mp->b_wptr)++ = ss->saddr; snum--; } } assure(!anum); assure(!snum); sctp_sign_cookie(sp, cp); mp->b_wptr += HMAC_SIZE; sctp_xmit_msg(daddr, mp, sp); } } /* * SEND COOKIE ECHO * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * If we fail to launch the COOKIE ECHO and get timers started, we must * return an error to the user interface calling this function. */ STATIC int sctp_send_cookie_echo(sp, kptr, klen) sctp_t *sp; caddr_t kptr; size_t klen; { sctp_daddr_t *sd; assert(sp); if ((sd = sp->daddr)) { mblk_t *mp; struct sctp_cookie_echo *m; size_t clen = sizeof(*m) + klen; size_t plen = PADC(clen); if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_cookie_echo *) mp->b_wptr; m->ch.type = SCTP_CTYPE_COOKIE_ECHO; m->ch.flags = 0; m->ch.len = htons(clen); bcopy(kptr, (m + 1), klen); mp->b_wptr += plen; sctp_bundle_more(sp, sd, mp); sctp_send_msg(sp, sd, mp); mod_timeout(sp, &sp->timer_cookie, &sctp_cookie_timeout, sd, sd->rto); unusual(sp->retry); freechunks(xchg(&sp->retry, mp)); sp->s_state = SCTP_COOKIE_ECHOED; return (0); } rare(); return (-ENOBUFS); } rare(); return (-EFAULT); } /* * SEND COOKIE ACK * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * SACK and DATA can be bundled with the COOKIE ACK. */ STATIC void sctp_send_cookie_ack(sp) sctp_t *sp; { sctp_daddr_t *sd; assert(sp); if ((sd = sctp_route_response(sp))) { mblk_t *mp; struct sctp_cookie_ack *m; size_t clen = sizeof(*m); size_t plen = PADC(clen); if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_cookie_ack *) mp->b_wptr; m->ch.type = SCTP_CTYPE_COOKIE_ACK; m->ch.flags = 0; m->ch.len = __constant_htons(clen); mp->b_wptr += plen; sctp_bundle_more(sp, sd, mp); sctp_send_msg(sp, sd, mp); freechunks(mp); } sp->s_state = SCTP_ESTABLISHED; /* start idle timers */ for (sd = sp->daddr; sd; sd = sd->next) sctp_reset_idle(sd); } } /* * SEND HEARTBEAT * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * We don't send heartbeats when idle timers expire if we are in the wrong * state, we just reset the idle timer. */ STATIC void sctp_send_heartbeat(sp, sd) sctp_t *sp; sctp_daddr_t *sd; { mblk_t *mp; struct sctp_heartbeat *m; struct sctp_heartbeat_info *h; size_t fill, clen, hlen, plen; assert(sp); assert(sd); fill = sd->hb_fill; clen = sizeof(*m) + sizeof(*h) + fill; hlen = clen - sizeof(struct sctpchdr); plen = PADC(clen); sd->hb_time = jiffies; if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_heartbeat *) mp->b_wptr; m->ch.type = SCTP_CTYPE_HEARTBEAT; m->ch.flags = 0; m->ch.len = htons(clen); h = (struct sctp_heartbeat_info *) (m + 1); h->ph.type = SCTP_PTYPE_HEARTBEAT_INFO; h->ph.len = htons(hlen); h->hb_info.timestamp = sd->hb_time; h->hb_info.daddr = sd->daddr; h->hb_info.mtu = sd->mtu; bzero(h->hb_info.fill, fill); mp->b_wptr += plen; sctp_send_msg(sp, sd, mp); freechunks(mp); } mod_timeout(sp, &sd->timer_heartbeat, &sctp_heartbeat_timeout, sd, sd->rto); } /* * SEND HEARTBEAT ACK * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Take the incoming HEARTBEAT message and turn it back around as a HEARTBEAT * ACK message. Note that if the incoming chunk parameters are invalid, so * are the outgoing parameters, this is because the hb_info parameter is * opaque to us. This is consistent with draft-stewart-ong-sctpbakeoff- * sigtran-01. */ STATIC void sctp_send_heartbeat_ack(sp, hptr, hlen) sctp_t *sp; caddr_t hptr; size_t hlen; { sctp_daddr_t *sd; assert(sp); if ((sd = sctp_route_response(sp))) { mblk_t *mp; struct sctp_heartbeat_ack *m; size_t clen = sizeof(*m) + hlen; size_t plen = PADC(clen); if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_heartbeat_ack *) mp->b_wptr; m->ch.type = SCTP_CTYPE_HEARTBEAT_ACK; m->ch.flags = 0; m->ch.len = htons(clen); bcopy(hptr, (m + 1), hlen); mp->b_wptr += plen; sctp_send_msg(sp, sd, mp); freechunks(mp); } } } /* * SEND ABORT * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * There is no point in bundling control chunks after an ABORT chunk. Also, * DATA chunks are not to be bundled with ABORT chunks. */ STATIC void sctp_send_abort(sp) sctp_t *sp; { sctp_daddr_t *sd; assert(sp); if ((1 << sp->s_state) & SCTPF_CONNECTED) sd = sctp_route_normal(sp); else sd = sp->daddr; if (sd) { mblk_t *mp; struct sctp_abort *m; size_t clen = sizeof(*m); size_t plen = PADC(clen); if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_abort *) mp->b_wptr; m->ch.type = SCTP_CTYPE_ABORT; m->ch.flags = 0; m->ch.len = __constant_htons(clen); mp->b_wptr += plen; sctp_send_msg(sp, sd, mp); freechunks(mp); } sp->s_state = sp->conind ? SCTP_LISTEN : SCTP_CLOSED; } } /* * SEND ABORT (w/ERROR CAUSE) * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Errors (beyond the error header) must be formatted by the called and * indicated by are and len. There is no point in bundling data or control * chunks after and abort chunk. */ STATIC void sctp_send_abort_error(sp, errn, aptr, alen) sctp_t *sp; int errn; void *aptr; /* argument ptr */ size_t alen; /* argument len */ { sctp_daddr_t *sd; assert(sp); if ((sd = sctp_route_normal(sp))) { if (errn) { mblk_t *mp; struct sctp_abort *m; struct sctpehdr *eh; size_t elen = sizeof(*eh) + alen; size_t clen = sizeof(*m) + elen; size_t plen = PADC(clen); if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_abort *) mp->b_wptr; m->ch.type = SCTP_CTYPE_ABORT; m->ch.flags = 0; m->ch.len = htons(clen); eh = (struct sctpehdr *) (m + 1); eh->code = htons(errn); eh->len = htons(elen); bcopy(aptr, (eh + 1), alen); mp->b_wptr += plen; sctp_send_msg(sp, sd, mp); freechunks(mp); } sp->s_state = sp->conind ? SCTP_LISTEN : SCTP_CLOSED; return; } sctp_send_abort(sp); } } /* * SEND SHUTDOWN * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * RFC 2960 6.2 "... DATA chunks cannot be bundled with SHUTDOWN or SHUTDOWN * ACK chunks ..." * * If we fail to launch the SHUTDOWN and get timers started, we must inform * the user interface calling this function. */ STATIC int sctp_send_shutdown(sp) sctp_t *sp; { mblk_t *mp; sctp_daddr_t *sd; assert(sp); if ((sd = sctp_route_normal(sp))) { struct sctp_shutdown *m; size_t clen = sizeof(*m); size_t plen = PADC(clen); if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_shutdown *) mp->b_wptr; m->ch.type = SCTP_CTYPE_SHUTDOWN; m->ch.flags = 0; m->ch.len = __constant_htons(clen); m->c_tsn = htonl(sp->r_ack); mp->b_wptr += plen; /* shutdown acks everything but dups and gaps */ sp->sackf &= (SCTP_SACKF_DUP | SCTP_SACKF_GAP); sctp_bundle_more(sp, sd, mp); /* not DATA */ sctp_send_msg(sp, sd, mp); mod_timeout(sp, &sp->timer_shutdown, &sctp_shutdown_timeout, sd, sd->rto); // unusual( sp->retry ); /* not that unusual */ freechunks(xchg(&sp->retry, mp)); sp->s_state = SCTP_SHUTDOWN_SENT; return (0); } rare(); return (-ENOBUFS); } rare(); return (-EFAULT); } /* * SEND SHUTDOWN ACK * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * SHUTDOWN ACK is sent in response to a SHUTDOWN message after all data has * cleared or in reponse to a COOKIE ECHO during the SHUTDOWN_ACK_SENT s_state. * If the error flag is set, we want to bundle and ERROR chunk with the * SHUTDOWN ACK indicating "cookie received while shutting down." * * RFC 2960 6.2. "... DATA chunks cannot be bundled with SHUTDOWN or * SHUTDOWN ACK chunks ..." * * If we fail to launch the SHUTDOWN ACK and get timers started, we must * return an error to the user interface calling this function. */ STATIC int sctp_send_shutdown_ack(sp) sctp_t *sp; { mblk_t *mp; sctp_daddr_t *sd; assert(sp); if ((sd = sctp_route_response(sp))) { struct sctp_shutdown_ack *m; size_t clen = sizeof(*m); size_t plen = PADC(clen); if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_shutdown_ack *) mp->b_wptr; m->ch.type = SCTP_CTYPE_SHUTDOWN_ACK; m->ch.flags = 0; m->ch.len = __constant_htons(clen); mp->b_wptr += plen; sctp_bundle_more(sp, sd, mp); /* not DATA */ sctp_send_msg(sp, sd, mp); mod_timeout(sp, &sp->timer_shutdown, &sctp_shutdown_timeout, sd, sd->rto); unusual(sp->retry); freechunks(xchg(&sp->retry, mp)); sp->s_state = SCTP_SHUTDOWN_ACK_SENT; return (0); } rare(); return (-ENOBUFS); } rare(); return (-EFAULT); } /* * SEND SHUTDOWN COMPLETE * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC void sctp_send_shutdown_complete(sp) sctp_t *sp; { mblk_t *mp; sctp_daddr_t *sd; assert(sp); if ((sd = sctp_route_response(sp))) { struct sctp_shutdown_comp *m; size_t clen = sizeof(*m); size_t plen = PADC(clen); if ((mp = sctp_alloc_msg(sp, clen))) { m = (struct sctp_shutdown_comp *) mp->b_wptr; m->ch.type = SCTP_CTYPE_SHUTDOWN_COMPLETE; m->ch.flags = 0; m->ch.len = __constant_htons(clen); mp->b_wptr += plen; sctp_send_msg(sp, sd, mp); freechunks(mp); } } sp->s_state = sp->conind ? SCTP_LISTEN : SCTP_CLOSED; } /* * SENDING WITHOUT TCB (Responding to OOTB packets) * ------------------------------------------------------------------------- * When sending without an SCTP TCB, we only have the IP header and the SCTP * header from which to work. We have no associated STREAM. These are * usually used for replying to OOTB messages. * * SEND ABORT (Without TCB) * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ void sctp_send_abort_ootb(daddr, saddr, sh) uint32_t daddr; uint32_t saddr; struct sctphdr *sh; { mblk_t *mp; struct sctp_abort *m; size_t clen = sizeof(*m); size_t plen = PADC(clen); assert(sh); if ((mp = sctp_alloc_reply(sh, clen))) { m = (struct sctp_abort *) mp->b_wptr; m->ch.type = SCTP_CTYPE_ABORT; m->ch.flags = 1; m->ch.len = __constant_htons(clen); mp->b_wptr += plen; sctp_xmit_ootb(daddr, saddr, mp); } else rare(); } /* * SEND ABORT (w/ERROR CAUSE) (Without TCB) * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC void sctp_send_abort_error_ootb(daddr, saddr, sh, errn, aptr, alen) uint32_t daddr; uint32_t saddr; struct sctphdr *sh; int errn; caddr_t aptr; /* argument ptr */ size_t alen; /* argument len */ { assert(sh); if (errn) { mblk_t *mp; struct sctp_abort *m; struct sctpehdr *eh; size_t elen = sizeof(*eh) + alen; size_t clen = sizeof(*m) + elen; size_t plen = PADC(clen); if ((mp = sctp_alloc_reply(sh, clen))) { m = (struct sctp_abort *) mp->b_wptr; m->ch.type = SCTP_CTYPE_ABORT; m->ch.flags = 1; m->ch.len = htons(clen); eh = (struct sctpehdr *) (m + 1); eh->code = htons(errn); eh->len = htons(elen); bcopy(aptr, (eh + 1), alen); mp->b_wptr += plen; sctp_xmit_ootb(daddr, saddr, mp); } else rare(); return; } sctp_send_abort_ootb(daddr, saddr, sh); } /* * SEND SHUTDOWN COMPLETE (Without TCB) * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ void sctp_send_shutdown_complete_ootb(daddr, saddr, sh) uint32_t daddr; uint32_t saddr; struct sctphdr *sh; { mblk_t *mp; struct sctp_shutdown_comp *m; size_t clen = sizeof(*m); size_t plen = PADC(clen); assert(sh); if ((mp = sctp_alloc_reply(sh, clen))) { m = (struct sctp_shutdown_comp *) mp->b_wptr; m->ch.type = SCTP_CTYPE_SHUTDOWN_COMPLETE; m->ch.flags = 1; m->ch.len = __constant_htons(clen); mp->b_wptr += plen; sctp_xmit_ootb(daddr, saddr, mp); } else rare(); } /* * BIND_REQ: * ------------------------------------------------------------------------- * Bind the stream to the addresses provided in its bound address lists. * There are some errors that may be returned here: * * Any UNIX error. * * NOADDR - A wildcard address was specified and we don't support * wildcards (maybe we will). * * ADDRBUSY - * */ int sctp_bind_req(sp, sport, sptr, snum, cons) sctp_t *sp; uint16_t sport; uint32_t *sptr; size_t snum; ulong cons; { int err; assert(sp); if (!cons && !sport) { rare(); return (-EADDRNOTAVAIL); } if ((err = sctp_alloc_saddrs(sp, sport, sptr, snum))) { rare(); return (err); } if (cons && (!sp->sanum || !sp->saddr)) { rare(); return (-EADDRNOTAVAIL); } if (cons) sp->s_state = SCTP_LISTEN; else sp->s_state = SCTP_CLOSED; if ((err = sctp_bind_hash(sp, cons))) { rare(); return (err); } sp->conind = cons; return (0); } /* * CONN_REQ: * ------------------------------------------------------------------------- * Connect to the peer. This launches the INIT process. */ int sctp_conn_req(sp, dport, dptr, dnum, dp) sctp_t *sp; uint16_t dport; uint32_t *dptr; size_t dnum; mblk_t *dp; { int err; assert(sp); if (!dport) { rare(); return (-EADDRNOTAVAIL); } if ((err = sctp_alloc_daddrs(sp, dport, dptr, dnum))) { rare(); return (err); } if (!sp->daddr || !sp->danum) { rare(); return (-EADDRNOTAVAIL); } sp->v_tag = sctp_get_vtag(sp->daddr->daddr, sp->saddr->saddr, sp->dport, sp->sport); sp->p_tag = 0; if ((err = sctp_conn_hash(sp))) { rare(); return (err); } /* XXX */ if ((err = sctp_update_routes(sp, 1))) { rare(); return (err); } sctp_reset(sp); /* clear old information */ sp->n_istr = 0; sp->n_ostr = 0; sp->t_tsn = sp->v_tag; sp->t_ack = sp->v_tag - 1; sp->r_ack = 0; /* fake a data request if data in conn req */ if (dp) { seldom(); if ((err = sctp_data_req(sp, sp->ppi, sp->sid, 0, 0, 0, dp))) { rare(); return (err); } } if ((err = sctp_send_init(sp))) { rare(); return (err); } return (0); } /* * CONN_RES: * ------------------------------------------------------------------------- */ STATIC int sctp_return_more(mblk_t *mp); int sctp_conn_res(sp, cp, ap, dp) sctp_t *sp; mblk_t *cp; sctp_t *ap; mblk_t *dp; { int err; struct sctp_cookie_echo *m; struct sctp_cookie *ck; uint32_t *daddrs; uint32_t *saddrs; assert(sp); assert(cp); assert(ap); m = (struct sctp_cookie_echo *) cp->b_rptr; ck = (struct sctp_cookie *) m->cookie; daddrs = (uint32_t *) (ck + 1); saddrs = daddrs + ck->danum; sctp_unbind(ap); /* we need to rebind the accepting stream */ if ((err = sctp_alloc_saddrs(ap, ck->sport, saddrs, ck->sanum))) { rare(); return (err); } if (!sctp_saddr_include(ap, ck->saddr, &err) && err) { rare(); return (err); } if ((err = sctp_bind_hash(ap, ap->conind))) { rare(); return (err); } if ((err = sctp_alloc_daddrs(ap, ck->dport, daddrs, ck->danum))) { rare(); return (err); } if (!sctp_daddr_include(ap, ck->daddr, &err) && err) { rare(); return (err); } ap->v_tag = ck->v_tag; ap->p_tag = ck->p_tag; if ((err = sctp_conn_hash(ap))) { rare(); return (err); } /* XXX */ if ((err = sctp_update_routes(ap, 1))) { rare(); return (err); } sctp_reset(ap); /* clear old information */ ap->n_istr = ck->n_istr; ap->n_ostr = ck->n_ostr; ap->t_tsn = ck->v_tag; ap->t_ack = ck->v_tag - 1; ap->r_ack = ck->p_tsn - 1; ap->p_rwnd = ck->p_rwnd; ap->s_state = SCTP_ESTABLISHED; /* process any chunks bundled with cookie echo on accepting stream */ if (sctp_return_more(cp) > 0) sctp_recv_msg(ap, cp); /* fake a data request if data in conn res */ if (dp) { if ((err = sctp_data_req(ap, ap->ppi, ap->sid, 0, 0, 0, dp))) { rare(); return (err); } } sctp_send_cookie_ack(ap); /* caller will unlink connect indication */ return (0); } /* * DATA_REQ: * ------------------------------------------------------------------------- */ int sctp_data_req(sp, ppi, sid, ord, more, rcpt, mp) sctp_t *sp; uint32_t ppi; uint16_t sid; uint ord; /* when non-zero, indicates ordered delivery */ uint more; /* when non-zero, indicates more data to follow */ uint rcpt; /* when non-zero, indicates receipt conf requested */ mblk_t *mp; { uint err = 0, flags = 0; sctp_strm_t *st; ensure(mp, return (-EFAULT)); /* don't allow zero-length data through */ if (!msgdsize(mp)) { freemsg(mp); return (0); } if (!(st = sctp_ostrm_find(sp, sid, &err))) { rare(); return (err); } /* we probably want to data ack out of order as well */ #if 0 if (rcpt || (ord && (sp->flags & SCTP_FLAG_DEFAULT_RC_SEL))) #else if (rcpt) #endif flags |= SCTPCB_FLAG_CONF; if (!ord) { flags |= SCTPCB_FLAG_URG; if (!st->x.more) { flags |= SCTPCB_FLAG_FIRST_FRAG; st->x.ppi = ppi; } } else { if (!st->n.more) { flags |= SCTPCB_FLAG_FIRST_FRAG; st->n.ppi = ppi; } } if (!more) flags |= SCTPCB_FLAG_LAST_FRAG; return sctp_send_data(sp, st, flags, mp); } /* * RESET_REQ: * ------------------------------------------------------------------------- * Don't know what to do here, probably nothing... * * Gee we could keep a copy of the old cookie against the stream of we * actively connected and send a COOKIE ECHO to generate a RESTART at the * other end???? */ int sctp_reset_req(sp) sctp_t *sp; { int err; /* do nothing */ if (sp->ops->sctp_reset_con && (err = sp->ops->sctp_reset_con(sp))) { rare(); return (err); } return (0); } /* * RESET_RES: * ------------------------------------------------------------------------- */ int sctp_reset_res(sp) sctp_t *sp; { mblk_t *cp; if (!(cp = bufq_dequeue(&sp->conq))) { rare(); return (-EFAULT); } return sctp_conn_res(sp, cp, sp, NULL); } /* * DISCON_REQ: * ------------------------------------------------------------------------- */ int sctp_discon_req(sp, cp) sctp_t *sp; mblk_t *cp; { /* * Caller must ensure that sp and cp (if any) are correct and * appropriate. */ if (cp) { struct iphdr *iph = (struct iphdr *) cp->b_datap->db_base; struct sctphdr *sh = (struct sctphdr *) (cp->b_datap->db_base + (iph->ihl << 2)); sctp_send_abort_ootb(iph->saddr, iph->daddr, sh); /* conn ind will be unlinked by caller */ return (0); } if ((1 << sp->s_state) & (SCTPF_NEEDABORT)) { sctp_send_abort(sp); } else rare(); sctp_disconnect(sp); return (0); } /* * ORDREL_REQ: * ------------------------------------------------------------------------- */ int sctp_ordrel_req(sp) sctp_t *sp; { switch (sp->s_state) { case SCTP_ESTABLISHED: if (!bufq_head(&sp->sndq) && !bufq_head(&sp->rtxq)) sctp_send_shutdown(sp); else sp->s_state = SCTP_SHUTDOWN_PENDING; return (0); case SCTP_SHUTDOWN_RECEIVED: if (!bufq_head(&sp->sndq) && !bufq_head(&sp->rtxq)) sctp_send_shutdown_ack(sp); else sp->s_state = SCTP_SHUTDOWN_RECVWAIT; return (0); } rare(); // ptrace(("sp->s_state = %d\n", sp->s_state)); return (-EPROTO); } /* * UNBIND_REQ: * ------------------------------------------------------------------------- */ int sctp_unbind_req(sp) sctp_t *sp; { switch (sp->s_state) { case SCTP_SHUTDOWN_ACK_SENT: /* can't wait for SHUTDOWN COMPLETE any longer */ sctp_disconnect(sp); case SCTP_CLOSED: case SCTP_LISTEN: sctp_unbind(sp); return (0); } rare(); return (-EPROTO); } /* * ========================================================================= * * SCTP Peer --> SCTP Primitives (Receive Messages) * * ========================================================================= */ /* * RETURN VALUE FUNCTIONS * ------------------------------------------------------------------------- * * RETURN VALUE when expecting more chunks * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC int sctp_return_more(mp) mblk_t *mp; { int ret; struct sctpchdr *ch; assert(mp); ch = (struct sctpchdr *) mp->b_rptr; mp->b_rptr += PADC(ntohs(ch->len)); ret = mp->b_wptr - mp->b_rptr; ret = (ret < 0 || (0 < ret && ret < sizeof(struct sctpchdr))) ? -EMSGSIZE : ret; unusual(ret < 0); return (ret); } /* * RETURN VALUE when not expecting more chunks * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC int sctp_return_stop(mp) mblk_t *mp; { int ret = sctp_return_more(mp) ? -EPROTO : 0; unusual(ret < 0); return (ret); } #if 0 /* * RETURN VALUE when expecting specific chunk or nothing * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ STATIC int sctp_return_check(mp, ctype) mblk_t *mp; uint ctype; { int ret = sctp_return_more(mp); ret = (ret > 0 && ((struct sctpchdr *) mp->b_rptr)->type != ctype) ? -EPROTO : ret; unusual(ret < 0); return (ret); } #endif /* is s2<=s1<=s3 ? */ #define between(__s1,__s2,__s3)((uint32_t)(__s3)-(uint32_t)(__s2)>=(uint32_t)(__s1)-(uint32_t)(__s2)) #define before(__s1,__s2) (((int32_t)((uint32_t)(__s1)-(uint32_t)(__s2))<0)) #define after(__s1,__s2) (((int32_t)((uint32_t)(__s2)-(uint32_t)(__s1))<0)) /* * RESET IDLE * ------------------------------------------------------------------------- * Reset the idle timer for generation of heartbeats. Stop any heartbeating * that we might be involved in at the moment. */ STATIC void sctp_reset_idle(sd) sctp_daddr_t *sd; { unsigned long rtt; assert(sd); if (sd->timer_heartbeat) untimeout(xchg(&sd->timer_heartbeat, 0)); rtt = sd->rto + sd->hb_itvl + ((jiffies & 0x1) * (sd->rto >> 1)); mod_timeout(sd->sp, &sd->timer_idle, &sctp_idle_timeout, sd, rtt); } /* * RTT CALC * ------------------------------------------------------------------------- * Round Trip Time calculations for messages acknowledged on the first * transmission. When a message is acknowledged, this function peforms and * update of the RTT calculation if appropriate. It is called by * sctp_ack_calc for control chunks which expect acknowledgements, and by * sctp_dest_calc when DATA chunks are acknolwedged on first transmission via * SACK or SHUTDOWN chunks, and by sctp_recv_heartbeat_ack when calculating * RTTs for HEARTBEAT chunks. */ STATIC void sctp_rtt_calc(sd, time) sctp_daddr_t *sd; unsigned long time; { unsigned long rtt; unsigned long rttvar; assert(sd); ensure(jiffies >= time, return); /* RFC 2960 6.3.1 */ rtt = jiffies - time; if (sd->srtt) { /* RFC 2960 6.3.1 (C3) */ rttvar = sd->srtt > rtt ? sd->srtt - rtt : rtt - sd->srtt; sd->rttvar += (rttvar - sd->rttvar) >> 2; sd->srtt += (rtt - sd->srtt) >> 3; sd->rto = rtt + (sd->rttvar << 2); } else { /* RFC 2960 6.3.1 (C2) */ sd->rttvar = rtt >> 1; sd->srtt = rtt; sd->rto = rtt + (rtt << 1); } sd->rttvar = sd->rttvar ? sd->rttvar : 1; /* RFC 2960 6.3.1 (G1) */ sd->rto = sd->rto_min > sd->rto ? sd->rto_min : sd->rto; /* RFC 2960 6.3.1 (C6) */ sd->rto = sd->rto_max < sd->rto ? sd->rto_max : sd->rto; /* RFC 2960 6.3.1 (C7) */ #ifdef _DEBUG #ifdef ERROR_GENERATOR if (sd->retransmits && (sd->sp->options & SCTP_OPTION_BREAK) && (sd->packets > BREAK_GENERATOR_LEVEL)) ptrace(("Aaaarg! Reseting counts for address %d.%d.%d.%d\n", (sd->daddr >> 0) & 0xff, (sd->daddr >> 8) & 0xff, (sd->daddr >> 16) & 0xff, (sd->daddr >> 24) & 0xff)); #endif #endif sd->dups = 0; /* RFC 2960 8.2 */ sd->retransmits = 0; /* RFC 2960 8.1 */ sd->sp->retransmits = 0; /* reset idle timer */ sctp_reset_idle(sd); } #ifndef SCTP_DESTF_DROP #define SCTP_DESTF_DROP 0x10000000 /* destination is dropping packets */ #endif /* * DEST CALC * ------------------------------------------------------------------------- * This function performs delayed processing of RTT and CWND calculations on * destinations which need it and is called from sctp_recv_shutdown and * sctp_recv_sack once all ack and gap ack processing is complete. This * performs the necessary calculations for each destination before closing * processing of the received SHUTDOWN or SACK chunk. */ STATIC void sctp_dest_calc(sp) sctp_t *sp; { sctp_daddr_t *sd; assert(sp); usual(sp->daddr); for (sd = sp->daddr; sd; sd = sd->next) { size_t accum; if (sd->when) { /* calculate RTT based on latest sent acked TSN */ sctp_rtt_calc(sd, sd->when); sd->when = 0; } /* * NOTE:- first we grow the congestion window according to * whatever TSNs were cummulatively acked to the destination * and then we back off if the destination is dropping (as * indiciated by gap reports). */ if ((accum = sd->ack_accum)) { if (sd->cwnd <= sd->ssthresh) { /* RFC 2960 7.2.1 */ if (sd->in_flight > sd->cwnd) sd->cwnd += accum < sd->mtu ? accum : sd->mtu; } else { /* RFC 2960 7.2.2 */ if (sd->in_flight > sd->cwnd) sd->cwnd += sd->mtu; } /* credit of destination (accum) */ normal(sd->in_flight >= accum); sd->in_flight = sd->in_flight > accum ? sd->in_flight - accum : 0; /* RFC 2960 6.3.2 (R3) */ if (sd->timer_retrans) untimeout(xchg(&sd->timer_retrans, 0)); sd->ack_accum = 0; } if (sd->flags & SCTP_DESTF_DROP) { /* RFC 2960 7.2.4 (2), 7.2.3 */ sd->ssthresh = sd->cwnd >> 1 > sd->mtu << 1 ? sd->cwnd >> 1 : sd->mtu << 1; sd->cwnd = sd->ssthresh; sd->flags &= ~SCTP_DESTF_DROP; } /* RFC 2960 6.3.2 (R2) */ if (!sd->in_flight && sd->timer_retrans) untimeout(xchg(&sd->timer_retrans, 0)); if (sd->in_flight && !sd->timer_retrans) set_timeout(sp, &sd->timer_retrans, &sctp_retrans_timeout, sd, sd->rto); } } /* * CUMM ACK * ------------------------------------------------------------------------- * This function is responsible for moving the cummulative ack point. The * sender must check that the ack is valid (monotonically increasing), but it * may be the same TSN as was previously acknowledged. When the ack point * advances, this function stikes DATA chunks from the retransmit buffer. * This also indirectly updates the amount of data outstanding for * retransmission. This function is called by both sctp_recv_sack and * sctp_recv_shutdown. * * We only perform calculations on TSNs that were not previously acknowledged * by a GAP Ack. If the TSN has not been retransmitted (Karn's algorithm RFC * 2960 6.3.1 (C5)), the destination that it was set to is marked for RTT * calculation update. If the TSN is currently marked for retransmission * awaiting available cwnd, it is unmarked and retranmission of the TSN is * aborted. If the TSN is not marked for retransmission, the destination's * acked bytes accumulator is increased and the need for a CWND calculation * for the destination indicated. The association number of bytes in flight * is decreased to account for the acknowledged TSN. If any calculations are * pending as a result of the ack, the function returns non-zero to * indication that calculations (RTT and CWND) must be processed before * message processing is complete. */ STATIC void sctp_cumm_ack(sp, ack) sctp_t *sp; uint32_t ack; { assert(sp); /* make sure we actually move the ack point */ if (after(ack, sp->t_ack)) { mblk_t *mp; sp->t_ack = ack; while ((mp = bufq_head(&sp->rtxq)) && !after(SCTP_TCB(mp)->tsn, ack)) { sctp_tcb_t *cb = SCTP_TCB(mp); if (!(cb->flags & SCTPCB_FLAG_SACKED)) { sctp_daddr_t *sd = cb->daddr; /* RFC 2960 6.3.1 (C5) */ if (cb->trans < 2) { /* remember latest transmitted packet acked for rtt calc */ sd->when = sd->when > cb->when ? sd->when : cb->when; } if (cb->flags & SCTPCB_FLAG_RETRANS) { cb->flags &= ~SCTPCB_FLAG_RETRANS; sp->nrtxs--; } else { size_t dlen = cb->dlen; /* credit destination (later) */ normal(sd->in_flight >= sd->ack_accum + dlen); sd->ack_accum = sd->in_flight > sd->ack_accum + dlen ? sd->ack_accum + dlen : sd->in_flight; /* credit association (now) */ normal(sp->in_flight >= dlen); sp->in_flight = sp->in_flight > dlen ? sp->in_flight - dlen : 0; } cb->flags |= SCTPCB_FLAG_SACKED; /* hack */ } if ((cb->flags & SCTPCB_FLAG_CONF) && sp->ops->sctp_datack_ind) bufq_queue(&sp->ackq, bufq_dequeue(&sp->rtxq)); else freemsg(bufq_dequeue(&sp->rtxq)); /* * Need to back-enable the write queue if required. */ if (sp->wq->q_count) qenable(sp->wq); } } return; } /* * ACK CALC * ------------------------------------------------------------------------- * This one is for messages for which a timer is set an retransmission occurs * until acknowledged. We stop the timer, perform an RTT calculation if the * message was not retransmitted, free the retry buffer, and clear the * association and destination retransmission counts. */ STATIC void sctp_ack_calc(sp, tp) sctp_t *sp; /* private structure for stream */ tid_t *tp; /* timer to cancel */ { sctp_tcb_t *cb; sctp_daddr_t *sd; assert(sp); assert(sp->retry); cb = SCTP_TCB(sp->retry); sd = cb->daddr; untimeout(xchg(tp, 0)); if (sd) { if (cb->trans < 2) sctp_rtt_calc(sd, cb->when); } else rare(); freechunks(xchg(&sp->retry, NULL)); } /* * RECV DATA * ------------------------------------------------------------------------- * We have received a DATA chunk in a T-Provider s_state where is it valid to * receive DATA (TS_DATA_XFER and TS_WIND_ORDREL). We can also receive data * chunks in TS_IDLE or TS_WRES_CIND on a listening socket bundled with a * COOKIE ECHO. In any other states we discard the data. Because no other * chunks can be bundled after a DATA chunk (just yet), we process all the * DATA chunks in the remainder of the message in a single loop here. */ STATIC int sctp_recv_data(sp, mp) sctp_t *sp; mblk_t *mp; { int err; int blen; size_t plen; uint newd; assert(sp); assert(mp); if (!((1 << sp->s_state) & (SCTPF_RECEIVING))) goto outstate; newd = 0; /* number of new data chunks */ for (; (blen = mp->b_wptr - mp->b_rptr); mp->b_rptr += plen) { mblk_t *dp, *db; sctp_tcb_t *cb; sctp_strm_t *st; struct sctp_data *m = (struct sctp_data *) mp->b_rptr; size_t clen = ntohs(m->ch.len); size_t dlen = clen > sizeof(*m) ? clen - sizeof(*m) : 0; uint32_t tsn = ntohl(m->tsn); uint16_t sid = ntohs(m->sid); uint16_t ssn = ntohs(m->ssn); uint32_t ppi = ntohl(m->ppi); uint flags = (m->ch.flags); int ord = !(flags & SCTPCB_FLAG_URG); int more = !(flags & SCTPCB_FLAG_LAST_FRAG); sctp_tcb_t **gap; err = 0; plen = PADC(clen); if (blen <= 0 || blen < sizeof(*m) || clen < sizeof(*m) || blen < plen) goto emsgsize; if (m->ch.type != SCTP_CTYPE_DATA) goto eproto; if (dlen <= 0) goto baddata; if (sid >= sp->n_istr) goto badsid; if (sp->a_rwnd <= bufq_size(&sp->oooq) + bufq_size(&sp->dupq) + bufq_size(&sp->rcvq)) goto ebusy; if (!(st = sctp_istrm_find(sp, sid, &err))) goto enomem; if (!(dp = dupb(mp))) goto enobufs; /* trim copy to data only */ dp->b_wptr = dp->b_rptr + clen; dp->b_rptr += sizeof(*m); /* fast path, nothing backed up */ if (tsn == sp->r_ack + 1 && !bufq_head(&sp->rcvq) && !bufq_head(&sp->oooq)) { /* we have next expected TSN, just process it */ if ((err = sp->ops->sctp_data_ind(sp, ppi, sid, ssn, tsn, ord, more, dp))) goto free_error; if (ord) { if (!(st->n.more = more)) st->ssn = ssn; } else { st->x.more = more; } sp->r_ack++; newd++; continue; } if (!(db = allocb(sizeof(*cb), BPRI_MED))) goto free_nobufs; gap = &sp->gaps; cb = (sctp_tcb_t *) db->b_rptr; db->b_datap->db_type = M_CTL; bzero(db->b_wptr, sizeof(*cb)); db->b_cont = dp; cb->dlen = dlen; cb->when = jiffies; cb->tsn = tsn; cb->sid = sid; cb->ssn = ssn; cb->ppi = ppi; cb->flags = flags & 0x7; cb->daddr = sp->caddr; cb->mp = db; cb->st = st; cb->tail = cb; cb->head = cb; usual(sp->caddr); if (after(tsn, sp->r_ack)) { for (; (*gap); gap = &((*gap)->tail->next)) { if (between(tsn, (*gap)->tsn, (*gap)->tail->tsn)) goto sctp_recv_data_duplicate; if (before(tsn, (*gap)->tsn)) { /* insert in front of gap */ bufq_insert(&sp->oooq, (*gap)->mp, db); cb->next = (*gap); (*gap) = cb; sp->ngaps++; } else if (tsn == (*gap)->tail->tsn + 1) { /* expand at end of gap */ bufq_queue(&sp->oooq, db); cb->next = (*gap)->tail->next; (*gap)->tail->next = cb; cb->head = (*gap); (*gap)->tail = cb; } else continue; if (cb->next && cb->next->tsn == tsn + 1) { /* join two gaps */ cb->next->tail->head = (*gap); (*gap)->tail = cb->next->tail; usual(sp->ngaps); sp->ngaps--; } break; } if (!(*gap)) { /* append to list */ bufq_queue(&sp->oooq, db); cb->next = (*gap); (*gap) = cb; sp->ngaps++; } sp->nunds++; /* more undelivered data */ newd++; } else { sctp_recv_data_duplicate: /* message is a duplicate tsn */ bufq_queue(&sp->dupq, db); cb->next = sp->dups; sp->dups = cb; sp->ndups++; } continue; free_error: freemsg(dp); rare(); break; free_nobufs: freemsg(dp); rare(); err = -ENOBUFS; break; /* couldn't allocate buffer */ enobufs: rare(); err = -ENOBUFS; break; /* couldn't allocate buffer */ enomem: rare(); err = -ENOMEM; break; /* couldn't allocate stream */ ebusy: rare(); err = -EBUSY; break; /* flow controlled (discard) */ badsid: rare(); sctp_send_error(sp, SCTP_CAUSE_INVALID_STR, &m->sid, sizeof(m->sid)); continue; /* just skip that DATA chunk */ baddata: rare(); /* RFC 2960 6.2: ...no user data... */ if ((1 << sp->s_state) & (SCTPF_HAVEUSER)) { if ((err = sp->ops->sctp_discon_ind(sp, SCTP_ORIG_PROVIDER, SCTP_CAUSE_NO_DATA, NULL))) break; if ((1 << sp->s_state) & (SCTPF_NEEDABORT)) { sctp_send_abort_error(sp, SCTP_CAUSE_NO_DATA, &m->tsn, sizeof(m->tsn)); } else rare(); sctp_disconnect(sp); return (-EPROTO); } else rare(); eproto: rare(); err = -EPROTO; break; /* non-data chunk after data */ emsgsize: rare(); err = -EMSGSIZE; break; /* bad message or chunk sizes */ } if (newd) { /* we have underlivered data and new data */ sctp_tcb_t *cb, *cb_next = sp->gaps; /* try to deliver undelivered data now */ while ((cb = cb_next)) { cb_next = cb->next; if (!(cb->flags & SCTPCB_FLAG_DELIV)) { mblk_t *db; sctp_strm_t *st = cb->st; uint flags = cb->flags; int ord = !(flags & SCTPCB_FLAG_URG); int more = !(flags & SCTPCB_FLAG_LAST_FRAG); int frag = !(flags & SCTPCB_FLAG_FIRST_FRAG); if (after(cb->tsn, sp->r_ack + 1)) { /* after gap */ if (frag) continue; if (ord) { if (st->n.more) continue; if (cb->ssn != ((st->ssn + 1) & 0xffff)) continue; } else { if (st->x.more) continue; if (more) continue; } } if ((db = dupb(cb->mp))) { db->b_cont = cb->mp->b_cont; cb->mp->b_cont = NULL; bufq_queue(&sp->rcvq, db); cb->flags |= SCTPCB_FLAG_DELIV; if (ord) { if (!(st->n.more = more)) st->ssn = cb->ssn; } else { st->x.more = more; } sp->nunds--; } else { rare(); break; } /* no buffers */ } if (cb->tsn == sp->r_ack + 1) { sp->r_ack++; sp->sackf |= SCTP_SACKF_NOD; } if ((cb->flags & SCTPCB_FLAG_DELIV) && !after(cb->tsn, sp->r_ack)) { assure(sp->gaps == cb); if (!(sp->gaps = cb->next)) sp->ngaps = 0; else { if (cb == cb->tail) sp->ngaps--; else { cb->next->tail = cb->tail; cb->tail->head = cb->next; } } freemsg(bufq_unlink(&sp->oooq, cb->mp)); } } } /* RFC 2960 6.2 */ if (sp->ndups) { /* * IMPLEMENTATION NOTE:- If we are receiving duplicates the * probability is high that our SACKs aren't getting through * (or have been delayed too long). If we do not have a sack * pending (one being delayed) then we will peg the * duplicates against the destination. This will change * where we are sending SACKs. * * Because we are being cruel to this destination and we * don't really know that this is where the offending SACKs * were sent, we send an immediate heartbeat if there is no * data is in flight to the destination (i.e., no * retransmission timer running for the destination). This * fixes some sticky problems when one-way data is being * sent. */ sctp_daddr_t *sd = sp->taddr; sp->sackf |= SCTP_SACKF_DUP; if (sd && !(sp->sackf & SCTP_SACKF_NEW)) { sd->dups += sp->ndups; if (!sd->in_flight) { if (sd->timer_idle) untimeout(xchg(&sd->timer_idle, 0)); sctp_send_heartbeat(sp, sd); } } } /* RFC 2960 7.2.4 */ if (sp->ngaps) { sp->sackf |= SCTP_SACKF_GAP; } /* RFC 2960 6.2 */ if (newd) { sp->sackf += ((sp->sackf & 0x3) < 3) ? SCTP_SACKF_NEW : 0; /* * IMPLEMENTATION NOTE:- The SACK timer is probably too * slow. For unidirectional operation, the sender may have * timed out before we send a sack. We should really not * wait any longer than some fraction of the RTO for the * destination from which we are receiving (or sending) data. * However, if we wait too long we will just get a * retransmission and a dup. * * If the sack delay is set to zero, we do not set the timer, * but issue the sack immediately. */ if (!sp->timer_sack) { if (sp->max_sack > 0) set_timeout(sp, &sp->timer_sack, &sctp_sack_timeout, sp, sp->max_sack); else sp->sackf |= SCTP_SACKF_NOD; } } if (sp->s_state == SCTP_SHUTDOWN_SENT) sctp_send_shutdown(sp); done: /* * We should not break with ENOBUFS or ENOMEM or EBUSY * here... I'm not sure that we have left the buffer in a * state where it can be put back on the queue and processed * later. */ switch (err) { case -ENOBUFS: case -EAGAIN: case -ENOMEM: case -EBUSY: /* SCTP Implementor's Guide Section 2.15.1 */ sp->sackf |= SCTP_SACKF_NOD; err = 0; } return (err); outstate: /* * We have received DATA in the wrong s_state. If so, it is probably * an old packet that was stuck in the network and just got delivered * to us. Nevertheless we should just ignore any message containing * DATA when we are not expecting it. The only exception to this * might be receiving DATA in the COOKIE-WAIT s_state. There should * not be data hanging around in the network that matches our tags. * If that is the case, we should abandon the connection attempt and * let the user try again with a different verification tag. */ rare(); if (sp->s_state == SCTP_COOKIE_WAIT) { rare(); ensure(sp->ops->sctp_discon_ind, return (-EFAULT)); if (!(err = sp->ops->sctp_discon_ind(sp, SCTP_ORIG_PROVIDER, -EPROTO, NULL))) sctp_disconnect(sp); err = -EPROTO; } goto done; } /* * CLEANUP READ * ------------------------------------------------------------------------- * This is called to clean up the read queue by the STREAMS read service * routine. This permits backenabling to work. */ void sctp_cleanup_read(sp) sctp_t *sp; { mblk_t *mp; assert(sp); while ((mp = bufq_head(&sp->ackq))) { sctp_tcb_t *cb = SCTP_TCB(mp); if (!sp->ops->sctp_datack_ind(sp, cb->ppi, cb->sid, cb->ssn, cb->tsn)) { mp = bufq_dequeue(&sp->ackq); freemsg(mp); continue; } seldom(); break; /* error on delivery (ENOBUFS, EBUSY) */ } if (bufq_head(&sp->rcvq)) { int need_sack = (sp->a_rwnd <= bufq_size(&sp->oooq) + bufq_size(&sp->dupq) + bufq_size(&sp->rcvq)); while ((mp = bufq_head(&sp->rcvq))) { sctp_tcb_t *cb = SCTP_TCB(mp); sctp_strm_t *st = cb->st; int ord = !(cb->flags & SCTPCB_FLAG_URG); int more = !(cb->flags & SCTPCB_FLAG_LAST_FRAG); ensure(st, return); if (!sp->ops-> sctp_data_ind(sp, cb->ppi, cb->sid, cb->ssn, cb->tsn, ord, more, mp->b_cont)) { mp = bufq_dequeue(&sp->rcvq); mp->b_cont = NULL; freemsg(mp); if (ord) { if (!(st->n.more = more)) st->ssn = cb->ssn; } else st->x.more = more; if (!need_sack) continue; /* * Should really do SWS here. */ sp->sackf |= SCTP_SACKF_NOD; need_sack = 0; continue; } seldom(); break; /* error on delivery (ENOBUFS, EBUSY) */ } } } /* * RECV SACK * ------------------------------------------------------------------------- */ STATIC int sctp_recv_sack(sp, mp) sctp_t *sp; mblk_t *mp; { assert(sp); assert(mp); if ((1 << sp->s_state) & (SCTPF_SENDING)) { mblk_t *dp; struct sctp_sack *m = (struct sctp_sack *) mp->b_rptr; uint32_t ack = htonl(m->c_tsn); uint32_t rwnd = htonl(m->a_rwnd); size_t ngaps = htons(m->ngaps); size_t ndups = htons(m->ndups); uint16_t *gap = m->gaps; /* RFC 2960 6.2.1 (D) i) */ if (before(ack, sp->t_ack)) { rare(); return 0; } /* * If the receive window is increasing and we have data in the write * queue, we might need to backenable. */ if (rwnd > sp->p_rwnd && sp->wq->q_count) qenable(sp->wq); /* RFC 2960 6.2.1 (D) ii) */ sp->p_rwnd = rwnd; /* we keep in_flight separate from a_rwnd */ /* * advance the cummulative ack point and check need to perform * per-round-trip and cwnd calcs */ sctp_cumm_ack(sp, ack); if (ndups) { // sp->sackf |= SCTP_SACKF_DUP; /* * TODO: we could look through the list of duplicate TSNs. * Duplicate TSNs really means that the peer's SACKs aren't * getting back to us. But there is nothing really that we * can do about that. The peer has already detected the * problem and should sent SACKs via an alternative route if * possible. But that's how this SACK go here... */ } /* process gap acks */ if (!ngaps) { /* perform fast retransmission algorithm on missing TSNs */ for (dp = bufq_head(&sp->rtxq); dp; dp = dp->b_next) { sctp_tcb_t *cb = SCTP_TCB(dp); /* RFC 2960 7.2.4 */ if (!(cb->flags & SCTPCB_FLAG_RETRANS) && ++(cb->sacks) >= 4) { size_t dlen = cb->dlen; sctp_daddr_t *sd = cb->daddr; /* RFC 2960 7.2.4 (1) */ cb->flags |= SCTPCB_FLAG_RETRANS; sp->nrtxs++; cb->sacks = 0; /* RFC 2960 7.2.4 (2) */ sd->flags |= SCTP_DESTF_DROP; /* credit destination (now) */ normal(sd->in_flight >= dlen); sd->in_flight = sd->in_flight > dlen ? sd->in_flight - dlen : 0; /* credit association (now) */ normal(sp->in_flight >= dlen); sp->in_flight = sp->in_flight > dlen ? sp->in_flight - dlen : 0; } /* RFC 2960 6.3.2 (R4) *//* reneg */ if (cb->flags & SCTPCB_FLAG_SACKED) { sctp_daddr_t *sd = cb->daddr; cb->flags &= ~SCTPCB_FLAG_SACKED; if (!sd->timer_retrans) { set_timeout(sp, &sd->timer_retrans, &sctp_retrans_timeout, sd, sd->rto); } else seldom(); } } } else { /* perform fast retransmission algorithm on gaps */ while (ngaps--) { uint32_t beg = ack + ntohs(*gap++); uint32_t end = ack + ntohs(*gap++); if (before(end, beg)) { rare(); continue; } /* move to the acks */ dp = bufq_head(&sp->rtxq); for (; dp && before(SCTP_TCB(dp)->tsn, beg); dp = dp->b_next) SCTP_TCB(dp)->flags |= SCTPCB_FLAG_NACK; /* sack the acks */ for (; dp && !after(SCTP_TCB(dp)->tsn, end); dp = dp->b_next) SCTP_TCB(dp)->flags |= SCTPCB_FLAG_ACK; } /* walk the whole retrans buffer looking for holes and renegs */ for (dp = bufq_head(&sp->rtxq); dp; dp = dp->b_next) { sctp_tcb_t *cb = SCTP_TCB(dp); /* msg is inside gapack block */ if (cb->flags & SCTPCB_FLAG_ACK) { cb->flags &= ~SCTPCB_FLAG_ACK; cb->flags &= ~SCTPCB_FLAG_NACK; if (!(cb->flags & SCTPCB_FLAG_SACKED)) { sctp_daddr_t *sd = cb->daddr; cb->flags |= SCTPCB_FLAG_SACKED; /* RFC 2960 6.3.1 (C5) */ if (cb->trans < 2) { /* remember latest transmitted packet acked for rtt calc */ sd->when = sd->when > cb->when ? sd->when : cb->when; } if (cb->flags & SCTPCB_FLAG_RETRANS) { cb->flags &= ~SCTPCB_FLAG_RETRANS; sp->nrtxs--; } else { size_t dlen = cb->dlen; /* credit destination */ normal(sd->in_flight >= dlen); sd->in_flight = sd->in_flight > dlen ? sd->in_flight - dlen : 0; /* credit association */ normal(sp->in_flight >= dlen); sp->in_flight = sp->in_flight > dlen ? sp->in_flight - dlen : 0; } } continue; } /* msg is between gapack blocks */ if (cb->flags & SCTPCB_FLAG_NACK) { cb->flags &= ~SCTPCB_FLAG_NACK; /* RFC 2960 7.2.4 */ if (!(cb->flags & SCTPCB_FLAG_RETRANS) && ++(cb->sacks) >= 4) { size_t dlen = cb->dlen; sctp_daddr_t *sd = cb->daddr; /* RFC 2960 7.2.4 (1) */ cb->flags |= SCTPCB_FLAG_RETRANS; sp->nrtxs++; cb->sacks = 0; /* RFC 2960 7.2.4 (2) */ sd->flags |= SCTP_DESTF_DROP; /* credit destination (now) */ normal(sd->in_flight >= dlen); sd->in_flight = sd->in_flight > dlen ? sd->in_flight - dlen : 0; /* credit association (now) */ normal(sp->in_flight >= dlen); sp->in_flight = sp->in_flight > dlen ? sp->in_flight - dlen : 0; } /* RFC 2960 6.3.2 (R4) *//* reneg */ if (cb->flags & SCTPCB_FLAG_SACKED) { sctp_daddr_t *sd = cb->daddr; cb->flags &= ~SCTPCB_FLAG_SACKED; if (!sd->timer_retrans) { set_timeout(sp, &sd->timer_retrans, &sctp_retrans_timeout, sd, sd->rto); } else seldom(); } continue; } /* msg is after all gapack blocks */ break; } } sctp_dest_calc(sp); if (((1 << sp-> s_state) & (SCTPF_SHUTDOWN_PENDING | SCTPF_SHUTDOWN_RECEIVED | SCTPF_SHUTDOWN_RECVWAIT)) && !bufq_head(&sp->sndq) && !bufq_head(&sp->rtxq)) { seldom(); /* * After receiving a cummulative ack, I want to check if the * sndq and rtxq is empty and a SHUTDOWN or SHUTDOWN-ACK is * pending. If so, I want to issue these primitives. */ switch (sp->s_state) { case SCTP_SHUTDOWN_PENDING: /* Send the SHUTDOWN I didn't send before. */ sctp_send_shutdown(sp); break; case SCTP_SHUTDOWN_RECEIVED: /* Send the SHUTDOWN-ACK I didn't send before */ if (!sp->ops->sctp_ordrel_ind) sctp_send_shutdown_ack(sp); break; case SCTP_SHUTDOWN_RECVWAIT: /* Send the SHUTDOWN-ACK I didn't send before */ sctp_send_shutdown_ack(sp); break; default: never(); break; } } } else { rare(); /* * We may have received a SACK in the wrong s_state. Because * SACKs are completely advisory, there is no reason to get * too upset about this. Simply ignore them. No need to * process them. */ } { int ret; if ((ret = sctp_return_more(mp)) > 0) { struct sctpchdr *ch = (struct sctpchdr *) mp->b_rptr; switch (ch->type) { /* RFC 2960 6 */ case SCTP_CTYPE_DATA: /* RFC 2960 6.5 */ case SCTP_CTYPE_ERROR: /* RFC 2960 3.3.7 */ case SCTP_CTYPE_ABORT: break; default: rare(); return (-EPROTO); } } return (ret); } } /* * RECV ERROR * ------------------------------------------------------------------------- * We have received an ERROR chunk in opening, connected or closing states. */ STATIC int sctp_recv_error(sp, mp) sctp_t *sp; mblk_t *mp; { int err; struct sctp_error *m; struct sctpehdr *eh; int ecode; assert(sp); assert(mp); seldom(); m = (struct sctp_error *) mp->b_rptr; eh = (struct sctpehdr *) (m + 1); ecode = ntohs(eh->code); switch (ecode) { case SCTP_CAUSE_STALE_COOKIE: if (sp->s_state == SCTP_COOKIE_ECHOED) { sctp_tcb_t *cb; sctp_daddr_t *sd; assert(sp->retry); cb = SCTP_TCB(sp->retry); sd = cb->daddr; assert(sd); seldom(); /* * We can try again with cookie preservative, * and then we can keep trying until we have * tried as many times as we can... */ if (!sp->ck_inc) { int err; rare(); sp->ck_inc = sp->ck_inc + (sd->rto >> 1); sctp_ack_calc(sp, &sp->timer_init); if ((err = sctp_send_init(sp))) { rare(); return (err); } return sctp_return_stop(mp); } /* RFC 2960 5.2.6 (1) */ if (cb->trans < sp->max_inits) { untimeout(xchg(&sp->timer_init, 0)); /* RFC 2960 5.2.6 (3) */ if (cb->trans < 2) sctp_rtt_calc(sd, cb->when); usual(sp->retry); sctp_send_msg(sp, sd, sp->retry); return sctp_return_stop(mp); } /* RFC 2960 5.2.6 (2) */ goto recv_error_error; } break; case SCTP_CAUSE_INVALID_PARM: case SCTP_CAUSE_BAD_ADDRESS: /* * If the sender of the ERROR has already given us a valid * INIT-ACK then we can ignore these errors. */ if (sp->s_state == SCTP_COOKIE_ECHOED) { break; } seldom(); case SCTP_CAUSE_MISSING_PARM: case SCTP_CAUSE_NO_RESOURCE: case SCTP_CAUSE_INVALID_STR: /* * These errors are bad. If we don't get an abort with them * then we must abort the association. */ recv_error_error: if (!((1 << sp->s_state) & (SCTPF_HAVEUSER))) { ensure(sp->ops->sctp_discon_ind, return (-EFAULT)); if ((err = sp->ops->sctp_discon_ind(sp, SCTP_ORIG_PROVIDER, ecode, NULL))) return (err); } else rare(); if ((1 << sp->s_state) & (SCTPF_NEEDABORT)) { sctp_send_abort(sp); } else rare(); sctp_disconnect(sp); return sctp_return_stop(mp); default: case SCTP_CAUSE_BAD_CHUNK_TYPE: case SCTP_CAUSE_BAD_PARM: case SCTP_CAUSE_NO_DATA: case SCTP_CAUSE_SHUTDOWN: rare(); break; } return sctp_return_more(mp); /* ignore */ } /* * RECV ABORT * ------------------------------------------------------------------------- * We have received an ABORT chunk in opening, connected or closing states. * If there is a user around we want to send a disconnect indication, * otherwise we want to just go away. */ STATIC int sctp_recv_abort(sp, mp) sctp_t *sp; mblk_t *mp; { int err; struct sctp_abort *m; ulong orig; assert(sp); assert(mp); m = (struct sctp_abort *) mp->b_rptr; orig = (m->ch.flags & 0x1) ? SCTP_ORIG_PROVIDER : SCTP_ORIG_USER; if (sp->s_state == SCTP_LISTEN) { struct iphdr *iph; struct sctphdr *sh; mblk_t *cp; struct sctp_cookie *ck; struct sctp_cookie_echo *ce; seldom(); iph = (struct iphdr *) mp->b_datap->db_base; sh = (struct sctphdr *) (mp->b_datap->db_base + (iph->ihl << 2)); for (cp = bufq_head(&sp->conq); cp; cp = cp->b_next) { ce = (struct sctp_cookie_echo *) cp->b_rptr; ck = (struct sctp_cookie *) ce->cookie; if (ck->v_tag == sh->v_tag && ck->sport == sh->dest && ck->dport == sh->srce && ck->saddr == iph->daddr && ck->daddr == iph->saddr) { ensure(sp->ops->sctp_discon_ind, return (-EFAULT)); if ((err = sp->ops->sctp_discon_ind(sp, orig, 0, cp))) { rare(); return (err); } return sctp_return_stop(mp); } } usual(cp); return (-EPROTO); /* discard it */ } if ((1 << sp->s_state) & (SCTPF_HAVEUSER)) { ensure(sp->ops->sctp_discon_ind, return (-EFAULT)); if ((err = sp->ops->sctp_discon_ind(sp, orig, 0, NULL))) /* XXX reason */ return (err); } else seldom(); sctp_disconnect(sp); return sctp_return_stop(mp); } /* * RECV INIT (Listener only) * ------------------------------------------------------------------------- * We have receive an INIT in the LISTEN s_state. This is the normal path for * the establishment of an SCTP association. There can be no other stream * bound to this local port but we can have accepted streams which share the * same local binding. * * INIT chunks cannot have other chunks bundled with them (RFC 2960 6.10). */ STATIC int sctp_recv_init(sp, mp) sctp_t *sp; mblk_t *mp; { struct iphdr *iph; struct sctphdr *sh; struct sctp_init *m; sctp_t *oldsp; union sctp_parm *ph; unsigned char *pptr, *pend; size_t plen; size_t anum = 0; ulong ck_inc = 0; int err = 0; size_t errl = 0; unsigned char *errp = NULL; struct sctp_cookie ck; assert(sp); assert(mp); iph = (struct iphdr *) mp->b_datap->db_base; sh = (struct sctphdr *) (mp->b_datap->db_base + (iph->ihl << 2)); m = (struct sctp_init *) mp->b_rptr; pptr = (unsigned char *) (m + 1); pend = pptr + ntohs(m->ch.len) - sizeof(*m); if (mp->b_wptr - mp->b_rptr < sizeof(*m)) { rare(); return (-EFAULT); } /* RFC 2960 p.26 initiate tag zero */ if (!m->i_tag) { err = -SCTP_CAUSE_INVALID_PARM; seldom(); goto init_error; } for (ph = (union sctp_parm *) pptr; pptr + sizeof(ph->ph) <= pend && pptr + (plen = ntohs(ph->ph.len)) <= pend; pptr += PADC(plen), ph = (union sctp_parm *) pptr) { uint type; switch ((type = ph->ph.type)) { case SCTP_PTYPE_IPV6_ADDR: case SCTP_PTYPE_HOST_NAME: errp = pptr; errl = plen; err = -SCTP_CAUSE_BAD_ADDRESS; seldom(); goto init_error; case SCTP_PTYPE_COOKIE_PSRV: if (plen == sizeof(ph->cookie_prsv)) { ck_inc = ntohl(ph->cookie_prsv.ck_inc); break; } goto init_bad_parm; case SCTP_PTYPE_IPV4_ADDR: if (plen == sizeof(ph->ipv4_addr)) { /* skip primary */ if (ph->ipv4_addr.addr != iph->saddr) anum++; break; } goto init_bad_parm; case SCTP_PTYPE_ADDR_TYPE: /* * Ensure that address types supported includes IPv4. * Actually address types must include IPv4 so we * just ignore. */ break; init_bad_parm: default: rare(); if (type & SCTP_PTYPE_MASK_REPORT) { err = -SCTP_CAUSE_BAD_PARM; errp = pptr; errl = plen; } if (!(type & SCTP_PTYPE_MASK_CONTINUE)) rare(); goto init_error; } } /* put together cookie */ { /* negotiate inbound and outbound streams */ size_t istrs = sp->n_istr; size_t ostrs = sp->n_ostr; istrs = ((istrs && istrs <= ntohs(m->n_istr))) ? istrs : ntohs(m->n_istr); ostrs = ((ostrs && ostrs <= ntohs(m->n_ostr))) ? ostrs : ntohs(m->n_ostr); errl = 0; errp = NULL; err = -SCTP_CAUSE_INVALID_PARM; if (m->n_istr && !istrs) { seldom(); goto init_error; } if (m->n_ostr && !ostrs) { seldom(); goto init_error; } /* RFC 2969 5.2.6 */ if (ck_inc) { seldom(); ck_inc = (ck_inc * HZ + 999) / 1000; usual(ck_inc); if (ck_inc > sp->rto_ini + sp->ck_inc) { rare(); ck_inc = sp->rto_ini + sp->ck_inc; } } ck.timestamp = jiffies; ck.lifespan = sp->ck_life + ck_inc; ck.daddr = iph->saddr; ck.saddr = iph->daddr; ck.dport = sh->srce; ck.sport = sh->dest; ck.v_tag = sctp_get_vtag(ck.daddr, ck.saddr, ck.dport, ck.sport); ck.p_tag = m->i_tag; ck.p_tsn = ntohl(m->i_tsn); ck.p_rwnd = ntohl(m->a_rwnd); ck.n_istr = istrs; ck.n_ostr = ostrs; ck.danum = anum; } /* RFC 2960 5.2.2 Note */ SCTPHASH_RLOCK(); oldsp = sctp_lookup_tcb(sh->dest, sh->srce, iph->daddr, iph->saddr); SCTPHASH_RUNLOCK(); if (oldsp && oldsp->s_state != SCTP_COOKIE_WAIT) { rare(); ck.l_ttag = oldsp->v_tag; ck.p_ttag = oldsp->p_tag; } else { ck.l_ttag = 0; ck.p_ttag = 0; } ck.sanum = sp->sanum - 1; /* don't include primary */ ck.opt_len = 0; sctp_send_init_ack(sp, iph->saddr, sh, &ck); return (0); init_error: seldom(); if (err) { rare(); sctp_send_abort_error_ootb(iph->saddr, iph->daddr, sh, -err, errp, errl); } return (0); } /* * RECV INIT ACK * ------------------------------------------------------------------------- * We have recevied an INIT ACK in the SCTP_COOKIE_WAIT (TS_WCON_CREQ) s_state. * (RFC 2960 5.2.3) */ STATIC int sctp_recv_init_ack(sp, mp) sctp_t *sp; mblk_t *mp; { int err; int reason; assert(sp); assert(mp); if (sp->s_state == SCTP_COOKIE_WAIT) { struct iphdr *iph = (struct iphdr *) mp->b_datap->db_base; struct sctp_init_ack *m = (struct sctp_init_ack *) mp->b_rptr; if (sctp_daddr_include(sp, iph->saddr, &err)) { unsigned char *kptr = NULL; size_t klen = 0; unsigned char *pptr = (unsigned char *) (m + 1); unsigned char *pend = pptr + ntohs(m->ch.len) - sizeof(*m); size_t plen; struct sctpphdr *ph = (struct sctpphdr *) pptr; ensure((pend <= mp->b_wptr), return (-EMSGSIZE)); { size_t ostr = htons(m->n_ostr); size_t istr = htons(m->n_istr); if (!ostr || !istr || istr > sp->max_istr) { err = -EPROTO; goto sctp_recv_init_ack_error; } sp->p_tag = m->i_tag; sp->p_rwnd = ntohl(m->a_rwnd); sp->n_ostr = ostr; sp->n_istr = istr; sp->r_ack = ntohl(m->i_tsn) - 1; } for (ph = (struct sctpphdr *) pptr; pptr + sizeof(*ph) <= pend && pptr + (plen = ntohs(ph->len)) <= pend; pptr += PADC(plen), ph = (struct sctpphdr *) pptr) { uint type; switch ((type = ph->type)) { case SCTP_PTYPE_IPV4_ADDR: if (!sctp_daddr_include(sp, *((uint32_t *) (ph + 1)), &err)) { rare(); sctp_send_error(sp, SCTP_CAUSE_BAD_ADDRESS, ph, plen); } continue; case SCTP_PTYPE_STATE_COOKIE: kptr = pptr + sizeof(*ph); klen = plen - sizeof(*ph); continue; case SCTP_PTYPE_IPV6_ADDR: case SCTP_PTYPE_HOST_NAME: rare(); sctp_send_error(sp, SCTP_CAUSE_BAD_ADDRESS, ph, plen); continue; case SCTP_PTYPE_UNREC_PARMS: default: rare(); reason = SCTP_CAUSE_BAD_PARM; if (type & SCTP_PTYPE_MASK_REPORT) sctp_send_error(sp, reason, ph, plen); if (type & SCTP_PTYPE_MASK_CONTINUE) continue; break; } } if (!kptr) { /* no cookie? */ struct { uint32_t num; uint16_t mparm; } es = { 1, SCTP_PTYPE_STATE_COOKIE}; rare(); reason = SCTP_CAUSE_MISSING_PARM; sctp_send_error(sp, reason, &es, sizeof(es)); return (-EPROTO); } sctp_ack_calc(sp, &sp->timer_init); sctp_phash_rehash(sp); if ((err = sctp_send_cookie_echo(sp, kptr, klen))) { rare(); return (err); } return sctp_return_stop(mp); } rare(); return (err); /* fall back on timer init */ } rare(); return (-EPROTO); sctp_recv_init_ack_error: rare(); if ((1 << sp->s_state) & (SCTPF_HAVEUSER)) { ensure(sp->ops->sctp_discon_ind, return (-EFAULT)); if ((err = sp->ops->sctp_discon_ind(sp, SCTP_ORIG_PROVIDER, err, NULL))) { rare(); return (err); } } else rare(); sctp_disconnect(sp); /* we won't be back */ return (err); } /* * RECV COOKIE ECHO * ------------------------------------------------------------------------- * We have received a COOKIE ECHO for a STREAM. We have already determined * the STREAM to which the COOKIE ECHO applies. We must still verify the * cookie and apply the appropriate action per RFC 2960 5.2.4. */ STATIC int sctp_recv_cookie_echo(sp, mp) sctp_t *sp; mblk_t *mp; { int err; struct iphdr *iph; struct sctphdr *sh; struct sctp_cookie_echo *m; struct sctp_cookie *ck; assert(sp); assert(mp); iph = (struct iphdr *) mp->b_datap->db_base; sh = (struct sctphdr *) (mp->b_datap->db_base + (iph->ihl << 2)); m = (struct sctp_cookie_echo *) mp->b_rptr; ck = (struct sctp_cookie *) m->cookie; /* RFC 2960 5.2.4 (1) & (2) */ if ((err = sctp_verify_cookie(sp, ck))) { rare(); return (err); } if (sp->v_tag) { if (ck->v_tag != sp->v_tag) { if (ck->p_tag != sp->p_tag) { if (ck->l_ttag == sp->v_tag && ck->p_ttag == sp->p_tag) /* RFC 2960 5.2.4. Action (A) */ goto recv_cookie_echo_action_a; } else if (ck->l_ttag == 0 && ck->p_ttag == 0) /* RFC 2960 5.2.4. Action (C). */ goto recv_cookie_echo_action_c; } else { if (!sp->p_tag || ((1 << sp->s_state) & (SCTPF_COOKIE_WAIT | SCTPF_COOKIE_ECHOED))) /* RFC 2960 5.2.4 Action (B). */ goto recv_cookie_echo_action_b; else if (ck->p_tag != sp->p_tag) /* RFC 2960 5.2.4 Action (B) */ goto recv_cookie_echo_action_b; else /* RFC 2960 5.2.4 Action (D). */ goto recv_cookie_echo_action_d; } } else /* RFC 2960 5.2.4 Action (D). */ goto recv_cookie_echo_action_d; rare(); /* RFC 2960 5.2.4 ...silently discarded */ return (0); recv_cookie_echo_action_a: rare(); /* * RFC 2960 5.2.4 Action (A) * * In this case, the peer may have restarted. When the endpoint * recognizes this potential 'restart', the existing session is * treated the same as if it received an ABORT followed by a new * COOKIED ECHO with the following exceptions: - Any SCTP DATA * Chunks MAY be retained (this is an implementation specific * option). - A notification of RESTART SHOULD be sent to the ULP * instead of "COMMUNICATION LOST" notification. * * All the Congestion control parameters (e.g., cwnd, ssthresh) * related to this peer MUST be reset to their initial values (see * Section 6.2.1). After this the endpoint shall enter the * ESTABLISHED s_state. */ switch (sp->s_state) { case SCTP_SHUTDOWN_ACK_SENT: rare(); /* RFC 2960 5.2.4 (A) */ sctp_send_abort_error(sp, SCTP_CAUSE_SHUTDOWN, NULL, 0); sctp_send_shutdown_ack(sp); return sctp_return_stop(mp); case SCTP_ESTABLISHED: case SCTP_SHUTDOWN_PENDING: case SCTP_SHUTDOWN_SENT: case SCTP_SHUTDOWN_RECEIVED: case SCTP_SHUTDOWN_RECVWAIT: rare(); /* * We trash all existing data in queue. */ sctp_disconnect(sp); /* * Notify user of reset or disconnect */ if (sp->ops->sctp_reset_ind) { fixme(("Need reason argument to reset ind\n")); if ((err = sp->ops->sctp_reset_ind(sp, SCTP_ORIG_USER, 0, mp))) { /* XXX reason */ rare(); return (err); } return (0); } if ((1 << sp->s_state) & (SCTPF_HAVEUSER)) { fixme(("Need reason argument to discon ind\n")); if ((err = sp->ops->sctp_discon_ind(sp, SCTP_ORIG_USER, 0, NULL))) { /* XXX reason */ rare(); return (err); } } else rare(); goto recv_cookie_echo_conn_ind; default: rare(); return (-EFAULT); } never(); recv_cookie_echo_action_b: rare(); /* * RFC 2960 5.2.4 Action (B) * * In this case, both sides may be attempting to start and * association at about the same time but the peer endpoint started * its INIT after responding to the local endpoint's INIT. Thus it * may have picked a new Verification Tag not being aware of the * previous Tag it had sent this endpoint. The endpoint should stay * in or enter the ESTABLISHED s_state but it MUST update its peer's * Verification Tag from the State Cookie, stop any init or cookie * timers that may be running and send a COOKIE ACK. */ switch (sp->s_state) { case SCTP_COOKIE_WAIT: if (sp->timer_init) { rare(); untimeout(xchg(&sp->timer_init, 0)); } /* * We haven't got an INIT ACK yet so we need some * stuff from the cookie */ sp->p_rwnd = ck->p_rwnd; sp->r_ack = ck->p_tsn - 1; sp->n_istr = ck->n_istr; sp->n_ostr = ck->n_ostr; ensure(sp->ops->sctp_conn_con, return (-EFAULT)); if ((err = sp->ops->sctp_conn_con(sp))) { rare(); return (err); } break; case SCTP_COOKIE_ECHOED: if (sp->timer_cookie) { rare(); untimeout(xchg(&sp->timer_cookie, 0)); } break; case SCTP_ESTABLISHED: break; default: rare(); return (-EFAULT); } sp->p_tag = ck->p_tag; sctp_phash_rehash(sp); sctp_send_sack(sp); sctp_send_cookie_ack(sp); return sctp_return_more(mp); recv_cookie_echo_action_c: rare(); /* * RFC 2960 5.2.4 Action (C) * * In this case, the local endpoint's cookie has arrived late. * Before it arrived, the local sendpoint sent an INIT and receive an * INIT-ACK and finally sent a COOKIE ECHO with the peer's same tag * but a new tag of its own. The cookie should be silently * discarded. The endpoint SHOULD NOT change states and should leave * any timers running. */ /* RFC 2960 5.2.4 (3) */ if (jiffies - ck->timestamp > ck->lifespan) { uint32_t staleness; rare(); staleness = htonl((jiffies - ck->timestamp - ck->lifespan) * HZ / 1000000); sctp_send_abort_error_ootb(iph->saddr, iph->daddr, sh, SCTP_CAUSE_STALE_COOKIE, &staleness, sizeof(staleness)); return (-ETIMEDOUT); } return (0); recv_cookie_echo_action_d: /* * RFC 2960 5.2.4 Action (D) * * When both local and remote tags match the endpoint should always * enter the ESTABLISHED s_state, it if has not already done so. It * should stop any init or cookie timers that may be running and send * a COOKIE ACK. */ /* RFC 2960 5.2.4 (3) */ if (jiffies - ck->timestamp > ck->lifespan) { uint32_t staleness; rare(); staleness = htonl((jiffies - ck->timestamp - ck->lifespan) * HZ / 1000000); sctp_send_abort_error_ootb(iph->saddr, iph->daddr, sh, SCTP_CAUSE_STALE_COOKIE, &staleness, sizeof(staleness)); return (-ETIMEDOUT); } recv_cookie_echo_conn_ind: if (sp->conind) { mblk_t *cp; /* check for existing conn ind */ for (cp = bufq_head(&sp->conq); cp; cp = cp->b_next) { struct sctp_cookie_echo *ce = (struct sctp_cookie_echo *) cp->b_rptr; struct sctp_cookie *co = (struct sctp_cookie *) ce->cookie; if (co->v_tag == ck->v_tag) { seldom(); return (0); } if (co->dport != ck->dport || co->sport != ck->sport) continue; if (co->daddr == ck->daddr && co->saddr == ck->saddr) { seldom(); return (0); } } /* RFC 2960 5.2.4 (4) */ if (bufq_length(&sp->conq) >= sp->conind) { seldom(); sctp_send_abort_error_ootb(iph->saddr, iph->daddr, sh, SCTP_CAUSE_NO_RESOURCE, NULL, 0); return (0); } ensure(sp->ops->sctp_conn_ind, return (-EFAULT)); if ((err = sp->ops->sctp_conn_ind(sp, mp))) { rare(); return (err); } return (1); /* leave cookie and data in the message for conn_res */ } normal(sp->s_state == SCTP_ESTABLISHED); sctp_send_cookie_ack(sp); return (0); } /* * RECV COOKIE ACK * ------------------------------------------------------------------------- * We have received a COOKIE ACK. If we are in COOKIE ECHOED s_state then we * inform the user interface that the previous connection request is * confirmed, cancel the cookie timer while performing an RTO calculation on * the message and enter the ESTABLISHED s_state. Any DATA that is bundled * with the COOKIE ACK will be separately indicated to the user with data * indications. In s_state other than COOKIE ECHOED the entire message is * silently discarded. */ STATIC int sctp_recv_cookie_ack(sp, mp) sctp_t *sp; mblk_t *mp; { int err; sctp_daddr_t *sd; assert(sp); assert(mp); switch (sp->s_state) { case SCTP_COOKIE_ECHOED: /* RFC 2960 5.1 (E) */ ensure(sp->ops->sctp_conn_con, return (-EFAULT)); if ((err = sp->ops->sctp_conn_con(sp))) { rare(); return (err); } sctp_ack_calc(sp, &sp->timer_cookie); sp->s_state = SCTP_ESTABLISHED; /* start idle timers */ usual(sp->daddr); for (sd = sp->daddr; sd; sd = sd->next) sctp_reset_idle(sd); return sctp_return_more(mp); default: /* RFC 2960 5.2.5 */ rare(); break; } return (0); /* silently discard */ } /* * RECV HEARTBEAT * ------------------------------------------------------------------------- * We have received a HEARTBEAT. Quite frankly we don't care what s_state we * are in, we take the heartbeat info and turn it back around as a HEARTBEAT * ACK msg. We do a little bit of error checking here to make sure that we * are not wasting our time on the packet. We should only receive HEARTBEAT * messages if we are in the vtag hashes: meaning that we will accept and * reply to a HEARTBEAT in any s_state other than CLOSED and LISTEN. * */ STATIC int sctp_recv_heartbeat(sp, mp) sctp_t *sp; mblk_t *mp; { struct sctp_heartbeat *m; struct sctpphdr *ph; size_t plen; assert(sp); assert(mp); m = (struct sctp_heartbeat *) mp->b_rptr; ph = (struct sctpphdr *) (m + 1); plen = PADC(htons(m->ch.len)) - sizeof(*m); if (plen >= sizeof(struct sctpphdr)) { if (ph->type == SCTP_PTYPE_HEARTBEAT_INFO) { { caddr_t hptr = (caddr_t) ph; size_t hlen = min(plen, PADC(ntohs(ph->len))); sctp_send_heartbeat_ack(sp, hptr, hlen); return sctp_return_stop(mp); } } rare(); return (-EPROTO); /* bad message */ } rare(); return (-EMSGSIZE); /* bad message */ } /* * RECV HEARTBEAT ACK * ------------------------------------------------------------------------- * This is our HEARTBEAT coming back. We check that the HEARTBEAT information matches * the information of the last sent HEARTBEAT message to ensure that no fiddling with the * HEARTBEAT info has occured. The only information we trust initially is the * destination address which is contained in the HEARTBEAT INFO. This just helps us * index the remainder of the hearbeat information. * * We have received a HEARTBEAT ACK message on an ESTABLISHED STREAM. Perform the * procedures from RFC 2960 8.3. */ STATIC int sctp_recv_heartbeat_ack(sp, mp) sctp_t *sp; mblk_t *mp; { sctp_daddr_t *sd; struct sctp_heartbeat_ack *m; struct sctp_heartbeat_info *hb; assert(sp); assert(mp); m = (struct sctp_heartbeat_ack *) mp->b_rptr; hb = (struct sctp_heartbeat_info *) (m + 1); if ((sd = sctp_find_daddr(sp, hb->hb_info.daddr))) { if (ntohs(hb->ph.len) == sizeof(*hb) + sd->hb_fill) { if (hb->ph.type == SCTP_PTYPE_HEARTBEAT_INFO) { if (sd->hb_time && sd->hb_time == hb->hb_info.timestamp) { { if (sd->timer_heartbeat) untimeout(xchg(&sd->timer_heartbeat, 0)); sctp_rtt_calc(sd, sd->hb_time); } } else rare(); /* old or fiddled timestamp */ } else rare(); /* bad hb parm type */ } else rare(); /* bad hb parm size */ } else rare(); /* bad dest parameter */ return sctp_return_stop(mp); } /* * RECV SHUTDOWN * ------------------------------------------------------------------------- */ STATIC int sctp_recv_shutdown(sp, mp) sctp_t *sp; mblk_t *mp; { int err; struct sctp_shutdown *m; uint32_t ack; assert(sp); assert(mp); m = (struct sctp_shutdown *) mp->b_rptr; ack = ntohl(m->c_tsn); if (sp->ngaps) { rare(); /* * Check sanity of sender: if we have gaps in our acks to the * peer and the peer sends a SHUTDOWN, then it is in error. * The peer cannot send SHUTDOWN when it has unacknowledged * data. If this is the case, we zap the connection. */ if ((1 << sp->s_state) & (SCTPF_HAVEUSER)) { ensure(sp->ops->sctp_discon_ind, return (-EFAULT)); if ((err = sp->ops->sctp_discon_ind(sp, SCTP_ORIG_USER, -EPIPE, NULL))) return (err); } else rare(); if ((1 << sp->s_state) & (SCTPF_NEEDABORT)) { sctp_send_abort(sp); } else rare(); sctp_disconnect(sp); return sctp_return_stop(mp); } if (before(ack, sp->t_ack)) { rare(); /* * If the SHUTDOWN acknowledges our sent data chunks that * have already been acknowledged, then it is an old (or * erroneous) message and we will ignore it. */ return sctp_return_stop(mp); } switch (sp->s_state) { case SCTP_ESTABLISHED: if (sp->ops->sctp_ordrel_ind) { if ((err = sp->ops->sctp_ordrel_ind(sp))) return (err); } else { if ((err = sp->ops->sctp_discon_ind(sp, SCTP_ORIG_USER, 0, NULL))) return (err); } sp->s_state = SCTP_SHUTDOWN_RECEIVED; /* fall thru */ case SCTP_SHUTDOWN_RECEIVED: sctp_cumm_ack(sp, ack); sctp_dest_calc(sp); if (!sp->ops->sctp_ordrel_ind) if (!bufq_head(&sp->sndq) && !bufq_head(&sp->rtxq)) sctp_send_shutdown_ack(sp); break; case SCTP_SHUTDOWN_PENDING: /* only when we have ordrel */ if ((err = sp->ops->sctp_ordrel_ind(sp))) return (err); sp->s_state = SCTP_SHUTDOWN_RECVWAIT; /* fall thru */ case SCTP_SHUTDOWN_RECVWAIT: sctp_cumm_ack(sp, ack); sctp_dest_calc(sp); if (!bufq_head(&sp->sndq) && !bufq_head(&sp->rtxq)) sctp_send_shutdown_ack(sp); break; case SCTP_SHUTDOWN_SENT: /* only when we have ordrel */ if ((err = sp->ops->sctp_ordrel_ind(sp))) return (err); sctp_cumm_ack(sp, ack); sctp_dest_calc(sp); /* faill thru */ case SCTP_SHUTDOWN_ACK_SENT: sctp_send_shutdown_ack(sp); /* We do this */ break; default: /* ignore the SHUTDOWN chunk */ rare(); break; } return sctp_return_more(mp); } /* * RECV SHUTDOWN ACK * ------------------------------------------------------------------------- * We have received a SHUTDOWN ACK chunk through the vtag hashes. We are expecting the * SHUTDOWN ACK because we have previously sent a SHUTDOWN or SHUTDOWN ACK or we ignore the * message. */ STATIC int sctp_recv_shutdown_ack(sp, mp) sctp_t *sp; mblk_t *mp; { int err; switch (sp->s_state) { case SCTP_SHUTDOWN_SENT: /* send up orderly release indication to ULP */ if (sp->ops->sctp_ordrel_ind) if ((err = sp->ops->sctp_ordrel_ind(sp))) return (err); /* fall thru */ case SCTP_SHUTDOWN_ACK_SENT: // sctp_ack_calc(sp, &sp->timer_shutdown); /* WHY? */ sctp_send_shutdown_complete(sp); sctp_disconnect(sp); break; default: /* ignore unexpected SHUTDOWN ACK */ rare(); break; } return sctp_return_stop(mp); } /* * RECV SHUTDOWN COMPLETE * ------------------------------------------------------------------------- * We have received a SHUTDOWN COMPLETE in the SHUTDOWN ACK SENT s_state. This is the * normal path for shutting down an SCTP association. Outstanding data has already been * processed. Remove ourselves from the hashes and process any backlog. RFC 2960 6.10: * SHUTDOWN COMPLETE cannot have any other chunks bundled with them. * * We receive SHUTDOWN COMPLETE chunks through the vtag hashes. We are expecting * SHUTDOWN COMPLETE because we sent a SHUTDOWN ACK. We are in the TS_IDLE, NS_IDLE * states but are still in the vtag hashes. */ STATIC int sctp_recv_shutdown_complete(sp, mp) sctp_t *sp; mblk_t *mp; { assert(sp); assert(mp); switch (sp->s_state) { case SCTP_SHUTDOWN_ACK_SENT: // sctp_ack_calc(sp, &sp->timer_shutdown); /* WHY? */ sctp_disconnect(sp); break; default: /* ignore unexpected SHUTDOWN COMPLETE */ rare(); break; } return sctp_return_stop(mp); } /* * RECV UNRECOGNIZED CHUNK TYPE * ------------------------------------------------------------------------- */ STATIC int sctp_recv_unrec_ctype(sp, mp) sctp_t *sp; mblk_t *mp; { int ctype; rare(); assert(sp); assert(mp); ctype = ((struct sctpchdr *) mp->b_rptr)->type; if (ctype & SCTP_CTYPE_MASK_REPORT) sctp_send_abort_error(sp, SCTP_CAUSE_BAD_CHUNK_TYPE, mp->b_rptr, mp->b_wptr - mp->b_rptr); if (ctype & SCTP_CTYPE_MASK_CONTINUE) return sctp_return_more(mp); return -EPROTO; } /* * RECV SCTP MESSAGE * ------------------------------------------------------------------------- */ int sctp_recv_msg(sp, mp) sctp_t *sp; mblk_t *mp; { int err = -EMSGSIZE; if (mp) { /* set the address for reply chunks */ if (sp->daddr) { sp->caddr = sctp_find_daddr(sp, ((struct iphdr *) mp->b_datap->db_base)->saddr); normal(sp->caddr); } do { int dlen = mp->b_wptr - mp->b_rptr; struct sctpchdr *ch = (struct sctpchdr *) mp->b_rptr; unusual(dlen < 0); if (dlen > 0 && dlen >= sizeof(*ch) && dlen >= PADC(ntohs(ch->len))) { switch ((ch->type & SCTP_CTYPE_MASK)) { case SCTP_CTYPE_DATA: err = sctp_recv_data(sp, mp); break; case SCTP_CTYPE_INIT: err = sctp_recv_init(sp, mp); break; case SCTP_CTYPE_INIT_ACK: err = sctp_recv_init_ack(sp, mp); break; case SCTP_CTYPE_SACK: err = sctp_recv_sack(sp, mp); break; case SCTP_CTYPE_HEARTBEAT: err = sctp_recv_heartbeat(sp, mp); break; case SCTP_CTYPE_HEARTBEAT_ACK: err = sctp_recv_heartbeat_ack(sp, mp); break; case SCTP_CTYPE_ABORT: err = sctp_recv_abort(sp, mp); break; case SCTP_CTYPE_SHUTDOWN: err = sctp_recv_shutdown(sp, mp); break; case SCTP_CTYPE_SHUTDOWN_ACK: err = sctp_recv_shutdown_ack(sp, mp); break; case SCTP_CTYPE_ERROR: err = sctp_recv_error(sp, mp); break; case SCTP_CTYPE_COOKIE_ECHO: err = sctp_recv_cookie_echo(sp, mp); break; case SCTP_CTYPE_COOKIE_ACK: err = sctp_recv_cookie_ack(sp, mp); break; case SCTP_CTYPE_ECNE: err = sctp_recv_unrec_ctype(sp, mp); break; case SCTP_CTYPE_CWR: err = sctp_recv_unrec_ctype(sp, mp); break; case SCTP_CTYPE_SHUTDOWN_COMPLETE: err = sctp_recv_shutdown_complete(sp, mp); break; default: err = sctp_recv_unrec_ctype(sp, mp); break; } normal(err >= 0); } else { rare(); err = -EMSGSIZE; } } while (err > 0 && err >= sizeof(struct sctpchdr)); } else rare(); if (err < 0) { rare(); ptrace(("sp = %x, Error %d returned.\n", (uint) sp, err)); } return (err); } /* * RECV SCTP ICMP ERROR * ------------------------------------------------------------------------- */ int sctp_recv_err(sp, mp) sctp_t *sp; mblk_t *mp; { struct sctp_daddr *sd; uint32_t daddr = *((uint32_t *) mp->b_rptr)++; struct icmphdr *icmph = (struct icmphdr *) mp->b_rptr; if ((sd = sctp_find_daddr(sp, daddr))) { int type = icmph->type; int code = icmph->code; switch (type) { case ICMP_SOURCE_QUENCH: rare(); sd->ssthresh = sd->cwnd >> 1 > sd->mtu << 1 ? sd->cwnd >> 1 : sd->mtu << 1; sd->cwnd = sd->mtu; sd->rto = sd->rto << 1 ? sd->rto << 1 : 1; sd->rto = sd->rto_min > sd->rto ? sd->rto_min : sd->rto; sd->rto = sd->rto_max < sd->rto ? sd->rto_max : sd->rto; break; case ICMP_DEST_UNREACH: { if (code > NR_ICMP_UNREACH) { rare(); return (0); } if (sd->dst_cache) { if (code == ICMP_FRAG_NEEDED) { size_t mtu = ntohs(icmph->un.frag.mtu); rare(); #ifdef ip_rt_update_pmtu ip_rt_update_pmtu(sd->dst_cache, mtu); #else /* not an exported symbol */ #ifndef ip_rt_mtu_expires #define ip_rt_mtu_expires 10*60*HZ #endif if (sd->dst_cache->pmtu > mtu && mtu && mtu >= 68 && !(sd->dst_cache->mxlock & (1 << RTAX_MTU))) { sd->dst_cache->pmtu = mtu; dst_set_expires(sd->dst_cache, ip_rt_mtu_expires); } #endif } else dst_release(xchg(&sd->dst_cache, NULL)); } else rare(); break; } default: rare(); return (0); } } else rare(); return (0); }
|
|||||||||||||||||||||||||||
OpenSS7 SS7 for the Common Man |
Home | Overview | Status | News | Documentation | Resources | About | ||||||||||||||||||||
© Copyright 1997-2004,OpenSS7 Corporation, All Rights Reserved. |