isisd: add new tlv parser

Signed-off-by: Christian Franke <chris@opensourcerouting.org>
This commit is contained in:
Christian Franke 2017-05-08 13:02:55 +02:00
parent 9879e291a4
commit 7ef5fefc3c
10 changed files with 3616 additions and 4 deletions

View file

@ -8,7 +8,7 @@ LIBS = @LIBS@
AM_CFLAGS = $(WERROR)
noinst_LIBRARIES = libisis.a
sbin_PROGRAMS = isisd
sbin_PROGRAMS = isisd
libisis_a_SOURCES = \
isis_memory.c \
@ -16,7 +16,8 @@ libisis_a_SOURCES = \
isis_tlv.c isisd.c isis_misc.c isis_zebra.c isis_dr.c \
isis_flags.c isis_dynhn.c iso_checksum.c isis_csm.c isis_events.c \
isis_spf.c isis_redist.c isis_route.c isis_routemap.c isis_te.c \
isis_vty.c isis_mt.c
isis_vty.c isis_mt.c \
isis_tlvs2.c
noinst_HEADERS = \
@ -25,7 +26,8 @@ noinst_HEADERS = \
isis_lsp.h dict.h isis_circuit.h isis_misc.h isis_network.h \
isis_zebra.h isis_dr.h isis_flags.h isis_dynhn.h isis_common.h \
iso_checksum.h isis_csm.h isis_events.h isis_spf.h isis_redist.h \
isis_route.h isis_routemap.h isis_te.h isis_mt.h
isis_route.h isis_routemap.h isis_te.h isis_mt.h \
isis_tlvs2.h
isisd_SOURCES = \
isis_main.c $(libisis_a_SOURCES) \

View file

@ -24,6 +24,7 @@
#define ISIS_MT_MASK 0x0fff
#define ISIS_MT_OL_MASK 0x8000
#define ISIS_MT_AT_MASK 0x4000
#define ISIS_MT_IPV4_UNICAST 0
#define ISIS_MT_IPV4_MGMT 1

3085
isisd/isis_tlvs2.c Normal file

File diff suppressed because it is too large Load diff

308
isisd/isis_tlvs2.h Normal file
View file

