Пример #1
0
 /* Build a timestamp option (12 bytes long) at the specified options pointer)
  *
  * @param pcb tcp_pcb
  * @param opts option pointer where to store the timestamp option
  */
 private void tcp_build_timestamp_option(tcp_pcb pcb, pointer opts)
 {
     /* Pad with two NOP options to make everything nicely aligned */
     opts.SetValue((uint)lwip.PP_HTONL(0x0101080A));
     opts += sizeof(uint);
     opts.SetValue((uint)lwip.lwip_htonl(lwip.sys.sys_now()));
     opts += sizeof(uint);
     opts.SetValue((uint)lwip.lwip_htonl(pcb.ts_recent));
 }
Пример #2
0
 public static bool tcp_nagle_disabled(tcp_pcb pcb)
 {
     return (pcb.flags & tcp_pcb.TF_NODELAY) != 0;
 }
Пример #3
0
        /** Send an ACK without data.
         *
         * @param pcb Protocol control block for the TCP connection to send the ACK
         */
        public err_t tcp_send_empty_ack(tcp_pcb pcb)
        {
            pbuf p;
            tcp_hdr tcphdr;
            byte optlen = 0;

            #if LWIP_TCP_TIMESTAMPS
            if ((pcb.flags & tcp_pcb.TF_TIMESTAMP) != 0) {
                optlen = (byte)tcp_seg.LWIP_TCP_OPT_LENGTH(tcp_seg.TF_SEG_OPTS_TS);
            }
            #endif

            p = tcp_output_alloc_header(pcb, optlen, 0, lwip.lwip_htonl(pcb.snd_nxt));
            if (p == null) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG, "tcp_output: (ACK) could not allocate pbuf\n");
                return err_t.ERR_BUF;
            }
            tcphdr = new tcp_hdr(p.payload);
            lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG,
                        "tcp_output: sending ACK for {0}\n", pcb.rcv_nxt);
            /* remove ACK flags from the PCB, as we send an empty ACK now */
            pcb.flags &= unchecked((byte)~(tcp_pcb.TF_ACK_DELAY | tcp_pcb.TF_ACK_NOW));

            /* NB. MSS option is only sent on SYNs, so ignore it here */
            #if LWIP_TCP_TIMESTAMPS
            pcb.ts_lastacksent = pcb.rcv_nxt;

            if ((pcb.flags & tcp_pcb.TF_TIMESTAMP) != 0) {
                tcp_build_timestamp_option(pcb, new pointer(tcphdr.data, tcphdr.offset + tcp_hdr.length));
            }
            #endif

            #if CHECKSUM_GEN_TCP
            tcphdr.chksum = lwip.inet_chksum_pseudo(p, pcb.local_ip, pcb.remote_ip,
                  lwip.IP_PROTO_TCP, p.tot_len);
            #endif
            #if LWIP_NETIF_HWADDRHINT
            lwip.ip_output_hinted(p, pcb.local_ip, pcb.remote_ip, pcb.ttl, pcb.tos,
                lwip.IP_PROTO_TCP, pcb.addr_hint);
            #else // LWIP_NETIF_HWADDRHINT
            lwip.ip_output(p, pcb.local_ip, pcb.remote_ip, pcb.ttl, pcb.tos,
                lwip.IP_PROTO_TCP);
            #endif // LWIP_NETIF_HWADDRHINT
            lwip.pbuf_free(p);

            return err_t.ERR_OK;
        }
Пример #4
0
        /**
         * Handle retransmission after three dupacks received
         *
         * @param pcb the tcp_pcb for which to retransmit the first unacked segment
         */
        public static void tcp_rexmit_fast(tcp_pcb pcb)
        {
            if (pcb.unacked != null && (pcb.flags & tcp_pcb.TF_INFR) == 0) {
                /* This is fast retransmit. Retransmit the first unacked segment. */
                lwip.LWIP_DEBUGF(opt.TCP_FR_DEBUG,
                            "tcp_receive: dupacks {0} ({1}"
                             + "), fast retransmit {2}\n",
                             (ushort)pcb.dupacks, pcb.lastack,
                             lwip.lwip_ntohl(pcb.unacked.tcphdr.seqno));
                tcp_rexmit(pcb);

                /* Set ssthresh to half of the minimum of the current
                 * cwnd and the advertised window */
                if (pcb.cwnd > pcb.snd_wnd) {
                    pcb.ssthresh = (ushort)(pcb.snd_wnd / 2);
                }
                else {
                    pcb.ssthresh = (ushort)(pcb.cwnd / 2);
                }

                /* The minimum value for ssthresh should be 2 MSS */
                if (pcb.ssthresh < 2 * pcb.mss) {
                    lwip.LWIP_DEBUGF(opt.TCP_FR_DEBUG,
                                "tcp_receive: The minimum value for ssthresh {0}"
                                 + " should be min 2 mss {1}...\n",
                                 pcb.ssthresh, 2 * pcb.mss);
                    pcb.ssthresh = (ushort)(2 * pcb.mss);
                }

                pcb.cwnd = (ushort)(pcb.ssthresh + 3 * pcb.mss);
                pcb.flags |= tcp_pcb.TF_INFR;
            }
        }
Пример #5
0
        private static pbuf tcp_pbuf_prealloc(pbuf_layer layer, ushort length, ushort mx,
			out ushort os, tcp_pcb pcb, byte api, byte fst)
        {
            os = 0;
            return lwip.pbuf_alloc(layer, length, pbuf_type.PBUF_RAM);
        }
Пример #6
0
        /**
         * Allocate a pbuf_type.PBUF_RAM pbuf, perhaps with extra space at the end.
         *
         * This function is like lwip.pbuf_alloc(layer, length, pbuf_type.PBUF_RAM) except
         * there may be extra bytes available at the end.
         *
         * @param layer flag to define header size.
         * @param length size of the pbuf's payload.
         * @param max_length maximum usable size of payload+oversize.
         * @param oversize pointer to a ushort that will receive the number of usable tail bytes.
         * @param pcb The TCP connection that willo enqueue the pbuf.
         * @param apiflags API flags given to tcp_write.
         * @param first_seg true when this pbuf will be used in the first enqueued segment.
         * @param
         */
        private pbuf tcp_pbuf_prealloc(pbuf_layer layer, ushort length, ushort max_length,
						  ref ushort oversize, tcp_pcb pcb, byte apiflags,
						  byte first_seg)
        {
            pbuf p;
            ushort alloc = length;

            #if LWIP_NETIF_TX_SINGLE_PBUF
            //LWIP_UNUSED_ARG(max_length);
            //LWIP_UNUSED_ARG(pcb);
            //LWIP_UNUSED_ARG(apiflags);
            //LWIP_UNUSED_ARG(first_seg);
            /* always create MSS-sized pbufs */
            alloc = max_length;
            #else // LWIP_NETIF_TX_SINGLE_PBUF
            if (length < max_length)
            {
                /* Should we allocate an oversized pbuf, or just the minimum
                 * length required? If tcp_write is going to be called again
                 * before this segment is transmitted, we want the oversized
                 * buffer. If the segment will be transmitted immediately, we can
                 * save memory by allocating only length. We use a simple
                 * heuristic based on the following information:
                 *
                 * Did the user set TCP_WRITE_FLAG_MORE?
                 *
                 * Will the Nagle algorithm defer transmission of this segment?
                 */
                if ((apiflags & tcp.TCP_WRITE_FLAG_MORE) != 0 ||
                    ((pcb.flags & tcp_pcb.TF_NODELAY) == 0 &&
                     (first_seg == 0 ||
                      pcb.unsent != null ||
                      pcb.unacked != null)))
                {
                    alloc = (ushort)Math.Min(max_length, lwip.LWIP_MEM_ALIGN_SIZE(length + opt.TCP_OVERSIZE));
                }
            }
            #endif // LWIP_NETIF_TX_SINGLE_PBUF
            p = lwip.pbuf_alloc(layer, alloc, pbuf_type.PBUF_RAM);
            if (p == null) {
                return null;
            }
            lwip.LWIP_ASSERT("need unchained pbuf", p.next == null);
            oversize = (ushort)(p.len - length);
            /* trim p.len to the currently used size */
            p.len = p.tot_len = length;
            return p;
        }
Пример #7
0
        /** Allocate a pbuf and create a tcphdr at p.payload, used for output
         * functions other than the default tcp.tcp_output . tcp_output_segment
         * (e.g. tcp_send_empty_ack, etc.)
         *
         * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
         * @param optlen length of header-options
         * @param datalen length of tcp data to reserve in pbuf
         * @param seqno_be seqno in network byte order (big-endian)
         * @return pbuf with p.payload being the tcp_hdr
         */
        private pbuf tcp_output_alloc_header(tcp_pcb pcb, ushort optlen, ushort datalen,
							  uint seqno_be /* already in network byte order */)
        {
            tcp_hdr tcphdr;
            pbuf p = lwip.pbuf_alloc(pbuf_layer.PBUF_IP, (ushort)(tcp.TCP_HLEN + optlen + datalen), pbuf_type.PBUF_RAM);
            if (p != null) {
                lwip.LWIP_ASSERT("check that first pbuf can hold tcp_hdr",
                             (p.len >= tcp.TCP_HLEN + optlen));
                tcphdr = new tcp_hdr(p.payload);
                tcphdr.src = lwip.lwip_htons(pcb.local_port);
                tcphdr.dest = lwip.lwip_htons(pcb.remote_port);
                tcphdr.seqno = seqno_be;
                tcphdr.ackno = lwip.lwip_htonl(pcb.rcv_nxt);
                tcp_hdr.TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), tcp.TCP_ACK);
                tcphdr.wnd = lwip.lwip_htons(pcb.rcv_ann_wnd);
                tcphdr.chksum = 0;
                tcphdr.urgp = 0;

                /* If we're sending a packet, update the announced right window edge */
                pcb.rcv_ann_right_edge = pcb.rcv_nxt + pcb.rcv_ann_wnd;
            }
            return p;
        }
Пример #8
0
        /**
         * Send keepalive packets to keep a connection active although
         * no data is sent over it.
         *
         * Called by tcp_slowtmr()
         *
         * @param pcb the tcp_pcb for which to send a keepalive packet
         */
        public void tcp_keepalive(tcp_pcb pcb)
        {
            pbuf p;
            tcp_hdr tcphdr;

            lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "tcp_keepalive: sending KEEPALIVE probe to {0}.{1}.{2}.{3}\n",
                                    ip_addr.ip4_addr1_16(pcb.remote_ip), ip_addr.ip4_addr2_16(pcb.remote_ip),
                                    ip_addr.ip4_addr3_16(pcb.remote_ip), ip_addr.ip4_addr4_16(pcb.remote_ip));

            lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "tcp_keepalive: tcp_ticks {0}   pcb.tmr {1} pcb.keep_cnt_sent {2}\n",
                                    tcp_ticks, pcb.tmr, pcb.keep_cnt_sent);

            p = tcp_output_alloc_header(pcb, 0, 0, lwip.lwip_htonl(pcb.snd_nxt - 1));
            if (p == null) {
                lwip.LWIP_DEBUGF(opt.TCP_DEBUG,
                            ("tcp_keepalive: could not allocate memory for pbuf\n"));
                return;
            }
            tcphdr = new tcp_hdr(p.payload);

            #if CHECKSUM_GEN_TCP
            tcphdr.chksum = lwip.inet_chksum_pseudo(p, pcb.local_ip, pcb.remote_ip,
                                                lwip.IP_PROTO_TCP, p.tot_len);
            #endif
            ++lwip.lwip_stats.tcp.xmit;

            /* Send output to IP */
            #if LWIP_NETIF_HWADDRHINT
            lwip.ip_output_hinted(p, pcb.local_ip, pcb.remote_ip, pcb.ttl, 0, lwip.IP_PROTO_TCP,
                pcb.addr_hint);
            #else // LWIP_NETIF_HWADDRHINT
            lwip.ip_output(p, pcb.local_ip, pcb.remote_ip, pcb.ttl, 0, lwip.IP_PROTO_TCP);
            #endif // LWIP_NETIF_HWADDRHINT

            lwip.pbuf_free(p);

            lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "tcp_keepalive: seqno {0} ackno {1}.\n",
                                    pcb.snd_nxt - 1, pcb.rcv_nxt);
        }
