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