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