diff --git a/arch/arm64/configs/openeuler_defconfig b/arch/arm64/configs/openeuler_defconfig index 1b633c835279ec77a22e65bbbe94603b7c0d1658..49e6d4734c59c44453a45c850a5805714d412956 100644 --- a/arch/arm64/configs/openeuler_defconfig +++ b/arch/arm64/configs/openeuler_defconfig @@ -3139,6 +3139,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 ff4475ef1822e5e6310ab1dddeba66a8c7466f7b..573b767680ac506048d598f169ffb5b79f8f808e 100644 --- a/arch/x86/configs/openeuler_defconfig +++ b/arch/x86/configs/openeuler_defconfig @@ -3216,6 +3216,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 f208080243055fe965b6586f624cc91e4496a0ef..913acb7eed6f4115c711342c26c2e39abd6b29b9 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 72e18d505d1acf73f540a7cad2a6c691784c81af..b4ff8a310dcc1dd3f4d5e8476607ec6441d02b98 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 0000000000000000000000000000000000000000..03b5357092710ac47671082b7fc3a076ebcb85eb --- /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 0000000000000000000000000000000000000000..9e3d90a7e5a575c8bb02ebb65e71c6ac52207580 --- /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 +#include +#include +#include +#include +#include +#include + +#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 2399073c3afc603bd303960302cf42f5bb38044a..8370167f4ab329c3f4eeb951cab7e542c2e5f9b7 100644 --- a/include/trace/events/net.h +++ b/include/trace/events/net.h @@ -326,6 +326,10 @@ DEFINE_EVENT(net_dev_rx_exit_template, netif_receive_skb_list_exit, TP_ARGS(ret) ); +DECLARE_TRACE(is_local_ipaddr, + TP_PROTO(int *ret, u32 ipaddr), + TP_ARGS(ret, 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 9e64dac44d606d484f6350509c984668a9b35e26..9373abafcb91c4e758aeaf23f837e4de09a19982 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 012a5070a9e578b1f39153fc941f2692ba339c01..2e4edda72693ccaea1f77ec3ea2c2b608b92e474 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -77,6 +77,7 @@ #include #include #include +#include static const struct bpf_func_proto * bpf_sk_base_func_proto(enum bpf_func_id func_id); @@ -5084,6 +5085,21 @@ static const struct bpf_func_proto bpf_sk_original_addr_proto = { .arg4_type = ARG_CONST_SIZE, }; +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 +7414,10 @@ 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; + case BPF_FUNC_get_current_comm: + return &bpf_get_current_comm_proto; default: return bpf_sk_base_func_proto(func_id); } diff --git a/net/core/net-traces.c b/net/core/net-traces.c index 283ddb2dbc7d3c2080a4252e99692cb235929b20..2262388005bf7177175fdd85f902c23376624bd9 100644 --- a/net/core/net-traces.c +++ b/net/core/net-traces.c @@ -60,3 +60,4 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb); EXPORT_TRACEPOINT_SYMBOL_GPL(napi_poll); EXPORT_TRACEPOINT_SYMBOL_GPL(tcp_send_reset); +EXPORT_TRACEPOINT_SYMBOL_GPL(is_local_ipaddr); diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index abf8023d606b631966d3a0f1d848c735fad73e1a..41bc2f4961764fce9367de625f84421a163d7dbf 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