-
-
Save nabijaczleweli/55bef1d1c0b868dbc4a9535ca4ddb013 to your computer and use it in GitHub Desktop.
A sample program of KCM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* A sample program of KCM. | |
* | |
* $ cc $(pkgconf --cflags --libs libbcc) kcm-sample.c -okcm-sample -Wall | |
* $ ./kcm-sample 10000 | |
*/ | |
#include <err.h> | |
#include <errno.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <poll.h> | |
#include <sys/ioctl.h> | |
#include <sys/socket.h> | |
#include <sys/types.h> | |
#include <sys/wait.h> | |
#include <netinet/in.h> | |
/* libbcc */ | |
#include <bcc/bcc_common.h> | |
#include <bcc/libbpf.h> | |
#include <linux/bpf.h> | |
#include <linux/kcm.h> | |
struct my_proto { | |
struct _hdr { | |
uint32_t len; | |
} hdr; | |
char data[32]; | |
}; | |
const char *bpf_prog_string = " \ | |
ssize_t bpf_prog1(struct __sk_buff *skb) \ | |
{ \ | |
return load_half(skb, 0) + 4; \ | |
} \ | |
"; | |
int servsock_init(int port) | |
{ | |
int s = socket(AF_INET, SOCK_STREAM, 0); | |
if (bind(s, | |
&(struct sockaddr_in){ | |
.sin_family = AF_INET, | |
.sin_addr = { htonl(INADDR_ANY) }, | |
.sin_port = htons(port), | |
}, | |
sizeof(struct sockaddr_in)) == -1) | |
err(1, "bind"); | |
if (listen(s, 10) == -1) | |
err(1, "listen"); | |
return s; | |
} | |
int bpf_init(void) | |
{ | |
void *mod = bpf_module_create_c_from_string(bpf_prog_string, 0, NULL, 0, | |
0, NULL); | |
int fd = bcc_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, "bpf_prog1", | |
bpf_function_start(mod, "bpf_prog1"), | |
bpf_function_size(mod, "bpf_prog1"), | |
bpf_module_license(mod), | |
bpf_module_kern_version(mod), 0, NULL, 0); | |
if (fd == -1) | |
exit(1); | |
return fd; | |
} | |
void client(int port) | |
{ | |
printf("client is starting\n"); | |
int s = socket(AF_INET, SOCK_STREAM, 0); | |
if (connect(s, | |
&(struct sockaddr_in){ | |
.sin_family = AF_INET, | |
.sin_addr = { htonl(INADDR_LOOPBACK) }, | |
.sin_port = htons(port), | |
}, | |
sizeof(struct sockaddr_in)) == -1) | |
err(1, "connect"); | |
struct my_proto my_msg = { .data = "hello", | |
.hdr.len = htons(strlen("hello")) }; | |
if (write(s, &my_msg, sizeof(my_msg.hdr) + strlen("hello")) == -1) | |
err(1, "write"); | |
printf("client sent data\n"); | |
printf("client is waiting a reply\n"); | |
if (read(s, &my_msg, sizeof(my_msg)) == -1) | |
err(1, "read"); | |
printf("\"%.*s\" from server\n", (int)my_msg.hdr.len, my_msg.data); | |
printf("client received data\n"); | |
close(s); | |
} | |
int kcm_init(void) | |
{ | |
int kcmfd; | |
kcmfd = socket(AF_KCM, SOCK_DGRAM, KCMPROTO_CONNECTED); | |
if (kcmfd == -1) | |
err(1, "socket(AF_KCM)"); | |
return kcmfd; | |
} | |
int kcm_clone(int kcmfd) | |
{ | |
struct kcm_clone clone_info = {}; | |
if (ioctl(kcmfd, SIOCKCMCLONE, &clone_info) == -1) | |
err(1, "ioctl(SIOCKCMCLONE)"); | |
return clone_info.fd; | |
} | |
void kcm_attach(int kcmfd, int csock, int bpf_prog_fd) | |
{ | |
if (ioctl(kcmfd, SIOCKCMATTACH, | |
&(struct kcm_attach){ | |
.fd = csock, | |
.bpf_fd = bpf_prog_fd, | |
}) == -1) | |
err(1, "ioctl(SIOCKCMATTACH)"); | |
} | |
void process(int kcmfd0, int kcmfd1) | |
{ | |
struct pollfd fds[2] = { { .fd = kcmfd0, .events = POLLIN }, | |
{ .fd = kcmfd1, .events = POLLIN } }; | |
printf("server is waiting data\n"); | |
if (poll(fds, 2, -1) == -1) | |
err(1, "poll"); | |
int fd; | |
if (fds[0].revents & POLLIN) { | |
fd = fds[0].fd; | |
printf("kcmfd0\n"); | |
} else if (fds[1].revents & POLLIN) { | |
fd = fds[1].fd; | |
printf("kcmfd1\n"); | |
} else | |
abort(); | |
struct my_proto my_msg = {}; | |
struct iovec iov = { .iov_base = &my_msg, .iov_len = sizeof(my_msg) }; | |
struct msghdr msg = { .msg_iov = &iov, .msg_iovlen = 1 }; | |
printf("server is receiving data\n"); | |
ssize_t len = recvmsg(fd, &msg, 0); | |
if (len == -1) | |
err(1, "recvmsg"); | |
printf("%zd: \"%.*s\" from client\n", len, (int)my_msg.hdr.len, | |
my_msg.data); | |
printf("server received data\n"); | |
my_msg = (struct my_proto){ .data = "goodbye", | |
.hdr.len = strlen("goodbye") }; | |
if (sendmsg(fd, &msg, 0) == -1) | |
err(1, "sendmsg"); | |
} | |
void server(int tcpfd, int bpf_prog_fd) | |
{ | |
printf("server is starting\n"); | |
int kcmfd0 = kcm_init(); | |
int kcmfd1 = kcm_clone(kcmfd0); | |
struct sockaddr_in client; | |
int csock = accept(tcpfd, (struct sockaddr *)&client, | |
&(socklen_t){ sizeof(client) }); | |
if (csock == -1) | |
err(1, "accept"); | |
kcm_attach(kcmfd0, csock, bpf_prog_fd); | |
kcm_attach(kcmfd1, csock, bpf_prog_fd); | |
process(kcmfd0, kcmfd1); | |
close(kcmfd0); | |
close(kcmfd1); | |
} | |
int main(int argc, char **argv) | |
{ | |
int tcpfd, bpf_prog_fd; | |
int pipefd[2]; | |
int dummy; | |
if (argc != 2) { | |
fprintf(stderr, "Format %s <port>\n", argv[0]); | |
exit(2); | |
} | |
pipe(pipefd); | |
if (!fork()) { | |
/* wait for server's ready */ | |
read(pipefd[0], &dummy, sizeof(dummy)); | |
client(atoi(argv[1])); | |
_exit(0); | |
} | |
tcpfd = servsock_init(atoi(argv[1])); | |
bpf_prog_fd = bpf_init(); | |
/* tell ready */ | |
write(pipefd[1], &dummy, sizeof(dummy)); | |
server(tcpfd, bpf_prog_fd); | |
wait(NULL); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This has been fixed to actually work.