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