FreeBSD 8.1 и версия IGMP

Настройка сетевых служб, маршрутизации, фаерволлов. Проблемы с сетевым оборудованием.
Правила форума
Убедительная просьба юзать теги [code] при оформлении листингов.
Сообщения не оформленные должным образом имеют все шансы быть незамеченными.
Аватара пользователя
VampireNF
ефрейтор
Сообщения: 60
Зарегистрирован: 2010-07-27 20:28:31
Откуда: Санкт-Петербург

FreeBSD 8.1 и версия IGMP

Непрочитанное сообщение VampireNF » 2011-12-28 19:20:02

Всем доброго времени суток! Поздравляю с наступающим Новым Годом! :)
Собственно о проблеме. Провайдер вещает IPTV по протоколу IGMP v2. Но FreeBSD упорно и без каких либо колебаний шлёт в сеть запросы исключительно третей версии. Переменная net.inet.igmp.default_version никак не виляет на то, какой версии будет послан запрос. Причём перезагружайся хоть 100 раз, эффетка не будет, всегда видна такая картина:

Код: Выделить всё

# ifmcstat -vvv -i em0
em0:
        inet 10.82.198.109
        igmpv3 flags=0<> rv 2 qi 60 qri 100 uri 3 v1timer 0 v2timer 0 v3timer 0
                group 224.0.0.1 mode exclude
                        mcast-macaddr 01:00:5e:00:00:01
        link 00:1b:21:7b:32:94
                group 01:00:5e:00:00:01
Заметил, что если у igmpproxy развернуть интерфейсы, т.е. внутренний сделать внешним, а внешний внутренним, то ifmcstat выдаёт, что em0 работает на IGMPv2 и всё хорошо. И действительно, начинает трафик ползти через udpxy. Но это не решение, тем более я не знаю через сколько может сбросится версия обратно на третью.
Есть у кого какие идеи, как победить сие чудо? Сеть провайдера работает на PIM-SM, заметил, что мультикаст роутреы между друг другом кидаются иногда IGMPv3, однако телевидение работает только на v2 и ни на какой другой.

Хостинговая компания Host-Food.ru
Хостинг HostFood.ru
 

Услуги хостинговой компании Host-Food.ru

Хостинг HostFood.ru

Тарифы на хостинг в России, от 12 рублей: https://www.host-food.ru/tariffs/hosting/
Тарифы на виртуальные сервера (VPS/VDS/KVM) в РФ, от 189 руб.: https://www.host-food.ru/tariffs/virtualny-server-vps/
Выделенные сервера, Россия, Москва, от 2000 рублей (HP Proliant G5, Intel Xeon E5430 (2.66GHz, Quad-Core, 12Mb), 8Gb RAM, 2x300Gb SAS HDD, P400i, 512Mb, BBU):
https://www.host-food.ru/tariffs/vydelennyi-server-ds/
Недорогие домены в популярных зонах: https://www.host-food.ru/domains/

Аватара пользователя
VampireNF
ефрейтор
Сообщения: 60
Зарегистрирован: 2010-07-27 20:28:31
Откуда: Санкт-Петербург

Re: FreeBSD 8.1 и версия IGMP

Непрочитанное сообщение VampireNF » 2011-12-29 13:12:32

В общем сейчас я только разобрался, что происходит когда igmpproxy переключает версию на двойку. После переключения согласно RFC 3376 запускается таймер, по истечению которого ОС переключает версию на 3-ю. Набросал нечто подобное:

Код: Выделить всё

e# diff -c /tmp/8.2/usr/src/sys/netinet/igmp.c /usr/src/sys/netinet/igmp.c
*** /tmp/8.2/usr/src/sys/netinet/igmp.c 2010-12-21 20:09:25.000000000 +0300
--- /usr/src/sys/netinet/igmp.c 2011-12-29 13:59:01.000000000 +0400
***************
*** 135,140 ****
--- 135,144 ----
  static int    sysctl_igmp_gsr(SYSCTL_HANDLER_ARGS);
  static int    sysctl_igmp_ifinfo(SYSCTL_HANDLER_ARGS);

