bpf: Invoke cgroup/connect{4,6} programs for unprivileged ICMP ping
authorYiFei Zhu <zhuyifei@google.com>
Fri, 9 Sep 2022 00:49:39 +0000 (00:49 +0000)
committerMartin KaFai Lau <martin.lau@kernel.org>
Fri, 9 Sep 2022 17:40:45 +0000 (10:40 -0700)
Usually when a TCP/UDP connection is initiated, we can bind the socket
to a specific IP attached to an interface in a cgroup/connect hook.
But for pings, this is impossible, as the hook is not being called.

This adds the hook invocation to unprivileged ICMP ping (i.e. ping
sockets created with SOCK_DGRAM IPPROTO_ICMP(V6) as opposed to
SOCK_RAW. Logic is mirrored from UDP sockets where the hook is invoked
during pre_connect, after a check for suficiently sized addr_len.

Signed-off-by: YiFei Zhu <zhuyifei@google.com>
Link: https://lore.kernel.org/r/5764914c252fad4cd134fb6664c6ede95f409412.1662682323.git.zhuyifei@google.com
Signed-off-by: Martin KaFai Lau <martin.lau@kernel.org>
net/ipv4/ping.c
net/ipv6/ping.c

index b83c2bd9d722388e948fc37a6388ce04fa8b0dbe..517042caf6dc10c46f6ddb349d99789e4f072382 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/skbuff.h>
 #include <linux/proc_fs.h>
 #include <linux/export.h>
+#include <linux/bpf-cgroup.h>
 #include <net/sock.h>
 #include <net/ping.h>
 #include <net/udp.h>
@@ -295,6 +296,19 @@ void ping_close(struct sock *sk, long timeout)
 }
 EXPORT_SYMBOL_GPL(ping_close);
 
+static int ping_pre_connect(struct sock *sk, struct sockaddr *uaddr,
+                           int addr_len)
+{
+       /* This check is replicated from __ip4_datagram_connect() and
+        * intended to prevent BPF program called below from accessing bytes
+        * that are out of the bound specified by user in addr_len.
+        */
+       if (addr_len < sizeof(struct sockaddr_in))
+               return -EINVAL;
+
+       return BPF_CGROUP_RUN_PROG_INET4_CONNECT_LOCK(sk, uaddr);
+}
+
 /* Checks the bind address and possibly modifies sk->sk_bound_dev_if. */
 static int ping_check_bind_addr(struct sock *sk, struct inet_sock *isk,
                                struct sockaddr *uaddr, int addr_len)
@@ -1009,6 +1023,7 @@ struct proto ping_prot = {
        .owner =        THIS_MODULE,
        .init =         ping_init_sock,
        .close =        ping_close,
+       .pre_connect =  ping_pre_connect,
        .connect =      ip4_datagram_connect,
        .disconnect =   __udp_disconnect,
        .setsockopt =   ip_setsockopt,
index 91b840514656901517f12757af04298c23d1af55..5f2ef84937142734f1df0d3b20ca1ae60ae5b70e 100644 (file)
@@ -20,6 +20,7 @@
 #include <net/udp.h>
 #include <net/transp_v6.h>
 #include <linux/proc_fs.h>
+#include <linux/bpf-cgroup.h>
 #include <net/ping.h>
 
 static void ping_v6_destroy(struct sock *sk)
@@ -49,6 +50,20 @@ static int dummy_ipv6_chk_addr(struct net *net, const struct in6_addr *addr,
        return 0;
 }
 
+static int ping_v6_pre_connect(struct sock *sk, struct sockaddr *uaddr,
+                              int addr_len)
+{
+       /* This check is replicated from __ip6_datagram_connect() and
+        * intended to prevent BPF program called below from accessing
+        * bytes that are out of the bound specified by user in addr_len.
+        */
+
+       if (addr_len < SIN6_LEN_RFC2133)
+               return -EINVAL;
+
+       return BPF_CGROUP_RUN_PROG_INET6_CONNECT_LOCK(sk, uaddr);
+}
+
 static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
 {
        struct inet_sock *inet = inet_sk(sk);
@@ -191,6 +206,7 @@ struct proto pingv6_prot = {
        .init =         ping_init_sock,
        .close =        ping_close,
        .destroy =      ping_v6_destroy,
+       .pre_connect =  ping_v6_pre_connect,
        .connect =      ip6_datagram_connect_v6_only,
        .disconnect =   __udp_disconnect,
        .setsockopt =   ipv6_setsockopt,