Пример #9
0
        /**
         * Implements the TCP state machine. Called by tcp_input. In some
         * states tcp_receive() is called to receive data. The tcp_seg
         * argument will be freed by the caller (tcp_input()) unless the
         * recv_data pointer in the pcb is set.
         *
         * @param pcb the tcp_pcb for which a segment arrived
         *
         * @note the segment which arrived is saved in global variables, therefore only the pcb
         *       involved is passed as a parameter to this function
         */
        private err_t tcp_process(tcp_pcb pcb)
        {
            tcp_seg rseg;
            byte acceptable = 0;
            err_t err;

            err = err_t.ERR_OK;

            /* Process incoming RST segments. */
            if ((flags & tcp.TCP_RST) != 0) {
                /* First, determine if the reset is acceptable. */
                if (pcb.state == tcp_state.SYN_SENT) {
                    if (ackno == pcb.snd_nxt) {
                        acceptable = 1;
                    }
                }
                else {
                    if (tcp.TCP_SEQ_BETWEEN(seqno, pcb.rcv_nxt,
                                        pcb.rcv_nxt + pcb.rcv_wnd)) {
                        acceptable = 1;
                    }
                }

                if (acceptable != 0) {
                    lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "tcp_process: Connection RESET\n");
                    lwip.LWIP_ASSERT("tcp_input: pcb.state != tcp_state.CLOSED", pcb.state != tcp_state.CLOSED);
                    recv_flags |= tcp.TF_RESET;
                    pcb.flags &= unchecked((byte)~tcp_pcb.TF_ACK_DELAY);
                    return err_t.ERR_RST;
                }
                else {
                    lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "tcp_process: unacceptable reset seqno {0} rcv_nxt {1}\n",
                     seqno, pcb.rcv_nxt);
                    lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "tcp_process: unacceptable reset seqno {0} rcv_nxt {1}\n",
                     seqno, pcb.rcv_nxt);
                    return err_t.ERR_OK;
                }
            }

            if ((flags & tcp.TCP_SYN) != 0 && (pcb.state != tcp_state.SYN_SENT && pcb.state != tcp_state.SYN_RCVD)) {
                /* Cope with new connection attempt after remote end crashed */
                tcp.tcp_ack_now(pcb);
                return err_t.ERR_OK;
            }

            if ((pcb.flags & tcp_pcb.TF_RXCLOSED) == 0) {
                /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
                pcb.tmr = tcp_ticks;
            }
            pcb.keep_cnt_sent = 0;

            tcp_parseopt(pcb);

            /* Do different things depending on the TCP state. */
            switch (pcb.state) {
            case tcp_state.SYN_SENT:
                lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "SYN-SENT: ackno {0} pcb.snd_nxt {1} unacked {2}\n", ackno,
                 pcb.snd_nxt, lwip.lwip_ntohl(pcb.unacked.tcphdr.seqno));
                /* received SYN ACK with expected sequence number? */
                if ((flags & tcp.TCP_ACK) != 0 && (flags & tcp.TCP_SYN) != 0
                    && ackno == lwip.lwip_ntohl(pcb.unacked.tcphdr.seqno) + 1) {
                    pcb.snd_buf++;
                    pcb.rcv_nxt = seqno + 1;
                    pcb.rcv_ann_right_edge = pcb.rcv_nxt;
                    pcb.lastack = ackno;
                    pcb.snd_wnd = tcphdr.wnd;
                    pcb.snd_wnd_max = tcphdr.wnd;
                    pcb.snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
                    pcb.state = tcp_state.ESTABLISHED;

            #if TCP_CALCULATE_EFF_SEND_MSS
                    pcb.mss = tcp_eff_send_mss(pcb.mss, pcb.remote_ip);
            #endif // TCP_CALCULATE_EFF_SEND_MSS

                    /* Set ssthresh again after changing pcb.mss (already set in tcp_connect
                     * but for the default value of pcb.mss) */
                    pcb.ssthresh = (ushort)(pcb.mss * 10);

                    pcb.cwnd = (ushort)((pcb.cwnd == 1) ? (pcb.mss * 2) : pcb.mss);
                    lwip.LWIP_ASSERT("pcb.snd_queuelen > 0", (pcb.snd_queuelen > 0));
                    --pcb.snd_queuelen;
                    lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "tcp_process: SYN-SENT --queuelen {0}\n", (ushort)pcb.snd_queuelen);
                    rseg = pcb.unacked;
                    pcb.unacked = rseg.next;
                    tcp_seg_free(rseg);

                    /* If there's nothing left to acknowledge, stop the retransmit
                       timer, otherwise reset it to start again */
                    if (pcb.unacked == null)
                        pcb.rtime = -1;
                    else {
                        pcb.rtime = 0;
                        pcb.nrtx = 0;
                    }

                    /* Call the user specified function to call when sucessfully
                     * connected. */
                    TCP_EVENT_CONNECTED(pcb, err_t.ERR_OK, out err);
                    if (err == err_t.ERR_ABRT) {
                        return err_t.ERR_ABRT;
                    }
                    tcp.tcp_ack_now(pcb);
                }
                /* received ACK? possibly a half-open connection */
                else if ((flags & tcp.TCP_ACK) != 0) {
                    /* send a RST to bring the other side in a non-synchronized state. */
                    tcp_rst(ackno, seqno + tcplen, lwip.ip_current_dest_addr(), lwip.ip_current_src_addr(),
                        tcphdr.dest, tcphdr.src);
                }
                break;
            case tcp_state.SYN_RCVD:
                if ((flags & tcp.TCP_ACK) != 0) {
                    /* expected ACK number? */
                    if (tcp.TCP_SEQ_BETWEEN(ackno, pcb.lastack + 1, pcb.snd_nxt)) {
                        ushort old_cwnd;
                        pcb.state = tcp_state.ESTABLISHED;
                        lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "TCP connection established {0} . {1}.\n", inseg.tcphdr.src, inseg.tcphdr.dest);
            #if LWIP_CALLBACK_API
                        lwip.LWIP_ASSERT("pcb.accept != null", pcb.accept != null);
            #endif
                        /* Call the accept function. */
                        TCP_EVENT_ACCEPT(pcb, err_t.ERR_OK, out err);
                        if (err != err_t.ERR_OK) {
                            /* If the accept function returns with an error, we abort
                             * the connection. */
                            /* Already aborted? */
                            if (err != err_t.ERR_ABRT) {
                                tcp_abort(pcb);
                            }
                            return err_t.ERR_ABRT;
                        }
                        old_cwnd = pcb.cwnd;
                        /* If there was any data contained within this ACK,
                         * we'd better pass it on to the application as well. */
                        tcp_receive(pcb);

                        /* Prevent ACK for SYN to generate a sent event */
                        if (pcb.acked != 0) {
                            pcb.acked--;
                        }

                        pcb.cwnd = (ushort)((old_cwnd == 1) ? (pcb.mss * 2) : pcb.mss);

                        if ((recv_flags & tcp.TF_GOT_FIN) != 0) {
                            tcp.tcp_ack_now(pcb);
                            pcb.state = tcp_state.CLOSE_WAIT;
                        }
                    }
                    else {
                        /* incorrect ACK number, send RST */
                        tcp_rst(ackno, seqno + tcplen, lwip.ip_current_dest_addr(), lwip.ip_current_src_addr(),
                            tcphdr.dest, tcphdr.src);
                    }
                }
                else if ((flags & tcp.TCP_SYN) != 0 && (seqno == pcb.rcv_nxt - 1)) {
                    /* Looks like another copy of the SYN - retransmit our SYN-ACK */
                    tcp.tcp_rexmit(pcb);
                }
                break;
            case tcp_state.CLOSE_WAIT:
            /* FALLTHROUGH */
            case tcp_state.ESTABLISHED:
                tcp_receive(pcb);
                if ((recv_flags & tcp.TF_GOT_FIN) != 0) { /* passive close */
                    tcp.tcp_ack_now(pcb);
                    pcb.state = tcp_state.CLOSE_WAIT;
                }
                break;
            case tcp_state.FIN_WAIT_1:
                tcp_receive(pcb);
                if ((recv_flags & tcp.TF_GOT_FIN) != 0) {
                    if ((flags & tcp.TCP_ACK) != 0 && (ackno == pcb.snd_nxt)) {
                        lwip.LWIP_DEBUGF(opt.TCP_DEBUG,
                          "TCP connection closed: tcp_state.FIN_WAIT_1 {0} . {1}.\n", inseg.tcphdr.src, inseg.tcphdr.dest);
                        tcp.tcp_ack_now(pcb);
                        tcp_pcb_purge(pcb);
                        TCP_RMV_ACTIVE(pcb);
                        pcb.state = tcp_state.TIME_WAIT;
                        TCP_REG(ref tcp_tw_pcbs, pcb);
                    }
                    else {
                        tcp.tcp_ack_now(pcb);
                        pcb.state = tcp_state.CLOSING;
                    }
                }
                else if ((flags & tcp.TCP_ACK) != 0 && (ackno == pcb.snd_nxt)) {
                    pcb.state = tcp_state.FIN_WAIT_2;
                }
                break;
            case tcp_state.FIN_WAIT_2:
                tcp_receive(pcb);
                if ((recv_flags & tcp.TF_GOT_FIN) != 0) {
                    lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "TCP connection closed: tcp_state.FIN_WAIT_2 {0} . {1}.\n", inseg.tcphdr.src, inseg.tcphdr.dest);
                    tcp.tcp_ack_now(pcb);
                    tcp_pcb_purge(pcb);
                    TCP_RMV_ACTIVE(pcb);
                    pcb.state = tcp_state.TIME_WAIT;
                    TCP_REG(ref tcp_tw_pcbs, pcb);
                }
                break;
            case tcp_state.CLOSING:
                tcp_receive(pcb);
                if ((flags & tcp.TCP_ACK) != 0 && ackno == pcb.snd_nxt) {
                    lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "TCP connection closed: tcp_state.CLOSING {0} . {1}.\n", inseg.tcphdr.src, inseg.tcphdr.dest);
                    tcp_pcb_purge(pcb);
                    TCP_RMV_ACTIVE(pcb);
                    pcb.state = tcp_state.TIME_WAIT;
                    TCP_REG(ref tcp_tw_pcbs, pcb);
                }
                break;
            case tcp_state.LAST_ACK:
                tcp_receive(pcb);
                if ((flags & tcp.TCP_ACK) != 0 && ackno == pcb.snd_nxt) {
                    lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "TCP connection closed: tcp_state.LAST_ACK {0} . {1}.\n", inseg.tcphdr.src, inseg.tcphdr.dest);
                    /* bugfix #21699: don't set pcb.state to tcp_state.CLOSED here or we risk leaking segments */
                    recv_flags |= tcp.TF_CLOSED;
                }
                break;
            default:
                break;
            }
            return err_t.ERR_OK;
        }
Пример #10
0
        /**
         * Parses the options contained in the incoming segment.
         *
         * Called from tcp_listen_input() and tcp_process().
         * Currently, only the MSS option is supported!
         *
         * @param pcb the tcp_pcb for which a segment arrived
         */
        private void tcp_parseopt(tcp_pcb pcb)
        {
            ushort c, max_c;
            ushort mss;
            pointer opts;
            byte opt;
            #if LWIP_TCP_TIMESTAMPS
            uint tsval;
            #endif

            opts = tcphdr + tcp.TCP_HLEN;

            /* Parse the TCP MSS option, if present. */
            if (tcp_hdr.TCPH_HDRLEN(tcphdr) > 0x5) {
                max_c = (ushort)((tcp_hdr.TCPH_HDRLEN(tcphdr) - 5) << 2);
                for (c = 0; c < max_c;) {
                    opt = opts[c];
                    switch (opt) {
                    case 0x00:
                        /* End of options. */
                        lwip.LWIP_DEBUGF(uITron3.opt.TCP_INPUT_DEBUG, "tcp_parseopt: EOL\n");
                        return;
                    case 0x01:
                        /* NOP option. */
                        ++c;
                        lwip.LWIP_DEBUGF(uITron3.opt.TCP_INPUT_DEBUG, "tcp_parseopt: NOP\n");
                        break;
                    case 0x02:
                        lwip.LWIP_DEBUGF(uITron3.opt.TCP_INPUT_DEBUG, "tcp_parseopt: MSS\n");
                        if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
                            /* Bad length */
                            lwip.LWIP_DEBUGF(uITron3.opt.TCP_INPUT_DEBUG, "tcp_parseopt: bad length\n");
                            return;
                        }
                        /* An MSS option with the right option length. */
                        mss = (ushort)((opts[c + 2] << 8) | opts[c + 3]);
                        /* Limit the mss to the configured TCP_MSS and prevent division by zero */
                        pcb.mss = (ushort)(((mss > uITron3.opt.TCP_MSS) || (mss == 0)) ? uITron3.opt.TCP_MSS : mss);
                        /* Advance to next option */
                        c += 0x04;
                        break;
            #if LWIP_TCP_TIMESTAMPS
                    case 0x08:
                        lwip.LWIP_DEBUGF(uITron3.opt.TCP_INPUT_DEBUG, "tcp_parseopt: TS\n");
                        if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
                            /* Bad length */
                            lwip.LWIP_DEBUGF(uITron3.opt.TCP_INPUT_DEBUG, "tcp_parseopt: bad length\n");
                            return;
                        }
                        /* TCP timestamp option with valid length */
                        tsval = (uint)((opts[c + 2]) | (opts[c + 3] << 8) |
                            (opts[c + 4] << 16) | (opts[c + 5] << 24));
                        if ((flags & tcp.TCP_SYN) != 0) {
                            pcb.ts_recent = lwip.lwip_ntohl(tsval);
                            pcb.flags |= tcp_pcb.TF_TIMESTAMP;
                        }
                        else if (tcp.TCP_SEQ_BETWEEN(pcb.ts_lastacksent, seqno, seqno + tcplen)) {
                            pcb.ts_recent = lwip.lwip_ntohl(tsval);
                        }
                        /* Advance to next option */
                        c += 0x0A;
                        break;
            #endif
                    default:
                        lwip.LWIP_DEBUGF(uITron3.opt.TCP_INPUT_DEBUG, "tcp_parseopt: other\n");
                        if (opts[c + 1] == 0) {
                            lwip.LWIP_DEBUGF(uITron3.opt.TCP_INPUT_DEBUG, "tcp_parseopt: bad length\n");
                            /* If the length field is zero, the options are malformed
                               and we don't process them further. */
                            return;
                        }
                        /* All other options have a length field, so that we easily
                           can skip past them. */
                        c += opts[c + 1];
                        break;
                    }
                }
            }
        }
Пример #11
0
        /**
         * Called by tcp_input() when a segment arrives for a connection in
         * tcp_state.TIME_WAIT.
         *
         * @param pcb the tcp_pcb for which a segment arrived
         *
         * @note the segment which arrived is saved in global variables, therefore only the pcb
         *       involved is passed as a parameter to this function
         */
        private err_t tcp_timewait_input(tcp_pcb pcb)
        {
            /* RFC 1337: in tcp_state.TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
            /* RFC 793 3.9 Event Processing - Segment Arrives:
             * - first check sequence number - we skip that one in tcp_state.TIME_WAIT (always
             *   acceptable since we only send ACKs)
             * - second check the RST bit (... return) */
            if ((flags & tcp.TCP_RST) != 0) {
                return err_t.ERR_OK;
            }
            /* - fourth, check the SYN bit, */
            if ((flags & tcp.TCP_SYN) != 0) {
                /* If an incoming segment is not acceptable, an acknowledgment
                   should be sent in reply */
                if (tcp.TCP_SEQ_BETWEEN(seqno, pcb.rcv_nxt, pcb.rcv_nxt + pcb.rcv_wnd)) {
                    /* If the SYN is in the window it is an error, send a reset */
                    tcp_rst(ackno, seqno + tcplen, lwip.ip_current_dest_addr(), lwip.ip_current_src_addr(),
                        tcphdr.dest, tcphdr.src);
                    return err_t.ERR_OK;
                }
            }
            else if ((flags & tcp.TCP_FIN) != 0) {
                /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
                     Restart the 2 MSL time-wait timeout.*/
                pcb.tmr = tcp_ticks;
            }

            if ((tcplen > 0)) {
                /* Acknowledge data, FIN or out-of-window SYN */
                pcb.flags |= tcp_pcb.TF_ACK_NOW;
                return tcp_output(pcb);
            }
            return err_t.ERR_OK;
        }
