diff --git a/.travis.yml b/.travis.yml index dc1e019..76153f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,7 @@ before_install: script: - sudo mknod /dev/net/tap c 10 200 - sudo chmod 0666 /dev/net/tap - - sudo setcap cap_net_admin=ep /bin/ip + - sudo ip tuntap add mode tap tap0 + - sudo ip link set dev tap0 up + - sudo ip addr add 10.0.0.5/24 dev tap0 - make test diff --git a/Documentation/getting-started.md b/Documentation/getting-started.md index a6377d6..a3bb592 100644 --- a/Documentation/getting-started.md +++ b/Documentation/getting-started.md @@ -10,34 +10,29 @@ DISCLAIMER: Level-IP is not a production-ready networking stack, and does not in Standard `make` stuff. - $ make all + make all -This builds `lvl-ip` itself, but also the libc wrapper and provided example applications. - -When building, `sudo setcap ...` probably asks super user permissions from you. This is because `lvl-ip` needs the `CAP_NET_ADMIN` capability to setup itself. After the setup, it drops that capability. - -Currently, `lvl-ip` also configures the tap interface through the `ip` tool. Hence, give it permissions too: - - $ which ip - /usr/bin/ip - $ sudo setcap cap_net_admin=ep /usr/bin/ip +This builds `lvl-ip` and the libc wrapper (under `tools/`) and provided example applications (under `apps/`). # Setup Level-IP uses a Linux TAP device to communicate to the outside world. In short, the tap device is initialized in the host Linux' networking stack, and `lvl-ip` can then read the L2 frames: - $ sudo mknod /dev/net/tap c 10 200 - $ sudo chmod 0666 /dev/net/tap + sudo mknod /dev/net/tap c 10 200 + sudo chmod 0666 /dev/net/tap + sudo ip tuntap add mode tap tap0 + sudo ip link set dev tap0 up + sudo ip addr add 10.0.0.5/24 dev tap0 In essence, `lvl-ip` operates as a host inside the tap device's subnet. Therefore, in order to communicate with other hosts, the tap device needs to be set in a forwarding mode: An example from my (Arch) Linux machine, where `wlp2s0` is my outgoing interface, and `tap0` is the tap device for `lvl-ip`: - $ sysctl -w net.ipv4.ip_forward=1 - $ iptables -I INPUT --source 10.0.0.0/24 -j ACCEPT - $ iptables -t nat -I POSTROUTING --out-interface wlp2s0 -j MASQUERADE - $ iptables -I FORWARD --in-interface wlp2s0 --out-interface tap0 -j ACCEPT - $ iptables -I FORWARD --in-interface tap0 --out-interface wlp2s0 -j ACCEPT + sysctl -w net.ipv4.ip_forward=1 + iptables -I INPUT --source 10.0.0.0/24 -j ACCEPT + iptables -t nat -I POSTROUTING --out-interface wlp2s0 -j MASQUERADE + iptables -I FORWARD --in-interface wlp2s0 --out-interface tap0 -j ACCEPT + iptables -I FORWARD --in-interface tap0 --out-interface wlp2s0 -j ACCEPT Now, packets coming from `lvl-ip` (10.0.0.4/24 in this case) should be NATed by the host Linux interfaces and traverse the FORWARD chain correctly to the host's outgoing gateway. @@ -47,21 +42,19 @@ See http://www.netfilter.org/documentation/HOWTO/packet-filtering-HOWTO-9.html f When you've built lvl-ip and setup your host stack to forward packets, you can try communicating to the Internet: - $ ./lvl-ip + ./lvl-ip -The userspace TCP/IP stack should start. Now, first test communications with the provided applications: +The userspace TCP/IP stack should start. - $ cd tools - $ ./level-ip ../apps/curl/curl google.com 80 +Now, first test communications with the provided applications: + + cd tools + ./level-ip ../apps/curl/curl google.com 80 `./level-ip` is just a bash-script that allows `liblevelip.so` to take precedence over the libc socket API calls. The important point is that `./level-ip` aims to be usable against any existing dynamically-linked application. Let's try the _real_ `curl`: - [saminiir@localhost tools]$ curl --version - curl 7.50.0 (x86_64-pc-linux-gnu) libcurl/7.50.0 OpenSSL/1.0.2h zlib/1.2.8 libidn/1.33 libssh2/1.7.0 - Protocols: dict file ftp ftps gopher http https imap imaps pop3 pop3s rtsp scp sftp smb smbs smtp smtps telnet tftp - Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP UnixSockets [saminiir@localhost tools]$ curl google.com 302 Moved @@ -89,3 +82,23 @@ Try browsing the Web, with Level-IP doing the packet transfer: [saminiir@localhost tools]$ ./level-ip firefox google.com That's it! + +# Troubleshooting + +Common errors: + +## Tun module not loaded/available + +`tun` is required to be loaded: + +``` +$ lsmod | grep tun +tun 57344 2 +``` + +Try loading it +``` +$ modprobe tun +``` + +Otherwise, consult your distro's documentation on setting it up. diff --git a/Makefile b/Makefile index ae9882e..bf30a7d 100644 --- a/Makefile +++ b/Makefile @@ -7,9 +7,6 @@ apps = apps/curl/curl lvl-ip: $(obj) $(CC) $(CFLAGS) $(CPPFLAGS) $(obj) -o lvl-ip - @echo - @echo "lvl-ip needs CAP_NET_ADMIN:" - sudo setcap cap_setpcap,cap_net_admin=ep lvl-ip build/%.o: src/%.c ${headers} $(CC) $(CFLAGS) $(CPPFLAGS) -c $< -o $@ diff --git a/include/ipc.h b/include/ipc.h index 4ae0ff9..8d422e9 100644 --- a/include/ipc.h +++ b/include/ipc.h @@ -7,7 +7,7 @@ #define ipc_dbg(msg, th) \ do { \ print_debug("IPC sockets count %d, current sock %d, tid %lu: %s", \ - socket_count, th->sock, th->id, msg); \ + socket_count, th->sock, th->id, msg); \ } while (0) #else #define ipc_dbg(msg, th) @@ -26,6 +26,8 @@ void *start_ipc_listener(); #define IPC_SETSOCKOPT 0x0009 #define IPC_GETPEERNAME 0x000A #define IPC_GETSOCKNAME 0x000B +#define IPC_SENDMSG 0x000C +#define IPC_RECVMSG 0x000D struct ipc_thread { struct list_head list; @@ -105,4 +107,18 @@ struct ipc_sockname { uint8_t sa_data[128]; }; +struct ipc_msghdr { + int sockfd; + socklen_t msg_namelen; + int msg_iovlen; + socklen_t msg_controllen; + int flags; + uint8_t data[]; +} __attribute__((packed)); + +struct ipc_iovec { + size_t iov_len; + uint8_t iov_base[]; +} __attribute__((packed)); + #endif diff --git a/include/netlink.h b/include/netlink.h new file mode 100644 index 0000000..b4024a4 --- /dev/null +++ b/include/netlink.h @@ -0,0 +1,40 @@ +#ifndef _NETLINK_H +#define _NETLINK_H + +#include "syshead.h" +#include "socket.h" +#include "skbuff.h" + +#ifdef DEBUG_SOCKET +#define netlink_dbg(sock, msg, ...) \ + do { \ + socket_dbg(sock, "NETLINK "msg, ##__VA_ARGS__); \ + } while (0) +#else +#define netlink_dbg(msg, th, ...) +#endif + +struct nl_message { + struct list_head list; + struct socket *sock; + struct nlmsghdr nl; + uint8_t data[]; +}; + +int netlink_create(struct socket *sock, int protocol); +int netlink_socket(struct socket *sock, int protocol); +int netlink_connect(struct socket *sock, struct sockaddr *addr, int addr_len, int flags); +int netlink_write(struct socket *sock, const void *buf, int len); +int netlink_read(struct socket *sock, void *buf, int len); +int netlink_close(struct socket *sock); +int netlink_free(struct socket *sock); +int netlink_abort(struct socket *sock); +int netlink_getpeername(struct socket *sock, struct sockaddr *restrict address, + socklen_t *restrict address_len); +int netlink_getsockname(struct socket *sock, struct sockaddr *restrict address, + socklen_t *restrict address_len); +int netlink_sendmsg(struct socket *sock, const struct msghdr *message, int flags); +int netlink_recvmsg(struct socket *sock, struct msghdr *message, int flags); + +struct sock *netlink_lookup(struct sk_buff *skb, uint16_t sport, uint16_t dport); +#endif diff --git a/include/socket.h b/include/socket.h index f9db44f..5b1f7fa 100644 --- a/include/socket.h +++ b/include/socket.h @@ -50,6 +50,8 @@ struct sock_ops { socklen_t *restrict address_len); int (*getsockname) (struct socket *sock, struct sockaddr *restrict addr, socklen_t *restrict address_len); + int (*sendmsg) (struct socket *sock, const struct msghdr *message, int flags); + int (*recvmsg) (struct socket *sock, struct msghdr *message, int flags); }; struct net_family { @@ -62,6 +64,7 @@ struct socket { pid_t pid; int refcnt; enum socket_state state; + int family; short type; int flags; struct sock *sk; @@ -83,9 +86,13 @@ int _getpeername(pid_t pid, int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); int _getsockname(pid_t pid, int socket, struct sockaddr *restrict address, socklen_t *restrict address_len); +int _sendmsg (pid_t pid, int socket, const struct msghdr *message, int flags); +int _recvmsg (pid_t pid, int socket, struct msghdr *message, int flags); struct socket *socket_lookup(uint16_t sport, uint16_t dport); struct socket *socket_find(struct socket *sock); +int filter_sockets(int family, int proto, uint8_t **store, + int (*f)(struct socket *s, uint8_t *ptr), int size); int socket_rd_acquire(struct socket *sock); int socket_wr_acquire(struct socket *sock); int socket_release(struct socket *sock); diff --git a/include/syshead.h b/include/syshead.h index 0f2cc23..e3ffdd6 100644 --- a/include/syshead.h +++ b/include/syshead.h @@ -1,6 +1,7 @@ #ifndef SYSHEAD_H #define SYSHEAD_H +#include #include #include #include @@ -13,6 +14,9 @@ #include #include #include +#include +#include +#include #include #include #include diff --git a/include/tcp.h b/include/tcp.h index 34f5d86..15fe390 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -142,33 +142,33 @@ struct tcpiphdr { } __attribute__((packed)); enum tcp_states { - TCP_LISTEN, /* represents waiting for a connection request from any remote - TCP and port. */ + TCP_ESTABLISHED = 1, /* represents an open connection, data received can be + delivered to the user. The normal state for the data transfer phase + of the connection. */ TCP_SYN_SENT, /* represents waiting for a matching connection request after having sent a connection request. */ TCP_SYN_RECEIVED, /* represents waiting for a confirming connection request acknowledgment after having both received and sent a connection request. */ - TCP_ESTABLISHED, /* represents an open connection, data received can be - delivered to the user. The normal state for the data transfer phase - of the connection. */ TCP_FIN_WAIT_1, /* represents waiting for a connection termination request from the remote TCP, or an acknowledgment of the connection termination request previously sent. */ TCP_FIN_WAIT_2, /* represents waiting for a connection termination request from the remote TCP. */ + TCP_TIME_WAIT, /* represents waiting for enough time to pass to be sure + the remote TCP received the acknowledgment of its connection + termination request. */ TCP_CLOSE, /* represents no connection state at all. */ TCP_CLOSE_WAIT, /* represents waiting for a connection termination request from the local user. */ - TCP_CLOSING, /* represents waiting for a connection termination request - acknowledgment from the remote TCP. */ TCP_LAST_ACK, /* represents waiting for an acknowledgment of the connection termination request previously sent to the remote TCP (which includes an acknowledgment of its connection termination request). */ - TCP_TIME_WAIT, /* represents waiting for enough time to pass to be sure - the remote TCP received the acknowledgment of its connection - termination request. */ + TCP_LISTEN, /* represents waiting for a connection request from any remote + TCP and port. */ + TCP_CLOSING, /* represents waiting for a connection termination request + acknowledgment from the remote TCP. */ }; struct tcb { diff --git a/include/utils.h b/include/utils.h index f70a498..00e894c 100644 --- a/include/utils.h +++ b/include/utils.h @@ -9,6 +9,9 @@ #define print_err(str, ...) \ fprintf(stderr, str, ##__VA_ARGS__); +#define exit_with_error(en, msg) \ + do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) + int run_cmd(char *cmd, ...); uint32_t sum_every_16bits(void *addr, int count); uint16_t checksum(void *addr, int count, int start_sum); diff --git a/src/cli.c b/src/cli.c index 3574b6c..e8e6886 100644 --- a/src/cli.c +++ b/src/cli.c @@ -9,7 +9,6 @@ static void usage(char *app) print_err("Usage: %s\n", app); print_err("\n"); print_err("Linux TCP/IP stack implemented with TUN/TAP devices.\n"); - print_err("Requires the CAP_NET_ADMIN capability. See capabilities(7).\n"); print_err("See https://www.kernel.org/doc/Documentation/networking/tuntap.txt\n"); print_err("\n"); print_err("Options:\n"); diff --git a/src/inet.c b/src/inet.c index 61a940d..224fd0a 100644 --- a/src/inet.c +++ b/src/inet.c @@ -113,6 +113,8 @@ static int inet_stream_connect(struct socket *sock, const struct sockaddr *addr, goto out; } + // False thread deadlock detected here by TSan? + // See commit c60d98322964976439ad4f09344de2989131d091 pthread_mutex_lock(&sock->sleep.lock); while (sock->state == SS_CONNECTING && sk->err == -EINPROGRESS) { socket_release(sock); @@ -120,7 +122,6 @@ static int inet_stream_connect(struct socket *sock, const struct sockaddr *addr, socket_wr_acquire(sock); } pthread_mutex_unlock(&sock->sleep.lock); - socket_wr_acquire(sock); switch (sk->err) { case -ETIMEDOUT: diff --git a/src/ipc.c b/src/ipc.c index 8e149e1..408c173 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -29,7 +29,7 @@ static void ipc_free_thread(int sock) { struct list_head *item, *tmp = NULL; struct ipc_thread *th = NULL; - + pthread_mutex_lock(&lock); list_for_each_safe(item, tmp, &sockets) { @@ -77,7 +77,7 @@ static int ipc_write_rc(int sockfd, pid_t pid, uint16_t type, int rc) err.err = 0; err.rc = rc; } - + memcpy(response->data, &err, sizeof(struct ipc_err)); if (ipc_try_send(sockfd, (char *)response, resplen) == -1) { @@ -107,7 +107,7 @@ static int ipc_read(int sockfd, struct ipc_msg *msg) print_err("Could not allocate memory for IPC read response\n"); return -1; } - + response->type = IPC_READ; response->pid = pid; @@ -133,7 +133,7 @@ static int ipc_write(int sockfd, struct ipc_msg *msg) int head = IPC_BUFLEN - sizeof(struct ipc_write) - sizeof(struct ipc_msg); char buf[payload->len]; - + memset(buf, 0, payload->len); memcpy(buf, payload->buf, payload->len > head ? head : payload->len); @@ -149,7 +149,7 @@ static int ipc_write(int sockfd, struct ipc_msg *msg) print_err("Hmm, we did not read exact payload amount in IPC write\n"); } } - + rc = _write(pid, payload->sockfd, buf, payload->len); return ipc_write_rc(sockfd, pid, IPC_WRITE, rc); @@ -161,7 +161,12 @@ static int ipc_connect(int sockfd, struct ipc_msg *msg) pid_t pid = msg->pid; int rc = -1; - rc = _connect(pid, payload->sockfd, &payload->addr, payload->addrlen); + struct sockaddr addr; + + addr.sa_family = payload->addr.sa_family; + memcpy(addr.sa_data, payload->addr.sa_data, sizeof(addr.sa_data)); + + rc = _connect(pid, payload->sockfd, &addr, payload->addrlen); return ipc_write_rc(sockfd, pid, IPC_CONNECT, rc); } @@ -226,7 +231,7 @@ static int ipc_poll(int sockfd, struct ipc_msg *msg) err.err = 0; err.rc = rc; } - + memcpy(response->data, &err, sizeof(struct ipc_err)); struct ipc_pollfd *polled = (struct ipc_pollfd *) ((struct ipc_err *)response->data)->data; @@ -240,7 +245,7 @@ static int ipc_poll(int sockfd, struct ipc_msg *msg) if (ipc_try_send(sockfd, (char *)response, resplen) == -1) { perror("Error on writing IPC poll response"); } - + return 0; } @@ -251,17 +256,17 @@ static int ipc_fcntl(int sockfd, struct ipc_msg *msg) int rc = -1; switch (fc->cmd) { - case F_GETFL: - rc = _fcntl(pid, fc->sockfd, fc->cmd); - break; - case F_SETFL: - rc = _fcntl(pid, fc->sockfd, fc->cmd, *(int *)fc->data); - break; - default: - print_err("IPC Fcntl cmd not supported %d\n", fc->cmd); - rc = -EINVAL; - } - + case F_GETFL: + rc = _fcntl(pid, fc->sockfd, fc->cmd); + break; + case F_SETFL: + rc = _fcntl(pid, fc->sockfd, fc->cmd, *(int *)fc->data); + break; + default: + print_err("IPC Fcntl cmd not supported %d\n", fc->cmd); + rc = -EINVAL; + } + return ipc_write_rc(sockfd, pid, IPC_FCNTL, rc); } @@ -272,9 +277,11 @@ static int ipc_getsockopt(int sockfd, struct ipc_msg *msg) pid_t pid = msg->pid; int rc = -1; - rc = _getsockopt(pid, opts->fd, opts->level, opts->optname, opts->optval, &opts->optlen); + socklen_t optlen; - int resplen = sizeof(struct ipc_msg) + sizeof(struct ipc_err) + sizeof(struct ipc_sockopt) + opts->optlen; + rc = _getsockopt(pid, opts->fd, opts->level, opts->optname, opts->optval, &optlen); + + int resplen = sizeof(struct ipc_msg) + sizeof(struct ipc_err) + sizeof(struct ipc_sockopt) + optlen; struct ipc_msg *response = alloca(resplen); if (response == NULL) { @@ -294,7 +301,7 @@ static int ipc_getsockopt(int sockfd, struct ipc_msg *msg) err.err = 0; err.rc = rc; } - + memcpy(response->data, &err, sizeof(struct ipc_err)); struct ipc_sockopt *optres = (struct ipc_sockopt *) ((struct ipc_err *)response->data)->data; @@ -302,8 +309,8 @@ static int ipc_getsockopt(int sockfd, struct ipc_msg *msg) optres->fd = opts->fd; optres->level = opts->level; optres->optname = opts->optname; - optres->optlen = opts->optlen; - memcpy(&optres->optval, opts->optval, opts->optlen); + optres->optlen = optlen; + memcpy(&optres->optval, opts->optval, optlen); if (ipc_try_send(sockfd, (char *)response, resplen) == -1) { perror("Error on writing IPC getsockopt response"); @@ -332,7 +339,7 @@ static int ipc_getpeername(int sockfd, struct ipc_msg *msg) struct ipc_sockname *nameres = (struct ipc_sockname *) ((struct ipc_err *)response->data)->data; rc = _getpeername(pid, name->socket, (struct sockaddr *)nameres->sa_data, &nameres->address_len); - + struct ipc_err err; if (rc < 0) { @@ -342,11 +349,11 @@ static int ipc_getpeername(int sockfd, struct ipc_msg *msg) err.err = 0; err.rc = rc; } - + memcpy(response->data, &err, sizeof(struct ipc_err)); nameres->socket = name->socket; - + if (ipc_try_send(sockfd, (char *)response, resplen) == -1) { perror("Error on writing IPC getpeername response"); } @@ -374,7 +381,7 @@ static int ipc_getsockname(int sockfd, struct ipc_msg *msg) struct ipc_sockname *nameres = (struct ipc_sockname *) ((struct ipc_err *)response->data)->data; rc = _getsockname(pid, name->socket, (struct sockaddr *)nameres->sa_data, &nameres->address_len); - + struct ipc_err err; if (rc < 0) { @@ -384,7 +391,7 @@ static int ipc_getsockname(int sockfd, struct ipc_msg *msg) err.err = 0; err.rc = rc; } - + memcpy(response->data, &err, sizeof(struct ipc_err)); nameres->socket = name->socket; @@ -396,43 +403,146 @@ static int ipc_getsockname(int sockfd, struct ipc_msg *msg) return rc; } +int deserialize_msghdr(struct ipc_msghdr *ipc, struct msghdr *message, struct iovec *iov) +{ + uint8_t *ptr = ipc->data; + message->msg_name = ptr; + message->msg_namelen = ipc->msg_namelen; + ptr += message->msg_namelen; + + message->msg_control = ptr; + message->msg_controllen = ipc->msg_controllen; + ptr += message->msg_controllen; + + message->msg_iovlen = ipc->msg_iovlen; + + for (int i = 0; i < message->msg_iovlen; i++) { + struct ipc_iovec *v = (struct ipc_iovec *) ptr; + iov[i].iov_len = v->iov_len; + iov[i].iov_base = v->iov_base; + ptr = v->iov_base; + ptr += iov[i].iov_len; + } + + message->msg_iov = iov; + + return 0; +} + +int serialize_msghdr(struct ipc_msghdr *imh, struct msghdr *hdr) +{ + imh->msg_namelen = hdr->msg_namelen; + imh->msg_iovlen = hdr->msg_iovlen; + imh->msg_controllen = hdr->msg_controllen; + imh->flags = hdr->msg_flags; + + uint8_t *ptr = imh->data; + for (int i = 0; i < hdr->msg_iovlen; i++) { + struct ipc_iovec *v = (struct ipc_iovec *)ptr; + struct iovec *iov = &hdr->msg_iov[i]; + + v->iov_len = iov->iov_len; + memcpy(v->iov_base, iov->iov_base, iov->iov_len); + + ptr += sizeof(struct ipc_iovec); + ptr += v->iov_len; + } + + return 0; +} + +static int ipc_sendmsg(int sockfd, struct ipc_msg *msg) +{ + struct ipc_msghdr *payload = (struct ipc_msghdr *) msg->data; + pid_t pid = msg->pid; + int rc = -1; + + struct msghdr message; + struct iovec iov[payload->msg_iovlen]; + + deserialize_msghdr(payload, &message, iov); + + rc = _sendmsg(pid, payload->sockfd, &message, payload->flags); + + return ipc_write_rc(sockfd, pid, IPC_SENDMSG, rc); +} + +static int ipc_recvmsg(int sockfd, struct ipc_msg *msg) +{ + struct ipc_msghdr *payload = (struct ipc_msghdr *) msg->data; + pid_t pid = msg->pid; + int rc = -1; + + struct msghdr message; + struct iovec iov[payload->msg_iovlen]; + + deserialize_msghdr(payload, &message, iov); + + rc = _recvmsg(pid, payload->sockfd, &message, payload->flags); + + int iovlen = 0; + + for (int i = 0; i < payload->msg_iovlen; i++) { + iovlen += iov[i].iov_len; + iovlen += sizeof(struct ipc_iovec); + } + + int resplen = sizeof(struct ipc_msg) + sizeof(struct ipc_err) + + sizeof(struct ipc_msghdr) + iovlen; + struct ipc_msg *response = alloca(resplen); + struct ipc_err *error = (struct ipc_err *) response->data; + struct ipc_msghdr *actual = (struct ipc_msghdr *) error->data; + + serialize_msghdr(actual, &message); + actual->sockfd = sockfd; + + response->type = IPC_RECVMSG; + response->pid = pid; + + error->rc = rc < 0 ? -1 : rc; + error->err = rc < 0 ? -rc : 0; + + if (ipc_try_send(sockfd, (char *)response, resplen) == -1) { + perror("Error on writing IPC recvmsg response"); + } + + return rc; +} + static int demux_ipc_socket_call(int sockfd, char *cmdbuf, int blen) { struct ipc_msg *msg = (struct ipc_msg *)cmdbuf; switch (msg->type) { - case IPC_SOCKET: - return ipc_socket(sockfd, msg); - break; - case IPC_CONNECT: - return ipc_connect(sockfd, msg); - break; - case IPC_WRITE: - return ipc_write(sockfd, msg); - break; - case IPC_READ: - return ipc_read(sockfd, msg); - break; - case IPC_CLOSE: - return ipc_close(sockfd, msg); - break; - case IPC_POLL: - return ipc_poll(sockfd, msg); - break; - case IPC_FCNTL: - return ipc_fcntl(sockfd, msg); - break; - case IPC_GETSOCKOPT: - return ipc_getsockopt(sockfd, msg); - case IPC_GETPEERNAME: - return ipc_getpeername(sockfd, msg); - case IPC_GETSOCKNAME: - return ipc_getsockname(sockfd, msg); - default: - print_err("No such IPC type %d\n", msg->type); - break; + case IPC_SOCKET: + return ipc_socket(sockfd, msg); + case IPC_CONNECT: + return ipc_connect(sockfd, msg); + case IPC_WRITE: + return ipc_write(sockfd, msg); + case IPC_READ: + return ipc_read(sockfd, msg); + case IPC_CLOSE: + return ipc_close(sockfd, msg); + case IPC_POLL: + return ipc_poll(sockfd, msg); + case IPC_FCNTL: + return ipc_fcntl(sockfd, msg); + case IPC_GETSOCKOPT: + return ipc_getsockopt(sockfd, msg); + case IPC_GETPEERNAME: + return ipc_getpeername(sockfd, msg); + case IPC_GETSOCKNAME: + return ipc_getsockname(sockfd, msg); + case IPC_SENDMSG: + return ipc_sendmsg(sockfd, msg); + case IPC_RECVMSG: + return ipc_recvmsg(sockfd, msg); + default: + print_err("No such IPC type %d\n", msg->type); + break; }; - + return 0; } @@ -457,7 +567,7 @@ void *socket_ipc_open(void *args) { if (rc == -1) { perror("socket ipc read"); } - + return NULL; } @@ -468,55 +578,45 @@ void *start_ipc_listener() char *sockname = "/tmp/lvlip.socket"; unlink(sockname); - + if (strnlen(sockname, sizeof(un.sun_path)) == sizeof(un.sun_path)) { // Path is too long print_err("Path for UNIX socket is too long\n"); exit(-1); } - + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { - perror("IPC listener UNIX socket"); - exit(EXIT_FAILURE); + exit_with_error(fd, "Unable to create IPC UNIX socket"); } memset(&un, 0, sizeof(struct sockaddr_un)); un.sun_family = AF_UNIX; strncpy(un.sun_path, sockname, sizeof(un.sun_path) - 1); - rc = bind(fd, (const struct sockaddr *) &un, sizeof(struct sockaddr_un)); - - if (rc == -1) { - perror("IPC bind"); - exit(EXIT_FAILURE); + if ((rc = bind(fd, (const struct sockaddr *) &un, sizeof(struct sockaddr_un))) == -1) { + print_err("Check socket file permissions: %s\n", sockname); + exit_with_error(rc, "Unable to bind to IPC socket"); } - rc = listen(fd, 20); - - if (rc == -1) { - perror("IPC listen"); - exit(EXIT_FAILURE); + if ((rc = listen(fd, 20)) == -1) { + exit_with_error(rc, "Unable to listen on IPC socket"); } - if (chmod(sockname, S_IRUSR | S_IWUSR | S_IXUSR | - S_IRGRP | S_IWGRP | S_IXGRP | - S_IROTH | S_IWOTH | S_IXOTH) == -1) { - perror("Chmod on lvl-ip IPC UNIX socket failed"); - exit(EXIT_FAILURE); + if ((rc = chmod(sockname, S_IRUSR | S_IWUSR | S_IXUSR | + S_IRGRP | S_IWGRP | S_IXGRP | + S_IROTH | S_IWOTH | S_IXOTH)) == -1) { + exit_with_error(rc, "Chmod on lvl-ip IPC UNIX socket failed"); } for (;;) { - datasock = accept(fd, NULL, NULL); - if (datasock == -1) { - perror("IPC accept"); - exit(EXIT_FAILURE); + if ((datasock = accept(fd, NULL, NULL)) == -1) { + exit_with_error(datasock ,"IPC accept"); } struct ipc_thread *th = ipc_alloc_thread(datasock); - if (pthread_create(&th->id, NULL, &socket_ipc_open, &th->sock) != 0) { - print_err("Error on socket thread creation\n"); - exit(1); + if ((rc = pthread_create(&th->id, NULL, &socket_ipc_open, &th->sock)) != 0) { + exit_with_error(rc, "Error on socket thread creation"); }; } diff --git a/src/main.c b/src/main.c index 9376ce2..e2be991 100644 --- a/src/main.c +++ b/src/main.c @@ -107,26 +107,12 @@ void free_stack() free_tun(); } -void init_security() -{ - if (prctl(PR_CAPBSET_DROP, CAP_NET_ADMIN) == -1) { - perror("Error on network admin capability drop"); - exit(1); - } - - if (prctl(PR_CAPBSET_DROP, CAP_SETPCAP) == -1) { - perror("Error on capability set drop"); - exit(1); - } -} - int main(int argc, char** argv) { parse_cli(argc, argv); init_signals(); init_stack(); - init_security(); run_threads(); wait_for_threads(); diff --git a/src/netlink.c b/src/netlink.c new file mode 100644 index 0000000..1974f1f --- /dev/null +++ b/src/netlink.c @@ -0,0 +1,475 @@ +#include "syshead.h" +#include "netlink.h" +#include "socket.h" +#include "sock.h" +#include "tcp.h" +#include "wait.h" + +extern struct net_ops tcp_ops; + +static int message_amount = 0; +static LIST_HEAD(messages); +static pthread_rwlock_t mlock = PTHREAD_RWLOCK_INITIALIZER; + +static int netlink_stream_connect(struct socket *sock, const struct sockaddr *addr, + int addr_len, int flags); + +static int netlink_OPS = 1; + + +struct net_family netlink = { + .create = netlink_create, +}; + +static struct sock_ops netlink_stream_ops = { + .connect = &netlink_stream_connect, + .write = &netlink_write, + .read = &netlink_read, + .close = &netlink_close, + .free = &netlink_free, + .abort = &netlink_abort, + .getpeername = &netlink_getpeername, + .getsockname = &netlink_getsockname, + .sendmsg = &netlink_sendmsg, + .recvmsg = &netlink_recvmsg, +}; + +static struct sock_type netlink_ops[] = { + { + .sock_ops = &netlink_stream_ops, + .net_ops = &tcp_ops, + .type = SOCK_STREAM, + .protocol = IPPROTO_TCP, + } +}; + +struct nl_message *alloc_message(struct socket *sock, const struct nlmsghdr *nl, void *payload, int flags) +{ + int payload_size = nl->nlmsg_len - sizeof(struct nlmsghdr); + struct nl_message *nlmsg = malloc(sizeof (struct nl_message) + payload_size); + list_init(&nlmsg->list); + + nlmsg->sock = sock; + memcpy(&nlmsg->nl, nl, sizeof(struct nlmsghdr)); + memcpy(nlmsg->data, payload, payload_size); + + return nlmsg; +} + +int message_free(struct nl_message *nlm) +{ + int rc = pthread_rwlock_wrlock(&mlock); + + if (rc != 0) { + perror("Could not acquire message lock"); + return rc; + } + + list_del(&nlm->list); + message_amount--; + + rc = pthread_rwlock_unlock(&mlock); + if (rc != 0) { + perror("Could not release message lock"); + return rc; + } + + return rc; +} + +int netlink_create(struct socket *sock, int protocol) +{ + struct sock *sk; + struct sock_type *skt = NULL; + + for (int i = 0; i < netlink_OPS; i++) { + if (netlink_ops[i].type & sock->type) { + skt = &netlink_ops[i]; + break; + } + } + + if (!skt) { + print_err("Could not find socktype for socket\n"); + return 1; + } + + sock->ops = skt->sock_ops; + + sk = sk_alloc(skt->net_ops, protocol); + sk->protocol = protocol; + + sock_init_data(sock, sk); + + return 0; +} + +int netlink_socket(struct socket *sock, int protocol) +{ + return 0; +} + +int netlink_connect(struct socket *sock, struct sockaddr *addr, + int addr_len, int flags) +{ + return 0; +} + +static int netlink_stream_connect(struct socket *sock, const struct sockaddr *addr, + int addr_len, int flags) +{ + struct sock *sk = sock->sk; + int rc = 0; + + if (addr_len < sizeof(addr->sa_family)) { + return -EINVAL; + } + + if (addr->sa_family == AF_UNSPEC) { + sk->ops->disconnect(sk, flags); + return -EAFNOSUPPORT; + } + + switch (sock->state) { + default: + sk->err = -EINVAL; + goto out; + case SS_CONNECTED: + sk->err = -EISCONN; + goto out; + case SS_CONNECTING: + sk->err = -EALREADY; + goto out; + case SS_UNCONNECTED: + sk->err = -EISCONN; + if (sk->state != TCP_CLOSE) { + goto out; + } + + sk->ops->connect(sk, addr, addr_len, flags); + sock->state = SS_CONNECTING; + sk->err = -EINPROGRESS; + + if (sock->flags & O_NONBLOCK) { + goto out; + } + + pthread_mutex_lock(&sock->sleep.lock); + while (sock->state == SS_CONNECTING && sk->err == -EINPROGRESS) { + socket_release(sock); + wait_sleep(&sock->sleep); + socket_wr_acquire(sock); + } + pthread_mutex_unlock(&sock->sleep.lock); + socket_wr_acquire(sock); + + switch (sk->err) { + case -ETIMEDOUT: + case -ECONNREFUSED: + goto sock_error; + } + + if (sk->err != 0) { + goto out; + } + + sock->state = SS_CONNECTED; + break; + } + +out: + return sk->err; +sock_error: + rc = sk->err; + return rc; +} + +int netlink_write(struct socket *sock, const void *buf, int len) +{ + struct sock *sk = sock->sk; + + return sk->ops->write(sk, buf, len); +} + +int netlink_read(struct socket *sock, void *buf, int len) +{ + struct sock *sk = sock->sk; + + return sk->ops->read(sk, buf, len); +} + +struct sock *netlink_lookup(struct sk_buff *skb, uint16_t sport, uint16_t dport) +{ + struct socket *sock = socket_lookup(sport, dport); + if (sock == NULL) return NULL; + + return sock->sk; +} + +int netlink_close(struct socket *sock) +{ + if (!sock) { + return 0; + } + + struct sock *sk = sock->sk; + + return sock->sk->ops->close(sk); +} + +int netlink_free(struct socket *sock) +{ + struct sock *sk = sock->sk; + sock_free(sk); + free(sock->sk); + + return 0; +} + +int netlink_abort(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (sk) { + sk->ops->abort(sk); + } + + return 0; +} + +int netlink_getpeername(struct socket *sock, struct sockaddr *restrict address, + socklen_t *address_len) +{ + struct sock *sk = sock->sk; + + if (sk == NULL) { + return -1; + } + + struct sockaddr_in *res = (struct sockaddr_in *) address; + res->sin_family = AF_NETLINK; + res->sin_port = htons(sk->dport); + res->sin_addr.s_addr = htonl(sk->daddr); + *address_len = sizeof(struct sockaddr_in); + + netlink_dbg(sock, "geetpeername sin_family %d sin_port %d sin_addr %d addrlen %d", + res->sin_family, ntohs(res->sin_port), ntohl(res->sin_addr.s_addr), *address_len); + + return 0; +} + +int netlink_getsockname(struct socket *sock, struct sockaddr *restrict address, + socklen_t *address_len) +{ + struct sock *sk = sock->sk; + + if (sk == NULL) { + return -1; + } + + struct sockaddr_nl *res = (struct sockaddr_nl *) address; + res->nl_family = AF_NETLINK; + res->nl_pid = 0; + *address_len = sizeof(struct sockaddr_nl); + + return 0; +} + +int netlink_sendmsg(struct socket *sock, const struct msghdr *message, int flags) +{ + struct sock *sk = sock->sk; + + if (sk == NULL) { + return -1; + } + + int rc = 0; + struct nl_message *nlmsg; + + for (int i = 0; imsg_iovlen; i++) { + struct iovec *v = &message->msg_iov[i]; + struct nlmsghdr *nl = v->iov_base; + struct sock_diag_req *sdr = v->iov_base + sizeof(struct nlmsghdr); + + nlmsg = alloc_message(sock, nl, (void *)sdr, 0); + + pthread_rwlock_wrlock(&mlock); + list_add_tail(&nlmsg->list, &messages); + message_amount++; + pthread_rwlock_unlock(&mlock); + + rc += nl->nlmsg_len; + } + + return rc; +} + +struct nl_message *find_netlink_request(int sockfd) +{ + struct list_head *item; + struct nl_message *entry; + struct nl_message *nlm = NULL; + + pthread_rwlock_wrlock(&mlock); + list_for_each(item, &messages) { + entry = list_entry(item, struct nl_message, list); + + if (entry->sock->fd == sockfd) { + nlm = entry; + } + } + pthread_rwlock_unlock(&mlock); + + return nlm; +} + +int convert_socket_to_inet_tcp_diag_msg(struct socket *s, uint8_t *ptr) +{ + struct inet_diag_msg *idm = (struct inet_diag_msg *) ptr; + + memset(ptr, 0, sizeof(struct inet_diag_msg)); + + idm->idiag_family = AF_INET; + idm->idiag_state = s->sk->state; + idm->idiag_timer = 0; + idm->idiag_retrans = 0; + idm->idiag_expires = 0; + idm->idiag_rqueue = 0; + idm->idiag_wqueue = 0; + idm->idiag_uid = 0; + idm->idiag_inode = 0; + idm->id.idiag_sport = htons(s->sk->sport); + idm->id.idiag_dport = htons(s->sk->dport); + idm->id.idiag_src[0] = htonl(s->sk->saddr); + idm->id.idiag_dst[0] = htonl(s->sk->daddr); + idm->id.idiag_if = 0; + + return sizeof(struct inet_diag_msg); +} + +int process_netlink_request_tcp(struct nlmsghdr *nl, struct nl_message *req) +{ + + struct inet_diag_msg *tmp = NULL; + + int rc = filter_sockets(AF_INET, SOCK_STREAM, (uint8_t **)&tmp, convert_socket_to_inet_tcp_diag_msg, sizeof(struct inet_diag_msg)); + + if (rc < 0) { + perror("Failed on netlink TCP processing"); + return -1; + } + + struct + { + struct nlmsghdr nlh; + struct inet_diag_msg idm; + struct nlattr nla; + uint32_t flag; + } resp[rc]; + + size_t size = sizeof(struct nlmsghdr) + sizeof(struct inet_diag_msg) + sizeof(struct nlattr) + sizeof(uint32_t); + + for (int i = 0; i < rc; i++) { + resp[i].nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY; + resp[i].nlh.nlmsg_len = size; + resp[i].nlh.nlmsg_flags = NLM_F_MULTI; + resp[i].nlh.nlmsg_seq = 123456; + resp[i].nlh.nlmsg_pid = 0; + resp[i].nla.nla_len = 5; + resp[i].nla.nla_type = INET_DIAG_SHUTDOWN; + resp[i].flag = 0; + + memcpy(&resp[i].idm, (tmp + i), sizeof(struct inet_diag_msg)); + } + + memcpy(nl, resp, sizeof(resp)); + + if (tmp != NULL) { + free(tmp); + } + + return size * rc; +} + +int process_netlink_request_not_supported(struct nlmsghdr *nl, struct nl_message *req) +{ + return 20; +} + +int demux_netlink_request(struct nlmsghdr *nl, struct nl_message *req, int flags) +{ + int rc = -1; + + if (req == NULL) { + return 0; + } + + struct sock_diag_req *sdr = (struct sock_diag_req *)req->data; + + switch (sdr->sdiag_family) { + case AF_INET: + switch (sdr->sdiag_protocol) { + case IPPROTO_TCP: + rc = process_netlink_request_tcp(nl, req); + break; + default: + print_err("Not supported sdiag_protocol: %d\n", sdr->sdiag_protocol); + rc = process_netlink_request_not_supported(nl, req); + break; + } + break; + default: + print_err("Not supported sdiag_family: %d\n", sdr->sdiag_family); + rc = process_netlink_request_not_supported(nl, req); + break; + } + + if (!(flags & (MSG_PEEK | MSG_TRUNC))) { + // Request has been consumed, delete it + assert(message_free(req) == 0); + } + + return rc; +} + +int netlink_recvmsg(struct socket *sock, struct msghdr *message, int flags) +{ + struct sock *sk = sock->sk; + + if (sk == NULL) { + return -1; + } + + struct sockaddr_nl *snl = message->msg_name; + memset(snl, 0, sizeof(struct sockaddr_nl)); + struct iovec *v = message->msg_iov; + + struct nlmsghdr *nl = v->iov_base; + memset(nl, 0, sizeof(struct nlmsghdr)); + + struct nl_message *nlm = find_netlink_request(sock->fd); + + int rc = demux_netlink_request(nl, nlm, flags); + + if (rc == 0) { + rc = 20; + } + + if (flags & (MSG_PEEK | MSG_TRUNC)) { + // Empty out msg_iov contents + nl->nlmsg_flags = MSG_TRUNC; + return rc; + } + + if (rc == 20) { + nl->nlmsg_len = 20; + nl->nlmsg_type = NLMSG_DONE; + nl->nlmsg_flags = NLM_F_MULTI; + nl->nlmsg_seq = 123456; + nl->nlmsg_pid = 0; + } + + v->iov_len = rc; + + return rc; +} diff --git a/src/socket.c b/src/socket.c index 0bac153..d085ef4 100644 --- a/src/socket.c +++ b/src/socket.c @@ -2,6 +2,7 @@ #include "utils.h" #include "socket.h" #include "inet.h" +#include "netlink.h" #include "wait.h" #include "timer.h" @@ -10,9 +11,11 @@ static LIST_HEAD(sockets); static pthread_rwlock_t slock = PTHREAD_RWLOCK_INITIALIZER; extern struct net_family inet; +extern struct net_family netlink; static struct net_family *families[128] = { [AF_INET] = &inet, + [AF_NETLINK] = &netlink, }; static struct socket *alloc_socket(pid_t pid) @@ -181,6 +184,47 @@ struct socket *socket_find(struct socket *find) return sock; } +int filter_sockets(int family, int type, uint8_t **store, + int (*f)(struct socket *s, uint8_t *ptr), int size) +{ + struct list_head *item; + struct socket *sock = NULL; + + int rc = 0; + int allocated = 0; + int amount = 0; + + // Socket mutex should already be locked upstairs + + list_for_each(item, &sockets) { + sock = list_entry(item, struct socket, list); + + if (sock->family == family && sock->type == type) { + allocated += size; + + *store = realloc(*store, allocated); + if (*store == NULL) { + perror("Failed on allocating socket filter store memory"); + goto error; + } + + rc = f(sock, (*store + (rc * amount))); + + if (rc < 0) { + perror("Failed on socket filtering"); + goto error; + } + + amount++; + } + } + + return amount; + +error: + return -1; +} + #ifdef DEBUG_SOCKET void socket_debug() { @@ -216,6 +260,7 @@ int _socket(pid_t pid, int domain, int type, int protocol) } sock->type = type; + sock->family = domain; family = families[domain]; @@ -466,3 +511,37 @@ int _getsockname(pid_t pid, int socket, struct sockaddr *restrict address, return rc; } + +int _sendmsg(pid_t pid, int socket, const struct msghdr *msg, int flags) +{ + struct socket *sock; + + if ((sock = get_socket(pid, socket)) == NULL) { + print_err("Sendmsg: could not find socket (fd %u) for connection (pid %d)\n", socket, pid); + return -EBADF; + } + + socket_wr_acquire(sock); + int rc = sock->ops->sendmsg(sock, msg, flags); + socket_release(sock); + + return rc; +} + +int _recvmsg(pid_t pid, int socket, struct msghdr *msg, int flags) +{ + struct socket *sock; + + if ((sock = get_socket(pid, socket)) == NULL) { + print_err("Recvmsg: could not find socket (fd %u) for connection (pid %d)\n", socket, pid); + return -EBADF; + } + + pthread_rwlock_rdlock(&slock); + socket_rd_acquire(sock); + int rc = sock->ops->recvmsg(sock, msg, flags); + socket_release(sock); + pthread_rwlock_unlock(&slock); + + return rc; +} diff --git a/src/tuntap_if.c b/src/tuntap_if.c index 5677a9d..8aaaf9d 100644 --- a/src/tuntap_if.c +++ b/src/tuntap_if.c @@ -6,23 +6,9 @@ static int tun_fd; static char* dev; char *tapaddr = "10.0.0.5"; +char *cidr = "10.0.0.5/24"; char *taproute = "10.0.0.0/24"; -static int set_if_route(char *dev, char *cidr) -{ - return run_cmd("ip route add dev %s %s", dev, cidr); -} - -static int set_if_address(char *dev, char *cidr) -{ - return run_cmd("ip address add dev %s local %s", dev, cidr); -} - -static int set_if_up(char *dev) -{ - return run_cmd("ip link set dev %s up", dev); -} - /* * Taken from Kernel Documentation/networking/tuntap.txt */ @@ -34,7 +20,7 @@ static int tun_alloc(char *dev) if( (fd = open("/dev/net/tap", O_RDWR)) < 0 ) { perror("Cannot open TUN/TAP dev\n" "Make sure one exists with " - "'$ mknod /dev/net/tap c 10 200'"); + "'mknod /dev/net/tap c 10 200'"); exit(1); } @@ -55,8 +41,7 @@ static int tun_alloc(char *dev) close(fd); return err; } - - strcpy(dev, ifr.ifr_name); + return fd; } @@ -72,20 +57,7 @@ int tun_write(char *buf, int len) void tun_init() { - dev = calloc(10, 1); - tun_fd = tun_alloc(dev); - - if (set_if_up(dev) != 0) { - print_err("ERROR when setting up if\n"); - } - - if (set_if_route(dev, taproute) != 0) { - print_err("ERROR when setting route for if\n"); - } - - if (set_if_address(dev, tapaddr) != 0) { - print_err("ERROR when setting addr for if\n"); - } + tun_fd = tun_alloc("tap0"); } void free_tun() diff --git a/tests/utils/common b/tests/utils/common index 36b4d3e..a5c51b0 100755 --- a/tests/utils/common +++ b/tests/utils/common @@ -6,7 +6,7 @@ repo="$(git rev-parse --show-toplevel)" folder="$(dirname $0)" function start_stack { - "$repo/lvl-ip" > ../lvl-ip-test.log 2>&1 & + sudo "$repo/lvl-ip" > ../lvl-ip-test.log 2>&1 & stackip="$!" for i in {1..3}; do @@ -26,6 +26,7 @@ function test_pass { function test_fail { echo -e "\t$2 Test fail: $1" 2>&1 + cat ../lvl-ip-test.log exit 1 } diff --git a/tools/Makefile b/tools/Makefile index e92019f..e317f3a 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -2,7 +2,7 @@ CPPFLAGS = -I ../include -Wall -Werror all: liblevelip -debug: CFLAGS+= -DDEBUG_API +debug: CFLAGS+= -g -DDEBUG_API debug: liblevelip liblevelip: liblevelip.c diff --git a/tools/liblevelip.c b/tools/liblevelip.c index e2512a2..0e8877c 100644 --- a/tools/liblevelip.c +++ b/tools/liblevelip.c @@ -8,14 +8,15 @@ #define RCBUF_LEN 512 static int (*__start_main)(int (*main) (int, char * *, char * *), int argc, \ - char * * ubp_av, void (*init) (void), void (*fini) (void), \ - void (*rtld_fini) (void), void (* stack_end)); + char * * ubp_av, void (*init) (void), void (*fini) (void), \ + void (*rtld_fini) (void), void (* stack_end)); +static int (*_bind)(int socket, const struct sockaddr *address, socklen_t address_len) = NULL; static int (*_fcntl)(int fildes, int cmd, ...) = NULL; static int (*_setsockopt)(int fd, int level, int optname, - const void *optval, socklen_t optlen) = NULL; + const void *optval, socklen_t optlen) = NULL; static int (*_getsockopt)(int fd, int level, int optname, - const void *optval, socklen_t *optlen) = NULL; + const void *optval, socklen_t *optlen) = NULL; static int (*_read)(int sockfd, void *buf, size_t len) = NULL; static int (*_write)(int sockfd, const void *buf, size_t len) = NULL; static int (*_connect)(int sockfd, const struct sockaddr *addr, socklen_t addrlen) = NULL; @@ -23,23 +24,25 @@ static int (*_socket)(int domain, int type, int protocol) = NULL; static int (*_close)(int fildes) = NULL; static int (*_poll)(struct pollfd fds[], nfds_t nfds, int timeout) = NULL; static int (*_pollchk)(struct pollfd *__fds, nfds_t __nfds, int __timeout, - __SIZE_TYPE__ __fdslen) = NULL; + __SIZE_TYPE__ __fdslen) = NULL; static int (*_ppoll)(struct pollfd *fds, nfds_t nfds, - const struct timespec *tmo_p, const sigset_t *sigmask) = NULL; + const struct timespec *tmo_p, const sigset_t *sigmask) = NULL; static int (*_select)(int nfds, fd_set *restrict readfds, - fd_set *restrict writefds, fd_set *restrict errorfds, - struct timeval *restrict timeout); + fd_set *restrict writefds, fd_set *restrict errorfds, + struct timeval *restrict timeout); static ssize_t (*_sendto)(int sockfd, const void *message, size_t length, - int flags, const struct sockaddr *dest_addr, - socklen_t dest_len) = NULL; + int flags, const struct sockaddr *dest_addr, + socklen_t dest_len) = NULL; static ssize_t (*_recvfrom)(int sockfd, void *buf, size_t len, - int flags, struct sockaddr *restrict address, - socklen_t *restrict addrlen) = NULL; + int flags, struct sockaddr *restrict address, + socklen_t *restrict addrlen) = NULL; static int (*_getpeername)(int socket, struct sockaddr *restrict address, - socklen_t *restrict address_len) = NULL; + socklen_t *restrict address_len) = NULL; static int (*_getsockname)(int socket, struct sockaddr *restrict address, - socklen_t *restrict address_len) = NULL; + socklen_t *restrict address_len) = NULL; +static ssize_t (*_sendmsg)(int socket, const struct msghdr *message, int flags) = NULL; +static ssize_t (*_recvmsg)(int socket, struct msghdr *message, int flags) = NULL; static int lvlip_socks_count = 0; static LIST_HEAD(lvlip_socks); @@ -50,7 +53,7 @@ static inline struct lvlip_sock *lvlip_get_sock(int fd) { list_for_each(item, &lvlip_socks) { sock = list_entry(item, struct lvlip_sock, list); - + if (sock->fd == fd) return sock; }; @@ -59,13 +62,21 @@ static inline struct lvlip_sock *lvlip_get_sock(int fd) { static int is_socket_supported(int domain, int type, int protocol) { - if (domain != AF_INET) return 0; - - if (!(type & SOCK_STREAM)) return 0; - - if (protocol != 0 && protocol != IPPROTO_TCP) return 0; + int supported = 0; + + switch (domain) { + case AF_INET: + if (type & SOCK_STREAM && + (protocol == IPPROTO_TCP || protocol == 0)) supported = 1; + break; + case AF_NETLINK: + if (protocol & NETLINK_SOCK_DIAG) supported = 1; + break; + default: + supported = 0; + } - return 1; + return supported; } static int init_socket(char *sockname) @@ -96,7 +107,7 @@ static int init_socket(char *sockname) strncpy(addr.sun_path, sockname, sizeof(addr.sun_path) - 1); ret = _connect(data_socket, (const struct sockaddr *) &addr, - sizeof(struct sockaddr_un)); + sizeof(struct sockaddr_un)); if (ret == -1) { print_err("Error connecting to level-ip. Is it up?\n"); exit(EXIT_FAILURE); @@ -123,13 +134,13 @@ static int transmit_lvlip(int lvlfd, struct ipc_msg *msg, int msglen) if (_read(lvlfd, buf, RCBUF_LEN) == -1) { perror("Could not read IPC response"); } - + struct ipc_msg *response = (struct ipc_msg *) buf; if (response->type != msg->type || response->pid != msg->pid) { print_err("ERR: IPC msg response expected type %d, pid %d\n" - " actual type %d, pid %d\n", - msg->type, msg->pid, response->type, response->pid); + " actual type %d, pid %d\n", + msg->type, msg->pid, response->type, response->pid); return -1; } @@ -147,18 +158,20 @@ int socket(int domain, int type, int protocol) } struct lvlip_sock *sock; - + int lvlfd = init_socket("/tmp/lvlip.socket"); sock = lvlip_alloc(); sock->lvlfd = lvlfd; list_add_tail(&sock->list, &lvlip_socks); lvlip_socks_count++; - + int pid = getpid(); int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_socket); struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + msg->type = IPC_SOCKET; msg->pid = pid; @@ -167,7 +180,7 @@ int socket(int domain, int type, int protocol) .type = type, .protocol = protocol }; - + memcpy(msg->data, &usersock, sizeof(struct ipc_socket)); int sockfd = transmit_lvlip(sock->lvlfd, msg, msglen); @@ -181,7 +194,7 @@ int socket(int domain, int type, int protocol) sock->fd = sockfd; lvl_sock_dbg("Socket called", sock); - + return sockfd; } @@ -195,12 +208,14 @@ int close(int fd) } lvl_sock_dbg("Close called", sock); - + int pid = getpid(); int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_close); int rc = 0; struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + msg->type = IPC_CLOSE; msg->pid = pid; @@ -223,11 +238,13 @@ int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) } lvl_sock_dbg("Connect called", sock); - + int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_connect); int pid = getpid(); - + struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + msg->type = IPC_CONNECT; msg->pid = pid; @@ -256,6 +273,8 @@ ssize_t write(int sockfd, const void *buf, size_t len) int pid = getpid(); struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + msg->type = IPC_WRITE; msg->pid = pid; @@ -285,6 +304,8 @@ ssize_t read(int sockfd, void *buf, size_t len) int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_read); struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + msg->type = IPC_READ; msg->pid = pid; @@ -308,13 +329,13 @@ ssize_t read(int sockfd, void *buf, size_t len) if (_read(sock->lvlfd, rbuf, rlen) == -1) { perror("Could not read IPC read response"); } - + struct ipc_msg *response = (struct ipc_msg *) rbuf; if (response->type != IPC_READ || response->pid != pid) { print_err("ERR: IPC read response expected: type %d, pid %d\n" - " actual: type %d, pid %d\n", - IPC_READ, pid, response->type, response->pid); + " actual: type %d, pid %d\n", + IPC_READ, pid, response->type, response->pid); return -1; } @@ -332,7 +353,7 @@ ssize_t read(int sockfd, void *buf, size_t len) memset(buf, 0, len); memcpy(buf, data->buf, data->len); - + return data->len; } @@ -342,30 +363,211 @@ ssize_t send(int fd, const void *buf, size_t len, int flags) } ssize_t sendto(int fd, const void *buf, size_t len, - int flags, const struct sockaddr *dest_addr, - socklen_t dest_len) + int flags, const struct sockaddr *dest_addr, + socklen_t dest_len) { if (!lvlip_get_sock(fd)) return _sendto(fd, buf, len, - flags, dest_addr, dest_len); + flags, dest_addr, dest_len); return write(fd, buf, len); } +ssize_t sendmsg(int socket, const struct msghdr *message, int flags) +{ + struct lvlip_sock *sock = lvlip_get_sock(socket); + + if (sock == NULL) return _sendmsg(socket, message, flags); + + lvl_sock_dbg("Sendmsg called", sock); + + int len = 0; + len += message->msg_namelen; + len += message->msg_controllen; + + for (int i = 0; i < message->msg_iovlen; i++) { + len += sizeof(size_t); + len += message->msg_iov[i].iov_len; + } + + int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_msghdr) + len; + int pid = getpid(); + + struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + + msg->type = IPC_SENDMSG; + msg->pid = pid; + + struct ipc_msghdr *payload = (struct ipc_msghdr *)msg->data; + payload->sockfd = socket; + payload->msg_namelen = message->msg_namelen; + payload->msg_iovlen = message->msg_iovlen; + payload->msg_controllen = message->msg_controllen; + payload->flags = flags; + + uint8_t *ptr = payload->data; + memcpy(ptr, message->msg_name, payload->msg_namelen); + ptr += payload->msg_namelen; + + memcpy(ptr, message->msg_control, payload->msg_controllen); + ptr += payload->msg_controllen; + + for (int i = 0; i < payload->msg_iovlen; i++) { + size_t iovlen = message->msg_iov[i].iov_len; + struct ipc_iovec *v = (struct ipc_iovec *) ptr; + v->iov_len = iovlen; + + memcpy(v->iov_base, message->msg_iov[i].iov_base, iovlen); + ptr = v->iov_base + iovlen; + } + + int rc = transmit_lvlip(sock->lvlfd, msg, msglen); + + struct sockaddr_nl *snl = message->msg_name; + snl->nl_pid = 0; + + lvl_sock_dbg("Sendmsg returning rc %d", sock, rc); + + return rc; +} + ssize_t recv(int fd, void *buf, size_t len, int flags) { return recvfrom(fd, buf, len, flags, NULL, 0); } ssize_t recvfrom(int fd, void *restrict buf, size_t len, - int flags, struct sockaddr *restrict address, - socklen_t *restrict addrlen) + int flags, struct sockaddr *restrict address, + socklen_t *restrict addrlen) { if (!lvlip_get_sock(fd)) return _recvfrom(fd, buf, len, - flags, address, addrlen); + flags, address, addrlen); return read(fd, buf, len); } +ssize_t recvmsg(int socket, struct msghdr *message, int flags) +{ + struct lvlip_sock *sock = lvlip_get_sock(socket); + + if (sock == NULL) return _recvmsg(socket, message, flags); + + lvl_sock_dbg("Recvmsg called with flags MSG_PEEK %d, MSG_TRUNC %d", sock, + flags & MSG_PEEK, flags & MSG_TRUNC); + + int len = 0; + len += message->msg_namelen; + len += message->msg_controllen; + + for (int i = 0; i < message->msg_iovlen; i++) { + len += sizeof(size_t); + len += message->msg_iov[i].iov_len; + } + + int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_msghdr) + len; + int pid = getpid(); + + struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + + msg->type = IPC_RECVMSG; + msg->pid = pid; + + struct ipc_msghdr *payload = (struct ipc_msghdr *)msg->data; + payload->sockfd = socket; + payload->msg_namelen = message->msg_namelen; + payload->msg_iovlen = message->msg_iovlen; + payload->msg_controllen = message->msg_controllen; + payload->flags = flags; + + uint8_t *ptr = payload->data; + memcpy(ptr, message->msg_name, payload->msg_namelen); + ptr += payload->msg_namelen; + + memcpy(ptr, message->msg_control, payload->msg_controllen); + ptr += payload->msg_controllen; + + for (int i = 0; i < payload->msg_iovlen; i++) { + size_t iovlen = message->msg_iov[i].iov_len; + struct ipc_iovec *v = (struct ipc_iovec *) ptr; + v->iov_len = iovlen; + + memcpy(v->iov_base, message->msg_iov[i].iov_base, iovlen); + ptr = v->iov_base + iovlen; + } + + // Send mocked syscall to lvl-ip + if (_write(sock->lvlfd, (char *)msg, msglen) == -1) { + perror("Error on writing IPC recvmsg"); + } + + int rlen = sizeof(struct ipc_msg) + sizeof(struct ipc_err) + sizeof(struct ipc_msghdr); + char rbuf[rlen]; + memset(rbuf, 0, rlen); + + // Read return value from lvl-ip + if (_read(sock->lvlfd, rbuf, rlen) == -1) { + perror("Could not read IPC recvmsg response"); + } + + lvl_sock_dbg("Recvmsg reading %d bytes for header", sock, rlen); + + struct ipc_msg *response = (struct ipc_msg *) rbuf; + + if (response->type != IPC_RECVMSG || response->pid != pid) { + print_err("ERR: IPC recvmsg response expected: type %d, pid %d\n" + " actual: type %d, pid %d\n", + IPC_RECVMSG, pid, response->type, response->pid); + return -1; + } + + struct ipc_err *error = (struct ipc_err *) response->data; + if (error->rc < 0) { + errno = error->err; + return error->rc; + } + + struct ipc_msghdr *imh = (struct ipc_msghdr *)error->data; + message->msg_iovlen = imh->msg_iovlen; + + int iovbuf_len = sizeof(struct ipc_iovec) + error->rc; + char iovbuf[iovbuf_len]; + memset(iovbuf, 0, iovbuf_len); + + lvl_sock_dbg("Recvmsg reading %d bytes for iov", sock, iovbuf_len); + + if (_read(sock->lvlfd, iovbuf, iovbuf_len) == -1) { + perror("Could not read IPC recvmsg iov response"); + } + + if (flags & MSG_TRUNC || flags & MSG_PEEK) { + lvl_sock_dbg("Recvmsg returning %d bytes because of trunc or peek", sock, error->rc); + return error->rc; + } + + uint8_t *iptr = (uint8_t *)iovbuf; + for (int i = 0; i < imh->msg_iovlen; i++) { + struct iovec *v = &message->msg_iov[i]; + struct ipc_iovec *iv = (struct ipc_iovec *)iptr; + + v->iov_len = iv->iov_len; + memcpy(v->iov_base, iv->iov_base, iv->iov_len); + + iptr += sizeof(struct ipc_iovec); + iptr += iv->iov_len; + } + + struct sockaddr_nl *snl = message->msg_name; + snl->nl_family = AF_NETLINK; + snl->nl_pad = 0; + snl->nl_pid = 0; + snl->nl_groups = 0; + + lvl_sock_dbg("Recvmsg returning rc %d", sock, error->rc); + + return error->rc; +} + int poll(struct pollfd *fds, nfds_t nfds, int timeout) { struct pollfd *kernel_fds[nfds]; @@ -402,7 +604,7 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout) for (int i = 0; i < kernel_nfds; i++) { lvl_dbg("Kernel nfd %d events %d timeout %d", kernel_fds[i]->fd, kernel_fds[i]->events, timeout); } - + events = _poll(*kernel_fds, kernel_nfds, timeout); if (events == -1) { @@ -415,11 +617,12 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout) if (lvlip_nfds < 1) { return events; } - + int pid = getpid(); int pollfd_size = sizeof(struct ipc_pollfd); int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_poll) + pollfd_size * lvlip_nfds; struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); msg->type = IPC_POLL; msg->pid = pid; @@ -452,13 +655,13 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout) errno = EAGAIN; return -1; } - + struct ipc_msg *response = (struct ipc_msg *) rbuf; if (response->type != IPC_POLL || response->pid != pid) { print_err("ERR: IPC poll response expected: type %d, pid %d\n" - " actual: type %d, pid %d\n", - IPC_POLL, pid, response->type, response->pid); + " actual: type %d, pid %d\n", + IPC_POLL, pid, response->type, response->pid); errno = EAGAIN; return -1; } @@ -478,12 +681,12 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout) } int result = events + error->rc; - + if (result > 0 || !blocking) { for (int i = 0; i < nfds; i++) { lvl_dbg("Returning counts %d nfd %d with revents %d events %d timeout %d", result, i, fds[i].revents, fds[i].events, timeout); } - + return result; } } @@ -493,21 +696,21 @@ int poll(struct pollfd *fds, nfds_t nfds, int timeout) } int __poll_chk (struct pollfd *__fds, nfds_t __nfds, int __timeout, - __SIZE_TYPE__ __fdslen) + __SIZE_TYPE__ __fdslen) { return poll(__fds, __nfds, __timeout); } int ppoll(struct pollfd *fds, nfds_t nfds, - const struct timespec *tmo_p, const sigset_t *sigmask) + const struct timespec *tmo_p, const sigset_t *sigmask) { print_err("Ppoll called but not supported\n"); return -1; } int select(int nfds, fd_set *restrict readfds, - fd_set *restrict writefds, fd_set *restrict errorfds, - struct timeval *restrict timeout) + fd_set *restrict writefds, fd_set *restrict errorfds, + struct timeval *restrict timeout) { print_err("Select not implemented yet\n"); return _select(nfds, readfds, writefds, errorfds, timeout); @@ -515,7 +718,7 @@ int select(int nfds, fd_set *restrict readfds, int setsockopt(int fd, int level, int optname, - const void *optval, socklen_t optlen) + const void *optval, socklen_t optlen) { struct lvlip_sock *sock = lvlip_get_sock(fd); if (sock == NULL) return _setsockopt(fd, level, optname, optval, optlen); @@ -523,23 +726,25 @@ int setsockopt(int fd, int level, int optname, lvl_sock_dbg("Setsockopt called", sock); /* WARN: Setsockopt not supported yet */ - + return 0; } int getsockopt(int fd, int level, int optname, - void *optval, socklen_t *optlen) + void *optval, socklen_t *optlen) { struct lvlip_sock *sock = lvlip_get_sock(fd); if (sock == NULL) return _getsockopt(fd, level, optname, optval, optlen); lvl_sock_dbg("Getsockopt called: level %d optname %d optval %d socklen %d", - sock, level, optname, *(int *)optval, *(int *)optlen); - + sock, level, optname, *(int *)optval, *(int *)optlen); + int pid = getpid(); int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_sockopt) + *optlen; struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + msg->type = IPC_GETSOCKOPT; msg->pid = pid; @@ -566,13 +771,13 @@ int getsockopt(int fd, int level, int optname, if (_read(sock->lvlfd, rbuf, rlen) == -1) { perror("Could not read IPC getsockopt response"); } - + struct ipc_msg *response = (struct ipc_msg *) rbuf; if (response->type != IPC_GETSOCKOPT || response->pid != pid) { print_err("ERR: IPC getsockopt response expected: type %d, pid %d\n" - " actual: type %d, pid %d\n", - IPC_GETSOCKOPT, pid, response->type, response->pid); + " actual: type %d, pid %d\n", + IPC_GETSOCKOPT, pid, response->type, response->pid); return -1; } @@ -585,7 +790,7 @@ int getsockopt(int fd, int level, int optname, struct ipc_sockopt *optres = (struct ipc_sockopt *) error->data; lvl_sock_dbg("Got getsockopt level %d optname %d optval %d socklen %d", - sock, optres->level, optres->optname, *(int *)optres->optval, optres->optlen); + sock, optres->level, optres->optname, *(int *)optres->optval, optres->optlen); int val = *(int *)optres->optval; @@ -599,7 +804,7 @@ int getsockopt(int fd, int level, int optname, } int getpeername(int socket, struct sockaddr *restrict address, - socklen_t *restrict address_len) + socklen_t *restrict address_len) { struct lvlip_sock *sock = lvlip_get_sock(socket); if (sock == NULL) return _getpeername(socket, address, address_len); @@ -610,6 +815,8 @@ int getpeername(int socket, struct sockaddr *restrict address, int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_sockname); struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + msg->type = IPC_GETPEERNAME; msg->pid = pid; @@ -629,13 +836,13 @@ int getpeername(int socket, struct sockaddr *restrict address, if (_read(sock->lvlfd, rbuf, rlen) == -1) { perror("Could not read IPC getpeername response"); } - + struct ipc_msg *response = (struct ipc_msg *) rbuf; if (response->type != IPC_GETPEERNAME || response->pid != pid) { print_err("ERR: IPC getpeername response expected: type %d, pid %d\n" - " actual: type %d, pid %d\n", - IPC_GETPEERNAME, pid, response->type, response->pid); + " actual: type %d, pid %d\n", + IPC_GETPEERNAME, pid, response->type, response->pid); return -1; } @@ -648,7 +855,7 @@ int getpeername(int socket, struct sockaddr *restrict address, struct ipc_sockname *nameres = (struct ipc_sockname *) error->data; lvl_sock_dbg("Got getpeername fd %d addrlen %d sa_data %p", - sock, nameres->socket, nameres->address_len, nameres->sa_data); + sock, nameres->socket, nameres->address_len, nameres->sa_data); if (nameres->socket != socket) { print_err("Got socket %d but requested %d\n", nameres->socket, socket); @@ -656,12 +863,12 @@ int getpeername(int socket, struct sockaddr *restrict address, *address_len = nameres->address_len; memcpy(address, nameres->sa_data, nameres->address_len); - + return 0; } int getsockname(int socket, struct sockaddr *restrict address, - socklen_t *restrict address_len) + socklen_t *restrict address_len) { struct lvlip_sock *sock = lvlip_get_sock(socket); if (sock == NULL) return _getsockname(socket, address, address_len); @@ -672,6 +879,8 @@ int getsockname(int socket, struct sockaddr *restrict address, int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_sockname); struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); + msg->type = IPC_GETSOCKNAME; msg->pid = pid; @@ -691,13 +900,13 @@ int getsockname(int socket, struct sockaddr *restrict address, if (_read(sock->lvlfd, rbuf, rlen) == -1) { perror("Could not read IPC getsockname response"); } - + struct ipc_msg *response = (struct ipc_msg *) rbuf; if (response->type != IPC_GETSOCKNAME || response->pid != pid) { print_err("ERR: IPC getsockname response expected: type %d, pid %d\n" - " actual: type %d, pid %d\n", - IPC_GETSOCKNAME, pid, response->type, response->pid); + " actual: type %d, pid %d\n", + IPC_GETSOCKNAME, pid, response->type, response->pid); return -1; } @@ -710,7 +919,7 @@ int getsockname(int socket, struct sockaddr *restrict address, struct ipc_sockname *nameres = (struct ipc_sockname *) error->data; lvl_sock_dbg("Got getsockname fd %d addrlen %d sa_data %p", - sock, nameres->socket, nameres->address_len, nameres->sa_data); + sock, nameres->socket, nameres->address_len, nameres->sa_data); if (nameres->socket != socket) { print_err("Got socket %d but requested %d\n", nameres->socket, socket); @@ -743,6 +952,7 @@ int fcntl(int fildes, int cmd, ...) int pid = getpid(); int msglen = sizeof(struct ipc_msg) + sizeof(struct ipc_fcntl) + sizeof(struct flock) + sizeof(int); struct ipc_msg *msg = alloca(msglen); + memset(msg, 0, msglen); msg->type = IPC_FCNTL; msg->pid = pid; @@ -750,37 +960,49 @@ int fcntl(int fildes, int cmd, ...) struct ipc_fcntl *fc = (struct ipc_fcntl *)msg->data; fc->sockfd = fildes; fc->cmd = cmd; - + switch (cmd) { - case F_GETFL: - lvl_sock_dbg("Fcntl GETFL", sock); + case F_GETFL: + lvl_sock_dbg("Fcntl GETFL", sock); - rc = transmit_lvlip(sock->lvlfd, msg, msglen); - break; - case F_SETFL: - lvl_sock_dbg("Fcntl SETFL", sock); + rc = transmit_lvlip(sock->lvlfd, msg, msglen); + break; + case F_SETFL: + lvl_sock_dbg("Fcntl SETFL", sock); - va_start(ap, cmd); + va_start(ap, cmd); - int flags = va_arg(ap, int); - memcpy(fc->data, &flags, sizeof(int)); + int flags = va_arg(ap, int); + memcpy(fc->data, &flags, sizeof(int)); - va_end(ap); + va_end(ap); - rc = transmit_lvlip(sock->lvlfd, msg, msglen); - break; - default: - rc = -1; - errno = EINVAL; - break; + rc = transmit_lvlip(sock->lvlfd, msg, msglen); + break; + default: + rc = -1; + errno = EINVAL; + break; } - + return rc; } +int bind(int socket, const struct sockaddr *address, socklen_t address_len) +{ + struct lvlip_sock *sock = lvlip_get_sock(socket); + + if (sock == NULL) { + /* No lvl-ip IPC socket associated */ + return _bind(socket, address, address_len); + } + + return 0; +} + int __libc_start_main(int (*main) (int, char * *, char * *), int argc, - char * * ubp_av, void (*init) (void), void (*fini) (void), - void (*rtld_fini) (void), void (* stack_end)) + char * * ubp_av, void (*init) (void), void (*fini) (void), + void (*rtld_fini) (void), void (* stack_end)) { __start_main = dlsym(RTLD_NEXT, "__libc_start_main"); @@ -800,6 +1022,9 @@ int __libc_start_main(int (*main) (int, char * *, char * *), int argc, _close = dlsym(RTLD_NEXT, "close"); _getpeername = dlsym(RTLD_NEXT, "getpeername"); _getsockname = dlsym(RTLD_NEXT, "getsockname"); + _bind = dlsym(RTLD_NEXT, "bind"); + _sendmsg = dlsym(RTLD_NEXT, "sendmsg"); + _recvmsg = dlsym(RTLD_NEXT, "recvmsg"); list_init(&lvlip_socks);