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 <stdlib.h> 22 #include <arpa/inet.h> 23 #include <sys/ioctl.h> 24 #include <sys/socket.h> 25 #include <sys/timerfd.h> 26 27 #include <netinet/in.h> 28 #include <netinet/ip6.h> 29 #include <netinet/icmp6.h> 30 #include <unistd.h> 31 #include <ifaddrs.h> 32 #include <linux/mroute6.h> 33 34 #include "mrib.h" 35 #include "querier.h" 36 37 struct mld_query { 38 struct mld_hdr mld; 39 uint8_t s_qrv; 40 uint8_t qqic; 41 uint16_t nsrc; 42 struct in6_addr addrs[0]; 43 }; 44 45 // Test whether group address is valid and interesting 46 static inline bool mld_is_valid_group(const struct in6_addr *addr) 47 { 48 return IN6_IS_ADDR_MULTICAST(addr); 49 } 50 51 // Handle Multicast Address Record from MLD-Packets (called by mld_receive) 52 static ssize_t mld_handle_record(struct groups *groups, const uint8_t *data, size_t len) 53 { 54 struct mld_record { 55 uint8_t type; 56 uint8_t aux; 57 uint16_t nsrc; 58 struct in6_addr addr; 59 struct in6_addr sources[]; 60 } *r = (struct mld_record*)data; 61 62 if (len < sizeof(*r)) 63 return -1; 64 65 size_t nsrc = ntohs(r->nsrc); 66 size_t read = sizeof(*r) + nsrc * sizeof(struct in6_addr) + r->aux; 67 if (len < read) 68 return -1; 69 70 if (r->type >= UPDATE_IS_INCLUDE && r->type <= UPDATE_BLOCK && mld_is_valid_group(&r->addr)) 71 groups_update_state(groups, &r->addr, r->sources, nsrc, r->type); 72 73 return read; 74 } 75 76 // Receive an MLD-Packet from a node (called by uloop as callback) 77 void mld_handle(struct mrib_querier *mrib, const struct mld_hdr *hdr, size_t len, 78 const struct sockaddr_in6 *from) 79 { 80 char addrbuf[INET_ADDRSTRLEN]; 81 omgp_time_t now = omgp_time(); 82 inet_ntop(AF_INET6, &hdr->mld_addr, addrbuf, sizeof(addrbuf)); 83 84 struct querier_iface *q = container_of(mrib, struct querier_iface, mrib); 85 if (hdr->mld_icmp6_hdr.icmp6_type == ICMPV6_MGM_QUERY) { 86 struct mld_query *query = (struct mld_query*)hdr; 87 88 if (len != 24 && ((size_t)len < sizeof(*query) || 89 (size_t)len < sizeof(*query) + ntohs(query->nsrc) * sizeof(struct in6_addr))) 90 return; 91 92 if (!IN6_IS_ADDR_UNSPECIFIED(&query->mld.mld_addr) && 93 !mld_is_valid_group(&query->mld.mld_addr)) 94 return; 95 96 // Detect local source address 97 struct in6_addr addr; 98 if (mrib_mld_source(mrib, &addr)) 99 return; 100 101 bool suppress = false; 102 size_t nsrc = 0; 103 int robustness = 2; 104 omgp_time_t mrd = 10000; 105 omgp_time_t query_interval = 125000; 106 107 if (query->mld.mld_icmp6_hdr.icmp6_dataun.icmp6_un_data16[0]) 108 mrd = (len == 24) ? ntohs(query->mld.mld_icmp6_hdr.icmp6_dataun.icmp6_un_data16[0]) : 109 querier_mrd(query->mld.mld_icmp6_hdr.icmp6_dataun.icmp6_un_data16[0]); 110 111 if (len > 24) { 112 if (query->s_qrv & 0x7) 113 robustness = query->s_qrv & 0x7; 114 115 if (query->qqic) 116 query_interval = querier_qqi(query->qqic) * 1000; 117 } 118 119 if (!suppress && !IN6_IS_ADDR_UNSPECIFIED(&query->mld.mld_addr)) 120 groups_update_timers(&q->groups, &query->mld.mld_addr, query->addrs, nsrc); 121 122 int election = memcmp(&from->sin6_addr, &addr, sizeof(from->sin6_addr)); 123 L_INFO("%s: detected other querier %s with priority %d on %d", 124 __FUNCTION__, addrbuf, election, q->ifindex); 125 126 // TODO: we ignore MLDv1 queriers for now, since a lot of them are dumb switches 127 128 if (election < 0 && IN6_IS_ADDR_UNSPECIFIED(&query->mld.mld_addr) && len > 24) { 129 groups_update_config(&q->groups, true, mrd, query_interval, robustness); 130 131 q->mld_other_querier = true; 132 q->mld_next_query = now + (q->groups.cfg_v6.query_response_interval / 2) + 133 (q->groups.cfg_v6.robustness * q->groups.cfg_v6.query_interval); 134 } 135 } else if (hdr->mld_icmp6_hdr.icmp6_type == ICMPV6_MLD2_REPORT) { 136 struct icmp6_hdr *mld_report = (struct icmp6_hdr *)hdr; 137 if ((size_t)len <= sizeof(*mld_report)) 138 return; 139 140 uint8_t *buf = (uint8_t*)hdr; 141 size_t count = ntohs(mld_report->icmp6_dataun.icmp6_un_data16[1]); 142 ssize_t offset = sizeof(*mld_report); 143 144 while (count > 0 && offset < (ssize_t)len) { 145 ssize_t read = mld_handle_record(&q->groups, &buf[offset], len - offset); 146 if (read < 0) 147 break; 148 149 offset += read; 150 --count; 151 } 152 } else if (hdr->mld_icmp6_hdr.icmp6_type == MLD_LISTENER_REPORT || 153 hdr->mld_icmp6_hdr.icmp6_type == MLD_LISTENER_REDUCTION) { 154 if (len != 24 || !mld_is_valid_group(&hdr->mld_addr)) 155 return; 156 157 groups_update_state(&q->groups, &hdr->mld_addr, NULL, 0, 158 (hdr->mld_icmp6_hdr.icmp6_type == MLD_LISTENER_REPORT) ? UPDATE_REPORT : UPDATE_DONE); 159 } 160 uloop_timeout_set(&q->timeout, 0); 161 } 162 163 164 // Send generic / group-specific / group-and-source-specific queries 165 ssize_t mld_send_query(struct querier_iface *q, const struct in6_addr *group, 166 const struct list_head *sources, bool suppress) 167 { 168 uint16_t mrc = querier_mrc((group) ? q->groups.cfg_v6.last_listener_query_interval : 169 q->groups.cfg_v6.query_response_interval); 170 struct { 171 struct mld_query q; 172 struct in6_addr addrs[QUERIER_MAX_SOURCE]; 173 } query = {.q = { 174 .mld = {.mld_icmp6_hdr = {MLD_LISTENER_QUERY, 0, 0, {.icmp6_un_data16 = {mrc, 0}}}}, 175 .s_qrv = (q->groups.cfg_v6.robustness & 0x7) | (suppress ? QUERIER_SUPPRESS : 0), 176 .qqic = querier_qqic(q->groups.cfg_v6.query_interval / 1000), 177 }}; 178 179 struct group_source *s; 180 size_t cnt = 0; 181 if (sources) { 182 list_for_each_entry(s, sources, head) { 183 if (cnt >= QUERIER_MAX_SOURCE) 184 break; // TODO: log source overflow 185 186 query.addrs[cnt++] = s->addr; 187 } 188 } 189 query.q.nsrc = htons(cnt); 190 191 struct sockaddr_in6 dest = {AF_INET6, 0, 0, IPV6_ALL_NODES_INIT, q->ifindex}; 192 193 if (group) 194 query.q.mld.mld_addr = dest.sin6_addr = *group; 195 196 return mrib_send_mld(&q->mrib, &query.q.mld, 197 sizeof(query.q) + cnt * sizeof(query.addrs[0]), &dest); 198 } 199
This page was automatically generated by LXR 0.3.1. • OpenWrt