162306a36Sopenharmony_ci/* net/tipc/udp_media.c: IP bearer support for TIPC
262306a36Sopenharmony_ci *
362306a36Sopenharmony_ci * Copyright (c) 2015, Ericsson AB
462306a36Sopenharmony_ci * All rights reserved.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without
762306a36Sopenharmony_ci * modification, are permitted provided that the following conditions are met:
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * 1. Redistributions of source code must retain the above copyright
1062306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer.
1162306a36Sopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
1262306a36Sopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
1362306a36Sopenharmony_ci *    documentation and/or other materials provided with the distribution.
1462306a36Sopenharmony_ci * 3. Neither the names of the copyright holders nor the names of its
1562306a36Sopenharmony_ci *    contributors may be used to endorse or promote products derived from
1662306a36Sopenharmony_ci *    this software without specific prior written permission.
1762306a36Sopenharmony_ci *
1862306a36Sopenharmony_ci * Alternatively, this software may be distributed under the terms of the
1962306a36Sopenharmony_ci * GNU General Public License ("GPL") version 2 as published by the Free
2062306a36Sopenharmony_ci * Software Foundation.
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
2362306a36Sopenharmony_ci * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2462306a36Sopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2562306a36Sopenharmony_ci * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
2662306a36Sopenharmony_ci * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2762306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2862306a36Sopenharmony_ci * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2962306a36Sopenharmony_ci * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
3062306a36Sopenharmony_ci * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
3162306a36Sopenharmony_ci * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
3262306a36Sopenharmony_ci * POSSIBILITY OF SUCH DAMAGE.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci#include <linux/socket.h>
3662306a36Sopenharmony_ci#include <linux/ip.h>
3762306a36Sopenharmony_ci#include <linux/udp.h>
3862306a36Sopenharmony_ci#include <linux/inet.h>
3962306a36Sopenharmony_ci#include <linux/inetdevice.h>
4062306a36Sopenharmony_ci#include <linux/igmp.h>
4162306a36Sopenharmony_ci#include <linux/kernel.h>
4262306a36Sopenharmony_ci#include <linux/workqueue.h>
4362306a36Sopenharmony_ci#include <linux/list.h>
4462306a36Sopenharmony_ci#include <net/sock.h>
4562306a36Sopenharmony_ci#include <net/ip.h>
4662306a36Sopenharmony_ci#include <net/udp_tunnel.h>
4762306a36Sopenharmony_ci#include <net/ipv6_stubs.h>
4862306a36Sopenharmony_ci#include <linux/tipc_netlink.h>
4962306a36Sopenharmony_ci#include "core.h"
5062306a36Sopenharmony_ci#include "addr.h"
5162306a36Sopenharmony_ci#include "net.h"
5262306a36Sopenharmony_ci#include "bearer.h"
5362306a36Sopenharmony_ci#include "netlink.h"
5462306a36Sopenharmony_ci#include "msg.h"
5562306a36Sopenharmony_ci#include "udp_media.h"
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci/* IANA assigned UDP port */
5862306a36Sopenharmony_ci#define UDP_PORT_DEFAULT	6118
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci#define UDP_MIN_HEADROOM        48
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * struct udp_media_addr - IP/UDP addressing information
6462306a36Sopenharmony_ci *
6562306a36Sopenharmony_ci * This is the bearer level originating address used in neighbor discovery
6662306a36Sopenharmony_ci * messages, and all fields should be in network byte order
6762306a36Sopenharmony_ci *
6862306a36Sopenharmony_ci * @proto: Ethernet protocol in use
6962306a36Sopenharmony_ci * @port: port being used
7062306a36Sopenharmony_ci * @ipv4: IPv4 address of neighbor
7162306a36Sopenharmony_ci * @ipv6: IPv6 address of neighbor
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistruct udp_media_addr {
7462306a36Sopenharmony_ci	__be16	proto;
7562306a36Sopenharmony_ci	__be16	port;
7662306a36Sopenharmony_ci	union {
7762306a36Sopenharmony_ci		struct in_addr ipv4;
7862306a36Sopenharmony_ci		struct in6_addr ipv6;
7962306a36Sopenharmony_ci	};
8062306a36Sopenharmony_ci};
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci/* struct udp_replicast - container for UDP remote addresses */
8362306a36Sopenharmony_cistruct udp_replicast {
8462306a36Sopenharmony_ci	struct udp_media_addr addr;
8562306a36Sopenharmony_ci	struct dst_cache dst_cache;
8662306a36Sopenharmony_ci	struct rcu_head rcu;
8762306a36Sopenharmony_ci	struct list_head list;
8862306a36Sopenharmony_ci};
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/**
9162306a36Sopenharmony_ci * struct udp_bearer - ip/udp bearer data structure
9262306a36Sopenharmony_ci * @bearer:	associated generic tipc bearer
9362306a36Sopenharmony_ci * @ubsock:	bearer associated socket
9462306a36Sopenharmony_ci * @ifindex:	local address scope
9562306a36Sopenharmony_ci * @work:	used to schedule deferred work on a bearer
9662306a36Sopenharmony_ci * @rcast:	associated udp_replicast container
9762306a36Sopenharmony_ci */
9862306a36Sopenharmony_cistruct udp_bearer {
9962306a36Sopenharmony_ci	struct tipc_bearer __rcu *bearer;
10062306a36Sopenharmony_ci	struct socket *ubsock;
10162306a36Sopenharmony_ci	u32 ifindex;
10262306a36Sopenharmony_ci	struct work_struct work;
10362306a36Sopenharmony_ci	struct udp_replicast rcast;
10462306a36Sopenharmony_ci};
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic int tipc_udp_is_mcast_addr(struct udp_media_addr *addr)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	if (ntohs(addr->proto) == ETH_P_IP)
10962306a36Sopenharmony_ci		return ipv4_is_multicast(addr->ipv4.s_addr);
11062306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
11162306a36Sopenharmony_ci	else
11262306a36Sopenharmony_ci		return ipv6_addr_is_multicast(&addr->ipv6);
11362306a36Sopenharmony_ci#endif
11462306a36Sopenharmony_ci	return 0;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/* udp_media_addr_set - convert a ip/udp address to a TIPC media address */
11862306a36Sopenharmony_cistatic void tipc_udp_media_addr_set(struct tipc_media_addr *addr,
11962306a36Sopenharmony_ci				    struct udp_media_addr *ua)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	memset(addr, 0, sizeof(struct tipc_media_addr));
12262306a36Sopenharmony_ci	addr->media_id = TIPC_MEDIA_TYPE_UDP;
12362306a36Sopenharmony_ci	memcpy(addr->value, ua, sizeof(struct udp_media_addr));
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	if (tipc_udp_is_mcast_addr(ua))
12662306a36Sopenharmony_ci		addr->broadcast = TIPC_BROADCAST_SUPPORT;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci/* tipc_udp_addr2str - convert ip/udp address to string */
13062306a36Sopenharmony_cistatic int tipc_udp_addr2str(struct tipc_media_addr *a, char *buf, int size)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	struct udp_media_addr *ua = (struct udp_media_addr *)&a->value;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	if (ntohs(ua->proto) == ETH_P_IP)
13562306a36Sopenharmony_ci		snprintf(buf, size, "%pI4:%u", &ua->ipv4, ntohs(ua->port));
13662306a36Sopenharmony_ci	else if (ntohs(ua->proto) == ETH_P_IPV6)
13762306a36Sopenharmony_ci		snprintf(buf, size, "%pI6:%u", &ua->ipv6, ntohs(ua->port));
13862306a36Sopenharmony_ci	else
13962306a36Sopenharmony_ci		pr_err("Invalid UDP media address\n");
14062306a36Sopenharmony_ci	return 0;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/* tipc_udp_msg2addr - extract an ip/udp address from a TIPC ndisc message */
14462306a36Sopenharmony_cistatic int tipc_udp_msg2addr(struct tipc_bearer *b, struct tipc_media_addr *a,
14562306a36Sopenharmony_ci			     char *msg)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct udp_media_addr *ua;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	ua = (struct udp_media_addr *) (msg + TIPC_MEDIA_ADDR_OFFSET);
15062306a36Sopenharmony_ci	if (msg[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_UDP)
15162306a36Sopenharmony_ci		return -EINVAL;
15262306a36Sopenharmony_ci	tipc_udp_media_addr_set(a, ua);
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ci}
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci/* tipc_udp_addr2msg - write an ip/udp address to a TIPC ndisc message */
15762306a36Sopenharmony_cistatic int tipc_udp_addr2msg(char *msg, struct tipc_media_addr *a)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	memset(msg, 0, TIPC_MEDIA_INFO_SIZE);
16062306a36Sopenharmony_ci	msg[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_UDP;
16162306a36Sopenharmony_ci	memcpy(msg + TIPC_MEDIA_ADDR_OFFSET, a->value,
16262306a36Sopenharmony_ci	       sizeof(struct udp_media_addr));
16362306a36Sopenharmony_ci	return 0;
16462306a36Sopenharmony_ci}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci/* tipc_send_msg - enqueue a send request */
16762306a36Sopenharmony_cistatic int tipc_udp_xmit(struct net *net, struct sk_buff *skb,
16862306a36Sopenharmony_ci			 struct udp_bearer *ub, struct udp_media_addr *src,
16962306a36Sopenharmony_ci			 struct udp_media_addr *dst, struct dst_cache *cache)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	struct dst_entry *ndst;
17262306a36Sopenharmony_ci	int ttl, err = 0;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	local_bh_disable();
17562306a36Sopenharmony_ci	ndst = dst_cache_get(cache);
17662306a36Sopenharmony_ci	if (dst->proto == htons(ETH_P_IP)) {
17762306a36Sopenharmony_ci		struct rtable *rt = (struct rtable *)ndst;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		if (!rt) {
18062306a36Sopenharmony_ci			struct flowi4 fl = {
18162306a36Sopenharmony_ci				.daddr = dst->ipv4.s_addr,
18262306a36Sopenharmony_ci				.saddr = src->ipv4.s_addr,
18362306a36Sopenharmony_ci				.flowi4_mark = skb->mark,
18462306a36Sopenharmony_ci				.flowi4_proto = IPPROTO_UDP
18562306a36Sopenharmony_ci			};
18662306a36Sopenharmony_ci			rt = ip_route_output_key(net, &fl);
18762306a36Sopenharmony_ci			if (IS_ERR(rt)) {
18862306a36Sopenharmony_ci				err = PTR_ERR(rt);
18962306a36Sopenharmony_ci				goto tx_error;
19062306a36Sopenharmony_ci			}
19162306a36Sopenharmony_ci			dst_cache_set_ip4(cache, &rt->dst, fl.saddr);
19262306a36Sopenharmony_ci		}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		ttl = ip4_dst_hoplimit(&rt->dst);
19562306a36Sopenharmony_ci		udp_tunnel_xmit_skb(rt, ub->ubsock->sk, skb, src->ipv4.s_addr,
19662306a36Sopenharmony_ci				    dst->ipv4.s_addr, 0, ttl, 0, src->port,
19762306a36Sopenharmony_ci				    dst->port, false, true);
19862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
19962306a36Sopenharmony_ci	} else {
20062306a36Sopenharmony_ci		if (!ndst) {
20162306a36Sopenharmony_ci			struct flowi6 fl6 = {
20262306a36Sopenharmony_ci				.flowi6_oif = ub->ifindex,
20362306a36Sopenharmony_ci				.daddr = dst->ipv6,
20462306a36Sopenharmony_ci				.saddr = src->ipv6,
20562306a36Sopenharmony_ci				.flowi6_proto = IPPROTO_UDP
20662306a36Sopenharmony_ci			};
20762306a36Sopenharmony_ci			ndst = ipv6_stub->ipv6_dst_lookup_flow(net,
20862306a36Sopenharmony_ci							       ub->ubsock->sk,
20962306a36Sopenharmony_ci							       &fl6, NULL);
21062306a36Sopenharmony_ci			if (IS_ERR(ndst)) {
21162306a36Sopenharmony_ci				err = PTR_ERR(ndst);
21262306a36Sopenharmony_ci				goto tx_error;
21362306a36Sopenharmony_ci			}
21462306a36Sopenharmony_ci			dst_cache_set_ip6(cache, ndst, &fl6.saddr);
21562306a36Sopenharmony_ci		}
21662306a36Sopenharmony_ci		ttl = ip6_dst_hoplimit(ndst);
21762306a36Sopenharmony_ci		err = udp_tunnel6_xmit_skb(ndst, ub->ubsock->sk, skb, NULL,
21862306a36Sopenharmony_ci					   &src->ipv6, &dst->ipv6, 0, ttl, 0,
21962306a36Sopenharmony_ci					   src->port, dst->port, false);
22062306a36Sopenharmony_ci#endif
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci	local_bh_enable();
22362306a36Sopenharmony_ci	return err;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_citx_error:
22662306a36Sopenharmony_ci	local_bh_enable();
22762306a36Sopenharmony_ci	kfree_skb(skb);
22862306a36Sopenharmony_ci	return err;
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_cistatic int tipc_udp_send_msg(struct net *net, struct sk_buff *skb,
23262306a36Sopenharmony_ci			     struct tipc_bearer *b,
23362306a36Sopenharmony_ci			     struct tipc_media_addr *addr)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value;
23662306a36Sopenharmony_ci	struct udp_media_addr *dst = (struct udp_media_addr *)&addr->value;
23762306a36Sopenharmony_ci	struct udp_replicast *rcast;
23862306a36Sopenharmony_ci	struct udp_bearer *ub;
23962306a36Sopenharmony_ci	int err = 0;
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (skb_headroom(skb) < UDP_MIN_HEADROOM) {
24262306a36Sopenharmony_ci		err = pskb_expand_head(skb, UDP_MIN_HEADROOM, 0, GFP_ATOMIC);
24362306a36Sopenharmony_ci		if (err)
24462306a36Sopenharmony_ci			goto out;
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	skb_set_inner_protocol(skb, htons(ETH_P_TIPC));
24862306a36Sopenharmony_ci	ub = rcu_dereference(b->media_ptr);
24962306a36Sopenharmony_ci	if (!ub) {
25062306a36Sopenharmony_ci		err = -ENODEV;
25162306a36Sopenharmony_ci		goto out;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	if (addr->broadcast != TIPC_REPLICAST_SUPPORT)
25562306a36Sopenharmony_ci		return tipc_udp_xmit(net, skb, ub, src, dst,
25662306a36Sopenharmony_ci				     &ub->rcast.dst_cache);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Replicast, send an skb to each configured IP address */
25962306a36Sopenharmony_ci	list_for_each_entry_rcu(rcast, &ub->rcast.list, list) {
26062306a36Sopenharmony_ci		struct sk_buff *_skb;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci		_skb = pskb_copy(skb, GFP_ATOMIC);
26362306a36Sopenharmony_ci		if (!_skb) {
26462306a36Sopenharmony_ci			err = -ENOMEM;
26562306a36Sopenharmony_ci			goto out;
26662306a36Sopenharmony_ci		}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		err = tipc_udp_xmit(net, _skb, ub, src, &rcast->addr,
26962306a36Sopenharmony_ci				    &rcast->dst_cache);
27062306a36Sopenharmony_ci		if (err)
27162306a36Sopenharmony_ci			goto out;
27262306a36Sopenharmony_ci	}
27362306a36Sopenharmony_ci	err = 0;
27462306a36Sopenharmony_ciout:
27562306a36Sopenharmony_ci	kfree_skb(skb);
27662306a36Sopenharmony_ci	return err;
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic bool tipc_udp_is_known_peer(struct tipc_bearer *b,
28062306a36Sopenharmony_ci				   struct udp_media_addr *addr)
28162306a36Sopenharmony_ci{
28262306a36Sopenharmony_ci	struct udp_replicast *rcast, *tmp;
28362306a36Sopenharmony_ci	struct udp_bearer *ub;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	ub = rcu_dereference_rtnl(b->media_ptr);
28662306a36Sopenharmony_ci	if (!ub) {
28762306a36Sopenharmony_ci		pr_err_ratelimited("UDP bearer instance not found\n");
28862306a36Sopenharmony_ci		return false;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
29262306a36Sopenharmony_ci		if (!memcmp(&rcast->addr, addr, sizeof(struct udp_media_addr)))
29362306a36Sopenharmony_ci			return true;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	return false;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic int tipc_udp_rcast_add(struct tipc_bearer *b,
30062306a36Sopenharmony_ci			      struct udp_media_addr *addr)
30162306a36Sopenharmony_ci{
30262306a36Sopenharmony_ci	struct udp_replicast *rcast;
30362306a36Sopenharmony_ci	struct udp_bearer *ub;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	ub = rcu_dereference_rtnl(b->media_ptr);
30662306a36Sopenharmony_ci	if (!ub)
30762306a36Sopenharmony_ci		return -ENODEV;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	rcast = kmalloc(sizeof(*rcast), GFP_ATOMIC);
31062306a36Sopenharmony_ci	if (!rcast)
31162306a36Sopenharmony_ci		return -ENOMEM;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (dst_cache_init(&rcast->dst_cache, GFP_ATOMIC)) {
31462306a36Sopenharmony_ci		kfree(rcast);
31562306a36Sopenharmony_ci		return -ENOMEM;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	memcpy(&rcast->addr, addr, sizeof(struct udp_media_addr));
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (ntohs(addr->proto) == ETH_P_IP)
32162306a36Sopenharmony_ci		pr_info("New replicast peer: %pI4\n", &rcast->addr.ipv4);
32262306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
32362306a36Sopenharmony_ci	else if (ntohs(addr->proto) == ETH_P_IPV6)
32462306a36Sopenharmony_ci		pr_info("New replicast peer: %pI6\n", &rcast->addr.ipv6);
32562306a36Sopenharmony_ci#endif
32662306a36Sopenharmony_ci	b->bcast_addr.broadcast = TIPC_REPLICAST_SUPPORT;
32762306a36Sopenharmony_ci	list_add_rcu(&rcast->list, &ub->rcast.list);
32862306a36Sopenharmony_ci	return 0;
32962306a36Sopenharmony_ci}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_cistatic int tipc_udp_rcast_disc(struct tipc_bearer *b, struct sk_buff *skb)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct udp_media_addr src = {0};
33462306a36Sopenharmony_ci	struct udp_media_addr *dst;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	dst = (struct udp_media_addr *)&b->bcast_addr.value;
33762306a36Sopenharmony_ci	if (tipc_udp_is_mcast_addr(dst))
33862306a36Sopenharmony_ci		return 0;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	src.port = udp_hdr(skb)->source;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	if (ip_hdr(skb)->version == 4) {
34362306a36Sopenharmony_ci		struct iphdr *iphdr = ip_hdr(skb);
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci		src.proto = htons(ETH_P_IP);
34662306a36Sopenharmony_ci		src.ipv4.s_addr = iphdr->saddr;
34762306a36Sopenharmony_ci		if (ipv4_is_multicast(iphdr->daddr))
34862306a36Sopenharmony_ci			return 0;
34962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
35062306a36Sopenharmony_ci	} else if (ip_hdr(skb)->version == 6) {
35162306a36Sopenharmony_ci		struct ipv6hdr *iphdr = ipv6_hdr(skb);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci		src.proto = htons(ETH_P_IPV6);
35462306a36Sopenharmony_ci		src.ipv6 = iphdr->saddr;
35562306a36Sopenharmony_ci		if (ipv6_addr_is_multicast(&iphdr->daddr))
35662306a36Sopenharmony_ci			return 0;
35762306a36Sopenharmony_ci#endif
35862306a36Sopenharmony_ci	} else {
35962306a36Sopenharmony_ci		return 0;
36062306a36Sopenharmony_ci	}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	if (likely(tipc_udp_is_known_peer(b, &src)))
36362306a36Sopenharmony_ci		return 0;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	return tipc_udp_rcast_add(b, &src);
36662306a36Sopenharmony_ci}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/* tipc_udp_recv - read data from bearer socket */
36962306a36Sopenharmony_cistatic int tipc_udp_recv(struct sock *sk, struct sk_buff *skb)
37062306a36Sopenharmony_ci{
37162306a36Sopenharmony_ci	struct udp_bearer *ub;
37262306a36Sopenharmony_ci	struct tipc_bearer *b;
37362306a36Sopenharmony_ci	struct tipc_msg *hdr;
37462306a36Sopenharmony_ci	int err;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	ub = rcu_dereference_sk_user_data(sk);
37762306a36Sopenharmony_ci	if (!ub) {
37862306a36Sopenharmony_ci		pr_err_ratelimited("Failed to get UDP bearer reference");
37962306a36Sopenharmony_ci		goto out;
38062306a36Sopenharmony_ci	}
38162306a36Sopenharmony_ci	skb_pull(skb, sizeof(struct udphdr));
38262306a36Sopenharmony_ci	hdr = buf_msg(skb);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	b = rcu_dereference(ub->bearer);
38562306a36Sopenharmony_ci	if (!b)
38662306a36Sopenharmony_ci		goto out;
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	if (b && test_bit(0, &b->up)) {
38962306a36Sopenharmony_ci		TIPC_SKB_CB(skb)->flags = 0;
39062306a36Sopenharmony_ci		tipc_rcv(sock_net(sk), skb, b);
39162306a36Sopenharmony_ci		return 0;
39262306a36Sopenharmony_ci	}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	if (unlikely(msg_user(hdr) == LINK_CONFIG)) {
39562306a36Sopenharmony_ci		err = tipc_udp_rcast_disc(b, skb);
39662306a36Sopenharmony_ci		if (err)
39762306a36Sopenharmony_ci			goto out;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ciout:
40162306a36Sopenharmony_ci	kfree_skb(skb);
40262306a36Sopenharmony_ci	return 0;
40362306a36Sopenharmony_ci}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_cistatic int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	int err = 0;
40862306a36Sopenharmony_ci	struct ip_mreqn mreqn;
40962306a36Sopenharmony_ci	struct sock *sk = ub->ubsock->sk;
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (ntohs(remote->proto) == ETH_P_IP) {
41262306a36Sopenharmony_ci		mreqn.imr_multiaddr = remote->ipv4;
41362306a36Sopenharmony_ci		mreqn.imr_ifindex = ub->ifindex;
41462306a36Sopenharmony_ci		err = ip_mc_join_group(sk, &mreqn);
41562306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
41662306a36Sopenharmony_ci	} else {
41762306a36Sopenharmony_ci		lock_sock(sk);
41862306a36Sopenharmony_ci		err = ipv6_stub->ipv6_sock_mc_join(sk, ub->ifindex,
41962306a36Sopenharmony_ci						   &remote->ipv6);
42062306a36Sopenharmony_ci		release_sock(sk);
42162306a36Sopenharmony_ci#endif
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci	return err;
42462306a36Sopenharmony_ci}
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_cistatic int __tipc_nl_add_udp_addr(struct sk_buff *skb,
42762306a36Sopenharmony_ci				  struct udp_media_addr *addr, int nla_t)
42862306a36Sopenharmony_ci{
42962306a36Sopenharmony_ci	if (ntohs(addr->proto) == ETH_P_IP) {
43062306a36Sopenharmony_ci		struct sockaddr_in ip4;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		memset(&ip4, 0, sizeof(ip4));
43362306a36Sopenharmony_ci		ip4.sin_family = AF_INET;
43462306a36Sopenharmony_ci		ip4.sin_port = addr->port;
43562306a36Sopenharmony_ci		ip4.sin_addr.s_addr = addr->ipv4.s_addr;
43662306a36Sopenharmony_ci		if (nla_put(skb, nla_t, sizeof(ip4), &ip4))
43762306a36Sopenharmony_ci			return -EMSGSIZE;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
44062306a36Sopenharmony_ci	} else if (ntohs(addr->proto) == ETH_P_IPV6) {
44162306a36Sopenharmony_ci		struct sockaddr_in6 ip6;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci		memset(&ip6, 0, sizeof(ip6));
44462306a36Sopenharmony_ci		ip6.sin6_family = AF_INET6;
44562306a36Sopenharmony_ci		ip6.sin6_port  = addr->port;
44662306a36Sopenharmony_ci		memcpy(&ip6.sin6_addr, &addr->ipv6, sizeof(struct in6_addr));
44762306a36Sopenharmony_ci		if (nla_put(skb, nla_t, sizeof(ip6), &ip6))
44862306a36Sopenharmony_ci			return -EMSGSIZE;
44962306a36Sopenharmony_ci#endif
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ci	return 0;
45362306a36Sopenharmony_ci}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ciint tipc_udp_nl_dump_remoteip(struct sk_buff *skb, struct netlink_callback *cb)
45662306a36Sopenharmony_ci{
45762306a36Sopenharmony_ci	u32 bid = cb->args[0];
45862306a36Sopenharmony_ci	u32 skip_cnt = cb->args[1];
45962306a36Sopenharmony_ci	u32 portid = NETLINK_CB(cb->skb).portid;
46062306a36Sopenharmony_ci	struct udp_replicast *rcast, *tmp;
46162306a36Sopenharmony_ci	struct tipc_bearer *b;
46262306a36Sopenharmony_ci	struct udp_bearer *ub;
46362306a36Sopenharmony_ci	void *hdr;
46462306a36Sopenharmony_ci	int err;
46562306a36Sopenharmony_ci	int i;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (!bid && !skip_cnt) {
46862306a36Sopenharmony_ci		struct nlattr **attrs = genl_dumpit_info(cb)->info.attrs;
46962306a36Sopenharmony_ci		struct net *net = sock_net(skb->sk);
47062306a36Sopenharmony_ci		struct nlattr *battrs[TIPC_NLA_BEARER_MAX + 1];
47162306a36Sopenharmony_ci		char *bname;
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci		if (!attrs[TIPC_NLA_BEARER])
47462306a36Sopenharmony_ci			return -EINVAL;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci		err = nla_parse_nested_deprecated(battrs, TIPC_NLA_BEARER_MAX,
47762306a36Sopenharmony_ci						  attrs[TIPC_NLA_BEARER],
47862306a36Sopenharmony_ci						  tipc_nl_bearer_policy, NULL);
47962306a36Sopenharmony_ci		if (err)
48062306a36Sopenharmony_ci			return err;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		if (!battrs[TIPC_NLA_BEARER_NAME])
48362306a36Sopenharmony_ci			return -EINVAL;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		bname = nla_data(battrs[TIPC_NLA_BEARER_NAME]);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		rtnl_lock();
48862306a36Sopenharmony_ci		b = tipc_bearer_find(net, bname);
48962306a36Sopenharmony_ci		if (!b) {
49062306a36Sopenharmony_ci			rtnl_unlock();
49162306a36Sopenharmony_ci			return -EINVAL;
49262306a36Sopenharmony_ci		}
49362306a36Sopenharmony_ci		bid = b->identity;
49462306a36Sopenharmony_ci	} else {
49562306a36Sopenharmony_ci		struct net *net = sock_net(skb->sk);
49662306a36Sopenharmony_ci		struct tipc_net *tn = net_generic(net, tipc_net_id);
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		rtnl_lock();
49962306a36Sopenharmony_ci		b = rtnl_dereference(tn->bearer_list[bid]);
50062306a36Sopenharmony_ci		if (!b) {
50162306a36Sopenharmony_ci			rtnl_unlock();
50262306a36Sopenharmony_ci			return -EINVAL;
50362306a36Sopenharmony_ci		}
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	ub = rtnl_dereference(b->media_ptr);
50762306a36Sopenharmony_ci	if (!ub) {
50862306a36Sopenharmony_ci		rtnl_unlock();
50962306a36Sopenharmony_ci		return -EINVAL;
51062306a36Sopenharmony_ci	}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	i = 0;
51362306a36Sopenharmony_ci	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
51462306a36Sopenharmony_ci		if (i < skip_cnt)
51562306a36Sopenharmony_ci			goto count;
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		hdr = genlmsg_put(skb, portid, cb->nlh->nlmsg_seq,
51862306a36Sopenharmony_ci				  &tipc_genl_family, NLM_F_MULTI,
51962306a36Sopenharmony_ci				  TIPC_NL_BEARER_GET);
52062306a36Sopenharmony_ci		if (!hdr)
52162306a36Sopenharmony_ci			goto done;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		err = __tipc_nl_add_udp_addr(skb, &rcast->addr,
52462306a36Sopenharmony_ci					     TIPC_NLA_UDP_REMOTE);
52562306a36Sopenharmony_ci		if (err) {
52662306a36Sopenharmony_ci			genlmsg_cancel(skb, hdr);
52762306a36Sopenharmony_ci			goto done;
52862306a36Sopenharmony_ci		}
52962306a36Sopenharmony_ci		genlmsg_end(skb, hdr);
53062306a36Sopenharmony_cicount:
53162306a36Sopenharmony_ci		i++;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_cidone:
53462306a36Sopenharmony_ci	rtnl_unlock();
53562306a36Sopenharmony_ci	cb->args[0] = bid;
53662306a36Sopenharmony_ci	cb->args[1] = i;
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	return skb->len;
53962306a36Sopenharmony_ci}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ciint tipc_udp_nl_add_bearer_data(struct tipc_nl_msg *msg, struct tipc_bearer *b)
54262306a36Sopenharmony_ci{
54362306a36Sopenharmony_ci	struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value;
54462306a36Sopenharmony_ci	struct udp_media_addr *dst;
54562306a36Sopenharmony_ci	struct udp_bearer *ub;
54662306a36Sopenharmony_ci	struct nlattr *nest;
54762306a36Sopenharmony_ci
54862306a36Sopenharmony_ci	ub = rtnl_dereference(b->media_ptr);
54962306a36Sopenharmony_ci	if (!ub)
55062306a36Sopenharmony_ci		return -ENODEV;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	nest = nla_nest_start_noflag(msg->skb, TIPC_NLA_BEARER_UDP_OPTS);
55362306a36Sopenharmony_ci	if (!nest)
55462306a36Sopenharmony_ci		goto msg_full;
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	if (__tipc_nl_add_udp_addr(msg->skb, src, TIPC_NLA_UDP_LOCAL))
55762306a36Sopenharmony_ci		goto msg_full;
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci	dst = (struct udp_media_addr *)&b->bcast_addr.value;
56062306a36Sopenharmony_ci	if (__tipc_nl_add_udp_addr(msg->skb, dst, TIPC_NLA_UDP_REMOTE))
56162306a36Sopenharmony_ci		goto msg_full;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	if (!list_empty(&ub->rcast.list)) {
56462306a36Sopenharmony_ci		if (nla_put_flag(msg->skb, TIPC_NLA_UDP_MULTI_REMOTEIP))
56562306a36Sopenharmony_ci			goto msg_full;
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	nla_nest_end(msg->skb, nest);
56962306a36Sopenharmony_ci	return 0;
57062306a36Sopenharmony_cimsg_full:
57162306a36Sopenharmony_ci	nla_nest_cancel(msg->skb, nest);
57262306a36Sopenharmony_ci	return -EMSGSIZE;
57362306a36Sopenharmony_ci}
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci/**
57662306a36Sopenharmony_ci * tipc_parse_udp_addr - build udp media address from netlink data
57762306a36Sopenharmony_ci * @nla:	netlink attribute containing sockaddr storage aligned address
57862306a36Sopenharmony_ci * @addr:	tipc media address to fill with address, port and protocol type
57962306a36Sopenharmony_ci * @scope_id:	IPv6 scope id pointer, not NULL indicates it's required
58062306a36Sopenharmony_ci */
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_cistatic int tipc_parse_udp_addr(struct nlattr *nla, struct udp_media_addr *addr,
58362306a36Sopenharmony_ci			       u32 *scope_id)
58462306a36Sopenharmony_ci{
58562306a36Sopenharmony_ci	struct sockaddr_storage sa;
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	nla_memcpy(&sa, nla, sizeof(sa));
58862306a36Sopenharmony_ci	if (sa.ss_family == AF_INET) {
58962306a36Sopenharmony_ci		struct sockaddr_in *ip4 = (struct sockaddr_in *)&sa;
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		addr->proto = htons(ETH_P_IP);
59262306a36Sopenharmony_ci		addr->port = ip4->sin_port;
59362306a36Sopenharmony_ci		addr->ipv4.s_addr = ip4->sin_addr.s_addr;
59462306a36Sopenharmony_ci		return 0;
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
59762306a36Sopenharmony_ci	} else if (sa.ss_family == AF_INET6) {
59862306a36Sopenharmony_ci		struct sockaddr_in6 *ip6 = (struct sockaddr_in6 *)&sa;
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		addr->proto = htons(ETH_P_IPV6);
60162306a36Sopenharmony_ci		addr->port = ip6->sin6_port;
60262306a36Sopenharmony_ci		memcpy(&addr->ipv6, &ip6->sin6_addr, sizeof(struct in6_addr));
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci		/* Scope ID is only interesting for local addresses */
60562306a36Sopenharmony_ci		if (scope_id) {
60662306a36Sopenharmony_ci			int atype;
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_ci			atype = ipv6_addr_type(&ip6->sin6_addr);
60962306a36Sopenharmony_ci			if (__ipv6_addr_needs_scope_id(atype) &&
61062306a36Sopenharmony_ci			    !ip6->sin6_scope_id) {
61162306a36Sopenharmony_ci				return -EINVAL;
61262306a36Sopenharmony_ci			}
61362306a36Sopenharmony_ci
61462306a36Sopenharmony_ci			*scope_id = ip6->sin6_scope_id ? : 0;
61562306a36Sopenharmony_ci		}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci		return 0;
61862306a36Sopenharmony_ci#endif
61962306a36Sopenharmony_ci	}
62062306a36Sopenharmony_ci	return -EADDRNOTAVAIL;
62162306a36Sopenharmony_ci}
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ciint tipc_udp_nl_bearer_add(struct tipc_bearer *b, struct nlattr *attr)
62462306a36Sopenharmony_ci{
62562306a36Sopenharmony_ci	int err;
62662306a36Sopenharmony_ci	struct udp_media_addr addr = {0};
62762306a36Sopenharmony_ci	struct nlattr *opts[TIPC_NLA_UDP_MAX + 1];
62862306a36Sopenharmony_ci	struct udp_media_addr *dst;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (nla_parse_nested_deprecated(opts, TIPC_NLA_UDP_MAX, attr, tipc_nl_udp_policy, NULL))
63162306a36Sopenharmony_ci		return -EINVAL;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	if (!opts[TIPC_NLA_UDP_REMOTE])
63462306a36Sopenharmony_ci		return -EINVAL;
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_REMOTE], &addr, NULL);
63762306a36Sopenharmony_ci	if (err)
63862306a36Sopenharmony_ci		return err;
63962306a36Sopenharmony_ci
64062306a36Sopenharmony_ci	dst = (struct udp_media_addr *)&b->bcast_addr.value;
64162306a36Sopenharmony_ci	if (tipc_udp_is_mcast_addr(dst)) {
64262306a36Sopenharmony_ci		pr_err("Can't add remote ip to TIPC UDP multicast bearer\n");
64362306a36Sopenharmony_ci		return -EINVAL;
64462306a36Sopenharmony_ci	}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	if (tipc_udp_is_known_peer(b, &addr))
64762306a36Sopenharmony_ci		return 0;
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci	return tipc_udp_rcast_add(b, &addr);
65062306a36Sopenharmony_ci}
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci/**
65362306a36Sopenharmony_ci * tipc_udp_enable - callback to create a new udp bearer instance
65462306a36Sopenharmony_ci * @net:	network namespace
65562306a36Sopenharmony_ci * @b:		pointer to generic tipc_bearer
65662306a36Sopenharmony_ci * @attrs:	netlink bearer configuration
65762306a36Sopenharmony_ci *
65862306a36Sopenharmony_ci * validate the bearer parameters and initialize the udp bearer
65962306a36Sopenharmony_ci * rtnl_lock should be held
66062306a36Sopenharmony_ci */
66162306a36Sopenharmony_cistatic int tipc_udp_enable(struct net *net, struct tipc_bearer *b,
66262306a36Sopenharmony_ci			   struct nlattr *attrs[])
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	int err = -EINVAL;
66562306a36Sopenharmony_ci	struct udp_bearer *ub;
66662306a36Sopenharmony_ci	struct udp_media_addr remote = {0};
66762306a36Sopenharmony_ci	struct udp_media_addr local = {0};
66862306a36Sopenharmony_ci	struct udp_port_cfg udp_conf = {0};
66962306a36Sopenharmony_ci	struct udp_tunnel_sock_cfg tuncfg = {NULL};
67062306a36Sopenharmony_ci	struct nlattr *opts[TIPC_NLA_UDP_MAX + 1];
67162306a36Sopenharmony_ci	u8 node_id[NODE_ID_LEN] = {0,};
67262306a36Sopenharmony_ci	struct net_device *dev;
67362306a36Sopenharmony_ci	int rmcast = 0;
67462306a36Sopenharmony_ci
67562306a36Sopenharmony_ci	ub = kzalloc(sizeof(*ub), GFP_ATOMIC);
67662306a36Sopenharmony_ci	if (!ub)
67762306a36Sopenharmony_ci		return -ENOMEM;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	INIT_LIST_HEAD(&ub->rcast.list);
68062306a36Sopenharmony_ci
68162306a36Sopenharmony_ci	if (!attrs[TIPC_NLA_BEARER_UDP_OPTS])
68262306a36Sopenharmony_ci		goto err;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	if (nla_parse_nested_deprecated(opts, TIPC_NLA_UDP_MAX, attrs[TIPC_NLA_BEARER_UDP_OPTS], tipc_nl_udp_policy, NULL))
68562306a36Sopenharmony_ci		goto err;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (!opts[TIPC_NLA_UDP_LOCAL] || !opts[TIPC_NLA_UDP_REMOTE]) {
68862306a36Sopenharmony_ci		pr_err("Invalid UDP bearer configuration");
68962306a36Sopenharmony_ci		err = -EINVAL;
69062306a36Sopenharmony_ci		goto err;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_LOCAL], &local,
69462306a36Sopenharmony_ci				  &ub->ifindex);
69562306a36Sopenharmony_ci	if (err)
69662306a36Sopenharmony_ci		goto err;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	err = tipc_parse_udp_addr(opts[TIPC_NLA_UDP_REMOTE], &remote, NULL);
69962306a36Sopenharmony_ci	if (err)
70062306a36Sopenharmony_ci		goto err;
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	if (remote.proto != local.proto) {
70362306a36Sopenharmony_ci		err = -EINVAL;
70462306a36Sopenharmony_ci		goto err;
70562306a36Sopenharmony_ci	}
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	/* Checking remote ip address */
70862306a36Sopenharmony_ci	rmcast = tipc_udp_is_mcast_addr(&remote);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/* Autoconfigure own node identity if needed */
71162306a36Sopenharmony_ci	if (!tipc_own_id(net)) {
71262306a36Sopenharmony_ci		memcpy(node_id, local.ipv6.in6_u.u6_addr8, 16);
71362306a36Sopenharmony_ci		tipc_net_init(net, node_id, 0);
71462306a36Sopenharmony_ci	}
71562306a36Sopenharmony_ci	if (!tipc_own_id(net)) {
71662306a36Sopenharmony_ci		pr_warn("Failed to set node id, please configure manually\n");
71762306a36Sopenharmony_ci		err = -EINVAL;
71862306a36Sopenharmony_ci		goto err;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	b->bcast_addr.media_id = TIPC_MEDIA_TYPE_UDP;
72262306a36Sopenharmony_ci	b->bcast_addr.broadcast = TIPC_BROADCAST_SUPPORT;
72362306a36Sopenharmony_ci	rcu_assign_pointer(b->media_ptr, ub);
72462306a36Sopenharmony_ci	rcu_assign_pointer(ub->bearer, b);
72562306a36Sopenharmony_ci	tipc_udp_media_addr_set(&b->addr, &local);
72662306a36Sopenharmony_ci	if (local.proto == htons(ETH_P_IP)) {
72762306a36Sopenharmony_ci		dev = __ip_dev_find(net, local.ipv4.s_addr, false);
72862306a36Sopenharmony_ci		if (!dev) {
72962306a36Sopenharmony_ci			err = -ENODEV;
73062306a36Sopenharmony_ci			goto err;
73162306a36Sopenharmony_ci		}
73262306a36Sopenharmony_ci		udp_conf.family = AF_INET;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci		/* Switch to use ANY to receive packets from group */
73562306a36Sopenharmony_ci		if (rmcast)
73662306a36Sopenharmony_ci			udp_conf.local_ip.s_addr = htonl(INADDR_ANY);
73762306a36Sopenharmony_ci		else
73862306a36Sopenharmony_ci			udp_conf.local_ip.s_addr = local.ipv4.s_addr;
73962306a36Sopenharmony_ci		udp_conf.use_udp_checksums = false;
74062306a36Sopenharmony_ci		ub->ifindex = dev->ifindex;
74162306a36Sopenharmony_ci		b->encap_hlen = sizeof(struct iphdr) + sizeof(struct udphdr);
74262306a36Sopenharmony_ci		b->mtu = b->media->mtu;
74362306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_IPV6)
74462306a36Sopenharmony_ci	} else if (local.proto == htons(ETH_P_IPV6)) {
74562306a36Sopenharmony_ci		dev = ub->ifindex ? __dev_get_by_index(net, ub->ifindex) : NULL;
74662306a36Sopenharmony_ci		dev = ipv6_dev_find(net, &local.ipv6, dev);
74762306a36Sopenharmony_ci		if (!dev) {
74862306a36Sopenharmony_ci			err = -ENODEV;
74962306a36Sopenharmony_ci			goto err;
75062306a36Sopenharmony_ci		}
75162306a36Sopenharmony_ci		udp_conf.family = AF_INET6;
75262306a36Sopenharmony_ci		udp_conf.use_udp6_tx_checksums = true;
75362306a36Sopenharmony_ci		udp_conf.use_udp6_rx_checksums = true;
75462306a36Sopenharmony_ci		if (rmcast)
75562306a36Sopenharmony_ci			udp_conf.local_ip6 = in6addr_any;
75662306a36Sopenharmony_ci		else
75762306a36Sopenharmony_ci			udp_conf.local_ip6 = local.ipv6;
75862306a36Sopenharmony_ci		ub->ifindex = dev->ifindex;
75962306a36Sopenharmony_ci		b->encap_hlen = sizeof(struct ipv6hdr) + sizeof(struct udphdr);
76062306a36Sopenharmony_ci		b->mtu = 1280;
76162306a36Sopenharmony_ci#endif
76262306a36Sopenharmony_ci	} else {
76362306a36Sopenharmony_ci		err = -EAFNOSUPPORT;
76462306a36Sopenharmony_ci		goto err;
76562306a36Sopenharmony_ci	}
76662306a36Sopenharmony_ci	udp_conf.local_udp_port = local.port;
76762306a36Sopenharmony_ci	err = udp_sock_create(net, &udp_conf, &ub->ubsock);
76862306a36Sopenharmony_ci	if (err)
76962306a36Sopenharmony_ci		goto err;
77062306a36Sopenharmony_ci	tuncfg.sk_user_data = ub;
77162306a36Sopenharmony_ci	tuncfg.encap_type = 1;
77262306a36Sopenharmony_ci	tuncfg.encap_rcv = tipc_udp_recv;
77362306a36Sopenharmony_ci	tuncfg.encap_destroy = NULL;
77462306a36Sopenharmony_ci	setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg);
77562306a36Sopenharmony_ci
77662306a36Sopenharmony_ci	err = dst_cache_init(&ub->rcast.dst_cache, GFP_ATOMIC);
77762306a36Sopenharmony_ci	if (err)
77862306a36Sopenharmony_ci		goto free;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	/*
78162306a36Sopenharmony_ci	 * The bcast media address port is used for all peers and the ip
78262306a36Sopenharmony_ci	 * is used if it's a multicast address.
78362306a36Sopenharmony_ci	 */
78462306a36Sopenharmony_ci	memcpy(&b->bcast_addr.value, &remote, sizeof(remote));
78562306a36Sopenharmony_ci	if (rmcast)
78662306a36Sopenharmony_ci		err = enable_mcast(ub, &remote);
78762306a36Sopenharmony_ci	else
78862306a36Sopenharmony_ci		err = tipc_udp_rcast_add(b, &remote);
78962306a36Sopenharmony_ci	if (err)
79062306a36Sopenharmony_ci		goto free;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	return 0;
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_cifree:
79562306a36Sopenharmony_ci	dst_cache_destroy(&ub->rcast.dst_cache);
79662306a36Sopenharmony_ci	udp_tunnel_sock_release(ub->ubsock);
79762306a36Sopenharmony_cierr:
79862306a36Sopenharmony_ci	kfree(ub);
79962306a36Sopenharmony_ci	return err;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci/* cleanup_bearer - break the socket/bearer association */
80362306a36Sopenharmony_cistatic void cleanup_bearer(struct work_struct *work)
80462306a36Sopenharmony_ci{
80562306a36Sopenharmony_ci	struct udp_bearer *ub = container_of(work, struct udp_bearer, work);
80662306a36Sopenharmony_ci	struct udp_replicast *rcast, *tmp;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	list_for_each_entry_safe(rcast, tmp, &ub->rcast.list, list) {
80962306a36Sopenharmony_ci		dst_cache_destroy(&rcast->dst_cache);
81062306a36Sopenharmony_ci		list_del_rcu(&rcast->list);
81162306a36Sopenharmony_ci		kfree_rcu(rcast, rcu);
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	atomic_dec(&tipc_net(sock_net(ub->ubsock->sk))->wq_count);
81562306a36Sopenharmony_ci	dst_cache_destroy(&ub->rcast.dst_cache);
81662306a36Sopenharmony_ci	udp_tunnel_sock_release(ub->ubsock);
81762306a36Sopenharmony_ci	synchronize_net();
81862306a36Sopenharmony_ci	kfree(ub);
81962306a36Sopenharmony_ci}
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_ci/* tipc_udp_disable - detach bearer from socket */
82262306a36Sopenharmony_cistatic void tipc_udp_disable(struct tipc_bearer *b)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	struct udp_bearer *ub;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	ub = rtnl_dereference(b->media_ptr);
82762306a36Sopenharmony_ci	if (!ub) {
82862306a36Sopenharmony_ci		pr_err("UDP bearer instance not found\n");
82962306a36Sopenharmony_ci		return;
83062306a36Sopenharmony_ci	}
83162306a36Sopenharmony_ci	sock_set_flag(ub->ubsock->sk, SOCK_DEAD);
83262306a36Sopenharmony_ci	RCU_INIT_POINTER(ub->bearer, NULL);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	/* sock_release need to be done outside of rtnl lock */
83562306a36Sopenharmony_ci	atomic_inc(&tipc_net(sock_net(ub->ubsock->sk))->wq_count);
83662306a36Sopenharmony_ci	INIT_WORK(&ub->work, cleanup_bearer);
83762306a36Sopenharmony_ci	schedule_work(&ub->work);
83862306a36Sopenharmony_ci}
83962306a36Sopenharmony_ci
84062306a36Sopenharmony_cistruct tipc_media udp_media_info = {
84162306a36Sopenharmony_ci	.send_msg	= tipc_udp_send_msg,
84262306a36Sopenharmony_ci	.enable_media	= tipc_udp_enable,
84362306a36Sopenharmony_ci	.disable_media	= tipc_udp_disable,
84462306a36Sopenharmony_ci	.addr2str	= tipc_udp_addr2str,
84562306a36Sopenharmony_ci	.addr2msg	= tipc_udp_addr2msg,
84662306a36Sopenharmony_ci	.msg2addr	= tipc_udp_msg2addr,
84762306a36Sopenharmony_ci	.priority	= TIPC_DEF_LINK_PRI,
84862306a36Sopenharmony_ci	.tolerance	= TIPC_DEF_LINK_TOL,
84962306a36Sopenharmony_ci	.min_win	= TIPC_DEF_LINK_WIN,
85062306a36Sopenharmony_ci	.max_win	= TIPC_DEF_LINK_WIN,
85162306a36Sopenharmony_ci	.mtu		= TIPC_DEF_LINK_UDP_MTU,
85262306a36Sopenharmony_ci	.type_id	= TIPC_MEDIA_TYPE_UDP,
85362306a36Sopenharmony_ci	.hwaddr_len	= 0,
85462306a36Sopenharmony_ci	.name		= "udp"
85562306a36Sopenharmony_ci};
856