Merge branch 'master' of https://github.com/frrouting/frr into evpn-ipv6-tenant-routing

Conflicts:
	zebra/zserv.c
This commit is contained in:
vivek 2018-03-06 22:19:24 +00:00
commit bfd498f0da
29 changed files with 668 additions and 557 deletions

View file

@ -492,6 +492,36 @@ In all cases, compatibility pieces should be marked with compiler/preprocessor
annotations to print warnings at compile time, pointing to the appropriate annotations to print warnings at compile time, pointing to the appropriate
update path. A `-Werror` build should fail if compatibility bits are used. update path. A `-Werror` build should fail if compatibility bits are used.
### Release Process/Schedule
FRR employs a <MAJOR>.<MINOR>.<BUGFIX> versioning scheme.
* MAJOR - Significant new features or multiple minor features
A example of a MAJOR feature is a New Routing Protocol
* MINOR - Smaller Features
A example of a MINOR feature is the addition of the BGP Shutdown feature.
* BUGFIX - Fixes for actual bugs and/or security issues.
We will pull a new development branch for the next release every 4 months.
The current schedule is Feb/June/October 1. The decision for a MAJOR/MINOR
release is made at the time of branch pull based on what has been received
the previous 4 months. The branch name will be dev/MAJOR.MINOR. At
this point in time the master branch configure.ac and packaging systems
will be updated to reflect the next possible release name to allow
for easy distinguishing. Additionally the new dev branch will have
these files updated too.
After one month the development branch will be renamed to
stable/MAJOR.MINOR. This process is not held up unless a crash or
security issue has been found and needs to be addressed. Issues
being fixed will not cause a delay.
Bug fix releases are at 1 month intervals until next MAJOR.MINOR is
pulled. Then at that time as needed for issues filed.
Security issues are fixed for 1 year minimum on old releases and
normal bug fixes for the current and previous release
### Miscellaneous ### Miscellaneous
When in doubt, follow the guidelines in the Linux kernel style guide, or ask on When in doubt, follow the guidelines in the Linux kernel style guide, or ask on

View file

@ -1754,8 +1754,10 @@ static int delete_routes_for_vni(struct bgp *bgp, struct bgpevpn *vpn)
} }
/* /*
* There is a tunnel endpoint IP address change for this VNI, * There is a tunnel endpoint IP address change for this VNI, delete
* need to re-advertise routes with the new nexthop. * prior type-3 route (if needed) and update.
* Note: Route re-advertisement happens elsewhere after other processing
* other changes.
*/ */
static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn, static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn,
struct in_addr originator_ip) struct in_addr originator_ip)
@ -1783,7 +1785,7 @@ static int handle_tunnel_ip_change(struct bgp *bgp, struct bgpevpn *vpn,
/* Update the tunnel IP and re-advertise all routes for this VNI. */ /* Update the tunnel IP and re-advertise all routes for this VNI. */
vpn->originator_ip = originator_ip; vpn->originator_ip = originator_ip;
return update_routes_for_vni(bgp, vpn); return 0;
} }
/* /*
@ -3253,15 +3255,25 @@ void bgp_evpn_withdraw_type5_routes(struct bgp *bgp_vrf,
{ {
struct bgp_table *table = NULL; struct bgp_table *table = NULL;
struct bgp_node *rn = NULL; struct bgp_node *rn = NULL;
struct bgp_info *ri;
/* Bail out early if we don't have to advertise type-5 routes. */ /* Bail out early if we don't have to advertise type-5 routes. */
if (!advertise_type5_routes(bgp_vrf, afi)) if (!advertise_type5_routes(bgp_vrf, afi))
return; return;
table = bgp_vrf->rib[afi][safi]; table = bgp_vrf->rib[afi][safi];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
bgp_evpn_withdraw_type5_route(bgp_vrf, &rn->p, afi, safi); /* Only care about "selected" routes - non-imported. */
/* TODO: Support for AddPath for EVPN. */
for (ri = rn->info; ri; ri = ri->next) {
if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED) &&
(!ri->extra || !ri->extra->parent)) {
bgp_evpn_withdraw_type5_route(bgp_vrf, &rn->p,
afi, safi);
break;
}
}
}
} }
/* /*
@ -3282,10 +3294,6 @@ void bgp_evpn_advertise_type5_route(struct bgp *bgp_vrf, struct prefix *p,
if (!advertise_type5_routes(bgp_vrf, afi)) if (!advertise_type5_routes(bgp_vrf, afi))
return; return;
/* only advertise subnet routes as type-5 */
if (is_host_route(p))
return;
build_type5_prefix_from_ip_prefix(&evp, p); build_type5_prefix_from_ip_prefix(&evp, p);
ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr); ret = update_evpn_type5_route(bgp_vrf, &evp, src_attr);
if (ret) if (ret)
@ -3313,11 +3321,12 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf,
table = bgp_vrf->rib[afi][safi]; table = bgp_vrf->rib[afi][safi];
for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) { for (rn = bgp_table_top(table); rn; rn = bgp_route_next(rn)) {
/* Need to identify the "selected" route entry to use its /* Need to identify the "selected" route entry to use its
* attribute. * attribute. Also, we only consider "non-imported" routes.
* TODO: Support for AddPath for EVPN. * TODO: Support for AddPath for EVPN.
*/ */
for (ri = rn->info; ri; ri = ri->next) { for (ri = rn->info; ri; ri = ri->next) {
if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED)) { if (CHECK_FLAG(ri->flags, BGP_INFO_SELECTED) &&
(!ri->extra || !ri->extra->parent)) {
/* apply the route-map */ /* apply the route-map */
if (bgp_vrf->adv_cmd_rmap[afi][safi].map) { if (bgp_vrf->adv_cmd_rmap[afi][safi].map) {
@ -3330,7 +3339,6 @@ void bgp_evpn_advertise_type5_routes(struct bgp *bgp_vrf,
if (ret == RMAP_DENYMATCH) if (ret == RMAP_DENYMATCH)
continue; continue;
} }
bgp_evpn_advertise_type5_route(bgp_vrf, &rn->p, bgp_evpn_advertise_type5_route(bgp_vrf, &rn->p,
ri->attr, ri->attr,
afi, safi); afi, safi);
@ -4457,8 +4465,8 @@ int bgp_evpn_local_vni_del(struct bgp *bgp, vni_t vni)
} }
/* /*
* Handle add (or update) of a local VNI. The only VNI change we care * Handle add (or update) of a local VNI. The VNI changes we care
* about is change to local-tunnel-ip. * about are for the local-tunnel-ip and the (tenant) VRF.
*/ */
int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni, int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
struct in_addr originator_ip, struct in_addr originator_ip,
@ -4476,24 +4484,31 @@ int bgp_evpn_local_vni_add(struct bgp *bgp, vni_t vni,
vpn = bgp_evpn_lookup_vni(bgp, vni); vpn = bgp_evpn_lookup_vni(bgp, vni);
if (vpn) { if (vpn) {
/* update tenant_vrf_id if required */
if (vpn->tenant_vrf_id != tenant_vrf_id) {
bgpevpn_unlink_from_l3vni(vpn);
vpn->tenant_vrf_id = tenant_vrf_id;
bgpevpn_link_to_l3vni(vpn);
/* update all routes with new export RT for VRFs */
update_routes_for_vni(bgp, vpn);
}
if (is_vni_live(vpn) if (is_vni_live(vpn)
&& IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)) && IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip)
&& vpn->tenant_vrf_id == tenant_vrf_id)
/* Probably some other param has changed that we don't /* Probably some other param has changed that we don't
* care about. */ * care about. */
return 0; return 0;
/* Local tunnel endpoint IP address has changed */ /* Update tenant_vrf_id if it has changed. */
handle_tunnel_ip_change(bgp, vpn, originator_ip); if (vpn->tenant_vrf_id != tenant_vrf_id) {
bgpevpn_unlink_from_l3vni(vpn);
vpn->tenant_vrf_id = tenant_vrf_id;
bgpevpn_link_to_l3vni(vpn);
}
/* If tunnel endpoint IP has changed, update (and delete prior
* type-3 route, if needed.)
*/
if (!IPV4_ADDR_SAME(&vpn->originator_ip, &originator_ip))
handle_tunnel_ip_change(bgp, vpn, originator_ip);
/* Update all routes with new endpoint IP and/or export RT
* for VRFs
*/
if (is_vni_live(vpn))
update_routes_for_vni(bgp, vpn);
} }
/* Create or update as appropriate. */ /* Create or update as appropriate. */

View file

@ -106,7 +106,7 @@ static int retain_mode = 0;
/* privileges */ /* privileges */
static zebra_capabilities_t _caps_p[] = { static zebra_capabilities_t _caps_p[] = {
ZCAP_BIND, ZCAP_NET_RAW, ZCAP_NET_ADMIN, ZCAP_BIND, ZCAP_NET_RAW, ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN
}; };
struct zebra_privs_t bgpd_privs = { struct zebra_privs_t bgpd_privs = {

View file

@ -2228,10 +2228,13 @@ static void bgp_process_main_one(struct bgp *bgp, struct bgp_node *rn,
/* advertise/withdraw type-5 routes */ /* advertise/withdraw type-5 routes */
if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) { if ((afi == AFI_IP || afi == AFI_IP6) && (safi == SAFI_UNICAST)) {
if (new_select) if (new_select &&
bgp_evpn_advertise_type5_route( (!new_select->extra || !new_select->extra->parent))
bgp, &rn->p, new_select->attr, afi, safi); bgp_evpn_advertise_type5_route(bgp, &rn->p,
else if (old_select) new_select->attr,
afi, safi);
else if (old_select &&
(!old_select->extra || !old_select->extra->parent))
bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi); bgp_evpn_withdraw_type5_route(bgp, &rn->p, afi, safi);
} }

View file

@ -833,7 +833,7 @@ DEFUN_NOSH (router_bgp,
if (listcount(bm->bgp) > 1) { if (listcount(bm->bgp) > 1) {
vty_out(vty, vty_out(vty,
"%% Multiple BGP processes are configured\n"); "%% Please specify ASN and VRF\n");
return CMD_WARNING_CONFIG_FAILED; return CMD_WARNING_CONFIG_FAILED;
} }
} }
@ -909,7 +909,7 @@ DEFUN (no_router_bgp,
if (listcount(bm->bgp) > 1) { if (listcount(bm->bgp) > 1) {
vty_out(vty, vty_out(vty,
"%% Multiple BGP processes are configured\n"); "%% Please specify ASN and VRF\n");
return CMD_WARNING_CONFIG_FAILED; return CMD_WARNING_CONFIG_FAILED;
} }

View file

@ -1026,14 +1026,14 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED if (peer->sort == BGP_PEER_IBGP || peer->sort == BGP_PEER_CONFED
|| info->sub_type == BGP_ROUTE_AGGREGATE) { || info->sub_type == BGP_ROUTE_AGGREGATE) {
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
} }
if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1) if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
|| CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) || CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|| bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) || bgp_flag_check(bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
/* Metric is currently based on the best-path only */ /* Metric is currently based on the best-path only */
metric = info->attr->med; metric = info->attr->med;
@ -1054,6 +1054,9 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
else else
continue; continue;
api_nh = &api.nexthops[valid_nh_count];
api_nh->vrf_id = bgp->vrf_id;
if (nh_family == AF_INET) { if (nh_family == AF_INET) {
struct in_addr *nexthop; struct in_addr *nexthop;
@ -1078,9 +1081,7 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
nexthop = &mpinfo_cp->attr->nexthop; nexthop = &mpinfo_cp->attr->nexthop;
api_nh = &api.nexthops[valid_nh_count];
api_nh->gate.ipv4 = *nexthop; api_nh->gate.ipv4 = *nexthop;
api_nh->vrf_id = bgp->vrf_id;
/* EVPN type-2 routes are /* EVPN type-2 routes are
programmed as onlink on l3-vni SVI programmed as onlink on l3-vni SVI
*/ */
@ -1135,7 +1136,6 @@ void bgp_zebra_announce(struct bgp_node *rn, struct prefix *p,
if (ifindex == 0) if (ifindex == 0)
continue; continue;
api_nh = &api.nexthops[valid_nh_count];
api_nh->gate.ipv6 = *nexthop; api_nh->gate.ipv6 = *nexthop;
api_nh->ifindex = ifindex; api_nh->ifindex = ifindex;
api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX; api_nh->type = NEXTHOP_TYPE_IPV6_IFINDEX;
@ -1265,14 +1265,14 @@ void bgp_zebra_withdraw(struct prefix *p, struct bgp_info *info, safi_t safi)
SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE); SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE);
if (peer->sort == BGP_PEER_IBGP) { if (peer->sort == BGP_PEER_IBGP) {
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
SET_FLAG(api.flags, ZEBRA_FLAG_IBGP); SET_FLAG(api.flags, ZEBRA_FLAG_IBGP);
} }
if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1) if ((peer->sort == BGP_PEER_EBGP && peer->ttl != 1)
|| CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK) || CHECK_FLAG(peer->flags, PEER_FLAG_DISABLE_CONNECTED_CHECK)
|| bgp_flag_check(peer->bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK)) || bgp_flag_check(peer->bgp, BGP_FLAG_DISABLE_NH_CONNECTED_CHK))
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
if (bgp_debug_zebra(p)) { if (bgp_debug_zebra(p)) {
char buf[PREFIX_STRLEN]; char buf[PREFIX_STRLEN];

View file

@ -5,6 +5,26 @@ This is an EXPERIMENTAL support of draft
`draft-ietf-ospf-segment-routing-extensions-24`. `draft-ietf-ospf-segment-routing-extensions-24`.
DON'T use it for production network. DON'T use it for production network.
Supported Features
------------------
* Automatic computation of Primary and Backup Adjacency SID with
Cisco experimental remote IP address
* SRGB configuration
* Prefix configuration for Node SID with optional NO-PHP flag (Linux
kernel support both mode)
* Node MSD configuration (with Linux Kernel >= 4.10 a maximum of 32 labels
could be stack)
* Automatic provisioning of MPLS table
* Static route configuration with label stack up to 32 labels
Interoperability
----------------
* tested on various topology including point-to-point and LAN interfaces
in a mix of Free Range Routing instance and Cisco IOS-XR 6.0.x
* check OSPF LSA conformity with latest wireshark release 2.5.0-rc
Implementation details Implementation details
---------------------- ----------------------
@ -248,9 +268,6 @@ Known limitations
* MPLS table are not flush at startup. Thus, restarting zebra process is * MPLS table are not flush at startup. Thus, restarting zebra process is
mandatory to remove old MPLS entries in the data plane after a crash of mandatory to remove old MPLS entries in the data plane after a crash of
ospfd daemon ospfd daemon
* Due to a bug in OSPF Opaque, LSA are not flood when enable Segment Routing
through CLI once OSPFd started. You must configure Segment Routing within
configuration file before launching OSPFd
* With NO Penultimate Hop Popping, it is not possible to express a Segment * With NO Penultimate Hop Popping, it is not possible to express a Segment
Path with an Adjacency SID due to the impossibility for the Linux Kernel to Path with an Adjacency SID due to the impossibility for the Linux Kernel to
perform double POP instruction. perform double POP instruction.

View file

@ -759,10 +759,11 @@ currently supported. The 'no-php-flag' means NO Penultimate Hop Popping that
allows SR node to request to its neighbor to not pop the label. allows SR node to request to its neighbor to not pop the label.
@end deffn @end deffn
@deffn {Command} {show ip ospf database segment-routing} {} @deffn {Command} {show ip ospf database segment-routing [json]} {}
@deffnx {Command} {show ip ospf database segment-routing adv-router @var{adv-router}} {} @deffnx {Command} {show ip ospf database segment-routing adv-router @var{adv-router} [json]} {}
@deffnx {Command} {show ip ospf database segment-routing self-originate} {} @deffnx {Command} {show ip ospf database segment-routing self-originate [json]} {}
Show Segment Routing Data Base, all SR nodes, specific advertized router or self router. Show Segment Routing Data Base, all SR nodes, specific advertized router or self router.
Optional Json output could be obtain by adding 'json' at the end of the command.
@end deffn @end deffn
@node Debugging OSPF @node Debugging OSPF

View file

@ -500,6 +500,9 @@ static int config_write_host(struct vty *vty)
if (cmd_hostname_get()) if (cmd_hostname_get())
vty_out(vty, "hostname %s\n", cmd_hostname_get()); vty_out(vty, "hostname %s\n", cmd_hostname_get());
if (cmd_domainname_get())
vty_out(vty, "domainname %s\n", cmd_domainname_get());
if (host.encrypt) { if (host.encrypt) {
if (host.password_encrypt) if (host.password_encrypt)
vty_out(vty, "password 8 %s\n", host.password_encrypt); vty_out(vty, "password 8 %s\n", host.password_encrypt);

View file

@ -86,6 +86,12 @@ static inline char *ipaddr2str(struct ipaddr *ip, char *buf, int size)
return buf; return buf;
} }
/*
* Convert IPv4 address to IPv4-mapped IPv6 address which is of the
* form ::FFFF:<IPv4 address> (RFC 4291). This IPv6 address can then
* be used to represent the IPv4 address, wherever only an IPv6 address
* is required.
*/
static inline void ipv4_to_ipv4_mapped_ipv6(struct in6_addr *in6, static inline void ipv4_to_ipv4_mapped_ipv6(struct in6_addr *in6,
struct in_addr in) struct in_addr in)
{ {

View file

@ -1085,7 +1085,7 @@ int zapi_route_decode(struct stream *s, struct zapi_route *api)
STREAM_GETC(s, api->message); STREAM_GETC(s, api->message);
STREAM_GETC(s, api->safi); STREAM_GETC(s, api->safi);
if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE)) if (CHECK_FLAG(api->flags, ZEBRA_FLAG_EVPN_ROUTE))
stream_get(&(api->rmac), s, sizeof(struct ethaddr)); STREAM_GET(&(api->rmac), s, sizeof(struct ethaddr));
/* Prefix. */ /* Prefix. */
STREAM_GETC(s, api->prefix.family); STREAM_GETC(s, api->prefix.family);
@ -1266,6 +1266,8 @@ bool zapi_nexthop_update_decode(struct stream *s, struct zapi_route *nhr)
break; break;
} }
STREAM_GETC(s, nhr->type);
STREAM_GETW(s, nhr->instance);
STREAM_GETC(s, nhr->distance); STREAM_GETC(s, nhr->distance);
STREAM_GETL(s, nhr->metric); STREAM_GETL(s, nhr->metric);
STREAM_GETC(s, nhr->nexthop_num); STREAM_GETC(s, nhr->nexthop_num);

