Add -Wall to CFLAGS.
[tinc] / src / multicast_device.c
1 /*
2     device.c -- multicast socket
3     Copyright (C) 2002-2005 Ivo Timmermans,
4                   2002-2013 Guus Sliepen <guus@tinc-vpn.org>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20
21 #include "system.h"
22
23 #include "conf.h"
24 #include "device.h"
25 #include "net.h"
26 #include "logger.h"
27 #include "netutl.h"
28 #include "utils.h"
29 #include "route.h"
30 #include "xalloc.h"
31
32 static char *device_info;
33
34 static struct addrinfo *ai = NULL;
35 static mac_t ignore_src = {{0}};
36
37 static bool setup_device(void) {
38         char *host = NULL;
39         char *port;
40         char *space;
41         int ttl = 1;
42
43         device_info = "multicast socket";
44
45         get_config_string(lookup_config(config_tree, "Interface"), &iface);
46
47         if(!get_config_string(lookup_config(config_tree, "Device"), &device)) {
48                 logger(DEBUG_ALWAYS, LOG_ERR, "Device variable required for %s", device_info);
49                 goto error;
50         }
51
52         host = xstrdup(device);
53         space = strchr(host, ' ');
54         if(!space) {
55                 logger(DEBUG_ALWAYS, LOG_ERR, "Port number required for %s", device_info);
56                 goto error;
57         }
58
59         *space++ = 0;
60         port = space;
61         space = strchr(port, ' ');
62
63         if(space) {
64                 *space++ = 0;
65                 ttl = atoi(space);
66         }
67
68         ai = str2addrinfo(host, port, SOCK_DGRAM);
69         if(!ai)
70                 goto error;
71
72         device_fd = socket(ai->ai_family, SOCK_DGRAM, IPPROTO_UDP);
73         if(device_fd < 0) {
74                 logger(DEBUG_ALWAYS, LOG_ERR, "Creating socket failed: %s", sockstrerror(sockerrno));
75                 goto error;
76         }
77
78 #ifdef FD_CLOEXEC
79         fcntl(device_fd, F_SETFD, FD_CLOEXEC);
80 #endif
81
82         static const int one = 1;
83         setsockopt(device_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof one);
84
85         if(bind(device_fd, ai->ai_addr, ai->ai_addrlen)) {
86                 logger(DEBUG_ALWAYS, LOG_ERR, "Can't bind to %s %s: %s", host, port, sockstrerror(sockerrno));
87                 goto error;
88         }
89
90         switch(ai->ai_family) {
91 #ifdef IP_ADD_MEMBERSHIP
92                 case AF_INET: {
93                         struct ip_mreq mreq;
94                         struct sockaddr_in in;
95                         memcpy(&in, ai->ai_addr, sizeof in);
96                         mreq.imr_multiaddr.s_addr = in.sin_addr.s_addr;
97                         mreq.imr_interface.s_addr = htonl(INADDR_ANY);
98                         if(setsockopt(device_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)&mreq, sizeof mreq)) {
99                                 logger(DEBUG_ALWAYS, LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
100                                 goto error;
101                         }
102 #ifdef IP_MULTICAST_LOOP
103                         setsockopt(device_fd, IPPROTO_IP, IP_MULTICAST_LOOP, (const void *)&one, sizeof one);
104 #endif
105 #ifdef IP_MULTICAST_TTL
106                         setsockopt(device_fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&ttl, sizeof ttl);
107 #endif
108                 } break;
109 #endif
110
111 #ifdef IPV6_JOIN_GROUP
112                 case AF_INET6: {
113                         struct ipv6_mreq mreq;
114                         struct sockaddr_in6 in6;
115                         memcpy(&in6, ai->ai_addr, sizeof in6);
116                         memcpy(&mreq.ipv6mr_multiaddr, &in6.sin6_addr, sizeof mreq.ipv6mr_multiaddr);
117                         mreq.ipv6mr_interface = in6.sin6_scope_id;
118                         if(setsockopt(device_fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, (void *)&mreq, sizeof mreq)) {
119                                 logger(DEBUG_ALWAYS, LOG_ERR, "Cannot join multicast group %s %s: %s", host, port, sockstrerror(sockerrno));
120                                 goto error;
121                         }
122 #ifdef IPV6_MULTICAST_LOOP
123                         setsockopt(device_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (const void *)&one, sizeof one);
124 #endif
125 #ifdef IPV6_MULTICAST_HOPS
126                         setsockopt(device_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (void *)&ttl, sizeof ttl);
127 #endif
128                 } break;
129 #endif
130
131                 default:
132                         logger(DEBUG_ALWAYS, LOG_ERR, "Multicast for address family %x unsupported", ai->ai_family);
133                         goto error;
134         }
135
136         logger(DEBUG_ALWAYS, LOG_INFO, "%s is a %s", device, device_info);
137
138         return true;
139
140 error:
141         if(device_fd >= 0)
142                 closesocket(device_fd);
143         if(ai)
144                 freeaddrinfo(ai);
145         free(host);
146
147         return false;
148 }
149
150 static void close_device(void) {
151         close(device_fd); device_fd = -1;
152
153         free(device); device = NULL;
154         free(iface); iface = NULL;
155
156         if(ai) {
157                 freeaddrinfo(ai); ai = NULL;
158         }
159         device_info = NULL;
160 }
161
162 static bool read_packet(vpn_packet_t *packet) {
163         int lenin;
164
165         if((lenin = recv(device_fd, (void *)DATA(packet), MTU, 0)) <= 0) {
166                 logger(DEBUG_ALWAYS, LOG_ERR, "Error while reading from %s %s: %s", device_info,
167                            device, sockstrerror(sockerrno));
168                 return false;
169         }
170
171         if(!memcmp(&ignore_src, DATA(packet) + 6, sizeof ignore_src)) {
172                 logger(DEBUG_SCARY_THINGS, LOG_DEBUG, "Ignoring loopback packet of %d bytes from %s", lenin, device_info);
173                 return false;
174         }
175
176         packet->len = lenin;
177
178         logger(DEBUG_TRAFFIC, LOG_DEBUG, "Read packet of %d bytes from %s", packet->len,
179                            device_info);
180
181         return true;
182 }
183
184 static bool write_packet(vpn_packet_t *packet) {
185         logger(DEBUG_TRAFFIC, LOG_DEBUG, "Writing packet of %d bytes to %s",
186                            packet->len, device_info);
187
188         if(sendto(device_fd, (void *)DATA(packet), packet->len, 0, ai->ai_addr, ai->ai_addrlen) < 0) {
189                 logger(DEBUG_ALWAYS, LOG_ERR, "Can't write to %s %s: %s", device_info, device,
190                            sockstrerror(sockerrno));
191                 return false;
192         }
193
194         memcpy(&ignore_src, DATA(packet) + 6, sizeof ignore_src);
195
196         return true;
197 }
198
199 const devops_t multicast_devops = {
200         .setup = setup_device,
201         .close = close_device,
202         .read = read_packet,
203         .write = write_packet,
204 };