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
48 #define DEFAULT_LOG_FILE "./status.log"
49 #define DEFAULT_CONF_FILE "v3_nbd.ini"
52 #define DEFAULT_PORT 9500
53 #define MAX_STRING_SIZE 1024
56 #define LOGFILE_TAG "logfile"
57 #define PORT_TAG "port"
58 #define DISKS_TAG "disks"
60 // Turn on 64 bit file offset support (see 'man fseeko')
61 #define _FILE_OFFSET_BITS 64
65 //using namespace __gnu_cxx;
69 bool operator()(const SOCK sock1, const SOCK sock2) const {
70 return sock1 == sock2;
75 // Server Port that we'll listen on
78 // List of disks being served
79 // eqstr from vtl (config.h)
80 map<const string, v3_disk *, eqstr> disks;
82 // List of open connections
83 map<const SOCK, v3_disk *, eqsock> conns;
87 static const int enable_debug = 1;
91 int config_nbd(string conf_file_name);
92 int serv_loop(int serv_sock);
93 void setup_disk(string disk_tag, config_t &config_map);
95 int handle_new_connection(SOCK new_conn);
96 int handle_disk_request(SOCK conn, v3_disk * disk);
98 int handle_capacity_request(SOCK conn, v3_disk * disk);
99 int handle_read_request(SOCK conn, v3_disk * disk);
100 int handle_write_request(SOCK conn, v3_disk * disk);
102 int __main (int argc, char ** argv);
108 int main(int argc, char ** argv) {
109 return __main(argc, argv);
120 int __main (int argc, char ** argv) {
137 config_file = string(argv[1]);
139 config_file = DEFAULT_CONF_FILE;
143 if (config_nbd(config_file) == -1) {
144 cerr << "Configuration Error" << endl;
148 // setup network sockets
149 serv_sock = CreateAndSetupTcpSocket();
151 if (serv_sock == -1) {
152 cerr << "Could not create server socket, exiting..." << endl;
156 if (BindSocket(serv_sock, server_port) == -1) {
157 cerr << "Could not bind socket to port: " << server_port << endl;
161 if (ListenSocket(serv_sock) == -1) {
162 cerr << "Could not listen on server socket (port=" << server_port << ")" << endl;
167 vtl_debug("Starting Server Loop\n");
168 serv_loop(serv_sock);
175 int serv_loop(int serv_sock) {
176 fd_set all_set, read_set;
178 RawEthernetPacket pkt;
181 list<SOCK> pending_cons;
184 FD_SET(serv_sock, &all_set);
191 nready = select(max_fd + 1, &read_set, NULL, NULL, NULL);
194 if (errno == EINTR) {
197 vtl_debug("Select returned error\n");
198 perror("Select returned error: ");
204 if (FD_ISSET(serv_sock, &read_set)) {
206 struct sockaddr_in rem_addr;
207 socklen_t addr_len = sizeof(struct sockaddr_in);
209 conn_socket = accept(serv_sock, (struct sockaddr *)&rem_addr, &addr_len);
211 if (conn_socket < 0) {
212 if (errno == EINTR) {
215 vtl_debug("Accept returned error\n");
220 pending_cons.push_front(conn_socket);
222 FD_SET(conn_socket, &all_set);
224 if (conn_socket > max_fd) {
225 max_fd = conn_socket;
228 if (--nready <= 0) continue;
232 // handle open connections
233 for (map<SOCK, v3_disk *, eqsock>::iterator con_iter = conns.begin();
234 con_iter != conns.end(); ) {
235 SOCK tmp_sock = con_iter->first;
236 v3_disk * tmp_disk = con_iter->second;
238 if (FD_ISSET(con_iter->first, &read_set)) {
240 if (handle_disk_request(tmp_sock, tmp_disk) == -1) {
241 vtl_debug("Error: Could not complete disk request\n");
243 map<SOCK, v3_disk *, eqsock>::iterator tmp_iter = con_iter;
249 FD_CLR(tmp_sock, &all_set);
251 conns.erase(tmp_iter);
256 if (--nready <= 0) break;
260 if (nready <= 0) continue;
262 // check pending connections
263 for (list<SOCK>::iterator pending_iter = pending_cons.begin();
264 pending_iter != pending_cons.end();
267 if (FD_ISSET(*pending_iter, &read_set)) {
268 if (handle_new_connection(*pending_iter) == -1) {
270 vtl_debug("Error: Could not connect to disk\n");
273 pending_cons.erase(pending_iter);
275 if (--nready <= 0) break;
279 if (nready <= 0) continue;
289 int serv_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) {
291 RawEthernetPacket pkt;
292 WSANETWORKEVENTS net_events;
296 events[VNET_EVENT] = WSACreateEvent();
298 WSAEventSelect(vnet_sock, events[VNET_EVENT], FD_READ | FD_CLOSE);
299 events[IF_EVENT] = if_get_event(iface);
302 event_i = WSAWaitForMultipleEvents(2, events, false, WSA_INFINITE, false);
303 cout << "Wait returned" << endl;
305 if (event_i == WAIT_FAILED) {
306 cout << "ERROR: " << GetLastError() << endl;
309 event_i -= WAIT_OBJECT_0;
311 if (event_i == VNET_EVENT) {
313 if (WSAEnumNetworkEvents(vnet_sock, events[event_i], &net_events) == SOCKET_ERROR) {
314 cout << "EnumEventsError: " << WSAGetLastError() << endl;
317 if (net_events.lNetworkEvents & FD_READ) {
321 if (vnet_info->link_type == TCP_LINK) {
322 pkt.Unserialize(vnet_sock);
323 } else if (vnet_info->link_type == UDP_LINK) {
324 pkt.UdpUnserialize(vnet_sock);
327 process_outbound_pkt(&pkt);
329 if_write_pkt(iface, &pkt);
331 } else if (net_events.lNetworkEvents & FD_CLOSE) {
344 // byte 1: command (read = 1, write = 2, capacity = 3)
346 int handle_disk_request(SOCK conn, v3_disk * disk) {
349 int read_len = Receive(conn, buf, 4, true);
352 vtl_debug("Detaching from disk (conn=%d)\n", conn);
356 if (read_len == -1) {
357 vtl_debug("Could not read command\n");
361 if ((buf[1] != 0) || (buf[2] != 0) || (buf[3] != 0)) {
363 vtl_debug("Invalid command padding\n");
368 case NBD_CAPACITY_CMD:
369 return handle_capacity_request(conn, disk);
371 return handle_read_request(conn, disk);
373 return handle_write_request(conn, disk);
375 vtl_debug("Invalid Disk Command %d\n", buf[0]);
384 // 8 bytes : capacity
385 int handle_capacity_request(SOCK conn, v3_disk * disk) {
386 off_t capacity = disk->get_capacity();
388 return Send(conn, (char *)&capacity, 8, true);
396 // 4 bytes : return length
398 int handle_read_request(SOCK conn, v3_disk * disk) {
407 int handle_write_request(SOCK conn, v3_disk * disk) {
413 * <NBD_KEY> <Disk Tag>\n
416 int handle_new_connection(SOCK new_conn) {
420 v3_disk * disk = NULL;
422 GetLine(new_conn, input);
425 istringstream is(input, istringstream::in);
426 is >> key_str >> tag_str;
429 if (key_str != NBD_KEY) {
430 vtl_debug("Error: Invalid NBD key string (%s)\n", key_str.c_str());
434 if (disks.count(tag_str) == 0) {
435 vtl_debug("Error: Requesting disk that does not exist (%s)\n", tag_str.c_str());
439 disk = disks[tag_str];
442 vtl_debug("Disk (%s) Does not exist\n", tag_str.c_str());
446 if (disk->locked == 1) {
447 vtl_debug("Attempting to attach to a device already in use\n");
451 conns[new_conn] = disk;
455 vtl_debug("Connected to disk %s\n", tag_str.c_str());
461 int config_nbd(string conf_file_name) {
464 if (read_config(conf_file_name, &config_map) != 0) {
465 cerr << "Could not read config file..." << endl;
469 if (config_map.count(PORT_TAG) > 0) {
470 server_port = atoi(config_map[PORT_TAG].c_str());
472 server_port = DEFAULT_PORT;
475 if (config_map.count(DISKS_TAG) > 0) {
476 istringstream disk_stream(config_map[DISKS_TAG], istringstream::in);
480 while (disk_stream >> disk_tag) {
482 if (i >= MAX_DISKS) {
483 cerr << "You specified too many disks, truncating..." << endl;
487 setup_disk(disk_tag, config_map);
491 cerr << "Must specify a set of disks" << endl;
495 if (config_map.count(LOGFILE_TAG) == 0) {
496 config_map[LOGFILE_TAG] = DEFAULT_LOG_FILE;
499 vtl_debug_init(config_map[LOGFILE_TAG], enable_debug);
504 void setup_disk(string disk_tag, config_t &config_map) {
505 string file_tag = disk_tag + ".file";
506 string type_tag = disk_tag + ".type";
512 cout << "Setting up " << disk_tag.c_str() << endl;
514 if ((config_map.count(file_tag) == 0) &&
515 (config_map.count(type_tag) == 0)) {
516 cerr << "Missing Disk configuration directive for " << disk_tag << endl;
519 type = config_map[type_tag];
522 disk = new raw_disk(config_map[file_tag]);
523 } else if (type == "ISO") {
524 disk = new iso_image(config_map[file_tag]);
527 disks[disk_tag] = disk;
538 cout << "Usage: v3_nbd [config_file]" << endl;