123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716 |
- /*
- PIM for Quagga
- Copyright (C) 2008 Everton da Silva Marques
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; see the file COPYING; if not, write to the
- Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
- MA 02110-1301 USA
-
- $QuaggaId: $Format:%an, %ai, %h$ $
- */
- #include <zebra.h>
- #include "log.h"
- #include "memory.h"
- #include "pimd.h"
- #include "pim_iface.h"
- #include "pim_igmp.h"
- #include "pim_igmpv3.h"
- #include "pim_str.h"
- #include "pim_util.h"
- #include "pim_time.h"
- #include "pim_zebra.h"
- #include "pim_oil.h"
- static void group_retransmit_timer_on(struct igmp_group *group);
- static long igmp_group_timer_remain_msec(struct igmp_group *group);
- static long igmp_source_timer_remain_msec(struct igmp_source *source);
- static void group_query_send(struct igmp_group *group);
- static void source_query_send_by_flag(struct igmp_group *group,
- int num_sources_tosend);
- static void on_trace(const char *label,
- struct interface *ifp, struct in_addr from,
- struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
- {
- if (PIM_DEBUG_IGMP_TRACE) {
- char from_str[100];
- char group_str[100];
- pim_inet4_dump("<from?>", from, from_str, sizeof(from_str));
- pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: from %s on %s: group=%s sources=%d",
- label, from_str, ifp->name, group_str, num_sources);
- }
- }
- int igmp_group_compat_mode(const struct igmp_sock *igmp,
- const struct igmp_group *group)
- {
- struct pim_interface *pim_ifp;
- int64_t now_dsec;
- long older_host_present_interval_dsec;
- zassert(igmp);
- zassert(igmp->interface);
- zassert(igmp->interface->info);
- pim_ifp = igmp->interface->info;
- /*
- RFC 3376: 8.13. Older Host Present Interval
- This value MUST be ((the Robustness Variable) times (the Query
- Interval)) plus (one Query Response Interval).
- older_host_present_interval_dsec = \
- igmp->querier_robustness_variable * \
- 10 * igmp->querier_query_interval + \
- pim_ifp->query_max_response_time_dsec;
- */
- older_host_present_interval_dsec =
- PIM_IGMP_OHPI_DSEC(igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec);
- now_dsec = pim_time_monotonic_dsec();
- if (now_dsec < 1) {
- /* broken timer logged by pim_time_monotonic_dsec() */
- return 3;
- }
- if ((now_dsec - group->last_igmp_v1_report_dsec) < older_host_present_interval_dsec)
- return 1; /* IGMPv1 */
- if ((now_dsec - group->last_igmp_v2_report_dsec) < older_host_present_interval_dsec)
- return 2; /* IGMPv2 */
- return 3; /* IGMPv3 */
- }
- void igmp_group_reset_gmi(struct igmp_group *group)
- {
- long group_membership_interval_msec;
- struct pim_interface *pim_ifp;
- struct igmp_sock *igmp;
- struct interface *ifp;
- igmp = group->group_igmp_sock;
- ifp = igmp->interface;
- pim_ifp = ifp->info;
- /*
- RFC 3376: 8.4. Group Membership Interval
- The Group Membership Interval is the amount of time that must pass
- before a multicast router decides there are no more members of a
- group or a particular source on a network.
- This value MUST be ((the Robustness Variable) times (the Query
- Interval)) plus (one Query Response Interval).
- group_membership_interval_msec = querier_robustness_variable *
- (1000 * querier_query_interval) +
- 100 * query_response_interval_dsec;
- */
- group_membership_interval_msec =
- PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec);
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- zlog_debug("Resetting group %s timer to GMI=%ld.%03ld sec on %s",
- group_str,
- group_membership_interval_msec / 1000,
- group_membership_interval_msec % 1000,
- ifp->name);
- }
- /*
- RFC 3376: 6.2.2. Definition of Group Timers
- The group timer is only used when a group is in EXCLUDE mode and
- it represents the time for the *filter-mode* of the group to
- expire and switch to INCLUDE mode.
- */
- zassert(group->group_filtermode_isexcl);
- igmp_group_timer_on(group, group_membership_interval_msec, ifp->name);
- }
- static int igmp_source_timer(struct thread *t)
- {
- struct igmp_source *source;
- struct igmp_group *group;
- zassert(t);
- source = THREAD_ARG(t);
- zassert(source);
- group = source->source_group;
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_debug("%s: Source timer expired for group %s source %s on %s",
- __PRETTY_FUNCTION__,
- group_str, source_str,
- group->group_igmp_sock->interface->name);
- }
- zassert(source->t_source_timer);
- source->t_source_timer = 0;
- /*
- RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
- Group
- Filter-Mode Source Timer Value Action
- ----------- ------------------ ------
- INCLUDE TIMER == 0 Suggest to stop forwarding
- traffic from source and
- remove source record. If
- there are no more source
- records for the group, delete
- group record.
- EXCLUDE TIMER == 0 Suggest to not forward
- traffic from source
- (DO NOT remove record)
- Source timer switched from (T > 0) to (T == 0): disable forwarding.
- */
- zassert(!source->t_source_timer);
- if (group->group_filtermode_isexcl) {
- /* EXCLUDE mode */
- igmp_source_forward_stop(source);
- }
- else {
- /* INCLUDE mode */
- /* igmp_source_delete() will stop forwarding source */
- igmp_source_delete(source);
- /*
- If there are no more source records for the group, delete group
- record.
- */
- if (!listcount(group->group_source_list)) {
- igmp_group_delete_empty_include(group);
- }
- }
- return 0;
- }
- static void source_timer_off(struct igmp_group *group,
- struct igmp_source *source)
- {
- if (!source->t_source_timer)
- return;
-
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_debug("Cancelling TIMER event for group %s source %s on %s",
- group_str, source_str,
- group->group_igmp_sock->interface->name);
- }
- THREAD_OFF(source->t_source_timer);
- zassert(!source->t_source_timer);
- }
- static void igmp_source_timer_on(struct igmp_group *group,
- struct igmp_source *source,
- long interval_msec)
- {
- source_timer_off(group, source);
- if (PIM_DEBUG_IGMP_EVENTS) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_debug("Scheduling %ld.%03ld sec TIMER event for group %s source %s on %s",
- interval_msec / 1000,
- interval_msec % 1000,
- group_str, source_str,
- group->group_igmp_sock->interface->name);
- }
- THREAD_TIMER_MSEC_ON(master, source->t_source_timer,
- igmp_source_timer,
- source, interval_msec);
- zassert(source->t_source_timer);
- /*
- RFC 3376: 6.3. IGMPv3 Source-Specific Forwarding Rules
-
- Source timer switched from (T == 0) to (T > 0): enable forwarding.
- */
- igmp_source_forward_start(source);
- }
- void igmp_source_reset_gmi(struct igmp_sock *igmp,
- struct igmp_group *group,
- struct igmp_source *source)
- {
- long group_membership_interval_msec;
- struct pim_interface *pim_ifp;
- struct interface *ifp;
- ifp = igmp->interface;
- pim_ifp = ifp->info;
- group_membership_interval_msec =
- PIM_IGMP_GMI_MSEC(igmp->querier_robustness_variable,
- igmp->querier_query_interval,
- pim_ifp->igmp_query_max_response_time_dsec);
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_debug("Resetting source %s timer to GMI=%ld.%03ld sec for group %s on %s",
- source_str,
- group_membership_interval_msec / 1000,
- group_membership_interval_msec % 1000,
- group_str,
- ifp->name);
- }
- igmp_source_timer_on(group, source,
- group_membership_interval_msec);
- }
- static void source_mark_delete_flag(struct list *source_list)
- {
- struct listnode *src_node;
- struct igmp_source *src;
- for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
- IGMP_SOURCE_DO_DELETE(src->source_flags);
- }
- }
- static void source_mark_send_flag(struct list *source_list)
- {
- struct listnode *src_node;
- struct igmp_source *src;
- for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
- IGMP_SOURCE_DO_SEND(src->source_flags);
- }
- }
- static int source_mark_send_flag_by_timer(struct list *source_list)
- {
- struct listnode *src_node;
- struct igmp_source *src;
- int num_marked_sources = 0;
- for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
- /* Is source timer running? */
- if (src->t_source_timer) {
- IGMP_SOURCE_DO_SEND(src->source_flags);
- ++num_marked_sources;
- }
- else {
- IGMP_SOURCE_DONT_SEND(src->source_flags);
- }
- }
- return num_marked_sources;
- }
- static void source_clear_send_flag(struct list *source_list)
- {
- struct listnode *src_node;
- struct igmp_source *src;
- for (ALL_LIST_ELEMENTS_RO(source_list, src_node, src)) {
- IGMP_SOURCE_DONT_SEND(src->source_flags);
- }
- }
- /*
- Any source (*,G) is forwarded only if mode is EXCLUDE {empty}
- */
- static void group_exclude_fwd_anysrc_ifempty(struct igmp_group *group)
- {
- zassert(group->group_filtermode_isexcl);
- if (listcount(group->group_source_list) < 1) {
- igmp_anysource_forward_start(group);
- }
- }
- void igmp_source_free(struct igmp_source *source)
- {
- /* make sure there is no source timer running */
- zassert(!source->t_source_timer);
- XFREE(MTYPE_PIM_IGMP_GROUP_SOURCE, source);
- }
- static void source_channel_oil_detach(struct igmp_source *source)
- {
- if (source->source_channel_oil) {
- pim_channel_oil_del(source->source_channel_oil);
- source->source_channel_oil = 0;
- }
- }
- /*
- igmp_source_delete: stop fowarding, and delete the source
- igmp_source_forward_stop: stop fowarding, but keep the source
- */
- void igmp_source_delete(struct igmp_source *source)
- {
- struct igmp_group *group;
- group = source->source_group;
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_debug("Deleting IGMP source %s for group %s from socket %d interface %s",
- source_str, group_str,
- group->group_igmp_sock->fd,
- group->group_igmp_sock->interface->name);
- }
- source_timer_off(group, source);
- igmp_source_forward_stop(source);
- /* sanity check that forwarding has been disabled */
- if (IGMP_SOURCE_TEST_FORWARDING(source->source_flags)) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_warn("%s: forwarding=ON(!) IGMP source %s for group %s from socket %d interface %s",
- __PRETTY_FUNCTION__,
- source_str, group_str,
- group->group_igmp_sock->fd,
- group->group_igmp_sock->interface->name);
- /* warning only */
- }
- source_channel_oil_detach(source);
- /*
- notice that listnode_delete() can't be moved
- into igmp_source_free() because the later is
- called by list_delete_all_node()
- */
- listnode_delete(group->group_source_list, source);
- igmp_source_free(source);
- if (group->group_filtermode_isexcl) {
- group_exclude_fwd_anysrc_ifempty(group);
- }
- }
- static void source_delete_by_flag(struct list *source_list)
- {
- struct listnode *src_node;
- struct listnode *src_nextnode;
- struct igmp_source *src;
-
- for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
- if (IGMP_SOURCE_TEST_DELETE(src->source_flags))
- igmp_source_delete(src);
- }
- void igmp_source_delete_expired(struct list *source_list)
- {
- struct listnode *src_node;
- struct listnode *src_nextnode;
- struct igmp_source *src;
-
- for (ALL_LIST_ELEMENTS(source_list, src_node, src_nextnode, src))
- if (!src->t_source_timer)
- igmp_source_delete(src);
- }
- struct igmp_source *igmp_find_source_by_addr(struct igmp_group *group,
- struct in_addr src_addr)
- {
- struct listnode *src_node;
- struct igmp_source *src;
- for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src))
- if (src_addr.s_addr == src->source_addr.s_addr)
- return src;
- return 0;
- }
- struct igmp_source *
- source_new (struct igmp_group *group,
- struct in_addr src_addr)
- {
- struct igmp_source *src;
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", src_addr, source_str, sizeof(source_str));
- zlog_debug("Creating new IGMP source %s for group %s on socket %d interface %s",
- source_str, group_str,
- group->group_igmp_sock->fd,
- group->group_igmp_sock->interface->name);
- }
- src = XMALLOC(MTYPE_PIM_IGMP_GROUP_SOURCE, sizeof(*src));
- if (!src) {
- zlog_warn("%s %s: XMALLOC() failure",
- __FILE__, __PRETTY_FUNCTION__);
- return 0; /* error, not found, could not create */
- }
-
- src->t_source_timer = NULL;
- src->source_group = group; /* back pointer */
- src->source_addr = src_addr;
- src->source_creation = pim_time_monotonic_sec();
- src->source_flags = 0;
- src->source_query_retransmit_count = 0;
- src->source_channel_oil = NULL;
- listnode_add(group->group_source_list, src);
- zassert(!src->t_source_timer); /* source timer == 0 */
- /* Any source (*,G) is forwarded only if mode is EXCLUDE {empty} */
- igmp_anysource_forward_stop(group);
- return src;
- }
- static struct igmp_source *add_source_by_addr(struct igmp_sock *igmp,
- struct igmp_group *group,
- struct in_addr src_addr)
- {
- struct igmp_source *src;
- src = igmp_find_source_by_addr(group, src_addr);
- if (src) {
- return src;
- }
- src = source_new(group, src_addr);
- if (!src) {
- return 0;
- }
- return src;
- }
- static void allow(struct igmp_sock *igmp, struct in_addr from,
- struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
- {
- struct igmp_group *group;
- int i;
- /* non-existant group is created as INCLUDE {empty} */
- group = igmp_add_group_by_addr(igmp, group_addr);
- if (!group) {
- return;
- }
- /* scan received sources */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
- src_addr = sources + i;
- source = add_source_by_addr(igmp, group, *src_addr);
- if (!source) {
- continue;
- }
- /*
- RFC 3376: 6.4.1. Reception of Current-State Records
- When receiving IS_IN reports for groups in EXCLUDE mode is
- sources should be moved from set with (timers = 0) to set with
- (timers > 0).
- igmp_source_reset_gmi() below, resetting the source timers to
- GMI, accomplishes this.
- */
- igmp_source_reset_gmi(igmp, group, source);
- } /* scan received sources */
- }
- void igmpv3_report_isin(struct igmp_sock *igmp, struct in_addr from,
- struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
- {
- on_trace(__PRETTY_FUNCTION__,
- igmp->interface, from, group_addr, num_sources, sources);
- allow(igmp, from, group_addr, num_sources, sources);
- }
- static void isex_excl(struct igmp_group *group,
- int num_sources, struct in_addr *sources)
- {
- int i;
- /* EXCLUDE mode */
- zassert(group->group_filtermode_isexcl);
-
- /* E.1: set deletion flag for known sources (X,Y) */
- source_mark_delete_flag(group->group_source_list);
- /* scan received sources (A) */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
- src_addr = sources + i;
- /* E.2: lookup reported source from (A) in (X,Y) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
- /* E.3: if found, clear deletion flag: (X*A) or (Y*A) */
- IGMP_SOURCE_DONT_DELETE(source->source_flags);
- }
- else {
- /* E.4: if not found, create source with timer=GMI: (A-X-Y) */
- source = source_new(group, *src_addr);
- if (!source) {
- /* ugh, internal malloc failure, skip source */
- continue;
- }
- zassert(!source->t_source_timer); /* timer == 0 */
- igmp_source_reset_gmi(group->group_igmp_sock, group, source);
- zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
- }
- } /* scan received sources */
- /* E.5: delete all sources marked with deletion flag: (X-A) and (Y-A) */
- source_delete_by_flag(group->group_source_list);
- }
- static void isex_incl(struct igmp_group *group,
- int num_sources, struct in_addr *sources)
- {
- int i;
- /* INCLUDE mode */
- zassert(!group->group_filtermode_isexcl);
-
- /* I.1: set deletion flag for known sources (A) */
- source_mark_delete_flag(group->group_source_list);
- /* scan received sources (B) */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
- src_addr = sources + i;
- /* I.2: lookup reported source (B) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
- /* I.3: if found, clear deletion flag (A*B) */
- IGMP_SOURCE_DONT_DELETE(source->source_flags);
- }
- else {
- /* I.4: if not found, create source with timer=0 (B-A) */
- source = source_new(group, *src_addr);
- if (!source) {
- /* ugh, internal malloc failure, skip source */
- continue;
- }
- zassert(!source->t_source_timer); /* (B-A) timer=0 */
- }
- } /* scan received sources */
- /* I.5: delete all sources marked with deletion flag (A-B) */
- source_delete_by_flag(group->group_source_list);
- group->group_filtermode_isexcl = 1; /* boolean=true */
- zassert(group->group_filtermode_isexcl);
- group_exclude_fwd_anysrc_ifempty(group);
- }
- void igmpv3_report_isex(struct igmp_sock *igmp, struct in_addr from,
- struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
- {
- struct interface *ifp = igmp->interface;
- struct igmp_group *group;
- on_trace(__PRETTY_FUNCTION__,
- ifp, from, group_addr, num_sources, sources);
- /* non-existant group is created as INCLUDE {empty} */
- group = igmp_add_group_by_addr(igmp, group_addr);
- if (!group) {
- return;
- }
- if (group->group_filtermode_isexcl) {
- /* EXCLUDE mode */
- isex_excl(group, num_sources, sources);
- }
- else {
- /* INCLUDE mode */
- isex_incl(group, num_sources, sources);
- zassert(group->group_filtermode_isexcl);
- }
- zassert(group->group_filtermode_isexcl);
- igmp_group_reset_gmi(group);
- }
- static void toin_incl(struct igmp_group *group,
- int num_sources, struct in_addr *sources)
- {
- struct igmp_sock *igmp = group->group_igmp_sock;
- int num_sources_tosend = listcount(group->group_source_list);
- int i;
- /* Set SEND flag for all known sources (A) */
- source_mark_send_flag(group->group_source_list);
- /* Scan received sources (B) */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
- src_addr = sources + i;
- /* Lookup reported source (B) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
- /* If found, clear SEND flag (A*B) */
- IGMP_SOURCE_DONT_SEND(source->source_flags);
- --num_sources_tosend;
- }
- else {
- /* If not found, create new source */
- source = source_new(group, *src_addr);
- if (!source) {
- /* ugh, internal malloc failure, skip source */
- continue;
- }
- }
- /* (B)=GMI */
- igmp_source_reset_gmi(igmp, group, source);
- }
- /* Send sources marked with SEND flag: Q(G,A-B) */
- if (num_sources_tosend > 0) {
- source_query_send_by_flag(group, num_sources_tosend);
- }
- }
- static void toin_excl(struct igmp_group *group,
- int num_sources, struct in_addr *sources)
- {
- struct igmp_sock *igmp = group->group_igmp_sock;
- int num_sources_tosend;
- int i;
- /* Set SEND flag for X (sources with timer > 0) */
- num_sources_tosend = source_mark_send_flag_by_timer(group->group_source_list);
- /* Scan received sources (A) */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
- src_addr = sources + i;
- /* Lookup reported source (A) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
- if (source->t_source_timer) {
- /* If found and timer running, clear SEND flag (X*A) */
- IGMP_SOURCE_DONT_SEND(source->source_flags);
- --num_sources_tosend;
- }
- }
- else {
- /* If not found, create new source */
- source = source_new(group, *src_addr);
- if (!source) {
- /* ugh, internal malloc failure, skip source */
- continue;
- }
- }
- /* (A)=GMI */
- igmp_source_reset_gmi(igmp, group, source);
- }
- /* Send sources marked with SEND flag: Q(G,X-A) */
- if (num_sources_tosend > 0) {
- source_query_send_by_flag(group, num_sources_tosend);
- }
- /* Send Q(G) */
- group_query_send(group);
- }
- void igmpv3_report_toin(struct igmp_sock *igmp, struct in_addr from,
- struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
- {
- struct interface *ifp = igmp->interface;
- struct igmp_group *group;
- on_trace(__PRETTY_FUNCTION__,
- ifp, from, group_addr, num_sources, sources);
- /* non-existant group is created as INCLUDE {empty} */
- group = igmp_add_group_by_addr(igmp, group_addr);
- if (!group) {
- return;
- }
- if (group->group_filtermode_isexcl) {
- /* EXCLUDE mode */
- toin_excl(group, num_sources, sources);
- }
- else {
- /* INCLUDE mode */
- toin_incl(group, num_sources, sources);
- }
- }
- static void toex_incl(struct igmp_group *group,
- int num_sources, struct in_addr *sources)
- {
- int num_sources_tosend = 0;
- int i;
- zassert(!group->group_filtermode_isexcl);
- /* Set DELETE flag for all known sources (A) */
- source_mark_delete_flag(group->group_source_list);
- /* Clear off SEND flag from all known sources (A) */
- source_clear_send_flag(group->group_source_list);
- /* Scan received sources (B) */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
- src_addr = sources + i;
- /* Lookup reported source (B) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
- /* If found, clear deletion flag: (A*B) */
- IGMP_SOURCE_DONT_DELETE(source->source_flags);
- /* and set SEND flag (A*B) */
- IGMP_SOURCE_DO_SEND(source->source_flags);
- ++num_sources_tosend;
- }
- else {
- /* If source not found, create source with timer=0: (B-A)=0 */
- source = source_new(group, *src_addr);
- if (!source) {
- /* ugh, internal malloc failure, skip source */
- continue;
- }
- zassert(!source->t_source_timer); /* (B-A) timer=0 */
- }
- } /* Scan received sources (B) */
- group->group_filtermode_isexcl = 1; /* boolean=true */
- /* Delete all sources marked with DELETE flag (A-B) */
- source_delete_by_flag(group->group_source_list);
- /* Send sources marked with SEND flag: Q(G,A*B) */
- if (num_sources_tosend > 0) {
- source_query_send_by_flag(group, num_sources_tosend);
- }
- zassert(group->group_filtermode_isexcl);
- group_exclude_fwd_anysrc_ifempty(group);
- }
- static void toex_excl(struct igmp_group *group,
- int num_sources, struct in_addr *sources)
- {
- int num_sources_tosend = 0;
- int i;
- /* set DELETE flag for all known sources (X,Y) */
- source_mark_delete_flag(group->group_source_list);
- /* clear off SEND flag from all known sources (X,Y) */
- source_clear_send_flag(group->group_source_list);
- /* scan received sources (A) */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
-
- src_addr = sources + i;
-
- /* lookup reported source (A) in known sources (X,Y) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
- /* if found, clear off DELETE flag from reported source (A) */
- IGMP_SOURCE_DONT_DELETE(source->source_flags);
- }
- else {
- /* if not found, create source with Group Timer: (A-X-Y)=Group Timer */
- long group_timer_msec;
- source = source_new(group, *src_addr);
- if (!source) {
- /* ugh, internal malloc failure, skip source */
- continue;
- }
- zassert(!source->t_source_timer); /* timer == 0 */
- group_timer_msec = igmp_group_timer_remain_msec(group);
- igmp_source_timer_on(group, source, group_timer_msec);
- zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
- /* make sure source is created with DELETE flag unset */
- zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
- }
- /* make sure reported source has DELETE flag unset */
- zassert(!IGMP_SOURCE_TEST_DELETE(source->source_flags));
- if (source->t_source_timer) {
- /* if source timer>0 mark SEND flag: Q(G,A-Y) */
- IGMP_SOURCE_DO_SEND(source->source_flags);
- ++num_sources_tosend;
- }
- } /* scan received sources (A) */
- /*
- delete all sources marked with DELETE flag:
- Delete (X-A)
- Delete (Y-A)
- */
- source_delete_by_flag(group->group_source_list);
-
- /* send sources marked with SEND flag: Q(G,A-Y) */
- if (num_sources_tosend > 0) {
- source_query_send_by_flag(group, num_sources_tosend);
- }
- }
- void igmpv3_report_toex(struct igmp_sock *igmp, struct in_addr from,
- struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
- {
- struct interface *ifp = igmp->interface;
- struct igmp_group *group;
- on_trace(__PRETTY_FUNCTION__,
- ifp, from, group_addr, num_sources, sources);
- /* non-existant group is created as INCLUDE {empty} */
- group = igmp_add_group_by_addr(igmp, group_addr);
- if (!group) {
- return;
- }
- if (group->group_filtermode_isexcl) {
- /* EXCLUDE mode */
- toex_excl(group, num_sources, sources);
- }
- else {
- /* INCLUDE mode */
- toex_incl(group, num_sources, sources);
- zassert(group->group_filtermode_isexcl);
- }
- zassert(group->group_filtermode_isexcl);
- /* Group Timer=GMI */
- igmp_group_reset_gmi(group);
- }
- void igmpv3_report_allow(struct igmp_sock *igmp, struct in_addr from,
- struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
- {
- on_trace(__PRETTY_FUNCTION__,
- igmp->interface, from, group_addr, num_sources, sources);
- allow(igmp, from, group_addr, num_sources, sources);
- }
- /*
- RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
- When transmitting a group specific query, if the group timer is
- larger than LMQT, the "Suppress Router-Side Processing" bit is set
- in the query message.
- */
- static void group_retransmit_group(struct igmp_group *group)
- {
- char query_buf[PIM_IGMP_BUFSIZE_WRITE];
- struct igmp_sock *igmp;
- struct pim_interface *pim_ifp;
- long lmqc; /* Last Member Query Count */
- long lmqi_msec; /* Last Member Query Interval */
- long lmqt_msec; /* Last Member Query Time */
- int s_flag;
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
- lmqc = igmp->querier_robustness_variable;
- lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
- lmqt_msec = lmqc * lmqi_msec;
- /*
- RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
-
- When transmitting a group specific query, if the group timer is
- larger than LMQT, the "Suppress Router-Side Processing" bit is set
- in the query message.
- */
- s_flag = igmp_group_timer_remain_msec(group) > lmqt_msec;
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- zlog_debug("retransmit_group_specific_query: group %s on %s: s_flag=%d count=%d",
- group_str, igmp->interface->name, s_flag,
- group->group_specific_query_retransmit_count);
- }
- /*
- RFC3376: 4.1.12. IP Destination Addresses for Queries
- Group-Specific and Group-and-Source-Specific Queries are sent with
- an IP destination address equal to the multicast address of
- interest.
- */
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf,
- sizeof(query_buf),
- 0 /* num_sources_tosend */,
- group->group_addr /* dst_addr */,
- group->group_addr /* group_addr */,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- s_flag,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
- }
- /*
- RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
- When building a group and source specific query for a group G, two
- separate query messages are sent for the group. The first one has
- the "Suppress Router-Side Processing" bit set and contains all the
- sources with retransmission state and timers greater than LMQT. The
- second has the "Suppress Router-Side Processing" bit clear and
- contains all the sources with retransmission state and timers lower
- or equal to LMQT. If either of the two calculated messages does not
- contain any sources, then its transmission is suppressed.
- */
- static int group_retransmit_sources(struct igmp_group *group,
- int send_with_sflag_set)
- {
- struct igmp_sock *igmp;
- struct pim_interface *pim_ifp;
- long lmqc; /* Last Member Query Count */
- long lmqi_msec; /* Last Member Query Interval */
- long lmqt_msec; /* Last Member Query Time */
- char query_buf1[PIM_IGMP_BUFSIZE_WRITE]; /* 1 = with s_flag set */
- char query_buf2[PIM_IGMP_BUFSIZE_WRITE]; /* 2 = with s_flag clear */
- int query_buf1_max_sources;
- int query_buf2_max_sources;
- struct in_addr *source_addr1;
- struct in_addr *source_addr2;
- int num_sources_tosend1;
- int num_sources_tosend2;
- struct listnode *src_node;
- struct igmp_source *src;
- int num_retransmit_sources_left = 0;
-
- query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
- query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
-
- source_addr1 = (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
- source_addr2 = (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
- lmqc = igmp->querier_robustness_variable;
- lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
- lmqt_msec = lmqc * lmqi_msec;
- /* Scan all group sources */
- for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
- /* Source has retransmission state? */
- if (src->source_query_retransmit_count < 1)
- continue;
- if (--src->source_query_retransmit_count > 0) {
- ++num_retransmit_sources_left;
- }
- /* Copy source address into appropriate query buffer */
- if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
- *source_addr1 = src->source_addr;
- ++source_addr1;
- }
- else {
- *source_addr2 = src->source_addr;
- ++source_addr2;
- }
- }
-
- num_sources_tosend1 = source_addr1 - (struct in_addr *)(query_buf1 + IGMP_V3_SOURCES_OFFSET);
- num_sources_tosend2 = source_addr2 - (struct in_addr *)(query_buf2 + IGMP_V3_SOURCES_OFFSET);
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- zlog_debug("retransmit_grp&src_specific_query: group %s on %s: srcs_with_sflag=%d srcs_wo_sflag=%d will_send_sflag=%d retransmit_src_left=%d",
- group_str, igmp->interface->name,
- num_sources_tosend1,
- num_sources_tosend2,
- send_with_sflag_set,
- num_retransmit_sources_left);
- }
- if (num_sources_tosend1 > 0) {
- /*
- Send group-and-source-specific query with s_flag set and all
- sources with timers greater than LMQT.
- */
- if (send_with_sflag_set) {
- query_buf1_max_sources = (sizeof(query_buf1) - IGMP_V3_SOURCES_OFFSET) >> 2;
- if (num_sources_tosend1 > query_buf1_max_sources) {
- char group_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- zlog_warn("%s: group %s on %s: s_flag=1 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
- __PRETTY_FUNCTION__, group_str, igmp->interface->name,
- num_sources_tosend1, sizeof(query_buf1), query_buf1_max_sources);
- }
- else {
- /*
- RFC3376: 4.1.12. IP Destination Addresses for Queries
-
- Group-Specific and Group-and-Source-Specific Queries are sent with
- an IP destination address equal to the multicast address of
- interest.
- */
-
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf1,
- sizeof(query_buf1),
- num_sources_tosend1,
- group->group_addr,
- group->group_addr,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- 1 /* s_flag */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
-
- }
- } /* send_with_sflag_set */
- }
- if (num_sources_tosend2 > 0) {
- /*
- Send group-and-source-specific query with s_flag clear and all
- sources with timers lower or equal to LMQT.
- */
-
- query_buf2_max_sources = (sizeof(query_buf2) - IGMP_V3_SOURCES_OFFSET) >> 2;
- if (num_sources_tosend2 > query_buf2_max_sources) {
- char group_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- zlog_warn("%s: group %s on %s: s_flag=0 unable to fit %d sources into buf_size=%zu (max_sources=%d)",
- __PRETTY_FUNCTION__, group_str, igmp->interface->name,
- num_sources_tosend2, sizeof(query_buf2), query_buf2_max_sources);
- }
- else {
- /*
- RFC3376: 4.1.12. IP Destination Addresses for Queries
- Group-Specific and Group-and-Source-Specific Queries are sent with
- an IP destination address equal to the multicast address of
- interest.
- */
- pim_igmp_send_membership_query(group,
- igmp->fd,
- igmp->interface->name,
- query_buf2,
- sizeof(query_buf2),
- num_sources_tosend2,
- group->group_addr,
- group->group_addr,
- pim_ifp->igmp_specific_query_max_response_time_dsec,
- 0 /* s_flag */,
- igmp->querier_robustness_variable,
- igmp->querier_query_interval);
- }
- }
- return num_retransmit_sources_left;
- }
- static int igmp_group_retransmit(struct thread *t)
- {
- struct igmp_group *group;
- int num_retransmit_sources_left;
- int send_with_sflag_set; /* boolean */
- zassert(t);
- group = THREAD_ARG(t);
- zassert(group);
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- zlog_debug("group_retransmit_timer: group %s on %s",
- group_str, group->group_igmp_sock->interface->name);
- }
- /* Retransmit group-specific queries? (RFC3376: 6.6.3.1) */
- if (group->group_specific_query_retransmit_count > 0) {
- /* Retransmit group-specific queries (RFC3376: 6.6.3.1) */
- group_retransmit_group(group);
- --group->group_specific_query_retransmit_count;
- /*
- RFC3376: 6.6.3.2
- If a group specific query is scheduled to be transmitted at the
- same time as a group and source specific query for the same group,
- then transmission of the group and source specific message with the
- "Suppress Router-Side Processing" bit set may be suppressed.
- */
- send_with_sflag_set = 0; /* boolean=false */
- }
- else {
- send_with_sflag_set = 1; /* boolean=true */
- }
- /* Retransmit group-and-source-specific queries (RFC3376: 6.6.3.2) */
- num_retransmit_sources_left = group_retransmit_sources(group,
- send_with_sflag_set);
- group->t_group_query_retransmit_timer = 0;
- /*
- Keep group retransmit timer running if there is any retransmit
- counter pending
- */
- if ((num_retransmit_sources_left > 0) ||
- (group->group_specific_query_retransmit_count > 0)) {
- group_retransmit_timer_on(group);
- }
- return 0;
- }
- /*
- group_retransmit_timer_on:
- if group retransmit timer isn't running, starts it;
- otherwise, do nothing
- */
- static void group_retransmit_timer_on(struct igmp_group *group)
- {
- struct igmp_sock *igmp;
- struct pim_interface *pim_ifp;
- long lmqi_msec; /* Last Member Query Interval */
- /* if group retransmit timer is running, do nothing */
- if (group->t_group_query_retransmit_timer) {
- return;
- }
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
- lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- zlog_debug("Scheduling %ld.%03ld sec retransmit timer for group %s on %s",
- lmqi_msec / 1000,
- lmqi_msec % 1000,
- group_str,
- igmp->interface->name);
- }
- THREAD_TIMER_MSEC_ON(master, group->t_group_query_retransmit_timer,
- igmp_group_retransmit,
- group, lmqi_msec);
- }
- static long igmp_group_timer_remain_msec(struct igmp_group *group)
- {
- return pim_time_timer_remain_msec(group->t_group_timer);
- }
- static long igmp_source_timer_remain_msec(struct igmp_source *source)
- {
- return pim_time_timer_remain_msec(source->t_source_timer);
- }
- /*
- RFC3376: 6.6.3.1. Building and Sending Group Specific Queries
- */
- static void group_query_send(struct igmp_group *group)
- {
- long lmqc; /* Last Member Query Count */
- lmqc = group->group_igmp_sock->querier_robustness_variable;
- /* lower group timer to lmqt */
- igmp_group_timer_lower_to_lmqt(group);
- /* reset retransmission counter */
- group->group_specific_query_retransmit_count = lmqc;
- /* immediately send group specific query (decrease retransmit counter by 1)*/
- group_retransmit_group(group);
- /* make sure group retransmit timer is running */
- group_retransmit_timer_on(group);
- }
- /*
- RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
- */
- static void source_query_send_by_flag(struct igmp_group *group,
- int num_sources_tosend)
- {
- struct igmp_sock *igmp;
- struct pim_interface *pim_ifp;
- struct listnode *src_node;
- struct igmp_source *src;
- long lmqc; /* Last Member Query Count */
- long lmqi_msec; /* Last Member Query Interval */
- long lmqt_msec; /* Last Member Query Time */
- zassert(num_sources_tosend > 0);
- igmp = group->group_igmp_sock;
- pim_ifp = igmp->interface->info;
- lmqc = igmp->querier_robustness_variable;
- lmqi_msec = 100 * pim_ifp->igmp_specific_query_max_response_time_dsec;
- lmqt_msec = lmqc * lmqi_msec;
- /*
- RFC3376: 6.6.3.2. Building and Sending Group and Source Specific Queries
- (...) for each of the sources in X of group G, with source timer larger
- than LMQT:
- o Set number of retransmissions for each source to [Last Member
- Query Count].
- o Lower source timer to LMQT.
- */
- for (ALL_LIST_ELEMENTS_RO(group->group_source_list, src_node, src)) {
- if (IGMP_SOURCE_TEST_SEND(src->source_flags)) {
- /* source "src" in X of group G */
- if (igmp_source_timer_remain_msec(src) > lmqt_msec) {
- src->source_query_retransmit_count = lmqc;
- igmp_source_timer_lower_to_lmqt(src);
- }
- }
- }
- /* send group-and-source specific queries */
- group_retransmit_sources(group, 1 /* send_with_sflag_set=true */);
- /* make sure group retransmit timer is running */
- group_retransmit_timer_on(group);
- }
- static void block_excl(struct igmp_group *group,
- int num_sources, struct in_addr *sources)
- {
- int num_sources_tosend = 0;
- int i;
- /* 1. clear off SEND flag from all known sources (X,Y) */
- source_clear_send_flag(group->group_source_list);
- /* 2. scan received sources (A) */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
-
- src_addr = sources + i;
-
- /* lookup reported source (A) in known sources (X,Y) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (!source) {
- /* 3: if not found, create source with Group Timer: (A-X-Y)=Group Timer */
- long group_timer_msec;
- source = source_new(group, *src_addr);
- if (!source) {
- /* ugh, internal malloc failure, skip source */
- continue;
- }
- zassert(!source->t_source_timer); /* timer == 0 */
- group_timer_msec = igmp_group_timer_remain_msec(group);
- igmp_source_timer_on(group, source, group_timer_msec);
- zassert(source->t_source_timer); /* (A-X-Y) timer > 0 */
- }
- if (source->t_source_timer) {
- /* 4. if source timer>0 mark SEND flag: Q(G,A-Y) */
- IGMP_SOURCE_DO_SEND(source->source_flags);
- ++num_sources_tosend;
- }
- }
-
- /* 5. send sources marked with SEND flag: Q(G,A-Y) */
- if (num_sources_tosend > 0) {
- source_query_send_by_flag(group, num_sources_tosend);
- }
- }
- static void block_incl(struct igmp_group *group,
- int num_sources, struct in_addr *sources)
- {
- int num_sources_tosend = 0;
- int i;
- /* 1. clear off SEND flag from all known sources (B) */
- source_clear_send_flag(group->group_source_list);
- /* 2. scan received sources (A) */
- for (i = 0; i < num_sources; ++i) {
- struct igmp_source *source;
- struct in_addr *src_addr;
-
- src_addr = sources + i;
-
- /* lookup reported source (A) in known sources (B) */
- source = igmp_find_source_by_addr(group, *src_addr);
- if (source) {
- /* 3. if found (A*B), mark SEND flag: Q(G,A*B) */
- IGMP_SOURCE_DO_SEND(source->source_flags);
- ++num_sources_tosend;
- }
- }
-
- /* 4. send sources marked with SEND flag: Q(G,A*B) */
- if (num_sources_tosend > 0) {
- source_query_send_by_flag(group, num_sources_tosend);
- }
- }
- void igmpv3_report_block(struct igmp_sock *igmp, struct in_addr from,
- struct in_addr group_addr,
- int num_sources, struct in_addr *sources)
- {
- struct interface *ifp = igmp->interface;
- struct igmp_group *group;
- on_trace(__PRETTY_FUNCTION__,
- ifp, from, group_addr, num_sources, sources);
- /* non-existant group is created as INCLUDE {empty} */
- group = igmp_add_group_by_addr(igmp, group_addr);
- if (!group) {
- return;
- }
- if (group->group_filtermode_isexcl) {
- /* EXCLUDE mode */
- block_excl(group, num_sources, sources);
- }
- else {
- /* INCLUDE mode */
- block_incl(group, num_sources, sources);
- }
- }
- void igmp_group_timer_lower_to_lmqt(struct igmp_group *group)
- {
- struct igmp_sock *igmp;
- struct interface *ifp;
- struct pim_interface *pim_ifp;
- char *ifname;
- int lmqi_dsec; /* Last Member Query Interval */
- int lmqc; /* Last Member Query Count */
- int lmqt_msec; /* Last Member Query Time */
- /*
- RFC 3376: 6.2.2. Definition of Group Timers
-
- The group timer is only used when a group is in EXCLUDE mode and
- it represents the time for the *filter-mode* of the group to
- expire and switch to INCLUDE mode.
- */
- if (!group->group_filtermode_isexcl) {
- return;
- }
- igmp = group->group_igmp_sock;
- ifp = igmp->interface;
- pim_ifp = ifp->info;
- ifname = ifp->name;
- lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
- lmqc = igmp->querier_robustness_variable;
- lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: group %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
- __PRETTY_FUNCTION__,
- group_str, ifname,
- lmqc, lmqi_dsec, lmqt_msec);
- }
- zassert(group->group_filtermode_isexcl);
- igmp_group_timer_on(group, lmqt_msec, ifname);
- }
- void igmp_source_timer_lower_to_lmqt(struct igmp_source *source)
- {
- struct igmp_group *group;
- struct igmp_sock *igmp;
- struct interface *ifp;
- struct pim_interface *pim_ifp;
- char *ifname;
- int lmqi_dsec; /* Last Member Query Interval */
- int lmqc; /* Last Member Query Count */
- int lmqt_msec; /* Last Member Query Time */
- group = source->source_group;
- igmp = group->group_igmp_sock;
- ifp = igmp->interface;
- pim_ifp = ifp->info;
- ifname = ifp->name;
- lmqi_dsec = pim_ifp->igmp_specific_query_max_response_time_dsec;
- lmqc = igmp->querier_robustness_variable;
- lmqt_msec = PIM_IGMP_LMQT_MSEC(lmqi_dsec, lmqc); /* lmqt_msec = (100 * lmqi_dsec) * lmqc */
- if (PIM_DEBUG_IGMP_TRACE) {
- char group_str[100];
- char source_str[100];
- pim_inet4_dump("<group?>", group->group_addr, group_str, sizeof(group_str));
- pim_inet4_dump("<source?>", source->source_addr, source_str, sizeof(source_str));
- zlog_debug("%s: group %s source %s on %s: LMQC=%d LMQI=%d dsec LMQT=%d msec",
- __PRETTY_FUNCTION__,
- group_str, source_str, ifname,
- lmqc, lmqi_dsec, lmqt_msec);
- }
- igmp_source_timer_on(group, source, lmqt_msec);
- }
- /*
- Copy sources to message:
-
- struct in_addr *sources = (struct in_addr *)(query_buf + IGMP_V3_SOURCES_OFFSET);
- if (num_sources > 0) {
- struct listnode *node;
- struct igmp_source *src;
- int i = 0;
- for (ALL_LIST_ELEMENTS_RO(source_list, node, src)) {
- sources[i++] = src->source_addr;
- }
- }
- */
- void pim_igmp_send_membership_query(struct igmp_group *group,
- int fd,
- const char *ifname,
- char *query_buf,
- int query_buf_size,
- int num_sources,
- struct in_addr dst_addr,
- struct in_addr group_addr,
- int query_max_response_time_dsec,
- uint8_t s_flag,
- uint8_t querier_robustness_variable,
- uint16_t querier_query_interval)
- {
- ssize_t msg_size;
- uint8_t max_resp_code;
- uint8_t qqic;
- ssize_t sent;
- struct sockaddr_in to;
- socklen_t tolen;
- uint16_t checksum;
- zassert(num_sources >= 0);
- msg_size = IGMP_V3_SOURCES_OFFSET + (num_sources << 2);
- if (msg_size > query_buf_size) {
- zlog_err("%s %s: unable to send: msg_size=%zd larger than query_buf_size=%d",
- __FILE__, __PRETTY_FUNCTION__,
- msg_size, query_buf_size);
- return;
- }
- s_flag = PIM_FORCE_BOOLEAN(s_flag);
- zassert((s_flag == 0) || (s_flag == 1));
- max_resp_code = igmp_msg_encode16to8(query_max_response_time_dsec);
- qqic = igmp_msg_encode16to8(querier_query_interval);
- /*
- RFC 3376: 4.1.6. QRV (Querier's Robustness Variable)
- If non-zero, the QRV field contains the [Robustness Variable]
- value used by the querier, i.e., the sender of the Query. If the
- querier's [Robustness Variable] exceeds 7, the maximum value of
- the QRV field, the QRV is set to zero.
- */
- if (querier_robustness_variable > 7) {
- querier_robustness_variable = 0;
- }
- query_buf[0] = PIM_IGMP_MEMBERSHIP_QUERY;
- query_buf[1] = max_resp_code;
- *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = 0; /* for computing checksum */
- memcpy(query_buf+4, &group_addr, sizeof(struct in_addr));
- query_buf[8] = (s_flag << 3) | querier_robustness_variable;
- query_buf[9] = qqic;
- *(uint16_t *)(query_buf + IGMP_V3_NUMSOURCES_OFFSET) = htons(num_sources);
- checksum = in_cksum(query_buf, msg_size);
- *(uint16_t *)(query_buf + IGMP_V3_CHECKSUM_OFFSET) = checksum;
- if (PIM_DEBUG_IGMP_PACKETS) {
- char dst_str[100];
- char group_str[100];
- pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
- pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_debug("%s: to %s on %s: group=%s sources=%d msg_size=%zd s_flag=%x QRV=%u QQI=%u QQIC=%02x checksum=%x",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, num_sources,
- msg_size, s_flag, querier_robustness_variable,
- querier_query_interval, qqic, checksum);
- }
- memset(&to, 0, sizeof(to));
- to.sin_family = AF_INET;
- to.sin_addr = dst_addr;
- tolen = sizeof(to);
- sent = sendto(fd, query_buf, msg_size, MSG_DONTWAIT,
- (struct sockaddr *)&to, tolen);
- if (sent != (ssize_t) msg_size) {
- int e = errno;
- char dst_str[100];
- char group_str[100];
- pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
- pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- if (sent < 0) {
- zlog_warn("%s: sendto() failure to %s on %s: group=%s msg_size=%zd: errno=%d: %s",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, msg_size,
- e, safe_strerror(e));
- }
- else {
- zlog_warn("%s: sendto() partial to %s on %s: group=%s msg_size=%zd: sent=%zd",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str,
- msg_size, sent);
- }
- return;
- }
- /*
- s_flag sanity test: s_flag must be set for general queries
- RFC 3376: 6.6.1. Timer Updates
- When a router sends or receives a query with a clear Suppress
- Router-Side Processing flag, it must update its timers to reflect
- the correct timeout values for the group or sources being queried.
- General queries don't trigger timer update.
- */
- if (!s_flag) {
- /* general query? */
- if (PIM_INADDR_IS_ANY(group_addr)) {
- char dst_str[100];
- char group_str[100];
- pim_inet4_dump("<dst?>", dst_addr, dst_str, sizeof(dst_str));
- pim_inet4_dump("<group?>", group_addr, group_str, sizeof(group_str));
- zlog_warn("%s: to %s on %s: group=%s sources=%d: s_flag is clear for general query!",
- __PRETTY_FUNCTION__,
- dst_str, ifname, group_str, num_sources);
- }
- }
- }
|