Line data Source code
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 : #include "lib_errors.h"
37 : #include "network.h"
38 :
39 : #include "pimd.h"
40 : #include "pim_instance.h"
41 : #include "pim_mroute.h"
42 : #include "pim_iface.h"
43 : #include "pim_sock.h"
44 : #include "pim_str.h"
45 :
46 : #if PIM_IPV == 4
47 : #define setsockopt_iptos setsockopt_ipv4_tos
48 : #define setsockopt_multicast_loop setsockopt_ipv4_multicast_loop
49 : #else
50 : #define setsockopt_iptos setsockopt_ipv6_tclass
51 : #define setsockopt_multicast_loop setsockopt_ipv6_multicast_loop
52 : #endif
53 :
54 4 : int pim_socket_raw(int protocol)
55 : {
56 4 : int fd;
57 :
58 4 : frr_with_privs(&pimd_privs) {
59 4 : fd = socket(PIM_AF, SOCK_RAW, protocol);
60 : }
61 :
62 4 : if (fd < 0) {
63 0 : zlog_warn("Could not create raw socket: errno=%d: %s", errno,
64 : safe_strerror(errno));
65 0 : return PIM_SOCK_ERR_SOCKET;
66 : }
67 :
68 : return fd;
69 : }
70 :
71 4 : void pim_socket_ip_hdr(int fd)
72 : {
73 8 : frr_with_privs(&pimd_privs) {
74 : #if PIM_IPV == 4
75 : const int on = 1;
76 :
77 : if (setsockopt(fd, IPPROTO_IP, IP_HDRINCL, &on, sizeof(on)))
78 : zlog_err("%s: Could not turn on IP_HDRINCL option: %m",
79 : __func__);
80 : #endif
81 : }
82 4 : }
83 :
84 : /*
85 : * Given a socket and a interface,
86 : * Bind that socket to that interface
87 : */
88 4 : int pim_socket_bind(int fd, struct interface *ifp)
89 : {
90 4 : int ret = 0;
91 :
92 : #ifdef SO_BINDTODEVICE
93 4 : frr_with_privs(&pimd_privs) {
94 4 : ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name,
95 4 : strlen(ifp->name));
96 : }
97 : #endif
98 4 : return ret;
99 : }
100 :
101 : #if PIM_IPV == 4
102 : static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
103 : {
104 : int one = 1;
105 : int ttl = 1;
106 :
107 : #if defined(HAVE_IP_PKTINFO)
108 : /* Linux and Solaris IP_PKTINFO */
109 : if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &one, sizeof(one)))
110 : zlog_warn("Could not set PKTINFO on socket fd=%d: %m", fd);
111 : #elif defined(HAVE_IP_RECVDSTADDR)
112 : /* BSD IP_RECVDSTADDR */
113 : if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &one, sizeof(one)))
114 : zlog_warn("Could not set IP_RECVDSTADDR on socket fd=%d: %m",
115 : fd);
116 : #else
117 : flog_err(
118 : EC_LIB_DEVELOPMENT,
119 : "Missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()");
120 : close(fd);
121 : return PIM_SOCK_ERR_DSTADDR;
122 : #endif
123 :
124 : /* Set router alert (RFC 2113) for all IGMP messages (RFC
125 : * 3376 4. Message Formats)*/
126 : if (protocol == IPPROTO_IGMP) {
127 : uint8_t ra[4];
128 :
129 : ra[0] = 148;
130 : ra[1] = 4;
131 : ra[2] = 0;
132 : ra[3] = 0;
133 : if (setsockopt(fd, IPPROTO_IP, IP_OPTIONS, ra, 4)) {
134 : zlog_warn(
135 : "Could not set Router Alert Option on socket fd=%d: %m",
136 : fd);
137 : close(fd);
138 : return PIM_SOCK_ERR_RA;
139 : }
140 : }
141 :
142 : if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) {
143 : zlog_warn("Could not set multicast TTL=%d on socket fd=%d: %m",
144 : ttl, fd);
145 : close(fd);
146 : return PIM_SOCK_ERR_TTL;
147 : }
148 :
149 : if (setsockopt_ipv4_multicast_if(fd, PIMADDR_ANY, ifp->ifindex)) {
150 : zlog_warn(
151 : "Could not set Outgoing Interface Option on socket fd=%d: %m",
152 : fd);
153 : close(fd);
154 : return PIM_SOCK_ERR_IFACE;
155 : }
156 :
157 : return 0;
158 : }
159 : #else /* PIM_IPV != 4 */
160 4 : static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
161 : {
162 4 : int ttl = 1;
163 4 : struct ipv6_mreq mreq = {};
164 :
165 4 : setsockopt_ipv6_pktinfo(fd, 1);
166 4 : setsockopt_ipv6_multicast_hops(fd, ttl);
167 :
168 4 : mreq.ipv6mr_interface = ifp->ifindex;
169 4 : if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq,
170 : sizeof(mreq))) {
171 0 : zlog_warn(
172 : "Could not set Outgoing Interface Option on socket fd=%d: %m",
173 : fd);
174 0 : close(fd);
175 0 : return PIM_SOCK_ERR_IFACE;
176 : }
177 :
178 : return 0;
179 : }
180 : #endif
181 :
182 2 : int pim_reg_sock(void)
183 : {
184 2 : int fd;
185 2 : long flags;
186 :
187 2 : frr_with_privs (&pimd_privs) {
188 2 : fd = socket(PIM_AF, SOCK_RAW, PIM_PROTO_REG);
189 : }
190 :
191 2 : if (fd < 0) {
192 0 : zlog_warn("Could not create raw socket: errno=%d: %s", errno,
193 : safe_strerror(errno));
194 0 : return PIM_SOCK_ERR_SOCKET;
195 : }
196 :
197 2 : if (sockopt_reuseaddr(fd)) {
198 0 : close(fd);
199 0 : return PIM_SOCK_ERR_REUSE;
200 : }
201 :
202 2 : flags = fcntl(fd, F_GETFL, 0);
203 2 : if (flags < 0) {
204 0 : zlog_warn(
205 : "Could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
206 : fd, errno, safe_strerror(errno));
207 0 : close(fd);
208 0 : return PIM_SOCK_ERR_NONBLOCK_GETFL;
209 : }
210 :
211 2 : if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
212 0 : zlog_warn(
213 : "Could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
214 : fd, errno, safe_strerror(errno));
215 0 : close(fd);
216 0 : return PIM_SOCK_ERR_NONBLOCK_SETFL;
217 : }
218 :
219 : return fd;
220 : }
221 :
222 4 : int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
223 : uint8_t loop)
224 : {
225 4 : int fd;
226 4 : int ret;
227 :
228 4 : fd = pim_socket_raw(protocol);
229 4 : if (fd < 0) {
230 0 : zlog_warn("Could not create multicast socket: errno=%d: %s",
231 : errno, safe_strerror(errno));
232 0 : return PIM_SOCK_ERR_SOCKET;
233 : }
234 :
235 : /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF
236 : * to emulate behaviour? Or change to only use 1 socket for all
237 : * interfaces? */
238 4 : ret = pim_socket_bind(fd, ifp);
239 4 : if (ret) {
240 0 : close(fd);
241 0 : zlog_warn("Could not set fd: %d for interface: %s to device",
242 : fd, ifp->name);
243 0 : return PIM_SOCK_ERR_BIND;
244 : }
245 :
246 4 : set_nonblocking(fd);
247 4 : sockopt_reuseaddr(fd);
248 4 : setsockopt_so_recvbuf(fd, 8 * 1024 * 1024);
249 :
250 4 : ret = pim_setsockopt(protocol, fd, ifp);
251 4 : if (ret) {
252 0 : zlog_warn("pim_setsockopt failed for interface: %s to device ",
253 : ifp->name);
254 0 : return ret;
255 : }
256 :
257 : /* leftover common sockopts */
258 4 : if (setsockopt_multicast_loop(fd, loop)) {
259 0 : zlog_warn(
260 : "Could not %s Multicast Loopback Option on socket fd=%d: %m",
261 : loop ? "enable" : "disable", fd);
262 0 : close(fd);
263 0 : return PIM_SOCK_ERR_LOOP;
264 : }
265 :
266 : /* Set Tx socket DSCP byte */
267 4 : if (setsockopt_iptos(fd, IPTOS_PREC_INTERNETCONTROL))
268 0 : zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd);
269 :
270 : return fd;
271 : }
272 :
273 4 : int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
274 : struct pim_interface *pim_ifp)
275 : {
276 4 : int ret;
277 :
278 : #if PIM_IPV == 4
279 : ret = setsockopt_ipv4_multicast(fd, IP_ADD_MEMBERSHIP, ifaddr,
280 : group.s_addr, ifindex);
281 : #else
282 4 : struct ipv6_mreq opt;
283 :
284 4 : memcpy(&opt.ipv6mr_multiaddr, &group, 16);
285 4 : opt.ipv6mr_interface = ifindex;
286 4 : ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &opt, sizeof(opt));
287 : #endif
288 :
289 4 : pim_ifp->igmp_ifstat_joins_sent++;
290 :
291 4 : if (ret) {
292 0 : flog_err(
293 : EC_LIB_SOCKET,
294 : "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m",
295 : fd, &group, &ifaddr);
296 0 : pim_ifp->igmp_ifstat_joins_failed++;
297 0 : return ret;
298 : }
299 :
300 4 : if (PIM_DEBUG_TRACE)
301 4 : zlog_debug(
302 : "Socket fd=%d joined group %pPAs on interface address %pPAs",
303 : fd, &group, &ifaddr);
304 : return ret;
305 : }
306 :
307 : #if PIM_IPV == 4
308 : static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
309 : ifindex_t *ifindex)
310 : {
311 : struct cmsghdr *cmsg;
312 : struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
313 :
314 : for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
315 : cmsg = CMSG_NXTHDR(mh, cmsg)) {
316 : #ifdef HAVE_IP_PKTINFO
317 : if ((cmsg->cmsg_level == IPPROTO_IP) &&
318 : (cmsg->cmsg_type == IP_PKTINFO)) {
319 : struct in_pktinfo *i;
320 :
321 : i = (struct in_pktinfo *)CMSG_DATA(cmsg);
322 : if (dst4)
323 : dst4->sin_addr = i->ipi_addr;
324 : if (ifindex)
325 : *ifindex = i->ipi_ifindex;
326 :
327 : break;
328 : }
329 : #endif
330 :
331 : #ifdef HAVE_IP_RECVDSTADDR
332 : if ((cmsg->cmsg_level == IPPROTO_IP) &&
333 : (cmsg->cmsg_type == IP_RECVDSTADDR)) {
334 : struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
335 :
336 : if (dst4)
337 : dst4->sin_addr = *i;
338 :
339 : break;
340 : }
341 : #endif
342 :
343 : #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
344 : if (cmsg->cmsg_type == IP_RECVIF)
345 : if (ifindex)
346 : *ifindex = CMSG_IFINDEX(cmsg);
347 : #endif
348 : }
349 : }
350 : #else /* PIM_IPV != 4 */
351 11 : static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
352 : ifindex_t *ifindex)
353 : {
354 11 : struct cmsghdr *cmsg;
355 11 : struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
356 :
357 11 : for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
358 11 : cmsg = CMSG_NXTHDR(mh, cmsg)) {
359 11 : if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
360 : (cmsg->cmsg_type == IPV6_PKTINFO)) {
361 11 : struct in6_pktinfo *i;
362 :
363 11 : i = (struct in6_pktinfo *)CMSG_DATA(cmsg);
364 :
365 11 : if (dst6)
366 11 : dst6->sin6_addr = i->ipi6_addr;
367 11 : if (ifindex)
368 11 : *ifindex = i->ipi6_ifindex;
369 : break;
370 : }
371 : }
372 11 : }
373 : #endif /* PIM_IPV != 4 */
374 :
375 19 : int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
376 : struct sockaddr_storage *from, socklen_t *fromlen,
377 : struct sockaddr_storage *to, socklen_t *tolen,
378 : ifindex_t *ifindex)
379 : {
380 19 : struct msghdr msgh;
381 19 : struct iovec iov;
382 19 : char cbuf[1000];
383 19 : int err;
384 :
385 : /*
386 : * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
387 : * Use getsockname() to get sin_port.
388 : */
389 19 : if (to) {
390 19 : socklen_t to_len = sizeof(*to);
391 :
392 19 : pim_socket_getsockname(fd, (struct sockaddr *)to, &to_len);
393 :
394 19 : if (tolen)
395 19 : *tolen = sizeof(*to);
396 : }
397 :
398 19 : memset(&msgh, 0, sizeof(msgh));
399 19 : iov.iov_base = buf;
400 19 : iov.iov_len = len;
401 19 : msgh.msg_control = cbuf;
402 19 : msgh.msg_controllen = sizeof(cbuf);
403 19 : msgh.msg_name = from;
404 19 : msgh.msg_namelen = fromlen ? *fromlen : 0;
405 19 : msgh.msg_iov = &iov;
406 19 : msgh.msg_iovlen = 1;
407 19 : msgh.msg_flags = 0;
408 :
409 19 : err = recvmsg(fd, &msgh, 0);
410 19 : if (err < 0)
411 : return err;
412 :
413 11 : if (fromlen)
414 11 : *fromlen = msgh.msg_namelen;
415 :
416 11 : cmsg_getdstaddr(&msgh, to, ifindex);
417 :
418 11 : return err; /* len */
419 : }
420 :
421 19 : int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
422 : {
423 19 : if (getsockname(fd, name, namelen)) {
424 0 : int e = errno;
425 0 : zlog_warn(
426 : "Could not get Socket Name for socket fd=%d: errno=%d: %s",
427 : fd, errno, safe_strerror(errno));
428 0 : errno = e;
429 0 : return PIM_SOCK_ERR_NAME;
430 : }
431 :
432 : return PIM_SOCK_ERR_NONE;
433 : }
|