diff --git a/nest/config.Y b/nest/config.Y index f212e711..b3831f1d 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -176,7 +176,7 @@ CF_ENUM(T_ENUM_SCOPE, SCOPE_, HOST, LINK, SITE, ORGANIZATION, UNIVERSE, UNDEFINE CF_ENUM(T_ENUM_RTD, RTD_, BLACKHOLE, UNREACHABLE, PROHIBIT) CF_ENUM(T_ENUM_ROA, ROA_, UNKNOWN, VALID, INVALID) CF_ENUM_PX(T_ENUM_AF, AF_, AFI_, IPV4, IPV6) -CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE) +CF_ENUM(T_ENUM_MPLS_POLICY, MPLS_POLICY_, NONE, STATIC, PREFIX, AGGREGATE, VRF) %type idval %type imexport diff --git a/nest/mpls.Y b/nest/mpls.Y index c3a03671..e234bdc9 100644 --- a/nest/mpls.Y +++ b/nest/mpls.Y @@ -20,7 +20,7 @@ static struct mpls_range_config *this_mpls_range; CF_DECLS -CF_KEYWORDS(MPLS, DOMAIN, LABEL, RANGE, STATIC, DYNAMIC, START, LENGTH, POLICY, PREFIX, AGGREGATE) +CF_KEYWORDS(MPLS, DOMAIN, LABEL, RANGE, STATIC, DYNAMIC, START, LENGTH, POLICY, PREFIX, AGGREGATE, VRF) %type mpls_label_policy %type mpls_channel_start mpls_channel @@ -112,6 +112,7 @@ mpls_label_policy: STATIC { $$ = MPLS_POLICY_STATIC; } | PREFIX { $$ = MPLS_POLICY_PREFIX; } | AGGREGATE { $$ = MPLS_POLICY_AGGREGATE; } + | VRF { $$ = MPLS_POLICY_VRF; } ; mpls_channel_opt: diff --git a/nest/mpls.c b/nest/mpls.c index 96bafcb3..269b4df7 100644 --- a/nest/mpls.c +++ b/nest/mpls.c @@ -52,14 +52,16 @@ * map, which can be used by the protocols that work with IP-prefix-based FECs. * * The FEC map keeps hash tables of FECs (struct &mpls_fec) based on network - * prefix, next hop eattr and assigned label. It has three labeling policies: + * prefix, next hop eattr and assigned label. It has three general labeling policies: * static assignment (%MPLS_POLICY_STATIC), per-prefix policy (%MPLS_POLICY_PREFIX), * and aggregating policy (%MPLS_POLICY_AGGREGATE). In per-prefix policy, each * distinct LSP is a separate FEC and uses a separate label, which is kept even * if the next hop of the LSP changes. In aggregating policy, LSPs with a same * next hop form one FEC and use one label, but when a next hop (or remote * label) of such LSP changes then the LSP must be moved to a different FEC and - * assigned a different label. + * assigned a different label. There is also a special VRF policy (%MPLS_POLICY_VRF) + * applicable for L3VPN protocols, which uses one label for all routes from a VRF, + * while replacing the original next hop with lookup in the VRF. * * The overall process works this way: A protocol wants to announce a LSP route, * it does that by announcing e.g. IP route with %EA_MPLS_POLICY attribute. @@ -744,6 +746,28 @@ mpls_get_fec_by_destination(struct mpls_fec_map *m, ea_list *dest) return fec; } +struct mpls_fec * +mpls_get_fec_for_vrf(struct mpls_fec_map *m) +{ + struct mpls_fec *fec = m->vrf_fec; + + if (fec) + return fec; + + fec = sl_allocz(mpls_slab(m, 0)); + + fec->label = mpls_new_label(m->domain, m->handle); + fec->policy = MPLS_POLICY_VRF; + fec->iface = m->vrf_iface; + + DBG("New FEC vrf %u\n", fec->label); + + m->vrf_fec = fec; + HASH_INSERT2(m->label_hash, LABEL, m->pool, fec); + + return fec; +} + void mpls_free_fec(struct mpls_fec_map *m, struct mpls_fec *fec) { @@ -769,6 +793,11 @@ mpls_free_fec(struct mpls_fec_map *m, struct mpls_fec *fec) HASH_REMOVE2(m->attrs_hash, RTA, m->pool, fec); break; + case MPLS_POLICY_VRF: + ASSERT(m->vrf_fec == fec); + m->vrf_fec = NULL; + break; + default: bug("Unknown fec type"); } @@ -890,6 +919,17 @@ mpls_apply_fec(rte *r, struct mpls_fec *fec) { ea_set_attr_u32(&r->attrs, &ea_gen_mpls_label, 0, fec->label); ea_set_attr_u32(&r->attrs, &ea_gen_mpls_policy, 0, fec->policy); + + if (fec->policy == MPLS_POLICY_VRF) + { + ea_unset_attr(&r->attrs, 0, &ea_gen_hostentry); + + struct nexthop_adata nhad = { + .nh.iface = fec->iface, + .ad.length = sizeof nhad - sizeof nhad.ad, + }; + ea_set_attr_data(&r->attrs, &ea_gen_nexthop, 0, nhad.ad.data, nhad.ad.length); + } } @@ -924,6 +964,13 @@ mpls_handle_rte(struct mpls_fec_map *m, const net_addr *n, rte *r) fec = mpls_get_fec_by_destination(m, r->attrs); break; + case MPLS_POLICY_VRF: + if (!m->vrf_iface) + return; + + fec = mpls_get_fec_for_vrf(m); + break; + default: log(L_WARN "Route %N has invalid MPLS policy %u", n, policy); return; diff --git a/nest/mpls.h b/nest/mpls.h index 2d188d02..57978c99 100644 --- a/nest/mpls.h +++ b/nest/mpls.h @@ -21,6 +21,7 @@ #define MPLS_POLICY_STATIC 1 #define MPLS_POLICY_PREFIX 2 #define MPLS_POLICY_AGGREGATE 3 +#define MPLS_POLICY_VRF 4 #define MPLS_FEC_DOWN 0 #define MPLS_FEC_CLEAN 1 @@ -134,6 +135,7 @@ struct mpls_fec { struct mpls_fec *next_l; /* Next in mpls_fec.label_hash */ union { /* Primary key */ struct ea_storage *rta; + struct iface *iface; net_addr net[0]; }; }; @@ -144,10 +146,12 @@ struct mpls_fec_map { HASH(struct mpls_fec) net_hash; /* Hash table for MPLS_POLICY_PREFIX FECs */ HASH(struct mpls_fec) attrs_hash; /* Hash table for MPLS_POLICY_AGGREGATE FECs */ HASH(struct mpls_fec) label_hash; /* Hash table for FEC lookup by label */ + struct mpls_fec *vrf_fec; /* Single FEC for MPLS_POLICY_VRF */ struct channel *channel; /* MPLS channel for FEC announcement */ struct mpls_domain *domain; /* MPLS domain, keeping reference */ struct mpls_handle *handle; /* Handle for allocation of labels */ + struct iface *vrf_iface; u8 mpls_rts; /* Source value used for MPLS routes (RTS_*) */ };