+
+ uint l = bsprintf(pos, "%d/", labels[i]);
+ ADVANCE(pos, size, l);
+ }
+
+ /* Clear last slash or terminate empty string */
+ pos[lnum ? -1 : 0] = 0;
+}
+
+static inline void
+bgp_decode_unknown(struct bgp_parse_state *s, uint code, uint flags, byte *data, uint len, ea_list **to)
+{
+ /* Cannot use bgp_set_attr_data() as it works on known attributes only */
+ ea_set_attr_data(to, s->pool, EA_CODE(PROTOCOL_BGP, code), flags, EAF_TYPE_OPAQUE, data, len);
+}
+
+
+/*
+ * Attribute table
+ */
+
+static const struct bgp_attr_desc bgp_attr_table[] = {
+ [BA_ORIGIN] = {
+ .name = "origin",
+ .type = EAF_TYPE_INT,
+ .flags = BAF_TRANSITIVE,
+ .export = bgp_export_origin,
+ .encode = bgp_encode_u8,
+ .decode = bgp_decode_origin,
+ .format = bgp_format_origin,
+ },
+ [BA_AS_PATH] = {
+ .name = "as_path",
+ .type = EAF_TYPE_AS_PATH,
+ .flags = BAF_TRANSITIVE,
+ .encode = bgp_encode_as_path,
+ .decode = bgp_decode_as_path,
+ },
+ [BA_NEXT_HOP] = {
+ .name = "next_hop",
+ .type = EAF_TYPE_IP_ADDRESS,
+ .flags = BAF_TRANSITIVE,
+ .encode = bgp_encode_next_hop,
+ .decode = bgp_decode_next_hop,
+ .format = bgp_format_next_hop,
+ },
+ [BA_MULTI_EXIT_DISC] = {
+ .name = "med",
+ .type = EAF_TYPE_INT,
+ .flags = BAF_OPTIONAL,
+ .encode = bgp_encode_u32,
+ .decode = bgp_decode_med,
+ },
+ [BA_LOCAL_PREF] = {
+ .name = "local_pref",
+ .type = EAF_TYPE_INT,
+ .flags = BAF_TRANSITIVE,
+ .export = bgp_export_local_pref,
+ .encode = bgp_encode_u32,
+ .decode = bgp_decode_local_pref,
+ },
+ [BA_ATOMIC_AGGR] = {
+ .name = "atomic_aggr",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_TRANSITIVE,
+ .encode = bgp_encode_raw,
+ .decode = bgp_decode_atomic_aggr,
+ },
+ [BA_AGGREGATOR] = {
+ .name = "aggregator",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .encode = bgp_encode_aggregator,
+ .decode = bgp_decode_aggregator,
+ .format = bgp_format_aggregator,
+ },
+ [BA_COMMUNITY] = {
+ .name = "community",
+ .type = EAF_TYPE_INT_SET,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .export = bgp_export_community,
+ .encode = bgp_encode_u32s,
+ .decode = bgp_decode_community,
+ },
+ [BA_ORIGINATOR_ID] = {
+ .name = "originator_id",
+ .type = EAF_TYPE_ROUTER_ID,
+ .flags = BAF_OPTIONAL,
+ .export = bgp_export_originator_id,
+ .encode = bgp_encode_u32,
+ .decode = bgp_decode_originator_id,
+ },
+ [BA_CLUSTER_LIST] = {
+ .name = "cluster_list",
+ .type = EAF_TYPE_INT_SET,
+ .flags = BAF_OPTIONAL,
+ .export = bgp_export_cluster_list,
+ .encode = bgp_encode_u32s,
+ .decode = bgp_decode_cluster_list,
+ .format = bgp_format_cluster_list,
+ },
+ [BA_MP_REACH_NLRI] = {
+ .name = "mp_reach_nlri",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_OPTIONAL,
+ .decode = bgp_decode_mp_reach_nlri,
+ },
+ [BA_MP_UNREACH_NLRI] = {
+ .name = "mp_unreach_nlri",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_OPTIONAL,
+ .decode = bgp_decode_mp_unreach_nlri,
+ },
+ [BA_EXT_COMMUNITY] = {
+ .name = "ext_community",
+ .type = EAF_TYPE_EC_SET,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .export = bgp_export_ext_community,
+ .encode = bgp_encode_u32s,
+ .decode = bgp_decode_ext_community,
+ },
+ [BA_AS4_PATH] = {
+ .name = "as4_path",
+ .type = EAF_TYPE_AS_PATH,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .encode = bgp_encode_raw,
+ .decode = bgp_decode_as4_path,
+ },
+ [BA_AS4_AGGREGATOR] = {
+ .name = "as4_aggregator",
+ .type = EAF_TYPE_OPAQUE,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .encode = bgp_encode_raw,
+ .decode = bgp_decode_as4_aggregator,
+ .format = bgp_format_aggregator,
+ },
+ [BA_LARGE_COMMUNITY] = {
+ .name = "large_community",
+ .type = EAF_TYPE_LC_SET,
+ .flags = BAF_OPTIONAL | BAF_TRANSITIVE,
+ .export = bgp_export_large_community,
+ .encode = bgp_encode_u32s,
+ .decode = bgp_decode_large_community,
+ },
+ [BA_MPLS_LABEL_STACK] = {
+ .name = "mpls_label_stack",
+ .type = EAF_TYPE_INT_SET,
+ .export = bgp_export_mpls_label_stack,
+ .encode = bgp_encode_mpls_label_stack,
+ .decode = bgp_decode_mpls_label_stack,
+ .format = bgp_format_mpls_label_stack,
+ },
+};
+
+static inline int
+bgp_attr_known(uint code)
+{
+ return (code < ARRAY_SIZE(bgp_attr_table)) && bgp_attr_table[code].name;
+}
+
+
+/*
+ * Attribute export
+ */
+
+static inline void
+bgp_export_attr(struct bgp_export_state *s, eattr *a, ea_list *to)
+{
+ if (EA_PROTO(a->id) != PROTOCOL_BGP)
+ return;
+
+ uint code = EA_ID(a->id);
+
+ if (bgp_attr_known(code))
+ {
+ const struct bgp_attr_desc *desc = &bgp_attr_table[code];
+
+ /* The flags might have been zero if the attr was added by filters */
+ a->flags = (a->flags & BAF_PARTIAL) | desc->flags;
+
+ /* Set partial bit if new opt-trans attribute is attached to non-local route */
+ if ((s->src != NULL) && (a->type & EAF_ORIGINATED) &&
+ (a->flags & BAF_OPTIONAL) && (a->flags & BAF_TRANSITIVE))
+ a->flags |= BAF_PARTIAL;
+
+ /* Call specific hook */
+ CALL(desc->export, s, a);
+
+ /* Attribute might become undefined in hook */
+ if ((a->type & EAF_TYPE_MASK) == EAF_TYPE_UNDEF)
+ return;
+ }