2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11 * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Jack Lange <jarusl@cs.northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
29 #include <sys/types.h>
31 #elif defined(WIN32) && !defined(__CYGWIN__)
40 #define NBD_KEY "V3_NBD_1"
43 #define NBD_READ_CMD 0x1
44 #define NBD_WRITE_CMD 0x2
45 #define NBD_CAPACITY_CMD 0x3
47 #define NBD_STATUS_OK 0x00
48 #define NBD_STATUS_ERR 0xff
51 #define DEFAULT_LOG_FILE "./status.log"
52 #define DEFAULT_CONF_FILE "v3_nbd.ini"
55 #define DEFAULT_PORT 9500
56 #define MAX_STRING_SIZE 1024
59 #define LOGFILE_TAG "logfile"
60 #define PORT_TAG "port"
61 #define DISKS_TAG "disks"
63 // Turn on 64 bit file offset support (see 'man fseeko')
64 #define _FILE_OFFSET_BITS 64
68 //using namespace __gnu_cxx;
72 bool operator()(const SOCK sock1, const SOCK sock2) const {
73 return sock1 == sock2;
78 // Server Port that we'll listen on
81 // List of disks being served
82 // eqstr from vtl (config.h)
83 map<const string, v3_disk *, eqstr> disks;
85 // List of open connections
86 map<const SOCK, v3_disk *, eqsock> conns;
90 static const int enable_debug = 1;
94 int config_nbd(string conf_file_name);
95 int serv_loop(int serv_sock);
96 void setup_disk(string disk_tag, config_t &config_map);
98 int handle_new_connection(SOCK new_conn);
99 int handle_disk_request(SOCK conn, v3_disk * disk);
101 int handle_capacity_request(SOCK conn, v3_disk * disk);
102 int handle_read_request(SOCK conn, v3_disk * disk);
103 int handle_write_request(SOCK conn, v3_disk * disk);
105 int __main (int argc, char ** argv);
111 int main(int argc, char ** argv) {
112 return __main(argc, argv);
123 int __main (int argc, char ** argv) {
140 config_file = string(argv[1]);
142 config_file = DEFAULT_CONF_FILE;
146 if (config_nbd(config_file) == -1) {
147 cerr << "Configuration Error" << endl;
151 // setup network sockets
152 serv_sock = CreateAndSetupTcpSocket();
154 if (serv_sock == -1) {
155 cerr << "Could not create server socket, exiting..." << endl;
159 if (BindSocket(serv_sock, server_port) == -1) {
160 cerr << "Could not bind socket to port: " << server_port << endl;
164 if (ListenSocket(serv_sock) == -1) {
165 cerr << "Could not listen on server socket (port=" << server_port << ")" << endl;
170 vtl_debug("Starting Server Loop\n");
171 serv_loop(serv_sock);
178 int serv_loop(int serv_sock) {
179 fd_set all_set, read_set;
181 RawEthernetPacket pkt;
184 list<SOCK> pending_cons;
187 FD_SET(serv_sock, &all_set);
194 nready = select(max_fd + 1, &read_set, NULL, NULL, NULL);
197 if (errno == EINTR) {
200 vtl_debug("Select returned error\n");
201 perror("Select returned error: ");
207 if (FD_ISSET(serv_sock, &read_set)) {
209 struct sockaddr_in rem_addr;
210 socklen_t addr_len = sizeof(struct sockaddr_in);
212 conn_socket = accept(serv_sock, (struct sockaddr *)&rem_addr, &addr_len);
214 vtl_debug("New Connection...\n");
216 if (conn_socket < 0) {
217 if (errno == EINTR) {
220 vtl_debug("Accept returned error\n");
225 pending_cons.push_front(conn_socket);
227 FD_SET(conn_socket, &all_set);
229 if (conn_socket > max_fd) {
230 max_fd = conn_socket;
233 if (--nready <= 0) continue;
237 // handle open connections
238 for (map<SOCK, v3_disk *, eqsock>::iterator con_iter = conns.begin();
239 con_iter != conns.end(); ) {
240 SOCK tmp_sock = con_iter->first;
241 v3_disk * tmp_disk = con_iter->second;
243 if (FD_ISSET(con_iter->first, &read_set)) {
245 if (handle_disk_request(tmp_sock, tmp_disk) == -1) {
246 vtl_debug("Error: Could not complete disk request\n");
248 map<SOCK, v3_disk *, eqsock>::iterator tmp_iter = con_iter;
254 FD_CLR(tmp_sock, &all_set);
257 conns.erase(tmp_iter);
262 if (--nready <= 0) break;
266 if (nready <= 0) continue;
268 // check pending connections
269 for (list<SOCK>::iterator pending_iter = pending_cons.begin();
270 pending_iter != pending_cons.end();) {
272 if (FD_ISSET(*pending_iter, &read_set)) {
273 if (handle_new_connection(*pending_iter) == -1) {
275 vtl_debug("Error: Could not connect to disk\n");
276 FD_CLR(*pending_iter, &all_set);
278 list<SOCK>::iterator tmp_iter = pending_iter;
281 pending_cons.erase(tmp_iter);
283 if (--nready <= 0) break;
289 if (nready <= 0) continue;
299 int serv_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) {
301 RawEthernetPacket pkt;
302 WSANETWORKEVENTS net_events;
306 events[VNET_EVENT] = WSACreateEvent();
308 WSAEventSelect(vnet_sock, events[VNET_EVENT], FD_READ | FD_CLOSE);
309 events[IF_EVENT] = if_get_event(iface);
312 event_i = WSAWaitForMultipleEvents(2, events, false, WSA_INFINITE, false);
313 cout << "Wait returned" << endl;
315 if (event_i == WAIT_FAILED) {
316 cout << "ERROR: " << GetLastError() << endl;
319 event_i -= WAIT_OBJECT_0;
321 if (event_i == VNET_EVENT) {
323 if (WSAEnumNetworkEvents(vnet_sock, events[event_i], &net_events) == SOCKET_ERROR) {
324 cout << "EnumEventsError: " << WSAGetLastError() << endl;
327 if (net_events.lNetworkEvents & FD_READ) {
331 if (vnet_info->link_type == TCP_LINK) {
332 pkt.Unserialize(vnet_sock);
333 } else if (vnet_info->link_type == UDP_LINK) {
334 pkt.UdpUnserialize(vnet_sock);
337 process_outbound_pkt(&pkt);
339 if_write_pkt(iface, &pkt);
341 } else if (net_events.lNetworkEvents & FD_CLOSE) {
354 // byte 1: command (read = 1, write = 2, capacity = 3)
356 int handle_disk_request(SOCK conn, v3_disk * disk) {
359 int read_len = Receive(conn, buf, 4, true);
362 vtl_debug("Detaching from disk (conn=%d)\n", conn);
366 if (read_len == -1) {
367 vtl_debug("Could not read command\n");
371 if ((buf[1] != 0) || (buf[2] != 0) || (buf[3] != 0)) {
373 vtl_debug("Invalid command padding\n");
378 case NBD_CAPACITY_CMD:
379 return handle_capacity_request(conn, disk);
381 return handle_read_request(conn, disk);
383 return handle_write_request(conn, disk);
385 vtl_debug("Invalid Disk Command %d\n", buf[0]);
394 // 8 bytes : capacity
395 int handle_capacity_request(SOCK conn, v3_disk * disk) {
396 off_t capacity = disk->get_capacity();
398 vtl_debug("Returing capacity %d\n", capacity);
400 return Send(conn, (char *)&capacity, 8, true);
408 // 4 bytes : return length
410 int handle_read_request(SOCK conn, v3_disk * disk) {
412 unsigned int length = 0;
413 unsigned char * buf = NULL;
414 unsigned int ret_len = 0;
415 unsigned char status = NBD_STATUS_OK;
417 vtl_debug("Read Request\n");
421 if (Receive(conn, (char *)&offset, 8, true) <= 0) {
422 vtl_debug("Error receiving read offset\n");
426 vtl_debug("Read Offset %d\n", offset);
428 if (Receive(conn, (char *)&length, 4, true) <= 0) {
429 vtl_debug("Error receiving read length\n");
433 vtl_debug("Read length: %d\n", length);
435 buf = new unsigned char[length];
437 ret_len = disk->read(buf, offset, length);
439 vtl_debug("Read %d bytes from source disk\n", ret_len);
442 vtl_debug("Read Error\n");
443 status = NBD_STATUS_ERR;
446 vtl_debug("Sending Status byte (%d)\n", status);
448 if (Send(conn, (char *)&status, 1, true) <= 0) {
449 vtl_debug("Error Sending Read Status\n");
453 vtl_debug("Sending Ret Len: %d\n", ret_len);
455 if (Send(conn, (char *)&ret_len, 4, true) <= 0) {
456 vtl_debug("Error Sending Read Length\n");
463 vtl_debug("Sending Data\n");
465 SetNoDelaySocket(conn, false);
467 if (Send(conn, (char *)buf, ret_len, true) <= 0) {
468 vtl_debug("Error sending Read Data\n");
472 SetNoDelaySocket(conn, true);
475 vtl_debug("Read Complete\n");
488 int handle_write_request(SOCK conn, v3_disk * disk) {
490 unsigned int length = 0;
491 unsigned char * buf = NULL;
492 unsigned int ret_len = 0;
493 unsigned char status = NBD_STATUS_OK;
495 vtl_debug("Write Request\n");
497 if (Receive(conn, (char *)&offset, 8, true) <= 0) {
498 vtl_debug("Error receiving write offset\n");
502 vtl_debug("Write Offset %d\n", offset);
504 if (Receive(conn, (char *)&length, 4, true) <= 0) {
505 vtl_debug("Error receiving write length\n");
509 vtl_debug("Write length: %d\n", length);
511 buf = new unsigned char[length];
513 vtl_debug("Receiving Data\n");
515 if (Receive(conn, (char *)buf, length, true) <= 0) {
516 vtl_debug("Error receiving Write Data\n");
520 vtl_debug("Wrote %d bytes to source disk\n", ret_len);
522 if (disk->write(buf, offset, length) != length) {
523 vtl_debug("Write Error\n");
524 status = NBD_STATUS_ERR;
527 vtl_debug("Sending Status byte (%d)\n", status);
529 if (Send(conn, (char *)&status, 1, true) <= 0) {
530 vtl_debug("Error Sending Wrte Status\n");
534 vtl_debug("Write Complete\n");
543 * <NBD_KEY> <Disk Tag>\n
546 int handle_new_connection(SOCK new_conn) {
550 v3_disk * disk = NULL;
552 GetLine(new_conn, input);
554 vtl_debug("New Connection: %s\n", input.c_str());
557 istringstream is(input, istringstream::in);
558 is >> key_str >> tag_str;
561 if (key_str != NBD_KEY) {
562 vtl_debug("Error: Invalid NBD key string (%s)\n", key_str.c_str());
566 if (disks.count(tag_str) == 0) {
567 vtl_debug("Error: Requesting disk that does not exist (%s)\n", tag_str.c_str());
571 disk = disks[tag_str];
574 vtl_debug("Disk (%s) Does not exist\n", tag_str.c_str());
578 if (disk->locked == 1) {
579 vtl_debug("Attempting to attach to a device already in use\n");
583 conns[new_conn] = disk;
588 vtl_debug("Connected to disk %s\n", tag_str.c_str());
594 int config_nbd(string conf_file_name) {
597 if (read_config(conf_file_name, &config_map) != 0) {
598 cerr << "Could not read config file..." << endl;
602 if (config_map.count(LOGFILE_TAG) == 0) {
603 config_map[LOGFILE_TAG] = DEFAULT_LOG_FILE;
606 vtl_debug_init(config_map[LOGFILE_TAG], enable_debug);
609 if (config_map.count(PORT_TAG) > 0) {
610 server_port = atoi(config_map[PORT_TAG].c_str());
612 server_port = DEFAULT_PORT;
615 if (config_map.count(DISKS_TAG) > 0) {
616 istringstream disk_stream(config_map[DISKS_TAG], istringstream::in);
620 while (disk_stream >> disk_tag) {
622 if (i >= MAX_DISKS) {
623 cerr << "You specified too many disks, truncating..." << endl;
627 setup_disk(disk_tag, config_map);
631 cerr << "Must specify a set of disks" << endl;
639 void setup_disk(string disk_tag, config_t &config_map) {
640 string file_tag = disk_tag + ".file";
641 string type_tag = disk_tag + ".type";
647 cout << "Setting up " << disk_tag.c_str() << endl;
649 if ((config_map.count(file_tag) == 0) &&
650 (config_map.count(type_tag) == 0)) {
651 cerr << "Missing Disk configuration directive for " << disk_tag << endl;
654 type = config_map[type_tag];
657 disk = new raw_disk(config_map[file_tag]);
658 } else if (type == "ISO") {
659 vtl_debug("Setting up ISO\n");
660 disk = new iso_image(config_map[file_tag]);
663 disks[disk_tag] = disk;
674 cout << "Usage: v3_nbd [config_file]" << endl;