]> git.proxmox.com Git - mirror_frr.git/commitdiff
isisd: add support for Prefix-SID subtlv
authorChristian Franke <chris@opensourcerouting.org>
Fri, 25 May 2018 11:26:27 +0000 (13:26 +0200)
committerChristian Franke <chris@opensourcerouting.org>
Wed, 5 Sep 2018 09:38:13 +0000 (11:38 +0200)
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 <chris@opensourcerouting.org>
isisd/isis_tlvs.c
isisd/isis_tlvs.h

index 44beb45e43b6e5a402d5a171b165c8bdf8b184bc..966350e30fbf679f0598f47b0e82f4bddf484528 100644 (file)
@@ -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));
 }
index 451321ea98066c11c010cfd78d005eb742706683..7ebb648cc6abf94b131575850f9c42f85e0259aa 100644 (file)
@@ -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
 };