On Tue, Jan 8, 2019 at 2:57 PM Darrell Ball <dlu...@gmail.com> wrote:

>
>
> On Tue, Jan 8, 2019 at 2:13 AM David Marchand <david.march...@redhat.com>
> wrote:
>
>> On Tue, Jan 8, 2019 at 5:49 AM Darrell Ball <dlu...@gmail.com> wrote:
>>
>>> On Thu, Dec 20, 2018 at 5:33 AM David Marchand <
>>> david.march...@redhat.com> wrote:
>>>
>>>> The ftp alg deals with packets in two ways for the command connection:
>>>> either they are inspected and can be mangled when nat is enabled
>>>> (CT_FTP_CTL_INTEREST) or they just go through without being modified
>>>> (CT_FTP_CTL_OTHER).
>>>>
>>>> For CT_FTP_CTL_INTEREST packets, we must both adjust the packet tcp seq
>>>> number by the connection current offset, then prepare for the next
>>>> packets by setting an accumulated offset in the ct object.
>>>>
>>>> The tests are updated so that ftp+NAT checks send multiple commands in a
>>>> single tcp command connection: wget is not able to do this, so switch to
>>>> lftp.
>>>>
>>>> Signed-off-by: David Marchand <david.march...@redhat.com>
>>>> ---
>>>>  Vagrantfile             |  9 ++++++---
>>>>  Vagrantfile-FreeBSD     |  2 +-
>>>>  lib/conntrack.c         | 52
>>>> ++++++++++++++++++++++++++-----------------------
>>>>  tests/system-traffic.at | 11 ++++++++++-
>>>>  4 files changed, 45 insertions(+), 29 deletions(-)
>>>>
>>>>
>>>>
>> [snip]
>>
>>
>>> diff --git a/lib/conntrack.c b/lib/conntrack.c
>>>> index 6f6021a..2e4141a 100644
>>>> --- a/lib/conntrack.c
>>>> +++ b/lib/conntrack.c
>>>> @@ -3214,30 +3214,34 @@ handle_ftp_ctl(struct conntrack *ct, const
>>>> struct conn_lookup_ctx *ctx,
>>>>              pkt->md.ct_state |= CS_TRACKED | CS_INVALID;
>>>>              return;
>>>>          } else if (rc == CT_FTP_CTL_INTEREST) {
>>>> -            uint16_t ip_len;
>>>> -
>>>> -            if (ctx->key.dl_type == htons(ETH_TYPE_IPV6)) {
>>>> -                seq_skew = repl_ftp_v6_addr(pkt, v6_addr_rep,
>>>> ftp_data_start,
>>>> -
>>>> addr_offset_from_ftp_data_start,
>>>> -                                            addr_size, mode);
>>>> -                if (seq_skew) {
>>>> -                    ip_len =
>>>> ntohs(nh6->ip6_ctlun.ip6_un1.ip6_un1_plen);
>>>> -                    ip_len += seq_skew;
>>>> -                    nh6->ip6_ctlun.ip6_un1.ip6_un1_plen =
>>>> htons(ip_len);
>>>> -                    conn_seq_skew_set(ct, &conn_for_expectation->key,
>>>> now,
>>>> -                                      seq_skew, ctx->reply);
>>>> -                }
>>>> -            } else {
>>>> -                seq_skew = repl_ftp_v4_addr(pkt, v4_addr_rep,
>>>> ftp_data_start,
>>>> -
>>>> addr_offset_from_ftp_data_start);
>>>> -                ip_len = ntohs(l3_hdr->ip_tot_len);
>>>> -                if (seq_skew) {
>>>> -                    ip_len += seq_skew;
>>>> -                    l3_hdr->ip_csum = recalc_csum16(l3_hdr->ip_csum,
>>>> -                                          l3_hdr->ip_tot_len,
>>>> htons(ip_len));
>>>> -                    l3_hdr->ip_tot_len = htons(ip_len);
>>>> -                    conn_seq_skew_set(ct, &conn_for_expectation->key,
>>>> now,
>>>> -                                      seq_skew, ctx->reply);
>>>> +            seq_skew = conn_for_expectation->seq_skew;
>>>> +            if (nat) {
>>>> +                uint16_t ip_len;
>>>> +                int64_t new_skew;
>>>> +
>>>> +                if (ctx->key.dl_type == htons(ETH_TYPE_IPV6)) {
>>>> +                    new_skew = repl_ftp_v6_addr(pkt, v6_addr_rep,
>>>> ftp_data_start,
>>>> +
>>>> addr_offset_from_ftp_data_start,
>>>> +                                                addr_size, mode);
>>>> +                    if (new_skew) {
>>>> +                        ip_len =
>>>> ntohs(nh6->ip6_ctlun.ip6_un1.ip6_un1_plen);
>>>> +                        ip_len += new_skew;
>>>> +                        nh6->ip6_ctlun.ip6_un1.ip6_un1_plen =
>>>> htons(ip_len);
>>>> +                        conn_seq_skew_set(ct,
>>>> &conn_for_expectation->key, now,
>>>> +                                          new_skew + seq_skew,
>>>> ctx->reply);
>>>> +                    }
>>>> +                } else {
>>>> +                    new_skew = repl_ftp_v4_addr(pkt, v4_addr_rep,
>>>> ftp_data_start,
>>>> +
>>>> addr_offset_from_ftp_data_start);
>>>> +                    ip_len = ntohs(l3_hdr->ip_tot_len);
>>>> +                    if (new_skew) {
>>>> +                        ip_len += new_skew;
>>>> +                        l3_hdr->ip_csum =
>>>> recalc_csum16(l3_hdr->ip_csum,
>>>> +                                              l3_hdr->ip_tot_len,
>>>> htons(ip_len));
>>>> +                        l3_hdr->ip_tot_len = htons(ip_len);
>>>> +                        conn_seq_skew_set(ct,
>>>> &conn_for_expectation->key, now,
>>>> +                                          new_skew + seq_skew,
>>>> ctx->reply);
>>>> +                    }
>>>>                  }
>>>>              }
>>>>          } else {
>>>>
>>>
>>>
>>> 1/ I don't think we need another local variable
>>> since conn_for_expectation->seq_skew
>>>     keeps the previous seq_skew, so we can just use it.
>>>
>>
>> Nop, because we need both the current "tcp skew" value to mangle the
>> current packet tcp seq number and an updated value to update the ip length
>> etc... and store the new value in the conn object for next packets.
>> See below.
>>
>>
>>> 2/ The "nat" check is semantically correct around the
>>> repl_ftp_v4/6_addr() calls
>>>
>>>                 if (nat) {
>>>                     seq_skew = repl_ftp_v4_addr(pkt, v4_addr_rep,
>>>                                    ftp_data_start,
>>>                                    addr_offset_from_ftp_data_start);
>>>                 }
>>>
>>> This yields:
>>>
>>> @@ -3217,27 +3225,37 @@ handle_ftp_ctl(struct conntrack *ct, const
>>> struct conn_lookup_ctx *ctx,
>>>              uint16_t ip_len;
>>>
>>>              if (ctx->key.dl_type == htons(ETH_TYPE_IPV6)) {
>>> -                seq_skew = repl_ftp_v6_addr(pkt, v6_addr_rep,
>>> ftp_data_start,
>>> -
>>> addr_offset_from_ftp_data_start,
>>> -                                            addr_size, mode);
>>> +                if (nat) {
>>> +                    seq_skew = repl_ftp_v6_addr(pkt, v6_addr_rep,
>>> +                                   ftp_data_start,
>>> +                                   addr_offset_from_ftp_data_start,
>>> +                                   addr_size, mode);
>>> +                }
>>> +
>>>                  if (seq_skew) {
>>>                      ip_len = ntohs(nh6->ip6_ctlun.ip6_un1.ip6_un1_plen);
>>>                      ip_len += seq_skew;
>>>                      nh6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(ip_len);
>>>                      conn_seq_skew_set(ct, &conn_for_expectation->key,
>>> now,
>>> -                                      seq_skew, ctx->reply);
>>> +                        seq_skew + conn_for_expectation->seq_skew,
>>> +                        ctx->reply);
>>>                  }
>>>              } else {
>>> -                seq_skew = repl_ftp_v4_addr(pkt, v4_addr_rep,
>>> ftp_data_start,
>>> -
>>> addr_offset_from_ftp_data_start);
>>> -                ip_len = ntohs(l3_hdr->ip_tot_len);
>>> +                if (nat) {
>>> +                    seq_skew = repl_ftp_v4_addr(pkt, v4_addr_rep,
>>> +                                   ftp_data_start,
>>> +                                   addr_offset_from_ftp_data_start);
>>> +                }
>>> +
>>>                  if (seq_skew) {
>>> +                    ip_len = ntohs(l3_hdr->ip_tot_len);
>>>                      ip_len += seq_skew;
>>>                      l3_hdr->ip_csum = recalc_csum16(l3_hdr->ip_csum,
>>>                                            l3_hdr->ip_tot_len,
>>> htons(ip_len));
>>>                      l3_hdr->ip_tot_len = htons(ip_len);
>>>                      conn_seq_skew_set(ct, &conn_for_expectation->key,
>>> now,
>>> -                                      seq_skew, ctx->reply);
>>> +                        seq_skew + conn_for_expectation->seq_skew,
>>> +                        ctx->reply);
>>>                  }
>>>              }
>>>          } else {
>>> (END)
>>>
>>
>> Let me take an example with the test CHECK_FTP_SNAT_POST_RECIRC() +
>> address 10.1.1.240.
>>
>> When dealing with the first mangle operation (client sending its first
>> "PORT xxx" for the first "ls" command), the conn tcp seq skew value is at 0.
>> repl_ftp_v4_addr() returns 2 since it replaced 10.1.1.1 with 10.1.1.240.
>> The tcp seq numbers is kept untouched in the "if (do_seq_skew_adj &&
>> seq_skew != 0)" block later in this function.
>>
>> When dealing with the second mangle operation (second "PORT xxx"), the
>> current conn tcp skew value is at 2.
>> repl_ftp_v4_addr() returns again 2 since it replaced 10.1.1.1 with
>> 10.1.1.240.
>> The tcp seq number is updated by 2 in the "if (do_seq_skew_adj &&
>> seq_skew != 0)" block later in this function.
>>
>> Now, you could argue that the code you propose can work so far, but if we
>> add a new command again in this session, the tcp seq numbers sequence is
>> broken.
>> The command packet tcp seq number would be updated by the
>> repl_ftp_v4_addr() return value 2 and not by the accumulated value which
>> would be 4 for it to work.
>>
>
>
> 1/ Cool; I took a second look.
> I also increased the "ls" commands to 4 for better testing; see below
>
>     AT_DATA([ftp.cmd], [dnl
> set net:max-retries 1
> set net:timeout 1
> set ftp:passive-mode off
> cache off
> connect ftp://anonymous:@10.1.1.2
> ls
> ls
> ls
> ls
> ])
>
> 2/ I think adding multiple sequence skew support is a mini-feature, even
> has exploit implications
>      and deserves some refactoring of the function (see below). Sending
> and handling multiple port cmds
>      in lieu of one, which has the same effect, does not seem like an
> optimization, although I know lftp
>      is doing it.
>
> @@ -3170,9 +3178,8 @@ repl_ftp_v6_addr(struct dp_packet *pkt, struct
> ct_addr v6_addr_rep,
>
>  static void
>  handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
> -               struct dp_packet *pkt,
> -               const struct conn *conn_for_expectation,
> -               long long now, enum ftp_ctl_pkt ftp_ctl, bool nat)
> +               struct dp_packet *pkt, const struct conn *ec, long long
> now,
> +               enum ftp_ctl_pkt ftp_ctl, bool nat)
>  {
>      struct ip_header *l3_hdr = dp_packet_l3(pkt);
>      ovs_be32 v4_addr_rep = 0;
> @@ -3187,24 +3194,22 @@ handle_ftp_ctl(struct conntrack *ct, const struct
> conn_lookup_ctx *ctx,
>          return;
>      }
>
> -    if (!nat || !conn_for_expectation->seq_skew) {
> +    if (!nat || !ec->seq_skew) {
>          do_seq_skew_adj = false;
>      }
>
>      struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
>      int64_t seq_skew = 0;
>
> -    if (ftp_ctl == CT_FTP_CTL_OTHER) {
> -        seq_skew = conn_for_expectation->seq_skew;
> -    } else if (ftp_ctl == CT_FTP_CTL_INTEREST) {
> +    if (ftp_ctl == CT_FTP_CTL_INTEREST) {
>          enum ftp_ctl_pkt rc;
>          if (ctx->key.dl_type == htons(ETH_TYPE_IPV6)) {
> -            rc = process_ftp_ctl_v6(ct, pkt, conn_for_expectation,
> +            rc = process_ftp_ctl_v6(ct, pkt, ec,
>                                      &v6_addr_rep, &ftp_data_start,
>                                      &addr_offset_from_ftp_data_start,
>                                      &addr_size, &mode);
>          } else {
> -            rc = process_ftp_ctl_v4(ct, pkt, conn_for_expectation,
> +            rc = process_ftp_ctl_v4(ct, pkt, ec,
>                                      &v4_addr_rep, &ftp_data_start,
>                                      &addr_offset_from_ftp_data_start);
>          }
> @@ -3214,65 +3219,67 @@ handle_ftp_ctl(struct conntrack *ct, const struct
> conn_lookup_ctx *ctx,
>              pkt->md.ct_state |= CS_TRACKED | CS_INVALID;
>              return;
>          } else if (rc == CT_FTP_CTL_INTEREST) {
> +
>              uint16_t ip_len;
>
>              if (ctx->key.dl_type == htons(ETH_TYPE_IPV6)) {
> -                seq_skew = repl_ftp_v6_addr(pkt, v6_addr_rep,
> ftp_data_start,
> -
> addr_offset_from_ftp_data_start,
> -                                            addr_size, mode);
> +                if (nat) {
> +                    seq_skew = repl_ftp_v6_addr(pkt, v6_addr_rep,
> +                                   ftp_data_start,
> +                                   addr_offset_from_ftp_data_start,
> +                                   addr_size, mode);
> +                }
> +
>                  if (seq_skew) {
> -                    ip_len = ntohs(nh6->ip6_ctlun.ip6_un1.ip6_un1_plen);
> -                    ip_len += seq_skew;
> +                    ip_len = ntohs(nh6->ip6_ctlun.ip6_un1.ip6_un1_plen) +
> +                                    seq_skew;
>                      nh6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(ip_len);
> -                    conn_seq_skew_set(ct, &conn_for_expectation->key, now,
> -                                      seq_skew, ctx->reply);
>                  }
>              } else {
> -                seq_skew = repl_ftp_v4_addr(pkt, v4_addr_rep,
> ftp_data_start,
> -
> addr_offset_from_ftp_data_start);
> -                ip_len = ntohs(l3_hdr->ip_tot_len);
> +                if (nat) {
> +                    seq_skew = repl_ftp_v4_addr(pkt, v4_addr_rep,
> +                                   ftp_data_start,
> +                                   addr_offset_from_ftp_data_start);
> +                }
> +
>                  if (seq_skew) {
> -                    ip_len += seq_skew;
> +                    ip_len = ntohs(l3_hdr->ip_tot_len) + seq_skew;
>                      l3_hdr->ip_csum = recalc_csum16(l3_hdr->ip_csum,
> -                                          l3_hdr->ip_tot_len,
> htons(ip_len));
> +                                   l3_hdr->ip_tot_len, htons(ip_len));
>                      l3_hdr->ip_tot_len = htons(ip_len);
> -                    conn_seq_skew_set(ct, &conn_for_expectation->key, now,
> -                                      seq_skew, ctx->reply);
>                  }
>              }
>          } else {
>              OVS_NOT_REACHED();
>          }
> -    } else {
> -        OVS_NOT_REACHED();
>      }
>
>      struct tcp_header *th = dp_packet_l4(pkt);
>
> -    if (do_seq_skew_adj && seq_skew != 0) {
> -        if (ctx->reply != conn_for_expectation->seq_skew_dir) {
> -
> +    if (do_seq_skew_adj && ec->seq_skew != 0) {
>

s/if (do_seq_skew_adj && ec->seq_skew != 0) {/if (do_seq_skew_adj) {/

since "ec->seq_skew != 0" is now redundant


> +        if (ctx->reply != ec->seq_skew_dir) {
>              uint32_t tcp_ack = ntohl(get_16aligned_be32(&th->tcp_ack));
>
> -            if ((seq_skew > 0) && (tcp_ack < seq_skew)) {
> +            if ((ec->seq_skew > 0) && (tcp_ack < ec->seq_skew)) {
>                  /* Should not be possible; will be marked invalid. */
>                  tcp_ack = 0;
> -            } else if ((seq_skew < 0) && (UINT32_MAX - tcp_ack <
> -seq_skew)) {
> -                tcp_ack = (-seq_skew) - (UINT32_MAX - tcp_ack);
> +            } else if ((ec->seq_skew < 0) &&
> +                       (UINT32_MAX - tcp_ack < -ec->seq_skew)) {
> +                tcp_ack = (-ec->seq_skew) - (UINT32_MAX - tcp_ack);
>              } else {
> -                tcp_ack -= seq_skew;
> +                tcp_ack -= ec->seq_skew;
>              }
>              ovs_be32 new_tcp_ack = htonl(tcp_ack);
>              put_16aligned_be32(&th->tcp_ack, new_tcp_ack);
>          } else {
>              uint32_t tcp_seq = ntohl(get_16aligned_be32(&th->tcp_seq));
> -            if ((seq_skew > 0) && (UINT32_MAX - tcp_seq < seq_skew)) {
> -                tcp_seq = seq_skew - (UINT32_MAX - tcp_seq);
> -            } else if ((seq_skew < 0) && (tcp_seq < -seq_skew)) {
> +            if ((ec->seq_skew > 0) && (UINT32_MAX - tcp_seq <
> ec->seq_skew)) {
> +                tcp_seq = ec->seq_skew - (UINT32_MAX - tcp_seq);
> +            } else if ((ec->seq_skew < 0) && (tcp_seq < -ec->seq_skew)) {
>                  /* Should not be possible; will be marked invalid. */
>                  tcp_seq = 0;
>              } else {
> -                tcp_seq += seq_skew;
> +                tcp_seq += ec->seq_skew;
>              }
>              ovs_be32 new_tcp_seq = htonl(tcp_seq);
>              put_16aligned_be32(&th->tcp_seq, new_tcp_seq);
> @@ -3290,6 +3297,11 @@ handle_ftp_ctl(struct conntrack *ct, const struct
> conn_lookup_ctx *ctx,
>      uint8_t pad = dp_packet_l2_pad_size(pkt);
>      th->tcp_csum = csum_finish(
>          csum_continue(tcp_csum, th, tail - (char *) th - pad));
> +
> +    if (seq_skew) {
> +        conn_seq_skew_set(ct, &ec->key, now, seq_skew + ec->seq_skew,
> +                          ctx->reply);
> +    }
>  }
>
>  static void
> (END)
>
>
>
>
>>
>>
>> diff --git a/tests/system-traffic.at b/tests/system-traffic.at
>>>> index 4c52431..3734497 100644
>>>> --- a/tests/system-traffic.at
>>>> +++ b/tests/system-traffic.at
>>>> @@ -4240,7 +4240,16 @@ m4_define([CHECK_FTP_NAT],
>>>>      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.2 -4 --no-passive-ftp
>>>> -t 3 -T 1 --retry-connrefused -v --server-response --no-remove-listing -o
>>>> wget0.log -d])
>>>> +    AT_DATA([ftp.cmd], [dnl
>>>> +set net:max-retries 1
>>>> +set net:timeout 1
>>>> +set ftp:passive-mode off
>>>> +cache off
>>>> +connect ftp://anonymous:@10.1.1.2
>>>> +ls
>>>> +ls
>>>> +])
>>>> +    NS_CHECK_EXEC([at_ns0], [lftp -f ftp.cmd > lftp.log])
>>>>
>>>
>>> A check is needed for running outside of vagrant which is how I ran it:
>>>
>>> see atlocal.in and add "find_command lftp"
>>> then check for it in system_traffic.at
>>>
>>>
>> Yes, done.
>>
>>
>> --
>> David Marchand
>>
>
_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to