914235aaaba09a0b501c42db53839fb100e1da3c
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 **/
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>
34 #include "network.h"
35 #include "common.h"
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 */
49 #define IPV4_MCAST_GROUP "239.192.74.66"
50 #define UDP_PORT 25826
52 /* 1500 - 40 - 8 = Ethernet packet - IPv6 header - UDP header */
53 #define BUFF_SIZE 1452
55 typedef struct socklist
56 {
57 int fd;
58 struct socklist *next;
59 } socklist_t;
61 static socklist_t *listen_socks_head = NULL;
63 uint16_t get_port (void)
64 {
65 char *port_str;
66 int port_int;
67 uint16_t ret;
69 port_str = cf_get_option ("Port", NULL);
70 port_int = 0;
72 if (port_str != NULL)
73 port_int = atoi (port_str);
75 if (port_int == 0)
76 port_int = UDP_PORT;
78 ret = htons (port_int);
79 return (ret);
80 }
82 int network_create_listen_socket (const char *node, const char *service)
83 {
84 socklist_t *socklist_tail;
86 struct addrinfo ai_hints;
87 struct addrinfo *ai_list, *ai_ptr;
88 int ai_return;
90 int num_added = 0;
92 socklist_tail = listen_socks_head;
93 while ((socklist_tail != NULL) && (socklist_tail->next != NULL))
94 socklist_tail = socklist_tail->next;
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;
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 }
111 for (ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next)
112 {
113 socklist_t *socklist_ent;
115 if ((socklist_ent = (socklist_t *) malloc (sizeof (socklist_t))) == NULL)
116 {
117 syslog (LOG_EMERG, "malloc: %s", strerror (errno));
118 continue;
119 }
121 socklist_ent->fd = socket (ai_ptr->ai_family, ai_ptr->ai_socktype, ai_ptr->ai_protocol);
122 socklist_ent->next = NULL;
124 if (socklist_ent->fd == -1)
125 {
126 syslog (LOG_ERR, "socket: %s", strerror (errno));
127 free (socklist_ent);
128 continue;
129 }
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 }
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;
146 mreq.imr_multiaddr.s_addr = addr->sin_addr.s_addr;
147 mreq.imr_interface.s_addr = htonl (INADDR_ANY);
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;
167 memcpy (&mreq.ipv6mr_multiaddr,
168 &addr->sin6_addr,
169 sizeof (addr->sin6_addr));
171 /* FIXME What do I need here? `netdevice(7)'
172 * doesn't tell me either.. */
173 mreq6.ipv6mr_interface = 0;
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 }
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 }
198 freeaddrinfo (ai_list);
200 return (num_added);
201 }
203 int get_write_socket (void)
204 {
205 static int sd = -1;
207 if (sd != -1)
208 return (sd);
210 if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
211 {
212 syslog (LOG_ERR, "socket: %s", strerror (errno));
213 return (-1);
214 }
216 return (sd);
217 }
219 char *addr_to_host (struct sockaddr_in *addr)
220 {
221 char *host;
222 struct hostent *he;
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 }
234 return (host);
235 }
237 int network_receive (char **host, char **type, char **instance, char **value)
238 {
239 int sd = get_read_socket ();
241 char buffer[BUFF_SIZE];
243 struct sockaddr_in addr;
244 socklen_t addr_size;
246 char *fields[4];
248 *host = NULL;
249 *type = NULL;
250 *instance = NULL;
251 *value = NULL;
253 if (sd == -1)
254 return (-1);
256 addr_size = sizeof (addr);
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 }
264 if (strsplit (buffer, fields, 4) != 3)
265 return (-1);
267 *host = addr_to_host (&addr);
268 *type = strdup (fields[0]);
269 *instance = strdup (fields[1]);
270 *value = strdup (fields[2]);
272 if (*host == NULL || *type == NULL || *instance == NULL || *value == NULL)
273 return (-1);
275 return (0);
276 }
278 int network_send (char *type, char *instance, char *value)
279 {
280 int sd = get_write_socket ();
281 struct sockaddr_in addr;
283 char buf[BUFF_SIZE];
284 int buflen;
286 if (sd == -1)
287 return (-1);
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';
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 ();
301 return (sendto (sd, buf, buflen, 0, (struct sockaddr *) &addr, sizeof (addr)));
302 }