void ci_tcp_linger(ci_netif* ni, ci_tcp_state* ts) { /* This is called at user-level when a socket is closed if linger is ** enabled and has a timeout, and there is TX data outstanding. ** ** Our job is to block until all data is successfully sent and acked, or ** until timeout. */ ci_uint64 sleep_seq; int rc = 0; ci_uint32 timeout = ts->s.so.linger * 1000; LOG_TC(log("%s: "NTS_FMT, __FUNCTION__, NTS_PRI_ARGS(ni, ts))); ci_assert(ts->s.b.sb_aflags & CI_SB_AFLAG_ORPHAN); ci_assert(ts->s.b.sb_aflags & CI_SB_AFLAG_IN_SO_LINGER); ci_assert(ts->s.s_flags & CI_SOCK_FLAG_LINGER); ci_assert(ts->s.b.state != CI_TCP_LISTEN); while( 1 ) { sleep_seq = ts->s.b.sleep_seq.all; ci_rmb(); if( SEQ_EQ(tcp_enq_nxt(ts), tcp_snd_una(ts)) ) return; rc = ci_sock_sleep(ni, &ts->s.b, CI_SB_FLAG_WAKE_TX, 0, sleep_seq, &timeout); if( rc ) break; } if( ! SEQ_EQ(tcp_enq_nxt(ts), tcp_snd_una(ts)) ) { ci_netif_lock(ni); /* check we are working with the same socket, and it was not closed and * dropped under our feet. */ if( ! SEQ_EQ(tcp_enq_nxt(ts), tcp_snd_una(ts)) && (ts->s.b.sb_aflags & CI_SB_AFLAG_IN_SO_LINGER) ) ci_tcp_drop(ni, ts, 0); ci_netif_unlock(ni); } }
/* NOTE: in the kernel version [fd] is unused and, if it's a ptr, [arg] will * be in user-space and may need to be fetched into kernel memory. */ static int ci_tcp_ioctl_lk(citp_socket* ep, ci_fd_t fd, int request, void* arg) { ci_netif* netif = ep->netif; ci_sock_cmn* s = ep->s; ci_tcp_state* ts = NULL; int rc = 0; int os_socket_exists = s->b.sb_aflags & CI_SB_AFLAG_OS_BACKED; if( s->b.state != CI_TCP_LISTEN ) ts = SOCK_TO_TCP(s); /* Keep the os socket in sync. If this is a "get" request then the * return will be based on our support, not the os's (except for EFAULT * handling which we get for free). * Exceptions: * - FIONBIO is applied just in time on handover if needed (listening * sockets always have a non-blocking OS socket) * - FIONREAD, TIOCOUTQ, SIOCOUTQNSD and SIOCATMARK are useless on OS * socket, let's avoid syscall. */ if( os_socket_exists && request != FIONREAD && request != SIOCATMARK && request != FIOASYNC && request != TIOCOUTQ && request != SIOCOUTQNSD && request != (int) FIONBIO ) { rc = oo_os_sock_ioctl(netif, s->b.bufid, request, arg, NULL); if( rc < 0 ) return rc; } /* ioctl defines are listed in `man ioctl_list` and the CI equivalent * CI defines are in include/ci/net/ioctls.h */ LOG_TV( ci_log("%s: request = %d, arg = %ld", __FUNCTION__, request, (long)arg)); switch( request ) { case FIONBIO: if( CI_IOCTL_ARG_OK(int, arg) ) { CI_CMN_IOCTL_FIONBIO(ep->s, arg); rc = 0; break; } goto fail_fault; case FIONREAD: /* synonym of SIOCINQ */ if( !CI_IOCTL_ARG_OK(int, arg) ) goto fail_fault; if( s->b.state == CI_TCP_LISTEN ) goto fail_inval; if( s->b.state == CI_TCP_SYN_SENT ) { CI_IOCTL_SETARG((int*)arg, 0); } else { /* In inline mode, return the total number of bytes in the receive queue. If SO_OOBINLINE isn't set then return the number of bytes up to the mark but without counting the mark */ int bytes_in_rxq = tcp_rcv_usr(ts); if (bytes_in_rxq && ! (ts->s.s_flags & CI_SOCK_FLAG_OOBINLINE)) { if (tcp_urg_data(ts) & CI_TCP_URG_PTR_VALID) { /*! \TODO: what if FIN has been received? */ unsigned int readnxt = tcp_rcv_nxt(ts) - bytes_in_rxq; if (SEQ_LT(readnxt, tcp_rcv_up(ts))) { bytes_in_rxq = tcp_rcv_up(ts) - readnxt; } else if (SEQ_EQ(readnxt, tcp_rcv_up(ts))) { bytes_in_rxq--; } } } CI_IOCTL_SETARG((int*)arg, bytes_in_rxq); } break; case TIOCOUTQ: /* synonym of SIOCOUTQ */ case SIOCOUTQNSD: { CI_BUILD_ASSERT(TIOCOUTQ == SIOCOUTQ); int outq_bytes = 0; if( !CI_IOCTL_ARG_OK(int, arg) ) goto fail_fault; if( s->b.state == CI_TCP_LISTEN ) goto fail_inval; if( s->b.state != CI_TCP_SYN_SENT ) { /* TIOCOUTQ counts all unacknowledged data, so includes retrans queue. */ if( request == TIOCOUTQ ) outq_bytes = SEQ_SUB(tcp_enq_nxt(ts), tcp_snd_una(ts)); else outq_bytes = SEQ_SUB(tcp_enq_nxt(ts), tcp_snd_nxt(ts)); } CI_IOCTL_SETARG((int*)arg, outq_bytes); } break; case SIOCATMARK: { if( !CI_IOCTL_ARG_OK(int, arg) ) goto fail_fault; /* return true, if we are at the out-of-band byte */ CI_IOCTL_SETARG((int*)arg, 0); if( s->b.state != CI_TCP_LISTEN ) { int readnxt; readnxt = SEQ_SUB(tcp_rcv_nxt(ts), tcp_rcv_usr(ts)); if( ~ts->s.b.state & CI_TCP_STATE_ACCEPT_DATA ) readnxt = SEQ_SUB(readnxt, 1); if( tcp_urg_data(ts) & CI_TCP_URG_PTR_VALID ) CI_IOCTL_SETARG((int*)arg, readnxt == tcp_rcv_up(ts)); LOG_URG(log(NTS_FMT "SIOCATMARK atmark=%d readnxt=%u rcv_up=%u%s", NTS_PRI_ARGS(ep->netif, ts), readnxt == tcp_rcv_up(ts), readnxt, tcp_rcv_up(SOCK_TO_TCP(ep->s)), (tcp_urg_data(ts)&CI_TCP_URG_PTR_VALID)?"":" (invalid)")); } break; } #ifndef __KERNEL__ case FIOASYNC: /* Need to apply this to [fd] so that our fasync file-op will be * invoked. */ rc = ci_sys_ioctl(fd, request, arg); break; case SIOCSPGRP: if( !CI_IOCTL_ARG_OK(int, arg) ) goto fail_fault; /* Need to apply this to [fd] to get signal delivery to work. However, * SIOCSPGRP is only supported on sockets, so we need to convert to * fcntl(). */ rc = ci_sys_fcntl(fd, F_SETOWN, CI_IOCTL_GETARG(int, arg)); if( rc == 0 ) { rc = ci_cmn_ioctl(netif, ep->s, request, arg, rc, os_socket_exists); } else { CI_SET_ERROR(rc, -rc); } break; #endif default: return ci_cmn_ioctl(netif, ep->s, request, arg, rc, os_socket_exists); }
int rudp_recv_cb(int fd, void *arg) { rudp_socket_t rsock = (rudp_socket_t)arg; rudp_session_t rsession = NULL; rudp_dgram_t rudp_pkt; struct sockaddr_in from; int i; size_t from_len; int ret; char buf[sizeof(struct _rudp_dgram_t)]; memset((char*)&from, 0, sizeof(struct sockaddr_in)); from_len = sizeof(from); ret = recvfrom(fd, buf, sizeof(struct _rudp_dgram_t), 0, (struct sockaddr*)&from, &from_len); if(ret < 0) { perror("recvfrom:"); return -1; } //drop = 1; rudp_pkt = (rudp_dgram_t) buf; #ifdef DROP ++drop; if((drop % 5 == 0) && (rudp_pkt->hdr.type == RUDP_DATA)) { printf("Dropping.. %d\n", drop); return 0; } #endif /* DROP */ if(rudp_pkt->hdr.type == RUDP_ACK) { rsession = rudp_get_session(rsock, &from); if(NULL == rsession) { printf("rudp_recv_cb: No session found\n"); return -1; } } /* Session not correct return immediatedly */ #ifdef DEB printf("Packet type: %d\n\ Packet version: %d\n\ Packet SeqNo.: %d\n", rudp_pkt->hdr.type, rudp_pkt->hdr.version, rudp_pkt->hdr.seqno); #endif /* DEB */ switch(rudp_pkt->hdr.type) { case RUDP_DATA: for(i = 0; i < recv_session; ++i) { /* Check if correct seq no. */ if((SEQ_EQ(recv_seq_no[i], rudp_pkt->hdr.seqno)) && (rsock->recv_handler != NULL)) { rsock->recv_handler(rsock, &from, rudp_pkt->data, rudp_pkt->data_len); rudp_send_ack(rsock, i, &from); break; } } /* Else drop the packet */ break; case RUDP_ACK: //if(rsession->last_seq + 1 != rudp_pkt->hdr.seqno) break; if(rsession->state == RUDP_SESSION_OPENED) { rsession->state = RUDP_SESSION_SYN; event_timeout_delete(rudp_ack_time_cb, rsession); rsession->wstart = rsession->head->next; rudp_send_dgram(RUDP_DATA, rsession); rsession->wsize = 0; } else if(rsession->state == RUDP_SESSION_SYN) { while(SEQ_LT(rsession->wstart->hdr.seqno, rudp_pkt->hdr.seqno)) { rsession->wstart = rsession->wstart->next; if(NULL == rsession->wstart) { event_timeout_delete(rudp_ack_time_cb, rsession); rsession->state = RUDP_SESSION_DAT; rudp_send_dgram(RUDP_FIN, rsession); break; } ++rsession->wsize; } //printf("Window Size: %d\n", rsession->wsize); if((rsession->wsize == RUDP_WINDOW) && (NULL != rsession->wstart)) { event_timeout_delete(rudp_ack_time_cb, rsession); rsession->wsize = 0; rudp_send_dgram(RUDP_DATA, rsession); } } else if(rsession->state == RUDP_SESSION_DAT) { rudp_socket_t r; rsession->state = RUDP_SESSION_FIN; event_timeout_delete(rudp_ack_time_cb, rsession); r = rsession->rsock; rudp_free_session(rsession); #ifdef DEBL printf("nsessions: %d\n", r->nsessions); #endif /* DEBL */ /* Last session on this socket */ if(r->nsessions == 1) { event_fd_delete(rudp_recv_cb, r); r->event_handler(r, RUDP_EVENT_CLOSED, NULL); close(r->sockfd); free(r); } else { /* Decrement the sessions on this socket */ --r->nsessions; } } break; case RUDP_SYN: recv_seq_no[recv_session] = rudp_pkt->hdr.seqno; rudp_send_ack(rsock, recv_session++, &from); all_close = recv_session; break; case RUDP_FIN: for(i = 0; i < recv_session; ++i) { if(SEQ_EQ(recv_seq_no[i], rudp_pkt->hdr.seqno)) { rudp_send_ack(rsock, i, &from); //recv_seq_no[i] = 0; --all_close; break; } } /* Close the socket on the reciever when all the files are recieved */ if(!all_close) rudp_close(rsock); break; } return 0; }
/** * \brief insert a SACK range * * \param le left edge in host order * \param re right edge in host order * * \retval 0 all is good * \retval -1 error */ static int StreamTcpSackInsertRange(TcpStream *stream, uint32_t le, uint32_t re) { SCLogDebug("le %u, re %u", le, re); #ifdef DEBUG StreamTcpSackPrintList(stream); #endif /* if to the left of last_ack then ignore */ if (SEQ_LT(re, stream->last_ack)) { SCLogDebug("too far left. discarding"); goto end; } /* if to the right of the tcp window then ignore */ if (SEQ_GT(le, (stream->last_ack + stream->window))) { SCLogDebug("too far right. discarding"); goto end; } if (stream->sack_head != NULL) { StreamTcpSackRecord *rec; for (rec = stream->sack_head; rec != NULL; rec = rec->next) { SCLogDebug("rec %p, le %u, re %u", rec, rec->le, rec->re); if (SEQ_LT(le, rec->le)) { SCLogDebug("SEQ_LT(le, rec->le)"); if (SEQ_LT(re, rec->le)) { SCLogDebug("SEQ_LT(re, rec->le)"); // entirely before, prepend StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc(); if (unlikely(stsr == NULL)) { SCReturnInt(-1); } stsr->le = le; stsr->re = re; stsr->next = stream->sack_head; stream->sack_head = stsr; goto end; } else if (SEQ_EQ(re, rec->le)) { SCLogDebug("SEQ_EQ(re, rec->le)"); // starts before, ends on rec->le, expand rec->le = le; } else if (SEQ_GT(re, rec->le)) { SCLogDebug("SEQ_GT(re, rec->le)"); // starts before, ends beyond rec->le if (SEQ_LEQ(re, rec->re)) { SCLogDebug("SEQ_LEQ(re, rec->re)"); // ends before rec->re, expand rec->le = le; } else { // implied if (re > rec->re) SCLogDebug("implied if (re > rec->re), le set to %u", rec->re); le = rec->re; continue; } } } else if (SEQ_EQ(le, rec->le)) { SCLogDebug("SEQ_EQ(le, rec->le)"); if (SEQ_LEQ(re, rec->re)) { SCLogDebug("SEQ_LEQ(re, rec->re)"); // new record fully overlapped SCReturnInt(0); } else { // implied re > rec->re SCLogDebug("implied re > rec->re"); if (rec->next != NULL) { if (SEQ_LEQ(re, rec->next->le)) { rec->re = re; goto end; } else { rec->re = rec->next->le; le = rec->next->le; SCLogDebug("le is now %u", le); continue; } } else { rec->re = re; goto end; } } } else { // implied (le > rec->le) SCLogDebug("implied (le > rec->le)"); if (SEQ_LT(le, rec->re)) { SCLogDebug("SEQ_LT(le, rec->re))"); // new record fully overlapped if (SEQ_GT(re, rec->re)) { SCLogDebug("SEQ_GT(re, rec->re)"); if (rec->next != NULL) { if (SEQ_LEQ(re, rec->next->le)) { rec->re = re; goto end; } else { rec->re = rec->next->le; le = rec->next->le; continue; } } else { rec->re = re; goto end; } } SCLogDebug("new range fully overlapped"); SCReturnInt(0); } else if (SEQ_EQ(le, rec->re)) { SCLogDebug("here"); // new record fully overlapped //int r = StreamTcpSackInsertRange(stream, rec->re+1, re); //SCReturnInt(r); le = rec->re; continue; } else { /* implied le > rec->re */ SCLogDebug("implied le > rec->re"); if (rec->next == NULL) { SCLogDebug("rec->next == NULL"); StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc(); if (unlikely(stsr == NULL)) { SCReturnInt(-1); } stsr->le = le; stsr->re = re; stsr->next = NULL; stream->sack_tail->next = stsr; stream->sack_tail = stsr; goto end; } else { SCLogDebug("implied rec->next != NULL"); if (SEQ_LT(le, rec->next->le) && SEQ_LT(re, rec->next->le)) { SCLogDebug("SEQ_LT(le, rec->next->le) && SEQ_LT(re, rec->next->le)"); StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc(); if (unlikely(stsr == NULL)) { SCReturnInt(-1); } stsr->le = le; stsr->re = re; stsr->next = rec->next; rec->next = stsr; } else if (SEQ_LT(le, rec->next->le) && SEQ_GEQ(re, rec->next->le)) { SCLogDebug("SEQ_LT(le, rec->next->le) && SEQ_GEQ(re, rec->next->le)"); StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc(); if (unlikely(stsr == NULL)) { SCReturnInt(-1); } stsr->le = le; stsr->re = rec->next->le; stsr->next = rec->next; rec->next = stsr; le = rec->next->le; } } } } } } else { SCLogDebug("implied empty list"); StreamTcpSackRecord *stsr = StreamTcpSackRecordAlloc(); if (unlikely(stsr == NULL)) { SCReturnInt(-1); } stsr->le = le; stsr->re = re; stsr->next = NULL; stream->sack_head = stsr; stream->sack_tail = stsr; } StreamTcpSackPruneList(stream); end: SCReturnInt(0); }
/** * \brief Compare the shared data portion of two segments * * If no data is shared, 0 will be returned. * * \param seg1 first segment * \param seg2 second segment * * \retval 0 shared data is the same (or no data is shared) * \retval 1 shared data is different */ int StreamTcpInlineSegmentCompare(TcpSegment *seg1, TcpSegment *seg2) { SCEnter(); if (seg1 == NULL || seg2 == NULL) { SCReturnInt(0); } if (SEQ_EQ(seg1->seq, seg2->seq) && seg1->payload_len == seg2->payload_len) { int r = SCMemcmp(seg1->payload, seg2->payload, seg1->payload_len); #if 0 if (r) { PrintRawDataFp(stdout,seg1->payload,seg1->payload_len); PrintRawDataFp(stdout,seg2->payload,seg2->payload_len); } #endif SCReturnInt(r); } else if (SEQ_GT(seg1->seq, (seg2->seq + seg2->payload_len))) { SCReturnInt(0); } else if (SEQ_GT(seg2->seq, (seg1->seq + seg1->payload_len))) { SCReturnInt(0); } else { SCLogDebug("seg1 %u (%u), seg2 %u (%u)", seg1->seq, seg1->payload_len, seg2->seq, seg2->payload_len); uint32_t seg1_end = seg1->seq + seg1->payload_len; uint32_t seg2_end = seg2->seq + seg2->payload_len; SCLogDebug("seg1_end %u, seg2_end %u", seg1_end, seg2_end); #if 0 SCLogDebug("seg1"); PrintRawDataFp(stdout,seg1->payload,seg1->payload_len); SCLogDebug("seg2"); PrintRawDataFp(stdout,seg2->payload,seg2->payload_len); #endif /* get the minimal seg*_end */ uint32_t end = (SEQ_GT(seg1_end, seg2_end)) ? seg2_end : seg1_end; /* and the max seq */ uint32_t seq = (SEQ_LT(seg1->seq, seg2->seq)) ? seg2->seq : seg1->seq; SCLogDebug("seq %u, end %u", seq, end); uint16_t seg1_off = seq - seg1->seq; uint16_t seg2_off = seq - seg2->seq; SCLogDebug("seg1_off %u, seg2_off %u", seg1_off, seg2_off); uint32_t range = end - seq; SCLogDebug("range %u", range); BUG_ON(range > 65536); if (range) { int r = SCMemcmp(seg1->payload+seg1_off, seg2->payload+seg2_off, range); #if 0 if (r) { PrintRawDataFp(stdout,seg1->payload+seg1_off,range); PrintRawDataFp(stdout,seg2->payload+seg2_off,range); PrintRawDataFp(stdout,seg1->payload,seg1->payload_len); PrintRawDataFp(stdout,seg2->payload,seg2->payload_len); } #endif SCReturnInt(r); } SCReturnInt(0); } }