Пример #12
0
 public static void tcp_accepted(tcp_pcb pcb)
 {
     lwip.LWIP_ASSERT("pcb.state == tcp_state.LISTEN (called for wrong pcb?)", (pcb).state == tcp_state.LISTEN);
 }
Пример #13
0
 public static ushort tcp_sndqueuelen(tcp_pcb pcb)
 {
     return pcb.snd_queuelen;
 }
Пример #14
0
 public static ushort tcp_sndbuf(tcp_pcb pcb)
 {
     return pcb.snd_buf;
 }
Пример #15
0
 public static ushort tcp_nagle_enable(tcp_pcb pcb)
 {
     return pcb.flags &= unchecked((byte)~tcp_pcb.TF_NODELAY);
 }
Пример #16
0
        /**
         * Create a TCP segment with prefilled header.
         *
         * Called by tcp_write and tcp_enqueue_flags.
         *
         * @param pcb Protocol control block for the TCP connection.
         * @param p pbuf that is used to hold the TCP header.
         * @param flags TCP flags for header.
         * @param seqno TCP sequence number of this packet
         * @param optflags options to include in TCP header
         * @return a new tcp_seg pointing to p, or null.
         * The TCP header is filled in except ackno and wnd.
         * p is freed on failure.
         */
        private tcp_seg tcp_create_segment(tcp_pcb pcb, pbuf p, byte flags, uint seqno, byte optflags)
        {
            tcp_seg seg;
            byte optlen = (byte)tcp_seg.LWIP_TCP_OPT_LENGTH(optflags);

            if ((seg = (tcp_seg)lwip.memp_malloc(memp_t.MEMP_TCP_SEG)) == null) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 2, "tcp_create_segment: no memory.\n");
                lwip.pbuf_free(p);
                return null;
            }
            seg.flags = optflags;
            seg.next = null;
            seg.p = p;
            seg.len = (ushort)(p.tot_len - optlen);
            #if TCP_OVERSIZE_DBGCHECK
            seg.oversize_left = 0;
            #endif // TCP_OVERSIZE_DBGCHECK
            #if TCP_CHECKSUM_ON_COPY
            seg.chksum = 0;
            seg.chksum_swapped = false;
            /* check optflags */
            lwip.LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
                        (optflags & tcp_seg.TF_SEG_DATA_CHECKSUMMED) == 0);
            #endif // TCP_CHECKSUM_ON_COPY

            /* build TCP header */
            if (lwip.pbuf_header(p, TCP_HLEN) != 0) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 2, "tcp_create_segment: no room for TCP header in pbuf.\n");
                ++lwip.lwip_stats.tcp.err;
                tcp_seg_free(seg);
                return null;
            }
            seg.tcphdr = new tcp_hdr(seg.p.payload);
            seg.tcphdr.src = lwip.lwip_htons(pcb.local_port);
            seg.tcphdr.dest = lwip.lwip_htons(pcb.remote_port);
            seg.tcphdr.seqno = lwip.lwip_htonl(seqno);
            /* ackno is set in tcp.tcp_output */
            tcp_hdr.TCPH_HDRLEN_FLAGS_SET(seg.tcphdr, (5 + optlen / 4), flags);
            /* wnd and chksum are set in tcp.tcp_output */
            seg.tcphdr.urgp = 0;
            return seg;
        }
Пример #17
0
        /**
         * Enqueue TCP options for transmission.
         *
         * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
         *
         * @param pcb Protocol control block for the TCP connection.
         * @param flags TCP header flags to set in the outgoing segment.
         * @param optdata pointer to TCP options, or null.
         * @param optlen length of TCP options in bytes.
         */
        public err_t tcp_enqueue_flags(tcp_pcb pcb, byte flags)
        {
            pbuf p;
            tcp_seg seg;
            byte optflags = 0;
            byte optlen = 0;

            lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "tcp_enqueue_flags: queuelen: {0}\n", (ushort)pcb.snd_queuelen);

            lwip.LWIP_ASSERT("tcp_enqueue_flags: need either tcp.TCP_SYN or tcp.TCP_FIN in flags (programmer violates API)",
                        (flags & (tcp.TCP_SYN | tcp.TCP_FIN)) != 0);

            /* check for configured max queuelen and possible overflow */
            if ((pcb.snd_queuelen >= opt.TCP_SND_QUEUELEN) || (pcb.snd_queuelen > tcp_pcb.TCP_SNDQUEUELEN_OVERFLOW)) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 3, "tcp_enqueue_flags: too long queue {0} (max {1})\n",
                                                   pcb.snd_queuelen, opt.TCP_SND_QUEUELEN);
                ++lwip.lwip_stats.tcp.memerr;
                pcb.flags |= tcp_pcb.TF_NAGLEMEMERR;
                return err_t.ERR_MEM;
            }

            if ((flags & tcp.TCP_SYN) != 0) {
                optflags = tcp_seg.TF_SEG_OPTS_MSS;
            }
            #if LWIP_TCP_TIMESTAMPS
            if ((pcb.flags & tcp_pcb.TF_TIMESTAMP) != 0) {
                optflags |= tcp_seg.TF_SEG_OPTS_TS;
            }
            #endif // LWIP_TCP_TIMESTAMPS
            optlen = (byte)tcp_seg.LWIP_TCP_OPT_LENGTH(optflags);

            /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
             * We need one available snd_buf byte to do that.
             * This means we can't send FIN while snd_buf==0. A better fix would be to
             * not include SYN and FIN sequence numbers in the snd_buf count. */
            if (pcb.snd_buf == 0) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 3, "tcp_enqueue_flags: no send buffer available\n");
                ++lwip.lwip_stats.tcp.memerr;
                return err_t.ERR_MEM;
            }

            /* Allocate pbuf with room for TCP header + options */
            if ((p = lwip.pbuf_alloc(pbuf_layer.PBUF_TRANSPORT, optlen, pbuf_type.PBUF_RAM)) == null) {
                pcb.flags |= tcp_pcb.TF_NAGLEMEMERR;
                ++lwip.lwip_stats.tcp.memerr;
                return err_t.ERR_MEM;
            }
            lwip.LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
                        (p.len >= optlen));

            /* Allocate memory for tcp_seg, and fill in fields. */
            if ((seg = tcp_create_segment(pcb, p, flags, pcb.snd_lbb, optflags)) == null) {
                pcb.flags |= tcp_pcb.TF_NAGLEMEMERR;
                ++lwip.lwip_stats.tcp.memerr;
                return err_t.ERR_MEM;
            }
            lwip.LWIP_ASSERT("seg.tcphdr not aligned", (seg.tcphdr.offset % opt.MEM_ALIGNMENT) == 0);
            lwip.LWIP_ASSERT("tcp_enqueue_flags: invalid segment length", seg.len == 0);

            lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | lwip.LWIP_DBG_TRACE,
                        "tcp_enqueue_flags: queueing {0}:{1} (0x{2:X})\n",
                         lwip.lwip_ntohl(seg.tcphdr.seqno),
                         lwip.lwip_ntohl(seg.tcphdr.seqno) + tcp_hdr.TCP_TCPLEN(seg),
                         (ushort)flags);

            /* Now append seg to pcb.unsent queue */
            if (pcb.unsent == null) {
                pcb.unsent = seg;
            }
            else {
                tcp_seg useg;
                for (useg = pcb.unsent; useg.next != null; useg = useg.next) ;
                useg.next = seg;
            }
            #if TCP_OVERSIZE
            /* The new unsent tail has no space */
            pcb.unsent_oversize = 0;
            #endif // TCP_OVERSIZE

            /* SYN and FIN bump the sequence number */
            if ((flags & tcp.TCP_SYN) != 0 || (flags & tcp.TCP_FIN) != 0) {
                pcb.snd_lbb++;
                /* optlen does not influence snd_buf */
                pcb.snd_buf--;
            }
            if ((flags & tcp.TCP_FIN) != 0) {
                pcb.flags |= tcp_pcb.TF_FIN;
            }

            /* update number of segments on the queues */
            pcb.snd_queuelen += lwip.pbuf_clen(seg.p);
            lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "tcp_enqueue_flags: {0} (after enqueued)\n", pcb.snd_queuelen);
            if (pcb.snd_queuelen != 0) {
                lwip.LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
                  pcb.unacked != null || pcb.unsent != null);
            }

            return err_t.ERR_OK;
        }
