Tizen 2.1 base
[external/device-mapper.git] / daemons / clvmd / tcp-comms.c
1 /*
2  *  Copyright (C) 2002-2003 Sistina Software, Inc. All rights reserved.
3  *  Copyright (C) 2004-2009 Red Hat, Inc. All rights reserved.
4  *
5  * This file is part of LVM2.
6  *
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.
10  *
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
14  */
15
16 /*
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
19  * normal way.
20  * It can also make outgoing connnections to the other clvmd nodes.
21  */
22
23 #include "clvmd-common.h"
24
25 #include <pthread.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>
31 #include <stdint.h>
32 #include <fcntl.h>
33 #include <stddef.h>
34 #include <stdint.h>
35 #include <errno.h>
36 #include <syslog.h>
37 #include <netdb.h>
38 #include <assert.h>
39
40 #include "clvm.h"
41 #include "clvmd-comms.h"
42 #include "clvmd.h"
43 #include "clvmd-gulm.h"
44
45 #define DEFAULT_TCP_PORT 21064
46
47 static int listen_fd = -1;
48 static int tcp_port;
49 struct dm_hash_table *sock_hash;
50
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);
54
55 /* Called by init_cluster() to open up the listening socket */
56 int init_comms(unsigned short port)
57 {
58     struct sockaddr_in6 addr;
59
60     sock_hash = dm_hash_create(100);
61     tcp_port = port ? : DEFAULT_TCP_PORT;
62
63     listen_fd = socket(AF_INET6, SOCK_STREAM, 0);
64
65     if (listen_fd < 0)
66     {
67         return -1;
68     }
69     else
70     {
71         int one = 1;
72         setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
73         setsockopt(listen_fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int));
74     }
75
76     memset(&addr, 0, sizeof(addr)); // Bind to INADDR_ANY
77     addr.sin6_family = AF_INET6;
78     addr.sin6_port = htons(tcp_port);
79
80     if (bind(listen_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
81     {
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);
84         close(listen_fd);
85         return -1;
86     }
87
88     listen(listen_fd, 5);
89
90     /* Set Close-on-exec */
91     fcntl(listen_fd, F_SETFD, 1);
92
93     return 0;
94 }
95
96 void tcp_remove_client(const char *c_csid)
97 {
98     struct local_client *client;
99     char csid[GULM_MAX_CSID_LEN];
100     unsigned int i;
101     memcpy(csid, c_csid, sizeof csid);
102     DEBUGLOG("tcp_remove_client\n");
103
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++)
109     {
110         client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
111         if (client)
112         {
113             dm_hash_remove_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
114             client->removeme = 1;
115             close(client->fd);
116         }
117         /* Look for a mangled one too, on the 2nd iteration. */
118         csid[0] ^= 0x80;
119     }
120 }
121
122 int alloc_client(int fd, const char *c_csid, struct local_client **new_client)
123 {
124     struct local_client *client;
125     char csid[GULM_MAX_CSID_LEN];
126     memcpy(csid, c_csid, sizeof csid);
127
128     DEBUGLOG("alloc_client %d csid = %s\n", fd, print_csid(csid));
129
130     /* Create a local_client and return it */
131     client = malloc(sizeof(struct local_client));
132     if (!client)
133     {
134         DEBUGLOG("malloc failed\n");
135         return -1;
136     }
137
138     memset(client, 0, sizeof(struct local_client));
139     client->fd = fd;
140     client->type = CLUSTER_DATA_SOCK;
141     client->callback = read_from_tcpsock;
142     if (new_client)
143         *new_client = client;
144
145     /* Add to our list of node sockets */
146     if (dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN))
147     {
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
153         */
154         csid[0] ^= 0x80;
155         client->bits.net.flags = 1; /* indicate mangled CSID */
156
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))
160         {
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]);
163
164             free(client);
165             errno = ECONNREFUSED;
166             return -1;
167         }
168     }
169     dm_hash_insert_binary(sock_hash, csid, GULM_MAX_CSID_LEN, client);
170
171     return 0;
172 }
173
174 int get_main_gulm_cluster_fd()
175 {
176     return listen_fd;
177 }
178
179
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)
183 {
184     int newfd;
185     struct sockaddr_in6 addr;
186     socklen_t addrlen = sizeof(addr);
187     int status;
188     char name[GULM_MAX_CLUSTER_MEMBER_NAME_LEN];
189
190     DEBUGLOG("cluster_fd_callback\n");
191     *new_client = NULL;
192     newfd = accept(listen_fd, (struct sockaddr *)&addr, &addrlen);
193
194     DEBUGLOG("cluster_fd_callback, newfd=%d (errno=%d)\n", newfd, errno);
195     if (!newfd)
196     {
197         syslog(LOG_ERR, "error in accept: %m");
198         errno = EAGAIN;
199         return -1; /* Don't return an error or clvmd will close the listening FD */
200     }
201
202     /* Check that the client is a member of the cluster
203        and reject if not.
204     */
205     if (gulm_name_from_csid((char *)&addr.sin6_addr, name) < 0)
206     {
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));
211         close(newfd);
212
213         errno = EAGAIN;
214         return -1;
215     }
216
217     status = alloc_client(newfd, (char *)&addr.sin6_addr, new_client);
218     if (status)
219     {
220         DEBUGLOG("cluster_fd_callback, alloc_client failed, status = %d\n", status);
221         close(newfd);
222         /* See above... */
223         errno = EAGAIN;
224         return -1;
225     }
226     DEBUGLOG("cluster_fd_callback, returning %d, %p\n", newfd, *new_client);
227     return newfd;
228 }
229
230 /* Try to get at least 'len' bytes from the socket */
231 static int really_read(int fd, char *buf, int len)
232 {
233         int got, offset;
234
235         got = offset = 0;
236
237         do {
238                 got = read(fd, buf+offset, len-offset);
239                 DEBUGLOG("really_read. got %d bytes\n", got);
240                 offset += got;
241         } while (got > 0 && offset < len);
242
243         if (got < 0)
244                 return got;
245         else
246                 return offset;
247 }
248
249
250 static int read_from_tcpsock(struct local_client *client, char *buf, int len, char *csid,
251                              struct local_client **new_client)
252 {
253     struct sockaddr_in6 addr;
254     socklen_t slen = sizeof(addr);
255     struct clvm_header *header = (struct clvm_header *)buf;
256     int status;
257     uint32_t arglen;
258
259     DEBUGLOG("read_from_tcpsock fd %d\n", client->fd);
260     *new_client = NULL;
261
262     /* Get "csid" */
263     getpeername(client->fd, (struct sockaddr *)&addr, &slen);
264     memcpy(csid, &addr.sin6_addr, GULM_MAX_CSID_LEN);
265
266     /* Read just the header first, then get the rest if there is any.
267      * Stream sockets, sigh.
268      */
269     status = really_read(client->fd, buf, sizeof(struct clvm_header));
270     if (status > 0)
271     {
272             int status2;
273
274             arglen = ntohl(header->arglen);
275
276             /* Get the rest */
277             if (arglen && arglen < GULM_MAX_CLUSTER_MESSAGE)
278             {
279                     status2 = really_read(client->fd, buf+status, arglen);
280                     if (status2 > 0)
281                             status += status2;
282                     else
283                             status = status2;
284             }
285     }
286
287     DEBUGLOG("read_from_tcpsock, status = %d(errno = %d)\n", status, errno);
288
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 */
291     if (status == 0 ||
292         (status < 0 && errno != EAGAIN && errno != EINTR))
293     {
294         char remcsid[GULM_MAX_CSID_LEN];
295
296         memcpy(remcsid, csid, GULM_MAX_CSID_LEN);
297         close(client->fd);
298
299         /* If the csid was mangled, then make sure we remove the right entry */
300         if (client->bits.net.flags)
301             remcsid[0] ^= 0x80;
302         dm_hash_remove_binary(sock_hash, remcsid, GULM_MAX_CSID_LEN);
303
304         /* Tell cluster manager layer */
305         add_down_node(remcsid);
306     }
307     else {
308             gulm_add_up_node(csid);
309             /* Send it back to clvmd */
310             process_message(client, buf, status, csid);
311     }
312     return status;
313 }
314
315 int gulm_connect_csid(const char *csid, struct local_client **newclient)
316 {
317     int fd;
318     struct sockaddr_in6 addr;
319     int status;
320     int one = 1;
321
322     DEBUGLOG("Connecting socket\n");
323     fd = socket(PF_INET6, SOCK_STREAM, 0);
324
325     if (fd < 0)
326     {
327         syslog(LOG_ERR, "Unable to create new socket: %m");
328         return -1;
329     }
330
331     addr.sin6_family = AF_INET6;
332     memcpy(&addr.sin6_addr, csid, GULM_MAX_CSID_LEN);
333     addr.sin6_port = htons(tcp_port);
334
335     DEBUGLOG("Connecting socket %d\n", fd);
336     if (connect(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in6)) < 0)
337     {
338         /* "Connection refused" is "normal" because clvmd may not yet be running
339          * on that node.
340          */
341         if (errno != ECONNREFUSED)
342         {
343             syslog(LOG_ERR, "Unable to connect to remote node: %m");
344         }
345         DEBUGLOG("Unable to connect to remote node: %s\n", strerror(errno));
346         close(fd);
347         return -1;
348     }
349
350     /* Set Close-on-exec */
351     fcntl(fd, F_SETFD, 1);
352     setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &one, sizeof(int));
353
354     status = alloc_client(fd, csid, newclient);
355     if (status)
356         close(fd);
357     else
358         add_client(*newclient);
359
360     /* If we can connect to it, it must be running a clvmd */
361     gulm_add_up_node(csid);
362     return status;
363 }
364
365 /* Send a message to a known CSID */
366 static int tcp_send_message(void *buf, int msglen, const char *csid, const char *errtext)
367 {
368     int status;
369     struct local_client *client;
370     char ourcsid[GULM_MAX_CSID_LEN];
371
372     assert(csid);
373
374     DEBUGLOG("tcp_send_message, csid = %s, msglen = %d\n", print_csid(csid), msglen);
375
376     /* Don't connect to ourself */
377     get_our_gulm_csid(ourcsid);
378     if (memcmp(csid, ourcsid, GULM_MAX_CSID_LEN) == 0)
379         return msglen;
380
381     client = dm_hash_lookup_binary(sock_hash, csid, GULM_MAX_CSID_LEN);
382     if (!client)
383     {
384         status = gulm_connect_csid(csid, &client);
385         if (status)
386             return -1;
387     }
388     DEBUGLOG("tcp_send_message, fd = %d\n", client->fd);
389
390     return write(client->fd, buf, msglen);
391 }
392
393
394 int gulm_cluster_send_message(void *buf, int msglen, const char *csid, const char *errtext)
395 {
396     int status=0;
397
398     DEBUGLOG("cluster send message, csid = %p, msglen = %d\n", csid, msglen);
399
400     /* If csid is NULL then send to all known (not just connected) nodes */
401     if (!csid)
402     {
403         void *context = NULL;
404         char loop_csid[GULM_MAX_CSID_LEN];
405
406         /* Loop round all gulm-known nodes */
407         while (get_next_node_csid(&context, loop_csid))
408         {
409             status = tcp_send_message(buf, msglen, loop_csid, errtext);
410             if (status == 0 ||
411                 (status < 0 && (errno == EAGAIN || errno == EINTR)))
412                 break;
413         }
414     }
415     else
416     {
417
418         status = tcp_send_message(buf, msglen, csid, errtext);
419     }
420     return status;
421 }
422
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)
427 {
428         struct utsname info;
429
430         uname(&info);
431         get_ip_address(info.nodename, addr);
432
433         return 0;
434 }
435
436 /* Public version of above for those that don't care what protocol
437    we're using */
438 void get_our_gulm_csid(char *csid)
439 {
440     static char our_csid[GULM_MAX_CSID_LEN];
441     static int got_csid = 0;
442
443     if (!got_csid)
444     {
445         int family;
446
447         memset(our_csid, 0, sizeof(our_csid));
448         if (get_our_ip_address(our_csid, &family))
449         {
450             got_csid = 1;
451         }
452     }
453     memcpy(csid, our_csid, GULM_MAX_CSID_LEN);
454 }
455
456 static void map_v4_to_v6(struct in_addr *ip4, struct in6_addr *ip6)
457 {
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;
462 }
463
464 /* Get someone else's IP address from DNS */
465 int get_ip_address(const char *node, char *addr)
466 {
467     struct hostent *he;
468
469     memset(addr, 0, GULM_MAX_CSID_LEN);
470
471     // TODO: what do we do about multi-homed hosts ???
472     // CCSs ip_interfaces solved this but some bugger removed it.
473
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);
477     if (he)
478     {
479         memcpy(addr, he->h_addr_list[0],
480                he->h_length);
481     }
482     else
483     {
484         he = gethostbyname2(node, AF_INET);
485         if (!he)
486             return -1;
487         map_v4_to_v6((struct in_addr *)he->h_addr_list[0], (struct in6_addr *)addr);
488     }
489
490     return 0;
491 }
492
493 char *print_csid(const char *csid)
494 {
495     static char buf[128];
496     int *icsid = (int *)csid;
497
498     sprintf(buf, "[%x.%x.%x.%x]",
499             icsid[0],icsid[1],icsid[2],icsid[3]);
500
501     return buf;
502 }