@ -0,0 +1,308 @@
/*
* IS-IS TLV Serializer/Deserializer
*
* Copyright (C) 2015,2017 Christian Franke
*
* This file is part of FRR.
*
* FRR 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.
*
* FRR 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 FRR; see the file COPYING. If not, write to the Free
* Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef ISIS_TLVS2_H
#define ISIS_TLVS2_H
#include "openbsd-tree.h"
#include "prefix.h"
#include "isisd/dict.h"
struct isis_subtlvs;
struct isis_area_address;
struct isis_area_address {
struct isis_area_address *next;
uint8_t addr[20];
uint8_t len;
};
struct isis_oldstyle_reach;
struct isis_oldstyle_reach {
struct isis_oldstyle_reach *next;
uint8_t id[7];
uint8_t metric;
};
struct isis_oldstyle_ip_reach;
struct isis_oldstyle_ip_reach {
struct isis_oldstyle_ip_reach *next;
uint8_t metric;
struct prefix_ipv4 prefix;
};
struct isis_lsp_entry;
struct isis_lsp_entry {
struct isis_lsp_entry *next;
uint16_t rem_lifetime;
uint8_t id[8];
uint16_t checksum;
uint32_t seqno;
struct isis_lsp *lsp;
};
struct isis_extended_reach;
struct isis_extended_reach {
struct isis_extended_reach *next;
uint8_t id[7];
uint32_t metric;
uint8_t *subtlvs;
uint8_t subtlv_len;
};
struct isis_extended_ip_reach;
struct isis_extended_ip_reach {
struct isis_extended_ip_reach *next;
uint32_t metric;
bool down;
struct prefix_ipv4 prefix;
};
struct isis_ipv6_reach;
struct isis_ipv6_reach {
struct isis_ipv6_reach *next;
uint32_t metric;
bool down;
bool external;
struct prefix_ipv6 prefix;
struct isis_subtlvs *subtlvs;
};
struct isis_protocols_supported {
uint8_t count;
uint8_t *protocols;
};
struct isis_item;
struct isis_item {
struct isis_item *next;
};
struct isis_lan_neighbor;
struct isis_lan_neighbor {
struct isis_lan_neighbor *next;
uint8_t mac[6];
};
struct isis_ipv4_address;
struct isis_ipv4_address {
struct isis_ipv4_address *next;
struct in_addr addr;
};
struct isis_ipv6_address;
struct isis_ipv6_address {
struct isis_ipv6_address *next;
struct in6_addr addr;
};
struct isis_mt_router_info;
struct isis_mt_router_info {
struct isis_mt_router_info *next;
bool overload;
bool attached;
uint16_t mtid;
};
struct isis_auth;
struct isis_auth {
struct isis_auth *next;
uint8_t type;
uint8_t length;
uint8_t value[256];
uint8_t plength;
uint8_t passwd[256];
size_t offset; /* Only valid after packing */
};
struct isis_item_list;
struct isis_item_list {
struct isis_item *head;
struct isis_item **tail;
RB_ENTRY(isis_item_list) mt_tree;
uint16_t mtid;
unsigned int count;
};
RB_HEAD(isis_mt_item_list, isis_item_list);
struct isis_item_list *isis_get_mt_items(struct isis_mt_item_list *m,
uint16_t mtid);
struct isis_item_list *isis_lookup_mt_items(struct isis_mt_item_list *m,
uint16_t mtid);
struct isis_tlvs {
struct isis_item_list isis_auth;
struct isis_item_list area_addresses;
struct isis_item_list oldstyle_reach;
struct isis_item_list lan_neighbor;
struct isis_item_list lsp_entries;
struct isis_item_list extended_reach;
struct isis_mt_item_list mt_reach;
struct isis_item_list oldstyle_ip_reach;
struct isis_protocols_supported protocols_supported;
struct isis_item_list oldstyle_ip_reach_ext;
struct isis_item_list ipv4_address;
struct isis_item_list ipv6_address;
struct isis_item_list mt_router_info;
bool mt_router_info_empty;
struct in_addr *te_router_id;
struct isis_item_list extended_ip_reach;
struct isis_mt_item_list mt_ip_reach;
char *hostname;
struct isis_item_list ipv6_reach;
struct isis_mt_item_list mt_ipv6_reach;
};
struct isis_subtlvs {
/* draft-baker-ipv6-isis-dst-src-routing-06 */
struct prefix_ipv6 *source_prefix;
};
enum isis_tlv_context {
ISIS_CONTEXT_LSP,
ISIS_CONTEXT_SUBTLV_NE_REACH,
ISIS_CONTEXT_SUBTLV_IP_REACH,
ISIS_CONTEXT_SUBTLV_IPV6_REACH,
ISIS_CONTEXT_MAX
};
enum isis_tlv_type {
ISIS_TLV_AREA_ADDRESSES = 1,
ISIS_TLV_OLDSTYLE_REACH = 2,
ISIS_TLV_LAN_NEIGHBORS = 6,
ISIS_TLV_PADDING = 8,
ISIS_TLV_LSP_ENTRY = 9,
ISIS_TLV_AUTH = 10,
ISIS_TLV_EXTENDED_REACH = 22,
ISIS_TLV_OLDSTYLE_IP_REACH = 128,
ISIS_TLV_PROTOCOLS_SUPPORTED = 129,
ISIS_TLV_OLDSTYLE_IP_REACH_EXT = 130,
ISIS_TLV_IPV4_ADDRESS = 132,
ISIS_TLV_TE_ROUTER_ID = 134,
ISIS_TLV_EXTENDED_IP_REACH = 135,
ISIS_TLV_DYNAMIC_HOSTNAME = 137,
ISIS_TLV_MT_REACH = 222,
ISIS_TLV_MT_ROUTER_INFO = 229,
ISIS_TLV_IPV6_ADDRESS = 232,
ISIS_TLV_MT_IP_REACH = 235,
ISIS_TLV_IPV6_REACH = 236,
ISIS_TLV_MT_IPV6_REACH = 237,
ISIS_TLV_MAX = 256,
ISIS_SUBTLV_IPV6_SOURCE_PREFIX = 22
};
#define IS_COMPAT_MT_TLV(tlv_type) \
((tlv_type == ISIS_TLV_MT_REACH) || (tlv_type == ISIS_TLV_MT_IP_REACH) \
|| (tlv_type == ISIS_TLV_MT_IPV6_REACH))
struct stream;
int isis_pack_tlvs(struct isis_tlvs *tlvs, struct stream *stream,
size_t len_pointer, bool pad, bool is_lsp);
void isis_free_tlvs(struct isis_tlvs *tlvs);
struct isis_tlvs *isis_alloc_tlvs(void);
int isis_unpack_tlvs(size_t avail_len, struct stream *stream,
struct isis_tlvs **dest, const char **error_log);
const char *isis_format_tlvs(struct isis_tlvs *tlvs);
struct isis_tlvs *isis_copy_tlvs(struct isis_tlvs *tlvs);
struct list *isis_fragment_tlvs(struct isis_tlvs *tlvs, size_t size);
#define ISIS_EXTENDED_IP_REACH_DOWN 0x80
#define ISIS_EXTENDED_IP_REACH_SUBTLV 0x40
#define ISIS_IPV6_REACH_DOWN 0x80
#define ISIS_IPV6_REACH_EXTERNAL 0x40
#define ISIS_IPV6_REACH_SUBTLV 0x20
#ifndef ISIS_MT_MASK
#define ISIS_MT_MASK 0x0fff
#define ISIS_MT_OL_MASK 0x8000
#define ISIS_MT_AT_MASK 0x4000
#endif
void isis_tlvs_add_auth(struct isis_tlvs *tlvs, struct isis_passwd *passwd);
void isis_tlvs_add_area_addresses(struct isis_tlvs *tlvs,
struct list *addresses);
void isis_tlvs_add_lan_neighbors(struct isis_tlvs *tlvs,
struct list *neighbors);
void isis_tlvs_set_protocols_supported(struct isis_tlvs *tlvs,
struct nlpids *nlpids);
void isis_tlvs_add_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid,
bool overload, bool attached);
void isis_tlvs_add_ipv4_address(struct isis_tlvs *tlvs, struct in_addr *addr);
void isis_tlvs_add_ipv4_addresses(struct isis_tlvs *tlvs,
struct list *addresses);
void isis_tlvs_add_ipv6_addresses(struct isis_tlvs *tlvs,
struct list *addresses);
bool isis_tlvs_auth_is_valid(struct isis_tlvs *tlvs, struct isis_passwd *passwd,
struct stream *stream, bool is_lsp);
bool isis_tlvs_area_addresses_match(struct isis_tlvs *tlvs,
struct list *addresses);
struct isis_adjacency;
void isis_tlvs_to_adj(struct isis_tlvs *tlvs, struct isis_adjacency *adj,
bool *changed);
bool isis_tlvs_own_snpa_found(struct isis_tlvs *tlvs, uint8_t *snpa);
void isis_tlvs_add_lsp_entry(struct isis_tlvs *tlvs, struct isis_lsp *lsp);
void isis_tlvs_add_csnp_entries(struct isis_tlvs *tlvs, uint8_t *start_id,
uint8_t *stop_id, uint16_t num_lsps,
dict_t *lspdb, struct isis_lsp **last_lsp);
void isis_tlvs_set_dynamic_hostname(struct isis_tlvs *tlvs,
const char *hostname);
void isis_tlvs_set_te_router_id(struct isis_tlvs *tlvs,
const struct in_addr *id);
void isis_tlvs_add_oldstyle_ip_reach(struct isis_tlvs *tlvs,
struct prefix_ipv4 *dest, uint8_t metric);
void isis_tlvs_add_extended_ip_reach(struct isis_tlvs *tlvs,
struct prefix_ipv4 *dest, uint32_t metric);
void isis_tlvs_add_ipv6_reach(struct isis_tlvs *tlvs, uint16_t mtid,
struct prefix_ipv6 *dest, uint32_t metric);
void isis_tlvs_add_oldstyle_reach(struct isis_tlvs *tlvs, uint8_t *id,
uint8_t metric);
void isis_tlvs_add_extended_reach(struct isis_tlvs *tlvs, uint16_t mtid,
uint8_t *id, uint32_t metric,
uint8_t *subtlvs, uint8_t subtlv_len);
struct isis_mt_router_info *
isis_tlvs_lookup_mt_router_info(struct isis_tlvs *tlvs, uint16_t mtid);
#endif

