1 /** @file
2  * @brief IPv6 MLD related functions
3  */
4 
5 /*
6  * Copyright (c) 2018 Intel Corporation
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <logging/log.h>
12 LOG_MODULE_DECLARE(net_ipv6, CONFIG_NET_IPV6_LOG_LEVEL);
13 
14 #include <errno.h>
15 #include <net/net_core.h>
16 #include <net/net_pkt.h>
17 #include <net/net_stats.h>
18 #include <net/net_context.h>
19 #include <net/net_mgmt.h>
20 #include "net_private.h"
21 #include "connection.h"
22 #include "icmpv6.h"
23 #include "udp_internal.h"
24 #include "tcp_internal.h"
25 #include "ipv6.h"
26 #include "nbr.h"
27 #include "6lo.h"
28 #include "route.h"
29 #include "net_stats.h"
30 
31 /* Timeout for various buffer allocations in this file. */
32 #define PKT_WAIT_TIME K_MSEC(50)
33 
34 #define MLDv2_MCAST_RECORD_LEN sizeof(struct net_icmpv6_mld_mcast_record)
35 #define IPV6_OPT_HDR_ROUTER_ALERT_LEN 8
36 
37 #define MLDv2_LEN (MLDv2_MCAST_RECORD_LEN + sizeof(struct in6_addr))
38 
mld_create(struct net_pkt * pkt,const struct in6_addr * addr,uint8_t record_type,uint16_t num_sources)39 static int mld_create(struct net_pkt *pkt,
40 		      const struct in6_addr *addr,
41 		      uint8_t record_type,
42 		      uint16_t num_sources)
43 {
44 	NET_PKT_DATA_ACCESS_DEFINE(mld_access,
45 				   struct net_icmpv6_mld_mcast_record);
46 	struct net_icmpv6_mld_mcast_record *mld;
47 
48 	mld = (struct net_icmpv6_mld_mcast_record *)
49 				net_pkt_get_data(pkt, &mld_access);
50 	if (!mld) {
51 		return -ENOBUFS;
52 	}
53 
54 	mld->record_type = record_type;
55 	mld->aux_data_len = 0U;
56 	mld->num_sources = htons(num_sources);
57 
58 	net_ipaddr_copy(&mld->mcast_address, addr);
59 
60 	if (net_pkt_set_data(pkt, &mld_access)) {
61 		return -ENOBUFS;
62 	}
63 
64 	if (num_sources > 0) {
65 		/* All source addresses, RFC 3810 ch 3 */
66 		if (net_pkt_write(pkt,
67 				  net_ipv6_unspecified_address()->s6_addr,
68 				  sizeof(struct in6_addr))) {
69 			return -ENOBUFS;
70 		}
71 	}
72 
73 	return 0;
74 }
75 
mld_create_packet(struct net_pkt * pkt,uint16_t count)76 static int mld_create_packet(struct net_pkt *pkt, uint16_t count)
77 {
78 	struct in6_addr dst;
79 
80 	/* Sent to all MLDv2-capable routers */
81 	net_ipv6_addr_create(&dst, 0xff02, 0, 0, 0, 0, 0, 0, 0x0016);
82 
83 	net_pkt_set_ipv6_hop_limit(pkt, 1); /* RFC 3810 ch 7.4 */
84 
85 	if (net_ipv6_create(pkt, net_if_ipv6_select_src_addr(
86 				    net_pkt_iface(pkt), &dst),
87 			    &dst)) {
88 		return -ENOBUFS;
89 	}
90 
91 	/* Add hop-by-hop option and router alert option, RFC 3810 ch 5. */
92 	if (net_pkt_write_u8(pkt, IPPROTO_ICMPV6) ||
93 	    net_pkt_write_u8(pkt, 0)) {
94 		return -ENOBUFS;
95 	}
96 
97 	/* IPv6 router alert option is described in RFC 2711.
98 	 * - 0x0502 RFC 2711 ch 2.1
99 	 * - MLD (value 0)
100 	 * - 2 bytes of padding
101 	 */
102 	if (net_pkt_write_be16(pkt, 0x0502) ||
103 	    net_pkt_write_be16(pkt, 0) ||
104 	    net_pkt_write_be16(pkt, 0)) {
105 		return -ENOBUFS;
106 	}
107 
108 	net_pkt_set_ipv6_ext_len(pkt, IPV6_OPT_HDR_ROUTER_ALERT_LEN);
109 
110 	/* ICMPv6 header + reserved space + count.
111 	 * MLDv6 stuff will come right after
112 	 */
113 	if (net_icmpv6_create(pkt, NET_ICMPV6_MLDv2, 0) ||
114 	    net_pkt_write_be16(pkt, 0) ||
115 	    net_pkt_write_be16(pkt, count)) {
116 		return -ENOBUFS;
117 	}
118 
119 	net_pkt_set_ipv6_next_hdr(pkt, NET_IPV6_NEXTHDR_HBHO);
120 
121 	return 0;
122 }
123 
mld_send(struct net_pkt * pkt)124 static int mld_send(struct net_pkt *pkt)
125 {
126 	net_pkt_cursor_init(pkt);
127 	net_ipv6_finalize(pkt, IPPROTO_ICMPV6);
128 
129 	if (net_send_data(pkt) < 0) {
130 		net_stats_update_icmp_drop(net_pkt_iface(pkt));
131 		net_stats_update_ipv6_mld_drop(net_pkt_iface(pkt));
132 
133 		net_pkt_unref(pkt);
134 
135 		return -1;
136 	}
137 
138 	net_stats_update_icmp_sent(net_pkt_iface(pkt));
139 	net_stats_update_ipv6_mld_sent(net_pkt_iface(pkt));
140 
141 	return 0;
142 }
143 
mld_send_generic(struct net_if * iface,const struct in6_addr * addr,uint8_t mode)144 static int mld_send_generic(struct net_if *iface,
145 			    const struct in6_addr *addr,
146 			    uint8_t mode)
147 {
148 	struct net_pkt *pkt;
149 	int ret;
150 
151 	pkt = net_pkt_alloc_with_buffer(iface, IPV6_OPT_HDR_ROUTER_ALERT_LEN +
152 					NET_ICMPV6_UNUSED_LEN +
153 					MLDv2_MCAST_RECORD_LEN +
154 					sizeof(struct in6_addr),
155 					AF_INET6, IPPROTO_ICMPV6,
156 					PKT_WAIT_TIME);
157 	if (!pkt) {
158 		return -ENOMEM;
159 	}
160 
161 	if (mld_create_packet(pkt, 1) ||
162 	    mld_create(pkt, addr, mode, 1)) {
163 		ret = -ENOBUFS;
164 		goto drop;
165 	}
166 
167 	ret = mld_send(pkt);
168 	if (ret) {
169 		goto drop;
170 	}
171 
172 	return 0;
173 
174 drop:
175 	net_pkt_unref(pkt);
176 
177 	return ret;
178 }
179 
net_ipv6_mld_join(struct net_if * iface,const struct in6_addr * addr)180 int net_ipv6_mld_join(struct net_if *iface, const struct in6_addr *addr)
181 {
182 	struct net_if_mcast_addr *maddr;
183 	int ret;
184 
185 	maddr = net_if_ipv6_maddr_lookup(addr, &iface);
186 	if (maddr && net_if_ipv6_maddr_is_joined(maddr)) {
187 		return -EALREADY;
188 	}
189 
190 	if (!maddr) {
191 		maddr = net_if_ipv6_maddr_add(iface, addr);
192 		if (!maddr) {
193 			return -ENOMEM;
194 		}
195 	}
196 
197 	ret = mld_send_generic(iface, addr, NET_IPV6_MLDv2_MODE_IS_EXCLUDE);
198 	if (ret < 0) {
199 		return ret;
200 	}
201 
202 	net_if_ipv6_maddr_join(maddr);
203 
204 	net_if_mcast_monitor(iface, addr, true);
205 
206 	net_mgmt_event_notify_with_info(NET_EVENT_IPV6_MCAST_JOIN, iface,
207 					&maddr->address.in6_addr,
208 					sizeof(struct in6_addr));
209 
210 	return ret;
211 }
212 
net_ipv6_mld_leave(struct net_if * iface,const struct in6_addr * addr)213 int net_ipv6_mld_leave(struct net_if *iface, const struct in6_addr *addr)
214 {
215 	struct net_if_mcast_addr *maddr;
216 	int ret;
217 
218 	maddr = net_if_ipv6_maddr_lookup(addr, &iface);
219 	if (!maddr) {
220 		return -ENOENT;
221 	}
222 
223 	if (!net_if_ipv6_maddr_rm(iface, addr)) {
224 		return -EINVAL;
225 	}
226 
227 	ret = mld_send_generic(iface, addr, NET_IPV6_MLDv2_MODE_IS_INCLUDE);
228 	if (ret < 0) {
229 		return ret;
230 	}
231 
232 	net_if_mcast_monitor(iface, addr, false);
233 
234 	net_mgmt_event_notify_with_info(NET_EVENT_IPV6_MCAST_LEAVE, iface,
235 					&maddr->address.in6_addr,
236 					sizeof(struct in6_addr));
237 
238 	return ret;
239 }
240 
send_mld_report(struct net_if * iface)241 static void send_mld_report(struct net_if *iface)
242 {
243 	struct net_if_ipv6 *ipv6 = iface->config.ip.ipv6;
244 	struct net_pkt *pkt;
245 	int i, count = 0;
246 
247 	NET_ASSERT(ipv6);
248 
249 	for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) {
250 		if (!ipv6->mcast[i].is_used || !ipv6->mcast[i].is_joined) {
251 			continue;
252 		}
253 
254 		count++;
255 	}
256 
257 	pkt = net_pkt_alloc_with_buffer(iface, IPV6_OPT_HDR_ROUTER_ALERT_LEN +
258 					NET_ICMPV6_UNUSED_LEN +
259 					count * MLDv2_MCAST_RECORD_LEN,
260 					AF_INET6, IPPROTO_ICMPV6,
261 					PKT_WAIT_TIME);
262 	if (!pkt) {
263 		return;
264 	}
265 
266 	if (mld_create_packet(pkt, count)) {
267 		goto drop;
268 	}
269 
270 	for (i = 0; i < NET_IF_MAX_IPV6_MADDR; i++) {
271 		if (!ipv6->mcast[i].is_used || !ipv6->mcast[i].is_joined) {
272 			continue;
273 		}
274 
275 		if (!mld_create(pkt, &ipv6->mcast[i].address.in6_addr,
276 				NET_IPV6_MLDv2_MODE_IS_EXCLUDE, 0)) {
277 			goto drop;
278 		}
279 	}
280 
281 	if (!mld_send(pkt)) {
282 		return;
283 	}
284 
285 drop:
286 	net_pkt_unref(pkt);
287 }
288 
289 #define dbg_addr(action, pkt_str, src, dst)				\
290 	do {								\
291 		NET_DBG("%s %s from %s to %s", action, pkt_str,         \
292 			log_strdup(net_sprint_ipv6_addr(src)),		\
293 			log_strdup(net_sprint_ipv6_addr(dst)));		\
294 	} while (0)
295 
296 #define dbg_addr_recv(pkt_str, src, dst)	\
297 	dbg_addr("Received", pkt_str, src, dst)
298 
handle_mld_query(struct net_pkt * pkt,struct net_ipv6_hdr * ip_hdr,struct net_icmp_hdr * icmp_hdr)299 static enum net_verdict handle_mld_query(struct net_pkt *pkt,
300 					 struct net_ipv6_hdr *ip_hdr,
301 					 struct net_icmp_hdr *icmp_hdr)
302 {
303 	NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(mld_access,
304 					      struct net_icmpv6_mld_query);
305 	uint16_t length = net_pkt_get_len(pkt);
306 	struct net_icmpv6_mld_query *mld_query;
307 	uint16_t pkt_len;
308 
309 	mld_query = (struct net_icmpv6_mld_query *)
310 				net_pkt_get_data(pkt, &mld_access);
311 	if (!mld_query) {
312 		NET_DBG("DROP: NULL MLD query");
313 		goto drop;
314 	}
315 
316 	net_pkt_acknowledge_data(pkt, &mld_access);
317 
318 	dbg_addr_recv("Multicast Listener Query", &ip_hdr->src, &ip_hdr->dst);
319 
320 	net_stats_update_ipv6_mld_recv(net_pkt_iface(pkt));
321 
322 	mld_query->num_sources = ntohs(mld_query->num_sources);
323 
324 	pkt_len = sizeof(struct net_ipv6_hdr) +	net_pkt_ipv6_ext_len(pkt) +
325 		sizeof(struct net_icmp_hdr) +
326 		sizeof(struct net_icmpv6_mld_query) +
327 		sizeof(struct in6_addr) * mld_query->num_sources;
328 
329 	if (length < pkt_len || pkt_len > NET_IPV6_MTU ||
330 	    ip_hdr->hop_limit != 1U || icmp_hdr->code != 0U) {
331 		goto drop;
332 	}
333 
334 	/* Currently we only support an unspecified address query. */
335 	if (!net_ipv6_addr_cmp(&mld_query->mcast_address,
336 			       net_ipv6_unspecified_address())) {
337 		NET_DBG("DROP: only supporting unspecified address query");
338 		goto drop;
339 	}
340 
341 	send_mld_report(net_pkt_iface(pkt));
342 
343 	net_pkt_unref(pkt);
344 
345 	return NET_OK;
346 
347 drop:
348 	net_stats_update_ipv6_mld_drop(net_pkt_iface(pkt));
349 
350 	return NET_DROP;
351 }
352 
353 static struct net_icmpv6_handler mld_query_input_handler = {
354 	.type = NET_ICMPV6_MLD_QUERY,
355 	.code = 0,
356 	.handler = handle_mld_query,
357 };
358 
net_ipv6_mld_init(void)359 void net_ipv6_mld_init(void)
360 {
361 	net_icmpv6_register_handler(&mld_query_input_handler);
362 }
363