diff --git a/isisd/isis_tlvs.c b/isisd/isis_tlvs.c index b0ec3d2d64..c09a7b2615 100644 --- a/isisd/isis_tlvs.c +++ b/isisd/isis_tlvs.c @@ -5625,6 +5625,103 @@ static int pack_item_srv6_locator(struct isis_item *i, struct stream *s, return 0; } +static int unpack_item_srv6_locator(uint16_t mtid, uint8_t len, + struct stream *s, struct sbuf *log, + void *dest, int indent) +{ + struct isis_tlvs *tlvs = dest; + struct isis_srv6_locator_tlv *rv = NULL; + size_t consume; + uint8_t subtlv_len; + struct isis_item_list *items; + + items = isis_get_mt_items(&tlvs->srv6_locator, mtid); + + sbuf_push(log, indent, "Unpacking SRv6 Locator...\n"); + consume = 7; + if (len < consume) { + sbuf_push( + log, indent, + "Not enough data left. (expected 7 or more bytes, got %hhu)\n", + len); + goto out; + } + + rv = XCALLOC(MTYPE_ISIS_TLV, sizeof(*rv)); + + rv->metric = stream_getl(s); + rv->flags = stream_getc(s); + rv->algorithm = stream_getc(s); + + rv->prefix.family = AF_INET6; + rv->prefix.prefixlen = stream_getc(s); + if (rv->prefix.prefixlen > IPV6_MAX_BITLEN) { + sbuf_push(log, indent, "Loc Size %u is implausible for SRv6\n", + rv->prefix.prefixlen); + goto out; + } + + consume += PSIZE(rv->prefix.prefixlen); + if (len < consume) { + sbuf_push( + log, indent, + "Expected %u bytes of prefix, but only %u bytes available.\n", + PSIZE(rv->prefix.prefixlen), len - 7); + goto out; + } + stream_get(&rv->prefix.prefix.s6_addr, s, PSIZE(rv->prefix.prefixlen)); + + struct in6_addr orig_locator = rv->prefix.prefix; + apply_mask_ipv6(&rv->prefix); + if (memcmp(&orig_locator, &rv->prefix.prefix, sizeof(orig_locator))) + sbuf_push(log, indent + 2, + "WARNING: SRv6 Locator had hostbits set.\n"); + format_item_srv6_locator(mtid, (struct isis_item *)rv, log, NULL, + indent + 2); + + consume += 1; + if (len < consume) { + sbuf_push( + log, indent, + "Expected 1 byte of subtlv len, but no more data persent.\n"); + goto out; + } + subtlv_len = stream_getc(s); + + if (subtlv_len) { + consume += subtlv_len; + if (len < consume) { + sbuf_push( + log, indent, + "Expected %hhu bytes of subtlvs, but only %u bytes available.\n", + subtlv_len, + len - 7 - PSIZE(rv->prefix.prefixlen)); + goto out; + } + + rv->subtlvs = + isis_alloc_subtlvs(ISIS_CONTEXT_SUBTLV_SRV6_LOCATOR); + + bool unpacked_known_tlvs = false; + if (unpack_tlvs(ISIS_CONTEXT_SUBTLV_SRV6_LOCATOR, subtlv_len, s, + log, rv->subtlvs, indent + 4, + &unpacked_known_tlvs)) { + goto out; + } + if (!unpacked_known_tlvs) { + isis_free_subtlvs(rv->subtlvs); + rv->subtlvs = NULL; + } + } + + append_item(items, (struct isis_item *)rv); + return 0; +out: + if (rv) + free_item_srv6_locator((struct isis_item *)rv); + return 1; +} + /* Functions related to tlvs in general */ struct isis_tlvs *isis_alloc_tlvs(void)