#include <linux/namei.h>
 #include <linux/mm.h>
 #include <linux/module.h>
+#include <linux/bpf-cgroup.h>
 #include "internal.h"
 
 static const struct dentry_operations proc_sys_dentry_operations;
        if (!table->proc_handler)
                goto out;
 
+       error = BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write);
+       if (error)
+               goto out;
+
        /* careful: calling conventions are nasty here */
        res = count;
        error = table->proc_handler(table, write, buf, &res, ppos);
 
 struct bpf_prog;
 struct bpf_sock_ops_kern;
 struct bpf_cgroup_storage;
+struct ctl_table;
+struct ctl_table_header;
 
 #ifdef CONFIG_CGROUP_BPF
 
 int __cgroup_bpf_check_dev_permission(short dev_type, u32 major, u32 minor,
                                      short access, enum bpf_attach_type type);
 
+int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
+                                  struct ctl_table *table, int write,
+                                  enum bpf_attach_type type);
+
 static inline enum bpf_cgroup_storage_type cgroup_storage_type(
        struct bpf_map *map)
 {
                                                                              \
        __ret;                                                                \
 })
+
+
+#define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write)                        \
+({                                                                            \
+       int __ret = 0;                                                         \
+       if (cgroup_bpf_enabled)                                                \
+               __ret = __cgroup_bpf_run_filter_sysctl(head, table, write,     \
+                                                      BPF_CGROUP_SYSCTL);     \
+       __ret;                                                                 \
+})
+
 int cgroup_bpf_prog_attach(const union bpf_attr *attr,
                           enum bpf_prog_type ptype, struct bpf_prog *prog);
 int cgroup_bpf_prog_detach(const union bpf_attr *attr,
 #define BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, uaddr, t_ctx) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_SOCK_OPS(sock_ops) ({ 0; })
 #define BPF_CGROUP_RUN_PROG_DEVICE_CGROUP(type,major,minor,access) ({ 0; })
+#define BPF_CGROUP_RUN_PROG_SYSCTL(head, table, write) ({ 0; })
 
 #define for_each_cgroup_storage_type(stype) for (; false; )
 
 
 #endif
 #ifdef CONFIG_CGROUP_BPF
 BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_DEVICE, cg_dev)
+BPF_PROG_TYPE(BPF_PROG_TYPE_CGROUP_SYSCTL, cg_sysctl)
 #endif
 #ifdef CONFIG_BPF_LIRC_MODE2
 BPF_PROG_TYPE(BPF_PROG_TYPE_LIRC_MODE2, lirc_mode2)
 
 struct xdp_rxq_info;
 struct xdp_buff;
 struct sock_reuseport;
+struct ctl_table;
+struct ctl_table_header;
 
 /* ArgX, context and stack frame pointer register positions. Note,
  * Arg1, Arg2, Arg3, etc are used as argument mappings of function
                                         */
 };
 
+struct bpf_sysctl_kern {
+       struct ctl_table_header *head;
+       struct ctl_table *table;
+       int write;
+};
+
 #endif /* __LINUX_FILTER_H__ */
 
        BPF_PROG_TYPE_LIRC_MODE2,
        BPF_PROG_TYPE_SK_REUSEPORT,
        BPF_PROG_TYPE_FLOW_DISSECTOR,
+       BPF_PROG_TYPE_CGROUP_SYSCTL,
 };
 
 enum bpf_attach_type {
        BPF_CGROUP_UDP6_SENDMSG,
        BPF_LIRC_MODE2,
        BPF_FLOW_DISSECTOR,
+       BPF_CGROUP_SYSCTL,
        __MAX_BPF_ATTACH_TYPE
 };
 
 struct bpf_spin_lock {
        __u32   val;
 };
+
+struct bpf_sysctl {
+       __u32   write;          /* Sysctl is being read (= 0) or written (= 1).
+                                * Allows 1,2,4-byte read, but no write.
+                                */
+};
+
 #endif /* _UAPI__LINUX_BPF_H__ */
 
 #include <linux/kernel.h>
 #include <linux/atomic.h>
 #include <linux/cgroup.h>
+#include <linux/filter.h>
 #include <linux/slab.h>
+#include <linux/sysctl.h>
 #include <linux/bpf.h>
 #include <linux/bpf-cgroup.h>
 #include <net/sock.h>
        .get_func_proto         = cgroup_dev_func_proto,
        .is_valid_access        = cgroup_dev_is_valid_access,
 };
