mirror of
				https://git.proxmox.com/git/mirror_frr
				synced 2025-10-26 05:24:56 +00:00 
			
		
		
		
	 2b64873d24
			
		
	
	
		2b64873d24
		
	
	
	
	
		
			
			const const const your boat, merrily down the stream... Signed-off-by: David Lamparter <equinox@diac24.net>
		
			
				
	
	
		
			1028 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1028 lines
		
	
	
		
			26 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * OSPF routing table.
 | |
|  * Copyright (C) 1999, 2000 Toshiaki Takada
 | |
|  *
 | |
|  * This file is part of GNU Zebra.
 | |
|  *
 | |
|  * GNU Zebra is free software; you can redistribute it and/or modify it
 | |
|  * under the terms of the GNU General Public License as published by the
 | |
|  * Free Software Foundation; either version 2, or (at your option) any
 | |
|  * later version.
 | |
|  *
 | |
|  * GNU Zebra is distributed in the hope that it will be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along
 | |
|  * with this program; see the file COPYING; if not, write to the Free Software
 | |
|  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| #include <zebra.h>
 | |
| 
 | |
| #include "prefix.h"
 | |
| #include "table.h"
 | |
| #include "memory.h"
 | |
| #include "linklist.h"
 | |
| #include "log.h"
 | |
| #include "if.h"
 | |
| #include "command.h"
 | |
| #include "sockunion.h"
 | |
| 
 | |
| #include "ospfd/ospfd.h"
 | |
| #include "ospfd/ospf_interface.h"
 | |
| #include "ospfd/ospf_asbr.h"
 | |
| #include "ospfd/ospf_lsa.h"
 | |
| #include "ospfd/ospf_route.h"
 | |
| #include "ospfd/ospf_spf.h"
 | |
| #include "ospfd/ospf_zebra.h"
 | |
| #include "ospfd/ospf_dump.h"
 | |
| 
 | |
| struct ospf_route *ospf_route_new(void)
 | |
| {
 | |
| 	struct ospf_route *new;
 | |
| 
 | |
| 	new = XCALLOC(MTYPE_OSPF_ROUTE, sizeof(struct ospf_route));
 | |
| 
 | |
| 	new->paths = list_new();
 | |
| 	new->paths->del = (void (*)(void *))ospf_path_free;
 | |
| 
 | |
| 	return new;
 | |
| }
 | |
| 
 | |
| void ospf_route_free(struct ospf_route *or)
 | |
| {
 | |
| 	if (or->paths)
 | |
| 		list_delete(& or->paths);
 | |
| 
 | |
| 	XFREE(MTYPE_OSPF_ROUTE, or);
 | |
| }
 | |
| 
 | |
| struct ospf_path *ospf_path_new(void)
 | |
| {
 | |
| 	struct ospf_path *new;
 | |
| 
 | |
| 	new = XCALLOC(MTYPE_OSPF_PATH, sizeof(struct ospf_path));
 | |
| 
 | |
| 	return new;
 | |
| }
 | |
| 
 | |
| static struct ospf_path *ospf_path_dup(struct ospf_path *path)
 | |
| {
 | |
| 	struct ospf_path *new;
 | |
| 
 | |
| 	new = ospf_path_new();
 | |
| 	memcpy(new, path, sizeof(struct ospf_path));
 | |
| 
 | |
| 	return new;
 | |
| }
 | |
| 
 | |
| void ospf_path_free(struct ospf_path *op)
 | |
| {
 | |
| 	XFREE(MTYPE_OSPF_PATH, op);
 | |
| }
 | |
| 
 | |
| void ospf_route_delete(struct ospf *ospf, struct route_table *rt)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 
 | |
| 	for (rn = route_top(rt); rn; rn = route_next(rn))
 | |
| 		if ((or = rn->info) != NULL) {
 | |
| 			if (or->type == OSPF_DESTINATION_NETWORK)
 | |
| 				ospf_zebra_delete(
 | |
| 					ospf, (struct prefix_ipv4 *)&rn->p, or);
 | |
| 			else if (or->type == OSPF_DESTINATION_DISCARD)
 | |
| 				ospf_zebra_delete_discard(
 | |
| 					ospf, (struct prefix_ipv4 *)&rn->p);
 | |
| 		}
 | |
| }
 | |
| 
 | |
| void ospf_route_table_free(struct route_table *rt)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 
 | |
| 	for (rn = route_top(rt); rn; rn = route_next(rn))
 | |
| 		if ((or = rn->info) != NULL) {
 | |
| 			ospf_route_free(or);
 | |
| 
 | |
| 			rn->info = NULL;
 | |
| 			route_unlock_node(rn);
 | |
| 		}
 | |
| 
 | |
| 	route_table_finish(rt);
 | |
| }
 | |
| 
 | |
| /* If a prefix exists in the new routing table, then return 1,
 | |
|    otherwise return 0. Since the ZEBRA-RIB does an implicit
 | |
|    withdraw, it is not necessary to send a delete, an add later
 | |
|    will act like an implicit delete. */
 | |
| static int ospf_route_exist_new_table(struct route_table *rt,
 | |
| 				      struct prefix_ipv4 *prefix)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 
 | |
| 	assert(rt);
 | |
| 	assert(prefix);
 | |
| 
 | |
| 	rn = route_node_lookup(rt, (struct prefix *)prefix);
 | |
| 	if (!rn) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 	route_unlock_node(rn);
 | |
