1 /* 2 * Author: Steven Barth <steven at midlink.org> 3 * 4 * Copyright 2015 Deutsche Telekom AG 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 */ 19 20 #include <errno.h> 21 #include <string.h> 22 #include <unistd.h> 23 #include <alloca.h> 24 25 #include <arpa/inet.h> 26 #include <netinet/in.h> 27 #include <linux/mroute6.h> 28 #include <libubox/list.h> 29 #include <libubox/avl.h> 30 31 #include "client.h" 32 33 34 // Add / update / remove a client entry for a multicast group 35 int client_set(struct client *client, const struct in6_addr *group, 36 bool include, const struct in6_addr sources[], size_t cnt) 37 { 38 int family = (IN6_IS_ADDR_V4MAPPED(group)) ? AF_INET : AF_INET6; 39 int sol = (family == AF_INET) ? SOL_IP : SOL_IPV6; 40 char addrbuf[INET6_ADDRSTRLEN]; 41 size_t len = sizeof(struct group_filter) + cnt * sizeof(struct sockaddr_storage); 42 struct { 43 struct group_filter f; 44 struct sockaddr_storage s[]; 45 } *filter = alloca(len); 46 struct sockaddr_in *in_addr = (struct sockaddr_in*)&filter->f.gf_group; 47 struct sockaddr_in6 *in6_addr = (struct sockaddr_in6*)&filter->f.gf_group; 48 49 inet_ntop(AF_INET6, group, addrbuf, sizeof(addrbuf)); 50 L_DEBUG("%s: %s on %d => %s (+%d sources)", __FUNCTION__, addrbuf, 51 client->ifindex, (include) ? "include" : "exclude", (int)cnt); 52 53 // Construct MSFILTER for outgoing IGMP / MLD 54 memset(filter, 0, len); 55 filter->f.gf_interface = client->ifindex; 56 filter->f.gf_fmode = include ? MCAST_INCLUDE : MCAST_EXCLUDE; 57 filter->f.gf_group.ss_family = family; 58 filter->f.gf_numsrc = cnt; 59 60 if (family == AF_INET) 61 client_unmap(&in_addr->sin_addr, group); 62 else 63 in6_addr->sin6_addr = *group; 64 65 for (size_t i = 0; i < cnt; ++i) { 66 filter->f.gf_slist[i].ss_family = family; 67 68 in_addr = (struct sockaddr_in*)&filter->f.gf_slist[i]; 69 in6_addr = (struct sockaddr_in6*)&filter->f.gf_slist[i]; 70 71 if (family == AF_INET) 72 client_unmap(&in_addr->sin_addr, &sources[i]); 73 else 74 in6_addr->sin6_addr = sources[i]; 75 } 76 77 int fd = (family == AF_INET) ? client->igmp_fd : client->mld_fd; 78 setsockopt(fd, sol, MCAST_LEAVE_GROUP, filter, sizeof(struct group_req)); 79 if (!include || cnt > 0) { 80 if (setsockopt(fd, sol, MCAST_JOIN_GROUP, filter, sizeof(struct group_req)) 81 && family == AF_INET && errno == ENOBUFS) { 82 L_WARN("proxy: kernel denied joining multicast group. check igmp_max_memberships?"); 83 return -errno; 84 } 85 86 if (setsockopt(fd, sol, MCAST_MSFILTER, filter, len)) 87 return -errno; 88 } 89 return 0; 90 } 91 92 // Initialize client-instance 93 int client_init(struct client *client, int ifindex) 94 { 95 client->igmp_fd = socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0); 96 if (client->igmp_fd < 0) 97 return -errno; 98 99 client->mld_fd = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC, 0); 100 if (client->mld_fd < 0) 101 return -errno; 102 103 client->ifindex = ifindex; 104 return 0; 105 } 106 107 // Cleanup client-instance 108 void client_deinit(struct client *client) 109 { 110 if (client->ifindex) { 111 close(client->igmp_fd); 112 close(client->mld_fd); 113 client->igmp_fd = -1; 114 client->mld_fd = -1; 115 client->ifindex = 0; 116 } 117 } 118
This page was automatically generated by LXR 0.3.1. • OpenWrt