File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / quagga / pimd / pim_ssmpingd.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Nov 2 10:09:11 2016 UTC (7 years, 7 months ago) by misho
Branches: quagga, MAIN
CVS tags: v1_0_20160315, HEAD
quagga 1.0.20160315

    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>