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