2
tests/.gitignore vendored
View file

@ -25,6 +25,8 @@ __pycache__
/bgpd/test_ecommunity
/bgpd/test_mp_attr
/bgpd/test_mpath
/isisd/test_fuzz_isis_tlv
/isisd/test_fuzz_isis_tlv_tests.h
/lib/cli/test_cli
/lib/cli/test_commands
/lib/cli/test_commands_defun.c

View file

@ -24,6 +24,13 @@ else
TESTS_BGPD =
endif
if ISISD
TESTS_ISISD = \
isisd/test_fuzz_isis_tlv
else
TESTS_ISISD =
endif
if OSPF6D
TESTS_OSPF6D = \
ospf6d/test_lsdb \
@ -61,6 +68,7 @@ check_PROGRAMS = \
lib/cli/test_cli \
lib/cli/test_commands \
$(TESTS_BGPD) \
$(TESTS_ISISD) \
$(TESTS_OSPF6D) \
# end
@ -75,7 +83,12 @@ lib/cli/test_commands_defun.c: ../vtysh/vtysh_cmd.c
< ../vtysh/vtysh_cmd.c \
> "$@"
BUILT_SOURCES = lib/cli/test_commands_defun.c
isisd/test_fuzz_isis_tlv_tests.h: $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz
gzip -d < $(top_srcdir)/tests/isisd/test_fuzz_isis_tlv_tests.h.gz > "$@"
BUILT_SOURCES = \
lib/cli/test_commands_defun.c \
isisd/test_fuzz_isis_tlv_tests.h
noinst_HEADERS = \
./helpers/c/prng.h \
@ -110,11 +123,14 @@ bgpd_test_capability_SOURCES = bgpd/test_capability.c
bgpd_test_ecommunity_SOURCES = bgpd/test_ecommunity.c
bgpd_test_mp_attr_SOURCES = bgpd/test_mp_attr.c
bgpd_test_mpath_SOURCES = bgpd/test_mpath.c
isisd_test_fuzz_isis_tlv_SOURCES = isisd/test_fuzz_isis_tlv.c
isisd_test_fuzz_isis_tlv_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_builddir)/tests/isisd
ospf6d_test_lsdb_SOURCES = ospf6d/test_lsdb.c lib/cli/common_cli.c
ALL_TESTS_LDADD = ../lib/libfrr.la @LIBCAP@
BGP_TEST_LDADD = ../bgpd/libbgp.a $(BGP_VNC_RFP_LIB) $(ALL_TESTS_LDADD) -lm
ISISD_TEST_LDADD = ../isisd/libisis.a $(ALL_TESTS_LDADD)
OSPF6_TEST_LDADD = ../ospf6d/libospf6.a $(ALL_TESTS_LDADD)
lib_test_buffer_LDADD = $(ALL_TESTS_LDADD)
@ -140,6 +156,7 @@ bgpd_test_capability_LDADD = $(BGP_TEST_LDADD)
bgpd_test_ecommunity_LDADD = $(BGP_TEST_LDADD)
bgpd_test_mp_attr_LDADD = $(BGP_TEST_LDADD)
bgpd_test_mpath_LDADD = $(BGP_TEST_LDADD)
isisd_test_fuzz_isis_tlv_LDADD = $(ISISD_TEST_LDADD)
ospf6d_test_lsdb_LDADD = $(OSPF6_TEST_LDADD)
EXTRA_DIST = \
@ -151,6 +168,8 @@ EXTRA_DIST = \
bgpd/test_mpath.py \
helpers/python/frrsix.py \
helpers/python/frrtest.py \
isisd/test_fuzz_isis_tlv.py \
isisd/test_fuzz_isis_tlv_tests.h.gz \
lib/cli/test_commands.in \
lib/cli/test_commands.py \
lib/cli/test_commands.refout \

