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