X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=blobdiff_plain;f=misc%2Fnetwork_servers%2Fv3_nbd%2Fv3_nbd.cc;h=b3eb805e084b645b0f41cf3ec1eae808c3b3d9b4;hb=2fc55bdc014038e625b3628fdaf0b60d7ebede77;hp=1b1099da057712dacc70f32b1c807893216fe2ff;hpb=ac93f0773f4c6cef2f2f542240695a8706037d41;p=palacios.git diff --git a/misc/network_servers/v3_nbd/v3_nbd.cc b/misc/network_servers/v3_nbd/v3_nbd.cc index 1b1099d..b3eb805 100644 --- a/misc/network_servers/v3_nbd/v3_nbd.cc +++ b/misc/network_servers/v3_nbd/v3_nbd.cc @@ -17,21 +17,95 @@ * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ + +#include +#include +#include #include +#include + +#ifdef linux +#include +#include +#include +#elif defined(WIN32) && !defined(__CYGWIN__) + +#endif + + +#include "v3_disk.h" +#include "raw.h" +#include "iso.h" + +#define NBD_KEY "V3_NBD_1" + + +#define NBD_READ_CMD 0x1 +#define NBD_WRITE_CMD 0x2 +#define NBD_CAPACITY_CMD 0x3 + +#define NBD_STATUS_OK 0x00 +#define NBD_STATUS_ERR 0xff + + +#define DEFAULT_LOG_FILE "./status.log" +#define DEFAULT_CONF_FILE "v3_nbd.ini" +#define DEFAULT_PORT 9500 +#define MAX_STRING_SIZE 1024 +#define MAX_DISKS 32 + +#define LOGFILE_TAG "logfile" +#define PORT_TAG "port" +#define DISKS_TAG "disks" + +// Turn on 64 bit file offset support (see 'man fseeko') +#define _FILE_OFFSET_BITS 64 -nbd_config_t g_nbd_conf; using namespace std; //using namespace __gnu_cxx; +struct eqsock { + bool operator()(const SOCK sock1, const SOCK sock2) const { + return sock1 == sock2; + } +}; + + +// Server Port that we'll listen on +int server_port; + +// List of disks being served +// eqstr from vtl (config.h) +map disks; + +// List of open connections +map conns; + + +// Enable Debugging +static const int enable_debug = 1; + + +void usage(); +int config_nbd(string conf_file_name); +int serv_loop(int serv_sock); +void setup_disk(string disk_tag, config_t &config_map); + +int handle_new_connection(SOCK new_conn); +int handle_disk_request(SOCK conn, v3_disk * disk); -config_t g_config; +int handle_capacity_request(SOCK conn, v3_disk * disk); +int handle_read_request(SOCK conn, v3_disk * disk); +int handle_write_request(SOCK conn, v3_disk * disk); int __main (int argc, char ** argv); + + #ifdef linux int main(int argc, char ** argv) { @@ -48,89 +122,181 @@ void main() { int __main (int argc, char ** argv) { string config_file; - SOCK vnet_sock = 0; - struct vnet_config vnet_info; - iface_t * iface; + SOCK serv_sock; + + + if (argc > 2) { usage(); exit(0); } + // init global maps + disks.clear(); + conns.clear(); + + if (argc == 2) { config_file = string(argv[1]); } else { - config_file = VIDS_CONF_FILE; - } - - - int * foo; - int num_ports = GetOpenUdpPorts(&foo); - int i; - for (i = 0; i < num_ports; i++) { - printf("port %d open\n", foo[i]); + config_file = DEFAULT_CONF_FILE; } - - - // g_conf.log_file = "./vids.log"; - if (config_vids(config_file) == -1) { + + if (config_nbd(config_file) == -1) { cerr << "Configuration Error" << endl; exit(-1); } - // JRL DEBUG - debug_init(g_config[LOGFILE_TAG].c_str()); - JRLDBG("testing...\n"); + // setup network sockets + serv_sock = CreateAndSetupTcpSocket(); + + if (serv_sock == -1) { + cerr << "Could not create server socket, exiting..." << endl; + exit(-1); + } + if (BindSocket(serv_sock, server_port) == -1) { + cerr << "Could not bind socket to port: " << server_port << endl; + exit(-1); + } + if (ListenSocket(serv_sock) == -1) { + cerr << "Could not listen on server socket (port=" << server_port << ")" << endl; + exit(-1); + } - // Configure pcap filter... - vids_loop(iface, vnet_sock, &vnet_info); + vtl_debug("Starting Server Loop\n"); + serv_loop(serv_sock); return 0; } #ifdef linux -int vids_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) { - fd_set all_set, read_set; - int max_fd = -1; - RawEthernetPacket pkt; +int serv_loop(int serv_sock) { + fd_set all_set, read_set; + int max_fd = -1; + RawEthernetPacket pkt; - FD_ZERO(&all_set); - FD_SET(vnet_sock, &all_set); - max_fd = vnet_sock; + list pending_cons; - while (1) { - int nready = 0; - read_set = all_set; - nready = select(max_fd + 1, &read_set, NULL, NULL, NULL); - + FD_ZERO(&all_set); + FD_SET(serv_sock, &all_set); + max_fd = serv_sock; + + + while (1) { + int nready = 0; + read_set = all_set; + nready = select(max_fd + 1, &read_set, NULL, NULL, NULL); - if (nready == -1) { - if (errno == EINTR) { - continue; - } else { - perror("Select returned error: "); - break; - } - } + if (nready == -1) { + if (errno == EINTR) { + continue; + } else { + vtl_debug("Select returned error\n"); + perror("Select returned error: "); + exit(-1); + } + } - if (FD_ISSET(vnet_sock, &read_set)) { - //vnet_recv(); - + if (FD_ISSET(serv_sock, &read_set)) { + SOCK conn_socket; + struct sockaddr_in rem_addr; + socklen_t addr_len = sizeof(struct sockaddr_in); + // new connection + conn_socket = accept(serv_sock, (struct sockaddr *)&rem_addr, &addr_len); + + vtl_debug("New Connection...\n"); + + if (conn_socket < 0) { + if (errno == EINTR) { + continue; + } else { + vtl_debug("Accept returned error\n"); + exit(-1); + } + } + + pending_cons.push_front(conn_socket); + + FD_SET(conn_socket, &all_set); + + if (conn_socket > max_fd) { + max_fd = conn_socket; + } + + if (--nready <= 0) continue; + } + + + // handle open connections + for (map::iterator con_iter = conns.begin(); + con_iter != conns.end(); ) { + SOCK tmp_sock = con_iter->first; + v3_disk * tmp_disk = con_iter->second; + + if (FD_ISSET(con_iter->first, &read_set)) { + + if (handle_disk_request(tmp_sock, tmp_disk) == -1) { + vtl_debug("Error: Could not complete disk request\n"); + + map::iterator tmp_iter = con_iter; + con_iter++; + + tmp_disk->detach(); + + + FD_CLR(tmp_sock, &all_set); + close(tmp_sock); + + conns.erase(tmp_iter); + } else { + con_iter++; + } + + if (--nready <= 0) break; + } + } + + if (nready <= 0) continue; + + // check pending connections + for (list::iterator pending_iter = pending_cons.begin(); + pending_iter != pending_cons.end();) { + + if (FD_ISSET(*pending_iter, &read_set)) { + if (handle_new_connection(*pending_iter) == -1) { + // error + vtl_debug("Error: Could not connect to disk\n"); + FD_CLR(*pending_iter, &all_set); + } + list::iterator tmp_iter = pending_iter; + pending_iter++; + + pending_cons.erase(tmp_iter); + + if (--nready <= 0) break; + } else { + pending_iter++; + } + } + + if (nready <= 0) continue; + + } - } - return 0; + return 0; } #elif WIN32 -int vids_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) { +int serv_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) { int ret; RawEthernetPacket pkt; WSANETWORKEVENTS net_events; @@ -160,7 +326,6 @@ int vids_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) { } if (net_events.lNetworkEvents & FD_READ) { - JRLDBG("Receied VNET Packet\n"); // we received data if (vnet_info->link_type == TCP_LINK) { @@ -186,103 +351,326 @@ int vids_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) { #endif +// byte 1: command (read = 1, write = 2, capacity = 3) +// byte 2 - 4: zero +int handle_disk_request(SOCK conn, v3_disk * disk) { + char buf[4]; -int config_vids(string conf_file_name) { - if (read_config(conf_file_name, &g_config) != 0) { - return -1; - } + int read_len = Receive(conn, buf, 4, true); + + if (read_len == 0) { + vtl_debug("Detaching from disk (conn=%d)\n", conn); + return -1; + } - if (g_config.count(VIDS_SERVER_TAG) > 0) { - g_vids_conf.server_addr = ToIPAddress(g_config[VIDS_SERVER_TAG].c_str()); - } else { - printf("Must specify VIDS server address\n"); - return -1; - } + if (read_len == -1) { + vtl_debug("Could not read command\n"); + return -1; + } - if (g_config.count(VIDS_SERVER_PORT_TAG) > 0) { - g_vids_conf.server_port = atoi(g_config[VIDS_SERVER_PORT_TAG].c_str()); - } else { - printf("Must specify VIDS server port\n"); - return -1; - } + if ((buf[1] != 0) || (buf[2] != 0) || (buf[3] != 0)) { + // error + vtl_debug("Invalid command padding\n"); + return -1; + } + + switch (buf[0]) { + case NBD_CAPACITY_CMD: + return handle_capacity_request(conn, disk); + case NBD_READ_CMD: + return handle_read_request(conn, disk); + case NBD_WRITE_CMD: + return handle_write_request(conn, disk); + default: + vtl_debug("Invalid Disk Command %d\n", buf[0]); + return -1; + } + + return 0; +} + + +// send: +// 8 bytes : capacity +int handle_capacity_request(SOCK conn, v3_disk * disk) { + off_t capacity = disk->get_capacity(); + + vtl_debug("Returing capacity %d\n", capacity); + + return Send(conn, (char *)&capacity, 8, true); +} - if (g_config.count(TCP_PORTS_TAG) > 0) { - istringstream port_stream(g_config[TCP_PORTS_TAG], istringstream::in); - int port; - int i = 0; +// receive: +// 8 bytes : offset +// 4 bytes : length +// send: +// 1 byte : status +// 4 bytes : return length +// x bytes : data +int handle_read_request(SOCK conn, v3_disk * disk) { + off_t offset = 0; + unsigned int length = 0; + unsigned char * buf = NULL; + unsigned int ret_len = 0; + unsigned char status = NBD_STATUS_OK; + + vtl_debug("Read Request\n"); + + + + if (Receive(conn, (char *)&offset, 8, true) <= 0) { + vtl_debug("Error receiving read offset\n"); + return -1; + } + + vtl_debug("Read Offset %d\n", offset); + + if (Receive(conn, (char *)&length, 4, true) <= 0) { + vtl_debug("Error receiving read length\n"); + return -1; + } + + vtl_debug("Read length: %d\n", length); + + buf = new unsigned char[length]; - while (port_stream >> port) { - if (i >= MAX_PORTS) { - cerr << "You specified too many ports to forward, truncating..." << endl; - break; - } - - g_vids_conf.tcp_ports[i] = port; - i++; + ret_len = disk->read(buf, offset, length); + + vtl_debug("Read %d bytes from source disk\n", ret_len); + + if (ret_len == 0) { + vtl_debug("Read Error\n"); + status = NBD_STATUS_ERR; } - g_vids_conf.num_tcp_ports = i; - } + vtl_debug("Sending Status byte (%d)\n", status); + if (Send(conn, (char *)&status, 1, true) <= 0) { + vtl_debug("Error Sending Read Status\n"); + return -1; + } + vtl_debug("Sending Ret Len: %d\n", ret_len); - if (g_config.count(VIRTUAL_MAC_TAG) > 0) { - string_to_mac(g_config[VIRTUAL_MAC_TAG].c_str(), g_vids_conf.virtual_mac); - } + if (Send(conn, (char *)&ret_len, 4, true) <= 0) { + vtl_debug("Error Sending Read Length\n"); + return -1; + } - if (g_config.count(LOGFILE_TAG) == 0) { - g_config[LOGFILE_TAG] = DEFAULT_LOG_FILE; - } - if (GetLocalMacAddress(g_config[INTERFACE_TAG], g_vids_conf.local_mac) == -1) { - cerr << "Could not get local mac address" << endl; - return -1; - } + if (ret_len > 0) { + vtl_debug("Sending Data\n"); - return 0; + SetNoDelaySocket(conn, false); + + if (Send(conn, (char *)buf, ret_len, true) <= 0) { + vtl_debug("Error sending Read Data\n"); + return -1; + } + + SetNoDelaySocket(conn, true); + } + + vtl_debug("Read Complete\n"); + + delete buf; + + return 0; } +// receive: +// 8 bytes : offset +// 4 bytes : length +// x bytes : data +// send : +// 1 bytes : status +int handle_write_request(SOCK conn, v3_disk * disk) { + off_t offset = 0; + unsigned int length = 0; + unsigned char * buf = NULL; + unsigned int ret_len = 0; + unsigned char status = NBD_STATUS_OK; + + vtl_debug("Write Request\n"); + + if (Receive(conn, (char *)&offset, 8, true) <= 0) { + vtl_debug("Error receiving write offset\n"); + return -1; + } + + vtl_debug("Write Offset %d\n", offset); + + if (Receive(conn, (char *)&length, 4, true) <= 0) { + vtl_debug("Error receiving write length\n"); + return -1; + } -int read_config(string conf_file_name) { - fstream conf_file(conf_file_name.c_str(), ios::in); - char line[MAX_STRING_SIZE]; + vtl_debug("Write length: %d\n", length); - while ((conf_file.getline(line, MAX_STRING_SIZE))) { - string conf_line = line; - string tag; - string value; - int offset, ltrim_index, rtrim_index; + buf = new unsigned char[length]; + + vtl_debug("Receiving Data\n"); - if (conf_line[0] == '#') { - continue; + if (Receive(conn, (char *)buf, length, true) <= 0) { + vtl_debug("Error receiving Write Data\n"); + return -1; } - offset = conf_line.find(":", 0); - tag = conf_line.substr(0,offset); + vtl_debug("Wrote %d bytes to source disk\n", ret_len); + + if (disk->write(buf, offset, length) != length) { + vtl_debug("Write Error\n"); + status = NBD_STATUS_ERR; + } - // kill white space - istringstream tag_stream(tag, istringstream::in); - tag_stream >> tag; + vtl_debug("Sending Status byte (%d)\n", status); - if (tag.empty()) { - continue; + if (Send(conn, (char *)&status, 1, true) <= 0) { + vtl_debug("Error Sending Wrte Status\n"); + return -1; } - // basic whitespace trimming, we assume that the config handlers will deal with - // tokenizing and further formatting - value = conf_line.substr(offset + 1, conf_line.length() - offset); - ltrim_index = value.find_first_not_of(" \t"); - rtrim_index = value.find_last_not_of(" \t"); - value = value.substr(ltrim_index, (rtrim_index + 1) - ltrim_index); + vtl_debug("Write Complete\n"); - g_config[tag] = value; - } - return 0; + delete buf; + + return 0; +} + + +/* Negotiation: + * \n + */ + +int handle_new_connection(SOCK new_conn) { + string input; + string key_str; + string tag_str; + v3_disk * disk = NULL; + + GetLine(new_conn, input); + + vtl_debug("New Connection: %s\n", input.c_str()); + + { + istringstream is(input, istringstream::in); + is >> key_str >> tag_str; + } + + if (key_str != NBD_KEY) { + vtl_debug("Error: Invalid NBD key string (%s)\n", key_str.c_str()); + return -1; + } + + if (disks.count(tag_str) == 0) { + vtl_debug("Error: Requesting disk that does not exist (%s)\n", tag_str.c_str()); + return -1; + } + + disk = disks[tag_str]; + + if (!disk) { + vtl_debug("Disk (%s) Does not exist\n", tag_str.c_str()); + return -1; + } + + if (disk->locked == 1) { + vtl_debug("Attempting to attach to a device already in use\n"); + return -1; + } + + conns[new_conn] = disk; + + disk->attach(); + + + vtl_debug("Connected to disk %s\n", tag_str.c_str()); + + return 0; } +int config_nbd(string conf_file_name) { + config_t config_map; + + if (read_config(conf_file_name, &config_map) != 0) { + cerr << "Could not read config file..." << endl; + return -1; + } + + if (config_map.count(LOGFILE_TAG) == 0) { + config_map[LOGFILE_TAG] = DEFAULT_LOG_FILE; + } + + vtl_debug_init(config_map[LOGFILE_TAG], enable_debug); + + + if (config_map.count(PORT_TAG) > 0) { + server_port = atoi(config_map[PORT_TAG].c_str()); + } else { + server_port = DEFAULT_PORT; + } + + if (config_map.count(DISKS_TAG) > 0) { + istringstream disk_stream(config_map[DISKS_TAG], istringstream::in); + string disk_tag; + int i = 0; + + while (disk_stream >> disk_tag) { + + if (i >= MAX_DISKS) { + cerr << "You specified too many disks, truncating..." << endl; + break; + } + + setup_disk(disk_tag, config_map); + i++; + } + } else { + cerr << "Must specify a set of disks" << endl; + return -1; + } + + + return 0; +} + +void setup_disk(string disk_tag, config_t &config_map) { + string file_tag = disk_tag + ".file"; + string type_tag = disk_tag + ".type"; + + v3_disk * disk; + string type; + + + cout << "Setting up " << disk_tag.c_str() << endl; + + if ((config_map.count(file_tag) == 0) && + (config_map.count(type_tag) == 0)) { + cerr << "Missing Disk configuration directive for " << disk_tag << endl; + } + + type = config_map[type_tag]; + + if (type == "RAW") { + disk = new raw_disk(config_map[file_tag]); + } else if (type == "ISO") { + vtl_debug("Setting up ISO\n"); + disk = new iso_image(config_map[file_tag]); + } + + disks[disk_tag] = disk; + + return; +} + + + + + + void usage() { - cout << "Usage: vids [config_file]" << endl; + cout << "Usage: v3_nbd [config_file]" << endl; return; }