1    	/*
2    	  Copyright Red Hat, Inc. 2006-2012
3    	
4    	  This program is free software; you can redistribute it and/or modify it
5    	  under the terms of the GNU General Public License as published by the
6    	  Free Software Foundation; either version 2, or (at your option) any
7    	  later version.
8    	
9    	  This program is distributed in the hope that it will be useful, but
10   	  WITHOUT ANY WARRANTY; without even the implied warranty of
11   	  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   	  General Public License for more details.
13   	
14   	  You should have received a copy of the GNU General Public License
15   	  along with this program; see the file COPYING.  If not, write to the
16   	  Free Software Foundation, Inc.,  675 Mass Ave, Cambridge, 
17   	  MA 02139, USA.
18   	*/
19   	
20   	#include "config.h"
21   	
22   	#include <unistd.h>
23   	#include <stdint.h>
24   	#include <stdlib.h>
25   	#include <stdio.h>
26   	#include <string.h>
27   	#include <signal.h>
28   	#include <errno.h>
29   	#include <nss.h>
30   	#include <sys/socket.h>
31   	#include <netdb.h>
32   	
33   	/* Local includes */
34   	#include "xvm.h"
35   	#include "simple_auth.h"
36   	#include "options.h"
37   	#include "mcast.h"
38   	#include "tcp.h"
39   	#include "tcp_listener.h"
40   	#include "debug.h"
41   	#include "fdops.h"
42   	#include "list.h"
43   	#include "simpleconfig.h"
44   	#include "static_map.h"
45   	#include "server_plugin.h"
46   	#include "history.h"
47   	
48   	#define NAME "tcp"
49   	#define TCP_VERSION "0.2"
50   	
51   	#define TCP_MAGIC 0xc3dff7a9
52   	
53   	#define VALIDATE(info) \
54   	do {\
55   		if (!info || info->magic != TCP_MAGIC)\
56   			return -EINVAL;\
57   	} while(0)
58   	
59   	typedef struct _tcp_options {
60   		char *key_file;
61   		char *addr;
62   		int family;
63   		unsigned int port;
64   		unsigned int hash;
65   		unsigned int auth;
66   		unsigned int flags;
67   	} tcp_options;
68   	
69   	
70   	typedef struct _tcp_info {
71   		uint64_t magic;
72   		void *priv;
73   		map_object_t *map;
74   		history_info_t *history;
75   		char key[MAX_KEY_LEN];
76   		tcp_options args;
77   		const fence_callbacks_t *cb;
78   		ssize_t key_len;
79   		int listen_sock;
80   	} tcp_info;
81   	
82   	
83   	struct tcp_hostlist_arg {
84   		map_object_t *map;
85   		const char *src;
86   		int fd;
87   	};
88   	
89   	
90   	/*
91   	 * See if we fenced this node recently (successfully)
92   	 * If so, ignore the request for a few seconds.
93   	 *
94   	 * We purge our history when the entries time out.
95   	 */
96   	static int
97   	check_history(void *a, void *b) {
98   		fence_req_t *old = a, *current = b;
99   	
100  		if (old->request == current->request &&
101  		    old->seqno == current->seqno &&
102  		    !strcasecmp((const char *)old->domain,
103  				(const char *)current->domain)) {
104  			return 1;
105  		}
106  		return 0;
107  	}
108  	
109  	static int 
110  	tcp_hostlist(const char *vm_name, const char *vm_uuid,
111  		       int state, void *priv)
112  	{
113  		struct tcp_hostlist_arg *arg = (struct tcp_hostlist_arg *)priv;
(1) Event var_decl: Declaring variable "hinfo" without initializer.
Also see events: [uninit_use_in_call]
114  		host_state_t hinfo;
115  		struct timeval tv;
116  		int ret;
117  	
(2) Event cond_false: Condition "arg->map->check(arg->map->info, arg->src, vm_uuid, vm_name) == 0", taking false branch.
118  		if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
119  			/* if we don't have access to fence this VM,
120  			 * we should not see it in a hostlist either */
121  			return 0;
(3) Event if_end: End of if statement.
122  		}
123  	
124  		strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
125  		strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
126  		hinfo.state = state;
127  	
128  		tv.tv_sec = 1;
129  		tv.tv_usec = 0;
(4) Event uninit_use_in_call: Using uninitialized value "hinfo". Field "hinfo.pad" is uninitialized when calling "_write_retry". [details]
Also see events: [var_decl]
130  		ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
131  		if (ret == sizeof(hinfo))
132  			return 0;
133  		return 1;
134  	}
135  	
136  	
137  	static int
138  	tcp_hostlist_begin(int fd)
139  	{
140  		struct timeval tv;
141  		char val = (char)RESP_HOSTLIST;
142  	
143  		tv.tv_sec = 1;
144  		tv.tv_usec = 0;
145  		return _write_retry(fd, &val, 1, &tv);
146  	}
147  	
148  	
149  	static int 
150  	tcp_hostlist_end(int fd)
151  	{
152  		host_state_t hinfo;
153  		struct timeval tv;
154  		int ret;
155  	
156  		printf("Sending terminator packet\n");
157  	
158  		memset(&hinfo, 0, sizeof(hinfo));
159  	
160  		tv.tv_sec = 1;
161  		tv.tv_usec = 0;
162  		ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
163  		if (ret == sizeof(hinfo))
164  			return 0;
165  		return 1;
166  	}
167  	
168  	static socklen_t
169  	sockaddr_len(const struct sockaddr_storage *ss)
170  	{
171  		if (ss->ss_family == AF_INET) {
172  			return sizeof(struct sockaddr_in);
173  		} else {
174  			return sizeof(struct sockaddr_in6);
175  		}
176  	}
177  	
178  	static int
179  	do_fence_request_tcp(int fd, struct sockaddr_storage *ss, socklen_t sock_len, fence_req_t *req, tcp_info *info)
180  	{
181  		char ip_addr_src[1024];
182  		char response = 1;
183  		struct tcp_hostlist_arg arg;
184  		int ret;
185  	
186  		/* Noops if auth == AUTH_NONE */
187  		if (sock_response(fd, info->args.auth, info->key, info->key_len, 10) <= 0) {
188  			printf("Failed to respond to challenge\n");
189  			close(fd);
190  			return -1;
191  		}
192  	
193  		ret = sock_challenge(fd, info->args.auth, info->key, info->key_len, 10);
194  		if (ret <= 0) {
195  			printf("Remote failed challenge\n");
196  			close(fd);
197  			return -1;
198  		}
199  	
200  	
201  		if (getnameinfo((struct sockaddr *)ss, sockaddr_len(ss),
202  				ip_addr_src, sizeof(ip_addr_src),
203  				NULL, 0,
204  				NI_NUMERICHOST | NI_NUMERICSERV) < 0) {
205  			printf("Unable to resolve!\n");
206  			close(fd);
207  			return -1;
208  		}
209  	
210  		dbg_printf(2, "Request %d seqno %d src %s target %s\n",
211  			   req->request, req->seqno, ip_addr_src, req->domain);
212  	
213  		switch(req->request) {
214  		case FENCE_NULL:
215  			response = info->cb->null((char *)req->domain, info->priv);
216  			break;
217  		case FENCE_ON:
218  			if (map_check(info->map, ip_addr_src,
219  					     (const char *)req->domain) == 0) {
220  				response = RESP_PERM;
221  				break;
222  			}
223  			response = info->cb->on((char *)req->domain, ip_addr_src,
224  						req->seqno, info->priv);
225  			break;
226  		case FENCE_OFF:
227  			if (map_check(info->map, ip_addr_src,
228  					     (const char *)req->domain) == 0) {
229  				response = RESP_PERM;
230  				break;
231  			}
232  			response = info->cb->off((char *)req->domain, ip_addr_src,
233  						 req->seqno, info->priv);
234  			break;
235  		case FENCE_REBOOT:
236  			if (map_check(info->map, ip_addr_src,
237  					     (const char *)req->domain) == 0) {
238  				response = RESP_PERM;
239  				break;
240  			}
241  			response = info->cb->reboot((char *)req->domain, ip_addr_src,
242  						    req->seqno, info->priv);
243  			break;
244  		case FENCE_STATUS:
245  			if (map_check(info->map, ip_addr_src,
246  					     (const char *)req->domain) == 0) {
247  				response = RESP_PERM;
248  				break;
249  			}
250  			response = info->cb->status((char *)req->domain, info->priv);
251  			break;
252  		case FENCE_DEVSTATUS:
253  			response = info->cb->devstatus(info->priv);
254  			break;
255  		case FENCE_HOSTLIST:
256  			arg.map = info->map;
257  			arg.src = ip_addr_src;
258  			arg.fd = fd;
259  	
260  			tcp_hostlist_begin(arg.fd);
261  			response = info->cb->hostlist(tcp_hostlist, &arg,
262  						      info->priv);
263  			tcp_hostlist_end(arg.fd);
264  			break;
265  		}
266  	
267  		dbg_printf(3, "Sending response to caller...\n");
268  		if (_write_retry(fd, &response, 1, NULL) < 0) {
269  			perror("write");
270  		}
271  	
272  		history_record(info->history, req);
273  	
274  		if (fd != -1)
275  			close(fd);
276  	
277  		return 1;
278  	}
279  	
280  	
281  	static int
282  	tcp_dispatch(listener_context_t c, struct timeval *timeout)
283  	{
284  		tcp_info *info;
285  		fence_req_t data;
286  		fd_set rfds;
287  		int n;
288  		int client_fd;
289  	    int ret;
290  		struct timeval tv;
291  		struct sockaddr_storage ss;
292  		socklen_t sock_len = sizeof(ss);
293  	
294  	    if (timeout != NULL)
295  	    	memcpy(&tv, timeout, sizeof(tv));
296  	    else {
297  	        tv.tv_sec = 1;
298  	        tv.tv_usec = 0;
299  	    }
300  	
301  		info = (tcp_info *)c;
302  		VALIDATE(info);
303  	
304  		FD_ZERO(&rfds);
305  		FD_SET(info->listen_sock, &rfds);
306  	
307  		n = select(info->listen_sock + 1, &rfds, NULL, NULL, timeout);
308  		if (n <= 0) {
309  			if (errno == EINTR || errno == EAGAIN)
310  				n = 0;
311  			else
312  				dbg_printf(2, "select: %s\n", strerror(errno));
313  			return n;
314  		}
315  		
316  		client_fd = accept(info->listen_sock, (struct sockaddr *)&ss, &sock_len);
317  		if (client_fd < 0) {
318  			perror("accept");
319  			return -1;
320  		}
321  	
322  		dbg_printf(3, "Accepted client...\n");
323  	
324  		ret = _read_retry(client_fd, &data, sizeof(data), &tv);
325  		if (ret != sizeof(data)) {
326  			dbg_printf(3, "Invalid request (read %d bytes)\n", ret);
327  			close(client_fd);
328  			return 0;
329  		}
330  	
331  		swab_fence_req_t(&data);
332  	
333  		if (!verify_request(&data, info->args.hash, info->key,
334  				    info->key_len)) {
335  			printf("Key mismatch; dropping client\n");
336  	        close(client_fd);
337  			return 0;
338  		}
339  	
340  		dbg_printf(3, "Request %d seqno %d domain %s\n",
341  			data.request, data.seqno, data.domain);
342  	
343  		if (history_check(info->history, &data) == 1) {
344  			printf("We just did this request; dropping client\n");
345  	        close(client_fd);
346  			return 0;
347  		}
348  			
349  		switch(info->args.auth) {
350  		case AUTH_NONE:
351  		case AUTH_SHA1:
352  		case AUTH_SHA256:
353  		case AUTH_SHA512:
354  			printf("Plain TCP request\n");
355  			do_fence_request_tcp(client_fd, &ss, sock_len, &data, info);
356  			break;
357  		default:
358  			printf("XXX Unhandled authentication\n");
359  		}
360  	
361  		return 0;
362  	}
363  	
364  	
365  	static int
366  	tcp_config(config_object_t *config, tcp_options *args)
367  	{
368  		char value[1024];
369  		int errors = 0;
370  	
371  		if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
372  			dset(atoi(value));
373  	
374  		if (sc_get(config, "listeners/tcp/@key_file",
375  			   value, sizeof(value)-1) == 0) {
376  			dbg_printf(1, "Got %s for key_file\n", value);
377  			args->key_file = strdup(value);
378  		} else {
379  			args->key_file = strdup(DEFAULT_KEY_FILE);
380  			if (!args->key_file) {
381  				dbg_printf(1, "Failed to allocate memory\n");
382  				return -1;
383  			}
384  		}
385  	
386  		args->hash = DEFAULT_HASH;
387  		if (sc_get(config, "listeners/tcp/@hash",
388  			   value, sizeof(value)-1) == 0) {
389  			dbg_printf(1, "Got %s for hash\n", value);
390  			if (!strcasecmp(value, "none")) {
391  				args->hash = HASH_NONE;
392  			} else if (!strcasecmp(value, "sha1")) {
393  				args->hash = HASH_SHA1;
394  			} else if (!strcasecmp(value, "sha256")) {
395  				args->hash = HASH_SHA256;
396  			} else if (!strcasecmp(value, "sha512")) {
397  				args->hash = HASH_SHA512;
398  			} else {
399  				dbg_printf(1, "Unsupported hash: %s\n", value);
400  				++errors;
401  			}
402  		}
403  		
404  		args->auth = DEFAULT_AUTH;
405  		if (sc_get(config, "listeners/tcp/@auth",
406  			   value, sizeof(value)-1) == 0) {
407  			dbg_printf(1, "Got %s for auth\n", value);
408  			if (!strcasecmp(value, "none")) {
409  				args->hash = AUTH_NONE;
410  			} else if (!strcasecmp(value, "sha1")) {
411  				args->hash = AUTH_SHA1;
412  			} else if (!strcasecmp(value, "sha256")) {
413  				args->hash = AUTH_SHA256;
414  			} else if (!strcasecmp(value, "sha512")) {
415  				args->hash = AUTH_SHA512;
416  			} else {
417  				dbg_printf(1, "Unsupported auth: %s\n", value);
418  				++errors;
419  			}
420  		}
421  		
422  		args->family = PF_INET;
423  		if (sc_get(config, "listeners/tcp/@family",
424  			   value, sizeof(value)-1) == 0) {
425  			dbg_printf(1, "Got %s for family\n", value);
426  			if (!strcasecmp(value, "ipv4")) {
427  				args->family = PF_INET;
428  			} else if (!strcasecmp(value, "ipv6")) {
429  				args->family = PF_INET6;
430  			} else {
431  				dbg_printf(1, "Unsupported family: %s\n", value);
432  				++errors;
433  			}
434  		}
435  	
436  		if (sc_get(config, "listeners/tcp/@address",
437  			   value, sizeof(value)-1) == 0) {
438  			dbg_printf(1, "Got %s for address\n", value);
439  			args->addr = strdup(value);
440  		} else {
441  			if (args->family == PF_INET) {
442  				args->addr = strdup(IPV4_TCP_ADDR_DEFAULT);
443  			} else {
444  				args->addr = strdup(IPV6_TCP_ADDR_DEFAULT);
445  			}
446  		}
447  		if (!args->addr) {
448  			return -1;
449  		}
450  	
451  		args->port = DEFAULT_MCAST_PORT;
452  		if (sc_get(config, "listeners/tcp/@port",
453  			   value, sizeof(value)-1) == 0) {
454  			dbg_printf(1, "Got %s for port\n", value);
455  			args->port = atoi(value);
456  			if (args->port <= 0) {
457  				dbg_printf(1, "Invalid port: %s\n", value);
458  				++errors;
459  			}
460  		}
461  	
462  		return errors;
463  	}
464  	
465  	
466  	static int
467  	tcp_init(listener_context_t *c, const fence_callbacks_t *cb,
468  		   config_object_t *config, map_object_t *map, void *priv)
469  	{
470  		tcp_info *info;
471  		int listen_sock, ret;
472  	
473  		/* Initialize NSS; required to do hashing, as silly as that
474  		   sounds... */
475  		if (NSS_NoDB_Init(NULL) != SECSuccess) {
476  			printf("Could not initialize NSS\n");
477  			return 1;
478  		}
479  	
480  		info = calloc(1, sizeof(*info));
481  		if (!info)
482  			return -1;
483  	
484  		info->priv = priv;
485  		info->cb = cb;
486  		info->map = map;
487  	
488  		ret = tcp_config(config, &info->args);
489  		if (ret < 0)
490  			perror("tcp_config");
491  		else if (ret > 0)
492  			printf("%d errors found during configuration\n",ret);
493  	
494  	    if (ret != 0) {
495  			if (info->args.key_file)
496  				free(info->args.key_file);
497  			if (info->args.addr)
498  				free(info->args.addr);
499  			free(info);
500  			return -1;
501  		}
502  	
503  		if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
504  			info->key_len = read_key_file(info->args.key_file,
505  						info->key, sizeof(info->key));
506  			if (info->key_len < 0) {
507  				printf("Could not read %s; operating without "
508  				       "authentication\n", info->args.key_file);
509  				info->args.auth = AUTH_NONE;
510  				info->args.hash = HASH_NONE;
511  				info->key_len = 0;
512  			}
513  		}
514  	
515  		if (info->args.family == PF_INET) {
516  			listen_sock = ipv4_listen(info->args.addr, info->args.port, 10);
517  		} else {
518  			listen_sock = ipv6_listen(info->args.addr, info->args.port, 10);
519  		}
520  	
521  		if (listen_sock < 0) {
522  			printf("Could not set up listen socket\n");
523  			if (info->args.key_file)
524  				free(info->args.key_file);
525  			if (info->args.addr)
526  				free(info->args.addr);
527  			free(info);
528  			return -1;
529  		}
530  	
531  		info->magic = TCP_MAGIC;
532  		info->listen_sock = listen_sock;
533  		info->history = history_init(check_history, 10, sizeof(fence_req_t));
534  		*c = (listener_context_t)info;
535  		return 0;
536  	}
537  	
538  	
539  	static int
540  	tcp_shutdown(listener_context_t c)
541  	{
542  		tcp_info *info = (tcp_info *)c;
543  	
544  		VALIDATE(info);
545  		info->magic = 0;
546  		history_wipe(info->history);
547  		free(info->history);
548  		free(info->args.key_file);
549  		free(info->args.addr);
550  		close(info->listen_sock);
551  		free(info);
552  	
553  		return 0;
554  	}
555  	
556  	
557  	static listener_plugin_t tcp_plugin = {
558  		.name = NAME,
559  		.version = TCP_VERSION,
560  		.init = tcp_init,
561  		.dispatch = tcp_dispatch,
562  		.cleanup = tcp_shutdown,
563  	};
564  	
565  	double
566  	LISTENER_VER_SYM(void)
567  	{
568  		return PLUGIN_VERSION_LISTENER;
569  	}
570  	
571  	const listener_plugin_t *
572  	LISTENER_INFO_SYM(void)
573  	{
574  		return &tcp_plugin;
575  	}
576