• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/omcproxy/src/igmp.c

  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