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