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 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 23#include <zebra.h> 24 25#include "if.h" 26#include "log.h" 27#include "memory.h" 28 29#include "pim_ssmpingd.h" 30#include "pim_time.h" 31#include "pim_sock.h" 32#include "pim_str.h" 33#include "pimd.h" 34 35static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234"; 36 37enum { 38 PIM_SSMPINGD_REQUEST = 'Q', 39 PIM_SSMPINGD_REPLY = 'A' 40}; 41 42static void ssmpingd_read_on(struct ssmpingd_sock *ss); 43 44void pim_ssmpingd_init() 45{ 46 int result; 47 48 zassert(!qpim_ssmpingd_list); 49 50 result = inet_pton(AF_INET, PIM_SSMPINGD_REPLY_GROUP, &qpim_ssmpingd_group_addr); 51 52 zassert(result > 0); 53} 54 55void pim_ssmpingd_destroy() 56{ 57 if (qpim_ssmpingd_list) { 58 list_free(qpim_ssmpingd_list); 59 qpim_ssmpingd_list = 0; 60 } 61} 62 63static struct ssmpingd_sock *ssmpingd_find(struct in_addr source_addr) 64{ 65 struct listnode *node; 66 struct ssmpingd_sock *ss; 67 68 if (!qpim_ssmpingd_list) 69 return 0; 70 71 for (ALL_LIST_ELEMENTS_RO(qpim_ssmpingd_list, node, ss)) 72 if (source_addr.s_addr == ss->source_addr.s_addr) 73 return ss; 74 75 return 0; 76} 77 78static void ssmpingd_free(struct ssmpingd_sock *ss) 79{ 80 XFREE(MTYPE_PIM_SSMPINGD, ss); 81} 82 83static int ssmpingd_socket(struct in_addr addr, int port, int mttl) 84{ 85 struct sockaddr_in sockaddr; 86 int fd; 87 88 fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 89 if (fd < 0) { 90 zlog_err("%s: could not create socket: errno=%d: %s", 91 __PRETTY_FUNCTION__, errno, safe_strerror(errno)); 92 return -1; 93 } 94 95 sockaddr.sin_family = AF_INET; 96 sockaddr.sin_addr = addr; 97 sockaddr.sin_port = htons(port); 98 99 if (bind(fd, &sockaddr, sizeof(sockaddr))) { 100 char addr_str[100]; 101 pim_inet4_dump("<addr?>", addr, addr_str, sizeof(addr_str)); 102 zlog_warn("%s: bind(fd=%d,addr=%s,port=%d,len=%zu) failure: errno=%d: %s", 103 __PRETTY_FUNCTION__, 104 fd, addr_str, port, sizeof(sockaddr), 105 errno, safe_strerror(errno)); 106 close(fd); 107 return -1; 108 } 109 110 /* Needed to obtain destination address from recvmsg() */ 111 { 112#if defined(HAVE_IP_PKTINFO) 113 /* Linux IP_PKTINFO */ 114 int opt = 1; 115 if (setsockopt(fd, SOL_IP, IP_PKTINFO, &opt, sizeof(opt))) { 116 zlog_warn("%s: could not set IP_PKTINFO on socket fd=%d: errno=%d: %s", 117 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); 118 } 119#elif defined(HAVE_IP_RECVDSTADDR) 120 /* BSD IP_RECVDSTADDR */ 121 int opt = 1; 122 if (setsockopt(fd, IPPROTO_IP, IP_RECVDSTADDR, &opt, sizeof(opt))) { 123 zlog_warn("%s: could not set IP_RECVDSTADDR on socket fd=%d: errno=%d: %s", 124 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); 125 } 126#else 127 zlog_err("%s %s: missing IP_PKTINFO and IP_RECVDSTADDR: unable to get dst addr from recvmsg()", 128 __FILE__, __PRETTY_FUNCTION__); 129 close(fd); 130 return -1; 131#endif 132 } 133 134 { 135 int reuse = 1; 136 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, 137 (void *) &reuse, sizeof(reuse))) { 138 zlog_warn("%s: could not set Reuse Address Option on socket fd=%d: errno=%d: %s", 139 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); 140 close(fd); 141 return -1; 142 } 143 } 144 145 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_TTL, 146 (void *) &mttl, sizeof(mttl))) { 147 zlog_warn("%s: could not set multicast TTL=%d on socket fd=%d: errno=%d: %s", 148 __PRETTY_FUNCTION__, mttl, fd, errno, safe_strerror(errno)); 149 close(fd); 150 return -1; 151 } 152 153 { 154 int loop = 0; 155 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_LOOP, 156 (void *) &loop, sizeof(loop))) { 157 zlog_warn("%s: could not %s Multicast Loopback Option on socket fd=%d: errno=%d: %s", 158 __PRETTY_FUNCTION__, 159 loop ? "enable" : "disable", 160 fd, errno, safe_strerror(errno)); 161 close(fd); 162 return PIM_SOCK_ERR_LOOP; 163 } 164 } 165 166 if (setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, 167 (void *) &addr, sizeof(addr))) { 168 zlog_warn("%s: could not set Outgoing Interface Option on socket fd=%d: errno=%d: %s", 169 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); 170 close(fd); 171 return -1; 172 } 173 174 { 175 long flags; 176 177 flags = fcntl(fd, F_GETFL, 0); 178 if (flags < 0) { 179 zlog_warn("%s: could not get fcntl(F_GETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", 180 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); 181 close(fd); 182 return -1; 183 } 184 185 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK)) { 186 zlog_warn("%s: could not set fcntl(F_SETFL,O_NONBLOCK) on socket fd=%d: errno=%d: %s", 187 __PRETTY_FUNCTION__, fd, errno, safe_strerror(errno)); 188 close(fd); 189 return -1; 190 } 191 } 192 193 return fd; 194} 195 196static void ssmpingd_delete(struct ssmpingd_sock *ss) 197{ 198 zassert(ss); 199 zassert(qpim_ssmpingd_list); 200 201 THREAD_OFF(ss->t_sock_read); 202 203 if (close(ss->sock_fd)) { 204 int e = errno; 205 char source_str[100]; 206 pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str)); 207 zlog_warn("%s: failure closing ssmpingd sock_fd=%d for source %s: errno=%d: %s", 208 __PRETTY_FUNCTION__, 209 ss->sock_fd, source_str, e, safe_strerror(e)); 210 /* warning only */ 211 } 212 213 listnode_delete(qpim_ssmpingd_list, ss); 214 ssmpingd_free(ss); 215} 216 217static void ssmpingd_sendto(struct ssmpingd_sock *ss, 218 const uint8_t *buf, 219 int len, 220 struct sockaddr_in to) 221{ 222 socklen_t tolen = sizeof(to); 223 int sent; 224 225 sent = sendto(ss->sock_fd, buf, len, MSG_DONTWAIT, &to, tolen); 226 if (sent != len) { 227 int e = errno; 228 char to_str[100]; 229 pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str)); 230 if (sent < 0) { 231 zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s", 232 __PRETTY_FUNCTION__, 233 to_str, ntohs(to.sin_port), ss->sock_fd, len, 234 e, safe_strerror(e)); 235 } 236 else { 237 zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d", 238 __PRETTY_FUNCTION__, 239 to_str, ntohs(to.sin_port), ss->sock_fd, 240 len, sent); 241 } 242 } 243} 244 245static int ssmpingd_read_msg(struct ssmpingd_sock *ss) 246{ 247 struct interface *ifp; 248 struct sockaddr_in from; 249 struct sockaddr_in to; 250 socklen_t fromlen = sizeof(from); 251 socklen_t tolen = sizeof(to); 252 int ifindex = -1; 253 uint8_t buf[1000]; 254 int len; 255 256 ++ss->requests; 257 258 len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf), 259 &from, &fromlen, 260 &to, &tolen, 261 &ifindex); 262 if (len < 0) { 263 char source_str[100]; 264 pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str)); 265 zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s", 266 __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno)); 267 return -1; 268 } 269 270 ifp = if_lookup_by_index(ifindex); 271 272 if (buf[0] != PIM_SSMPINGD_REQUEST) { 273 char source_str[100]; 274 char from_str[100]; 275 char to_str[100]; 276 pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str)); 277 pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str)); 278 pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str)); 279 zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", 280 __PRETTY_FUNCTION__, 281 buf[0], 282 from_str, ntohs(from.sin_port), 283 to_str, ntohs(to.sin_port), 284 ifp ? ifp->name : "<iface?>", 285 ifindex, ss->sock_fd, 286 source_str); 287 return 0; 288 } 289 290 if (PIM_DEBUG_SSMPINGD) { 291 char source_str[100]; 292 char from_str[100]; 293 char to_str[100]; 294 pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str)); 295 pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str)); 296 pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str)); 297 zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s", 298 __PRETTY_FUNCTION__, 299 from_str, ntohs(from.sin_port), 300 to_str, ntohs(to.sin_port), 301 ifp ? ifp->name : "<iface?>", 302 ifindex, ss->sock_fd, 303 source_str); 304 } 305 306 buf[0] = PIM_SSMPINGD_REPLY; 307 308 /* unicast reply */ 309 ssmpingd_sendto(ss, buf, len, from); 310 311 /* multicast reply */ 312 from.sin_addr = qpim_ssmpingd_group_addr; 313 ssmpingd_sendto(ss, buf, len, from); 314 315 return 0; 316} 317 318static int ssmpingd_sock_read(struct thread *t) 319{ 320 struct ssmpingd_sock *ss; 321 int sock_fd; 322 int result; 323 324 zassert(t); 325 326 ss = THREAD_ARG(t); 327 zassert(ss); 328 329 sock_fd = THREAD_FD(t); 330 zassert(sock_fd == ss->sock_fd); 331 332 result = ssmpingd_read_msg(ss); 333 334 /* Keep reading */ 335 ss->t_sock_read = 0; 336 ssmpingd_read_on(ss); 337 338 return result; 339} 340 341static void ssmpingd_read_on(struct ssmpingd_sock *ss) 342{ 343 zassert(!ss->t_sock_read); 344 THREAD_READ_ON(master, ss->t_sock_read, 345 ssmpingd_sock_read, ss, ss->sock_fd); 346} 347 348static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr) 349{ 350 struct ssmpingd_sock *ss; 351 int sock_fd; 352 353 if (!qpim_ssmpingd_list) { 354 qpim_ssmpingd_list = list_new(); 355 if (!qpim_ssmpingd_list) { 356 zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()", 357 __FILE__, __PRETTY_FUNCTION__); 358 return 0; 359 } 360 qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free; 361 } 362 363 sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64); 364 if (sock_fd < 0) { 365 char source_str[100]; 366 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); 367 zlog_warn("%s: ssmpingd_socket() failure for source %s", 368 __PRETTY_FUNCTION__, source_str); 369 return 0; 370 } 371 372 ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss)); 373 if (!ss) { 374 char source_str[100]; 375 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); 376 zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s", 377 __PRETTY_FUNCTION__, 378 sizeof(*ss), source_str); 379 close(sock_fd); 380 return 0; 381 } 382 383 ss->sock_fd = sock_fd; 384 ss->t_sock_read = 0; 385 ss->source_addr = source_addr; 386 ss->creation = pim_time_monotonic_sec(); 387 ss->requests = 0; 388 389 listnode_add(qpim_ssmpingd_list, ss); 390 391 ssmpingd_read_on(ss); 392 393 return ss; 394} 395 396int pim_ssmpingd_start(struct in_addr source_addr) 397{ 398 struct ssmpingd_sock *ss; 399 400 ss = ssmpingd_find(source_addr); 401 if (ss) { 402 /* silently ignore request to recreate entry */ 403 return 0; 404 } 405 406 { 407 char source_str[100]; 408 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); 409 zlog_info("%s: starting ssmpingd for source %s", 410 __PRETTY_FUNCTION__, source_str); 411 } 412 413 ss = ssmpingd_new(source_addr); 414 if (!ss) { 415 char source_str[100]; 416 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); 417 zlog_warn("%s: ssmpingd_new() failure for source %s", 418 __PRETTY_FUNCTION__, source_str); 419 return -1; 420 } 421 422 return 0; 423} 424 425int pim_ssmpingd_stop(struct in_addr source_addr) 426{ 427 struct ssmpingd_sock *ss; 428 429 ss = ssmpingd_find(source_addr); 430 if (!ss) { 431 char source_str[100]; 432 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); 433 zlog_warn("%s: could not find ssmpingd for source %s", 434 __PRETTY_FUNCTION__, source_str); 435 return -1; 436 } 437 438 { 439 char source_str[100]; 440 pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str)); 441 zlog_info("%s: stopping ssmpingd for source %s", 442 __PRETTY_FUNCTION__, source_str); 443 } 444 445 ssmpingd_delete(ss); 446 447 return 0; 448} 449