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

Sources/omcproxy/src/mld.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 <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