bpfilter: remove bpfilter
authorQuentin Deslandes <qde@naccy.de>
Tue, 26 Dec 2023 13:07:42 +0000 (14:07 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Thu, 4 Jan 2024 18:23:10 +0000 (10:23 -0800)
bpfilter was supposed to convert iptables filtering rules into
BPF programs on the fly, from the kernel, through a usermode
helper. The base code for the UMH was introduced in 2018, and
couple of attempts (2, 3) tried to introduce the BPF program
generate features but were abandoned.

bpfilter now sits in a kernel tree unused and unusable, occasionally
causing confusion amongst Linux users (4, 5).

As bpfilter is now developed in a dedicated repository on GitHub (6),
it was suggested a couple of times this year (LSFMM/BPF 2023,
LPC 2023) to remove the deprecated kernel part of the project. This
is the purpose of this patch.

[1]: https://lore.kernel.org/lkml/20180522022230.2492505-1-ast@kernel.org/
[2]: https://lore.kernel.org/bpf/20210829183608.2297877-1-me@ubique.spb.ru/#t
[3]: https://lore.kernel.org/lkml/20221224000402.476079-1-qde@naccy.de/
[4]: https://dxuuu.xyz/bpfilter.html
[5]: https://github.com/linuxkit/linuxkit/pull/3904
[6]: https://github.com/facebook/bpfilter

Signed-off-by: Quentin Deslandes <qde@naccy.de>
Link: https://lore.kernel.org/r/20231226130745.465988-1-qde@naccy.de
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
21 files changed:
arch/loongarch/configs/loongson3_defconfig
include/linux/bpfilter.h [deleted file]
include/uapi/linux/bpfilter.h [deleted file]
net/Kconfig
net/Makefile
net/bpfilter/.gitignore [deleted file]
net/bpfilter/Kconfig [deleted file]
net/bpfilter/Makefile [deleted file]
net/bpfilter/bpfilter_kern.c [deleted file]
net/bpfilter/bpfilter_umh_blob.S [deleted file]
net/bpfilter/main.c [deleted file]
net/bpfilter/msgfmt.h [deleted file]
net/ipv4/Makefile
net/ipv4/bpfilter/Makefile [deleted file]
net/ipv4/bpfilter/sockopt.c [deleted file]
net/ipv4/ip_sockglue.c
tools/bpf/bpftool/feature.c
tools/testing/selftests/bpf/config.aarch64
tools/testing/selftests/bpf/config.s390x
tools/testing/selftests/bpf/config.x86_64
tools/testing/selftests/hid/config

index 9c333d133c3033b552ba940c31cab056148f92ca..60e331af98398149df56cfed7bc7b27abe21c526 100644 (file)
@@ -276,7 +276,6 @@ CONFIG_BRIDGE_EBT_T_NAT=m
 CONFIG_BRIDGE_EBT_ARP=m
 CONFIG_BRIDGE_EBT_IP=m
 CONFIG_BRIDGE_EBT_IP6=m
-CONFIG_BPFILTER=y
 CONFIG_IP_SCTP=m
 CONFIG_RDS=y
 CONFIG_L2TP=m
diff --git a/include/linux/bpfilter.h b/include/linux/bpfilter.h
deleted file mode 100644 (file)
index 736ded4..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _LINUX_BPFILTER_H
-#define _LINUX_BPFILTER_H
-
-#include <uapi/linux/bpfilter.h>
-#include <linux/usermode_driver.h>
-#include <linux/sockptr.h>
-
-struct sock;
-int bpfilter_ip_set_sockopt(struct sock *sk, int optname, sockptr_t optval,
-                           unsigned int optlen);
-int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval,
-                           int __user *optlen);
-
-struct bpfilter_umh_ops {
-       struct umd_info info;
-       /* since ip_getsockopt() can run in parallel, serialize access to umh */
-       struct mutex lock;
-       int (*sockopt)(struct sock *sk, int optname, sockptr_t optval,
-                      unsigned int optlen, bool is_set);
-       int (*start)(void);
-};
-extern struct bpfilter_umh_ops bpfilter_ops;
-#endif
diff --git a/include/uapi/linux/bpfilter.h b/include/uapi/linux/bpfilter.h
deleted file mode 100644 (file)
index cbc1f58..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
-#ifndef _UAPI_LINUX_BPFILTER_H
-#define _UAPI_LINUX_BPFILTER_H
-
-#include <linux/if.h>
-
-enum {
-       BPFILTER_IPT_SO_SET_REPLACE = 64,
-       BPFILTER_IPT_SO_SET_ADD_COUNTERS = 65,
-       BPFILTER_IPT_SET_MAX,
-};
-
-enum {
-       BPFILTER_IPT_SO_GET_INFO = 64,
-       BPFILTER_IPT_SO_GET_ENTRIES = 65,
-       BPFILTER_IPT_SO_GET_REVISION_MATCH = 66,
-       BPFILTER_IPT_SO_GET_REVISION_TARGET = 67,
-       BPFILTER_IPT_GET_MAX,
-};
-
-#endif /* _UAPI_LINUX_BPFILTER_H */
index 3ec6bc98fa05d206ff28511f7d22e18ee8300bbe..4adc47d0c9c2ac94fd1ef1b9f4b5544426e3567b 100644 (file)
@@ -233,8 +233,6 @@ source "net/bridge/netfilter/Kconfig"
 
 endif
 
