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