/* * Function: multicast_list_program * Purpose: * Program the multicast filter on "target_ifp" using the values from * "source_ifp", and saving the result in "mc_list" * * We build a new list of multicast addresses while programming the new list. * If that completes successfully, we remove the old list, and return the * new list. * * If it fails, we remove what we've added to the new list, and * return an error. */ __private_extern__ int multicast_list_program(struct multicast_list * mc_list, struct ifnet * source_ifp, struct ifnet * target_ifp) { int alen; int error = 0; int i; struct multicast_entry * mc = NULL; struct multicast_list new_mc_list; struct sockaddr_dl source_sdl; ifmultiaddr_t * source_multicast_list; struct sockaddr_dl target_sdl; alen = target_ifp->if_addrlen; bzero((char *)&target_sdl, sizeof(target_sdl)); target_sdl.sdl_len = sizeof(target_sdl); target_sdl.sdl_family = AF_LINK; target_sdl.sdl_type = target_ifp->if_type; target_sdl.sdl_alen = alen; target_sdl.sdl_index = target_ifp->if_index; /* build a new list */ multicast_list_init(&new_mc_list); error = ifnet_get_multicast_list(source_ifp, &source_multicast_list); if (error != 0) { printf("multicast_list_program: " "ifnet_get_multicast_list(%s%d) failed, %d\n", source_ifp->if_name, source_ifp->if_unit, error); return (error); } for (i = 0; source_multicast_list[i] != NULL; i++) { if (ifmaddr_address(source_multicast_list[i], (struct sockaddr *)&source_sdl, sizeof(source_sdl)) != 0 || source_sdl.sdl_family != AF_LINK) { continue; } mc = _MALLOC(sizeof(struct multicast_entry), M_DEVBUF, M_WAITOK); bcopy(LLADDR(&source_sdl), LLADDR(&target_sdl), alen); error = ifnet_add_multicast(target_ifp, (struct sockaddr *)&target_sdl, &mc->mc_ifma); if (error != 0) { FREE(mc, M_DEVBUF); break; } SLIST_INSERT_HEAD(&new_mc_list, mc, mc_entries); } if (error != 0) { /* restore previous state */ (void)multicast_list_remove(&new_mc_list); } else { /* remove the old entries, and return the new list */ (void)multicast_list_remove(mc_list); *mc_list = new_mc_list; } return (error); }
IOReturn IOEthernetInterface::setupMulticastFilter(IONetworkController * ctr) { void * multiAddrs = 0; UInt mcount; OSData * mcData = 0; ifnet_t interface; struct sockaddr dlAddress; IOReturn ret = kIOReturnSuccess; bool ok; ifmultiaddr_t *addressList; interface = getIfnet(); assert(interface); // get the list and count how many mcast link addresses there are if(ifnet_get_multicast_list(interface, &addressList)) return kIOReturnNoMemory; mcount = 0; for(int i=0; addressList[i]; i++) { ifmaddr_address(addressList[i], &dlAddress, sizeof(dlAddress)); if (dlAddress.sa_family == AF_UNSPEC || dlAddress.sa_family == AF_LINK) mcount++; } _mcAddrCount = mcount; // now rewalk the list and copy the addresses to a format suitable to give to the controller if ( mcount ) { char * addrp; mcData = OSData::withCapacity(mcount * ETHER_ADDR_LEN); if (!mcData) { DLOG("%s: no memory for multicast address list\n", getName()); ifnet_free_multicast_list(addressList); return kIOReturnNoMemory; } // Loop through the list and copy the link multicast // address to the OSData. for(int i = 0; addressList[i]; i++) { //retrieve the datalink mcast address ifmaddr_address(addressList[i], &dlAddress, sizeof(dlAddress)); if (dlAddress.sa_family == AF_UNSPEC) addrp = &dlAddress.sa_data[0]; else if (dlAddress.sa_family == AF_LINK) addrp = LLADDR((struct sockaddr_dl *)&dlAddress); else continue; ok = mcData->appendBytes((const void *) addrp, ETHER_ADDR_LEN); assert(ok); } multiAddrs = (void *) mcData->getBytesNoCopy(); assert(multiAddrs); } // Issue a controller command to setup the multicast filter. ret = ((IOEthernetController *)ctr)->setMulticastList( (IOEthernetAddress *) multiAddrs, mcount); if (mcData) { if (ret == kIOReturnSuccess) setProperty(kIOMulticastAddressList, mcData); mcData->release(); } else { removeProperty(kIOMulticastAddressList); } ifnet_free_multicast_list(addressList); return ret; }