-source "net/bpfilter/Kconfig"
-
 source "net/dccp/Kconfig"
 source "net/sctp/Kconfig"
 source "net/rds/Kconfig"
index 4c4dc535453dff3a11d230ebde5dbdd03c687f2b..b06b5539e7a6b49146f6d21bf5d50fc53ad2d31e 100644 (file)
@@ -19,7 +19,6 @@ obj-$(CONFIG_TLS)             += tls/
 obj-$(CONFIG_XFRM)             += xfrm/
 obj-$(CONFIG_UNIX_SCM)         += unix/
 obj-y                          += ipv6/
-obj-$(CONFIG_BPFILTER)         += bpfilter/
 obj-$(CONFIG_PACKET)           += packet/
 obj-$(CONFIG_NET_KEY)          += key/
 obj-$(CONFIG_BRIDGE)           += bridge/
diff --git a/net/bpfilter/.gitignore b/net/bpfilter/.gitignore
deleted file mode 100644 (file)
index f34e85e..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-bpfilter_umh
diff --git a/net/bpfilter/Kconfig b/net/bpfilter/Kconfig
deleted file mode 100644 (file)
index 3d4a214..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-menuconfig BPFILTER
-       bool "BPF based packet filtering framework (BPFILTER)"
-       depends on BPF && INET
-       select USERMODE_DRIVER
-       help
-         This builds experimental bpfilter framework that is aiming to
-         provide netfilter compatible functionality via BPF
-
-if BPFILTER
-config BPFILTER_UMH
-       tristate "bpfilter kernel module with user mode helper"
-       depends on CC_CAN_LINK
-       depends on m || CC_CAN_LINK_STATIC
-       default m
-       help
-         This builds bpfilter kernel module with embedded user mode helper
-
-         Note: To compile this as built-in, your toolchain must support
-         building static binaries, since rootfs isn't mounted at the time
-         when __init functions are called and do_execv won't be able to find
-         the elf interpreter.
-endif
diff --git a/net/bpfilter/Makefile b/net/bpfilter/Makefile
deleted file mode 100644 (file)
index cdac82b..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-#
-# Makefile for the Linux BPFILTER layer.
-#
-
-userprogs := bpfilter_umh
-bpfilter_umh-objs := main.o
-userccflags += -I $(srctree)/tools/include/ -I $(srctree)/tools/include/uapi
-
-ifeq ($(CONFIG_BPFILTER_UMH), y)
-# builtin bpfilter_umh should be linked with -static
-# since rootfs isn't mounted at the time of __init
-# function is called and do_execv won't find elf interpreter
-userldflags += -static
-endif
-
-$(obj)/bpfilter_umh_blob.o: $(obj)/bpfilter_umh
-
-obj-$(CONFIG_BPFILTER_UMH) += bpfilter.o
-bpfilter-objs += bpfilter_kern.o bpfilter_umh_blob.o
diff --git a/net/bpfilter/bpfilter_kern.c b/net/bpfilter/bpfilter_kern.c
deleted file mode 100644 (file)
index 97e129e..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/umh.h>
-#include <linux/bpfilter.h>
-#include <linux/sched.h>
-#include <linux/sched/signal.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include "msgfmt.h"
-
-extern char bpfilter_umh_start;
-extern char bpfilter_umh_end;
-
-static void shutdown_umh(void)
-{
-       struct umd_info *info = &bpfilter_ops.info;
-       struct pid *tgid = info->tgid;
-
-       if (tgid) {
-               kill_pid(tgid, SIGKILL, 1);
-               wait_event(tgid->wait_pidfd, thread_group_exited(tgid));
-               umd_cleanup_helper(info);
-       }
-}
-
-static void __stop_umh(void)
-{
-       if (IS_ENABLED(CONFIG_INET))
-               shutdown_umh();
-}
-
-static int bpfilter_send_req(struct mbox_request *req)
-{
-       struct mbox_reply reply;
-       loff_t pos = 0;
-       ssize_t n;
-
-       if (!bpfilter_ops.info.tgid)
-               return -EFAULT;
-       pos = 0;
-       n = kernel_write(bpfilter_ops.info.pipe_to_umh, req, sizeof(*req),
-                          &pos);
-       if (n != sizeof(*req)) {
-               pr_err("write fail %zd\n", n);
-               goto stop;
-       }
-       pos = 0;
-       n = kernel_read(bpfilter_ops.info.pipe_from_umh, &reply, sizeof(reply),
-                       &pos);
-       if (n != sizeof(reply)) {
-               pr_err("read fail %zd\n", n);
-               goto stop;
-       }
-       return reply.status;
-stop:
-       __stop_umh();
-       return -EFAULT;
-}
-
-static int bpfilter_process_sockopt(struct sock *sk, int optname,
-                                   sockptr_t optval, unsigned int optlen,
-                                   bool is_set)
-{
-       struct mbox_request req = {
-               .is_set         = is_set,
-               .pid            = current->pid,
-               .cmd            = optname,
-               .addr           = (uintptr_t)optval.user,
-               .len            = optlen,
-       };
-       if (sockptr_is_kernel(optval)) {
-               pr_err("kernel access not supported\n");
-               return -EFAULT;
-       }
-       return bpfilter_send_req(&req);
-}
-
-static int start_umh(void)
-{
-       struct mbox_request req = { .pid = current->pid };
-       int err;
-
-       /* fork usermode process */
-       err = fork_usermode_driver(&bpfilter_ops.info);
-       if (err)
-               return err;
-       pr_info("Loaded bpfilter_umh pid %d\n", pid_nr(bpfilter_ops.info.tgid));
-
-       /* health check that usermode process started correctly */
-       if (bpfilter_send_req(&req) != 0) {
-               shutdown_umh();
-               return -EFAULT;
-       }
-
-       return 0;
-}
-
-static int __init load_umh(void)
-{
-       int err;
-
-       err = umd_load_blob(&bpfilter_ops.info,
-                           &bpfilter_umh_start,
-                           &bpfilter_umh_end - &bpfilter_umh_start);
-       if (err)
-               return err;
-
-       mutex_lock(&bpfilter_ops.lock);
-       err = start_umh();
-       if (!err && IS_ENABLED(CONFIG_INET)) {
-               bpfilter_ops.sockopt = &bpfilter_process_sockopt;
-               bpfilter_ops.start = &start_umh;
-       }
-       mutex_unlock(&bpfilter_ops.lock);
-       if (err)
-               umd_unload_blob(&bpfilter_ops.info);
-       return err;
-}
-
-static void __exit fini_umh(void)
-{
-       mutex_lock(&bpfilter_ops.lock);
-       if (IS_ENABLED(CONFIG_INET)) {
-               shutdown_umh();
-               bpfilter_ops.start = NULL;
-               bpfilter_ops.sockopt = NULL;
-       }
-       mutex_unlock(&bpfilter_ops.lock);
-
-       umd_unload_blob(&bpfilter_ops.info);
-}
-module_init(load_umh);
-module_exit(fini_umh);
-MODULE_LICENSE("GPL");
diff --git a/net/bpfilter/bpfilter_umh_blob.S b/net/bpfilter/bpfilter_umh_blob.S
deleted file mode 100644 (file)
index 40311d1..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-       .section .init.rodata, "a"
-       .global bpfilter_umh_start
-bpfilter_umh_start:
-       .incbin "net/bpfilter/bpfilter_umh"
-       .global bpfilter_umh_end
-bpfilter_umh_end:
diff --git a/net/bpfilter/main.c b/net/bpfilter/main.c
deleted file mode 100644 (file)
index 291a925..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#define _GNU_SOURCE
-#include <sys/uio.h>
-#include <errno.h>
-#include <stdio.h>
-#include <sys/socket.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include "../../include/uapi/linux/bpf.h"
-#include <asm/unistd.h>
-#include "msgfmt.h"
-
-FILE *debug_f;
-
-static int handle_get_cmd(struct mbox_request *cmd)
-{
-       switch (cmd->cmd) {
-       case 0:
-               return 0;
-       default:
-               break;
-       }
-       return -ENOPROTOOPT;
-}
-
-static int handle_set_cmd(struct mbox_request *cmd)
-{
-       return -ENOPROTOOPT;
-}
-
-static void loop(void)
-{
-       while (1) {
-               struct mbox_request req;
-               struct mbox_reply reply;
-               int n;
-
-               n = read(0, &req, sizeof(req));
-               if (n != sizeof(req)) {
-                       fprintf(debug_f, "invalid request %d\n", n);
-                       return;
-               }
-
-               reply.status = req.is_set ?
-                       handle_set_cmd(&req) :
-                       handle_get_cmd(&req);
-
-               n = write(1, &reply, sizeof(reply));
-               if (n != sizeof(reply)) {
-                       fprintf(debug_f, "reply failed %d\n", n);
-                       return;
-               }
-       }
-}
-
-int main(void)
-{
-       debug_f = fopen("/dev/kmsg", "w");
-       setvbuf(debug_f, 0, _IOLBF, 0);
-       fprintf(debug_f, "<5>Started bpfilter\n");
-       loop();
-       fclose(debug_f);
-       return 0;
-}
diff --git a/net/bpfilter/msgfmt.h b/net/bpfilter/msgfmt.h
deleted file mode 100644 (file)
index 98d121c..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _NET_BPFILTER_MSGFMT_H
-#define _NET_BPFILTER_MSGFMT_H
-
-struct mbox_request {
-       __u64 addr;
-       __u32 len;
-       __u32 is_set;
-       __u32 cmd;
-       __u32 pid;
-};
-
-struct mbox_reply {
-       __u32 status;
-};
-
-#endif
index e144a02a6a610aba585d94550da29cf974b6c78e..ec36d2ec059e800ec840e61a9e480e17c4c585ba 100644 (file)
@@ -16,8 +16,6 @@ obj-y     := route.o inetpeer.o protocol.o \
             inet_fragment.o ping.o ip_tunnel_core.o gre_offload.o \
             metrics.o netlink.o nexthop.o udp_tunnel_stub.o
 
