blob: 4eb5c8dcf77f7c738f9dac630740494426b9e43f [file] [log] [blame]
Everton Marques96f91ae2009-10-07 18:41:45 -03001/*
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
Everton Marques824adbe2009-10-08 09:16:27 -030023#include <zebra.h>
24
25#include "if.h"
26#include "log.h"
27#include "memory.h"
28
Everton Marques96f91ae2009-10-07 18:41:45 -030029#include "pim_ssmpingd.h"
30#include "pim_time.h"
Everton Marques824adbe2009-10-08 09:16:27 -030031#include "pim_sock.h"
32#include "pim_str.h"
Everton Marques96f91ae2009-10-07 18:41:45 -030033#include "pimd.h"
34
Everton Marques824adbe2009-10-08 09:16:27 -030035static void ssmpingd_read_on(struct ssmpingd_sock *ss);
36
Everton Marques96f91ae2009-10-07 18:41:45 -030037void pim_ssmpingd_init()
38{
Everton Marques824adbe2009-10-08 09:16:27 -030039 zassert(!qpim_ssmpingd_list);
Everton Marques96f91ae2009-10-07 18:41:45 -030040}
41
42void pim_ssmpingd_destroy()
43{
Everton Marques824adbe2009-10-08 09:16:27 -030044 if (qpim_ssmpingd_list) {
Everton Marques96f91ae2009-10-07 18:41:45 -030045 list_free(qpim_ssmpingd_list);
Everton Marques824adbe2009-10-08 09:16:27 -030046 qpim_ssmpingd_list = 0;
47 }
48}
49
50static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr)
51{
52 struct listnode *node;
53 struct ssmpingd_sock *ss;
54
55 if (!qpim_ssmpingd_list)
56 return 0;
57
58 for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss))
59 if (source_addr.s_addr == ss->source_addr.s_addr)
60 return ss;
61
62 return 0;
63}
64
65static void ssmpingd_free(struct ssmpingd_sock *ss)
66{
67 XFREE(MTYPE_PIM_SSMPINGD, ss);
68}
69
70static int ssmpingd_socket(struct in_addr addr, int mttl)
71{
72 int fd;
73
74 fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
75 if (fd < 0) {
76 zlog_err("%s: could not create socket: errno=%d: %s",
77 __PRETTY_FUNCTION__, errno, safe_strerror(errno));
78 return -1;
79 }
80
81 /* Needed to obtain destination address from recvmsg() */
82 {
83#if defined(HAVE_IP_PKTINFO)
84 /* Linux IP_PKTINFO */
85 int opt = 1;
86 if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) {
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 -1;
102#endif
103 }
104
105 {
106 int reuse = 1;
107 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
108 (void *) &reuse, sizeof(reuse))) {
109 zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s",
110 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
111 close(fd);
112 return -1;
113 }
114 }
115
116 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL,
117 (void *) &mttl, sizeof(mttl))) {
118 zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s",
119 __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno));
120 close(fd);
121 return -1;
122 }
123
124 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF,
125 (void *) &addr, sizeof(addr))) {
126 zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s",
127 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
128 close(fd);
129 return -1;
130 }
131
132 {
133 long flags;
134
135 flags = fcntl(fd, F_GETFL, 0);
136 if (flags < 0) {
137 zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
138 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
139 close(fd);
140 return -1;
141 }
142
143 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) {
144 zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s",
145 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno));
146 close(fd);
147 return -1;
148 }
149 }
150
151 return fd;
152}
153
154static void ssmpingd_delete(struct ssmpingd_sock *ss)
155{
156 zassert(ss);
157 zassert(qpim_ssmpingd_list);
158
159 THREAD_OFF(ss->t_sock_read);
160
161 if (close(ss->sock_fd)) {
162 int e = errno;
163 char source_str[100];
164 pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
165 zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s",
166 __PRETTY_FUNCTION__,
167 ss->sock_fd, source_str, e, safe_strerror(e));
168 /* warning only */
169 }
170
171 listnode_delete(qpim_ssmpingd_list, ss);
172 ssmpingd_free(ss);
173}
174
175static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
176{
177 struct interface *ifp;
178 struct sockaddr_in from;
179 struct sockaddr_in to;
180 socklen_t fromlen = sizeof(from);
181 socklen_t tolen = sizeof(to);
182 int ifindex = -1;
183 char buf[1000];
184 int len;
185
186 len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
187 &from, &fromlen,
188 &to, &tolen,
189 &ifindex);
190 if (len < 0) {
191 char source_str[100];
192 pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
193 zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
194 __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
195 return -1;
196 }
197
198 ifp = if_lookup_by_index(ifindex);
199
200 if (PIM_DEBUG_SSMPINGD) {
201 char source_str[100];
202 char from_str[100];
203 char to_str[100];
204 pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
205 pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
206 pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
207 zlog_debug("%s: ssmpingd on source %s: interface %s ifindex=%d received ssmping from %s to %s on fd=%d",
208 __PRETTY_FUNCTION__,
209 source_str,
210 ifp ? ifp->name : "<iface?>",
211 ifindex, from_str, to_str, ss->sock_fd);
212 }
213
214 return 0;
215}
216
217static int ssmpingd_sock_read(struct thread *t)
218{
219 struct ssmpingd_sock *ss;
220 int sock_fd;
221 int result;
222
223 zassert(t);
224
225 ss = THREAD_ARG(t);
226 zassert(ss);
227
228 sock_fd = THREAD_FD(t);
229 zassert(sock_fd == ss->sock_fd);
230
231 result = ssmpingd_read_msg(ss);
232
233 /* Keep reading */
234 ss->t_sock_read = 0;
235 ssmpingd_read_on(ss);
236
237 return result;
238}
239
240static void ssmpingd_read_on(struct ssmpingd_sock *ss)
241{
242 zassert(!ss->t_sock_read);
243 THREAD_READ_ON(master, ss->t_sock_read,
244 ssmpingd_sock_read, ss, ss->sock_fd);
245}
246
247static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
248{
249 struct ssmpingd_sock *ss;
250 int sock_fd;
251
252 if (!qpim_ssmpingd_list) {
253 qpim_ssmpingd_list = list_new();
254 if (!qpim_ssmpingd_list) {
255 zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()",
256 __FILE__, __PRETTY_FUNCTION__);
257 return 0;
258 }
259 qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
260 }
261
262 sock_fd = ssmpingd_socket(source_addr, 64 /* ttl */);
263 if (sock_fd < 0) {
264 char source_str[100];
265 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
266 zlog_warn("%s: ssmpingd_socket() failure for source %s",
267 __PRETTY_FUNCTION__, source_str);
268 return 0;
269 }
270
271 ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
272 if (!ss) {
273 char source_str[100];
274 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
275 zlog_err("%s: XMALLOC(%d) failure for ssmpingd source %s",
276 __PRETTY_FUNCTION__,
277 sizeof(*ss), source_str);
278 close(sock_fd);
279 return 0;
280 }
281
282 ss->sock_fd = sock_fd;
283 ss->t_sock_read = 0;
284 ss->source_addr = source_addr;
285 ss->creation = pim_time_monotonic_sec();
286 ss->requests = 0;
287
288 listnode_add(qpim_ssmpingd_list, ss);
289
290 ssmpingd_read_on(ss);
291
292 return ss;
Everton Marques96f91ae2009-10-07 18:41:45 -0300293}
294
295int pim_ssmpingd_start(struct in_addr source_addr)
296{
Everton Marques824adbe2009-10-08 09:16:27 -0300297 struct ssmpingd_sock *ss;
298
299 ss = ssmpingd_find(source_addr);
300 if (ss) {
301 /* silently ignore request to recreate entry */
302 return 0;
303 }
304
305 {
306 char source_str[100];
307 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
308 zlog_info("%s: starting ssmpingd for source %s",
309 __PRETTY_FUNCTION__, source_str);
310 }
311
312 ss = ssmpingd_new(source_addr);
313 if (!ss) {
314 char source_str[100];
315 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
316 zlog_warn("%s: ssmpingd_new() failure for source %s",
317 __PRETTY_FUNCTION__, source_str);
318 return -1;
319 }
320
Everton Marques96f91ae2009-10-07 18:41:45 -0300321 return 0;
322}
323
324int pim_ssmpingd_stop(struct in_addr source_addr)
325{
Everton Marques824adbe2009-10-08 09:16:27 -0300326 struct ssmpingd_sock *ss;
327
328 ss = ssmpingd_find(source_addr);
329 if (!ss) {
330 char source_str[100];
331 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
332 zlog_warn("%s: could not find ssmpingd for source %s",
333 __PRETTY_FUNCTION__, source_str);
334 return -1;
335 }
336
337 {
338 char source_str[100];
339 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
340 zlog_info("%s: stopping ssmpingd for source %s",
341 __PRETTY_FUNCTION__, source_str);
342 }
343
344 ssmpingd_delete(ss);
345
Everton Marques96f91ae2009-10-07 18:41:45 -0300346 return 0;
347}