+
+/**
+ * __cgroup_bpf_run_filter_sysctl - Run a program on sysctl
+ *
+ * @head: sysctl table header
+ * @table: sysctl table
+ * @write: sysctl is being read (= 0) or written (= 1)
+ * @type: type of program to be executed
+ *
+ * Program is run when sysctl is being accessed, either read or written, and
+ * can allow or deny such access.
+ *
+ * This function will return %-EPERM if an attached program is found and
+ * returned value != 1 during execution. In all other cases 0 is returned.
+ */
+int __cgroup_bpf_run_filter_sysctl(struct ctl_table_header *head,
+                                  struct ctl_table *table, int write,
+                                  enum bpf_attach_type type)
+{
+       struct bpf_sysctl_kern ctx = {
+               .head = head,
+               .table = table,
+               .write = write,
+       };
+       struct cgroup *cgrp;
+       int ret;
+
+       rcu_read_lock();
+       cgrp = task_dfl_cgroup(current);
+       ret = BPF_PROG_RUN_ARRAY(cgrp->bpf.effective[type], &ctx, BPF_PROG_RUN);
+       rcu_read_unlock();
+
+       return ret == 1 ? 0 : -EPERM;
+}
+EXPORT_SYMBOL(__cgroup_bpf_run_filter_sysctl);
+
+static const struct bpf_func_proto *
+sysctl_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
+{
+       return cgroup_base_func_proto(func_id, prog);
+}
+
+static bool sysctl_is_valid_access(int off, int size, enum bpf_access_type type,
+                                  const struct bpf_prog *prog,
+                                  struct bpf_insn_access_aux *info)
+{
+       const int size_default = sizeof(__u32);
+
+       if (off < 0 || off + size > sizeof(struct bpf_sysctl) ||
+           off % size || type != BPF_READ)
+               return false;
+
+       switch (off) {
+       case offsetof(struct bpf_sysctl, write):
+               bpf_ctx_record_field_size(info, size_default);
+               return bpf_ctx_narrow_access_ok(off, size, size_default);
+       default:
+               return false;
+       }
+}
+
+static u32 sysctl_convert_ctx_access(enum bpf_access_type type,
+                                    const struct bpf_insn *si,
+                                    struct bpf_insn *insn_buf,
+                                    struct bpf_prog *prog, u32 *target_size)
+{
+       struct bpf_insn *insn = insn_buf;
+
+       switch (si->off) {
+       case offsetof(struct bpf_sysctl, write):
+               *insn++ = BPF_LDX_MEM(
+                       BPF_SIZE(si->code), si->dst_reg, si->src_reg,
+                       bpf_target_off(struct bpf_sysctl_kern, write,
+                                      FIELD_SIZEOF(struct bpf_sysctl_kern,
+                                                   write),
+                                      target_size));
+               break;
+       }
+
+       return insn - insn_buf;
+}
+
+const struct bpf_verifier_ops cg_sysctl_verifier_ops = {
+       .get_func_proto         = sysctl_func_proto,
+       .is_valid_access        = sysctl_is_valid_access,
+       .convert_ctx_access     = sysctl_convert_ctx_access,
+};
+
+const struct bpf_prog_ops cg_sysctl_prog_ops = {
+};
 
        case BPF_FLOW_DISSECTOR:
                ptype = BPF_PROG_TYPE_FLOW_DISSECTOR;
                break;
+       case BPF_CGROUP_SYSCTL:
+               ptype = BPF_PROG_TYPE_CGROUP_SYSCTL;
+               break;
        default:
                return -EINVAL;
        }
                return lirc_prog_detach(attr);
        case BPF_FLOW_DISSECTOR:
                return skb_flow_dissector_bpf_prog_detach(attr);
+       case BPF_CGROUP_SYSCTL:
+               ptype = BPF_PROG_TYPE_CGROUP_SYSCTL;
+               break;
        default:
                return -EINVAL;
        }
        case BPF_CGROUP_UDP6_SENDMSG:
        case BPF_CGROUP_SOCK_OPS:
        case BPF_CGROUP_DEVICE:
+       case BPF_CGROUP_SYSCTL:
                break;
        case BPF_LIRC_MODE2:
                return lirc_prog_query(attr, uattr);
 
        case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
        case BPF_PROG_TYPE_SOCK_OPS:
        case BPF_PROG_TYPE_CGROUP_DEVICE:
+       case BPF_PROG_TYPE_CGROUP_SYSCTL:
                break;
        default:
                return 0;