View file

@ -402,7 +402,7 @@ extern const char *zserv_command_string(unsigned int command);
#define strmatch(a,b) (!strcmp((a), (b))) #define strmatch(a,b) (!strcmp((a), (b)))
/* Zebra message flags */ /* Zebra message flags */
#define ZEBRA_FLAG_INTERNAL 0x01 #define ZEBRA_FLAG_ALLOW_RECURSION 0x01
#define ZEBRA_FLAG_SELFROUTE 0x02 #define ZEBRA_FLAG_SELFROUTE 0x02
#define ZEBRA_FLAG_IBGP 0x08 #define ZEBRA_FLAG_IBGP 0x08
#define ZEBRA_FLAG_SELECTED 0x10 #define ZEBRA_FLAG_SELECTED 0x10

View file

@ -114,7 +114,7 @@ void nhrp_route_announce(int add, enum nhrp_cache_type type, const struct prefix
SET_FLAG(api.flags, ZEBRA_FLAG_FIB_OVERRIDE); SET_FLAG(api.flags, ZEBRA_FLAG_FIB_OVERRIDE);
break; break;
} }
SET_FLAG(api.flags, ZEBRA_FLAG_INTERNAL); SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api.nexthop_num = 1; api.nexthop_num = 1;

View file

@ -204,26 +204,142 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
{ {
struct ospf6_route *old_route; struct ospf6_route *old_route;
struct ospf6_path *ecmp_path, *o_path = NULL; struct ospf6_path *ecmp_path, *o_path = NULL;
struct listnode *anode; struct listnode *anode, *anext;
struct listnode *nnode, *rnode, *rnext; struct listnode *nnode, *rnode, *rnext;
struct ospf6_nexthop *nh, *rnh; struct ospf6_nexthop *nh, *rnh;
char buf[PREFIX2STR_BUFFER]; char buf[PREFIX2STR_BUFFER];
bool route_found = false; bool route_found = false;
/* check for old entry match with new route origin,
* delete old entry.
*/
for (old_route = old; old_route; old_route = old_route->next) { for (old_route = old; old_route; old_route = old_route->next) {
if (ospf6_route_is_same(old_route, route) && bool route_updated = false;
(old_route->path.type == route->path.type) &&
(old_route->path.cost == route->path.cost) && if (!ospf6_route_is_same(old_route, route) ||
(old_route->path.u.cost_e2 == route->path.u.cost_e2)) { (old_route->path.type != route->path.type))
continue;
/* Current and New route has same origin,
* delete old entry.
*/
for (ALL_LIST_ELEMENTS(old_route->paths, anode, anext,
o_path)) {
/* Check old route path and route has same
* origin.
*/
if (o_path->area_id != route->path.area_id ||
(memcmp(&(o_path)->origin, &(route)->path.origin,
sizeof(struct ospf6_ls_origin)) != 0))
continue;
/* Cost is not same then delete current path */
if ((o_path->cost == route->path.cost) &&
(o_path->u.cost_e2 == route->path.u.cost_e2))
continue;
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&old_route->prefix, buf, prefix2str(&old_route->prefix, buf,
sizeof(buf)); sizeof(buf));
zlog_debug("%s: old route %s path cost %u [%u]", zlog_debug("%s: route %s cost old %u new %u is not same, replace route",
__PRETTY_FUNCTION__, buf,
o_path->cost, route->path.cost);
}
/* Remove selected current rout path's nh from
* effective nh list.
*/
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, nnode, nh)) {
for (ALL_LIST_ELEMENTS(old_route->nh_list,
rnode, rnext, rnh)) {
if (!ospf6_nexthop_is_same(rnh, nh))
continue;
listnode_delete(old_route->nh_list,
rnh);
ospf6_nexthop_delete(rnh);
route_updated = true;
}
}
listnode_delete(old_route->paths, o_path);
ospf6_path_free(o_path);
/* Current route's path (adv_router info) is similar
* to route being added.
* Replace current route's path with paths list head.
* Update FIB with effective NHs.
*/
if (listcount(old_route->paths)) {
if (old_route->path.origin.id ==
route->path.origin.id &&
old_route->path.origin.adv_router ==
route->path.origin.adv_router) {
struct ospf6_path *h_path;
h_path = (struct ospf6_path *)
listgetdata(listhead(old_route->paths));
old_route->path.origin.type =
h_path->origin.type;
old_route->path.origin.id =
h_path->origin.id;
old_route->path.origin.adv_router =
h_path->origin.adv_router;
}
if (route_updated) {
for (ALL_LIST_ELEMENTS(old_route->paths,
anode, anext, o_path)) {
ospf6_merge_nexthops(
old_route->nh_list,
o_path->nh_list);
}
/* Update RIB/FIB with effective
* nh_list
*/
if (ospf6->route_table->hook_add)
(*ospf6->route_table->hook_add)
(old_route);
break;
}
} else {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&old_route->prefix, buf,
sizeof(buf));
zlog_debug("%s: route %s old cost %u new cost %u, delete old entry.",
__PRETTY_FUNCTION__, buf,
old_route->path.cost,
route->path.cost);
}
ospf6_route_remove(old_route,
ospf6->route_table);
break;
}
}
if (route_updated)
break;
}
/* Add new route */
for (old_route = old; old_route; old_route = old_route->next) {
/* Current and New Route prefix or route type
* is not same skip this current node.
*/
if (!ospf6_route_is_same(old_route, route) ||
(old_route->path.type != route->path.type))
continue;
/* Old Route and New Route have Equal Cost, Merge NHs */
if ((old_route->path.cost == route->path.cost) &&
(old_route->path.u.cost_e2 == route->path.u.cost_e2)) {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&old_route->prefix, buf,
sizeof(buf));
zlog_debug("%s: old route %s path cost %u e2 %u",
__PRETTY_FUNCTION__, buf, __PRETTY_FUNCTION__, buf,
old_route->path.cost, old_route->path.cost,
ospf6_route_is_same(old_route, old_route->path.u.cost_e2);
route));
} }
route_found = true; route_found = true;
/* check if this path exists already in /* check if this path exists already in
@ -232,9 +348,10 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
*/ */
for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode, for (ALL_LIST_ELEMENTS_RO(old_route->paths, anode,
o_path)) { o_path)) {
if ((o_path->origin.id == route->path.origin.id) if (o_path->area_id == route->path.area_id &&
&& (o_path->origin.adv_router == (memcmp(&(o_path)->origin,
route->path.origin.adv_router)) &(route)->path.origin,
sizeof(struct ospf6_ls_origin)) == 0))
break; break;
} }
/* If path is not found in old_route paths's list, /* If path is not found in old_route paths's list,
@ -262,12 +379,13 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&route->prefix, buf, prefix2str(&route->prefix, buf,
sizeof(buf)); sizeof(buf));
zlog_debug("%s: route %s another path added with nh %u, Paths %u", zlog_debug("%s: route %s another path added with nh %u, effective paths %u nh %u",
__PRETTY_FUNCTION__, buf, __PRETTY_FUNCTION__, buf,
listcount(ecmp_path->nh_list), listcount(ecmp_path->nh_list),
old_route->paths ? old_route->paths ?
listcount(old_route->paths) listcount(old_route->paths)
: 0); : 0,
listcount(old_route->nh_list));
} }
} else { } else {
for (ALL_LIST_ELEMENTS_RO(o_path->nh_list, for (ALL_LIST_ELEMENTS_RO(o_path->nh_list,
@ -313,6 +431,7 @@ void ospf6_asbr_update_route_ecmp_path(struct ospf6_route *old,
* route. * route.
*/ */
ospf6_route_delete(route); ospf6_route_delete(route);
break; break;
} }
} }
@ -426,11 +545,12 @@ void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa)
} }
void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa) void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
struct ospf6_route *asbr_entry)
{ {
struct ospf6_as_external_lsa *external; struct ospf6_as_external_lsa *external;
struct prefix prefix; struct prefix prefix;
struct ospf6_route *route, *nroute; struct ospf6_route *route, *nroute, *route_to_del;
char buf[PREFIX2STR_BUFFER]; char buf[PREFIX2STR_BUFFER];
external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END( external = (struct ospf6_as_external_lsa *)OSPF6_LSA_HEADER_END(
@ -445,6 +565,35 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
return; return;
} }
route_to_del = ospf6_route_create();
route_to_del->type = OSPF6_DEST_TYPE_NETWORK;
route_to_del->prefix.family = AF_INET6;
route_to_del->prefix.prefixlen = external->prefix.prefix_length;
ospf6_prefix_in6_addr(&route_to_del->prefix.u.prefix6,
&external->prefix);
route_to_del->path.origin.type = lsa->header->type;
route_to_del->path.origin.id = lsa->header->id;
route_to_del->path.origin.adv_router = lsa->header->adv_router;
if (asbr_entry) {
route_to_del->path.area_id = asbr_entry->path.area_id;
if (CHECK_FLAG(external->bits_metric, OSPF6_ASBR_BIT_E)) {
route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL2;
route_to_del->path.metric_type = 2;
route_to_del->path.cost = asbr_entry->path.cost;
route_to_del->path.u.cost_e2 =
OSPF6_ASBR_METRIC(external);
} else {
route_to_del->path.type = OSPF6_PATH_TYPE_EXTERNAL1;
route_to_del->path.metric_type = 1;
route_to_del->path.cost =
asbr_entry->path.cost +
OSPF6_ASBR_METRIC(external);
route_to_del->path.u.cost_e2 = 0;
}
}
memset(&prefix, 0, sizeof(struct prefix)); memset(&prefix, 0, sizeof(struct prefix));
prefix.family = AF_INET6; prefix.family = AF_INET6;
prefix.prefixlen = external->prefix.prefix_length; prefix.prefixlen = external->prefix.prefix_length;
@ -459,14 +608,25 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
return; return;
} }
for (ospf6_route_lock(route); if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
route && ospf6_route_is_prefix(&prefix, route); route = nroute) { prefix2str(&prefix, buf, sizeof(buf));
zlog_debug("%s: Current route %s cost %u e2 %u, route to del cost %u e2 %u",
__PRETTY_FUNCTION__, buf, route->path.cost,
route->path.u.cost_e2,
route_to_del->path.cost,
route_to_del->path.u.cost_e2);
}
for (ospf6_route_lock(route); route &&
ospf6_route_is_prefix(&prefix, route); route = nroute) {
nroute = ospf6_route_next(route); nroute = ospf6_route_next(route);
if (route->type != OSPF6_DEST_TYPE_NETWORK) if (route->type != OSPF6_DEST_TYPE_NETWORK)
continue; continue;
/* Route has multiple ECMP paths remove, /* Route has multiple ECMP paths, remove matching
* matching path and update effective route's nh list. * path. Update current route's effective nh list
* after removal of one of the path.
*/ */
if (listcount(route->paths) > 1) { if (listcount(route->paths) > 1) {
struct listnode *anode, *anext; struct listnode *anode, *anext;
@ -481,18 +641,36 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
*/ */
for (ALL_LIST_ELEMENTS(route->paths, anode, anext, for (ALL_LIST_ELEMENTS(route->paths, anode, anext,
o_path)) { o_path)) {
if (o_path->origin.type != lsa->header->type) if ((o_path->origin.type != lsa->header->type)
|| (o_path->origin.adv_router !=
lsa->header->adv_router) ||
(o_path->origin.id != lsa->header->id))
continue; continue;
if (o_path->origin.id != lsa->header->id)
continue; /* Compare LSA cost with current
if (o_path->origin.adv_router != * route info.
lsa->header->adv_router) */
if (!asbr_entry && (o_path->cost !=
route_to_del->path.cost ||
o_path->u.cost_e2 !=
route_to_del->path.u.cost_e2)) {
if (IS_OSPF6_DEBUG_EXAMIN(
AS_EXTERNAL)) {
prefix2str(&prefix, buf,
sizeof(buf));
zlog_debug(
"%s: route %s to delete is not same, cost %u del cost %u. skip",
__PRETTY_FUNCTION__, buf,
route->path.cost,
route_to_del->path.cost);
}
continue; continue;
}
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&prefix, buf, sizeof(buf)); prefix2str(&prefix, buf, sizeof(buf));
zlog_debug( zlog_debug(
"%s: route %s path found with nh %u", "%s: route %s path found with nh %u to remove.",
__PRETTY_FUNCTION__, buf, __PRETTY_FUNCTION__, buf,
listcount(o_path->nh_list)); listcount(o_path->nh_list));
} }
@ -542,13 +720,13 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
listcount(route->nh_list)); listcount(route->nh_list));
} }
/* Update RIB/FIB w/ effective nh_list */ /* Update RIB/FIB with effective nh_list */
if (ospf6->route_table->hook_add) if (ospf6->route_table->hook_add)
(*ospf6->route_table->hook_add)(route); (*ospf6->route_table->hook_add)(route);
/* route's path is similar to lsa header, /* route's primary path is similar to LSA,
* replace route's path with route's * replace route's primary path with
* paths list head. * route's paths list head.
*/ */
if (route->path.origin.id == lsa->header->id && if (route->path.origin.id == lsa->header->id &&
route->path.origin.adv_router == route->path.origin.adv_router ==
@ -568,12 +746,29 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
continue; continue;
} else { } else {
if (route->path.origin.type != lsa->header->type) /* Compare LSA origin and cost with current route info.
* if any check fails skip del this route node.
*/
if (asbr_entry && (!ospf6_route_is_same_origin(route,
route_to_del) ||
(route->path.type != route_to_del->path.type) ||
(route->path.cost != route_to_del->path.cost) ||
(route->path.u.cost_e2 !=
route_to_del->path.u.cost_e2))) {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
prefix2str(&prefix, buf, sizeof(buf));
zlog_debug("%s: route %s to delete is not same, cost %u del cost %u. skip",
__PRETTY_FUNCTION__, buf,
route->path.cost,
route_to_del->path.cost);
}
continue; continue;
if (route->path.origin.id != lsa->header->id) }
continue;
if (route->path.origin.adv_router != if ((route->path.origin.type != lsa->header->type) ||
lsa->header->adv_router) (route->path.origin.adv_router !=
lsa->header->adv_router) ||
(route->path.origin.id != lsa->header->id))
continue; continue;
} }
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
@ -589,6 +784,8 @@ void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa)
} }
if (route != NULL) if (route != NULL)
ospf6_route_unlock(route); ospf6_route_unlock(route);
ospf6_route_delete(route_to_del);
} }
void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry) void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry)
@ -622,7 +819,7 @@ void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry)
type = htons(OSPF6_LSTYPE_AS_EXTERNAL); type = htons(OSPF6_LSTYPE_AS_EXTERNAL);
router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix); router = ospf6_linkstate_prefix_adv_router(&asbr_entry->prefix);
for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa)) for (ALL_LSDB_TYPED_ADVRTR(ospf6->lsdb, type, router, lsa))
ospf6_asbr_lsa_remove(lsa); ospf6_asbr_lsa_remove(lsa, asbr_entry);
} }