+ /*My add*/
+ /*--------------------------------------------------------------------------------*/
+ static int    sysctl_igmp_max_version(SYSCTL_HANDLER_ARGS);
+ /*--------------------------------------------------------------------------------*/
  static const struct netisr_handler igmp_nh = {
        .nh_name = "igmp",
        .nh_handler = igmp_intr,
***************
*** 237,242 ****
--- 241,251 ----
  static VNET_DEFINE(int, igmp_legacysupp);
  static VNET_DEFINE(int, igmp_default_version) = IGMP_VERSION_3;

+ /*My add*/
+ /*---------------------------------------------------------------------------------------*/
+ static VNET_DEFINE(int, igmp_max_version) = IGMP_MAX_USE_VERSION_3;
+ /*---------------------------------------------------------------------------------------*/
+
  #define       V_igmp_recvifkludge             VNET(igmp_recvifkludge)
  #define       V_igmp_sendra                   VNET(igmp_sendra)
  #define       V_igmp_sendlocal                VNET(igmp_sendlocal)
***************
*** 245,250 ****
--- 254,265 ----
  #define       V_igmp_legacysupp               VNET(igmp_legacysupp)
  #define       V_igmp_default_version          VNET(igmp_default_version)

+ /*My add*/
+ /*---------------------------------------------------------------------------------------*/
+ #define       V_igmp_max_version              VNET(igmp_max_version)
+ /*---------------------------------------------------------------------------------------*/
+
+
  /*
   * Virtualized sysctls.
   */
***************
*** 277,282 ****
--- 292,307 ----
      &VNET_NAME(igmp_gsrdelay.tv_sec), 0, sysctl_igmp_gsr, "I",
      "Rate limit for IGMPv3 Group-and-Source queries in seconds");

+ /*My add*/
+ /*---------------------------------------------------------------------------------------*/
+ SYSCTL_VNET_PROC(_net_inet_igmp, OID_AUTO, max_version,
+     CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE,
+     &VNET_NAME(igmp_max_version), 0, sysctl_igmp_max_version, "I",
+     "Maximal version of IGMP to use on each interface");
+
+ /*---------------------------------------------------------------------------------------*/
+
+
  /*
   * Non-virtualized sysctls.
   */
***************
*** 371,376 ****
--- 396,444 ----
        return (error);
  }

+ /*My add*/
+ /*----------------------------------------------------------------------------------------------------*/
+ /*
+  * Retrieve or set max use IGMP version.
+  *
+  * VIMAGE: Assume curvnet set by caller.
+  * SMPng: NOTE: Serialized by IGMP lock.
+  */
+ static int
+ sysctl_igmp_max_version(SYSCTL_HANDLER_ARGS)
+ {
+     int               error;
+     int               new;
+
+      error = sysctl_wire_old_buffer(req, sizeof(int));
+      if (error)
+           return (error);
+
+      IGMP_LOCK();
+
+      new = V_igmp_max_version;
+
+      error = sysctl_handle_int(oidp, &new, 0, req);
+      if (error || !req->newptr)
+           goto out_locked;
+
+      if (new < IGMP_MAX_USE_VERSION_1 || new > IGMP_MAX_USE_VERSION_3) {
+           error = EINVAL;
+           goto out_locked;
+      }
+
+      CTR2(KTR_IGMPV3, "change igmp_max_version from %d to %d",
+         V_igmp_max_version, new);
+
+      V_igmp_max_version = new;
+
+      out_locked:
+      IGMP_UNLOCK();
+      return (error);
+ }
+
+ /*-------------------------------------------------------------------------------------------------------*/
+
  /*
   * Retrieve or set threshold between group-source queries in seconds.
   *
***************
*** 2093,2105 ****
                 *
                 * Revert to IGMPv3.
                 */
