xref: /openbmc/linux/net/tipc/udp_media.c (revision 16f6ccde74a6f8538c62f127f17207c75f4dba7a)
1d0f91938SErik Hugne /* net/tipc/udp_media.c: IP bearer support for TIPC
2d0f91938SErik Hugne  *
3d0f91938SErik Hugne  * Copyright (c) 2015, Ericsson AB
4d0f91938SErik Hugne  * All rights reserved.
5d0f91938SErik Hugne  *
6d0f91938SErik Hugne  * Redistribution and use in source and binary forms, with or without
7d0f91938SErik Hugne  * modification, are permitted provided that the following conditions are met:
8d0f91938SErik Hugne  *
9d0f91938SErik Hugne  * 1. Redistributions of source code must retain the above copyright
10d0f91938SErik Hugne  *    notice, this list of conditions and the following disclaimer.
11d0f91938SErik Hugne  * 2. Redistributions in binary form must reproduce the above copyright
12d0f91938SErik Hugne  *    notice, this list of conditions and the following disclaimer in the
13d0f91938SErik Hugne  *    documentation and/or other materials provided with the distribution.
14d0f91938SErik Hugne  * 3. Neither the names of the copyright holders nor the names of its
15d0f91938SErik Hugne  *    contributors may be used to endorse or promote products derived from
16d0f91938SErik Hugne  *    this software without specific prior written permission.
17d0f91938SErik Hugne  *
18d0f91938SErik Hugne  * Alternatively, this software may be distributed under the terms of the
19d0f91938SErik Hugne  * GNU General Public License ("GPL") version 2 as published by the Free
20d0f91938SErik Hugne  * Software Foundation.
21d0f91938SErik Hugne  *
22d0f91938SErik Hugne  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
23d0f91938SErik Hugne  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24d0f91938SErik Hugne  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25d0f91938SErik Hugne  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
26d0f91938SErik Hugne  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27d0f91938SErik Hugne  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28d0f91938SErik Hugne  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29d0f91938SErik Hugne  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30d0f91938SErik Hugne  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31d0f91938SErik Hugne  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32d0f91938SErik Hugne  * POSSIBILITY OF SUCH DAMAGE.
33d0f91938SErik Hugne  */
34d0f91938SErik Hugne 
35d0f91938SErik Hugne #include <linux/socket.h>
36d0f91938SErik Hugne #include <linux/ip.h>
37d0f91938SErik Hugne #include <linux/udp.h>
38d0f91938SErik Hugne #include <linux/inet.h>
39d0f91938SErik Hugne #include <linux/inetdevice.h>
40d0f91938SErik Hugne #include <linux/igmp.h>
41d0f91938SErik Hugne #include <linux/kernel.h>
42d0f91938SErik Hugne #include <linux/workqueue.h>
43d0f91938SErik Hugne #include <linux/list.h>
44d0f91938SErik Hugne #include <net/sock.h>
45d0f91938SErik Hugne #include <net/ip.h>
46d0f91938SErik Hugne #include <net/udp_tunnel.h>
473616d08bSDavid Ahern #include <net/ipv6_stubs.h>
48d0f91938SErik Hugne #include <linux/tipc_netlink.h>
49d0f91938SErik Hugne #include "core.h"
5052dfae5cSJon Maloy #include "addr.h"
5152dfae5cSJon Maloy #include "net.h"
52d0f91938SErik Hugne #include "bearer.h"
5349cc66eaSRichard Alpe #include "netlink.h"
54ef20cd4dSRichard Alpe #include "msg.h"
555f3666e8SWang Hai #include "udp_media.h"
56d0f91938SErik Hugne 
57d0f91938SErik Hugne /* IANA assigned UDP port */
58d0f91938SErik Hugne #define UDP_PORT_DEFAULT	6118
59d0f91938SErik Hugne 
609bd160bfSRichard Alpe #define UDP_MIN_HEADROOM        48
61e5356794SJon Paul Maloy 
62d0f91938SErik Hugne /**
63d0f91938SErik Hugne  * struct udp_media_addr - IP/UDP addressing information
64d0f91938SErik Hugne  *
65d0f91938SErik Hugne  * This is the bearer level originating address used in neighbor discovery
66d0f91938SErik Hugne  * messages, and all fields should be in network byte order
675fcb7d47SRandy Dunlap  *
685fcb7d47SRandy Dunlap  * @proto: Ethernet protocol in use
695fcb7d47SRandy Dunlap  * @port: port being used
705fcb7d47SRandy Dunlap  * @ipv4: IPv4 address of neighbor
715fcb7d47SRandy Dunlap  * @ipv6: IPv6 address of neighbor
72d0f91938SErik Hugne  */
73d0f91938SErik Hugne struct udp_media_addr {
74d0f91938SErik Hugne 	__be16	proto;
75bc3a334cSRichard Alpe 	__be16	port;
76d0f91938SErik Hugne 	union {
77d0f91938SErik Hugne 		struct in_addr ipv4;
78d0f91938SErik Hugne 		struct in6_addr ipv6;
79d0f91938SErik Hugne 	};
80d0f91938SErik Hugne };
81d0f91938SErik Hugne 
82ef20cd4dSRichard Alpe /* struct udp_replicast - container for UDP remote addresses */
83ef20cd4dSRichard Alpe struct udp_replicast {
84ef20cd4dSRichard Alpe 	struct udp_media_addr addr;
85e9c1a793SXin Long 	struct dst_cache dst_cache;
86ef20cd4dSRichard Alpe 	struct rcu_head rcu;
87ef20cd4dSRichard Alpe 	struct list_head list;
88ef20cd4dSRichard Alpe };
89ef20cd4dSRichard Alpe 
90d0f91938SErik Hugne /**
91d0f91938SErik Hugne  * struct udp_bearer - ip/udp bearer data structure
92d0f91938SErik Hugne  * @bearer:	associated generic tipc bearer
93d0f91938SErik Hugne  * @ubsock:	bearer associated socket
94d0f91938SErik Hugne  * @ifindex:	local address scope
95d0f91938SErik Hugne  * @work:	used to schedule deferred work on a bearer
965fcb7d47SRandy Dunlap  * @rcast:	associated udp_replicast container
97d0f91938SErik Hugne  */
98d0f91938SErik Hugne struct udp_bearer {
99d0f91938SErik Hugne 	struct tipc_bearer __rcu *bearer;
100d0f91938SErik Hugne 	struct socket *ubsock;
101d0f91938SErik Hugne 	u32 ifindex;
102d0f91938SErik Hugne 	struct work_struct work;
103ef20cd4dSRichard Alpe 	struct udp_replicast rcast;
104d0f91938SErik Hugne };
105d0f91938SErik Hugne 
tipc_udp_is_mcast_addr(struct udp_media_addr * addr)1061ca73e3fSRichard Alpe static int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
1071ca73e3fSRichard Alpe {
1081ca73e3fSRichard Alpe 	if (ntohs(addr->proto) == ETH_P_IP)
1091ca73e3fSRichard Alpe 		return ipv4_is_multicast(addr->ipv4.s_addr);
1101ca73e3fSRichard Alpe #if IS_ENABLED(CONFIG_IPV6)
1111ca73e3fSRichard Alpe 	else
1121ca73e3fSRichard Alpe 		return ipv6_addr_is_multicast(&addr->ipv6);
1131ca73e3fSRichard Alpe #endif
1141ca73e3fSRichard Alpe 	return 0;
1151ca73e3fSRichard Alpe }
1161ca73e3fSRichard Alpe 
117d0f91938SErik Hugne /* udp_media_addr_set - convert a ip/udp address to a TIPC media address */
tipc_udp_media_addr_set(struct tipc_media_addr * addr,struct udp_media_addr * ua)118d0f91938SErik Hugne static void tipc_udp_media_addr_set(struct tipc_media_addr *addr,
119d0f91938SErik Hugne 				    struct udp_media_addr *ua)
120d0f91938SErik Hugne {
121d0f91938SErik Hugne 	memset(addr, 0, sizeof(struct tipc_media_addr));
122d0f91938SErik Hugne 	addr->media_id = TIPC_MEDIA_TYPE_UDP;
123d0f91938SErik Hugne 	memcpy(addr->value, ua, sizeof(struct udp_media_addr));
1241ca73e3fSRichard Alpe 
1251ca73e3fSRichard Alpe 	if (tipc_udp_is_mcast_addr(ua))
1269999974aSJon Paul Maloy 		addr->broadcast = TIPC_BROADCAST_SUPPORT;
127d0f91938SErik Hugne }
128d0f91938SErik Hugne 
129d0f91938SErik Hugne /* tipc_udp_addr2str - convert ip/udp address to string */
tipc_udp_addr2str(struct tipc_media_addr * a,char * buf,int size)130d0f91938SErik Hugne static int tipc_udp_addr2str(struct tipc_media_addr *a, char *buf, int size)
131d0f91938SErik Hugne {
132d0f91938SErik Hugne 	struct udp_media_addr *ua = (struct udp_media_addr *)&a->value;
133d0f91938SErik Hugne 
134d0f91938SErik Hugne 	if (ntohs(ua->proto) == ETH_P_IP)
135bc3a334cSRichard Alpe 		snprintf(buf, size, "%pI4:%u", &ua->ipv4, ntohs(ua->port));
136d0f91938SErik Hugne 	else if (ntohs(ua->proto) == ETH_P_IPV6)
137bc3a334cSRichard Alpe 		snprintf(buf, size, "%pI6:%u", &ua->ipv6, ntohs(ua->port));
13876ddf84aSShigeru Yoshida 	else {
139d0f91938SErik Hugne 		pr_err("Invalid UDP media address\n");
14076ddf84aSShigeru Yoshida 		return 1;
14176ddf84aSShigeru Yoshida 	}
14276ddf84aSShigeru Yoshida 
143d0f91938SErik Hugne 	return 0;
144d0f91938SErik Hugne }
145d0f91938SErik Hugne 
146d0f91938SErik Hugne /* tipc_udp_msg2addr - extract an ip/udp address from a TIPC ndisc message */
tipc_udp_msg2addr(struct tipc_bearer * b,struct tipc_media_addr * a,char * msg)147d0f91938SErik Hugne static int tipc_udp_msg2addr(struct tipc_bearer *b, struct tipc_media_addr *a,
148d0f91938SErik Hugne 			     char *msg)
149d0f91938SErik Hugne {
150d0f91938SErik Hugne 	struct udp_media_addr *ua;
151d0f91938SErik Hugne 
152d0f91938SErik Hugne 	ua = (struct udp_media_addr *) (msg + TIPC_MEDIA_ADDR_OFFSET);
153d0f91938SErik Hugne 	if (msg[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_UDP)
154d0f91938SErik Hugne 		return -EINVAL;
155d0f91938SErik Hugne 	tipc_udp_media_addr_set(a, ua);
156d0f91938SErik Hugne 	return 0;
157d0f91938SErik Hugne }
158d0f91938SErik Hugne 
159d0f91938SErik Hugne /* tipc_udp_addr2msg - write an ip/udp address to a TIPC ndisc message */
tipc_udp_addr2msg(char * msg,struct tipc_media_addr * a)160d0f91938SErik Hugne static int tipc_udp_addr2msg(char *msg, struct tipc_media_addr *a)
161d0f91938SErik Hugne {
162d0f91938SErik Hugne 	memset(msg, 0, TIPC_MEDIA_INFO_SIZE);
163d0f91938SErik Hugne 	msg[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_UDP;
164d0f91938SErik Hugne 	memcpy(msg + TIPC_MEDIA_ADDR_OFFSET, a->value,
165d0f91938SErik Hugne 	       sizeof(struct udp_media_addr));
166d0f91938SErik Hugne 	return 0;
167d0f91938SErik Hugne }
168d0f91938SErik Hugne 
169d0f91938SErik Hugne /* tipc_send_msg - enqueue a send request */
tipc_udp_xmit(struct net * net,struct sk_buff * skb,struct udp_bearer * ub,struct udp_media_addr * src,struct udp_media_addr * dst,struct dst_cache * cache)170ce984da3SRichard Alpe static int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
171ce984da3SRichard Alpe 			 struct udp_bearer *ub, struct udp_media_addr *src,
172e9c1a793SXin Long 			 struct udp_media_addr *dst, struct dst_cache *cache)
173d0f91938SErik Hugne {
17413788174SEric Dumazet 	struct dst_entry *ndst;
175d0f91938SErik Hugne 	int ttl, err = 0;
176d0f91938SErik Hugne 
17713788174SEric Dumazet 	local_bh_disable();
17813788174SEric Dumazet 	ndst = dst_cache_get(cache);
1794fee6be8SErik Hugne 	if (dst->proto == htons(ETH_P_IP)) {
180e9c1a793SXin Long 		struct rtable *rt = (struct rtable *)ndst;
181e9c1a793SXin Long 
182e9c1a793SXin Long 		if (!rt) {
183d0f91938SErik Hugne 			struct flowi4 fl = {
184d0f91938SErik Hugne 				.daddr = dst->ipv4.s_addr,
185d0f91938SErik Hugne 				.saddr = src->ipv4.s_addr,
1867214bcf8SJon Paul Maloy 				.flowi4_mark = skb->mark,
187d0f91938SErik Hugne 				.flowi4_proto = IPPROTO_UDP
188d0f91938SErik Hugne 			};
189d0f91938SErik Hugne 			rt = ip_route_output_key(net, &fl);
190d0f91938SErik Hugne 			if (IS_ERR(rt)) {
191d0f91938SErik Hugne 				err = PTR_ERR(rt);
192d0f91938SErik Hugne 				goto tx_error;
193d0f91938SErik Hugne 			}
194e9c1a793SXin Long 			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
195e9c1a793SXin Long 		}
1969b300960SRichard Alpe 
197d0f91938SErik Hugne 		ttl = ip4_dst_hoplimit(&rt->dst);
198039f5062SPravin B Shelar 		udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr,
199bc3a334cSRichard Alpe 				    dst->ipv4.s_addr, 0, ttl, 0, src->port,
200bc3a334cSRichard Alpe 				    dst->port, false, true);
201d0f91938SErik Hugne #if IS_ENABLED(CONFIG_IPV6)
202d0f91938SErik Hugne 	} else {
203e9c1a793SXin Long 		if (!ndst) {
204d0f91938SErik Hugne 			struct flowi6 fl6 = {
205d0f91938SErik Hugne 				.flowi6_oif = ub->ifindex,
206d0f91938SErik Hugne 				.daddr = dst->ipv6,
207d0f91938SErik Hugne 				.saddr = src->ipv6,
208d0f91938SErik Hugne 				.flowi6_proto = IPPROTO_UDP
209d0f91938SErik Hugne 			};
2106c8991f4SSabrina Dubroca 			ndst = ipv6_stub->ipv6_dst_lookup_flow(net,
2116c8991f4SSabrina Dubroca 							       ub->ubsock->sk,
2126c8991f4SSabrina Dubroca 							       &fl6, NULL);
2136c8991f4SSabrina Dubroca 			if (IS_ERR(ndst)) {
2146c8991f4SSabrina Dubroca 				err = PTR_ERR(ndst);
215d0f91938SErik Hugne 				goto tx_error;
2166c8991f4SSabrina Dubroca 			}
217e9c1a793SXin Long 			dst_cache_set_ip6(cache, ndst, &fl6.saddr);
218e9c1a793SXin Long 		}
219d0f91938SErik Hugne 		ttl = ip6_dst_hoplimit(ndst);
220c3bcde02SXin Long 		err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, NULL,
221c3bcde02SXin Long 					   &src->ipv6, &dst->ipv6, 0, ttl, 0,
222c3bcde02SXin Long 					   src->port, dst->port, false);
223d0f91938SErik Hugne #endif
224d0f91938SErik Hugne 	}
22513788174SEric Dumazet 	local_bh_enable();
226d0f91938SErik Hugne 	return err;
227d0f91938SErik Hugne 
228d0f91938SErik Hugne tx_error:
22913788174SEric Dumazet 	local_bh_enable();
2307214bcf8SJon Paul Maloy 	kfree_skb(skb);
231d0f91938SErik Hugne 	return err;
232d0f91938SErik Hugne }
233d0f91938SErik Hugne 
tipc_udp_send_msg(struct net * net,struct sk_buff * skb,struct tipc_bearer * b,struct tipc_media_addr * addr)234ce984da3SRichard Alpe static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
235ce984da3SRichard Alpe 			     struct tipc_bearer *b,
236ce984da3SRichard Alpe 			     struct tipc_media_addr *addr)
237ce984da3SRichard Alpe {
238ce984da3SRichard Alpe 	struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value;
239ce984da3SRichard Alpe 	struct udp_media_addr *dst = (struct udp_media_addr *)&addr->value;
240ef20cd4dSRichard Alpe 	struct udp_replicast *rcast;
241ce984da3SRichard Alpe 	struct udp_bearer *ub;
242ce984da3SRichard Alpe 	int err = 0;
243ce984da3SRichard Alpe 
244ce984da3SRichard Alpe 	if (skb_headroom(skb) < UDP_MIN_HEADROOM) {
245ce984da3SRichard Alpe 		err = pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC);
246ce984da3SRichard Alpe 		if (err)
247ef20cd4dSRichard Alpe 			goto out;
248ce984da3SRichard Alpe 	}
249ce984da3SRichard Alpe 
250ce984da3SRichard Alpe 	skb_set_inner_protocol(skb, htons(ETH_P_TIPC));
25130a4616cSXin Long 	ub = rcu_dereference(b->media_ptr);
252ce984da3SRichard Alpe 	if (!ub) {
253ce984da3SRichard Alpe 		err = -ENODEV;
254ef20cd4dSRichard Alpe 		goto out;
255ce984da3SRichard Alpe 	}
256ce984da3SRichard Alpe 
2579999974aSJon Paul Maloy 	if (addr->broadcast != TIPC_REPLICAST_SUPPORT)
258e9c1a793SXin Long 		return tipc_udp_xmit(net, skb, ub, src, dst,
259e9c1a793SXin Long 				     &ub->rcast.dst_cache);
260ce984da3SRichard Alpe 
261ef20cd4dSRichard Alpe 	/* Replicast, send an skb to each configured IP address */
262ef20cd4dSRichard Alpe 	list_for_each_entry_rcu(rcast, &ub->rcast.list, list) {
263ef20cd4dSRichard Alpe 		struct sk_buff *_skb;
264ef20cd4dSRichard Alpe 
265ef20cd4dSRichard Alpe 		_skb = pskb_copy(skb, GFP_ATOMIC);
266ef20cd4dSRichard Alpe 		if (!_skb) {
267ef20cd4dSRichard Alpe 			err = -ENOMEM;
268ef20cd4dSRichard Alpe 			goto out;
269ef20cd4dSRichard Alpe 		}
270ef20cd4dSRichard Alpe 
271e9c1a793SXin Long 		err = tipc_udp_xmit(net, _skb, ub, src, &rcast->addr,
272e9c1a793SXin Long 				    &rcast->dst_cache);
273acb4a33eSCong Wang 		if (err)
274ef20cd4dSRichard Alpe 			goto out;
275ef20cd4dSRichard Alpe 	}
276ef20cd4dSRichard Alpe 	err = 0;
277ef20cd4dSRichard Alpe out:
278ce984da3SRichard Alpe 	kfree_skb(skb);
279ce984da3SRichard Alpe 	return err;
280ce984da3SRichard Alpe }
281ce984da3SRichard Alpe 
tipc_udp_is_known_peer(struct tipc_bearer * b,struct udp_media_addr * addr)282c9b64d49SRichard Alpe static bool tipc_udp_is_known_peer(struct tipc_bearer *b,
283c9b64d49SRichard Alpe 				   struct udp_media_addr *addr)
284c9b64d49SRichard Alpe {
285c9b64d49SRichard Alpe 	struct udp_replicast *rcast, *tmp;
286c9b64d49SRichard Alpe 	struct udp_bearer *ub;
287c9b64d49SRichard Alpe 
288c9b64d49SRichard Alpe 	ub = rcu_dereference_rtnl(b->media_ptr);
289c9b64d49SRichard Alpe 	if (!ub) {
290c9b64d49SRichard Alpe 		pr_err_ratelimited("UDP bearer instance not found\n");
291c9b64d49SRichard Alpe 		return false;
292c9b64d49SRichard Alpe 	}
293c9b64d49SRichard Alpe 
294c9b64d49SRichard Alpe 	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
295c9b64d49SRichard Alpe 		if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
296c9b64d49SRichard Alpe 			return true;
297c9b64d49SRichard Alpe 	}
298c9b64d49SRichard Alpe 
299c9b64d49SRichard Alpe 	return false;
300c9b64d49SRichard Alpe }
301c9b64d49SRichard Alpe 
tipc_udp_rcast_add(struct tipc_bearer * b,struct udp_media_addr * addr)302ef20cd4dSRichard Alpe static int tipc_udp_rcast_add(struct tipc_bearer *b,
303ef20cd4dSRichard Alpe 			      struct udp_media_addr *addr)
304ef20cd4dSRichard Alpe {
305ef20cd4dSRichard Alpe 	struct udp_replicast *rcast;
306ef20cd4dSRichard Alpe 	struct udp_bearer *ub;
307ef20cd4dSRichard Alpe 
308ef20cd4dSRichard Alpe 	ub = rcu_dereference_rtnl(b->media_ptr);
309ef20cd4dSRichard Alpe 	if (!ub)
310ef20cd4dSRichard Alpe 		return -ENODEV;
311ef20cd4dSRichard Alpe 
312ef20cd4dSRichard Alpe 	rcast = kmalloc(sizeof(*rcast), GFP_ATOMIC);
313ef20cd4dSRichard Alpe 	if (!rcast)
314ef20cd4dSRichard Alpe 		return -ENOMEM;
315ef20cd4dSRichard Alpe 
316e9c1a793SXin Long 	if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) {
317e9c1a793SXin Long 		kfree(rcast);
318e9c1a793SXin Long 		return -ENOMEM;
319e9c1a793SXin Long 	}
320e9c1a793SXin Long 
321ef20cd4dSRichard Alpe 	memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr));
322ef20cd4dSRichard Alpe 
323ef20cd4dSRichard Alpe 	if (ntohs(addr->proto) == ETH_P_IP)
324ef20cd4dSRichard Alpe 		pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4);
325ef20cd4dSRichard Alpe #if IS_ENABLED(CONFIG_IPV6)
326ef20cd4dSRichard Alpe 	else if (ntohs(addr->proto) == ETH_P_IPV6)
327ef20cd4dSRichard Alpe 		pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
328ef20cd4dSRichard Alpe #endif
3299999974aSJon Paul Maloy 	b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
330ef20cd4dSRichard Alpe 	list_add_rcu(&rcast->list, &ub->rcast.list);
331ef20cd4dSRichard Alpe 	return 0;
332ef20cd4dSRichard Alpe }
333ef20cd4dSRichard Alpe 
tipc_udp_rcast_disc(struct tipc_bearer * b,struct sk_buff * skb)334c9b64d49SRichard Alpe static int tipc_udp_rcast_disc(struct tipc_bearer *b, struct sk_buff *skb)
335c9b64d49SRichard Alpe {
336c9b64d49SRichard Alpe 	struct udp_media_addr src = {0};
337c9b64d49SRichard Alpe 	struct udp_media_addr *dst;
338c9b64d49SRichard Alpe 
339c9b64d49SRichard Alpe 	dst = (struct udp_media_addr *)&b->bcast_addr.value;
340c9b64d49SRichard Alpe 	if (tipc_udp_is_mcast_addr(dst))
341c9b64d49SRichard Alpe 		return 0;
342c9b64d49SRichard Alpe 
343c9b64d49SRichard Alpe 	src.port = udp_hdr(skb)->source;
344c9b64d49SRichard Alpe 
345c9b64d49SRichard Alpe 	if (ip_hdr(skb)->version == 4) {
346c9b64d49SRichard Alpe 		struct iphdr *iphdr = ip_hdr(skb);
347c9b64d49SRichard Alpe 
348c9b64d49SRichard Alpe 		src.proto = htons(ETH_P_IP);
349c9b64d49SRichard Alpe 		src.ipv4.s_addr = iphdr->saddr;
350c9b64d49SRichard Alpe 		if (ipv4_is_multicast(iphdr->daddr))
351c9b64d49SRichard Alpe 			return 0;
352c9b64d49SRichard Alpe #if IS_ENABLED(CONFIG_IPV6)
353c9b64d49SRichard Alpe 	} else if (ip_hdr(skb)->version == 6) {
354c9b64d49SRichard Alpe 		struct ipv6hdr *iphdr = ipv6_hdr(skb);
355c9b64d49SRichard Alpe 
356c9b64d49SRichard Alpe 		src.proto = htons(ETH_P_IPV6);
357c9b64d49SRichard Alpe 		src.ipv6 = iphdr->saddr;
358c9b64d49SRichard Alpe 		if (ipv6_addr_is_multicast(&iphdr->daddr))
359c9b64d49SRichard Alpe 			return 0;
360c9b64d49SRichard Alpe #endif
361c9b64d49SRichard Alpe 	} else {
362c9b64d49SRichard Alpe 		return 0;
363c9b64d49SRichard Alpe 	}
364c9b64d49SRichard Alpe 
365c9b64d49SRichard Alpe 	if (likely(tipc_udp_is_known_peer(b, &src)))
366c9b64d49SRichard Alpe 		return 0;
367c9b64d49SRichard Alpe 
368c9b64d49SRichard Alpe 	return tipc_udp_rcast_add(b, &src);
369c9b64d49SRichard Alpe }
370c9b64d49SRichard Alpe 
371d0f91938SErik Hugne /* tipc_udp_recv - read data from bearer socket */
tipc_udp_recv(struct sock * sk,struct sk_buff * skb)372d0f91938SErik Hugne static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
373d0f91938SErik Hugne {
374d0f91938SErik Hugne 	struct udp_bearer *ub;
375d0f91938SErik Hugne 	struct tipc_bearer *b;
376c9b64d49SRichard Alpe 	struct tipc_msg *hdr;
377c9b64d49SRichard Alpe 	int err;
378d0f91938SErik Hugne 
379d0f91938SErik Hugne 	ub = rcu_dereference_sk_user_data(sk);
380d0f91938SErik Hugne 	if (!ub) {
381d0f91938SErik Hugne 		pr_err_ratelimited("Failed to get UDP bearer reference");
382c9b64d49SRichard Alpe 		goto out;
383d0f91938SErik Hugne 	}
384d0f91938SErik Hugne 	skb_pull(skb, sizeof(struct udphdr));
385c9b64d49SRichard Alpe 	hdr = buf_msg(skb);
386c9b64d49SRichard Alpe 
3874109a2c3SEric Dumazet 	b = rcu_dereference(ub->bearer);
388c9b64d49SRichard Alpe 	if (!b)
3894109a2c3SEric Dumazet 		goto out;
390d0f91938SErik Hugne 
3910d051bf9SJon Paul Maloy 	if (b && test_bit(0, &b->up)) {
392fc1b6d6dSTuong Lien 		TIPC_SKB_CB(skb)->flags = 0;
393d0f91938SErik Hugne 		tipc_rcv(sock_net(sk), skb, b);
394d0f91938SErik Hugne 		return 0;
395d0f91938SErik Hugne 	}
396c9b64d49SRichard Alpe 
397c9b64d49SRichard Alpe 	if (unlikely(msg_user(hdr) == LINK_CONFIG)) {
398c9b64d49SRichard Alpe 		err = tipc_udp_rcast_disc(b, skb);
399c9b64d49SRichard Alpe 		if (err)
4004109a2c3SEric Dumazet 			goto out;
401c9b64d49SRichard Alpe 	}
402c9b64d49SRichard Alpe 
403c9b64d49SRichard Alpe out:
404d0f91938SErik Hugne 	kfree_skb(skb);
405d0f91938SErik Hugne 	return 0;
406d0f91938SErik Hugne }
407d0f91938SErik Hugne 
enable_mcast(struct udp_bearer * ub,struct udp_media_addr * remote)408d0f91938SErik Hugne static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote)
409d0f91938SErik Hugne {
410d0f91938SErik Hugne 	int err = 0;
411d0f91938SErik Hugne 	struct ip_mreqn mreqn;
412d0f91938SErik Hugne 	struct sock *sk = ub->ubsock->sk;
413d0f91938SErik Hugne 
414d0f91938SErik Hugne 	if (ntohs(remote->proto) == ETH_P_IP) {
415d0f91938SErik Hugne 		mreqn.imr_multiaddr = remote->ipv4;
416d0f91938SErik Hugne 		mreqn.imr_ifindex = ub->ifindex;
41754ff9ef3SMarcelo Ricardo Leitner 		err = ip_mc_join_group(sk, &mreqn);
418446981e5SMarcelo Ricardo Leitner #if IS_ENABLED(CONFIG_IPV6)
419d0f91938SErik Hugne 	} else {
4204b4b8446STaehee Yoo 		lock_sock(sk);
421446981e5SMarcelo Ricardo Leitner 		err = ipv6_stub->ipv6_sock_mc_join(sk, ub->ifindex,
422446981e5SMarcelo Ricardo Leitner 						   &remote->ipv6);
4234b4b8446STaehee Yoo 		release_sock(sk);
424446981e5SMarcelo Ricardo Leitner #endif
425d0f91938SErik Hugne 	}
426d0f91938SErik Hugne 	return err;
427d0f91938SErik Hugne }
428d0f91938SErik Hugne 
__tipc_nl_add_udp_addr(struct sk_buff * skb,struct udp_media_addr * addr,int nla_t)429fdb3acccSRichard Alpe static int __tipc_nl_add_udp_addr(struct sk_buff *skb,
430fdb3acccSRichard Alpe 				  struct udp_media_addr *addr, int nla_t)
431fdb3acccSRichard Alpe {
432fdb3acccSRichard Alpe 	if (ntohs(addr->proto) == ETH_P_IP) {
433fdb3acccSRichard Alpe 		struct sockaddr_in ip4;
434fdb3acccSRichard Alpe 
43573076162SDan Carpenter 		memset(&ip4, 0, sizeof(ip4));
436fdb3acccSRichard Alpe 		ip4.sin_family = AF_INET;
437fdb3acccSRichard Alpe 		ip4.sin_port = addr->port;
438fdb3acccSRichard Alpe 		ip4.sin_addr.s_addr = addr->ipv4.s_addr;
439fdb3acccSRichard Alpe 		if (nla_put(skb, nla_t, sizeof(ip4), &ip4))
440fdb3acccSRichard Alpe 			return -EMSGSIZE;
441fdb3acccSRichard Alpe 
442fdb3acccSRichard Alpe #if IS_ENABLED(CONFIG_IPV6)
443fdb3acccSRichard Alpe 	} else if (ntohs(addr->proto) == ETH_P_IPV6) {
444fdb3acccSRichard Alpe 		struct sockaddr_in6 ip6;
445fdb3acccSRichard Alpe 
44673076162SDan Carpenter 		memset(&ip6, 0, sizeof(ip6));
447fdb3acccSRichard Alpe 		ip6.sin6_family = AF_INET6;
448fdb3acccSRichard Alpe 		ip6.sin6_port  = addr->port;
449fdb3acccSRichard Alpe 		memcpy(&ip6.sin6_addr, &addr->ipv6, sizeof(struct in6_addr));
450fdb3acccSRichard Alpe 		if (nla_put(skb, nla_t, sizeof(ip6), &ip6))
451fdb3acccSRichard Alpe 			return -EMSGSIZE;
452fdb3acccSRichard Alpe #endif
453fdb3acccSRichard Alpe 	}
454fdb3acccSRichard Alpe 
455fdb3acccSRichard Alpe 	return 0;
456fdb3acccSRichard Alpe }
457fdb3acccSRichard Alpe 
tipc_udp_nl_dump_remoteip(struct sk_buff * skb,struct netlink_callback * cb)458832629caSRichard Alpe int tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb)
459832629caSRichard Alpe {
460832629caSRichard Alpe 	u32 bid = cb->args[0];
461832629caSRichard Alpe 	u32 skip_cnt = cb->args[1];
462832629caSRichard Alpe 	u32 portid = NETLINK_CB(cb->skb).portid;
463832629caSRichard Alpe 	struct udp_replicast *rcast, *tmp;
464832629caSRichard Alpe 	struct tipc_bearer *b;
465832629caSRichard Alpe 	struct udp_bearer *ub;
466832629caSRichard Alpe 	void *hdr;
467832629caSRichard Alpe 	int err;
468832629caSRichard Alpe 	int i;
469832629caSRichard Alpe 
470832629caSRichard Alpe 	if (!bid && !skip_cnt) {
4717288dd2fSJakub Kicinski 		struct nlattr **attrs = genl_dumpit_info(cb)->info.attrs;
472832629caSRichard Alpe 		struct net *net = sock_net(skb->sk);
473832629caSRichard Alpe 		struct nlattr *battrs[TIPC_NLA_BEARER_MAX + 1];
474832629caSRichard Alpe 		char *bname;
475832629caSRichard Alpe 
476832629caSRichard Alpe 		if (!attrs[TIPC_NLA_BEARER])
477832629caSRichard Alpe 			return -EINVAL;
478832629caSRichard Alpe 
4798cb08174SJohannes Berg 		err = nla_parse_nested_deprecated(battrs, TIPC_NLA_BEARER_MAX,
480832629caSRichard Alpe 						  attrs[TIPC_NLA_BEARER],
481fceb6435SJohannes Berg 						  tipc_nl_bearer_policy, NULL);
482832629caSRichard Alpe 		if (err)
483832629caSRichard Alpe 			return err;
484832629caSRichard Alpe 
485832629caSRichard Alpe 		if (!battrs[TIPC_NLA_BEARER_NAME])
486832629caSRichard Alpe 			return -EINVAL;
487832629caSRichard Alpe 
488832629caSRichard Alpe 		bname = nla_data(battrs[TIPC_NLA_BEARER_NAME]);
489832629caSRichard Alpe 
490832629caSRichard Alpe 		rtnl_lock();
491832629caSRichard Alpe 		b = tipc_bearer_find(net, bname);
492832629caSRichard Alpe 		if (!b) {
493832629caSRichard Alpe 			rtnl_unlock();
494832629caSRichard Alpe 			return -EINVAL;
495832629caSRichard Alpe 		}
496832629caSRichard Alpe 		bid = b->identity;
497832629caSRichard Alpe 	} else {
498832629caSRichard Alpe 		struct net *net = sock_net(skb->sk);
499832629caSRichard Alpe 		struct tipc_net *tn = net_generic(net, tipc_net_id);
500832629caSRichard Alpe 
501832629caSRichard Alpe 		rtnl_lock();
502832629caSRichard Alpe 		b = rtnl_dereference(tn->bearer_list[bid]);
503832629caSRichard Alpe 		if (!b) {
504832629caSRichard Alpe 			rtnl_unlock();
505832629caSRichard Alpe 			return -EINVAL;
506832629caSRichard Alpe 		}
507832629caSRichard Alpe 	}
508832629caSRichard Alpe 
50930a4616cSXin Long 	ub = rtnl_dereference(b->media_ptr);
510832629caSRichard Alpe 	if (!ub) {
511832629caSRichard Alpe 		rtnl_unlock();
512832629caSRichard Alpe 		return -EINVAL;
513832629caSRichard Alpe 	}
514832629caSRichard Alpe 
515832629caSRichard Alpe 	i = 0;
516832629caSRichard Alpe 	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
517832629caSRichard Alpe 		if (i < skip_cnt)
518832629caSRichard Alpe 			goto count;
519832629caSRichard Alpe 
520832629caSRichard Alpe 		hdr = genlmsg_put(skb, portid, cb->nlh->nlmsg_seq,
521832629caSRichard Alpe 				  &tipc_genl_family, NLM_F_MULTI,
522832629caSRichard Alpe 				  TIPC_NL_BEARER_GET);
523832629caSRichard Alpe 		if (!hdr)
524832629caSRichard Alpe 			goto done;
525832629caSRichard Alpe 
526832629caSRichard Alpe 		err = __tipc_nl_add_udp_addr(skb, &rcast->addr,
527832629caSRichard Alpe 					     TIPC_NLA_UDP_REMOTE);
528832629caSRichard Alpe 		if (err) {
529832629caSRichard Alpe 			genlmsg_cancel(skb, hdr);
530832629caSRichard Alpe 			goto done;
531832629caSRichard Alpe 		}
532832629caSRichard Alpe 		genlmsg_end(skb, hdr);
533832629caSRichard Alpe count:
534832629caSRichard Alpe 		i++;
535832629caSRichard Alpe 	}
536832629caSRichard Alpe done:
537832629caSRichard Alpe 	rtnl_unlock();
538832629caSRichard Alpe 	cb->args[0] = bid;
539832629caSRichard Alpe 	cb->args[1] = i;
540832629caSRichard Alpe 
541832629caSRichard Alpe 	return skb->len;
542832629caSRichard Alpe }
543832629caSRichard Alpe 
tipc_udp_nl_add_bearer_data(struct tipc_nl_msg * msg,struct tipc_bearer * b)544fdb3acccSRichard Alpe int tipc_udp_nl_add_bearer_data(struct tipc_nl_msg *msg, struct tipc_bearer *b)
545fdb3acccSRichard Alpe {
546fdb3acccSRichard Alpe 	struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value;
547fdb3acccSRichard Alpe 	struct udp_media_addr *dst;
548fdb3acccSRichard Alpe 	struct udp_bearer *ub;
549fdb3acccSRichard Alpe 	struct nlattr *nest;
550fdb3acccSRichard Alpe 
55130a4616cSXin Long 	ub = rtnl_dereference(b->media_ptr);
552fdb3acccSRichard Alpe 	if (!ub)
553fdb3acccSRichard Alpe 		return -ENODEV;
554fdb3acccSRichard Alpe 
555ae0be8deSMichal Kubecek 	nest = nla_nest_start_noflag(msg->skb, TIPC_NLA_BEARER_UDP_OPTS);
556fdb3acccSRichard Alpe 	if (!nest)
557fdb3acccSRichard Alpe 		goto msg_full;
558fdb3acccSRichard Alpe 
559fdb3acccSRichard Alpe 	if (__tipc_nl_add_udp_addr(msg->skb, src, TIPC_NLA_UDP_LOCAL))
560fdb3acccSRichard Alpe 		goto msg_full;
561fdb3acccSRichard Alpe 
562fdb3acccSRichard Alpe 	dst = (struct udp_media_addr *)&b->bcast_addr.value;
563fdb3acccSRichard Alpe 	if (__tipc_nl_add_udp_addr(msg->skb, dst, TIPC_NLA_UDP_REMOTE))
564fdb3acccSRichard Alpe 		goto msg_full;
565fdb3acccSRichard Alpe 
566fdb3acccSRichard Alpe 	if (!list_empty(&ub->rcast.list)) {
567fdb3acccSRichard Alpe 		if (nla_put_flag(msg->skb, TIPC_NLA_UDP_MULTI_REMOTEIP))
568fdb3acccSRichard Alpe 			goto msg_full;
569fdb3acccSRichard Alpe 	}
570fdb3acccSRichard Alpe 
571fdb3acccSRichard Alpe 	nla_nest_end(msg->skb, nest);
572fdb3acccSRichard Alpe 	return 0;
573fdb3acccSRichard Alpe msg_full:
574fdb3acccSRichard Alpe 	nla_nest_cancel(msg->skb, nest);
575fdb3acccSRichard Alpe 	return -EMSGSIZE;
576fdb3acccSRichard Alpe }
577fdb3acccSRichard Alpe 
578d0f91938SErik Hugne /**
579ba5aa84aSRichard Alpe  * tipc_parse_udp_addr - build udp media address from netlink data
580d8141208SAndrew Lunn  * @nla:	netlink attribute containing sockaddr storage aligned address
581ba5aa84aSRichard Alpe  * @addr:	tipc media address to fill with address, port and protocol type
582ba5aa84aSRichard Alpe  * @scope_id:	IPv6 scope id pointer, not NULL indicates it's required
583d0f91938SErik Hugne  */
584ba5aa84aSRichard Alpe 
tipc_parse_udp_addr(struct nlattr * nla,struct udp_media_addr * addr,u32 * scope_id)585ba5aa84aSRichard Alpe static int tipc_parse_udp_addr(struct nlattr *nla, struct udp_media_addr *addr,
586ba5aa84aSRichard Alpe 			       u32 *scope_id)
587d0f91938SErik Hugne {
588ba5aa84aSRichard Alpe 	struct sockaddr_storage sa;
589d0f91938SErik Hugne 
590ba5aa84aSRichard Alpe 	nla_memcpy(&sa, nla, sizeof(sa));
591ba5aa84aSRichard Alpe 	if (sa.ss_family == AF_INET) {
592ba5aa84aSRichard Alpe 		struct sockaddr_in *ip4 = (struct sockaddr_in *)&sa;
593d0f91938SErik Hugne 
594ba5aa84aSRichard Alpe 		addr->proto = htons(ETH_P_IP);
595ba5aa84aSRichard Alpe 		addr->port = ip4->sin_port;
596ba5aa84aSRichard Alpe 		addr->ipv4.s_addr = ip4->sin_addr.s_addr;
597d0f91938SErik Hugne 		return 0;
598d0f91938SErik Hugne 
599d0f91938SErik Hugne #if IS_ENABLED(CONFIG_IPV6)
600ba5aa84aSRichard Alpe 	} else if (sa.ss_family == AF_INET6) {
601ba5aa84aSRichard Alpe 		struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)&sa;
602ba5aa84aSRichard Alpe 
603ba5aa84aSRichard Alpe 		addr->proto = htons(ETH_P_IPV6);
604ba5aa84aSRichard Alpe 		addr->port = ip6->sin6_port;
605ba5aa84aSRichard Alpe 		memcpy(&addr->ipv6, &ip6->sin6_addr, sizeof(struct in6_addr));
606ba5aa84aSRichard Alpe 
607ba5aa84aSRichard Alpe 		/* Scope ID is only interesting for local addresses */
608ba5aa84aSRichard Alpe 		if (scope_id) {
60934f65dbbSRichard Alpe 			int atype;
610d0f91938SErik Hugne 
61134f65dbbSRichard Alpe 			atype = ipv6_addr_type(&ip6->sin6_addr);
612ba5aa84aSRichard Alpe 			if (__ipv6_addr_needs_scope_id(atype) &&
613ba5aa84aSRichard Alpe 			    !ip6->sin6_scope_id) {
61434f65dbbSRichard Alpe 				return -EINVAL;
615ba5aa84aSRichard Alpe 			}
61634f65dbbSRichard Alpe 
617ba5aa84aSRichard Alpe 			*scope_id = ip6->sin6_scope_id ? : 0;
618ba5aa84aSRichard Alpe 		}
619d0f91938SErik Hugne 
620d0f91938SErik Hugne 		return 0;
621d0f91938SErik Hugne #endif
622d0f91938SErik Hugne 	}
623d0f91938SErik Hugne 	return -EADDRNOTAVAIL;
624d0f91938SErik Hugne }
625d0f91938SErik Hugne 
tipc_udp_nl_bearer_add(struct tipc_bearer * b,struct nlattr * attr)626ef20cd4dSRichard Alpe int tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr)
627ef20cd4dSRichard Alpe {
628ef20cd4dSRichard Alpe 	int err;
629ef20cd4dSRichard Alpe 	struct udp_media_addr addr = {0};
630ef20cd4dSRichard Alpe 	struct nlattr *opts[TIPC_NLA_UDP_MAX + 1];
631ef20cd4dSRichard Alpe 	struct udp_media_addr *dst;
632ef20cd4dSRichard Alpe 
6338cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(opts, TIPC_NLA_UDP_MAX, attr, tipc_nl_udp_policy, NULL))
634ef20cd4dSRichard Alpe 		return -EINVAL;
635ef20cd4dSRichard Alpe 
636ef20cd4dSRichard Alpe 	if (!opts[TIPC_NLA_UDP_REMOTE])
637ef20cd4dSRichard Alpe 		return -EINVAL;
638ef20cd4dSRichard Alpe 
639ef20cd4dSRichard Alpe 	err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_REMOTE], &addr, NULL);
640ef20cd4dSRichard Alpe 	if (err)
641ef20cd4dSRichard Alpe 		return err;
642ef20cd4dSRichard Alpe 
643ef20cd4dSRichard Alpe 	dst = (struct udp_media_addr *)&b->bcast_addr.value;
644ef20cd4dSRichard Alpe 	if (tipc_udp_is_mcast_addr(dst)) {
645ef20cd4dSRichard Alpe 		pr_err("Can't add remote ip to TIPC UDP multicast bearer\n");
646ef20cd4dSRichard Alpe 		return -EINVAL;
647ef20cd4dSRichard Alpe 	}
648ef20cd4dSRichard Alpe 
649c9b64d49SRichard Alpe 	if (tipc_udp_is_known_peer(b, &addr))
650c9b64d49SRichard Alpe 		return 0;
651c9b64d49SRichard Alpe 
652ef20cd4dSRichard Alpe 	return tipc_udp_rcast_add(b, &addr);
653ef20cd4dSRichard Alpe }
654ef20cd4dSRichard Alpe 
655d0f91938SErik Hugne /**
656d0f91938SErik Hugne  * tipc_udp_enable - callback to create a new udp bearer instance
657d0f91938SErik Hugne  * @net:	network namespace
658d0f91938SErik Hugne  * @b:		pointer to generic tipc_bearer
659d0f91938SErik Hugne  * @attrs:	netlink bearer configuration
660d0f91938SErik Hugne  *
661d0f91938SErik Hugne  * validate the bearer parameters and initialize the udp bearer
662d0f91938SErik Hugne  * rtnl_lock should be held
663d0f91938SErik Hugne  */
tipc_udp_enable(struct net * net,struct tipc_bearer * b,struct nlattr * attrs[])664d0f91938SErik Hugne static int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
665d0f91938SErik Hugne 			   struct nlattr *attrs[])
666d0f91938SErik Hugne {
667d0f91938SErik Hugne 	int err = -EINVAL;
668d0f91938SErik Hugne 	struct udp_bearer *ub;
669ef20cd4dSRichard Alpe 	struct udp_media_addr remote = {0};
670d0f91938SErik Hugne 	struct udp_media_addr local = {0};
671d0f91938SErik Hugne 	struct udp_port_cfg udp_conf = {0};
6724fee6be8SErik Hugne 	struct udp_tunnel_sock_cfg tuncfg = {NULL};
673ba5aa84aSRichard Alpe 	struct nlattr *opts[TIPC_NLA_UDP_MAX + 1];
67452dfae5cSJon Maloy 	u8 node_id[NODE_ID_LEN] = {0,};
6754ef1a7cbSXin Long 	struct net_device *dev;
676acad76a5SHoang Le 	int rmcast = 0;
677d0f91938SErik Hugne 
678d0f91938SErik Hugne 	ub = kzalloc(sizeof(*ub), GFP_ATOMIC);
679d0f91938SErik Hugne 	if (!ub)
680d0f91938SErik Hugne 		return -ENOMEM;
681d0f91938SErik Hugne 
682ef20cd4dSRichard Alpe 	INIT_LIST_HEAD(&ub->rcast.list);
683ef20cd4dSRichard Alpe 
684ba5aa84aSRichard Alpe 	if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
685ba5aa84aSRichard Alpe 		goto err;
686ba5aa84aSRichard Alpe 
6878cb08174SJohannes Berg 	if (nla_parse_nested_deprecated(opts, TIPC_NLA_UDP_MAX, attrs[TIPC_NLA_BEARER_UDP_OPTS], tipc_nl_udp_policy, NULL))
688ba5aa84aSRichard Alpe 		goto err;
689ba5aa84aSRichard Alpe 
690ba5aa84aSRichard Alpe 	if (!opts[TIPC_NLA_UDP_LOCAL] || !opts[TIPC_NLA_UDP_REMOTE]) {
691ba5aa84aSRichard Alpe 		pr_err("Invalid UDP bearer configuration");
692c20cb811SWei Yongjun 		err = -EINVAL;
693c20cb811SWei Yongjun 		goto err;
694ba5aa84aSRichard Alpe 	}
695ba5aa84aSRichard Alpe 
696ba5aa84aSRichard Alpe 	err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_LOCAL], &local,
697ba5aa84aSRichard Alpe 				  &ub->ifindex);
698ba5aa84aSRichard Alpe 	if (err)
699ba5aa84aSRichard Alpe 		goto err;
700ba5aa84aSRichard Alpe 
701ef20cd4dSRichard Alpe 	err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_REMOTE], &remote, NULL);
702d0f91938SErik Hugne 	if (err)
703d0f91938SErik Hugne 		goto err;
704d0f91938SErik Hugne 
705fb83ed49SCong Wang 	if (remote.proto != local.proto) {
706fb83ed49SCong Wang 		err = -EINVAL;
707fb83ed49SCong Wang 		goto err;
708fb83ed49SCong Wang 	}
709fb83ed49SCong Wang 
710acad76a5SHoang Le 	/* Checking remote ip address */
711acad76a5SHoang Le 	rmcast = tipc_udp_is_mcast_addr(&remote);
712acad76a5SHoang Le 
71352dfae5cSJon Maloy 	/* Autoconfigure own node identity if needed */
71452dfae5cSJon Maloy 	if (!tipc_own_id(net)) {
71552dfae5cSJon Maloy 		memcpy(node_id, local.ipv6.in6_u.u6_addr8, 16);
71652dfae5cSJon Maloy 		tipc_net_init(net, node_id, 0);
71752dfae5cSJon Maloy 	}
71852dfae5cSJon Maloy 	if (!tipc_own_id(net)) {
71952dfae5cSJon Maloy 		pr_warn("Failed to set node id, please configure manually\n");
720c76f2481SWei Yongjun 		err = -EINVAL;
721c76f2481SWei Yongjun 		goto err;
72252dfae5cSJon Maloy 	}
72352dfae5cSJon Maloy 
724d0f91938SErik Hugne 	b->bcast_addr.media_id = TIPC_MEDIA_TYPE_UDP;
7259999974aSJon Paul Maloy 	b->bcast_addr.broadcast = TIPC_BROADCAST_SUPPORT;
726d0f91938SErik Hugne 	rcu_assign_pointer(b->media_ptr, ub);
727d0f91938SErik Hugne 	rcu_assign_pointer(ub->bearer, b);
728d0f91938SErik Hugne 	tipc_udp_media_addr_set(&b->addr, &local);
7294fee6be8SErik Hugne 	if (local.proto == htons(ETH_P_IP)) {
730d0f91938SErik Hugne 		dev = __ip_dev_find(net, local.ipv4.s_addr, false);
731d0f91938SErik Hugne 		if (!dev) {
732d0f91938SErik Hugne 			err = -ENODEV;
733d0f91938SErik Hugne 			goto err;
734d0f91938SErik Hugne 		}
735d0f91938SErik Hugne 		udp_conf.family = AF_INET;
736acad76a5SHoang Le 
737acad76a5SHoang Le 		/* Switch to use ANY to receive packets from group */
738acad76a5SHoang Le 		if (rmcast)
739d0f91938SErik Hugne 			udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
740acad76a5SHoang Le 		else
741acad76a5SHoang Le 			udp_conf.local_ip.s_addr = local.ipv4.s_addr;
742d0f91938SErik Hugne 		udp_conf.use_udp_checksums = false;
743d0f91938SErik Hugne 		ub->ifindex = dev->ifindex;
7443ae6d66bSXin Long 		b->encap_hlen = sizeof(struct iphdr) + sizeof(struct udphdr);
745a4dfa72dSGhantaKrishnamurthy MohanKrishna 		b->mtu = b->media->mtu;
746d0f91938SErik Hugne #if IS_ENABLED(CONFIG_IPV6)
7474fee6be8SErik Hugne 	} else if (local.proto == htons(ETH_P_IPV6)) {
7484ef1a7cbSXin Long 		dev = ub->ifindex ? __dev_get_by_index(net, ub->ifindex) : NULL;
7494ef1a7cbSXin Long 		dev = ipv6_dev_find(net, &local.ipv6, dev);
7505a6f6f57SXin Long 		if (!dev) {
7515a6f6f57SXin Long 			err = -ENODEV;
7525a6f6f57SXin Long 			goto err;
7535a6f6f57SXin Long 		}
754d0f91938SErik Hugne 		udp_conf.family = AF_INET6;
755d0f91938SErik Hugne 		udp_conf.use_udp6_tx_checksums = true;
756d0f91938SErik Hugne 		udp_conf.use_udp6_rx_checksums = true;
757acad76a5SHoang Le 		if (rmcast)
758d0f91938SErik Hugne 			udp_conf.local_ip6 = in6addr_any;
759acad76a5SHoang Le 		else
760acad76a5SHoang Le 			udp_conf.local_ip6 = local.ipv6;
7615a6f6f57SXin Long 		ub->ifindex = dev->ifindex;
7623ae6d66bSXin Long 		b->encap_hlen = sizeof(struct ipv6hdr) + sizeof(struct udphdr);
763d0f91938SErik Hugne 		b->mtu = 1280;
764d0f91938SErik Hugne #endif
765d0f91938SErik Hugne 	} else {
766d0f91938SErik Hugne 		err = -EAFNOSUPPORT;
767d0f91938SErik Hugne 		goto err;
768d0f91938SErik Hugne 	}
769bc3a334cSRichard Alpe 	udp_conf.local_udp_port = local.port;
770d0f91938SErik Hugne 	err = udp_sock_create(net, &udp_conf, &ub->ubsock);
771d0f91938SErik Hugne 	if (err)
772d0f91938SErik Hugne 		goto err;
773d0f91938SErik Hugne 	tuncfg.sk_user_data = ub;
774d0f91938SErik Hugne 	tuncfg.encap_type = 1;
775d0f91938SErik Hugne 	tuncfg.encap_rcv = tipc_udp_recv;
776d0f91938SErik Hugne 	tuncfg.encap_destroy = NULL;
777d0f91938SErik Hugne 	setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg);
778d0f91938SErik Hugne 
779e9c1a793SXin Long 	err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC);
780e9c1a793SXin Long 	if (err)
781d2c3a4baSXin Long 		goto free;
782e9c1a793SXin Long 
7835fcb7d47SRandy Dunlap 	/*
784ef20cd4dSRichard Alpe 	 * The bcast media address port is used for all peers and the ip
785ef20cd4dSRichard Alpe 	 * is used if it's a multicast address.
786ef20cd4dSRichard Alpe 	 */
787ef20cd4dSRichard Alpe 	memcpy(&b->bcast_addr.value, &remote, sizeof(remote));
788acad76a5SHoang Le 	if (rmcast)
789ef20cd4dSRichard Alpe 		err = enable_mcast(ub, &remote);
790ef20cd4dSRichard Alpe 	else
791ef20cd4dSRichard Alpe 		err = tipc_udp_rcast_add(b, &remote);
792ef20cd4dSRichard Alpe 	if (err)
793d2c3a4baSXin Long 		goto free;
7941ca73e3fSRichard Alpe 
795d0f91938SErik Hugne 	return 0;
796d2c3a4baSXin Long 
797d2c3a4baSXin Long free:
798e9c1a793SXin Long 	dst_cache_destroy(&ub->rcast.dst_cache);
799a5de125dSWei Yongjun 	udp_tunnel_sock_release(ub->ubsock);
800d2c3a4baSXin Long err:
801d0f91938SErik Hugne 	kfree(ub);
802d0f91938SErik Hugne 	return err;
803d0f91938SErik Hugne }
804d0f91938SErik Hugne 
805d0f91938SErik Hugne /* cleanup_bearer - break the socket/bearer association */
cleanup_bearer(struct work_struct * work)806d0f91938SErik Hugne static void cleanup_bearer(struct work_struct *work)
807d0f91938SErik Hugne {
808d0f91938SErik Hugne 	struct udp_bearer *ub = container_of(work, struct udp_bearer, work);
809ef20cd4dSRichard Alpe 	struct udp_replicast *rcast, *tmp;
810*89ecda49SEric Dumazet 	struct tipc_net *tn;
811ef20cd4dSRichard Alpe 
812ef20cd4dSRichard Alpe 	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
813e9c1a793SXin Long 		dst_cache_destroy(&rcast->dst_cache);
814ef20cd4dSRichard Alpe 		list_del_rcu(&rcast->list);
815ef20cd4dSRichard Alpe 		kfree_rcu(rcast, rcu);
816ef20cd4dSRichard Alpe 	}
817d0f91938SErik Hugne 
818*89ecda49SEric Dumazet 	tn = tipc_net(sock_net(ub->ubsock->sk));
819*89ecda49SEric Dumazet 
820e9c1a793SXin Long 	dst_cache_destroy(&ub->rcast.dst_cache);
821d0f91938SErik Hugne 	udp_tunnel_sock_release(ub->ubsock);
822*89ecda49SEric Dumazet 
823*89ecda49SEric Dumazet 	/* Note: could use a call_rcu() to avoid another synchronize_net() */
824d0f91938SErik Hugne 	synchronize_net();
825*89ecda49SEric Dumazet 	atomic_dec(&tn->wq_count);
826d0f91938SErik Hugne 	kfree(ub);
827d0f91938SErik Hugne }
828d0f91938SErik Hugne 
829d0f91938SErik Hugne /* tipc_udp_disable - detach bearer from socket */
tipc_udp_disable(struct tipc_bearer * b)830d0f91938SErik Hugne static void tipc_udp_disable(struct tipc_bearer *b)
831d0f91938SErik Hugne {
832d0f91938SErik Hugne 	struct udp_bearer *ub;
833d0f91938SErik Hugne 
83430a4616cSXin Long 	ub = rtnl_dereference(b->media_ptr);
835d0f91938SErik Hugne 	if (!ub) {
836d0f91938SErik Hugne 		pr_err("UDP bearer instance not found\n");
837d0f91938SErik Hugne 		return;
838d0f91938SErik Hugne 	}
839d0f91938SErik Hugne 	sock_set_flag(ub->ubsock->sk, SOCK_DEAD);
840d0f91938SErik Hugne 	RCU_INIT_POINTER(ub->bearer, NULL);
841d0f91938SErik Hugne 
842d0f91938SErik Hugne 	/* sock_release need to be done outside of rtnl lock */
84304c26faaSXin Long 	atomic_inc(&tipc_net(sock_net(ub->ubsock->sk))->wq_count);
844d0f91938SErik Hugne 	INIT_WORK(&ub->work, cleanup_bearer);
845d0f91938SErik Hugne 	schedule_work(&ub->work);
846d0f91938SErik Hugne }
847d0f91938SErik Hugne 
848d0f91938SErik Hugne struct tipc_media udp_media_info = {
849d0f91938SErik Hugne 	.send_msg	= tipc_udp_send_msg,
850d0f91938SErik Hugne 	.enable_media	= tipc_udp_enable,
851d0f91938SErik Hugne 	.disable_media	= tipc_udp_disable,
852d0f91938SErik Hugne 	.addr2str	= tipc_udp_addr2str,
853d0f91938SErik Hugne 	.addr2msg	= tipc_udp_addr2msg,
854d0f91938SErik Hugne 	.msg2addr	= tipc_udp_msg2addr,
855d0f91938SErik Hugne 	.priority	= TIPC_DEF_LINK_PRI,
856d0f91938SErik Hugne 	.tolerance	= TIPC_DEF_LINK_TOL,
85716ad3f40SJon Maloy 	.min_win	= TIPC_DEF_LINK_WIN,
85816ad3f40SJon Maloy 	.max_win	= TIPC_DEF_LINK_WIN,
859a4dfa72dSGhantaKrishnamurthy MohanKrishna 	.mtu		= TIPC_DEF_LINK_UDP_MTU,
860d0f91938SErik Hugne 	.type_id	= TIPC_MEDIA_TYPE_UDP,
861d0f91938SErik Hugne 	.hwaddr_len	= 0,
862d0f91938SErik Hugne 	.name		= "udp"
863d0f91938SErik Hugne };
864