Pull to refresh

Comments 44

А что требуется в conntrack в Linux для того, чтобы tcp соединение считалось established? (вопрос с тремя уровнями подводха).

RFC 793, пункт 2.7 определяет установление TCP соединения. CT будет считать соединение на основании описанных stream index с учётом тайм-аутов из последнего скрина.

Это первый уровень. Если conntrack заметит SYN в одну сторону, а потом SYN+ACK в другую, он добавит соединение как established. Но единственный ли это метод появления established tcp в conntrack? Переходим на второй уровень.

Спасибо за хороший вопрос, надо подумать.

Не гуглил, не смотрел, второе не expected entry какой-нибудь?

Подсказка nf_conntrack_tcp_loose

  • ip_conntrack_tcp_loose - при "подхватывании" уже установленного соединения сколько пакетов требуется в обоих направлениях для подтверждения; если 0, то установленное соединение не подхватывается вовсе; по умолчанию - 3

Ну если роутер был перезагружен или таблица соединений сброшена, то уже существующие соединения подхватятся, но при наличии NAT это вряд ли работает.

По-умолчанию там 1. Это означает, что ваш stateful firewall смывается банальным ACK-флудом.

RoutersOS для защиты от ACK-флуда имеет функционал в firewall, основанный на количестве соединений (заданного типа) от определенной сети (хоста). Сегодня выйдет вторая часть статьи, в ней будет приведено правило.
RouterOS имеет значение по умолчанию:
/ip firewall connection tracking set loose-tcp-tracking=yes
Как видно, нет возможности задать количество пакетов по-умолчанию. Почему вы считаете, что значение на самом деле является 3?

Это где вы такое нашли? Я аж почти вам поверил. Полез в доки - нет такого. Полез в сырцы - нет такого. Вот что есть:

    if (new_state == TCP_CONNTRACK_SYN_SENT) {
        memset(&ct->proto.tcp, 0, sizeof(ct->proto.tcp));
        /* SYN packet */
        ct->proto.tcp.seen[0].td_end =
            segment_seq_plus_len(ntohl(th->seq), skb->len,
                         dataoff, th);
        ct->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
        if (ct->proto.tcp.seen[0].td_maxwin == 0)
            ct->proto.tcp.seen[0].td_maxwin = 1;
        ct->proto.tcp.seen[0].td_maxend =
            ct->proto.tcp.seen[0].td_end;

        tcp_options(skb, dataoff, th, &ct->proto.tcp.seen[0]);
    } else if (tn->tcp_loose == 0) {
        /* Don't try to pick up connections. */
        return false;
    } else {
        memset(&ct->proto.tcp, 0, sizeof(ct->proto.tcp));
        /*
         * We are in the middle of a connection,
         * its history is lost for us.
         * Let's try to use the data from the packet.
         */
        ct->proto.tcp.seen[0].td_end =
            segment_seq_plus_len(ntohl(th->seq), skb->len,
                         dataoff, th);
        ct->proto.tcp.seen[0].td_maxwin = ntohs(th->window);
        if (ct->proto.tcp.seen[0].td_maxwin == 0)
            ct->proto.tcp.seen[0].td_maxwin = 1;
        ct->proto.tcp.seen[0].td_maxend =
            ct->proto.tcp.seen[0].td_end +
            ct->proto.tcp.seen[0].td_maxwin;

        /* We assume SACK and liberal window checking to handle
         * window scaling */
        ct->proto.tcp.seen[0].flags =
        ct->proto.tcp.seen[1].flags = IP_CT_TCP_FLAG_SACK_PERM |
                          IP_CT_TCP_FLAG_BE_LIBERAL;
    }

И где тут счётчик ack'ов?

Еще может быть related (типа GRE туннель в PPTP), но это вроде не established как таковой. Еще может быть если роутер сам инициировал соединение, или к нему было инициировано соединение.

Related, хорошо, это второй уровень.

Третий уровень, вы имеете ввиду, соединения, которые установлены ранее и возобновлены в течении времени tcp таймаута?

Или речь идёт про udp invalid?

Третий уровень - это то, что всё, что первые два уровня говорили не важно. прошёл ack - получился established.

И только самые мудрые знают про отключение этой опции.

RouterOS при получении TCP пакетов с флагами ack воспринимает их, как соединения new. Привожу доказательство.
1) Настроим firewall детектировать флаги TCP пакетов:
/ip firewall filter add action=passthrough chain=input protocol=tcp tcp-flags=syn
/ip firewall filter add action=passthrough chain=input protocol=tcp tcp-flags=ack

2) Запускаю syn-flood, вижу, что детектор срабатывает:
hping3 --count 1000 --syn -p 80 --fast 192.168.1.1
# CHAIN ACTION BYTES PACKETS
0 ;;; TEST TCP
input passthrough 10 760 269
1 ;;; TEST TCP
input passthrough 0

2) Запускаю ack-flood, вижу, что детектор срабатывает:
# CHAIN ACTION BYTES PACKETS
0 ;;; TEST TCP
input passthrough 400 10
1 ;;; TEST TCP
input passthrough 1 055 840 26 396

3) Прикручиваю к детектору еще один счетчик, в котором при ack-flood будет все по нулям:
/ip firewall filter add action=passthrough chain=input connection-state=established protocol=tcp tcp-flags=ack
Последний счетчик сработает только для connection-state=new.

Я не собираюсь нырять в особенности врапера "routeros". Флаг net.netfilter.nf_conntrack_tcp_loose в линуксах управляет тем, как netfilter/conntrack обрабатывает ack-пакеты. Создатели конкретно этого дистрибутива могли что-то поменять в настройках (молодцы), но конкретные дистрибутивы к ядру относятся постольку-поскольку.

