2 * Author: Steven Barth <steven at midlink.org>
4 * Copyright 2015 Deutsche Telekom AG
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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
23 #include <netinet/in.h>
24 #include <libubox/ustream.h>
25 #include <libubox/usock.h>
26 #include <libubox/list.h>
30 static struct list_head ifaces
= LIST_HEAD_INIT(ifaces
);
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
)
38 struct in6_addr sources
[group
->source_count
];
41 struct group_source
*source
;
42 group_for_each_active_source(source
, group
, now
)
43 sources
[cnt
++] = source
->addr
;
45 include
= group_is_included(group
, now
);
49 user
->user_cb(user
, &group
->addr
, include
, sources
, cnt
);
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
)
55 struct querier_iface
*iface
= container_of(groups
, struct querier_iface
, groups
);
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);
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
)
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
));
71 L_DEBUG("%s: sending %s-specific query for %s on %d (S: %d)", __FUNCTION__
,
72 (!sources
) ? "group" : "source", addrbuf
, iface
->ifindex
, suppress
);
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
);
81 // Expire interface timers and send queries (called by timer as callback)
82 static void querier_iface_timer(struct uloop_timeout
*timeout
)
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
;
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;
95 igmp_send_query(iface
, NULL
, NULL
, false);
96 L_DEBUG("%s: sending generic IGMP-query on %d (S: 0)", __FUNCTION__
, iface
->ifindex
);
98 if (iface
->igmp_startup_tries
> 0)
99 --iface
->igmp_startup_tries
;
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
);
106 if (iface
->igmp_next_query
< next_event
)
107 next_event
= iface
->igmp_next_query
;
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;
116 mld_send_query(iface
, NULL
, NULL
, false);
117 L_DEBUG("%s: sending generic MLD-query on %d (S: 0)", __FUNCTION__
, iface
->ifindex
);
119 if (iface
->mld_startup_tries
> 0)
120 --iface
->mld_startup_tries
;
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
);
127 if (iface
->mld_next_query
< next_event
)
128 next_event
= iface
->mld_next_query
;
130 uloop_timeout_set(&iface
->timeout
, (next_event
> now
) ? next_event
- now
: 0);
134 // Calculate QQI from QQIC
135 int querier_qqi(uint8_t qqic
)
137 return (qqic
& 0x80) ? (((qqic
& 0xf) | 0x10) << (((qqic
>> 4) & 0x7) + 3)) : qqic
;
140 // Calculate MRD from MRC
141 int querier_mrd(uint16_t mrc
)
144 return (mrc
& 0x8000) ? (((mrc
& 0xfff) | 0x1000) << (((mrc
>> 12) & 0x7) + 3)) : mrc
;
147 // Calculate QQIC from QQI
148 uint8_t querier_qqic(int qqi
)
153 while ((qqi
>> exp
) > 0x1f && exp
<= 10)
159 qqi
= 0x80 | ((exp
- 3) << 4) | ((qqi
>> exp
) & 0xf);
164 // Calculate MRC from MRD
165 uint16_t querier_mrc(int mrd
)
170 while ((mrd
>> exp
) > 0x1fff && exp
<= 10)
176 mrd
= 0x8000 | ((exp
- 3) << 12) | ((mrd
>> exp
) & 0xfff);
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
)
185 struct querier_iface
*c
, *iface
= NULL
;
186 list_for_each_entry(c
, &ifaces
, head
) {
187 if (c
->ifindex
== ifindex
) {
193 omgp_time_t now
= omgp_time();
196 if (!(iface
= calloc(1, sizeof(*iface
)))) {
201 list_add(&iface
->head
, &ifaces
);
202 INIT_LIST_HEAD(&iface
->users
);
204 iface
->ifindex
= ifindex
;
205 iface
->timeout
.cb
= querier_iface_timer
;
206 uloop_timeout_set(&iface
->timeout
, 0);
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
;
217 if ((res
= mrib_attach_querier(&iface
->mrib
, ifindex
, igmp_handle
, mld_handle
)))
223 list_add(&user
->head
, &iface
->users
);
226 list_add(&user
->user
.head
, &querier
->ifaces
);
228 user
->user
.querier
= querier
;
229 user
->user
.groups
= &iface
->groups
;
232 groups_for_each_group(group
, &iface
->groups
)
233 querier_announce_iface(user
, now
, group
, true);
237 querier_detach(user
);
241 // Detach an interface from a querier-instance
242 void querier_detach(struct querier_user_iface
*user
)
244 struct querier_iface
*iface
= user
->iface
;
245 list_del(&user
->user
.head
);
246 list_del(&user
->head
);
248 omgp_time_t now
= omgp_time();
250 groups_for_each_group(group
, &iface
->groups
)
251 querier_announce_iface(user
, now
, group
, false);
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
);
262 // Initialize querier-instance
263 int querier_init(struct querier
*querier
)
265 memset(querier
, 0, sizeof(*querier
));
266 INIT_LIST_HEAD(&querier
->ifaces
);
270 // Cleanup querier-instance
271 void querier_deinit(struct querier
*querier
)
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
));