From: Christian Franke Date: Fri, 25 May 2018 11:26:27 +0000 (+0200) Subject: isisd: add support for Prefix-SID subtlv X-Git-Tag: frr-7.1-dev~384^2~10 X-Git-Url: https://git.puffer.fish/?a=commitdiff_plain;h=bd507085e0559ae6c8c6076c2948fdd6c66e4d1f;p=mirror%2Ffrr.git isisd: add support for Prefix-SID subtlv Extend isisd's TLV parser to support the Prefix-SID subtlv as per draft-ietf-isis-segment-routing-extensions-19 Signed-off-by: Christian Franke --- diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index 44beb45e43..966350e30f 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -107,6 +107,111 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX]; /* Prototypes */ static void append_item(struct isis_item_list *dest, struct isis_item *item); +/* Functions for Sub-TLV 3 SR Prefix-SID */ + +static struct isis_item *copy_item_prefix_sid(struct isis_item *i) +{ + struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; + struct isis_prefix_sid *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); + + rv->flags = sid->flags; + rv->algorithm = sid->algorithm; + rv->value = sid->value; + return (struct isis_item *)rv; +} + +static void format_item_prefix_sid(uint16_t mtid, struct isis_item *i, + struct sbuf *buf, int indent) +{ + struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; + + sbuf_push(buf, indent, "SR Prefix-SID:\n"); + sbuf_push(buf, indent, " Flags:%s%s%s%s%s%s\n", + sid->flags & ISIS_PREFIX_SID_READVERTISED ? " READVERTISED" : "", + sid->flags & ISIS_PREFIX_SID_NODE ? " NODE" : "", + sid->flags & ISIS_PREFIX_SID_NO_PHP ? " NO_PHP" : "", + sid->flags & ISIS_PREFIX_SID_EXPLICIT_NULL ? " EXPLICIT-NULL" : "", + sid->flags & ISIS_PREFIX_SID_VALUE ? " VALUE" : "", + sid->flags & ISIS_PREFIX_SID_LOCAL ? " LOCAL" : ""); + sbuf_push(buf, indent, " Algorithm: %" PRIu8 "\n", sid->algorithm); + if (sid->flags & ISIS_PREFIX_SID_VALUE) { + sbuf_push(buf, indent, "Label: %" PRIu32 "\n", sid->value); + } else { + sbuf_push(buf, indent, "Index: %" PRIu32 "\n", sid->value); + } +} + +static void free_item_prefix_sid(struct isis_item *i) +{ + XFREE(MTYPE_ISIS_SUBTLV, i); +} + +static int pack_item_prefix_sid(struct isis_item *i, struct stream *s) +{ + struct isis_prefix_sid *sid = (struct isis_prefix_sid *)i; + + uint8_t size = (sid->flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6; + + if (STREAM_WRITEABLE(s) < size) + return 1; + + stream_putc(s, sid->flags); + stream_putc(s, sid->algorithm); + + if (sid->flags & ISIS_PREFIX_SID_VALUE) { + stream_put3(s, sid->value); + } else { + stream_putl(s, sid->value); + } + + return 0; +} + +static int unpack_item_prefix_sid(uint16_t mtid, uint8_t len, struct stream *s, + struct sbuf *log, void *dest, int indent) +{ + struct isis_subtlvs *subtlvs = dest; + struct isis_prefix_sid sid = { + }; + + sbuf_push(log, indent, "Unpacking SR Prefix-SID...\n"); + + if (len < 5) { + sbuf_push(log, indent, + "Not enough data left. (expected 5 or more bytes, got %" PRIu8 ")\n", + len); + return 1; + } + + sid.flags = stream_getc(s); + if ((sid.flags & ISIS_PREFIX_SID_VALUE) + != (sid.flags & ISIS_PREFIX_SID_LOCAL)) { + sbuf_push(log, indent, "Flags inplausible: Local Flag needs to match Value Flag\n"); + return 0; + } + + sid.algorithm = stream_getc(s); + + uint8_t expected_size = (sid.flags & ISIS_PREFIX_SID_VALUE) ? 5 : 6; + if (len != expected_size) { + sbuf_push(log, indent, + "TLV size differs from expected size. " + "(expected %u but got %" PRIu8 ")\n", + expected_size, len); + return 1; + } + + if (sid.flags & ISIS_PREFIX_SID_VALUE) { + sid.value = stream_get3(s); + } else { + sid.value = stream_getl(s); + } + + format_item_prefix_sid(mtid, (struct isis_item *)&sid, log, indent + 2); + append_item(&subtlvs->prefix_sids, copy_item_prefix_sid((struct isis_item *)&sid)); + return 0; +} + /* Functions for Sub-TVL ??? IPv6 Source Prefix */ static struct prefix_ipv6 *copy_subtlv_ipv6_source_prefix(struct prefix_ipv6 *p) @@ -198,14 +303,36 @@ static int unpack_subtlv_ipv6_source_prefix(enum isis_tlv_context context, memcpy(subtlvs->source_prefix, &p, sizeof(p)); return 0; } +static void init_item_list(struct isis_item_list *items); +static struct isis_item *copy_item(enum isis_tlv_context context, + enum isis_tlv_type type, + struct isis_item *item); +static void copy_items(enum isis_tlv_context context, enum isis_tlv_type type, + struct isis_item_list *src, struct isis_item_list *dest); +static void format_items_(uint16_t mtid, enum isis_tlv_context context, + enum isis_tlv_type type, struct isis_item_list *items, + struct sbuf *buf, int indent); +#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) +static void free_items(enum isis_tlv_context context, enum isis_tlv_type type, + struct isis_item_list *items); +static int pack_items_(uint16_t mtid, enum isis_tlv_context context, + enum isis_tlv_type type, struct isis_item_list *items, + struct stream *s, struct isis_tlvs **fragment_tlvs, + struct pack_order_entry *pe, + struct isis_tlvs *(*new_fragment)(struct list *l), + struct list *new_fragment_arg); +#define pack_items(...) pack_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) /* Functions related to subtlvs */ -static struct isis_subtlvs *isis_alloc_subtlvs(void) +static struct isis_subtlvs *isis_alloc_subtlvs(enum isis_tlv_context context) { struct isis_subtlvs *result; result = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*result)); + result->context = context; + + init_item_list(&result->prefix_sids); return result; } @@ -217,6 +344,11 @@ static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs) struct isis_subtlvs *rv = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*rv)); + rv->context = subtlvs->context; + + copy_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, + &subtlvs->prefix_sids, &rv->prefix_sids); + rv->source_prefix = copy_subtlv_ipv6_source_prefix(subtlvs->source_prefix); return rv; @@ -225,6 +357,9 @@ static struct isis_subtlvs *copy_subtlvs(struct isis_subtlvs *subtlvs) static void format_subtlvs(struct isis_subtlvs *subtlvs, struct sbuf *buf, int indent) { + format_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, + &subtlvs->prefix_sids, buf, indent); + format_subtlv_ipv6_source_prefix(subtlvs->source_prefix, buf, indent); } @@ -233,6 +368,9 @@ static void isis_free_subtlvs(struct isis_subtlvs *subtlvs) if (!subtlvs) return; + free_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, + &subtlvs->prefix_sids); + XFREE(MTYPE_ISIS_SUBTLV, subtlvs->source_prefix); XFREE(MTYPE_ISIS_SUBTLV, subtlvs); @@ -248,6 +386,11 @@ static int pack_subtlvs(struct isis_subtlvs *subtlvs, struct stream *s) stream_putc(s, 0); /* Put 0 as subtlvs length, filled in later */ + rv = pack_items(subtlvs->context, ISIS_SUBTLV_PREFIX_SID, + &subtlvs->prefix_sids, s, NULL, NULL, NULL, NULL); + if (rv) + return rv; + rv = pack_subtlv_ipv6_source_prefix(subtlvs->source_prefix, s); if (rv) return rv; @@ -1135,6 +1278,7 @@ static void free_item_extended_ip_reach(struct isis_item *i) { struct isis_extended_ip_reach *item = (struct isis_extended_ip_reach *)i; + isis_free_subtlvs(item->subtlvs); XFREE(MTYPE_ISIS_TLV, item); } @@ -1149,11 +1293,16 @@ static int pack_item_extended_ip_reach(struct isis_item *i, struct stream *s) control = r->down ? ISIS_EXTENDED_IP_REACH_DOWN : 0; control |= r->prefix.prefixlen; + control |= r->subtlvs ? ISIS_EXTENDED_IP_REACH_SUBTLV : 0; + stream_putc(s, control); if (STREAM_WRITEABLE(s) < (unsigned)PSIZE(r->prefix.prefixlen)) return 1; stream_put(s, &r->prefix.prefix.s_addr, PSIZE(r->prefix.prefixlen)); + + if (r->subtlvs) + return pack_subtlvs(r->subtlvs, s); return 0; } @@ -1235,9 +1384,12 @@ static int unpack_item_extended_ip_reach(uint16_t mtid, uint8_t len, len - 6 - PSIZE(rv->prefix.prefixlen)); goto out; } - sbuf_push(log, indent, "Skipping %" PRIu8 " bytes of subvls", - subtlv_len); - stream_forward_getp(s, subtlv_len); + + rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IP_REACH); + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IP_REACH, subtlv_len, s, + log, rv->subtlvs, indent + 4)) { + goto out; + } } append_item(items, (struct isis_item *)rv); @@ -1712,7 +1864,7 @@ static int unpack_item_ipv6_reach(uint16_t mtid, uint8_t len, struct stream *s, goto out; } - rv->subtlvs = isis_alloc_subtlvs(); + rv->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH); if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH, subtlv_len, s, log, rv->subtlvs, indent + 4)) { goto out; @@ -1890,7 +2042,6 @@ static void format_items_(uint16_t mtid, enum isis_tlv_context context, for (i = items->head; i; i = i->next) format_item(mtid, context, type, i, buf, indent); } -#define format_items(...) format_items_(ISIS_MT_IPV4_UNICAST, __VA_ARGS__) static void free_item(enum isis_tlv_context tlv_context, enum isis_tlv_type tlv_type, struct isis_item *item) @@ -1996,6 +2147,14 @@ top: break; } + /* Multiple prefix-sids don't go into one TLV, so always break */ + if (type == ISIS_SUBTLV_PREFIX_SID + && (context == ISIS_CONTEXT_SUBTLV_IP_REACH + || context == ISIS_CONTEXT_SUBTLV_IPV6_REACH)) { + item = item->next; + break; + } + if (len > 255) { if (!last_len) /* strange, not a single item fit */ return 1; @@ -2800,6 +2959,9 @@ int isis_unpack_tlvs(size_t avail_len, struct stream *stream, .name = _desc_, .unpack = unpack_subtlv_##_name_, \ } +#define ITEM_SUBTLV_OPS(_name_, _desc_) \ + ITEM_TLV_OPS(_name_, _desc_) + ITEM_TLV_OPS(area_address, "TLV 1 Area Addresses"); ITEM_TLV_OPS(oldstyle_reach, "TLV 2 IS Reachability"); ITEM_TLV_OPS(lan_neighbor, "TLV 6 LAN Neighbors"); @@ -2818,6 +2980,7 @@ TLV_OPS(threeway_adj, "TLV 240 P2P Three-Way Adjacency"); ITEM_TLV_OPS(ipv6_address, "TLV 232 IPv6 Interface Address"); ITEM_TLV_OPS(ipv6_reach, "TLV 236 IPv6 Reachability"); +ITEM_SUBTLV_OPS(prefix_sid, "Sub-TLV 3 SR Prefix-SID"); SUBTLV_OPS(ipv6_source_prefix, "Sub-TLV 22 IPv6 Source Prefix"); static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { @@ -2845,8 +3008,11 @@ static const struct tlv_ops *tlv_table[ISIS_CONTEXT_MAX][ISIS_TLV_MAX] = { [ISIS_TLV_MT_IPV6_REACH] = &tlv_ipv6_reach_ops, }, [ISIS_CONTEXT_SUBTLV_NE_REACH] = {}, - [ISIS_CONTEXT_SUBTLV_IP_REACH] = {}, + [ISIS_CONTEXT_SUBTLV_IP_REACH] = { + [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops, + }, [ISIS_CONTEXT_SUBTLV_IPV6_REACH] = { + [ISIS_SUBTLV_PREFIX_SID] = &tlv_prefix_sid_ops, [ISIS_SUBTLV_IPV6_SOURCE_PREFIX] = &subtlv_ipv6_source_prefix_ops, } }; @@ -3318,7 +3484,7 @@ void isis_tlvs_add_ipv6_dstsrc_reach(struct isis_tlvs *tlvs, uint16_t mtid, mtid); struct isis_ipv6_reach *r = (struct isis_ipv6_reach*)last_item(l); - r->subtlvs = isis_alloc_subtlvs(); + r->subtlvs = isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_IPV6_REACH); r->subtlvs->source_prefix = XCALLOC(MTYPE_ISIS_SUBTLV, sizeof(*src)); memcpy(r->subtlvs->source_prefix, src, sizeof(*src)); } diff --git a/isisd/isis_tlvs.h b/isisd/isis_tlvs.h index 451321ea98..7ebb648cc6 100644 --- a/isisd/isis_tlvs.h +++ b/isisd/isis_tlvs.h @@ -83,6 +83,8 @@ struct isis_extended_ip_reach { uint32_t metric; bool down; struct prefix_ipv4 prefix; + + struct isis_subtlvs *subtlvs; }; struct isis_ipv6_reach; @@ -219,9 +221,21 @@ struct isis_tlvs { struct isis_spine_leaf *spine_leaf; }; -struct isis_subtlvs { - /* draft-baker-ipv6-isis-dst-src-routing-06 */ - struct prefix_ipv6 *source_prefix; +#define ISIS_PREFIX_SID_READVERTISED 0x80 +#define ISIS_PREFIX_SID_NODE 0x40 +#define ISIS_PREFIX_SID_NO_PHP 0x20 +#define ISIS_PREFIX_SID_EXPLICIT_NULL 0x10 +#define ISIS_PREFIX_SID_VALUE 0x08 +#define ISIS_PREFIX_SID_LOCAL 0x04 + +struct isis_prefix_sid; +struct isis_prefix_sid { + struct isis_prefix_sid *next; + + uint8_t flags; + uint8_t algorithm; + + uint32_t value; }; enum isis_tlv_context { @@ -232,6 +246,15 @@ enum isis_tlv_context { ISIS_CONTEXT_MAX }; +struct isis_subtlvs { + enum isis_tlv_context context; + + /* draft-baker-ipv6-isis-dst-src-routing-06 */ + struct prefix_ipv6 *source_prefix; + /* draft-ietf-isis-segment-routing-extensions-16 */ + struct isis_item_list prefix_sids; +}; + enum isis_tlv_type { ISIS_TLV_AREA_ADDRESSES = 1, ISIS_TLV_OLDSTYLE_REACH = 2, @@ -258,6 +281,7 @@ enum isis_tlv_type { ISIS_TLV_THREE_WAY_ADJ = 240, ISIS_TLV_MAX = 256, + ISIS_SUBTLV_PREFIX_SID = 3, ISIS_SUBTLV_IPV6_SOURCE_PREFIX = 22 };