1 /* Multicast group management (join/leave) API 2 * 3 * Copyright (C) 2001-2005 Carsten Schill <carsten@cschill.de> 4 * Copyright (C) 2006-2009 Julien BLACHE <jb@jblache.org> 5 * Copyright (C) 2009 Todd Hayton <todd.hayton@gmail.com> 6 * Copyright (C) 2009-2011 Micha Lenk <micha@debian.org> 7 * Copyright (C) 2011-2013 Joachim Nilsson <troglobit@gmail.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24 #include "mclab.h" 25 26 static int mcgroup4_socket = -1; 27 28 #ifdef __linux__ 29 /* Extremely simple "drop everything" filter for Linux so we do not get 30 * a copy each packet of every routed group we join. */ 31 static struct sock_filter filter[] = { 32 { 0x6, 0, 0, 0x00000000 }, 33 }; 34 35 static struct sock_fprog fprog = { 36 sizeof(filter) / sizeof(filter[0]), 37 filter 38 }; 39 #endif 40 41 static struct iface *find_valid_iface(const char *ifname, int cmd) 42 { 43 const char *command = cmd == 'j' ? "Join" : "Leave"; 44 struct iface *iface = iface_find_by_name(ifname); 45 46 if (!iface) { 47 smclog(LOG_WARNING, "%s multicast group, unknown interface %s", command, ifname); 48 return NULL; 49 } 50 51 return iface; 52 } 53 54 static void mcgroup4_init(void) 55 { 56 if (mcgroup4_socket < 0) { 57 #ifdef SOCK_CLOEXEC 58 mcgroup4_socket = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); 59 #else 60 mcgroup4_socket = socket(AF_INET, SOCK_DGRAM, 0); 61 #endif 62 if (mcgroup4_socket < 0) { 63 smclog(LOG_ERR, "Failed creating socket for joining IPv4 multicast groups: %m"); 64 exit(255); 65 } 66 #ifndef SOCK_CLOEXEC 67 if (fcntl(mcgroup4_socket, F_SETFD, FD_CLOEXEC) < 0) { 68 smclog(LOG_WARNING, "Failed creating socket for joining IPv4 multicast groups: %m"); 69 close(mcgroup4_socket); 70 mcgroup4_socket = -1; 71 return; 72 } 73 #endif 74 75 #ifdef __linux__ 76 if (setsockopt(mcgroup4_socket, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) 77 smclog(LOG_DEBUG, "Failed setting IPv4 socket filter, continuing anyway"); 78 #endif 79 } 80 } 81 82 static int mcgroup_join_leave_ipv4(int sd, int cmd, const char *ifname, struct in_addr group) 83 { 84 int joinleave = cmd == 'j' ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP; 85 struct ip_mreq mreq; 86 struct iface *iface = find_valid_iface(ifname, cmd); 87 88 if (!iface) 89 return 1; 90 91 mreq.imr_multiaddr.s_addr = group.s_addr; 92 mreq.imr_interface.s_addr = iface->inaddr.s_addr; 93 if (setsockopt(sd, IPPROTO_IP, joinleave, (void *)&mreq, sizeof(mreq))) { 94 if (EADDRINUSE != errno) 95 smclog(LOG_WARNING, "%s MEMBERSHIP failed: %m", cmd == 'j' ? "ADD" : "DROP"); 96 return 1; 97 } 98 99 return 0; 100 } 101 102 /* 103 * Joins the MC group with the address 'group' on the interface 'ifname'. 104 * The join is bound to the UDP socket 'sd', so if this socket is 105 * closed the membership is dropped. 106 * 107 * returns: - 0 if the function succeeds 108 * - 1 if parameters are wrong or the join fails 109 */ 110 int mcgroup4_join(const char *ifname, struct in_addr group) 111 { 112 mcgroup4_init(); 113 114 return mcgroup_join_leave_ipv4(mcgroup4_socket, 'j', ifname, group); 115 } 116 117 /* 118 * Leaves the MC group with the address 'group' on the interface 'ifname'. 119 * 120 * returns: - 0 if the function succeeds 121 * - 1 if parameters are wrong or the join fails 122 */ 123 int mcgroup4_leave(const char *ifname, struct in_addr group) 124 { 125 mcgroup4_init(); 126 127 return mcgroup_join_leave_ipv4(mcgroup4_socket, 'l', ifname, group); 128 } 129 130 /* 131 * Close IPv4 multicast socket to kernel to leave any joined groups 132 */ 133 void mcgroup4_disable(void) 134 { 135 if (mcgroup4_socket != -1) { 136 close(mcgroup4_socket); 137 mcgroup4_socket = -1; 138 } 139 } 140 141 #ifdef HAVE_IPV6_MULTICAST_HOST 142 static int mcgroup6_socket = -1; 143 144 static void mcgroup6_init(void) 145 { 146 if (mcgroup6_socket < 0) { 147 #ifdef SOCK_CLOEXEC 148 mcgroup6_socket = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, IPPROTO_UDP); 149 #else 150 mcgroup6_socket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); 151 #endif 152 if (mcgroup6_socket < 0) { 153 smclog(LOG_WARNING, "Failed creating socket for joining IPv6 multicast groups: %m"); 154 return; 155 } 156 #ifndef SOCK_CLOEXEC 157 if (fcntl(mcgroup6_socket, F_SETFD, FD_CLOEXEC) < 0) { 158 smclog(LOG_WARNING, "Failed creating socket for joining IPv6 multicast groups: %m"); 159 close(mcgroup6_socket); 160 mcgroup6_socket = -1; 161 return; 162 } 163 #endif 164 165 #ifdef __linux__ 166 if (setsockopt(mcgroup6_socket, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)) < 0) 167 smclog(LOG_DEBUG, "Failed setting IPv6 socket filter, continuing anyway"); 168 #endif 169 } 170 } 171 172 static int mcgroup_join_leave_ipv6(int sd, int cmd, const char *ifname, struct in6_addr group) 173 { 174 int joinleave = cmd == 'j' ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP; 175 struct ipv6_mreq mreq; 176 struct iface *iface = find_valid_iface(ifname, cmd); 177 178 if (!iface) 179 return 1; 180 181 mreq.ipv6mr_multiaddr = group; 182 mreq.ipv6mr_interface = iface->ifindex; 183 if (setsockopt(sd, IPPROTO_IPV6, joinleave, (void *)&mreq, sizeof(mreq))) { 184 if (EADDRINUSE != errno) 185 smclog(LOG_WARNING, "%s MEMBERSHIP failed: %m", cmd == 'j' ? "ADD" : "DROP"); 186 return 1; 187 } 188 189 return 0; 190 } 191 192 /* 193 * Joins the MC group with the address 'group' on the interface 'ifname'. 194 * The join is bound to the UDP socket 'sd', so if this socket is 195 * closed the membership is dropped. 196 * 197 * returns: - 0 if the function succeeds 198 * - 1 if parameters are wrong or the join fails 199 */ 200 int mcgroup6_join(const char *ifname, struct in6_addr group) 201 { 202 mcgroup6_init(); 203 204 return mcgroup_join_leave_ipv6(mcgroup6_socket, 'j', ifname, group); 205 } 206 207 /* 208 * Leaves the MC group with the address 'group' on the interface 'ifname'. 209 * 210 * returns: - 0 if the function succeeds 211 * - 1 if parameters are wrong or the join fails 212 */ 213 int mcgroup6_leave(const char *ifname, struct in6_addr group) 214 { 215 mcgroup6_init(); 216 217 return mcgroup_join_leave_ipv6(mcgroup6_socket, 'l', ifname, group); 218 } 219 #endif /* HAVE_IPV6_MULTICAST_HOST */ 220 221 /* 222 * Close IPv6 multicast socket to kernel to leave any joined groups 223 */ 224 void mcgroup6_disable(void) 225 { 226 #ifdef HAVE_IPV6_MULTICAST_HOST 227 if (mcgroup6_socket != -1) { 228 close(mcgroup6_socket); 229 mcgroup6_socket = -1; 230 } 231 #endif /* HAVE_IPV6_MULTICAST_HOST */ 232 } 233 234 /** 235 * Local Variables: 236 * version-control: t 237 * indent-tabs-mode: t 238 * c-file-style: "linux" 239 * End: 240 */ 241