diff options
Diffstat (limited to 'net/tipc')
-rw-r--r-- | net/tipc/Kconfig | 8 | ||||
-rw-r--r-- | net/tipc/Makefile | 1 | ||||
-rw-r--r-- | net/tipc/bearer.c | 13 | ||||
-rw-r--r-- | net/tipc/bearer.h | 12 | ||||
-rw-r--r-- | net/tipc/msg.h | 2 | ||||
-rw-r--r-- | net/tipc/udp_media.c | 442 |
6 files changed, 470 insertions, 8 deletions
diff --git a/net/tipc/Kconfig b/net/tipc/Kconfig index 91c8a8e031db..c25a3a149dc4 100644 --- a/net/tipc/Kconfig +++ b/net/tipc/Kconfig | |||
@@ -26,3 +26,11 @@ config TIPC_MEDIA_IB | |||
26 | help | 26 | help |
27 | Saying Y here will enable support for running TIPC on | 27 | Saying Y here will enable support for running TIPC on |
28 | IP-over-InfiniBand devices. | 28 | IP-over-InfiniBand devices. |
29 | config TIPC_MEDIA_UDP | ||
30 | bool "IP/UDP media type support" | ||
31 | depends on TIPC | ||
32 | select NET_UDP_TUNNEL | ||
33 | help | ||
34 | Saying Y here will enable support for running TIPC over IP/UDP | ||
35 | bool | ||
36 | default y | ||
diff --git a/net/tipc/Makefile b/net/tipc/Makefile index 599b1a540d2b..57e460be4692 100644 --- a/net/tipc/Makefile +++ b/net/tipc/Makefile | |||
@@ -10,5 +10,6 @@ tipc-y += addr.o bcast.o bearer.o \ | |||
10 | netlink.o netlink_compat.o node.o socket.o eth_media.o \ | 10 | netlink.o netlink_compat.o node.o socket.o eth_media.o \ |
11 | server.o socket.o | 11 | server.o socket.o |
12 | 12 | ||
13 | tipc-$(CONFIG_TIPC_MEDIA_UDP) += udp_media.o | ||
13 | tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o | 14 | tipc-$(CONFIG_TIPC_MEDIA_IB) += ib_media.o |
14 | tipc-$(CONFIG_SYSCTL) += sysctl.o | 15 | tipc-$(CONFIG_SYSCTL) += sysctl.o |
diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index af6deeb397a8..840db89e4283 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c | |||
@@ -48,6 +48,9 @@ static struct tipc_media * const media_info_array[] = { | |||
48 | #ifdef CONFIG_TIPC_MEDIA_IB | 48 | #ifdef CONFIG_TIPC_MEDIA_IB |
49 | &ib_media_info, | 49 | &ib_media_info, |
50 | #endif | 50 | #endif |
51 | #ifdef CONFIG_TIPC_MEDIA_UDP | ||
52 | &udp_media_info, | ||
53 | #endif | ||
51 | NULL | 54 | NULL |
52 | }; | 55 | }; |
53 | 56 | ||
@@ -216,7 +219,8 @@ void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest) | |||
216 | * tipc_enable_bearer - enable bearer with the given name | 219 | * tipc_enable_bearer - enable bearer with the given name |
217 | */ | 220 | */ |
218 | static int tipc_enable_bearer(struct net *net, const char *name, | 221 | static int tipc_enable_bearer(struct net *net, const char *name, |
219 | u32 disc_domain, u32 priority) | 222 | u32 disc_domain, u32 priority, |
223 | struct nlattr *attr[]) | ||
220 | { | 224 | { |
221 | struct tipc_net *tn = net_generic(net, tipc_net_id); | 225 | struct tipc_net *tn = net_generic(net, tipc_net_id); |
222 | struct tipc_bearer *b_ptr; | 226 | struct tipc_bearer *b_ptr; |
@@ -304,7 +308,7 @@ restart: | |||
304 | 308 | ||
305 | strcpy(b_ptr->name, name); | 309 | strcpy(b_ptr->name, name); |
306 | b_ptr->media = m_ptr; | 310 | b_ptr->media = m_ptr; |
307 | res = m_ptr->enable_media(net, b_ptr); | 311 | res = m_ptr->enable_media(net, b_ptr, attr); |
308 | if (res) { | 312 | if (res) { |
309 | pr_warn("Bearer <%s> rejected, enable failure (%d)\n", | 313 | pr_warn("Bearer <%s> rejected, enable failure (%d)\n", |
310 | name, -res); | 314 | name, -res); |
@@ -372,7 +376,8 @@ static void bearer_disable(struct net *net, struct tipc_bearer *b_ptr, | |||
372 | kfree_rcu(b_ptr, rcu); | 376 | kfree_rcu(b_ptr, rcu); |
373 | } | 377 | } |
374 | 378 | ||
375 | int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b) | 379 | int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b, |
380 | struct nlattr *attr[]) | ||
376 | { | 381 | { |
377 | struct net_device *dev; | 382 | struct net_device *dev; |
378 | char *driver_name = strchr((const char *)b->name, ':') + 1; | 383 | char *driver_name = strchr((const char *)b->name, ':') + 1; |
@@ -791,7 +796,7 @@ int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info) | |||
791 | } | 796 | } |
792 | 797 | ||
793 | rtnl_lock(); | 798 | rtnl_lock(); |
794 | err = tipc_enable_bearer(net, bearer, domain, prio); | 799 | err = tipc_enable_bearer(net, bearer, domain, prio, attrs); |
795 | if (err) { | 800 | if (err) { |
796 | rtnl_unlock(); | 801 | rtnl_unlock(); |
797 | return err; | 802 | return err; |
diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 097aff08ad5b..5cad243ee8fc 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h | |||
@@ -41,7 +41,7 @@ | |||
41 | #include <net/genetlink.h> | 41 | #include <net/genetlink.h> |
42 | 42 | ||
43 | #define MAX_BEARERS 2 | 43 | #define MAX_BEARERS 2 |
44 | #define MAX_MEDIA 2 | 44 | #define MAX_MEDIA 3 |
45 | #define MAX_NODES 4096 | 45 | #define MAX_NODES 4096 |
46 | #define WSIZE 32 | 46 | #define WSIZE 32 |
47 | 47 | ||
@@ -59,6 +59,7 @@ | |||
59 | */ | 59 | */ |
60 | #define TIPC_MEDIA_TYPE_ETH 1 | 60 | #define TIPC_MEDIA_TYPE_ETH 1 |
61 | #define TIPC_MEDIA_TYPE_IB 2 | 61 | #define TIPC_MEDIA_TYPE_IB 2 |
62 | #define TIPC_MEDIA_TYPE_UDP 3 | ||
62 | 63 | ||
63 | /** | 64 | /** |
64 | * struct tipc_node_map - set of node identifiers | 65 | * struct tipc_node_map - set of node identifiers |
@@ -104,7 +105,8 @@ struct tipc_media { | |||
104 | int (*send_msg)(struct net *net, struct sk_buff *buf, | 105 | int (*send_msg)(struct net *net, struct sk_buff *buf, |
105 | struct tipc_bearer *b_ptr, | 106 | struct tipc_bearer *b_ptr, |
106 | struct tipc_media_addr *dest); | 107 | struct tipc_media_addr *dest); |
107 | int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr); | 108 | int (*enable_media)(struct net *net, struct tipc_bearer *b_ptr, |
109 | struct nlattr *attr[]); | ||
108 | void (*disable_media)(struct tipc_bearer *b_ptr); | 110 | void (*disable_media)(struct tipc_bearer *b_ptr); |
109 | int (*addr2str)(struct tipc_media_addr *addr, | 111 | int (*addr2str)(struct tipc_media_addr *addr, |
110 | char *strbuf, | 112 | char *strbuf, |
@@ -183,6 +185,9 @@ extern struct tipc_media eth_media_info; | |||
183 | #ifdef CONFIG_TIPC_MEDIA_IB | 185 | #ifdef CONFIG_TIPC_MEDIA_IB |
184 | extern struct tipc_media ib_media_info; | 186 | extern struct tipc_media ib_media_info; |
185 | #endif | 187 | #endif |
188 | #ifdef CONFIG_TIPC_MEDIA_UDP | ||
189 | extern struct tipc_media udp_media_info; | ||
190 | #endif | ||
186 | 191 | ||
187 | int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info); | 192 | int tipc_nl_bearer_disable(struct sk_buff *skb, struct genl_info *info); |
188 | int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info); | 193 | int tipc_nl_bearer_enable(struct sk_buff *skb, struct genl_info *info); |
@@ -197,7 +202,8 @@ int tipc_nl_media_set(struct sk_buff *skb, struct genl_info *info); | |||
197 | int tipc_media_set_priority(const char *name, u32 new_value); | 202 | int tipc_media_set_priority(const char *name, u32 new_value); |
198 | int tipc_media_set_window(const char *name, u32 new_value); | 203 | int tipc_media_set_window(const char *name, u32 new_value); |
199 | void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a); | 204 | void tipc_media_addr_printf(char *buf, int len, struct tipc_media_addr *a); |
200 | int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b); | 205 | int tipc_enable_l2_media(struct net *net, struct tipc_bearer *b, |
206 | struct nlattr *attrs[]); | ||
201 | void tipc_disable_l2_media(struct tipc_bearer *b); | 207 | void tipc_disable_l2_media(struct tipc_bearer *b); |
202 | int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, | 208 | int tipc_l2_send_msg(struct net *net, struct sk_buff *buf, |
203 | struct tipc_bearer *b, struct tipc_media_addr *dest); | 209 | struct tipc_bearer *b, struct tipc_media_addr *dest); |
diff --git a/net/tipc/msg.h b/net/tipc/msg.h index c1cc8d7a5d52..fa167846d1ab 100644 --- a/net/tipc/msg.h +++ b/net/tipc/msg.h | |||
@@ -87,7 +87,7 @@ struct plist; | |||
87 | * Note: Headroom should be a multiple of 4 to ensure the TIPC header fields | 87 | * Note: Headroom should be a multiple of 4 to ensure the TIPC header fields |
88 | * are word aligned for quicker access | 88 | * are word aligned for quicker access |
89 | */ | 89 | */ |
90 | #define BUF_HEADROOM LL_MAX_HEADER | 90 | #define BUF_HEADROOM (LL_MAX_HEADER + 48) |
91 | 91 | ||
92 | struct tipc_skb_cb { | 92 | struct tipc_skb_cb { |
93 | void *handle; | 93 | void *handle; |
diff --git a/net/tipc/udp_media.c b/net/tipc/udp_media.c new file mode 100644 index 000000000000..0d10001db40d --- /dev/null +++ b/net/tipc/udp_media.c | |||
@@ -0,0 +1,442 @@ | |||
1 | /* net/tipc/udp_media.c: IP bearer support for TIPC | ||
2 | * | ||
3 | * Copyright (c) 2015, Ericsson AB | ||
4 | * All rights reserved. | ||
5 | * | ||
6 | * Redistribution and use in source and binary forms, with or without | ||
7 | * modification, are permitted provided that the following conditions are met: | ||
8 | * | ||
9 | * 1. Redistributions of source code must retain the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer. | ||
11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
12 | * notice, this list of conditions and the following disclaimer in the | ||
13 | * documentation and/or other materials provided with the distribution. | ||
14 | * 3. Neither the names of the copyright holders nor the names of its | ||
15 | * contributors may be used to endorse or promote products derived from | ||
16 | * this software without specific prior written permission. | ||
17 | * | ||
18 | * Alternatively, this software may be distributed under the terms of the | ||
19 | * GNU General Public License ("GPL") version 2 as published by the Free | ||
20 | * Software Foundation. | ||
21 | * | ||
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | ||
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | ||
26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | ||
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | ||
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | ||
29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | ||
30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | ||
32 | * POSSIBILITY OF SUCH DAMAGE. | ||
33 | */ | ||
34 | |||
35 | #include <linux/socket.h> | ||
36 | #include <linux/ip.h> | ||
37 | #include <linux/udp.h> | ||
38 | #include <linux/inet.h> | ||
39 | #include <linux/inetdevice.h> | ||
40 | #include <linux/igmp.h> | ||
41 | #include <linux/kernel.h> | ||
42 | #include <linux/workqueue.h> | ||
43 | #include <linux/list.h> | ||
44 | #include <net/sock.h> | ||
45 | #include <net/ip.h> | ||
46 | #include <net/udp_tunnel.h> | ||
47 | #include <linux/tipc_netlink.h> | ||
48 | #include "core.h" | ||
49 | #include "bearer.h" | ||
50 | |||
51 | /* IANA assigned UDP port */ | ||
52 | #define UDP_PORT_DEFAULT 6118 | ||
53 | |||
54 | static const struct nla_policy tipc_nl_udp_policy[TIPC_NLA_UDP_MAX + 1] = { | ||
55 | [TIPC_NLA_UDP_UNSPEC] = {.type = NLA_UNSPEC}, | ||
56 | [TIPC_NLA_UDP_LOCAL] = {.type = NLA_BINARY, | ||
57 | .len = sizeof(struct sockaddr_storage)}, | ||
58 | [TIPC_NLA_UDP_REMOTE] = {.type = NLA_BINARY, | ||
59 | .len = sizeof(struct sockaddr_storage)}, | ||
60 | }; | ||
61 | |||
62 | /** | ||
63 | * struct udp_media_addr - IP/UDP addressing information | ||
64 | * | ||
65 | * This is the bearer level originating address used in neighbor discovery | ||
66 | * messages, and all fields should be in network byte order | ||
67 | */ | ||
68 | struct udp_media_addr { | ||
69 | __be16 proto; | ||
70 | __be16 udp_port; | ||
71 | union { | ||
72 | struct in_addr ipv4; | ||
73 | struct in6_addr ipv6; | ||
74 | }; | ||
75 | }; | ||
76 | |||
77 | /** | ||
78 | * struct udp_bearer - ip/udp bearer data structure | ||
79 | * @bearer: associated generic tipc bearer | ||
80 | * @ubsock: bearer associated socket | ||
81 | * @ifindex: local address scope | ||
82 | * @work: used to schedule deferred work on a bearer | ||
83 | */ | ||
84 | struct udp_bearer { | ||
85 | struct tipc_bearer __rcu *bearer; | ||
86 | struct socket *ubsock; | ||
87 | u32 ifindex; | ||
88 | struct work_struct work; | ||
89 | }; | ||
90 | |||
91 | /* udp_media_addr_set - convert a ip/udp address to a TIPC media address */ | ||
92 | static void tipc_udp_media_addr_set(struct tipc_media_addr *addr, | ||
93 | struct udp_media_addr *ua) | ||
94 | { | ||
95 | memset(addr, 0, sizeof(struct tipc_media_addr)); | ||
96 | addr->media_id = TIPC_MEDIA_TYPE_UDP; | ||
97 | memcpy(addr->value, ua, sizeof(struct udp_media_addr)); | ||
98 | if (ntohs(ua->proto) == ETH_P_IP) { | ||
99 | if (ipv4_is_multicast(ua->ipv4.s_addr)) | ||
100 | addr->broadcast = 1; | ||
101 | } else if (ntohs(ua->proto) == ETH_P_IPV6) { | ||
102 | if (ipv6_addr_type(&ua->ipv6) & IPV6_ADDR_MULTICAST) | ||
103 | addr->broadcast = 1; | ||
104 | } else { | ||
105 | pr_err("Invalid UDP media address\n"); | ||
106 | } | ||
107 | } | ||
108 | |||
109 | /* tipc_udp_addr2str - convert ip/udp address to string */ | ||
110 | static int tipc_udp_addr2str(struct tipc_media_addr *a, char *buf, int size) | ||
111 | { | ||
112 | struct udp_media_addr *ua = (struct udp_media_addr *)&a->value; | ||
113 | |||
114 | if (ntohs(ua->proto) == ETH_P_IP) | ||
115 | snprintf(buf, size, "%pI4:%u", &ua->ipv4, ntohs(ua->udp_port)); | ||
116 | else if (ntohs(ua->proto) == ETH_P_IPV6) | ||
117 | snprintf(buf, size, "%pI6:%u", &ua->ipv6, ntohs(ua->udp_port)); | ||
118 | else | ||
119 | pr_err("Invalid UDP media address\n"); | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* tipc_udp_msg2addr - extract an ip/udp address from a TIPC ndisc message */ | ||
124 | static int tipc_udp_msg2addr(struct tipc_bearer *b, struct tipc_media_addr *a, | ||
125 | char *msg) | ||
126 | { | ||
127 | struct udp_media_addr *ua; | ||
128 | |||
129 | ua = (struct udp_media_addr *) (msg + TIPC_MEDIA_ADDR_OFFSET); | ||
130 | if (msg[TIPC_MEDIA_TYPE_OFFSET] != TIPC_MEDIA_TYPE_UDP) | ||
131 | return -EINVAL; | ||
132 | tipc_udp_media_addr_set(a, ua); | ||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | /* tipc_udp_addr2msg - write an ip/udp address to a TIPC ndisc message */ | ||
137 | static int tipc_udp_addr2msg(char *msg, struct tipc_media_addr *a) | ||
138 | { | ||
139 | memset(msg, 0, TIPC_MEDIA_INFO_SIZE); | ||
140 | msg[TIPC_MEDIA_TYPE_OFFSET] = TIPC_MEDIA_TYPE_UDP; | ||
141 | memcpy(msg + TIPC_MEDIA_ADDR_OFFSET, a->value, | ||
142 | sizeof(struct udp_media_addr)); | ||
143 | return 0; | ||
144 | } | ||
145 | |||
146 | /* tipc_send_msg - enqueue a send request */ | ||
147 | static int tipc_udp_send_msg(struct net *net, struct sk_buff *skb, | ||
148 | struct tipc_bearer *b, | ||
149 | struct tipc_media_addr *dest) | ||
150 | { | ||
151 | int ttl, err = 0; | ||
152 | struct udp_bearer *ub; | ||
153 | struct udp_media_addr *dst = (struct udp_media_addr *)&dest->value; | ||
154 | struct udp_media_addr *src = (struct udp_media_addr *)&b->addr.value; | ||
155 | struct sk_buff *clone; | ||
156 | struct rtable *rt; | ||
157 | |||
158 | clone = skb_clone(skb, GFP_ATOMIC); | ||
159 | skb_set_inner_protocol(clone, htons(ETH_P_TIPC)); | ||
160 | ub = rcu_dereference_rtnl(b->media_ptr); | ||
161 | if (!ub) { | ||
162 | err = -ENODEV; | ||
163 | goto tx_error; | ||
164 | } | ||
165 | if (htons(dst->proto) == ETH_P_IP) { | ||
166 | struct flowi4 fl = { | ||
167 | .daddr = dst->ipv4.s_addr, | ||
168 | .saddr = src->ipv4.s_addr, | ||
169 | .flowi4_mark = clone->mark, | ||
170 | .flowi4_proto = IPPROTO_UDP | ||
171 | }; | ||
172 | rt = ip_route_output_key(net, &fl); | ||
173 | if (IS_ERR(rt)) { | ||
174 | err = PTR_ERR(rt); | ||
175 | goto tx_error; | ||
176 | } | ||
177 | ttl = ip4_dst_hoplimit(&rt->dst); | ||
178 | err = udp_tunnel_xmit_skb(rt, clone, src->ipv4.s_addr, | ||
179 | dst->ipv4.s_addr, 0, ttl, 0, | ||
180 | src->udp_port, dst->udp_port, | ||
181 | false, true); | ||
182 | if (err < 0) { | ||
183 | ip_rt_put(rt); | ||
184 | goto tx_error; | ||
185 | } | ||
186 | #if IS_ENABLED(CONFIG_IPV6) | ||
187 | } else { | ||
188 | struct dst_entry *ndst; | ||
189 | struct flowi6 fl6 = { | ||
190 | .flowi6_oif = ub->ifindex, | ||
191 | .daddr = dst->ipv6, | ||
192 | .saddr = src->ipv6, | ||
193 | .flowi6_proto = IPPROTO_UDP | ||
194 | }; | ||
195 | err = ip6_dst_lookup(ub->ubsock->sk, &ndst, &fl6); | ||
196 | if (err) | ||
197 | goto tx_error; | ||
198 | ttl = ip6_dst_hoplimit(ndst); | ||
199 | err = udp_tunnel6_xmit_skb(ndst, clone, ndst->dev, &src->ipv6, | ||
200 | &dst->ipv6, 0, ttl, src->udp_port, | ||
201 | dst->udp_port, false); | ||
202 | #endif | ||
203 | } | ||
204 | return err; | ||
205 | |||
206 | tx_error: | ||
207 | kfree_skb(clone); | ||
208 | return err; | ||
209 | } | ||
210 | |||
211 | /* tipc_udp_recv - read data from bearer socket */ | ||
212 | static int tipc_udp_recv(struct sock *sk, struct sk_buff *skb) | ||
213 | { | ||
214 | struct udp_bearer *ub; | ||
215 | struct tipc_bearer *b; | ||
216 | |||
217 | ub = rcu_dereference_sk_user_data(sk); | ||
218 | if (!ub) { | ||
219 | pr_err_ratelimited("Failed to get UDP bearer reference"); | ||
220 | kfree_skb(skb); | ||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | skb_pull(skb, sizeof(struct udphdr)); | ||
225 | rcu_read_lock(); | ||
226 | b = rcu_dereference_rtnl(ub->bearer); | ||
227 | |||
228 | if (b) { | ||
229 | tipc_rcv(sock_net(sk), skb, b); | ||
230 | rcu_read_unlock(); | ||
231 | return 0; | ||
232 | } | ||
233 | rcu_read_unlock(); | ||
234 | kfree_skb(skb); | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static int enable_mcast(struct udp_bearer *ub, struct udp_media_addr *remote) | ||
239 | { | ||
240 | int err = 0; | ||
241 | struct ip_mreqn mreqn; | ||
242 | struct sock *sk = ub->ubsock->sk; | ||
243 | |||
244 | if (ntohs(remote->proto) == ETH_P_IP) { | ||
245 | if (!ipv4_is_multicast(remote->ipv4.s_addr)) | ||
246 | return 0; | ||
247 | mreqn.imr_multiaddr = remote->ipv4; | ||
248 | mreqn.imr_ifindex = ub->ifindex; | ||
249 | err = __ip_mc_join_group(sk, &mreqn); | ||
250 | } else { | ||
251 | if (!ipv6_addr_is_multicast(&remote->ipv6)) | ||
252 | return 0; | ||
253 | err = __ipv6_sock_mc_join(sk, ub->ifindex, &remote->ipv6); | ||
254 | } | ||
255 | return err; | ||
256 | } | ||
257 | |||
258 | /** | ||
259 | * parse_options - build local/remote addresses from configuration | ||
260 | * @attrs: netlink config data | ||
261 | * @ub: UDP bearer instance | ||
262 | * @local: local bearer IP address/port | ||
263 | * @remote: peer or multicast IP/port | ||
264 | */ | ||
265 | static int parse_options(struct nlattr *attrs[], struct udp_bearer *ub, | ||
266 | struct udp_media_addr *local, | ||
267 | struct udp_media_addr *remote) | ||
268 | { | ||
269 | struct nlattr *opts[TIPC_NLA_UDP_MAX + 1]; | ||
270 | struct sockaddr_storage *sa_local, *sa_remote; | ||
271 | |||
272 | if (!attrs[TIPC_NLA_BEARER_UDP_OPTS]) | ||
273 | goto err; | ||
274 | if (nla_parse_nested(opts, TIPC_NLA_UDP_MAX, | ||
275 | attrs[TIPC_NLA_BEARER_UDP_OPTS], | ||
276 | tipc_nl_udp_policy)) | ||
277 | goto err; | ||
278 | if (opts[TIPC_NLA_UDP_LOCAL] && opts[TIPC_NLA_UDP_REMOTE]) { | ||
279 | sa_local = nla_data(opts[TIPC_NLA_UDP_LOCAL]); | ||
280 | sa_remote = nla_data(opts[TIPC_NLA_UDP_REMOTE]); | ||
281 | } else { | ||
282 | err: | ||
283 | pr_err("Invalid UDP bearer configuration"); | ||
284 | return -EINVAL; | ||
285 | } | ||
286 | if ((sa_local->ss_family & sa_remote->ss_family) == AF_INET) { | ||
287 | struct sockaddr_in *ip4; | ||
288 | |||
289 | ip4 = (struct sockaddr_in *)sa_local; | ||
290 | local->proto = htons(ETH_P_IP); | ||
291 | local->udp_port = ip4->sin_port; | ||
292 | local->ipv4.s_addr = ip4->sin_addr.s_addr; | ||
293 | |||
294 | ip4 = (struct sockaddr_in *)sa_remote; | ||
295 | remote->proto = htons(ETH_P_IP); | ||
296 | remote->udp_port = ip4->sin_port; | ||
297 | remote->ipv4.s_addr = ip4->sin_addr.s_addr; | ||
298 | return 0; | ||
299 | |||
300 | #if IS_ENABLED(CONFIG_IPV6) | ||
301 | } else if ((sa_local->ss_family & sa_remote->ss_family) == AF_INET6) { | ||
302 | struct sockaddr_in6 *ip6; | ||
303 | |||
304 | ip6 = (struct sockaddr_in6 *)sa_local; | ||
305 | local->proto = htons(ETH_P_IPV6); | ||
306 | local->udp_port = ip6->sin6_port; | ||
307 | local->ipv6 = ip6->sin6_addr; | ||
308 | ub->ifindex = ip6->sin6_scope_id; | ||
309 | |||
310 | ip6 = (struct sockaddr_in6 *)sa_remote; | ||
311 | remote->proto = htons(ETH_P_IPV6); | ||
312 | remote->udp_port = ip6->sin6_port; | ||
313 | remote->ipv6 = ip6->sin6_addr; | ||
314 | return 0; | ||
315 | #endif | ||
316 | } | ||
317 | return -EADDRNOTAVAIL; | ||
318 | } | ||
319 | |||
320 | /** | ||
321 | * tipc_udp_enable - callback to create a new udp bearer instance | ||
322 | * @net: network namespace | ||
323 | * @b: pointer to generic tipc_bearer | ||
324 | * @attrs: netlink bearer configuration | ||
325 | * | ||
326 | * validate the bearer parameters and initialize the udp bearer | ||
327 | * rtnl_lock should be held | ||
328 | */ | ||
329 | static int tipc_udp_enable(struct net *net, struct tipc_bearer *b, | ||
330 | struct nlattr *attrs[]) | ||
331 | { | ||
332 | int err = -EINVAL; | ||
333 | struct udp_bearer *ub; | ||
334 | struct udp_media_addr *remote; | ||
335 | struct udp_media_addr local = {0}; | ||
336 | struct udp_port_cfg udp_conf = {0}; | ||
337 | struct udp_tunnel_sock_cfg tuncfg = {0}; | ||
338 | |||
339 | ub = kzalloc(sizeof(*ub), GFP_ATOMIC); | ||
340 | if (!ub) | ||
341 | return -ENOMEM; | ||
342 | |||
343 | remote = (struct udp_media_addr *)&b->bcast_addr.value; | ||
344 | memset(remote, 0, sizeof(struct udp_media_addr)); | ||
345 | err = parse_options(attrs, ub, &local, remote); | ||
346 | if (err) | ||
347 | goto err; | ||
348 | |||
349 | b->bcast_addr.media_id = TIPC_MEDIA_TYPE_UDP; | ||
350 | b->bcast_addr.broadcast = 1; | ||
351 | rcu_assign_pointer(b->media_ptr, ub); | ||
352 | rcu_assign_pointer(ub->bearer, b); | ||
353 | tipc_udp_media_addr_set(&b->addr, &local); | ||
354 | if (htons(local.proto) == ETH_P_IP) { | ||
355 | struct net_device *dev; | ||
356 | |||
357 | dev = __ip_dev_find(net, local.ipv4.s_addr, false); | ||
358 | if (!dev) { | ||
359 | err = -ENODEV; | ||
360 | goto err; | ||
361 | } | ||
362 | udp_conf.family = AF_INET; | ||
363 | udp_conf.local_ip.s_addr = htonl(INADDR_ANY); | ||
364 | udp_conf.use_udp_checksums = false; | ||
365 | ub->ifindex = dev->ifindex; | ||
366 | b->mtu = dev->mtu - sizeof(struct iphdr) | ||
367 | - sizeof(struct udphdr); | ||
368 | #if IS_ENABLED(CONFIG_IPV6) | ||
369 | } else if (htons(local.proto) == ETH_P_IPV6) { | ||
370 | udp_conf.family = AF_INET6; | ||
371 | udp_conf.use_udp6_tx_checksums = true; | ||
372 | udp_conf.use_udp6_rx_checksums = true; | ||
373 | udp_conf.local_ip6 = in6addr_any; | ||
374 | b->mtu = 1280; | ||
375 | #endif | ||
376 | } else { | ||
377 | err = -EAFNOSUPPORT; | ||
378 | goto err; | ||
379 | } | ||
380 | udp_conf.local_udp_port = local.udp_port; | ||
381 | err = udp_sock_create(net, &udp_conf, &ub->ubsock); | ||
382 | if (err) | ||
383 | goto err; | ||
384 | tuncfg.sk_user_data = ub; | ||
385 | tuncfg.encap_type = 1; | ||
386 | tuncfg.encap_rcv = tipc_udp_recv; | ||
387 | tuncfg.encap_destroy = NULL; | ||
388 | setup_udp_tunnel_sock(net, ub->ubsock, &tuncfg); | ||
389 | |||
390 | if (enable_mcast(ub, remote)) | ||
391 | goto err; | ||
392 | return 0; | ||
393 | err: | ||
394 | kfree(ub); | ||
395 | return err; | ||
396 | } | ||
397 | |||
398 | /* cleanup_bearer - break the socket/bearer association */ | ||
399 | static void cleanup_bearer(struct work_struct *work) | ||
400 | { | ||
401 | struct udp_bearer *ub = container_of(work, struct udp_bearer, work); | ||
402 | |||
403 | if (ub->ubsock) | ||
404 | udp_tunnel_sock_release(ub->ubsock); | ||
405 | synchronize_net(); | ||
406 | kfree(ub); | ||
407 | } | ||
408 | |||
409 | /* tipc_udp_disable - detach bearer from socket */ | ||
410 | static void tipc_udp_disable(struct tipc_bearer *b) | ||
411 | { | ||
412 | struct udp_bearer *ub; | ||
413 | |||
414 | ub = rcu_dereference_rtnl(b->media_ptr); | ||
415 | if (!ub) { | ||
416 | pr_err("UDP bearer instance not found\n"); | ||
417 | return; | ||
418 | } | ||
419 | if (ub->ubsock) | ||
420 | sock_set_flag(ub->ubsock->sk, SOCK_DEAD); | ||
421 | RCU_INIT_POINTER(b->media_ptr, NULL); | ||
422 | RCU_INIT_POINTER(ub->bearer, NULL); | ||
423 | |||
424 | /* sock_release need to be done outside of rtnl lock */ | ||
425 | INIT_WORK(&ub->work, cleanup_bearer); | ||
426 | schedule_work(&ub->work); | ||
427 | } | ||
428 | |||
429 | struct tipc_media udp_media_info = { | ||
430 | .send_msg = tipc_udp_send_msg, | ||
431 | .enable_media = tipc_udp_enable, | ||
432 | .disable_media = tipc_udp_disable, | ||
433 | .addr2str = tipc_udp_addr2str, | ||
434 | .addr2msg = tipc_udp_addr2msg, | ||
435 | .msg2addr = tipc_udp_msg2addr, | ||
436 | .priority = TIPC_DEF_LINK_PRI, | ||
437 | .tolerance = TIPC_DEF_LINK_TOL, | ||
438 | .window = TIPC_DEF_LINK_WIN, | ||
439 | .type_id = TIPC_MEDIA_TYPE_UDP, | ||
440 | .hwaddr_len = 0, | ||
441 | .name = "udp" | ||
442 | }; | ||