back to topotato report
topotato coverage report
Current view: top level - bgpd - bgp_io.c (source / functions) Hit Total Coverage
Test: test_bgp_distance_change.py::BGPDistanceChange Lines: 214 275 77.8 %
Date: 2023-02-24 18:37:13 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /* BGP I/O.
       2             :  * Implements packet I/O in a pthread.
       3             :  * Copyright (C) 2017  Cumulus Networks
       4             :  * Quentin Young
       5             :  *
       6             :  * This program is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 2 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * This program is distributed in the hope that it will be useful, but
      12             :  * WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      14             :  * General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program; see the file COPYING; if not, write to the
      18             :  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
      19             :  * MA 02110-1301 USA
      20             :  */
      21             : 
      22             : /* clang-format off */
      23             : #include <zebra.h>
      24             : #include <pthread.h>              // for pthread_mutex_unlock, pthread_mutex_lock
      25             : #include <sys/uio.h>              // for writev
      26             : 
      27             : #include "frr_pthread.h"
      28             : #include "linklist.h"         // for list_delete, list_delete_all_node, lis...
      29             : #include "log.h"              // for zlog_debug, safe_strerror, zlog_err
      30             : #include "memory.h"           // for MTYPE_TMP, XCALLOC, XFREE
      31             : #include "network.h"          // for ERRNO_IO_RETRY
      32             : #include "stream.h"           // for stream_get_endp, stream_getw_from, str...
      33             : #include "ringbuf.h"          // for ringbuf_remain, ringbuf_peek, ringbuf_...
      34             : #include "thread.h"           // for THREAD_OFF, THREAD_ARG, thread...
      35             : 
      36             : #include "bgpd/bgp_io.h"
      37             : #include "bgpd/bgp_debug.h"   // for bgp_debug_neighbor_events, bgp_type_str
      38             : #include "bgpd/bgp_errors.h"  // for expanded error reference information
      39             : #include "bgpd/bgp_fsm.h"     // for BGP_EVENT_ADD, bgp_event
      40             : #include "bgpd/bgp_packet.h"  // for bgp_notify_io_invalid...
      41             : #include "bgpd/bgp_trace.h"   // for frrtraces
      42             : #include "bgpd/bgpd.h"                // for peer, BGP_MARKER_SIZE, bgp_master, bm
      43             : /* clang-format on */
      44             : 
      45             : /* forward declarations */
      46             : static uint16_t bgp_write(struct peer *);
      47             : static uint16_t bgp_read(struct peer *peer, int *code_p);
      48             : static void bgp_process_writes(struct thread *);
      49             : static void bgp_process_reads(struct thread *);
      50             : static bool validate_header(struct peer *);
      51             : 
      52             : /* generic i/o status codes */
      53             : #define BGP_IO_TRANS_ERR (1 << 0) /* EAGAIN or similar occurred */
      54             : #define BGP_IO_FATAL_ERR (1 << 1) /* some kind of fatal TCP error */
      55             : #define BGP_IO_WORK_FULL_ERR (1 << 2) /* No room in work buffer */
      56             : 
      57             : /* Thread external API ----------------------------------------------------- */
      58             : 
      59          10 : void bgp_writes_on(struct peer *peer)
      60             : {
      61          10 :         struct frr_pthread *fpt = bgp_pth_io;
      62          10 :         assert(fpt->running);
      63             : 
      64          10 :         assert(peer->status != Deleted);
      65          10 :         assert(peer->obuf);
      66          10 :         assert(peer->ibuf);
      67          10 :         assert(peer->ibuf_work);
      68          10 :         assert(!peer->t_connect_check_r);
      69          10 :         assert(!peer->t_connect_check_w);
      70          10 :         assert(peer->fd);
      71             : 
      72          10 :         thread_add_write(fpt->master, bgp_process_writes, peer, peer->fd,
      73             :                          &peer->t_write);
      74          10 :         SET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON);
      75          10 : }
      76             : 
      77          25 : void bgp_writes_off(struct peer *peer)
      78             : {
      79          25 :         struct frr_pthread *fpt = bgp_pth_io;
      80          25 :         assert(fpt->running);
      81             : 
      82          25 :         thread_cancel_async(fpt->master, &peer->t_write, NULL);
      83          25 :         THREAD_OFF(peer->t_generate_updgrp_packets);
      84             : 
      85          25 :         UNSET_FLAG(peer->thread_flags, PEER_THREAD_WRITES_ON);
      86          25 : }
      87             : 
      88           5 : void bgp_reads_on(struct peer *peer)
      89             : {
      90           5 :         struct frr_pthread *fpt = bgp_pth_io;
      91           5 :         assert(fpt->running);
      92             : 
      93           5 :         assert(peer->status != Deleted);
      94           5 :         assert(peer->ibuf);
      95           5 :         assert(peer->fd);
      96           5 :         assert(peer->ibuf_work);
      97           5 :         assert(peer->obuf);
      98           5 :         assert(!peer->t_connect_check_r);
      99           5 :         assert(!peer->t_connect_check_w);
     100           5 :         assert(peer->fd);
     101             : 
     102           5 :         thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd,
     103             :                         &peer->t_read);
     104             : 
     105           5 :         SET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON);
     106           5 : }
     107             : 
     108          25 : void bgp_reads_off(struct peer *peer)
     109             : {
     110          25 :         struct frr_pthread *fpt = bgp_pth_io;
     111          25 :         assert(fpt->running);
     112             : 
     113          25 :         thread_cancel_async(fpt->master, &peer->t_read, NULL);
     114          25 :         THREAD_OFF(peer->t_process_packet);
     115          25 :         THREAD_OFF(peer->t_process_packet_error);
     116             : 
     117          25 :         UNSET_FLAG(peer->thread_flags, PEER_THREAD_READS_ON);
     118          25 : }
     119             : 
     120             : /* Thread internal functions ----------------------------------------------- */
     121             : 
     122             : /*
     123             :  * Called from I/O pthread when a file descriptor has become ready for writing.
     124             :  */
     125          10 : static void bgp_process_writes(struct thread *thread)
     126             : {
     127          10 :         static struct peer *peer;
     128          10 :         peer = THREAD_ARG(thread);
     129          10 :         uint16_t status;
     130          10 :         bool reschedule;
     131          10 :         bool fatal = false;
     132             : 
     133          10 :         if (peer->fd < 0)
     134             :                 return;
     135             : 
     136          10 :         struct frr_pthread *fpt = bgp_pth_io;
     137             : 
     138          20 :         frr_with_mutex (&peer->io_mtx) {
     139          10 :                 status = bgp_write(peer);
     140          10 :                 reschedule = (stream_fifo_head(peer->obuf) != NULL);
     141             :         }
     142             : 
     143             :         /* no problem */
     144          10 :         if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) {
     145          10 :         }
     146             : 
     147             :         /* problem */
     148          10 :         if (CHECK_FLAG(status, BGP_IO_FATAL_ERR)) {
     149             :                 reschedule = false;
     150             :                 fatal = true;
     151             :         }
     152             : 
     153             :         /* If suppress fib pending is enabled, route is advertised to peers when
     154             :          * the status is received from the FIB. The delay is added
     155             :          * to update group packet generate which will allow more routes to be
     156             :          * sent in the update message
     157             :          */
     158          10 :         if (reschedule) {
     159           0 :                 thread_add_write(fpt->master, bgp_process_writes, peer,
     160             :                                  peer->fd, &peer->t_write);
     161          10 :         } else if (!fatal) {
     162          10 :                 BGP_UPDATE_GROUP_TIMER_ON(&peer->t_generate_updgrp_packets,
     163             :                                           bgp_generate_updgrp_packets);
     164             :         }
     165             : }
     166             : 
     167          26 : static int read_ibuf_work(struct peer *peer)
     168             : {
     169             :         /* static buffer for transferring packets */
     170             :         /* shorter alias to peer's input buffer */
     171          26 :         struct ringbuf *ibw = peer->ibuf_work;
     172             :         /* packet size as given by header */
     173          26 :         uint16_t pktsize = 0;
     174          26 :         struct stream *pkt;
     175             : 
     176             :         /* ============================================== */
     177          52 :         frr_with_mutex (&peer->io_mtx) {
     178          26 :                 if (peer->ibuf->count >= bm->inq_limit)
     179           0 :                         return -ENOMEM;
     180             :         }
     181             : 
     182             :         /* check that we have enough data for a header */
     183          26 :         if (ringbuf_remain(ibw) < BGP_HEADER_SIZE)
     184             :                 return 0;
     185             : 
     186             :         /* check that header is valid */
     187          14 :         if (!validate_header(peer))
     188             :                 return -EBADMSG;
     189             : 
     190             :         /* header is valid; retrieve packet size */
     191          14 :         ringbuf_peek(ibw, BGP_MARKER_SIZE, &pktsize, sizeof(pktsize));
     192             : 
     193          14 :         pktsize = ntohs(pktsize);
     194             : 
     195             :         /* if this fails we are seriously screwed */
     196          14 :         assert(pktsize <= peer->max_packet_size);
     197             : 
     198             :         /*
     199             :          * If we have that much data, chuck it into its own
     200             :          * stream and append to input queue for processing.
     201             :          *
     202             :          * Otherwise, come back later.
     203             :          */
     204          14 :         if (ringbuf_remain(ibw) < pktsize)
     205             :                 return 0;
     206             : 
     207          14 :         pkt = stream_new(pktsize);
     208          14 :         assert(STREAM_WRITEABLE(pkt) == pktsize);
     209          14 :         assert(ringbuf_get(ibw, pkt->data, pktsize) == pktsize);
     210          14 :         stream_set_endp(pkt, pktsize);
     211             : 
     212          14 :         frrtrace(2, frr_bgp, packet_read, peer, pkt);
     213          28 :         frr_with_mutex (&peer->io_mtx) {
     214          14 :                 stream_fifo_push(peer->ibuf, pkt);
     215             :         }
     216             : 
     217          14 :         return pktsize;
     218             : }
     219             : 
     220             : /*
     221             :  * Called from I/O pthread when a file descriptor has become ready for reading,
     222             :  * or has hung up.
     223             :  *
     224             :  * We read as much data as possible, process as many packets as we can and
     225             :  * place them on peer->ibuf for secondary processing by the main thread.
     226             :  */
     227          13 : static void bgp_process_reads(struct thread *thread)
     228             : {
     229             :         /* clang-format off */
     230          13 :         static struct peer *peer;       /* peer to read from */
     231          13 :         uint16_t status;                /* bgp_read status code */
     232          13 :         bool fatal = false;             /* whether fatal error occurred */
     233          13 :         bool added_pkt = false;         /* whether we pushed onto ->ibuf */
     234          13 :         int code = 0;                   /* FSM code if error occurred */
     235          13 :         bool ibuf_full = false;         /* Is peer fifo IN Buffer full */
     236          13 :         static bool ibuf_full_logged;   /* Have we logged full already */
     237          13 :         int ret = 1;
     238             :         /* clang-format on */
     239             : 
     240          13 :         peer = THREAD_ARG(thread);
     241             : 
     242          13 :         if (peer->fd < 0 || bm->terminating)
     243           1 :                 return;
     244             : 
     245          13 :         struct frr_pthread *fpt = bgp_pth_io;
     246             : 
     247          13 :         frr_with_mutex (&peer->io_mtx) {
     248          13 :                 status = bgp_read(peer, &code);
     249             :         }
     250             : 
     251             :         /* error checking phase */
     252          13 :         if (CHECK_FLAG(status, BGP_IO_TRANS_ERR)) {
     253             :                 /* no problem; just don't process packets */
     254           0 :                 goto done;
     255             :         }
     256             : 
     257          13 :         if (CHECK_FLAG(status, BGP_IO_FATAL_ERR)) {
     258             :                 /* problem; tear down session */
     259           1 :                 fatal = true;
     260             : 
     261             :                 /* Handle the error in the main pthread, include the
     262             :                  * specific state change from 'bgp_read'.
     263             :                  */
     264           1 :                 thread_add_event(bm->master, bgp_packet_process_error,
     265             :                                  peer, code, &peer->t_process_packet_error);
     266           1 :                 goto done;
     267             :         }
     268             : 
     269          26 :         while (true) {
     270          26 :                 ret = read_ibuf_work(peer);
     271          26 :                 if (ret <= 0)
     272             :                         break;
     273             : 
     274             :                 added_pkt = true;
     275             :         }
     276             : 
     277          12 :         switch (ret) {
     278             :         case -EBADMSG:
     279             :                 fatal = true;
     280             :                 break;
     281           0 :         case -ENOMEM:
     282           0 :                 ibuf_full = true;
     283           0 :                 if (!ibuf_full_logged) {
     284           0 :                         if (bgp_debug_neighbor_events(peer))
     285           0 :                                 zlog_debug(
     286             :                                         "%s [Event] Peer Input-Queue is full: limit (%u)",
     287             :                                         peer->host, bm->inq_limit);
     288             : 
     289           0 :                         ibuf_full_logged = true;
     290             :                 }
     291             :                 break;
     292          12 :         default:
     293          12 :                 ibuf_full_logged = false;
     294          12 :                 break;
     295             :         }
     296             : 
     297          13 : done:
     298             :         /* handle invalid header */
     299          13 :         if (fatal) {
     300             :                 /* wipe buffer just in case someone screwed up */
     301           1 :                 ringbuf_wipe(peer->ibuf_work);
     302           1 :                 return;
     303             :         }
     304             : 
     305             :         /* ringbuf should be fully drained unless ibuf is full */
     306          12 :         if (!ibuf_full)
     307          12 :                 assert(ringbuf_space(peer->ibuf_work) >= peer->max_packet_size);
     308             : 
     309          12 :         thread_add_read(fpt->master, bgp_process_reads, peer, peer->fd,
     310             :                         &peer->t_read);
     311          12 :         if (added_pkt)
     312          12 :                 thread_add_event(bm->master, bgp_process_packet, peer, 0,
     313             :                                  &peer->t_process_packet);
     314             : }
     315             : 
     316             : /*
     317             :  * Flush peer output buffer.
     318             :  *
     319             :  * This function pops packets off of peer->obuf and writes them to peer->fd.
     320             :  * The amount of packets written is equal to the minimum of peer->wpkt_quanta
     321             :  * and the number of packets on the output buffer, unless an error occurs.
     322             :  *
     323             :  * If write() returns an error, the appropriate FSM event is generated.
     324             :  *
     325             :  * The return value is equal to the number of packets written
     326             :  * (which may be zero).
     327             :  */
     328          10 : static uint16_t bgp_write(struct peer *peer)
     329          10 : {
     330          10 :         uint8_t type;
     331          10 :         struct stream *s;
     332          10 :         int update_last_write = 0;
     333          10 :         unsigned int count;
     334          10 :         uint32_t uo = 0;
     335          10 :         uint16_t status = 0;
     336          10 :         uint32_t wpkt_quanta_old;
     337             : 
     338          10 :         int writenum = 0;
     339          10 :         int num;
     340          10 :         unsigned int iovsz;
     341          10 :         unsigned int strmsz;
     342          10 :         unsigned int total_written;
     343          10 :         time_t now;
     344             : 
     345          10 :         wpkt_quanta_old = atomic_load_explicit(&peer->bgp->wpkt_quanta,
     346             :                                                memory_order_relaxed);
     347          10 :         struct stream *ostreams[wpkt_quanta_old];
     348          10 :         struct stream **streams = ostreams;
     349          10 :         struct iovec iov[wpkt_quanta_old];
     350             : 
     351          10 :         s = stream_fifo_head(peer->obuf);
     352             : 
     353          10 :         if (!s)
     354           1 :                 goto done;
     355             : 
     356             :         count = iovsz = 0;
     357          20 :         while (count < wpkt_quanta_old && iovsz < array_size(iov) && s) {
     358          11 :                 ostreams[iovsz] = s;
     359          11 :                 iov[iovsz].iov_base = stream_pnt(s);
     360          11 :                 iov[iovsz].iov_len = STREAM_READABLE(s);
     361          11 :                 writenum += STREAM_READABLE(s);
     362          11 :                 s = s->next;
     363          11 :                 ++iovsz;
     364          11 :                 ++count;
     365             :         }
     366             : 
     367             :         strmsz = iovsz;
     368             :         total_written = 0;
     369             : 
     370           9 :         do {
     371           9 :                 num = writev(peer->fd, iov, iovsz);
     372             : 
     373           9 :                 if (num < 0) {
     374           0 :                         if (!ERRNO_IO_RETRY(errno)) {
     375           0 :                                 BGP_EVENT_ADD(peer, TCP_fatal_error);
     376             :                                 SET_FLAG(status, BGP_IO_FATAL_ERR);
     377             :                         } else {
     378             :                                 SET_FLAG(status, BGP_IO_TRANS_ERR);
     379             :                         }
     380             : 
     381             :                         break;
     382           9 :                 } else if (num != writenum) {
     383             :                         unsigned int msg_written = 0;
     384             :                         unsigned int ic = iovsz;
     385             : 
     386           0 :                         for (unsigned int i = 0; i < ic; i++) {
     387           0 :                                 size_t ss = iov[i].iov_len;
     388             : 
     389           0 :                                 if (ss > (unsigned int) num)
     390             :                                         break;
     391             : 
     392           0 :                                 msg_written++;
     393           0 :                                 iovsz--;
     394           0 :                                 writenum -= ss;
     395           0 :                                 num -= ss;
     396             :                         }
     397             : 
     398           0 :                         total_written += msg_written;
     399             : 
     400           0 :                         assert(total_written < count);
     401             : 
     402           0 :                         memmove(&iov, &iov[msg_written],
     403             :                                 sizeof(iov[0]) * iovsz);
     404           0 :                         streams = &streams[msg_written];
     405           0 :                         stream_forward_getp(streams[0], num);
     406           0 :                         iov[0].iov_base = stream_pnt(streams[0]);
     407           0 :                         iov[0].iov_len = STREAM_READABLE(streams[0]);
     408             : 
     409           0 :                         writenum -= num;
     410           0 :                         num = 0;
     411           0 :                         assert(writenum > 0);
     412             :                 } else {
     413             :                         total_written = strmsz;
     414             :                 }
     415             : 
     416           9 :         } while (num != writenum);
     417             : 
     418             :         /* Handle statistics */
     419          20 :         for (unsigned int i = 0; i < total_written; i++) {
     420          11 :                 s = stream_fifo_pop(peer->obuf);
     421             : 
     422          11 :                 assert(s == ostreams[i]);
     423             : 
     424             :                 /* Retrieve BGP packet type. */
     425          11 :                 stream_set_getp(s, BGP_MARKER_SIZE + 2);
     426          11 :                 type = stream_getc(s);
     427             : 
     428          11 :                 switch (type) {
     429           4 :                 case BGP_MSG_OPEN:
     430           4 :                         atomic_fetch_add_explicit(&peer->open_out, 1,
     431             :                                                   memory_order_relaxed);
     432           4 :                         break;
     433           5 :                 case BGP_MSG_UPDATE:
     434           5 :                         atomic_fetch_add_explicit(&peer->update_out, 1,
     435             :                                                   memory_order_relaxed);
     436           5 :                         uo++;
     437           5 :                         break;
     438           0 :                 case BGP_MSG_NOTIFY:
     439           0 :                         atomic_fetch_add_explicit(&peer->notify_out, 1,
     440             :                                                   memory_order_relaxed);
     441             :                         /* Double start timer. */
     442           0 :                         peer->v_start *= 2;
     443             : 
     444             :                         /* Overflow check. */
     445           0 :                         if (peer->v_start >= (60 * 2))
     446           0 :                                 peer->v_start = (60 * 2);
     447             : 
     448             :                         /*
     449             :                          * Handle Graceful Restart case where the state changes
     450             :                          * to Connect instead of Idle.
     451             :                          */
     452           0 :                         BGP_EVENT_ADD(peer, BGP_Stop);
     453           0 :                         goto done;
     454             : 
     455           2 :                 case BGP_MSG_KEEPALIVE:
     456           2 :                         atomic_fetch_add_explicit(&peer->keepalive_out, 1,
     457             :                                                   memory_order_relaxed);
     458           2 :                         break;
     459           0 :                 case BGP_MSG_ROUTE_REFRESH_NEW:
     460             :                 case BGP_MSG_ROUTE_REFRESH_OLD:
     461           0 :                         atomic_fetch_add_explicit(&peer->refresh_out, 1,
     462             :                                                   memory_order_relaxed);
     463           0 :                         break;
     464           0 :                 case BGP_MSG_CAPABILITY:
     465           0 :                         atomic_fetch_add_explicit(&peer->dynamic_cap_out, 1,
     466             :                                                   memory_order_relaxed);
     467           0 :                         break;
     468             :                 }
     469             : 
     470          11 :                 stream_free(s);
     471          11 :                 ostreams[i] = NULL;
     472          11 :                 update_last_write = 1;
     473             :         }
     474             : 
     475           9 : done : {
     476          10 :         now = monotime(NULL);
     477             :         /*
     478             :          * Update last_update if UPDATEs were written.
     479             :          * Note: that these are only updated at end,
     480             :          *       not per message (i.e., per loop)
     481             :          */
     482          10 :         if (uo)
     483           3 :                 atomic_store_explicit(&peer->last_update, now,
     484             :                                       memory_order_relaxed);
     485             : 
     486             :         /* If we TXed any flavor of packet */
     487          10 :         if (update_last_write) {
     488           9 :                 atomic_store_explicit(&peer->last_write, now,
     489             :                                       memory_order_relaxed);
     490           9 :                 peer->last_sendq_ok = now;
     491             :         }
     492             : }
     493             : 
     494          10 :         return status;
     495             : }
     496             : 
     497             : /*
     498             :  * Reads a chunk of data from peer->fd into peer->ibuf_work.
     499             :  *
     500             :  * code_p
     501             :  *    Pointer to location to store FSM event code in case of fatal error.
     502             :  *
     503             :  * @return status flag (see top-of-file)
     504             :  */
     505          13 : static uint16_t bgp_read(struct peer *peer, int *code_p)
     506             : {
     507          13 :         size_t readsize; /* how many bytes we want to read */
     508          13 :         ssize_t nbytes;  /* how many bytes we actually read */
     509          13 :         size_t ibuf_work_space; /* space we can read into the work buf */
     510          13 :         uint16_t status = 0;
     511             : 
     512          13 :         ibuf_work_space = ringbuf_space(peer->ibuf_work);
     513             : 
     514          13 :         if (ibuf_work_space == 0) {
     515          13 :                 SET_FLAG(status, BGP_IO_WORK_FULL_ERR);
     516             :                 return status;
     517             :         }
     518             : 
     519          13 :         readsize = MIN(ibuf_work_space, sizeof(peer->ibuf_scratch));
     520             : 
     521          13 :         nbytes = read(peer->fd, peer->ibuf_scratch, readsize);
     522             : 
     523             :         /* EAGAIN or EWOULDBLOCK; come back later */
     524          13 :         if (nbytes < 0 && ERRNO_IO_RETRY(errno)) {
     525             :                 SET_FLAG(status, BGP_IO_TRANS_ERR);
     526           0 :         } else if (nbytes < 0) {
     527             :                 /* Fatal error; tear down session */
     528           0 :                 flog_err(EC_BGP_UPDATE_RCV,
     529             :                          "%s [Error] bgp_read_packet error: %s", peer->host,
     530             :                          safe_strerror(errno));
     531             : 
     532             :                 /* Handle the error in the main pthread. */
     533           0 :                 if (code_p)
     534           0 :                         *code_p = TCP_fatal_error;
     535             : 
     536             :                 SET_FLAG(status, BGP_IO_FATAL_ERR);
     537             : 
     538          13 :         } else if (nbytes == 0) {
     539             :                 /* Received EOF / TCP session closed */
     540           1 :                 if (bgp_debug_neighbor_events(peer))
     541           0 :                         zlog_debug("%s [Event] BGP connection closed fd %d",
     542             :                                    peer->host, peer->fd);
     543             : 
     544             :                 /* Handle the error in the main pthread. */
     545           1 :                 if (code_p)
     546           1 :                         *code_p = TCP_connection_closed;
     547             : 
     548             :                 SET_FLAG(status, BGP_IO_FATAL_ERR);
     549             :         } else {
     550          12 :                 assert(ringbuf_put(peer->ibuf_work, peer->ibuf_scratch, nbytes)
     551             :                        == (size_t)nbytes);
     552             :         }
     553             : 
     554             :         return status;
     555             : }
     556             : 
     557             : /*
     558             :  * Called after we have read a BGP packet header. Validates marker, message
     559             :  * type and packet length. If any of these aren't correct, sends a notify.
     560             :  *
     561             :  * Assumes that there are at least BGP_HEADER_SIZE readable bytes in the input
     562             :  * buffer.
     563             :  */
     564          14 : static bool validate_header(struct peer *peer)
     565             : {
     566          14 :         uint16_t size;
     567          14 :         uint8_t type;
     568          14 :         struct ringbuf *pkt = peer->ibuf_work;
     569             : 
     570          14 :         static const uint8_t m_correct[BGP_MARKER_SIZE] = {
     571             :                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
     572             :                 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
     573          14 :         uint8_t m_rx[BGP_MARKER_SIZE] = {0x00};
     574             : 
     575          14 :         if (ringbuf_peek(pkt, 0, m_rx, BGP_MARKER_SIZE) != BGP_MARKER_SIZE)
     576             :                 return false;
     577             : 
     578          14 :         if (memcmp(m_correct, m_rx, BGP_MARKER_SIZE) != 0) {
     579           0 :                 bgp_notify_io_invalid(peer, BGP_NOTIFY_HEADER_ERR,
     580             :                                       BGP_NOTIFY_HEADER_NOT_SYNC, NULL, 0);
     581           0 :                 return false;
     582             :         }
     583             : 
     584             :         /* Get size and type in network byte order. */
     585          14 :         ringbuf_peek(pkt, BGP_MARKER_SIZE, &size, sizeof(size));
     586          14 :         ringbuf_peek(pkt, BGP_MARKER_SIZE + 2, &type, sizeof(type));
     587             : 
     588          14 :         size = ntohs(size);
     589             : 
     590             :         /* BGP type check. */
     591          14 :         if (type != BGP_MSG_OPEN && type != BGP_MSG_UPDATE
     592             :             && type != BGP_MSG_NOTIFY && type != BGP_MSG_KEEPALIVE
     593             :             && type != BGP_MSG_ROUTE_REFRESH_NEW
     594          14 :             && type != BGP_MSG_ROUTE_REFRESH_OLD
     595           0 :             && type != BGP_MSG_CAPABILITY) {
     596           0 :                 if (bgp_debug_neighbor_events(peer))
     597           0 :                         zlog_debug("%s unknown message type 0x%02x", peer->host,
     598             :                                    type);
     599             : 
     600           0 :                 bgp_notify_io_invalid(peer, BGP_NOTIFY_HEADER_ERR,
     601             :                                       BGP_NOTIFY_HEADER_BAD_MESTYPE, &type, 1);
     602           0 :                 return false;
     603             :         }
     604             : 
     605             :         /* Minimum packet length check. */
     606          14 :         if ((size < BGP_HEADER_SIZE) || (size > peer->max_packet_size)
     607          14 :             || (type == BGP_MSG_OPEN && size < BGP_MSG_OPEN_MIN_SIZE)
     608          14 :             || (type == BGP_MSG_UPDATE && size < BGP_MSG_UPDATE_MIN_SIZE)
     609          14 :             || (type == BGP_MSG_NOTIFY && size < BGP_MSG_NOTIFY_MIN_SIZE)
     610          14 :             || (type == BGP_MSG_KEEPALIVE && size != BGP_MSG_KEEPALIVE_MIN_SIZE)
     611          14 :             || (type == BGP_MSG_ROUTE_REFRESH_NEW
     612           0 :                 && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE)
     613          14 :             || (type == BGP_MSG_ROUTE_REFRESH_OLD
     614           0 :                 && size < BGP_MSG_ROUTE_REFRESH_MIN_SIZE)
     615          14 :             || (type == BGP_MSG_CAPABILITY
     616           0 :                 && size < BGP_MSG_CAPABILITY_MIN_SIZE)) {
     617           0 :                 if (bgp_debug_neighbor_events(peer)) {
     618           0 :                         zlog_debug("%s bad message length - %d for %s",
     619             :                                    peer->host, size,
     620             :                                    type == 128 ? "ROUTE-REFRESH"
     621             :                                                : bgp_type_str[(int)type]);
     622             :                 }
     623             : 
     624           0 :                 uint16_t nsize = htons(size);
     625             : 
     626           0 :                 bgp_notify_io_invalid(peer, BGP_NOTIFY_HEADER_ERR,
     627             :                                       BGP_NOTIFY_HEADER_BAD_MESLEN,
     628             :                                       (unsigned char *)&nsize, 2);
     629           0 :                 return false;
     630             :         }
     631             : 
     632             :         return true;
     633             : }

Generated by: LCOV version v1.16-topotato