]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_sock.c
doc: Add `show ipv6 rpf X:X::X:X` command to docs
[mirror_frr.git] / pimd / pim_sock.c
1 /*
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 */
19
20 #include <zebra.h>
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
31 #include "log.h"
32 #include "privs.h"
33 #include "if.h"
34 #include "vrf.h"
35 #include "sockopt.h"
36 #include "lib_errors.h"
37 #include "network.h"
38
39 #include "pimd.h"
40 #include "pim_instance.h"
41 #include "pim_mroute.h"
42 #include "pim_iface.h"
43 #include "pim_sock.h"
44 #include "pim_str.h"
45
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
53
54 int pim_socket_raw(int protocol)
55 {
56 int fd;
57
58 frr_with_privs(&pimd_privs) {
59 fd = socket(PIM_AF, SOCK_RAW, protocol);
60 }
61
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;
69 }
70
71 void pim_socket_ip_hdr(int fd)
72 {
73 frr_with_privs(&pimd_privs) {
74 #if PIM_IPV == 4
75 const int on = 1;
76
77 if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)))
78 zlog_err("%s: Could not turn on IP_HDRINCL option: %m",
79 __func__);
80 #endif
81 }
82 }
83
84 /*
85 * Given a socket and a interface,
86 * Bind that socket to that interface
87 */
88 int pim_socket_bind(int fd, struct interface *ifp)
89 {
90 int ret = 0;
91
92 #ifdef SO_BINDTODEVICE
93 frr_with_privs(&pimd_privs) {
94 ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name,
95 strlen(ifp->name));
96 }
97 #endif
98 return ret;
99 }
100
101 #if PIM_IPV == 4
102 static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
103 {
104 int one = 1;
105 int ttl = 1;
106
107 #if defined(HAVE_IP_PKTINFO)
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);
111 #elif defined(HAVE_IP_RECVDSTADDR)
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);
116 #else
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;
122 #endif
123
124 /* Set router alert (RFC 2113) for all IGMP messages (RFC
125 * 3376 4. Message Formats)*/
126 if (protocol == IPPROTO_IGMP) {
127 uint8_t ra[4];
128
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(
135 "Could not set Router Alert Option on socket fd=%d: %m",
136 fd);
137 close(fd);
138 return PIM_SOCK_ERR_RA;
139 }
140 }
141
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;
147 }
148
149 if (setsockopt_ipv4_multicast_if(fd, PIMADDR_ANY, ifp->ifindex)) {
150 zlog_warn(
151 "Could not set Outgoing Interface Option on socket fd=%d: %m",
152 fd);
153 close(fd);
154 return PIM_SOCK_ERR_IFACE;
155 }
156
157 return 0;
158 }
159 #else /* PIM_IPV != 4 */
160 static 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);
167
168 mreq.ipv6mr_interface = ifp->ifindex;
169 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq,
170 sizeof(mreq))) {
171 zlog_warn(
172 "Could not set Outgoing Interface Option on socket fd=%d: %m",
173 fd);
174 close(fd);
175 return PIM_SOCK_ERR_IFACE;
176 }
177
178 return 0;
179 }
180 #endif
181
182 int pim_reg_sock(void)
183 {
184 int fd;
185 long flags;
186
187 frr_with_privs (&pimd_privs) {
188 fd = socket(PIM_AF, SOCK_RAW, PIM_PROTO_REG);
189 }
190
191 if (fd < 0) {
192 zlog_warn("Could not create raw socket: errno=%d: %s", errno,
193 safe_strerror(errno));
194 return PIM_SOCK_ERR_SOCKET;
195 }
196
197 if (sockopt_reuseaddr(fd)) {
198 close(fd);
199 return PIM_SOCK_ERR_REUSE;
200 }
201
202 flags = fcntl(fd, F_GETFL, 0);
203 if (flags < 0) {
204 zlog_warn(
205 "Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
206 fd, errno, safe_strerror(errno));
207 close(fd);
208 return PIM_SOCK_ERR_NONBLOCK_GETFL;
209 }
210
211 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
212 zlog_warn(
213 "Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
214 fd, errno, safe_strerror(errno));
215 close(fd);
216 return PIM_SOCK_ERR_NONBLOCK_SETFL;
217 }
218
219 return fd;
220 }
221
222 int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
223 uint8_t loop)
224 {
225 int fd;
226 int ret;
227
228 fd = pim_socket_raw(protocol);
229 if (fd < 0) {
230 zlog_warn("Could not create multicast socket: errno=%d: %s",
231 errno, safe_strerror(errno));
232 return PIM_SOCK_ERR_SOCKET;
233 }
234
235 /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF
236 * to emulate behaviour? Or change to only use 1 socket for all
237 * interfaces? */
238 ret = pim_socket_bind(fd, ifp);
239 if (ret) {
240 close(fd);
241 zlog_warn("Could not set fd: %d for interface: %s to device",
242 fd, ifp->name);
243 return PIM_SOCK_ERR_BIND;
244 }
245
246 set_nonblocking(fd);
247 sockopt_reuseaddr(fd);
248 setsockopt_so_recvbuf(fd, 8 * 1024 * 1024);
249
250 ret = pim_setsockopt(protocol, fd, ifp);
251 if (ret) {
252 zlog_warn("pim_setsockopt failed for interface: %s to device ",
253 ifp->name);
254 return ret;
255 }
256
257 /* leftover common sockopts */
258 if (setsockopt_multicast_loop(fd, loop)) {
259 zlog_warn(
260 "Could not %s Multicast Loopback Option on socket fd=%d: %m",
261 loop ? "enable" : "disable", fd);
262 close(fd);
263 return PIM_SOCK_ERR_LOOP;
264 }
265
266 /* Set Tx socket DSCP byte */
267 if (setsockopt_iptos(fd, IPTOS_PREC_INTERNETCONTROL))
268 zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd);
269
270 return fd;
271 }
272
273 int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
274 struct pim_interface *pim_ifp)
275 {
276 int ret;
277
278 #if PIM_IPV == 4
279 ret = setsockopt_ipv4_multicast(fd, IP_ADD_MEMBERSHIP, ifaddr,
280 group.s_addr, ifindex);
281 #else
282 struct ipv6_mreq opt;
283
284 memcpy(&opt.ipv6mr_multiaddr, &group, 16);
285 opt.ipv6mr_interface = ifindex;
286 ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &opt, sizeof(opt));
287 #endif
288
289 pim_ifp->igmp_ifstat_joins_sent++;
290
291 if (ret) {
292 flog_err(
293 EC_LIB_SOCKET,
294 "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m",
295 fd, &group, &ifaddr);
296 pim_ifp->igmp_ifstat_joins_failed++;
297 return ret;
298 }
299
300 if (PIM_DEBUG_TRACE)
301 zlog_debug(
302 "Socket fd=%d joined group %pPAs on interface address %pPAs",
303 fd, &group, &ifaddr);
304 return ret;
305 }
306
307 #if PIM_IPV == 4
308 static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
309 ifindex_t *ifindex)
310 {
311 struct cmsghdr *cmsg;
312 struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
313
314 for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
315 cmsg = CMSG_NXTHDR(mh, cmsg)) {
316 #ifdef HAVE_IP_PKTINFO
317 if ((cmsg->cmsg_level == IPPROTO_IP) &&
318 (cmsg->cmsg_type == IP_PKTINFO)) {
319 struct in_pktinfo *i;
320
321 i = (struct in_pktinfo *)CMSG_DATA(cmsg);
322 if (dst4)
323 dst4->sin_addr = i->ipi_addr;
324 if (ifindex)
325 *ifindex = i->ipi_ifindex;
326
327 break;
328 }
329 #endif
330
331 #ifdef HAVE_IP_RECVDSTADDR
332 if ((cmsg->cmsg_level == IPPROTO_IP) &&
333 (cmsg->cmsg_type == IP_RECVDSTADDR)) {
334 struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
335
336 if (dst4)
337 dst4->sin_addr = *i;
338
339 break;
340 }
341 #endif
342
343 #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
344 if (cmsg->cmsg_type == IP_RECVIF)
345 if (ifindex)
346 *ifindex = CMSG_IFINDEX(cmsg);
347 #endif
348 }
349 }
350 #else /* PIM_IPV != 4 */
351 static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
352 ifindex_t *ifindex)
353 {
354 struct cmsghdr *cmsg;
355 struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
356
357 for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
358 cmsg = CMSG_NXTHDR(mh, cmsg)) {
359 if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
360 (cmsg->cmsg_type == IPV6_PKTINFO)) {
361 struct in6_pktinfo *i;
362
363 i = (struct in6_pktinfo *)CMSG_DATA(cmsg);
364
365 if (dst6)
366 dst6->sin6_addr = i->ipi6_addr;
367 if (ifindex)
368 *ifindex = i->ipi6_ifindex;
369 break;
370 }
371 }
372 }
373 #endif /* PIM_IPV != 4 */
374
375 int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
376 struct sockaddr_storage *from, socklen_t *fromlen,
377 struct sockaddr_storage *to, socklen_t *tolen,
378 ifindex_t *ifindex)
379 {
380 struct msghdr msgh;
381 struct iovec iov;
382 char cbuf[1000];
383 int err;
384
385 /*
386 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
387 * Use getsockname() to get sin_port.
388 */
389 if (to) {
390 socklen_t to_len = sizeof(*to);
391
392 pim_socket_getsockname(fd, (struct sockaddr *)to, &to_len);
393
394 if (tolen)
395 *tolen = sizeof(*to);
396 }
397
398 memset(&msgh, 0, sizeof(msgh));
399 iov.iov_base = buf;
400 iov.iov_len = len;
401 msgh.msg_control = cbuf;
402 msgh.msg_controllen = sizeof(cbuf);
403 msgh.msg_name = from;
404 msgh.msg_namelen = fromlen ? *fromlen : 0;
405 msgh.msg_iov = &iov;
406 msgh.msg_iovlen = 1;
407 msgh.msg_flags = 0;
408
409 err = recvmsg(fd, &msgh, 0);
410 if (err < 0)
411 return err;
412
413 if (fromlen)
414 *fromlen = msgh.msg_namelen;
415
416 cmsg_getdstaddr(&msgh, to, ifindex);
417
418 return err; /* len */
419 }
420
421 int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
422 {
423 if (getsockname(fd, name, namelen)) {
424 int e = errno;
425 zlog_warn(
426 "Could not get Socket Name for socket fd=%d: errno=%d: %s",
427 fd, errno, safe_strerror(errno));
428 errno = e;
429 return PIM_SOCK_ERR_NAME;
430 }
431
432 return PIM_SOCK_ERR_NONE;
433 }