/* * Generic Link State Packet generation for link-state protocols. * * Hannes Gredler, January 2022 * * Copyright (C) 2015-2022, RtBrick, Inc. */ #include #include "libdict/dict.h" #include "lspgen.h" #include "lspgen_lsdb.h" #include "lspgen_isis.h" #include "lspgen_ospf.h" /* * Globals */ bool loop_running = true; /* * Prototypes */ const char banner[] = "\n" " ____ __ ____ _ __ ,/\n" " / __ \\ / /_ / __ ) _____ (_)_____ / /__ ,'/\n" " / /_/ // __// __ |/ ___// // ___// //_/ ,' /\n" " / _, _// /_ / /_/ // / / // /__ / ,< ,' /_____, \n" " /_/ |_| \\__//_____//_/ /_/ \\___//_/|_| .'____ ,' \n" " __ _____ ____ ______ / ,'\n" " / / / ___// __ \\/ ____/__ ____ / ,'\n" " / / \\__ \\/ /_/ / / __/ _ \\/ __ \\ /,'\n" " / /______/ / ____/ /_/ / __/ / / / / \n" " /_____/____/_/ \\____/\\___/_/ /_/\n\n"; /* * Command line options. */ static struct option long_options[] = { {"version", no_argument, NULL, 'v'}, {"area", required_argument, NULL, 'a'}, {"protocol", required_argument, NULL, 'P'}, {"authentication-key", required_argument, NULL, 'K'}, {"authentication-type", required_argument, NULL, 'T'}, {"read-config-file", required_argument, NULL, 'r'}, {"write-config-file", required_argument, NULL, 'w'}, {"connector", required_argument, NULL, 'C'}, {"control-socket", required_argument, NULL, 'S'}, {"ipv4-link-prefix", required_argument, NULL, 'l'}, {"ipv6-link-prefix", required_argument, NULL, 'L'}, {"ipv4-node-prefix", required_argument, NULL, 'n'}, {"ipv6-node-prefix", required_argument, NULL, 'N'}, {"ipv4-external-prefix", required_argument, NULL, 'x'}, {"ipv6-external-prefix", required_argument, NULL, 'X'}, {"link-multiplier", required_argument, NULL, 'u'}, {"lsp-lifetime", required_argument, NULL, 'M'}, {"no-ipv4", no_argument, NULL, 'z'}, {"no-ipv6", no_argument, NULL, 'Z'}, {"no-sr", no_argument, NULL, 'y'}, {"external-count", required_argument, NULL, 'e'}, {"graphviz-file", required_argument, NULL, 'g'}, {"help", no_argument, NULL, 'h'}, {"mrt-file", required_argument, NULL, 'm'}, {"node-count", required_argument, NULL, 'c'}, {"pcap-file", required_argument, NULL, 'p'}, {"purge", no_argument, NULL, 'G'}, {"stream-file", required_argument, NULL, 'f'}, {"seed", required_argument, NULL, 's'}, {"sequence", required_argument, NULL, 'q'}, {"quit-loop", no_argument, NULL, 'Q'}, {"level", required_argument, NULL, 'V'}, {"log", required_argument, NULL, 't' }, {NULL, 0, NULL, 0} }; /* * Log target / name translation table. */ struct keyval_ log_names[] = { { NORMAL, "normal" }, { DEBUG, "debug" }, { LSP, "lsp" }, { LSDB, "lsdb" }, { PACKET, "packet" }, { CTRL, "ctrl" }, { ERROR, "error" }, #ifdef BNGBLASTER_TIMER_LOGGING { TIMER, "timer" }, { TIMER_DETAIL, "timer-detail" }, #endif { 0, NULL} }; /* * Protocool / name translation table. */ struct keyval_ proto_names[] = { { PROTO_ISIS, "isis" }, { PROTO_OSPF2, "ospf2" }, { PROTO_OSPF3, "ospf3" }, { 0, NULL} }; const char * lsdb_format_proto (struct lsdb_ctx_ *ctx) { return val2key(proto_names, ctx->protocol_id); } /* * Authentication type / name translation table. */ struct keyval_ auth_type_names[] = { { ISIS_AUTH_NONE, "none" }, { ISIS_AUTH_SIMPLE, "simple" }, { ISIS_AUTH_MD5, "md5" }, { 0, NULL} }; char * lspgen_print_arg_options(struct keyval_ *ptr) { static char buf[128]; int len; len = 0; buf[0] = 0; if (!ptr) { return buf; } while (ptr->key) { len += snprintf(buf+len, sizeof(buf)-len, "%s%s", len ? "|" : " ", ptr->key); ptr++; } return buf; } char * lspgen_print_usage_arg(struct option *option) { if (option->has_arg == 1) { /* protocol */ if (strcmp(option->name, "protocol") == 0) { return lspgen_print_arg_options(proto_names); } /* logging */ if (strcmp(option->name, "log") == 0) { return lspgen_print_arg_options(log_names); } /* authentication-type */ if (strcmp(option->name, "authentication-type") == 0) { return lspgen_print_arg_options(auth_type_names); } /* filenames */ if (strstr(option->name, "file")) { return " "; } /* IP prefixes */ if (strstr(option->name, "prefix")) { return " "; } return " "; } return ""; } static void lspgen_print_version(void) { if(sizeof(BNGBLASTER_VERSION)-1) { printf("Version: %s\n", BNGBLASTER_VERSION); } if(sizeof(GIT_REF)-1 + sizeof(GIT_SHA)-1) { printf("GIT:\n"); printf(" REF: %s\n", GIT_REF); printf(" SHA: %s\n", GIT_SHA); } } void lspgen_print_usage(void) { uint16_t idx; printf("%s", banner); printf("Usage: lspgen [OPTIONS]\n\n"); for (idx = 0;; idx++) { if (!long_options[idx].name) { break; } printf(" -%c --%s%s\n", long_options[idx].val, long_options[idx].name, lspgen_print_usage_arg(&long_options[idx])); } } /* * Map the authentication type */ uint32_t get_authentication_type(char *auth_type_name) { int idx; idx = 0; while (auth_type_names[idx].key) { if (strcmp(auth_type_names[idx].key, auth_type_name) == 0) { return auth_type_names[idx].val; } idx++; } return ISIS_AUTH_NONE; } __uint128_t lspgen_load_addr(uint8_t *buf, uint32_t len) { __uint128_t addr; uint32_t idx; addr = *buf; for (idx = 1; idx < len; idx++) { addr = addr << 8; addr |= buf[idx]; } return addr; } void lspgen_store_addr(__uint128_t addr, uint8_t *buf, uint32_t len) { uint32_t idx; for (idx = len; idx; idx--) { buf[idx-1] = addr & 0xff; addr = addr >> 8; } } /* * Drain the stack of nibbles. */ void lspgen_drain_nibble_stack(uint8_t *stack, uint32_t *stack_idx, uint8_t **end_buf_ptr) { uint32_t idx; if (*stack_idx < 2) { return; } idx = 0; while ((*stack_idx - idx) >= 2) { **end_buf_ptr = stack[idx] + (stack[idx+1] << 4); *end_buf_ptr -= 1; idx += 2; } /* * Rebase Stack. */ stack[0] = stack[idx]; *stack_idx -= idx; } void lspgen_store_bcd_addr(__uint128_t addr, uint8_t *buf, uint32_t len) { uint32_t idx, stack_idx, digit; uint8_t bcd_digit[3]; uint8_t stack[4]; uint8_t *end_buf; /* Length must only be multiple of two */ if (len % 2) { return; } /* * BCD strings consume 50% more space and written from tail to head */ end_buf = buf + ((len*3)/2)-1; stack_idx = 0; for (idx = 0; idx < len; idx++) { /* * Load a byte and do the decimal conversation. */ digit = addr & 0xff; bcd_digit[0] = digit/100; digit -= bcd_digit[0] * 100; bcd_digit[1] = digit/10; digit -= bcd_digit[1] * 10; bcd_digit[2] = digit; /* * Now push the nibbles to the stack. */ stack[stack_idx++] = bcd_digit[2]; stack[stack_idx++] = bcd_digit[1]; stack[stack_idx++] = bcd_digit[0]; lspgen_drain_nibble_stack(stack, &stack_idx, &end_buf); addr = addr >> 8; } } /* * Calculate the prefix increment length depending on the prefix length */ __uint128_t lspgen_get_prefix_inc(uint32_t afi, uint32_t prefix_len) { __uint128_t prefix_inc; uint32_t inc_bit; prefix_inc = 0; switch(afi) { case AF_INET: prefix_inc = 1L << (32 - prefix_len); break; case AF_INET6: /* We have to do this madness below to overcome the fact * That the gcc code generated from shifting uint128 is not * what is expected. */ inc_bit = 128 - prefix_len; prefix_inc = 1L; while (inc_bit > 32) { prefix_inc = prefix_inc << 32; inc_bit -= 32; } prefix_inc = prefix_inc << inc_bit; break; default: break; } return prefix_inc; } /* * Walk the graph of the LSDB and add the required node/link attributes for IS-IS LSP generation. */ void lspgen_gen_isis_attr(struct lsdb_ctx_ *ctx) { struct lsdb_node_ *node; struct lsdb_link_ *link; struct lsdb_attr_ attr_template; dict_itor *itor; __uint128_t addr, inc, ext_addr4, ext_incr4, ext_addr6, ext_incr6; uint32_t ext_per_node, idx, nodes_left, ext_left; /* * Walk the node DB. */ itor = dict_itor_new(ctx->node_dict); if (!itor) { return; } /* * Node DB empty ? */ if (!dict_itor_first(itor)) { dict_itor_free(itor); LOG_NOARG(ERROR, "Empty LSDB.\n"); return; } /* * For external routes load the first address of the pool. */ ext_addr4 = lspgen_load_addr((uint8_t*)&ctx->ipv4_ext_prefix.address, sizeof(ipv4addr_t)); ext_incr4 = lspgen_get_prefix_inc(AF_INET, ctx->ipv4_ext_prefix.len); ext_addr6 = lspgen_load_addr((uint8_t*)&ctx->ipv6_ext_prefix.address, IPV6_ADDR_LEN); ext_incr6 = lspgen_get_prefix_inc(AF_INET6, ctx->ipv6_ext_prefix.len); nodes_left = ctx->num_nodes; ext_left = ctx->num_ext; do { node = *dict_itor_datum(itor); LOG(LSDB, "Generating node %s (%s) attributes\n", lsdb_format_node(node), lsdb_format_node_id(node->key.node_id)); /* Area */ for (idx = 0; idx < ctx->num_area; idx++) { lsdb_reset_attr_template(&attr_template); memcpy(&attr_template.key.area, &ctx->area[idx], sizeof(attr_template.key.area)); attr_template.key.ordinal = 1; attr_template.key.attr_type = ISIS_TLV_AREA; lsdb_add_node_attr(node, &attr_template); } /* Protocols */ lsdb_reset_attr_template(&attr_template); attr_template.key.ordinal = 1; attr_template.key.protocol = NLPID_IPV4; /* ipv4 */ attr_template.key.attr_type = ISIS_TLV_PROTOCOLS; lsdb_add_node_attr(node, &attr_template); attr_template.key.protocol = NLPID_IPV6; /* ipv6 */ lsdb_add_node_attr(node, &attr_template); /* Host name */ if (node->node_name) { lsdb_reset_attr_template(&attr_template); attr_template.key.ordinal = 1; attr_template.key.attr_type = ISIS_TLV_HOSTNAME; strncpy(attr_template.key.hostname, node->node_name, sizeof(attr_template.key.hostname)-1); lsdb_add_node_attr(node, &attr_template); } /* IPv4 loopback address */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_node_prefix.address, sizeof(ipv4addr_t)); addr += node->node_index; lspgen_store_addr(addr, attr_template.key.ipv4_addr, sizeof(ipv4addr_t)); attr_template.key.ordinal = 1; attr_template.key.attr_type = ISIS_TLV_IPV4_ADDR; lsdb_add_node_attr(node, &attr_template); /* IPv4 loopback prefix */ lsdb_reset_attr_template(&attr_template); lspgen_store_addr(addr, (uint8_t*)&attr_template.key.prefix.ipv4_prefix.address, sizeof(ipv4addr_t)); attr_template.key.prefix.ipv4_prefix.len = ctx->ipv4_node_prefix.len; if (!ctx->no_sr) { attr_template.key.prefix.sid = node->node_index; attr_template.key.prefix.adv_sid = true; } attr_template.key.prefix.node_flag = true; attr_template.key.ordinal = 1; attr_template.key.attr_type = ISIS_TLV_EXTD_IPV4_REACH; lsdb_add_node_attr(node, &attr_template); /* IPv6 loopback address */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr(ctx->ipv6_node_prefix.address, IPV6_ADDR_LEN); addr += node->node_index; lspgen_store_addr(addr, attr_template.key.ipv6_addr, IPV6_ADDR_LEN); attr_template.key.ordinal = 1; attr_template.key.attr_type = ISIS_TLV_IPV6_ADDR; lsdb_add_node_attr(node, &attr_template); /* IPv6 loopback prefix */ lsdb_reset_attr_template(&attr_template); lspgen_store_addr(addr, attr_template.key.prefix.ipv6_prefix.address, IPV6_ADDR_LEN); attr_template.key.prefix.ipv6_prefix.len = ctx->ipv6_node_prefix.len; if (!ctx->no_sr) { attr_template.key.prefix.sid = ctx->srgb_range/2 + node->node_index; attr_template.key.prefix.adv_sid = true; } attr_template.key.prefix.node_flag = true; attr_template.key.ordinal = 1; attr_template.key.attr_type = ISIS_TLV_EXTD_IPV6_REACH; lsdb_add_node_attr(node, &attr_template); /* external prefixes */ ext_per_node = ext_left / nodes_left; ext_left -= ext_per_node; while (ext_per_node--) { /* ipv4 external prefix */ lsdb_reset_attr_template(&attr_template); lspgen_store_addr(ext_addr4, (uint8_t*)&attr_template.key.prefix.ipv4_prefix.address, 4); attr_template.key.prefix.ipv4_prefix.len = ctx->ipv4_ext_prefix.len; attr_template.key.prefix.metric = 100; attr_template.key.attr_type = ISIS_TLV_EXTD_IPV4_REACH; lsdb_add_node_attr(node, &attr_template); ext_addr4 += ext_incr4; /* ipv6 external prefix */ lsdb_reset_attr_template(&attr_template); lspgen_store_addr(ext_addr6, attr_template.key.prefix.ipv6_prefix.address, IPV6_ADDR_LEN); attr_template.key.prefix.ipv6_prefix.len = ctx->ipv6_ext_prefix.len; attr_template.key.prefix.metric = 100; attr_template.key.attr_type = ISIS_TLV_EXTD_IPV6_REACH; lsdb_add_node_attr(node, &attr_template); ext_addr6 += ext_incr6; } if (!ctx->no_sr) { /* SR capability */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_node_prefix.address, sizeof(ipv4addr_t)); addr += node->node_index; lspgen_store_addr(addr, attr_template.key.cap.router_id, sizeof(ipv4addr_t)); attr_template.key.cap.srgb_base = ctx->srgb_base; attr_template.key.cap.srgb_range = ctx->srgb_range; if (!ctx->no_ipv4) { attr_template.key.cap.mpls_ipv4_flag = true; /* mpls ipv4 */ } if (!ctx->no_ipv6) { attr_template.key.cap.mpls_ipv6_flag = true; /* mpls ipv6 */ } attr_template.key.attr_type = ISIS_TLV_CAP; attr_template.key.ordinal = 1; lsdb_add_node_attr(node, &attr_template); } /* * Walk all of our neighbors. */ CIRCLEQ_FOREACH(link, &node->link_qhead, link_qnode) { /* Generate an IS reach for each link */ lsdb_reset_attr_template(&attr_template); attr_template.key.attr_type = ISIS_TLV_EXTD_IS_REACH; memcpy(attr_template.key.link.remote_node_id, link->key.remote_node_id, 7); attr_template.key.link.metric = link->link_metric; lsdb_add_node_attr(node, &attr_template); /* Generate an IPv4 prefix for each link */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_link_prefix.address, sizeof(ipv4addr_t)); inc = lspgen_get_prefix_inc(AF_INET, ctx->ipv4_link_prefix.len); addr += link->link_index * inc; lspgen_store_addr(addr, (uint8_t*)&attr_template.key.prefix.ipv4_prefix.address, sizeof(ipv4addr_t)); attr_template.key.prefix.ipv4_prefix.len = ctx->ipv4_link_prefix.len; attr_template.key.prefix.metric = link->link_metric; attr_template.key.attr_type = ISIS_TLV_EXTD_IPV4_REACH; lsdb_add_node_attr(node, &attr_template); /* Generate an IPv6 prefix for each link */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr(ctx->ipv6_link_prefix.address, IPV6_ADDR_LEN); inc = lspgen_get_prefix_inc(AF_INET6, ctx->ipv6_link_prefix.len); addr += link->link_index * inc; lspgen_store_addr(addr, attr_template.key.prefix.ipv6_prefix.address, IPV6_ADDR_LEN); attr_template.key.prefix.ipv6_prefix.len = ctx->ipv6_link_prefix.len; attr_template.key.prefix.metric = link->link_metric; attr_template.key.attr_type = ISIS_TLV_EXTD_IPV6_REACH; lsdb_add_node_attr(node, &attr_template); } nodes_left--; } while (dict_itor_next(itor)); dict_itor_free(itor); } /* * Walk the graph of the LSDB and add the required node/link attributes for OSPFv2 LSP generation. */ void lspgen_gen_ospf2_attr(struct lsdb_ctx_ *ctx) { struct lsdb_node_ *node; struct lsdb_link_ *link; struct lsdb_attr_ attr_template; dict_itor *itor; __uint128_t addr, inc, ext_addr4, ext_incr4, addr_offset; uint32_t ext_per_node, metric, nodes_left, ext_left; /* * Walk the node DB. */ itor = dict_itor_new(ctx->node_dict); if (!itor) { return; } /* * Node DB empty ? */ if (!dict_itor_first(itor)) { dict_itor_free(itor); LOG_NOARG(ERROR, "Empty LSDB.\n"); return; } /* * For external routes load the first address of the pool. */ ext_addr4 = lspgen_load_addr((uint8_t*)&ctx->ipv4_ext_prefix.address, sizeof(ipv4addr_t)); ext_incr4 = lspgen_get_prefix_inc(AF_INET, ctx->ipv4_ext_prefix.len); nodes_left = ctx->num_nodes; ext_left = ctx->num_ext; do { node = *dict_itor_datum(itor); LOG(LSDB, "Generating node %s (%s) attributes\n", lsdb_format_node(node), lsdb_format_node_id(node->key.node_id)); /* Host name */ if (node->node_name) { lsdb_reset_attr_template(&attr_template); attr_template.key.ordinal = 1; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_OPAQUE_AREA_RI; attr_template.key.attr_cp[2] = OSPF_TLV_HOSTNAME; strncpy(attr_template.key.hostname, node->node_name, sizeof(attr_template.key.hostname)-1); lsdb_add_node_attr(node, &attr_template); } /* IPv4 loopback prefix */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_node_prefix.address, sizeof(ipv4addr_t)); addr += node->node_index; lspgen_store_addr(addr, (uint8_t*)&attr_template.key.prefix.ipv4_prefix.address, sizeof(ipv4addr_t)); attr_template.key.prefix.ipv4_prefix.len = ctx->ipv4_node_prefix.len; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_ROUTER; attr_template.key.attr_cp[2] = OSPF_ROUTER_LSA_LINK_STUB; lsdb_add_node_attr(node, &attr_template); if (!ctx->no_sr) { lsdb_reset_attr_template(&attr_template); lspgen_store_addr(addr, (uint8_t*)&attr_template.key.prefix.ipv4_prefix.address, sizeof(ipv4addr_t)); attr_template.key.prefix.ipv4_prefix.len = ctx->ipv4_node_prefix.len; attr_template.key.prefix.sid = node->node_index; attr_template.key.ordinal = 1; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_OPAQUE_AREA_EP; attr_template.key.attr_cp[2] = OSPF_TLV_EXTENDED_PREFIX; attr_template.key.attr_cp[3] = OSPF_SUBTLV_PREFIX_SID; lsdb_add_node_attr(node, &attr_template); } addr += node->node_index; /* external prefixes */ ext_per_node = ext_left / nodes_left; ext_left -= ext_per_node; while (ext_per_node--) { /* ipv4 external prefix */ lsdb_reset_attr_template(&attr_template); lspgen_store_addr(ext_addr4, (uint8_t*)&attr_template.key.prefix.ipv4_prefix.address, 4); attr_template.key.prefix.ipv4_prefix.len = ctx->ipv4_ext_prefix.len; attr_template.key.prefix.metric = 100; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_EXTERNAL; attr_template.key.start_tlv = true; lsdb_add_node_attr(node, &attr_template); ext_addr4 += ext_incr4; } if (!ctx->no_sr) { /* SR capability */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_node_prefix.address, sizeof(ipv4addr_t)); addr += node->node_index; lspgen_store_addr(addr, attr_template.key.cap.router_id, sizeof(ipv4addr_t)); attr_template.key.cap.srgb_base = ctx->srgb_base; attr_template.key.cap.srgb_range = ctx->srgb_range; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_OPAQUE_AREA_RI; attr_template.key.attr_cp[2] = OSPF_TLV_SID_LABEL_RANGE; attr_template.key.ordinal = 1; lsdb_add_node_attr(node, &attr_template); } /* * Walk all of our neighbors. */ CIRCLEQ_FOREACH(link, &node->link_qhead, link_qnode) { /* * Is this a connector link ? */ if (link->key.remote_node_id[7] == CONNECTOR_MARKER) { lsdb_reset_attr_template(&attr_template); attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_ROUTER; attr_template.key.attr_cp[2] = OSPF_ROUTER_LSA_LINK_PTP; memcpy(attr_template.key.link.remote_node_id, link->key.remote_node_id, 4); memcpy(attr_template.key.link.remote_link_id, link->key.remote_link_id, 4); memcpy(attr_template.key.link.local_link_id, link->key.local_link_id, 4); attr_template.key.link.metric = link->link_metric; lsdb_add_node_attr(node, &attr_template); continue; } /* * Generate a ptp neighbor for each link * TODO Type-2 LSA handling. */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_link_prefix.address, sizeof(ipv4addr_t)); inc = lspgen_get_prefix_inc(AF_INET, ctx->ipv4_link_prefix.len); addr += link->link_index * inc; addr_offset = 0; if (lspgen_load_addr(link->key.remote_node_id, 4) < lspgen_load_addr(link->key.local_node_id, 4)) { addr_offset = 1; } lspgen_store_addr(addr + addr_offset, attr_template.key.link.local_link_id, 4); attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_ROUTER; attr_template.key.attr_cp[2] = OSPF_ROUTER_LSA_LINK_PTP; memcpy(attr_template.key.link.remote_node_id, link->key.remote_node_id, 4); metric = link->link_metric; if (metric > 65535) { metric = 65535; } attr_template.key.link.metric = metric; lsdb_add_node_attr(node, &attr_template); /* Generate an IPv4 prefix for each link */ lsdb_reset_attr_template(&attr_template); lspgen_store_addr(addr, (uint8_t*)&attr_template.key.prefix.ipv4_prefix.address, sizeof(ipv4addr_t)); attr_template.key.prefix.ipv4_prefix.len = ctx->ipv4_link_prefix.len; attr_template.key.prefix.metric = metric; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_ROUTER; attr_template.key.attr_cp[2] = OSPF_ROUTER_LSA_LINK_STUB; lsdb_add_node_attr(node, &attr_template); } nodes_left--; } while (dict_itor_next(itor)); dict_itor_free(itor); } /* * Walk the graph of the LSDB and add the required node/link attributes for OSPFv3 LSP generation. */ void lspgen_gen_ospf3_attr(struct lsdb_ctx_ *ctx) { struct lsdb_node_ *node; struct lsdb_link_ *link; struct lsdb_attr_ attr_template; dict_itor *itor; __uint128_t addr, inc, ext_addr6, ext_incr6; uint32_t ext_per_node, metric, nodes_left, ext_left; /* * Walk the node DB. */ itor = dict_itor_new(ctx->node_dict); if (!itor) { return; } /* * Node DB empty ? */ if (!dict_itor_first(itor)) { dict_itor_free(itor); LOG_NOARG(ERROR, "Empty LSDB.\n"); return; } /* * For external routes load the first address of the pool. */ ext_addr6 = lspgen_load_addr((uint8_t*)&ctx->ipv6_ext_prefix.address, IPV6_ADDR_LEN); ext_incr6 = lspgen_get_prefix_inc(AF_INET6, ctx->ipv6_ext_prefix.len); nodes_left = ctx->num_nodes; ext_left = ctx->num_ext; do { node = *dict_itor_datum(itor); LOG(LSDB, "Generating node %s (%s) attributes\n", lsdb_format_node(node), lsdb_format_node_id(node->key.node_id)); /* Host name */ if (node->node_name) { lsdb_reset_attr_template(&attr_template); attr_template.key.ordinal = 1; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_OPAQUE_AREA_RI; attr_template.key.attr_cp[2] = OSPF_TLV_HOSTNAME; strncpy(attr_template.key.hostname, node->node_name, sizeof(attr_template.key.hostname)-1); lsdb_add_node_attr(node, &attr_template); } /* IPv6 loopback prefix */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv6_node_prefix.address, IPV6_ADDR_LEN); addr += node->node_index; lspgen_store_addr(addr, attr_template.key.prefix.ipv6_prefix.address, IPV6_ADDR_LEN); attr_template.key.prefix.ipv6_prefix.len = ctx->ipv6_node_prefix.len; if (ctx->no_sr) { /* For non-SR use the Intra-Area-Router-LSA */ attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_INTRA_AREA_PREFIX; attr_template.key.attr_cp[2] = OSPF_IA_PREFIX_LSA_PREFIX; lsdb_add_node_attr(node, &attr_template); } else { attr_template.key.ordinal = 1; attr_template.key.prefix.sid = node->node_index; /* For SR use the Extended-Intra-Area-Router-LSA */ attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_E_INTRA_AREA_PREFIX; attr_template.key.attr_cp[2] = OSPF_TLV_INTRA_AREA_PREFIX; attr_template.key.attr_cp[3] = OSPF_SUBTLV_PREFIX_SID; lsdb_add_node_attr(node, &attr_template); } /* external prefixes */ ext_per_node = ext_left / nodes_left; ext_left -= ext_per_node; while (ext_per_node--) { /* ipv6 external prefix */ lsdb_reset_attr_template(&attr_template); lspgen_store_addr(ext_addr6, attr_template.key.prefix.ipv6_prefix.address, IPV6_ADDR_LEN); attr_template.key.prefix.ipv6_prefix.len = ctx->ipv6_ext_prefix.len; attr_template.key.prefix.metric = 100; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_EXTERNAL6; attr_template.key.start_tlv = true; lsdb_add_node_attr(node, &attr_template); ext_addr6 += ext_incr6; } if (!ctx->no_sr) { /* SR capability */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_node_prefix.address, sizeof(ipv4addr_t)); addr += node->node_index; lspgen_store_addr(addr, attr_template.key.cap.router_id, sizeof(ipv4addr_t)); attr_template.key.cap.srgb_base = ctx->srgb_base; attr_template.key.cap.srgb_range = ctx->srgb_range; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_OPAQUE_AREA_RI; attr_template.key.attr_cp[2] = OSPF_TLV_SID_LABEL_RANGE; //attr_template.key.ordinal = 1; lsdb_add_node_attr(node, &attr_template); } /* * Walk all of our neighbors. */ CIRCLEQ_FOREACH(link, &node->link_qhead, link_qnode) { /* * Is this a connector link ? */ if (link->key.remote_node_id[7] == CONNECTOR_MARKER) { lsdb_reset_attr_template(&attr_template); attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_ROUTER; attr_template.key.attr_cp[2] = OSPF_ROUTER_LSA_LINK_PTP; memcpy(attr_template.key.link.remote_node_id, link->key.remote_node_id, 4); memcpy(attr_template.key.link.remote_link_id, link->key.remote_link_id, 4); memcpy(attr_template.key.link.local_link_id, link->key.local_link_id, 4); attr_template.key.link.metric = link->link_metric; lsdb_add_node_attr(node, &attr_template); continue; } /* * Generate a ptp neighbor for each link * TODO Type-2 LSA handling. */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr((uint8_t*)&ctx->ipv4_link_prefix.address, sizeof(ipv4addr_t)); inc = lspgen_get_prefix_inc(AF_INET, ctx->ipv4_link_prefix.len); addr += link->link_index * inc; if (lspgen_load_addr(link->key.remote_node_id, 4) < lspgen_load_addr(link->key.local_node_id, 4)) { lspgen_store_addr(addr, attr_template.key.link.remote_link_id, 4); lspgen_store_addr(addr+1, attr_template.key.link.local_link_id, 4); } else { lspgen_store_addr(addr+1, attr_template.key.link.remote_link_id, 4); lspgen_store_addr(addr, attr_template.key.link.local_link_id, 4); } attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_ROUTER; attr_template.key.attr_cp[2] = OSPF_ROUTER_LSA_LINK_PTP; memcpy(attr_template.key.link.remote_node_id, link->key.remote_node_id, 4); metric = link->link_metric; if (metric > 65535) { metric = 65535; } attr_template.key.link.metric = metric; attr_template.link_state_id = 1; lsdb_add_node_attr(node, &attr_template); /* Generate an IPv6 prefix for each link */ lsdb_reset_attr_template(&attr_template); addr = lspgen_load_addr(ctx->ipv6_link_prefix.address, IPV6_ADDR_LEN); inc = lspgen_get_prefix_inc(AF_INET6, ctx->ipv6_link_prefix.len); addr += link->link_index * inc; lspgen_store_addr(addr, attr_template.key.prefix.ipv6_prefix.address, IPV6_ADDR_LEN); attr_template.key.prefix.ipv6_prefix.len = ctx->ipv6_link_prefix.len; metric = link->link_metric; if (metric > 65535) { metric = 65535; } attr_template.key.prefix.metric = metric; attr_template.key.attr_cp[0] = OSPF_MSG_LSUPDATE; attr_template.key.attr_cp[1] = OSPF_LSA_INTRA_AREA_PREFIX; attr_template.key.attr_cp[2] = OSPF_IA_PREFIX_LSA_PREFIX; lsdb_add_node_attr(node, &attr_template); } nodes_left--; } while (dict_itor_next(itor)); dict_itor_free(itor); } void lspgen_init_ctx(struct lsdb_ctx_ *ctx) { CIRCLEQ_INIT(&ctx->packet_change_qhead); timer_init_root(&ctx->timer_root); ctx->protocol_id = PROTO_ISIS; ctx->num_nodes = 10; /* Number of nodes */ /* IS-IS area */ ctx->area[0].address[0] = 0x49; ctx->area[0].address[1] = 0x00; ctx->area[0].address[2] = 0x01; ctx->area[0].len = 24; ctx->topology_id.level = 1; ctx->sequence = 1; ctx->authentication_type = ISIS_AUTH_NONE; ctx->seed = 0x74522142; /* RtB! */ ctx->lsp_lifetime = 65535; ctx->link_multiplier = 1; /* ipv4 link prefix */ inet_pton(AF_INET, "172.16.0.0", &ctx->ipv4_link_prefix.address); ctx->ipv4_link_prefix.len = 31; /* ipv4 loopback prefix */ inet_pton(AF_INET, "192.168.0.0", &ctx->ipv4_node_prefix.address); ctx->ipv4_node_prefix.len = 32; /* ipv4 external prefix */ inet_pton(AF_INET, "10.0.0.0", &ctx->ipv4_ext_prefix.address); ctx->ipv4_ext_prefix.len = 28; /* ipv6 link prefix */ inet_pton(AF_INET6, "fc00::ac10:0", &ctx->ipv6_link_prefix.address); ctx->ipv6_link_prefix.len = 127; /* ipv6 loopback prefix */ inet_pton(AF_INET6, "fc00::c0a8:0", &ctx->ipv6_node_prefix.address); ctx->ipv6_node_prefix.len = 128; /* ipv6 external prefix */ inet_pton(AF_INET6, "fc00::0a00:0", &ctx->ipv6_ext_prefix.address); ctx->ipv6_ext_prefix.len = 124; /* label */ ctx->srgb_base = 10000; ctx->srgb_range = 2000; /* MRT must haves */ time(&ctx->now); } struct ipv4_prefix_ * lspgen_compute_end_prefix4(struct ipv4_prefix_ *start_prefix, unsigned int num_prefixes) { __uint128_t addr, prefix_inc; static struct ipv4_prefix_ end_prefix; end_prefix = *start_prefix; if (!num_prefixes) { return &end_prefix; } prefix_inc = lspgen_get_prefix_inc(AF_INET, start_prefix->len); addr = lspgen_load_addr((uint8_t *)&start_prefix->address, 4); addr += prefix_inc * (num_prefixes-1); lspgen_store_addr(addr, (uint8_t *)&end_prefix.address, 4); return &end_prefix; } struct ipv6_prefix_ * lspgen_compute_end_prefix6(struct ipv6_prefix_ *start_prefix, unsigned int num_prefixes) { __uint128_t addr, prefix_inc; static struct ipv6_prefix_ end_prefix; end_prefix = *start_prefix; if (!num_prefixes) { return &end_prefix; } prefix_inc = lspgen_get_prefix_inc(AF_INET6, start_prefix->len); addr = lspgen_load_addr((uint8_t *)&start_prefix->address, IPV6_ADDR_LEN); addr += prefix_inc * (num_prefixes-1); lspgen_store_addr(addr, (uint8_t *)&end_prefix.address, IPV6_ADDR_LEN); return &end_prefix; } void lspgen_log_ctx(struct lsdb_ctx_ *ctx) { struct ipv4_prefix_ *end_prefix4; struct ipv6_prefix_ *end_prefix6; uint32_t idx; LOG_NOARG(NORMAL, "LSP generation parameters\n"); LOG(NORMAL, " Protocol %s\n", lsdb_format_proto(ctx)); /* * No Area specified ? Show at least the default. */ if (!ctx->num_area) { ctx->num_area = 1; } if (ctx->protocol_id == PROTO_ISIS) { for (idx = 0; idx < ctx->num_area; idx++) { LOG(NORMAL, " Area %s\n", format_iso_prefix(&ctx->area[idx])); } LOG(NORMAL, " Level %u\n", ctx->topology_id.level); } else if (ctx->protocol_id == PROTO_OSPF2 || ctx->protocol_id == PROTO_OSPF3) { LOG(NORMAL, " Area %s\n", format_ipv4_address(&ctx->topology_id.area)); } LOG(NORMAL, " Sequence 0x%x, lsp-lifetime %u%s\n", ctx->sequence, ctx->lsp_lifetime, ctx->purge ? ", Purge" : ""); if (ctx->authentication_key) { LOG(NORMAL, " Authentication-key %s, Authentication-type %s\n", ctx->authentication_key, val2key(auth_type_names, ctx->authentication_type)); } if (ctx->link_multiplier) { LOG(NORMAL, " Link-multiplier %u\n", ctx->link_multiplier); } if (!ctx->no_ipv4) { end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_node_prefix, ctx->num_nodes); LOG(NORMAL, " IPv4 Node Base Prefix %s, End Prefix %s, %u prefixes\n", format_ipv4_prefix(&ctx->ipv4_node_prefix), format_ipv4_prefix(end_prefix4), ctx->num_nodes); end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_link_prefix, ctx->num_nodes*2); LOG(NORMAL, " IPv4 Link Base Prefix %s, End Prefix %s, %u prefixes\n", format_ipv4_prefix(&ctx->ipv4_link_prefix), format_ipv4_prefix(end_prefix4), ctx->num_nodes*2); end_prefix4 = lspgen_compute_end_prefix4(&ctx->ipv4_ext_prefix, ctx->num_ext); LOG(NORMAL, " IPv4 External Base Prefix %s, End Prefix %s, %u prefixes\n", format_ipv4_prefix(&ctx->ipv4_ext_prefix), format_ipv4_prefix(end_prefix4), ctx->num_ext); } if (!ctx->no_ipv6) { end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_node_prefix, ctx->num_nodes); LOG(NORMAL, " IPv6 Node Base Prefix %s, End Prefix %s, %u prefixes\n", format_ipv6_prefix(&ctx->ipv6_node_prefix), format_ipv6_prefix(end_prefix6), ctx->num_nodes); end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_link_prefix, ctx->num_nodes*2); LOG(NORMAL, " IPv6 Link Base Prefix %s, End Prefix %s, %u prefixes\n", format_ipv6_prefix(&ctx->ipv6_link_prefix), format_ipv6_prefix(end_prefix6), ctx->num_nodes*2); end_prefix6 = lspgen_compute_end_prefix6(&ctx->ipv6_ext_prefix, ctx->num_ext); LOG(NORMAL, " IPv6 External Base Prefix %s, End Prefix %s, %u prefixes\n", format_ipv6_prefix(&ctx->ipv6_ext_prefix), format_ipv6_prefix(end_prefix6), ctx->num_ext); } if (!ctx->no_sr) { LOG(NORMAL, " SRGB base %u, range %u\n", ctx->srgb_base, ctx->srgb_range); } } /* * Compute the SRGB range to be large enough to hold indexes for ipv4 and ipv6 SIDs. */ void lspgen_compute_srgb_range(struct lsdb_ctx_ *ctx) { unsigned int range; if (ctx->no_ipv4 && ctx->no_ipv6) { ctx->srgb_range = 0; return; } range = ctx->num_nodes * 2; if (ctx->no_ipv4) { range = ctx->num_nodes; } if (ctx->no_ipv6) { range = ctx->num_nodes; } ctx->srgb_range = range; } /* * Add a connector string in the form of an * iso remote_id xxxx.xxxx.xxxx.xx or * ipv4 remote_id:local_ip xxx.xxx.xxx.xxx:yyy.yyy.yyy.yyy */ void lspgen_add_connector(struct lsdb_ctx_ *ctx, char *conn_src) { long long int iso_node_id; /* Used for ISO */ uint32_t remote_id, local_ip; /* Used for IP */ char conn_dst[32]; size_t src_len, dst_len; uint32_t src, dst, shift; bool colon_found; if (ctx->num_connector >= (sizeof(ctx->connector)/sizeof(ctx->connector[0]))) { LOG(ERROR, "Maximum connector limit (%u) exceeded\n", ctx->num_connector); return; } src_len = strlen(conn_src); if (ctx->protocol_id == PROTO_OSPF2 || ctx->protocol_id == PROTO_OSPF3) { char *tok; /* * First check if this is an remote-id:local_ip format. */ colon_found = false; for (src = 0; src < src_len; src++) { if (conn_src[src] == ':') { colon_found = true; break; } } if (!colon_found) { LOG(ERROR, "Connector '%s' not in remote_id:local_ip format\n", conn_src); return; } remote_id = 0; local_ip = 0; tok = strtok(conn_src, ":"); if (tok && scan_ipv4_address(conn_src, &remote_id)) { tok = strtok(NULL, ":"); if (tok && scan_ipv4_address(tok, &local_ip)) { write_le_uint(ctx->connector[ctx->num_connector].remote_node_id, 4, remote_id); write_le_uint(ctx->connector[ctx->num_connector].local_link_id, 4, local_ip); } } LOG(NORMAL, "Add connector to %s:%s\n", format_ipv4_address(&remote_id), format_ipv4_address(&local_ip)); ctx->num_connector++; } else if (ctx->protocol_id == PROTO_ISIS) { /* ISO node-ID format */ memset(conn_dst, 0, sizeof(conn_dst)); conn_dst[0] = '0'; conn_dst[1] = 'x'; dst = 2; for (src = 0; src < src_len; src++) { if (dst >= sizeof(conn_dst)-2) { break; } if (conn_src[src] >= 'a' && conn_src[src] <= 'f') { conn_dst[dst++] = conn_src[src]; continue; } if (conn_src[src] >= '0' && conn_src[src] <= '9') { conn_dst[dst++] = conn_src[src]; continue; } } iso_node_id = strtoll(conn_dst, NULL, 16); dst_len = strlen(&conn_dst[2]); if (dst_len > 16) { LOG(ERROR, "illegal connector length %s\n", conn_src); return; } shift = (16 - dst_len) * 4; write_be_uint(ctx->connector[ctx->num_connector].remote_node_id, 8, iso_node_id << shift); LOG(NORMAL, "Add connector to %s\n", lsdb_format_node_id(ctx->connector[ctx->num_connector].remote_node_id)); ctx->num_connector++; } } /* Get out of event loop */ void lspgen_quit_loop (void) { loop_running = false; } void lspgen_sig_handler (int signum) { LOG(NORMAL, "Received %s signal\n", strsignal(signum)); switch (signum) { case SIGINT: lspgen_quit_loop(); break; default: break; } } int main(int argc, char *argv[]) { int opt, idx, res; struct lsdb_ctx_ *ctx; /* * Init default options. */ log_id[NORMAL].enable = true; log_id[ERROR].enable = true; ctx = lsdb_alloc_ctx("default"); if (!ctx) { exit(EXIT_FAILURE); } lspgen_init_ctx(ctx); /* * Parse options. */ idx = 0; while ((opt = getopt_long(argc, argv, "vha:c:C:e:f:g:Gl:L:m:M:n:K:N:p:P:q:Qr:s:S:t:T:u:V:w:x:X:yzZ", long_options, &idx)) != -1) { switch (opt) { case 'v': lspgen_print_version(); exit(0); case 'G': ctx->purge = true; break; case 'P': ctx->protocol_id = key2val(proto_names, optarg); if (ctx->protocol_id == PROTO_OSPF2) { ctx->no_ipv6 = true; } if (ctx->protocol_id == PROTO_OSPF3) { ctx->no_ipv4 = true; } if (ctx->protocol_id == PROTO_OSPF2 || ctx->protocol_id == PROTO_OSPF3) { ctx->topology_id.area = 0; /* reset area */ ctx->sequence = 0x80000001; ctx->lsp_lifetime = 3600; } break; case 'r': if (ctx->config_filename) { ctx->config_write = false; free(ctx->config_filename); } ctx->config_filename = strdup(optarg); ctx->config_read = true; break; case 'w': if (ctx->config_filename) { ctx->config_read = false; free(ctx->config_filename); } ctx->config_filename = strdup(optarg); ctx->config_write = true; break; case 'a': if (ctx->protocol_id == PROTO_ISIS) { if (ctx->num_area < 3) { scan_iso_prefix(optarg, &ctx->area[ctx->num_area++]); } } else { /* ospf2 and ospf3 */ scan_ipv4_address(optarg, &ctx->topology_id.area); } break; case 'M': ctx->lsp_lifetime = atoi(optarg); if (ctx->lsp_lifetime < 120) { ctx->lsp_lifetime = 120; LOG(ERROR, "Set lsp-lifetime to min %us\n", ctx->lsp_lifetime); } break; case 'y': /* no-sr */ ctx->no_sr = true; break; case 'z': /* no-ipv4 */ ctx->no_ipv4 = true; break; case 'Z': /* no-ipv6 */ ctx->no_ipv6 = true; break; case 'T': /* authentication-type */ ctx->authentication_type = get_authentication_type(optarg); break; case 't': /* logging */ log_enable(optarg); break; case 'u': ctx->link_multiplier = strtol(optarg, NULL, 10); if (ctx->link_multiplier > 150) { ctx->link_multiplier = 150; LOG(ERROR, "Set link-multiplier to maximum %u\n", ctx->link_multiplier); } break; case 'c': ctx->num_nodes = strtol(optarg, NULL, 10); if (ctx->num_nodes < 5) { ctx->num_nodes = 5; LOG(ERROR, "Set node count to minimal %u\n", ctx->num_nodes); } lspgen_compute_srgb_range(ctx); break; case 'C': /* connector */ lspgen_add_connector(ctx, optarg); break; case 'n': /* base prefix for ipv4 loopbacks */ scan_ipv4_prefix(optarg, &ctx->ipv4_node_prefix); break; case 'm': ctx->mrt_filename = strdup(optarg); break; case 'N': /* base prefix for ipv6 loopbacks */ scan_ipv6_prefix(optarg, &ctx->ipv6_node_prefix); break; case 'l': /* base prefix for ipv4 interface */ scan_ipv4_prefix(optarg, &ctx->ipv4_link_prefix); break; case 'L': /* base prefix for ipv6 interfaces */ scan_ipv6_prefix(optarg, &ctx->ipv6_link_prefix); break; case 'x': /* base prefix for ipv4 externals */ scan_ipv4_prefix(optarg, &ctx->ipv4_ext_prefix); break; case 'X': /* base prefix for ipv6 externals */ scan_ipv6_prefix(optarg, &ctx->ipv6_ext_prefix); break; case 'e': ctx->num_ext = strtol(optarg, NULL, 10); break; case 'K': /* authentication-key */ ctx->authentication_key = strdup(optarg); if (ctx->authentication_type == ISIS_AUTH_NONE) { ctx->authentication_type = ISIS_AUTH_SIMPLE; } break; case 'g': /* export LSDB as graphviz file and render into a SVG */ ctx->graphviz_filename = strdup(optarg); break; case 'p': /* export LSDB as pcap file */ ctx->pcap_filename = strdup(optarg); break; case 'f': /* export traffic stream file */ ctx->stream_filename = strdup(optarg); break; case 'q': /* sequence */ ctx->sequence = strtol(optarg, NULL, 0); if (ctx->sequence == 0) { if (ctx->protocol_id == PROTO_ISIS) { ctx->sequence = 1; } else { ctx->sequence = 0x80000001; } } break; case 'Q': /* Quit event loop after draining LSDB once */ ctx->quit_loop = true; break; case 's': /* seed value such that random graph generation becomes deterministic */ ctx->seed = strtol(optarg, NULL, 0); break; case 'S': /* open control socket to BNG Blaster */ ctx->ctrl_socket_path = strdup(optarg); break; case 'V': /* level */ if (ctx->protocol_id != PROTO_ISIS) { LOG(ERROR, "Level may not be set for protocol %s\n", lsdb_format_proto(ctx)); exit(EXIT_FAILURE); } ctx->topology_id.level = atoi(optarg); if (ctx->topology_id.level < 1 || ctx->topology_id.level > 2) { LOG(ERROR, "Level %u is not supported\n", ctx->topology_id.level); exit(EXIT_FAILURE); } break; case 'h': /* fall through */ default: lspgen_print_usage(); exit(EXIT_FAILURE); } } /* * Display options. */ lspgen_log_ctx(ctx); /* * Read the link-state database from a config file. */ if (ctx->config_read && ctx->config_filename) { lspgen_read_config(ctx); } else { /* * Generate a random graph. */ lsdb_init_graph(ctx); /* * Generate the node and link attributes. */ if (ctx->protocol_id == PROTO_ISIS) { lspgen_gen_isis_attr(ctx); } else if (ctx->protocol_id == PROTO_OSPF2) { lspgen_gen_ospf2_attr(ctx); } else if (ctx->protocol_id == PROTO_OSPF3) { lspgen_gen_ospf3_attr(ctx); } } /* * Bump the sequence numbers if there is a cache file. */ lspgen_read_seq_cache(ctx); /* * Serialize the Link-State packets. */ lspgen_gen_packet(ctx); /* * Dump the lsdb into a PCAP file. */ if (ctx->pcap_filename) { lspgen_dump_pcap(ctx); } /* * Dump the lsdb into a MRT file. */ if (ctx->mrt_filename) { lspgen_dump_mrt(ctx); } /* * Dump the lsdb into a graphviz file. */ if (ctx->graphviz_filename) { char cmd[256]; lsdb_dump_graphviz(ctx); snprintf(cmd, sizeof(cmd), "dot -Tsvg %s.dot -o %s.svg", ctx->graphviz_filename, ctx->graphviz_filename); res = system(cmd); /* convert to svg */ if (!res) { LOG(NORMAL, "Converted graphviz file %s.dot into %s.svg\n", ctx->graphviz_filename, ctx->graphviz_filename); } else { LOG(ERROR, "Could not convert graphviz file %s.dot into %s.svg\n", ctx->graphviz_filename, ctx->graphviz_filename); } } /* * Generate a traffic stream file description. */ if (ctx->stream_filename) { lspgen_dump_stream(ctx); } /* * Generate a config file from the link-state database. */ if (ctx->config_write && ctx->config_filename) { lspgen_write_config(ctx); } /* * Open Control socket. */ if (ctx->ctrl_socket_path) { ctx->ctrl_io_buf.start_idx = 0; ctx->ctrl_io_buf.idx = 0; ctx->ctrl_io_buf.size = CTRL_SOCKET_BUFSIZE; ctx->ctrl_io_buf.data = malloc(ctx->ctrl_io_buf.size); if (!ctx->ctrl_io_buf.data) { goto EXIT; } timer_add_periodic(&ctx->timer_root, &ctx->ctrl_socket_connect_timer, "connect", 1, 0, ctx, &lspgen_ctrl_connect_cb); /* * Wakeup at least once a second doing nothing, * such that we do not sleep while user tries to quit the timer loop. */ timer_add_periodic(&ctx->timer_root, &ctx->ctrl_socket_wakeup_timer, "wakeup", 1, 0, ctx, &lspgen_ctrl_wakeup_cb); /* * Block SIGPIPE. This happens when a session disconnects. * EPIPE gets handled when writing the buffer. */ signal(SIGPIPE, SIG_IGN); /* * Keep running until Ctrl-C is hit. */ signal(SIGINT, lspgen_sig_handler); while (loop_running) { timer_walk(&ctx->timer_root); } } /* * Write the sequence cache file. */ lspgen_write_seq_cache(ctx); /* * Flush and close all we have. */ EXIT: lsdb_delete_ctx(ctx); return 0; }