-obj-$(CONFIG_BPFILTER) += bpfilter/
-
 obj-$(CONFIG_NET_IP_TUNNEL) += ip_tunnel.o
 obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o
 obj-$(CONFIG_PROC_FS) += proc.o
diff --git a/net/ipv4/bpfilter/Makefile b/net/ipv4/bpfilter/Makefile
deleted file mode 100644 (file)
index 00af530..0000000
+++ /dev/null
@@ -1,2 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_BPFILTER) += sockopt.o
diff --git a/net/ipv4/bpfilter/sockopt.c b/net/ipv4/bpfilter/sockopt.c
deleted file mode 100644 (file)
index 193bcc2..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/init.h>
-#include <linux/module.h>
-#include <linux/uaccess.h>
-#include <linux/bpfilter.h>
-#include <uapi/linux/bpf.h>
-#include <linux/wait.h>
-#include <linux/kmod.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-
-struct bpfilter_umh_ops bpfilter_ops;
-EXPORT_SYMBOL_GPL(bpfilter_ops);
-
-static int bpfilter_mbox_request(struct sock *sk, int optname, sockptr_t optval,
-                                unsigned int optlen, bool is_set)
-{
-       int err;
-       mutex_lock(&bpfilter_ops.lock);
-       if (!bpfilter_ops.sockopt) {
-               mutex_unlock(&bpfilter_ops.lock);
-               request_module("bpfilter");
-               mutex_lock(&bpfilter_ops.lock);
-
-               if (!bpfilter_ops.sockopt) {
-                       err = -ENOPROTOOPT;
-                       goto out;
-               }
-       }
-       if (bpfilter_ops.info.tgid &&
-           thread_group_exited(bpfilter_ops.info.tgid))
-               umd_cleanup_helper(&bpfilter_ops.info);
-
-       if (!bpfilter_ops.info.tgid) {
-               err = bpfilter_ops.start();
-               if (err)
-                       goto out;
-       }
-       err = bpfilter_ops.sockopt(sk, optname, optval, optlen, is_set);
-out:
-       mutex_unlock(&bpfilter_ops.lock);
-       return err;
-}
-
-int bpfilter_ip_set_sockopt(struct sock *sk, int optname, sockptr_t optval,
-                           unsigned int optlen)
-{
-       return bpfilter_mbox_request(sk, optname, optval, optlen, true);
-}
-
-int bpfilter_ip_get_sockopt(struct sock *sk, int optname, char __user *optval,
-                           int __user *optlen)
-{
-       int len;
-
-       if (get_user(len, optlen))
-               return -EFAULT;
-
-       return bpfilter_mbox_request(sk, optname, USER_SOCKPTR(optval), len,
-                                    false);
-}
-
-static int __init bpfilter_sockopt_init(void)
-{
-       mutex_init(&bpfilter_ops.lock);
-       bpfilter_ops.info.tgid = NULL;
-       bpfilter_ops.info.driver_name = "bpfilter_umh";
-
-       return 0;
-}
-device_initcall(bpfilter_sockopt_init);
index 66247e8b429e43c8fd5887d651e1a98d31453901..7aa9dc0e6760df6c9980252854014ab6fdd1c3f7 100644 (file)
@@ -47,8 +47,6 @@
 #include <linux/errqueue.h>
 #include <linux/uaccess.h>
 