Пример #18
0
        /**
         * Called by tcp_process. Checks if the given segment is an ACK for outstanding
         * data, and if so frees the memory of the buffered data. Next, is places the
         * segment on any of the receive queues (pcb.recved or pcb.ooseq). If the segment
         * is buffered, the pbuf is referenced by lwip.pbuf_ref so that it will not be freed until
         * it has been removed from the buffer.
         *
         * If the incoming segment constitutes an ACK for a segment that was used for RTT
         * estimation, the RTT is estimated here as well.
         *
         * Called from tcp_process().
         */
        private void tcp_receive(tcp_pcb pcb)
        {
            tcp_seg next;
            #if TCP_QUEUE_OOSEQ
            tcp_seg prev, cseg;
            #endif // TCP_QUEUE_OOSEQ
            pbuf p;
            int off;
            short m;
            uint right_wnd_edge;
            ushort new_tot_len;
            int found_dupack = 0;
            #if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
            uint ooseq_blen;
            ushort ooseq_qlen;
            #endif // TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS

            lwip.LWIP_ASSERT("tcp_receive: wrong state", pcb.state >= tcp_state.ESTABLISHED);

            if ((flags & tcp.TCP_ACK) != 0) {
                right_wnd_edge = pcb.snd_wnd + pcb.snd_wl2;

                /* Update window. */
                if (tcp.TCP_SEQ_LT(pcb.snd_wl1, seqno) ||
                    (pcb.snd_wl1 == seqno && tcp.TCP_SEQ_LT(pcb.snd_wl2, ackno)) ||
                    (pcb.snd_wl2 == ackno && tcphdr.wnd > pcb.snd_wnd)) {
                    pcb.snd_wnd = tcphdr.wnd;
                    /* keep track of the biggest window announced by the remote host to calculate
                       the maximum segment size */
                    if (pcb.snd_wnd_max < tcphdr.wnd) {
                        pcb.snd_wnd_max = tcphdr.wnd;
                    }
                    pcb.snd_wl1 = seqno;
                    pcb.snd_wl2 = ackno;
                    if (pcb.snd_wnd == 0) {
                        if (pcb.persist_backoff == 0) {
                            /* start persist timer */
                            pcb.persist_cnt = 0;
                            pcb.persist_backoff = 1;
                        }
                    }
                    else if (pcb.persist_backoff > 0) {
                        /* stop persist timer */
                        pcb.persist_backoff = 0;
                    }
                    lwip.LWIP_DEBUGF(opt.TCP_WND_DEBUG, "tcp_receive: window update {0}\n", pcb.snd_wnd);
            #if TCP_WND_DEBUG
                }
                else {
                    if (pcb.snd_wnd != tcphdr.wnd) {
                        lwip.LWIP_DEBUGF(opt.TCP_WND_DEBUG,
                                    "tcp_receive: no window update lastack {0} ackno {1} wl1 {2} seqno {3} wl2 {4}\n",
                                     pcb.lastack, ackno, pcb.snd_wl1, seqno, pcb.snd_wl2);
                    }
            #endif // TCP_WND_DEBUG
                }

                /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
                 * duplicate ack if:
                 * 1) It doesn't ACK new data
                 * 2) length of received packet is zero (i.e. no payload)
                 * 3) the advertised window hasn't changed
                 * 4) There is outstanding unacknowledged data (retransmission timer running)
                 * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
                 *
                 * If it passes all five, should process as a dupack:
                 * a) dupacks < 3: do nothing
                 * b) dupacks == 3: fast retransmit
                 * c) dupacks > 3: increase cwnd
                 *
                 * If it only passes 1-3, should reset dupack counter (and add to
                 * stats, which we don't do in lwIP)
                 *
                 * If it only passes 1, should reset dupack counter
                 *
                 */

                /* Clause 1 */
                if (tcp.TCP_SEQ_LEQ(ackno, pcb.lastack)) {
                    pcb.acked = 0;
                    /* Clause 2 */
                    if (tcplen == 0) {
                        /* Clause 3 */
                        if (pcb.snd_wl2 + pcb.snd_wnd == right_wnd_edge) {
                            /* Clause 4 */
                            if (pcb.rtime >= 0) {
                                /* Clause 5 */
                                if (pcb.lastack == ackno) {
                                    found_dupack = 1;
                                    if ((byte)(pcb.dupacks + 1) > pcb.dupacks) {
                                        ++pcb.dupacks;
                                    }
                                    if (pcb.dupacks > 3) {
                                        /* Inflate the congestion window, but not if it means that
                                           the value overflows. */
                                        if ((ushort)(pcb.cwnd + pcb.mss) > pcb.cwnd) {
                                            pcb.cwnd += pcb.mss;
                                        }
                                    }
                                    else if (pcb.dupacks == 3) {
                                        /* Do fast retransmit */
                                        tcp.tcp_rexmit_fast(pcb);
                                    }
                                }
                            }
                        }
                    }
                    /* If Clause (1) or more is true, but not a duplicate ack, reset
                     * count of consecutive duplicate acks */
                    if (found_dupack == 0) {
                        pcb.dupacks = 0;
                    }
                }
                else if (tcp.TCP_SEQ_BETWEEN(ackno, pcb.lastack + 1, pcb.snd_nxt)) {
                    /* We come here when the ACK acknowledges new data. */

                    /* Reset the "IN Fast Retransmit" flag, since we are no longer
                       in fast retransmit. Also reset the congestion window to the
                       slow start threshold. */
                    if ((pcb.flags & tcp_pcb.TF_INFR) != 0) {
                        pcb.flags &= unchecked((byte)~tcp_pcb.TF_INFR);
                        pcb.cwnd = pcb.ssthresh;
                    }

                    /* Reset the number of retransmissions. */
                    pcb.nrtx = 0;

                    /* Reset the retransmission time-out. */
                    pcb.rto = (short)((pcb.sa >> 3) + pcb.sv);

                    /* Update the send buffer space. Diff between the two can never exceed 64K? */
                    pcb.acked = (ushort)(ackno - pcb.lastack);

                    pcb.snd_buf += pcb.acked;

                    /* Reset the fast retransmit variables. */
                    pcb.dupacks = 0;
                    pcb.lastack = ackno;

                    /* Update the congestion control variables (cwnd and
                       ssthresh). */
                    if (pcb.state >= tcp_state.ESTABLISHED) {
                        if (pcb.cwnd < pcb.ssthresh) {
                            if ((ushort)(pcb.cwnd + pcb.mss) > pcb.cwnd) {
                                pcb.cwnd += pcb.mss;
                            }
                            lwip.LWIP_DEBUGF(opt.TCP_CWND_DEBUG, "tcp_receive: slow start cwnd {0}\n", pcb.cwnd);
                        }
                        else {
                            ushort new_cwnd = (ushort)(pcb.cwnd + pcb.mss * pcb.mss / pcb.cwnd);
                            if (new_cwnd > pcb.cwnd) {
                                pcb.cwnd = new_cwnd;
                            }
                            lwip.LWIP_DEBUGF(opt.TCP_CWND_DEBUG, "tcp_receive: congestion avoidance cwnd {0}\n", pcb.cwnd);
                        }
                    }
                    lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "tcp_receive: ACK for {0}, unacked.seqno {1}:{2}\n",
                                                  ackno,
                                                  pcb.unacked != null ?
                                                  lwip.lwip_ntohl(pcb.unacked.tcphdr.seqno) : 0,
                                                  pcb.unacked != null ?
                                                  lwip.lwip_ntohl(pcb.unacked.tcphdr.seqno) + tcp_hdr.TCP_TCPLEN(pcb.unacked) : 0);

                    /* Remove segment from the unacknowledged list if the incoming
                       ACK acknowlegdes them. */
                    while (pcb.unacked != null &&
                           tcp.TCP_SEQ_LEQ(lwip.lwip_ntohl(pcb.unacked.tcphdr.seqno) +
                                       (uint)tcp_hdr.TCP_TCPLEN(pcb.unacked), ackno)) {
                        lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "tcp_receive: removing {0}:{1} from pcb.unacked\n",
                                                      lwip.lwip_ntohl(pcb.unacked.tcphdr.seqno),
                                                      lwip.lwip_ntohl(pcb.unacked.tcphdr.seqno) +
                                                      tcp_hdr.TCP_TCPLEN(pcb.unacked));

                        next = pcb.unacked;
                        pcb.unacked = pcb.unacked.next;

                        lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "tcp_receive: queuelen {0} ... ", (ushort)pcb.snd_queuelen);
                        lwip.LWIP_ASSERT("pcb.snd_queuelen >= pbuf_clen(next.p)", (pcb.snd_queuelen >= lwip.pbuf_clen(next.p)));
                        /* Prevent ACK for FIN to generate a sent event */
                        if ((pcb.acked != 0) && ((tcp_hdr.TCPH_FLAGS(next.tcphdr) & tcp.TCP_FIN) != 0)) {
                            pcb.acked--;
                        }

                        pcb.snd_queuelen -= lwip.pbuf_clen(next.p);
                        tcp_seg_free(next);

                        lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "{0} (after freeing unacked)\n", (ushort)pcb.snd_queuelen);
                        if (pcb.snd_queuelen != 0) {
                            lwip.LWIP_ASSERT("tcp_receive: valid queue length", pcb.unacked != null ||
                                        pcb.unsent != null);
                        }
                    }

                    /* If there's nothing left to acknowledge, stop the retransmit
                       timer, otherwise reset it to start again */
                    if (pcb.unacked == null)
                        pcb.rtime = -1;
                    else
                        pcb.rtime = 0;

                    pcb.polltmr = 0;
                }
                else {
                    /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
                    pcb.acked = 0;
                }

                /* We go through the .unsent list to see if any of the segments
                   on the list are acknowledged by the ACK. This may seem
                   strange since an "unsent" segment shouldn't be acked. The
                   rationale is that lwIP puts all outstanding segments on the
                   .unsent list after a retransmission, so these segments may
                   in fact have been sent once. */
                while (pcb.unsent != null &&
                       tcp.TCP_SEQ_BETWEEN(ackno, lwip.lwip_ntohl(pcb.unsent.tcphdr.seqno) +
                                       (uint)tcp_hdr.TCP_TCPLEN(pcb.unsent), pcb.snd_nxt)) {
                    lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "tcp_receive: removing %{0}:{1} from pcb.unsent\n",
                                                  lwip.lwip_ntohl(pcb.unsent.tcphdr.seqno), lwip.lwip_ntohl(pcb.unsent.tcphdr.seqno) +
                                                  tcp_hdr.TCP_TCPLEN(pcb.unsent));

                    next = pcb.unsent;
                    pcb.unsent = pcb.unsent.next;
            #if TCP_OVERSIZE
                    if (pcb.unsent == null) {
                        pcb.unsent_oversize = 0;
                    }
            #endif // TCP_OVERSIZE
                    lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "tcp_receive: queuelen {0} ... ", (ushort)pcb.snd_queuelen);
                    lwip.LWIP_ASSERT("pcb.snd_queuelen >= pbuf_clen(next.p)", (pcb.snd_queuelen >= lwip.pbuf_clen(next.p)));
                    /* Prevent ACK for FIN to generate a sent event */
                    if ((pcb.acked != 0) && ((tcp_hdr.TCPH_FLAGS(next.tcphdr) & tcp.TCP_FIN) != 0)) {
                        pcb.acked--;
                    }
                    pcb.snd_queuelen -= lwip.pbuf_clen(next.p);
                    tcp_seg_free(next);
                    lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "{0} (after freeing unsent)\n", (ushort)pcb.snd_queuelen);
                    if (pcb.snd_queuelen != 0) {
                        lwip.LWIP_ASSERT("tcp_receive: valid queue length",
                          pcb.unacked != null || pcb.unsent != null);
                    }
                }
                /* End of ACK for new data processing. */

                lwip.LWIP_DEBUGF(opt.TCP_RTO_DEBUG, "tcp_receive: pcb.rttest {0} rtseq {1} ackno {2}\n",
                                            pcb.rttest, pcb.rtseq, ackno);

                /* RTT estimation calculations. This is done by checking if the
                   incoming segment acknowledges the segment we use to take a
                   round-trip time measurement. */
                if (pcb.rttest != 0 && tcp.TCP_SEQ_LT(pcb.rtseq, ackno)) {
                    /* diff between this shouldn't exceed 32K since this are tcp timer ticks
                       and a round-trip shouldn't be that long... */
                    m = (short)(tcp_ticks - pcb.rttest);

                    lwip.LWIP_DEBUGF(opt.TCP_RTO_DEBUG, "tcp_receive: experienced rtt {0} ticks ({1} msec).\n",
                                                m, m * tcp.TCP_SLOW_INTERVAL);

                    /* This is taken directly from VJs original code in his paper */
                    m = (short)(m - (pcb.sa >> 3));
                    pcb.sa += m;
                    if (m < 0) {
                        m = (short)-m;
                    }
                    m = (short)(m - (pcb.sv >> 2));
                    pcb.sv += m;
                    pcb.rto = (short)((pcb.sa >> 3) + pcb.sv);

                    lwip.LWIP_DEBUGF(opt.TCP_RTO_DEBUG, "tcp_receive: RTO {0} ({1} milliseconds)\n",
                                                pcb.rto, pcb.rto * tcp.TCP_SLOW_INTERVAL);

                    pcb.rttest = 0;
                }
            }

            /* If the incoming segment contains data, we must process it
               further unless the pcb already received a FIN.
               (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, tcp_state.CLOSING,
               LAST-ACK and TIME-WAIT: "Ignore the segment text.") */
            if ((tcplen > 0) && (pcb.state < tcp_state.CLOSE_WAIT)) {
                /* This code basically does three things:

                +) If the incoming segment contains data that is the next
                in-sequence data, this data is passed to the application. This
                might involve trimming the first edge of the data. The rcv_nxt
                variable and the advertised window are adjusted.

                +) If the incoming segment has data that is above the next
                sequence number expected (.rcv_nxt), the segment is placed on
                the .ooseq queue. This is done by finding the appropriate
                place in the .ooseq queue (which is ordered by sequence
                number) and trim the segment in both ends if needed. An
                immediate ACK is sent to indicate that we received an
                out-of-sequence segment.

                +) Finally, we check if the first segment on the .ooseq queue
                now is in sequence (i.e., if rcv_nxt >= ooseq.seqno). If
                rcv_nxt > ooseq.seqno, we must trim the first edge of the
                segment on .ooseq before we adjust rcv_nxt. The data in the
                segments that are now on sequence are chained onto the
                incoming segment so that we only need to call the application
                once.
                */

                /* First, we check if we must trim the first edge. We have to do
                   this if the sequence number of the incoming segment is less
                   than rcv_nxt, and the sequence number plus the length of the
                   segment is larger than rcv_nxt. */
                /*    if (tcp.TCP_SEQ_LT(seqno, pcb.rcv_nxt)){
                      if (tcp.TCP_SEQ_LT(pcb.rcv_nxt, seqno + tcplen)) {*/
                if (tcp.TCP_SEQ_BETWEEN(pcb.rcv_nxt, seqno + 1, seqno + tcplen - 1)) {
                    /* Trimming the first edge is done by pushing the payload
                       pointer in the pbuf downwards. This is somewhat tricky since
                       we do not want to discard the full contents of the pbuf up to
                       the new starting point of the data since we have to keep the
                       TCP header which is present in the first pbuf in the chain.

                       What is done is really quite a nasty hack: the first pbuf in
                       the pbuf chain is pointed to by inseg.p. Since we need to be
                       able to deallocate the whole pbuf, we cannot change this
                       inseg.p pointer to point to any of the later pbufs in the
                       chain. Instead, we point the .payload pointer in the first
                       pbuf to data in one of the later pbufs. We also set the
                       inseg.data pointer to point to the right place. This way, the
                       .p pointer will still point to the first pbuf, but the
                       .p.payload pointer will point to data in another pbuf.

                       After we are done with adjusting the pbuf pointers we must
                       adjust the .data pointer in the seg and the segment
                       length.*/

                    off = (int)(pcb.rcv_nxt - seqno);
                    p = inseg.p;
                    lwip.LWIP_ASSERT("inseg.p != null", inseg.p != null);
                    lwip.LWIP_ASSERT("insane offset!", (off < 0x7fff));
                    if (inseg.p.len < off) {
                        lwip.LWIP_ASSERT("pbuf too short!", (((int)inseg.p.tot_len) >= off));
                        new_tot_len = (ushort)(inseg.p.tot_len - off);
                        while (p.len < off) {
                            off -= p.len;
                            /* KJM following line changed (with addition of new_tot_len var)
                               to fix bug #9076
                               inseg.p.tot_len -= p.len; */
                            p.tot_len = new_tot_len;
                            p.len = 0;
                            p = p.next;
                        }
                        if (lwip.pbuf_header(p, (short)-off) != 0) {
                            /* Do we need to cope with this failing?  Assert for now */
                            lwip.LWIP_ASSERT("pbuf_header failed", false);
                        }
                    }
                    else {
                        if (lwip.pbuf_header(inseg.p, (short)-off) != 0) {
                            /* Do we need to cope with this failing?  Assert for now */
                            lwip.LWIP_ASSERT("pbuf_header failed", false);
                        }
                    }
                    inseg.len -= (ushort)(pcb.rcv_nxt - seqno);
                    inseg.tcphdr.seqno = seqno = pcb.rcv_nxt;
                }
                else {
                    if (tcp.TCP_SEQ_LT(seqno, pcb.rcv_nxt)) {
                        /* the whole segment is < rcv_nxt */
                        /* must be a duplicate of a packet that has already been correctly handled */

                        lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "tcp_receive: duplicate seqno {0}\n", seqno);
                        tcp.tcp_ack_now(pcb);
                    }
                }

                /* The sequence number must be within the window (above rcv_nxt
                   and below rcv_nxt + rcv_wnd) in order to be further
                   processed. */
                if (tcp.TCP_SEQ_BETWEEN(seqno, pcb.rcv_nxt,
                                    pcb.rcv_nxt + pcb.rcv_wnd - 1)) {
                    if (pcb.rcv_nxt == seqno) {
                        /* The incoming segment is the next in sequence. We check if
                           we have to trim the end of the segment and update rcv_nxt
                           and pass the data to the application. */
                        tcplen = (ushort)tcp_hdr.TCP_TCPLEN(inseg);

                        if (tcplen > pcb.rcv_wnd) {
                            lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG,
                                        "tcp_receive: other end overran receive window"
                                         + "seqno {0} len {1} right edge {2}\n",
                                         seqno, tcplen, pcb.rcv_nxt + pcb.rcv_wnd);
                            if ((tcp_hdr.TCPH_FLAGS(inseg.tcphdr) & tcp.TCP_FIN) != 0) {
                                /* Must remove the FIN from the header as we're trimming
                                 * that byte of sequence-space from the packet */
                                tcp_hdr.TCPH_FLAGS_SET(inseg.tcphdr, (ushort)(tcp_hdr.TCPH_FLAGS(inseg.tcphdr) & ~tcp.TCP_FIN));
                            }
                            /* Adjust length of segment to fit in the window. */
                            inseg.len = pcb.rcv_wnd;
                            if ((tcp_hdr.TCPH_FLAGS(inseg.tcphdr) & tcp.TCP_SYN) != 0) {
                                inseg.len -= 1;
                            }
                            lwip.pbuf_realloc(inseg.p, inseg.len);
                            tcplen = (ushort)tcp_hdr.TCP_TCPLEN(inseg);
                            lwip.LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
                                        (seqno + tcplen) == (pcb.rcv_nxt + pcb.rcv_wnd));
                        }
            #if TCP_QUEUE_OOSEQ
                        /* Received in-sequence data, adjust ooseq data if:
                           - FIN has been received or
                           - inseq overlaps with ooseq */
                        if (pcb.ooseq != null) {
                            if ((tcp_hdr.TCPH_FLAGS(inseg.tcphdr) & tcp.TCP_FIN) != 0) {
                                lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG,
                                            ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
                                /* Received in-order FIN means anything that was received
                                 * out of order must now have been received in-order, so
                                 * bin the ooseq queue */
                                while (pcb.ooseq != null) {
                                    tcp_seg old_ooseq = pcb.ooseq;
                                    pcb.ooseq = pcb.ooseq.next;
                                    tcp_seg_free(old_ooseq);
                                }
                            }
                            else {
                                next = pcb.ooseq;
                                /* Remove all segments on ooseq that are covered by inseg already.
                                 * FIN is copied from ooseq to inseg if present. */
                                while ((next != null) &&
                                    TCP_SEQ_GEQ(seqno + tcplen, next.tcphdr.seqno + next.len)) {
                                    /* inseg cannot have FIN here (already processed above) */
                                    if ((tcp_hdr.TCPH_FLAGS(next.tcphdr) & tcp.TCP_FIN) != 0 &&
                                        (tcp_hdr.TCPH_FLAGS(inseg.tcphdr) & tcp.TCP_SYN) == 0) {
                                        tcp_hdr.TCPH_SET_FLAG(inseg.tcphdr, tcp.TCP_FIN);
                                        tcplen = (ushort)tcp_hdr.TCP_TCPLEN(inseg);
                                    }
                                    prev = next;
                                    next = next.next;
                                    tcp_seg_free(prev);
                                }
                                /* Now trim right side of inseg if it overlaps with the first
                                 * segment on ooseq */
                                if ((next != null) &&
                                    TCP_SEQ_GT(seqno + tcplen, next.tcphdr.seqno)) {
                                    /* inseg cannot have FIN here (already processed above) */
                                    inseg.len = (ushort)(next.tcphdr.seqno - seqno);
                                    if ((tcp_hdr.TCPH_FLAGS(inseg.tcphdr) & tcp.TCP_SYN) != 0) {
                                        inseg.len -= 1;
                                    }
                                    lwip.pbuf_realloc(inseg.p, inseg.len);
                                    tcplen = (ushort)tcp_hdr.TCP_TCPLEN(inseg);
                                    lwip.LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
                                                (seqno + tcplen) == next.tcphdr.seqno);
                                }
                                pcb.ooseq = next;
                            }
                        }
            #endif // TCP_QUEUE_OOSEQ

                        pcb.rcv_nxt = seqno + tcplen;

                        /* Update the receiver's (our) window. */
                        lwip.LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb.rcv_wnd >= tcplen);
                        pcb.rcv_wnd -= tcplen;

                        tcp.tcp_update_rcv_ann_wnd(pcb);

                        /* If there is data in the segment, we make preparations to
                           pass this up to the application. The .recv_data variable
                           is used for holding the pbuf that goes to the
                           application. The code for reassembling out-of-sequence data
                           chains its data on this pbuf as well.

                           If the segment was a FIN, we set the TF_GOT_FIN flag that will
                           be used to indicate to the application that the remote side has
                           closed its end of the connection. */
                        if (inseg.p.tot_len > 0) {
                            recv_data = inseg.p;
                            /* Since this pbuf now is the responsibility of the
                               application, we delete our reference to it so that we won't
                               (mistakingly) deallocate it. */
                            inseg.p = null;
                        }
                        if ((tcp_hdr.TCPH_FLAGS(inseg.tcphdr) & tcp.TCP_FIN) != 0) {
                            lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "tcp_receive: received FIN.\n");
                            recv_flags |= tcp.TF_GOT_FIN;
                        }

            #if TCP_QUEUE_OOSEQ
                        /* We now check if we have segments on the .ooseq queue that
                           are now in sequence. */
                        while (pcb.ooseq != null &&
                               pcb.ooseq.tcphdr.seqno == pcb.rcv_nxt) {

                            cseg = pcb.ooseq;
                            seqno = pcb.ooseq.tcphdr.seqno;

                            pcb.rcv_nxt += (uint)tcp_hdr.TCP_TCPLEN(cseg);
                            lwip.LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
                                        pcb.rcv_wnd >= tcp_hdr.TCP_TCPLEN(cseg));
                            pcb.rcv_wnd -= (ushort)tcp_hdr.TCP_TCPLEN(cseg);

                            tcp.tcp_update_rcv_ann_wnd(pcb);

                            if (cseg.p.tot_len > 0) {
                                /* Chain this pbuf onto the pbuf that we will pass to
                                   the application. */
                                if (recv_data != null) {
                                    lwip.pbuf_cat(recv_data, cseg.p);
                                }
                                else {
                                    recv_data = cseg.p;
                                }
                                cseg.p = null;
                            }
                            if ((tcp_hdr.TCPH_FLAGS(cseg.tcphdr) & tcp.TCP_FIN) != 0) {
                                lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG, "tcp_receive: dequeued FIN.\n");
                                recv_flags |= tcp.TF_GOT_FIN;
                                if (pcb.state == tcp_state.ESTABLISHED) { /* force passive close or we can move to active close */
                                    pcb.state = tcp_state.CLOSE_WAIT;
                                }
                            }

                            pcb.ooseq = cseg.next;
                            tcp_seg_free(cseg);
                        }
            #endif // TCP_QUEUE_OOSEQ

                        /* Acknowledge the segment(s). */
                        tcp.tcp_ack(pcb);

                    }
                    else {
                        /* We get here if the incoming segment is out-of-sequence. */
                        tcp_send_empty_ack(pcb);
            #if TCP_QUEUE_OOSEQ
                        /* We queue the segment on the .ooseq queue. */
                        if (pcb.ooseq == null) {
                            pcb.ooseq = tcp_seg_copy(inseg);
                        }
                        else {
                            /* If the queue is not empty, we walk through the queue and
                               try to find a place where the sequence number of the
                               incoming segment is between the sequence numbers of the
                               previous and the next segment on the .ooseq queue. That is
                               the place where we put the incoming segment. If needed, we
                               trim the second edges of the previous and the incoming
                               segment so that it will fit into the sequence.

                               If the incoming segment has the same sequence number as a
                               segment on the .ooseq queue, we discard the segment that
                               contains less data. */

                            prev = null;
                            for (next = pcb.ooseq; next != null; next = next.next) {
                                if (seqno == next.tcphdr.seqno) {
                                    /* The sequence number of the incoming segment is the
                                       same as the sequence number of the segment on
                                       .ooseq. We check the lengths to see which one to
                                       discard. */
                                    if (inseg.len > next.len) {
                                        /* The incoming segment is larger than the old
                                           segment. We replace some segments with the new
                                           one. */
                                        cseg = tcp_seg_copy(inseg);
                                        if (cseg != null) {
                                            if (prev != null) {
                                                prev.next = cseg;
                                            }
                                            else {
                                                pcb.ooseq = cseg;
                                            }
                                            tcp_oos_insert_segment(cseg, next);
                                        }
                                        break;
                                    }
                                    else {
                                        /* Either the lenghts are the same or the incoming
                                           segment was smaller than the old one; in either
                                           case, we ditch the incoming segment. */
                                        break;
                                    }
                                }
                                else {
                                    if (prev == null) {
                                        if (tcp.TCP_SEQ_LT(seqno, next.tcphdr.seqno)) {
                                            /* The sequence number of the incoming segment is lower
                                               than the sequence number of the first segment on the
                                               queue. We put the incoming segment first on the
                                               queue. */
                                            cseg = tcp_seg_copy(inseg);
                                            if (cseg != null) {
                                                pcb.ooseq = cseg;
                                                tcp_oos_insert_segment(cseg, next);
                                            }
                                            break;
                                        }
                                    }
                                    else {
                                        /*if (tcp.TCP_SEQ_LT(prev.tcphdr.seqno, seqno) &&
                                          tcp.TCP_SEQ_LT(seqno, next.tcphdr.seqno)) {*/
                                        if (tcp.TCP_SEQ_BETWEEN(seqno, prev.tcphdr.seqno + 1, next.tcphdr.seqno - 1)) {
                                            /* The sequence number of the incoming segment is in
                                               between the sequence numbers of the previous and
                                               the next segment on .ooseq. We trim trim the previous
                                               segment, delete next segments that included in received segment
                                               and trim received, if needed. */
                                            cseg = tcp_seg_copy(inseg);
                                            if (cseg != null) {
                                                if (TCP_SEQ_GT(prev.tcphdr.seqno + prev.len, seqno)) {
                                                    /* We need to trim the prev segment. */
                                                    prev.len = (ushort)(seqno - prev.tcphdr.seqno);
                                                    lwip.pbuf_realloc(prev.p, prev.len);
                                                }
                                                prev.next = cseg;
                                                tcp_oos_insert_segment(cseg, next);
                                            }
                                            break;
                                        }
                                    }
                                    /* If the "next" segment is the last segment on the
                                       ooseq queue, we add the incoming segment to the end
                                       of the list. */
                                    if (next.next == null &&
                                        TCP_SEQ_GT(seqno, next.tcphdr.seqno)) {
                                        if ((tcp_hdr.TCPH_FLAGS(next.tcphdr) & tcp.TCP_FIN) != 0) {
                                            /* segment "next" already contains all data */
                                            break;
                                        }
                                        next.next = tcp_seg_copy(inseg);
                                        if (next.next != null) {
                                            if (TCP_SEQ_GT(next.tcphdr.seqno + next.len, seqno)) {
                                                /* We need to trim the last segment. */
                                                next.len = (ushort)(seqno - next.tcphdr.seqno);
                                                lwip.pbuf_realloc(next.p, next.len);
                                            }
                                            /* check if the remote side overruns our receive window */
                                            if ((uint)tcplen + seqno > pcb.rcv_nxt + (uint)pcb.rcv_wnd) {
                                                lwip.LWIP_DEBUGF(opt.TCP_INPUT_DEBUG,
                                                            "tcp_receive: other end overran receive window"
                                                             + "seqno {0} len {1} right edge {2}\n",
                                                             seqno, tcplen, pcb.rcv_nxt + pcb.rcv_wnd);
                                                if ((tcp_hdr.TCPH_FLAGS(next.next.tcphdr) & tcp.TCP_FIN) != 0) {
                                                    /* Must remove the FIN from the header as we're trimming
                                                     * that byte of sequence-space from the packet */
                                                    tcp_hdr.TCPH_FLAGS_SET(next.next.tcphdr, (ushort)(tcp_hdr.TCPH_FLAGS(next.next.tcphdr) & ~tcp.TCP_FIN));
                                                }
                                                /* Adjust length of segment to fit in the window. */
                                                next.next.len = (ushort)(pcb.rcv_nxt + pcb.rcv_wnd - seqno);
                                                lwip.pbuf_realloc(next.next.p, next.next.len);
                                                tcplen = (ushort)tcp_hdr.TCP_TCPLEN(next.next);
                                                lwip.LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
                                                            (seqno + tcplen) == (pcb.rcv_nxt + pcb.rcv_wnd));
                                            }
                                        }
                                        break;
                                    }
                                }
                                prev = next;
                            }
                        }
            #if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
                        /* Check that the data on ooseq doesn't exceed one of the limits
                           and throw away everything above that limit. */
                        ooseq_blen = 0;
                        ooseq_qlen = 0;
                        prev = null;
                        for (next = pcb.ooseq; next != null; prev = next, next = next.next) {
                            pbuf p1 = next.p;
                            ooseq_blen += p1.tot_len;
                            ooseq_qlen += lwip.pbuf_clen(p1);
                            if ((ooseq_blen > opt.TCP_OOSEQ_MAX_BYTES) ||
                                (ooseq_qlen > opt.TCP_OOSEQ_MAX_PBUFS)) {
                                /* too much ooseq data, dump this and everything after it */
                                tcp_segs_free(next);
                                if (prev == null) {
                                    /* first ooseq segment is too much, dump the whole queue */
                                    pcb.ooseq = null;
                                }
                                else {
                                    /* just dump 'next' and everything after it */
                                    prev.next = null;
                                }
                                break;
                            }
                        }
            #endif // TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
            #endif // TCP_QUEUE_OOSEQ
                    }
                }
                else {
                    /* The incoming segment is not withing the window. */
                    tcp_send_empty_ack(pcb);
                }
            }
            else {
                /* Segments with length 0 is taken care of here. Segments that
                   fall out of the window are ACKed. */
                /*if (TCP_SEQ_GT(pcb.rcv_nxt, seqno) ||
                  TCP_SEQ_GEQ(seqno, pcb.rcv_nxt + pcb.rcv_wnd)) {*/
                if (!tcp.TCP_SEQ_BETWEEN(seqno, pcb.rcv_nxt, pcb.rcv_nxt + pcb.rcv_wnd - 1)) {
                    tcp.tcp_ack_now(pcb);
                }
            }
        }
