Skip to content

Instantly share code, notes, and snippets.

@nabijaczleweli
Forked from dhowells/kcm-sample.c
Last active December 20, 2023 18:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nabijaczleweli/55bef1d1c0b868dbc4a9535ca4ddb013 to your computer and use it in GitHub Desktop.
Save nabijaczleweli/55bef1d1c0b868dbc4a9535ca4ddb013 to your computer and use it in GitHub Desktop.
A sample program of KCM
/*
* 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);
}
@nabijaczleweli
Copy link
Author

This has been fixed to actually work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment