]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_sock.c
Merge pull request #461 from donaldsharp/poll_2.0
[mirror_frr.git] / pimd / pim_sock.c
1 /*
2 PIM for Quagga
3 Copyright (C) 2008 Everton da Silva Marques
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your 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
16 along with this program; see the file COPYING; if not, write to the
17 Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
18 MA 02110-1301 USA
19 */
20
21 #include <zebra.h>
22
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <netinet/igmp.h>
27 #include <arpa/inet.h>
28 #include <unistd.h>
29 #include <netdb.h>
30 #include <errno.h>
31
32 #include "log.h"
33 #include "privs.h"
34 #include "if.h"
35 #include "vrf.h"
36 #include "sockopt.h"
37
38 #include "pimd.h"
39 #include "pim_mroute.h"
40 #include "pim_sock.h"
41 #include "pim_str.h"
42 #include "pim_igmp_join.h"
43
44 /* GLOBAL VARS */
45 extern struct zebra_privs_t pimd_privs;
46
47 int pim_socket_raw(int protocol)
48 {
49 int fd;
50
51 if ( pimd_privs.change (ZPRIVS_RAISE) )
52 zlog_err ("pim_sockek_raw: could not raise privs, %s",
53 safe_strerror (errno) );
54
55 fd = socket(AF_INET, SOCK_RAW, protocol);
56
57 if ( pimd_privs.change (ZPRIVS_LOWER) )
58 zlog_err ("pim_socket_raw: could not lower privs, %s",
59 safe_strerror (errno) );
60
61 if (fd < 0) {
62 zlog_warn("Could not create raw socket: errno=%d: %s",
63 errno, safe_strerror(errno));
64 return PIM_SOCK_ERR_SOCKET;
65 }
66
67 return fd;
68 }
69
70 int pim_socket_mcast(int protocol, struct in_addr ifaddr, int ifindex, u_char loop)
71 {
72 int fd;
73
74 fd = pim_socket_raw(protocol);
75 if (fd < 0) {
76 zlog_warn("Could not create multicast socket: errno=%d: %s",
77 errno, safe_strerror(errno));
78 return PIM_SOCK_ERR_SOCKET;
79 }
80
81 #ifdef SO_BINDTODEVICE
82 if (protocol == IPPROTO_PIM)
83 {
84 int ret;
85 struct interface *ifp = NULL;
86
87 ifp = if_lookup_by_index_vrf (ifindex, VRF_DEFAULT);
88
89 if (pimd_privs.change (ZPRIVS_RAISE))
90 zlog_err ("%s: could not raise privs, %s",
91 __PRETTY_FUNCTION__, safe_strerror (errno));
92
93 ret = setsockopt (fd, SOL_SOCKET,
94 SO_BINDTODEVICE, ifp->name, strlen (ifp->name));
95
96 if (pimd_privs.change (ZPRIVS_LOWER))
97 zlog_err ("%s: could not lower privs, %s",
98 __PRETTY_FUNCTION__, safe_strerror (errno));
99
100 if (ret)
101 {
102 zlog_warn("Could not set fd: %d for interface: %s to device",
103 fd, ifp->name);
104 return PIM_SOCK_ERR_BIND;
105 }
106 }
107 #else
108 /* XXX: use IP_PKTINFO / IP_RECVIF to emulate behaviour? Or change to
109 * only use 1 socket for all interfaces? */
110 #endif
111
112 /* Needed to obtain destination address from recvmsg() */
113 {
114 #if defined(HAVE_IP_PKTINFO)
115 /* Linux and Solaris IP_PKTINFO */
116 int opt = 1;
117 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
118 zlog_warn("Could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
119 fd, errno, safe_strerror(errno));
120 }
121 #elif defined(HAVE_IP_RECVDSTADDR)
122 /* BSD IP_RECVDSTADDR */
123 int opt = 1;
124 if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
125 zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
126 fd, errno, safe_strerror(errno));
127 }
128 #else
129 zlog_err("%s %s: Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
130 __FILE__, __PRETTY_FUNCTION__);
131 close(fd);
132 return PIM_SOCK_ERR_DSTADDR;
133 #endif
134 }
135
136
137 /* Set router alert (RFC 2113) for all IGMP messages (RFC 3376 4. Message Formats)*/
138 if (protocol == IPPROTO_IGMP) {
139 uint8_t ra[4];
140 ra[0] = 148;
141 ra[1] = 4;
142 ra[2] = 0;
143 ra[3] = 0;
144 if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
145 zlog_warn("Could not set Router Alert Option on socket fd=%d: errno=%d: %s",
146 fd, errno, safe_strerror(errno));
147 close(fd);
148 return PIM_SOCK_ERR_RA;
149 }
150 }
151
152 {
153 int reuse = 1;
154 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
155 (void *) &reuse, sizeof(reuse))) {
156 zlog_warn("Could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
157 fd, errno, safe_strerror(errno));
158 close(fd);
159 return PIM_SOCK_ERR_REUSE;
160 }
161 }
162
163 {
164 const int MTTL = 1;
165 int ttl = MTTL;
166 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
167 (void *) &ttl, sizeof(ttl))) {
168 zlog_warn("Could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
169 MTTL, fd, errno, safe_strerror(errno));
170 close(fd);
171 return PIM_SOCK_ERR_TTL;
172 }
173 }
174
175 if (setsockopt_ipv4_multicast_loop (fd, loop)) {
176 zlog_warn("Could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s",
177 loop ? "enable" : "disable",
178 fd, errno, safe_strerror(errno));
179 close(fd);
180 return PIM_SOCK_ERR_LOOP;
181 }
182
183 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
184 (void *) &ifaddr, sizeof(ifaddr))) {
185 zlog_warn("Could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
186 fd, errno, safe_strerror(errno));
187 close(fd);
188 return PIM_SOCK_ERR_IFACE;
189 }
190
191 {
192 long flags;
193
194 flags = fcntl(fd, F_GETFL, 0);
195 if (flags < 0) {
196 zlog_warn("Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
197 fd, errno, safe_strerror(errno));
198 close(fd);
199 return PIM_SOCK_ERR_NONBLOCK_GETFL;
200 }
201
202 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
203 zlog_warn("Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
204 fd, errno, safe_strerror(errno));
205 close(fd);
206 return PIM_SOCK_ERR_NONBLOCK_SETFL;
207 }
208 }
209
210 return fd;
211 }
212
213 int pim_socket_join(int fd, struct in_addr group,
214 struct in_addr ifaddr, ifindex_t ifindex)
215 {
216 int ret;
217
218 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
219 struct ip_mreqn opt;
220 #else
221 struct ip_mreq opt;
222 #endif
223
224 opt.imr_multiaddr = group;
225
226 #ifdef HAVE_STRUCT_IP_MREQN_IMR_IFINDEX
227 opt.imr_address = ifaddr;
228 opt.imr_ifindex = ifindex;
229 #else
230 opt.imr_interface = ifaddr;
231 #endif
232
233 ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &opt, sizeof(opt));
234 if (ret) {
235 char group_str[100];
236 char ifaddr_str[100];
237 if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
238 sprintf(group_str, "<group?>");
239 if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
240 sprintf(ifaddr_str, "<ifaddr?>");
241
242 zlog_err("Failure socket joining fd=%d group %s on interface address %s: errno=%d: %s",
243 fd, group_str, ifaddr_str, errno, safe_strerror(errno));
244 return ret;
245 }
246
247 if (PIM_DEBUG_TRACE) {
248 char group_str[100];
249 char ifaddr_str[100];
250 if (!inet_ntop(AF_INET, &group, group_str , sizeof(group_str)))
251 sprintf(group_str, "<group?>");
252 if (!inet_ntop(AF_INET, &ifaddr, ifaddr_str , sizeof(ifaddr_str)))
253 sprintf(ifaddr_str, "<ifaddr?>");
254
255 zlog_debug("Socket fd=%d joined group %s on interface address %s",
256 fd, group_str, ifaddr_str);
257 }
258
259 return ret;
260 }
261
262 int pim_socket_join_source(int fd, ifindex_t ifindex,
263 struct in_addr group_addr,
264 struct in_addr source_addr,
265 const char *ifname)
266 {
267 if (pim_igmp_join_source(fd, ifindex, group_addr, source_addr)) {
268 int e = errno;
269 char group_str[100];
270 char source_str[100];
271 pim_inet4_dump("<grp?>", group_addr, group_str, sizeof(group_str));
272 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
273 zlog_warn("%s: setsockopt(fd=%d) failure for IGMP group %s source %s ifindex %d on interface %s: errno=%d: %s",
274 __PRETTY_FUNCTION__,
275 fd, group_str, source_str, ifindex, ifname,
276 e, safe_strerror(e));
277 return -1;
278 }
279
280 return 0;
281 }
282
283 int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
284 struct sockaddr_in *from, socklen_t *fromlen,
285 struct sockaddr_in *to, socklen_t *tolen,
286 ifindex_t *ifindex)
287 {
288 struct msghdr msgh;
289 struct cmsghdr *cmsg;
290 struct iovec iov;
291 char cbuf[1000];
292 int err;
293
294 /*
295 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
296 * Use getsockname() to get sin_port.
297 */
298 if (to) {
299 struct sockaddr_in si;
300 socklen_t si_len = sizeof(si);
301
302 ((struct sockaddr_in *) to)->sin_family = AF_INET;
303
304 if (pim_socket_getsockname(fd, (struct sockaddr *) &si, &si_len)) {
305 ((struct sockaddr_in *) to)->sin_port = ntohs(0);
306 ((struct sockaddr_in *) to)->sin_addr.s_addr = ntohl(0);
307 }
308 else {
309 ((struct sockaddr_in *) to)->sin_port = si.sin_port;
310 ((struct sockaddr_in *) to)->sin_addr = si.sin_addr;
311 }
312
313 if (tolen)
314 *tolen = sizeof(si);
315 }
316
317 memset(&msgh, 0, sizeof(struct msghdr));
318 iov.iov_base = buf;
319 iov.iov_len = len;
320 msgh.msg_control = cbuf;
321 msgh.msg_controllen = sizeof(cbuf);
322 msgh.msg_name = from;
323 msgh.msg_namelen = fromlen ? *fromlen : 0;
324 msgh.msg_iov = &iov;
325 msgh.msg_iovlen = 1;
326 msgh.msg_flags = 0;
327
328 err = recvmsg(fd, &msgh, 0);
329 if (err < 0)
330 return err;
331
332 if (fromlen)
333 *fromlen = msgh.msg_namelen;
334
335 for (cmsg = CMSG_FIRSTHDR(&msgh);
336 cmsg != NULL;
337 cmsg = CMSG_NXTHDR(&msgh,cmsg)) {
338
339 #ifdef HAVE_IP_PKTINFO
340 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_PKTINFO)) {
341 struct in_pktinfo *i = (struct in_pktinfo *) CMSG_DATA(cmsg);
342 if (to)
343 ((struct sockaddr_in *) to)->sin_addr = i->ipi_addr;
344 if (tolen)
345 *tolen = sizeof(struct sockaddr_in);
346 if (ifindex)
347 *ifindex = i->ipi_ifindex;
348
349 if (to && PIM_DEBUG_PACKETS) {
350 char to_str[100];
351 pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
352 zlog_debug("%s: HAVE_IP_PKTINFO to=%s,%d",
353 __PRETTY_FUNCTION__,
354 to_str, ntohs(to->sin_port));
355 }
356
357 break;
358 }
359 #endif
360
361 #ifdef HAVE_IP_RECVDSTADDR
362 if ((cmsg->cmsg_level == IPPROTO_IP) && (cmsg->cmsg_type == IP_RECVDSTADDR)) {
363 struct in_addr *i = (struct in_addr *) CMSG_DATA(cmsg);
364 if (to)
365 ((struct sockaddr_in *) to)->sin_addr = *i;
366 if (tolen)
367 *tolen = sizeof(struct sockaddr_in);
368
369 if (to && PIM_DEBUG_PACKETS) {
370 char to_str[100];
371 pim_inet4_dump("<to?>", to->sin_addr, to_str, sizeof(to_str));
372 zlog_debug("%s: HAVE_IP_RECVDSTADDR to=%s,%d",
373 __PRETTY_FUNCTION__,
374 to_str, ntohs(to->sin_port));
375 }
376
377 break;
378 }
379 #endif
380
381 #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
382 if (cmsg->cmsg_type == IP_RECVIF)
383 if (ifindex)
384 *ifindex = CMSG_IFINDEX(cmsg);
385 #endif
386
387 } /* for (cmsg) */
388
389 return err; /* len */
390 }
391
392 int pim_socket_mcastloop_get(int fd)
393 {
394 int loop;
395 socklen_t loop_len = sizeof(loop);
396
397 if (getsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP,
398 &loop, &loop_len)) {
399 int e = errno;
400 zlog_warn("Could not get Multicast Loopback Option on socket fd=%d: errno=%d: %s",
401 fd, errno, safe_strerror(errno));
402 errno = e;
403 return PIM_SOCK_ERR_LOOP;
404 }
405
406 return loop;
407 }
408
409 int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
410 {
411 if (getsockname(fd, name, namelen)) {
412 int e = errno;
413 zlog_warn("Could not get Socket Name for socket fd=%d: errno=%d: %s",
414 fd, errno, safe_strerror(errno));
415 errno = e;
416 return PIM_SOCK_ERR_NAME;
417 }
418
419 return PIM_SOCK_ERR_NONE;
420 }