Пример #19
0
        /**
         * Find out what we can send and send it
         *
         * @param pcb Protocol control block for the TCP connection to send data
         * @return err_t.ERR_OK if data has been sent or nothing to send
         *         another err_t on error
         */
        public err_t tcp_output(tcp_pcb pcb)
        {
            tcp_seg seg, useg;
            uint wnd, snd_nxt;
            #if TCP_CWND_DEBUG
            short i = 0;
            #endif // TCP_CWND_DEBUG

            /* pcb.state tcp_state.LISTEN not allowed here */
            lwip.LWIP_ASSERT("don't call tcp.tcp_output for listen-pcbs",
              pcb.state != tcp_state.LISTEN);

            /* First, check if we are invoked by the TCP input processing
               code. If so, we do not output anything. Instead, we rely on the
               input processing code to call us when input processing is done
               with. */
            if (lwip.tcp.tcp_input_pcb == pcb) {
                return err_t.ERR_OK;
            }

            wnd = Math.Min(pcb.snd_wnd, pcb.cwnd);

            seg = pcb.unsent;

            /* If the tcp_pcb.TF_ACK_NOW flag is set and no data will be sent (either
             * because the .unsent queue is empty or because the window does
             * not allow it), construct an empty ACK segment and send it.
             *
             * If data is to be sent, we will just piggyback the ACK (see below).
             */
            if ((pcb.flags & tcp_pcb.TF_ACK_NOW) != 0 &&
               (seg == null ||
                lwip.lwip_ntohl(seg.tcphdr.seqno) - pcb.lastack + seg.len > wnd)) {
                return tcp_send_empty_ack(pcb);
            }

            /* useg should point to last segment on unacked queue */
            useg = pcb.unacked;
            if (useg != null) {
                for (; useg.next != null; useg = useg.next) ;
            }

            #if TCP_OUTPUT_DEBUG
            if (seg == null) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG, "tcp_output: nothing to send ({0})\n",
                                               pcb.unsent);
            }
            #endif // TCP_OUTPUT_DEBUG
            #if TCP_CWND_DEBUG
            if (seg == null) {
                lwip.LWIP_DEBUGF(opt.TCP_CWND_DEBUG, "tcp_output: snd_wnd {0}"
                                             + ", cwnd {1}, wnd {2}"
                                             + ", seg == null, ack {3}\n",
                                             pcb.snd_wnd, pcb.cwnd, wnd, pcb.lastack);
            }
            else {
                lwip.LWIP_DEBUGF(opt.TCP_CWND_DEBUG,
                            "tcp_output: snd_wnd {0}, cwnd {1}, wnd {2}"
                             + ", effwnd {3}, seq {4}, ack {5}\n",
                             pcb.snd_wnd, pcb.cwnd, wnd,
                             lwip.lwip_ntohl(seg.tcphdr.seqno) - pcb.lastack + seg.len,
                             lwip.lwip_ntohl(seg.tcphdr.seqno), pcb.lastack);
            }
            #endif // TCP_CWND_DEBUG
            /* data available and window allows it to be sent? */
            while (seg != null &&
                   lwip.lwip_ntohl(seg.tcphdr.seqno) - pcb.lastack + seg.len <= wnd) {
                lwip.LWIP_ASSERT("RST not expected here!",
                            (tcp_hdr.TCPH_FLAGS(seg.tcphdr) & tcp.TCP_RST) == 0);
                /* Stop sending if the nagle algorithm would prevent it
                 * Don't stop:
                 * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
                 * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
                 *   either seg.next != null or pcb.unacked == null;
                 *   RST is no sent using tcp_write/tcp.tcp_output.
                 */
                if (!tcp.tcp_do_output_nagle(pcb) &&
                  ((pcb.flags & (tcp_pcb.TF_NAGLEMEMERR | tcp_pcb.TF_FIN)) == 0)) {
                    break;
                }
            #if TCP_CWND_DEBUG
                lwip.LWIP_DEBUGF(opt.TCP_CWND_DEBUG, "tcp_output: snd_wnd {0}, cwnd {1}, wnd {2}, effwnd {3}, seq {4}, ack {5}, i {6}\n",
                                        pcb.snd_wnd, pcb.cwnd, wnd,
                                        lwip.lwip_ntohl(seg.tcphdr.seqno) + seg.len -
                                        pcb.lastack,
                                        lwip.lwip_ntohl(seg.tcphdr.seqno), pcb.lastack, i);
                ++i;
            #endif // TCP_CWND_DEBUG

                pcb.unsent = seg.next;

                if (pcb.state != tcp_state.SYN_SENT) {
                    tcp_hdr.TCPH_SET_FLAG(seg.tcphdr, tcp.TCP_ACK);
                    pcb.flags &= unchecked((byte)~(tcp_pcb.TF_ACK_DELAY | tcp_pcb.TF_ACK_NOW));
                }

                tcp_output_segment(seg, pcb);
                snd_nxt = lwip.lwip_ntohl(seg.tcphdr.seqno) + (uint)tcp_hdr.TCP_TCPLEN(seg);
                if (tcp.TCP_SEQ_LT(pcb.snd_nxt, snd_nxt)) {
                    pcb.snd_nxt = snd_nxt;
                }
                /* put segment on unacknowledged list if length > 0 */
                if (tcp_hdr.TCP_TCPLEN(seg) > 0) {
                    seg.next = null;
                    /* unacked list is empty? */
                    if (pcb.unacked == null) {
                        pcb.unacked = seg;
                        useg = seg;
                        /* unacked list is not empty? */
                    }
                    else {
                        /* In the case of fast retransmit, the packet should not go to the tail
                         * of the unacked queue, but rather somewhere before it. We need to check for
                         * this case. -STJ Jul 27, 2004 */
                        if (tcp.TCP_SEQ_LT(lwip.lwip_ntohl(seg.tcphdr.seqno), lwip.lwip_ntohl(useg.tcphdr.seqno))) {
                            /* add segment to before tail of unacked list, keeping the list sorted */
                            tcp_seg cur_seg = pcb.unacked;
                            while (cur_seg != null &&
                              tcp.TCP_SEQ_LT(lwip.lwip_ntohl((cur_seg).tcphdr.seqno), lwip.lwip_ntohl(seg.tcphdr.seqno))) {
                                cur_seg = cur_seg.next;
                            }
                            seg.next = (cur_seg);
                            (cur_seg) = seg;
                        }
                        else {
                            /* add segment to tail of unacked list */
                            useg.next = seg;
                            useg = useg.next;
                        }
                    }
                    /* do not queue empty segments on the unacked list */
                }
                else {
                    tcp_seg_free(seg);
                }
                seg = pcb.unsent;
            }
            #if TCP_OVERSIZE
            if (pcb.unsent == null) {
                /* last unsent has been removed, reset unsent_oversize */
                pcb.unsent_oversize = 0;
            }
            #endif // TCP_OVERSIZE

            pcb.flags &= unchecked((byte)~tcp_pcb.TF_NAGLEMEMERR);
            return err_t.ERR_OK;
        }
