On Mon, Jan 21, 2019 at 9:27 PM Darrell Ball <[email protected]> wrote:

> When replacing the ipv4 address in repl_ftp_v4_addr(), the remaining size
> was incorrectly calculated which could lead to the wrong replacement
> adjustment.
>
> This goes unnoticed most of the time, unless you choose carefully your
> initial and replacement addresses.
>
> Example fail address combination with 10.1.1.200 DNAT'd to 10.1.100.1.
>
> Fix this by doing something similar to V6 and also splicing out common
> code for better coverage and maintainability.
>
> A test is updated to exercise different initial and replacement addresses
> and another test is added.
>
> Fixes: bd5e81a0e596 ("Userspace Datapath: Add ALG infra and FTP.")
> Co-authored-by: David Marchand <[email protected]>
> Signed-off-by: David Marchand <[email protected]>
> Signed-off-by: Darrell Ball <[email protected]>
> ---
>
> v4: Fix bug in call to inet_ntop() in repl_ftp_v4_addr().
> v3: Make V4 and V6 code paths more common.
> v2: Minimum alternative patch; also add tests.
>
>  lib/conntrack.c         | 118
> +++++++++++++++++++++++-------------------------
>  tests/system-traffic.at |  74 +++++++++++++++++++++++++++---
>  2 files changed, 123 insertions(+), 69 deletions(-)
>
> diff --git a/lib/conntrack.c b/lib/conntrack.c
> index f732b9e..6b66750 100644
> --- a/lib/conntrack.c
> +++ b/lib/conntrack.c
> @@ -136,7 +136,7 @@ expectation_lookup(struct hmap *alg_expectations,
> const struct conn_key *key,
>  static int
>  repl_ftp_v4_addr(struct dp_packet *pkt, ovs_be32 v4_addr_rep,
>                   char *ftp_data_v4_start,
> -                 size_t addr_offset_from_ftp_data_start);
> +                 size_t addr_offset_from_ftp_data_start, size_t
> addr_size);
>
>  static enum ftp_ctl_pkt
>  process_ftp_ctl_v4(struct conntrack *ct,
> @@ -144,7 +144,8 @@ process_ftp_ctl_v4(struct conntrack *ct,
>                     const struct conn *conn_for_expectation,
>                     ovs_be32 *v4_addr_rep,
>                     char **ftp_data_v4_start,
> -                   size_t *addr_offset_from_ftp_data_start);
> +                   size_t *addr_offset_from_ftp_data_start,
> +                   size_t *addr_size);
>
>  static enum ftp_ctl_pkt
>  detect_ftp_ctl_type(const struct conn_lookup_ctx *ctx,
> @@ -2771,13 +2772,6 @@ expectation_create(struct conntrack *ct, ovs_be16
> dst_port,
>      ct_rwlock_unlock(&ct->resources_lock);
>  }
>
> -static uint8_t
> -get_v4_byte_be(ovs_be32 v4_addr, uint8_t index)
> -{
> -    uint8_t *byte_ptr = (OVS_FORCE uint8_t *) &v4_addr;
> -    return byte_ptr[index];
> -}
> -
>  static void
>  replace_substring(char *substr, uint8_t substr_size,
>                    uint8_t total_size, char *rep_str,
> @@ -2788,51 +2782,56 @@ replace_substring(char *substr, uint8_t
> substr_size,
>      memcpy(substr, rep_str, rep_str_size);
>  }
>
> +static void
> +repl_bytes(char *str, char c1, char c2)
> +{
> +    while (*str) {
> +        if (*str == c1) {
> +            *str = c2;
> +        }
> +        str++;
> +    }
> +}
> +
> +static void
> +modify_packet(struct dp_packet *pkt, char *pkt_str, size_t size,
> +              char *repl_str, size_t repl_size,
> +              uint32_t orig_used_size)
> +{
> +    replace_substring(pkt_str, size,
> +                      (const char *) dp_packet_tail(pkt) - pkt_str,
> +                      repl_str, repl_size);
> +    dp_packet_set_size(pkt, orig_used_size + (int) repl_size - (int)
> size);
> +}
> +
>  /* Replace IPV4 address in FTP message with NATed address. */
>  static int
>  repl_ftp_v4_addr(struct dp_packet *pkt, ovs_be32 v4_addr_rep,
>                   char *ftp_data_start,
> -                 size_t addr_offset_from_ftp_data_start)
> +                 size_t addr_offset_from_ftp_data_start,
> +                 size_t addr_size OVS_UNUSED)
>


'OVS_UNUSED' above will be removed.



>  {
>      enum { MAX_FTP_V4_NAT_DELTA = 8 };
>
>      /* Do conservative check for pathological MTU usage. */
>      uint32_t orig_used_size = dp_packet_size(pkt);
> -    uint16_t allocated_size = dp_packet_get_allocated(pkt);
> -    if (orig_used_size + MAX_FTP_V4_NAT_DELTA > allocated_size) {
> +    if (orig_used_size + MAX_FTP_V4_NAT_DELTA >
> +        dp_packet_get_allocated(pkt)) {
> +
>          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
> -        VLOG_WARN_RL(&rl, "Unsupported effective MTU %u used with FTP",
> -                     allocated_size);
> +        VLOG_WARN_RL(&rl, "Unsupported effective MTU %u used with FTP V4",
> +                     dp_packet_get_allocated(pkt));
>          return 0;
>      }
>
> -    size_t remain_size = tcp_payload_length(pkt) -
> -                             addr_offset_from_ftp_data_start;
> -    int overall_delta = 0;
> -    char *byte_str = ftp_data_start + addr_offset_from_ftp_data_start;
> -
> -    /* Replace the existing IPv4 address by the new one. */
> -    for (uint8_t i = 0; i < 4; i++) {
> -        /* Find the end of the string for this octet. */
> -        char *next_delim = memchr(byte_str, ',', 4);
> -        ovs_assert(next_delim);
> -        int substr_size = next_delim - byte_str;
> -        remain_size -= substr_size;
> -
> -        /* Compose the new string for this octet, and replace it. */
> -        char rep_str[4];
> -        uint8_t rep_byte = get_v4_byte_be(v4_addr_rep, i);
> -        int replace_size = sprintf(rep_str, "%d", rep_byte);
> -        replace_substring(byte_str, substr_size, remain_size,
> -                          rep_str, replace_size);
> -        overall_delta += replace_size - substr_size;
> -
> -        /* Advance past the octet and the following comma. */
> -        byte_str += replace_size + 1;
> -    }
> -
> -    dp_packet_set_size(pkt, orig_used_size + overall_delta);
> -    return overall_delta;
> +    char v4_addr_str[INET_ADDRSTRLEN] = {0};
> +    ovs_assert(inet_ntop(AF_INET, &v4_addr_rep, v4_addr_str,
> +                         sizeof v4_addr_str));
> +    repl_bytes(v4_addr_str, '.', ',');
> +    modify_packet(pkt, ftp_data_start + addr_offset_from_ftp_data_start,
> +                  addr_size, v4_addr_str, strlen(v4_addr_str),
> +                  orig_used_size);
> +    return (int) strlen(v4_addr_str) - (int) addr_size;
>  }
>
>  static char *
> @@ -2901,7 +2900,8 @@ process_ftp_ctl_v4(struct conntrack *ct,
>                     const struct conn *conn_for_expectation,
>                     ovs_be32 *v4_addr_rep,
>                     char **ftp_data_v4_start,
> -                   size_t *addr_offset_from_ftp_data_start)
> +                   size_t *addr_offset_from_ftp_data_start,
> +                   size_t *addr_size)
>  {
>      struct tcp_header *th = dp_packet_l4(pkt);
>      size_t tcp_hdr_len = TCP_OFFSET(th->tcp_ctl) * 4;
> @@ -2957,6 +2957,7 @@ process_ftp_ctl_v4(struct conntrack *ct,
>          return CT_FTP_CTL_INVALID;
>      }
>
> +    *addr_size = ftp - ip_addr_start - 1;
>      char *save_ftp = ftp;
>      ftp = terminate_number_str(ftp, MAX_FTP_PORT_DGTS);
>      if (!ftp) {
> @@ -3149,31 +3150,22 @@ repl_ftp_v6_addr(struct dp_packet *pkt, struct
> ct_addr v6_addr_rep,
>
>      /* Do conservative check for pathological MTU usage. */
>      uint32_t orig_used_size = dp_packet_size(pkt);
> -    uint16_t allocated_size = dp_packet_get_allocated(pkt);
> -    if (orig_used_size + MAX_FTP_V6_NAT_DELTA > allocated_size) {
> +    if (orig_used_size + MAX_FTP_V6_NAT_DELTA >
> +        dp_packet_get_allocated(pkt)) {
> +
>          static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
> -        VLOG_WARN_RL(&rl, "Unsupported effective MTU %u used with FTP",
> -                     allocated_size);
> +        VLOG_WARN_RL(&rl, "Unsupported effective MTU %u used with FTP V6",
> +                     dp_packet_get_allocated(pkt));
>          return 0;
>      }
>
>      char v6_addr_str[IPV6_SCAN_LEN] = {0};
>      ovs_assert(inet_ntop(AF_INET6, &v6_addr_rep.ipv6_aligned, v6_addr_str,
>                           IPV6_SCAN_LEN - 1));
> -
> -    size_t replace_addr_size = strlen(v6_addr_str);
> -
> -    size_t remain_size = tcp_payload_length(pkt) -
> -                             addr_offset_from_ftp_data_start;
> -
> -    char *pkt_addr_str = ftp_data_start + addr_offset_from_ftp_data_start;
> -    replace_substring(pkt_addr_str, addr_size, remain_size,
> -                      v6_addr_str, replace_addr_size);
> -
> -    int overall_delta = (int) replace_addr_size - (int) addr_size;
> -
> -    dp_packet_set_size(pkt, orig_used_size + overall_delta);
> -    return overall_delta;
> +    modify_packet(pkt, ftp_data_start + addr_offset_from_ftp_data_start,
> +                  addr_size, v6_addr_str, strlen(v6_addr_str),
> +                  orig_used_size);
> +    return (int) strlen(v6_addr_str) - (int) addr_size;
>  }
>
>  /* Increment/decrement a TCP sequence number. */
> @@ -3213,7 +3205,8 @@ handle_ftp_ctl(struct conntrack *ct, const struct
> conn_lookup_ctx *ctx,
>          } else {
>              rc = process_ftp_ctl_v4(ct, pkt, ec,
>                                      &v4_addr_rep, &ftp_data_start,
> -                                    &addr_offset_from_ftp_data_start);
> +                                    &addr_offset_from_ftp_data_start,
> +                                    &addr_size);
>          }
>          if (rc == CT_FTP_CTL_INVALID) {
>              static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
> @@ -3240,7 +3233,8 @@ handle_ftp_ctl(struct conntrack *ct, const struct
> conn_lookup_ctx *ctx,
>                  if (nat) {
>                      seq_skew = repl_ftp_v4_addr(pkt, v4_addr_rep,
>                                     ftp_data_start,
> -                                   addr_offset_from_ftp_data_start);
> +                                   addr_offset_from_ftp_data_start,
> +                                   addr_size);
>                  }
>                  if (seq_skew) {
>                      ip_len = ntohs(l3_hdr->ip_tot_len) + seq_skew;
> diff --git a/tests/system-traffic.at b/tests/system-traffic.at
> index 3a62e17..3e3b9e7 100644
> --- a/tests/system-traffic.at
> +++ b/tests/system-traffic.at
> @@ -4652,6 +4652,66 @@
> tcp,orig=(src=10.1.1.1,dst=10.1.1.2,sport=<cleared>,dport=<cleared>),reply=(src=
>  OVS_TRAFFIC_VSWITCHD_STOP
>  AT_CLEANUP
>
> +AT_SETUP([conntrack - IPv4 FTP Passive with DNAT 2])
> +AT_SKIP_IF([test $HAVE_FTP = no])
> +CHECK_CONNTRACK()
> +CHECK_CONNTRACK_NAT()
> +CHECK_CONNTRACK_ALG()
> +
> +OVS_TRAFFIC_VSWITCHD_START()
> +
> +ADD_NAMESPACES(at_ns0, at_ns1)
> +
> +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/16")
> +NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11])
> +NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.200 e6:66:c1:22:22:22])
> +NS_CHECK_EXEC([at_ns0], [arp -s 10.1.100.1 e6:66:c1:22:22:22])
> +
> +ADD_VETH(p1, at_ns1, br0, "10.1.100.1/16")
> +NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22])
> +NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11])
> +
> +dnl Allow any traffic from ns0->ns1.
> +AT_DATA([flows.txt], [dnl
> +dnl track all IPv4 traffic and NAT any established traffic.
> +table=0 priority=10 ip, action=ct(nat,table=1)
> +table=0 priority=0 action=drop
> +dnl
> +dnl Table 1
> +dnl
> +dnl Allow new FTP control connections.
> +table=1 in_port=1 ct_state=+new tcp nw_src=10.1.1.1 tp_dst=21
> action=ct(alg=ftp,commit,nat(dst=10.1.100.1)),2
> +dnl Allow related TCP connections from port 1.
> +table=1 in_port=1 ct_state=+new+rel tcp nw_src=10.1.1.1
> action=ct(commit,nat),2
> +dnl Allow established TCP connections both ways, post-NAT match.
> +table=1 in_port=1 ct_state=+est tcp nw_dst=10.1.100.1 action=2
> +table=1 in_port=2 ct_state=+est tcp nw_dst=10.1.1.1 action=1
> +
> +dnl Allow ICMP both ways.
> +table=1 priority=100 in_port=1 icmp, action=2
> +table=1 priority=100 in_port=2 icmp, action=1
> +table=1 priority=0, action=drop
> +])
> +
> +AT_CHECK([ovs-ofctl --bundle add-flows br0 flows.txt])
> +
> +dnl Check that the stacks working to avoid races.
> +OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1 10.1.100.1 >/dev/null])
> +
> +OVS_START_L7([at_ns1], [ftp])
> +
> +dnl FTP requests from p0->p1 should work fine.
> +NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.200 -t 3 -T 1
> --retry-connrefused -v -o wget0.log])
> +
> +dnl Discards CLOSE_WAIT and CLOSING
> +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.200)], [0],
> [dnl
>
> +tcp,orig=(src=10.1.1.1,dst=10.1.1.200,sport=<cleared>,dport=<cleared>),reply=(src=10.1.100.1,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>)
>
> +tcp,orig=(src=10.1.1.1,dst=10.1.1.200,sport=<cleared>,dport=<cleared>),reply=(src=10.1.100.1,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>),helper=ftp
> +])
> +
> +OVS_TRAFFIC_VSWITCHD_STOP
> +AT_CLEANUP
> +
>  AT_SETUP([conntrack - IPv4 FTP Active with DNAT])
>  AT_SKIP_IF([test $HAVE_FTP = no])
>  CHECK_CONNTRACK()
> @@ -4722,12 +4782,12 @@ OVS_TRAFFIC_VSWITCHD_START()
>
>  ADD_NAMESPACES(at_ns0, at_ns1)
>
> -ADD_VETH(p0, at_ns0, br0, "10.1.1.1/24")
> +ADD_VETH(p0, at_ns0, br0, "10.1.1.1/16")
>  NS_CHECK_EXEC([at_ns0], [ip link set dev p0 address e6:66:c1:11:11:11])
>  NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.2 e6:66:c1:22:22:22])
> -NS_CHECK_EXEC([at_ns0], [arp -s 10.1.1.240 e6:66:c1:22:22:22])
> +NS_CHECK_EXEC([at_ns0], [arp -s 10.1.120.240 e6:66:c1:22:22:22])
>
> -ADD_VETH(p1, at_ns1, br0, "10.1.1.2/24")
> +ADD_VETH(p1, at_ns1, br0, "10.1.1.2/16")
>  NS_CHECK_EXEC([at_ns1], [ip link set dev p1 address e6:66:c1:22:22:22])
>  NS_CHECK_EXEC([at_ns1], [arp -s 10.1.1.1 e6:66:c1:11:11:11])
>
> @@ -4761,12 +4821,12 @@ OVS_WAIT_UNTIL([ip netns exec at_ns0 ping -c 1
> 10.1.1.2 >/dev/null])
>  OVS_START_L7([at_ns1], [ftp])
>
>  dnl FTP requests from p0->p1 should work fine.
> -NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.1.240 --no-passive-ftp -t 3 -T
> 1 --retry-connrefused -v -o wget0.log])
> +NS_CHECK_EXEC([at_ns0], [wget ftp://10.1.120.240 --no-passive-ftp -t 3
> -T 1 --retry-connrefused -v -o wget0.log])
>
>  dnl Discards CLOSE_WAIT and CLOSING
> -AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.1.2)], [0],
> [dnl
>
> -tcp,orig=(src=10.1.1.1,dst=10.1.1.240,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>),helper=ftp
>
> -tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.1,dst=10.1.1.240,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>)
> +AT_CHECK([ovs-appctl dpctl/dump-conntrack | FORMAT_CT(10.1.120.240)],
> [0], [dnl
>
> +tcp,orig=(src=10.1.1.1,dst=10.1.120.240,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>),helper=ftp
>
> +tcp,orig=(src=10.1.1.2,dst=10.1.1.1,sport=<cleared>,dport=<cleared>),reply=(src=10.1.1.1,dst=10.1.120.240,sport=<cleared>,dport=<cleared>),protoinfo=(state=<cleared>)
>  ])
>
>  OVS_TRAFFIC_VSWITCHD_STOP
> --
> 1.9.1
>
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to