Commit 50d52727 authored by JofDiamonds's avatar JofDiamonds
Browse files

bpf: Add new bpf helper to get SO_ORIGINAL_DST/REPLY_SRC

hulk inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I7LTRR
CVE: NA

Reference: https://gitee.com/openeuler/kernel/commit/97aeb284efece2a8af5bb424d4e980905927f7bb



--------------------------------

Add new optname(BPF_SO_ORIGINAL_DST 800, BPF_SO_REPLY_SRC 801)
to get origdst/reply src for bpf progs.
Now only support IPv4.

Signed-off-by: default avatarWang Yufen <wangyufen@huawei.com>
Signed-off-by: default avatarLiu Jian <liujian56@huawei.com>
Signed-off-by: default avatarJofDiamonds <kwb0523@163.com>
Reviewed-by: default avatarwuchangye <wuchangye@huawei.com>
parent 35377bf0
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -381,4 +381,8 @@ int nf_ct_handle_fragments(struct net *net, struct sk_buff *skb,
#define MODULE_ALIAS_NFCT_HELPER(helper) \
        MODULE_ALIAS("nfct-helper-" helper)

typedef int (*bpf_getorigdst_opt_func)(struct sock *sk, int optname,
				       void *optval, int *optlen, int dir);
extern bpf_getorigdst_opt_func bpf_getorigdst_opt;

#endif /* _NF_CONNTRACK_H */
+7 −0
Original line number Diff line number Diff line
@@ -5566,6 +5566,12 @@ union bpf_attr {
 *     Return
 *             A 64-bit integer containing the current GID and UID, and
 *             created as such: *current_gid* **<< 32 \|** *current_uid*.
 *
 * int bpf_sk_original_addr(void *bpf_socket, int optname, char *optval, int optlen)
 *     Description
 *             Get Ipv4 origdst or replysrc. Works with IPv4.
 *     Return
 *             0 on success, or a negative error in case of failure.
 */
#define ___BPF_FUNC_MAPPER(FN, ctx...)			\
	FN(unspec, 0, ##ctx)				\
@@ -5781,6 +5787,7 @@ union bpf_attr {
	FN(cgrp_storage_get, 210, ##ctx)		\
	FN(cgrp_storage_delete, 211, ##ctx)		\
	FN(get_sockops_uid_gid, 212, ##ctx)		\
	FN(sk_original_addr, 213, ##ctx)		\
	/* */

/* backwards-compatibility macros for users of __BPF_FUNC_MAPPER that don't
+2 −0
Original line number Diff line number Diff line
@@ -50,6 +50,8 @@ enum nf_ip_hook_priorities {
/* 2.2 firewalling (+ masq) went from 64 through 76 */
/* 2.4 firewalling went 64 through 67. */
#define SO_ORIGINAL_DST 80
#define BPF_SO_ORIGINAL_DST 800
#define BPF_SO_REPLY_SRC 801


#endif /* _UAPI__LINUX_IP_NETFILTER_H */
+49 −0
Original line number Diff line number Diff line
@@ -5506,6 +5506,53 @@ static const struct bpf_func_proto bpf_get_sockops_uid_gid_proto = {
	.arg1_type	= ARG_PTR_TO_CTX,
};

#include <net/netfilter/nf_conntrack.h>
#include <linux/netfilter_ipv4.h>

bpf_getorigdst_opt_func bpf_getorigdst_opt;
EXPORT_SYMBOL(bpf_getorigdst_opt);

BPF_CALL_4(bpf_sk_original_addr, struct bpf_sock_ops_kern *, bpf_sock,
	   int, optname, char *, optval, int, optlen)
{
	struct sock *sk = bpf_sock->sk;
	int ret = -EINVAL;

	if (!sk_fullsock(sk))
		goto err_clear;

	if (optname != BPF_SO_ORIGINAL_DST && optname != BPF_SO_REPLY_SRC)
		goto err_clear;

	if (!bpf_getorigdst_opt)
		goto err_clear;
#if IS_ENABLED(CONFIG_NF_CONNTRACK)
	if (optname == BPF_SO_ORIGINAL_DST)
		ret = bpf_getorigdst_opt(sk, optname, optval, &optlen,
					 IP_CT_DIR_ORIGINAL);
	else if (optname == BPF_SO_REPLY_SRC)
		ret = bpf_getorigdst_opt(sk, optname, optval, &optlen,
					 IP_CT_DIR_REPLY);
	if (ret < 0)
		goto err_clear;

	return 0;
#endif
err_clear:
	memset(optval, 0, optlen);
	return ret;
}

static const struct bpf_func_proto bpf_sk_original_addr_proto = {
	.func		= bpf_sk_original_addr,
	.gpl_only	= false,
	.ret_type	= RET_INTEGER,
	.arg1_type	= ARG_PTR_TO_CTX,
	.arg2_type	= ARG_ANYTHING,
	.arg3_type	= ARG_PTR_TO_UNINIT_MEM,
	.arg4_type	= ARG_CONST_SIZE,
};

BPF_CALL_5(bpf_sock_addr_getsockopt, struct bpf_sock_addr_kern *, ctx,
	   int, level, int, optname, char *, optval, int, optlen)
{
@@ -8147,6 +8194,8 @@ sock_ops_func_proto(enum bpf_func_id func_id, const struct bpf_prog *prog)
		return &bpf_get_netns_cookie_sock_ops_proto;
	case BPF_FUNC_get_sockops_uid_gid:
		return &bpf_get_sockops_uid_gid_proto;
	case BPF_FUNC_sk_original_addr:
		return &bpf_sk_original_addr_proto;
#ifdef CONFIG_INET
	case BPF_FUNC_load_hdr_opt:
		return &bpf_sock_ops_load_hdr_opt_proto;
+65 −0
Original line number Diff line number Diff line
@@ -311,6 +311,67 @@ getorigdst(struct sock *sk, int optval, void __user *user, int *len)
	return -ENOENT;
}

static int
bpf_getorigdst_impl(struct sock *sk, int optval, void *user, int *len, int dir)
{
	const struct inet_sock *inet = inet_sk(sk);
	const struct nf_conntrack_tuple_hash *h;
	struct nf_conntrack_tuple tuple;

	memset(&tuple, 0, sizeof(tuple));

	tuple.src.u3.ip = inet->inet_rcv_saddr;
	tuple.src.u.tcp.port = inet->inet_sport;
	tuple.dst.u3.ip = inet->inet_daddr;
	tuple.dst.u.tcp.port = inet->inet_dport;
	tuple.src.l3num = PF_INET;
	tuple.dst.protonum = sk->sk_protocol;

	/* We only do TCP and SCTP at the moment: is there a better way? */
	if (tuple.dst.protonum != IPPROTO_TCP &&
	    tuple.dst.protonum != IPPROTO_SCTP) {
		pr_debug("SO_ORIGINAL_DST: Not a TCP/SCTP socket\n");
		return -ENOPROTOOPT;
	}

	if ((unsigned int)*len < sizeof(struct sockaddr_in)) {
		pr_debug("SO_ORIGINAL_DST: len %d not %zu\n",
			 *len, sizeof(struct sockaddr_in));
		return -EINVAL;
	}

	h = nf_conntrack_find_get(sock_net(sk), &nf_ct_zone_dflt, &tuple);
	if (h) {
		struct sockaddr_in sin;
		struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h);

		sin.sin_family = AF_INET;
		if (dir == IP_CT_DIR_REPLY) {
			sin.sin_port = ct->tuplehash[IP_CT_DIR_REPLY]
					.tuple.src.u.tcp.port;
			sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_REPLY]
					.tuple.src.u3.ip;
		} else {
			sin.sin_port = ct->tuplehash[IP_CT_DIR_ORIGINAL]
					.tuple.dst.u.tcp.port;
			sin.sin_addr.s_addr = ct->tuplehash[IP_CT_DIR_ORIGINAL]
					.tuple.dst.u3.ip;
		}
		memset(sin.sin_zero, 0, sizeof(sin.sin_zero));

		pr_debug("SO_ORIGINAL_DST: %pI4 %u\n",
			 &sin.sin_addr.s_addr, ntohs(sin.sin_port));
		nf_ct_put(ct);

		memcpy(user, &sin, sizeof(sin));
		return 0;
	}
	pr_debug("SO_ORIGINAL_DST: Can't find %pI4/%u-%pI4/%u.\n",
		 &tuple.src.u3.ip, ntohs(tuple.src.u.tcp.port),
		 &tuple.dst.u3.ip, ntohs(tuple.dst.u.tcp.port));
	return -ENOENT;
}

static struct nf_sockopt_ops so_getorigdst = {
	.pf		= PF_INET,
	.get_optmin	= SO_ORIGINAL_DST,
@@ -655,6 +716,8 @@ int nf_conntrack_proto_init(void)
		goto cleanup_sockopt;
#endif

	bpf_getorigdst_opt = bpf_getorigdst_impl;

	return ret;

#if IS_ENABLED(CONFIG_IPV6)
@@ -666,6 +729,8 @@ int nf_conntrack_proto_init(void)

void nf_conntrack_proto_fini(void)
{
	bpf_getorigdst_opt = NULL;

	nf_unregister_sockopt(&so_getorigdst);
#if IS_ENABLED(CONFIG_IPV6)
	nf_unregister_sockopt(&so_getorigdst6);
Loading