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