Initial Release
[project/omcproxy.git] / 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 }