View file

@ -71,7 +71,8 @@ struct ospf6_as_external_lsa {
} }
extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa); extern void ospf6_asbr_lsa_add(struct ospf6_lsa *lsa);
extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa); extern void ospf6_asbr_lsa_remove(struct ospf6_lsa *lsa,
struct ospf6_route *asbr_entry);
extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry); extern void ospf6_asbr_lsentry_add(struct ospf6_route *asbr_entry);
extern void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry); extern void ospf6_asbr_lsentry_remove(struct ospf6_route *asbr_entry);

View file

@ -469,6 +469,8 @@ int ospf6_route_cmp(struct ospf6_route *ra, struct ospf6_route *rb)
if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2) { if (ra->path.type == OSPF6_PATH_TYPE_EXTERNAL2) {
if (ra->path.u.cost_e2 != rb->path.u.cost_e2) if (ra->path.u.cost_e2 != rb->path.u.cost_e2)
return (ra->path.u.cost_e2 - rb->path.u.cost_e2); return (ra->path.u.cost_e2 - rb->path.u.cost_e2);
else
return (ra->path.cost - rb->path.cost);
} else { } else {
if (ra->path.cost != rb->path.cost) if (ra->path.cost != rb->path.cost)
return (ra->path.cost - rb->path.cost); return (ra->path.cost - rb->path.cost);
@ -627,10 +629,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
if (ospf6_route_is_identical(old, route)) { if (ospf6_route_is_identical(old, route)) {
if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug( zlog_debug(
"%s %p: route add %p: needless update of %p", "%s %p: route add %p: needless update of %p old cost %u",
ospf6_route_table_name(table), ospf6_route_table_name(table),
(void *)table, (void *)route, (void *)table, (void *)route,
(void *)old); (void *)old, old->path.cost);
else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: needless update", zlog_debug("%s: route add: needless update",
ospf6_route_table_name(table)); ospf6_route_table_name(table));
@ -645,9 +647,10 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
} }
if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug("%s %p: route add %p: update of %p", zlog_debug("%s %p: route add %p cost %u: update of %p old cost %u",
ospf6_route_table_name(table), (void *)table, ospf6_route_table_name(table), (void *)table,
(void *)route, (void *)old); (void *)route, route->path.cost, (void *)old,
old->path.cost);
else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: update", zlog_debug("%s: route add: update",
ospf6_route_table_name(table)); ospf6_route_table_name(table));
@ -686,13 +689,14 @@ struct ospf6_route *ospf6_route_add(struct ospf6_route *route,
if (prev || next) { if (prev || next) {
if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug( zlog_debug(
"%s %p: route add %p: another path: prev %p, next %p node refcount %u", "%s %p: route add %p cost %u: another path: prev %p, next %p node ref %u",
ospf6_route_table_name(table), (void *)table, ospf6_route_table_name(table), (void *)table,
(void *)route, (void *)prev, (void *)next, (void *)route, route->path.cost, (void *)prev,
node->lock); (void *)next, node->lock);
else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route add: another path found", zlog_debug("%s: route add cost %u: another path found",
ospf6_route_table_name(table)); ospf6_route_table_name(table),
route->path.cost);
if (prev == NULL) if (prev == NULL)
prev = next->prev; prev = next->prev;
@ -814,9 +818,9 @@ void ospf6_route_remove(struct ospf6_route *route,
prefix2str(&route->prefix, buf, sizeof(buf)); prefix2str(&route->prefix, buf, sizeof(buf));
if (IS_OSPF6_DEBUG_ROUTE(MEMORY)) if (IS_OSPF6_DEBUG_ROUTE(MEMORY))
zlog_debug("%s %p: route remove %p: %s refcount %u", zlog_debug("%s %p: route remove %p: %s cost %u refcount %u",
ospf6_route_table_name(table), (void *)table, ospf6_route_table_name(table), (void *)table,
(void *)route, buf, route->lock); (void *)route, buf, route->path.cost, route->lock);
else if (IS_OSPF6_DEBUG_ROUTE(TABLE)) else if (IS_OSPF6_DEBUG_ROUTE(TABLE))
zlog_debug("%s: route remove: %s", zlog_debug("%s: route remove: %s",
ospf6_route_table_name(table), buf); ospf6_route_table_name(table), buf);

View file

@ -72,7 +72,7 @@ static void ospf6_top_lsdb_hook_remove(struct ospf6_lsa *lsa)
{ {
switch (ntohs(lsa->header->type)) { switch (ntohs(lsa->header->type)) {
case OSPF6_LSTYPE_AS_EXTERNAL: case OSPF6_LSTYPE_AS_EXTERNAL:
ospf6_asbr_lsa_remove(lsa); ospf6_asbr_lsa_remove(lsa, NULL);
break; break;
default: default:
@ -96,11 +96,16 @@ static void ospf6_top_route_hook_remove(struct ospf6_route *route)
static void ospf6_top_brouter_hook_add(struct ospf6_route *route) static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
{ {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
char buf[PREFIX2STR_BUFFER]; uint32_t brouter_id;
char brouter_name[16];
prefix2str(&route->prefix, buf, sizeof(buf)); brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix);
zlog_debug("%s: brouter %s add with nh count %u", inet_ntop(AF_INET, &brouter_id, brouter_name,
__PRETTY_FUNCTION__, buf, listcount(route->nh_list)); sizeof(brouter_name));
zlog_debug("%s: brouter %s add with adv router %x nh count %u",
__PRETTY_FUNCTION__, brouter_name,
route->path.origin.adv_router,
listcount(route->nh_list));
} }
ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix)); ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix));
ospf6_asbr_lsentry_add(route); ospf6_asbr_lsentry_add(route);
@ -110,11 +115,15 @@ static void ospf6_top_brouter_hook_add(struct ospf6_route *route)
static void ospf6_top_brouter_hook_remove(struct ospf6_route *route) static void ospf6_top_brouter_hook_remove(struct ospf6_route *route)
{ {
if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) { if (IS_OSPF6_DEBUG_EXAMIN(AS_EXTERNAL)) {
char buf[PREFIX2STR_BUFFER]; uint32_t brouter_id;
char brouter_name[16];
prefix2str(&route->prefix, buf, sizeof(buf)); brouter_id = ADV_ROUTER_IN_PREFIX(&route->prefix);
inet_ntop(AF_INET, &brouter_id, brouter_name,
sizeof(brouter_name));
zlog_debug("%s: brouter %s del with nh count %u", zlog_debug("%s: brouter %s del with nh count %u",
__PRETTY_FUNCTION__, buf, listcount(route->nh_list)); __PRETTY_FUNCTION__, brouter_name,
listcount(route->nh_list));
} }
route->flag |= OSPF6_ROUTE_REMOVE; route->flag |= OSPF6_ROUTE_REMOVE;
ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix)); ospf6_abr_examin_brouter(ADV_ROUTER_IN_PREFIX(&route->prefix));