Так обсуждаемый пост-то про routeros, а не про ядро

Да, конечно, в разных сборках может быть по-разному. Можете пожалуйста привести код для обработчика ack пакетов, о котором вы говорите?
Вы имеете ввиду код:

else if (tn->tcp_loose == 0) {
/* Don't try to pick up connections. */
return false;

В нем указано, что если флаг равен loose, тогда:
/*
* We are in the middle of a connection,
* its history is lost for us.
* Let's try to use the data from the packet.
*/

А если флаг !tcp_loose и !tcp_syn, тогда return false.

Да, я про это и говорю. Обычное поведение ядра (это был процитирован код netfilter'а в районе conntrack'а). С учётом, что дефолт ядра 1, получается, что любой ACK делает ESTABLISHED в коннтреке.

Если кто-то это меняет, молодец, но дефолт именно такой. И грустно становится под ACK-флудом, который смывает другие established.

Смотрю код ядра (https://github.com/torvalds/linux/blob/master/net/netfilter/nf_conntrack_proto_tcp.c):
1) Смотрю функцию tcp_new (строка 754);
2) Спотыкаюсь о проверку if (new_state == TCP_CONNTRACK_SYN_SENT);
3) Попадаю под условие:
else if (tn->tcp_loose == 0) {
/* Don't try to pick up connections. */
return false;
4)Возвращаю 0, следовательно функция tcp_new возвращает ноль
5) Спотыкаюсь о проверку if (!nf_ct_is_confirmed(ct) && !tcp_new(ct, skb, dataoff, th))
6) Возвращаю return -NF_ACCEPT, т.е. не принимаю соединение.

Поправьте где не верно?

Статья пытается запутать не только новичков.

Каждое реально существующее TCP соединение имеет уникальный (случайный) Sequence number, который генерируется после трёхстороннего handshakes.

Нет, номер последовательности в принципе не может идентифицировать соединение, так как относится к отдельному пакету. И их два. И они не генерируются после рукопожатия, а согласуются в процессе, для того оно и нужно.

Здесь появляется новый термин – сокет. Под ним понимается совокупность пар: IP адрес отправителя и номер порта отправителя, а также IP адрес получателя и номера порта получателя.

Во-первых, термин "сокет" имеет смысл только для стороны подключения, но ладно, мы говорим о "виртуальных сокетах", которые не у роутера, а у клиента и сервера. Во-вторых, даже в посте Q&A, на который ссылка (так себе источник определений), говорится о парах сокетов (socket pairs), адреса и порты (endpoint) которых и образуют то, что промужуточный узел считает соединением. Иногда это называют flow.

По первой части вашего комментария. В статье говорится точно также.

Про wireshark не согласен. Разрабочики вправе вводить в свои продукты различные механизмы и давай им собственные названия. В статье применена антология, показывающая на общность этих механизмов.

UFO just landed and posted this here

Расскажите пожалуйста подробнее.

UFO just landed and posted this here

Насколько я знаю, обработка RAW Prerouting всегда существовала в Linux. В RouterOS ранее не было интерфейса управления им.

А по назначению его я с вами согласен .

UFO just landed and posted this here

Есть практический вопрос.
пакет от ДНС сервера микротика к компу
End output rules output: in:(unknown 0) out:LAN-bridge, proto UDP, 192.168.66.1:53->192.168.66.6:51496, len 60
Почему он не попал в правило?
;;; established related chain=output action=accept connection-state=established,related log=no

С TCP тоже бывает случается

End output rules output: in:(unknown 0) out:LAN-bridge, proto TCP (SYN,ACK), 192.168.66.1:53->192.168.66.4:3765, len 52

С коннекшн трекером по умолчанию такая-же фигня. Но на всякий случай прилагаю текущие настройки.

ip firewall connection tracking print
enabled: auto
tcp-syn-sent-timeout: 5s
tcp-syn-received-timeout: 10s
tcp-established-timeout: 12h
tcp-fin-wait-timeout: 20s
tcp-close-wait-timeout: 10s
tcp-last-ack-timeout: 30m
tcp-time-wait-timeout: 10s
tcp-close-timeout: 10s
tcp-max-retrans-timeout: 5m
tcp-unacked-timeout: 10m
loose-tcp-tracking: yes
udp-timeout: 20s
udp-stream-timeout: 3m
icmp-timeout: 10s
generic-timeout: 10m
max-entries: 183768
total-entries: 870

UFO just landed and posted this here
пакет от ДНС сервера микротика к компу

Это и есть пакет, рождённый на роутере, это ответ dns сервера роутера

Работа протокола DNS в контексте connections очень подробно рассмотрена во второй части цикла статей. А в третьей будут еще и уточняющие диаграммы. После их прочтения данный вопрос у вас будет снят.

Если получится, вспомните про fasttrack, хочется почитать подробно в Вашем изложении.

Кстати, а в ROS еще не добавили возможность посмотреть НАТ таблицу?

Отличная команда, спасибо за совет. С помощью нее можно увидеть результат работы NAT. Кому интересно, ниже пример.
/ip firewall connection print detail where dstnat=yes || srcnat=yes
Флаги: SAC s
protocol=tcp
src-address=IP_LAN:60644 dst-address=173.194.222.119:443
reply-src-address=173.194.222.119:443 reply-dst-address=IP_WAN:60644
tcp-state=established timeout=много счетчики

S — seen-reply, A — assured, C — confirmed, s — srcnat
Sign up to leave a comment.