| 
 | |
| 	if (!rn->info) {
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| /* If a prefix and a nexthop match any route in the routing table,
 | |
|    then return 1, otherwise return 0. */
 | |
| int ospf_route_match_same(struct route_table *rt, struct prefix_ipv4 *prefix,
 | |
| 			  struct ospf_route *newor)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 	struct ospf_path *op;
 | |
| 	struct ospf_path *newop;
 | |
| 	struct listnode *n1;
 | |
| 	struct listnode *n2;
 | |
| 
 | |
| 	if (!rt || !prefix)
 | |
| 		return 0;
 | |
| 
 | |
| 	rn = route_node_lookup(rt, (struct prefix *)prefix);
 | |
| 	if (!rn || !rn->info)
 | |
| 		return 0;
 | |
| 
 | |
| 	route_unlock_node(rn);
 | |
| 
 | |
| 	or = rn->info;
 | |
| 	if (or->type == newor->type && or->cost == newor->cost) {
 | |
| 		if (or->type == OSPF_DESTINATION_NETWORK) {
 | |
| 			if (or->paths->count != newor->paths->count)
 | |
| 				return 0;
 | |
| 
 | |
| 			/* Check each path. */
 | |
| 			for (n1 = listhead(or->paths),
 | |
| 			    n2 = listhead(newor->paths);
 | |
| 			     n1 && n2; n1 = listnextnode_unchecked(n1),
 | |
| 			    n2 = listnextnode_unchecked(n2)) {
 | |
| 				op = listgetdata(n1);
 | |
| 				newop = listgetdata(n2);
 | |
| 
 | |
| 				if (!IPV4_ADDR_SAME(&op->nexthop,
 | |
| 						    &newop->nexthop))
 | |
| 					return 0;
 | |
| 				if (op->ifindex != newop->ifindex)
 | |
| 					return 0;
 | |
| 			}
 | |
| 			return 1;
 | |
| 		} else if (prefix_same(&rn->p, (struct prefix *)prefix))
 | |
| 			return 1;
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* delete routes generated from AS-External routes if there is a inter/intra
 | |
|  * area route
 | |
|  */
 | |
| static void ospf_route_delete_same_ext(struct ospf *ospf,
 | |
| 				       struct route_table *external_routes,
 | |
| 				       struct route_table *routes)
 | |
| {
 | |
| 	struct route_node *rn, *ext_rn;
 | |
| 
 | |
| 	if ((external_routes == NULL) || (routes == NULL))
 | |
| 		return;
 | |
| 
 | |
| 	/* Remove deleted routes */
 | |
| 	for (rn = route_top(routes); rn; rn = route_next(rn)) {
 | |
| 		if (rn && rn->info) {
 | |
| 			struct prefix_ipv4 *p = (struct prefix_ipv4 *)(&rn->p);
 | |
| 			if ((ext_rn = route_node_lookup(external_routes,
 | |
| 							(struct prefix *)p))) {
 | |
| 				if (ext_rn->info) {
 | |
| 					ospf_zebra_delete(ospf, p,
 | |
| 							  ext_rn->info);
 | |
| 					ospf_route_free(ext_rn->info);
 | |
| 					ext_rn->info = NULL;
 | |
| 				}
 | |
| 				route_unlock_node(ext_rn);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* rt: Old, cmprt: New */
 | |
| static void ospf_route_delete_uniq(struct ospf *ospf, struct route_table *rt,
 | |
| 				   struct route_table *cmprt)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 
 | |
| 	for (rn = route_top(rt); rn; rn = route_next(rn))
 | |
| 		if ((or = rn->info) != NULL)
 | |
| 			if (or->path_type == OSPF_PATH_INTRA_AREA ||
 | |
| 			    or->path_type == OSPF_PATH_INTER_AREA) {
 | |
| 				if (or->type == OSPF_DESTINATION_NETWORK) {
 | |
| 					if (!ospf_route_exist_new_table(
 | |
| 						    cmprt,
 | |
| 						    (struct prefix_ipv4 *)&rn
 | |
| 							    ->p))
 | |
| 						ospf_zebra_delete(
 | |
| 							ospf,
 | |
| 							(struct prefix_ipv4
 | |
| 								 *)&rn->p,
 | |
| 							or);
 | |
| 				} else if (or->type == OSPF_DESTINATION_DISCARD)
 | |
| 					if (!ospf_route_exist_new_table(
 | |
| 						    cmprt,
 | |
| 						    (struct prefix_ipv4 *)&rn
 | |
| 							    ->p))
 | |
| 						ospf_zebra_delete_discard(
 | |
| 							ospf,
 | |
| 							(struct prefix_ipv4
 | |
| 								 *)&rn->p);
 | |
| 			}
 | |
| }
 | |
| 
 | |
| /* Install routes to table. */
 | |
| void ospf_route_install(struct ospf *ospf, struct route_table *rt)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 
 | |
| 	/* rt contains new routing table, new_table contains an old one.
 | |
| 	   updating pointers */
 | |
| 	if (ospf->old_table)
 | |
| 		ospf_route_table_free(ospf->old_table);
 | |
| 
 | |
| 	ospf->old_table = ospf->new_table;
 | |
| 	ospf->new_table = rt;
 | |
| 
 | |
| 	/* Delete old routes. */
 | |
| 	if (ospf->old_table)
 | |
| 		ospf_route_delete_uniq(ospf, ospf->old_table, rt);
 | |
| 	if (ospf->old_external_route)
 | |
| 		ospf_route_delete_same_ext(ospf, ospf->old_external_route, rt);
 | |
| 
 | |
| 	/* Install new routes. */
 | |
| 	for (rn = route_top(rt); rn; rn = route_next(rn))
 | |
| 		if ((or = rn->info) != NULL) {
 | |
| 			if (or->type == OSPF_DESTINATION_NETWORK) {
 | |
| 				if (!ospf_route_match_same(
 | |
| 					    ospf->old_table,
 | |
| 					    (struct prefix_ipv4 *)&rn->p, or))
 | |
| 					ospf_zebra_add(
 | |
| 						ospf,
 | |
| 						(struct prefix_ipv4 *)&rn->p,
 | |
| 						or);
 | |
| 			} else if (or->type == OSPF_DESTINATION_DISCARD)
 | |
| 				if (!ospf_route_match_same(
 | |
| 					    ospf->old_table,
 | |
| 					    (struct prefix_ipv4 *)&rn->p, or))
 | |
| 					ospf_zebra_add_discard(
 | |
| 						ospf,
 | |
| 						(struct prefix_ipv4 *)&rn->p);
 | |
| 		}
 | |
| }
 | |
| 
 | |
| /* RFC2328 16.1. (4). For "router". */
 | |
| void ospf_intra_add_router(struct route_table *rt, struct vertex *v,
 | |
| 			   struct ospf_area *area)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 	struct prefix_ipv4 p;
 | |
| 	struct router_lsa *lsa;
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("ospf_intra_add_router: Start");
 | |
| 
 | |
| 	lsa = (struct router_lsa *)v->lsa;
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("ospf_intra_add_router: LS ID: %s",
 | |
| 			   inet_ntoa(lsa->header.id));
 | |
| 
 | |
| 	if (!OSPF_IS_AREA_BACKBONE(area))
 | |
| 		ospf_vl_up_check(area, lsa->header.id, v);
 | |
| 
 | |
| 	if (!CHECK_FLAG(lsa->flags, ROUTER_LSA_SHORTCUT))
 | |
| 		area->shortcut_capability = 0;
 | |
| 
 | |
| 	/* If the newly added vertex is an area border router or AS boundary
 | |
| 	   router, a routing table entry is added whose destination type is
 | |
| 	   "router". */
 | |
| 	if (!IS_ROUTER_LSA_BORDER(lsa) && !IS_ROUTER_LSA_EXTERNAL(lsa)) {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug(
 | |
| 				"ospf_intra_add_router: "
 | |
| 				"this router is neither ASBR nor ABR, skipping it");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* Update ABR and ASBR count in this area. */
 | |
| 	if (IS_ROUTER_LSA_BORDER(lsa))
 | |
| 		area->abr_count++;
 | |
| 	if (IS_ROUTER_LSA_EXTERNAL(lsa))
 | |
| 		area->asbr_count++;
 | |
| 
 | |
| 	/* The Options field found in the associated router-LSA is copied
 | |
| 	   into the routing table entry's Optional capabilities field. Call
 | |
| 	   the newly added vertex Router X. */
 | |
| 	or = ospf_route_new();
 | |
| 
 | |
| 	or->id = v->id;
 | |
| 	or->u.std.area_id = area->area_id;
 | |
| 	or->u.std.external_routing = area->external_routing;
 | |
| 	or->path_type = OSPF_PATH_INTRA_AREA;
 | |
| 	or->cost = v->distance;
 | |
| 	or->type = OSPF_DESTINATION_ROUTER;
 | |
| 	or->u.std.origin = (struct lsa_header *)lsa;
 | |
| 	or->u.std.options = lsa->header.options;
 | |
| 	or->u.std.flags = lsa->flags;
 | |
| 
 | |
| 	/* If Router X is the endpoint of one of the calculating router's
 | |
| 	   virtual links, and the virtual link uses Area A as Transit area:
 | |
| 	   the virtual link is declared up, the IP address of the virtual
 | |
| 	   interface is set to the IP address of the outgoing interface
 | |
| 	   calculated above for Router X, and the virtual neighbor's IP
 | |
| 	   address is set to Router X's interface address (contained in
 | |
| 	   Router X's router-LSA) that points back to the root of the
 | |
| 	   shortest- path tree; equivalently, this is the interface that
 | |
| 	   points back to Router X's parent vertex on the shortest-path tree
 | |
| 	   (similar to the calculation in Section 16.1.1). */
 | |
| 
 | |
| 	p.family = AF_INET;
 | |
| 	p.prefix = v->id;
 | |
| 	p.prefixlen = IPV4_MAX_BITLEN;
 | |
| 	apply_mask_ipv4(&p);
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("ospf_intra_add_router: talking about %s/%d",
 | |
| 			   inet_ntoa(p.prefix), p.prefixlen);
 | |
| 
 | |
| 	rn = route_node_get(rt, (struct prefix *)&p);
 | |
| 
 | |
| 	/* Note that we keep all routes to ABRs and ASBRs, not only the best */
 | |
| 	if (rn->info == NULL)
 | |
| 		rn->info = list_new();
 | |
| 	else
 | |
| 		route_unlock_node(rn);
 | |
| 
 | |
| 	ospf_route_copy_nexthops_from_vertex(or, v);
 | |
| 
 | |
| 	listnode_add(rn->info, or);
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("ospf_intra_add_router: Stop");
 | |
| }
 | |
| 
 | |
| /* RFC2328 16.1. (4).  For transit network. */
 | |
| void ospf_intra_add_transit(struct route_table *rt, struct vertex *v,
 | |
| 			    struct ospf_area *area)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 	struct prefix_ipv4 p;
 | |
| 	struct network_lsa *lsa;
 | |
| 
 | |
| 	lsa = (struct network_lsa *)v->lsa;
 | |
| 
 | |
| 	/* If the newly added vertex is a transit network, the routing table
 | |
| 	   entry for the network is located.  The entry's Destination ID is
 | |
| 	   the IP network number, which can be obtained by masking the
 | |
| 	   Vertex ID (Link State ID) with its associated subnet mask (found
 | |
| 	   in the body of the associated network-LSA). */
 | |
| 	p.family = AF_INET;
 | |
| 	p.prefix = v->id;
 | |
| 	p.prefixlen = ip_masklen(lsa->mask);
 | |
| 	apply_mask_ipv4(&p);
 | |
| 
 | |
| 	rn = route_node_get(rt, (struct prefix *)&p);
 | |
| 
 | |
| 	/* If the routing table entry already exists (i.e., there is already
 | |
| 	   an intra-area route to the destination installed in the routing
 | |
| 	   table), multiple vertices have mapped to the same IP network.
 | |
| 	   For example, this can occur when a new Designated Router is being
 | |
| 	   established.  In this case, the current routing table entry
 | |
| 	   should be overwritten if and only if the newly found path is just
 | |
| 	   as short and the current routing table entry's Link State Origin
 | |
| 	   has a smaller Link State ID than the newly added vertex' LSA. */
 | |
| 	if (rn->info) {
 | |
| 		struct ospf_route *cur_or;
 | |
| 
 | |
| 		route_unlock_node(rn);
 | |
| 		cur_or = rn->info;
 | |
| 
 | |
| 		if (v->distance > cur_or->cost
 | |
| 		    || IPV4_ADDR_CMP(&cur_or->u.std.origin->id, &lsa->header.id)
 | |
| 			       > 0)
 | |
| 			return;
 | |
| 
 | |
| 		ospf_route_free(rn->info);
 | |
| 	}
 | |
| 
 | |
| 	or = ospf_route_new();
 | |
| 
 | |
| 	or->id = v->id;
 | |
| 	or->u.std.area_id = area->area_id;
 | |
| 	or->u.std.external_routing = area->external_routing;
 | |
| 	or->path_type = OSPF_PATH_INTRA_AREA;
 | |
| 	or->cost = v->distance;
 | |
| 	or->type = OSPF_DESTINATION_NETWORK;
 | |
| 	or->u.std.origin = (struct lsa_header *)lsa;
 | |
| 
 | |
| 	ospf_route_copy_nexthops_from_vertex(or, v);
 | |
| 
 | |
| 	rn->info = or ;
 | |
| }
 | |
| 
 | |
| /* RFC2328 16.1. second stage. */
 | |
| void ospf_intra_add_stub(struct route_table *rt, struct router_lsa_link *link,
 | |
| 			 struct vertex *v, struct ospf_area *area,
 | |
| 			 int parent_is_root, int lsa_pos)
 | |
| {
 | |
| 	uint32_t cost;
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 	struct prefix_ipv4 p;
 | |
| 	struct router_lsa *lsa;
 | |
| 	struct ospf_interface *oi;
 | |
| 	struct ospf_path *path;
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("ospf_intra_add_stub(): Start");
 | |
| 
 | |
| 	lsa = (struct router_lsa *)v->lsa;
 | |
| 
 | |
| 	p.family = AF_INET;
 | |
| 	p.prefix = link->link_id;
 | |
| 	p.prefixlen = ip_masklen(link->link_data);
 | |
| 	apply_mask_ipv4(&p);
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("ospf_intra_add_stub(): processing route to %s/%d",
 | |
| 			   inet_ntoa(p.prefix), p.prefixlen);
 | |
| 
 | |
| 	/* (1) Calculate the distance D of stub network from the root.  D is
 | |
| 	   equal to the distance from the root to the router vertex
 | |
| 	   (calculated in stage 1), plus the stub network link's advertised
 | |
| 	   cost. */
 | |
| 	cost = v->distance + ntohs(link->m[0].metric);
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug(
 | |
| 			"ospf_intra_add_stub(): calculated cost is %d + %d = %d",
 | |
| 			v->distance, ntohs(link->m[0].metric), cost);
 | |
| 
 | |
| 	/* PtP links with /32 masks adds host routes to remote, directly
 | |
| 	 * connected hosts, see RFC 2328, 12.4.1.1, Option 1.
 | |
| 	 * Such routes can just be ignored for the sake of tidyness.
 | |
| 	 */
 | |
| 	if (parent_is_root && link->link_data.s_addr == 0xffffffff
 | |
| 	    && ospf_if_lookup_by_local_addr(area->ospf, NULL, link->link_id)) {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug("%s: ignoring host route %s/32 to self.",
 | |
| 				   __func__, inet_ntoa(link->link_id));
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	rn = route_node_get(rt, (struct prefix *)&p);
 | |
| 
 | |
| 	/* Lookup current routing table. */
 | |
| 	if (rn->info) {
 | |
| 		struct ospf_route *cur_or;
 | |
| 
 | |
| 		route_unlock_node(rn);
 | |
| 
 | |
| 		cur_or = rn->info;
 | |
| 
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug(
 | |
| 				"ospf_intra_add_stub(): "
 | |
| 				"another route to the same prefix found with cost %u",
 | |
| 				cur_or->cost);
 | |
| 
 | |
| 		/* Compare this distance to the current best cost to the stub
 | |
| 		   network.  This is done by looking up the stub network's
 | |
| 		   current routing table entry.  If the calculated distance D is
 | |
| 		   larger, go on to examine the next stub network link in the
 | |
| 		   LSA. */
 | |
| 		if (cost > cur_or->cost) {
 | |
| 			if (IS_DEBUG_OSPF_EVENT)
 | |
| 				zlog_debug(
 | |
| 					"ospf_intra_add_stub(): old route is better, exit");
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		/* (2) If this step is reached, the stub network's routing table
 | |
| 		   entry must be updated.  Calculate the set of next hops that
 | |
| 		   would result from using the stub network link.  This
 | |
| 		   calculation is shown in Section 16.1.1; input to this
 | |
| 		   calculation is the destination (the stub network) and the
 | |
| 		   parent vertex (the router vertex). If the distance D is the
 | |
| 		   same as the current routing table cost, simply add this set
 | |
| 		   of next hops to the routing table entry's list of next hops.
 | |
| 		   In this case, the routing table already has a Link State
 | |
| 		   Origin.  If this Link State Origin is a router-LSA whose Link
 | |
| 		   State ID is smaller than V's Router ID, reset the Link State
 | |
| 		   Origin to V's router-LSA. */
 | |
| 
 | |
| 		if (cost == cur_or->cost) {
 | |
| 			if (IS_DEBUG_OSPF_EVENT)
 | |
| 				zlog_debug(
 | |
| 					"ospf_intra_add_stub(): routes are equal, merge");
 | |
| 
 | |
| 			ospf_route_copy_nexthops_from_vertex(cur_or, v);
 | |
| 
 | |
| 			if (IPV4_ADDR_CMP(&cur_or->u.std.origin->id,
 | |
| 					  &lsa->header.id)
 | |
| 			    < 0)
 | |
| 				cur_or->u.std.origin = (struct lsa_header *)lsa;
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		/* Otherwise D is smaller than the routing table cost.
 | |
| 		   Overwrite the current routing table entry by setting the
 | |
| 		   routing table entry's cost to D, and by setting the entry's
 | |
| 		   list of next hops to the newly calculated set.  Set the
 | |
| 		   routing table entry's Link State Origin to V's router-LSA.
 | |
| 		   Then go on to examine the next stub network link. */
 | |
| 
 | |
| 		if (cost < cur_or->cost) {
 | |
| 			if (IS_DEBUG_OSPF_EVENT)
 | |
| 				zlog_debug(
 | |
| 					"ospf_intra_add_stub(): new route is better, set it");
 | |
| 
 | |
| 			cur_or->cost = cost;
 | |
| 
 | |
| 			list_delete_all_node(cur_or->paths);
 | |
| 
 | |
| 			ospf_route_copy_nexthops_from_vertex(cur_or, v);
 | |
| 
 | |
| 			cur_or->u.std.origin = (struct lsa_header *)lsa;
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("ospf_intra_add_stub(): installing new route");
 | |
| 
 | |
| 	or = ospf_route_new();
 | |
| 
 | |
| 	or->id = v->id;
 | |
| 	or->u.std.area_id = area->area_id;
 | |
| 	or->u.std.external_routing = area->external_routing;
 | |
| 	or->path_type = OSPF_PATH_INTRA_AREA;
 | |
| 	or->cost = cost;
 | |
| 	or->type = OSPF_DESTINATION_NETWORK;
 | |
| 	or->u.std.origin = (struct lsa_header *)lsa;
 | |
| 
 | |
| 	/* Nexthop is depend on connection type. */
 | |
| 	if (v != area->spf) {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug(
 | |
| 				"ospf_intra_add_stub(): this network is on remote router");
 | |
| 		ospf_route_copy_nexthops_from_vertex(or, v);
 | |
| 	} else {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug(
 | |
| 				"ospf_intra_add_stub(): this network is on this router");
 | |
| 
 | |
| 		if ((oi = ospf_if_lookup_by_lsa_pos(area, lsa_pos))) {
 | |
| 			if (IS_DEBUG_OSPF_EVENT)
 | |
| 				zlog_debug(
 | |
| 					"ospf_intra_add_stub(): the interface is %s",
 | |
| 					IF_NAME(oi));
 | |
| 
 | |
| 			path = ospf_path_new();
 | |
| 			path->nexthop.s_addr = 0;
 | |
| 			path->ifindex = oi->ifp->ifindex;
 | |
| 			if (CHECK_FLAG(oi->connected->flags,
 | |
| 				       ZEBRA_IFA_UNNUMBERED))
 | |
| 				path->unnumbered = 1;
 | |
| 			listnode_add(or->paths, path);
 | |
| 		} else {
 | |
| 			if (IS_DEBUG_OSPF_EVENT)
 | |
| 				zlog_debug(
 | |
| 					"ospf_intra_add_stub(): where's the interface ?");
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	rn->info = or ;
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("ospf_intra_add_stub(): Stop");
 | |
| }
 | |
| 
 | |
| static const char *const ospf_path_type_str[] = {
 | |
| 	"unknown-type", "intra-area", "inter-area", "type1-external",
 | |
| 	"type2-external"
 | |
| };
 | |
| 
 | |
| void ospf_route_table_dump(struct route_table *rt)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 	char buf1[BUFSIZ];
 | |
| 	char buf2[BUFSIZ];
 | |
| 	struct listnode *pnode;
 | |
| 	struct ospf_path *path;
 | |
| 
 | |
| #if 0
 | |
|   zlog_debug ("Type   Dest   Area   Path	 Type	 Cost	Next	 Adv.");
 | |
|   zlog_debug ("					Hop(s)	 Router(s)");
 | |
| #endif /* 0 */
 | |
| 
 | |
| 	zlog_debug("========== OSPF routing table ==========");
 | |
| 	for (rn = route_top(rt); rn; rn = route_next(rn))
 | |
| 		if ((or = rn->info) != NULL) {
 | |
| 			if (or->type == OSPF_DESTINATION_NETWORK) {
 | |
| 				zlog_debug("N %s/%d\t%s\t%s\t%d",
 | |
| 					   inet_ntop(AF_INET, &rn->p.u.prefix4,
 | |
| 						     buf1, BUFSIZ),
 | |
| 					   rn->p.prefixlen,
 | |
| 					   inet_ntop(AF_INET,
 | |
| 						     & or->u.std.area_id, buf2,
 | |
| 						     BUFSIZ),
 | |
| 					   ospf_path_type_str[or->path_type],
 | |
| 					   or->cost);
 | |
| 				for (ALL_LIST_ELEMENTS_RO(or->paths, pnode,
 | |
| 							  path))
 | |
| 					zlog_debug("  -> %s",
 | |
| 						   inet_ntoa(path->nexthop));
 | |
| 			} else
 | |
| 				zlog_debug("R %s\t%s\t%s\t%d",
 | |
| 					   inet_ntop(AF_INET, &rn->p.u.prefix4,
 | |
| 						     buf1, BUFSIZ),
 | |
| 					   inet_ntop(AF_INET,
 | |
| 						     & or->u.std.area_id, buf2,
 | |
| 						     BUFSIZ),
 | |
| 					   ospf_path_type_str[or->path_type],
 | |
| 					   or->cost);
 | |
| 		}
 | |
| 	zlog_debug("========================================");
 | |
| }
 | |
| 
 | |
| /* This is 16.4.1 implementation.
 | |
|    o Intra-area paths using non-backbone areas are always the most preferred.
 | |
|    o The other paths, intra-area backbone paths and inter-area paths,
 | |
|      are of equal preference. */
 | |
| static int ospf_asbr_route_cmp(struct ospf *ospf, struct ospf_route *r1,
 | |
| 			       struct ospf_route *r2)
 | |
| {
 | |
| 	uint8_t r1_type, r2_type;
 | |
| 
 | |
| 	r1_type = r1->path_type;
 | |
| 	r2_type = r2->path_type;
 | |
| 
 | |
| 	/* r1/r2 itself is backbone, and it's Inter-area path. */
 | |
| 	if (OSPF_IS_AREA_ID_BACKBONE(r1->u.std.area_id))
 | |
| 		r1_type = OSPF_PATH_INTER_AREA;
 | |
| 	if (OSPF_IS_AREA_ID_BACKBONE(r2->u.std.area_id))
 | |
| 		r2_type = OSPF_PATH_INTER_AREA;
 | |
| 
 | |
| 	return (r1_type - r2_type);
 | |
| }
 | |
| 
 | |
| /* Compare two routes.
 | |
|  ret <  0 -- r1 is better.
 | |
|  ret == 0 -- r1 and r2 are the same.
 | |
|  ret >  0 -- r2 is better. */
 | |
| int ospf_route_cmp(struct ospf *ospf, struct ospf_route *r1,
 | |
| 		   struct ospf_route *r2)
 | |
| {
 | |
| 	int ret = 0;
 | |
| 
 | |
| 	/* Path types of r1 and r2 are not the same. */
 | |
| 	if ((ret = (r1->path_type - r2->path_type)))
 | |
| 		return ret;
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("Route[Compare]: Path types are the same.");
 | |
| 	/* Path types are the same, compare any cost. */
 | |
| 	switch (r1->path_type) {
 | |
| 	case OSPF_PATH_INTRA_AREA:
 | |
| 	case OSPF_PATH_INTER_AREA:
 | |
| 		break;
 | |
| 	case OSPF_PATH_TYPE1_EXTERNAL:
 | |
| 		if (!CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) {
 | |
| 			ret = ospf_asbr_route_cmp(ospf, r1->u.ext.asbr,
 | |
| 						  r2->u.ext.asbr);
 | |
| 			if (ret != 0)
 | |
| 				return ret;
 | |
| 		}
 | |
| 		break;
 | |
| 	case OSPF_PATH_TYPE2_EXTERNAL:
 | |
| 		if ((ret = (r1->u.ext.type2_cost - r2->u.ext.type2_cost)))
 | |
| 			return ret;
 | |
| 
 | |
| 		if (!CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE)) {
 | |
| 			ret = ospf_asbr_route_cmp(ospf, r1->u.ext.asbr,
 | |
| 						  r2->u.ext.asbr);
 | |
| 			if (ret != 0)
 | |
| 				return ret;
 | |
| 		}
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	/* Anyway, compare the costs. */
 | |
| 	return (r1->cost - r2->cost);
 | |
| }
 | |
| 
 | |
| static int ospf_path_exist(struct list *plist, struct in_addr nexthop,
 | |
| 			   struct ospf_interface *oi)
 | |
| {
 | |
| 	struct listnode *node, *nnode;
 | |
| 	struct ospf_path *path;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS(plist, node, nnode, path))
 | |
| 		if (IPV4_ADDR_SAME(&path->nexthop, &nexthop)
 | |
| 		    && path->ifindex == oi->ifp->ifindex)
 | |
| 			return 1;
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| void ospf_route_copy_nexthops_from_vertex(struct ospf_route *to,
 | |
| 					  struct vertex *v)
 | |
| {
 | |
| 	struct listnode *node;
 | |
| 	struct ospf_path *path;
 | |
| 	struct vertex_nexthop *nexthop;
 | |
| 	struct vertex_parent *vp;
 | |
| 
 | |
| 	assert(to->paths);
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(v->parents, node, vp)) {
 | |
| 		nexthop = vp->nexthop;
 | |
| 
 | |
| 		if (nexthop->oi != NULL) {
 | |
| 			if (!ospf_path_exist(to->paths, nexthop->router,
 | |
| 					     nexthop->oi)) {
 | |
| 				path = ospf_path_new();
 | |
| 				path->nexthop = nexthop->router;
 | |
| 				path->ifindex = nexthop->oi->ifp->ifindex;
 | |
| 				if (CHECK_FLAG(nexthop->oi->connected->flags,
 | |
| 					       ZEBRA_IFA_UNNUMBERED))
 | |
| 					path->unnumbered = 1;
 | |
| 				listnode_add(to->paths, path);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| struct ospf_path *ospf_path_lookup(struct list *plist, struct ospf_path *path)
 | |
| {
 | |
| 	struct listnode *node;
 | |
| 	struct ospf_path *op;
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS_RO(plist, node, op)) {
 | |
| 		if (!IPV4_ADDR_SAME(&op->nexthop, &path->nexthop))
 | |
| 			continue;
 | |
| 		if (!IPV4_ADDR_SAME(&op->adv_router, &path->adv_router))
 | |
| 			continue;
 | |
| 		if (op->ifindex != path->ifindex)
 | |
| 			continue;
 | |
| 		return op;
 | |
| 	}
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| void ospf_route_copy_nexthops(struct ospf_route *to, struct list *from)
 | |
| {
 | |
| 	struct listnode *node, *nnode;
 | |
| 	struct ospf_path *path;
 | |
| 
 | |
| 	assert(to->paths);
 | |
| 
 | |
| 	for (ALL_LIST_ELEMENTS(from, node, nnode, path))
 | |
| 		/* The same routes are just discarded. */
 | |
| 		if (!ospf_path_lookup(to->paths, path))
 | |
| 			listnode_add(to->paths, ospf_path_dup(path));
 | |
| }
 | |
| 
 | |
| void ospf_route_subst_nexthops(struct ospf_route *to, struct list *from)
 | |
| {
 | |
| 
 | |
| 	list_delete_all_node(to->paths);
 | |
| 	ospf_route_copy_nexthops(to, from);
 | |
| }
 | |
| 
 | |
| void ospf_route_subst(struct route_node *rn, struct ospf_route *new_or,
 | |
| 		      struct ospf_route *over)
 | |
| {
 | |
| 	route_lock_node(rn);
 | |
| 	ospf_route_free(rn->info);
 | |
| 
 | |
| 	ospf_route_copy_nexthops(new_or, over->paths);
 | |
| 	rn->info = new_or;
 | |
| 	route_unlock_node(rn);
 | |
| }
 | |
| 
 | |
| void ospf_route_add(struct route_table *rt, struct prefix_ipv4 *p,
 | |
| 		    struct ospf_route *new_or, struct ospf_route *over)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 
 | |
| 	rn = route_node_get(rt, (struct prefix *)p);
 | |
| 
 | |
| 	ospf_route_copy_nexthops(new_or, over->paths);
 | |
| 
 | |
| 	if (rn->info) {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug("ospf_route_add(): something's wrong !");
 | |
| 		route_unlock_node(rn);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	rn->info = new_or;
 | |
| }
 | |
| 
 | |
| void ospf_prune_unreachable_networks(struct route_table *rt)
 | |
| {
 | |
| 	struct route_node *rn, *next;
 | |
| 	struct ospf_route * or ;
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("Pruning unreachable networks");
 | |
| 
 | |
| 	for (rn = route_top(rt); rn; rn = next) {
 | |
| 		next = route_next(rn);
 | |
| 		if (rn->info != NULL) {
 | |
| 			or = rn->info;
 | |
| 			if (listcount(or->paths) == 0) {
 | |
| 				if (IS_DEBUG_OSPF_EVENT)
 | |
| 					zlog_debug("Pruning route to %s/%d",
 | |
| 						   inet_ntoa(rn->p.u.prefix4),
 | |
| 						   rn->p.prefixlen);
 | |
| 
 | |
| 				ospf_route_free(or);
 | |
| 				rn->info = NULL;
 | |
| 				route_unlock_node(rn);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ospf_prune_unreachable_routers(struct route_table *rtrs)
 | |
| {
 | |
| 	struct route_node *rn, *next;
 | |
| 	struct ospf_route * or ;
 | |
| 	struct listnode *node, *nnode;
 | |
| 	struct list *paths;
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug("Pruning unreachable routers");
 | |
| 
 | |
| 	for (rn = route_top(rtrs); rn; rn = next) {
 | |
| 		next = route_next(rn);
 | |
| 		if ((paths = rn->info) == NULL)
 | |
| 			continue;
 | |
| 
 | |
| 		for (ALL_LIST_ELEMENTS(paths, node, nnode, or)) {
 | |
| 			if (listcount(or->paths) == 0) {
 | |
| 				if (IS_DEBUG_OSPF_EVENT) {
 | |
| 					zlog_debug("Pruning route to rtr %s",
 | |
| 						   inet_ntoa(rn->p.u.prefix4));
 | |
| 					zlog_debug(
 | |
| 						"               via area %s",
 | |
| 						inet_ntoa(or->u.std.area_id));
 | |
| 				}
 | |
| 
 | |
| 				listnode_delete(paths, or);
 | |
| 				ospf_route_free(or);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		if (listcount(paths) == 0) {
 | |
| 			if (IS_DEBUG_OSPF_EVENT)
 | |
| 				zlog_debug("Pruning router node %s",
 | |
| 					   inet_ntoa(rn->p.u.prefix4));
 | |
| 
 | |
| 			list_delete(&paths);
 | |
| 			rn->info = NULL;
 | |
| 			route_unlock_node(rn);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| int ospf_add_discard_route(struct ospf *ospf, struct route_table *rt,
 | |
| 			   struct ospf_area *area, struct prefix_ipv4 *p)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or, *new_or;
 | |
| 
 | |
| 	rn = route_node_get(rt, (struct prefix *)p);
 | |
| 
 | |
| 	if (rn == NULL) {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug(
 | |
| 				"ospf_add_discard_route(): router installation error");
 | |
| 		return 0;
 | |
| 	}
 | |
| 
 | |
| 	if (rn->info) /* If the route to the same destination is found */
 | |
| 	{
 | |
| 		route_unlock_node(rn);
 | |
| 
 | |
| 		or = rn->info;
 | |
| 
 | |
| 		if (or->path_type == OSPF_PATH_INTRA_AREA) {
 | |
| 			if (IS_DEBUG_OSPF_EVENT)
 | |
| 				zlog_debug(
 | |
| 					"ospf_add_discard_route(): "
 | |
| 					"an intra-area route exists");
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 		if (or->type == OSPF_DESTINATION_DISCARD) {
 | |
| 			if (IS_DEBUG_OSPF_EVENT)
 | |
| 				zlog_debug(
 | |
| 					"ospf_add_discard_route(): "
 | |
| 					"discard entry already installed");
 | |
| 			return 0;
 | |
| 		}
 | |
| 
 | |
| 		ospf_route_free(rn->info);
 | |
| 	}
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug(
 | |
| 			"ospf_add_discard_route(): "
 | |
| 			"adding %s/%d",
 | |
| 			inet_ntoa(p->prefix), p->prefixlen);
 | |
| 
 | |
| 	new_or = ospf_route_new();
 | |
| 	new_or->type = OSPF_DESTINATION_DISCARD;
 | |
| 	new_or->id.s_addr = 0;
 | |
| 	new_or->cost = 0;
 | |
| 	new_or->u.std.area_id = area->area_id;
 | |
| 	new_or->u.std.external_routing = area->external_routing;
 | |
| 	new_or->path_type = OSPF_PATH_INTER_AREA;
 | |
| 	rn->info = new_or;
 | |
| 
 | |
| 	ospf_zebra_add_discard(ospf, p);
 | |
| 
 | |
| 	return 1;
 | |
| }
 | |
| 
 | |
| void ospf_delete_discard_route(struct ospf *ospf, struct route_table *rt,
 | |
| 			       struct prefix_ipv4 *p)
 | |
| {
 | |
| 	struct route_node *rn;
 | |
| 	struct ospf_route * or ;
 | |
| 
 | |
| 	if (IS_DEBUG_OSPF_EVENT)
 | |
| 		zlog_debug(
 | |
| 			"ospf_delete_discard_route(): "
 | |
| 			"deleting %s/%d",
 | |
| 			inet_ntoa(p->prefix), p->prefixlen);
 | |
| 
 | |
| 	rn = route_node_lookup(rt, (struct prefix *)p);
 | |
| 
 | |
| 	if (rn == NULL) {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug(
 | |
| 				"ospf_delete_discard_route(): no route found");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	or = rn->info;
 | |
| 
 | |
| 	if (or->path_type == OSPF_PATH_INTRA_AREA) {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug(
 | |
| 				"ospf_delete_discard_route(): "
 | |
| 				"an intra-area route exists");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (or->type != OSPF_DESTINATION_DISCARD) {
 | |
| 		if (IS_DEBUG_OSPF_EVENT)
 | |
| 			zlog_debug(
 | |
| 				"ospf_delete_discard_route(): "
 | |
| 				"not a discard entry");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	/* free the route entry and the route node */
 | |
| 	ospf_route_free(rn->info);
 | |
| 
 | |
| 	rn->info = NULL;
 | |
| 	route_unlock_node(rn);
 | |
| 	route_unlock_node(rn);
 | |
| 
 | |
| 	/* remove the discard entry from the rib */
 | |
| 	ospf_zebra_delete_discard(ospf, p);
 | |
| 
 | |
| 	return;
 | |
| }
 |