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

Sources/omcproxy/src/querier.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 <string.h>
 22 #include <stdlib.h>
 23 #include <netinet/in.h>
 24 #include <libubox/ustream.h>
 25 #include <libubox/usock.h>
 26 #include <libubox/list.h>
 27 
 28 #include "querier.h"
 29 
 30 static struct list_head ifaces = LIST_HEAD_INIT(ifaces);
 31 
 32 
 33 // Handle querier update event from a querier-interface
 34 static void querier_announce_iface(struct querier_user_iface *user, omgp_time_t now, const struct group *group, bool enabled)
 35 {
 36         bool include = true;
 37         size_t cnt = 0;
 38         struct in6_addr sources[group->source_count];
 39 
 40         if (enabled) {
 41                 struct group_source *source;
 42                 group_for_each_active_source(source, group, now)
 43                         sources[cnt++] = source->addr;
 44 
 45                 include = group_is_included(group, now);
 46         }
 47 
 48         if (user->user_cb)
 49                 user->user_cb(user, &group->addr, include, sources, cnt);
 50 }
 51 
 52 // Handle changes from a querier for a given group (called by a group-state as callback)
 53 static void querier_announce_change(struct groups *groups, struct group *group, omgp_time_t now)
 54 {
 55         struct querier_iface *iface = container_of(groups, struct querier_iface, groups);
 56 
 57         // Only recognize changes to non-link-local groups
 58         struct querier_user_iface *user;
 59         list_for_each_entry(user, &iface->users, head)
 60                 querier_announce_iface(user, now, group, true);
 61 }
 62 
 63 // Send query for a group + sources (called by a group-state as callback)
 64 static void querier_send_query(struct groups *groups, const struct in6_addr *group,
 65                 const struct list_head *sources, bool suppress)
 66 {
 67         struct querier_iface *iface = container_of(groups, struct querier_iface, groups);
 68         char addrbuf[INET6_ADDRSTRLEN] = "::";
 69         inet_ntop(AF_INET6, group, addrbuf, sizeof(addrbuf));
 70 
 71         L_DEBUG("%s: sending %s-specific query for %s on %d (S: %d)", __FUNCTION__,
 72                         (!sources) ? "group" : "source", addrbuf, iface->ifindex, suppress);
 73 
 74         bool v4 = IN6_IS_ADDR_V4MAPPED(group);
 75         if (v4 && !iface->igmp_other_querier)
 76                 igmp_send_query(iface, group, sources, suppress);
 77         else if (!v4 && !iface->mld_other_querier)
 78                 mld_send_query(iface, group, sources, suppress);
 79 }
 80 
 81 // Expire interface timers and send queries (called by timer as callback)
 82 static void querier_iface_timer(struct uloop_timeout *timeout)
 83 {
 84         struct querier_iface *iface = container_of(timeout, struct querier_iface, timeout);
 85         omgp_time_t now = omgp_time();
 86         omgp_time_t next_event = now + 3600 * OMGP_TIME_PER_SECOND;
 87 
 88         if (iface->igmp_next_query <= now) {
 89                 // If the other querier is gone, reset interface config
 90                 if (iface->igmp_other_querier) {
 91                         iface->groups.cfg_v4 = iface->cfg;
 92                         iface->igmp_other_querier = false;
 93                 }
 94 
 95                 igmp_send_query(iface, NULL, NULL, false);
 96                 L_DEBUG("%s: sending generic IGMP-query on %d (S: 0)", __FUNCTION__, iface->ifindex);
 97 
 98                 if (iface->igmp_startup_tries > 0)
 99                         --iface->igmp_startup_tries;
100 
101                 iface->igmp_next_query = now + ((iface->igmp_startup_tries > 0) ?
102                                                 (iface->groups.cfg_v4.query_interval / 4) :
103                                                 iface->groups.cfg_v4.query_interval);
104         }
105 
106         if (iface->igmp_next_query < next_event)
107                 next_event = iface->igmp_next_query;
108 
109         if (iface->mld_next_query <= now) {
110                 // If the other querier is gone, reset interface config
111                 if (iface->mld_other_querier) {
112                         iface->groups.cfg_v6 = iface->cfg;
113                         iface->mld_other_querier = false;
114                 }
115 
116                 mld_send_query(iface, NULL, NULL, false);
117                 L_DEBUG("%s: sending generic MLD-query on %d (S: 0)", __FUNCTION__, iface->ifindex);
118 
119                 if (iface->mld_startup_tries > 0)
120                         --iface->mld_startup_tries;
121 
122                 iface->mld_next_query = now + ((iface->mld_startup_tries > 0) ?
123                                                 (iface->groups.cfg_v6.query_interval / 4) :
124                                                 iface->groups.cfg_v6.query_interval);
125         }
126 
127         if (iface->mld_next_query < next_event)
128                 next_event = iface->mld_next_query;
129 
130         uloop_timeout_set(&iface->timeout, (next_event > now) ? next_event - now : 0);
131 }
132 
133 
134 // Calculate QQI from QQIC
135 int querier_qqi(uint8_t qqic)
136 {
137         return (qqic & 0x80) ? (((qqic & 0xf) | 0x10) << (((qqic >> 4) & 0x7) + 3)) : qqic;
138 }
139 
140 // Calculate MRD from MRC
141 int querier_mrd(uint16_t mrc)
142 {
143         mrc = ntohs(mrc);
144         return (mrc & 0x8000) ? (((mrc & 0xfff) | 0x1000) << (((mrc >> 12) & 0x7) + 3)) : mrc;
145 }
146 
147 // Calculate QQIC from QQI
148 uint8_t querier_qqic(int qqi)
149 {
150         if (qqi >= 128) {
151                 int exp = 3;
152 
153                 while ((qqi >> exp) > 0x1f && exp <= 10)
154                         ++exp;
155 
156                 if (exp > 10)
157                         qqi = 0xff;
158                 else
159                         qqi = 0x80 | ((exp - 3) << 4) | ((qqi >> exp) & 0xf);
160         }
161         return qqi;
162 }
163 
164 // Calculate MRC from MRD
165 uint16_t querier_mrc(int mrd)
166 {
167         if (mrd >= 32768) {
168                 int exp = 3;
169 
170                 while ((mrd >> exp) > 0x1fff && exp <= 10)
171                         ++exp;
172 
173                 if (exp > 10)
174                         mrd = 0xffff;
175                 else
176                         mrd = 0x8000 | ((exp - 3) << 12) | ((mrd >> exp) & 0xfff);
177         }
178         return htons(mrd);
179 }
180 
181 // Attach an interface to a querier-instance
182 int querier_attach(struct querier_user_iface *user,
183                 struct querier *querier, int ifindex, querier_iface_cb *cb)
184 {
185         struct querier_iface *c, *iface = NULL;
186         list_for_each_entry(c, &ifaces, head) {
187                 if (c->ifindex == ifindex) {
188                         iface = c;
189                         break;
190                 }
191         }
192 
193         omgp_time_t now = omgp_time();
194         int res = 0;
195         if (!iface) {
196                 if (!(iface = calloc(1, sizeof(*iface)))) {
197                         res = -errno;
198                         goto out;
199                 }
200 
201                 list_add(&iface->head, &ifaces);
202                 INIT_LIST_HEAD(&iface->users);
203 
204                 iface->ifindex = ifindex;
205                 iface->timeout.cb = querier_iface_timer;
206                 uloop_timeout_set(&iface->timeout, 0);
207 
208                 groups_init(&iface->groups);
209                 iface->groups.source_limit = QUERIER_MAX_SOURCE;
210                 iface->groups.group_limit = QUERIER_MAX_GROUPS;
211                 iface->groups.cb_update = querier_announce_change;
212                 iface->groups.cb_query = querier_send_query;
213                 iface->cfg = iface->groups.cfg_v6;
214                 iface->igmp_startup_tries = iface->groups.cfg_v4.robustness;
215                 iface->mld_startup_tries = iface->groups.cfg_v6.robustness;
216 
217                 if ((res = mrib_attach_querier(&iface->mrib, ifindex, igmp_handle, mld_handle)))
218                         goto out;
219         }
220 
221 out:
222         if (iface) {
223                 list_add(&user->head, &iface->users);
224                 user->iface = iface;
225 
226                 list_add(&user->user.head, &querier->ifaces);
227                 user->user_cb = cb;
228                 user->user.querier = querier;
229                 user->user.groups = &iface->groups;
230 
231                 struct group *group;
232                 groups_for_each_group(group, &iface->groups)
233                         querier_announce_iface(user, now, group, true);
234         }
235 
236         if (res)
237                 querier_detach(user);
238         return res;
239 }
240 
241 // Detach an interface from a querier-instance
242 void querier_detach(struct querier_user_iface *user)
243 {
244         struct querier_iface *iface = user->iface;
245         list_del(&user->user.head);
246         list_del(&user->head);
247 
248         omgp_time_t now = omgp_time();
249         struct group *group;
250         groups_for_each_group(group, &iface->groups)
251                 querier_announce_iface(user, now, group, false);
252 
253         if (list_empty(&iface->users)) {
254                 uloop_timeout_cancel(&iface->timeout);
255                 groups_deinit(&iface->groups);
256                 mrib_detach_querier(&iface->mrib);
257                 list_del(&iface->head);
258                 free(iface);
259         }
260 }
261 
262 // Initialize querier-instance
263 int querier_init(struct querier *querier)
264 {
265         memset(querier, 0, sizeof(*querier));
266         INIT_LIST_HEAD(&querier->ifaces);
267         return 0;
268 }
269 
270 // Cleanup querier-instance
271 void querier_deinit(struct querier *querier)
272 {
273         struct querier_user *user, *n;
274         list_for_each_entry_safe(user, n, &querier->ifaces, head)
275                 querier_detach(container_of(user, struct querier_user_iface, user));
276 }
277 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt