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 "if.h"
23 : #include "log.h"
24 : #include "memory.h"
25 : #include "sockopt.h"
26 : #include "vrf.h"
27 : #include "lib_errors.h"
28 :
29 : #include "pimd.h"
30 : #include "pim_instance.h"
31 : #include "pim_ssmpingd.h"
32 : #include "pim_time.h"
33 : #include "pim_sock.h"
34 : #include "network.h"
35 :
36 : #if PIM_IPV == 4
37 : static const char *const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
38 : #else
39 : static const char *const PIM_SSMPINGD_REPLY_GROUP = "ff3e::4321:1234";
40 : #endif
41 :
42 : enum { PIM_SSMPINGD_REQUEST = 'Q', PIM_SSMPINGD_REPLY = 'A' };
43 :
44 : static void ssmpingd_read_on(struct ssmpingd_sock *ss);
45 :
46 1 : void pim_ssmpingd_init(struct pim_instance *pim)
47 : {
48 1 : int result;
49 :
50 1 : assert(!pim->ssmpingd_list);
51 :
52 2 : result = inet_pton(PIM_AF, PIM_SSMPINGD_REPLY_GROUP,
53 1 : &pim->ssmpingd_group_addr);
54 :
55 1 : assert(result > 0);
56 1 : }
57 :
58 1 : void pim_ssmpingd_destroy(struct pim_instance *pim)
59 : {
60 1 : if (pim->ssmpingd_list)
61 0 : list_delete(&pim->ssmpingd_list);
62 1 : }
63 :
64 0 : static struct ssmpingd_sock *ssmpingd_find(struct pim_instance *pim,
65 : pim_addr source_addr)
66 : {
67 0 : struct listnode *node;
68 0 : struct ssmpingd_sock *ss;
69 :
70 0 : if (!pim->ssmpingd_list)
71 : return 0;
72 :
73 0 : for (ALL_LIST_ELEMENTS_RO(pim->ssmpingd_list, node, ss))
74 0 : if (!pim_addr_cmp(source_addr, ss->source_addr))
75 0 : return ss;
76 :
77 : return 0;
78 : }
79 :
80 0 : static void ssmpingd_free(struct ssmpingd_sock *ss)
81 : {
82 0 : XFREE(MTYPE_PIM_SSMPINGD, ss);
83 0 : }
84 :
85 : #if PIM_IPV == 4
86 : static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
87 : {
88 : /* Needed to obtain destination address from recvmsg() */
89 : #if defined(HAVE_IP_PKTINFO)
90 : /* Linux and Solaris IP_PKTINFO */
91 : int opt = 1;
92 : if (setsockopt(fd, IPPROTO_IP, IP_PKTINFO, &opt, sizeof(opt))) {
93 : zlog_warn(
94 : "%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s",
95 : __func__, fd, errno, safe_strerror(errno));
96 : }
97 : #elif defined(HAVE_IP_RECVDSTADDR)
98 : /* BSD IP_RECVDSTADDR */
99 : int opt = 1;
100 : if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) {
101 : zlog_warn(
102 : "%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s",
103 : __func__, fd, errno, safe_strerror(errno));
104 : }
105 : #else
106 : flog_err(
107 : EC_LIB_DEVELOPMENT,
108 : "%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()",
109 : __FILE__, __func__);
110 : close(fd);
111 : return -1;
112 : #endif
113 :
114 : if (setsockopt_ipv4_multicast_loop(fd, 0)) {
115 : zlog_warn(
116 : "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
117 : __func__, fd, errno, safe_strerror(errno));
118 : close(fd);
119 : return PIM_SOCK_ERR_LOOP;
120 : }
121 :
122 : if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&addr,
123 : sizeof(addr))) {
124 : zlog_warn(
125 : "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
126 : __func__, fd, errno, safe_strerror(errno));
127 : close(fd);
128 : return -1;
129 : }
130 :
131 : if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, (void *)&mttl,
132 : sizeof(mttl))) {
133 : zlog_warn(
134 : "%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
135 : __func__, mttl, fd, errno, safe_strerror(errno));
136 : close(fd);
137 : return -1;
138 : }
139 :
140 : return 0;
141 : }
142 : #else
143 0 : static inline int ssmpingd_setsockopt(int fd, pim_addr addr, int mttl)
144 : {
145 0 : setsockopt_ipv6_pktinfo(fd, 1);
146 0 : setsockopt_ipv6_multicast_hops(fd, mttl);
147 :
148 0 : if (setsockopt_ipv6_multicast_loop(fd, 0)) {
149 0 : zlog_warn(
150 : "%s: could not disable Multicast Loopback Option on socket fd=%d: errno=%d: %s",
151 : __func__, fd, errno, safe_strerror(errno));
152 0 : close(fd);
153 0 : return PIM_SOCK_ERR_LOOP;
154 : }
155 :
156 0 : if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, (void *)&addr,
157 : sizeof(addr))) {
158 0 : zlog_warn(
159 : "%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
160 : __func__, fd, errno, safe_strerror(errno));
161 0 : close(fd);
162 0 : return -1;
163 : }
164 : return 0;
165 : }
166 : #endif
167 :
168 :
169 0 : static int ssmpingd_socket(pim_addr addr, int port, int mttl)
170 : {
171 0 : struct sockaddr_storage sockaddr;
172 0 : int fd;
173 0 : int ret;
174 0 : socklen_t len = sizeof(sockaddr);
175 :
176 0 : fd = socket(PIM_AF, SOCK_DGRAM, IPPROTO_UDP);
177 0 : if (fd < 0) {
178 0 : flog_err_sys(EC_LIB_SOCKET,
179 : "%s: could not create socket: errno=%d: %s",
180 : __func__, errno, safe_strerror(errno));
181 0 : return -1;
182 : }
183 :
184 0 : pim_socket_getsockname(fd, (struct sockaddr *)&sockaddr, &len);
185 :
186 0 : if (bind(fd, (struct sockaddr *)&sockaddr, sizeof(sockaddr))) {
187 0 : zlog_warn(
188 : "%s: bind(fd=%d,addr=%pSUp,port=%d,len=%zu) failure: errno=%d: %s",
189 : __func__, fd, &sockaddr, port, sizeof(sockaddr), errno,
190 : safe_strerror(errno));
191 0 : close(fd);
192 0 : return -1;
193 : }
194 :
195 0 : set_nonblocking(fd);
196 0 : sockopt_reuseaddr(fd);
197 :
198 0 : ret = ssmpingd_setsockopt(fd, addr, mttl);
199 0 : if (ret) {
200 0 : zlog_warn("ssmpingd_setsockopt failed");
201 0 : close(fd);
202 0 : return -1;
203 : }
204 :
205 : return fd;
206 : }
207 :
208 0 : static void ssmpingd_delete(struct ssmpingd_sock *ss)
209 : {
210 0 : assert(ss);
211 :
212 0 : THREAD_OFF(ss->t_sock_read);
213 :
214 0 : if (close(ss->sock_fd)) {
215 0 : zlog_warn(
216 : "%s: failure closing ssmpingd sock_fd=%d for source %pPA: errno=%d: %s",
217 : __func__, ss->sock_fd, &ss->source_addr, errno,
218 : safe_strerror(errno));
219 : /* warning only */
220 : }
221 :
222 0 : listnode_delete(ss->pim->ssmpingd_list, ss);
223 0 : ssmpingd_free(ss);
224 0 : }
225 :
226 0 : static void ssmpingd_sendto(struct ssmpingd_sock *ss, const uint8_t *buf,
227 : int len, struct sockaddr_storage to)
228 : {
229 0 : socklen_t tolen = sizeof(to);
230 0 : int sent;
231 :
232 0 : sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT,
233 : (struct sockaddr *)&to, tolen);
234 0 : if (sent != len) {
235 0 : if (sent < 0) {
236 0 : zlog_warn(
237 : "%s: sendto() failure to %pSUp,fd=%d len=%d: errno=%d: %s",
238 : __func__, &to, ss->sock_fd, len, errno,
239 : safe_strerror(errno));
240 : } else {
241 0 : zlog_warn(
242 : "%s: sendto() partial to %pSUp, fd=%d len=%d: sent=%d",
243 : __func__, &to, ss->sock_fd, len, sent);
244 : }
245 : }
246 0 : }
247 :
248 0 : static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
249 : {
250 0 : struct interface *ifp;
251 0 : struct sockaddr_storage from;
252 0 : struct sockaddr_storage to;
253 0 : socklen_t fromlen = sizeof(from);
254 0 : socklen_t tolen = sizeof(to);
255 0 : ifindex_t ifindex = -1;
256 0 : uint8_t buf[1000];
257 0 : int len;
258 :
259 0 : ++ss->requests;
260 :
261 0 : len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf), &from,
262 : &fromlen, &to, &tolen, &ifindex);
263 :
264 0 : if (len < 0) {
265 0 : zlog_warn(
266 : "%s: failure receiving ssmping for source %pPA on fd=%d: errno=%d: %s",
267 : __func__, &ss->source_addr, ss->sock_fd, errno,
268 : safe_strerror(errno));
269 0 : return -1;
270 : }
271 :
272 0 : ifp = if_lookup_by_index(ifindex, ss->pim->vrf->vrf_id);
273 :
274 0 : if (buf[0] != PIM_SSMPINGD_REQUEST) {
275 0 : zlog_warn(
276 : "%s: bad ssmping type=%d from %pSUp to %pSUp on interface %s ifindex=%d fd=%d src=%pPA",
277 : __func__, buf[0], &from, &to,
278 : ifp ? ifp->name : "<iface?>", ifindex, ss->sock_fd,
279 : &ss->source_addr);
280 0 : return 0;
281 : }
282 :
283 0 : if (PIM_DEBUG_SSMPINGD) {
284 0 : zlog_debug(
285 : "%s: recv ssmping from %pSUp, to %pSUp, on interface %s ifindex=%d fd=%d src=%pPA",
286 : __func__, &from, &to, ifp ? ifp->name : "<iface?>",
287 : ifindex, ss->sock_fd, &ss->source_addr);
288 : }
289 :
290 0 : buf[0] = PIM_SSMPINGD_REPLY;
291 :
292 : /* unicast reply */
293 0 : ssmpingd_sendto(ss, buf, len, from);
294 :
295 : /* multicast reply */
296 0 : memcpy(&from, &ss->pim->ssmpingd_group_addr, sizeof(pim_addr));
297 0 : ssmpingd_sendto(ss, buf, len, from);
298 :
299 0 : return 0;
300 : }
301 :
302 0 : static void ssmpingd_sock_read(struct thread *t)
303 : {
304 0 : struct ssmpingd_sock *ss;
305 :
306 0 : ss = THREAD_ARG(t);
307 :
308 0 : ssmpingd_read_msg(ss);
309 :
310 : /* Keep reading */
311 0 : ssmpingd_read_on(ss);
312 0 : }
313 :
314 0 : static void ssmpingd_read_on(struct ssmpingd_sock *ss)
315 : {
316 0 : thread_add_read(router->master, ssmpingd_sock_read, ss, ss->sock_fd,
317 : &ss->t_sock_read);
318 0 : }
319 :
320 0 : static struct ssmpingd_sock *ssmpingd_new(struct pim_instance *pim,
321 : pim_addr source_addr)
322 : {
323 0 : struct ssmpingd_sock *ss;
324 0 : int sock_fd;
325 :
326 0 : if (!pim->ssmpingd_list) {
327 0 : pim->ssmpingd_list = list_new();
328 0 : pim->ssmpingd_list->del = (void (*)(void *))ssmpingd_free;
329 : }
330 :
331 0 : sock_fd =
332 0 : ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
333 0 : if (sock_fd < 0) {
334 0 : zlog_warn("%s: ssmpingd_socket() failure for source %pPA",
335 : __func__, &source_addr);
336 0 : return 0;
337 : }
338 :
339 0 : ss = XCALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
340 :
341 0 : ss->pim = pim;
342 0 : ss->sock_fd = sock_fd;
343 0 : ss->t_sock_read = NULL;
344 0 : ss->source_addr = source_addr;
345 0 : ss->creation = pim_time_monotonic_sec();
346 0 : ss->requests = 0;
347 :
348 0 : listnode_add(pim->ssmpingd_list, ss);
349 :
350 0 : ssmpingd_read_on(ss);
351 :
352 0 : return ss;
353 : }
354 :
355 0 : int pim_ssmpingd_start(struct pim_instance *pim, pim_addr source_addr)
356 : {
357 0 : struct ssmpingd_sock *ss;
358 :
359 0 : ss = ssmpingd_find(pim, source_addr);
360 0 : if (ss) {
361 : /* silently ignore request to recreate entry */
362 : return 0;
363 : }
364 :
365 0 : zlog_info("%s: starting ssmpingd for source %pPAs", __func__,
366 : &source_addr);
367 :
368 0 : ss = ssmpingd_new(pim, source_addr);
369 0 : if (!ss) {
370 0 : zlog_warn("%s: ssmpingd_new() failure for source %pPAs",
371 : __func__, &source_addr);
372 0 : return -1;
373 : }
374 :
375 : return 0;
376 : }
377 :
378 0 : int pim_ssmpingd_stop(struct pim_instance *pim, pim_addr source_addr)
379 : {
380 0 : struct ssmpingd_sock *ss;
381 :
382 0 : ss = ssmpingd_find(pim, source_addr);
383 0 : if (!ss) {
384 0 : zlog_warn("%s: could not find ssmpingd for source %pPAs",
385 : __func__, &source_addr);
386 0 : return -1;
387 : }
388 :
389 0 : zlog_info("%s: stopping ssmpingd for source %pPAs", __func__,
390 : &source_addr);
391 :
392 0 : ssmpingd_delete(ss);
393 :
394 0 : return 0;
395 : }
|