]> git.proxmox.com Git - mirror_frr.git/blob - pimd/pim_sock.c
Merge pull request #10962 from louis-6wind/lfa-netlink
[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 #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 int pim_socket_raw(int protocol)
55 {
56 int fd;
57
58 frr_with_privs(&pimd_privs) {
59 fd = socket(PIM_AF, SOCK_RAW, protocol);
60 }
61
62 if (fd < 0) {
63 zlog_warn("Could not create raw socket: errno=%d: %s", errno,
64 safe_strerror(errno));
65 return PIM_SOCK_ERR_SOCKET;
66 }
67
68 return fd;
69 }
70
71 void pim_socket_ip_hdr(int fd)
72 {
73 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 }
83
84 /*
85 * Given a socket and a interface,
86 * Bind that socket to that interface
87 */
88 int pim_socket_bind(int fd, struct interface *ifp)
89 {
90 int ret = 0;
91
92 #ifdef SO_BINDTODEVICE
93 frr_with_privs(&pimd_privs) {
94 ret = setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, ifp->name,
95 strlen(ifp->name));
96 }
97 #endif
98 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 static inline int pim_setsockopt(int protocol, int fd, struct interface *ifp)
161 {
162 int ttl = 1;
163 struct ipv6_mreq mreq = {};
164
165 setsockopt_ipv6_pktinfo(fd, 1);
166 setsockopt_ipv6_multicast_hops(fd, ttl);
167
168 mreq.ipv6mr_interface = ifp->ifindex;
169 if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &mreq,
170 sizeof(mreq))) {
171 zlog_warn(
172 "Could not set Outgoing Interface Option on socket fd=%d: %m",
173 fd);
174 close(fd);
175 return PIM_SOCK_ERR_IFACE;
176 }
177
178 return 0;
179 }
180 #endif
181
182 int pim_socket_mcast(int protocol, pim_addr ifaddr, struct interface *ifp,
183 uint8_t loop)
184 {
185 int fd;
186 int ret;
187
188 fd = pim_socket_raw(protocol);
189 if (fd < 0) {
190 zlog_warn("Could not create multicast socket: errno=%d: %s",
191 errno, safe_strerror(errno));
192 return PIM_SOCK_ERR_SOCKET;
193 }
194
195 /* XXX: if SO_BINDTODEVICE isn't available, use IP_PKTINFO / IP_RECVIF
196 * to emulate behaviour? Or change to only use 1 socket for all
197 * interfaces? */
198 ret = pim_socket_bind(fd, ifp);
199 if (ret) {
200 close(fd);
201 zlog_warn("Could not set fd: %d for interface: %s to device",
202 fd, ifp->name);
203 return PIM_SOCK_ERR_BIND;
204 }
205
206 set_nonblocking(fd);
207 sockopt_reuseaddr(fd);
208 setsockopt_so_recvbuf(fd, 8 * 1024 * 1024);
209
210 ret = pim_setsockopt(protocol, fd, ifp);
211 if (ret) {
212 zlog_warn("pim_setsockopt failed for interface: %s to device ",
213 ifp->name);
214 return ret;
215 }
216
217 /* leftover common sockopts */
218 if (setsockopt_multicast_loop(fd, loop)) {
219 zlog_warn(
220 "Could not %s Multicast Loopback Option on socket fd=%d: %m",
221 loop ? "enable" : "disable", fd);
222 close(fd);
223 return PIM_SOCK_ERR_LOOP;
224 }
225
226 /* Set Tx socket DSCP byte */
227 if (setsockopt_iptos(fd, IPTOS_PREC_INTERNETCONTROL))
228 zlog_warn("can't set sockopt IP[V6]_TOS to socket %d: %m", fd);
229
230 return fd;
231 }
232
233 int pim_socket_join(int fd, pim_addr group, pim_addr ifaddr, ifindex_t ifindex,
234 struct pim_interface *pim_ifp)
235 {
236 int ret;
237
238 #if PIM_IPV == 4
239 ret = setsockopt_ipv4_multicast(fd, IP_ADD_MEMBERSHIP, ifaddr,
240 group.s_addr, ifindex);
241 #else
242 struct ipv6_mreq opt;
243
244 memcpy(&opt.ipv6mr_multiaddr, &group, 16);
245 opt.ipv6mr_interface = ifindex;
246 ret = setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &opt, sizeof(opt));
247 #endif
248
249 pim_ifp->igmp_ifstat_joins_sent++;
250
251 if (ret) {
252 flog_err(
253 EC_LIB_SOCKET,
254 "Failure socket joining fd=%d group %pPAs on interface address %pPAs: %m",
255 fd, &group, &ifaddr);
256 pim_ifp->igmp_ifstat_joins_failed++;
257 return ret;
258 }
259
260 if (PIM_DEBUG_TRACE)
261 zlog_debug(
262 "Socket fd=%d joined group %pPAs on interface address %pPAs",
263 fd, &group, &ifaddr);
264 return ret;
265 }
266
267 #if PIM_IPV == 4
268 static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
269 ifindex_t *ifindex)
270 {
271 struct cmsghdr *cmsg;
272 struct sockaddr_in *dst4 = (struct sockaddr_in *)dst;
273
274 for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
275 cmsg = CMSG_NXTHDR(mh, cmsg)) {
276 #ifdef HAVE_IP_PKTINFO
277 if ((cmsg->cmsg_level == IPPROTO_IP) &&
278 (cmsg->cmsg_type == IP_PKTINFO)) {
279 struct in_pktinfo *i;
280
281 i = (struct in_pktinfo *)CMSG_DATA(cmsg);
282 if (dst4)
283 dst4->sin_addr = i->ipi_addr;
284 if (ifindex)
285 *ifindex = i->ipi_ifindex;
286
287 break;
288 }
289 #endif
290
291 #ifdef HAVE_IP_RECVDSTADDR
292 if ((cmsg->cmsg_level == IPPROTO_IP) &&
293 (cmsg->cmsg_type == IP_RECVDSTADDR)) {
294 struct in_addr *i = (struct in_addr *)CMSG_DATA(cmsg);
295
296 if (dst4)
297 dst4->sin_addr = *i;
298
299 break;
300 }
301 #endif
302
303 #if defined(HAVE_IP_RECVIF) && defined(CMSG_IFINDEX)
304 if (cmsg->cmsg_type == IP_RECVIF)
305 if (ifindex)
306 *ifindex = CMSG_IFINDEX(cmsg);
307 #endif
308 }
309 }
310 #else /* PIM_IPV != 4 */
311 static void cmsg_getdstaddr(struct msghdr *mh, struct sockaddr_storage *dst,
312 ifindex_t *ifindex)
313 {
314 struct cmsghdr *cmsg;
315 struct sockaddr_in6 *dst6 = (struct sockaddr_in6 *)dst;
316
317 for (cmsg = CMSG_FIRSTHDR(mh); cmsg != NULL;
318 cmsg = CMSG_NXTHDR(mh, cmsg)) {
319 if ((cmsg->cmsg_level == IPPROTO_IPV6) &&
320 (cmsg->cmsg_type == IPV6_PKTINFO)) {
321 struct in6_pktinfo *i;
322
323 i = (struct in6_pktinfo *)CMSG_DATA(cmsg);
324
325 if (dst6)
326 dst6->sin6_addr = i->ipi6_addr;
327 if (ifindex)
328 *ifindex = i->ipi6_ifindex;
329 break;
330 }
331 }
332 }
333 #endif /* PIM_IPV != 4 */
334
335 int pim_socket_recvfromto(int fd, uint8_t *buf, size_t len,
336 struct sockaddr_storage *from, socklen_t *fromlen,
337 struct sockaddr_storage *to, socklen_t *tolen,
338 ifindex_t *ifindex)
339 {
340 struct msghdr msgh;
341 struct iovec iov;
342 char cbuf[1000];
343 int err;
344
345 /*
346 * IP_PKTINFO / IP_RECVDSTADDR don't yield sin_port.
347 * Use getsockname() to get sin_port.
348 */
349 if (to) {
350 socklen_t to_len = sizeof(*to);
351
352 pim_socket_getsockname(fd, (struct sockaddr *)to, &to_len);
353
354 if (tolen)
355 *tolen = sizeof(*to);
356 }
357
358 memset(&msgh, 0, sizeof(msgh));
359 iov.iov_base = buf;
360 iov.iov_len = len;
361 msgh.msg_control = cbuf;
362 msgh.msg_controllen = sizeof(cbuf);
363 msgh.msg_name = from;
364 msgh.msg_namelen = fromlen ? *fromlen : 0;
365 msgh.msg_iov = &iov;
366 msgh.msg_iovlen = 1;
367 msgh.msg_flags = 0;
368
369 err = recvmsg(fd, &msgh, 0);
370 if (err < 0)
371 return err;
372
373 if (fromlen)
374 *fromlen = msgh.msg_namelen;
375
376 cmsg_getdstaddr(&msgh, to, ifindex);
377
378 return err; /* len */
379 }
380
381 int pim_socket_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
382 {
383 if (getsockname(fd, name, namelen)) {
384 int e = errno;
385 zlog_warn(
386 "Could not get Socket Name for socket fd=%d: errno=%d: %s",
387 fd, errno, safe_strerror(errno));
388 errno = e;
389 return PIM_SOCK_ERR_NAME;
390 }
391
392 return PIM_SOCK_ERR_NONE;
393 }