2 * Copyright (C) 2002-2003 Sistina Software, Inc. All rights reserved.
3 * Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
5 * This file is part of LVM2.
7 * This copyrighted material is made available to anyone wishing to use,
8 * modify, copy, or redistribute it subject to the terms and conditions
9 * of the GNU Lesser General Public License v.2.1.
11 * You should have received a copy of the GNU Lesser General Public License
12 * along with this program; if not, write to the Free Software Foundation,
13 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 * This provides the inter-clvmd communications for a system without CMAN.
18 * There is a listening TCP socket which accepts new connections in the
20 * It can also make outgoing connnections to the other clvmd nodes.
23 #include "clvmd-common.h"
26 #include <sys/utsname.h>
27 #include <sys/ioctl.h>
28 #include <sys/socket.h>
29 #include <sys/socket.h>
30 #include <netinet/in.h>
41 #include "clvmd-comms.h"
43 #include "clvmd-gulm.h"
45 #define DEFAULT_TCP_PORT 21064
47 static int listen_fd = -1;
49 struct dm_hash_table *sock_hash;
51 static int get_our_ip_address(char *addr, int *family);
52 static int read_from_tcpsock(struct local_client *fd, char *buf, int len, char *csid,
53 struct local_client **new_client);
55 /* Called by init_cluster() to open up the listening socket */
56 int init_comms(unsigned short port)
58 struct sockaddr_in6 addr;
60 sock_hash = dm_hash_create(100);
61 tcp_port = port ? : DEFAULT_TCP_PORT;
63 listen_fd = socket(AF_INET6, SOCK_STREAM, 0);
72 setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
73 setsockopt(listen_fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int));
76 memset(&addr, 0, sizeof(addr)); // Bind to INADDR_ANY
77 addr.sin6_family = AF_INET6;
78 addr.sin6_port = htons(tcp_port);
80 if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
82 DEBUGLOG("Can't bind to port: %s\n", strerror(errno));
83 syslog(LOG_ERR, "Can't bind to port %d, is clvmd already running ?", tcp_port);
90 /* Set Close-on-exec */
91 fcntl(listen_fd, F_SETFD, 1);
96 void tcp_remove_client(const char *c_csid)
98 struct local_client *client;
99 char csid[GULM_MAX_CSID_LEN];
101 memcpy(csid, c_csid, sizeof csid);
102 DEBUGLOG("tcp_remove_client\n");
104 /* Don't actually close the socket here - that's the
105 job of clvmd.c whch will do the job when it notices the
106 other end has gone. We just need to remove the client(s) from
107 the hash table so we don't try to use it for sending any more */
108 for (i = 0; i < 2; i++)
110 client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
113 dm_hash_remove_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
114 client->removeme = 1;
117 /* Look for a mangled one too, on the 2nd iteration. */
122 int alloc_client(int fd, const char *c_csid, struct local_client **new_client)
124 struct local_client *client;
125 char csid[GULM_MAX_CSID_LEN];
126 memcpy(csid, c_csid, sizeof csid);
128 DEBUGLOG("alloc_client %d csid = %s\n", fd, print_csid(csid));
130 /* Create a local_client and return it */
131 client = malloc(sizeof(struct local_client));
134 DEBUGLOG("malloc failed\n");
138 memset(client, 0, sizeof(struct local_client));
140 client->type = CLUSTER_DATA_SOCK;
141 client->callback = read_from_tcpsock;
143 *new_client = client;
145 /* Add to our list of node sockets */
146 if (dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN))
148 DEBUGLOG("alloc_client mangling CSID for second connection\n");
149 /* This is a duplicate connection but we can't close it because
150 the other end may already have started sending.
151 So, we mangle the IP address and keep it, all sending will
152 go out of the main FD
155 client->bits.net.flags = 1; /* indicate mangled CSID */
157 /* If it still exists then kill the connection as we should only
158 ever have one incoming connection from each node */
159 if (dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN))
161 DEBUGLOG("Multiple incoming connections from node\n");
162 syslog(LOG_ERR, " Bogus incoming connection from %d.%d.%d.%d\n", csid[0],csid[1],csid[2],csid[3]);
165 errno = ECONNREFUSED;
169 dm_hash_insert_binary(sock_hash, csid, GULM_MAX_CSID_LEN, client);
174 int get_main_gulm_cluster_fd()
180 /* Read on main comms (listen) socket, accept it */
181 int cluster_fd_gulm_callback(struct local_client *fd, char *buf, int len, const char *csid,
182 struct local_client **new_client)
185 struct sockaddr_in6 addr;
186 socklen_t addrlen = sizeof(addr);
188 char name[GULM_MAX_CLUSTER_MEMBER_NAME_LEN];
190 DEBUGLOG("cluster_fd_callback\n");
192 newfd = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
194 DEBUGLOG("cluster_fd_callback, newfd=%d (errno=%d)\n", newfd, errno);
197 syslog(LOG_ERR, "error in accept: %m");
199 return -1; /* Don't return an error or clvmd will close the listening FD */
202 /* Check that the client is a member of the cluster
205 if (gulm_name_from_csid((char *)&addr.sin6_addr, name) < 0)
207 syslog(LOG_ERR, "Got connect from non-cluster node %s\n",
208 print_csid((char *)&addr.sin6_addr));
209 DEBUGLOG("Got connect from non-cluster node %s\n",
210 print_csid((char *)&addr.sin6_addr));
217 status = alloc_client(newfd, (char *)&addr.sin6_addr, new_client);
220 DEBUGLOG("cluster_fd_callback, alloc_client failed, status = %d\n", status);
226 DEBUGLOG("cluster_fd_callback, returning %d, %p\n", newfd, *new_client);
230 /* Try to get at least 'len' bytes from the socket */
231 static int really_read(int fd, char *buf, int len)
238 got = read(fd, buf+offset, len-offset);
239 DEBUGLOG("really_read. got %d bytes\n", got);
241 } while (got > 0 && offset < len);
250 static int read_from_tcpsock(struct local_client *client, char *buf, int len, char *csid,
251 struct local_client **new_client)
253 struct sockaddr_in6 addr;
254 socklen_t slen = sizeof(addr);
255 struct clvm_header *header = (struct clvm_header *)buf;
259 DEBUGLOG("read_from_tcpsock fd %d\n", client->fd);
263 getpeername(client->fd, (struct sockaddr *)&addr, &slen);
264 memcpy(csid, &addr.sin6_addr, GULM_MAX_CSID_LEN);
266 /* Read just the header first, then get the rest if there is any.
267 * Stream sockets, sigh.
269 status = really_read(client->fd, buf, sizeof(struct clvm_header));
274 arglen = ntohl(header->arglen);
277 if (arglen && arglen < GULM_MAX_CLUSTER_MESSAGE)
279 status2 = really_read(client->fd, buf+status, arglen);
287 DEBUGLOG("read_from_tcpsock, status = %d(errno = %d)\n", status, errno);
289 /* Remove it from the hash table if there's an error, clvmd will
290 remove the socket from its lists and free the client struct */
292 (status < 0 && errno != EAGAIN && errno != EINTR))
294 char remcsid[GULM_MAX_CSID_LEN];
296 memcpy(remcsid, csid, GULM_MAX_CSID_LEN);
299 /* If the csid was mangled, then make sure we remove the right entry */
300 if (client->bits.net.flags)
302 dm_hash_remove_binary(sock_hash, remcsid, GULM_MAX_CSID_LEN);
304 /* Tell cluster manager layer */
305 add_down_node(remcsid);
308 gulm_add_up_node(csid);
309 /* Send it back to clvmd */
310 process_message(client, buf, status, csid);
315 int gulm_connect_csid(const char *csid, struct local_client **newclient)
318 struct sockaddr_in6 addr;
322 DEBUGLOG("Connecting socket\n");
323 fd = socket(PF_INET6, SOCK_STREAM, 0);
327 syslog(LOG_ERR, "Unable to create new socket: %m");
331 addr.sin6_family = AF_INET6;
332 memcpy(&addr.sin6_addr, csid, GULM_MAX_CSID_LEN);
333 addr.sin6_port = htons(tcp_port);
335 DEBUGLOG("Connecting socket %d\n", fd);
336 if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in6)) < 0)
338 /* "Connection refused" is "normal" because clvmd may not yet be running
341 if (errno != ECONNREFUSED)
343 syslog(LOG_ERR, "Unable to connect to remote node: %m");
345 DEBUGLOG("Unable to connect to remote node: %s\n", strerror(errno));
350 /* Set Close-on-exec */
351 fcntl(fd, F_SETFD, 1);
352 setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int));
354 status = alloc_client(fd, csid, newclient);
358 add_client(*newclient);
360 /* If we can connect to it, it must be running a clvmd */
361 gulm_add_up_node(csid);
365 /* Send a message to a known CSID */
366 static int tcp_send_message(void *buf, int msglen, const char *csid, const char *errtext)
369 struct local_client *client;
370 char ourcsid[GULM_MAX_CSID_LEN];
374 DEBUGLOG("tcp_send_message, csid = %s, msglen = %d\n", print_csid(csid), msglen);
376 /* Don't connect to ourself */
377 get_our_gulm_csid(ourcsid);
378 if (memcmp(csid, ourcsid, GULM_MAX_CSID_LEN) == 0)
381 client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
384 status = gulm_connect_csid(csid, &client);
388 DEBUGLOG("tcp_send_message, fd = %d\n", client->fd);
390 return write(client->fd, buf, msglen);
394 int gulm_cluster_send_message(void *buf, int msglen, const char *csid, const char *errtext)
398 DEBUGLOG("cluster send message, csid = %p, msglen = %d\n", csid, msglen);
400 /* If csid is NULL then send to all known (not just connected) nodes */
403 void *context = NULL;
404 char loop_csid[GULM_MAX_CSID_LEN];
406 /* Loop round all gulm-known nodes */
407 while (get_next_node_csid(&context, loop_csid))
409 status = tcp_send_message(buf, msglen, loop_csid, errtext);
411 (status < 0 && (errno == EAGAIN || errno == EINTR)))
418 status = tcp_send_message(buf, msglen, csid, errtext);
423 /* To get our own IP address we get the locally bound address of the
424 socket that's talking to GULM in the assumption(eek) that it will
425 be on the "right" network in a multi-homed system */
426 static int get_our_ip_address(char *addr, int *family)
431 get_ip_address(info.nodename, addr);
436 /* Public version of above for those that don't care what protocol
438 void get_our_gulm_csid(char *csid)
440 static char our_csid[GULM_MAX_CSID_LEN];
441 static int got_csid = 0;
447 memset(our_csid, 0, sizeof(our_csid));
448 if (get_our_ip_address(our_csid, &family))
453 memcpy(csid, our_csid, GULM_MAX_CSID_LEN);
456 static void map_v4_to_v6(struct in_addr *ip4, struct in6_addr *ip6)
458 ip6->s6_addr32[0] = 0;
459 ip6->s6_addr32[1] = 0;
460 ip6->s6_addr32[2] = htonl(0xffff);
461 ip6->s6_addr32[3] = ip4->s_addr;
464 /* Get someone else's IP address from DNS */
465 int get_ip_address(const char *node, char *addr)
469 memset(addr, 0, GULM_MAX_CSID_LEN);
471 // TODO: what do we do about multi-homed hosts ???
472 // CCSs ip_interfaces solved this but some bugger removed it.
474 /* Try IPv6 first. The man page for gethostbyname implies that
475 it will lookup ip6 & ip4 names, but it seems not to */
476 he = gethostbyname2(node, AF_INET6);
479 memcpy(addr, he->h_addr_list[0],
484 he = gethostbyname2(node, AF_INET);
487 map_v4_to_v6((struct in_addr *)he->h_addr_list[0], (struct in6_addr *)addr);
493 char *print_csid(const char *csid)
495 static char buf[128];
496 int *icsid = (int *)csid;
498 sprintf(buf, "[%x.%x.%x.%x]",
499 icsid[0],icsid[1],icsid[2],icsid[3]);