!               if (igi->igi_version != IGMP_VERSION_3) {
                        CTR5(KTR_IGMPV3,
                            "%s: transition from v%d -> v%d on %p(%s)",
                            __func__, igi->igi_version, IGMP_VERSION_3,
                            igi->igi_ifp, igi->igi_ifp->if_xname);
                        igi->igi_version = IGMP_VERSION_3;
                }
        } else if (igi->igi_v1_timer == 0 && igi->igi_v2_timer > 0) {
                /*
                 * IGMPv1 Querier Present timer expired,
--- 2161,2178 ----
                 *
                 * Revert to IGMPv3.
                 */
! /*My add*/
! /*------------------------------------------------------------------------------------------------------*/
! /*Original            if (igi->igi_version != IGMP_VERSION_3) {*/
!               if ((igi->igi_version != IGMP_VERSION_3) && (V_igmp_max_version == IGMP_MAX_USE_VERSION_3)) {
! /*------------------------------------------------------------------------------------------------------*/
                        CTR5(KTR_IGMPV3,
                            "%s: transition from v%d -> v%d on %p(%s)",
                            __func__, igi->igi_version, IGMP_VERSION_3,
                            igi->igi_ifp, igi->igi_ifp->if_xname);
                        igi->igi_version = IGMP_VERSION_3;
                }
+ /*----------------------------------------------------------------------------------------------------------*/
        } else if (igi->igi_v1_timer == 0 && igi->igi_v2_timer > 0) {
                /*
                 * IGMPv1 Querier Present timer expired,

# diff -c /tmp/8.2/usr/src/sys/netinet/igmp_var.h /usr/src/sys/netinet/igmp_var.h
*** /tmp/8.2/usr/src/sys/netinet/igmp_var.h     2010-12-21 20:09:25.000000000 +0300
--- /usr/src/sys/netinet/igmp_var.h     2011-12-29 00:01:59.000000000 +0400
***************
*** 143,148 ****
--- 143,155 ----
  #define IGMP_VERSION_2                        2
  #define IGMP_VERSION_3                        3 /* Default */

+ /*My ADD*/
+ /*------------------------------------------------------------------------*/
+ #define IGMP_MAX_USE_VERSION_1                1
+ #define IGMP_MAX_USE_VERSION_2                2
+ #define IGMP_MAX_USE_VERSION_3                3
+ /*------------------------------------------------------------------------*/
+
  /*
   * IGMPv3 protocol control variables.
   */
Но по мне это грязный хак, не более. К тому же я так и не нашёл в каком месте устанавливается версия IGMP во время загрузки ОС. Потому что если перезагружаться то вернётся третья версия и чтобы поставить вторую опять придётся запустить хотя бы на 1 сек igmpproxy. Потом уже из-за внесённых изменений версия не сможет по таймеру сбросится на третью. Может кто знает где устанавливается версия во время загрузки? Потому как в igmp.c при аттаче устройства чётко написано, что установить версию в дефолтную которая указана в сисктле, однако всё равно получается третья.
А вообще, на сколько я понимаю RFC 3376, провайдер должен передавать в сеть посылки, которые бы включали нужную версию и обновляли таймер, но провайдер этого не делает.
Очень сильно жду ваших предложений и помощи. Хотя бы с указанием места где во время загрузки устанавливается версия IGMP и почему вместо дефолтной второй встаёт третья. Спасибо.

Аватара пользователя
VampireNF
ефрейтор
Сообщения: 60
Зарегистрирован: 2010-07-27 20:28:31
Откуда: Санкт-Петербург

Re: FreeBSD 8.1 и версия IGMP

Непрочитанное сообщение VampireNF » 2011-12-29 22:42:04

Ну собственно как я и ожидал, данное исправление как-то коряво работает. Проходит пакет джоин, ТВ показывает секунд 30, а потом вырубается. Как выяснилось связано это с тем, что не отправляется пакет с подтверждением нахождения в группе. Если подцеплять машину с ХР-ой и прописанной в ней HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\IGMPVersion=3 (число 3 включает вторую версию), то всё работает прекрасно и прекращения показа нет. Очень надеюсь на ваши подсказки.

Аватара пользователя
VampireNF
ефрейтор
Сообщения: 60
Зарегистрирован: 2010-07-27 20:28:31
Откуда: Санкт-Петербург

Re: FreeBSD 8.1 и версия IGMP

Непрочитанное сообщение VampireNF » 2011-12-31 17:09:30

Ну собственно решил я проблемы. Всё по порядку. Для начала мой провайдер по каким-то причинам не рассылает Querier ответы второй версии, хотя вещание ведётся именно на ней. Вместо этого попадаются подобные пакеты третей версии.
Почему переменная сисктл дефалт_версион не пашет? Во-первых, по моим наблюдениям по каким-то причинам сначала подключается IGMP протокол, а потом меняется переменная, по этому во время создания ИП стека версия у IGMP 3. Так что я сделал чтобы во время загрузки была вторая версия при любом раскладе. А на третью протокол умеет переключатся сам, по истечению таймер (см. RFC). Да и на любую может переключится.
Почему пропадал IPTV сигнал? Тоже оказалось всё просто. Фря шлёт пакет с приглашением и честно ждёт ответа от сервера с временем подтверждения состояния в группе. Но провайдер "какаха", он такой пакет не шлёт. По этому я вставил посылку пакета даже если ещё ожидается подключение к группе. И теперь всё работает на ура.
А вообще провайдеру бы вставить, если честно за такое. Почему работало в ХР я не знаю, видимо ей наплевать включили её в группу или нет, шлёт пакеты и всё :)
Вот патч, если кому интересно:

