#include #include #include #include #include #include #include #include #include #include #include static void usage(const char* program) { fprintf(stderr, "Usage: %s [-s|-c|-b] \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(&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(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); } }