summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFlorin Coras <fcoras@cisco.com>2017-05-09 18:54:52 -0700
committerDave Barach <openvpp@barachs.net>2017-05-10 14:02:51 +0000
commitdb84e579ef77476e3c73780e20243ee1799530f3 (patch)
treef475a8d9466729c3cb8902e9b4ad96b201e91567
parent1015a1ef1355b1160dcf2a3c0cbe0cfe340be653 (diff)
Improve TCP option handling, VPP-757
Change-Id: Ica634536387d1196366ec96c52770287fcab0768 Signed-off-by: Florin Coras <fcoras@cisco.com>
-rw-r--r--src/vnet/tcp/tcp.c6
-rw-r--r--src/vnet/tcp/tcp_input.c47
-rw-r--r--src/vnet/tcp/tcp_output.c16
3 files changed, 54 insertions, 15 deletions
diff --git a/src/vnet/tcp/tcp.c b/src/vnet/tcp/tcp.c
index 224ee0dd160..a65ab7ffac4 100644
--- a/src/vnet/tcp/tcp.c
+++ b/src/vnet/tcp/tcp.c
@@ -154,6 +154,10 @@ tcp_connection_reset (tcp_connection_t * tc)
return;
tc->state = TCP_STATE_CLOSED;
+
+ /* Make sure all timers are cleared */
+ tcp_connection_timers_reset (tc);
+
stream_session_reset_notify (&tc->connection);
}
@@ -585,7 +589,7 @@ tcp_round_snd_space (tcp_connection_t * tc, u32 snd_space)
{
if (tc->snd_wnd < tc->snd_mss)
{
- return tc->snd_wnd < snd_space ? tc->snd_wnd : 0;
+ return tc->snd_wnd <= snd_space ? tc->snd_wnd : 0;
}
/* If we can't write at least a segment, don't try at all */
diff --git a/src/vnet/tcp/tcp_input.c b/src/vnet/tcp/tcp_input.c
index 82e676d4654..318d8ec5c89 100644
--- a/src/vnet/tcp/tcp_input.c
+++ b/src/vnet/tcp/tcp_input.c
@@ -112,7 +112,14 @@ tcp_segment_in_rcv_wnd (tcp_connection_t * tc, u32 seq, u32 end_seq)
&& seq_leq (seq, tc->rcv_nxt + tc->rcv_wnd));
}
-void
+/**
+ * Parse TCP header options.
+ *
+ * @param th TCP header
+ * @param to TCP options data structure to be populated
+ * @return -1 if parsing failed
+ */
+int
tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
{
const u8 *data;
@@ -134,17 +141,20 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
if (kind == TCP_OPTION_EOL)
break;
else if (kind == TCP_OPTION_NOOP)
- opt_len = 1;
+ {
+ opt_len = 1;
+ continue;
+ }
else
{
/* broken options */
if (opts_len < 2)
- break;
+ return -1;
opt_len = data[1];
/* weird option length */
if (opt_len < 2 || opt_len > opts_len)
- break;
+ return -1;
}
/* Parse options */
@@ -206,6 +216,7 @@ tcp_options_parse (tcp_header_t * th, tcp_options_t * to)
continue;
}
}
+ return 0;
}
/**
@@ -261,7 +272,10 @@ tcp_segment_validate (vlib_main_t * vm, tcp_connection_t * tc0,
if (PREDICT_FALSE (!tcp_ack (th0) && !tcp_rst (th0) && !tcp_syn (th0)))
return -1;
- tcp_options_parse (th0, &tc0->opt);
+ if (PREDICT_FALSE (tcp_options_parse (th0, &tc0->opt)))
+ {
+ return -1;
+ }
if (tcp_segment_check_paws (tc0))
{
@@ -1109,19 +1123,24 @@ static int
tcp_segment_rcv (tcp_main_t * tm, tcp_connection_t * tc, vlib_buffer_t * b,
u16 n_data_bytes, u32 * next0)
{
- u32 error = 0;
+ u32 error = 0, n_bytes_to_drop;
/* Handle out-of-order data */
if (PREDICT_FALSE (vnet_buffer (b)->tcp.seq_number != tc->rcv_nxt))
{
/* Old sequence numbers allowed through because they overlapped
* the rx window */
-
if (seq_lt (vnet_buffer (b)->tcp.seq_number, tc->rcv_nxt))
{
error = TCP_ERROR_SEGMENT_OLD;
*next0 = TCP_NEXT_DROP;
- goto done;
+
+ /* Chop off the bytes in the past */
+ n_bytes_to_drop = tc->rcv_nxt - vnet_buffer (b)->tcp.seq_number;
+ n_data_bytes -= n_bytes_to_drop;
+ vlib_buffer_advance (b, n_bytes_to_drop);
+
+ goto in_order;
}
error = tcp_session_enqueue_ooo (tc, b, n_data_bytes);
@@ -1145,6 +1164,8 @@ tcp_segment_rcv (tcp_main_t * tm, tcp_connection_t * tc, vlib_buffer_t * b,
goto done;
}
+in_order:
+
/* In order data, enqueue. Fifo figures out by itself if any out-of-order
* segments can be enqueued after fifo tail offset changes. */
error = tcp_session_enqueue_data (tc, b, n_data_bytes);
@@ -1540,7 +1561,8 @@ tcp46_syn_sent_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
new_tc0->irs = seq0;
/* Parse options */
- tcp_options_parse (tcp0, &new_tc0->opt);
+ if (tcp_options_parse (tcp0, &new_tc0->opt))
+ goto drop;
if (tcp_opts_tstamp (&new_tc0->opt))
{
@@ -1943,6 +1965,7 @@ tcp46_rcv_process_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
case TCP_STATE_FIN_WAIT_2:
/* Got FIN, send ACK! */
tc0->state = TCP_STATE_TIME_WAIT;
+ tcp_connection_timers_reset (tc0);
tcp_timer_set (tc0, TCP_TIMER_WAITCLOSE, TCP_CLOSEWAIT_TIME);
tcp_make_ack (tc0, b0);
next0 = tcp_next_output (is_ip4);
@@ -2149,7 +2172,10 @@ tcp46_listen_inline (vlib_main_t * vm, vlib_node_runtime_t * node,
goto drop;
}
- tcp_options_parse (th0, &child0->opt);
+ if (tcp_options_parse (th0, &child0->opt))
+ {
+ goto drop;
+ }
child0->irs = vnet_buffer (b0)->tcp.seq_number;
child0->rcv_nxt = vnet_buffer (b0)->tcp.seq_number + 1;
@@ -2553,6 +2579,7 @@ do { \
_(FIN_WAIT_2, TCP_FLAG_FIN | TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS,
TCP_ERROR_NONE);
_(LAST_ACK, TCP_FLAG_ACK, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
+ _(LAST_ACK, TCP_FLAG_RST, TCP_INPUT_NEXT_RCV_PROCESS, TCP_ERROR_NONE);
_(CLOSED, TCP_FLAG_ACK, TCP_INPUT_NEXT_RESET, TCP_ERROR_CONNECTION_CLOSED);
_(CLOSED, TCP_FLAG_RST, TCP_INPUT_NEXT_DROP, TCP_ERROR_CONNECTION_CLOSED);
#undef _
diff --git a/src/vnet/tcp/tcp_output.c b/src/vnet/tcp/tcp_output.c
index 39891fc3fde..a462d8dac3d 100644
--- a/src/vnet/tcp/tcp_output.c
+++ b/src/vnet/tcp/tcp_output.c
@@ -396,20 +396,24 @@ tcp_update_snd_mss (tcp_connection_t * tc)
/* XXX check if MTU has been updated */
tc->snd_mss = clib_min (tc->mss, tc->opt.mss) - tc->snd_opts_len;
+ ASSERT (tc->snd_mss > 0);
}
void
tcp_init_mss (tcp_connection_t * tc)
{
+ u16 default_min_mss = 536;
tcp_update_rcv_mss (tc);
/* TODO cache mss and consider PMTU discovery */
tc->snd_mss = clib_min (tc->opt.mss, tc->mss);
- if (tc->snd_mss == 0)
+ if (tc->snd_mss < 45)
{
clib_warning ("snd mss is 0");
- tc->snd_mss = tc->mss;
+ /* Assume that at least the min default mss works */
+ tc->snd_mss = default_min_mss;
+ tc->opt.mss = default_min_mss;
}
/* We should have enough space for 40 bytes of options */
@@ -1171,13 +1175,17 @@ tcp_timer_persist_handler (u32 index)
vlib_buffer_t *b;
u32 bi, n_bytes;
- tc = tcp_connection_get (index, thread_index);
+ tc = tcp_connection_get_if_valid (index, thread_index);
+
+ if (!tc)
+ return;
/* Make sure timer handle is set to invalid */
tc->timers[TCP_TIMER_PERSIST] = TCP_TIMER_HANDLE_INVALID;
/* Problem already solved or worse */
- if (tc->snd_wnd > tc->snd_mss || tcp_in_recovery (tc))
+ if (tc->state == TCP_STATE_CLOSED
+ || tc->snd_wnd > tc->snd_mss || tcp_in_recovery (tc))
return;
/* Increment RTO backoff */
t: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */ }
# Copyright (c) 2019 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""Stream profile for T-rex traffic generator.

Stream profile:
 - Two streams sent in directions 0 --> 1 and 1 --> 0 at the same time.
 - Packet: ETH / IP /
 - Direction 0 --> 1:
   - Source IP address range:      10.0.0.1
   - Destination IP address range: 20.0.0.0 - 20.0.156.63
 - Direction 1 --> 0:
   - Source IP address range:      20.0.0.1
   - Destination IP address range: 10.0.0.0 - 10.0.156.63
"""

from trex.stl.api import *
from profile_trex_stateless_base_class import TrafficStreamsBaseClass


class TrafficStreams(TrafficStreamsBaseClass):
    """Stream profile."""

    def __init__(self):
        """Initialization and setting of streams' parameters."""

        super(TrafficStreamsBaseClass, self).__init__()

        self.p2_dst_start_mac = '02:02:00:00:00:00'
        self.p2_dst_end_mac = '02:02:00:00:00:03'

        # IPs used in packet headers.
        self.p1_src_start_ip = '10.0.0.1'
        self.p1_dst_start_ip = '20.0.0.0'
        self.p1_dst_end_ip = '20.0.156.63'

        self.p2_src_start_ip = '20.0.0.1'
        self.p2_dst_start_ip = '10.0.0.0'
        self.p2_dst_end_ip = '10.0.156.63'

    def define_packets(self):
        """Defines the packets to be sent from the traffic generator.

        Packet definition: | ETH | IP |

        :returns: Packets to be sent from the traffic generator.
        :rtype: tuple
        """

        # Direction 0 --> 1
        base_pkt_a = (Ether() /
                      IP(src=self.p1_src_start_ip,
                         dst=self.p1_dst_start_ip,
                         proto=61))
        # Direction 1 --> 0
        base_pkt_b = (Ether(dst=self.p2_dst_start_mac) /
                      IP(src=self.p2_src_start_ip,
                         dst=self.p2_dst_start_ip,
                         proto=61))

        # Direction 0 --> 1
        vm1 = STLScVmRaw([STLVmFlowVar(name="dst",
                                       min_value=self.p1_dst_start_ip,
                                       max_value=self.p1_dst_end_ip,
                                       size=4, op="inc"),
                          STLVmWrFlowVar(fv_name="dst", pkt_offset="IP.dst"),
                          STLVmFixIpv4(offset="IP")])
        # Direction 1 --> 0
        vm2 = STLScVmRaw([STLVmFlowVar(name="mac_dst",
                                       min_value=0,
                                       max_value=3,
                                       size=1, op="inc"),
                          STLVmWrFlowVar(fv_name="mac_dst", pkt_offset=5),
                          STLVmFlowVar(name="dst",
                                       min_value=self.p2_dst_start_ip,
                                       max_value=self.p2_dst_end_ip,
                                       size=4, op="inc"),
                          STLVmWrFlowVar(fv_name="dst", pkt_offset="IP.dst"),
                          STLVmFixIpv4(offset="IP")])

        return base_pkt_a, base_pkt_b, vm1, vm2


def register():
    """Register this traffic profile to T-rex.

    Do not change this function.

    :return: Traffic streams.
    :rtype: Object
    """
    return TrafficStreams()