void igmp_joingroup(struct in_multi *inm) { crit_enter(); if (inm->inm_addr.s_addr == igmp_all_hosts_group || inm->inm_ifp->if_flags & IFF_LOOPBACK) { inm->inm_timer = 0; inm->inm_state = IGMP_OTHERMEMBER; } else { inm->inm_rti = find_rti(inm->inm_ifp); igmp_sendpkt(inm, inm->inm_rti->rti_type, 0); inm->inm_timer = IGMP_RANDOM_DELAY( IGMP_MAX_HOST_REPORT_DELAY*PR_FASTHZ); inm->inm_state = IGMP_IREPORTEDLAST; igmp_timers_are_running = 1; } crit_exit(); }
void igmp_joingroup(struct in_multi * inm) { NET ifp; /* extract the network interface to which this multicast * address is "attached" */ ifp = inm->inm_netp; if (inm->inm_addr == igmp_all_hosts_group) { inm->inm_timer = 0; } else { /* send unsolicited membership report in appropriate format */ if (ifp->igmpv1_rtr_present) { /* always true for IGMPv1, may be true for IGMPv2 */ igmp_send (IGMP_HOST_MEMBERSHIP_REPORT, inm); /* set a delay timer (with a duration of * IGMP_MAX_HOST_REPORT_DELAY) for a second unsolicited report */ inm->inm_timer = (unsigned) IGMP_RANDOM_DELAY(inm->inm_addr); } else { igmp_send (IGMPv2_MEMBERSHIP_REPORT, inm); /* the delay time duration is the Unsolicited Report Interval */ inm->inm_timer = (unsigned) IGMPv2_RANDOM_DELAY ((UNSOLIC_RPT_INTERVAL * PR_FASTHZ), inm->inm_addr); } /* for IGMPv2, indicate that we were the last to send * a Report for this multicast group (relevant for * IGMPv2 only). */ if (ifp->igmp_oper_mode == IGMP_MODE_V2) { inm->last2send_report = IGMP_TRUE; } ++igmp_timers_are_running; } return; }
void igmp_input(struct mbuf *m, int iphlen) { register struct igmp *igmp; register struct ip *ip; register int igmplen; register struct ifnet *ifp = m->m_pkthdr.rcvif; register int minlen; register struct in_multi *inm; register struct in_ifaddr *ia; struct in_multistep step; struct router_info *rti; int timer; /** timer value in the igmp query header **/ ++igmpstat.igps_rcv_total; ip = mtod(m, struct ip *); igmplen = ip->ip_len; /* * Validate lengths */ if (igmplen < IGMP_MINLEN) { ++igmpstat.igps_rcv_tooshort; m_freem(m); return; } minlen = iphlen + IGMP_MINLEN; if ((m->m_flags & M_EXT || m->m_len < minlen) && (m = m_pullup(m, minlen)) == 0) { ++igmpstat.igps_rcv_tooshort; return; } /* * Validate checksum */ m->m_data += iphlen; m->m_len -= iphlen; igmp = mtod(m, struct igmp *); if (in_cksum(m, igmplen)) { ++igmpstat.igps_rcv_badsum; m_freem(m); return; } m->m_data -= iphlen; m->m_len += iphlen; ip = mtod(m, struct ip *); timer = igmp->igmp_code * PR_FASTHZ / IGMP_TIMER_SCALE; rti = find_rti(ifp); /* * In the IGMPv2 specification, there are 3 states and a flag. * * In Non-Member state, we simply don't have a membership record. * In Delaying Member state, our timer is running (inm->inm_timer) * In Idle Member state, our timer is not running (inm->inm_timer==0) * * The flag is inm->inm_state, it is set to IGMP_OTHERMEMBER if * we have heard a report from another member, or IGMP_IREPORTEDLAST * if I sent the last report. */ switch (igmp->igmp_type) { case IGMP_MEMBERSHIP_QUERY: ++igmpstat.igps_rcv_queries; if (ifp->if_flags & IFF_LOOPBACK) break; if (igmp->igmp_code == 0) { /* * Old router. Remember that the querier on this * interface is old, and set the timer to the * value in RFC 1112. */ rti->rti_type = IGMP_V1_ROUTER; rti->rti_time = 0; timer = IGMP_MAX_HOST_REPORT_DELAY * PR_FASTHZ; if (ip->ip_dst.s_addr != igmp_all_hosts_group || igmp->igmp_group.s_addr != 0) { ++igmpstat.igps_rcv_badqueries; m_freem(m); return; } } else { /* * New router. Simply do the new validity check. */ if (igmp->igmp_group.s_addr != 0 && !IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { ++igmpstat.igps_rcv_badqueries; m_freem(m); return; } } /* * - Start the timers in all of our membership records * that the query applies to for the interface on * which the query arrived excl. those that belong * to the "all-hosts" group (224.0.0.1). * - Restart any timer that is already running but has * a value longer than the requested timeout. * - Use the value specified in the query message as * the maximum timeout. */ IN_FIRST_MULTI(step, inm); while (inm != NULL) { if (inm->inm_ifp == ifp && inm->inm_addr.s_addr != igmp_all_hosts_group && (igmp->igmp_group.s_addr == 0 || igmp->igmp_group.s_addr == inm->inm_addr.s_addr)) { if (inm->inm_timer == 0 || inm->inm_timer > timer) { inm->inm_timer = IGMP_RANDOM_DELAY(timer); igmp_timers_are_running = 1; } } IN_NEXT_MULTI(step, inm); } break; case IGMP_V1_MEMBERSHIP_REPORT: case IGMP_V2_MEMBERSHIP_REPORT: /* * For fast leave to work, we have to know that we are the * last person to send a report for this group. Reports * can potentially get looped back if we are a multicast * router, so discard reports sourced by me. */ IFP_TO_IA(ifp, ia); if (ia && ip->ip_src.s_addr == IA_SIN(ia)->sin_addr.s_addr) break; ++igmpstat.igps_rcv_reports; if (ifp->if_flags & IFF_LOOPBACK) break; if (!IN_MULTICAST(ntohl(igmp->igmp_group.s_addr))) { ++igmpstat.igps_rcv_badreports; m_freem(m); return; } /* * KLUDGE: if the IP source address of the report has an * unspecified (i.e., zero) subnet number, as is allowed for * a booting host, replace it with the correct subnet number * so that a process-level multicast routing demon can * determine which subnet it arrived from. This is necessary * to compensate for the lack of any way for a process to * determine the arrival interface of an incoming packet. */ if ((ntohl(ip->ip_src.s_addr) & IN_CLASSA_NET) == 0) if (ia) ip->ip_src.s_addr = htonl(ia->ia_subnet); /* * If we belong to the group being reported, stop * our timer for that group. */ IN_LOOKUP_MULTI(igmp->igmp_group, ifp, inm); if (inm != NULL) { inm->inm_timer = 0; ++igmpstat.igps_rcv_ourreports; inm->inm_state = IGMP_OTHERMEMBER; } break; } /* * Pass all valid IGMP packets up to any process(es) listening * on a raw IGMP socket. */ rip_input(m, iphlen); }
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; }