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