/*
* Copyright (c) 2003 Joacim H�ggmark, Magnus Erixzon, Nils-Erik Mattsson
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* Id: dccp_usrreq.c,v 1.47 2003/07/31 11:23:08 joahag-9 Exp
*/
/*
* Copyright (c) 1982, 1986, 1988, 1990, 1993, 1995
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)udp_usrreq.c 8.6 (Berkeley) 5/23/95
*/
#ifdef INET6
if (isipv6) {
DCCP_DEBUG((LOG_INFO, "Got DCCP ipv6 packet, iphlen = %u!\n", iphlen));
ip6 = mtod(m, struct ip6_hdr *);
M_REGION_GET(dh, struct dccphdr *, m, iphlen, sizeof(*dh));
if (dh == NULL) {
dccpstat.dccps_badlen++;
return;
}
} else
#endif
{
/*
* Strip IP options, if any; should skip this,
* make available to user, and use on returned packets,
* but we don't yet have a way to check the checksum
* with options still present.
*/
if (iphlen > sizeof (struct ip)) {
DCCP_DEBUG((LOG_INFO, "Need to strip options\n"));
#if 0 /* XXX */
ip_stripoptions(m, (struct mbuf *)0);
#endif
iphlen = sizeof(struct ip);
}
/*
* Get IP and DCCP header together in first mbuf.
*/
ip = mtod(m, struct ip *);
M_REGION_GET(dh, struct dccphdr *, m, iphlen, sizeof(*dh));
if (dh == NULL) {
dccpstat.dccps_badlen++;
return;
}
}
dlh = (struct dccplhdr*)dh;
is_shortseq = !dh->dh_x;
/*
* Make mbuf data length reflect DCCP length.
* If not enough data to reflect DCCP length, drop.
*/
#ifdef INET6
if (isipv6)
len = m->m_pkthdr.len - off;
else
#endif
{
len = ntohs(ip->ip_len);
len -= ip->ip_hl << 2;
}
if (len < sizeof(struct dccphdr)) {
DCCP_DEBUG((LOG_INFO, "Dropping DCCP packet!\n"));
dccpstat.dccps_badlen++;
goto badunlocked;
}
/*
* Save a copy of the IP header in case we want restore it
* for sending a DCCP reset packet in response.
*/
if (!isipv6) {
/*save_ip = *ip;*/
ipov = (struct ipovly *)ip;
}
/*
* We should send DCCP reset here but we can't call dccp_output
* since we have no dccpcb. A icmp unreachable works great but
* the specs says DCCP reset :(
*
* if (!isipv6) {
* *ip = save_ip;
* ip->ip_len += iphlen;
* icmp_error(m, ICMP_UNREACH, ICMP_UNREACH_PORT, 0, 0);
* }
*/
/* These are ok if the sender has a valid init Cookie */
case DCCP_TYPE_ACK:
case DCCP_TYPE_DATAACK:
case DCCP_TYPE_DATA:
DCCP_DEBUG((LOG_INFO, "Got DCCP ACK/DATAACK/DATA, should check init cookie...\n"));
dccp_output(dp, DCCP_TYPE_RESET + 2);
break;
if (dp->cc_in_use[1] > 0) {
/* This is called so Acks on Acks can be handled */
if (!dp->ack_snd) dp->ack_snd = dp->seq_rcv;
DCCP_DEBUG((LOG_INFO, "Calling ACK *cc_sw[%u].cc_recv_packet_recv! %llx %llx\n", dp->cc_in_use[1], dp->ack_snd, dp->seq_rcv));
(*cc_sw[dp->cc_in_use[1]].cc_recv_packet_recv)(dp->cc_state[1], options, optlen);
}
break;
case DCCP_TYPE_DATAACK:
DCCP_DEBUG((LOG_INFO, "Got DCCP DATAACK\n"));
/*
* Notify a dccp user of an asynchronous error;
* just wake up so that he can collect error status.
*/
void
dccp_notify(struct inpcb *inp, int errno)
{
inp->inp_socket->so_error = errno;
sorwakeup(inp->inp_socket);
sowwakeup(inp->inp_socket);
return;
}
/*
* Called when we get ICMP errors (destination unreachable,
* parameter problem, source quench, time exceeded and redirects)
*/
void *
dccp_ctlinput(int cmd, const struct sockaddr *sa, void *vip)
{
struct ip *ip = vip;
struct dccphdr *dh;
void (*notify)(struct inpcb *, int) = dccp_notify;
struct in_addr faddr;
struct inpcb *inp = NULL;
/*
* Called by getsockopt and setsockopt.
*
*/
int
dccp_ctloutput(int op, struct socket *so, struct sockopt *sopt)
{
int s, error = 0;
struct inpcb *inp;
struct dccpcb *dp;
int family; /* family of the socket */
family = so->so_proto->pr_domain->dom_family;
error = 0;
s = splsoftnet();
INP_INFO_RLOCK(&dccpbinfo);
inp = sotoinpcb(so);
if (inp == NULL)
{
INP_INFO_RUNLOCK(&dccpbinfo);
splx(s);
return (ECONNRESET);
}
/*
if (inp)
INP_LOCK(inp);
else
IN6P_LOCK(in6p);
INP_INFO_RUNLOCK(&dccpbinfo);
*/
if (sopt->sopt_level != IPPROTO_DCCP) {
switch (family) {
case PF_INET:
error = ip_ctloutput(op, so, sopt);
break;
#if defined(INET6)
case PF_INET6:
error = ip6_ctloutput(op, so, sopt);
break;
#endif
}
splx(s);
return (error);
}
DCCP_DEBUG((LOG_INFO, "Going to send a DCCP packet!\n"));
#if defined(__FreeBSD__) && __FreeBSD_version >= 500000
KASSERT(mutex_assert(&dp->d_inpcb->inp_mtx, MA_OWNED));
#endif
inp = dp->d_inpcb;
so = inp->inp_socket;
if (dp->state != DCCPS_ESTAB && extra == 1) {
/* Only let cc decide when to resend if we are in established state */
return 0;
}
if (so->so_snd.sb_cc){
pktlen = dp->pktlen[dp->pktlenidx];
} else
pktlen = 0;
/* Check with CC if we can send... */
if (pktlen && dp->cc_in_use[0] > 0 && dp->state == DCCPS_ESTAB) {
if (!(*cc_sw[dp->cc_in_use[0]].cc_send_packet)(dp->cc_state[0], pktlen)) {
DCCP_DEBUG((LOG_INFO, "Not allowed to send right now\n"));
return 0;
}
}
/*
* off not needed for dccp because we do not need to wait for ACK
* before removing the packet
*/
off = 0;
optlen = 0;
if (pktlen > dp->d_maxseg) {
/* this should not happen */
DCCP_DEBUG((LOG_INFO, "packet will be fragmented! maxseg %d\n", dp->d_maxseg));
len = dp->d_maxseg;
pktlen -= len;
sendalot = 1;
} else
len = pktlen;
if (extra == DCCP_TYPE_RESET + 2) {
DCCP_DEBUG((LOG_INFO, "Force sending of DCCP TYPE_RESET! seq=%llu\n", dp->seq_snd));
type = DCCP_TYPE_RESET;
extrah_len = 12;
} else if (dp->state <= DCCPS_REQUEST && dp->who == DCCP_CLIENT) {
DCCP_DEBUG((LOG_INFO, "Sending DCCP TYPE_REQUEST!\n"));
type = DCCP_TYPE_REQUEST;
dp->state = DCCPS_REQUEST;
extrah_len = 4;
} else if (dp->state == DCCPS_REQUEST && dp->who == DCCP_SERVER) {
DCCP_DEBUG((LOG_INFO, "Sending DCCP TYPE_RESPONSE!\n"));
type = DCCP_TYPE_RESPONSE;
dp->state = DCCPS_RESPOND;
extrah_len = 12;
} else if (dp->state == DCCPS_RESPOND) {
DCCP_DEBUG((LOG_INFO, "Still in feature neg, sending DCCP TYPE_ACK!\n"));
type = DCCP_TYPE_ACK;
if (!dp->shortseq)
extrah_len = 8;
else {
extrah_len = 4;
use_shortseq = 1;
}
} else if (dp->state == DCCPS_ESTAB) {
if (dp->ack_snd && len) {
DCCP_DEBUG((LOG_INFO, "Sending DCCP TYPE_DATAACK!\n"));
type = DCCP_TYPE_DATAACK;
/*(u_int32_t *)&extrah = dp->seq_rcv; */
if (!dp->shortseq)
extrah_len = 8;
else {
extrah_len = 4;
use_shortseq = 1;
}
} else if (dp->ack_snd) {
DCCP_DEBUG((LOG_INFO, "Sending DCCP TYPE_ACK!\n"));
type = DCCP_TYPE_ACK;
if (!dp->shortseq)
extrah_len = 8;
else {
extrah_len = 4;
use_shortseq = 1;
}
} else if (len) {
DCCP_DEBUG((LOG_INFO, "Sending DCCP TYPE_DATA!\n"));
type = DCCP_TYPE_DATA;
extrah_len = 0;
} else {
DCCP_DEBUG((LOG_INFO, "No ack or data to send!\n"));
return 0;
}
} else if (dp->state == DCCPS_CLIENT_CLOSE) {
DCCP_DEBUG((LOG_INFO, "Sending DCCP TYPE_CLOSE!\n"));
type = DCCP_TYPE_CLOSE;
extrah_len = 8;
} else if (dp->state == DCCPS_SERVER_CLOSE) {
DCCP_DEBUG((LOG_INFO, "Sending DCCP TYPE_CLOSEREQ!\n"));
type = DCCP_TYPE_CLOSEREQ;
extrah_len = 8;
} else {
DCCP_DEBUG((LOG_INFO, "Hey, we should never get here, state = %u\n", dp->state));
return 1;
}
/* Stop all timers */
callout_stop(&dp->connect_timer);
callout_stop(&dp->retrans_timer);
callout_stop(&dp->close_timer);
callout_stop(&dp->timewait_timer);
if (dp->cc_in_use[0] > 0)
(*cc_sw[dp->cc_in_use[0]].cc_send_free)(dp->cc_state[0]);
if (dp->cc_in_use[1] > 0)
(*cc_sw[dp->cc_in_use[1]].cc_recv_free)(dp->cc_state[1]);
/*
* Runs when a new socket is created with the
* socket system call or sonewconn.
*/
int
dccp_attach(struct socket *so, int proto)
{
struct inpcb *inp = NULL;
struct dccpcb *dp;
int s, error = 0;
DCCP_DEBUG((LOG_INFO, "Entering dccp_attach(proto=%d)!\n", proto));
INP_INFO_WLOCK(&dccpbinfo);
s = splsoftnet();
sosetlock(so);
/* Do not bind to multicast addresses! */
if (sin->sin_family == AF_INET &&
IN_MULTICAST(ntohl(sin->sin_addr.s_addr))) {
INP_INFO_WUNLOCK(&dccpbinfo);
return EAFNOSUPPORT;
}
INP_LOCK(inp);
s = splsoftnet();
error = inpcb_bind(inp, sin, l);
splx(s);
INP_UNLOCK(inp);
INP_INFO_WUNLOCK(&dccpbinfo);
return error;
}
/*
* Initiates a connection to a server
* Called by the connect system call.
*/
static int
dccp_connect(struct socket *so, struct sockaddr *nam, struct lwp *l)
{
struct inpcb *inp;
struct dccpcb *dp;
int error;
struct sockaddr_in *sin;
char test[2];
/*
* If we have don't have an established connection
* we can call dccp_close, otherwise we can just
* set SS_ISDISCONNECTED and flush the receive queue.
*/
static int
dccp_disconnect2(struct dccpcb *dp)
{
struct socket *so = dptosocket(dp);
if (dp->state != DCCPS_ESTAB) {
DCCP_DEBUG((LOG_INFO, "We have no established connection!\n"));
}
if (control != NULL) {
DCCP_DEBUG((LOG_INFO, "We got a control message!\n"));
/* Are we going to use control messages??? */
if (control->m_len) {
m_freem(control);
}
}
for (i = 0; i<val_len; i++) {
dp->options[dp->optlen] = val[i];
dp->optlen++;
}
}
} else {
DCCP_DEBUG((LOG_INFO, "No room for more options, optlen = %u\n", dp->optlen));
return -1;
}
return 0;
}
/*
* Searches "options" for given option type. if found, the data is copied to buffer
* and returns the data length.
* Returns 0 if option type not found
*/
int
dccp_get_option(char *options, int optlen, int type, char *buffer, int buflen)
{
int i, j, size;
u_int8_t t;
for (i=0; i < optlen;) {
t = options[i++];
if (t >= 32) {
size = options[i++] - 2;
if (t == type) {
if (size > buflen)
return 0;
for (j = 0; j < size; j++)
buffer[j] = options[i++];
return size;
}
i += size;
}
}
/* If we get here the options was not found */
DCCP_DEBUG((LOG_INFO, "dccp_get_option option(%d) not found\n", type));
return 0;
}
void
dccp_parse_options(struct dccpcb *dp, char *options, int optlen)
{
u_int8_t opt, size, i, j;
char val[8];
while (i < dp->featlen) {
t_opt = dp->features[i];
len = dp->features[i+ 1];
if (i + len > dp->featlen) {
DCCP_DEBUG((LOG_INFO, "Error, len = %u and i(%u) + len > dp->featlen (%u)\n", len, i, dp->featlen));
return 1;
}
t_feature = dp->features[i+2];
if (t_opt == opt && t_feature == feature) {
i += len;
} else {
if (i != j) {
for (k = 0; k < len; k++) {
dp->features[j+k] = dp->features[i+k];
}
}
i += len;
j += len;
}
}
dp->featlen = j;
DCCP_DEBUG((LOG_INFO, "Exiting dccp_remove_feature, featlen = %u\n", dp->featlen));
return 0;
}
switch (feature) {
case DCCP_FEATURE_CC:
DCCP_DEBUG((LOG_INFO, "Got CCID negotiation, opt = %u, val[0] = %u\n", opt, val[0]));
if (opt == DCCP_OPT_CHANGE_R) {
if (val[0] == 2 || val[0] == 3 || val[0] == 0) {
/* try to use preferable CCID */
int i;
for (i = 1; i < val_len; i ++) if (val[i] == dp->pref_cc) val[0] = dp->pref_cc;
DCCP_DEBUG((LOG_INFO, "Sending DCCP_OPT_CONFIRM_L on CCID %u\n", val[0]));
dccp_remove_feature(dp, DCCP_OPT_CONFIRM_L, DCCP_FEATURE_CC);
dccp_add_feature_option(dp, DCCP_OPT_CONFIRM_L, DCCP_FEATURE_CC , val, 1);
if (dp->cc_in_use[0] < 1) {
dp->cc_state[0] = (*cc_sw[val[0] + 1].cc_send_init)(dp);
dp->cc_in_use[0] = val[0] + 1;
} else {
DCCP_DEBUG((LOG_INFO, "We already have negotiated a CC!!!\n"));
}
}
} else if (opt == DCCP_OPT_CONFIRM_L) {
DCCP_DEBUG((LOG_INFO, "Got DCCP_OPT_CONFIRM_L on CCID %u\n", val[0]));
dccp_remove_feature(dp, DCCP_OPT_CHANGE_R, DCCP_FEATURE_CC);
if (dp->cc_in_use[1] < 1) {
dp->cc_state[1] = (*cc_sw[val[0] + 1].cc_recv_init)(dp);
dp->cc_in_use[1] = val[0] + 1;
DCCP_DEBUG((LOG_INFO, "confirmed cc_in_use[1] = %d\n", dp->cc_in_use[1]));
} else {
DCCP_DEBUG((LOG_INFO, "We already have negotiated a CC!!! (confirm) %d\n", dp->cc_in_use[1]));
}
}
break;
case DCCP_FEATURE_ACKVECTOR:
ACK_DEBUG((LOG_INFO, "Got _Use Ack Vector_\n"));
if (opt == DCCP_OPT_CHANGE_R) {
if (val[0] == 1) {
dccp_use_ackvector(dp);
dccp_remove_feature(dp, DCCP_OPT_CONFIRM_L, DCCP_FEATURE_ACKVECTOR);
dccp_add_feature_option(dp, DCCP_OPT_CONFIRM_L, DCCP_FEATURE_ACKVECTOR , val, 1);
} else {
ACK_DEBUG((LOG_INFO, "ERROR. Strange val %u\n", val[0]));
}
} else if (opt == DCCP_OPT_CONFIRM_L) {
dccp_remove_feature(dp, DCCP_OPT_CONFIRM_L, DCCP_FEATURE_ACKVECTOR);
if (val[0] == 1) {
dp->remote_ackvector = 1;
ACK_DEBUG((LOG_INFO,"Remote side confirmed AckVector usage\n"));
} else {
ACK_DEBUG((LOG_INFO, "ERROR. Strange val %u\n", val[0]));
}
}
break;
case DCCP_FEATURE_ACKRATIO:
if (opt == DCCP_OPT_CHANGE_R) {
memcpy(&(dp->ack_ratio), val, 1);
ACK_DEBUG((LOG_INFO, "Feature: Change Ack Ratio to %u\n", dp->ack_ratio));
}
break;
case DCCP_FEATURE_ECN:
case DCCP_FEATURE_MOBILITY:
default:
/* we should send back empty CONFIRM_L for unknown feature unless it's not mandatory */
dccp_add_option(dp, DCCP_OPT_CONFIRM_L, NULL, 0);
break;
}
}
#ifdef __FreeBSD__
static int
dccp_pcblist(SYSCTL_HANDLER_ARGS)
{
int error, i, n, s;
struct inpcb *inp, **inp_list;
inp_gen_t gencnt;
struct xinpgen xig;
/*
* The process of preparing the TCB list is too time-consuming and
* resource-intensive to repeat twice on every request.
*/
if (req->oldptr == 0) {
n = dccpbinfo.ipi_count;
req->oldidx = 2 * (sizeof xig)
+ (n + n/8) * sizeof(struct xdccpcb);
return 0;
}
if (req->newptr != 0)
return EPERM;
/*
* OK, now we're committed to doing something.
*/
s = splnet();
gencnt = dccpbinfo.ipi_gencnt;
n = dccpbinfo.ipi_count;
splx(s);
for (inp = LIST_FIRST(dccpbinfo.listhead), i = 0; inp && i < n;
inp = LIST_NEXT(inp, inp_list)) {
INP_LOCK(inp);
if (inp->inp_gencnt <= gencnt &&
#if __FreeBSD_version >= 500000
cr_canseesocket(req->td->td_ucred, inp->inp_socket) == 0)
#else
!prison_xinpcb(req->p, inp))
#endif
inp_list[i++] = inp;
INP_UNLOCK(inp);
}
INP_INFO_RUNLOCK(&dccpbinfo);
splx(s);
n = i;
error = 0;
for (i = 0; i < n; i++) {
inp = inp_list[i];
INP_LOCK(inp);
if (inp->inp_gencnt <= gencnt) {
struct xdccpcb xd;
vaddr_t inp_ppcb;
xd.xd_len = sizeof xd;
/* XXX should avoid extra copy */
memcpy(&xd.xd_inp, inp, sizeof *inp);
inp_ppcb = inp->inp_ppcb;
if (inp_ppcb != NULL)
memcpy(&xd.xd_dp, inp_ppcb, sizeof xd.xd_dp);
else
memset((char *) &xd.xd_dp, 0, sizeof xd.xd_dp);
if (inp->inp_socket)
sotoxsocket(inp->inp_socket, &xd.xd_socket);
error = SYSCTL_OUT(req, &xd, sizeof xd);
}
INP_UNLOCK(inp);
}
if (!error) {
/*
* Give the user an updated idea of our state.
* If the generation differs from what we told
* her before, she knows that something happened
* while we were processing this request, and it
* might be necessary to retry.
*/
s = splnet();
INP_INFO_RLOCK(&dccpbinfo);
xig.xig_gen = dccpbinfo.ipi_gencnt;
xig.xig_sogen = so_gencnt;
xig.xig_count = dccpbinfo.ipi_count;
/**
* Initialize and allocate mem for Ack Vector
**/
void
dccp_use_ackvector(struct dccpcb *dp)
{
DCCP_DEBUG((LOG_INFO,"Initializing AckVector\n"));
if (dp->ackvector != 0) {
DCCP_DEBUG((LOG_INFO, "It was already initialized!!!\n"));
return;
}
dp->av_size = DCCP_VECTORSIZE;
/* need 2 bits per entry */
dp->ackvector = malloc(dp->av_size/4, M_PCB, M_NOWAIT | M_ZERO);
if (dp->ackvector == 0) {
DCCP_DEBUG((LOG_INFO, "Unable to allocate memory for ackvector\n"));
/* What to do now? */
dp->av_size = 0;
return;
}
memset(dp->ackvector, 0xff, dp->av_size/4);
dp->av_hs = dp->av_ts = 0;
dp->av_hp = dp->ackvector;
}
/**
* Set 'seqnr' as the new head in ackvector
**/
void
dccp_update_ackvector(struct dccpcb *dp, u_int64_t seqnr)
{
int64_t gap;
u_char *t;
/* Ignore wrapping for now */
ACK_DEBUG((LOG_INFO,"New head in ackvector: %u\n", seqnr));
if (dp->av_size == 0) {
ACK_DEBUG((LOG_INFO, "Update: AckVector NOT YET INITIALIZED!!!\n"));
dccp_use_ackvector(dp);
}
if (seqnr > dp->av_hs) {
gap = seqnr - dp->av_hs;
} else {
/* We received obsolete information */
return;
}
t = dp->av_hp + (gap/4);
if (t >= (dp->ackvector + (dp->av_size/4)))
t -= (dp->av_size / 4); /* ackvector wrapped */
dp->av_hp = t;
dp->av_hs = seqnr;
}
/**
* We've received a packet. store in local av so it's included in
* next Ack Vector sent
**/
void
dccp_increment_ackvector(struct dccpcb *dp, u_int64_t seqnr)
{
u_int64_t offset, dc;
int64_t gap;
u_char *t, *n;
DCCP_DEBUG((LOG_INFO, "Entering dccp_increment_ackvecktor %d\n", dp->av_size));
if (dp->av_size == 0) {
DCCP_DEBUG((LOG_INFO, "Increment: AckVector NOT YET INITIALIZED!!!\n"));
dccp_use_ackvector(dp);
}
/* Check for wrapping */
if (seqnr >= dp->av_hs) {
/* Not wrapped */
gap = seqnr - dp->av_hs;
} else {
/* Wrapped */
gap = seqnr + 0x1000000000000LL - dp->av_hs; /* seqnr = 48 bits */
}
DCCP_DEBUG((LOG_INFO, "dccp_increment_ackvecktor gap=%llu av_size %d\n", gap, dp->av_size));
if (gap >= dp->av_size) {
/* gap is bigger than ackvector size? baaad */
/* maybe we should increase the ackvector here */
DCCP_DEBUG((LOG_INFO, "increment_ackvector error. gap: %llu, av_size: %d, seqnr: %d\n",
gap, dp->av_size, seqnr));
return;
}
offset = gap % 4; /* hi or low 2 bits to mark */
t = dp->av_hp + (gap/4);
if (t >= (dp->ackvector + (dp->av_size/4)))
t -= (dp->av_size / 4); /* ackvector wrapped */
*t = *t & (~(0x03 << (offset *2))); /* turn off bits, 00 is rcvd, 11 is missing */
if (gap > (dp->av_size - 128)) {
n = malloc(dp->av_size/2, M_PCB, M_NOWAIT | M_ZERO); /* old size * 2 */
memset (n + dp->av_size / 4, 0xff, dp->av_size / 4); /* new half all missing */
dc = (dp->ackvector + (dp->av_size/4)) - dp->av_hp;
memcpy (n, dp->av_hp, dc); /* tail to end */
memcpy (n+dc, dp->ackvector, dp->av_hp - dp->ackvector); /* start to tail */
dp->av_size = dp->av_size * 2; /* counted in items, so it';s a doubling */
free (dp->ackvector, M_PCB);
dp->av_hp = dp->ackvector = n;
}
}
/**
* Generates the ack vector to send in outgoing packet.
* These are backwards (first packet in ack vector is packet indicated by Ack Number,
* subsequent are older packets).
**/