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