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