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