Пример #20
0
 public err_t tcp_output_nagle(tcp_pcb tpcb)
 {
     return tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : err_t.ERR_OK;
 }
Пример #21
0
        /**
         * Called by tcp.tcp_output() to actually send a TCP segment over IP.
         *
         * @param seg the tcp_seg to send
         * @param pcb the tcp_pcb for the TCP connection used to send the segment
         */
        private void tcp_output_segment(tcp_seg seg, tcp_pcb pcb)
        {
            ushort len;
            pointer opts;

            /** @bug Exclude retransmitted segments from this count. */
            //snmp.snmp_inc_tcpoutsegs();

            /* The TCP header has already been constructed, but the ackno and
             wnd fields remain. */
            seg.tcphdr.ackno = lwip.lwip_htonl(pcb.rcv_nxt);

            /* advertise our receive window size in this TCP segment */
            seg.tcphdr.wnd = lwip.lwip_htons(pcb.rcv_ann_wnd);

            pcb.rcv_ann_right_edge = pcb.rcv_nxt + pcb.rcv_ann_wnd;

            /* Add any requested options.  NB MSS option is only set on SYN
               packets, so ignore it here */
            opts = new pointer(seg.tcphdr.data, seg.tcphdr.offset + tcp_hdr.length);
            if ((seg.flags & tcp_seg.TF_SEG_OPTS_MSS) != 0) {
                ushort mss;
            #if TCP_CALCULATE_EFF_SEND_MSS
                mss = tcp_eff_send_mss(opt.TCP_MSS, pcb.remote_ip);
            #else // TCP_CALCULATE_EFF_SEND_MSS
                mss = opt.TCP_MSS;
            #endif // TCP_CALCULATE_EFF_SEND_MSS
                opts.SetValue(tcp_seg.TCP_BUILD_MSS_OPTION(mss));
                opts += 1;
            }
            #if LWIP_TCP_TIMESTAMPS
            pcb.ts_lastacksent = pcb.rcv_nxt;

            if ((seg.flags & tcp_seg.TF_SEG_OPTS_TS) != 0) {
                tcp_build_timestamp_option(pcb, /*opts*/new pointer(seg.tcphdr.data, seg.tcphdr.offset + 1));
                opts += 3;
            }
            #endif

            /* Set retransmission timer running if it is not currently enabled
               This must be set before checking the route. */
            if (pcb.rtime == -1) {
                pcb.rtime = 0;
            }

            /* If we don't have a local IP address, we get one by
               calling ip.ip_route(). */
            if (ip_addr.ip_addr_isany(pcb.local_ip)) {
                ip_addr.ip_addr_copy(pcb.local_ip, lwip.ip_addr);
            }

            if (pcb.rttest == 0) {
                pcb.rttest = tcp_ticks;
                pcb.rtseq = lwip.lwip_ntohl(seg.tcphdr.seqno);

                lwip.LWIP_DEBUGF(opt.TCP_RTO_DEBUG, "tcp_output_segment: rtseq {0}\n", pcb.rtseq);
            }
            lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG, "tcp_output_segment: {0}:{1}\n",
                    lwip.lwip_htonl(seg.tcphdr.seqno), lwip.lwip_htonl(seg.tcphdr.seqno) +
                    seg.len);

            len = (ushort)(seg.tcphdr - seg.p.payload);

            seg.p.len -= len;
            seg.p.tot_len -= len;

            seg.p.payload = seg.tcphdr;

            seg.tcphdr.chksum = 0;
            #if CHECKSUM_GEN_TCP
            #if TCP_CHECKSUM_ON_COPY
            {
                uint acc;
            #if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
                ushort chksum_slow = lwip.inet_chksum_pseudo(seg.p, pcb.local_ip,
                       pcb.remote_ip, lwip.IP_PROTO_TCP, seg.p.tot_len);
            #endif // TCP_CHECKSUM_ON_COPY_SANITY_CHECK
                if ((seg.flags & tcp_seg.TF_SEG_DATA_CHECKSUMMED) == 0) {
                    lwip.LWIP_ASSERT("data included but not checksummed",
                      seg.p.tot_len == (tcp_hdr.TCPH_HDRLEN(seg.tcphdr) * 4));
                }

                /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
                acc = lwip.inet_chksum_pseudo_partial(seg.p, pcb.local_ip,
                         pcb.remote_ip,
                         lwip.IP_PROTO_TCP, seg.p.tot_len, (ushort)(tcp_hdr.TCPH_HDRLEN(seg.tcphdr) * 4));
                /* add payload checksum */
                if (seg.chksum_swapped) {
                    seg.chksum = lwip.SWAP_BYTES_IN_WORD(seg.chksum);
                    seg.chksum_swapped = false;
                }
                acc += (ushort)~(seg.chksum);
                seg.tcphdr.chksum = (ushort)lwip.FOLD_U32T(acc);
            #if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
                if (chksum_slow != seg.tcphdr.chksum) {
                    lwip.LWIP_DEBUGF(opt.TCP_DEBUG | lwip.LWIP_DBG_LEVEL_WARNING,
                                "tcp_output_segment: calculated checksum is {0} instead of {1:X}\n",
                                seg.tcphdr.chksum, chksum_slow);
                    seg.tcphdr.chksum = chksum_slow;
                }
            #endif // TCP_CHECKSUM_ON_COPY_SANITY_CHECK
            }
            #else // TCP_CHECKSUM_ON_COPY
            seg.tcphdr.chksum = lwip.inet_chksum_pseudo(seg.p, pcb.local_ip,
                pcb.remote_ip,
                lwip.IP_PROTO_TCP, seg.p.tot_len);
            #endif // TCP_CHECKSUM_ON_COPY
            #endif // CHECKSUM_GEN_TCP
            ++lwip.lwip_stats.tcp.xmit;

            #if LWIP_NETIF_HWADDRHINT
            lwip.ip_output_hinted(seg.p, pcb.local_ip, pcb.remote_ip, pcb.ttl, pcb.tos,
                lwip.IP_PROTO_TCP, pcb.addr_hint);
            #else // LWIP_NETIF_HWADDRHINT
            lwip.ip_output(seg.p, pcb.local_ip, pcb.remote_ip, pcb.ttl, pcb.tos,
                lwip.IP_PROTO_TCP);
            #endif // LWIP_NETIF_HWADDRHINT
        }
Пример #22
0
 /**
  * This is the Nagle algorithm: try to combine user data to send as few TCP
  * segments as possible. Only send if
  * - no previously transmitted data on the connection remains unacknowledged or
  * - the tcp_pcb.TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or
  * - the only unsent segment is at least pcb.mss bytes long (or there is more
  *   than one unsent segment - with lwIP, this can happen although unsent.len < mss)
  * - or if we are in fast-retransmit (tcp_pcb.TF_INFR)
  */
 public static bool tcp_do_output_nagle(tcp_pcb tpcb)
 {
     return ((tpcb.unacked == null) ||
         ((tpcb.flags & (tcp_pcb.TF_NODELAY | tcp_pcb.TF_INFR)) != 0) ||
         ((tpcb.unsent != null) && ((tpcb.unsent.next != null) ||
         (tpcb.unsent.len >= tpcb.mss))) /*||
         ((tcp_sndbuftpcb == 0) || (tcp_sndqueuelentpcb >= opt.TCP_SND_QUEUELEN))*/
         );
 }
