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