View file

@ -55,7 +55,7 @@
/* ospfd privileges */ /* ospfd privileges */
zebra_capabilities_t _caps_p[] = { zebra_capabilities_t _caps_p[] = {
ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_NET_RAW, ZCAP_BIND, ZCAP_NET_ADMIN, ZCAP_SYS_ADMIN
}; };
struct zebra_privs_t ospfd_privs = { struct zebra_privs_t ospfd_privs = {

View file

@ -47,6 +47,7 @@
#include "thread.h" #include "thread.h"
#include "vty.h" #include "vty.h"
#include "zclient.h" #include "zclient.h"
#include <lib/json.h>
#include "ospfd/ospfd.h" #include "ospfd/ospfd.h"
#include "ospfd/ospf_interface.h" #include "ospfd/ospf_interface.h"
@ -306,7 +307,8 @@ int ospf_sr_init(void)
{ {
int rc = -1; int rc = -1;
zlog_info("SR (%s): Initialize SR Data Base", __func__); if (IS_DEBUG_OSPF_SR)
zlog_info("SR (%s): Initialize SR Data Base", __func__);
memset(&OspfSR, 0, sizeof(struct ospf_sr_db)); memset(&OspfSR, 0, sizeof(struct ospf_sr_db));
OspfSR.enabled = false; OspfSR.enabled = false;
@ -675,6 +677,7 @@ static int ospf_zebra_send_mpls_ftn(int cmd, struct sr_nhlfe nhlfe)
SET_FLAG(api.message, ZAPI_MESSAGE_LABEL); SET_FLAG(api.message, ZAPI_MESSAGE_LABEL);
api_nh->labels[0] = nhlfe.label_out; api_nh->labels[0] = nhlfe.label_out;
api_nh->label_num = 1; api_nh->label_num = 1;
api_nh->vrf_id = VRF_DEFAULT;
api.nexthop_num = 1; api.nexthop_num = 1;
} }
@ -1616,11 +1619,11 @@ static int ospf_sr_update_schedule(struct thread *t)
monotime(&stop_time); monotime(&stop_time);
zlog_info( if (IS_DEBUG_OSPF_SR)
"SR (%s): SPF Processing Time(usecs): %lld\n", zlog_debug("SR (%s): SPF Processing Time(usecs): %lld\n",
__func__, __func__,
(stop_time.tv_sec - start_time.tv_sec) * 1000000LL (stop_time.tv_sec - start_time.tv_sec) * 1000000LL
+ (stop_time.tv_usec - start_time.tv_usec)); + (stop_time.tv_usec - start_time.tv_usec));
OspfSR.update = false; OspfSR.update = false;
return 1; return 1;
@ -2128,91 +2131,197 @@ DEFUN (no_sr_prefix_sid,
static void show_vty_sr_node(struct vty *vty, struct sr_node *srn) static void show_sr_node(struct vty *vty, struct json_object *json,
struct sr_node *srn)
{ {
struct listnode *node; struct listnode *node;
struct sr_link *srl; struct sr_link *srl;
struct sr_prefix *srp; struct sr_prefix *srp;
struct interface *itf; struct interface *itf;
char pref[16]; char pref[19];
char sid[22]; char sid[22];
char label[8]; char label[8];
json_object *json_node = NULL, *json_algo, *json_obj;
json_object *json_prefix = NULL, *json_link = NULL;
/* Sanity Check */ /* Sanity Check */
if (srn == NULL) if (srn == NULL)
return; return;
vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router)); if (json) {
vty_out(vty, "\tSRGB (Size/Label): %u/%u", srn->srgb.range_size, json_node = json_object_new_object();
srn->srgb.lower_bound); json_object_string_add(json_node, "routerID",
vty_out(vty, "\tAlgorithm(s): %s", inet_ntoa(srn->adv_router));
srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); json_object_int_add(json_node, "srgbSize",
for (int i = 1; i < ALGORITHM_COUNT; i++) { srn->srgb.range_size);
if (srn->algo[i] == SR_ALGORITHM_UNSET) json_object_int_add(json_node, "srgbLabel",
continue; srn->srgb.lower_bound);
vty_out(vty, "/%s", json_algo = json_object_new_array();
srn->algo[i] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF"); json_object_object_add(json_node, "algorithms", json_algo);
} for (int i = 0; i < ALGORITHM_COUNT; i++) {
if (srn->msd != 0) if (srn->algo[i] == SR_ALGORITHM_UNSET)
vty_out(vty, "\tMSD: %u", srn->msd); continue;
json_obj = json_object_new_object();
char tmp[2];
vty_out(vty, snprintf(tmp, 2, "%u", i);
"\n\n Prefix or Link Label In Label Out " json_object_string_add(json_obj, tmp,
"Node or Adj. SID Interface Nexthop\n"); srn->algo[i] == SR_ALGORITHM_SPF ?
vty_out(vty, "SPF" : "S-SPF");
"------------------ -------- --------- " json_object_array_add(json_algo, json_obj);
"--------------------- --------- ---------------\n"); }
if (srn->msd != 0)
json_object_int_add(json_node, "nodeMsd", srn->msd);
} else {
vty_out(vty, "SR-Node: %s", inet_ntoa(srn->adv_router));
vty_out(vty, "\tSRGB (Size/Label): %u/%u",
srn->srgb.range_size, srn->srgb.lower_bound);
vty_out(vty, "\tAlgorithm(s): %s",
srn->algo[0] == SR_ALGORITHM_SPF ? "SPF" : "S-SPF");
for (int i = 1; i < ALGORITHM_COUNT; i++) {
if (srn->algo[i] == SR_ALGORITHM_UNSET)
continue;
vty_out(vty, "/%s",
srn->algo[i] == SR_ALGORITHM_SPF ?
"SPF" : "S-SPF");
}
if (srn->msd != 0)
vty_out(vty, "\tMSD: %u", srn->msd);
}
if (!json) {
vty_out(vty,
"\n\n Prefix or Link Label In Label Out "
"Node or Adj. SID Interface Nexthop\n");
vty_out(vty,
"------------------ -------- --------- "
"--------------------- --------- ---------------\n");
}
for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) { for (ALL_LIST_ELEMENTS_RO(srn->ext_prefix, node, srp)) {
strncpy(pref, inet_ntoa(srp->nhlfe.prefv4.prefix), 16); snprintf(pref, 19, "%s/%u",
inet_ntoa(srp->nhlfe.prefv4.prefix),
srp->nhlfe.prefv4.prefixlen);
snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid); snprintf(sid, 22, "SR Pfx (idx %u)", srp->sid);
if (srp->nhlfe.label_out == MPLS_LABEL_IMPLICIT_NULL) if (srp->nhlfe.label_out == MPLS_LABEL_IMPLICIT_NULL)
sprintf(label, "pop"); sprintf(label, "pop");
else else
sprintf(label, "%u", srp->nhlfe.label_out); sprintf(label, "%u", srp->nhlfe.label_out);
itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT); itf = if_lookup_by_index(srp->nhlfe.ifindex, VRF_DEFAULT);
vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref, if (json) {
srp->nhlfe.prefv4.prefixlen, srp->nhlfe.label_in, label, if (!json_prefix) {
sid, itf ? itf->name : "-", json_prefix = json_object_new_array();
inet_ntoa(srp->nhlfe.nexthop)); json_object_object_add(json_node,
"extendedPrefix", json_prefix);
}
json_obj = json_object_new_object();
json_object_string_add(json_obj, "prefix", pref);
json_object_int_add(json_obj, "sid", srp->sid);
json_object_int_add(json_obj, "inputLabel",
srp->nhlfe.label_in);
json_object_string_add(json_obj, "outputLabel",
label);
json_object_string_add(json_obj, "interface",
itf ? itf->name : "-");
json_object_string_add(json_obj, "nexthop",
inet_ntoa(srp->nhlfe.nexthop));
json_object_array_add(json_prefix, json_obj);
} else {
vty_out(vty, "%18s %8u %9s %21s %9s %15s\n",
pref, srp->nhlfe.label_in, label,
sid, itf ? itf->name : "-",
inet_ntoa(srp->nhlfe.nexthop));
}
} }
for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) { for (ALL_LIST_ELEMENTS_RO(srn->ext_link, node, srl)) {
strncpy(pref, inet_ntoa(srl->nhlfe[0].prefv4.prefix), 16); snprintf(pref, 19, "%s/%u",
inet_ntoa(srl->nhlfe[0].prefv4.prefix),
srl->nhlfe[0].prefv4.prefixlen);
snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]); snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[0]);
if (srl->nhlfe[0].label_out == MPLS_LABEL_IMPLICIT_NULL) if (srl->nhlfe[0].label_out == MPLS_LABEL_IMPLICIT_NULL)
sprintf(label, "pop"); sprintf(label, "pop");
else else
sprintf(label, "%u", srl->nhlfe[0].label_out); sprintf(label, "%u", srl->nhlfe[0].label_out);
itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT); itf = if_lookup_by_index(srl->nhlfe[0].ifindex, VRF_DEFAULT);
vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref, if (json) {
srl->nhlfe[0].prefv4.prefixlen, srl->nhlfe[0].label_in, if (!json_link) {
label, sid, itf ? itf->name : "-", json_link = json_object_new_array();
inet_ntoa(srl->nhlfe[0].nexthop)); json_object_object_add(json_node,
snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]); "extendedLink", json_link);
if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL) }
sprintf(label, "pop"); /* Primary Link */
else json_obj = json_object_new_object();
sprintf(label, "%u", srl->nhlfe[0].label_out); json_object_string_add(json_obj, "prefix", pref);
vty_out(vty, "%15s/%u %8u %9s %21s %9s %15s\n", pref, json_object_int_add(json_obj, "sid", srl->sid[0]);
srl->nhlfe[1].prefv4.prefixlen, srl->nhlfe[1].label_in, json_object_int_add(json_obj, "inputLabel",
label, sid, itf ? itf->name : "-", srl->nhlfe[0].label_in);
inet_ntoa(srl->nhlfe[1].nexthop)); json_object_string_add(json_obj, "outputLabel",
label);
json_object_string_add(json_obj, "interface",
itf ? itf->name : "-");
json_object_string_add(json_obj, "nexthop",
inet_ntoa(srl->nhlfe[0].nexthop));
json_object_array_add(json_link, json_obj);
/* Backup Link */
json_obj = json_object_new_object();
snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]);
if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL)
sprintf(label, "pop");
else
sprintf(label, "%u", srl->nhlfe[0].label_out);
json_object_string_add(json_obj, "prefix", pref);
json_object_int_add(json_obj, "sid", srl->sid[1]);
json_object_int_add(json_obj, "inputLabel",
srl->nhlfe[1].label_in);
json_object_string_add(json_obj, "outputLabel",
label);
json_object_string_add(json_obj, "interface",
itf ? itf->name : "-");
json_object_string_add(json_obj, "nexthop",
inet_ntoa(srl->nhlfe[1].nexthop));
json_object_array_add(json_link, json_obj);
} else {
vty_out(vty, "%18s %8u %9s %21s %9s %15s\n",
pref, srl->nhlfe[0].label_in,
label, sid, itf ? itf->name : "-",
inet_ntoa(srl->nhlfe[0].nexthop));
snprintf(sid, 22, "SR Adj. (lbl %u)", srl->sid[1]);
if (srl->nhlfe[1].label_out == MPLS_LABEL_IMPLICIT_NULL)
sprintf(label, "pop");
else
sprintf(label, "%u", srl->nhlfe[1].label_out);
vty_out(vty, "%18s %8u %9s %21s %9s %15s\n",
pref, srl->nhlfe[1].label_in,
label, sid, itf ? itf->name : "-",
inet_ntoa(srl->nhlfe[1].nexthop));
}
} }
vty_out(vty, "\n"); if (json)
json_object_array_add(json, json_node);
else
vty_out(vty, "\n");
} }
static void show_srdb_entry(struct hash_backet *backet, void *args) static void show_vty_srdb(struct hash_backet *backet, void *args)
{ {
struct vty *vty = (struct vty *)args; struct vty *vty = (struct vty *)args;
struct sr_node *srn = (struct sr_node *)backet->data; struct sr_node *srn = (struct sr_node *)backet->data;
show_vty_sr_node(vty, srn); show_sr_node(vty, NULL, srn);
}
static void show_json_srdb(struct hash_backet *backet, void *args)
{
struct json_object *json = (struct json_object *)args;
struct sr_node *srn = (struct sr_node *)backet->data;
show_sr_node(NULL, json, srn);
} }
DEFUN (show_ip_opsf_srdb, DEFUN (show_ip_opsf_srdb,
show_ip_ospf_srdb_cmd, show_ip_ospf_srdb_cmd,
"show ip ospf database segment-routing [adv-router A.B.C.D|self-originate]", "show ip ospf database segment-routing [adv-router A.B.C.D|self-originate] [json]",
SHOW_STR SHOW_STR
IP_STR IP_STR
OSPF_STR OSPF_STR
@ -2220,23 +2329,41 @@ DEFUN (show_ip_opsf_srdb,
"Show Segment Routing Data Base\n" "Show Segment Routing Data Base\n"
"Advertising SR node\n" "Advertising SR node\n"
"Advertising SR node ID (as an IP address)\n" "Advertising SR node ID (as an IP address)\n"
"Self-originated SR node\n") "Self-originated SR node\n"
JSON_STR)
{ {
int idx = 0; int idx = 0;
struct in_addr rid; struct in_addr rid;
struct sr_node *srn; struct sr_node *srn;
u_char uj = use_json(argc, argv);
json_object *json = NULL, *json_node_array = NULL;
if (!OspfSR.enabled) { if (!OspfSR.enabled) {
vty_out(vty, "Segment Routing is disabled on this router\n"); vty_out(vty, "Segment Routing is disabled on this router\n");
return CMD_WARNING; return CMD_WARNING;
} }
vty_out(vty, "\n OSPF Segment Routing database for ID %s\n\n", if (uj) {
inet_ntoa(OspfSR.self->adv_router)); json = json_object_new_object();
json_node_array = json_object_new_array();
json_object_string_add(json, "srdbID",
inet_ntoa(OspfSR.self->adv_router));
json_object_object_add(json, "srNodes", json_node_array);
} else {
vty_out(vty,
"\n\t\tOSPF Segment Routing database for ID %s\n\n",
inet_ntoa(OspfSR.self->adv_router));
}
if (argv_find(argv, argc, "self-originate", &idx)) { if (argv_find(argv, argc, "self-originate", &idx)) {
srn = OspfSR.self; srn = OspfSR.self;
show_vty_sr_node(vty, srn); show_sr_node(vty, json_node_array, srn);
if (uj) {
vty_out(vty, "%s\n",
json_object_to_json_string_ext(json,
JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
return CMD_SUCCESS; return CMD_SUCCESS;
} }
@ -2250,15 +2377,32 @@ DEFUN (show_ip_opsf_srdb,
/* Get the SR Node from the SRDB */ /* Get the SR Node from the SRDB */
srn = (struct sr_node *)hash_lookup(OspfSR.neighbors, srn = (struct sr_node *)hash_lookup(OspfSR.neighbors,
(void *)&rid); (void *)&rid);
show_vty_sr_node(vty, srn); show_sr_node(vty, json_node_array, srn);
if (uj) {
vty_out(vty, "%s\n",
json_object_to_json_string_ext(json,
JSON_C_TO_STRING_PRETTY));
json_object_free(json);
}
return CMD_SUCCESS; return CMD_SUCCESS;
} }
/* No parameters have been provided, Iterate through all the SRDB */ /* No parameters have been provided, Iterate through all the SRDB */
hash_iterate( if (uj) {
OspfSR.neighbors, hash_iterate(
(void (*)(struct hash_backet *, void *))show_srdb_entry, OspfSR.neighbors,
(void *)vty); (void (*)(struct hash_backet *, void *))show_json_srdb,
(void *)json_node_array);
vty_out(vty, "%s\n",
json_object_to_json_string_ext(json,
JSON_C_TO_STRING_PRETTY));
json_object_free(json);
} else {
hash_iterate(
OspfSR.neighbors,
(void (*)(struct hash_backet *, void *))show_vty_srdb,
(void *)vty);
}
return CMD_SUCCESS; return CMD_SUCCESS;
} }

372
pimd/TODO
View file

@ -1,375 +1,21 @@
T1 DONE Implement debug command T1 Consider reliable pim solution (refresh reduction)
test pim receive join
T2 DONE Implement debug command
test pim receive prune
T3 DONE Per-interface Downstream (S,G) state machine
(RFC 4601 4.5.3. Receiving (S,G) Join/Prune Messages)
T4 DONE Upstream (S,G) state machine
(RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages)
T5 DONE Verify Data Packet Forwarding Rules
RFC 4601 4.2. Data Packet Forwarding Rules
RFC 4601 4.8.2. PIM-SSM-Only Routers
Additionally, the Packet forwarding rules of Section 4.2 can be
simplified in a PIM-SSM-only router:
iif is the incoming interface of the packet.
oiflist = NULL
if (iif == RPF_interface(S) AND UpstreamJPState(S,G) == Joined) {
oiflist = inherited_olist(S,G)
} else if (iif is in inherited_olist(S,G)) {
send Assert(S,G) on iif
}
oiflist = oiflist (-) iif
forward packet on all interfaces in oiflist
Macro:
inherited_olist(S,G) =
joins(S,G) (+) pim_include(S,G) (-) lost_assert(S,G)
T6 DONE Implement (S,G) Assert state machine (RFC 4601, section 4.6.1).
Changes in pim_ifchannel.ifassert_winner should trigger
pim_upstream_update_join_desired().
Depends on TODO T27.
Depends on TODO T33.
See also CAVEAT C7.
See also: RFC 4601 4.5.7. Sending (S,G) Join/Prune Messages
Transitions from Joined State
RPF'(S,G) changes due to an Assert
http://www.hep.ucl.ac.uk/~ytl/multi-cast/pim-dm_01.html:
The PIM Assert mechanism is used to shutoff duplicate flows onto
the same multiaccess network. Routers detect this condiction when
they receive an (S,G) packet via a multi-access interface that is
in the (S,G) OIL. This causes the routers to send Assert
Messages.
Note that neighbors will not accept Join/Prune or Assert messages
from a router unless they have first heard a Hello message from that
router. Thus, if a router needs to send a Join/Prune or Assert
message on an interface on which it has not yet sent a Hello message
with the currently configured IP address, then it MUST immediately
send the relevant Hello message without waiting for the Hello Timer
to expire, followed by the Join/Prune or Assert message.
T7 DONE Implement hello option: LAN Prune Delay
T8 DONE Implement J/P_Override_Interval(I)
Depends on TODO T7.
See pim_ifchannel.c, pim_ifchannel_prune(), jp_override_interval.
T9 DONE Detect change in IGMPv3 RPF interface/next-hop for S and update.
channel_oil vif index accordingly ?
Beware accidentaly adding looped MFC entries (IIF=OIF).
T10 DONE React to (S,G) join directed to another upstream address. See
also:
RFC 4601: 4.5.7. Sending (S,G) Join/Prune Messages
If a router wishes to propagate a Join(S,G) upstream, it must also
watch for messages on its upstream interface from other routers on
that subnet, and these may modify its behavior. If it sees a
Join(S,G) to the correct upstream neighbor, it should suppress its
own Join(S,G). If it sees a Prune(S,G), Prune(S,G,rpt), or
Prune(*,G) to the correct upstream neighbor towards S, it should
be prepared to override that prune by scheduling a Join(S,G) to be
sent almost immediately.
T11 DONE Review protocol modifications for SSM
(RFC 4601 4.8.1. Protocol Modifications for SSM Destination
Addresses)
T12 DONE Review updates of RPF entries.
FIXME pim_upstream.c send_join():
Currently only one upstream state is affected by detection of RPF change.
RPF change should affect all upstream states sharing the RPF cache.
T13 DONE Check that RFC macros using S,G,RPF_interface(S) are actually
implemented with this strategy:
rpf_ifch=find_ifch(up->rpf->interface).
See pim_rpf.c pim_rpf_find_rpf_addr() for a correct example.
$ grep -i macro pimd/*.c
pimd/pim_iface.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros
pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.1.6. State Summarization Macros
pimd/pim_ifchannel.c: RFC 4601: 4.6.5. Assert State Macros
pimd/pim_ifchannel.c: Macro:
pimd/pim_rpf.c: RFC 4601: 4.1.6. State Summarization Macros
T14 DONE Send Assert(S,G) on iif as response to WRONGVIF kernel upcall.
See pim_mroute.c mroute_msg().
T15 DONE Interface command to statically join (S,G).
interface eth0
ip igmp join-group 239.1.1.1 source 1.1.1.1
T16 DONE RPF'(S,G) lookup is not working for S reachable with default route.
See "RPF'(S,G) not found" in pim_rpf_update() from pim_rpf.c.
Zebra daemon RIB is not reflecting changes in kernel routes
accurately?
T17 DONE Prevent CLI from creating bogus interfaces.
Example:
conf t
interface xxx
T18 Consider reliable pim solution (refresh reduction)
A Reliable Transport Mechanism for PIM A Reliable Transport Mechanism for PIM
http://tools.ietf.org/wg/pim/draft-ietf-pim-port/ http://tools.ietf.org/wg/pim/draft-ietf-pim-port/
PORT=PIM-Over-Reliable-Transport PORT=PIM-Over-Reliable-Transport
T19 DONE Fix self as neighbor T2 If an interface changes one of its secondary IP addresses, a Hello
See mailing list post:
http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html
T20 DONE Fix debug message: "pim_neighbor_update: internal error:
trying to replace same prefix list"
See mailing list post:
http://lists.gnu.org/archive/html/qpimd-users/2009-04/msg00000.html
T21 DONE Clean-up PIM/IGMP interface mismatch debugging
See option PIM_CHECK_RECV_IFINDEX_SANITY in pimd/Makefile.am
See mailing list post:
http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00003.html
T22 DONE IGMP must be protected against adding looped MFC entries
created by both source and receiver attached to the same
interface.
T23 DONE libfrr crash after zclient_lookup_nexthop.
See mailing list post:
http://lists.nongnu.org/archive/html/qpimd-users/2009-04/msg00008.html
T24 DONE zserv may return recursive routes:
- nexthop type is set to ZEBRA_NEXTHOP_IPV4
- ifindex is not reported
- calls expecting ifindex (fib_lookup_if_vif_index) are disrupted
See also this mailing list post:
[PATCH 21/21] Link detect and recursive routes
http://www.gossamer-threads.com/lists/quagga/dev/17564
T25 DONE Zclient nexthop lookup missing OSPF route to 1.1.1.1/32
See also:
pim_zlookup.c zclient_lookup_nexthop misses OSPF 1.1.1.1/32
zebra/zebra_vty.c show_ip_route_addr_cmd hits OSPF 1.1.1.1/32
T26 DONE Zebra daemon is marking recursive static route as inactive.
FIXED: zebra daemon was incorrectly marking recursive routes
pointing to kernel routes as inactive:
zebra/zebra_rib.c nexthop_active_ipv4:
-- Original:
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL))
-- Fixed:
else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL) ||
match->type == ZEBRA_ROUTE_KERNEL)
Old problem description:
This prevents rib_match_ipv4 from returning its nexthop:
client: pim_zlookup.c zclient_read_nexthop
server: zebra/zserv.c zsend_ipv4_nexthop_lookup_v2 -> rib_match_ipv4
Kernel route is injected into zebra in zebra_rib.c rib_add_ipv4
Examples:
rt_netlink.c:726: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, flags, &p, gate, src, index, table, metric, 0);
rt_netlink.c:864: rib_add_ipv4 (ZEBRA_ROUTE_KERNEL, 0, &p, gate, src, index, table, 0, 0);
This patch didn't fix the issue:
[PATCH 21/21] Link detect and recursive routes
http://www.gossamer-threads.com/lists/quagga/dev/17564
See the example below for the route 2.2.2.2.
bash# route add -host 1.1.1.1 gw 127.0.0.1
bash# route add -host 2.2.2.2 gw 1.1.1.1
bash# netstat -nvr
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
2.2.2.2 1.1.1.1 255.255.255.255 UGH 0 0 0 lo
1.1.1.1 127.0.0.1 255.255.255.255 UGH 0 0 0 lo
192.168.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0
0.0.0.0 192.168.0.2 0.0.0.0 UG 0 0 0 eth0
bash#
zebra# sh ip route
Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,
I - ISIS, B - BGP, > - selected route, * - FIB route
K>* 0.0.0.0/0 via 192.168.0.2, eth0
K>* 1.1.1.1/32 via 127.0.0.1, lo
K * 2.2.2.2/32 via 1.1.1.1, lo inactive
C>* 127.0.0.0/8 is directly connected, lo
C>* 192.168.0.0/24 is directly connected, eth0
quagga-pimd-router# sh ip route 1.1.1.1
Address NextHop Interface Metric Preference
1.1.1.1 127.0.0.1 lo 0 0
quagga-pimd-router#
quagga-pimd-router# sh ip route 2.2.2.2
Address NextHop Interface Metric Preference
2.2.2.2 192.168.0.2 eth0 0 0
quagga-pimd-router#
T27 DONE Implement debug command
test pim receive assert
See also TODO T6: (S,G) Assert state machine.
T28 DONE Bad IPv4 address family=02 in Join/Prune dump
Reported by Andrew Lunn <andrew.lunn@ascom.ch>
# 58-byte pim v2 Join/Prune dump
# ------------------------------
# IPv4 address family=02 is wrong, correct IPv4 address family is 01
# See http://www.iana.org/assignments/address-family-numbers
#
c8XX YY03 : ip src 200.xx.yy.3
e000 000d : ip dst 224.0.0.13
9404 0000 : ip router alert option 148.4.0.0
2300 ab13 : pimv2,type=3 res=00 checksum=ab13
0200 : upstream family=02, encoding=00
c8XX YY08 : upstream 200.xx.yy.8
0001 00d2 : res=00 groups=01 holdtime=00d2
0200 0020 : group family=02, encoding=00, res=00, mask_len=20
ef01 0101 : group address 239.1.1.1
0001 0000 : joined=0001 pruned=0000
0200 0020 : source family=02, encoding=00, res=00, mask_len=20
0101 0101 : source address 1.1.1.1
T29 DONE Reset interface PIM-hello-sent counter when primary address changes
See pim_ifp->pim_ifstat_hello_sent
RFC 4601: 4.3.1. Sending Hello Messages
Thus, if a router needs to send a Join/Prune or Assert message on
an interface on which it has not yet sent a Hello message with the
currently configured IP address, then it MUST immediately send the
relevant Hello message without waiting for the Hello Timer to
expire, followed by the Join/Prune or Assert message.
T30 DONE Run interface DR election when primary address changes
Reported by Andrew Lunn <andrew.lunn@ascom.ch>
See pim_if_dr_election().
T31 If an interface changes one of its secondary IP addresses, a Hello
message with an updated Address_List option and a non-zero message with an updated Address_List option and a non-zero
HoldTime should be sent immediately. HoldTime should be sent immediately.
See also detect_secondary_address_change See also detect_secondary_address_change
See also CAVEAT C15. See also CAVEAT C15.
See also RFC 4601: 4.3.1. Sending Hello Messages See also RFC 4601: 4.3.1. Sending Hello Messages
T32 FIXED Detection of interface primary address changes may fail when T3 Lightweight MLDv2
there are multiple addresses.
See also CAVEAT C14.
pim_find_primary_addr() should return interface primary address
from connected list. Currently it returns the first address.
Zebra daemon "show int" is able to keep the primary address as
first address.
T33 DONE Implement debug command: test pim receive upcall
See also TODO T6: (S,G) Assert state machine.
T34 DONE assert_action_a1
T35 DONE Review macros depending on interface I.
See also: grep ,I\) pimd/*.c
For the case (S,G,I) check if I is either
1) interface attached to this per-interface S,G state (don't think so)
or
2) an arbitrary interface (most probably)
For the arbitrary interface case (2), consider representing
interface ifp as its primary address (struct in_addr ifaddr). The
benefit is in_addr does not need to be dereferenced, so it does
not demand protection against crashes.
T36 DONE React to zebra daemon link-detect up/down notification.
pim_ifp->primary_address is managed by detect_primary_address_change()
depending on to ifp->connected (managed by zebra_interface_address_read()).
T37 DONE Review list of variables which may affect pim_upstream.c
pim_upstream_evaluate_join_desired().
Call pim_upstream_update_join_desired() accordingly.
See the order of invokation:
pim_if_dr_election(ifp);
pim_if_update_join_desired(pim_ifp); /* depends on DR */
pim_if_update_could_assert(ifp); /* depends on DR */
pim_if_update_my_assert_metric(ifp); /* depends on could_assert */
join_desired depends on:
pim_ifp->primary_address
pim_ifp->pim_dr_addr
ch->ifassert_winner_metric
ch->ifassert_winner
ch->local_ifmembership
ch->ifjoin_state
ch->upstream->rpf.source_nexthop.mrib_metric_preference
ch->upstream->rpf.source_nexthop.mrib_route_metric
ch->upstream->rpf.source_nexthop.interface
T38 DONE Detect change in AssertTrackingDesired(S,G,I)
See the order of invokation:
dr_election: none
update_join_desired: depends on DR
update_tracking_desired: depends on DR, join_desired
AssertTrackingDesired(S,G,I) depends on:
pim_ifp->primary_address
pim_ifp->pim_dr_addr
ch->local_ifmembership
ch->ifassert_winner
ch->ifjoin_state
ch->upstream->rpf.source_nexthop.interface
PIM_UPSTREAM_FLAG_TEST_DR_JOIN_DESIRED(ch->upstream->flags)
T39 DONE AssertTrackingDesired: flags is not matching evaluation
# show ip pim assert-internal
CA: CouldAssert
ECA: Evaluate CouldAssert
ATD: AssertTrackingDesired
eATD: Evaluate AssertTrackingDesired
Interface Address Source Group CA eCA ATD eATD
eth0 192.168.1.100 1.1.1.1 239.1.1.1 no no no yes
#
T40 Lightweight MLDv2
http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05 http://tools.ietf.org/html/draft-ietf-mboned-lightweight-igmpv3-mldv2-05
http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt http://www.ietf.org/internet-drafts/draft-ietf-mboned-lightweight-igmpv3-mldv2-05.txt
http://www.ietf.org/html.charters/mboned-charter.html http://www.ietf.org/html.charters/mboned-charter.html
T41 DONE ssmping support T4 Static igmp join fails when loading config at boot time
See also:
http://www.venaas.no/multicast/ssmping/
draft-ietf-mboned-ssmping-07
http://tools.ietf.org/html/draft-ietf-mboned-ssmping-07
Example:
debug ssmpingd
conf t
ip ssmpingd 1.1.1.1
show ip ssmpingd
T42 Static igmp join fails when loading config at boot time
! Wrong behavior seen at boot time: ! Wrong behavior seen at boot time:
! !
@ -396,7 +42,7 @@ T42 Static igmp join fails when loading config at boot time
eth0 200.202.112.3 2 2 0 0 0 0 eth0 200.202.112.3 2 2 0 0 0 0
lo 127.0.0.1 1 1 0 0 0 0 lo 127.0.0.1 1 1 0 0 0 0
T43 PIM Neighbor Reduction T5 PIM Neighbor Reduction
https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/ https://datatracker.ietf.org/doc/draft-wijnands-pim-neighbor-reduction/
"In a transit LAN (no directly connected source or receiver), many "In a transit LAN (no directly connected source or receiver), many
@ -404,19 +50,19 @@ T43 PIM Neighbor Reduction
a procedure to reduce the amount of neighbors established over a a procedure to reduce the amount of neighbors established over a
transit LAN." transit LAN."
T44 Single Stream Multicast Fast Reroute (SMFR) Method T6 Single Stream Multicast Fast Reroute (SMFR) Method
https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/ https://datatracker.ietf.org/doc/draft-liu-pim-single-stream-multicast-frr/
"This document proposes an IP multicast fast convergence method "This document proposes an IP multicast fast convergence method
based on differentiating primary and backup PIM join." based on differentiating primary and backup PIM join."
T45 RFC5384 - The Join Attribute Format T7 RFC5384 - The Join Attribute Format
"This document describes a modification of the Join message that "This document describes a modification of the Join message that
allows a node to associate attributes with a particular tree." allows a node to associate attributes with a particular tree."
T46 PIM Multi-Topology ID (MT-ID) Join-Attribute T8 PIM Multi-Topology ID (MT-ID) Join-Attribute
http://tools.ietf.org/html/draft-cai-pim-mtid-00 http://tools.ietf.org/html/draft-cai-pim-mtid-00
Depends on T45. Depends on T7.
"This draft introduces a new type of PIM Join Attribute used to "This draft introduces a new type of PIM Join Attribute used to
encode the identity of the topology PIM uses for RPF." encode the identity of the topology PIM uses for RPF."

View file

@ -169,6 +169,7 @@ void route_add(struct prefix *p, struct nexthop *nh)
api.safi = SAFI_UNICAST; api.safi = SAFI_UNICAST;
memcpy(&api.prefix, p, sizeof(*p)); memcpy(&api.prefix, p, sizeof(*p));
SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION);
SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP);
api_nh = &api.nexthops[0]; api_nh = &api.nexthops[0];

4
tests/.gitignore vendored
View file

@ -24,10 +24,12 @@ __pycache__
/bgpd/test_ecommunity /bgpd/test_ecommunity
/bgpd/test_mp_attr /bgpd/test_mp_attr
/bgpd/test_mpath /bgpd/test_mpath
/bgpd/test_packet
/isisd/test_fuzz_isis_tlv /isisd/test_fuzz_isis_tlv
/isisd/test_fuzz_isis_tlv_tests.h /isisd/test_fuzz_isis_tlv_tests.h
/isisd/test_isis_vertex_queue /isisd/test_isis_vertex_queue
/lib/cli/test_cli /lib/cli/test_cli
/lib/cli/test_cli_clippy.c
/lib/cli/test_commands /lib/cli/test_commands
/lib/cli/test_commands_defun.c /lib/cli/test_commands_defun.c
/lib/test_buffer /lib/test_buffer
@ -38,6 +40,7 @@ __pycache__
/lib/test_memory /lib/test_memory
/lib/test_nexthop_iter /lib/test_nexthop_iter
/lib/test_privs /lib/test_privs
/lib/test_ringbuf
/lib/test_srcdest_table /lib/test_srcdest_table
/lib/test_segv /lib/test_segv
/lib/test_sig /lib/test_sig
@ -48,3 +51,4 @@ __pycache__
/lib/test_ttable /lib/test_ttable
/lib/test_zmq /lib/test_zmq
/ospf6d/test_lsdb /ospf6d/test_lsdb
/ospf6d/test_lsdb_clippy.c

View file

@ -1,4 +1,6 @@
import frrtest import frrtest
class TestRingbuf(frrtest.TestExitNonzero): class TestRingbuf(frrtest.TestMultiOut):
program = './test_ringbuf' program = './test_ringbuf'
TestRingbuf.exit_cleanly()

View file

@ -539,7 +539,7 @@ int vtysh_mark_file(const char *filename)
} }
vty = vty_new(); vty = vty_new();
vty->fd = 0; /* stdout */ vty->wfd = STDERR_FILENO;
vty->type = VTY_TERM; vty->type = VTY_TERM;
vty->node = CONFIG_NODE; vty->node = CONFIG_NODE;

