📄 sctp_msg.c
字号:
return;}/* * 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 ); if ( sctp_locked(sp) ) { seldom(); sp->timer_cookie = timeout(sctp_cookie_timeout, (caddr_t)sd, 1); return; } sp->timer_cookie = 0; if ( sp->s_state != SCTP_COOKIE_ECHOED ) { rare(); return; } if ( sctp_assoc_timedout(sp, sd, sp->max_retrans) ) { seldom(); return; } /* 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, return ); set_timeout(&sp->timer_cookie, sctp_cookie_timeout, sd, sd->rto); usual( sp->retry ); sctp_send_msg(sp, sd, sp->retry); return;}/* * 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 ); if ( sctp_locked(sp) ) { seldom(); sd->timer_retrans = timeout(sctp_retrans_timeout, (caddr_t)sd, 1); return; } sd->timer_retrans = 0; if ( !((1<<sp->s_state) & (SCTPF_CONNECTED)) ) { rare(); return; } if ( sctp_assoc_timedout(sp, sd, sp->max_retrans) ) { seldom(); return; } /* 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); return;}/* * 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 ); if ( sctp_locked(sp) ) { seldom(); sp->timer_sack = timeout(sctp_sack_timeout, (caddr_t)sp, 1); return; } sp->timer_sack = 0; if ( !((1<<sp->s_state) & (SCTPF_RECEIVING)) ) { rare(); return; } sp->sackf |= SCTP_SACKF_TIM; /* RFC 2960 6.2 */ sctp_transmit_wakeup(sp); return;}/* * 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 ); if ( sctp_locked(sp) ) { seldom(); sd->timer_idle = timeout(&sctp_idle_timeout, (caddr_t)sd, 1); return; } sd->timer_idle = 0; if ( !((1<<sp->s_state) & (SCTPF_CONNECTED|SCTPF_CLOSING)) ) { rare(); return; } sctp_send_heartbeat(sp, sd); return;}/* * 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 ); if ( sctp_locked(sp) ) { seldom(); sd->timer_heartbeat = timeout(sctp_heartbeat_timeout, (caddr_t)sd, 1); return; } sd->timer_heartbeat = 0; if ( !((1<<sp->s_state) & (SCTPF_CONNECTED|SCTPF_CLOSING)) ) { rare(); return; } if ( sctp_assoc_timedout(sp, sd, 0) ) { seldom(); return; } sctp_send_heartbeat(sp, sd); return;}/* * 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 ); if ( sctp_locked(sp) ) { seldom(); sp->timer_shutdown = timeout(sctp_shutdown_timeout, (caddr_t)sd, 1); return; } sp->timer_shutdown = 0; if ( !((1<<sp->s_state) & (SCTPF_CLOSING)) ) { rare(); return; } if ( sctp_assoc_timedout(sp, sd, sp->max_retrans) ) { seldom(); return; } sd = sp->taddr; ensure( sd, return ); set_timeout(&sp->timer_shutdown, sctp_shutdown_timeout, (caddr_t)sd, sd->rto); usual(sp->retry); sctp_send_msg(sp, sd, sp->retry); return;}/* * 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 ) { 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -