218 lines
5.9 KiB
218 lines
5.9 KiB
|
|
#include <errno.h>
|
|
#include <netdb.h>
|
|
#include <net/if.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
#include <initializer_list>
|
|
|
|
static void usage(const char* program) {
|
|
fprintf(stderr, "Usage: %s [-s|-c|-b] <ip> <port>\n", program);
|
|
}
|
|
|
|
enum class Mode {
|
|
Bridge,
|
|
Client,
|
|
Server,
|
|
};
|
|
|
|
bool resolve(const char* name, const char* port, struct addrinfo** addrs) {
|
|
struct addrinfo hints;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_socktype = SOCK_DGRAM;
|
|
|
|
int res = ::getaddrinfo(name, port, &hints, addrs);
|
|
if (res != 0) {
|
|
fprintf(stderr, "ERROR: Unable to resolve '%s' and port '%s': %s\n",
|
|
name, port, gai_strerror(res));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int runClient(struct addrinfo* addrs) {
|
|
int fd = -1;
|
|
for (struct addrinfo* addr = addrs; addr != nullptr; addr = addr->ai_next) {
|
|
fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
|
if (fd < 0) {
|
|
continue;
|
|
}
|
|
if (::connect(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
|
|
break;
|
|
}
|
|
::close(fd);
|
|
}
|
|
::freeaddrinfo(addrs);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "Unable to connect to server\n");
|
|
return 1;
|
|
}
|
|
if (::send(fd, "boop", 4, 0) != 4) {
|
|
::close(fd);
|
|
fprintf(stderr, "Failed to send message to server\n");
|
|
return 1;
|
|
}
|
|
::close(fd);
|
|
return 0;
|
|
}
|
|
|
|
int runServer(struct addrinfo* addrs) {
|
|
int fd = -1;
|
|
for (struct addrinfo* addr = addrs; addr != nullptr; addr = addr->ai_next) {
|
|
fd = ::socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
|
if (fd < 0) {
|
|
continue;
|
|
}
|
|
if (::bind(fd, addr->ai_addr, addr->ai_addrlen) == 0) {
|
|
break;
|
|
}
|
|
::close(fd);
|
|
}
|
|
::freeaddrinfo(addrs);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "Unable to bind to address\n");
|
|
return 1;
|
|
}
|
|
char buffer[1024];
|
|
for (;;) {
|
|
struct sockaddr_storage addr;
|
|
socklen_t addrSize = sizeof(addr);
|
|
ssize_t bytesRead = recvfrom(fd, buffer, sizeof(buffer), 0,
|
|
reinterpret_cast<struct sockaddr*>(&addr),
|
|
&addrSize);
|
|
if (bytesRead < 0) {
|
|
if (errno == EINTR) {
|
|
continue;
|
|
}
|
|
fprintf(stderr, "Error receiving on socket: %s\n", strerror(errno));
|
|
::close(fd);
|
|
return 1;
|
|
} else if (bytesRead == 0) {
|
|
fprintf(stderr, "Socket unexpectedly closed\n");
|
|
::close(fd);
|
|
return 1;
|
|
}
|
|
printf("Received message from client '%*s'\n",
|
|
static_cast<int>(bytesRead), buffer);
|
|
}
|
|
}
|
|
|
|
static const char kBridgeName[] = "br0";
|
|
|
|
static int configureBridge() {
|
|
int fd = ::socket(AF_LOCAL, SOCK_STREAM, 0);
|
|
if (fd < 0) {
|
|
fprintf(stderr, "ERROR: Could not open bridge socket: %s\n",
|
|
strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
int res = ::ioctl(fd, SIOCBRADDBR, kBridgeName);
|
|
if (res < 0) {
|
|
fprintf(stderr, "ERROR: cannot create bridge: %s\n", strerror(errno));
|
|
::close(fd);
|
|
return 1;
|
|
}
|
|
|
|
for (const auto& ifName : { "eth0", "wlan1", "radio0-peer" }) {
|
|
struct ifreq request;
|
|
memset(&request, 0, sizeof(request));
|
|
request.ifr_ifindex = if_nametoindex(ifName);
|
|
if (request.ifr_ifindex == 0) {
|
|
fprintf(stderr, "ERROR: Unable to get interface index for %s\n",
|
|
ifName);
|
|
::close(fd);
|
|
return 1;
|
|
}
|
|
strlcpy(request.ifr_name, kBridgeName, sizeof(request.ifr_name));
|
|
res = ::ioctl(fd, SIOCBRADDIF, &request);
|
|
if (res < 0) {
|
|
fprintf(stderr, "ERROR: cannot add if %s to bridge: %s\n",
|
|
ifName, strerror(errno));
|
|
::close(fd);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
struct ifreq request;
|
|
memset(&request, 0, sizeof(request));
|
|
request.ifr_ifindex = if_nametoindex(kBridgeName);
|
|
if (request.ifr_ifindex == 0) {
|
|
fprintf(stderr, "ERROR: Unable to get interface index for %s\n",
|
|
kBridgeName);
|
|
::close(fd);
|
|
return 1;
|
|
}
|
|
strlcpy(request.ifr_name, kBridgeName, sizeof(request.ifr_name));
|
|
res = ::ioctl(fd, SIOCGIFFLAGS, &request);
|
|
if (res != 0) {
|
|
fprintf(stderr, "ERROR: Unable to get interface index for %s\n",
|
|
kBridgeName);
|
|
::close(fd);
|
|
return 1;
|
|
}
|
|
if ((request.ifr_flags & IFF_UP) == 0) {
|
|
// Bridge is not up, it needs to be up to work
|
|
request.ifr_flags |= IFF_UP;
|
|
res = ::ioctl(fd, SIOCSIFFLAGS, &request);
|
|
if (res != 0) {
|
|
fprintf(stderr, "ERROR: Unable to set interface flags for %s\n",
|
|
kBridgeName);
|
|
::close(fd);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
::close(fd);
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char* argv[]) {
|
|
if (argc < 2) {
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
Mode mode;
|
|
if (strcmp("-b", argv[1]) == 0) {
|
|
mode = Mode::Bridge;
|
|
} else if (strcmp("-c", argv[1]) == 0) {
|
|
mode = Mode::Client;
|
|
} else if (strcmp("-s", argv[1]) == 0) {
|
|
mode = Mode::Server;
|
|
} else {
|
|
fprintf(stderr, "ERROR: Invalid option '%s'\n", argv[1]);
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
struct addrinfo* addrs = nullptr;
|
|
if (mode == Mode::Client || mode == Mode::Server) {
|
|
if (argc != 4) {
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
if (!resolve(argv[2], argv[3], &addrs)) {
|
|
usage(argv[0]);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
switch (mode) {
|
|
case Mode::Bridge:
|
|
return configureBridge();
|
|
case Mode::Client:
|
|
return runClient(addrs);
|
|
case Mode::Server:
|
|
return runServer(addrs);
|
|
}
|
|
}
|
|
|