View file

@ -398,7 +398,7 @@ static int vtysh_read_file(FILE *confp)
int ret; int ret;
vty = vty_new(); vty = vty_new();
vty->fd = 0; /* stdout */ vty->wfd = STDERR_FILENO;
vty->type = VTY_TERM; vty->type = VTY_TERM;
vty->node = CONFIG_NODE; vty->node = CONFIG_NODE;
@ -448,6 +448,11 @@ void vtysh_config_write()
sprintf(line, "hostname %s", cmd_hostname_get()); sprintf(line, "hostname %s", cmd_hostname_get());
vtysh_config_parse_line(NULL, line); vtysh_config_parse_line(NULL, line);
} }
if (cmd_domainname_get()) {
sprintf(line, "domainname %s", cmd_domainname_get());
vtysh_config_parse_line(NULL, line);
}
if (vtysh_write_integrated == WRITE_INTEGRATED_NO) if (vtysh_write_integrated == WRITE_INTEGRATED_NO)
vtysh_config_parse_line(NULL, vtysh_config_parse_line(NULL,
"no service integrated-vtysh-config"); "no service integrated-vtysh-config");

View file

@ -520,7 +520,7 @@ static int nexthop_active(afi_t afi, struct route_entry *re,
nexthop->ifindex = newhop->ifindex; nexthop->ifindex = newhop->ifindex;
} }
return 1; return 1;
} else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_INTERNAL)) { } else if (CHECK_FLAG(re->flags, ZEBRA_FLAG_ALLOW_RECURSION)) {
resolved = 0; resolved = 0;
for (ALL_NEXTHOPS(match->nexthop, newhop)) { for (ALL_NEXTHOPS(match->nexthop, newhop)) {
if (!CHECK_FLAG(newhop->flags, if (!CHECK_FLAG(newhop->flags,

View file

@ -1019,6 +1019,8 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
break; break;
} }
if (re) { if (re) {
stream_putc(s, re->type);
stream_putw(s, re->instance);
stream_putc(s, re->distance); stream_putc(s, re->distance);
stream_putl(s, re->metric); stream_putl(s, re->metric);
num = 0; num = 0;
@ -1054,6 +1056,8 @@ static int send_client(struct rnh *rnh, struct zserv *client, rnh_type_t type,
} }
stream_putc_at(s, nump, num); stream_putc_at(s, nump, num);
} else { } else {
stream_putc(s, 0); // type
stream_putw(s, 0); // instance
stream_putc(s, 0); // distance stream_putc(s, 0); // distance
stream_putl(s, 0); // metric stream_putl(s, 0); // metric
stream_putc(s, 0); // nexthops stream_putc(s, 0); // nexthops

View file

@ -1135,7 +1135,8 @@ static void zvni_print_hash(struct hash_backet *backet, void *ctxt[])
"%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n", "%-10u %-4s %-21s %-8u %-8u %-15u %-37s\n",
zvni->vni, "L2", zvni->vni, "L2",
zvni->vxlan_if ? zvni->vxlan_if->name : "unknown", zvni->vxlan_if ? zvni->vxlan_if->name : "unknown",
num_macs, num_neigh, num_vteps, num_macs, num_neigh,
num_vteps,
vrf_id_to_name(zvni->vrf_id)); vrf_id_to_name(zvni->vrf_id));
else { else {
char vni_str[VNI_STR_LEN]; char vni_str[VNI_STR_LEN];
@ -1858,20 +1859,18 @@ static int zvni_gw_macip_del(struct interface *ifp, zebra_vni_t *zvni,
return -1; return -1;
/* only need to delete the entry from bgp if we sent it before */ /* only need to delete the entry from bgp if we sent it before */
if (advertise_gw_macip_enabled(zvni)) { if (IS_ZEBRA_DEBUG_VXLAN)
if (IS_ZEBRA_DEBUG_VXLAN) zlog_debug("%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP",
zlog_debug("%u:SVI %s(%u) VNI %u, sending GW MAC %s IP %s del to BGP", ifp->vrf_id, ifp->name,
ifp->vrf_id, ifp->name, ifp->ifindex, zvni->vni,
ifp->ifindex, zvni->vni, prefix_mac2str(&(n->emac),
prefix_mac2str(&(n->emac), NULL,
NULL, ETHER_ADDR_STRLEN),
ETHER_ADDR_STRLEN), ipaddr2str(ip, buf2, sizeof(buf2)));
ipaddr2str(ip, buf2, sizeof(buf2)));
/* Remove neighbor from BGP. */ /* Remove neighbor from BGP. */
zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac, zvni_neigh_send_del_to_client(zvni->vni, &n->ip, &n->emac,
ZEBRA_MACIP_TYPE_GW); ZEBRA_MACIP_TYPE_GW);
}
/* Delete this neighbor entry. */ /* Delete this neighbor entry. */
zvni_neigh_del(zvni, n); zvni_neigh_del(zvni, n);
@ -6256,15 +6255,33 @@ int zebra_vxlan_if_update(struct interface *ifp, u_int16_t chgflags)
zebra_vxlan_process_l3vni_oper_down(zl3vni); zebra_vxlan_process_l3vni_oper_down(zl3vni);
zl3vni->svi_if = NULL; zl3vni->svi_if = NULL;
zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni); zl3vni->svi_if = zl3vni_map_to_svi_if(zl3vni);
zl3vni->local_vtep_ip = vxl->vtep_ip;
if (is_l3vni_oper_up(zl3vni)) if (is_l3vni_oper_up(zl3vni))
zebra_vxlan_process_l3vni_oper_up( zebra_vxlan_process_l3vni_oper_up(
zl3vni); zl3vni);
} }
} }
/*
* local-ip change - process oper down, associate with new
* local-ip and then process oper up again
*/
if (chgflags & ZEBRA_VXLIF_LOCAL_IP_CHANGE) {
if (if_is_operative(ifp)) {
zebra_vxlan_process_l3vni_oper_down(zl3vni);
zl3vni->local_vtep_ip = vxl->vtep_ip;
if (is_l3vni_oper_up(zl3vni))
zebra_vxlan_process_l3vni_oper_up(
zl3vni);
}
}
/* Update local tunnel IP. */
zl3vni->local_vtep_ip = vxl->vtep_ip;
/* if we have a valid new master, process l3-vni oper up */ /* if we have a valid new master, process l3-vni oper up */
if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) { if (chgflags & ZEBRA_VXLIF_MASTER_CHANGE) {
if (is_l3vni_oper_up(zl3vni)) if (if_is_operative(ifp) && is_l3vni_oper_up(zl3vni))
zebra_vxlan_process_l3vni_oper_up(zl3vni); zebra_vxlan_process_l3vni_oper_up(zl3vni);
} }
} else { } else {
@ -6704,6 +6721,10 @@ int zebra_vxlan_advertise_gw_macip(struct zserv *client, u_short length,
struct interface *vlan_if = NULL; struct interface *vlan_if = NULL;
struct interface *vrr_if = NULL; struct interface *vrr_if = NULL;
zvni = zvni_lookup(vni);
if (!zvni)
return 0;
if (IS_ZEBRA_DEBUG_VXLAN) if (IS_ZEBRA_DEBUG_VXLAN)
zlog_debug( zlog_debug(
"EVPN gateway macip Adv %s on VNI %d , currently %s", "EVPN gateway macip Adv %s on VNI %d , currently %s",
@ -6712,10 +6733,6 @@ int zebra_vxlan_advertise_gw_macip(struct zserv *client, u_short length,
? "enabled" ? "enabled"
: "disabled"); : "disabled");
zvni = zvni_lookup(vni);
if (!zvni)
return 0;
if (zvni->advertise_gw_macip == advertise) if (zvni->advertise_gw_macip == advertise)
return 0; return 0;

View file

@ -1167,12 +1167,12 @@ static int zread_route_add(struct zserv *client, u_short length,
switch (api_nh->type) { switch (api_nh->type) {
case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFINDEX:
nexthop = route_entry_nexthop_ifindex_add( nexthop = route_entry_nexthop_ifindex_add(
re, api_nh->ifindex, re->vrf_id); re, api_nh->ifindex, api_nh->vrf_id);
break; break;
case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4:
nexthop = route_entry_nexthop_ipv4_add( nexthop = route_entry_nexthop_ipv4_add(
re, &api_nh->gate.ipv4, NULL, re, &api_nh->gate.ipv4, NULL,
re->vrf_id); api_nh->vrf_id);
break; break;
case NEXTHOP_TYPE_IPV4_IFINDEX: case NEXTHOP_TYPE_IPV4_IFINDEX:
@ -1187,7 +1187,7 @@ static int zread_route_add(struct zserv *client, u_short length,
nexthop = route_entry_nexthop_ipv4_ifindex_add( nexthop = route_entry_nexthop_ipv4_ifindex_add(
re, &api_nh->gate.ipv4, NULL, ifindex, re, &api_nh->gate.ipv4, NULL, ifindex,
re->vrf_id); api_nh->vrf_id);
/* if this an EVPN route entry, /* if this an EVPN route entry,
* program the nh as neigh * program the nh as neigh
@ -1209,7 +1209,7 @@ static int zread_route_add(struct zserv *client, u_short length,
break; break;
case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6:
nexthop = route_entry_nexthop_ipv6_add( nexthop = route_entry_nexthop_ipv6_add(
re, &api_nh->gate.ipv6, re->vrf_id); re, &api_nh->gate.ipv6, api_nh->vrf_id);
break; break;
case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6_IFINDEX:
memset(&vtep_ip, 0, sizeof(struct ipaddr)); memset(&vtep_ip, 0, sizeof(struct ipaddr));
@ -1223,7 +1223,7 @@ static int zread_route_add(struct zserv *client, u_short length,
nexthop = route_entry_nexthop_ipv6_ifindex_add( nexthop = route_entry_nexthop_ipv6_ifindex_add(
re, &api_nh->gate.ipv6, ifindex, re, &api_nh->gate.ipv6, ifindex,
re->vrf_id); api_nh->vrf_id);
/* if this an EVPN route entry, /* if this an EVPN route entry,
* program the nh as neigh * program the nh as neigh