summaryrefslogtreecommitdiff
path: root/bgpd/bgp_attr.c
diff options
context:
space:
mode:
Diffstat (limited to 'bgpd/bgp_attr.c')
-rw-r--r--bgpd/bgp_attr.c1838
1 files changed, 1838 insertions, 0 deletions
diff --git a/bgpd/bgp_attr.c b/bgpd/bgp_attr.c
new file mode 100644
index 00000000..480bb912
--- /dev/null
+++ b/bgpd/bgp_attr.c
@@ -0,0 +1,1838 @@
+/* BGP attributes management routines.
+ Copyright (C) 1996, 97, 98, 1999 Kunihiro Ishiguro
+
+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 "linklist.h"
+#include "prefix.h"
+#include "memory.h"
+#include "vector.h"
+#include "vty.h"
+#include "stream.h"
+#include "log.h"
+#include "hash.h"
+
+#include "bgpd/bgpd.h"
+#include "bgpd/bgp_attr.h"
+#include "bgpd/bgp_route.h"
+#include "bgpd/bgp_aspath.h"
+#include "bgpd/bgp_community.h"
+#include "bgpd/bgp_debug.h"
+#include "bgpd/bgp_packet.h"
+#include "bgpd/bgp_ecommunity.h"
+
+/* Attribute strings for logging. */
+struct message attr_str [] =
+{
+ { BGP_ATTR_ORIGIN, "ORIGIN" },
+ { BGP_ATTR_AS_PATH, "AS_PATH" },
+ { BGP_ATTR_NEXT_HOP, "NEXT_HOP" },
+ { BGP_ATTR_MULTI_EXIT_DISC, "MULTI_EXIT_DISC" },
+ { BGP_ATTR_LOCAL_PREF, "LOCAL_PREF" },
+ { BGP_ATTR_ATOMIC_AGGREGATE, "ATOMIC_AGGREGATE" },
+ { BGP_ATTR_AGGREGATOR, "AGGREGATOR" },
+ { BGP_ATTR_COMMUNITIES, "COMMUNITY" },
+ { BGP_ATTR_ORIGINATOR_ID, "ORIGINATOR_ID" },
+ { BGP_ATTR_CLUSTER_LIST, "CLUSTERLIST" },
+ { BGP_ATTR_DPA, "DPA" },
+ { BGP_ATTR_ADVERTISER, "ADVERTISER"} ,
+ { BGP_ATTR_RCID_PATH, "RCID_PATH" },
+ { BGP_ATTR_MP_REACH_NLRI, "MP_REACH_NLRI" },
+ { BGP_ATTR_MP_UNREACH_NLRI, "MP_UNREACH_NLRI" },
+ { 0, NULL }
+};
+
+struct hash *cluster_hash;
+
+void *
+cluster_hash_alloc (struct cluster_list *val)
+{
+ struct cluster_list *cluster;
+
+ cluster = XMALLOC (MTYPE_CLUSTER, sizeof (struct cluster_list));
+ cluster->length = val->length;
+
+ if (cluster->length)
+ {
+ cluster->list = XMALLOC (MTYPE_CLUSTER_VAL, val->length);
+ memcpy (cluster->list, val->list, val->length);
+ }
+ else
+ cluster->list = NULL;
+
+ cluster->refcnt = 0;
+
+ return cluster;
+}
+
+/* Cluster list related functions. */
+struct cluster_list *
+cluster_parse (caddr_t pnt, int length)
+{
+ struct cluster_list tmp;
+ struct cluster_list *cluster;
+
+ tmp.length = length;
+ tmp.list = (struct in_addr *) pnt;
+
+ cluster = hash_get (cluster_hash, &tmp, cluster_hash_alloc);
+ cluster->refcnt++;
+ return cluster;
+}
+
+int
+cluster_loop_check (struct cluster_list *cluster, struct in_addr originator)
+{
+ int i;
+
+ for (i = 0; i < cluster->length / 4; i++)
+ if (cluster->list[i].s_addr == originator.s_addr)
+ return 1;
+ return 0;
+}
+
+unsigned int
+cluster_hash_key_make (struct cluster_list *cluster)
+{
+ unsigned int key = 0;
+ int length;
+ caddr_t pnt;
+
+ length = cluster->length;
+ pnt = (caddr_t) cluster->list;
+
+ while (length)
+ key += pnt[--length];
+
+ return key;
+}
+
+int
+cluster_hash_cmp (struct cluster_list *cluster1, struct cluster_list *cluster2)
+{
+ if (cluster1->length == cluster2->length &&
+ memcmp (cluster1->list, cluster2->list, cluster1->length) == 0)
+ return 1;
+ return 0;
+}
+
+void
+cluster_free (struct cluster_list *cluster)
+{
+ if (cluster->list)
+ XFREE (MTYPE_CLUSTER_VAL, cluster->list);
+ XFREE (MTYPE_CLUSTER, cluster);
+}
+
+struct cluster_list *
+cluster_dup (struct cluster_list *cluster)
+{
+ struct cluster_list *new;
+
+ new = XMALLOC (MTYPE_CLUSTER, sizeof (struct cluster_list));
+ memset (new, 0, sizeof (struct cluster_list));
+ new->length = cluster->length;
+
+ if (cluster->length)
+ {
+ new->list = XMALLOC (MTYPE_CLUSTER_VAL, cluster->length);
+ memcpy (new->list, cluster->list, cluster->length);
+ }
+ else
+ new->list = NULL;
+
+ return new;
+}
+
+struct cluster_list *
+cluster_intern (struct cluster_list *cluster)
+{
+ struct cluster_list *find;
+
+ find = hash_get (cluster_hash, cluster, cluster_hash_alloc);
+ find->refcnt++;
+
+ return find;
+}
+
+void
+cluster_unintern (struct cluster_list *cluster)
+{
+ struct cluster_list *ret;
+
+ if (cluster->refcnt)
+ cluster->refcnt--;
+
+ if (cluster->refcnt == 0)
+ {
+ ret = hash_release (cluster_hash, cluster);
+ cluster_free (cluster);
+ }
+}
+
+void
+cluster_init ()
+{
+ cluster_hash = hash_create (cluster_hash_key_make, cluster_hash_cmp);
+}
+
+/* Unknown transit attribute. */
+struct hash *transit_hash;
+
+void
+transit_free (struct transit *transit)
+{
+ if (transit->val)
+ XFREE (MTYPE_TRANSIT_VAL, transit->val);
+ XFREE (MTYPE_TRANSIT, transit);
+}
+
+void *
+transit_hash_alloc (struct transit *transit)
+{
+ /* Transit structure is already allocated. */
+ return transit;
+}
+
+struct transit *
+transit_intern (struct transit *transit)
+{
+ struct transit *find;
+
+ find = hash_get (transit_hash, transit, transit_hash_alloc);
+ if (find != transit)
+ transit_free (transit);
+ find->refcnt++;
+
+ return find;
+}
+
+void
+transit_unintern (struct transit *transit)
+{
+ struct transit *ret;
+
+ if (transit->refcnt)
+ transit->refcnt--;
+
+ if (transit->refcnt == 0)
+ {
+ ret = hash_release (transit_hash, transit);
+ transit_free (transit);
+ }
+}
+
+unsigned int
+transit_hash_key_make (struct transit *transit)
+{
+ unsigned int key = 0;
+ int length;
+ caddr_t pnt;
+
+ length = transit->length;
+ pnt = (caddr_t) transit->val;
+
+ while (length)
+ key += pnt[--length];
+
+ return key;
+}
+
+int
+transit_hash_cmp (struct transit *transit1, struct transit *transit2)
+{
+ if (transit1->length == transit2->length &&
+ memcmp (transit1->val, transit2->val, transit1->length) == 0)
+ return 1;
+ return 0;
+}
+
+void
+transit_init ()
+{
+ transit_hash = hash_create (transit_hash_key_make, transit_hash_cmp);
+}
+
+/* Attribute hash routines. */
+
+struct hash *attrhash;
+
+unsigned int
+attrhash_key_make (struct attr *attr)
+{
+ unsigned int key = 0;
+
+ key += attr->origin;
+ key += attr->nexthop.s_addr;
+ key += attr->med;
+ key += attr->local_pref;
+ key += attr->aggregator_as;
+ key += attr->aggregator_addr.s_addr;
+ key += attr->weight;
+
+ key += attr->mp_nexthop_global_in.s_addr;
+ if (attr->aspath)
+ key += aspath_key_make (attr->aspath);
+ if (attr->community)
+ key += community_hash_make (attr->community);
+ if (attr->ecommunity)
+ key += ecommunity_hash_make (attr->ecommunity);
+ if (attr->cluster)
+ key += cluster_hash_key_make (attr->cluster);
+ if (attr->transit)
+ key += transit_hash_key_make (attr->transit);
+
+#ifdef HAVE_IPV6
+ {
+ int i;
+
+ key += attr->mp_nexthop_len;
+ for (i = 0; i < 16; i++)
+ key += attr->mp_nexthop_global.s6_addr[i];
+ for (i = 0; i < 16; i++)
+ key += attr->mp_nexthop_local.s6_addr[i];
+ }
+#endif /* HAVE_IPV6 */
+
+ return key;
+}
+
+int
+attrhash_cmp (struct attr *attr1, struct attr *attr2)
+{
+ if (attr1->flag == attr2->flag
+ && attr1->origin == attr2->origin
+ && attr1->nexthop.s_addr == attr2->nexthop.s_addr
+ && attr1->med == attr2->med
+ && attr1->local_pref == attr2->local_pref
+ && attr1->aggregator_as == attr2->aggregator_as
+ && attr1->aggregator_addr.s_addr == attr2->aggregator_addr.s_addr
+ && attr1->weight == attr2->weight
+#ifdef HAVE_IPV6
+ && attr1->mp_nexthop_len == attr2->mp_nexthop_len
+ && IPV6_ADDR_SAME (&attr1->mp_nexthop_global, &attr2->mp_nexthop_global)
+ && IPV6_ADDR_SAME (&attr1->mp_nexthop_local, &attr2->mp_nexthop_local)
+#endif /* HAVE_IPV6 */
+ && IPV4_ADDR_SAME (&attr1->mp_nexthop_global_in, &attr2->mp_nexthop_global_in)
+ && attr1->aspath == attr2->aspath
+ && attr1->community == attr2->community
+ && attr1->ecommunity == attr2->ecommunity
+ && attr1->cluster == attr2->cluster
+ && attr1->transit == attr2->transit)
+ return 1;
+ else
+ return 0;
+}
+
+void
+attrhash_init ()
+{
+ attrhash = hash_create (attrhash_key_make, attrhash_cmp);
+}
+
+void
+attr_show_all_iterator (struct hash_backet *backet, struct vty *vty)
+{
+ struct attr *attr = backet->data;
+
+ vty_out (vty, "attr[%ld] nexthop %s%s", attr->refcnt,
+ inet_ntoa (attr->nexthop), VTY_NEWLINE);
+}
+
+void
+attr_show_all (struct vty *vty)
+{
+ hash_iterate (attrhash,
+ (void (*)(struct hash_backet *, void *))
+ attr_show_all_iterator,
+ vty);
+}
+
+void *
+bgp_attr_hash_alloc (struct attr *val)
+{
+ struct attr *attr;
+
+ attr = XMALLOC (MTYPE_ATTR, sizeof (struct attr));
+ *attr = *val;
+ attr->refcnt = 0;
+ return attr;
+}
+
+/* Internet argument attribute. */
+struct attr *
+bgp_attr_intern (struct attr *attr)
+{
+ struct attr *find;
+
+ /* Intern referenced strucutre. */
+ if (attr->aspath)
+ {
+ if (! attr->aspath->refcnt)
+ attr->aspath = aspath_intern (attr->aspath);
+ else
+ attr->aspath->refcnt++;
+ }
+ if (attr->community)
+ {
+ if (! attr->community->refcnt)
+ attr->community = community_intern (attr->community);
+ else
+ attr->community->refcnt++;
+ }
+ if (attr->ecommunity)
+ {
+ if (! attr->ecommunity->refcnt)
+ attr->ecommunity = ecommunity_intern (attr->ecommunity);
+ else
+ attr->ecommunity->refcnt++;
+ }
+ if (attr->cluster)
+ {
+ if (! attr->cluster->refcnt)
+ attr->cluster = cluster_intern (attr->cluster);
+ else
+ attr->cluster->refcnt++;
+ }
+ if (attr->transit)
+ {
+ if (! attr->transit->refcnt)
+ attr->transit = transit_intern (attr->transit);
+ else
+ attr->transit->refcnt++;
+ }
+
+ find = (struct attr *) hash_get (attrhash, attr, bgp_attr_hash_alloc);
+ find->refcnt++;
+
+ return find;
+}
+
+/* Make network statement's attribute. */
+struct attr *
+bgp_attr_default_set (struct attr *attr, u_char origin)
+{
+ memset (attr, 0, sizeof (struct attr));
+
+ attr->origin = origin;
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
+ attr->aspath = aspath_empty ();
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+ attr->weight = 32768;
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+#ifdef HAVE_IPV6
+ attr->mp_nexthop_len = 16;
+#endif
+ return attr;
+}
+
+/* Make network statement's attribute. */
+struct attr *
+bgp_attr_default_intern (u_char origin)
+{
+ struct attr attr;
+ struct attr *new;
+
+ memset (&attr, 0, sizeof (struct attr));
+
+ attr.origin = origin;
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
+ attr.aspath = aspath_empty ();
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+ attr.weight = 32768;
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+#ifdef HAVE_IPV6
+ attr.mp_nexthop_len = 16;
+#endif
+
+ new = bgp_attr_intern (&attr);
+ aspath_unintern (new->aspath);
+ return new;
+}
+
+struct attr *
+bgp_attr_aggregate_intern (struct bgp *bgp, u_char origin,
+ struct aspath *aspath,
+ struct community *community, int as_set)
+{
+ struct attr attr;
+ struct attr *new;
+
+ memset (&attr, 0, sizeof (struct attr));
+
+ /* Origin attribute. */
+ attr.origin = origin;
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
+
+ /* AS path attribute. */
+ if (aspath)
+ attr.aspath = aspath_intern (aspath);
+ else
+ attr.aspath = aspath_empty ();
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+
+ /* Next hop attribute. */
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+
+ if (community)
+ {
+ attr.community = community;
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
+ }
+
+ attr.weight = 32768;
+#ifdef HAVE_IPV6
+ attr.mp_nexthop_len = 16;
+#endif
+ if (! as_set)
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
+ attr.flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
+ if (CHECK_FLAG (bgp->config, BGP_CONFIG_CONFEDERATION))
+ attr.aggregator_as = bgp->confed_id;
+ else
+ attr.aggregator_as = bgp->as;
+ attr.aggregator_addr = bgp->router_id;
+
+ new = bgp_attr_intern (&attr);
+ aspath_unintern (new->aspath);
+ return new;
+}
+
+/* Free bgp attribute and aspath. */
+void
+bgp_attr_unintern (struct attr *attr)
+{
+ struct attr *ret;
+ struct aspath *aspath;
+ struct community *community;
+ struct ecommunity *ecommunity;
+ struct cluster_list *cluster;
+ struct transit *transit;
+
+ /* Decrement attribute reference. */
+ attr->refcnt--;
+ aspath = attr->aspath;
+ community = attr->community;
+ ecommunity = attr->ecommunity;
+ cluster = attr->cluster;
+ transit = attr->transit;
+
+ /* If reference becomes zero then free attribute object. */
+ if (attr->refcnt == 0)
+ {
+ ret = hash_release (attrhash, attr);
+ assert (ret != NULL);
+ XFREE (MTYPE_ATTR, attr);
+ }
+
+ /* aspath refcount shoud be decrement. */
+ if (aspath)
+ aspath_unintern (aspath);
+ if (community)
+ community_unintern (community);
+ if (ecommunity)
+ ecommunity_unintern (ecommunity);
+ if (cluster)
+ cluster_unintern (cluster);
+ if (transit)
+ transit_unintern (transit);
+}
+
+void
+bgp_attr_flush (struct attr *attr)
+{
+ if (attr->aspath && ! attr->aspath->refcnt)
+ aspath_free (attr->aspath);
+ if (attr->community && ! attr->community->refcnt)
+ community_free (attr->community);
+ if (attr->ecommunity && ! attr->ecommunity->refcnt)
+ ecommunity_free (attr->ecommunity);
+ if (attr->cluster && ! attr->cluster->refcnt)
+ cluster_free (attr->cluster);
+ if (attr->transit && ! attr->transit->refcnt)
+ transit_free (attr->transit);
+}
+
+/* Get origin attribute of the update message. */
+int
+bgp_attr_origin (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag, u_char *startp)
+{
+ bgp_size_t total;
+
+ /* total is entire attribute length include Attribute Flags (1),
+ Attribute Type code (1) and Attribute length (1 or 2). */
+ total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+ /* If any recognized attribute has Attribute Flags that conflict
+ with the Attribute Type Code, then the Error Subcode is set to
+ Attribute Flags Error. The Data field contains the erroneous
+ attribute (type, length and value). */
+ if (flag != BGP_ATTR_FLAG_TRANS)
+ {
+ zlog (peer->log, LOG_ERR,
+ "Origin attribute flag isn't transitive %d", flag);
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ startp, total);
+ return -1;
+ }
+
+ /* If any recognized attribute has Attribute Length that conflicts
+ with the expected length (based on the attribute type code), then
+ the Error Subcode is set to Attribute Length Error. The Data
+ field contains the erroneous attribute (type, length and
+ value). */
+ if (length != 1)
+ {
+ zlog (peer->log, LOG_ERR, "Origin attribute length is not one %d",
+ length);
+ bgp_notify_send_with_data (peer, BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ startp, total);
+ return -1;
+ }
+
+ /* Fetch origin attribute. */
+ attr->origin = stream_getc (BGP_INPUT (peer));
+
+ /* If the ORIGIN attribute has an undefined value, then the Error
+ Subcode is set to Invalid Origin Attribute. The Data field
+ contains the unrecognized attribute (type, length and value). */
+ if ((attr->origin != BGP_ORIGIN_IGP)
+ && (attr->origin != BGP_ORIGIN_EGP)
+ && (attr->origin != BGP_ORIGIN_INCOMPLETE))
+ {
+ zlog (peer->log, LOG_ERR, "Origin attribute value is invalid %d",
+ attr->origin);
+
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_INVAL_ORIGIN,
+ startp, total);
+ return -1;
+ }
+
+ /* Set oring attribute flag. */
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGIN);
+
+ return 0;
+}
+
+/* Parse AS path information. This function is wrapper of
+ aspath_parse. */
+int
+bgp_attr_aspath (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag, u_char *startp)
+{
+ struct bgp *bgp;
+ struct aspath *aspath;
+ bgp_size_t total;
+
+ total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+ /* Flag check. */
+ if (CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
+ || ! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
+ {
+ zlog (peer->log, LOG_ERR,
+ "Origin attribute flag isn't transitive %d", flag);
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ startp, total);
+ return -1;
+ }
+
+ /* In case of IBGP, length will be zero. */
+ attr->aspath = aspath_parse (stream_pnt (peer->ibuf), length);
+ if (! attr->aspath)
+ {
+ zlog (peer->log, LOG_ERR, "Malformed AS path length is %d", length);
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_AS_PATH);
+ return -1;
+ }
+
+ bgp = peer->bgp;
+
+ /* First AS check for EBGP. */
+ if (bgp != NULL && bgp_flag_check (bgp, BGP_FLAG_ENFORCE_FIRST_AS))
+ {
+ if (peer_sort (peer) == BGP_PEER_EBGP
+ && ! aspath_firstas_check (attr->aspath, peer->as))
+ {
+ zlog (peer->log, LOG_ERR,
+ "%s incorrect first AS (must be %d)", peer->host, peer->as);
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_AS_PATH);
+ return -1;
+ }
+ }
+
+ /* local-as prepend */
+ if (peer->change_local_as &&
+ ! CHECK_FLAG (peer->flags, PEER_FLAG_LOCAL_AS_NO_PREPEND))
+ {
+ aspath = aspath_dup (attr->aspath);
+ aspath = aspath_add_seq (aspath, peer->change_local_as);
+ aspath_unintern (attr->aspath);
+ attr->aspath = aspath_intern (aspath);
+ }
+
+ /* Forward pointer. */
+ stream_forward (peer->ibuf, length);
+
+ /* Set aspath attribute flag. */
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AS_PATH);
+
+ return 0;
+}
+
+/* Nexthop attribute. */
+int
+bgp_attr_nexthop (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag, u_char *startp)
+{
+ bgp_size_t total;
+
+ total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+ /* Flag check. */
+ if (CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL)
+ || ! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
+ {
+ zlog (peer->log, LOG_ERR,
+ "Origin attribute flag isn't transitive %d", flag);
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_FLAG_ERR,
+ startp, total);
+ return -1;
+ }
+
+ /* Check nexthop attribute length. */
+ if (length != 4)
+ {
+ zlog (peer->log, LOG_ERR, "Nexthop attribute length isn't four [%d]",
+ length);
+
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ startp, total);
+ return -1;
+ }
+
+ attr->nexthop.s_addr = stream_get_ipv4 (peer->ibuf);
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP);
+
+ return 0;
+}
+
+/* MED atrribute. */
+int
+bgp_attr_med (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag, u_char *startp)
+{
+ bgp_size_t total;
+
+ total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+ /* Length check. */
+ if (length != 4)
+ {
+ zlog (peer->log, LOG_ERR,
+ "MED attribute length isn't four [%d]", length);
+
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR,
+ startp, total);
+ return -1;
+ }
+
+ attr->med = stream_getl (peer->ibuf);
+
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC);
+
+ return 0;
+}
+
+/* Local preference attribute. */
+int
+bgp_attr_local_pref (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag)
+{
+ /* If it is contained in an UPDATE message that is received from an
+ external peer, then this attribute MUST be ignored by the
+ receiving speaker. */
+ if (peer_sort (peer) == BGP_PEER_EBGP)
+ {
+ stream_forward (peer->ibuf, length);
+ return 0;
+ }
+
+ if (length == 4)
+ attr->local_pref = stream_getl (peer->ibuf);
+ else
+ attr->local_pref = 0;
+
+ /* Set atomic aggregate flag. */
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF);
+
+ return 0;
+}
+
+/* Atomic aggregate. */
+int
+bgp_attr_atomic (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag)
+{
+ if (length != 0)
+ {
+ zlog (peer->log, LOG_ERR, "Bad atomic aggregate length %d", length);
+
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ return -1;
+ }
+
+ /* Set atomic aggregate flag. */
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE);
+
+ return 0;
+}
+
+/* Aggregator attribute */
+int
+bgp_attr_aggregator (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag)
+{
+ if (length != 6)
+ {
+ zlog (peer->log, LOG_ERR, "Aggregator length is not 6 [%d]", length);
+
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ return -1;
+ }
+ attr->aggregator_as = stream_getw (peer->ibuf);
+ attr->aggregator_addr.s_addr = stream_get_ipv4 (peer->ibuf);
+
+ /* Set atomic aggregate flag. */
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR);
+
+ return 0;
+}
+
+/* Community attribute. */
+int
+bgp_attr_community (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag)
+{
+ if (length == 0)
+ attr->community = NULL;
+ else
+ {
+ attr->community = community_parse (stream_pnt (peer->ibuf), length);
+ stream_forward (peer->ibuf, length);
+ }
+
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES);
+
+ return 0;
+}
+
+/* Originator ID attribute. */
+int
+bgp_attr_originator_id (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag)
+{
+ if (length != 4)
+ {
+ zlog (peer->log, LOG_ERR, "Bad originator ID length %d", length);
+
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ return -1;
+ }
+
+ attr->originator_id.s_addr = stream_get_ipv4 (peer->ibuf);
+
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_ORIGINATOR_ID);
+
+ return 0;
+}
+
+/* Cluster list attribute. */
+int
+bgp_attr_cluster_list (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag)
+{
+ /* Check length. */
+ if (length % 4)
+ {
+ zlog (peer->log, LOG_ERR, "Bad cluster list length %d", length);
+
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ return -1;
+ }
+
+ attr->cluster = cluster_parse (stream_pnt (peer->ibuf), length);
+
+ stream_forward (peer->ibuf, length);;
+
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_CLUSTER_LIST);
+
+ return 0;
+}
+
+/* Multiprotocol reachability information parse. */
+int
+bgp_mp_reach_parse (struct peer *peer, bgp_size_t length, struct attr *attr,
+ struct bgp_nlri *mp_update)
+{
+ u_int16_t afi;
+ u_char safi;
+ u_char snpa_num;
+ u_char snpa_len;
+ u_char *lim;
+ bgp_size_t nlri_len;
+ int ret;
+ struct stream *s;
+
+ /* Set end of packet. */
+ s = peer->ibuf;
+ lim = stream_pnt (s) + length;
+
+ /* Load AFI, SAFI. */
+ afi = stream_getw (s);
+ safi = stream_getc (s);
+
+ /* Get nexthop length. */
+ attr->mp_nexthop_len = stream_getc (s);
+
+ /* Nexthop length check. */
+ switch (attr->mp_nexthop_len)
+ {
+ case 4:
+ stream_get (&attr->mp_nexthop_global_in, s, 4);
+ break;
+ case 12:
+ {
+ u_int32_t rd_high;
+ u_int32_t rd_low;
+
+ rd_high = stream_getl (s);
+ rd_low = stream_getl (s);
+ stream_get (&attr->mp_nexthop_global_in, s, 4);
+ }
+ break;
+#ifdef HAVE_IPV6
+ case 16:
+ stream_get (&attr->mp_nexthop_global, s, 16);
+ break;
+ case 32:
+ stream_get (&attr->mp_nexthop_global, s, 16);
+ stream_get (&attr->mp_nexthop_local, s, 16);
+ if (! IN6_IS_ADDR_LINKLOCAL (&attr->mp_nexthop_local))
+ {
+ char buf1[INET6_ADDRSTRLEN];
+ char buf2[INET6_ADDRSTRLEN];
+
+ if (BGP_DEBUG (update, UPDATE_IN))
+ zlog_warn ("%s got two nexthop %s %s but second one is not a link-local nexthop", peer->host,
+ inet_ntop (AF_INET6, &attr->mp_nexthop_global,
+ buf1, INET6_ADDRSTRLEN),
+ inet_ntop (AF_INET6, &attr->mp_nexthop_local,
+ buf2, INET6_ADDRSTRLEN));
+
+ attr->mp_nexthop_len = 16;
+ }
+ break;
+#endif /* HAVE_IPV6 */
+ default:
+ zlog_info ("Wrong multiprotocol next hop length: %d",
+ attr->mp_nexthop_len);
+ return -1;
+ break;
+ }
+
+ snpa_num = stream_getc (s);
+
+ while (snpa_num--)
+ {
+ snpa_len = stream_getc (s);
+ stream_forward (s, (snpa_len + 1) >> 1);
+ }
+
+ /* If peer is based on old draft-00. I read NLRI length from the
+ packet. */
+ if (peer->version == BGP_VERSION_MP_4_DRAFT_00)
+ {
+ bgp_size_t nlri_total_len;
+ nlri_total_len = stream_getw (s);
+ }
+
+ nlri_len = lim - stream_pnt (s);
+
+ if (safi != BGP_SAFI_VPNV4)
+ {
+ ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), nlri_len);
+ if (ret < 0)
+ return -1;
+ }
+
+ mp_update->afi = afi;
+ mp_update->safi = safi;
+ mp_update->nlri = stream_pnt (s);
+ mp_update->length = nlri_len;
+
+ stream_forward (s, nlri_len);
+
+ return 0;
+}
+
+/* Multiprotocol unreachable parse */
+int
+bgp_mp_unreach_parse (struct peer *peer, int length,
+ struct bgp_nlri *mp_withdraw)
+{
+ struct stream *s;
+ u_int16_t afi;
+ u_char safi;
+ u_char *lim;
+ u_int16_t withdraw_len;
+ int ret;
+
+ s = peer->ibuf;
+ lim = stream_pnt (s) + length;
+
+ afi = stream_getw (s);
+ safi = stream_getc (s);
+
+ withdraw_len = lim - stream_pnt (s);
+
+ if (safi != BGP_SAFI_VPNV4)
+ {
+ ret = bgp_nlri_sanity_check (peer, afi, stream_pnt (s), withdraw_len);
+ if (ret < 0)
+ return -1;
+ }
+
+ mp_withdraw->afi = afi;
+ mp_withdraw->safi = safi;
+ mp_withdraw->nlri = stream_pnt (s);
+ mp_withdraw->length = withdraw_len;
+
+ stream_forward (s, withdraw_len);
+
+ return 0;
+}
+
+/* Extended Community attribute. */
+int
+bgp_attr_ext_communities (struct peer *peer, bgp_size_t length,
+ struct attr *attr, u_char flag)
+{
+ if (length == 0)
+ attr->ecommunity = NULL;
+ else
+ {
+ attr->ecommunity = ecommunity_parse (stream_pnt (peer->ibuf), length);
+ stream_forward (peer->ibuf, length);
+ }
+ attr->flag |= ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES);
+
+ return 0;
+}
+
+/* BGP unknown attribute treatment. */
+int
+bgp_attr_unknown (struct peer *peer, struct attr *attr, u_char flag,
+ u_char type, bgp_size_t length, u_char *startp)
+{
+ bgp_size_t total;
+ struct transit *transit;
+
+ if (BGP_DEBUG (events, EVENTS))
+ zlog (peer->log, LOG_INFO,
+ "Unknown attribute type %d length %d is received", type, length);
+
+ /* Forward read pointer of input stream. */
+ stream_forward (peer->ibuf, length);
+
+ /* Adjest total length to include type and length. */
+ total = length + (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN) ? 4 : 3);
+
+ /* If any of the mandatory well-known attributes are not recognized,
+ then the Error Subcode is set to Unrecognized Well-known
+ Attribute. The Data field contains the unrecognized attribute
+ (type, length and value). */
+ if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_OPTIONAL))
+ {
+ /* Adjust startp to do not include flag value. */
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_UNREC_ATTR,
+ startp, total);
+ return -1;
+ }
+
+ /* Unrecognized non-transitive optional attributes must be quietly
+ ignored and not passed along to other BGP peers. */
+ if (! CHECK_FLAG (flag, BGP_ATTR_FLAG_TRANS))
+ return 0;
+
+ /* If a path with recognized transitive optional attribute is
+ accepted and passed along to other BGP peers and the Partial bit
+ in the Attribute Flags octet is set to 1 by some previous AS, it
+ is not set back to 0 by the current AS. */
+ SET_FLAG (*startp, BGP_ATTR_FLAG_PARTIAL);
+
+ /* Store transitive attribute to the end of attr->transit. */
+ if (! attr->transit)
+ {
+ attr->transit = XMALLOC (MTYPE_TRANSIT, sizeof (struct transit));
+ memset (attr->transit, 0, sizeof (struct transit));
+ }
+
+ transit = attr->transit;
+
+ if (transit->val)
+ transit->val = XREALLOC (MTYPE_TRANSIT_VAL, transit->val,
+ transit->length + total);
+ else
+ transit->val = XMALLOC (MTYPE_TRANSIT_VAL, total);
+
+ memcpy (transit->val + transit->length, startp, total);
+ transit->length += total;
+
+ return 0;
+}
+
+/* Read attribute of update packet. This function is called from
+ bgp_update() in bgpd.c. */
+int
+bgp_attr_parse (struct peer *peer, struct attr *attr, bgp_size_t size,
+ struct bgp_nlri *mp_update, struct bgp_nlri *mp_withdraw)
+{
+ int ret;
+ u_char flag;
+ u_char type;
+ bgp_size_t length;
+ u_char *startp, *endp;
+ u_char *attr_endp;
+ u_char seen[BGP_ATTR_BITMAP_SIZE];
+
+ /* Initialize bitmap. */
+ memset (seen, 0, BGP_ATTR_BITMAP_SIZE);
+
+ /* End pointer of BGP attribute. */
+ endp = BGP_INPUT_PNT (peer) + size;
+
+ /* Get attributes to the end of attribute length. */
+ while (BGP_INPUT_PNT (peer) < endp)
+ {
+ /* Check remaining length check.*/
+ if (endp - BGP_INPUT_PNT (peer) < BGP_ATTR_MIN_LEN)
+ {
+ zlog (peer->log, LOG_WARNING,
+ "%s error BGP attribute length %d is smaller than min len",
+ peer->host, endp - STREAM_PNT (BGP_INPUT (peer)));
+
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ return -1;
+ }
+
+ /* Fetch attribute flag and type. */
+ startp = BGP_INPUT_PNT (peer);
+ flag = stream_getc (BGP_INPUT (peer));
+ type = stream_getc (BGP_INPUT (peer));
+
+ /* Check extended attribue length bit. */
+ if (CHECK_FLAG (flag, BGP_ATTR_FLAG_EXTLEN))
+ length = stream_getw (BGP_INPUT (peer));
+ else
+ length = stream_getc (BGP_INPUT (peer));
+
+ /* If any attribute appears more than once in the UPDATE
+ message, then the Error Subcode is set to Malformed Attribute
+ List. */
+
+ if (CHECK_BITMAP (seen, type))
+ {
+ zlog (peer->log, LOG_WARNING,
+ "%s error BGP attribute type %d appears twice in a message",
+ peer->host, type);
+
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MAL_ATTR);
+ return -1;
+ }
+
+ /* Set type to bitmap to check duplicate attribute. `type' is
+ unsigned char so it never overflow bitmap range. */
+
+ SET_BITMAP (seen, type);
+
+ /* Overflow check. */
+ attr_endp = BGP_INPUT_PNT (peer) + length;
+
+ if (attr_endp > endp)
+ {
+ zlog (peer->log, LOG_WARNING,
+ "%s BGP type %d length %d is too large, attribute total length is %d. attr_endp is %p. endp is %p", peer->host, type, length, size, attr_endp, endp);
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ return -1;
+ }
+
+ /* OK check attribute and store it's value. */
+ switch (type)
+ {
+ case BGP_ATTR_ORIGIN:
+ ret = bgp_attr_origin (peer, length, attr, flag, startp);
+ break;
+ case BGP_ATTR_AS_PATH:
+ ret = bgp_attr_aspath (peer, length, attr, flag, startp);
+ break;
+ case BGP_ATTR_NEXT_HOP:
+ ret = bgp_attr_nexthop (peer, length, attr, flag, startp);
+ break;
+ case BGP_ATTR_MULTI_EXIT_DISC:
+ ret = bgp_attr_med (peer, length, attr, flag, startp);
+ break;
+ case BGP_ATTR_LOCAL_PREF:
+ ret = bgp_attr_local_pref (peer, length, attr, flag);
+ break;
+ case BGP_ATTR_ATOMIC_AGGREGATE:
+ ret = bgp_attr_atomic (peer, length, attr, flag);
+ break;
+ case BGP_ATTR_AGGREGATOR:
+ ret = bgp_attr_aggregator (peer, length, attr, flag);
+ break;
+ case BGP_ATTR_COMMUNITIES:
+ ret = bgp_attr_community (peer, length, attr, flag);
+ break;
+ case BGP_ATTR_ORIGINATOR_ID:
+ ret = bgp_attr_originator_id (peer, length, attr, flag);
+ break;
+ case BGP_ATTR_CLUSTER_LIST:
+ ret = bgp_attr_cluster_list (peer, length, attr, flag);
+ break;
+ case BGP_ATTR_MP_REACH_NLRI:
+ ret = bgp_mp_reach_parse (peer, length, attr, mp_update);
+ break;
+ case BGP_ATTR_MP_UNREACH_NLRI:
+ ret = bgp_mp_unreach_parse (peer, length, mp_withdraw);
+ break;
+ case BGP_ATTR_EXT_COMMUNITIES:
+ ret = bgp_attr_ext_communities (peer, length, attr, flag);
+ break;
+ default:
+ ret = bgp_attr_unknown (peer, attr, flag, type, length, startp);
+ break;
+ }
+
+ /* If error occured immediately return to the caller. */
+ if (ret < 0)
+ return ret;
+
+ /* Check the fetched length. */
+ if (BGP_INPUT_PNT (peer) != attr_endp)
+ {
+ zlog (peer->log, LOG_WARNING,
+ "%s BGP attribute fetch error", peer->host);
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ return -1;
+ }
+ }
+
+ /* Check final read pointer is same as end pointer. */
+ if (BGP_INPUT_PNT (peer) != endp)
+ {
+ zlog (peer->log, LOG_WARNING,
+ "%s BGP attribute length mismatch", peer->host);
+ bgp_notify_send (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_ATTR_LENG_ERR);
+ return -1;
+ }
+
+ /* Finally intern unknown attribute. */
+ if (attr->transit)
+ attr->transit = transit_intern (attr->transit);
+
+ return 0;
+}
+
+/* Well-known attribute check. */
+int
+bgp_attr_check (struct peer *peer, struct attr *attr)
+{
+ u_char type = 0;
+
+ if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_ORIGIN)))
+ type = BGP_ATTR_ORIGIN;
+
+ if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_AS_PATH)))
+ type = BGP_ATTR_AS_PATH;
+
+ if (! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP)))
+ type = BGP_ATTR_NEXT_HOP;
+
+ if (peer_sort (peer) == BGP_PEER_IBGP
+ && ! CHECK_FLAG (attr->flag, ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF)))
+ type = BGP_ATTR_LOCAL_PREF;
+
+ if (type)
+ {
+ zlog (peer->log, LOG_WARNING,
+ "%s Missing well-known attribute %d.",
+ peer->host, type);
+ bgp_notify_send_with_data (peer,
+ BGP_NOTIFY_UPDATE_ERR,
+ BGP_NOTIFY_UPDATE_MISS_ATTR,
+ &type, 1);
+ return -1;
+ }
+ return 0;
+}
+
+int stream_put_prefix (struct stream *, struct prefix *);
+
+/* Make attribute packet. */
+bgp_size_t
+bgp_packet_attribute (struct bgp *bgp, struct peer *peer,
+ struct stream *s, struct attr *attr, struct prefix *p,
+ afi_t afi, safi_t safi, struct peer *from,
+ struct prefix_rd *prd, u_char *tag)
+{
+ unsigned long cp;
+ struct aspath *aspath;
+
+ if (! bgp)
+ bgp = bgp_get_default ();
+
+ /* Remember current pointer. */
+ cp = stream_get_putp (s);
+
+ /* Origin attribute. */
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_ORIGIN);
+ stream_putc (s, 1);
+ stream_putc (s, attr->origin);
+
+ /* AS path attribute. */
+
+ /* If remote-peer is EBGP */
+ if (peer_sort (peer) == BGP_PEER_EBGP
+ && (! CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_AS_PATH_UNCHANGED)
+ || attr->aspath->length == 0)
+ && ! (CHECK_FLAG (from->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)
+ && CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_RSERVER_CLIENT)))
+ {
+ aspath = aspath_dup (attr->aspath);
+
+ if (CHECK_FLAG(bgp->config, BGP_CONFIG_CONFEDERATION))
+ {
+ /* Strip the confed info, and then stuff our path CONFED_ID
+ on the front */
+ aspath = aspath_delete_confed_seq (aspath);
+ aspath = aspath_add_seq (aspath, bgp->confed_id);
+ }
+ else
+ {
+ aspath = aspath_add_seq (aspath, peer->local_as);
+ if (peer->change_local_as)
+ aspath = aspath_add_seq (aspath, peer->change_local_as);
+ }
+ }
+ else if (peer_sort (peer) == BGP_PEER_CONFED)
+ {
+ /* A confed member, so we need to do the AS_CONFED_SEQUENCE thing */
+ aspath = aspath_dup (attr->aspath);
+ aspath = aspath_add_confed_seq (aspath, peer->local_as);
+ }
+ else
+ aspath = attr->aspath;
+
+ /* AS path attribute extended length bit check. */
+ if (aspath->length > 255)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_AS_PATH);
+ stream_putw (s, aspath->length);
+ }
+ else
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc(s, BGP_ATTR_AS_PATH);
+ stream_putc (s, aspath->length);
+ }
+ stream_put (s, aspath->data, aspath->length);
+
+ if (aspath != attr->aspath)
+ aspath_free (aspath);
+
+ /* Nexthop attribute. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_NEXT_HOP) && afi == AFI_IP)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_NEXT_HOP);
+ stream_putc (s, 4);
+ if (safi == SAFI_MPLS_VPN)
+ {
+ if (attr->nexthop.s_addr == 0)
+ stream_put_ipv4 (s, peer->nexthop.v4.s_addr);
+ else
+ stream_put_ipv4 (s, attr->nexthop.s_addr);
+ }
+ else
+ stream_put_ipv4 (s, attr->nexthop.s_addr);
+ }
+
+ /* MED attribute. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_MULTI_EXIT_DISC);
+ stream_putc (s, 4);
+ stream_putl (s, attr->med);
+ }
+
+ /* Local preference. */
+ if (peer_sort (peer) == BGP_PEER_IBGP ||
+ peer_sort (peer) == BGP_PEER_CONFED)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_LOCAL_PREF);
+ stream_putc (s, 4);
+ stream_putl (s, attr->local_pref);
+ }
+
+ /* Atomic aggregate. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE))
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_ATOMIC_AGGREGATE);
+ stream_putc (s, 0);
+ }
+
+ /* Aggregator. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_AGGREGATOR);
+ stream_putc (s, 6);
+ stream_putw (s, attr->aggregator_as);
+ stream_put_ipv4 (s, attr->aggregator_addr.s_addr);
+ }
+
+ /* Community attribute. */
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_COMMUNITY)
+ && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES)))
+ {
+ if (attr->community->size * 4 > 255)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_COMMUNITIES);
+ stream_putw (s, attr->community->size * 4);
+ }
+ else
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_COMMUNITIES);
+ stream_putc (s, attr->community->size * 4);
+ }
+ stream_put (s, attr->community->val, attr->community->size * 4);
+ }
+
+ /* Route Reflector. */
+ if (peer_sort (peer) == BGP_PEER_IBGP
+ && from
+ && peer_sort (from) == BGP_PEER_IBGP)
+ {
+ /* Originator ID. */
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_ORIGINATOR_ID);
+ stream_putc (s, 4);
+
+ if (attr->flag & ATTR_FLAG_BIT(BGP_ATTR_ORIGINATOR_ID))
+ stream_put_in_addr (s, &attr->originator_id);
+ else
+ {
+ if (from)
+ stream_put_in_addr (s, &from->remote_id);
+ else
+ stream_put_in_addr (s, &attr->originator_id);
+ }
+
+ /* Cluster list. */
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_CLUSTER_LIST);
+
+ if (attr->cluster)
+ {
+ stream_putc (s, attr->cluster->length + 4);
+ /* If this peer configuration's parent BGP has cluster_id. */
+ if (bgp->config & BGP_CONFIG_CLUSTER_ID)
+ stream_put_in_addr (s, &bgp->cluster_id);
+ else
+ stream_put_in_addr (s, &bgp->router_id);
+ stream_put (s, attr->cluster->list, attr->cluster->length);
+ }
+ else
+ {
+ stream_putc (s, 4);
+ /* If this peer configuration's parent BGP has cluster_id. */
+ if (bgp->config & BGP_CONFIG_CLUSTER_ID)
+ stream_put_in_addr (s, &bgp->cluster_id);
+ else
+ stream_put_in_addr (s, &bgp->router_id);
+ }
+ }
+
+#ifdef HAVE_IPV6
+ /* If p is IPv6 address put it into attribute. */
+ if (p->family == AF_INET6)
+ {
+ unsigned long sizep;
+ unsigned long draftp = 0;
+
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
+ sizep = stream_get_putp (s);
+ stream_putc (s, 0); /* Length of this attribute. */
+ stream_putw (s, AFI_IP6); /* AFI */
+ stream_putc (s, safi); /* SAFI */
+
+ stream_putc (s, attr->mp_nexthop_len);
+
+ if (attr->mp_nexthop_len == 16)
+ stream_put (s, &attr->mp_nexthop_global, 16);
+ else if (attr->mp_nexthop_len == 32)
+ {
+ stream_put (s, &attr->mp_nexthop_global, 16);
+ stream_put (s, &attr->mp_nexthop_local, 16);
+ }
+
+ /* SNPA */
+ stream_putc (s, 0);
+
+ /* In case of old draft BGP-4+. */
+ if (peer->version == BGP_VERSION_MP_4_DRAFT_00)
+ {
+ draftp = stream_get_putp (s);
+ stream_putw (s, 0);
+ }
+
+ /* Prefix write. */
+ stream_put_prefix (s, p);
+
+ /* Set MP attribute length. */
+ stream_putc_at (s, sizep, (stream_get_putp (s) - sizep) - 1);
+
+ /* In case of old draft BGP-4+. */
+ if (peer->version == BGP_VERSION_MP_4_DRAFT_00)
+ stream_putw_at (s, draftp, (stream_get_putp (s) - draftp) - 2);
+ }
+#endif /* HAVE_IPV6 */
+
+ if (p->family == AF_INET && safi == SAFI_MULTICAST)
+ {
+ unsigned long sizep;
+ unsigned long draftp = 0;
+
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
+ sizep = stream_get_putp (s);
+ stream_putc (s, 0); /* Length of this attribute. */
+ stream_putw (s, AFI_IP); /* AFI */
+ stream_putc (s, SAFI_MULTICAST); /* SAFI */
+
+ stream_putc (s, 4);
+ stream_put_ipv4 (s, attr->nexthop.s_addr);
+
+ /* SNPA */
+ stream_putc (s, 0);
+
+ /* In case of old draft BGP-4+. */
+ if (peer->version == BGP_VERSION_MP_4_DRAFT_00)
+ {
+ draftp = stream_get_putp (s);
+ stream_putw (s, 0);
+ }
+
+ /* Prefix write. */
+ stream_put_prefix (s, p);
+
+ /* Set MP attribute length. */
+ stream_putc_at (s, sizep, (stream_get_putp (s) - sizep) - 1);
+
+ /* In case of old draft BGP-4+. */
+ if (peer->version == BGP_VERSION_MP_4_DRAFT_00)
+ stream_putw_at (s, draftp, (stream_get_putp (s) - draftp) - 2);
+ }
+
+ if (p->family == AF_INET && safi == SAFI_MPLS_VPN)
+ {
+ unsigned long sizep;
+ unsigned long draftp = 0;
+
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_MP_REACH_NLRI);
+ sizep = stream_get_putp (s);
+ stream_putc (s, 0); /* Length of this attribute. */
+ stream_putw (s, AFI_IP); /* AFI */
+ stream_putc (s, BGP_SAFI_VPNV4); /* SAFI */
+
+ stream_putc (s, 12);
+ stream_putl (s, 0);
+ stream_putl (s, 0);
+ stream_put (s, &attr->mp_nexthop_global_in, 4);
+
+ /* SNPA */
+ stream_putc (s, 0);
+
+ /* In case of old draft BGP-4+. */
+ if (peer->version == BGP_VERSION_MP_4_DRAFT_00)
+ {
+ draftp = stream_get_putp (s);
+ stream_putw (s, 0);
+ }
+
+ /* Tag, RD, Prefix write. */
+ stream_putc (s, p->prefixlen + 88);
+ stream_put (s, tag, 3);
+ stream_put (s, prd->val, 8);
+ stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
+
+ /* Set MP attribute length. */
+ stream_putc_at (s, sizep, (stream_get_putp (s) - sizep) - 1);
+
+ /* In case of old draft BGP-4+. */
+ if (peer->version == BGP_VERSION_MP_4_DRAFT_00)
+ stream_putw_at (s, draftp, (stream_get_putp (s) - draftp) - 2);
+ }
+
+ /* Extended Communities attribute. */
+ if (CHECK_FLAG (peer->af_flags[afi][safi], PEER_FLAG_SEND_EXT_COMMUNITY)
+ && (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_EXT_COMMUNITIES)))
+ {
+ if (attr->ecommunity->size * 8 > 255)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
+ stream_putw (s, attr->ecommunity->size * 8);
+ }
+ else
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_EXT_COMMUNITIES);
+ stream_putc (s, attr->ecommunity->size * 8);
+ }
+ stream_put (s, attr->ecommunity->val, attr->ecommunity->size * 8);
+ }
+
+ /* Unknown transit attribute. */
+ if (attr->transit)
+ stream_put (s, attr->transit->val, attr->transit->length);
+
+ /* Return total size of attribute. */
+ return stream_get_putp (s) - cp;
+}
+
+bgp_size_t
+bgp_packet_withdraw (struct peer *peer, struct stream *s, struct prefix *p,
+ afi_t afi, safi_t safi, struct prefix_rd *prd,
+ u_char *tag)
+{
+ unsigned long cp;
+ unsigned long attrlen_pnt;
+ bgp_size_t size;
+
+ cp = stream_get_putp (s);
+
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_MP_UNREACH_NLRI);
+
+ attrlen_pnt = stream_get_putp (s);
+ stream_putc (s, 0); /* Length of this attribute. */
+
+ stream_putw (s, family2afi (p->family));
+
+ if (safi == SAFI_MPLS_VPN)
+ {
+ /* SAFI */
+ stream_putc (s, BGP_SAFI_VPNV4);
+
+ /* prefix. */
+ stream_putc (s, p->prefixlen + 88);
+ stream_put (s, tag, 3);
+ stream_put (s, prd->val, 8);
+ stream_put (s, &p->u.prefix, PSIZE (p->prefixlen));
+ }
+ else
+ {
+ /* SAFI */
+ stream_putc (s, safi);
+
+ /* prefix */
+ stream_put_prefix (s, p);
+ }
+
+ /* Set MP attribute length. */
+ size = stream_get_putp (s) - attrlen_pnt - 1;
+ stream_putc_at (s, attrlen_pnt, size);
+
+ return stream_get_putp (s) - cp;
+}
+
+/* Initialization of attribute. */
+void
+bgp_attr_init ()
+{
+ void attrhash_init ();
+
+ aspath_init ();
+ attrhash_init ();
+ community_init ();
+ ecommunity_init ();
+ cluster_init ();
+ transit_init ();
+}
+
+/* Make attribute packet. */
+void
+bgp_dump_routes_attr (struct stream *s, struct attr *attr)
+{
+ unsigned long cp;
+ unsigned long len;
+ struct aspath *aspath;
+
+ /* Remember current pointer. */
+ cp = stream_get_putp (s);
+
+ /* Place holder of length. */
+ stream_putw (s, 0);
+
+ /* Origin attribute. */
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_ORIGIN);
+ stream_putc (s, 1);
+ stream_putc (s, attr->origin);
+
+ aspath = attr->aspath;
+
+ if (aspath->length > 255)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_AS_PATH);
+ stream_putw (s, aspath->length);
+ }
+ else
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_AS_PATH);
+ stream_putc (s, aspath->length);
+ }
+ stream_put (s, aspath->data, aspath->length);
+
+ /* Nexthop attribute. */
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_NEXT_HOP);
+ stream_putc (s, 4);
+ stream_put_ipv4 (s, attr->nexthop.s_addr);
+
+ /* MED attribute. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_MULTI_EXIT_DISC))
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL);
+ stream_putc (s, BGP_ATTR_MULTI_EXIT_DISC);
+ stream_putc (s, 4);
+ stream_putl (s, attr->med);
+ }
+
+ /* Local preference. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_LOCAL_PREF))
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_LOCAL_PREF);
+ stream_putc (s, 4);
+ stream_putl (s, attr->local_pref);
+ }
+
+ /* Atomic aggregate. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_ATOMIC_AGGREGATE))
+ {
+ stream_putc (s, BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_ATOMIC_AGGREGATE);
+ stream_putc (s, 0);
+ }
+
+ /* Aggregator. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_AGGREGATOR))
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_AGGREGATOR);
+ stream_putc (s, 6);
+ stream_putw (s, attr->aggregator_as);
+ stream_put_ipv4 (s, attr->aggregator_addr.s_addr);
+ }
+
+ /* Community attribute. */
+ if (attr->flag & ATTR_FLAG_BIT (BGP_ATTR_COMMUNITIES))
+ {
+ if (attr->community->size * 4 > 255)
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS|BGP_ATTR_FLAG_EXTLEN);
+ stream_putc (s, BGP_ATTR_COMMUNITIES);
+ stream_putw (s, attr->community->size * 4);
+ }
+ else
+ {
+ stream_putc (s, BGP_ATTR_FLAG_OPTIONAL|BGP_ATTR_FLAG_TRANS);
+ stream_putc (s, BGP_ATTR_COMMUNITIES);
+ stream_putc (s, attr->community->size * 4);
+ }
+ stream_put (s, attr->community->val, attr->community->size * 4);
+ }
+
+ /* Return total size of attribute. */
+ len = stream_get_putp (s) - cp - 2;
+ stream_putw_at (s, cp, len);
+}