]> git.proxmox.com Git - mirror_frr.git/blame - pimd/pim_sock.c
Merge pull request #12798 from donaldsharp/rib_match_multicast
[mirror_frr.git] / pimd / pim_sock.c
CommitLineData
acddc0ed 1// SPDX-License-Identifier: GPL-2.0-or-later
12e41d03 2/*
896014f4
DL
3 * PIM for Quagga
4 * Copyright (C) 2008 Everton da Silva Marques
896014f4 5 */
12e41d03 6
7a2fbbf0 7#include <zebra.h>
12e41d03
DL
8
9#include <sys/types.h>
10#include <sys/socket.h>
11#include <netinet/in.h>
12#include <netinet/igmp.h>
13#include <arpa/inet.h>
14#include <unistd.h>
15#include <netdb.h>
16#include <errno.h>
17
12e41d03
DL
18#include "log.h"
19#include "privs.h"
9df99407 20#include "if.h"
61ea3951 21#include "vrf.h"
c5bdb09f 22#include "sockopt.h"
3613d898 23#include "lib_errors.h"
023d3e4a 24#include "network.h"
12e41d03
DL
25
26#include "pimd.h"
993e3d8e 27#include "pim_instance.h"
9df99407 28#include "pim_mroute.h"
f2058cb4 29#include "pim_iface.h"
12e41d03
DL
30#include "pim_sock.h"
31#include "pim_str.h"
12e41d03 32
023d3e4a
BG
33#if PIM_IPV == 4
34#define setsockopt_iptos setsockopt_ipv4_tos
35#define setsockopt_multicast_loop setsockopt_ipv4_multicast_loop
36#else
37#define setsockopt_iptos setsockopt_ipv6_tclass
38#define setsockopt_multicast_loop setsockopt_ipv6_multicast_loop
39#endif
12e41d03 40
d62a17ae 41int pim_socket_raw(int protocol)
12e41d03 42{
d62a17ae 43 int fd;
12e41d03 44
0cf6db21 45 frr_with_privs(&pimd_privs) {
023d3e4a 46 fd = socket(PIM_AF, SOCK_RAW, protocol);
01b9e3fd 47 }
12e41d03 48
d62a17ae 49 if (fd < 0) {
50 zlog_warn("Could not create raw socket: errno=%d: %s", errno,
51 safe_strerror(errno));
52 return PIM_SOCK_ERR_SOCKET;
53 }
54
55 return fd;
12e41d03
DL
56}
57
e691f179 58void pim_socket_ip_hdr(int fd)
31c680fc 59{
0cf6db21 60 frr_with_privs(&pimd_privs) {
023d3e4a 61#if PIM_IPV == 4
529f5225
BG
62 const int on = 1;
63
01b9e3fd 64 if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)))
023d3e4a
BG
65 zlog_err("%s: Could not turn on IP_HDRINCL option: %m",
66 __func__);
023d3e4a 67#endif
01b9e3fd 68 }
31c680fc
DS
69}
70
728cd663
DS
71/*
72 * Given a socket and a interface,
73 * Bind that socket to that interface
74 */
d62a17ae 75int pim_socket_bind(int fd, struct interface *ifp)
728cd663 76{
d62a17ae 77 int ret = 0;
728cd663 78
023d3e4a 79#ifdef SO_BINDTODEVICE
0cf6db21 80 frr_with_privs(&pimd_privs) {
01b9e3fd
DL
81 ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name,
82 strlen(ifp->name));
01b9e3fd 83 }
f15bf553 84#endif
d62a17ae 85 return ret;
728cd663
DS
86}
87
023d3e4a
BG
88#if PIM_IPV == 4
89static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
12e41d03 90{
023d3e4a
BG
91 int one = 1;
92 int ttl = 1;
12e41d03 93
12e41d03 94#if defined(HAVE_IP_PKTINFO)
023d3e4a
BG
95 /* Linux and Solaris IP_PKTINFO */
96 if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)))
97 zlog_warn("Could not set PKTINFO on socket fd=%d: %m", fd);
12e41d03 98#elif defined(HAVE_IP_RECVDSTADDR)
023d3e4a
BG
99 /* BSD IP_RECVDSTADDR */
100 if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one)))
101 zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: %m",
102 fd);
12e41d03 103#else
023d3e4a
BG
104 flog_err(
105 EC_LIB_DEVELOPMENT,
106 "Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()");
107 close(fd);
108 return PIM_SOCK_ERR_DSTADDR;
12e41d03 109#endif
d62a17ae 110
023d3e4a
BG
111 /* Set router alert (RFC 2113) for all IGMP messages (RFC
112 * 3376 4. Message Formats)*/
d62a17ae 113 if (protocol == IPPROTO_IGMP) {
114 uint8_t ra[4];
023d3e4a 115
d62a17ae 116 ra[0] = 148;
117 ra[1] = 4;
118 ra[2] = 0;
119 ra[3] = 0;
120 if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
121 zlog_warn(
023d3e4a
BG
122 "Could not set Router Alert Option on socket fd=%d: %m",
123 fd);
d62a17ae 124 close(fd);
125 return PIM_SOCK_ERR_RA;
126 }
127 }
128
023d3e4a
BG
129 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) {
130 zlog_warn("Could not set multicast TTL=%d on socket fd=%d: %m",
131 ttl, fd);
132 close(fd);
133 return PIM_SOCK_ERR_TTL;
d62a17ae 134 }
135
023d3e4a 136 if (setsockopt_ipv4_multicast_if(fd, PIMADDR_ANY, ifp->ifindex)) {
d62a17ae 137 zlog_warn(
023d3e4a
BG
138 "Could not set Outgoing Interface Option on socket fd=%d: %m",
139 fd);
d62a17ae 140 close(fd);
023d3e4a 141 return PIM_SOCK_ERR_IFACE;
d62a17ae 142 }
143
023d3e4a
BG
144 return 0;
145}
146#else /* PIM_IPV != 4 */
147static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
148{
149 int ttl = 1;
150 struct ipv6_mreq mreq = {};
151
152 setsockopt_ipv6_pktinfo(fd, 1);
153 setsockopt_ipv6_multicast_hops(fd, ttl);
c2403b25 154
023d3e4a
BG
155 mreq.ipv6mr_interface = ifp->ifindex;
156 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq,
d62a17ae 157 sizeof(mreq))) {
158 zlog_warn(
023d3e4a
BG
159 "Could not set Outgoing Interface Option on socket fd=%d: %m",
160 fd);
d62a17ae 161 close(fd);
162 return PIM_SOCK_ERR_IFACE;
163 }
164
023d3e4a
BG
165 return 0;
166}
167#endif
d62a17ae 168
94619539
MR
169int pim_reg_sock(void)
170{
171 int fd;
172 long flags;
173
174 frr_with_privs (&pimd_privs) {
e23b5d86 175 fd = socket(PIM_AF, SOCK_RAW, PIM_PROTO_REG);
94619539
MR
176 }
177
178 if (fd < 0) {
179 zlog_warn("Could not create raw socket: errno=%d: %s", errno,
180 safe_strerror(errno));
181 return PIM_SOCK_ERR_SOCKET;
182 }
183
184 if (sockopt_reuseaddr(fd)) {
185 close(fd);
186 return PIM_SOCK_ERR_REUSE;
187 }
188
189 flags = fcntl(fd, F_GETFL, 0);
190 if (flags < 0) {
191 zlog_warn(
192 "Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
193 fd, errno, safe_strerror(errno));
194 close(fd);
195 return PIM_SOCK_ERR_NONBLOCK_GETFL;
196 }
197
198 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
199 zlog_warn(
200 "Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
201 fd, errno, safe_strerror(errno));
202 close(fd);
203 return PIM_SOCK_ERR_NONBLOCK_SETFL;
204 }
205
206 return fd;
207}
208
023d3e4a
BG
209int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
210 uint8_t loop)
211{
212 int fd;
213 int ret;
d62a17ae 214
023d3e4a
BG
215 fd = pim_socket_raw(protocol);
216 if (fd < 0) {
217 zlog_warn("Could not create multicast socket: errno=%d: %s",
218 errno, safe_strerror(errno));
219 return PIM_SOCK_ERR_SOCKET;
220 }
d62a17ae 221
023d3e4a
BG
222 /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF
223 * to emulate behaviour? Or change to only use 1 socket for all
224 * interfaces? */
225 ret = pim_socket_bind(fd, ifp);
226 if (ret) {
227 close(fd);
228 zlog_warn("Could not set fd: %d for interface: %s to device",
229 fd, ifp->name);
230 return PIM_SOCK_ERR_BIND;
d62a17ae 231 }
232
023d3e4a
BG
233 set_nonblocking(fd);
234 sockopt_reuseaddr(fd);
235 setsockopt_so_recvbuf(fd, 8 * 1024 * 1024);
236
237 ret = pim_setsockopt(protocol, fd, ifp);
238 if (ret) {
239 zlog_warn("pim_setsockopt failed for interface: %s to device ",
240 ifp->name);
241 return ret;
9036d0ef
SK
242 }
243
023d3e4a
BG
244 /* leftover common sockopts */
245 if (setsockopt_multicast_loop(fd, loop)) {
246 zlog_warn(
247 "Could not %s Multicast Loopback Option on socket fd=%d: %m",
248 loop ? "enable" : "disable", fd);
249 close(fd);
250 return PIM_SOCK_ERR_LOOP;
251 }
252
253 /* Set Tx socket DSCP byte */
254 if (setsockopt_iptos(fd, IPTOS_PREC_INTERNETCONTROL))
255 zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd);
256
d62a17ae 257 return fd;
12e41d03
DL
258}
259
f2058cb4
DA
260int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
261 struct pim_interface *pim_ifp)
12e41d03 262{
d62a17ae 263 int ret;
12e41d03 264
023d3e4a
BG
265#if PIM_IPV == 4
266 ret = setsockopt_ipv4_multicast(fd, IP_ADD_MEMBERSHIP, ifaddr,
267 group.s_addr, ifindex);
12e41d03 268#else
023d3e4a 269 struct ipv6_mreq opt;
12e41d03 270
023d3e4a
BG
271 memcpy(&opt.ipv6mr_multiaddr, &group, 16);
272 opt.ipv6mr_interface = ifindex;
273 ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &opt, sizeof(opt));
12e41d03
DL
274#endif
275
f2058cb4
DA
276 pim_ifp->igmp_ifstat_joins_sent++;
277
d62a17ae 278 if (ret) {
af4c2728 279 flog_err(
450971aa 280 EC_LIB_SOCKET,
023d3e4a
BG
281 "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m",
282 fd, &group, &ifaddr);
f2058cb4 283 pim_ifp->igmp_ifstat_joins_failed++;
d62a17ae 284 return ret;
285 }
286
023d3e4a 287 if (PIM_DEBUG_TRACE)
d62a17ae 288 zlog_debug(
023d3e4a
BG
289 "Socket fd=%d joined group %pPAs on interface address %pPAs",
290 fd, &group, &ifaddr);
291 return ret;
292}
293
294#if PIM_IPV == 4
295static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
296 ifindex_t *ifindex)
297{
298 struct cmsghdr *cmsg;
299 struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
300
301 for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
302 cmsg = CMSG_NXTHDR(mh, cmsg)) {
303#ifdef HAVE_IP_PKTINFO
304 if ((cmsg->cmsg_level == IPPROTO_IP) &&
305 (cmsg->cmsg_type == IP_PKTINFO)) {
306 struct in_pktinfo *i;
307
308 i = (struct in_pktinfo *)CMSG_DATA(cmsg);
309 if (dst4)
310 dst4->sin_addr = i->ipi_addr;
311 if (ifindex)
312 *ifindex = i->ipi_ifindex;
313
314 break;
315 }
316#endif
317
318#ifdef HAVE_IP_RECVDSTADDR
319 if ((cmsg->cmsg_level == IPPROTO_IP) &&
320 (cmsg->cmsg_type == IP_RECVDSTADDR)) {
321 struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
322
323 if (dst4)
324 dst4->sin_addr = *i;
325
326 break;
327 }
328#endif
329
330#if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
331 if (cmsg->cmsg_type == IP_RECVIF)
332 if (ifindex)
333 *ifindex = CMSG_IFINDEX(cmsg);
334#endif
d62a17ae 335 }
023d3e4a
BG
336}
337#else /* PIM_IPV != 4 */
338static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
339 ifindex_t *ifindex)
340{
341 struct cmsghdr *cmsg;
342 struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
d62a17ae 343
023d3e4a
BG
344 for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
345 cmsg = CMSG_NXTHDR(mh, cmsg)) {
346 if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
347 (cmsg->cmsg_type == IPV6_PKTINFO)) {
348 struct in6_pktinfo *i;
349
350 i = (struct in6_pktinfo *)CMSG_DATA(cmsg);
351
352 if (dst6)
353 dst6->sin6_addr = i->ipi6_addr;
354 if (ifindex)
355 *ifindex = i->ipi6_ifindex;
356 break;
357 }
358 }
12e41d03 359}
023d3e4a 360#endif /* PIM_IPV != 4 */
12e41d03 361
12e41d03 362int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
023d3e4a
BG
363 struct sockaddr_storage *from, socklen_t *fromlen,
364 struct sockaddr_storage *to, socklen_t *tolen,
b892f1dd 365 ifindex_t *ifindex)
12e41d03 366{
d62a17ae 367 struct msghdr msgh;
d62a17ae 368 struct iovec iov;
369 char cbuf[1000];
370 int err;
371
372 /*
373 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
374 * Use getsockname() to get sin_port.
375 */
376 if (to) {
023d3e4a 377 socklen_t to_len = sizeof(*to);
d62a17ae 378
023d3e4a 379 pim_socket_getsockname(fd, (struct sockaddr *)to, &to_len);
d62a17ae 380
381 if (tolen)
023d3e4a 382 *tolen = sizeof(*to);
d62a17ae 383 }
384
6006b807 385 memset(&msgh, 0, sizeof(msgh));
d62a17ae 386 iov.iov_base = buf;
387 iov.iov_len = len;
388 msgh.msg_control = cbuf;
389 msgh.msg_controllen = sizeof(cbuf);
390 msgh.msg_name = from;
391 msgh.msg_namelen = fromlen ? *fromlen : 0;
392 msgh.msg_iov = &iov;
393 msgh.msg_iovlen = 1;
394 msgh.msg_flags = 0;
395
396 err = recvmsg(fd, &msgh, 0);
397 if (err < 0)
398 return err;
399
400 if (fromlen)
401 *fromlen = msgh.msg_namelen;
402
023d3e4a 403 cmsg_getdstaddr(&msgh, to, ifindex);
12e41d03 404
d62a17ae 405 return err; /* len */
12e41d03
DL
406}
407
12e41d03
DL
408int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
409{
d62a17ae 410 if (getsockname(fd, name, namelen)) {
411 int e = errno;
412 zlog_warn(
413 "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;
12e41d03 420}