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@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 | 157 +++++++++++++++++++++++++ include/trace/events/net.h | 17 +++ include/uapi/linux/bpf.h | 7 ++ net/core/filter.c | 19 +++ tools/include/uapi/linux/bpf.h | 7 ++ 10 files changed, 226 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..9e3d90a7e5a5 --- /dev/null +++ b/drivers/net/localip/localip.c @@ -0,0 +1,157 @@ +// 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> +#include <trace/events/net.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, +}; + +static void is_local_ipaddr_trace(void *data, int *ret, uint32_t ipaddr) +{ + *ret = is_local_ipaddr(ipaddr); +} + +static int localip_init(void) +{ + int i, err; + + for (i = 0; i < IN4_ADDR_HSIZE; i++) + INIT_HLIST_HEAD(&localip_lst[i]); + + err = register_inetaddr_notifier(&localip_notifier); + if (err) + return err; + + err = register_trace_is_local_ipaddr(is_local_ipaddr_trace, NULL); + if (err) { + pr_err("Failed to connet probe to is_local_ipaddr.\n"); + unregister_inetaddr_notifier(&localip_notifier); + return err; + } + return 0; +} + +static void localip_cleanup(void) +{ + struct localipaddr *localip; + struct hlist_node *n; + int i; + + unregister_trace_is_local_ipaddr(is_local_ipaddr_trace, 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/trace/events/net.h b/include/trace/events/net.h index 2399073c3afc..ec4532e8328b 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -326,6 +326,23 @@ DEFINE_EVENT(net_dev_rx_exit_template, netif_receive_skb_list_exit, TP_ARGS(ret) );
+TRACE_EVENT(is_local_ipaddr, + + TP_PROTO(int *ret, u32 ipaddr), + + TP_ARGS(ret, ipaddr), + + TP_STRUCT__entry( + __field(int, ret) + __field(u32, ipaddr) + ), + TP_fast_assign( + __entry->ret = *ret; + __entry->ipaddr = ipaddr; + ), + + TP_printk("ret=%d, ipaddr:0x%x", __entry->ret, __entry->ipaddr) +); #endif /* _TRACE_NET_H */
/* This part must be outside protection */ 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..5aae086cf173 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -77,6 +77,7 @@ #include <net/transp_v6.h> #include <linux/btf_ids.h> #include <net/tls.h> +#include <trace/events/net.h>
static const struct bpf_func_proto * bpf_sk_base_func_proto(enum bpf_func_id func_id); @@ -5084,6 +5085,22 @@ static const struct bpf_func_proto bpf_sk_original_addr_proto = { .arg4_type = ARG_CONST_SIZE, };
+EXPORT_TRACEPOINT_SYMBOL_GPL(is_local_ipaddr); +BPF_CALL_1(bpf_is_local_ipaddr, uint32_t, ipaddr) +{ + int ret = 0; + + trace_is_local_ipaddr(&ret, ipaddr); + return ret; +} + +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 +7415,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