Annotation of embedaddon/quagga/pimd/pim_ssmpingd.c, revision 1.1
1.1 ! misho 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:
! 35: static const char * const PIM_SSMPINGD_REPLY_GROUP = "232.43.211.234";
! 36:
! 37: enum {
! 38: PIM_SSMPINGD_REQUEST = 'Q',
! 39: PIM_SSMPINGD_REPLY = 'A'
! 40: };
! 41:
! 42: static void ssmpingd_read_on(struct ssmpingd_sock *ss);
! 43:
! 44: void 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:
! 55: void pim_ssmpingd_destroy()
! 56: {
! 57: if (qpim_ssmpingd_list) {
! 58: list_free(qpim_ssmpingd_list);
! 59: qpim_ssmpingd_list = 0;
! 60: }
! 61: }
! 62:
! 63: static 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:
! 78: static void ssmpingd_free(struct ssmpingd_sock *ss)
! 79: {
! 80: XFREE(MTYPE_PIM_SSMPINGD, ss);
! 81: }
! 82:
! 83: static 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, (struct sockaddr *)&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 and Solaris IP_PKTINFO */
! 114: int opt = 1;
! 115: if (setsockopt(fd, IPPROTO_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:
! 196: static 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:
! 217: static 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,
! 226: (struct sockaddr *)&to, tolen);
! 227: if (sent != len) {
! 228: int e = errno;
! 229: char to_str[100];
! 230: pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
! 231: if (sent < 0) {
! 232: zlog_warn("%s: sendto() failure to %s,%d: fd=%d len=%d: errno=%d: %s",
! 233: __PRETTY_FUNCTION__,
! 234: to_str, ntohs(to.sin_port), ss->sock_fd, len,
! 235: e, safe_strerror(e));
! 236: }
! 237: else {
! 238: zlog_warn("%s: sendto() partial to %s,%d: fd=%d len=%d: sent=%d",
! 239: __PRETTY_FUNCTION__,
! 240: to_str, ntohs(to.sin_port), ss->sock_fd,
! 241: len, sent);
! 242: }
! 243: }
! 244: }
! 245:
! 246: static int ssmpingd_read_msg(struct ssmpingd_sock *ss)
! 247: {
! 248: struct interface *ifp;
! 249: struct sockaddr_in from;
! 250: struct sockaddr_in to;
! 251: socklen_t fromlen = sizeof(from);
! 252: socklen_t tolen = sizeof(to);
! 253: ifindex_t ifindex = -1;
! 254: uint8_t buf[1000];
! 255: int len;
! 256:
! 257: ++ss->requests;
! 258:
! 259: len = pim_socket_recvfromto(ss->sock_fd, buf, sizeof(buf),
! 260: &from, &fromlen,
! 261: &to, &tolen,
! 262: &ifindex);
! 263: if (len < 0) {
! 264: char source_str[100];
! 265: pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
! 266: zlog_warn("%s: failure receiving ssmping for source %s on fd=%d: errno=%d: %s",
! 267: __PRETTY_FUNCTION__, source_str, ss->sock_fd, errno, safe_strerror(errno));
! 268: return -1;
! 269: }
! 270:
! 271: ifp = if_lookup_by_index(ifindex);
! 272:
! 273: if (buf[0] != PIM_SSMPINGD_REQUEST) {
! 274: char source_str[100];
! 275: char from_str[100];
! 276: char to_str[100];
! 277: pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
! 278: pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
! 279: pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
! 280: zlog_warn("%s: bad ssmping type=%d from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
! 281: __PRETTY_FUNCTION__,
! 282: buf[0],
! 283: from_str, ntohs(from.sin_port),
! 284: to_str, ntohs(to.sin_port),
! 285: ifp ? ifp->name : "<iface?>",
! 286: ifindex, ss->sock_fd,
! 287: source_str);
! 288: return 0;
! 289: }
! 290:
! 291: if (PIM_DEBUG_SSMPINGD) {
! 292: char source_str[100];
! 293: char from_str[100];
! 294: char to_str[100];
! 295: pim_inet4_dump("<src?>", ss->source_addr, source_str, sizeof(source_str));
! 296: pim_inet4_dump("<from?>", from.sin_addr, from_str, sizeof(from_str));
! 297: pim_inet4_dump("<to?>", to.sin_addr, to_str, sizeof(to_str));
! 298: zlog_debug("%s: recv ssmping from %s,%d to %s,%d on interface %s ifindex=%d fd=%d src=%s",
! 299: __PRETTY_FUNCTION__,
! 300: from_str, ntohs(from.sin_port),
! 301: to_str, ntohs(to.sin_port),
! 302: ifp ? ifp->name : "<iface?>",
! 303: ifindex, ss->sock_fd,
! 304: source_str);
! 305: }
! 306:
! 307: buf[0] = PIM_SSMPINGD_REPLY;
! 308:
! 309: /* unicast reply */
! 310: ssmpingd_sendto(ss, buf, len, from);
! 311:
! 312: /* multicast reply */
! 313: from.sin_addr = qpim_ssmpingd_group_addr;
! 314: ssmpingd_sendto(ss, buf, len, from);
! 315:
! 316: return 0;
! 317: }
! 318:
! 319: static int ssmpingd_sock_read(struct thread *t)
! 320: {
! 321: struct ssmpingd_sock *ss;
! 322: int sock_fd;
! 323: int result;
! 324:
! 325: zassert(t);
! 326:
! 327: ss = THREAD_ARG(t);
! 328: zassert(ss);
! 329:
! 330: sock_fd = THREAD_FD(t);
! 331: zassert(sock_fd == ss->sock_fd);
! 332:
! 333: result = ssmpingd_read_msg(ss);
! 334:
! 335: /* Keep reading */
! 336: ss->t_sock_read = 0;
! 337: ssmpingd_read_on(ss);
! 338:
! 339: return result;
! 340: }
! 341:
! 342: static void ssmpingd_read_on(struct ssmpingd_sock *ss)
! 343: {
! 344: zassert(!ss->t_sock_read);
! 345: THREAD_READ_ON(master, ss->t_sock_read,
! 346: ssmpingd_sock_read, ss, ss->sock_fd);
! 347: }
! 348:
! 349: static struct ssmpingd_sock *ssmpingd_new(struct in_addr source_addr)
! 350: {
! 351: struct ssmpingd_sock *ss;
! 352: int sock_fd;
! 353:
! 354: if (!qpim_ssmpingd_list) {
! 355: qpim_ssmpingd_list = list_new();
! 356: if (!qpim_ssmpingd_list) {
! 357: zlog_err("%s %s: failure: qpim_ssmpingd_list=list_new()",
! 358: __FILE__, __PRETTY_FUNCTION__);
! 359: return 0;
! 360: }
! 361: qpim_ssmpingd_list->del = (void (*)(void *)) ssmpingd_free;
! 362: }
! 363:
! 364: sock_fd = ssmpingd_socket(source_addr, /* port: */ 4321, /* mTTL: */ 64);
! 365: if (sock_fd < 0) {
! 366: char source_str[100];
! 367: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
! 368: zlog_warn("%s: ssmpingd_socket() failure for source %s",
! 369: __PRETTY_FUNCTION__, source_str);
! 370: return 0;
! 371: }
! 372:
! 373: ss = XMALLOC(MTYPE_PIM_SSMPINGD, sizeof(*ss));
! 374: if (!ss) {
! 375: char source_str[100];
! 376: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
! 377: zlog_err("%s: XMALLOC(%zu) failure for ssmpingd source %s",
! 378: __PRETTY_FUNCTION__,
! 379: sizeof(*ss), source_str);
! 380: close(sock_fd);
! 381: return 0;
! 382: }
! 383:
! 384: ss->sock_fd = sock_fd;
! 385: ss->t_sock_read = 0;
! 386: ss->source_addr = source_addr;
! 387: ss->creation = pim_time_monotonic_sec();
! 388: ss->requests = 0;
! 389:
! 390: listnode_add(qpim_ssmpingd_list, ss);
! 391:
! 392: ssmpingd_read_on(ss);
! 393:
! 394: return ss;
! 395: }
! 396:
! 397: int pim_ssmpingd_start(struct in_addr source_addr)
! 398: {
! 399: struct ssmpingd_sock *ss;
! 400:
! 401: ss = ssmpingd_find(source_addr);
! 402: if (ss) {
! 403: /* silently ignore request to recreate entry */
! 404: return 0;
! 405: }
! 406:
! 407: {
! 408: char source_str[100];
! 409: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
! 410: zlog_info("%s: starting ssmpingd for source %s",
! 411: __PRETTY_FUNCTION__, source_str);
! 412: }
! 413:
! 414: ss = ssmpingd_new(source_addr);
! 415: if (!ss) {
! 416: char source_str[100];
! 417: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
! 418: zlog_warn("%s: ssmpingd_new() failure for source %s",
! 419: __PRETTY_FUNCTION__, source_str);
! 420: return -1;
! 421: }
! 422:
! 423: return 0;
! 424: }
! 425:
! 426: int pim_ssmpingd_stop(struct in_addr source_addr)
! 427: {
! 428: struct ssmpingd_sock *ss;
! 429:
! 430: ss = ssmpingd_find(source_addr);
! 431: if (!ss) {
! 432: char source_str[100];
! 433: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
! 434: zlog_warn("%s: could not find ssmpingd for source %s",
! 435: __PRETTY_FUNCTION__, source_str);
! 436: return -1;
! 437: }
! 438:
! 439: {
! 440: char source_str[100];
! 441: pim_inet4_dump("<src?>", source_addr, source_str, sizeof(source_str));
! 442: zlog_info("%s: stopping ssmpingd for source %s",
! 443: __PRETTY_FUNCTION__, source_str);
! 444: }
! 445:
! 446: ssmpingd_delete(ss);
! 447:
! 448: return 0;
! 449: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>