From patchwork Fri Aug 17 23:08:25 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Tushar Dave X-Patchwork-Id: 959138 X-Patchwork-Delegate: bpf@iogearbox.net Return-Path: X-Original-To: patchwork-incoming-netdev@ozlabs.org Delivered-To: patchwork-incoming-netdev@ozlabs.org Authentication-Results: ozlabs.org; spf=none (mailfrom) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=netdev-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=pass (p=none dis=none) header.from=oracle.com Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=oracle.com header.i=@oracle.com header.b="vPalf8Km"; dkim-atps=neutral Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 41sf6l5QD1z9s5b for ; Sat, 18 Aug 2018 09:09:51 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726272AbeHRCPH (ORCPT ); Fri, 17 Aug 2018 22:15:07 -0400 Received: from userp2120.oracle.com ([156.151.31.85]:46536 "EHLO userp2120.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726154AbeHRCPH (ORCPT ); Fri, 17 Aug 2018 22:15:07 -0400 Received: from pps.filterd (userp2120.oracle.com [127.0.0.1]) by userp2120.oracle.com (8.16.0.22/8.16.0.22) with SMTP id w7HMwvam184739; Fri, 17 Aug 2018 23:09:21 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : subject : date : message-id : in-reply-to : references; s=corp-2018-07-02; bh=/ig+5o+cOsc0c4vd/JdkCgs/AU6x+YcSw1FTh5zVjmY=; b=vPalf8Kmg2cZiJ2g10y8UToWr9s+iyQcuiwk1IdKVZYBT69cPoER5dO70IqRtPewe3KH NpGhefv5ERhwKcQN+CWOIZzjU1ayb8U6447C4rBh2J8oW7pJVCMV+W1xt8ymz1hKqBYz fOuT8U9ePU1onfpUDIOL0d8S0imxmyJPwGrLCRCBfPJFr+UO1ahmK3KNipQVIubuL7lQ jJ+RF+f4UqRZrxGKi66cOaZc84pO5oAI5K2rvcxYZGYyF3BiZch37iTHMwXFPDW9HSms 2nk4zRBanpuhmeBOo/6R7ulr2StKzZxTgf3RNF+Hwxz7qDUXW0pIwbAj67IUa/L6Y1Jm nA== Received: from userv0022.oracle.com (userv0022.oracle.com [156.151.31.74]) by userp2120.oracle.com with ESMTP id 2ksreqhs2r-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 17 Aug 2018 23:09:21 +0000 Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by userv0022.oracle.com (8.14.4/8.14.4) with ESMTP id w7HN9GCm009137 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Fri, 17 Aug 2018 23:09:16 GMT Received: from abhmp0014.oracle.com (abhmp0014.oracle.com [141.146.116.20]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id w7HN9Fm5025876; Fri, 17 Aug 2018 23:09:15 GMT Received: from lab71.no.oracle.com (/10.172.144.174) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Fri, 17 Aug 2018 16:09:15 -0700 From: Tushar Dave To: john.fastabend@gmail.com, ast@kernel.org, daniel@iogearbox.net, davem@davemloft.net, sowmini.varadhan@oracle.com, santosh.shilimkar@oracle.com, jakub.kicinski@netronome.com, quentin.monnet@netronome.com, jiong.wang@netronome.com, sandipan@linux.vnet.ibm.com, kafai@fb.com, rdna@fb.com, yhs@fb.com, netdev@vger.kernel.org Subject: [RFC v3 net-next 5/5] ebpf: Add sample ebpf program for SOCKET_SG_FILTER Date: Sat, 18 Aug 2018 01:08:25 +0200 Message-Id: <1534547305-25140-6-git-send-email-tushar.n.dave@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1534547305-25140-1-git-send-email-tushar.n.dave@oracle.com> References: <1534547305-25140-1-git-send-email-tushar.n.dave@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=5900 definitions=8988 signatures=668707 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1807170000 definitions=main-1808170242 Sender: netdev-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: netdev@vger.kernel.org Add a sample program that shows how socksg program is used and attached to socket filter. The kernel sample program deals with struct scatterlist that is passed as bpf context. When run in server mode, the sample RDS program opens PF_RDS socket, attaches eBPF program to RDS socket which then uses bpf_msg_pull_data helper to inspect packet data contained in struct scatterlist and returns appropriate action code back to kernel. To ease testing, RDS client functionality is also added so that users can generate RDS packet. Server: [root@lab71 bpf]# ./rds_filter -s 192.168.3.71 -t tcp running server in a loop transport tcp server bound to address: 192.168.3.71 port 4000 server listening on 192.168.3.71 Client: [root@lab70 bpf]# ./rds_filter -s 192.168.3.71 -c 192.168.3.70 -t tcp transport tcp client bound to address: 192.168.3.70 port 25278 client sending 8192 byte message from 192.168.3.70 to 192.168.3.71 on port 25278 payload contains:30 31 32 33 34 35 36 37 38 39 ... Server output: 192.168.3.71 received a packet from 192.168.3.71 of len 8192 cmsg len 0, on port 25278 payload contains:30 31 32 33 34 35 36 37 38 39 ... server listening on 192.168.3.71 [root@lab71 tushar]# cat /sys/kernel/debug/tracing/trace_pipe -0 [038] ..s. 146.947362: 0: 30 31 32 -0 [038] ..s. 146.947364: 0: 33 34 35 Similarly specifying '-t ib' will run this on IB link. Signed-off-by: Tushar Dave Acked-by: Sowmini Varadhan --- samples/bpf/Makefile | 3 + samples/bpf/rds_filter_kern.c | 42 ++++++ samples/bpf/rds_filter_user.c | 339 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 384 insertions(+) create mode 100644 samples/bpf/rds_filter_kern.c create mode 100644 samples/bpf/rds_filter_user.c diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile index 36f9f41..dbb30d0 100644 --- a/samples/bpf/Makefile +++ b/samples/bpf/Makefile @@ -53,6 +53,7 @@ hostprogs-y += xdpsock hostprogs-y += xdp_fwd hostprogs-y += task_fd_query hostprogs-y += xdp_sample_pkts +hostprogs-y += rds_filter # Libbpf dependencies LIBBPF = $(TOOLS_PATH)/lib/bpf/libbpf.a @@ -109,6 +110,7 @@ xdpsock-objs := xdpsock_user.o xdp_fwd-objs := xdp_fwd_user.o task_fd_query-objs := bpf_load.o task_fd_query_user.o $(TRACE_HELPERS) xdp_sample_pkts-objs := xdp_sample_pkts_user.o $(TRACE_HELPERS) +rds_filter-objs := bpf_load.o rds_filter_user.o # Tell kbuild to always build the programs always := $(hostprogs-y) @@ -166,6 +168,7 @@ always += xdpsock_kern.o always += xdp_fwd_kern.o always += task_fd_query_kern.o always += xdp_sample_pkts_kern.o +always += rds_filter_kern.o KBUILD_HOSTCFLAGS += -I$(objtree)/usr/include KBUILD_HOSTCFLAGS += -I$(srctree)/tools/lib/ diff --git a/samples/bpf/rds_filter_kern.c b/samples/bpf/rds_filter_kern.c new file mode 100644 index 0000000..633e687 --- /dev/null +++ b/samples/bpf/rds_filter_kern.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include "bpf_helpers.h" + +#define bpf_printk(fmt, ...) \ +({ \ + char ____fmt[] = fmt; \ + bpf_trace_printk(____fmt, sizeof(____fmt), \ + ##__VA_ARGS__); \ +}) + +SEC("socksg") +int main_prog(struct sk_msg_md *msg) +{ + int start, end, err; + unsigned char *d; + + start = 0; + end = 6; + + err = bpf_msg_pull_data(msg, start, end, 0); + if (err) { + bpf_printk("socksg: pull_data err %i\n", err); + return SOCKSG_PASS; + } + + if (msg->data + 6 > msg->data_end) + return SOCKSG_PASS; + + d = (unsigned char *)msg->data; + bpf_printk("%x %x %x\n", d[0], d[1], d[2]); + bpf_printk("%x %x %x\n", d[3], d[4], d[5]); + + return SOCKSG_PASS; +} + +char _license[] SEC("license") = "GPL"; +u32 _version SEC("version") = LINUX_VERSION_CODE; diff --git a/samples/bpf/rds_filter_user.c b/samples/bpf/rds_filter_user.c new file mode 100644 index 0000000..1186345 --- /dev/null +++ b/samples/bpf/rds_filter_user.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include "bpf_load.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TESTPORT 4000 +#define BUFSIZE 8192 + +int transport = -1; + +static int str2trans(const char *trans) +{ + if (strcmp(trans, "tcp") == 0) + return RDS_TRANS_TCP; + if (strcmp(trans, "ib") == 0) + return RDS_TRANS_IB; + return (RDS_TRANS_NONE); +} + +static const char *trans2str(int trans) +{ + switch (trans) { + case RDS_TRANS_TCP: + return ("tcp"); + case RDS_TRANS_IB: + return ("ib"); + case RDS_TRANS_NONE: + return ("none"); + default: + return ("unknown"); + } +} + +static int gettransport(int sock) +{ + int err; + char val; + socklen_t len = sizeof(int); + + err = getsockopt(sock, SOL_RDS, SO_RDS_TRANSPORT, + (char *)&val, &len); + if (err < 0) { + fprintf(stderr, "%s: getsockopt %s\n", + __func__, strerror(errno)); + return err; + } + return (int)val; +} + +static int settransport(int sock, int transport) +{ + int err; + + err = setsockopt(sock, SOL_RDS, SO_RDS_TRANSPORT, + (char *)&transport, sizeof(transport)); + if (err < 0) { + fprintf(stderr, "could not set transport %s, %s\n", + trans2str(transport), strerror(errno)); + } + return err; +} + +static void print_sock_local_info(int fd, char *str, struct sockaddr_in *ret) +{ + socklen_t sin_size = sizeof(struct sockaddr_in); + struct sockaddr_in sin; + int err; + + err = getsockname(fd, (struct sockaddr *)&sin, &sin_size); + if (err < 0) { + fprintf(stderr, "%s getsockname %s\n", + __func__, strerror(errno)); + return; + } + printf("%s address: %s port %d\n", + (str ? str : ""), inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); + + if (ret != NULL) + *ret = sin; +} + +static void print_payload(char *buf) +{ + int i; + + printf("payload contains:"); + for (i = 0; i < 10; i++) + printf("%x ", buf[i]); + printf("...\n"); +} + +static void server(char *address, in_port_t port) +{ + struct sockaddr_in sin, din; + struct msghdr msg; + struct iovec *iov; + int rc, sock; + char *buf; + + buf = calloc(BUFSIZE, sizeof(char)); + if (!buf) { + fprintf(stderr, "%s: calloc %s\n", __func__, strerror(errno)); + return; + } + + sock = socket(PF_RDS, SOCK_SEQPACKET, 0); + if (sock < 0) { + fprintf(stderr, "%s: socket %s\n", __func__, strerror(errno)); + goto out; + } + if (settransport(sock, transport) < 0) + goto out; + + printf("transport %s\n", trans2str(gettransport(sock))); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(address); + sin.sin_port = htons(port); + + rc = bind(sock, (struct sockaddr *)&sin, sizeof(sin)); + if (rc < 0) { + fprintf(stderr, "%s: bind %s\n", __func__, strerror(errno)); + goto out; + } + + /* attach bpf prog */ + assert(setsockopt(sock, SOL_SOCKET, SO_ATTACH_BPF, prog_fd, + sizeof(prog_fd[0])) == 0); + + print_sock_local_info(sock, "server bound to", NULL); + + iov = calloc(1, sizeof(struct iovec)); + if (!iov) { + fprintf(stderr, "%s: calloc %s\n", __func__, strerror(errno)); + goto out; + } + + while (1) { + memset(buf, 0, BUFSIZE); + iov[0].iov_base = buf; + iov[0].iov_len = BUFSIZE; + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &din; + msg.msg_namelen = sizeof(din); + msg.msg_iov = iov; + msg.msg_iovlen = 1; + + printf("server listening on %s\n", inet_ntoa(sin.sin_addr)); + + rc = recvmsg(sock, &msg, 0); + if (rc < 0) { + fprintf(stderr, "%s: recvmsg %s\n", + __func__, strerror(errno)); + break; + } + + printf("%s received a packet from %s of len %d cmsg len %d, on port %d\n", + inet_ntoa(sin.sin_addr), + inet_ntoa(din.sin_addr), + (uint32_t) iov[0].iov_len, + (uint32_t) msg.msg_controllen, + ntohs(din.sin_port)); + + print_payload(buf); + } + free(iov); +out: + free(buf); +} + +static void create_message(char *buf) +{ + unsigned int i; + + for (i = 0; i < BUFSIZE; i++) { + buf[i] = i + 0x30; + } +} + +static int build_rds_packet(struct msghdr *msg, char *buf) +{ + struct iovec *iov; + + iov = calloc(1, sizeof(struct iovec)); + if (!iov) { + fprintf(stderr, "%s: calloc %s\n", __func__, strerror(errno)); + return -1; + } + + msg->msg_iov = iov; + msg->msg_iovlen = 1; + + iov[0].iov_base = buf; + iov[0].iov_len = BUFSIZE * sizeof(char); + + return 0; +} + +static void client(char *localaddr, char *remoteaddr, in_port_t server_port) +{ + struct sockaddr_in sin, din; + struct msghdr msg; + int rc, sock; + char *buf; + + buf = calloc(BUFSIZE, sizeof(char)); + if (!buf) { + fprintf(stderr, "%s: calloc %s\n", __func__, strerror(errno)); + return; + } + + create_message(buf); + + sock = socket(PF_RDS, SOCK_SEQPACKET, 0); + if (sock < 0) { + fprintf(stderr, "%s: socket %s\n", __func__, strerror(errno)); + goto out; + } + + if (settransport(sock, transport) < 0) + goto out; + + printf("transport %s\n", trans2str(gettransport(sock))); + + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(localaddr); + sin.sin_port = 0; + + rc = bind(sock, (struct sockaddr *)&sin, sizeof(sin)); + if (rc < 0) { + fprintf(stderr, "%s: bind %s\n", __func__, strerror(errno)); + goto out; + } + print_sock_local_info(sock, "client bound to", &sin); + + memset(&msg, 0, sizeof(msg)); + msg.msg_name = &din; + msg.msg_namelen = sizeof(din); + + memset(&din, 0, sizeof(din)); + din.sin_family = AF_INET; + din.sin_addr.s_addr = inet_addr(remoteaddr); + din.sin_port = htons(server_port); + + rc = build_rds_packet(&msg, buf); + if (rc < 0) + goto out; + + printf("client sending %d byte message from %s to %s on port %d\n", + (uint32_t) msg.msg_iov->iov_len, localaddr, + remoteaddr, ntohs(sin.sin_port)); + + rc = sendmsg(sock, &msg, 0); + if (rc < 0) + fprintf(stderr, "%s: sendmsg %s\n", __func__, strerror(errno)); + + print_payload(buf); + + if (msg.msg_control) + free(msg.msg_control); + if (msg.msg_iov) + free(msg.msg_iov); +out: + free(buf); + + return; +} + +static void usage(char *progname) +{ + fprintf(stderr, "Usage %s [-s srvaddr] [-c clientaddr] [-t transport]" + "\n", progname); +} + +int main(int argc, char **argv) +{ + in_port_t server_port = TESTPORT; + char *serveraddr = NULL; + char *clientaddr = NULL; + char filename[256]; + int opt; + + while ((opt = getopt(argc, argv, "s:c:t:")) != -1) { + switch (opt) { + case 's': + serveraddr = optarg; + break; + case 'c': + clientaddr = optarg; + break; + case 't': + transport = str2trans(optarg); + if (transport == RDS_TRANS_NONE) { + fprintf(stderr, + "unknown transport %s\n", optarg); + usage(argv[0]); + return (-1); + } + break; + default: + usage(argv[0]); + return 1; + } + } + + snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]); + + if (load_bpf_file(filename)) { + fprintf(stderr, "Error: load_bpf_file %s", bpf_log_buf); + return 1; + } + + if (serveraddr && !clientaddr) { + printf("running server in a loop\n"); + server(serveraddr, server_port); + } else if (serveraddr && clientaddr) { + client(clientaddr, serveraddr, server_port); + } + + return 0; +}