Exemple #1
0
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);
  }
}
Exemple #2
0
/* 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);
  }
Exemple #3
0
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);
    }
}