int igmpv1_input(PACKET p) { u_char igmp_type; ip_addr igmp_group; struct in_multi * inm; NET netp = p->net; int rc; /* extract IGMP type from received packet */ rc = cb_write(p, 0, (uint8_t *) &igmp_type, sizeof(igmp_type)); if (rc != sizeof(igmp_type)) { LOCK_NET_RESOURCE(FREEQ_RESID); PK_FREE(p); UNLOCK_NET_RESOURCE(FREEQ_RESID); return (IGMP_ERR); } switch (igmp_type) { case IGMP_HOST_MEMBERSHIP_QUERY: ++igmpstats.igmpv1mode_v1_queries_rcvd; /* * Start the timers in all of our membership records for * the interface on which the query arrived, except those * that are already running and those that belong to the * "all-hosts" group. */ for (inm = netp->mc_list; inm; inm = inm->inm_next) { /* skip all IPv6 entries - they are indicated by * an IPv4 address field of 0 */ if (inm->inm_addr == 0) continue; /* skip IPv4 multicast address of 224.0.0.1 (note that * the IPv4 address stored in inm_addr is in network * byte order */ if (inm->inm_addr != igmp_all_hosts_group) { if (inm->inm_timer == 0) { inm->inm_timer = (unsigned) IGMP_RANDOM_DELAY(inm->inm_addr); /* increment the count of running timers */ ++igmp_timers_are_running; } } } rc = IGMP_OK; break; case IGMP_HOST_MEMBERSHIP_REPORT: ++igmpstats.igmpv1mode_v1_reports_rcvd; /* * If we belong to the group being reported and have a * running timer for that group, stop our timer for that * group. */ /* extract IGMP group from received packet */ rc = cb_write(p, IGMP_GRP_OFFSET, (uint8_t *) &igmp_group, sizeof(igmp_group)); if (rc != sizeof(igmp_group)) { rc = IGMP_ERR; break; } inm = lookup_mcast(igmp_group, netp); if (inm != NULL) { if (inm->inm_timer > 0) { inm->inm_timer = 0; /* decrement the count of running timers */ --igmp_timers_are_running; ++igmpstats.igmpv1mode_v1_reports_rcvd_canceled_timer; } } rc = IGMP_OK; break; default: ++igmpstats.igmpv1mode_unknown_pkttype; rc = IGMP_ERR; break; } /* we're done with the received packet; return packet buffer back * to free pool */ LOCK_NET_RESOURCE(FREEQ_RESID); PK_FREE(p); UNLOCK_NET_RESOURCE(FREEQ_RESID); return rc; }
int igmpv2_process_report (PACKET p) { struct igmp * igmp; struct ip * pip; NET netp; struct in_multi * inm; netp = p->net; pip = ip_head (p); igmp = (struct igmp *) (ip_data (pip)); /* If we receive a IGMP (v1 or v2) report for a multicast group * that we have a timer running for, the IGMPv2 specification * requires that we stop the timer (and thereby cancel the * future transmission of our report). * However, we will use the following table to guide our actions * instead. Scenario #4 causes us to not cancel the timer, * since we have received a IGMPv2 report, but we believe that * the querier on the network is running IGMPv1, and therefore * will not be able to understand the IGMPv2 report. As a * result, we let our timer run, and if it expires, we will * send out a report. * The type of a received report can be determined by examining * the first byte of the IGMP message. This byte is 0x12 for a * IGMPv1 Host Membership Report, and 0x16 for a Version 2 * Membership Report. * Scenario# igmpv1_rtr_present Type of rcvd report Cancel timer * ============================================================= * 1 No IGMPv1 Yes * 2 No IGMPv2 Yes * 3 Yes IGMPv1 Yes * 4 Yes IGMPv2 No * In scenario #1 and #2, we have a IGMPv2-capable router that * can understand both IGMPv1 and IGMPv2 reports. In scenario * #3, we have a IGMPv1-capable router that can understand IGMPv1 * reports. In scenario #4, we have a IGMPv1-capable router that * cannot understand a IGMPv2 report. It is possible that the * IGMPv1-capable router in scenario #4 is also capable of * processing IGMPv2 packets (it has "downgraded" itself because * there are IGMPv1 routers on that network); however, we do not * know that, and hence we don't cancel our timer (for the * subsequent transmission of a IGMPv1 report). */ inm = lookup_mcast(igmp->igmp_group, netp); if (inm != NULL) { if (inm->inm_timer != 0) { /* we have a timer running */ if (!(netp->igmpv1_rtr_present && igmp->igmp_type == IGMPv2_MEMBERSHIP_REPORT)) { /* cancel timer */ inm->inm_timer = 0; /* decrement the count of running timers */ --igmp_timers_are_running; /* indicate that we are not the last host to send a * report for this group */ inm->last2send_report = IGMP_FALSE; ++igmpstats.igmpv2mode_v12_reports_rcvd_canceled_timer; } } else { /* we don't have a timer running; perhaps the source * host has just joined the group, and has sent an * unsolicited report */ ++igmpstats.igmpv2mode_v12_reports_rcvd_no_timer; } } else { /* since the Reports are sent the group address, we should * never receive them unless we are a member of that group * on that interface. Even if imperfect filtering at the * device level causes reports for unregistered groups to * be passed up to the IP module, ip_rcv_phase2 () is * responsible for dropping them, and so we should never * receive such packets. */ ++igmpstats.igmpv2mode_v12_unknown_grp_reports_rcvd; } return IGMP_OK; }
int igmpv2_process_query (PACKET p) { struct igmp * igmp; struct ip * pip; NET netp; u_short max_resp_time; u_char process_all; struct in_multi * inm; ip_addr mcgrp_addr; netp = p->net; pip = ip_head (p); igmp = (struct igmp *) (ip_data (pip)); mcgrp_addr = ntohl(igmp->igmp_group); if (igmp->igmp_code == 0) { /* this is a IGMPv1 Host Membership Query */ netp->igmpv1_rtr_present = IGMP_TRUE; netp->igmpv1_query_rcvd_time = cticks; ++igmpstats.igmpv2mode_v1_queries_rcvd; /* set maximum time to respond to the equivalent of 10 * seconds worth of "ticks" (the timeout routine is * intended to be invoked PR_FASTHZ (5) times a second, * so each tick is equal to 200 ms) */ max_resp_time = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; process_all = IGMP_TRUE; } else { /* this is either a IGMPv2 General Query or * a IGMPv2 Group-Specific Query */ if (igmp->igmp_group == 0) { /* this is a IGMPv2 General Query */ ++igmpstats.igmpv2mode_v2_general_queries_rcvd; process_all = IGMP_TRUE; } else { /* this is a IGMPv2 Group-Specific Query */ ++igmpstats.igmpv2mode_v2_grp_specific_queries_rcvd; process_all = IGMP_FALSE; } /* irrespective of whether received message is a * IGMPv2 General Query or a IGMPv2 Group-Specific Query, * set maximum time to respond to value extracted * from received message. The value in the message * is in tenths of a second. max_resp_time is in * units of ticks (where one tick is 200 ms) */ max_resp_time = (igmp->igmp_code * PR_FASTHZ) / 10; } /* process all entries in a link's multicast address linked * list (pointed to by mc_list) as part of the response to * the received IGMPv1 Host Membership Query or IGMPv2 General * Query message */ if (process_all) { for (inm = netp->mc_list; inm; inm = inm->inm_next) { /* skip all IPv6 entries - they are indicated by * an IPv4 address field of 0 */ if (!(inm->inm_addr)) continue; /* skip IPv4 multicast address of 224.0.0.1 (note that * the IPv4 address stored in inm_addr is in network * byte order */ if (inm->inm_addr != igmp_all_hosts_group) igmpv2_chk_set_timer (inm, max_resp_time); } /* end FOR (iterate thru' mc_list on net) */ } /* end IF (process all) */ else { /* process one (for IGMPv2 Group-Specific Query) entry (the * one that corresponds to the address listed in the received * query) - it should be present in the link's multicast * address list */ inm = lookup_mcast(igmp->igmp_group, netp); if (inm != NULL) igmpv2_chk_set_timer (inm, max_resp_time); else ++igmpstats.igmpv2mode_v2_unknown_grp_specific_queries_rcvd; } /* end ELSE (process ALL) */ /* return success; caller will the received packet back to the * free pool */ return IGMP_OK; }
struct in_multi * in_addmulti(ip_addr *ap, struct net *netp, int addrtype) { struct in_multi *inm = (struct in_multi *)NULL; int error; /* check for good addr. */ if ((ap == (ip_addr *)NULL) || (*ap == 0)) return ((struct in_multi *)NULL); ENTER_CRIT_SECTION(netp); /* See if address already in list. */ #ifdef IP_V6 if(addrtype == 6) inm = v6_lookup_mcast((ip6_addr*)ap, netp); #endif #ifdef IP_V4 if(addrtype != 6) inm = lookup_mcast(*ap, netp); #endif if (inm != (struct in_multi *)NULL) { /* Found it; just increment the reference count. */ ++inm->inm_refcount; } else { /* * New address; allocate a new multicast record * and link it into the interface's multicast list. */ inm = (struct in_multi *)INM_ALLOC(sizeof(*inm)); if (inm == (struct in_multi *)NULL) { EXIT_CRIT_SECTION(netp); return ((struct in_multi *)NULL); } #ifdef IP_V6 if(addrtype == 6) IP6CPY(&inm->ip6addr, (struct in6_addr *)ap); #endif #ifdef IP_V4 if(addrtype != 6) inm->inm_addr = *ap; #endif inm->inm_netp = netp; inm->inm_refcount = 1; inm->inm_next = netp->mc_list; netp->mc_list = inm; /* * If net has a multicast address registration routine then ask * the network driver to update its multicast reception * filter appropriately for the new address. */ if(netp->n_mcastlist) error = netp->n_mcastlist(inm); else error = 0; #if defined (IGMP_V1) || defined (IGMP_V2) /* * Let IGMP know that we have joined a new IP multicast group. */ if (inm->inm_addr) igmp_joingroup(inm); #endif } EXIT_CRIT_SECTION(netp); USE_ARG(error); return (inm); }