-#include <linux/bpfilter.h>
-
 /*
  *     SOL_IP control messages.
  */
@@ -1411,11 +1409,6 @@ int ip_setsockopt(struct sock *sk, int level, int optname, sockptr_t optval,
                return -ENOPROTOOPT;
 
        err = do_ip_setsockopt(sk, level, optname, optval, optlen);
-#if IS_ENABLED(CONFIG_BPFILTER_UMH)
-       if (optname >= BPFILTER_IPT_SO_SET_REPLACE &&
-           optname < BPFILTER_IPT_SET_MAX)
-               err = bpfilter_ip_set_sockopt(sk, optname, optval, optlen);
-#endif
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default case */
        if (err == -ENOPROTOOPT && optname != IP_HDRINCL &&
@@ -1763,11 +1756,6 @@ int ip_getsockopt(struct sock *sk, int level,
        err = do_ip_getsockopt(sk, level, optname,
                               USER_SOCKPTR(optval), USER_SOCKPTR(optlen));
 
-#if IS_ENABLED(CONFIG_BPFILTER_UMH)
-       if (optname >= BPFILTER_IPT_SO_GET_INFO &&
-           optname < BPFILTER_IPT_GET_MAX)
-               err = bpfilter_ip_get_sockopt(sk, optname, optval, optlen);
-#endif
 #ifdef CONFIG_NETFILTER
        /* we need to exclude all possible ENOPROTOOPTs except default case */
        if (err == -ENOPROTOOPT && optname != IP_PKTOPTIONS &&
index edda4fc2c4d03baa5af5ff53bd3b99bfed9c3cb5..708733b0ea061ec46c01797998bb45f7a0840e9c 100644 (file)
@@ -426,10 +426,6 @@ static void probe_kernel_image_config(const char *define_prefix)
                { "CONFIG_BPF_STREAM_PARSER", },
                /* xt_bpf module for passing BPF programs to netfilter  */
                { "CONFIG_NETFILTER_XT_MATCH_BPF", },
-               /* bpfilter back-end for iptables */
-               { "CONFIG_BPFILTER", },
-               /* bpftilter module with "user mode helper" */
-               { "CONFIG_BPFILTER_UMH", },
 
                /* test_bpf module for BPF tests */
                { "CONFIG_TEST_BPF", },
index 29c8635c572200468ce0dde71655904f5fb4253e..3720b7611523b3cdfa1a7c4a7de5ca63311ebe67 100644 (file)
@@ -11,7 +11,6 @@ CONFIG_BLK_DEV_IO_TRACE=y
 CONFIG_BLK_DEV_RAM=y
 CONFIG_BLK_DEV_SD=y
 CONFIG_BONDING=y
-CONFIG_BPFILTER=y
 CONFIG_BPF_JIT_ALWAYS_ON=y
 CONFIG_BPF_JIT_DEFAULT_ON=y
 CONFIG_BPF_PRELOAD_UMD=y
index e933303828494858918694589babfa13796fabc2..706931a8c2c69d0f70c8678614ea7dee17a8044a 100644 (file)
@@ -9,7 +9,6 @@ CONFIG_BPF_JIT_ALWAYS_ON=y
 CONFIG_BPF_JIT_DEFAULT_ON=y
 CONFIG_BPF_PRELOAD=y
 CONFIG_BPF_PRELOAD_UMD=y
-CONFIG_BPFILTER=y
 CONFIG_CGROUP_CPUACCT=y
 CONFIG_CGROUP_DEVICE=y
 CONFIG_CGROUP_FREEZER=y
index b946088017f1d40c3367af5c24668f9a8a6c075e..5680befae8c6d17692704e6b9410166392587dc1 100644 (file)
@@ -19,7 +19,6 @@ CONFIG_BOOTTIME_TRACING=y
 CONFIG_BPF_JIT_ALWAYS_ON=y
 CONFIG_BPF_PRELOAD=y
 CONFIG_BPF_PRELOAD_UMD=y
-CONFIG_BPFILTER=y
 CONFIG_BSD_DISKLABEL=y
 CONFIG_BSD_PROCESS_ACCT=y
 CONFIG_CFS_BANDWIDTH=y
index 4f425178b56faccd61ec40b3720c7e2c30f03462..1758b055f29526f530631e05187259827d84a61c 100644 (file)
@@ -1,5 +1,4 @@
 CONFIG_BPF_EVENTS=y
-CONFIG_BPFILTER=y
 CONFIG_BPF_JIT_ALWAYS_ON=y
 CONFIG_BPF_JIT=y
 CONFIG_BPF_KPROBE_OVERRIDE=y