if (ret != 0)
goto done;
+ if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
+ !CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
+ return 0;
+
if (!CHECK_FLAG(next1->flags, NEXTHOP_FLAG_HAS_BACKUP) &&
CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return -1;
!CHECK_FLAG(next2->flags, NEXTHOP_FLAG_HAS_BACKUP))
return 1;
- if (next1->backup_idx < next2->backup_idx)
+ if (next1->backup_num == 0 && next2->backup_num == 0)
+ goto done;
+
+ if (next1->backup_num < next2->backup_num)
return -1;
- if (next1->backup_idx > next2->backup_idx)
+ if (next1->backup_num > next2->backup_num)
return 1;
+ ret = memcmp(next1->backup_idx,
+ next2->backup_idx, next1->backup_num);
+
done:
return ret;
}
uint32_t nexthop_hash_quick(const struct nexthop *nexthop)
{
uint32_t key = 0x45afe398;
- uint32_t val;
+ int i;
key = jhash_3words(nexthop->type, nexthop->vrf_id,
nexthop->nh_label_type, key);
if (nexthop->nh_label) {
int labels = nexthop->nh_label->num_labels;
- int i = 0;
+
+ i = 0;
while (labels >= 3) {
key = jhash_3words(nexthop->nh_label->label[i],
key = jhash_1word(nexthop->nh_label->label[i], key);
}
- val = 0;
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- val = (uint32_t)nexthop->backup_idx;
-
- key = jhash_3words(nexthop->ifindex,
- CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK), val,
+ key = jhash_2words(nexthop->ifindex,
+ CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_ONLINK),
key);
+ /* Include backup nexthops, if present */
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ int backups = nexthop->backup_num;
+
+ i = 0;
+
+ while (backups >= 3) {
+ key = jhash_3words(nexthop->backup_idx[i],
+ nexthop->backup_idx[i + 1],
+ nexthop->backup_idx[i + 2], key);
+ backups -= 3;
+ i += 3;
+ }
+
+ while (backups >= 2) {
+ key = jhash_2words(nexthop->backup_idx[i],
+ nexthop->backup_idx[i + 1], key);
+ backups -= 2;
+ i += 2;
+ }
+
+ if (backups >= 1)
+ key = jhash_1word(nexthop->backup_idx[i], key);
+ }
+
return key;
}
copy->type = nexthop->type;
copy->flags = nexthop->flags;
copy->weight = nexthop->weight;
- copy->backup_idx = nexthop->backup_idx;
+
+ assert(nexthop->backup_num < NEXTHOP_MAX_BACKUPS);
+ copy->backup_num = nexthop->backup_num;
+ if (copy->backup_num > 0)
+ memcpy(copy->backup_idx, nexthop->backup_idx, copy->backup_num);
+
memcpy(©->gate, &nexthop->gate, sizeof(nexthop->gate));
memcpy(©->src, &nexthop->src, sizeof(nexthop->src));
memcpy(©->rmap_src, &nexthop->rmap_src, sizeof(nexthop->rmap_src));
NET_VXLAN = 100, /* value copied from FPM_NH_ENCAP_VXLAN. */
};
+/* Fixed limit on the number of backup nexthops per primary nexthop */
+#define NEXTHOP_MAX_BACKUPS 8
+
+/* Backup index value is limited */
+#define NEXTHOP_BACKUP_IDX_MAX 255
+
/* Nexthop structure. */
struct nexthop {
struct nexthop *next;
/* Weight of the nexthop ( for unequal cost ECMP ) */
uint8_t weight;
- /* Index of a corresponding backup nexthop in a backup list;
+ /* Count and index of corresponding backup nexthop(s) in a backup list;
* only meaningful if the HAS_BACKUP flag is set.
*/
- uint8_t backup_idx;
+ uint8_t backup_num;
+ uint8_t backup_idx[NEXTHOP_MAX_BACKUPS];
/* Encapsulation information. */
enum nh_encap_type nh_encap_type;
} nh_encap;
};
-/* Backup index value is limited */
-#define NEXTHOP_BACKUP_IDX_MAX 255
-
/* Utility to append one nexthop to another. */
#define NEXTHOP_APPEND(to, new) \
do { \
return false;
SET_FLAG(nhop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nhop->backup_idx = backup_idx;
+ nhop->backup_num = 1;
+ nhop->backup_idx[0] = backup_idx;
}
return true;
vty_out(vty, " weight %u", nh->weight);
if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
- vty_out(vty, " backup-idx %d", nh->backup_idx);
+ vty_out(vty, " backup-idx %d", nh->backup_idx[0]);
vty_out(vty, "\n");
}
json_object_int_add(j, "weight", nh->weight);
if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP))
- json_object_int_add(j, "backupIdx", nh->backup_idx);
+ json_object_int_add(j, "backupIdx", nh->backup_idx[0]);
}
static void nexthop_group_write_nexthop_internal(struct vty *vty,
int zapi_nexthop_encode(struct stream *s, const struct zapi_nexthop *api_nh,
uint32_t api_flags)
{
- int ret = 0;
+ int i, ret = 0;
int nh_flags = api_nh->flags;
stream_putl(s, api_nh->vrf_id);
sizeof(struct ethaddr));
/* Index of backup nexthop */
- if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP))
- stream_putc(s, api_nh->backup_idx);
+ if (CHECK_FLAG(nh_flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
+ /* Validate backup count */
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) {
+ ret = -1;
+ goto done;
+ }
+
+ stream_putc(s, api_nh->backup_num);
+ for (i = 0; i < api_nh->backup_num; i++)
+ stream_putc(s, api_nh->backup_idx[i]);
+ }
done:
return ret;
static int zapi_nexthop_decode(struct stream *s, struct zapi_nexthop *api_nh,
uint32_t api_flags)
{
- int ret = -1;
+ int i, ret = -1;
STREAM_GETL(s, api_nh->vrf_id);
STREAM_GETC(s, api_nh->type);
sizeof(struct ethaddr));
/* Backup nexthop index */
- if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP))
- STREAM_GETC(s, api_nh->backup_idx);
+ if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
+ STREAM_GETC(s, api_nh->backup_num);
+
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS)
+ return -1;
+
+ for (i = 0; i < api_nh->backup_num; i++)
+ STREAM_GETC(s, api_nh->backup_idx[i]);
+ }
/* Success */
ret = 0;
if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
SET_FLAG(n->flags, NEXTHOP_FLAG_HAS_BACKUP);
- n->backup_idx = znh->backup_idx;
+ n->backup_num = znh->backup_num;
+ memcpy(n->backup_idx, znh->backup_idx, n->backup_num);
}
return n;
}
if (CHECK_FLAG(nh->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ if (nh->backup_num > NEXTHOP_MAX_BACKUPS)
+ return -1;
+
SET_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP);
- znh->backup_idx = nh->backup_idx;
+ znh->backup_num = nh->backup_num;
+ memcpy(znh->backup_idx, nh->backup_idx, znh->backup_num);
}
return 0;
uint32_t weight;
- /* Index of backup nexthop */
- uint8_t backup_idx;
+ /* Backup nexthops, for IP-FRR, TI-LFA, etc */
+ uint8_t backup_num;
+ uint8_t backup_idx[NEXTHOP_MAX_BACKUPS];
};
/*
if (backup) {
/* Set flag and index in primary nexthop */
SET_FLAG(sg.r.nhop.flags, NEXTHOP_FLAG_HAS_BACKUP);
- sg.r.nhop.backup_idx = 0;
+ sg.r.nhop.backup_num = 1;
+ sg.r.nhop.backup_idx[0] = 0;
if (backup_nexthop4.s_addr != INADDR_ANY) {
sg.r.backup_nhop.gate.ipv4 = backup_nexthop4;
return -1;
i++;
+ if (i >= MULTIPATH_NUM)
+ break;
}
}
return -1;
i++;
+ if (i >= MULTIPATH_NUM)
+ break;
}
if (i > 0)
struct nexthop *nexthop = NULL;
struct ipaddr vtep_ip;
struct interface *ifp;
+ int i;
char nhbuf[INET6_ADDRSTRLEN] = "";
switch (api_nh->type) {
nexthop->weight = api_nh->weight;
if (CHECK_FLAG(api_nh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
- if (api_nh->backup_idx < api->backup_nexthop_num) {
- /* Capture backup info */
- SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nexthop->backup_idx = api_nh->backup_idx;
- } else {
- /* Warn about invalid backup index */
+ /* Validate count */
+ if (api_nh->backup_num > NEXTHOP_MAX_BACKUPS) {
if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT)
- zlog_debug("%s: invalid backup nh idx %d",
- __func__, api_nh->backup_idx);
+ zlog_debug("%s: invalid backup nh count %d",
+ __func__, api_nh->backup_num);
+ nexthop_free(nexthop);
+ nexthop = NULL;
+ goto done;
+ }
+
+ /* Copy backup info */
+ SET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
+ nexthop->backup_num = api_nh->backup_num;
+
+ for (i = 0; i < api_nh->backup_num; i++) {
+ /* Validate backup index */
+ if (api_nh->backup_idx[i] < api->backup_nexthop_num) {
+ nexthop->backup_idx[i] = api_nh->backup_idx[i];
+ } else {
+ if (IS_ZEBRA_DEBUG_RECV || IS_ZEBRA_DEBUG_EVENT)
+ zlog_debug("%s: invalid backup nh idx %d",
+ __func__,
+ api_nh->backup_idx[i]);
+ nexthop_free(nexthop);
+ nexthop = NULL;
+ goto done;
+ }
}
}
+
done:
return nexthop;
}
__func__, nhbuf);
}
UNSET_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
- nexthop->backup_idx = 0;
+ nexthop->backup_num = 0;
}
/* MPLS labels for BGP-LU or Segment Routing */
static void lsp_print(struct vty *vty, zebra_lsp_t *lsp)
{
zebra_nhlfe_t *nhlfe, *backup;
- int i;
+ int i, j;
vty_out(vty, "Local label: %u%s\n", lsp->ile.in_label,
CHECK_FLAG(lsp->flags, LSP_FLAG_INSTALLED) ? " (installed)"
nhlfe_print(nhlfe, vty);
if (nhlfe->nexthop &&
- CHECK_FLAG(nhlfe->nexthop->flags,
- NEXTHOP_FLAG_HAS_BACKUP)) {
- /* Find backup in backup list */
+ !CHECK_FLAG(nhlfe->nexthop->flags,
+ NEXTHOP_FLAG_HAS_BACKUP))
+ continue;
+
+ /* Backup nhlfes: find backups in backup list */
+ for (j = 0; j < nhlfe->nexthop->backup_num; j++) {
i = 0;
backup = NULL;
frr_each(nhlfe_list, &lsp->backup_nhlfe_list, backup) {
- if (i == nhlfe->nexthop->backup_idx)
+ if (i == nhlfe->nexthop->backup_idx[j])
break;
i++;
}
/* Update backup info if present */
if (CHECK_FLAG(znh->flags, ZAPI_NEXTHOP_FLAG_HAS_BACKUP)) {
- nhlfe->nexthop->backup_idx = znh->backup_idx;
+ if (znh->backup_num > NEXTHOP_MAX_BACKUPS) {
+ nhlfe_del(nhlfe);
+ return -1;
+ }
+
+ nhlfe->nexthop->backup_num = znh->backup_num;
+ memcpy(nhlfe->nexthop->backup_idx, znh->backup_idx,
+ znh->backup_num);
SET_FLAG(nhlfe->nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP);
}
char nhname[PREFIX_STRLEN];
char backup_str[50];
char wgt_str[50];
+ char temp_str[10];
+ int i;
struct interface *ifp;
struct vrf *vrf = vrf_lookup_by_id(nexthop->vrf_id);
backup_str[0] = '\0';
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
- snprintf(backup_str, sizeof(backup_str), "backup %d,",
- (int)nexthop->backup_idx);
+ snprintf(backup_str, sizeof(backup_str), "backup ");
+ for (i = 0; i < nexthop->backup_num; i++) {
+ snprintf(temp_str, sizeof(temp_str), "%d, ",
+ nexthop->backup_idx[i]);
+ strlcat(backup_str, temp_str, sizeof(backup_str));
+ }
}
wgt_str[0] = '\0';
const struct nexthop *nexthop)
{
char buf[MPLS_LABEL_STRLEN];
+ int i;
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
if (nexthop->weight)
vty_out(vty, ", weight %u", nexthop->weight);
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- vty_out(vty, ", backup %d", nexthop->backup_idx);
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ vty_out(vty, ", backup %d", nexthop->backup_idx[0]);
+
+ for (i = 1; i < nexthop->backup_num; i++)
+ vty_out(vty, ", %d", nexthop->backup_idx[i]);
+ }
}
/*
char buf[SRCDEST2STR_BUFFER];
struct vrf *vrf = NULL;
json_object *json_labels = NULL;
+ json_object *json_backups = NULL;
+ int i;
json_object_int_add(json_nexthop, "flags",
nexthop->flags);
json_object_boolean_true_add(json_nexthop,
"recursive");
- if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP))
- json_object_int_add(json_nexthop, "backupIndex",
- nexthop->backup_idx);
+ if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
+ json_backups = json_object_new_array();
+ for (i = 0; i < nexthop->backup_num; i++) {
+ json_object_array_add(
+ json_backups,
+ json_object_new_int(nexthop->backup_idx[i]));
+ }
+
+ json_object_object_add(json_nexthop, "backupIndex",
+ json_backups);
+ }
switch (nexthop->type) {
case NEXTHOP_TYPE_IPV4:
if (CHECK_FLAG(nexthop->flags,
NEXTHOP_FLAG_HAS_BACKUP))
vty_out(vty, " [backup %d]",
- nexthop->backup_idx);
+ nexthop->backup_idx[0]);
vty_out(vty, "\n");
continue;
/* TODO -- print more useful backup info */
if (CHECK_FLAG(nexthop->flags, NEXTHOP_FLAG_HAS_BACKUP)) {
- struct nexthop *backup;
int i;
- i = 0;
- for (ALL_NEXTHOPS(nhe->backup_info->nhe->nhg, backup)) {
- if (i == nexthop->backup_idx)
- break;
- i++;
- }
+ vty_out(vty, "[backup");
+ for (i = 0; i < nexthop->backup_num; i++)
+ vty_out(vty, " %d", nexthop->backup_idx[i]);
- /* TODO */
- if (backup)
- vty_out(vty, " [backup %d]",
- nexthop->backup_idx);
- else
- vty_out(vty, " [backup INVALID]");
+ vty_out(vty, "]");
}
vty_out(vty, "\n");