Код: Выделить всё

# diff /tmp/8.2/usr/src/sys/netinet/igmp.c /usr/src/sys/netinet/igmp.c
231a232
> static VNET_DEFINE(int, igmp_debug) = 0;
239a241
> #define       V_igmp_debug                    VNET(igmp_debug)
250a253,255
> SYSCTL_VNET_INT(_net_inet_igmp, OID_AUTO, igmp_debug, CTLFLAG_RW,
>     &VNET_NAME(igmp_debug), 0,
>     "Enable debug messages");
346a352,353
> if(V_igmp_debug) printf("IGMP.C: sysctl_igmp_default_version() start \n");
>
354a362,363
> if(V_igmp_debug) printf("IGMP.C: sysctl_igmp_default_version() V_igmp_default_version=%u \n", V_igmp_default_version);
>
360a370,372
>
>               if(V_igmp_debug) printf("IGMP.C: sysctl_igmp_default_version() ERROR to change! \n");
>
370a383,385
>
>       if(V_igmp_debug) printf("IGMP.C: sysctl_igmp_default_version() end \n");
>
547a563,564
>       if(V_igmp_debug) printf("IGMP.C: igmp_domifattach() start \n");
>
555a573,574
>       if(V_igmp_debug) printf("IGMP.C: igmp_domifattach() end \n");
>
566a586,593
>       int old_version_timer;
>
>       old_version_timer = IGMP_RV_INIT * IGMP_QI_INIT + IGMP_QRI_INIT;
>       old_version_timer *= IGMP_URI_INIT;
>
>       if(V_igmp_debug) printf("IGMP.C: igi_alloc_locked() start \n");
>       if(V_igmp_debug) printf("IGMP.C: igi_alloc_locked() old_version_timer=%i \n", old_version_timer);
>
574c601,602
<       igi->igi_version = V_igmp_default_version;
---
>       igi->igi_version = IGMP_VERSION_2;/*V_igmp_default_version;*/
>       igi->igi_v2_timer = old_version_timer;
580a609,611
>       if(V_igmp_debug) printf("IGMP.C: igi_allocl_locked() V_igmp_default_version = %u, for ifp ??? \n", V_igmp_default_version);
>       if(V_igmp_debug) printf("IGMP.C: igi_allocl_locked() igi->igiversion = %u, for ifp ??? \n", igi->igi_version);
>
593a625,627
>
>       if(V_igmp_debug) printf("IGMP.C: igi_alloc_locked() end \n");
>
715a750,751
> if(V_igmp_debug) printf("IGMP.C: igmp_input_v1_query() start \n");
>
786c822
<
---
> if(V_igmp_debug) printf("IGMP.C: igmp_input_v1_query() stop \n");
804a841,842
>       if(V_igmp_debug) printf("IGMP.C: igmp_input_v2_query() start \n");
>
851a890,892
>
>               if(V_igmp_debug) printf("IGMP.C: igmp_input_v2_query() is_general_query \n");
>
867a909,911
>
>               if(V_igmp_debug) printf("IGMP.C: igmp_input_v2_query() Group-specific IGMPv2 query \n");
>
879a924,925
> if(V_igmp_debug) printf("IGMP.C: igmp_input_v2_query() end \n");
>
1650a1697,1698
>       if(V_igmp_debug) printf("IGMP.C: igmp_fasttimo_vnet() start \n");
>
1658a1707,1710
>       {
>
>               if(V_igmp_debug) printf("IGMP.C: igmp_fasttimo_vnet() 1 or 2 or 3 timers are running. V_current_state_timers_running=%u V_interface_timers_running=%u V_state_change_timers_running=%u  \n", V_current_state_timers_running, V_interface_timers_running ,V_state_change_timers_running);
>
1659a1712
>       }
1673a1727,1729
>
>                               if(V_igmp_debug) printf("IGMP.C: igmp_fasttimo_vnet() igi_v3_timer=0 \n");
>
1674a1731,1733
>
>                       if(V_igmp_debug) printf("IGMP.C: igmp_fasttimo_vnet() igmp_v3_dispatch_general_query() now running \n");
>
1719a1779,1781
>
>                               if(V_igmp_debug) printf("IGMP.C: igmp_fasttimo_vnet() igmp_v1v2_process_timer()  now running; IGMPv=%u \n", igi->igi_version);
>
1723a1786,1788
>
>                       if(V_igmp_debug) printf("IGMP.C: igmp_fasttimo_vnet() igmp_v3_process_group_timers() now running \n");
>
1750a1816,1819
>
> if(V_igmp_debug) printf("IGMP.C: igmp_fasttimo_vnet() end \n");
>
>
1763a1833,1834
> if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_group_timer() start \n");
>
1767a1839,1841
>
>       if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_group_timer() inm_timer=0. Interface name=%s\n", inm->inm_ifp->if_xname);
>
1769a1844,1846
>
>       if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_group_timer() --inm_timer=0. Interface name=%s\n", inm->inm_ifp->if_xname);
>
1771a1849,1851
>
>       if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_group_timer() V_current_state_timers_running=1. inm->inm_timer=%u. Interface name=%s\n", inm->inm_timer, inm->inm_ifp->if_xname);
>
1775a1856,1857
> if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_group_timer() switch of inm->inm_state start. inm_state=%u. Interface name=%s\n", inm->inm_state, inm->inm_ifp->if_xname);
>
1779d1860
<       case IGMP_IDLE_MEMBER:
1783c1864,1882
<               break;
---
>       break;
>       case IGMP_IDLE_MEMBER:
>                       /*
>                       * If your ISP is the same shit as me, then there is the possibility
>                       * that the Querier will not send the answers that you join the group,
>                       * and in fact in this package contains a timer for the confirmation
>                       * of the group. According to this here, when you exit the timer is
>                       * still sent to report on membership. Although there is an option that
>                       * your network is simply no Querier.
>                       */
>                       if (inm->inm_state == IGMP_IDLE_MEMBER) {
>                               inm->inm_state = IGMP_REPORTING_MEMBER;
>                               inm->inm_timer = IGMP_RANDOM_DELAY(IGMP_V1V2_MAX_RI * 10); /* Max 10 sec RFC 1112*/
>                               V_current_state_timers_running = 1;
>                               if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_group_timer() MUST BE 2! inm_state=%u", inm->inm_state);
>                               if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_group_timer() Your ISP shit or in your network is simply no Querier, but REPORT send by me \n");
>
>                       }
>                       else break;
1785a1885,1886
>               if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_group_timer() IGMP_v1_v2_HOST_MEMBERSHIP_REPORT \n");
>
1797a1899,1901
>
> if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_proc_grp_timers() end \n");
>
1812a1917,1918
> if(V_igmp_debug) printf("IGMP.C: igmp_v3_process_group_timers() start \n");
>
1845a1952,1953
> if(V_igmp_debug) printf("IGMP.C: igmp_v3_process_group_timers() switch of inm_state start \n");
>
1862a1971,1973
>
>                       if(V_igmp_debug) printf("IGMP.C: igmp_v3_process_group_timers() IGMP_REPORTING_MEMBER \n");
>
1915a2027,2029
>
> if(V_igmp_debug) printf("IGMP.C: igmp_v3_process_group_timers() end \n");
>
1956a2071,2072
> if(V_igmp_debug) printf("IGMP.C: igmp_set_version() start \n");
>
1969a2086,2087
>               if(V_igmp_debug) printf("IGMP.C: igmp_set_version() old_timer=%i \n", old_version_timer);
>
1981a2100,2102
>
>                       if(V_igmp_debug) printf("IGMP.C: igmp_set_version() IGMP_VERSION=2. And now igmp_v3_cancel_link_timers starts \n");
>
1989a2111,2113
>
> if(V_igmp_debug) printf("IGMP.C: igmp_set_version() end \n");
>
2089a2214,2215
> if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_querier_timers() start \n");
>
2096c2222,2223
<               if (igi->igi_version != IGMP_VERSION_3) {
---
> /*            if (igi->igi_version != IGMP_VERSION_3) {*/
>               if ((igi->igi_version != IGMP_VERSION_3) && (V_igmp_default_version == IGMP_VERSION_3)) {
2100a2228,2230
>
>                       if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_querier_timers() transition to IGMPv3 on %s \n", igi->igi_ifp->if_xname);
>
2119c2249,2256
<                       --igi->igi_v2_timer;
---
>                       if (igi->igi_v2_timer > 0) {
>
>                               --igi->igi_v2_timer;
>
>                               if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_querier_timers() v2_timer-1 on %s\n", igi->igi_ifp->if_xname);
>                       }
>                       else    if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_querier_timers() v2_timer=0 on %s\n", igi->igi_ifp->if_xname);
>
2153a2291,2293
>
> if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_process_querier_timers() end \n");
>
2197a2338,2340
>
> if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_queue_report() start \n");
>
2248a2392,2393
> if(V_igmp_debug) printf("IGMP.C: igmp_v1v2_queue_report() end \n");
>
2347a2493,2495
> if(V_igmp_debug) printf("IGMP.C: igmp_initial_join() start \n");
>
>
2377a2526,2528
>
>               if(V_igmp_debug) printf("IGMP.C: igmp_initial_join() Attention! inm_state=IGMP_SILENT_MEMBER \n");
>
2393a2545,2547
>
>               if(V_igmp_debug) printf("IGMP.C: igmp_initial_join() IGMPv1v2 join on %s\n", inm->inm_ifp->if_xname);
>
2399a2554,2556
>
>                               if(V_igmp_debug) printf("IGMP.C: igmp_initial_join() error=igmp_v1v2_queue_report()=0 \n");
>
2402a2560
>                               if(V_igmp_debug) printf("IGMP.C: igmp_initial_join() error=0 inm->inm_timer=%u, V_current_state_timers_running=%u. Interface %s.\n", inm->inm_timer, V_current_state_timers_running, inm->inm_ifp->if_xname);
2462a2621,2622
> if(V_igmp_debug) printf("IGMP.C: igmp_initial_join() end \n");
>

vich
проходил мимо

Re: FreeBSD 8.1 и версия IGMP

Непрочитанное сообщение vich » 2014-01-08 14:15:25

В общем мне удалось решить проблему несколько проще (pfSense 2.0.3):
в /etc/rc добавил после:
PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin:/usr/local/sbin
export HOME PATH

это:

# Set sysctl options
/sbin/sysctl net.inet.igmp.default_version=2
/sbin/sysctl net.inet.igmp.sendra=0


работает...