isisd: Fix Attach-bit processing

The purpose of the Attach-bit is to accomplish inter-area routing.  In other
venders, the Attached-bit is automatically set when a router is configured
as a L1|L2 router and has two adjacencies.  When a L1 router receives a LSP
with the Attached-bit set it is supposed to create a default route pointing
toward the neighbor to provide a default path out of the L1 area.

ISIS implementation has been fixed to support the above definition:
Setting the Attach-bit is now the default behavior and we allow the user to
turn it off.

We will only set the Default Attach-bit when creating a L1 LSP, if we are
a L1|L2 router and have a L2 adjacency up.

When a L1 router receives a LSP with the Attach-bit set, we will create a
default route pointing to the L1|L2 router as the nexthop.

The default route will be removed if the LSP is received with the Attach-bit
cleared.

Signed-off-by: Lynne Morrison <lynne@voltanet.io>
This commit is contained in:
lynne 2020-12-24 13:29:42 -05:00
parent 44ba9e779d
commit f3abc412a5
11 changed files with 238 additions and 40 deletions

View File

@ -593,6 +593,10 @@ void cli_show_isis_overload(struct vty *vty, struct lyd_node *dnode,
vty_out(vty, " set-overload-bit\n");
}
#if CONFDATE > 20220119
CPP_NOTICE(
"Use of `set-attached-bit` is deprecated please use attached-bit [send | receive]")
#endif
/*
* XPath: /frr-isisd:isis/instance/attached
*/
@ -600,18 +604,57 @@ DEFPY_YANG(set_attached_bit, set_attached_bit_cmd, "[no] set-attached-bit",
"Reset attached bit\n"
"Set attached bit to identify as L1/L2 router for inter-area traffic\n")
{
nb_cli_enqueue_change(vty, "./attached", NB_OP_MODIFY,
vty_out(vty,
"set-attached-bit deprecated please use attached-bit [send | receive]\n");
return CMD_SUCCESS;
}
/*
* XPath: /frr-isisd:isis/instance/attach-send
*/
DEFPY_YANG(attached_bit_send, attached_bit_send_cmd, "[no] attached-bit send",
"Reset attached bit\n"
"Set attached bit for inter-area traffic\n"
"Set attached bit in LSP sent to L1 router\n")
{
nb_cli_enqueue_change(vty, "./attach-send", NB_OP_MODIFY,
no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
}
void cli_show_isis_attached(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
void cli_show_isis_attached_send(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
if (!yang_dnode_get_bool(dnode, NULL))
vty_out(vty, " no");
vty_out(vty, " set-attached-bit\n");
vty_out(vty, " attached-bit send\n");
}
/*
* XPath: /frr-isisd:isis/instance/attach-receive-ignore
*/
DEFPY_YANG(
attached_bit_receive_ignore, attached_bit_receive_ignore_cmd,
"[no] attached-bit receive ignore",
"Reset attached bit\n"
"Set attach bit for inter-area traffic\n"
"If LSP received with attached bit set, create default route to neighbor\n"
"Do not process attached bit\n")
{
nb_cli_enqueue_change(vty, "./attach-receive-ignore", NB_OP_MODIFY,
no ? "false" : "true");
return nb_cli_apply_changes(vty, NULL);
}
void cli_show_isis_attached_receive(struct vty *vty, struct lyd_node *dnode,
bool show_defaults)
{
if (!yang_dnode_get_bool(dnode, NULL))
vty_out(vty, " no");
vty_out(vty, " attached-bit receive ignore\n");
}
/*
@ -3206,6 +3249,8 @@ void isis_cli_init(void)
install_element(ISIS_NODE, &set_overload_bit_cmd);
install_element(ISIS_NODE, &set_attached_bit_cmd);
install_element(ISIS_NODE, &attached_bit_send_cmd);
install_element(ISIS_NODE, &attached_bit_receive_ignore_cmd);
install_element(ISIS_NODE, &metric_style_cmd);
install_element(ISIS_NODE, &no_metric_style_cmd);

View File

@ -140,7 +140,7 @@
* LSP bit masks
*/
#define LSPBIT_P 0x80
#define LSPBIT_ATT 0x78
#define LSPBIT_ATT 0x08 /* only use the Default ATT bit */
#define LSPBIT_OL 0x04
#define LSPBIT_IST 0x03
@ -158,7 +158,6 @@
#define ISIS_MASK_LSP_ATT_ERROR_BIT(x) ((x)&0x40)
#define ISIS_MASK_LSP_ATT_EXPENSE_BIT(x) ((x)&0x20)
#define ISIS_MASK_LSP_ATT_DELAY_BIT(x) ((x)&0x10)
#define ISIS_MASK_LSP_ATT_DEFAULT_BIT(x) ((x)&0x8)
#define LLC_LEN 3

View File

@ -399,7 +399,50 @@ static void lsp_seqno_update(struct isis_lsp *lsp0)
return;
}
static uint8_t lsp_bits_generate(int level, int overload_bit, int attached_bit)
static bool isis_level2_adj_up(struct isis_area *curr_area)
{
struct listnode *node, *cnode;
struct isis_circuit *circuit;
struct list *adjdb;
struct isis_adjacency *adj;
struct isis *isis = curr_area->isis;
struct isis_area *area;
/* lookup for a Level2 adjacency up in another area */
for (ALL_LIST_ELEMENTS_RO(isis->area_list, node, area)) {
if (area->area_tag
&& strcmp(area->area_tag, curr_area->area_tag) == 0)
continue;
for (ALL_LIST_ELEMENTS_RO(area->circuit_list, cnode, circuit)) {
if (circuit->circ_type == CIRCUIT_T_BROADCAST) {
adjdb = circuit->u.bc.adjdb[1];
if (adjdb && adjdb->count) {
for (ALL_LIST_ELEMENTS_RO(adjdb, node,
adj))
if ((adj->level
== ISIS_ADJ_LEVEL2
|| adj->level
== ISIS_ADJ_LEVEL1AND2)
&& adj->adj_state
== ISIS_ADJ_UP)
return true;
}
} else if (circuit->circ_type == CIRCUIT_T_P2P
&& circuit->u.p2p.neighbor) {
adj = circuit->u.p2p.neighbor;
if ((adj->level == ISIS_ADJ_LEVEL2
|| adj->level == ISIS_ADJ_LEVEL1AND2)
&& adj->adj_state == ISIS_ADJ_UP)
return true;
}
}
}
return false;
}
static uint8_t lsp_bits_generate(int level, int overload_bit, int attached_bit,
struct isis_area *area)
{
uint8_t lsp_bits = 0;
if (level == IS_LEVEL_1)
@ -408,8 +451,13 @@ static uint8_t lsp_bits_generate(int level, int overload_bit, int attached_bit)
lsp_bits = IS_LEVEL_1_AND_2;
if (overload_bit)
lsp_bits |= overload_bit;
if (attached_bit)
lsp_bits |= attached_bit;
/* only set the attach bit if we are a level-1-2 router and this is
* a level-1 LSP and we have a level-2 adjacency up from another area
*/
if (area->is_type == IS_LEVEL_1_AND_2 && level == IS_LEVEL_1
&& attached_bit && isis_level2_adj_up(area))
lsp_bits |= LSPBIT_ATT;
return lsp_bits;
}
@ -632,13 +680,13 @@ static const char *lsp_bits2string(uint8_t lsp_bits, char *buf, size_t buf_size)
return " error";
/* we only focus on the default metric */
pos += sprintf(pos, "%d/",
ISIS_MASK_LSP_ATT_DEFAULT_BIT(lsp_bits) ? 1 : 0);
pos += snprintf(pos, buf_size, "%d/",
ISIS_MASK_LSP_ATT_BITS(lsp_bits) ? 1 : 0);
pos += sprintf(pos, "%d/",
ISIS_MASK_LSP_PARTITION_BIT(lsp_bits) ? 1 : 0);
pos += snprintf(pos, buf_size, "%d/",
ISIS_MASK_LSP_PARTITION_BIT(lsp_bits) ? 1 : 0);
sprintf(pos, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0);
snprintf(pos, buf_size, "%d", ISIS_MASK_LSP_OL_BIT(lsp_bits) ? 1 : 0);
return buf;
}
@ -838,7 +886,7 @@ static struct isis_lsp *lsp_next_frag(uint8_t frag_num, struct isis_lsp *lsp0,
lsp = lsp_new(area, frag_id, lsp0->hdr.rem_lifetime, 0,
lsp_bits_generate(level, area->overload_bit,
area->attached_bit),
area->attached_bit_send, area),
0, lsp0, level);
lsp->own_lsp = 1;
lsp_insert(&area->lspdb[level - 1], lsp);
@ -864,7 +912,7 @@ static void lsp_build(struct isis_lsp *lsp, struct isis_area *area)
area->area_tag, level);
lsp->hdr.lsp_bits = lsp_bits_generate(level, area->overload_bit,
area->attached_bit);
area->attached_bit_send, area);
lsp_add_auth(lsp);
@ -1223,10 +1271,10 @@ int lsp_generate(struct isis_area *area, int level)
oldlsp->hdr.lsp_id);
}
rem_lifetime = lsp_rem_lifetime(area, level);
newlsp =
lsp_new(area, lspid, rem_lifetime, seq_num,
area->is_type | area->overload_bit | area->attached_bit,
0, NULL, level);
newlsp = lsp_new(area, lspid, rem_lifetime, seq_num,
lsp_bits_generate(area->is_type, area->overload_bit,
area->attached_bit_send, area),
0, NULL, level);
newlsp->area = area;
newlsp->own_lsp = 1;
@ -1310,8 +1358,9 @@ static int lsp_regenerate(struct isis_area *area, int level)
continue;
}
frag->hdr.lsp_bits = lsp_bits_generate(
level, area->overload_bit, area->attached_bit);
frag->hdr.lsp_bits =
lsp_bits_generate(level, area->overload_bit,
area->attached_bit_send, area);
/* Set the lifetime values of all the fragments to the same
* value,
* so that no fragment expires before the lsp is refreshed.
@ -1518,8 +1567,8 @@ static void lsp_build_pseudo(struct isis_lsp *lsp, struct isis_circuit *circuit,
lsp->level = level;
/* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */
lsp->hdr.lsp_bits =
lsp_bits_generate(level, 0, circuit->area->attached_bit);
lsp->hdr.lsp_bits = lsp_bits_generate(
level, 0, circuit->area->attached_bit_send, area);
/*
* add self to IS neighbours
@ -1617,8 +1666,10 @@ int lsp_generate_pseudo(struct isis_circuit *circuit, int level)
rem_lifetime = lsp_rem_lifetime(circuit->area, level);
/* RFC3787 section 4 SHOULD not set overload bit in pseudo LSPs */
lsp = lsp_new(circuit->area, lsp_id, rem_lifetime, 1,
circuit->area->is_type | circuit->area->attached_bit, 0,
NULL, level);
lsp_bits_generate(circuit->area->is_type, 0,
circuit->area->attached_bit_send,
circuit->area),
0, NULL, level);
lsp->area = circuit->area;
lsp_build_pseudo(lsp, circuit, level);

