void uip_igmpsend(FAR struct uip_driver_s *dev, FAR struct igmp_group_s *group, FAR uip_ipaddr_t *destipaddr) { nllvdbg("msgid: %02x destipaddr: %08x\n", group->msgid, (int)*destipaddr); /* The total length to send is the size of the IP and IGMP headers and 4 * bytes for the ROUTER ALERT (and, eventually, the ethernet header) */ dev->d_len = UIP_IPIGMPH_LEN; /* The total size of the data is the size of the IGMP header */ dev->d_sndlen = UIP_IGMPH_LEN; /* Add the router alert option */ IGMPBUF->ra[0] = HTONS(IPOPT_RA >> 16); IGMPBUF->ra[1] = HTONS(IPOPT_RA & 0xffff); /* Initialize the IPv4 header */ IGMPBUF->vhl = 0x46; /* 4->IP; 6->24 bytes */ IGMPBUF->tos = 0; IGMPBUF->len[0] = (dev->d_len >> 8); IGMPBUF->len[1] = (dev->d_len & 0xff); ++g_ipid; IGMPBUF->ipid[0] = g_ipid >> 8; IGMPBUF->ipid[1] = g_ipid & 0xff; IGMPBUF->ipoffset[0] = UIP_TCPFLAG_DONTFRAG >> 8; IGMPBUF->ipoffset[1] = UIP_TCPFLAG_DONTFRAG & 0xff; IGMPBUF->ttl = IGMP_TTL; IGMPBUF->proto = UIP_PROTO_IGMP; uiphdr_ipaddr_copy(IGMPBUF->srcipaddr, &dev->d_ipaddr); uiphdr_ipaddr_copy(IGMPBUF->destipaddr, destipaddr); /* Calculate IP checksum. */ IGMPBUF->ipchksum = 0; IGMPBUF->ipchksum = ~uip_igmpchksum((FAR uint8_t *)IGMPBUF, UIP_IPH_LEN + RASIZE); /* Set up the IGMP message */ IGMPBUF->type = group->msgid; IGMPBUF->maxresp = 0; uiphdr_ipaddr_copy(IGMPBUF->grpaddr, &group->grpaddr); /* Calculate the IGMP checksum. */ IGMPBUF->chksum = 0; IGMPBUF->chksum = ~uip_igmpchksum(&IGMPBUF->type, UIP_IPIGMPH_LEN); IGMP_STATINCR(uip_stat.igmp.poll_send); IGMP_STATINCR(uip_stat.ip.sent); nllvdbg("Outgoing IGMP packet length: %d (%d)\n", dev->d_len, (IGMPBUF->len[0] << 8) | IGMPBUF->len[1]); igmp_dumppkt(RA, UIP_IPIGMPH_LEN + RASIZE); }
static void uip_igmptimeout(int argc, uint32_t arg, ...) { FAR struct igmp_group_s *group; /* If the state is DELAYING_MEMBER then we send a report for this group */ nllvdbg("Timeout!\n"); group = (FAR struct igmp_group_s *)arg; DEBUGASSERT(argc == 1 && group); /* If the group exists and is no an IDLE MEMBER, then it must be a DELAYING * member. Race conditions are avoided because (1) the timer is not started * until after the first IGMPv2_MEMBERSHIP_REPORT during the join, and (2) * the timer is canceled before sending the IGMP_LEAVE_GROUP during a leave. */ if (!IS_IDLEMEMBER(group->flags)) { /* Schedule (and forget) the Membership Report. NOTE: * Since we are executing from a timer interrupt, we cannot wait * for the message to be sent. */ IGMP_STATINCR(uip_stat.igmp.report_sched); uip_igmpschedmsg(group, IGMPv2_MEMBERSHIP_REPORT); /* Also note: The Membership Report is sent at most two times becasue * the timer is not reset here. Hmm.. does this mean that the group * is stranded if both reports were lost? This is consistent with the * RFC that states: "To cover the possibility of the initial Membership * Report being lost or damaged, it is recommended that it be repeated * once or twice after shortdelays [Unsolicited Report Interval]..." */ } }
static inline void igmp_sched_send(FAR struct net_driver_s *dev, FAR struct igmp_group_s *group) { net_ipaddr_t *dest; /* Check what kind of message we need to send. There are only two * possibilities: */ if (group->msgid == IGMPv2_MEMBERSHIP_REPORT) { dest = &group->grpaddr; nllvdbg("Send IGMPv2_MEMBERSHIP_REPORT, dest=%08x flags=%02x\n", *dest, group->flags); IGMP_STATINCR(g_netstats.igmp.report_sched); SET_LASTREPORT(group->flags); /* Remember we were the last to report */ } else { DEBUGASSERT(group->msgid == IGMP_LEAVE_GROUP); dest = &g_allrouters; nllvdbg("Send IGMP_LEAVE_GROUP, dest=%08x flags=%02x\n", *dest, group->flags); IGMP_STATINCR(g_netstats.igmp.leave_sched); } /* Send the message */ igmp_send(dev, group, dest); /* Indicate that the message has been sent */ CLR_SCHEDMSG(group->flags); group->msgid = 0; /* If there is a thread waiting fore the message to be sent, wake it up */ if (IS_WAITMSG(group->flags)) { nllvdbg("Awakening waiter\n"); sem_post(&group->sem); } }
int igmp_joingroup(struct uip_driver_s *dev, FAR const struct in_addr *grpaddr) { struct igmp_group_s *group; DEBUGASSERT(dev && grpaddr); /* Check if a this address is already in the group */ group = uip_grpfind(dev, &grpaddr->s_addr); if (!group) { /* No... allocate a new entry */ nvdbg("Join to new group: %08x\n", grpaddr->s_addr); group = uip_grpalloc(dev, &grpaddr->s_addr); IGMP_STATINCR(uip_stat.igmp.joins); /* Send the Membership Report */ IGMP_STATINCR(uip_stat.igmp.report_sched); uip_igmpwaitmsg(group, IGMPv2_MEMBERSHIP_REPORT); /* And start the timer at 10*100 msec */ uip_igmpstarttimer(group, 10); /* Add the group (MAC) address to the ether drivers MAC filter list */ uip_addmcastmac(dev, (FAR uip_ipaddr_t *)&grpaddr->s_addr); return OK; } /* Return EEXIST if the address is already a member of the group */ return -EEXIST; }