| // SPDX-License-Identifier: GPL-2.0 |
| /* Copyright(c) 2017 - 2018 Intel Corporation. */ |
| |
| #include <errno.h> |
| #include <getopt.h> |
| #include <libgen.h> |
| #include <net/if.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <sys/socket.h> |
| #include <sys/un.h> |
| #include <unistd.h> |
| |
| #include <bpf/bpf.h> |
| #include <bpf/xsk.h> |
| #include "xdpsock.h" |
| |
| static const char *opt_if = ""; |
| |
| static struct option long_options[] = { |
| {"interface", required_argument, 0, 'i'}, |
| {0, 0, 0, 0} |
| }; |
| |
| static void usage(const char *prog) |
| { |
| const char *str = |
| " Usage: %s [OPTIONS]\n" |
| " Options:\n" |
| " -i, --interface=n Run on interface n\n" |
| "\n"; |
| fprintf(stderr, "%s\n", str); |
| |
| exit(0); |
| } |
| |
| static void parse_command_line(int argc, char **argv) |
| { |
| int option_index, c; |
| |
| opterr = 0; |
| |
| for (;;) { |
| c = getopt_long(argc, argv, "i:", |
| long_options, &option_index); |
| if (c == -1) |
| break; |
| |
| switch (c) { |
| case 'i': |
| opt_if = optarg; |
| break; |
| default: |
| usage(basename(argv[0])); |
| } |
| } |
| } |
| |
| static int send_xsks_map_fd(int sock, int fd) |
| { |
| char cmsgbuf[CMSG_SPACE(sizeof(int))]; |
| struct msghdr msg; |
| struct iovec iov; |
| int value = 0; |
| |
| if (fd == -1) { |
| fprintf(stderr, "Incorrect fd = %d\n", fd); |
| return -1; |
| } |
| iov.iov_base = &value; |
| iov.iov_len = sizeof(int); |
| |
| msg.msg_name = NULL; |
| msg.msg_namelen = 0; |
| msg.msg_iov = &iov; |
| msg.msg_iovlen = 1; |
| msg.msg_flags = 0; |
| msg.msg_control = cmsgbuf; |
| msg.msg_controllen = CMSG_LEN(sizeof(int)); |
| |
| struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg); |
| |
| cmsg->cmsg_level = SOL_SOCKET; |
| cmsg->cmsg_type = SCM_RIGHTS; |
| cmsg->cmsg_len = CMSG_LEN(sizeof(int)); |
| |
| *(int *)CMSG_DATA(cmsg) = fd; |
| int ret = sendmsg(sock, &msg, 0); |
| |
| if (ret == -1) { |
| fprintf(stderr, "Sendmsg failed with %s", strerror(errno)); |
| return -errno; |
| } |
| |
| return ret; |
| } |
| |
| int |
| main(int argc, char **argv) |
| { |
| struct sockaddr_un server; |
| int listening = 1; |
| int rval, msgsock; |
| int ifindex = 0; |
| int flag = 1; |
| int cmd = 0; |
| int sock; |
| int err; |
| int xsks_map_fd; |
| |
| parse_command_line(argc, argv); |
| |
| ifindex = if_nametoindex(opt_if); |
| if (ifindex == 0) { |
| fprintf(stderr, "Unable to get ifindex for Interface %s. Reason:%s", |
| opt_if, strerror(errno)); |
| return -errno; |
| } |
| |
| sock = socket(AF_UNIX, SOCK_STREAM, 0); |
| if (sock < 0) { |
| fprintf(stderr, "Opening socket stream failed: %s", strerror(errno)); |
| return -errno; |
| } |
| |
| server.sun_family = AF_UNIX; |
| strcpy(server.sun_path, SOCKET_NAME); |
| |
| setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)); |
| |
| if (bind(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_un))) { |
| fprintf(stderr, "Binding to socket stream failed: %s", strerror(errno)); |
| return -errno; |
| } |
| |
| listen(sock, MAX_NUM_OF_CLIENTS); |
| |
| err = xsk_setup_xdp_prog(ifindex, &xsks_map_fd); |
| if (err) { |
| fprintf(stderr, "Setup of xdp program failed\n"); |
| goto close_sock; |
| } |
| |
| while (listening) { |
| msgsock = accept(sock, 0, 0); |
| if (msgsock == -1) { |
| fprintf(stderr, "Error accepting connection: %s", strerror(errno)); |
| err = -errno; |
| goto close_sock; |
| } |
| err = send_xsks_map_fd(msgsock, xsks_map_fd); |
| if (err <= 0) { |
| fprintf(stderr, "Error %d sending xsks_map_fd\n", err); |
| goto cleanup; |
| } |
| do { |
| rval = read(msgsock, &cmd, sizeof(int)); |
| if (rval < 0) { |
| fprintf(stderr, "Error reading stream message"); |
| } else { |
| if (cmd != CLOSE_CONN) |
| fprintf(stderr, "Recv unknown cmd = %d\n", cmd); |
| listening = 0; |
| break; |
| } |
| } while (rval > 0); |
| } |
| close(msgsock); |
| close(sock); |
| unlink(SOCKET_NAME); |
| |
| /* Unset fd for given ifindex */ |
| err = bpf_set_link_xdp_fd(ifindex, -1, 0); |
| if (err) { |
| fprintf(stderr, "Error when unsetting bpf prog_fd for ifindex(%d)\n", ifindex); |
| return err; |
| } |
| |
| return 0; |
| |
| cleanup: |
| close(msgsock); |
| close_sock: |
| close(sock); |
| unlink(SOCKET_NAME); |
| return err; |
| } |