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>