Added `network.c' which will replace `multicast.c' at some point..
[collectd.git] / src / network.c
1 /**
2  * collectd - src/network.c
3  * Copyright (C) 2006  Florian octo Forster
4  *
5  * This program is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by the
7  * Free Software Foundation; either version 2 of the License, or (at your
8  * option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   Florian octo Forster <octo at verplant.org>
21  **/
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include <netdb.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #include <syslog.h>
32 #include <errno.h>
33
34 #include "network.h"
35 #include "common.h"
36
37 /*
38  * From RFC2365:
39  *
40  * The IPv4 Organization Local Scope -- 239.192.0.0/14
41  *
42  * 239.192.0.0/14 is defined to be the IPv4 Organization Local Scope, and is
43  * the space from which an organization should allocate sub-ranges when
44  * defining scopes for private use.
45  *
46  * Port 25826 is not assigned as of 2005-09-12
47  */
48
49 #define IPV4_MCAST_GROUP "239.192.74.66"
50 #define UDP_PORT 25826
51
52 /* 1500 - 40 - 8  =  Ethernet packet - IPv6 header - UDP header */
53 #define BUFF_SIZE 1452
54
55 typedef struct socklist
56 {
57         int              fd;
58         struct socklist *next;
59 } socklist_t;
60
61 static socklist_t *listen_socks_head = NULL;
62
63 uint16_t get_port (void)
64 {
65         char *port_str;
66         int   port_int;
67         uint16_t ret;
68
69         port_str = cf_get_option ("Port", NULL);
70         port_int = 0;
71
72         if (port_str != NULL)
73                 port_int = atoi (port_str);
74
75         if (port_int == 0)
76                 port_int = UDP_PORT;
77
78         ret = htons (port_int);
79         return (ret);
80 }
81
82 int network_create_listen_socket (const char *node, const char *service)
83 {
84         socklist_t *socklist_tail;
85
86         struct addrinfo  ai_hints;
87         struct addrinfo *ai_list, *ai_ptr;
88         int              ai_return;
89
90         int num_added = 0;
91
92         socklist_tail = listen_socks_head;
93         while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
94                 socklist_tail = socklist_tail->next;
95
96         memset (&ai_hints, '\0', sizeof (ai_hints));
97         ai_hints.ai_flags    = AI_PASSIVE | AI_ADDRCONFIG;
98         ai_hints.ai_family   = AF_UNSPEC;
99         ai_hints.ai_socktype = SOCK_DGRAM;
100         ai_hints.ai_protocol = IPPROTO_UDP;
101
102         if ((ai_return = getaddrinfo (node, service, &ai_hints, &ai_list)) != 0)
103         {
104                 syslog (LOG_ERR, "getaddrinfo (%s, %s): %s",
105                                 node == NULL ? "(null)" : node,
106                                 service == NULL ? "(null)" : service,
107                                 ai_return == EAI_SYSTEM ? strerror (errno) : gai_strerror (ai_return));
108                 return (-1);
109         }
110
111         for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
112         {
113                 socklist_t *socklist_ent;
114
115                 if ((socklist_ent = (socklist_t *) malloc (sizeof (socklist_t))) == NULL)
116                 {
117                         syslog (LOG_EMERG, "malloc: %s", strerror (errno));
118                         continue;
119                 }
120
121                 socklist_ent->fd   = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
122                 socklist_ent->next = NULL;
123
124                 if (socklist_ent->fd == -1)
125                 {
126                         syslog (LOG_ERR, "socket: %s", strerror (errno));
127                         free (socklist_ent);
128                         continue;
129                 }
130
131                 if (bind (socklist_ent->fd, ai_ptr->ai_addr, ai_ptr->ai_addrlen) == -1)
132                 {
133                         syslog (LOG_ERR, "bind: %s", strerror (errno));
134                         close (socklist_ent->fd);
135                         free (socklist_ent);
136                         continue;
137                 }
138
139                 if (ai_ptr->ai_family == AF_INET)
140                 {
141                         struct sockaddr_in *addr = (struct sockaddr_in *) ai_ptr->ai_addr;
142                         if (IN_MULTICAST (ntohl (addr->sin_addr.s_addr)))
143                         {
144                                 struct ip_mreq mreq;
145
146                                 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
147                                 mreq.imr_interface.s_addr = htonl (INADDR_ANY);
148
149                                 if (setsockopt (socklist_ent->fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
150                                                         &mreq, sizeof (mreq)) == -1)
151                                 {
152                                         syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
153                                         close (socklist_ent->fd);
154                                         free (socklist_ent);
155                                         continue;
156                                 }
157                         }
158                 }
159                 else if (ai_ptr->ai_family == AF_INET6)
160                 {
161                         /* Useful example: http://gsyc.escet.urjc.es/~eva/IPv6-web/examples/mcast.html */
162                         struct sockaddr_in6 *addr = (struct sockaddr_in6 *) ai_ptr->ai_addr;
163                         if (IN6_IS_ADDR_MULTICAST (&addr->sin6_addr))
164                         {
165                                 struct ipv6_mreq mreq;
166
167                                 memcpy (&mreq.ipv6mr_multiaddr,
168                                                 &addr->sin6_addr,
169                                                 sizeof (addr->sin6_addr));
170
171                                 /* FIXME What do I need here? `netdevice(7)'
172                                  * doesn't tell me either.. */
173                                 mreq6.ipv6mr_interface = 0;
174
175                                 if (setsockopt (socklist_ent->fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP,
176                                                         &mreq, sizeof (mreq)) == -1)
177                                 {
178                                         syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
179                                         close (socklist_ent->fd);
180                                         free (socklist_ent);
181                                         continue;
182                                 }
183                         }
184                 }
185
186                 if (socklist_tail == NULL)
187                 {
188                         listen_socks_head = socklist_tail = socklist_ent;
189                 }
190                 else
191                 {
192                         socklist_tail->next = socklist_ent;
193                         socklist_tail = socklist_ent;
194                 }
195                 num_added++;
196         }
197
198         freeaddrinfo (ai_list);
199
200         return (num_added);
201 }
202
203 int get_write_socket (void)
204 {
205         static int sd = -1;
206
207         if (sd != -1)
208                 return (sd);
209
210         if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
211         {
212                 syslog (LOG_ERR, "socket: %s", strerror (errno));
213                 return (-1);
214         }
215
216         return (sd);
217 }
218
219 char *addr_to_host (struct sockaddr_in *addr)
220 {
221         char *host;
222         struct hostent *he;
223
224         if ((he = gethostbyaddr ((char *) &addr->sin_addr, sizeof (addr->sin_addr), AF_INET)) != NULL)
225         {
226                 host = strdup (he->h_name);
227         }
228         else
229         {
230                 char *tmp = inet_ntoa (addr->sin_addr);
231                 host = strdup (tmp);
232         }
233
234         return (host);
235 }
236
237 int network_receive (char **host, char **type, char **instance, char **value)
238 {
239         int sd = get_read_socket ();
240
241         char buffer[BUFF_SIZE];
242
243         struct sockaddr_in addr;
244         socklen_t addr_size;
245
246         char *fields[4];
247
248         *host     = NULL;
249         *type     = NULL;
250         *instance = NULL;
251         *value    = NULL;
252
253         if (sd == -1)
254                 return (-1);
255
256         addr_size = sizeof (addr);
257
258         if (recvfrom (sd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addr_size) == -1)
259         {
260                 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
261                 return (-1);
262         }
263
264         if (strsplit (buffer, fields, 4) != 3)
265                 return (-1);
266
267         *host     = addr_to_host (&addr);
268         *type     = strdup (fields[0]);
269         *instance = strdup (fields[1]);
270         *value    = strdup (fields[2]);
271
272         if (*host == NULL || *type == NULL || *instance == NULL || *value == NULL)
273                 return (-1);
274
275         return (0);
276 }
277
278 int network_send (char *type, char *instance, char *value)
279 {
280         int sd = get_write_socket ();
281         struct sockaddr_in addr;
282
283         char buf[BUFF_SIZE];
284         int buflen;
285
286         if (sd == -1)
287                 return (-1);
288
289         if ((buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, instance, value)) >= BUFF_SIZE)
290         {
291                 syslog (LOG_WARNING, "network_send: Output truncated..");
292                 return (-1);
293         }
294         buf[buflen++] = '\0';
295
296         memset(&addr, '\0', sizeof (addr));
297         addr.sin_family = AF_INET;
298         addr.sin_addr.s_addr = inet_addr (IPV4_MCAST_GROUP);
299         addr.sin_port = get_port ();
300
301         return (sendto (sd, buf, buflen, 0, (struct sockaddr *) &addr, sizeof (addr)));
302 }