1 /**
2 * collectd - src/multicast.c
3 * Copyright (C) 2005 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 "multicast.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 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 int get_read_socket (void)
56 {
57 static int sd = -1; /* socket descriptor */
58 int optval;
60 struct sockaddr_in addr;
61 struct ip_mreq mreq;
63 if (sd != -1)
64 return (sd);
66 /* Create UDP sicket */
67 if ((sd = socket (PF_INET, SOCK_DGRAM, 0)) == -1)
68 {
69 syslog (LOG_ERR, "socket: %s", strerror (errno));
70 return (-1);
71 }
73 optval = 1;
74 if (setsockopt (sd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) == -1)
75 {
76 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
77 shutdown (sd, SHUT_RD);
78 sd = -1;
79 return (-1);
80 }
82 memset (&addr, '\0', sizeof(addr));
83 addr.sin_family = AF_INET;
84 addr.sin_addr.s_addr = htonl (INADDR_ANY);
85 addr.sin_port = htons (UDP_PORT);
86 if (bind (sd, (struct sockaddr *) &addr, sizeof (addr)) == -1)
87 {
88 syslog (LOG_ERR, "bind: %s", strerror (errno));
89 shutdown (sd, SHUT_RD);
90 sd = -1;
91 return (-1);
92 }
94 mreq.imr_multiaddr.s_addr = inet_addr (MCAST_GROUP);
95 mreq.imr_interface.s_addr = htonl (INADDR_ANY);
96 if (setsockopt (sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof (mreq)) == -1)
97 {
98 syslog (LOG_ERR, "setsockopt: %s", strerror (errno));
99 shutdown (sd, SHUT_RD);
100 sd = -1;
101 return (-1);
102 }
104 return (sd);
105 }
107 int get_write_socket (void)
108 {
109 static int sd = -1;
111 if (sd != -1)
112 return (sd);
114 if ((sd = socket (AF_INET, SOCK_DGRAM, 0)) == -1)
115 {
116 syslog (LOG_ERR, "socket: %s", strerror (errno));
117 return (-1);
118 }
120 return (sd);
121 }
123 char *addr_to_host (struct sockaddr_in *addr)
124 {
125 char *host;
126 struct hostent *he;
128 if ((he = gethostbyaddr ((char *) &addr->sin_addr, sizeof (addr->sin_addr), AF_INET)) != NULL)
129 {
130 host = strdup (he->h_name);
131 }
132 else
133 {
134 char *tmp = inet_ntoa (addr->sin_addr);
135 host = strdup (tmp);
136 }
138 return (host);
139 }
141 int multicast_receive (char **host, char **type, char **instance, char **value)
142 {
143 int sd = get_read_socket ();
145 char buffer[BUFF_SIZE];
147 struct sockaddr_in addr;
148 socklen_t addr_size;
150 char *fields[4];
152 *host = NULL;
153 *type = NULL;
154 *instance = NULL;
155 *value = NULL;
157 if (sd == -1)
158 return (-1);
160 addr_size = sizeof (addr);
162 if (recvfrom (sd, buffer, BUFF_SIZE, 0, (struct sockaddr *) &addr, &addr_size) == -1)
163 {
164 syslog (LOG_ERR, "recvfrom: %s", strerror (errno));
165 return (-1);
166 }
168 if (strsplit (buffer, fields, 4) != 3)
169 return (-1);
171 *host = addr_to_host (&addr);
172 *type = strdup (fields[0]);
173 *instance = strdup (fields[1]);
174 *value = strdup (fields[2]);
176 if (*host == NULL || *type == NULL || *instance == NULL || *value == NULL)
177 return (-1);
179 return (0);
180 }
182 int multicast_send (char *type, char *instance, char *value)
183 {
184 int sd = get_write_socket ();
185 struct sockaddr_in addr;
187 char buf[BUFF_SIZE];
188 int buflen;
190 if (sd == -1)
191 return (-1);
193 if ((buflen = snprintf (buf, BUFF_SIZE, "%s %s %s", type, instance, value)) >= BUFF_SIZE)
194 {
195 syslog (LOG_WARNING, "multicast_send: Output truncated..");
196 return (-1);
197 }
198 buf[buflen++] = '\0';
200 memset(&addr, '\0', sizeof (addr));
201 addr.sin_family = AF_INET;
202 addr.sin_addr.s_addr = inet_addr (MCAST_GROUP);
203 addr.sin_port = htons (UDP_PORT);
205 return (sendto (sd, buf, buflen, 0, (struct sockaddr *) &addr, sizeof (addr)));
206 }