1    	/*
2    	  Copyright Red Hat, Inc. 2006
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   	 * Author: Lon Hohberger <lhh at redhat.com>
21   	 */
22   	
23   	#include "config.h"
24   	
25   	#include <stdio.h>
26   	#include <stdlib.h>
27   	#include <string.h>
28   	#include <unistd.h>
29   	#include <signal.h>
30   	#include <string.h>
31   	#include <sys/types.h>
32   	#include <sys/stat.h>
33   	#include <sys/wait.h>
34   	#include <sys/un.h>
35   	#include <sys/socket.h>
36   	#include <sys/select.h>
37   	#include <sys/ioctl.h>
38   	#include <arpa/inet.h>
39   	#include <net/if.h>
40   	#include <netinet/in.h>
41   	#include <netdb.h>
42   	#include <sys/time.h>
43   	#include <fcntl.h>
44   	#include <errno.h>
45   	#include <pthread.h>
46   	#include <nss.h>
47   	#include <libgen.h>
48   	
49   	/* Local includes */
50   	#include "xvm.h"
51   	#include "simple_auth.h"
52   	#include "options.h"
53   	#include "mcast.h"
54   	#include "tcp.h"
55   	#include "debug.h"
56   	#include "fdops.h"
57   	#include "list.h"
58   	#include "simpleconfig.h"
59   	#include "static_map.h"
60   	#include "server_plugin.h"
61   	#include "history.h"
62   	
63   	#define NAME "multicast"
64   	#define MCAST_VERSION "1.3"
65   	
66   	#define MCAST_MAGIC 0xabb911a3
67   	
68   	#define VALIDATE(info) \
69   	do {\
70   		if (!info || info->magic != MCAST_MAGIC)\
71   			return -EINVAL;\
72   	} while(0)
73   	
74   	typedef struct _mcast_options {
75   		char *addr;
76   		char *key_file;
77   		int ifindex;
78   		int family;
79   		unsigned int port;
80   		unsigned int hash;
81   		unsigned int auth;
82   		unsigned int flags;
83   	} mcast_options;
84   	
85   	
86   	typedef struct _mcast_info {
87   		uint64_t magic;
88   		void *priv;
89   		map_object_t *map;
90   		history_info_t *history;
91   		char key[MAX_KEY_LEN];
92   		mcast_options args;
93   		const fence_callbacks_t *cb;
94   		ssize_t key_len;
95   		int mc_sock;
96   		int need_kill;
97   	} mcast_info;
98   	
99   	
100  	struct mcast_hostlist_arg {
101  		map_object_t *map;
102  		const char *src;
103  		int fd;
104  	};
105  	
106  	
107  	/*
108  	 * See if we fenced this node recently (successfully)
109  	 * If so, ignore the request for a few seconds.
110  	 *
111  	 * We purge our history when the entries time out.
112  	 */
113  	static int
114  	check_history(void *a, void *b) {
115  		fence_req_t *old = a, *current = b;
116  	
117  		if (old->request == current->request &&
118  		    old->seqno == current->seqno &&
119  		    !strcasecmp((const char *)old->domain,
120  				(const char *)current->domain)) {
121  			return 1;
122  		}
123  		return 0;
124  	}
125  	
126  	
127  	static int
128  	connect_tcp(fence_req_t *req, fence_auth_type_t auth,
129  		    void *key, size_t key_len)
130  	{
131  		int fd = -1;
132  		struct sockaddr_in sin;
133  		struct sockaddr_in6 sin6;
134  		char buf[128];
135  	
136  		switch(req->family) {
137  		case PF_INET:
138  			memset(&sin, 0, sizeof(sin));
139  			memcpy(&sin.sin_addr, req->address,
140  			       sizeof(sin.sin_addr));
141  			sin.sin_family = PF_INET;
142  			fd = ipv4_connect(&sin.sin_addr, req->port,
143  					  5);
144  			if (fd < 0) {
145  				printf("Failed to call back\n");
146  				return -1;
147  			}
148  			break;
149  		case PF_INET6:
150  			memset(&sin6, 0, sizeof(sin6));
151  			memcpy(&sin6.sin6_addr, req->address,
152  			       sizeof(sin6.sin6_addr));
153  			sin.sin_family = PF_INET6;
154  			fd = ipv6_connect(&sin6.sin6_addr, req->port,
155  					  5);
156  	
157  			memset(buf,0,sizeof(buf));
158  			inet_ntop(PF_INET6, &sin6.sin6_addr, buf, sizeof(buf));
159  	
160  			if (fd < 0) {
161  				printf("Failed to call back %s\n", buf);
162  				return -1;
163  			}
164  			break;
165  		default:
166  			printf("Family = %d\n", req->family);
167  			return -1;
168  		}
169  	
170  		/* Noops if auth == AUTH_NONE */
171  		if (sock_response(fd, auth, key, key_len, 10) <= 0) {
172  			printf("Failed to respond to challenge\n");
173  			close(fd);
174  			return -1;
175  		}
176  	
177  		if (sock_challenge(fd, auth, key, key_len, 10) <= 0) {
178  			printf("Remote failed challenge\n");
179  			close(fd);
180  			return -1;
181  		}
182  		return fd;
183  	}
184  	
185  	
186  	static int 
187  	mcast_hostlist(const char *vm_name, const char *vm_uuid,
188  		       int state, void *priv)
189  	{
190  		struct mcast_hostlist_arg *arg = (struct mcast_hostlist_arg *)priv;
(1) Event var_decl: Declaring variable "hinfo" without initializer.
Also see events: [uninit_use_in_call]
191  		host_state_t hinfo;
192  		struct timeval tv;
193  		int ret;
194  	
(2) Event cond_false: Condition "arg->map->check(arg->map->info, arg->src, vm_uuid, vm_name) == 0", taking false branch.
195  		if (map_check2(arg->map, arg->src, vm_uuid, vm_name) == 0) {
196  			/* if we don't have access to fence this VM,
197  			 * we should not see it in a hostlist either */
198  			return 0;
(3) Event if_end: End of if statement.
199  		}
200  	
201  		strncpy((char *)hinfo.domain, vm_name, sizeof(hinfo.domain) - 1);
202  		strncpy((char *)hinfo.uuid, vm_uuid, sizeof(hinfo.uuid) - 1);
203  		hinfo.state = state;
204  	
205  		tv.tv_sec = 1;
206  		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]
207  		ret = _write_retry(arg->fd, &hinfo, sizeof(hinfo), &tv);
208  		if (ret == sizeof(hinfo))
209  			return 0;
210  		return 1;
211  	}
212  	
213  	
214  	static int
215  	mcast_hostlist_begin(int fd)
216  	{
217  		struct timeval tv;
218  		char val = (char)RESP_HOSTLIST;
219  	
220  		tv.tv_sec = 1;
221  		tv.tv_usec = 0;
222  		return _write_retry(fd, &val, 1, &tv);
223  	}
224  	
225  	
226  	static int 
227  	mcast_hostlist_end(int fd)
228  	{
229  		host_state_t hinfo;
230  		struct timeval tv;
231  		int ret;
232  	
233  		printf("Sending terminator packet\n");
234  	
235  		memset(&hinfo, 0, sizeof(hinfo));
236  	
237  		tv.tv_sec = 1;
238  		tv.tv_usec = 0;
239  		ret = _write_retry(fd, &hinfo, sizeof(hinfo), &tv);
240  		if (ret == sizeof(hinfo))
241  			return 0;
242  		return 1;
243  	}
244  	
245  	
246  	static int
247  	do_fence_request_tcp(fence_req_t *req, mcast_info *info)
248  	{
249  		char ip_addr_src[1024];
250  		int fd = -1;
251  		char response = 1;
252  		struct mcast_hostlist_arg arg;
253  	
254  		fd = connect_tcp(req, info->args.auth, info->key, info->key_len);
255  		if (fd < 0) {
256  			dbg_printf(2, "Could not send reply to fence request: %s\n",
257  				strerror(errno));
258  			goto out;
259  		}
260  	
261  		inet_ntop(req->family, req->address,
262  			  ip_addr_src, sizeof(ip_addr_src));
263  	
264  		dbg_printf(2, "Request %d seqno %d src %s target %s\n", 
265  			   req->request, req->seqno, ip_addr_src, req->domain);
266  	
267  		switch(req->request) {
268  		case FENCE_NULL:
269  			response = info->cb->null((char *)req->domain, info->priv);
270  			break;
271  		case FENCE_ON:
272  			if (map_check(info->map, ip_addr_src,
273  					     (const char *)req->domain) == 0) {
274  				response = RESP_PERM;
275  				break;
276  			}
277  			response = info->cb->on((char *)req->domain, ip_addr_src,
278  						req->seqno, info->priv);
279  			break;
280  		case FENCE_OFF:
281  			if (map_check(info->map, ip_addr_src,
282  					     (const char *)req->domain) == 0) {
283  				response = RESP_PERM;
284  				break;
285  			}
286  			response = info->cb->off((char *)req->domain, ip_addr_src,
287  						 req->seqno, info->priv);
288  			break;
289  		case FENCE_REBOOT:
290  			if (map_check(info->map, ip_addr_src,
291  					     (const char *)req->domain) == 0) {
292  				response = RESP_PERM;
293  				break;
294  			}
295  			response = info->cb->reboot((char *)req->domain, ip_addr_src,
296  						    req->seqno, info->priv);
297  			break;
298  		case FENCE_STATUS:
299  			if (map_check(info->map, ip_addr_src,
300  					     (const char *)req->domain) == 0) {
301  				response = RESP_PERM;
302  				break;
303  			}
304  			response = info->cb->status((char *)req->domain, info->priv);
305  			break;
306  		case FENCE_DEVSTATUS:
307  			response = info->cb->devstatus(info->priv);
308  			break;
309  		case FENCE_HOSTLIST:
310  			arg.map = info->map;
311  			arg.src = ip_addr_src;
312  			arg.fd = fd;
313  	
314  			mcast_hostlist_begin(arg.fd);
315  			response = info->cb->hostlist(mcast_hostlist, &arg,
316  						      info->priv);
317  			mcast_hostlist_end(arg.fd);
318  			break;
319  		}
320  	
321  		dbg_printf(3, "Sending response to caller...\n");
322  		if (_write_retry(fd, &response, 1, NULL) < 0) {
323  			perror("write");
324  		}
325  	
326  		/* XVM shotguns multicast packets, so we want to avoid 
327  		 * acting on the same request multiple times if the first
328  		 * attempt was successful.
329  		 */
330  		history_record(info->history, req);
331  	out:
332  		if (fd != -1)
333  			close(fd);
334  	
335  		return 1;
336  	}
337  	
338  	
339  	static int
340  	mcast_dispatch(listener_context_t c, struct timeval *timeout)
341  	{
342  		mcast_info *info;
343  		fence_req_t data;
344  		fd_set rfds;
345  		struct sockaddr_in sin;
346  		int len;
347  		int n;
348  		socklen_t slen;
349  	
350  		info = (mcast_info *)c;
351  		VALIDATE(info);
352  	
353  		FD_ZERO(&rfds);
354  		FD_SET(info->mc_sock, &rfds);
355  	
356  		n = select((info->mc_sock)+1, &rfds, NULL, NULL, timeout);
357  		if (n <= 0) {
358  			if (errno == EINTR || errno == EAGAIN)
359  				n = 0;
360  			else
361  				dbg_printf(2, "select: %s\n", strerror(errno));
362  			return n;
363  		}
364  		
365  		slen = sizeof(sin);
366  		len = recvfrom(info->mc_sock, &data, sizeof(data), 0,
367  			       (struct sockaddr *)&sin, &slen);
368  			
369  		if (len <= 0) {
370  			perror("recvfrom");
371  			return len;
372  		}
373  	
374  		swab_fence_req_t(&data);
375  	
376  		if (!verify_request(&data, info->args.hash, info->key,
377  				    info->key_len)) {
378  			printf("Key mismatch; dropping packet\n");
379  			return 0;
380  		}
381  	
382  		printf("Request %d seqno %d domain %s\n", data.request, data.seqno,
383  		       data.domain);
384  	
385  		if (history_check(info->history, &data) == 1) {
386  			printf("We just did this request; dropping packet\n");
387  			return 0;
388  		}
389  			
390  		switch(info->args.auth) {
391  		case AUTH_NONE:
392  		case AUTH_SHA1:
393  		case AUTH_SHA256:
394  		case AUTH_SHA512:
395  			printf("Plain TCP request\n");
396  			do_fence_request_tcp(&data, info);
397  			break;
398  		default:
399  			printf("XXX Unhandled authentication\n");
400  		}
401  	
402  		return 0;
403  	}
404  	
405  	
406  	static int
407  	mcast_config(config_object_t *config, mcast_options *args)
408  	{
409  		char value[1024];
410  		int errors = 0;
411  	
412  		if (sc_get(config, "fence_virtd/@debug", value, sizeof(value))==0)
413  			dset(atoi(value));
414  	
415  		if (sc_get(config, "listeners/multicast/@key_file",
416  			   value, sizeof(value)-1) == 0) {
417  			dbg_printf(1, "Got %s for key_file\n", value);
418  			args->key_file = strdup(value);
419  		} else {
420  			args->key_file = strdup(DEFAULT_KEY_FILE);
421  			if (!args->key_file) {
422  				dbg_printf(1, "Failed to allocate memory\n");
423  				return -1;
424  			}
425  		}
426  	
427  		args->hash = DEFAULT_HASH;
428  		if (sc_get(config, "listeners/multicast/@hash",
429  			   value, sizeof(value)-1) == 0) {
430  			dbg_printf(1, "Got %s for hash\n", value);
431  			if (!strcasecmp(value, "none")) {
432  				args->hash = HASH_NONE;
433  			} else if (!strcasecmp(value, "sha1")) {
434  				args->hash = HASH_SHA1;
435  			} else if (!strcasecmp(value, "sha256")) {
436  				args->hash = HASH_SHA256;
437  			} else if (!strcasecmp(value, "sha512")) {
438  				args->hash = HASH_SHA512;
439  			} else {
440  				dbg_printf(1, "Unsupported hash: %s\n", value);
441  				++errors;
442  			}
443  		}
444  		
445  		args->auth = DEFAULT_AUTH;
446  		if (sc_get(config, "listeners/multicast/@auth",
447  			   value, sizeof(value)-1) == 0) {
448  			dbg_printf(1, "Got %s for auth\n", value);
449  			if (!strcasecmp(value, "none")) {
450  				args->auth = AUTH_NONE;
451  			} else if (!strcasecmp(value, "sha1")) {
452  				args->auth = AUTH_SHA1;
453  			} else if (!strcasecmp(value, "sha256")) {
454  				args->auth = AUTH_SHA256;
455  			} else if (!strcasecmp(value, "sha512")) {
456  				args->auth = AUTH_SHA512;
457  			} else {
458  				dbg_printf(1, "Unsupported auth: %s\n", value);
459  				++errors;
460  			}
461  		}
462  		
463  		args->family = PF_INET;
464  		if (sc_get(config, "listeners/multicast/@family",
465  			   value, sizeof(value)-1) == 0) {
466  			dbg_printf(1, "Got %s for family\n", value);
467  			if (!strcasecmp(value, "ipv4")) {
468  				args->family = PF_INET;
469  			} else if (!strcasecmp(value, "ipv6")) {
470  				args->family = PF_INET6;
471  			} else {
472  				dbg_printf(1, "Unsupported family: %s\n", value);
473  				++errors;
474  			}
475  		}
476  	
477  		if (sc_get(config, "listeners/multicast/@address",
478  			   value, sizeof(value)-1) == 0) {
479  			dbg_printf(1, "Got %s for address\n", value);
480  			args->addr = strdup(value);
481  		} else {
482  			if (args->family == PF_INET) {
483  				args->addr = strdup(IPV4_MCAST_DEFAULT);
484  			} else {
485  				args->addr = strdup(IPV6_MCAST_DEFAULT);
486  			}
487  		}
488  		if (!args->addr) {
489  			return -1;
490  		}
491  	
492  		args->port = DEFAULT_MCAST_PORT;
493  		if (sc_get(config, "listeners/multicast/@port",
494  			   value, sizeof(value)-1) == 0) {
495  			dbg_printf(1, "Got %s for port\n", value);
496  			args->port = atoi(value);
497  			if (args->port <= 0) {
498  				dbg_printf(1, "Invalid port: %s\n", value);
499  				++errors;
500  			}
501  		}
502  	
503  		args->ifindex = 0;
504  		if (sc_get(config, "listeners/multicast/@interface",
505  			   value, sizeof(value)-1) == 0) {
506  			dbg_printf(1, "Got %s for interface\n", value);
507  			args->ifindex = if_nametoindex(value);
508  			if (args->ifindex < 0) {
509  				dbg_printf(1, "Invalid interface: %s\n", value);
510  				++errors;
511  			}
512  		}
513  	
514  		return errors;
515  	}
516  	
517  	
518  	static int
519  	mcast_init(listener_context_t *c, const fence_callbacks_t *cb,
520  		   config_object_t *config, map_object_t *map, void *priv)
521  	{
522  		mcast_info *info;
523  		int mc_sock, ret;
524  	
525  		/* Initialize NSS; required to do hashing, as silly as that
526  		   sounds... */
527  		if (NSS_NoDB_Init(NULL) != SECSuccess) {
528  			printf("Could not initialize NSS\n");
529  			return 1;
530  		}
531  	
532  		info = malloc(sizeof(*info));
533  		if (!info)
534  			return -1;
535  		memset(info, 0, sizeof(*info));
536  	
537  		info->priv = priv;
538  		info->cb = cb;
539  		info->map = map;
540  	
541  		ret = mcast_config(config, &info->args);
542  		if (ret < 0) {
543  			perror("mcast_config");
544  			free(info);
545  			return -1;
546  		} else if (ret > 0) {
547  			printf("%d errors found during configuration\n",ret);
548  			free(info);
549  			return -1;
550  		}
551  	
552  		if (info->args.auth != AUTH_NONE || info->args.hash != HASH_NONE) {
553  			info->key_len = read_key_file(info->args.key_file,
554  						info->key, sizeof(info->key));
555  			if (info->key_len < 0) {
556  				printf("Could not read %s; operating without "
557  				       "authentication\n", info->args.key_file);
558  				info->args.auth = AUTH_NONE;
559  				info->args.hash = HASH_NONE;
560  				info->key_len = 0;
561  			}
562  		}
563  	
564  		if (info->args.family == PF_INET)
565  			mc_sock = ipv4_recv_sk(info->args.addr,
566  					       info->args.port,
567  					       info->args.ifindex);
568  		else
569  			mc_sock = ipv6_recv_sk(info->args.addr,
570  					       info->args.port,
571  					       info->args.ifindex);
572  		if (mc_sock < 0) {
573  			printf("Could not set up multicast listen socket\n");
574  			free(info);
575  			return -1;
576  		}
577  	
578  		info->magic = MCAST_MAGIC;
579  		info->mc_sock = mc_sock;
580  		info->history = history_init(check_history, 10, sizeof(fence_req_t));
581  		*c = (listener_context_t)info;
582  		return 0;
583  	}
584  	
585  	
586  	static int
587  	mcast_shutdown(listener_context_t c)
588  	{
589  		mcast_info *info = (mcast_info *)c;
590  	
591  		VALIDATE(info);
592  		info->magic = 0;
593  		history_wipe(info->history);
594  		free(info->history);
595  		free(info->args.key_file);
596  		free(info->args.addr);
597  		close(info->mc_sock);
598  		free(info);
599  	
600  		return 0;
601  	}
602  	
603  	
604  	static listener_plugin_t mcast_plugin = {
605  		.name = NAME,
606  		.version = MCAST_VERSION,
607  		.init = mcast_init,
608  		.dispatch = mcast_dispatch,
609  		.cleanup = mcast_shutdown,
610  	};
611  	
612  	double
613  	LISTENER_VER_SYM(void)
614  	{
615  		return PLUGIN_VERSION_LISTENER;
616  	}
617  	
618  	const listener_plugin_t *
619  	LISTENER_INFO_SYM(void)
620  	{
621  		return &mcast_plugin;
622  	}
623