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