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