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 <sys/socket.h> 22 #include <sys/ioctl.h> 23 #include <netinet/ip.h> 24 #include <netinet/in.h> 25 #include <net/if.h> 26 #include <string.h> 27 #include <unistd.h> 28 29 #include "querier.h" 30 31 // Test if multicast-group is valid and relevant 32 static inline bool igmp_is_valid_group(in_addr_t group) 33 { 34 return IN_MULTICAST(be32_to_cpu(group)); 35 } 36 37 // Handle an IGMP-record from an IGMP-packet (called by igmp_receive) 38 static ssize_t igmp_handle_record(struct groups *groups, const uint8_t *data, size_t len) 39 { 40 struct igmpv3_grec *r = (struct igmpv3_grec*)data; 41 42 if (len < sizeof(*r)) 43 return -1; 44 45 size_t nsrc = ntohs(r->grec_nsrcs); 46 size_t read = sizeof(*r) + nsrc * sizeof(struct in_addr) + r->grec_auxwords * 4; 47 48 if (len < read) 49 return -1; 50 51 if (r->grec_type >= UPDATE_IS_INCLUDE && r->grec_type <= UPDATE_BLOCK && 52 igmp_is_valid_group(r->grec_mca)) { 53 struct in6_addr addr, sources[nsrc]; 54 querier_map(&addr, r->grec_mca); 55 56 for (size_t i = 0; i < nsrc; ++i) 57 querier_map(&sources[i], r->grec_src[i]); 58 59 groups_update_state(groups, &addr, sources, nsrc, r->grec_type); 60 } 61 62 return read; 63 } 64 65 // Receive and parse an IGMP-packet (called by uloop as callback) 66 void igmp_handle(struct mrib_querier *mrib, const struct igmphdr *igmp, size_t len, 67 const struct sockaddr_in *from) 68 { 69 struct querier_iface *q = container_of(mrib, struct querier_iface, mrib); 70 omgp_time_t now = omgp_time(); 71 char addrbuf[INET_ADDRSTRLEN]; 72 struct in6_addr group; 73 74 querier_map(&group, igmp->group); 75 inet_ntop(AF_INET, &from->sin_addr, addrbuf, sizeof(addrbuf)); 76 77 if (igmp->type == IGMP_HOST_MEMBERSHIP_QUERY) { 78 struct igmpv3_query *query = (struct igmpv3_query*)igmp; 79 80 if (len != sizeof(*igmp) && ((size_t)len < sizeof(*query) || 81 (size_t)len < sizeof(*query) + ntohs(query->nsrcs) * sizeof(struct in_addr))) 82 return; 83 84 if (query->group && !igmp_is_valid_group(query->group)) 85 return; 86 87 // Setup query target address 88 struct in_addr addr; 89 if (mrib_igmp_source(mrib, &addr)) 90 return; 91 92 bool suppress = false; 93 size_t nsrc = 0; 94 int robustness = 2; 95 omgp_time_t mrd = 10000; 96 omgp_time_t query_interval = 125000; 97 98 if (igmp->code) 99 mrd = 100 * ((len == sizeof(*igmp)) ? igmp->code : querier_qqi(igmp->code)); 100 101 if ((size_t)len > sizeof(*igmp)) { 102 if (query->qrv) 103 robustness = query->qrv; 104 105 if (query->qqic) 106 query_interval = querier_qqi(query->qqic) * 1000; 107 108 suppress = query->suppress; 109 nsrc = ntohs(query->nsrcs); 110 } 111 112 if (!suppress && query->group) { 113 struct in6_addr sources[nsrc]; 114 for (size_t i = 0; i < nsrc; ++i) 115 querier_map(&sources[i], query->srcs[i]); 116 117 groups_update_timers(&q->groups, &group, sources, nsrc); 118 } 119 120 int election = memcmp(&from->sin_addr, &addr, sizeof(from->sin_addr)); 121 L_INFO("%s: detected other querier %s with priority %d on %d", 122 __FUNCTION__, addrbuf, election, q->ifindex); 123 124 // TODO: we ignore IGMPv1/v2 queriers for now, since a lot of them are dumb switches 125 126 if (election < 0 && !query->group && len > sizeof(*igmp)) { 127 groups_update_config(&q->groups, false, mrd, query_interval, robustness); 128 129 q->igmp_other_querier = true; 130 q->igmp_next_query = now + (q->groups.cfg_v4.query_response_interval / 2) + 131 (q->groups.cfg_v4.robustness * q->groups.cfg_v4.query_interval); 132 } 133 } else if (igmp->type == IGMPV3_HOST_MEMBERSHIP_REPORT) { 134 struct igmpv3_report *report = (struct igmpv3_report*)igmp; 135 136 if ((size_t)len <= sizeof(*report)) 137 return; 138 139 uint8_t *ibuf = (uint8_t*)igmp; 140 size_t count = ntohs(report->ngrec); 141 size_t offset = sizeof(*report); 142 143 while (count > 0 && offset < len) { 144 ssize_t read = igmp_handle_record(&q->groups, &ibuf[offset], len - offset); 145 if (read < 0) 146 break; 147 148 offset += read; 149 --count; 150 } 151 } else if (igmp->type == IGMPV2_HOST_MEMBERSHIP_REPORT || 152 igmp->type == IGMP_HOST_LEAVE_MESSAGE || 153 igmp->type == IGMP_HOST_MEMBERSHIP_REPORT) { 154 155 if (len != sizeof(*igmp) || !igmp_is_valid_group(igmp->group)) 156 return; 157 158 groups_update_state(&q->groups, &group, NULL, 0, 159 (igmp->type == IGMPV2_HOST_MEMBERSHIP_REPORT) ? UPDATE_REPORT : 160 (igmp->type == IGMP_HOST_MEMBERSHIP_REPORT) ? UPDATE_REPORT_V1 : UPDATE_DONE); 161 } 162 163 uloop_timeout_set(&q->timeout, 0); 164 } 165 166 // Send generic / group-specific / group-and-source specific IGMP-query 167 int igmp_send_query(struct querier_iface *q, 168 const struct in6_addr *group, 169 const struct list_head *sources, 170 bool suppress) 171 { 172 uint8_t qqic = querier_qqic(((group) ? q->groups.cfg_v4.last_listener_query_interval : 173 q->groups.cfg_v4.query_response_interval) / 100); 174 struct { 175 struct igmpv3_query q; 176 struct in_addr srcs[QUERIER_MAX_SOURCE]; 177 } query = {.q = { 178 .type = IGMP_HOST_MEMBERSHIP_QUERY, 179 .code = qqic, 180 .qrv = q->groups.cfg_v4.robustness, 181 .suppress = suppress, 182 .qqic = querier_qqic(q->groups.cfg_v4.query_interval / 1000), 183 }}; 184 185 struct group_source *s; 186 size_t cnt = 0; 187 if (sources) { 188 list_for_each_entry(s, sources, head) { 189 if (cnt >= QUERIER_MAX_SOURCE) { 190 L_WARN("%s: maximum source count (%d) exceeded", 191 __FUNCTION__, QUERIER_MAX_SOURCE); 192 break; 193 } 194 195 query.q.srcs[cnt] = querier_unmap(&s->addr); 196 } 197 } 198 query.q.nsrcs = htons(cnt); 199 200 struct sockaddr_in dest = { .sin_family = AF_INET, .sin_addr = {htonl(0xe0000001U)}}; 201 if (group) { 202 query.q.group = querier_unmap(group); 203 dest.sin_addr.s_addr = query.q.group; 204 } 205 206 return mrib_send_igmp(&q->mrib, &query.q, 207 sizeof(query.q) + cnt * sizeof(query.srcs[0]), &dest); 208 } 209 210
This page was automatically generated by LXR 0.3.1. • OpenWrt