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