1
tests/isisd/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/*_afl/*

View file

@ -0,0 +1,188 @@
#include "test_fuzz_isis_tlv_tests.h"
#include <zebra.h>
#include "memory.h"
#include "sbuf.h"
#include "stream.h"
#include "thread.h"
#include "isisd/isis_circuit.h"
#include "isisd/isis_tlvs2.h"
#define TEST_STREAM_SIZE 1500
struct thread_master *master;
int isis_sock_init(struct isis_circuit *circuit);
int isis_sock_init(struct isis_circuit *circuit)
{
return 0;
}
static bool atexit_registered;
static void show_meminfo_at_exit(void)
{
log_memstats_stderr("isis fuzztest");
}
static int comp_line(const void *p1, const void *p2)
{
return strcmp(*(char * const *)p1, *(char * const *)p2);
}
static char *sortlines(char *in)
{
size_t line_count = 1;
size_t rv_len = strlen(in) + 1;
size_t rv_pos = 0;
char *rv = XMALLOC(MTYPE_TMP, rv_len);
for (char *c = in; *c; c++) {
if (*c == '\n')
line_count++;
}
if (line_count == 1) {
strncpy(rv, in, rv_len);
return rv;
}
char **lines = XCALLOC(MTYPE_TMP, sizeof(char *)*line_count);
char *saveptr = NULL;
size_t i = 0;
for (char *line = strtok_r(in, "\n", &saveptr); line;
line = strtok_r(NULL, "\n", &saveptr)) {
lines[i++] = line;
assert(i <= line_count);
}
line_count = i;
qsort(lines, line_count, sizeof(char *), comp_line);
for (i = 0; i < line_count; i++) {
int printf_rv = snprintf(rv + rv_pos, rv_len - rv_pos, "%s\n", lines[i]);
assert(printf_rv >= 0);
rv_pos += printf_rv;
}
XFREE(MTYPE_TMP, lines);
return rv;
}
static int test(FILE *input, FILE *output)
{
struct stream *s = stream_new(TEST_STREAM_SIZE);
char buf[TEST_STREAM_SIZE];
size_t bytes_read = 0;
if (!atexit_registered) {
atexit(show_meminfo_at_exit);
atexit_registered = true;
}
while (STREAM_WRITEABLE(s) && !feof(input)) {
bytes_read = fread(buf, 1, STREAM_WRITEABLE(s), input);
if (bytes_read == 0)
break;
stream_put(s, buf, bytes_read);
}
if (bytes_read && !feof(input)) {
fprintf(output, "Too much input data.\n");
stream_free(s);
return 1;
}
stream_set_getp(s, 0);
struct isis_tlvs *tlvs;
const char *log;
int rv = isis_unpack_tlvs(STREAM_READABLE(s), s, &tlvs, &log);
if (rv) {
fprintf(output, "Could not unpack TLVs:\n%s\n", log);
isis_free_tlvs(tlvs);
stream_free(s);
return 2;
}
fprintf(output, "Unpack log:\n%s", log);
const char *s_tlvs = isis_format_tlvs(tlvs);
fprintf(output, "Unpacked TLVs:\n%s", s_tlvs);
struct isis_tlvs *tlv_copy = isis_copy_tlvs(tlvs);
isis_free_tlvs(tlvs);
struct stream *s2 = stream_new(TEST_STREAM_SIZE);
if (isis_pack_tlvs(tlv_copy, s2, (size_t)-1, false, false)) {
fprintf(output, "Could not pack TLVs.\n");
assert(0);
}
stream_set_getp(s2, 0);
rv = isis_unpack_tlvs(STREAM_READABLE(s2), s2, &tlvs, &log);
if (rv) {
fprintf(output, "Could not unpack own TLVs:\n%s\n", log);
assert(0);
}
char *orig_tlvs = XSTRDUP(MTYPE_TMP, s_tlvs);
s_tlvs = isis_format_tlvs(tlvs);
if (strcmp(orig_tlvs, s_tlvs)) {
fprintf(output,
"Deserialized and Serialized LSP seem to differ.\n");
fprintf(output, "Re-Unpacked TLVs:\n%s", s_tlvs);
assert(0);
}
isis_free_tlvs(tlv_copy);
stream_free(s);
stream_free(s2);
struct list *fragments = isis_fragment_tlvs(tlvs, 550);
isis_free_tlvs(tlvs);
if (!fragments) {
XFREE(MTYPE_TMP, orig_tlvs);
return 0;
}
s = stream_new(550);
struct sbuf fragment_format;
sbuf_init(&fragment_format, NULL, 0);
struct listnode *node;
for (ALL_LIST_ELEMENTS_RO(fragments, node, tlvs)) {
stream_reset(s);
int rv = isis_pack_tlvs(tlvs, s, (size_t)-1, false, false);
if (rv) {
fprintf(output, "Could not pack fragment, too large.\n");
assert(0);
}
sbuf_push(&fragment_format, 0, "%s", isis_format_tlvs(tlvs));
isis_free_tlvs(tlvs);
}
list_delete(fragments);
stream_free(s);
char *fragment_content = sortlines((char *)sbuf_buf(&fragment_format));
sbuf_free(&fragment_format);
char *orig_tlv_content = sortlines(orig_tlvs);
XFREE(MTYPE_TMP, orig_tlvs);
if (strcmp(fragment_content, orig_tlv_content)) {
fprintf(output, "Fragmented and unfragmented LSP seem to differ.\n");
fprintf(output, "Original:\n%s\nFragmented:\n%s\n",
orig_tlv_content, fragment_content);
assert(0);
}
XFREE(MTYPE_TMP, fragment_content);
XFREE(MTYPE_TMP, orig_tlv_content);
return 0;
}

View file

@ -0,0 +1,6 @@
import frrtest
class TestFuzzIsisTLV(frrtest.TestMultiOut):
program = './test_fuzz_isis_tlv'
TestFuzzIsisTLV.exit_cleanly()

Binary file not shown.