diff options
Diffstat (limited to 'bgpd/bgp_msg_write.c')
-rw-r--r-- | bgpd/bgp_msg_write.c | 871 |
1 files changed, 871 insertions, 0 deletions
diff --git a/bgpd/bgp_msg_write.c b/bgpd/bgp_msg_write.c new file mode 100644 index 00000000..21e48d69 --- /dev/null +++ b/bgpd/bgp_msg_write.c @@ -0,0 +1,871 @@ +/* BGP message writing -- in BGP Engine + * Copyright (C) 1999 Kunihiro Ishiguro + * + * Recast for pthreaded bgpd: Copyright (C) 2009 Chris Hall (GMCH), Highwayman + * + * This file is part of GNU Zebra. + * + * GNU Zebra is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * GNU Zebra is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Zebra; see the file COPYING. If not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <zebra.h> +#include <stdbool.h> + +#include "bgpd/bgp_common.h" +#include "bgpd/bgp_msg_write.h" +#include "bgpd/bgp_route_refresh.h" + +#include "thread.h" +#include "stream.h" +#include "network.h" +#include "prefix.h" +#include "command.h" +#include "log.h" +#include "memory.h" +#include "sockunion.h" /* for inet_ntop () */ +#include "linklist.h" +#include "plist.h" + +#include "bgpd/bgpd.h" + +#include "bgpd/bgp_peer.h" + +#include "bgpd/bgp_table.h" +#include "bgpd/bgp_dump.h" +#include "bgpd/bgp_attr.h" +#include "bgpd/bgp_debug.h" +#include "bgpd/bgp_fsm.h" +#include "bgpd/bgp_route.h" +#include "bgpd/bgp_packet.h" +#include "bgpd/bgp_open.h" +#include "bgpd/bgp_aspath.h" +#include "bgpd/bgp_community.h" +#include "bgpd/bgp_ecommunity.h" +#include "bgpd/bgp_network.h" +#include "bgpd/bgp_mplsvpn.h" +#include "bgpd/bgp_advertise.h" +#include "bgpd/bgp_vty.h" + +/*============================================================================== + * BGP Engine BGP Message encoding and sending. + * + * + */ + +/*============================================================================== + * NOTIFICATION and KEEPALIVE + */ + +/*------------------------------------------------------------------------------ + * Make NOTIFICATION message and dispatch. + * + * NB: the write buffers MUST have been flushed -- so demand success ! + * + * Returns: 1 => written to wbuff -- qpselect will write from there + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! + * + * NB: requires the session LOCKED -- connection-wise + */ +extern int +bgp_msg_write_notification(bgp_connection connection, bgp_notify notification) +{ + struct stream *s = connection->obuf ; + int length; + + ++connection->session->stats.notify_out ; + + assert(notification != NULL) ; + + bgp_notify_print(connection->session->peer, notification) ; + + /* Make NOTIFY message header */ + bgp_packet_set_marker (s, BGP_MSG_NOTIFY); + + /* Set notify code and subcode */ + stream_putc(s, bgp_notify_get_code(notification)) ; + stream_putc(s, bgp_notify_get_subcode(notification)) ; + + /* Copy the data portion, if any. */ + length = bgp_notify_get_length(notification) ; + if (length != 0) + stream_write(s, bgp_notify_get_data(notification), length) ; + + /* Set and get BGP packet length. */ + length = bgp_packet_set_size(s); + + /* Set flag so that write_action raises required event when buffer becomes + * empty. + */ + connection->notification_pending = 1 ; + + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; + +/*------------------------------------------------------------------------------ + * Make KEEPALIVE message and dispatch. + * + * Generally, does nothing if the write buffer is not empty -- a KEEPALIVE is + * redundant if there is stuff waiting to go ! + * + * KEEPALIVE is sent in response to OPEN, and that MUST be sent, hence the + * 'must_send' option. + * + * Returns: 1 => written to wbuff -- qpselect will write from there + * 0 => nothing written -- no need, buffer not empty ! + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! + * + * NB: requires the session LOCKED -- connection-wise + */ +extern int +bgp_msg_send_keepalive(bgp_connection connection, bool must_send) +{ + struct stream *s = connection->obuf ; + int length; + + if (!must_send && !bgp_connection_write_empty(connection)) + return 0 ; + + ++connection->session->stats.keepalive_out ; + + /* Make KEEPALIVE message -- comprises header only */ + bgp_packet_set_marker(s, BGP_MSG_KEEPALIVE); + length = bgp_packet_set_size(s); + + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + + if (BGP_DEBUG (keepalive, KEEPALIVE)) + zlog_debug ("%s sending KEEPALIVE", connection->host); + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + connection->host, BGP_MSG_KEEPALIVE, length); + + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; + +/*============================================================================== + * OPEN message -- transform bgp_open_state into BGP message + */ + +static void +bgp_open_options(struct stream *s, bgp_open_state open_state, + bool cap_suppress) ; + +static void +bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi, + u_char cap_code, u_char orf_type, u_char mode) ; + +/*------------------------------------------------------------------------------ + * Make OPEN message and dispatch. + * + * OPEN is the first message to be sent. If the buffers are not empty, + * something is badly wrong ! + * + * Returns: 1 => written to wbuff -- qpselect will write from there + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! + * + * NB: requires the session LOCKED -- connection-wise + */ +extern int +bgp_msg_send_open(bgp_connection connection, bgp_open_state open_state) +{ + struct stream *s = connection->obuf ; + int length ; + + ++connection->session->stats.open_out ; + + /* Make OPEN message header */ + bgp_packet_set_marker(s, BGP_MSG_OPEN) ; + + /* Set OPEN message fixed part */ + stream_putc(s, BGP_VERSION_4) ; + stream_putw(s, (open_state->my_as <= BGP_AS_MAX) + ? (u_int16_t) open_state->my_as : BGP_AS_TRANS) ; + stream_putw(s, open_state->holdtime) ; + stream_put_ipv4(s, open_state->bgp_id) ; + + /* Set OPEN message options */ + bgp_open_options(s, open_state, connection->cap_suppress) ; + + /* Set BGP message length. */ + length = bgp_packet_set_size(s) ; + + if (BGP_DEBUG (normal, NORMAL)) + { + char buf[INET_ADDRSTRLEN] ; + const char* no_cap ; + + if (!open_state->can_capability) + no_cap = " (sans capabilities)" ; + else if (connection->cap_suppress) + no_cap = " (capabilities suppressed)" ; + else + no_cap = "" ; + + inet_ntop(AF_INET, &open_state->bgp_id, buf, INET_ADDRSTRLEN) ; + + zlog_debug ("%s sending OPEN, version %d, my as %u, holdtime %d, id %s%s", + connection->host, BGP_VERSION_4, open_state->my_as, + open_state->holdtime, buf, no_cap) ; + + } ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s send message type %d, length (incl. header) %d", + connection->host, BGP_MSG_OPEN, length); + + /* Dump packet if debug option is set. */ + /* bgp_packet_dump (s); */ + + /* Finally -- write the obuf away */ + return bgp_connection_write(connection, s) ; +} ; + +enum +{ + have_ipv6 = +#ifdef HAVE_IPV6 + 1 +#else + 0 +#endif +} ; + +/*------------------------------------------------------------------------------ + * Add options to given encoded OPEN message. + * + * Supports the status quo: only Capability Options. + * + * Creates an empty options part of there are no capabilities to set. + */ +static void +bgp_open_options(struct stream *s, bgp_open_state open_state, bool cap_suppress) +{ + u_char len ; + unsigned long cp ; + qafx_num_t qafx ; + + /* Remember current pointer for Opt Parm Len. */ + cp = stream_get_endp (s); + + /* Opt Parm Len. */ + stream_putc(s, 0); + + /* If do not send capability, quit now -- zero options. */ + if (!open_state->can_capability || cap_suppress) + return; + + /* TODO: RFC 5492 (2009): SHOULD send only one Capability Option !! */ + /* RFC 3392 (2002): silent on the matter */ + /* RFC 2842 (2000): silent on the matter */ + + /* Send capability for every AFI/SAFI supported. */ + for (qafx = qafx_num_first ; qafx <= qafx_num_last ; ++qafx) + { + if (open_state->can_mp_ext & qafx_bit(qafx)) + { + iAFI_t afi = get_iAFI(qafx) ; + iSAFI_t safi = get_iSAFI(qafx) ; + + if (!have_ipv6 && (afi == iAFI_IP6)) + continue ; + + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_MP_LEN + 2); + stream_putc (s, CAPABILITY_CODE_MP); + stream_putc (s, CAPABILITY_CODE_MP_LEN); + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + } ; + } ; + + /* Route refresh. */ + + if (open_state->can_r_refresh & bgp_form_pre) + { + stream_putc (s, BGP_OPEN_OPT_CAP) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_OLD) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN) ; + } ; + + if (open_state->can_r_refresh & bgp_form_pre) + { + stream_putc (s, BGP_OPEN_OPT_CAP) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN + 2) ; + stream_putc (s, CAPABILITY_CODE_REFRESH) ; + stream_putc (s, CAPABILITY_CODE_REFRESH_LEN) ; + } ; + + /* AS4 */ + + if (open_state->can_as4) + { + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_AS4_LEN + 2); + stream_putc (s, CAPABILITY_CODE_AS4); + stream_putc (s, CAPABILITY_CODE_AS4_LEN); + stream_putl (s, open_state->my_as) ; + } ; + + /* ORF Capabilities */ + + for (qafx = qafx_num_first ; qafx <= qafx_num_last ; ++qafx) + { + u_char mode = 0 ; + + if (open_state->can_orf_prefix_send & qafx_bit(qafx)) + mode |= ORF_MODE_SEND ; + if (open_state->can_orf_prefix_recv & qafx_bit(qafx)) + mode |= ORF_MODE_RECEIVE ; + + confirm((ORF_MODE_SEND | ORF_MODE_RECEIVE) == ORF_MODE_BOTH) ; + + if (mode != 0) + { + iAFI_t afi = get_iAFI(qafx) ; + iSAFI_t safi = get_iSAFI(qafx) ; + + if (!have_ipv6 && (afi == iAFI_IP6)) + continue ; + + if (open_state->can_orf_prefix & bgp_form_pre) + bgp_open_capability_orf(s, afi, safi, CAPABILITY_CODE_ORF_OLD, + ORF_TYPE_PREFIX_OLD, mode) ; + + if (open_state->can_orf_prefix & bgp_form_rfc) + bgp_open_capability_orf(s, afi, safi, CAPABILITY_CODE_ORF, + ORF_TYPE_PREFIX, mode) ; + } ; + } ; + + /* Dynamic capability. */ + if (open_state->can_dynamic) + { + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN + 2); + stream_putc (s, CAPABILITY_CODE_DYNAMIC); + stream_putc (s, CAPABILITY_CODE_DYNAMIC_LEN); + } ; + + /* Graceful restart capability */ + if (open_state->can_g_restart) + { + stream_putc (s, BGP_OPEN_OPT_CAP); + stream_putc (s, CAPABILITY_CODE_RESTART_LEN + 2); + stream_putc (s, CAPABILITY_CODE_RESTART); + stream_putc (s, CAPABILITY_CODE_RESTART_LEN); + stream_putw (s, open_state->restart_time); + } ; + + /* TODO: restarting flag ?? */ + /* TODO: graceful restart preserving forwarding state */ + + /* Total Opt Parm Len. */ + len = stream_get_endp (s) - cp - 1; + stream_putc_at (s, cp, len); +} ; + +/*------------------------------------------------------------------------------ + * Add an ORF capability to the given encoded OPEN message. + * + * Supports the status quo: only prefix-list filtering ! + */ +static void +bgp_open_capability_orf (struct stream *s, iAFI_t afi, iSAFI_t safi, + u_char cap_code, u_char orf_type, u_char mode) +{ + u_char cap_len; + u_char orf_len; + unsigned long capp; + unsigned long orfp; + unsigned long numberp; + + int number_of_orfs = 0; + + stream_putc (s, BGP_OPEN_OPT_CAP); + capp = stream_get_endp (s); /* Set Capability Len Pointer */ + stream_putc (s, 0); /* Capability Length */ + stream_putc (s, cap_code); /* Capability Code */ + orfp = stream_get_endp (s); /* Set ORF Len Pointer */ + stream_putc (s, 0); /* ORF Length */ + + stream_putw (s, afi); + stream_putc (s, 0); + stream_putc (s, safi); + + numberp = stream_get_endp (s); /* Set Number Pointer */ + stream_putc (s, 0); /* Number of ORFs */ + + /* Address Prefix ORF */ + stream_putc (s, orf_type) ; /* type of ORF */ + stream_putc (s, mode) ; + + number_of_orfs++; + + /* Total Number of ORFs. */ + stream_putc_at (s, numberp, number_of_orfs); + + /* Total ORF Len. */ + orf_len = stream_get_endp (s) - orfp - 1; + stream_putc_at (s, orfp, orf_len); + + /* Total Capability Len. */ + cap_len = stream_get_endp (s) - capp - 1; + stream_putc_at (s, capp, cap_len); +} ; + +/*============================================================================== + * ROUTE-REFRESH -- transform bgp_prefix_orf_update into BGP message + */ + +static int +bgp_msg_orf_part(struct stream* s, bgp_connection connection, + bgp_route_refresh rr) ; +static int +bgp_msg_orf_unknown(struct stream* s, bgp_orf_unknown_entry orf_unknown, + bgp_size_t left) ; +static int +bgp_msg_orf_remove_all(struct stream* s, bgp_size_t left) ; + +static int +bgp_msg_orf_prefix(struct stream* s, uint8_t common, + orf_prefix orfpe, bgp_size_t left) ; + +/*------------------------------------------------------------------------------ + * Make Route-Refresh message(s) and dispatch. + * + * May return before all required messages have been sent, if the write + * buffer is or becomes full. The "next" entry in the "bgp_prefix_orf_update" + * allows the process to be continued, later. + * + * If has to send more than one message, then all but the last will be set + * "defer". The last will be set as per the defer flag. + * + * Supports the status quo, only Address-Prefix ORF. + * + * Returns: 1 => written to wbuff -- qpselect will write from there + * 0 => nothing written -- insufficient space in wbuff + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! + * + * NB: requires the session LOCKED -- connection-wise + */ +extern int +bgp_msg_send_route_refresh(bgp_connection connection, bgp_route_refresh rr) +{ + struct stream *s = connection->obuf ; + uint8_t msg_type ; + bool done ; + bgp_size_t msg_len ; + + ++connection->session->stats.refresh_out ; + + msg_type = (connection->route_refresh == bgp_form_pre) + ? BGP_MT_ROUTE_REFRESH_pre + : BGP_MT_ROUTE_REFRESH ; + done = (bgp_orf_get_count(rr) == 0) ; + + do + { + if (bgp_connection_write_cannot_max(connection)) + return 0 ; + + /* Construct BGP message header for new/old form ROUTE-REFRESH */ + bgp_packet_set_marker(s, msg_type) ; + + /* Encode Route Refresh message. */ + stream_putw(s, rr->afi) ; + stream_putc(s, 0); + stream_putc(s, rr->safi); + + /* Process as many (remaining) ORF entries as can into message */ + if (!done) + done = bgp_msg_orf_part(s, connection, rr) ; + + /* Set BGP message length & dispatch. */ + msg_len = bgp_packet_set_size(s) ; + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("%s sending REFRESH_REQ for afi/safi: %d/%d length %d", + connection->host, rr->afi, rr->safi, msg_len) ; + + bgp_connection_write(connection, s) ; + } while (!done) ; + + return done ; +} ; + +/*------------------------------------------------------------------------------ + * Set the current ORF entry length (if any) + * + * Returns total length of BGP message. + */ +inline static bgp_size_t +bgp_msg_set_orf_length(struct stream* s, unsigned long elenp) +{ + bgp_size_t length = stream_get_endp(s) ; + + if (elenp != 0) + stream_putw_at(s, elenp, length - elenp - 2) ; + + return length ; +} ; + +/*------------------------------------------------------------------------------ + * Put ORF entries to the given stream until run out of entries or run out + * of room in the message. + * + * There MUST BE at least one ORF entry to go. + * + * Returns true <=> done all available entries. + */ +static int +bgp_msg_orf_part(struct stream* s, bgp_connection connection, + bgp_route_refresh rr) +{ + bgp_orf_entry entry ; + + uint8_t orf_type ; + uint8_t orf_type_sent ; + + unsigned long whenp ; /* where the "when" byte is */ + unsigned long elenp ; /* where the entries length is */ + + bgp_size_t left ; + bgp_size_t length ; + bool done ; + bool first ; + + /* Heading for Prefix-Address ORF type section */ + whenp = stream_get_endp(s) ; /* position of "when" */ + stream_putc(s, rr->defer ? BGP_ORF_WTR_DEFER : BGP_ORF_WTR_IMMEDIATE) ; + + /* Process ORF entries until run out of entries or space */ + + elenp = 0 ; /* no entries length, yet */ + orf_type = 0 ; /* no ORF type, yet */ + + first = 1 ; /* next entry is first of its ORF type */ + + while (1) + { + entry = bgp_orf_get_entry(rr, rr->next) ; + done = (entry == NULL) ; + + if (done) + break ; + + /* How much space is there left -- give up if very little + * + * What is "very little" is arbitrary, BUT MUST cover the ORF Type + * byte and the Length of ORF entries word, AT LEAST. + * */ + left = BGP_MSG_MAX_L - stream_get_endp(s) ; + + if (left < 16) + break ; /* NB: done == false */ + + confirm(16 > BGP_ORF_MIN_L) ; /* Type & Length */ + + /* Start new collection of ORF entries, if required. */ + if (first || (orf_type != entry->orf_type)) + { + /* fill in length of previous ORF entries, if any */ + bgp_msg_set_orf_length(s, elenp) ; + + /* set type and dummy entries length. */ + orf_type = entry->orf_type ; + orf_type_sent = entry->orf_type ; + + if ((orf_type == BGP_ORF_T_PREFIX) && + (connection->orf_prefix == bgp_form_pre)) + orf_type_sent = BGP_ORF_T_PREFIX_pre ; + + stream_putc(s, orf_type_sent) ; /* ORF entries type */ + + elenp = stream_get_endp(s) ; /* offset of the length */ + stream_putw(s, 0) ; /* length of ORF entries */ + + first = 1 ; /* next ORF entry is first of collection */ + } ; + + /* Insert the entry, if will fit. + * + * sets done <=> fitted + */ + if (entry->unknown) + done = bgp_msg_orf_unknown(s, &entry->body.orf_unknown, left) ; + else + { + if (entry->remove_all) + done = bgp_msg_orf_remove_all(s, left) ; + else + { + uint8_t common = (entry->remove ? BGP_ORF_EA_REMOVE + : BGP_ORF_EA_ADD) + | (entry->deny ? BGP_ORF_EA_DENY + : BGP_ORF_EA_PERMIT) ; + switch (entry->orf_type) + { + case BGP_ORF_T_PREFIX: + done = bgp_msg_orf_prefix(s, common, &entry->body.orf_prefix, + left) ; + break ; + default: + zabort("unknown ORF type") ; + break ; + } ; + } ; + } ; + + /* exit loop now if not enough room for current ORF entry */ + if (!done) + break ; + + /* Done ORF entry. Step to the next. NB: done == true */ + ++rr->next ; + first = 0 ; /* no longer first */ + } ; + + /* If not done, need to: + * + * a) force defer, + * b) undo ORF entries if none output of current type + * + */ + if (!done) + { + stream_putc_at(s, whenp , BGP_ORF_WTR_DEFER) ; + + if (first) + { + stream_set_endp(s, elenp - 1) ; + elenp = 0 ; /* no entries length to set */ + } ; + } ; + + /* fill in length of last ORF entries (if any) */ + length = bgp_msg_set_orf_length(s, elenp) ; + + /* Something has gone wrong if nothing has been output after the "when" + * byte. Two possibilities: + * + * a) have been called again after having reported "done" (so there are + * no more entries to deal with. + * + * b) have been asked to output an "unknown" ORF entry which is too long + * for a BGP message !! + */ + if (length == (whenp + 1)) + { + if (entry == NULL) + zabort("called bgp_msg_send_route_refresh() after said was done") ; + + if (entry->unknown) + zlog_err("%s sending REFRESH_REQ with impossible length (%d) ORF", + connection->host, entry->body.orf_unknown.length) ; + else + zabort("failed to put even one ORF entry") ; + + done = 1 ; /* force done */ + } ; + + return done ; +} ; + +/*------------------------------------------------------------------------------ + * Put given unknown ORF entry to stream -- verbatim -- if possible. + */ +static int +bgp_msg_orf_unknown(struct stream* s, bgp_orf_unknown_entry orf_unknown, + bgp_size_t left) +{ + if (left < orf_unknown->length) + return 0 ; + + stream_write(s, orf_unknown->data, orf_unknown->length) ; + return 1 ; +} ; + +/*------------------------------------------------------------------------------ + * Put remove all ORF entry to stream -- if possible. + */ +static int +bgp_msg_orf_remove_all(struct stream* s, bgp_size_t left) +{ + if (left == 1) /* only one byte required ! */ + return 0 ; + + stream_putc(s, BGP_ORF_EA_RM_ALL) ; + return 1 ; +} ; + +/*------------------------------------------------------------------------------ + * Put given Address-Prefix ORF entry to stream -- if possible. + */ +static int +bgp_msg_orf_prefix(struct stream* s, uint8_t common, + orf_prefix orfpe, bgp_size_t left) +{ + bgp_size_t blen = (orfpe->p.prefixlen + 7) / 8 ; + + if (left < (BGP_ORF_E_P_MIN_L + blen)) + return 0 ; + + stream_putc(s, common) ; + stream_putl(s, orfpe->seq) ; + stream_putc(s, orfpe->ge) ; /* aka min */ + stream_putc(s, orfpe->le) ; /* aka max */ + stream_putc(s, orfpe->p.prefixlen) ; + stream_write(s, &orfpe->p.u.prefix, blen) ; + + return 1 ; +} ; + +/*============================================================================== + * UPDATE -- send an UPDATE message + * + * PRO TEM -- this is passed a raw BGP message in a stream buffer + */ + +/*------------------------------------------------------------------------------ + * Make UPDATE message and dispatch. + * + * Returns: 1 => written to wbuff -- qpselect will write from there + * 0 => nothing written -- insufficient space in wbuff + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! + * + * NB: requires the session LOCKED -- connection-wise + */ +extern int +bgp_msg_send_update(bgp_connection connection, struct stream* s) +{ + if (bgp_connection_write_cannot_max(connection)) + return 0 ; + + ++connection->session->stats.update_out ; + + return bgp_connection_write(connection, s) ; +} ; + +/*============================================================================== + * End-of-RIB -- send an End-of-RIB BGP message (see Graceful Restart) + */ + +/*------------------------------------------------------------------------------ + * Make End-of-RIB message and dispatch. + * + * Returns: 1 => written to wbuff -- qpselect will write from there + * 0 => nothing written -- insufficient space in wbuff + * + * NB: actual I/O occurs in the qpselect action function -- so this cannot + * fail ! + */ +extern int +bgp_msg_send_end_of_rib(bgp_connection connection, iAFI_t afi, iSAFI_t safi) +{ + struct stream *s = connection->obuf ; + + if (bgp_connection_write_cannot_max(connection)) + return 0 ; + + /* Make UPDATE message header */ + bgp_packet_set_marker(s, BGP_MSG_UPDATE) ; + + /* Minimum size UPDATE */ + stream_putw(s, 0) ; /* no Withdrawn Routes */ + stream_putw(s, 0) ; /* no Attributes => no NLRI */ + + /* If not IPv4/Unicast, need empty MP Unreachable attribute */ + if ((afi != iAFI_IP) || (safi != iSAFI_Unicast)) + { + bgp_size_t attrp = stream_get_endp(s) ; + + stream_putc (s, BGP_ATTR_FLAG_OPTIONAL); + stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI); + stream_putc (s, 3); + stream_putw (s, afi); + stream_putc (s, safi); + + stream_putw_at(s, attrp-2, stream_get_endp(s) - attrp) ; + } + + bgp_packet_set_size(s); + + if (BGP_DEBUG (normal, NORMAL)) + zlog_debug ("send End-of-RIB for %s to %s", afi_safi_print (afi, safi), + connection->host) ; + + /* Finally -- write the buffer away */ + return bgp_connection_write(connection, s) ; +} ; + +/*============================================================================== + * Utilities for creating BGP messages + */ + /* 0 1 2 3 4 5 6 7 */ +static const char bgp_header[] = "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 8 */ + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" /* 16 */ + "\x00" ; +CONFIRM(sizeof(bgp_header) == (BGP_MARKER_SIZE + 2)) ; + +/*------------------------------------------------------------------------------ + * Insert BGP message standard header + * + * 16 bytes of 0xFF + * 2 bytes -- total length of message -- filled in later + * 1 byte -- the type of message as given + */ +extern int +bgp_packet_set_marker(struct stream *s, uint8_t type) +{ + /* Fill in marker & dummy total length (to be filled in later on) */ + stream_write(s, bgp_header, BGP_MARKER_SIZE + 2) ; + + /* BGP packet type. */ + stream_putc (s, type); + + /* Return current stream size. */ + return stream_get_endp (s); +} ; + +/*------------------------------------------------------------------------------ + * Set BGP packet header size entry and return same. + */ +extern int +bgp_packet_set_size (struct stream *s) +{ + int cp; + + /* Preserve current pointer. */ + cp = stream_get_endp (s); + stream_putw_at (s, BGP_MARKER_SIZE, cp); + + return cp; +} ; |