Пример #23
0
        internal memp memp_malloc(memp_t type)
        {
            memp memp;

            switch (type) {
            #if LWIP_RAW
                case memp_t.MEMP_RAW_PCB:
                    memp = new raw_pcb(this);
                    break;
            #endif
            #if LWIP_UDP
            case memp_t.MEMP_UDP_PCB:
                memp = new udp_pcb(this);
                break;
            #endif
            #if LWIP_TCP
            case memp_t.MEMP_TCP_PCB:
                memp = new tcp_pcb(this);
                break;
            case memp_t.MEMP_TCP_PCB_LISTEN:
                memp = new tcp_pcb_listen(this);
                break;
            case memp_t.MEMP_TCP_SEG:
                memp = new tcp_seg(this);
                break;
            #endif
            #if IP_REASSEMBLY
            case memp_t.MEMP_REASSDATA:
                memp = new ip_reassdata(this);
                break;
            case memp_t.MEMP_FRAG_PBUF:
                memp = new frag_pbuf(this);
                break;
            #endif
            #if LWIP_NETCONN
            case memp_t.MEMP_NETBUF:
                memp = new netbuf(this);
                break;
            case memp_t.MEMP_NETCONN:
                memp = new netconn(this);
                break;
            #endif
            #if false //!NO_SYS
            case memp_t.MEMP_TCPIP_MSG_API:
                memp = new tcpip_msg(this);
                break;
            case memp_t.MEMP_TCPIP_MSG_INPKT:
                memp = new tcpip_msg(this);
                break;
            #endif
            #if LWIP_ARP && ARP_QUEUEING
            case memp_t.MEMP_ARP_QUEUE:
                memp = new etharp_q_entry(this);
                break;
            #endif
            #if LWIP_IGMP
            case memp_t.MEMP_IGMP_GROUP:
                memp = new igmp_group(this);
                break;
            #endif
            #if false //(!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS))
            case memp_t.MEMP_SYS_TIMEOUT:
                memp = new sys_timeo(this);
                break;
            #endif
            #if LWIP_SNMP
            case memp_t.MEMP_SNMP_ROOTNODE:
                memp = new mib_list_rootnode(this);
                break;
            case memp_t.MEMP_SNMP_NODE:
                memp = new mib_list_node(this);
                break;
            case memp_t.MEMP_SNMP_VARBIND:
                memp = new snmp_varbind(this);
                break;
            case memp_t.MEMP_SNMP_VALUE:
                memp = new snmp_value(this);
                break;
            #endif
            #if LWIP_DNS && LWIP_SOCKET
            case memp_t.MEMP_NETDB:
                memp = new netdb(this);
                break;
            #endif
            #if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
            case memp_t.MEMP_LOCALHOSTLIST:
                memp = new local_hostlist_entry(this);
                break;
            #endif
            #if PPP_SUPPORT && PPPOE_SUPPORT
            case memp_t.MEMP_PPPOE_IF:
                memp = new pppoe_softc(this);
                break;
            #endif
            default:
                throw new InvalidOperationException();
            }

            memp._type = type;
            memp_heap.AddLast(memp);
            return memp;
        }
Пример #24
0
        /**
         * Write data for sending (but does not send it immediately).
         *
         * It waits in the expectation of more data being sent soon (as
         * it can send them more efficiently by combining them together).
         * To prompt the system to send data now, call tcp.tcp_output() after
         * calling tcp_write().
         *
         * @param pcb Protocol control block for the TCP connection to enqueue data for.
         * @param arg Pointer to the data to be enqueued for sending.
         * @param len Data length in bytes
         * @param apiflags combination of following flags :
         * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
         * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
         * @return err_t.ERR_OK if enqueued, another err_t on error
         */
        public err_t tcp_write(tcp_pcb pcb, pointer arg, ushort len, byte apiflags)
        {
            pbuf concat_p = null;
            tcp_seg last_unsent = null, seg = null, prev_seg = null, queue = null;
            ushort pos = 0; /* position in 'arg' data */
            ushort queuelen;
            byte optlen = 0;
            byte optflags = 0;
            #if TCP_OVERSIZE
            ushort oversize = 0;
            ushort oversize_used = 0;
            #endif // TCP_OVERSIZE
            #if TCP_CHECKSUM_ON_COPY
            ushort concat_chksum = 0;
            bool concat_chksum_swapped = false;
            ushort concat_chksummed = 0;
            #endif // TCP_CHECKSUM_ON_COPY
            err_t err;
            /* don't allocate segments bigger than half the maximum window we ever received */
            ushort mss_local = Math.Min(pcb.mss, (ushort)(pcb.snd_wnd_max / 2));

            #if LWIP_NETIF_TX_SINGLE_PBUF
            /* Always copy to try to create single pbufs for TX */
            apiflags |= tcp.TCP_WRITE_FLAG_COPY;
            #endif // LWIP_NETIF_TX_SINGLE_PBUF

            lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG, "tcp_write(pcb={0}, data={1}, len={2}, apiflags={3})\n",
              pcb, arg, len, (ushort)apiflags);
            if (lwip.LWIP_ERROR("tcp_write: arg == null (programmer violates API)",
                       arg != null))
                return err_t.ERR_ARG;

            err = tcp_write_checks(pcb, len);
            if (err != err_t.ERR_OK) {
                return err;
            }
            queuelen = pcb.snd_queuelen;

            #if LWIP_TCP_TIMESTAMPS
            if ((pcb.flags & tcp_pcb.TF_TIMESTAMP) != 0) {
                optflags = tcp_seg.TF_SEG_OPTS_TS;
                optlen = (byte)tcp_seg.LWIP_TCP_OPT_LENGTH(tcp_seg.TF_SEG_OPTS_TS);
            }
            #endif // LWIP_TCP_TIMESTAMPS

            /*
             * TCP segmentation is done in three phases with increasing complexity:
             *
             * 1. Copy data directly into an oversized pbuf.
             * 2. Chain a new pbuf to the end of pcb.unsent.
             * 3. Create new segments.
             *
             * We may run out of memory at any point. In that case we must
             * return err_t.ERR_MEM and not change anything in pcb. Therefore, all
             * changes are recorded in local variables and committed at the end
             * of the function. Some pcb fields are maintained in local copies:
             *
             * queuelen = pcb.snd_queuelen
             * oversize = pcb.unsent_oversize
             *
             * These variables are set consistently by the phases:
             *
             * seg points to the last segment tampered with.
             *
             * pos records progress as data is segmented.
             */

            /* Find the tail of the unsent queue. */
            if (pcb.unsent != null) {
                ushort space;
                ushort unsent_optlen;

                /* @todo: this could be sped up by keeping last_unsent in the pcb */
                for (last_unsent = pcb.unsent; last_unsent.next != null;
                     last_unsent = last_unsent.next)
                    ;

                /* Usable space at the end of the last unsent segment */
                unsent_optlen = (ushort)tcp_seg.LWIP_TCP_OPT_LENGTH(last_unsent.flags);
                space = (ushort)(mss_local - (last_unsent.len + unsent_optlen));

                /*
                 * Phase 1: Copy data directly into an oversized pbuf.
                 *
                 * The number of bytes copied is recorded in the oversize_used
                 * variable. The actual copying is done at the bottom of the
                 * function.
                 */
            #if TCP_OVERSIZE
            #if TCP_OVERSIZE_DBGCHECK
                /* check that pcb.unsent_oversize matches last_unsent.unsent_oversize */
                lwip.LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
                            pcb.unsent_oversize == last_unsent.oversize_left);
            #endif // TCP_OVERSIZE_DBGCHECK
                oversize = pcb.unsent_oversize;
                if (oversize > 0) {
                    lwip.LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
                    seg = last_unsent;
                    oversize_used = oversize < len ? oversize : len;
                    pos += oversize_used;
                    oversize -= oversize_used;
                    space -= oversize_used;
                }
                /* now we are either finished or oversize is zero */
                lwip.LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
            #endif // TCP_OVERSIZE

                /*
                 * Phase 2: Chain a new pbuf to the end of pcb.unsent.
                 *
                 * We don't extend segments containing SYN/FIN flags or options
                 * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
                 * the end.
                 */
                if ((pos < len) && (space > 0) && (last_unsent.len > 0)) {
                    ushort seglen = (ushort)(space < len - pos ? space : len - pos);
                    //ushort oversize;
                    //ushort concat_chksum = 0;
                    //bool concat_chksum_swapped = false;
                    seg = last_unsent;

                    /* Create a pbuf with a copy or reference to seglen bytes. We
                     * can use pbuf_layer.PBUF_RAW here since the data appears in the middle of
                     * a segment. A header will never be prepended. */
                    if ((apiflags & tcp.TCP_WRITE_FLAG_COPY) != 0) {
                        /* Data is copied */
                        if ((concat_p = tcp_pbuf_prealloc(pbuf_layer.PBUF_RAW, seglen, space, ref oversize, pcb, apiflags, 1)) == null) {
                            lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 2,
                                        "tcp_write : could not allocate memory for pbuf copy size {0}\n",
                                         seglen);
                            goto memerr;
                        }
            #if TCP_OVERSIZE_DBGCHECK
                        last_unsent.oversize_left += oversize;
            #endif // TCP_OVERSIZE_DBGCHECK
                        TCP_DATA_COPY2(concat_p.payload, arg + pos, seglen, ref concat_chksum, ref concat_chksum_swapped);
            #if TCP_CHECKSUM_ON_COPY
                        concat_chksummed += seglen;
            #endif // TCP_CHECKSUM_ON_COPY
                    }
                    else {
                        /* Data is not copied */
                        if ((concat_p = lwip.pbuf_alloc(pbuf_layer.PBUF_RAW, seglen, pbuf_type.PBUF_ROM)) == null) {
                            lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 2,
                                        ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
                            goto memerr;
                        }
            #if TCP_CHECKSUM_ON_COPY
                        /* calculate the checksum of nocopy-data */
                        tcp_seg_add_chksum((ushort)~lwip.inet_chksum(arg + pos, seglen), seglen,
                            ref concat_chksum, ref concat_chksum_swapped);
                        concat_chksummed += seglen;
            #endif // TCP_CHECKSUM_ON_COPY
                        /* reference the non-volatile payload data */
                        concat_p.payload = arg + pos;
                    }

                    pos += seglen;
                    queuelen += lwip.pbuf_clen(concat_p);
                }
            }
            else {
            #if TCP_OVERSIZE
                lwip.LWIP_ASSERT("unsent_oversize mismatch (pcb.unsent is null)",
                            pcb.unsent_oversize == 0);
            #endif // TCP_OVERSIZE
            }

            /*
             * Phase 3: Create new segments.
             *
             * The new segments are chained together in the local 'queue'
             * variable, ready to be appended to pcb.unsent.
             */
            while (pos < len) {
                pbuf p;
                ushort left = (ushort)(len - pos);
                ushort max_len = (ushort)(mss_local - optlen);
                ushort seglen = left > max_len ? max_len : left;
                //ushort oversize;
                ushort chksum = 0;
                bool chksum_swapped = false;

                if ((apiflags & tcp.TCP_WRITE_FLAG_COPY) != 0) {
                    /* If copy is set, memory should be allocated and data copied
                     * into pbuf */
                    if ((p = tcp_pbuf_prealloc(pbuf_layer.PBUF_TRANSPORT, (ushort)(seglen + optlen), mss_local, ref oversize, pcb, apiflags, (byte)(queue == null ? 1 : 0))) == null) {
                        lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 2, "tcp_write : could not allocate memory for pbuf copy size {0}\n", seglen);
                        goto memerr;
                    }
                    lwip.LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
                                (p.len >= seglen));
                    TCP_DATA_COPY2(p.payload + optlen, arg + pos, seglen, ref chksum, ref chksum_swapped);
                }
                else {
                    /* Copy is not set: First allocate a pbuf for holding the data.
                     * Since the referenced data is available at least until it is
                     * sent out on the link (as it has to be ACKed by the remote
                     * party) we can safely use pbuf_type.PBUF_ROM instead of pbuf_type.PBUF_REF here.
                     */
                    pbuf p2;
            #if TCP_OVERSIZE
                    lwip.LWIP_ASSERT("oversize == 0", oversize == 0);
            #endif // TCP_OVERSIZE
                    if ((p2 = lwip.pbuf_alloc(pbuf_layer.PBUF_TRANSPORT, seglen, pbuf_type.PBUF_ROM)) == null) {
                        lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 2, "tcp_write: could not allocate memory for zero-copy pbuf\n");
                        goto memerr;
                    }
            #if TCP_CHECKSUM_ON_COPY
                    /* calculate the checksum of nocopy-data */
                    chksum = (ushort)~lwip.inet_chksum(arg + pos, seglen);
            #endif // TCP_CHECKSUM_ON_COPY
                    /* reference the non-volatile payload data */
                    p2.payload = arg + pos;

                    /* Second, allocate a pbuf for the headers. */
                    if ((p = lwip.pbuf_alloc(pbuf_layer.PBUF_TRANSPORT, optlen, pbuf_type.PBUF_RAM)) == null) {
                        /* If allocation fails, we have to deallocate the data pbuf as
                         * well. */
                        lwip.pbuf_free(p2);
                        lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 2, "tcp_write: could not allocate memory for header pbuf\n");
                        goto memerr;
                    }
                    /* Concatenate the headers and data pbufs together. */
                    lwip.pbuf_cat(p/*header*/, p2/*data*/);
                }

                queuelen += lwip.pbuf_clen(p);

                /* Now that there are more segments queued, we check again if the
                 * length of the queue exceeds the configured maximum or
                 * overflows. */
                if ((queuelen > opt.TCP_SND_QUEUELEN) || (queuelen > tcp_pcb.TCP_SNDQUEUELEN_OVERFLOW)) {
                    lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 2, "tcp_write: queue too long {0} ({1})\n", queuelen, opt.TCP_SND_QUEUELEN);
                    lwip.pbuf_free(p);
                    goto memerr;
                }

                if ((seg = tcp_create_segment(pcb, p, 0, pcb.snd_lbb + pos, optflags)) == null) {
                    goto memerr;
                }
            #if TCP_OVERSIZE_DBGCHECK
                seg.oversize_left = oversize;
            #endif // TCP_OVERSIZE_DBGCHECK
            #if TCP_CHECKSUM_ON_COPY
                seg.chksum = chksum;
                seg.chksum_swapped = chksum_swapped;
                seg.flags |= tcp_seg.TF_SEG_DATA_CHECKSUMMED;
            #endif // TCP_CHECKSUM_ON_COPY

                /* first segment of to-be-queued data? */
                if (queue == null) {
                    queue = seg;
                }
                else {
                    /* Attach the segment to the end of the queued segments */
                    lwip.LWIP_ASSERT("prev_seg != null", prev_seg != null);
                    prev_seg.next = seg;
                }
                /* remember last segment of to-be-queued data for next iteration */
                prev_seg = seg;

                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | lwip.LWIP_DBG_TRACE, "tcp_write: queueing {0}:{1}\n",
                  lwip.lwip_ntohl(seg.tcphdr.seqno),
                  lwip.lwip_ntohl(seg.tcphdr.seqno) + tcp_hdr.TCP_TCPLEN(seg));

                pos += seglen;
            }

            /*
             * All three segmentation phases were successful. We can commit the
             * transaction.
             */

            /*
             * Phase 1: If data has been added to the preallocated tail of
             * last_unsent, we update the length fields of the pbuf chain.
             */
            #if TCP_OVERSIZE
            if (oversize_used > 0) {
                pbuf p;
                /* Bump tot_len of whole chain, len of tail */
                for (p = last_unsent.p; p != null; p = p.next) {
                    p.tot_len += oversize_used;
                    if (p.next == null) {
                        TCP_DATA_COPY(new pointer(p.payload.data, p.payload.offset + p.len), arg, oversize_used, last_unsent);
                        p.len += oversize_used;
                    }
                }
                last_unsent.len += oversize_used;
            #if TCP_OVERSIZE_DBGCHECK
                lwip.LWIP_ASSERT("last_unsent.oversize_left >= oversize_used",
                            last_unsent.oversize_left >= oversize_used);
                last_unsent.oversize_left -= oversize_used;
            #endif // TCP_OVERSIZE_DBGCHECK
            }
            pcb.unsent_oversize = oversize;
            #endif // TCP_OVERSIZE

            /*
             * Phase 2: concat_p can be concatenated onto last_unsent.p
             */
            if (concat_p != null) {
                lwip.LWIP_ASSERT("tcp_write: cannot concatenate when pcb.unsent is empty",
                  (last_unsent != null));
                lwip.pbuf_cat(last_unsent.p, concat_p);
                last_unsent.len += concat_p.tot_len;
            #if TCP_CHECKSUM_ON_COPY
                if (concat_chksummed != 0) {
                    tcp_seg_add_chksum(concat_chksum, concat_chksummed, ref last_unsent.chksum,
                      ref last_unsent.chksum_swapped);
                    last_unsent.flags |= tcp_seg.TF_SEG_DATA_CHECKSUMMED;
                }
            #endif // TCP_CHECKSUM_ON_COPY
            }

            /*
             * Phase 3: Append queue to pcb.unsent. Queue may be null, but that
             * is harmless
             */
            if (last_unsent == null) {
                pcb.unsent = queue;
            }
            else {
                last_unsent.next = queue;
            }

            /*
             * Finally update the pcb state.
             */
            pcb.snd_lbb += len;
            pcb.snd_buf -= len;
            pcb.snd_queuelen = queuelen;

            lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "tcp_write: {0} (after enqueued)\n",
              pcb.snd_queuelen);
            if (pcb.snd_queuelen != 0) {
                lwip.LWIP_ASSERT("tcp_write: valid queue length",
                            pcb.unacked != null || pcb.unsent != null);
            }

            /* Set the PSH flag in the last segment that we enqueued. */
            if (seg != null && seg.tcphdr != null && ((apiflags & tcp.TCP_WRITE_FLAG_MORE) == 0)) {
                tcp_hdr.TCPH_SET_FLAG(seg.tcphdr, tcp.TCP_PSH);
            }

            return err_t.ERR_OK;
            memerr:
            pcb.flags |= tcp_pcb.TF_NAGLEMEMERR;
            ++lwip.lwip_stats.tcp.memerr;

            if (concat_p != null) {
                lwip.pbuf_free(concat_p);
            }
            if (queue != null) {
                tcp_segs_free(queue);
            }
            if (pcb.snd_queuelen != 0) {
                lwip.LWIP_ASSERT("tcp_write: valid queue length", pcb.unacked != null ||
                  pcb.unsent != null);
            }
            lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG | lwip.LWIP_DBG_STATE, "tcp_write: {0} (with mem err)\n", pcb.snd_queuelen);
            return err_t.ERR_MEM;
        }
