[Openvpn-devel,ovpn,net,1/4] ovpn: avoid sending UDP packets with source port 0
Commit Message
ovpn operates on a userspace-owned UDP socket, which may be manipulated
in various ways by userspace. If the socket is never bound, connected,
or used for communication, it may not have a source port assigned.
Similarly, if the socket was connect()'ed to AF_INET or AF_INET6, it can
be disconnected by connect() with AF_UNSPEC, which resets the source
port unless the socket was explicitly bound.
Since we must not transmit packets with source port 0, gate UDP TX on
the presence of a valid source port and drop packets otherwise. To avoid
ambiguity, sample the current source port once before route lookup and
header build and enforce the check on that value.
Fixes: 08857b5ec5d9 ("ovpn: implement basic TX path (UDP)")
Signed-off-by: Ralf Lici <ralf@mandelbit.com>
---
drivers/net/ovpn/udp.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
@@ -149,13 +149,17 @@ static int ovpn_udp4_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
struct flowi4 fl = {
.saddr = bind->local.ipv4.s_addr,
.daddr = bind->remote.in4.sin_addr.s_addr,
- .fl4_sport = inet_sk(sk)->inet_sport,
+ .fl4_sport = READ_ONCE(inet_sk(sk)->inet_sport),
.fl4_dport = bind->remote.in4.sin_port,
.flowi4_proto = sk->sk_protocol,
.flowi4_mark = sk->sk_mark,
};
int ret;
+ /* an uninitialized socket or connect(AF_UNSPEC) can cause this */
+ if (unlikely(!fl.fl4_sport))
+ return -EADDRNOTAVAIL;
+
local_bh_disable();
rt = dst_cache_get_ip4(cache, &fl.saddr);
if (rt)
@@ -226,13 +230,17 @@ static int ovpn_udp6_output(struct ovpn_peer *peer, struct ovpn_bind *bind,
struct flowi6 fl = {
.saddr = bind->local.ipv6,
.daddr = bind->remote.in6.sin6_addr,
- .fl6_sport = inet_sk(sk)->inet_sport,
+ .fl6_sport = READ_ONCE(inet_sk(sk)->inet_sport),
.fl6_dport = bind->remote.in6.sin6_port,
.flowi6_proto = sk->sk_protocol,
.flowi6_mark = sk->sk_mark,
.flowi6_oif = bind->remote.in6.sin6_scope_id,
};
+ /* an uninitialized socket or connect(AF_UNSPEC) can cause this */
+ if (unlikely(!fl.fl6_sport))
+ return -EADDRNOTAVAIL;
+
local_bh_disable();
dst = dst_cache_get_ip6(cache, &fl.saddr);
if (dst)