There are use-cases where a host wants to use a UDP socket with a specific 4-tuple. The way to do this is to bind() and then connect() the socket. However, after the bind(), the socket starts receiving data even if it does not match the intended 4-tuple. That is because after the bind() UDP-socket will match in the lookup for all incoming UDP-traffic that has the specific IP/port.
This patch prevents any incoming traffic until the connect() system-call is called whenever the app sets the UDP socket-option UDP_WAIT_FOR_CONNECT. Signed-off-by: Christoph Paasch <cpaa...@apple.com> --- Notes: Changes compared to the original RFC-submission: * Make it a UDP-specific socket-option * Rename it to 'wait-for-connect' Wrt to the discussion on the original RFC submission (cfr., https://marc.info/?l=linux-netdev&m=154102843910587&w=2): We believe that this patch is still useful to enable applications to use different models of implementing a UDP-server. For some frameworks it is much easier to have a socket per-connection and thus let the de-multiplexing happen in the kernel instead of the app. include/linux/udp.h | 5 ++++- include/net/udp.h | 1 + include/uapi/linux/udp.h | 1 + net/ipv4/udp.c | 26 +++++++++++++++++++++++++- net/ipv4/udplite.c | 2 +- net/ipv6/udp.c | 15 ++++++++++++++- net/ipv6/udp_impl.h | 1 + net/ipv6/udplite.c | 2 +- 8 files changed, 48 insertions(+), 5 deletions(-) diff --git a/include/linux/udp.h b/include/linux/udp.h index 2725c83395bf..9a715d25ce36 100644 --- a/include/linux/udp.h +++ b/include/linux/udp.h @@ -55,7 +55,10 @@ struct udp_sock { * different encapsulation layer set * this */ - gro_enabled:1; /* Can accept GRO packets */ + gro_enabled:1, /* Can accept GRO packets */ + wait_for_connect:1;/* Wait until app calls connect() + * before accepting incoming data + */ /* * Following member retains the information to create a UDP header * when the socket is uncorked. diff --git a/include/net/udp.h b/include/net/udp.h index fd6d948755c8..b7467a4129b3 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -285,6 +285,7 @@ int udp_get_port(struct sock *sk, unsigned short snum, const struct sock *)); int udp_err(struct sk_buff *, u32); int udp_abort(struct sock *sk, int err); +int udp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len); int udp_push_pending_frames(struct sock *sk); void udp_flush_pending_frames(struct sock *sk); diff --git a/include/uapi/linux/udp.h b/include/uapi/linux/udp.h index 30baccb6c9c4..b61f8e8dd80b 100644 --- a/include/uapi/linux/udp.h +++ b/include/uapi/linux/udp.h @@ -34,6 +34,7 @@ struct udphdr { #define UDP_NO_CHECK6_RX 102 /* Disable accpeting checksum for UDP6 */ #define UDP_SEGMENT 103 /* Set GSO segmentation size */ #define UDP_GRO 104 /* This socket can receive UDP GRO packets */ +#define UDP_WAIT_FOR_CONNECT 105 /* Don't accept incoming data until the app calls connect() */ /* UDP encapsulation types */ #define UDP_ENCAP_ESPINUDP_NON_IKE 1 /* draft-ietf-ipsec-nat-t-ike-00/01 */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index aff2a8e99e01..c5adafcfd52f 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -407,6 +407,9 @@ static int compute_score(struct sock *sk, struct net *net, return -1; score += 4; + if (udp_sk(sk)->wait_for_connect) + return -1; + if (sk->sk_incoming_cpu == raw_smp_processor_id()) score++; return score; @@ -2601,6 +2604,10 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, release_sock(sk); break; + case UDP_WAIT_FOR_CONNECT: + up->wait_for_connect = valbool; + break; + /* * UDP-Lite's partial checksum coverage (RFC 3828). */ @@ -2695,6 +2702,10 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname, val = up->gso_size; break; + case UDP_WAIT_FOR_CONNECT: + val = up->wait_for_connect; + break; + /* The following two cannot be changed on UDP sockets, the return is * always 0 (which corresponds to the full checksum coverage of UDP). */ case UDPLITE_SEND_CSCOV: @@ -2779,12 +2790,25 @@ int udp_abort(struct sock *sk, int err) } EXPORT_SYMBOL_GPL(udp_abort); +int udp_v4_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) +{ + int ret; + + ret = ip4_datagram_connect(sk, uaddr, addr_len); + + if (!ret) + udp_sk(sk)->wait_for_connect = 0; + + return ret; +} +EXPORT_SYMBOL_GPL(udp_v4_connect); + struct proto udp_prot = { .name = "UDP", .owner = THIS_MODULE, .close = udp_lib_close, .pre_connect = udp_pre_connect, - .connect = ip4_datagram_connect, + .connect = udp_v4_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, .init = udp_init_sock, diff --git a/net/ipv4/udplite.c b/net/ipv4/udplite.c index 39c7f17d916f..22c74822ff30 100644 --- a/net/ipv4/udplite.c +++ b/net/ipv4/udplite.c @@ -41,7 +41,7 @@ struct proto udplite_prot = { .name = "UDP-Lite", .owner = THIS_MODULE, .close = udp_lib_close, - .connect = ip4_datagram_connect, + .connect = udp_v4_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, .init = udplite_sk_init, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 09cba4cfe31f..c3a9101380de 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1681,6 +1681,19 @@ void udp6_proc_exit(struct net *net) } #endif /* CONFIG_PROC_FS */ +int udp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) +{ + int ret; + + ret = ip6_datagram_connect(sk, uaddr, addr_len); + + if (!ret) + udp_sk(sk)->wait_for_connect = 0; + + return ret; +} +EXPORT_SYMBOL_GPL(udp_v6_connect); + /* ------------------------------------------------------------------------ */ struct proto udpv6_prot = { @@ -1688,7 +1701,7 @@ struct proto udpv6_prot = { .owner = THIS_MODULE, .close = udp_lib_close, .pre_connect = udpv6_pre_connect, - .connect = ip6_datagram_connect, + .connect = udp_v6_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, .init = udp_init_sock, diff --git a/net/ipv6/udp_impl.h b/net/ipv6/udp_impl.h index 5730e6503cb4..14e847b63db9 100644 --- a/net/ipv6/udp_impl.h +++ b/net/ipv6/udp_impl.h @@ -14,6 +14,7 @@ int __udp6_lib_err(struct sk_buff *, struct inet6_skb_parm *, u8, u8, int, int udp_v6_get_port(struct sock *sk, unsigned short snum); +int udp_v6_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len); int udpv6_getsockopt(struct sock *sk, int level, int optname, char __user *optval, int __user *optlen); int udpv6_setsockopt(struct sock *sk, int level, int optname, diff --git a/net/ipv6/udplite.c b/net/ipv6/udplite.c index a125aebc29e5..035e1b2f2529 100644 --- a/net/ipv6/udplite.c +++ b/net/ipv6/udplite.c @@ -38,7 +38,7 @@ struct proto udplitev6_prot = { .name = "UDPLITEv6", .owner = THIS_MODULE, .close = udp_lib_close, - .connect = ip6_datagram_connect, + .connect = udp_v6_connect, .disconnect = udp_disconnect, .ioctl = udp_ioctl, .init = udplite_sk_init, -- 2.16.2