1/* 2 * Copyright (c) 2004-2010 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29/* 30 * multicast_util.c: 31 * - keep track of multicast addresses added to one interface based on the 32 * actual multicast addresses in another 33 * - used by VLAN and BOND 34 */ 35 36/* 37 * Modification History: 38 * 39 * April 29, 2004 Dieter Siegmund (dieter@apple.com) 40 * - created 41 */ 42 43#include <net/multicast_list.h> 44#include <sys/param.h> 45#include <sys/systm.h> 46#include <sys/malloc.h> 47#include <net/if_dl.h> 48 49__private_extern__ void 50multicast_list_init(struct multicast_list * mc_list) 51{ 52 SLIST_INIT(mc_list); 53 return; 54} 55 56/* 57 * Function: multicast_list_remove 58 * Purpose: 59 * Remove the given list of multicast addresses from the interface and from 60 * the multicast list structure. 61 */ 62__private_extern__ int 63multicast_list_remove(struct multicast_list * mc_list) 64{ 65 int error; 66 struct multicast_entry * mc; 67 int result = 0; 68 69 while ((mc = SLIST_FIRST(mc_list)) != NULL) { 70 error = ifnet_remove_multicast(mc->mc_ifma); 71 if (error != 0) { 72 result = error; 73 } 74 SLIST_REMOVE_HEAD(mc_list, mc_entries); 75 ifmaddr_release(mc->mc_ifma); 76 FREE(mc, M_DEVBUF); 77 } 78 return (result); 79} 80 81/* 82 * Function: multicast_list_program 83 * Purpose: 84 * Program the multicast filter on "target_ifp" using the values from 85 * "source_ifp", and saving the result in "mc_list" 86 * 87 * We build a new list of multicast addresses while programming the new list. 88 * If that completes successfully, we remove the old list, and return the 89 * new list. 90 * 91 * If it fails, we remove what we've added to the new list, and 92 * return an error. 93 */ 94__private_extern__ int 95multicast_list_program(struct multicast_list * mc_list, 96 struct ifnet * source_ifp, 97 struct ifnet * target_ifp) 98{ 99 int alen; 100 int error = 0; 101 int i; 102 struct multicast_entry * mc = NULL; 103 struct multicast_list new_mc_list; 104 struct sockaddr_dl source_sdl; 105 ifmultiaddr_t * source_multicast_list; 106 struct sockaddr_dl target_sdl; 107 108 alen = target_ifp->if_addrlen; 109 bzero((char *)&target_sdl, sizeof(target_sdl)); 110 target_sdl.sdl_len = sizeof(target_sdl); 111 target_sdl.sdl_family = AF_LINK; 112 target_sdl.sdl_type = target_ifp->if_type; 113 target_sdl.sdl_alen = alen; 114 target_sdl.sdl_index = target_ifp->if_index; 115 116 /* build a new list */ 117 multicast_list_init(&new_mc_list); 118 error = ifnet_get_multicast_list(source_ifp, &source_multicast_list); 119 if (error != 0) { 120 printf("multicast_list_program: " 121 "ifnet_get_multicast_list(%s%d) failed, %d\n", 122 source_ifp->if_name, source_ifp->if_unit, error); 123 return (error); 124 } 125 for (i = 0; source_multicast_list[i] != NULL; i++) { 126 if (ifmaddr_address(source_multicast_list[i], 127 (struct sockaddr *)&source_sdl, 128 sizeof(source_sdl)) != 0 129 || source_sdl.sdl_family != AF_LINK) { 130 continue; 131 } 132 mc = _MALLOC(sizeof(struct multicast_entry), M_DEVBUF, M_WAITOK); 133 if (mc == NULL) { 134 error = ENOBUFS; 135 break; 136 } 137 bcopy(LLADDR(&source_sdl), LLADDR(&target_sdl), alen); 138 error = ifnet_add_multicast(target_ifp, (struct sockaddr *)&target_sdl, 139 &mc->mc_ifma); 140 if (error != 0) { 141 FREE(mc, M_DEVBUF); 142 break; 143 } 144 SLIST_INSERT_HEAD(&new_mc_list, mc, mc_entries); 145 } 146 if (error != 0) { 147 /* restore previous state */ 148 (void)multicast_list_remove(&new_mc_list); 149 } else { 150 /* remove the old entries, and return the new list */ 151 (void)multicast_list_remove(mc_list); 152 *mc_list = new_mc_list; 153 } 154 ifnet_free_multicast_list(source_multicast_list); 155 return (error); 156} 157