Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


added disk support to the nbd server
[palacios.git] / misc / network_servers / v3_nbd / v3_nbd.cc
1 /* 
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.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
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.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20
21 #include <iostream>
22 #include <fstream>
23 #include <stdio.h>
24 #include <sstream>
25 #include <list>
26
27 #ifdef linux 
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31 #elif defined(WIN32) && !defined(__CYGWIN__)
32
33 #endif
34
35
36 #include "v3_disk.h"
37 #include "raw.h"
38 #include "iso.h"
39
40 #define NBD_KEY "V3_NBD_1"
41
42
43 #define NBD_READ_CMD 0x1
44 #define NBD_WRITE_CMD 0x2
45 #define NBD_CAPACITY_CMD 0x3
46
47 #define NBD_STATUS_OK 0x00
48 #define NBD_STATUS_ERR 0xff
49
50
51 #define DEFAULT_LOG_FILE "./status.log"
52 #define DEFAULT_CONF_FILE "v3_nbd.ini"
53
54
55 #define DEFAULT_PORT 9500
56 #define MAX_STRING_SIZE 1024
57 #define MAX_DISKS 32
58
59 #define LOGFILE_TAG "logfile"
60 #define PORT_TAG  "port"
61 #define DISKS_TAG "disks"
62
63 // Turn on 64 bit file offset support (see 'man fseeko')
64 #define _FILE_OFFSET_BITS 64
65
66
67 using namespace std;
68 //using namespace __gnu_cxx;
69
70
71 struct eqsock {
72     bool operator()(const SOCK sock1, const SOCK sock2) const {
73         return sock1 == sock2;
74     }
75 };
76
77
78 // Server Port that we'll listen on
79 int server_port;
80
81 // List of disks being served 
82 // eqstr from vtl (config.h)
83 map<const string, v3_disk *, eqstr> disks;
84
85 // List of open connections
86 map<const SOCK, v3_disk *, eqsock> conns;
87
88
89 // Enable Debugging
90 static const int enable_debug = 1;
91
92
93 void usage();
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);
97
98 int handle_new_connection(SOCK new_conn);
99 int handle_disk_request(SOCK conn, v3_disk * disk);
100
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);
104
105 int __main (int argc, char ** argv);
106
107
108
109 #ifdef linux
110
111 int main(int argc, char ** argv) {
112   return __main(argc, argv);
113 }
114
115 #elif WIN32
116
117 void main() {
118   __main(0, NULL);
119 }
120
121 #endif 
122
123 int __main (int argc, char ** argv) {
124   string config_file;
125   SOCK serv_sock;
126
127
128
129   if (argc > 2) {
130     usage();
131     exit(0);
132   }
133
134   // init global maps
135   disks.clear();
136   conns.clear();
137
138
139   if (argc == 2) {
140     config_file = string(argv[1]);
141   } else {
142     config_file = DEFAULT_CONF_FILE;
143   }
144
145  
146   if (config_nbd(config_file) == -1) {
147     cerr << "Configuration Error" << endl;
148     exit(-1);
149   }
150
151   // setup network sockets
152   serv_sock = CreateAndSetupTcpSocket();
153   
154   if (serv_sock == -1) {
155       cerr << "Could not create server socket, exiting..." << endl;
156       exit(-1);
157   }
158
159   if (BindSocket(serv_sock, server_port) == -1) {
160       cerr << "Could not bind socket to port: " << server_port << endl;
161       exit(-1);
162   }
163
164   if (ListenSocket(serv_sock) == -1) {
165       cerr << "Could not listen on server socket (port=" << server_port << ")" << endl;
166       exit(-1);
167   }
168
169
170   vtl_debug("Starting Server Loop\n");
171   serv_loop(serv_sock);
172
173   return 0;
174 }
175
176
177 #ifdef linux
178 int serv_loop(int serv_sock) {
179     fd_set all_set, read_set;
180     int max_fd = -1;
181     RawEthernetPacket pkt;
182
183
184     list<SOCK> pending_cons;
185
186     FD_ZERO(&all_set);
187     FD_SET(serv_sock, &all_set);
188     max_fd = serv_sock;
189
190
191     while (1) {
192         int nready = 0;
193         read_set = all_set;
194         nready = select(max_fd + 1, &read_set, NULL, NULL, NULL);
195     
196         if (nready == -1) {
197             if (errno == EINTR) {
198                 continue;
199             } else {
200                 vtl_debug("Select returned error\n");
201                 perror("Select returned error: ");
202                 exit(-1);
203             }
204         }
205     
206
207         if (FD_ISSET(serv_sock, &read_set)) {
208             SOCK conn_socket;
209             struct sockaddr_in rem_addr;
210             socklen_t addr_len = sizeof(struct sockaddr_in);
211             // new connection
212             conn_socket = accept(serv_sock, (struct sockaddr *)&rem_addr, &addr_len);
213
214             vtl_debug("New Connection...\n");
215
216             if (conn_socket < 0) {
217                 if (errno == EINTR) {
218                     continue;
219                 } else {
220                     vtl_debug("Accept returned error\n");
221                     exit(-1);
222                 }
223             }
224
225             pending_cons.push_front(conn_socket);
226
227             FD_SET(conn_socket, &all_set);
228
229             if (conn_socket > max_fd) {
230                 max_fd = conn_socket;
231             }
232
233             if (--nready <= 0) continue;
234         }
235
236         
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;
242
243             if (FD_ISSET(con_iter->first, &read_set)) {
244
245                 if (handle_disk_request(tmp_sock, tmp_disk) == -1) {
246                     vtl_debug("Error: Could not complete disk request\n");
247
248                     map<SOCK, v3_disk *, eqsock>::iterator tmp_iter = con_iter;
249                     con_iter++;
250
251                     tmp_disk->detach();
252
253
254                     FD_CLR(tmp_sock, &all_set);
255                     close(tmp_sock);
256
257                     conns.erase(tmp_iter);
258                 } else {
259                     con_iter++;
260                 }
261
262                 if (--nready <= 0) break;
263             }
264         }
265
266         if (nready <= 0) continue;
267
268         // check pending connections
269         for (list<SOCK>::iterator pending_iter = pending_cons.begin();
270              pending_iter != pending_cons.end();) {
271             
272             if (FD_ISSET(*pending_iter, &read_set)) {
273                 if (handle_new_connection(*pending_iter) == -1) {
274                     // error
275                     vtl_debug("Error: Could not connect to disk\n");
276                     FD_CLR(*pending_iter, &all_set);
277                 }
278                 list<SOCK>::iterator tmp_iter = pending_iter;
279                 pending_iter++;
280
281                 pending_cons.erase(tmp_iter);
282                 
283                 if (--nready <= 0) break;
284             } else {
285                 pending_iter++;
286             }
287         }
288         
289         if (nready <= 0) continue;
290         
291
292
293     }
294
295     return 0;
296 }
297
298 #elif WIN32
299 int serv_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) {
300   int ret;
301   RawEthernetPacket pkt;
302   WSANETWORKEVENTS net_events;
303   WSAEVENT events[2];
304   DWORD event_i;
305   
306   events[VNET_EVENT] = WSACreateEvent();
307   
308   WSAEventSelect(vnet_sock, events[VNET_EVENT], FD_READ | FD_CLOSE);
309   events[IF_EVENT] = if_get_event(iface);
310   
311   while (1) {
312     event_i = WSAWaitForMultipleEvents(2, events, false, WSA_INFINITE, false);
313     cout << "Wait returned" << endl;
314     
315     if (event_i == WAIT_FAILED) {
316       cout << "ERROR: " <<   GetLastError() << endl;
317       exit(-1);
318     }
319     event_i -= WAIT_OBJECT_0;
320     
321     if (event_i == VNET_EVENT) {
322       
323       if (WSAEnumNetworkEvents(vnet_sock, events[event_i], &net_events) == SOCKET_ERROR) {
324         cout << "EnumEventsError: " << WSAGetLastError() << endl;
325         exit(-1);
326       }
327       if (net_events.lNetworkEvents & FD_READ) {
328         
329         // we received data
330         
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);
335         }
336         
337         process_outbound_pkt(&pkt);
338         
339         if_write_pkt(iface, &pkt);
340         
341       } else if (net_events.lNetworkEvents & FD_CLOSE) {
342         CLOSE(vnet_sock);
343         return 0;
344       }
345       
346     }
347   }
348   
349   return 0;
350 }
351 #endif
352
353
354 // byte 1: command (read = 1, write = 2, capacity = 3)
355 // byte 2 - 4: zero
356 int handle_disk_request(SOCK conn, v3_disk * disk) {
357     char buf[4];
358
359     int read_len = Receive(conn, buf, 4, true);
360     
361     if (read_len == 0) {
362         vtl_debug("Detaching from disk (conn=%d)\n", conn);
363         return -1;
364     }
365
366     if (read_len == -1) {
367         vtl_debug("Could not read command\n");
368         return -1;
369     }
370
371     if ((buf[1] != 0) || (buf[2] != 0) || (buf[3] != 0)) {
372         // error
373         vtl_debug("Invalid command padding\n");
374         return -1;
375     }
376
377     switch (buf[0]) {
378         case NBD_CAPACITY_CMD:
379             return handle_capacity_request(conn, disk);
380         case NBD_READ_CMD:
381             return handle_read_request(conn, disk);
382         case NBD_WRITE_CMD:
383             return handle_write_request(conn, disk);
384         default:
385             vtl_debug("Invalid Disk Command %d\n", buf[0]);
386             return -1;
387     }
388
389     return 0;
390 }
391
392
393 // send: 
394 //    8 bytes : capacity
395 int handle_capacity_request(SOCK conn, v3_disk * disk) {
396     off_t capacity = disk->get_capacity();
397
398     vtl_debug("Returing capacity %d\n", capacity);
399
400     return Send(conn, (char *)&capacity, 8, true);
401 }
402
403 // receive:
404 //    8 bytes : offset
405 //    4 bytes : length
406 // send:
407 //    1 byte  : status
408 //    4 bytes : return length
409 //    x bytes : data
410 int handle_read_request(SOCK conn, v3_disk * disk) {
411     off_t offset = 0;
412     unsigned int length = 0;
413     unsigned char * buf = NULL;
414     unsigned int ret_len = 0;
415     unsigned char status = NBD_STATUS_OK;
416
417     vtl_debug("Read Request\n");
418
419
420
421     if (Receive(conn, (char *)&offset, 8, true) <= 0) {
422         vtl_debug("Error receiving read offset\n");
423         return -1;
424     }
425
426     vtl_debug("Read Offset %d\n", offset);
427
428     if (Receive(conn, (char *)&length, 4, true) <= 0) {
429         vtl_debug("Error receiving read length\n");
430         return -1;
431     }
432
433     vtl_debug("Read length: %d\n", length);
434
435     buf = new unsigned char[length];
436     
437     ret_len = disk->read(buf, offset, length);
438
439     vtl_debug("Read %d bytes from source disk\n", ret_len);
440
441     if (ret_len == 0) {
442         vtl_debug("Read Error\n");
443         status = NBD_STATUS_ERR;
444     }
445
446     vtl_debug("Sending Status byte (%d)\n", status);
447
448     if (Send(conn, (char *)&status, 1, true) <= 0) {
449         vtl_debug("Error Sending Read Status\n");
450         return -1;
451     }
452
453     vtl_debug("Sending Ret Len: %d\n", ret_len);
454
455     if (Send(conn, (char *)&ret_len, 4, true) <= 0) {
456         vtl_debug("Error Sending Read Length\n");
457         return -1;
458     }
459
460
461
462     if (ret_len > 0) {
463         vtl_debug("Sending Data\n");
464
465         SetNoDelaySocket(conn, false);
466
467         if (Send(conn, (char *)buf, ret_len, true)  <= 0) {
468             vtl_debug("Error sending Read Data\n");
469             return -1;
470         }
471
472         SetNoDelaySocket(conn, true);
473     }
474
475     vtl_debug("Read Complete\n");
476
477     delete buf;
478
479     return 0;
480 }
481
482 // receive: 
483 //    8 bytes : offset
484 //    4 bytes : length
485 //    x bytes : data
486 // send : 
487 //    1 bytes : status
488 int handle_write_request(SOCK conn, v3_disk * disk) {
489     off_t offset = 0;
490     unsigned int length = 0;
491     unsigned char * buf = NULL;
492     unsigned int ret_len = 0;
493     unsigned char status = NBD_STATUS_OK;
494
495     vtl_debug("Write Request\n");
496     
497     if (Receive(conn, (char *)&offset, 8, true) <= 0) {
498         vtl_debug("Error receiving write offset\n");
499         return -1;
500     }
501
502     vtl_debug("Write Offset %d\n", offset);
503
504     if (Receive(conn, (char *)&length, 4, true) <= 0) {
505         vtl_debug("Error receiving write length\n");
506         return -1;
507     }
508
509     vtl_debug("Write length: %d\n", length);
510
511     buf = new unsigned char[length];
512     
513     vtl_debug("Receiving Data\n");
514
515     if (Receive(conn, (char *)buf, length, true)  <= 0) {
516         vtl_debug("Error receiving Write Data\n");
517         return -1;
518     }
519
520     vtl_debug("Wrote %d bytes to source disk\n", ret_len);
521
522     if (disk->write(buf, offset, length) != length) {
523         vtl_debug("Write Error\n");
524         status = NBD_STATUS_ERR;
525     }
526
527     vtl_debug("Sending Status byte (%d)\n", status);
528
529     if (Send(conn, (char *)&status, 1, true) <= 0) {
530         vtl_debug("Error Sending Wrte Status\n");
531         return -1;
532     }
533
534     vtl_debug("Write Complete\n");
535
536     delete buf;
537
538     return 0;
539 }
540
541
542 /* Negotiation:
543  * <NBD_KEY> <Disk Tag>\n
544  */
545
546 int handle_new_connection(SOCK new_conn) {
547     string input;
548     string key_str;
549     string tag_str;
550     v3_disk * disk = NULL;
551
552     GetLine(new_conn, input);
553
554     vtl_debug("New Connection: %s\n", input.c_str());
555
556     {
557         istringstream is(input, istringstream::in);
558         is >> key_str >> tag_str;
559     }
560
561     if (key_str != NBD_KEY) {
562         vtl_debug("Error: Invalid NBD key string (%s)\n", key_str.c_str());
563         return -1;
564     }
565
566     if (disks.count(tag_str) == 0) {
567         vtl_debug("Error: Requesting disk that does not exist (%s)\n", tag_str.c_str());
568         return -1;
569     }
570
571     disk = disks[tag_str];
572
573     if (!disk) {
574         vtl_debug("Disk (%s) Does not exist\n", tag_str.c_str());
575         return -1;
576     }
577
578     if (disk->locked == 1) {
579         vtl_debug("Attempting to attach to a device already in use\n");
580         return -1;
581     }
582
583     conns[new_conn] = disk;
584
585     disk->attach();
586
587
588     vtl_debug("Connected to disk %s\n", tag_str.c_str());
589
590     return 0;
591 }
592
593
594 int config_nbd(string conf_file_name) {
595     config_t config_map;
596     
597     if (read_config(conf_file_name, &config_map) != 0) {
598         cerr << "Could not read config file..." << endl;
599         return -1;
600     }
601
602     if (config_map.count(LOGFILE_TAG) == 0) {
603         config_map[LOGFILE_TAG] = DEFAULT_LOG_FILE;
604     }
605
606     vtl_debug_init(config_map[LOGFILE_TAG], enable_debug);
607
608
609     if (config_map.count(PORT_TAG) > 0) {
610         server_port = atoi(config_map[PORT_TAG].c_str());
611     } else {
612         server_port = DEFAULT_PORT;
613     }
614         
615     if (config_map.count(DISKS_TAG) > 0) {
616         istringstream disk_stream(config_map[DISKS_TAG], istringstream::in);
617         string disk_tag;
618         int i = 0;
619         
620         while (disk_stream >> disk_tag) {
621
622             if (i >= MAX_DISKS) {
623                 cerr << "You specified too many disks, truncating..." << endl;
624                 break;
625             }
626             
627             setup_disk(disk_tag, config_map);
628             i++;
629         }       
630     } else {
631         cerr << "Must specify a set of disks" << endl;
632         return -1;
633     }
634     
635
636     return 0;
637 }
638
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";
642     
643     v3_disk * disk;
644     string type;
645
646
647     cout << "Setting up " << disk_tag.c_str() << endl;
648
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;
652     }
653
654     type = config_map[type_tag];  
655
656     if (type == "RAW") {
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]);
661     }
662
663     disks[disk_tag] = disk;
664
665     return;
666 }
667
668
669
670
671
672
673 void usage() {
674   cout << "Usage: v3_nbd [config_file]" << endl;
675   return;
676 }