Пример #25
0
        /**
         * Requeue the first unacked segment for retransmission
         *
         * Called by tcp_receive() for fast retramsmit.
         *
         * @param pcb the tcp_pcb for which to retransmit the first unacked segment
         */
        public static void tcp_rexmit(tcp_pcb pcb)
        {
            tcp_seg seg;
            tcp_seg cur_seg;

            if (pcb.unacked == null) {
                return;
            }

            /* Move the first unacked segment to the unsent queue */
            /* Keep the unsent queue sorted. */
            seg = pcb.unacked;
            pcb.unacked = seg.next;

            cur_seg = pcb.unsent;
            while (cur_seg != null &&
              tcp.TCP_SEQ_LT(lwip.lwip_ntohl((cur_seg).tcphdr.seqno), lwip.lwip_ntohl(seg.tcphdr.seqno))) {
                cur_seg = cur_seg.next;
            }
            seg.next = cur_seg;
            cur_seg = seg;
            #if TCP_OVERSIZE
            if (seg.next == null) {
                /* the retransmitted segment is last in unsent, so reset unsent_oversize */
                pcb.unsent_oversize = 0;
            }
            #endif // TCP_OVERSIZE

            ++pcb.nrtx;

            /* Don't take any rtt measurements after retransmitting. */
            pcb.rttest = 0;

            /* Do the actual retransmission. */
            //snmp.snmp_inc_tcpretranssegs();
            /* No need to call tcp.tcp_output: we are always called from tcp_input()
               and thus tcp.tcp_output directly returns. */
        }
Пример #26
0
        /** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
         *
         * @param pcb the tcp pcb to check for
         * @param len length of data to send (checked agains snd_buf)
         * @return err_t.ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
         */
        private err_t tcp_write_checks(tcp_pcb pcb, ushort len)
        {
            /* connection is in invalid state for data transmission? */
            if ((pcb.state != tcp_state.ESTABLISHED) &&
                (pcb.state != tcp_state.CLOSE_WAIT) &&
                (pcb.state != tcp_state.SYN_SENT) &&
                (pcb.state != tcp_state.SYN_RCVD)) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | lwip.LWIP_DBG_STATE | lwip.LWIP_DBG_LEVEL_SEVERE, "tcp_write() called in invalid state\n");
                return err_t.ERR_CONN;
            }
            else if (len == 0) {
                return err_t.ERR_OK;
            }

            /* fail on too much data */
            if (len > pcb.snd_buf) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 3, "tcp_write: too much data (len={0} > snd_buf={1})\n",
                  len, pcb.snd_buf);
                pcb.flags |= tcp_pcb.TF_NAGLEMEMERR;
                return err_t.ERR_MEM;
            }

            lwip.LWIP_DEBUGF(opt.TCP_QLEN_DEBUG, "tcp_write: queuelen: {0}\n", (ushort)pcb.snd_queuelen);

            /* If total number of pbufs on the unsent/unacked queues exceeds the
             * configured maximum, return an error */
            /* check for configured max queuelen and possible overflow */
            if ((pcb.snd_queuelen >= opt.TCP_SND_QUEUELEN) || (pcb.snd_queuelen > tcp_pcb.TCP_SNDQUEUELEN_OVERFLOW)) {
                lwip.LWIP_DEBUGF(opt.TCP_OUTPUT_DEBUG | 3, "tcp_write: too long queue {0} (max {1})\n",
                  pcb.snd_queuelen, opt.TCP_SND_QUEUELEN);
                ++lwip.lwip_stats.tcp.memerr;
                pcb.flags |= tcp_pcb.TF_NAGLEMEMERR;
                return err_t.ERR_MEM;
            }
            if (pcb.snd_queuelen != 0) {
                lwip.LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
                  pcb.unacked != null || pcb.unsent != null);
            }
            else {
                lwip.LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
                  pcb.unacked == null && pcb.unsent == null);
            }
            return err_t.ERR_OK;
        }
Пример #27
0
        /**
         * Requeue all unacked segments for retransmission
         *
         * Called by tcp_slowtmr() for slow retransmission.
         *
         * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
         */
        public void tcp_rexmit_rto(tcp_pcb pcb)
        {
            tcp_seg seg;

            if (pcb.unacked == null) {
                return;
            }

            /* Move all unacked segments to the head of the unsent queue */
            for (seg = pcb.unacked; seg.next != null; seg = seg.next) ;
            /* concatenate unsent queue after unacked queue */
            seg.next = pcb.unsent;
            /* unsent queue is the concatenated queue (of unacked, unsent) */
            pcb.unsent = pcb.unacked;
            /* unacked queue is now empty */
            pcb.unacked = null;
            /* last unsent hasn't changed, no need to reset unsent_oversize */

            /* increment number of retransmissions */
            ++pcb.nrtx;

            /* Don't take any RTT measurements after retransmitting. */
            pcb.rttest = 0;

            /* Do the actual retransmission */
            tcp_output(pcb);
        }
Пример #28
0
        /**
         * Send persist timer zero-window probes to keep a connection active
         * when a window update is lost.
         *
         * Called by tcp_slowtmr()
         *
         * @param pcb the tcp_pcb for which to send a zero-window probe packet
         */
        public void tcp_zero_window_probe(tcp_pcb pcb)
        {
            pbuf p;
            tcp_hdr tcphdr;
            tcp_seg seg;
            ushort len;
            byte is_fin;

            lwip.LWIP_DEBUGF(opt.TCP_DEBUG,
                        "tcp_zero_window_probe: sending ZERO WINDOW probe to "
                         + "{0}.{1}.{2}.{3}\n",
                         ip_addr.ip4_addr1_16(pcb.remote_ip), ip_addr.ip4_addr2_16(pcb.remote_ip),
                         ip_addr.ip4_addr3_16(pcb.remote_ip), ip_addr.ip4_addr4_16(pcb.remote_ip));

            lwip.LWIP_DEBUGF(opt.TCP_DEBUG,
                        "tcp_zero_window_probe: tcp_ticks "
                        + "{0}   pcb.tmr {1} pcb.keep_cnt_sent {2}\n",
                        tcp_ticks, pcb.tmr, pcb.keep_cnt_sent);

            seg = pcb.unacked;

            if (seg == null) {
                seg = pcb.unsent;
            }
            if (seg == null) {
                return;
            }

            is_fin = (((tcp_hdr.TCPH_FLAGS(seg.tcphdr) & tcp.TCP_FIN) != 0) && (seg.len == 0)) ? (byte)1 : (byte)0;
            /* we want to send one seqno: either FIN or data (no options) */
            len = is_fin != 0 ? (ushort)0 : (ushort)1;

            p = tcp_output_alloc_header(pcb, 0, len, seg.tcphdr.seqno);
            if (p == null) {
                lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "tcp_zero_window_probe: no memory for pbuf\n");
                return;
            }
            tcphdr = new tcp_hdr(p.payload);

            if (is_fin != 0) {
                /* FIN segment, no data */
                tcp_hdr.TCPH_FLAGS_SET(tcphdr, tcp.TCP_ACK | tcp.TCP_FIN);
            }
            else {
                /* Data segment, copy in one byte from the head of the unacked queue */
                pointer d = p.payload + tcp.TCP_HLEN;
                /* Depending on whether the segment has already been sent (unacked) or not
                   (unsent), seg.p.payload points to the IP header or TCP header.
                   Ensure we copy the first TCP data byte: */
                lwip.pbuf_copy_partial(seg.p, d, 1, (ushort)(seg.p.tot_len - seg.len));
            }

            #if CHECKSUM_GEN_TCP
            tcphdr.chksum = lwip.inet_chksum_pseudo(p, pcb.local_ip, pcb.remote_ip,
                                                lwip.IP_PROTO_TCP, p.tot_len);
            #endif
            ++lwip.lwip_stats.tcp.xmit;

            /* Send output to IP */
            #if LWIP_NETIF_HWADDRHINT
            lwip.ip_output_hinted(p, pcb.local_ip, pcb.remote_ip, pcb.ttl, 0, lwip.IP_PROTO_TCP,
                pcb.addr_hint);
            #else // LWIP_NETIF_HWADDRHINT
            lwip.ip_output(p, pcb.local_ip, pcb.remote_ip, pcb.ttl, 0, lwip.IP_PROTO_TCP);
            #endif // LWIP_NETIF_HWADDRHINT

            lwip.pbuf_free(p);

            lwip.LWIP_DEBUGF(opt.TCP_DEBUG, "tcp_zero_window_probe: seqno {0}"
                                    + " ackno {1}.\n",
                                    pcb.snd_nxt - 1, pcb.rcv_nxt);
        }
Пример #29
0
        /**
         * Called by tcp_close() to send a segment including FIN flag but not data.
         *
         * @param pcb the tcp_pcb over which to send a segment
         * @return err_t.ERR_OK if sent, another err_t otherwise
         */
        public err_t tcp_send_fin(tcp_pcb pcb)
        {
            /* first, try to add the fin to the last unsent segment */
            if (pcb.unsent != null) {
                tcp_seg last_unsent;
                for (last_unsent = pcb.unsent; last_unsent.next != null;
                     last_unsent = last_unsent.next)
                    ;

                if ((tcp_hdr.TCPH_FLAGS(last_unsent.tcphdr) & (tcp.TCP_SYN | tcp.TCP_FIN | tcp.TCP_RST)) == 0) {
                    /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
                    tcp_hdr.TCPH_SET_FLAG(last_unsent.tcphdr, tcp.TCP_FIN);
                    pcb.flags |= tcp_pcb.TF_FIN;
                    return err_t.ERR_OK;
                }
            }
            /* no data, no length, flags, copy=1, no optdata */
            return tcp_enqueue_flags(pcb, tcp.TCP_FIN);
        }
Пример #30
0
 public static ushort tcp_nagle_disable(tcp_pcb pcb)
 {
     return pcb.flags |= tcp_pcb.TF_NODELAY;
 }