Annotation of embedaddon/quagga/pimd/pim_ssmpingd.c, revision 1.1.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>