From: dean <dluga93@gmail.com> This patch adds a new network of type NET_SADR_IP6, including new structures, constants, and switch cases for the new network type. Some existing functions are duplicates to handle the new network type, and netlink can now handle SADR routes. The net_route_sadr_ip6 function is a bit of a hack. Routing in SADR is supposed to match the destination address first, then out of the entries with that dst, it should choose the most specific matching src address. For the destination-only part, I use a net_addr_ip6 as the destination prefix. Its type is changed to NET_SADR_IP6 to satisfy the assertion in fib_find. The fib_find function checks the length of the prefix to distinguish between destination-only or full SADR searches. --- lib/net.c | 34 ++++++++++++++++++++++++++ lib/net.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++--- nest/rt-fib.c | 16 ++++++++++++ nest/rt-table.c | 35 +++++++++++++++++++++++++- sysdep/linux/netlink.c | 26 ++++++++++++++++++-- sysdep/unix/krt.c | 4 ++- 6 files changed, 174 insertions(+), 7 deletions(-) diff --git a/lib/net.c b/lib/net.c index a00ff27..41084fe 100644 --- a/lib/net.c +++ b/lib/net.c @@ -15,6 +15,7 @@ const char * const net_label[] = { [NET_FLOW4] = "flow4", [NET_FLOW6] = "flow6", [NET_MPLS] = "mpls", + [NET_SADR_IP6] = "sadr6", }; const u16 net_addr_length[] = { @@ -27,6 +28,7 @@ const u16 net_addr_length[] = { [NET_FLOW4] = 0, [NET_FLOW6] = 0, [NET_MPLS] = sizeof(net_addr_mpls), + [NET_SADR_IP6]= sizeof(net_addr_sadr_ip6), }; const u8 net_max_prefix_length[] = { @@ -39,6 +41,7 @@ const u8 net_max_prefix_length[] = { [NET_FLOW4] = IP4_MAX_PREFIX_LENGTH, [NET_FLOW6] = IP6_MAX_PREFIX_LENGTH, [NET_MPLS] = 0, + [NET_SADR_IP6]= IP6_MAX_PREFIX_LENGTH, }; const u16 net_max_text_length[] = { @@ -51,6 +54,7 @@ const u16 net_max_text_length[] = { [NET_FLOW4] = 0, /* "flow4 { ... }" */ [NET_FLOW6] = 0, /* "flow6 { ... }" */ [NET_MPLS] = 7, /* "1048575" */ + [NET_SADR_IP6]= 92, /* "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128 from ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128" */ }; @@ -104,6 +108,8 @@ net_format(const net_addr *N, char *buf, int buflen) return flow6_net_format(buf, buflen, &n->flow6); case NET_MPLS: return bsnprintf(buf, buflen, "%u", n->mpls.label); + case NET_SADR_IP6: + return bsnprintf(buf, buflen, "%I6/%d from %I6/%d", n->sadr_ip6.dst_prefix, n->sadr_ip6.dst_pxlen, n->sadr_ip6.src_prefix, n->sadr_ip6.src_pxlen); } bug("unknown network type"); @@ -126,6 +132,9 @@ net_pxmask(const net_addr *a) case NET_FLOW6: return ipa_from_ip6(ip6_mkmask(net6_pxlen(a))); + case NET_SADR_IP6: + return ipa_from_ip6(ip6_mkmask(net6_sadr_dst_pxlen(a))); + case NET_MPLS: default: return IPA_NONE; @@ -133,6 +142,16 @@ net_pxmask(const net_addr *a) } int +net_compare_sadr_ip6(const net_addr_sadr_ip6 *a, const net_addr_sadr_ip6 *b) +{ + int dst_cmp = ip6_compare(a->dst_prefix, b->dst_prefix) ?: uint_cmp(a->dst_pxlen, b->dst_pxlen); + if (dst_cmp) + return dst_cmp; + else + return ip6_compare(a->src_prefix, b->src_prefix) ?: uint_cmp(a->src_pxlen, b->src_pxlen); +} + +int net_compare(const net_addr *a, const net_addr *b) { if (a->type != b->type) @@ -158,6 +177,8 @@ net_compare(const net_addr *a, const net_addr *b) return net_compare_flow6((const net_addr_flow6 *) a, (const net_addr_flow6 *) b); case NET_MPLS: return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b); + case NET_SADR_IP6: + return net_compare_sadr_ip6((const net_addr_sadr_ip6 *) a, (const net_addr_sadr_ip6 *) b); } return 0; } @@ -178,6 +199,7 @@ net_hash(const net_addr *n) case NET_FLOW4: return NET_HASH(n, flow4); case NET_FLOW6: return NET_HASH(n, flow6); case NET_MPLS: return NET_HASH(n, mpls); + case NET_SADR_IP6: return NET_HASH(n, ip6); default: bug("invalid type"); } } @@ -199,6 +221,7 @@ net_validate(const net_addr *n) case NET_FLOW4: return NET_VALIDATE(n, flow4); case NET_FLOW6: return NET_VALIDATE(n, flow6); case NET_MPLS: return NET_VALIDATE(n, mpls); + case NET_SADR_IP6: return NET_VALIDATE(n, sadr_ip6); default: return 0; } } @@ -224,6 +247,9 @@ net_normalize(net_addr *N) case NET_MPLS: return; + + case NET_SADR_IP6: + return net_normalize_sadr_ip6(&n->sadr_ip6); } } @@ -248,6 +274,9 @@ net_classify(const net_addr *N) case NET_MPLS: return IADDR_HOST | SCOPE_UNIVERSE; + + case NET_SADR_IP6: + return ip6_zero(n->sadr_ip6.dst_prefix) ? (IADDR_HOST | SCOPE_UNIVERSE) : ip6_classify(&n->sadr_ip6.dst_prefix); } return IADDR_INVALID; @@ -274,6 +303,11 @@ ipa_in_netX(const ip_addr a, const net_addr *n) return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_prefix(n)), ip6_mkmask(net6_pxlen(n)))); + case NET_SADR_IP6: + if (ipa_is_ip4(a)) return 0; + return ip6_zero(ip6_and(ip6_xor(ipa_to_ip6(a), net6_sadr_dst_prefix(n)), + ip6_mkmask(net6_sadr_dst_pxlen(n)))); + case NET_MPLS: default: return 0; diff --git a/lib/net.h b/lib/net.h index 332f4c9..22eee60 100644 --- a/lib/net.h +++ b/lib/net.h @@ -12,7 +12,6 @@ #include "lib/ip.h" - #define NET_IP4 1 #define NET_IP6 2 #define NET_VPN4 3 @@ -22,7 +21,8 @@ #define NET_FLOW4 7 #define NET_FLOW6 8 #define NET_MPLS 9 -#define NET_MAX 10 +#define NET_SADR_IP6 10 +#define NET_MAX 11 #define NB_IP4 (1 << NET_IP4) #define NB_IP6 (1 << NET_IP6) @@ -33,8 +33,9 @@ #define NB_FLOW4 (1 << NET_FLOW4) #define NB_FLOW6 (1 << NET_FLOW6) #define NB_MPLS (1 << NET_MPLS) +#define NB_SADR (1 << NET_SADR_IP6) -#define NB_IP (NB_IP4 | NB_IP6) +#define NB_IP (NB_IP4 | NB_IP6 | NB_SADR) #define NB_VPN (NB_VPN4 | NB_VPN6) #define NB_FLOW (NB_FLOW4 | NB_FLOW6) #define NB_DEST (NB_IP | NB_VPN | NB_MPLS) @@ -120,6 +121,15 @@ typedef struct net_addr_mpls { u32 label; } net_addr_mpls; +typedef struct net_addr_sadr_ip6 { + u8 type; + u8 dst_pxlen; + u16 length; + ip6_addr dst_prefix; + u32 src_pxlen; // if u8, NET_ADDR_SADR_IP6 would leave non-zero padding + ip6_addr src_prefix; +} net_addr_sadr_ip6; + typedef union net_addr_union { net_addr n; net_addr_ip4 ip4; @@ -131,6 +141,7 @@ typedef union net_addr_union { net_addr_flow4 flow4; net_addr_flow6 flow6; net_addr_mpls mpls; + net_addr_sadr_ip6 sadr_ip6; } net_addr_union; @@ -169,6 +180,9 @@ extern const u16 net_max_text_length[]; #define NET_ADDR_MPLS(label) \ ((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label }) +#define NET_ADDR_SADR_IP6(dst_prefix,dst_pxlen,src_prefix,src_pxlen) \ + ((net_addr_sadr_ip6) { NET_SADR_IP6, dst_pxlen, sizeof(net_addr_sadr_ip6), dst_prefix, src_pxlen, src_prefix }) + static inline void net_fill_ip4(net_addr *a, ip4_addr prefix, uint pxlen) { *(net_addr_ip4 *)a = NET_ADDR_IP4(prefix, pxlen); } @@ -191,6 +205,9 @@ static inline void net_fill_roa6(net_addr *a, ip6_addr prefix, uint pxlen, uint static inline void net_fill_mpls(net_addr *a, u32 label) { *(net_addr_mpls *)a = NET_ADDR_MPLS(label); } +static inline void net_fill_sadr_ip6(net_addr *a, ip6_addr dst_prefix, uint dst_pxlen, ip6_addr src_prefix, uint src_pxlen) +{ *(net_addr_sadr_ip6 *)a = NET_ADDR_SADR_IP6(dst_prefix, dst_pxlen, src_prefix, src_pxlen); } + static inline void net_fill_ipa(net_addr *a, ip_addr prefix, uint pxlen) { if (ipa_is_ip4(prefix)) @@ -236,6 +253,9 @@ static inline int net_is_roa(const net_addr *a) static inline int net_is_vpn(const net_addr *a) { return (a->type == NET_VPN4) || (a->type == NET_VPN6); } +static inline int net_is_sadr(const net_addr *a) +{ return (a->type == NET_SADR_IP6); } + static inline ip4_addr net4_prefix(const net_addr *a) { return ((net_addr_ip4 *) a)->prefix; } @@ -243,6 +263,12 @@ static inline ip4_addr net4_prefix(const net_addr *a) static inline ip6_addr net6_prefix(const net_addr *a) { return ((net_addr_ip6 *) a)->prefix; } +static inline ip6_addr net6_sadr_dst_prefix(const net_addr *a) +{ return ((net_addr_sadr_ip6 *) a)->dst_prefix; } + +static inline ip6_addr net6_sadr_src_prefix(const net_addr *a) +{ return ((net_addr_sadr_ip6 *) a)->src_prefix; } + static inline ip_addr net_prefix(const net_addr *a) { switch (a->type) @@ -259,6 +285,9 @@ static inline ip_addr net_prefix(const net_addr *a) case NET_FLOW6: return ipa_from_ip6(net6_prefix(a)); + case NET_SADR_IP6: + return ipa_from_ip6(net6_sadr_dst_prefix(a)); + case NET_MPLS: default: return IPA_NONE; @@ -279,6 +308,12 @@ static inline uint net4_pxlen(const net_addr *a) static inline uint net6_pxlen(const net_addr *a) { return a->pxlen; } +static inline uint net6_sadr_dst_pxlen(const net_addr *a) +{ return ((net_addr_sadr_ip6 *) a)->dst_pxlen; } + +static inline uint net6_sadr_src_pxlen(const net_addr *a) +{ return ((net_addr_sadr_ip6 *) a)->src_pxlen; } + static inline uint net_pxlen(const net_addr *a) { return a->pxlen; } @@ -327,6 +362,13 @@ static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6 static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b) { return !memcmp(a, b, sizeof(net_addr_mpls)); } +typedef net_addr_ip6 net_addr_sadr_dst; +static inline int net_equal_sadr_dst(const net_addr_ip6 *a, const net_addr_ip6 *b) +{ return net_equal_ip6(a, b); } + +static inline int net_equal_sadr_ip6(const net_addr_sadr_ip6 *a, const net_addr_sadr_ip6 *b) +{ return !memcmp(a, b, sizeof(net_addr_sadr_ip6)); } + static inline int net_equal_prefix_roa4(const net_addr_roa4 *a, const net_addr_roa4 *b) { return ip4_equal(a->prefix, b->prefix) && (a->pxlen == b->pxlen); } @@ -390,6 +432,8 @@ static inline int net_compare_flow6(const net_addr_flow6 *a, const net_addr_flow static inline int net_compare_mpls(const net_addr_mpls *a, const net_addr_mpls *b) { return uint_cmp(a->label, b->label); } +int net_compare_sadr_ip6(const net_addr_sadr_ip6 *a, const net_addr_sadr_ip6 *b); + int net_compare(const net_addr *a, const net_addr *b); @@ -423,6 +467,9 @@ static inline void net_copy_flow6(net_addr_flow6 *dst, const net_addr_flow6 *src static inline void net_copy_mpls(net_addr_mpls *dst, const net_addr_mpls *src) { memcpy(dst, src, sizeof(net_addr_mpls)); } +static inline void net_copy_sadr_ip6(net_addr_sadr_ip6 *dst, const net_addr_sadr_ip6 *src) +{ memcpy(dst, src, sizeof(net_addr_sadr_ip6)); } + /* XXXX */ static inline u32 u64_hash(u64 a) @@ -455,6 +502,12 @@ static inline u32 net_hash_flow6(const net_addr_flow6 *n) static inline u32 net_hash_mpls(const net_addr_mpls *n) { return n->label; } +static inline u32 net_hash_sadr_dst(const net_addr_sadr_dst* n) +{ return net_hash_ip6(n); } + +static inline u32 net_hash_sadr_ip6(const net_addr_sadr_ip6 *n) +{ return ip6_hash(n->dst_prefix) ^ ((u32) n->dst_pxlen << 26); } + u32 net_hash(const net_addr *a); @@ -504,6 +557,9 @@ static inline int net_validate_flow6(const net_addr_flow6 *n) static inline int net_validate_mpls(const net_addr_mpls *n) { return n->label < (1 << 20); } +static inline int net_validate_sadr_ip6(const net_addr_sadr_ip6 *n) +{ return net_validate_px6(n->dst_prefix, n->dst_pxlen) && net_validate_px6(n->src_prefix, n->src_pxlen); } + int net_validate(const net_addr *N); @@ -519,6 +575,10 @@ static inline void net_normalize_vpn4(net_addr_vpn4 *n) static inline void net_normalize_vpn6(net_addr_vpn6 *n) { net_normalize_ip6((net_addr_ip6 *) n); } +static inline void net_normalize_sadr_ip6(net_addr_sadr_ip6 *n) +{ n->dst_prefix = ip6_and(n->dst_prefix, ip6_mkmask(n->dst_pxlen)); + n->src_prefix = ip6_and(n->src_prefix, ip6_mkmask(n->src_pxlen)); } + void net_normalize(net_addr *N); diff --git a/nest/rt-fib.c b/nest/rt-fib.c index 11c31d0..5831b33 100644 --- a/nest/rt-fib.c +++ b/nest/rt-fib.c @@ -195,6 +195,7 @@ fib_hash(struct fib *f, const net_addr *a) case NET_ROA6: return FIB_HASH(f, a, roa6); case NET_FLOW4: return FIB_HASH(f, a, flow4); case NET_FLOW6: return FIB_HASH(f, a, flow6); + case NET_SADR_IP6: return FIB_HASH(f, a, sadr_ip6); default: bug("invalid type"); } } @@ -231,6 +232,20 @@ fib_find(struct fib *f, const net_addr *a) case NET_ROA6: return FIB_FIND(f, a, roa6); case NET_FLOW4: return FIB_FIND(f, a, flow4); case NET_FLOW6: return FIB_FIND(f, a, flow6); + case NET_SADR_IP6: + { + if (a->length != sizeof(net_addr_sadr_ip6)) + { + // dst only search + net_addr_ip6 a0; + net_copy((net_addr *)&a0, a); + a0.length = sizeof(net_addr_sadr_ip6); + + return FIB_FIND(f, &a0, sadr_dst); + } + else + return FIB_FIND(f, a, sadr_ip6); + } default: bug("invalid type"); } } @@ -250,6 +265,7 @@ fib_insert(struct fib *f, const net_addr *a, struct fib_node *e) case NET_ROA6: FIB_INSERT(f, a, e, roa6); return; case NET_FLOW4: FIB_INSERT(f, a, e, flow4); return; case NET_FLOW6: FIB_INSERT(f, a, e, flow6); return; + case NET_SADR_IP6: FIB_INSERT(f, a, e, sadr_ip6); return; default: bug("invalid type"); } } diff --git a/nest/rt-table.c b/nest/rt-table.c index 67ccc88..49347f8 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -95,6 +95,35 @@ net_route_ip6(rtable *t, net_addr_ip6 *n) return r; } +static inline void * +net_route_sadr_ip6(rtable *t, net_addr_sadr_ip6 *n) +{ + net *r; + + net_addr_ip6 dst = NET_ADDR_IP6(n->dst_prefix, n->dst_pxlen); + dst.type = NET_SADR_IP6; + + // search for matching dst + while (r = net_route_ip6(t, &dst)) + { + // found a matching dst, start search for entries that match both prefixes + net_addr_sadr_ip6 full_addr = + NET_ADDR_SADR_IP6(dst.prefix, dst.pxlen, n->src_prefix, n->src_pxlen); + + while (r = net_find_valid(t, (net_addr *) &full_addr), (!r) && (full_addr.src_pxlen > 0)) + { + full_addr.src_pxlen--; + ip6_clrbit(&full_addr.src_prefix, full_addr.src_pxlen); + } + + if (r) + return r; + dst.pxlen--; + } + + return NULL; +} + void * net_route(rtable *tab, const net_addr *n) { @@ -115,6 +144,9 @@ net_route(rtable *tab, const net_addr *n) case NET_ROA6: return net_route_ip6(tab, (net_addr_ip6 *) n0); + case NET_SADR_IP6: + return net_route_sadr_ip6(tab, (net_addr_sadr_ip6 *) n0); + default: return NULL; } @@ -1747,6 +1779,7 @@ rt_preconfig(struct config *c) rt_new_table(cf_get_symbol("master4"), NET_IP4); rt_new_table(cf_get_symbol("master6"), NET_IP6); + rt_new_table(cf_get_symbol("master_sadr6"), NET_SADR_IP6); } @@ -1980,7 +2013,7 @@ rt_new_table(struct symbol *s, uint addr_type) /* Hack that allows to 'redefine' the master table */ if ((s->class == SYM_TABLE) && (s->def == new_config->def_tables[addr_type]) && - ((addr_type == NET_IP4) || (addr_type == NET_IP6))) + ((addr_type == NET_IP4) || (addr_type == NET_IP6) || (addr_type == NET_SADR_IP6))) return s->def; struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config)); diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index 083a8d9..d89ae10 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -369,6 +369,7 @@ static struct nl_want_attrs rtm_attr_want4[BIRD_RTA_MAX] = { static struct nl_want_attrs rtm_attr_want6[BIRD_RTA_MAX] = { [RTA_DST] = { 1, 1, sizeof(ip6_addr) }, + [RTA_SRC] = { 1, 1, sizeof(ip6_addr) }, [RTA_IIF] = { 1, 1, sizeof(u32) }, [RTA_OIF] = { 1, 1, sizeof(u32) }, [RTA_GATEWAY] = { 1, 1, sizeof(ip6_addr) }, @@ -1161,8 +1162,17 @@ nl_send_route(struct krt_proto *p, rte *e, struct ea_list *eattrs, int op, int d nl_add_attr_mpls(&r->h, rsize, RTA_DST, 1, &label); } else + { nl_add_attr_ipa(&r->h, rsize, RTA_DST, net_prefix(net->n.addr)); + // add source address for SADR routes + if (net->n.addr->type == NET_SADR_IP6) + { + r->r.rtm_src_len = net6_sadr_src_pxlen(net->n.addr); + nl_add_attr_ipa(&r->h, rsize, RTA_SRC, net6_sadr_src_prefix(net->n.addr)); + } + } + /* * Strange behavior for RTM_DELROUTE: * 1) rtm_family is ignored in IPv6, works for IPv4 @@ -1394,7 +1404,8 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) struct rtattr *a[BIRD_RTA_MAX]; int new = h->nlmsg_type == RTM_NEWROUTE; - net_addr dst; + net_addr dst, sadr_src; + net_fill_ip6(&sadr_src, IP6_NONE, 0); u32 oif = ~0; u32 table_id; u32 priority = 0; @@ -1420,6 +1431,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want6, a, sizeof(a))) return; + if (a[RTA_SRC]) + net_fill_ip6(&sadr_src, rta_get_ip6(a[RTA_SRC]), i->rtm_src_len); + if (a[RTA_DST]) net_fill_ip6(&dst, rta_get_ip6(a[RTA_DST]), i->rtm_dst_len); else @@ -1496,7 +1510,15 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) src = KRT_SRC_ALIEN; } - net *net = net_get(p->p.main_channel->table, &dst); + net *net; + if (p->p.main_channel->table->addr_type == NET_SADR_IP6) + { + net_addr_sadr_ip6 sadr_addr = + NET_ADDR_SADR_IP6(net_prefix(&dst), net_pxlen(&dst), net_prefix(&sadr_src), net_pxlen(&sadr_src)); + net = net_get(p->p.main_channel->table, (net_addr *)&sadr_addr); + } + else + net = net_get(p->p.main_channel->table, &dst); if (s->net && !nl_mergable_route(s, net, p, priority, i->rtm_type)) nl_announce_route(s); diff --git a/sysdep/unix/krt.c b/sysdep/unix/krt.c index c6ff627..a9ce96b 100644 --- a/sysdep/unix/krt.c +++ b/sysdep/unix/krt.c @@ -1141,7 +1141,9 @@ krt_start(struct proto *P) switch (p->p.net_type) { case NET_IP4: p->af = AF_INET; break; - case NET_IP6: p->af = AF_INET6; break; + case NET_IP6: + case NET_SADR_IP6: + p->af = AF_INET6; break; case NET_MPLS: p->af = AF_MPLS; break; default: log(L_ERR "KRT: Tried to start with strange net type: %d", p->p.net_type); return PS_START; break; } -- 2.7.4
From: dean <dluga93@gmail.com> A new channel, sadr_ip6, is used for SADR both in the kernel and static protocols. In the static protocol, routes can be inserted with the following syntax: route <dst_prefix> from <src_prefix> via "interface" route <dst_prefix> from <src_prefix> recursive <ip> There is a bug in the Linux kernel that causes undefined behavior when both SADR and dst-only routes are put in the same table. This bug is present at least until kernel version 4.10.15. A workaround would be to use 2000::/3 as a source prefix instead of ::/0, but it would reduce the range of acceptable source addresses. This bug also affects the gateway of recursive routes. There shouldn't be SADR entries with the gateway as destination in the routing table. --- conf/conf.c | 1 + nest/config.Y | 5 +++-- nest/route.h | 9 +++++++++ nest/rt-table.c | 39 +++++++++++++++++++++++++++++++++++---- proto/static/config.Y | 15 +++++++++++++++ proto/static/static.c | 34 ++++++++++++++++++++++++++++++++-- proto/static/static.h | 2 ++ sysdep/linux/netlink.c | 3 ++- 8 files changed, 99 insertions(+), 9 deletions(-) diff --git a/conf/conf.c b/conf/conf.c index a283788..16f81d9 100644 --- a/conf/conf.c +++ b/conf/conf.c @@ -129,6 +129,7 @@ config_parse(struct config *c) int done = 0; DBG("Parsing configuration file `%s'\n", c->file_name); new_config = c; + cfg_mem = c->mem; if (setjmp(conf_jmpbuf)) goto cleanup; diff --git a/nest/config.Y b/nest/config.Y index b0f9642..407ab8e 100644 --- a/nest/config.Y +++ b/nest/config.Y @@ -76,7 +76,7 @@ CF_KEYWORDS(RELOAD, IN, OUT, MRTDUMP, MESSAGES, RESTRICT, MEMORY, IGP_METRIC, CL CF_KEYWORDS(GRACEFUL, RESTART, WAIT, MAX, FLUSH, AS) /* For r_args_channel */ -CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, IPV6_MC, IPV6_MPLS, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC) +CF_KEYWORDS(IPV4, IPV4_MC, IPV4_MPLS, IPV6, SADR_IP6, IPV6_MC, IPV6_MPLS, VPN4, VPN4_MC, VPN4_MPLS, VPN6, VPN6_MC, VPN6_MPLS, ROA4, ROA6, FLOW4, FLOW6, MPLS, PRI, SEC) CF_ENUM(T_ENUM_RTS, RTS_, DUMMY, STATIC, INHERIT, DEVICE, STATIC_DEVICE, REDIRECT, RIP, OSPF, OSPF_IA, OSPF_EXT1, OSPF_EXT2, BGP, PIPE, BABEL) @@ -157,9 +157,10 @@ net_type: | ROA6 { $$ = NET_ROA6; } | FLOW4{ $$ = NET_FLOW4; } | FLOW6{ $$ = NET_FLOW6; } + | SADR_IP6 { $$ = NET_SADR_IP6; } ; -CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6) +CF_ENUM(T_ENUM_NETTYPE, NET_, IP4, IP6, VPN4, VPN6, ROA4, ROA6, FLOW4, FLOW6, SADR_IP6) /* Creation of routing tables */ diff --git a/nest/route.h b/nest/route.h index 6c9b00c..b12d208 100644 --- a/nest/route.h +++ b/nest/route.h @@ -195,6 +195,7 @@ struct hostentry { ip_addr addr; /* IP address of host, part of key */ ip_addr link; /* (link-local) IP address of host, used as gw if host is directly attached */ + net_addr_ip6 src_addr; /* Source IP address to be used for SADR recursive routes */ struct rtable *tab; /* Dependent table, part of key */ struct hostentry *next; /* Next in hash chain */ unsigned hash_key; /* Hash key */ @@ -576,6 +577,7 @@ void rta_dump_all(void); void rta_show(struct cli *, rta *, ea_list *); struct hostentry * rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep); +struct hostentry * rt_get_sadr_hostentry(rtable *tab, net_addr_sadr_ip6* a, ip_addr ll, rtable *dep); void rta_apply_hostentry(rta *a, struct hostentry *he, mpls_label_stack *mls); static inline void @@ -584,6 +586,13 @@ rta_set_recursive_next_hop(rtable *dep, rta *a, rtable *tab, ip_addr gw, ip_addr rta_apply_hostentry(a, rt_get_hostentry(tab, gw, ll, dep), mls); } +static inline void +rta_set_recursive_sadr_next_hop(rtable *dep, rta *a, rtable *tab, net_addr_sadr_ip6* gw, ip_addr ll, mpls_label_stack *mls) +{ + struct hostentry *he = rt_get_sadr_hostentry(tab, gw, ll, dep); + rta_apply_hostentry(a, he, mls); +} + /* * rta_set_recursive_next_hop() acquires hostentry from hostcache and fills * rta->hostentry field. New hostentry has zero use count. Cached rta locks its diff --git a/nest/rt-table.c b/nest/rt-table.c index 49347f8..7d2d2c7 100644 --- a/nest/rt-table.c +++ b/nest/rt-table.c @@ -2436,9 +2436,21 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) he->dest = RTD_UNREACHABLE; he->igp_metric = 0; - net_addr he_addr; - net_fill_ip_host(&he_addr, he->addr); - net *n = net_route(tab, &he_addr); + net *n; + net_addr_sadr_ip6 trie_he_addr; + + if (tab->addr_type == NET_SADR_IP6) + { + trie_he_addr = NET_ADDR_SADR_IP6(he->addr, IP6_MAX_PREFIX_LENGTH, + he->src_addr.prefix, he->src_addr.pxlen); + n = net_route(tab, (net_addr *)(&trie_he_addr)); + } + else + { + net_fill_ip_host((net_addr *)&trie_he_addr, he->addr); + n = net_route(tab, (net_addr *)&trie_he_addr); + } + if (n) { rte *e = n->routes; @@ -2479,7 +2491,7 @@ rt_update_hostentry(rtable *tab, struct hostentry *he) done: /* Add a prefix range to the trie */ - trie_add_prefix(tab->hostcache->trie, &he_addr, pxlen, he_addr.pxlen); + trie_add_prefix(tab->hostcache->trie, (net_addr *)&trie_he_addr, pxlen, trie_he_addr.dst_pxlen); rta_free(old_src); return old_src != he->src; @@ -2531,6 +2543,25 @@ rt_get_hostentry(rtable *tab, ip_addr a, ip_addr ll, rtable *dep) return he; } +struct hostentry * +rt_get_sadr_hostentry(rtable *tab, net_addr_sadr_ip6* a, ip_addr ll, rtable *dep) +{ + struct hostentry *he; + + if (!tab->hostcache) + rt_init_hostcache(tab); + + u32 k = hc_hash(a->dst_prefix, dep); + struct hostcache *hc = tab->hostcache; + for (he = hc->hash_table[k >> hc->hash_shift]; he != NULL; he = he->next) + if (ipa_equal(he->addr, a->dst_prefix) && (he->tab == dep)) + return he; + + he = hc_new_hostentry(hc, a->dst_prefix, ipa_zero(ll) ? a->dst_prefix : ll, dep, k); + he->src_addr = NET_ADDR_IP6(a->src_prefix, a->src_pxlen); + rt_update_hostentry(tab, he); + return he; +} /* * CLI commands diff --git a/proto/static/config.Y b/proto/static/config.Y index 66ae3c9..3a176d4 100644 --- a/proto/static/config.Y +++ b/proto/static/config.Y @@ -67,6 +67,8 @@ static_proto: STATIC_CFG->igp_table_ip4 = $4; else if ($4->addr_type == NET_IP6) STATIC_CFG->igp_table_ip6 = $4; + else if ($4->addr_type == NET_SADR_IP6) + STATIC_CFG->igp_table_sadr_ip6 = $4; else cf_error("Incompatible IGP table type"); } @@ -109,6 +111,19 @@ stat_route0: ROUTE net_any { this_srt->mp_next = NULL; this_snh = NULL; } + | ROUTE net_ip6_ FROM net_ip6_ { + this_srt = cfg_allocz(sizeof(struct static_route)); + add_tail(&STATIC_CFG->routes, &this_srt->n); + + net_addr_sadr_ip6 *address = cfg_alloc(sizeof(net_addr_sadr_ip6)); + net_fill_sadr_ip6((net_addr *)address, + net6_prefix(&$2), net6_pxlen(&$2), net6_prefix(&$4), net6_pxlen(&$4)); + this_srt->net = (net_addr *)address; + + this_srt_last_cmd = &(this_srt->cmds); + this_srt->mp_next = NULL; + this_snh = NULL; + } ; stat_route: diff --git a/proto/static/static.c b/proto/static/static.c index f74ecee..aee3e44 100644 --- a/proto/static/static.c +++ b/proto/static/static.c @@ -89,8 +89,25 @@ static_announce_rte(struct static_proto *p, struct static_route *r) if (r->dest == RTDX_RECURSIVE) { - rtable *tab = ipa_is_ip4(r->via) ? p->igp_table_ip4 : p->igp_table_ip6; - rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls); + rtable *tab; + if (ipa_is_ip4(r->via)) + tab = p->igp_table_ip4; + else + { + if (r->net->type == NET_SADR_IP6) + tab = p->igp_table_sadr_ip6; + else + tab = p->igp_table_ip6; + } + + if (net_is_sadr(r->net)) + { + net_addr_sadr_ip6 addr = + NET_ADDR_SADR_IP6(r->via, IP6_MAX_PREFIX_LENGTH, net6_sadr_src_prefix(r->net), net6_sadr_src_pxlen(r->net)); + rta_set_recursive_sadr_next_hop(p->p.main_channel->table, a, tab, &addr, IPA_NONE, r->mls); + } + else + rta_set_recursive_next_hop(p->p.main_channel->table, a, tab, r->via, IPA_NONE, r->mls); } /* Already announced */ @@ -376,6 +393,10 @@ static_postconfig(struct proto_config *CF) cf->igp_table_ip6 = (cc->table->addr_type == NET_IP6) ? cc->table : cf->c.global->def_tables[NET_IP6]; + if (!cf->igp_table_sadr_ip6) + cf->igp_table_sadr_ip6 = (cc->table->addr_type == NET_SADR_IP6) ? + cc->table : cf->c.global->def_tables[NET_SADR_IP6]; + WALK_LIST(r, cf->routes) if (r->net && (r->net->type != CF->net_type)) cf_error("Route %N incompatible with channel type", r->net); @@ -399,6 +420,9 @@ static_init(struct proto_config *CF) if (cf->igp_table_ip6) p->igp_table_ip6 = cf->igp_table_ip6->table; + if (cf->igp_table_sadr_ip6) + p->igp_table_sadr_ip6 = cf->igp_table_sadr_ip6->table; + return P; } @@ -418,6 +442,9 @@ static_start(struct proto *P) if (p->igp_table_ip6) rt_lock_table(p->igp_table_ip6); + if (p->igp_table_sadr_ip6) + rt_lock_table(p->igp_table_sadr_ip6); + p->event = ev_new(p->p.pool); p->event->hook = static_announce_marked; p->event->data = p; @@ -457,6 +484,9 @@ static_cleanup(struct proto *P) if (p->igp_table_ip6) rt_unlock_table(p->igp_table_ip6); + + if (p->igp_table_sadr_ip6) + rt_unlock_table(p->igp_table_sadr_ip6); } static void diff --git a/proto/static/static.h b/proto/static/static.h index c84dfa9..2d919e2 100644 --- a/proto/static/static.h +++ b/proto/static/static.h @@ -19,6 +19,7 @@ struct static_config { int check_link; /* Whether iface link state is used */ struct rtable_config *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */ struct rtable_config *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */ + struct rtable_config *igp_table_sadr_ip6; /* Table for recursive SADR IPv6 next hop lookups */ }; struct static_proto { @@ -28,6 +29,7 @@ struct static_proto { BUFFER(struct static_route *) marked; /* Routes marked for reannouncement */ rtable *igp_table_ip4; /* Table for recursive IPv4 next hop lookups */ rtable *igp_table_ip6; /* Table for recursive IPv6 next hop lookups */ + rtable *igp_table_sadr_ip6; /* Table for recursive SADR IPv6 next hop lookups */ }; struct static_route { diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index d89ae10..073bf65 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1937,7 +1937,8 @@ krt_sys_start(struct krt_proto *p) { struct krt_proto *old = HASH_FIND(nl_table_map, RTH, p->af, krt_table_id(p)); - if (old) + // checking net_type as well so that ipv6 and sadr_ip6 are put in the same table + if (old && old->p.net_type == p->p.net_type) { log(L_ERR "%s: Kernel table %u already registered by %s", p->p.name, krt_table_id(p), old->p.name); -- 2.7.4
On Sun, May 21, 2017 at 11:01:35PM +0200, Dean Luga wrote:
From: dean <dluga93@gmail.com>
A new channel, sadr_ip6, is used for SADR both in the kernel and static protocols. In the static protocol, routes can be inserted with the following syntax:
route <dst_prefix> from <src_prefix> via "interface" route <dst_prefix> from <src_prefix> recursive <ip>
Hi You should add parsing of SADR networks to conf/confbase.Y together with other network types in net_ nonterminal symbol. That would add support of SADR to static procotol automatically. Also, SADR-based recursive routes seems like a whole new can of worms. And we are currently rethinking recursive routes internally. Perhaps you could postpone changes to hostentry and stick with SADR recursive routes dependent on non-SADR IGP tables, which fits to the current framework. Note that this was updated in 2.0-pre1 so that for other network types than IP4/IP6, igp tables are of type IP4/IP6. -- Elen sila lumenn' omentielvo Ondrej 'Santiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
On Sun, May 21, 2017 at 11:01:35PM +0200, Dean Luga wrote:
struct static_route { diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c index d89ae10..073bf65 100644 --- a/sysdep/linux/netlink.c +++ b/sysdep/linux/netlink.c @@ -1937,7 +1937,8 @@ krt_sys_start(struct krt_proto *p) { struct krt_proto *old = HASH_FIND(nl_table_map, RTH, p->af, krt_table_id(p));
- if (old) + // checking net_type as well so that ipv6 and sadr_ip6 are put in the same table + if (old && old->p.net_type == p->p.net_type)
This is not completely correct. If you do HASH_INSERT for both IP6 and SADR_IP6, you end up with two hash entries with the same hash key, but subsequent HASH_FIND finds (here or in nl_parse_route()) just the first table. I am not really sure what is the best way to fix this. Sure, one can change hash key to involve net_type instead of af. But IMHO this is deeper issue related to the question asked by Toke before:
It looks like a separate channel is needed for SADR routes; (right?) but can SADR and non-SADR ipv6 routes co-exist in the same FIB?
I just glimpsed at the OSPF changes and if i undertand it correctly, only external routes are SADR, while regular internal routes are non-SADR? But they are both connected by one SADR_IP6 channel to one SADR_IP6 routing table? Non-SADR internal routes are represented as SADR routes with source set to ::/0? Alternative way would be to have OSPFv3 dual-channel - one IP6 for regular routes and one SADR_IP6 for SADR routes, connected to two separate tables. But that is probably an overkill. My opinion is that behavior of OSPF and Kernel protocols should be consistent. If OSPFv3 uses one channel and one table for SADR and non-SADR routes, then Kernel should do the same and there is no reason to connect two Kernel protocols to one kernel table. -- Elen sila lumenn' omentielvo Ondrej 'Santiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
From: dean <dluga93@gmail.com> A new OSPF variant can be used to support SADR. protocol ospf v3_sadr { ... } The channel type should be the same as for the static protocol, sadr_ip6. Static routes are distributed in OSPF using external LSAs. A new LSA type was created (LSA_T_EXT_SADR 0x4025) to distribute SADR routes. The format of the LSA is similar to external LSAs, but it will contain a pair of (dst, src) prefixes instead of just dst. Since the U-bit of the LSA is not set, routers that don't support SADR will not flood the new type of LSA. Parsing, validation, prep and origination functions were added for the new type of LSA. The external path processing function handles the new type of LSA. The prefix and summary LSA processing functions in rt.c use SADR networks with ::/0 source prefix to satisfy the type assertion of fib_get. The same thing stands for the net_fib and enet_fib data members of the ospf_area structure, they are not both of type NET_SADR_IP6. --- proto/ospf/config.Y | 12 ++++-- proto/ospf/lsalib.c | 68 ++++++++++++++++++++++++++++-- proto/ospf/lsalib.h | 3 +- proto/ospf/lsupd.c | 2 +- proto/ospf/ospf.c | 69 +++++++++++++++++++++++-------- proto/ospf/ospf.h | 13 ++++++ proto/ospf/rt.c | 112 +++++++++++++++++++++++++++++++++++++++++++++----- proto/ospf/topology.c | 62 +++++++++++++++++++++++++++- 8 files changed, 304 insertions(+), 37 deletions(-) diff --git a/proto/ospf/config.Y b/proto/ospf/config.Y index 8a1e005..f732ead 100644 --- a/proto/ospf/config.Y +++ b/proto/ospf/config.Y @@ -139,7 +139,7 @@ ospf_check_auth(void) CF_DECLS -CF_KEYWORDS(OSPF, V2, V3, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID) +CF_KEYWORDS(OSPF, V2, V3, V3_SADR, OSPF_METRIC1, OSPF_METRIC2, OSPF_TAG, OSPF_ROUTER_ID) CF_KEYWORDS(AREA, NEIGHBORS, RFC1583COMPAT, STUB, TICK, COST, COST2, RETRANSMIT) CF_KEYWORDS(HELLO, TRANSMIT, PRIORITY, DEAD, TYPE, BROADCAST, BCAST, DEFAULT) CF_KEYWORDS(NONBROADCAST, NBMA, POINTOPOINT, PTP, POINTOMULTIPOINT, PTMP) @@ -161,17 +161,23 @@ ospf_variant: OSPF { $$ = 1; } | OSPF V2 { $$ = 1; } | OSPF V3 { $$ = 0; } + | OSPF V3_SADR { $$ = 2; } ; ospf_proto_start: proto_start ospf_variant { this_proto = proto_config_new(&proto_ospf, $1); - this_proto->net_type = $2 ? NET_IP4 : NET_IP6; + + switch ($2) { + case 0: this_proto->net_type = NET_IP6; break; + case 1: this_proto->net_type = NET_IP4; break; + case 2: this_proto->net_type = NET_SADR_IP6; break; + } init_list(&OSPF_CFG->area_list); init_list(&OSPF_CFG->vlink_list); OSPF_CFG->tick = OSPF_DEFAULT_TICK; - OSPF_CFG->ospf2 = $2; + OSPF_CFG->ospf2 = ($2 == 1); }; ospf_proto: diff --git a/proto/ospf/lsalib.c b/proto/ospf/lsalib.c index b88a114..c8d1064 100644 --- a/proto/ospf/lsalib.c +++ b/proto/ospf/lsalib.c @@ -11,6 +11,7 @@ #include "ospf.h" #include "lib/fletcher16.h" +#include "ospf.h" #ifndef CPU_BIG_ENDIAN void @@ -93,7 +94,7 @@ lsa_flooding_allowed(u32 type, u32 domain, struct ospf_iface *ifa) static int -unknown_lsa_type(u32 type) +unknown_lsa_type(u32 type, struct ospf_proto *p) { switch (type) { @@ -107,6 +108,9 @@ unknown_lsa_type(u32 type) case LSA_T_PREFIX: return 0; + case LSA_T_EXT_SADR: + return !ospf_is_v3_sadr(p); + default: return 1; } @@ -127,7 +131,7 @@ lsa_get_type_domain_(u32 itype, struct ospf_iface *ifa, u32 *otype, u32 *domain) else { /* For unkown LSAs without U-bit change scope to LSA_SCOPE_LINK */ - if (unknown_lsa_type(itype) && !(itype & LSA_UBIT)) + if (unknown_lsa_type(itype, ifa->oa->po) && !(itype & LSA_UBIT)) itype = itype & ~LSA_SCOPE_MASK; } @@ -353,6 +357,30 @@ lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *r } } +void +lsa_parse_ext_sadr(struct top_hash_entry *en, struct ospf_lsa_ext_sadr_local *rt) +{ + struct ospf_lsa_ext3 *ext = en->lsa_body; + + u8 dummy_opts; + net_addr_ip6 dst, src; + u32 *buf = ospf_get_ipv6_prefix(ext->rest, (net_addr *)&dst, &rt->pxopts, NULL); + buf = ospf_get_ipv6_prefix(buf, (net_addr *)&src, &dummy_opts, NULL); + rt->net = NET_ADDR_SADR_IP6(dst.prefix, dst.pxlen, src.prefix, src.pxlen); + + rt->metric = ext->metric & LSA_METRIC_MASK; + rt->ebit = ext->metric & LSA_EXT3_EBIT; + + rt->fbit = ext->metric & LSA_EXT3_FBIT; + if (rt->fbit) + buf = ospf_get_ipv6_addr(buf, &rt->fwaddr); + else + rt->fwaddr = IPA_NONE; + + rt->tag = (ext->metric & LSA_EXT3_TBIT) ? *buf++ : 0; + rt->propagate = rt->pxopts & OPT_PX_P; +} + #define HDRLEN sizeof(struct ospf_lsa_header) static int @@ -507,6 +535,35 @@ lsa_validate_ext3(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body) } static int +lsa_validate_ext3_sadr(struct ospf_lsa_header *lsa, struct ospf_lsa_ext3 *body) +{ + if (lsa->length < (HDRLEN + sizeof(struct ospf_lsa_ext3) + 8)) + return 0; + + u8 pxl = pxlen(body->rest); + if (pxl > IP6_MAX_PREFIX_LENGTH) + return 0; + + int len = IPV6_PREFIX_SPACE(pxl); + + // source prefix + int spxl = pxlen(body->rest + len/4); + len += IPV6_PREFIX_SPACE(spxl); + + if (body->metric & LSA_EXT3_FBIT) // forwarding address + len += 16; + if (body->metric & LSA_EXT3_TBIT) // route tag + len += 4; + if (*body->rest & 0xFFFF) // referenced LS type field + len += 4; + + if (lsa->length != (HDRLEN + sizeof(struct ospf_lsa_ext3) + len)) + return 0; + + return 1; +} + +static int lsa_validate_pxlist(struct ospf_lsa_header *lsa, u32 pxcount, uint offset, u8 *pbuf) { uint bound = lsa->length - HDRLEN - 4; @@ -560,9 +617,9 @@ lsa_validate_prefix(struct ospf_lsa_header *lsa, struct ospf_lsa_prefix *body) * consistency). Returns true if valid. */ int -lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body) +lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, struct ospf_proto *p, void *body) { - if (ospf2) + if (p->ospf2) { switch (lsa_type) { @@ -600,6 +657,9 @@ lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body) return lsa_validate_link(lsa, body); case LSA_T_PREFIX: return lsa_validate_prefix(lsa, body); + case LSA_T_EXT_SADR: + if (ospf_is_v3_sadr(p)) + return lsa_validate_ext3_sadr(lsa, body); default: return 1; /* Unknown LSAs are OK in OSPFv3 */ } diff --git a/proto/ospf/lsalib.h b/proto/ospf/lsalib.h index c93f029..fe25cb9 100644 --- a/proto/ospf/lsalib.h +++ b/proto/ospf/lsalib.h @@ -58,6 +58,7 @@ int lsa_walk_rt(struct ospf_lsa_rt_walk *rt); void lsa_parse_sum_net(struct top_hash_entry *en, int ospf2, net_addr *net, u8 *pxopts, u32 *metric); void lsa_parse_sum_rt(struct top_hash_entry *en, int ospf2, u32 *drid, u32 *metric, u32 *options); void lsa_parse_ext(struct top_hash_entry *en, int ospf2, struct ospf_lsa_ext_local *rt); -int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, int ospf2, void *body); +void lsa_parse_ext_sadr(struct top_hash_entry *en, struct ospf_lsa_ext_sadr_local *rt); +int lsa_validate(struct ospf_lsa_header *lsa, u32 lsa_type, struct ospf_proto *p, void *body); #endif /* _BIRD_OSPF_LSALIB_H_ */ diff --git a/proto/ospf/lsupd.c b/proto/ospf/lsupd.c index 157d962..82a955d 100644 --- a/proto/ospf/lsupd.c +++ b/proto/ospf/lsupd.c @@ -583,7 +583,7 @@ ospf_receive_lsupd(struct ospf_packet *pkt, struct ospf_iface *ifa, void *body = mb_alloc(p->p.pool, blen); lsa_ntoh_body(lsa_n + 1, body, blen); - if (lsa_validate(&lsa, lsa_type, ospf_is_v2(p), body) == 0) + if (lsa_validate(&lsa, lsa_type, p, body) == 0) { mb_free(body); SKIP("invalid body"); diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index daf76ff..fc96d48 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -114,21 +114,37 @@ add_area_nets(struct ospf_area *oa, struct ospf_area_config *ac) struct ospf_proto *p = oa->po; struct area_net_config *anc; struct area_net *an; + struct net_addr_sadr_ip6 addr; - fib_init(&oa->net_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6, + fib_init(&oa->net_fib, p->p.pool, p->p.net_type, sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL); - fib_init(&oa->enet_fib, p->p.pool, ospf_is_v2(p) ? NET_IP4 : NET_IP6, + fib_init(&oa->enet_fib, p->p.pool, p->p.net_type, sizeof(struct area_net), OFFSETOF(struct area_net, fn), 0, NULL); WALK_LIST(anc, ac->net_list) { - an = fib_get(&oa->net_fib, &anc->prefix); + if (ospf_is_v3_sadr(p)) + { + addr = NET_ADDR_SADR_IP6(net6_prefix(&anc->prefix), + anc->prefix.pxlen, IP6_NONE, 0); + an = fib_get(&oa->net_fib, (net_addr *)&addr); + } + else + an = fib_get(&oa->net_fib, &anc->prefix); + an->hidden = anc->hidden; } WALK_LIST(anc, ac->enet_list) { - an = fib_get(&oa->enet_fib, &anc->prefix); + if (ospf_is_v3_sadr(p)) + { + an = fib_get(&oa->enet_fib, (net_addr *)&addr); + addr = NET_ADDR_SADR_IP6(net6_prefix(&anc->prefix), + anc->prefix.pxlen, IP6_NONE, 0); + } + else + an = fib_get(&oa->enet_fib, &anc->prefix); an->hidden = anc->hidden; an->tag = anc->tag; } @@ -238,7 +254,7 @@ ospf_start(struct proto *P) p->nhpool = lp_new(P->pool, 12*sizeof(struct nexthop)); init_list(&(p->iface_list)); init_list(&(p->area_list)); - fib_init(&p->rtf, P->pool, p->ospf2 ? NET_IP4 : NET_IP6, + fib_init(&p->rtf, P->pool, p->p.net_type, sizeof(ort), OFFSETOF(ort, fn), 0, NULL); if (ospf_is_v3(p)) idm_init(&p->idm, P->pool, 16); @@ -1094,28 +1110,48 @@ show_lsa_sum_rt(struct top_hash_entry *he, int ospf2) cli_msg(-1016, "\t\txrouter %R metric %u", dst_rid, metric); } - static inline void show_lsa_external(struct top_hash_entry *he, int ospf2) { - struct ospf_lsa_ext_local rt; char str_via[IPA_MAX_TEXT_LENGTH + 8] = ""; char str_tag[16] = ""; - if (he->lsa_type == LSA_T_EXT) + if (he->lsa_type == LSA_T_EXT || he->lsa_type == LSA_T_EXT_SADR) he->domain = 0; /* Unmark the LSA */ - lsa_parse_ext(he, ospf2, &rt); + if (he->lsa_type == LSA_T_EXT) + { + struct ospf_lsa_ext_local rt; + lsa_parse_ext(he, ospf2, &rt); + + if (rt.fbit) + bsprintf(str_via, " via %I", rt.fwaddr); - if (rt.fbit) - bsprintf(str_via, " via %I", rt.fwaddr); + if (rt.tag) + bsprintf(str_tag, " tag %08x", rt.tag); - if (rt.tag) - bsprintf(str_tag, " tag %08x", rt.tag); + cli_msg(-1016, "\t\t%s %N metric%s %u%s%s", + (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external", + &rt.net, rt.ebit ? "2" : "", rt.metric, str_via, str_tag); + } + else + { + struct ospf_lsa_ext_sadr_local rt; + lsa_parse_ext_sadr(he, &rt); - cli_msg(-1016, "\t\t%s %N metric%s %u%s%s", - (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external", - &rt.net, rt.ebit ? "2" : "", rt.metric, str_via, str_tag); + if (rt.fbit) + bsprintf(str_via, " via %I", rt.fwaddr); + + if (rt.tag) + bsprintf(str_tag, " tag %08x", rt.tag); + + net_addr_ip6 dst = NET_ADDR_IP6(rt.net.dst_prefix, rt.net.dst_pxlen); + net_addr_ip6 src = NET_ADDR_IP6(rt.net.src_prefix, rt.net.src_pxlen); + + cli_msg(-1016, "\t\t%s %N from %N metric%s %u%s%s", + (he->lsa_type == LSA_T_NSSA) ? "nssa-ext" : "external", + &dst, &src, rt.ebit ? "2" : "", rt.metric, str_via, str_tag); + } } static inline void @@ -1286,6 +1322,7 @@ ospf_sh_state(struct proto *P, int verbose, int reachable) case LSA_T_EXT: case LSA_T_NSSA: + case LSA_T_EXT_SADR: show_lsa_external(he, ospf2); break; diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index e3eae2b..9e69bb3 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -520,6 +520,8 @@ union ospf_auth #define LSA_T_LINK 0x0008 #define LSA_T_PREFIX 0x2009 +#define LSA_T_EXT_SADR 0x4025 + #define LSA_T_V2_MASK 0x00ff #define LSA_UBIT 0x8000 @@ -679,6 +681,14 @@ struct ospf_lsa_ext_local u8 pxopts; }; +struct ospf_lsa_ext_sadr_local +{ + net_addr_sadr_ip6 net; + ip_addr fwaddr; + u32 metric, ebit, fbit, tag, propagate; + u8 pxopts; +}; + struct ospf_lsa_link { u32 options; @@ -835,6 +845,9 @@ static inline int ospf_is_v2(struct ospf_proto *p) static inline int ospf_is_v3(struct ospf_proto *p) { return ! p->ospf2; } +static inline int ospf_is_v3_sadr(struct ospf_proto *p) +{ return p->p.net_type == NET_SADR_IP6; } + static inline int ospf_get_version(struct ospf_proto *p) { return ospf_is_v2(p) ? 2 : 3; } diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index df9eb75..c0559b2 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -589,7 +589,12 @@ spfa_process_prefixes(struct ospf_proto *p, struct ospf_area *oa) if ((pxopts & OPT_PX_LA) && ipa_zero(src->lb)) src->lb = ipa_from_ip6(net.prefix); - add_network(oa, (net_addr *) &net, src->dist + metric, src, i); + if (ospf_is_v3_sadr(p)) { + net_addr_sadr_ip6 sadr_net = NET_ADDR_SADR_IP6(net.prefix, net.pxlen, IP6_NONE, 0); + add_network(oa, (net_addr *) &sadr_net, src->dist + metric, src, i); + } else { + add_network(oa, (net_addr *) &net, src->dist + metric, src, i); + } } } } @@ -817,8 +822,14 @@ ospf_rt_sum(struct ospf_area *oa) .nhs = abr->n.nhs }; - if (type == ORT_NET) - ri_install_net(p, &net, &nf); + if (type == ORT_NET) { + if (ospf_is_v3_sadr(p)) { + net_addr_sadr_ip6 sadr_net = NET_ADDR_SADR_IP6(net_prefix(&net), net_pxlen(&net), IP6_NONE, 0); + ri_install_net(p, (net_addr *)&sadr_net, &nf); + } else { + ri_install_net(p, &net, &nf); + } + } else ri_install_rt(oa, dst_rid, &nf); } @@ -1207,7 +1218,14 @@ ospf_rt_abr1(struct ospf_proto *p) else net_fill_ip6(&default_net, IP6_NONE, 0); - default_nf = fib_get(&p->rtf, &default_net); + if (ospf_is_v3_sadr(p)) + { + net_addr_sadr_ip6 sadr_default = + NET_ADDR_SADR_IP6(IP6_NONE, 0, IP6_NONE, 0); + default_nf = fib_get(&p->rtf, &sadr_default); + } + else + default_nf = fib_get(&p->rtf, &default_net); default_nf->area_net = 1; struct ospf_area *oa; @@ -1411,10 +1429,52 @@ loop: } static void * -ospf_fib_route(struct fib *f, ip_addr a) +ospf_fib_route_sadr_ip6(struct fib *f, ip6_addr a, int len, ip6_addr sa, int slen) +{ + net_addr_ip6 dst = NET_ADDR_IP6(a, len); + dst.type = NET_SADR_IP6; + + // dst search + ort *tnf; + while (tnf = fib_find(f, (net_addr *) &dst), (!tnf) && dst.pxlen) + { + if (tnf) + { + // src addr search + ort *nf; + + net_addr_sadr_ip6 full_addr = + NET_ADDR_SADR_IP6(dst.prefix, dst.pxlen, sa, slen); + + while (nf = fib_find(f, (net_addr *)&full_addr), + (!nf || !nf->n.type) && (full_addr.src_pxlen > 0)) + { + full_addr.src_pxlen--; + ip6_clrbit(&full_addr.src_prefix, full_addr.src_pxlen); + } + + if (nf) + return nf; + } + + if (dst.pxlen == 0) + break; + + dst.pxlen--; + ip6_clrbit(&dst.prefix, dst.pxlen); + } + + return NULL; +} + +static void * +ospf_fib_route(struct fib *f, ip_addr a, ip_addr sa, int slen) { if (f->addr_type == NET_IP4) return ospf_fib_route_ip4(f, ipa_to_ip4(a), IP4_MAX_PREFIX_LENGTH); + else if (f->addr_type == NET_SADR_IP6) + return ospf_fib_route_sadr_ip6(f, ipa_to_ip6(a), IP6_MAX_PREFIX_LENGTH, + ipa_to_ip6(sa), slen); else return ospf_fib_route_ip6(f, ipa_to_ip6(a), IP6_MAX_PREFIX_LENGTH); } @@ -1426,6 +1486,7 @@ ospf_ext_spf(struct ospf_proto *p) { struct top_hash_entry *en; struct ospf_lsa_ext_local rt; + net_addr_ip6 src; ort *nf1, *nf2; u32 br_metric; struct ospf_area *atmp; @@ -1437,7 +1498,8 @@ ospf_ext_spf(struct ospf_proto *p) orta nfa = {}; /* 16.4. (1) */ - if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_NSSA)) + if ((en->lsa_type != LSA_T_EXT) && (en->lsa_type != LSA_T_EXT_SADR) + && (en->lsa_type != LSA_T_NSSA)) continue; if (en->lsa.age == LSA_MAXAGE) @@ -1450,7 +1512,26 @@ ospf_ext_spf(struct ospf_proto *p) DBG("%s: Working on LSA. ID: %R, RT: %R, Type: %u\n", p->p.name, en->lsa.id, en->lsa.rt, en->lsa_type); - lsa_parse_ext(en, ospf_is_v2(p), &rt); + if (en->lsa_type == LSA_T_EXT_SADR) + { + struct ospf_lsa_ext_sadr_local rt_sadr; + lsa_parse_ext_sadr(en, &rt_sadr); + + src = NET_ADDR_IP6(rt_sadr.net.src_prefix, rt_sadr.net.src_pxlen); + net_fill_ip6(&rt.net, rt_sadr.net.dst_prefix, rt_sadr.net.dst_pxlen); + rt.fwaddr = rt_sadr.fwaddr; + rt.metric = rt_sadr.metric; + rt.ebit = rt_sadr.ebit; + rt.fbit = rt_sadr.fbit; + rt.tag = rt_sadr.tag; + rt.propagate = rt_sadr.propagate; + rt.pxopts = rt_sadr.pxopts; + } + else + { + lsa_parse_ext(en, ospf_is_v2(p), &rt); + src = NET_ADDR_IP6(IP6_NONE, 0); + } if (!ospf_valid_prefix(&rt.net)) { @@ -1469,7 +1550,7 @@ ospf_ext_spf(struct ospf_proto *p) /* If there are more areas, we already precomputed preferred ASBR entries in ospf_rt_abr1() and stored them in the backbone table. For NSSA, we examine the area to which the LSA is assigned */ - if (en->lsa_type == LSA_T_EXT) + if (en->lsa_type == LSA_T_EXT || en->lsa_type == LSA_T_EXT_SADR) atmp = ospf_main_area(p); else /* NSSA */ atmp = ospf_find_area(p, en->domain); @@ -1500,11 +1581,14 @@ ospf_ext_spf(struct ospf_proto *p) } else { - nf2 = ospf_fib_route(&p->rtf, rt.fwaddr); + if (en->lsa_type == LSA_T_EXT_SADR) + nf2 = ospf_fib_route(&p->rtf, rt.fwaddr, src.prefix, src.pxlen); + else + nf2 = ospf_fib_route(&p->rtf, rt.fwaddr, IP6_NONE, 0); if (!nf2) continue; - if (en->lsa_type == LSA_T_EXT) + if (en->lsa_type == LSA_T_EXT || en->lsa_type == LSA_T_EXT_SADR) { /* For ext routes, we accept intra-area or inter-area routes */ if ((nf2->n.type != RTS_OSPF) && (nf2->n.type != RTS_OSPF_IA)) @@ -1562,7 +1646,13 @@ ospf_ext_spf(struct ospf_proto *p) nfa.oa = atmp; /* undefined in RFC 2328 */ nfa.en = en; /* store LSA for later (NSSA processing) */ - ri_install_ext(p, &rt.net, &nfa); + if (ospf_is_v3_sadr(p)) { + net_addr_sadr_ip6 sadr_net = NET_ADDR_SADR_IP6(net_prefix(&rt.net), net_pxlen(&rt.net), + src.prefix, src.pxlen); + ri_install_ext(p, (net_addr *)&sadr_net, &nfa); + } + else + ri_install_ext(p, &rt.net, &nfa); } } diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index ce77f57..3891b40 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -1154,6 +1154,63 @@ ospf_originate_ext_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 m ospf_originate_lsa(p, &lsa); } +static inline void +prepare_ext3_sadr_lsa_body(struct ospf_proto *p, ort *nf, + u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit) +{ + struct ospf_lsa_ext3 *ext; + + net_addr_sadr_ip6 *sadr_net = (net_addr_sadr_ip6 *)(nf->fn.addr); + net_addr_ip6 dst = NET_ADDR_IP6(sadr_net->dst_prefix, sadr_net->dst_pxlen); + net_addr_ip6 src = NET_ADDR_IP6(sadr_net->src_prefix, sadr_net->src_pxlen); + + int bsize = sizeof(struct ospf_lsa_ext3) + + IPV6_PREFIX_SPACE(dst.pxlen) + + IPV6_PREFIX_SPACE(src.pxlen) + + (ipa_nonzero(fwaddr) ? 16 : 0) + + (tag ? 4 : 0); + + ext = lsab_allocz(p, bsize); + ext->metric = metric & LSA_METRIC_MASK; + u32 *buf = ext->rest; + + buf = ospf_put_ipv6_prefix(buf, (net_addr *)&dst, pbit ? OPT_PX_P : 0, 0); + buf = ospf_put_ipv6_prefix(buf, (net_addr *)&src, 0, 0); + + if (ebit) + ext->metric |= LSA_EXT3_EBIT; + + if (ipa_nonzero(fwaddr)) + { + ext->metric |= LSA_EXT3_FBIT; + buf = ospf_put_ipv6_addr(buf, fwaddr); + } + + if (tag) + { + ext->metric |= LSA_EXT3_TBIT; + *buf++ = tag; + } +} + +void +ospf_originate_ext_sadr_lsa(struct ospf_proto *p, struct ospf_area *oa, ort *nf, u8 mode, + u32 metric, u32 ebit, ip_addr fwaddr, u32 tag, int pbit) +{ + struct ospf_new_lsa lsa = { + .type = LSA_T_EXT_SADR, + .mode = mode, + .dom = oa ? oa->areaid : 0, + .id = ort_to_lsaid(p, nf), + .opts = oa ? (pbit ? OPT_P : 0) : OPT_E, + .nf = nf + }; + + prepare_ext3_sadr_lsa_body(p, nf, metric, ebit, fwaddr, tag, oa && pbit); + + ospf_originate_lsa(p, &lsa); +} + static struct top_hash_entry * ospf_hash_find_(struct top_graph *f, u32 domain, u32 lsa, u32 rtr, u32 type); @@ -1305,7 +1362,10 @@ ospf_rt_notify(struct proto *P, struct channel *ch UNUSED, net *n, rte *new, rte } nf = fib_get(&p->rtf, n->n.addr); - ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1); + if (ospf_is_v3_sadr(p) && !ip6_zero(net6_sadr_src_prefix(nf->fn.addr))) + ospf_originate_ext_sadr_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1); + else + ospf_originate_ext_lsa(p, oa, nf, LSA_M_EXPORT, metric, ebit, fwd, tag, 1); nf->external_rte = 1; } -- 2.7.4
From: dean <dluga93@gmail.com> In networks with both routers that support SADR and routers that don't, there is the possibility of routing loops. draft-ietf-rtgwg-dst-src-routing-04 describes some ways to avoid these loops. One of these ways is to detect that the path for an SADR route passes through a non-SADR router, and replace that route with a blackhole route. To be able to do this, I added a new option flag OPT_SADR (0x40) to indicate SADR capability of a router. This flag will be included in LSAs, and can be used during the shortest path calculation. SADR capability of a node can be read from its LSA. SADR capability of a path can be calculated during the next-hop calculation. If the destination node doesn't support SADR, or if the path to its parent contains non-SADR routers, the path to that destination doesn't support SADR. The SADR capability of the path to a destination is stored in the nexthop structure for that destination. To handle inter-area routes, ABRs clear the OPT_SADR bit when sending inter-area-router-LSAs if the path to the advertised router includes non-SADR routers. Then, during summary LSA processing in the reciever, the next hops for an advertised router will have the SADR capability as indicated in the OPT_SADR flag of the inter-area-router-LSA. Now that SADR capability is indicated by the nexthop, the external LSA processing function (ospf_ext_spf) uses only the SADR capable nexthops, and deletes the others. In rt_sync, I set the route's destination to RTD_BLACKHOLE. --- nest/route.h | 1 + proto/ospf/ospf.c | 6 +- proto/ospf/ospf.h | 2 + proto/ospf/rt.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++---- proto/ospf/topology.c | 8 +++ proto/ospf/topology.h | 6 ++ 6 files changed, 184 insertions(+), 12 deletions(-) diff --git a/nest/route.h b/nest/route.h index b12d208..6a946dd 100644 --- a/nest/route.h +++ b/nest/route.h @@ -367,6 +367,7 @@ struct nexthop { ip_addr gw; /* Next hop */ struct iface *iface; /* Outgoing interface */ struct nexthop *next; + u8 sadr_capability; /* Whether path through this next hop is SADR capable */ byte weight; byte labels_orig; /* Number of labels before hostentry was applied */ byte labels; /* Number of all labels */ diff --git a/proto/ospf/ospf.c b/proto/ospf/ospf.c index fc96d48..9e08e49 100644 --- a/proto/ospf/ospf.c +++ b/proto/ospf/ospf.c @@ -174,7 +174,8 @@ ospf_area_add(struct ospf_proto *p, struct ospf_area_config *ac) if (ospf_is_v2(p)) oa->options = ac->type; else - oa->options = ac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R); + oa->options = ac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R) + | (ospf_is_v3_sadr(p) ? OPT_SADR : 0); ospf_notify_rt_lsa(oa); } @@ -620,6 +621,9 @@ ospf_area_reconfigure(struct ospf_area *oa, struct ospf_area_config *nac) if (ospf_is_v2(p)) oa->options = nac->type; + else if (ospf_is_v3_sadr(p)) + oa->options = nac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R) + | (ospf_is_v3_sadr(p) ? OPT_SADR : 0); else oa->options = nac->type | OPT_V6 | (p->stub_router ? 0 : OPT_R); diff --git a/proto/ospf/ospf.h b/proto/ospf/ospf.h index 9e69bb3..e1d895c 100644 --- a/proto/ospf/ospf.h +++ b/proto/ospf/ospf.h @@ -458,6 +458,8 @@ struct ospf_neighbor #define OPT_R 0x10 /* OSPFv3, originator is active router */ #define OPT_DC 0x20 /* Related to demand circuits, not used */ +#define OPT_SADR 0x40 /* SADR capability bit */ + /* Router-LSA VEB flags are are stored together with links (OSPFv2) or options (OSPFv3) */ #define OPT_RT_B (0x01 << 24) #define OPT_RT_E (0x02 << 24) diff --git a/proto/ospf/rt.c b/proto/ospf/rt.c index c0559b2..2490687 100644 --- a/proto/ospf/rt.c +++ b/proto/ospf/rt.c @@ -40,6 +40,7 @@ new_nexthop(struct ospf_proto *p, ip_addr gw, struct iface *iface, byte weight) nh->gw = gw; nh->iface = iface; nh->weight = weight; + nh->sadr_capability = 0; return nh; } @@ -64,7 +65,12 @@ fix_device_nexthops(struct ospf_proto *p, const struct nexthop *n, ip_addr gw) struct nexthop **nn2 = &root2; if (!p->ecmp) - return new_nexthop(p, gw, n->iface, n->weight); + { + struct nexthop *nh = new_nexthop(p, gw, n->iface, n->weight); + if (ospf_is_v3_sadr(p)) + nh->sadr_capability = n->sadr_capability; + return nh; + } /* This is a bit tricky. We cannot just copy the list and update n->gw, because the list should stay sorted, so we create two lists, one with new @@ -73,6 +79,8 @@ fix_device_nexthops(struct ospf_proto *p, const struct nexthop *n, ip_addr gw) for (; n; n = n->next) { struct nexthop *nn = new_nexthop(p, ipa_zero(n->gw) ? gw : n->gw, n->iface, n->weight); + if (ospf_is_v3_sadr(p)) + nn->sadr_capability = n->sadr_capability; if (ipa_zero(n->gw)) { @@ -731,6 +739,41 @@ link_back(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry return 0; } +/* Returns a copy of abr_nhs but with sadr_capability set to 0 */ +static struct nexthop * +copy_nhs_no_sadr(struct ospf_proto *p, struct nexthop *abr_nhs) +{ + struct nexthop *ret = NULL; + + if (!p->ecmp) + ret = new_nexthop(p, abr_nhs->gw, abr_nhs->iface, + abr_nhs->weight); + else + { + struct nexthop *nf_nh = NULL, *abr_nh = abr_nhs; + do + { + if (!nf_nh) + { + nf_nh = new_nexthop(p, abr_nhs->gw, abr_nhs->iface, + abr_nhs->weight); + ret = nf_nh; + abr_nh = abr_nh->next; + } + else + { + nf_nh->next = new_nexthop(p, abr_nh->gw, abr_nh->iface, + abr_nh->weight); + nf_nh = nf_nh->next; + abr_nh = abr_nh->next; + } + } while (abr_nh); + + nf_nh->next = NULL; + } + + return ret; +} /* RFC 2328 16.2. calculating inter-area routes */ static void @@ -822,6 +865,11 @@ ospf_rt_sum(struct ospf_area *oa) .nhs = abr->n.nhs }; + /* if a router's SADR capability is not set in the summary LSA, the abr + doesn't have an SADR path to that router */ + if (ospf_is_v3_sadr(p) && !(options & OPT_SADR)) + nf.nhs = copy_nhs_no_sadr(p, abr->n.nhs); + if (type == ORT_NET) { if (ospf_is_v3_sadr(p)) { net_addr_sadr_ip6 sadr_net = NET_ADDR_SADR_IP6(net_prefix(&net), net_pxlen(&net), IP6_NONE, 0); @@ -1222,7 +1270,7 @@ ospf_rt_abr1(struct ospf_proto *p) { net_addr_sadr_ip6 sadr_default = NET_ADDR_SADR_IP6(IP6_NONE, 0, IP6_NONE, 0); - default_nf = fib_get(&p->rtf, &sadr_default); + default_nf = fib_get(&p->rtf, (net_addr *)&sadr_default); } else default_nf = fib_get(&p->rtf, &default_net); @@ -1479,6 +1527,28 @@ ospf_fib_route(struct fib *f, ip_addr a, ip_addr sa, int slen) return ospf_fib_route_ip6(f, ipa_to_ip6(a), IP6_MAX_PREFIX_LENGTH); } +static struct nexthop * +copy_sadr_nh(struct ospf_proto *p, struct nexthop *src) +{ + struct nexthop *nh = src; + struct nexthop *dst = NULL; + + if (!p->ecmp && nh->sadr_capability) + { + dst = new_nexthop(p, nh->gw, nh->iface, nh->weight); + dst->sadr_capability = 1; + return dst; + } + + while (nh) + { + if (nh->sadr_capability) + dst = nexthop_merge(dst, new_nexthop(p, nh->gw, nh->iface, nh->weight), 1, 1, p->ecmp, NULL); + nh = nh->next; + } + + return dst; +} /* RFC 2328 16.4. calculating external routes */ static void @@ -1576,7 +1646,13 @@ ospf_ext_spf(struct ospf_proto *p) if (!rt.fbit) { nf2 = nf1; - nfa.nhs = nf1->n.nhs; + + // if this is an EXT SADR lsa, we only keep the nexthops with sadr capability + if (en->lsa_type == LSA_T_EXT_SADR) + nfa.nhs = copy_sadr_nh(p, nf1->n.nhs); + else + nfa.nhs = nf1->n.nhs; + br_metric = nf1->n.metric1; } else @@ -1605,7 +1681,12 @@ ospf_ext_spf(struct ospf_proto *p) if (!nf2->n.nhs) continue; - nfa.nhs = nf2->n.nhs; + // if this is an EXT SADR lsa, we only keep the nexthops with sadr capability + if (en->lsa_type == LSA_T_EXT_SADR) + nfa.nhs = copy_sadr_nh(p, nf2->n.nhs); + else + nfa.nhs = nf2->n.nhs; + br_metric = nf2->n.metric1; /* Replace device nexthops with nexthops to forwarding address from LSA */ @@ -1769,6 +1850,26 @@ inherit_nexthops(struct nexthop *pn) return pn && (ipa_nonzero(pn->gw) || !pn->iface); } +static int +calc_nh_sadr_capability(struct top_hash_entry *par, struct top_hash_entry *en) +{ + int parent_capable = 0; + + struct nexthop *nh = par->nhs; + while (nh) + { + if (nh->sadr_capability) + { + parent_capable = 1; + break; + } + + nh = nh->next; + } + + return ospf_entry_sadr_capable(en) && parent_capable; +} + static struct nexthop * calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, struct top_hash_entry *par, int pos) @@ -1784,7 +1885,25 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, /* Usually, we inherit parent nexthops */ if (inherit_nexthops(pn)) + { + // turn off sadr capability if the next node doesn't support SADR + if (ospf_is_v3_sadr(p) && !ospf_entry_sadr_capable(en)) + { + if (!p->ecmp) + return pn->sadr_capability ? new_nexthop(p, pn->gw, pn->iface, pn->weight) : pn; + + struct nexthop *nh = nexthop_merge(pn, NULL, 0, 0, p->ecmp, p->nhpool); + while (nh) + { + nh->sadr_capability = 0; + nh = nh->next; + } + + return nh; + } + return pn; + } /* * There are three cases: @@ -1800,7 +1919,12 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, if (!ifa) return NULL; - return new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight); + struct nexthop *nh = new_nexthop(p, IPA_NONE, ifa->iface, ifa->ecmp_weight); + + if (ospf_is_v3_sadr(p)) + nh->sadr_capability = calc_nh_sadr_capability(par, en); + + return nh; } /* The second case - ptp or ptmp neighbor */ @@ -1811,13 +1935,19 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, return NULL; if (ifa->type == OSPF_IT_VLINK) - return new_nexthop(p, IPA_NONE, NULL, 0); + { + struct nexthop *nh = new_nexthop(p, IPA_NONE, NULL, 0); + return nh; + } struct ospf_neighbor *m = find_neigh(ifa, rid); if (!m || (m->state != NEIGHBOR_FULL)) return NULL; - return new_nexthop(p, m->ip, ifa->iface, ifa->ecmp_weight); + struct nexthop *nh = new_nexthop(p, m->ip, ifa->iface, ifa->ecmp_weight); + if (ospf_is_v3_sadr(p)) + nh->sadr_capability = ospf_entry_sadr_capable(en); + return nh; } /* The third case - bcast or nbma neighbor */ @@ -1855,7 +1985,10 @@ calc_next_hop(struct ospf_area *oa, struct top_hash_entry *en, if (ip6_zero(llsa->lladdr)) return NULL; - return new_nexthop(p, ipa_from_ip6(llsa->lladdr), pn->iface, pn->weight); + struct nexthop *nh = new_nexthop(p, ipa_from_ip6(llsa->lladdr), pn->iface, pn->weight); + if (ospf_is_v3_sadr(p)) + nh->sadr_capability = calc_nh_sadr_capability(par, en); + return nh; } } @@ -2030,11 +2163,28 @@ again1: } } - /* Remove configured stubnets but keep the entries */ if (nf->n.type && !nf->n.nhs) { - reset_ri(nf); - nf->keep = 1; + if (nf->n.type == RTS_OSPF_EXT2) + { + rta a0 = { + .src = p->p.main_source, + .source = nf->n.type, + .scope = SCOPE_UNIVERSE, + .dest = RTD_BLACKHOLE, + }; + + rta *a = rta_lookup(&a0); + rte *e = rte_get_temp(a); + rte_update(&p->p, nf->fn.addr, e); + + goto blackholed_sadr; + } + else /* Remove configured stubnets but keep the entries */ + { + reset_ri(nf); + nf->keep = 1; + } } if (nf->n.type) /* Add the route */ @@ -2085,6 +2235,7 @@ again1: goto again1; } } +blackholed_sadr: FIB_ITERATE_END; diff --git a/proto/ospf/topology.c b/proto/ospf/topology.c index 3891b40..dcc8d09 100644 --- a/proto/ospf/topology.c +++ b/proto/ospf/topology.c @@ -1056,7 +1056,15 @@ ospf_originate_sum_rt_lsa(struct ospf_proto *p, struct ospf_area *oa, u32 drid, if (ospf_is_v2(p)) prepare_sum2_lsa_body(p, 0, metric); else + { + // unset the sadr capability flag if there's no sadr path to dst + net_addr_ip4 dst_rid = net_from_rid(drid); + ort *dst_rt = fib_find(&oa->rtr, (net_addr *)&dst_rid); + if (dst_rt && !dst_rt->n.nhs->sadr_capability) + options &= ~OPT_SADR; + prepare_sum3_rt_lsa_body(p, drid, metric, options & LSA_OPTIONS_MASK); + } ospf_originate_lsa(p, &lsa); } diff --git a/proto/ospf/topology.h b/proto/ospf/topology.h index d1682c5..1d32f9c 100644 --- a/proto/ospf/topology.h +++ b/proto/ospf/topology.h @@ -201,6 +201,12 @@ static inline struct top_hash_entry * ospf_hash_find_entry(struct top_graph *f, static inline struct top_hash_entry * ospf_hash_get_entry(struct top_graph *f, struct top_hash_entry *en) { return ospf_hash_get(f, en->domain, en->lsa.id, en->lsa.rt, en->lsa_type); } +static inline int ospf_entry_sadr_capable(struct top_hash_entry *en) +{ if (en->lsa_type == LSA_T_NET) return (((struct ospf_lsa_net *)en->lsa_body)->optx & OPT_SADR) ? 1 : 0; + else if (en->lsa_type == LSA_T_RT) return (((struct ospf_lsa_rt *)en->lsa_body)->options & OPT_SADR) ? 1 : 0; + else return 0; +} + struct top_hash_entry * ospf_hash_find_rt(struct top_graph *f, u32 domain, u32 rtr); struct top_hash_entry * ospf_hash_find_rt3_first(struct top_graph *f, u32 domain, u32 rtr); struct top_hash_entry * ospf_hash_find_rt3_next(struct top_hash_entry *e); -- 2.7.4
On Sun, May 21, 2017 at 11:01:34PM +0200, Dean Luga wrote:
From: dean <dluga93@gmail.com>
This patch adds a new network of type NET_SADR_IP6, including new structures, constants, and switch cases for the new network type. Some existing functions are duplicates to handle the new network type, and netlink can now handle SADR routes.
The net_route_sadr_ip6 function is a bit of a hack. Routing in SADR is supposed to match the destination address first, then out of the entries with that dst, it should choose the most specific matching src address.
Well, net_route() in integrated branch is a bit of a hack generally. It was carried over from plain-IP branch, it needs some redesign to better handle net types different than NET_IP.
For the destination-only part, I use a net_addr_ip6 as the destination prefix. Its type is changed to NET_SADR_IP6 to satisfy the assertion in fib_find. The fib_find function checks the length of the prefix to distinguish between destination-only or full SADR searches. --- lib/net.c | 34 ++++++++++++++++++++++++++ lib/net.h | 66 +++++++++++++++++++++++++++++++++++++++++++++++--- nest/rt-fib.c | 16 ++++++++++++ nest/rt-table.c | 35 +++++++++++++++++++++++++- sysdep/linux/netlink.c | 26 ++++++++++++++++++-- sysdep/unix/krt.c | 4 ++- 6 files changed, 174 insertions(+), 7 deletions(-)
int +net_compare_sadr_ip6(const net_addr_sadr_ip6 *a, const net_addr_sadr_ip6 *b) +{ + int dst_cmp = ip6_compare(a->dst_prefix, b->dst_prefix) ?: uint_cmp(a->dst_pxlen, b->dst_pxlen); + if (dst_cmp) + return dst_cmp; + else + return ip6_compare(a->src_prefix, b->src_prefix) ?: uint_cmp(a->src_pxlen, b->src_pxlen); +} + +int net_compare(const net_addr *a, const net_addr *b) { if (a->type != b->type) @@ -158,6 +177,8 @@ net_compare(const net_addr *a, const net_addr *b) return net_compare_flow6((const net_addr_flow6 *) a, (const net_addr_flow6 *) b); case NET_MPLS: return net_compare_mpls((const net_addr_mpls *) a, (const net_addr_mpls *) b); + case NET_SADR_IP6: + return net_compare_sadr_ip6((const net_addr_sadr_ip6 *) a, (const net_addr_sadr_ip6 *) b); } return 0; } @@ -178,6 +199,7 @@ net_hash(const net_addr *n) case NET_FLOW4: return NET_HASH(n, flow4); case NET_FLOW6: return NET_HASH(n, flow6); case NET_MPLS: return NET_HASH(n, mpls); + case NET_SADR_IP6: return NET_HASH(n, ip6); default: bug("invalid type"); } }
This should be NET_HASH(n, sadr_ip6) even if it is the same function.
diff --git a/lib/net.h b/lib/net.h index 332f4c9..22eee60 100644 --- a/lib/net.h +++ b/lib/net.h @@ -12,7 +12,6 @@
#include "lib/ip.h"
- #define NET_IP4 1 #define NET_IP6 2 #define NET_VPN4 3 @@ -22,7 +21,8 @@ #define NET_FLOW4 7 #define NET_FLOW6 8 #define NET_MPLS 9 -#define NET_MAX 10 +#define NET_SADR_IP6 10 +#define NET_MAX 11
I would prefer that NET_SADR_IP6 would be before NET_MPLS, as it is still IP-family net type,
#define NB_IP4 (1 << NET_IP4) #define NB_IP6 (1 << NET_IP6) @@ -33,8 +33,9 @@ #define NB_FLOW4 (1 << NET_FLOW4) #define NB_FLOW6 (1 << NET_FLOW6) #define NB_MPLS (1 << NET_MPLS) +#define NB_SADR (1 << NET_SADR_IP6)
-#define NB_IP (NB_IP4 | NB_IP6) +#define NB_IP (NB_IP4 | NB_IP6 | NB_SADR)
No, NB_IP should stay as is. It is used by protocols to specify what network types they support and they do not get SADR support automatically.
#define NB_VPN (NB_VPN4 | NB_VPN6) #define NB_FLOW (NB_FLOW4 | NB_FLOW6) #define NB_DEST (NB_IP | NB_VPN | NB_MPLS)
But you should add it to NB_DEST, so destinations are allowed.
@@ -120,6 +121,15 @@ typedef struct net_addr_mpls { u32 label; } net_addr_mpls;
+typedef struct net_addr_sadr_ip6 { + u8 type; + u8 dst_pxlen; + u16 length; + ip6_addr dst_prefix; + u32 src_pxlen; // if u8, NET_ADDR_SADR_IP6 would leave non-zero padding + ip6_addr src_prefix; +} net_addr_sadr_ip6; + typedef union net_addr_union { net_addr n; net_addr_ip4 ip4; @@ -131,6 +141,7 @@ typedef union net_addr_union { net_addr_flow4 flow4; net_addr_flow6 flow6; net_addr_mpls mpls; + net_addr_sadr_ip6 sadr_ip6; } net_addr_union;
@@ -169,6 +180,9 @@ extern const u16 net_max_text_length[]; #define NET_ADDR_MPLS(label) \ ((net_addr_mpls) { NET_MPLS, 20, sizeof(net_addr_mpls), label })
static inline ip4_addr net4_prefix(const net_addr *a) { return ((net_addr_ip4 *) a)->prefix; } @@ -243,6 +263,12 @@ static inline ip4_addr net4_prefix(const net_addr *a) static inline ip6_addr net6_prefix(const net_addr *a) { return ((net_addr_ip6 *) a)->prefix; }
+static inline ip6_addr net6_sadr_dst_prefix(const net_addr *a) +{ return ((net_addr_sadr_ip6 *) a)->dst_prefix; } + +static inline ip6_addr net6_sadr_src_prefix(const net_addr *a) +{ return ((net_addr_sadr_ip6 *) a)->src_prefix; } +
These accessors seems pointless. dst_prefix is the same as net6_prefix() (assuming net_addr_sadr_ip6 extends net_addr_ip6 like other IPv6-based nettypes) and src_prefix is not used outside specialized code, which would have net typed as net_addr_sadr_ip6 and could access src_prefix directly.
static inline ip_addr net_prefix(const net_addr *a) { switch (a->type) @@ -259,6 +285,9 @@ static inline ip_addr net_prefix(const net_addr *a) case NET_FLOW6: return ipa_from_ip6(net6_prefix(a));
+ case NET_SADR_IP6: + return ipa_from_ip6(net6_sadr_dst_prefix(a)); +
You could handle that by net6_prefix() together with other IPv6-based nettypes.
case NET_MPLS: default: return IPA_NONE; @@ -279,6 +308,12 @@ static inline uint net4_pxlen(const net_addr *a) static inline uint net6_pxlen(const net_addr *a) { return a->pxlen; }
+static inline uint net6_sadr_dst_pxlen(const net_addr *a) +{ return ((net_addr_sadr_ip6 *) a)->dst_pxlen; } + +static inline uint net6_sadr_src_pxlen(const net_addr *a) +{ return ((net_addr_sadr_ip6 *) a)->src_pxlen; } +
Like net6_sadr_*_prefix
@@ -327,6 +362,13 @@ static inline int net_equal_flow6(const net_addr_flow6 *a, const net_addr_flow6 static inline int net_equal_mpls(const net_addr_mpls *a, const net_addr_mpls *b) { return !memcmp(a, b, sizeof(net_addr_mpls)); }
+typedef net_addr_ip6 net_addr_sadr_dst; +static inline int net_equal_sadr_dst(const net_addr_ip6 *a, const net_addr_ip6 *b) +{ return net_equal_ip6(a, b); } + +static inline int net_equal_sadr_ip6(const net_addr_sadr_ip6 *a, const net_addr_sadr_ip6 *b) +{ return !memcmp(a, b, sizeof(net_addr_sadr_ip6)); } +
@@ -455,6 +502,12 @@ static inline u32 net_hash_flow6(const net_addr_flow6 *n) static inline u32 net_hash_mpls(const net_addr_mpls *n) { return n->label; }
+static inline u32 net_hash_sadr_dst(const net_addr_sadr_dst* n) +{ return net_hash_ip6(n); } + +static inline u32 net_hash_sadr_ip6(const net_addr_sadr_ip6 *n) +{ return ip6_hash(n->dst_prefix) ^ ((u32) n->dst_pxlen << 26); } +
Perhaps unnecessary, see my comment to fib_find() below
@@ -231,6 +232,20 @@ fib_find(struct fib *f, const net_addr *a) case NET_ROA6: return FIB_FIND(f, a, roa6); case NET_FLOW4: return FIB_FIND(f, a, flow4); case NET_FLOW6: return FIB_FIND(f, a, flow6); + case NET_SADR_IP6: + { + if (a->length != sizeof(net_addr_sadr_ip6)) + { + // dst only search + net_addr_ip6 a0; + net_copy((net_addr *)&a0, a); + a0.length = sizeof(net_addr_sadr_ip6); + + return FIB_FIND(f, &a0, sadr_dst); + } + else + return FIB_FIND(f, a, sadr_ip6); + }
No. Generic fib_find() should implement exact-match search with matching net_addr type. If you need/want partial match, it should be done as a separate function. BTW, where do you need that? Perhaps we should add some generic function partial-match IP-based search for network types that support it (e.g., ROA, SADR). But it would need a different interface, so caller could enumerate all valid matches.
+static inline void * +net_route_sadr_ip6(rtable *t, net_addr_sadr_ip6 *n) +{ + net *r; + + net_addr_ip6 dst = NET_ADDR_IP6(n->dst_prefix, n->dst_pxlen); + dst.type = NET_SADR_IP6; + + // search for matching dst + while (r = net_route_ip6(t, &dst)) + { + // found a matching dst, start search for entries that match both prefixes + net_addr_sadr_ip6 full_addr = + NET_ADDR_SADR_IP6(dst.prefix, dst.pxlen, n->src_prefix, n->src_pxlen); + + while (r = net_find_valid(t, (net_addr *) &full_addr), (!r) && (full_addr.src_pxlen > 0)) + { + full_addr.src_pxlen--; + ip6_clrbit(&full_addr.src_prefix, full_addr.src_pxlen); + } + + if (r) + return r; + dst.pxlen--;
You should set dst.pxlen based on r found by net_route_ip6(), to avoid repeated iteration over the same prefix lengths that did not match in the inner cycle. Also be sure that pxlen is not already zero.
@@ -1747,6 +1779,7 @@ rt_preconfig(struct config *c)
rt_new_table(cf_get_symbol("master4"), NET_IP4); rt_new_table(cf_get_symbol("master6"), NET_IP6); + rt_new_table(cf_get_symbol("master_sadr6"), NET_SADR_IP6); }
No, we have only IPv4 and IPv6 master table defined by default. Other tables are defined in config file if necessary.
@@ -1980,7 +2013,7 @@ rt_new_table(struct symbol *s, uint addr_type) /* Hack that allows to 'redefine' the master table */ if ((s->class == SYM_TABLE) && (s->def == new_config->def_tables[addr_type]) && - ((addr_type == NET_IP4) || (addr_type == NET_IP6))) + ((addr_type == NET_IP4) || (addr_type == NET_IP6) || (addr_type == NET_SADR_IP6))) return s->def;
ditto
struct rtable_config *c = cfg_allocz(sizeof(struct rtable_config)); diff --git a/sysdep/linux/netlink.c b/sysdep/linux/netlink.c
I will check KRT/Netlink code later. -- Elen sila lumenn' omentielvo Ondrej 'Santiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
On Sun, May 21, 2017 at 11:01:34PM +0200, Dean Luga wrote:
@@ -1394,7 +1404,8 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) struct rtattr *a[BIRD_RTA_MAX]; int new = h->nlmsg_type == RTM_NEWROUTE;
- net_addr dst; + net_addr dst, sadr_src; + net_fill_ip6(&sadr_src, IP6_NONE, 0); u32 oif = ~0; u32 table_id; u32 priority = 0; @@ -1420,6 +1431,9 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) if (!nl_parse_attrs(RTM_RTA(i), rtm_attr_want6, a, sizeof(a))) return;
+ if (a[RTA_SRC]) + net_fill_ip6(&sadr_src, rta_get_ip6(a[RTA_SRC]), i->rtm_src_len); + if (a[RTA_DST]) net_fill_ip6(&dst, rta_get_ip6(a[RTA_DST]), i->rtm_dst_len); else @@ -1496,7 +1510,15 @@ nl_parse_route(struct nl_parse_state *s, struct nlmsghdr *h) src = KRT_SRC_ALIEN; }
- net *net = net_get(p->p.main_channel->table, &dst); + net *net; + if (p->p.main_channel->table->addr_type == NET_SADR_IP6) + { + net_addr_sadr_ip6 sadr_addr = + NET_ADDR_SADR_IP6(net_prefix(&dst), net_pxlen(&dst), net_prefix(&sadr_src), net_pxlen(&sadr_src)); + net = net_get(p->p.main_channel->table, (net_addr *)&sadr_addr); + } + else + net = net_get(p->p.main_channel->table, &dst);
IMHO there should be check that if the found protocol is not SADR, then routes with RTA_SRC other than ::/0 should be ignored, in the same way like routes with IIF or ToS are ignored: if (a[RTA_IIF]) SKIP("IIF set\n"); if (i->rtm_tos != 0) /* We don't support TOS */ SKIP("TOS %02x\n", i->rtm_tos); -- Elen sila lumenn' omentielvo Ondrej 'Santiago' Zajicek (email: santiago@crfreenet.org) OpenPGP encrypted e-mails preferred (KeyID 0x11DEADC3, wwwkeys.pgp.net) "To err is human -- to blame it on a computer is even more so."
participants (2)
-
Dean Luga -
Ondrej Zajicek