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.


nbd updates
[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
48 #define DEFAULT_LOG_FILE "./status.log"
49 #define DEFAULT_CONF_FILE "v3_nbd.ini"
50
51
52 #define DEFAULT_PORT 9500
53 #define MAX_STRING_SIZE 1024
54 #define MAX_DISKS 32
55
56 #define LOGFILE_TAG "logfile"
57 #define PORT_TAG  "port"
58 #define DISKS_TAG "disks"
59
60 // Turn on 64 bit file offset support (see 'man fseeko')
61 #define _FILE_OFFSET_BITS 64
62
63
64 using namespace std;
65 //using namespace __gnu_cxx;
66
67
68 struct eqsock {
69     bool operator()(const SOCK sock1, const SOCK sock2) const {
70         return sock1 == sock2;
71     }
72 };
73
74
75 // Server Port that we'll listen on
76 int server_port;
77
78 // List of disks being served 
79 // eqstr from vtl (config.h)
80 map<const string, v3_disk *, eqstr> disks;
81
82 // List of open connections
83 map<const SOCK, v3_disk *, eqsock> conns;
84
85
86 // Enable Debugging
87 static const int enable_debug = 1;
88
89
90 void usage();
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);
94
95 int handle_new_connection(SOCK new_conn);
96 int handle_disk_request(SOCK conn, v3_disk * disk);
97
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);
101
102 int __main (int argc, char ** argv);
103
104
105
106 #ifdef linux
107
108 int main(int argc, char ** argv) {
109   return __main(argc, argv);
110 }
111
112 #elif WIN32
113
114 void main() {
115   __main(0, NULL);
116 }
117
118 #endif 
119
120 int __main (int argc, char ** argv) {
121   string config_file;
122   SOCK serv_sock;
123
124
125
126   if (argc > 2) {
127     usage();
128     exit(0);
129   }
130
131   // init global maps
132   disks.clear();
133   conns.clear();
134
135
136   if (argc == 2) {
137     config_file = string(argv[1]);
138   } else {
139     config_file = DEFAULT_CONF_FILE;
140   }
141
142  
143   if (config_nbd(config_file) == -1) {
144     cerr << "Configuration Error" << endl;
145     exit(-1);
146   }
147
148   // setup network sockets
149   serv_sock = CreateAndSetupTcpSocket();
150   
151   if (serv_sock == -1) {
152       cerr << "Could not create server socket, exiting..." << endl;
153       exit(-1);
154   }
155
156   if (BindSocket(serv_sock, server_port) == -1) {
157       cerr << "Could not bind socket to port: " << server_port << endl;
158       exit(-1);
159   }
160
161   if (ListenSocket(serv_sock) == -1) {
162       cerr << "Could not listen on server socket (port=" << server_port << ")" << endl;
163       exit(-1);
164   }
165
166
167   vtl_debug("Starting Server Loop\n");
168   serv_loop(serv_sock);
169
170   return 0;
171 }
172
173
174 #ifdef linux
175 int serv_loop(int serv_sock) {
176     fd_set all_set, read_set;
177     int max_fd = -1;
178     RawEthernetPacket pkt;
179
180
181     list<SOCK> pending_cons;
182
183     FD_ZERO(&all_set);
184     FD_SET(serv_sock, &all_set);
185     max_fd = serv_sock;
186
187
188     while (1) {
189         int nready = 0;
190         read_set = all_set;
191         nready = select(max_fd + 1, &read_set, NULL, NULL, NULL);
192     
193         if (nready == -1) {
194             if (errno == EINTR) {
195                 continue;
196             } else {
197                 vtl_debug("Select returned error\n");
198                 perror("Select returned error: ");
199                 exit(-1);
200             }
201         }
202     
203
204         if (FD_ISSET(serv_sock, &read_set)) {
205             SOCK conn_socket;
206             struct sockaddr_in rem_addr;
207             socklen_t addr_len = sizeof(struct sockaddr_in);
208             // new connection
209             conn_socket = accept(serv_sock, (struct sockaddr *)&rem_addr, &addr_len);
210
211             if (conn_socket < 0) {
212                 if (errno == EINTR) {
213                     continue;
214                 } else {
215                     vtl_debug("Accept returned error\n");
216                     exit(-1);
217                 }
218             }
219
220             pending_cons.push_front(conn_socket);
221
222             FD_SET(conn_socket, &all_set);
223
224             if (conn_socket > max_fd) {
225                 max_fd = conn_socket;
226             }
227
228             if (--nready <= 0) continue;
229         }
230
231         
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;
237
238             if (FD_ISSET(con_iter->first, &read_set)) {
239
240                 if (handle_disk_request(tmp_sock, tmp_disk) == -1) {
241                     vtl_debug("Error: Could not complete disk request\n");
242
243                     map<SOCK, v3_disk *, eqsock>::iterator tmp_iter = con_iter;
244                     con_iter++;
245
246                     tmp_disk->detach();
247
248                     close(tmp_sock);
249                     FD_CLR(tmp_sock, &all_set);
250
251                     conns.erase(tmp_iter);
252                 } else {
253                     con_iter++;
254                 }
255
256                 if (--nready <= 0) break;
257             }
258         }
259
260         if (nready <= 0) continue;
261
262         // check pending connections
263         for (list<SOCK>::iterator pending_iter = pending_cons.begin();
264              pending_iter != pending_cons.end();
265              pending_iter++) {
266             
267             if (FD_ISSET(*pending_iter, &read_set)) {
268                 if (handle_new_connection(*pending_iter) == -1) {
269                     // error
270                     vtl_debug("Error: Could not connect to disk\n");
271                 }
272                 
273                 pending_cons.erase(pending_iter);
274                 
275                 if (--nready <= 0) break;
276             }
277         }
278         
279         if (nready <= 0) continue;
280         
281
282
283     }
284
285     return 0;
286 }
287
288 #elif WIN32
289 int serv_loop(iface_t * iface, SOCK vnet_sock, struct vnet_config * vnet_info) {
290   int ret;
291   RawEthernetPacket pkt;
292   WSANETWORKEVENTS net_events;
293   WSAEVENT events[2];
294   DWORD event_i;
295   
296   events[VNET_EVENT] = WSACreateEvent();
297   
298   WSAEventSelect(vnet_sock, events[VNET_EVENT], FD_READ | FD_CLOSE);
299   events[IF_EVENT] = if_get_event(iface);
300   
301   while (1) {
302     event_i = WSAWaitForMultipleEvents(2, events, false, WSA_INFINITE, false);
303     cout << "Wait returned" << endl;
304     
305     if (event_i == WAIT_FAILED) {
306       cout << "ERROR: " <<   GetLastError() << endl;
307       exit(-1);
308     }
309     event_i -= WAIT_OBJECT_0;
310     
311     if (event_i == VNET_EVENT) {
312       
313       if (WSAEnumNetworkEvents(vnet_sock, events[event_i], &net_events) == SOCKET_ERROR) {
314         cout << "EnumEventsError: " << WSAGetLastError() << endl;
315         exit(-1);
316       }
317       if (net_events.lNetworkEvents & FD_READ) {
318         
319         // we received data
320         
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);
325         }
326         
327         process_outbound_pkt(&pkt);
328         
329         if_write_pkt(iface, &pkt);
330         
331       } else if (net_events.lNetworkEvents & FD_CLOSE) {
332         CLOSE(vnet_sock);
333         return 0;
334       }
335       
336     }
337   }
338   
339   return 0;
340 }
341 #endif
342
343
344 // byte 1: command (read = 1, write = 2, capacity = 3)
345 // byte 2 - 4: zero
346 int handle_disk_request(SOCK conn, v3_disk * disk) {
347     char buf[4];
348
349     int read_len = Receive(conn, buf, 4, true);
350     
351     if (read_len == 0) {
352         vtl_debug("Detaching from disk (conn=%d)\n", conn);
353         return -1;
354     }
355
356     if (read_len == -1) {
357         vtl_debug("Could not read command\n");
358         return -1;
359     }
360
361     if ((buf[1] != 0) || (buf[2] != 0) || (buf[3] != 0)) {
362         // error
363         vtl_debug("Invalid command padding\n");
364         return -1;
365     }
366
367     switch (buf[0]) {
368         case NBD_CAPACITY_CMD:
369             return handle_capacity_request(conn, disk);
370         case NBD_READ_CMD:
371             return handle_read_request(conn, disk);
372         case NBD_WRITE_CMD:
373             return handle_write_request(conn, disk);
374         default:
375             vtl_debug("Invalid Disk Command %d\n", buf[0]);
376             return -1;
377     }
378
379     return 0;
380 }
381
382
383 // send: 
384 //    8 bytes : capacity
385 int handle_capacity_request(SOCK conn, v3_disk * disk) {
386     off_t capacity = disk->get_capacity();
387
388     return Send(conn, (char *)&capacity, 8, true);
389 }
390
391 // receive:
392 //    8 bytes : offset
393 //    4 bytes : length
394 // send:
395 //    1 byte  : status
396 //    4 bytes : return length
397 //    x bytes : data
398 int handle_read_request(SOCK conn, v3_disk * disk) {
399     return -1;
400 }
401
402 // receive: 
403 //    8 bytes : offset
404 //    4 bytes : length
405 // send : 
406 //    1 bytes : status
407 int handle_write_request(SOCK conn, v3_disk * disk) {
408     return -1;
409 }
410
411
412 /* Negotiation:
413  * <NBD_KEY> <Disk Tag>\n
414  */
415
416 int handle_new_connection(SOCK new_conn) {
417     string input;
418     string key_str;
419     string tag_str;
420     v3_disk * disk = NULL;
421
422     GetLine(new_conn, input);
423
424     {
425         istringstream is(input, istringstream::in);
426         is >> key_str >> tag_str;
427     }
428
429     if (key_str != NBD_KEY) {
430         vtl_debug("Error: Invalid NBD key string (%s)\n", key_str.c_str());
431         return -1;
432     }
433
434     if (disks.count(tag_str) == 0) {
435         vtl_debug("Error: Requesting disk that does not exist (%s)\n", tag_str.c_str());
436         return -1;
437     }
438
439     disk = disks[tag_str];
440
441     if (!disk) {
442         vtl_debug("Disk (%s) Does not exist\n", tag_str.c_str());
443         return -1;
444     }
445
446     if (disk->locked == 1) {
447         vtl_debug("Attempting to attach to a device already in use\n");
448         return -1;
449     }
450
451     conns[new_conn] = disk;
452
453     disk->attach();
454
455     vtl_debug("Connected to disk %s\n", tag_str.c_str());
456
457     return 0;
458 }
459
460
461 int config_nbd(string conf_file_name) {
462     config_t config_map;
463     
464     if (read_config(conf_file_name, &config_map) != 0) {
465         cerr << "Could not read config file..." << endl;
466         return -1;
467     }
468
469     if (config_map.count(PORT_TAG) > 0) {
470         server_port = atoi(config_map[PORT_TAG].c_str());
471     } else {
472         server_port = DEFAULT_PORT;
473     }
474         
475     if (config_map.count(DISKS_TAG) > 0) {
476         istringstream disk_stream(config_map[DISKS_TAG], istringstream::in);
477         string disk_tag;
478         int i = 0;
479         
480         while (disk_stream >> disk_tag) {
481
482             if (i >= MAX_DISKS) {
483                 cerr << "You specified too many disks, truncating..." << endl;
484                 break;
485             }
486             
487             setup_disk(disk_tag, config_map);
488             i++;
489         }       
490     } else {
491         cerr << "Must specify a set of disks" << endl;
492         return -1;
493     }
494     
495     if (config_map.count(LOGFILE_TAG) == 0) {
496         config_map[LOGFILE_TAG] = DEFAULT_LOG_FILE;
497     }
498     
499     vtl_debug_init(config_map[LOGFILE_TAG], enable_debug);
500
501     return 0;
502 }
503
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";
507     
508     v3_disk * disk;
509     string type;
510
511
512     cout << "Setting up " << disk_tag.c_str() << endl;
513
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;
517     }
518
519     type = config_map[type_tag];  
520
521     if (type == "RAW") {
522         disk = new raw_disk(config_map[file_tag]);
523     } else if (type == "ISO") {
524         disk = new iso_image(config_map[file_tag]);
525     }
526
527     disks[disk_tag] = disk;
528
529     return;
530 }
531
532
533
534
535
536
537 void usage() {
538   cout << "Usage: v3_nbd [config_file]" << endl;
539   return;
540 }