hulk inclusion
category: feature
bugzilla: NA
CVE: N/A
----------------------------------------------------
Some network acceleration solutions, such as sockmap, are valid only for
internal packets of the local host. The bpf_is_local_ipaddr() bpf helper
function is added so that the ebpf program can determine whether a packet
is an internal packet of the local host.
Signed-off-by: Liu Jian <liujian56(a)huawei.com>
---
arch/arm64/configs/openeuler_defconfig | 1 +
arch/x86/configs/openeuler_defconfig | 1 +
drivers/net/Kconfig | 8 ++
drivers/net/Makefile | 1 +
drivers/net/localip/Makefile | 8 ++
drivers/net/localip/localip.c | 146 +++++++++++++++++++++++++
include/uapi/linux/bpf.h | 7 ++
net/core/filter.c | 20 ++++
tools/include/uapi/linux/bpf.h | 7 ++
9 files changed, 199 insertions(+)
create mode 100644 drivers/net/localip/Makefile
create mode 100644 drivers/net/localip/localip.c
diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig
index f934557ff765..b2d06797c08a 100644
--- a/arch/arm64/configs/openeuler_defconfig
+++ b/arch/arm64/configs/openeuler_defconfig
@@ -3131,6 +3131,7 @@ CONFIG_DLCI_MAX=8
CONFIG_USB4_NET=m
# CONFIG_NETDEVSIM is not set
CONFIG_NET_FAILOVER=m
+CONFIG_NET_LOCALIP_LST=m
# CONFIG_ISDN is not set
#
diff --git a/arch/x86/configs/openeuler_defconfig b/arch/x86/configs/openeuler_defconfig
index 11323cdc3301..4f2682d4b71e 100644
--- a/arch/x86/configs/openeuler_defconfig
+++ b/arch/x86/configs/openeuler_defconfig
@@ -3206,6 +3206,7 @@ CONFIG_USB4_NET=m
CONFIG_HYPERV_NET=m
CONFIG_NETDEVSIM=m
CONFIG_NET_FAILOVER=m
+CONFIG_NET_LOCALIP_LST=m
CONFIG_ISDN=y
CONFIG_ISDN_CAPI=y
CONFIG_CAPI_TRACE=y
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index f20808024305..913acb7eed6f 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -592,4 +592,12 @@ config NET_FAILOVER
a VM with direct attached VF by failing over to the paravirtual
datapath when the VF is unplugged.
+config NET_LOCALIP_LST
+ tristate "Collect local ipv4 address"
+ depends on INET
+ default n
+ help
+ Similar to inet_addr_lst, only the IP address is recorded, and
+ net_namespace is not concerned.
+
endif # NETDEVICES
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index 72e18d505d1a..b4ff8a310dcc 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -84,3 +84,4 @@ thunderbolt-net-y += thunderbolt.o
obj-$(CONFIG_USB4_NET) += thunderbolt-net.o
obj-$(CONFIG_NETDEVSIM) += netdevsim/
obj-$(CONFIG_NET_FAILOVER) += net_failover.o
+obj-$(CONFIG_NET_LOCALIP_LST) += localip/
diff --git a/drivers/net/localip/Makefile b/drivers/net/localip/Makefile
new file mode 100644
index 000000000000..03b535709271
--- /dev/null
+++ b/drivers/net/localip/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the linux kernel.
+#
+
+# Object file lists.
+
+obj-$(CONFIG_NET_LOCALIP_LST) += localip.o
diff --git a/drivers/net/localip/localip.c b/drivers/net/localip/localip.c
new file mode 100644
index 000000000000..3756924553bf
--- /dev/null
+++ b/drivers/net/localip/localip.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright(c) 2023 Huawei Technologies Co., Ltd
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/inetdevice.h>
+#include <linux/spinlock.h>
+
+#define IN4_ADDR_HSIZE_SHIFT 8
+#define IN4_ADDR_HSIZE (1U << IN4_ADDR_HSIZE_SHIFT)
+
+static struct hlist_head localip_lst[IN4_ADDR_HSIZE];
+
+static DEFINE_SPINLOCK(localip_lock);
+
+struct localipaddr {
+ struct hlist_node node;
+ struct rcu_head rcu;
+ __u32 ipaddr;
+};
+
+static u32 localip_hash(__be32 addr)
+{
+ return hash_32(addr, IN4_ADDR_HSIZE_SHIFT);
+}
+
+static void localip_hash_insert(struct localipaddr *ip)
+{
+ u32 hash = localip_hash(ip->ipaddr);
+
+ hlist_add_head_rcu(&ip->node, &localip_lst[hash]);
+}
+
+static void localip_hash_remove(struct localipaddr *ip)
+{
+ hlist_del_init_rcu(&ip->node);
+}
+
+static int is_local_ipaddr(uint32_t ipaddr)
+{
+ u32 hash = localip_hash(ipaddr);
+ struct localipaddr *localip;
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(localip, &localip_lst[hash], node) {
+ if (localip->ipaddr == ipaddr) {
+ rcu_read_unlock();
+ return 1;
+ }
+ }
+ rcu_read_unlock();
+
+ return 0;
+}
+
+static int localip_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ struct in_ifaddr *ifa = ptr;
+ struct net_device *event_netdev = ifa->ifa_dev->dev;
+ struct localipaddr *localip;
+ u32 hash;
+
+ if (ipv4_is_loopback(ifa->ifa_local))
+ return NOTIFY_DONE;
+
+ switch (event) {
+ case NETDEV_UP:
+ pr_debug("UP, dev:%s, ip:0x%x, mask:0x%x\n", event_netdev->name,
+ ifa->ifa_local, ifa->ifa_mask);
+ localip = kzalloc(sizeof(struct localipaddr), GFP_KERNEL);
+ if (!localip) {
+ pr_err("kzalloc failed.\n");
+ break;
+ }
+ localip->ipaddr = ifa->ifa_local;
+ spin_lock(&localip_lock);
+ localip_hash_insert(localip);
+ spin_unlock(&localip_lock);
+ break;
+ case NETDEV_DOWN:
+ pr_debug("DOWN, dev:%s, ip:0x%x, mask:0x%x\n", event_netdev->name,
+ ifa->ifa_local, ifa->ifa_mask);
+ hash = localip_hash(ifa->ifa_local);
+ spin_lock(&localip_lock);
+ hlist_for_each_entry(localip, &localip_lst[hash], node) {
+ if (localip->ipaddr == ifa->ifa_local) {
+ localip_hash_remove(localip);
+ kfree_rcu(localip, rcu);
+ break;
+ }
+ }
+ spin_unlock(&localip_lock);
+ break;
+ default:
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block localip_notifier = {
+ .notifier_call = localip_event,
+};
+
+typedef int (*is_local_ipaddr_func)(uint32_t ipaddr);
+extern is_local_ipaddr_func bpf_is_local_ipaddr_func;
+
+int localip_init(void)
+{
+ int i;
+
+ for (i = 0; i < IN4_ADDR_HSIZE; i++)
+ INIT_HLIST_HEAD(&localip_lst[i]);
+
+ register_inetaddr_notifier(&localip_notifier);
+ bpf_is_local_ipaddr_func = is_local_ipaddr;
+ return 0;
+}
+
+void localip_cleanup(void)
+{
+ struct localipaddr *localip;
+ struct hlist_node *n;
+ int i;
+
+ bpf_is_local_ipaddr_func = NULL;
+ unregister_inetaddr_notifier(&localip_notifier);
+
+ spin_lock(&localip_lock);
+ for (i = 0; i < IN4_ADDR_HSIZE; i++) {
+ hlist_for_each_entry_safe(localip, n, &localip_lst[i], node) {
+ pr_debug("cleanup, hash:%i, ip:0x%x\n", i, localip->ipaddr);
+ localip_hash_remove(localip);
+ kfree_rcu(localip, rcu);
+ }
+ }
+ spin_unlock(&localip_lock);
+ synchronize_rcu();
+}
+
+module_init(localip_init);
+module_exit(localip_cleanup);
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 9e64dac44d60..9373abafcb91 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -3872,6 +3872,12 @@ union bpf_attr {
* check src_cpu whether share cache with dst_cpu.
* Return
* yes 1, no 0.
+ *
+ * long bpf_is_local_ipaddr(u32 ipaddr)
+ * Description
+ * Check the ipaddr is local address or not.
+ * Return
+ * 1 is local address, 0 is not.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -4044,6 +4050,7 @@ union bpf_attr {
FN(sched_entity_to_tg), \
FN(cpumask_op), \
FN(cpus_share_cache), \
+ FN(is_local_ipaddr), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
diff --git a/net/core/filter.c b/net/core/filter.c
index 012a5070a9e5..527b66921dd6 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -5084,6 +5084,24 @@ static const struct bpf_func_proto bpf_sk_original_addr_proto = {
.arg4_type = ARG_CONST_SIZE,
};
+typedef int (*is_local_ipaddr_func)(uint32_t ipaddr);
+is_local_ipaddr_func bpf_is_local_ipaddr_func;
+EXPORT_SYMBOL(bpf_is_local_ipaddr_func);
+
+BPF_CALL_1(bpf_is_local_ipaddr, uint32_t, ipaddr)
+{
+ if (!bpf_is_local_ipaddr_func)
+ return 0;
+ return bpf_is_local_ipaddr_func(ipaddr);
+}
+
+static const struct bpf_func_proto bpf_is_local_ipaddr_proto = {
+ .func = bpf_is_local_ipaddr,
+ .gpl_only = false,
+ .ret_type = RET_INTEGER,
+ .arg1_type = ARG_ANYTHING,
+};
+
BPF_CALL_5(bpf_sock_addr_getsockopt, struct bpf_sock_addr_kern *, ctx,
int, level, int, optname, char *, optval, int, optlen)
{
@@ -7398,6 +7416,8 @@ sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
case BPF_FUNC_tcp_sock:
return &bpf_tcp_sock_proto;
#endif /* CONFIG_INET */
+ case BPF_FUNC_is_local_ipaddr:
+ return &bpf_is_local_ipaddr_proto;
default:
return bpf_sk_base_func_proto(func_id);
}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index abf8023d606b..41bc2f496176 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -3872,6 +3872,12 @@ union bpf_attr {
* check src_cpu whether share cache with dst_cpu.
* Return
* true yes, false no.
+ *
+ * long bpf_is_local_ipaddr(u32 ipaddr)
+ * Description
+ * Check the ipaddr is local address or not.
+ * Return
+ * 1 is local address, 0 is not.
*/
#define __BPF_FUNC_MAPPER(FN) \
FN(unspec), \
@@ -4044,6 +4050,7 @@ union bpf_attr {
FN(sched_entity_to_tg), \
FN(cpumask_op), \
FN(cpus_share_cache), \
+ FN(is_local_ipaddr), \
/* */
/* integer value in 'imm' field of BPF_CALL instruction selects which helper
--
2.34.1