View File

@ -59,10 +59,23 @@ const struct frr_yang_module_info frr_isisd_info = {
.modify = isis_instance_dynamic_hostname_modify,
},
},
{
.xpath = "/frr-isisd:isis/instance/attach-send",
.cbs = {
.cli_show = cli_show_isis_attached_send,
.modify = isis_instance_attached_send_modify,
},
},
{
.xpath = "/frr-isisd:isis/instance/attach-receive-ignore",
.cbs = {
.cli_show = cli_show_isis_attached_receive,
.modify = isis_instance_attached_receive_modify,
},
},
{
.xpath = "/frr-isisd:isis/instance/attached",
.cbs = {
.cli_show = cli_show_isis_attached,
.modify = isis_instance_attached_modify,
},
},

View File

@ -34,6 +34,8 @@ int isis_instance_is_type_modify(struct nb_cb_modify_args *args);
int isis_instance_area_address_create(struct nb_cb_create_args *args);
int isis_instance_area_address_destroy(struct nb_cb_destroy_args *args);
int isis_instance_dynamic_hostname_modify(struct nb_cb_modify_args *args);
int isis_instance_attached_send_modify(struct nb_cb_modify_args *args);
int isis_instance_attached_receive_modify(struct nb_cb_modify_args *args);
int isis_instance_attached_modify(struct nb_cb_modify_args *args);
int isis_instance_overload_modify(struct nb_cb_modify_args *args);
int isis_instance_metric_style_modify(struct nb_cb_modify_args *args);
@ -424,8 +426,10 @@ void cli_show_isis_is_type(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_dynamic_hostname(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_attached(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_attached_send(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_attached_receive(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_overload(struct vty *vty, struct lyd_node *dnode,
bool show_defaults);
void cli_show_isis_metric_style(struct vty *vty, struct lyd_node *dnode,

View File

@ -272,9 +272,9 @@ int isis_instance_dynamic_hostname_modify(struct nb_cb_modify_args *args)
}
/*
* XPath: /frr-isisd:isis/instance/attached
* XPath: /frr-isisd:isis/instance/attach-send
*/
int isis_instance_attached_modify(struct nb_cb_modify_args *args)
int isis_instance_attached_send_modify(struct nb_cb_modify_args *args)
{
struct isis_area *area;
bool attached;
@ -284,11 +284,37 @@ int isis_instance_attached_modify(struct nb_cb_modify_args *args)
area = nb_running_get_entry(args->dnode, NULL, true);
attached = yang_dnode_get_bool(args->dnode, NULL);
isis_area_attached_bit_set(area, attached);
isis_area_attached_bit_send_set(area, attached);
return NB_OK;
}
/*
* XPath: /frr-isisd:isis/instance/attach-receive-ignore
*/
int isis_instance_attached_receive_modify(struct nb_cb_modify_args *args)
{
struct isis_area *area;
bool attached;
if (args->event != NB_EV_APPLY)
return NB_OK;
area = nb_running_get_entry(args->dnode, NULL, true);
attached = yang_dnode_get_bool(args->dnode, NULL);
isis_area_attached_bit_receive_set(area, attached);
return NB_OK;
}
/*
* XPath: /frr-isisd:isis/instance/attached
*/
int isis_instance_attached_modify(struct nb_cb_modify_args *args)
{
return NB_OK;
}
/*
* XPath: /frr-isisd:isis/instance/overload
*/

View File

@ -729,8 +729,8 @@ static int process_hello(uint8_t pdu_type, struct isis_circuit *circuit,
if (!memcmp(iih.sys_id, circuit->isis->sysid, ISIS_SYS_ID_LEN)) {
zlog_warn(
"ISIS-Adj (%s): Received IIH with own sysid - discard",
circuit->area->area_tag);
"ISIS-Adj (%s): Received IIH with own sysid on %s - discard",
circuit->area->area_tag, circuit->interface->name);
circuit->rej_adjacencies++;
#ifndef FABRICD
isis_notif_reject_adjacency(

View File

@ -1046,6 +1046,32 @@ lspfragloop:
}
end:
/* if attach bit set and we are a level-1 router
* and attach-bit-rcv-ignore is not configured
* add a default route toward this neighbor
*/
if ((lsp->hdr.lsp_bits & LSPBIT_ATT) == LSPBIT_ATT
&& !spftree->area->attached_bit_rcv_ignore
&& spftree->area->is_type == IS_LEVEL_1) {
struct prefix_pair ip_info = { {0} };
if (IS_DEBUG_SPF_EVENTS)
zlog_debug("ISIS-Spf (%s): add default %s route",
rawlspid_print(lsp->hdr.lsp_id),
spftree->family == AF_INET ? "ipv4"
: "ipv6");
if (spftree->family == AF_INET) {
ip_info.dest.family = AF_INET;
vtype = VTYPE_IPREACH_INTERNAL;
} else {
ip_info.dest.family = AF_INET6;
vtype = VTYPE_IP6REACH_INTERNAL;
}
process_N(spftree, vtype, &ip_info, cost, depth + 1, NULL,
parent);
}
if (fragnode == NULL)
fragnode = listhead(lsp->lspu.frags);
else

View File

@ -316,6 +316,11 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
"/frr-isisd:isis/instance/fast-reroute/level-1/lfa/load-sharing");
area->lfa_load_sharing[1] = yang_get_default_bool(
"/frr-isisd:isis/instance/fast-reroute/level-2/lfa/load-sharing");
area->attached_bit_send =
yang_get_default_bool("/frr-isisd:isis/instance/attach-send");
area->attached_bit_rcv_ignore = yang_get_default_bool(
"/frr-isisd:isis/instance/attach-receive-ignore");
#else
area->max_lsp_lifetime[0] = DEFAULT_LSP_LIFETIME; /* 1200 */
area->max_lsp_lifetime[1] = DEFAULT_LSP_LIFETIME; /* 1200 */
@ -332,6 +337,8 @@ struct isis_area *isis_area_create(const char *area_tag, const char *vrf_name)
area->lsp_mtu = DEFAULT_LSP_MTU;
area->lfa_load_sharing[0] = true;
area->lfa_load_sharing[1] = true;
area->attached_bit_send = true;
area->attached_bit_rcv_ignore = false;
#endif /* ifndef FABRICD */
area->lfa_priority_limit[0] = SPF_PREFIX_PRIO_LOW;
area->lfa_priority_limit[1] = SPF_PREFIX_PRIO_LOW;
@ -2547,12 +2554,21 @@ void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit)
#endif /* ifndef FABRICD */
}
void isis_area_attached_bit_set(struct isis_area *area, bool attached_bit)
void isis_area_attached_bit_send_set(struct isis_area *area, bool attached_bit)
{
char new_attached_bit = attached_bit ? LSPBIT_ATT : 0;
if (new_attached_bit != area->attached_bit) {
area->attached_bit = new_attached_bit;
if (attached_bit != area->attached_bit_send) {
area->attached_bit_send = attached_bit;
lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1);
}
}
void isis_area_attached_bit_receive_set(struct isis_area *area,
bool attached_bit)
{
if (attached_bit != area->attached_bit_rcv_ignore) {
area->attached_bit_rcv_ignore = attached_bit;
lsp_regenerate_schedule(area, IS_LEVEL_1 | IS_LEVEL_2, 1);
}
}

View File

@ -169,7 +169,8 @@ struct isis_area {
/* are we overloaded? */
char overload_bit;
/* L1/L2 router identifier for inter-area traffic */
char attached_bit;
char attached_bit_send;
char attached_bit_rcv_ignore;
uint16_t lsp_refresh[ISIS_LEVELS];
/* minimum time allowed before lsp retransmission */
uint16_t lsp_gen_interval[ISIS_LEVELS];
@ -253,7 +254,9 @@ void isis_area_invalidate_routes(struct isis_area *area, int levels);
void isis_area_verify_routes(struct isis_area *area);
void isis_area_overload_bit_set(struct isis_area *area, bool overload_bit);
void isis_area_attached_bit_set(struct isis_area *area, bool attached_bit);
void isis_area_attached_bit_send_set(struct isis_area *area, bool attached_bit);
void isis_area_attached_bit_receive_set(struct isis_area *area,
bool attached_bit);
void isis_area_dynhostname_set(struct isis_area *area, bool dynhostname);
void isis_area_metricstyle_set(struct isis_area *area, bool old_metric,
bool new_metric);

View File

@ -1043,9 +1043,24 @@ module frr-isisd {
"Dynamic hostname support for IS-IS.";
}
leaf attach-send {
type boolean;
default "true";
description
"If true, attached bits are sent in LSP if L1/L2 router for inter-area traffic.";
}
leaf attach-receive-ignore {
type boolean;
default "false";
description
"If false, attached bits received in LSP, cause default route add, if L1 router for inter-area traffic.";
}
leaf attached {
type boolean;
default "false";
status deprecated;
description
"If true, identify as L1/L2 router for inter-area traffic.";
}