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