"Fossies" - the Fresh Open Source Software Archive

Member "openbgpd-6.5p0/src/bgpd/control.c" (1 Apr 2019, 13424 Bytes) of package /linux/privat/openbgpd-6.5p0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "control.c" see the Fossies "Dox" file reference documentation.

    1 /*  $OpenBSD: control.c,v 1.96 2019/03/31 16:57:38 claudio Exp $ */
    2 
    3 /*
    4  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
    5  *
    6  * Permission to use, copy, modify, and distribute this software for any
    7  * purpose with or without fee is hereby granted, provided that the above
    8  * copyright notice and this permission notice appear in all copies.
    9  *
   10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
   11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
   12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
   13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
   14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
   15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
   16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
   17  */
   18 
   19 #include <sys/types.h>
   20 #include <sys/stat.h>
   21 #include <sys/socket.h>
   22 #include <sys/un.h>
   23 #include <errno.h>
   24 #include <stdlib.h>
   25 #include <string.h>
   26 #include <unistd.h>
   27 
   28 #include "bgpd.h"
   29 #include "session.h"
   30 #include "log.h"
   31 
   32 #define CONTROL_BACKLOG 5
   33 
   34 struct ctl_conn *control_connbyfd(int);
   35 struct ctl_conn *control_connbypid(pid_t);
   36 int      control_close(int);
   37 void         control_result(struct ctl_conn *, u_int);
   38 ssize_t      imsg_read_nofd(struct imsgbuf *);
   39 
   40 int
   41 control_check(char *path)
   42 {
   43     struct sockaddr_un   sun;
   44     int          fd;
   45 
   46     bzero(&sun, sizeof(sun));
   47     sun.sun_family = AF_UNIX;
   48     strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
   49 
   50     if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
   51         log_warn("%s: socket", __func__);
   52         return (-1);
   53     }
   54 
   55     if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0) {
   56         log_warnx("control socket %s already in use", path);
   57         close(fd);
   58         return (-1);
   59     }
   60 
   61     close(fd);
   62 
   63     return (0);
   64 }
   65 
   66 int
   67 control_init(int restricted, char *path)
   68 {
   69     struct sockaddr_un   sun;
   70     int          fd;
   71     mode_t           old_umask, mode;
   72 
   73     if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
   74          0)) == -1) {
   75         log_warn("control_init: socket");
   76         return (-1);
   77     }
   78 
   79     bzero(&sun, sizeof(sun));
   80     sun.sun_family = AF_UNIX;
   81     if (strlcpy(sun.sun_path, path, sizeof(sun.sun_path)) >=
   82         sizeof(sun.sun_path)) {
   83         log_warn("control_init: socket name too long");
   84         close(fd);
   85         return (-1);
   86     }
   87 
   88     if (unlink(path) == -1)
   89         if (errno != ENOENT) {
   90             log_warn("control_init: unlink %s", path);
   91             close(fd);
   92             return (-1);
   93         }
   94 
   95     if (restricted) {
   96         old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
   97         mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
   98     } else {
   99         old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
  100         mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
  101     }
  102 
  103     if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
  104         log_warn("control_init: bind: %s", path);
  105         close(fd);
  106         umask(old_umask);
  107         return (-1);
  108     }
  109 
  110     umask(old_umask);
  111 
  112     if (chmod(path, mode) == -1) {
  113         log_warn("control_init: chmod: %s", path);
  114         close(fd);
  115         unlink(path);
  116         return (-1);
  117     }
  118 
  119     return (fd);
  120 }
  121 
  122 int
  123 control_listen(int fd)
  124 {
  125     if (fd != -1 && listen(fd, CONTROL_BACKLOG) == -1) {
  126         log_warn("control_listen: listen");
  127         return (-1);
  128     }
  129 
  130     return (0);
  131 }
  132 
  133 void
  134 control_shutdown(int fd)
  135 {
  136     close(fd);
  137 }
  138 
  139 unsigned int
  140 control_accept(int listenfd, int restricted)
  141 {
  142     int          connfd;
  143     socklen_t        len;
  144     struct sockaddr_un   sun;
  145     struct ctl_conn     *ctl_conn;
  146 
  147     len = sizeof(sun);
  148     if ((connfd = accept4(listenfd,
  149         (struct sockaddr *)&sun, &len,
  150         SOCK_NONBLOCK | SOCK_CLOEXEC)) == -1) {
  151         if (errno == ENFILE || errno == EMFILE) {
  152             pauseaccept = getmonotime();
  153             return (0);
  154         } else if (errno != EWOULDBLOCK && errno != EINTR &&
  155             errno != ECONNABORTED)
  156             log_warn("control_accept: accept");
  157         return (0);
  158     }
  159 
  160     if ((ctl_conn = calloc(1, sizeof(struct ctl_conn))) == NULL) {
  161         log_warn("control_accept");
  162         close(connfd);
  163         return (0);
  164     }
  165 
  166     imsg_init(&ctl_conn->ibuf, connfd);
  167     ctl_conn->restricted = restricted;
  168 
  169     TAILQ_INSERT_TAIL(&ctl_conns, ctl_conn, entry);
  170 
  171     return (1);
  172 }
  173 
  174 struct ctl_conn *
  175 control_connbyfd(int fd)
  176 {
  177     struct ctl_conn *c;
  178 
  179     TAILQ_FOREACH(c, &ctl_conns, entry) {
  180         if (c->ibuf.fd == fd)
  181             break;
  182     }
  183 
  184     return (c);
  185 }
  186 
  187 struct ctl_conn *
  188 control_connbypid(pid_t pid)
  189 {
  190     struct ctl_conn *c;
  191 
  192     TAILQ_FOREACH(c, &ctl_conns, entry) {
  193         if (c->ibuf.pid == pid)
  194             break;
  195     }
  196 
  197     return (c);
  198 }
  199 
  200 int
  201 control_close(int fd)
  202 {
  203     struct ctl_conn *c;
  204 
  205     if ((c = control_connbyfd(fd)) == NULL) {
  206         log_warn("control_close: fd %d: not found", fd);
  207         return (0);
  208     }
  209 
  210     if (c->terminate && c->ibuf.pid)
  211         imsg_ctl_rde(IMSG_CTL_TERMINATE, c->ibuf.pid, NULL, 0);
  212 
  213     msgbuf_clear(&c->ibuf.w);
  214     TAILQ_REMOVE(&ctl_conns, c, entry);
  215 
  216     close(c->ibuf.fd);
  217     free(c);
  218     pauseaccept = 0;
  219     return (1);
  220 }
  221 
  222 int
  223 control_dispatch_msg(struct pollfd *pfd, u_int *ctl_cnt,
  224     struct peer_head *peers)
  225 {
  226     struct imsg      imsg;
  227     struct ctl_conn     *c;
  228     ssize_t          n;
  229     int          verbose, matched;
  230     struct peer     *p;
  231     struct ctl_neighbor *neighbor;
  232     struct ctl_show_rib_request *ribreq;
  233 
  234     if ((c = control_connbyfd(pfd->fd)) == NULL) {
  235         log_warn("control_dispatch_msg: fd %d: not found", pfd->fd);
  236         return (0);
  237     }
  238 
  239     if (pfd->revents & POLLOUT) {
  240         if (msgbuf_write(&c->ibuf.w) <= 0 && errno != EAGAIN) {
  241             *ctl_cnt -= control_close(pfd->fd);
  242             return (1);
  243         }
  244         if (c->throttled && c->ibuf.w.queued < CTL_MSG_LOW_MARK) {
  245             if (imsg_ctl_rde(IMSG_XON, c->ibuf.pid, NULL, 0) != -1)
  246                 c->throttled = 0;
  247         }
  248     }
  249 
  250     if (!(pfd->revents & POLLIN))
  251         return (0);
  252 
  253     if (((n = imsg_read_nofd(&c->ibuf)) == -1 && errno != EAGAIN) ||
  254         n == 0) {
  255         *ctl_cnt -= control_close(pfd->fd);
  256         return (1);
  257     }
  258 
  259     for (;;) {
  260         if ((n = imsg_get(&c->ibuf, &imsg)) == -1) {
  261             *ctl_cnt -= control_close(pfd->fd);
  262             return (1);
  263         }
  264 
  265         if (n == 0)
  266             break;
  267 
  268         if (c->restricted) {
  269             switch (imsg.hdr.type) {
  270             case IMSG_CTL_SHOW_NEIGHBOR:
  271             case IMSG_CTL_SHOW_NEXTHOP:
  272             case IMSG_CTL_SHOW_INTERFACE:
  273             case IMSG_CTL_SHOW_RIB_MEM:
  274             case IMSG_CTL_SHOW_TERSE:
  275             case IMSG_CTL_SHOW_TIMER:
  276             case IMSG_CTL_SHOW_NETWORK:
  277             case IMSG_CTL_SHOW_RIB:
  278             case IMSG_CTL_SHOW_RIB_PREFIX:
  279                 break;
  280             default:
  281                 /* clear imsg type to prevent processing */
  282                 imsg.hdr.type = IMSG_NONE;
  283                 control_result(c, CTL_RES_DENIED);
  284                 break;
  285             }
  286         }
  287 
  288         switch (imsg.hdr.type) {
  289         case IMSG_NONE:
  290             /* message was filtered out, nothing to do */
  291             break;
  292         case IMSG_CTL_FIB_COUPLE:
  293         case IMSG_CTL_FIB_DECOUPLE:
  294             imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
  295                 0, NULL, 0);
  296             break;
  297         case IMSG_CTL_SHOW_TERSE:
  298             TAILQ_FOREACH(p, peers, entry)
  299                 imsg_compose(&c->ibuf, IMSG_CTL_SHOW_NEIGHBOR,
  300                     0, 0, -1, p, sizeof(struct peer));
  301             imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1, NULL, 0);
  302             break;
  303         case IMSG_CTL_SHOW_NEIGHBOR:
  304             c->ibuf.pid = imsg.hdr.pid;
  305 
  306             if (imsg.hdr.len == IMSG_HEADER_SIZE +
  307                 sizeof(struct ctl_neighbor)) {
  308                 neighbor = imsg.data;
  309                 neighbor->descr[PEER_DESCR_LEN - 1] = 0;
  310             } else {
  311                 neighbor = NULL;
  312             }
  313             matched = 0;
  314             TAILQ_FOREACH(p, peers, entry) {
  315                 if (!peer_matched(p, neighbor))
  316                     continue;
  317 
  318                 matched = 1;
  319                 if (!neighbor || !neighbor->show_timers) {
  320                     imsg_ctl_rde(imsg.hdr.type,
  321                         imsg.hdr.pid,
  322                         p, sizeof(struct peer));
  323                 } else {
  324                     u_int            i;
  325                     time_t           d;
  326                     struct ctl_timer     ct;
  327 
  328                     imsg_compose(&c->ibuf,
  329                         IMSG_CTL_SHOW_NEIGHBOR,
  330                         0, 0, -1, p, sizeof(*p));
  331                     for (i = 1; i < Timer_Max; i++) {
  332                         if (!timer_running(p, i, &d))
  333                             continue;
  334                         ct.type = i;
  335                         ct.val = d;
  336                         imsg_compose(&c->ibuf,
  337                             IMSG_CTL_SHOW_TIMER,
  338                             0, 0, -1, &ct, sizeof(ct));
  339                     }
  340                 }
  341             }
  342             if (!matched && TAILQ_EMPTY(peers)) {
  343                 control_result(c, CTL_RES_NOSUCHPEER);
  344             } else if (!neighbor || !neighbor->show_timers) {
  345                 imsg_ctl_rde(IMSG_CTL_END, imsg.hdr.pid,
  346                     NULL, 0);
  347             } else {
  348                 imsg_compose(&c->ibuf, IMSG_CTL_END, 0, 0, -1,
  349                     NULL, 0);
  350             }
  351             break;
  352         case IMSG_CTL_NEIGHBOR_UP:
  353         case IMSG_CTL_NEIGHBOR_DOWN:
  354         case IMSG_CTL_NEIGHBOR_CLEAR:
  355         case IMSG_CTL_NEIGHBOR_RREFRESH:
  356         case IMSG_CTL_NEIGHBOR_DESTROY:
  357             if (imsg.hdr.len != IMSG_HEADER_SIZE +
  358                 sizeof(struct ctl_neighbor)) {
  359                 log_warnx("got IMSG_CTL_NEIGHBOR_ with "
  360                     "wrong length");
  361                 break;
  362             }
  363 
  364             neighbor = imsg.data;
  365             neighbor->descr[PEER_DESCR_LEN - 1] = 0;
  366 
  367             matched = 0;
  368             TAILQ_FOREACH(p, peers, entry) {
  369                 if (!peer_matched(p, neighbor))
  370                     continue;
  371 
  372                 matched = 1;
  373 
  374                 switch (imsg.hdr.type) {
  375                 case IMSG_CTL_NEIGHBOR_UP:
  376                     bgp_fsm(p, EVNT_START);
  377                     p->conf.down = 0;
  378                     p->conf.shutcomm[0] = '\0';
  379                     control_result(c, CTL_RES_OK);
  380                     break;
  381                 case IMSG_CTL_NEIGHBOR_DOWN:
  382                     p->conf.down = 1;
  383                     strlcpy(p->conf.shutcomm,
  384                         neighbor->shutcomm,
  385                         sizeof(neighbor->shutcomm));
  386                     session_stop(p, ERR_CEASE_ADMIN_DOWN);
  387                     control_result(c, CTL_RES_OK);
  388                     break;
  389                 case IMSG_CTL_NEIGHBOR_CLEAR:
  390                     strlcpy(p->conf.shutcomm,
  391                         neighbor->shutcomm,
  392                         sizeof(neighbor->shutcomm));
  393                     if (!p->conf.down) {
  394                         session_stop(p,
  395                             ERR_CEASE_ADMIN_RESET);
  396                         timer_set(p, Timer_IdleHold,
  397                             SESSION_CLEAR_DELAY);
  398                     } else {
  399                         session_stop(p,
  400                             ERR_CEASE_ADMIN_DOWN);
  401                     }
  402                     control_result(c, CTL_RES_OK);
  403                     break;
  404                 case IMSG_CTL_NEIGHBOR_RREFRESH:
  405                     if (session_neighbor_rrefresh(p))
  406                         control_result(c,
  407                             CTL_RES_NOCAP);
  408                     else
  409                         control_result(c, CTL_RES_OK);
  410                     break;
  411                 case IMSG_CTL_NEIGHBOR_DESTROY:
  412                     if (!p->template)
  413                         control_result(c,
  414                             CTL_RES_BADPEER);
  415                     else if (p->state != STATE_IDLE)
  416                         control_result(c,
  417                             CTL_RES_BADSTATE);
  418                     else {
  419                         /*
  420                          * Mark as deleted, will be
  421                          * collected on next poll loop.
  422                          */
  423                         p->reconf_action =
  424                             RECONF_DELETE;
  425                         control_result(c, CTL_RES_OK);
  426                     }
  427                     break;
  428                 default:
  429                     fatal("king bula wants more humppa");
  430                 }
  431             }
  432             if (!matched)
  433                 control_result(c, CTL_RES_NOSUCHPEER);
  434             break;
  435         case IMSG_CTL_RELOAD:
  436         case IMSG_CTL_SHOW_INTERFACE:
  437         case IMSG_CTL_SHOW_FIB_TABLES:
  438             c->ibuf.pid = imsg.hdr.pid;
  439             imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
  440                 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
  441             break;
  442         case IMSG_CTL_KROUTE:
  443         case IMSG_CTL_KROUTE_ADDR:
  444         case IMSG_CTL_SHOW_NEXTHOP:
  445             c->ibuf.pid = imsg.hdr.pid;
  446             imsg_ctl_parent(imsg.hdr.type, imsg.hdr.peerid,
  447                 imsg.hdr.pid, imsg.data, imsg.hdr.len -
  448                 IMSG_HEADER_SIZE);
  449             break;
  450         case IMSG_CTL_SHOW_RIB:
  451         case IMSG_CTL_SHOW_RIB_PREFIX:
  452             if (imsg.hdr.len != IMSG_HEADER_SIZE +
  453                 sizeof(struct ctl_show_rib_request)) {
  454                 log_warnx("got IMSG_CTL_SHOW_RIB with "
  455                     "wrong length");
  456                 break;
  457             }
  458 
  459             ribreq = imsg.data;
  460             neighbor = &ribreq->neighbor;
  461             neighbor->descr[PEER_DESCR_LEN - 1] = 0;
  462 
  463             /* check if at least one neighbor exists */
  464             TAILQ_FOREACH(p, peers, entry)
  465                 if (peer_matched(p, neighbor))
  466                     break;
  467             if (p == NULL && TAILQ_EMPTY(peers)) {
  468                 control_result(c, CTL_RES_NOSUCHPEER);
  469                 break;
  470             }
  471 
  472             if ((imsg.hdr.type == IMSG_CTL_SHOW_RIB_PREFIX)
  473                 && (ribreq->prefix.aid == AID_UNSPEC)) {
  474                 /* malformed request, must specify af */
  475                 control_result(c, CTL_RES_PARSE_ERROR);
  476                 break;
  477             }
  478 
  479             c->ibuf.pid = imsg.hdr.pid;
  480             c->terminate = 1;
  481 
  482             imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
  483                 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
  484             break;
  485         case IMSG_CTL_SHOW_NETWORK:
  486             c->terminate = 1;
  487             /* FALLTHROUGH */
  488         case IMSG_CTL_SHOW_RIB_MEM:
  489             c->ibuf.pid = imsg.hdr.pid;
  490             imsg_ctl_rde(imsg.hdr.type, imsg.hdr.pid,
  491                 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
  492             break;
  493         case IMSG_NETWORK_ADD:
  494         case IMSG_NETWORK_ASPATH:
  495         case IMSG_NETWORK_ATTR:
  496         case IMSG_NETWORK_REMOVE:
  497         case IMSG_NETWORK_FLUSH:
  498         case IMSG_NETWORK_DONE:
  499         case IMSG_FILTER_SET:
  500             imsg_ctl_rde(imsg.hdr.type, 0,
  501                 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
  502             break;
  503         case IMSG_CTL_LOG_VERBOSE:
  504             if (imsg.hdr.len != IMSG_HEADER_SIZE +
  505                 sizeof(verbose))
  506                 break;
  507 
  508             /* forward to other processes */
  509             imsg_ctl_parent(imsg.hdr.type, 0, imsg.hdr.pid,
  510                 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
  511             imsg_ctl_rde(imsg.hdr.type, 0,
  512                 imsg.data, imsg.hdr.len - IMSG_HEADER_SIZE);
  513 
  514             memcpy(&verbose, imsg.data, sizeof(verbose));
  515             log_setverbose(verbose);
  516             break;
  517         default:
  518             break;
  519         }
  520         imsg_free(&imsg);
  521     }
  522 
  523     return (0);
  524 }
  525 
  526 int
  527 control_imsg_relay(struct imsg *imsg)
  528 {
  529     struct ctl_conn *c;
  530 
  531     if ((c = control_connbypid(imsg->hdr.pid)) == NULL)
  532         return (0);
  533 
  534     /* if command finished no need to send exit message */
  535     if (imsg->hdr.type == IMSG_CTL_END || imsg->hdr.type == IMSG_CTL_RESULT)
  536         c->terminate = 0;
  537 
  538     if (!c->throttled && c->ibuf.w.queued > CTL_MSG_HIGH_MARK) {
  539         if (imsg_ctl_rde(IMSG_XOFF, imsg->hdr.pid, NULL, 0) != -1)
  540             c->throttled = 1;
  541     }
  542 
  543     return (imsg_compose(&c->ibuf, imsg->hdr.type, 0, imsg->hdr.pid, -1,
  544         imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE));
  545 }
  546 
  547 void
  548 control_result(struct ctl_conn *c, u_int code)
  549 {
  550     imsg_compose(&c->ibuf, IMSG_CTL_RESULT, 0, c->ibuf.pid, -1,
  551         &code, sizeof(code));
  552 }
  553 
  554 /* This should go into libutil, from smtpd/mproc.c */
  555 ssize_t
  556 imsg_read_nofd(struct imsgbuf *ibuf)
  557 {
  558     ssize_t  n;
  559     char    *buf;
  560     size_t   len;
  561 
  562     buf = ibuf->r.buf + ibuf->r.wpos;
  563     len = sizeof(ibuf->r.buf) - ibuf->r.wpos;
  564 
  565     while ((n = recv(ibuf->fd, buf, len, 0)) == -1) {
  566         if (errno != EINTR)
  567             return (n);
  568     }
  569 
  570     ibuf->r.wpos += n;
  571     return (n);
  572 }