/src/frr/ospfd/ospf_ase.c
Line  | Count  | Source  | 
1  |  | // SPDX-License-Identifier: GPL-2.0-or-later  | 
2  |  | /*  | 
3  |  |  * OSPF AS external route calculation.  | 
4  |  |  * Copyright (C) 1999, 2000 Alex Zinin, Toshiaki Takada  | 
5  |  |  */  | 
6  |  |  | 
7  |  | #include <zebra.h>  | 
8  |  |  | 
9  |  | #include "frrevent.h"  | 
10  |  | #include "memory.h"  | 
11  |  | #include "hash.h"  | 
12  |  | #include "linklist.h"  | 
13  |  | #include "prefix.h"  | 
14  |  | #include "if.h"  | 
15  |  | #include "table.h"  | 
16  |  | #include "vty.h"  | 
17  |  | #include "log.h"  | 
18  |  |  | 
19  |  | #include "ospfd/ospfd.h"  | 
20  |  | #include "ospfd/ospf_interface.h"  | 
21  |  | #include "ospfd/ospf_ism.h"  | 
22  |  | #include "ospfd/ospf_asbr.h"  | 
23  |  | #include "ospfd/ospf_lsa.h"  | 
24  |  | #include "ospfd/ospf_lsdb.h"  | 
25  |  | #include "ospfd/ospf_neighbor.h"  | 
26  |  | #include "ospfd/ospf_nsm.h"  | 
27  |  | #include "ospfd/ospf_spf.h"  | 
28  |  | #include "ospfd/ospf_route.h"  | 
29  |  | #include "ospfd/ospf_ase.h"  | 
30  |  | #include "ospfd/ospf_zebra.h"  | 
31  |  | #include "ospfd/ospf_dump.h"  | 
32  |  |  | 
33  |  | struct ospf_route *ospf_find_asbr_route(struct ospf *ospf,  | 
34  |  |           struct route_table *rtrs,  | 
35  |  |           struct prefix_ipv4 *asbr)  | 
36  | 0  | { | 
37  | 0  |   struct route_node *rn;  | 
38  | 0  |   struct ospf_route * or, *best = NULL;  | 
39  | 0  |   struct listnode *node;  | 
40  | 0  |   struct list *chosen;  | 
41  |  |  | 
42  |  |   /* Sanity check. */  | 
43  | 0  |   if (rtrs == NULL)  | 
44  | 0  |     return NULL;  | 
45  |  |  | 
46  | 0  |   rn = route_node_lookup(rtrs, (struct prefix *)asbr);  | 
47  | 0  |   if (!rn)  | 
48  | 0  |     return NULL;  | 
49  |  |  | 
50  | 0  |   route_unlock_node(rn);  | 
51  |  | 
  | 
52  | 0  |   chosen = list_new();  | 
53  |  |  | 
54  |  |   /* First try to find intra-area non-bb paths. */  | 
55  | 0  |   if (!CHECK_FLAG(ospf->config, OSPF_RFC1583_COMPATIBLE))  | 
56  | 0  |     for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or))  | 
57  | 0  |       if (or->cost < OSPF_LS_INFINITY)  | 
58  | 0  |         if (!OSPF_IS_AREA_ID_BACKBONE(or->u.std.area_id)  | 
59  | 0  |               &&  | 
60  | 0  |             or->path_type == OSPF_PATH_INTRA_AREA)  | 
61  | 0  |           listnode_add(chosen, or);  | 
62  |  |  | 
63  |  |   /* If none is found -- look through all. */  | 
64  | 0  |   if (listcount(chosen) == 0) { | 
65  | 0  |     list_delete(&chosen);  | 
66  | 0  |     chosen = rn->info;  | 
67  | 0  |   }  | 
68  |  |  | 
69  |  |   /* Now find the route with least cost. */  | 
70  | 0  |   for (ALL_LIST_ELEMENTS_RO(chosen, node, or))  | 
71  | 0  |     if (or->cost < OSPF_LS_INFINITY) { | 
72  | 0  |       if (best == NULL)  | 
73  | 0  |         best = or ;  | 
74  | 0  |       else if (best->cost > or->cost)  | 
75  | 0  |         best = or ;  | 
76  | 0  |       else if (best->cost ==  | 
77  | 0  |          or->cost  | 
78  | 0  |              && IPV4_ADDR_CMP(  | 
79  | 0  |                   &best->u.std.area_id,  | 
80  | 0  |                   & or->u.std.area_id)  | 
81  | 0  |                   < 0)  | 
82  | 0  |         best = or ;  | 
83  | 0  |     }  | 
84  |  | 
  | 
85  | 0  |   if (chosen != rn->info)  | 
86  | 0  |     list_delete(&chosen);  | 
87  |  | 
  | 
88  | 0  |   return best;  | 
89  | 0  | }  | 
90  |  |  | 
91  |  | struct ospf_route *ospf_find_asbr_route_through_area(struct route_table *rtrs,  | 
92  |  |                  struct prefix_ipv4 *asbr,  | 
93  |  |                  struct ospf_area *area)  | 
94  | 0  | { | 
95  | 0  |   struct route_node *rn;  | 
96  |  |  | 
97  |  |   /* Sanity check. */  | 
98  | 0  |   if (rtrs == NULL)  | 
99  | 0  |     return NULL;  | 
100  |  |  | 
101  | 0  |   rn = route_node_lookup(rtrs, (struct prefix *)asbr);  | 
102  |  | 
  | 
103  | 0  |   if (rn) { | 
104  | 0  |     struct listnode *node;  | 
105  | 0  |     struct ospf_route * or ;  | 
106  |  | 
  | 
107  | 0  |     route_unlock_node(rn);  | 
108  |  | 
  | 
109  | 0  |     for (ALL_LIST_ELEMENTS_RO((struct list *)rn->info, node, or))  | 
110  | 0  |       if (IPV4_ADDR_SAME(& or->u.std.area_id, &area->area_id))  | 
111  | 0  |         return or ;  | 
112  | 0  |   }  | 
113  |  |  | 
114  | 0  |   return NULL;  | 
115  | 0  | }  | 
116  |  |  | 
117  |  | static void ospf_ase_complete_direct_routes(struct ospf_route *ro,  | 
118  |  |               struct in_addr nexthop)  | 
119  | 0  | { | 
120  | 0  |   struct listnode *node;  | 
121  | 0  |   struct ospf_path *op;  | 
122  |  | 
  | 
123  | 0  |   for (ALL_LIST_ELEMENTS_RO(ro->paths, node, op))  | 
124  | 0  |     if (op->nexthop.s_addr == INADDR_ANY)  | 
125  | 0  |       op->nexthop.s_addr = nexthop.s_addr;  | 
126  | 0  | }  | 
127  |  |  | 
128  |  | static int ospf_ase_forward_address_check(struct ospf *ospf,  | 
129  |  |             struct in_addr fwd_addr)  | 
130  | 0  | { | 
131  | 0  |   struct listnode *ifn;  | 
132  | 0  |   struct ospf_interface *oi;  | 
133  |  | 
  | 
134  | 0  |   for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, ifn, oi))  | 
135  | 0  |     if (if_is_operative(oi->ifp))  | 
136  | 0  |       if (oi->type != OSPF_IFTYPE_VIRTUALLINK)  | 
137  | 0  |         if (IPV4_ADDR_SAME(&oi->address->u.prefix4,  | 
138  | 0  |                &fwd_addr))  | 
139  | 0  |           return 0;  | 
140  |  |  | 
141  | 0  |   return 1;  | 
142  | 0  | }  | 
143  |  |  | 
144  |  | static struct ospf_route *  | 
145  |  | ospf_ase_calculate_new_route(struct ospf_lsa *lsa,  | 
146  |  |            struct ospf_route *asbr_route, uint32_t metric)  | 
147  | 0  | { | 
148  | 0  |   struct as_external_lsa *al;  | 
149  | 0  |   struct ospf_route *new;  | 
150  |  | 
  | 
151  | 0  |   al = (struct as_external_lsa *)lsa->data;  | 
152  |  | 
  | 
153  | 0  |   new = ospf_route_new();  | 
154  |  |  | 
155  |  |   /* Set redistributed type -- does make sense? */  | 
156  |  |   /* new->type = type; */  | 
157  | 0  |   new->id = al->header.id;  | 
158  | 0  |   new->mask = al->mask;  | 
159  |  | 
  | 
160  | 0  |   if (!IS_EXTERNAL_METRIC(al->e[0].tos)) { | 
161  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
162  | 0  |       zlog_debug(  | 
163  | 0  |         "Route[External]: type-1 created, asbr cost:%d  metric:%d.",  | 
164  | 0  |         asbr_route->cost, metric);  | 
165  | 0  |     new->path_type = OSPF_PATH_TYPE1_EXTERNAL;  | 
166  | 0  |     new->cost = asbr_route->cost + metric; /* X + Y */  | 
167  | 0  |   } else { | 
168  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
169  | 0  |       zlog_debug("Route[External]: type-2 created."); | 
170  | 0  |     new->path_type = OSPF_PATH_TYPE2_EXTERNAL;  | 
171  | 0  |     new->cost = asbr_route->cost;   /* X */  | 
172  | 0  |     new->u.ext.type2_cost = metric; /* Y */  | 
173  | 0  |   }  | 
174  |  | 
  | 
175  | 0  |   new->type = OSPF_DESTINATION_NETWORK;  | 
176  | 0  |   new->u.ext.origin = lsa;  | 
177  | 0  |   new->u.ext.tag = ntohl(al->e[0].route_tag);  | 
178  | 0  |   new->u.ext.asbr = asbr_route;  | 
179  |  | 
  | 
180  | 0  |   assert(new != asbr_route);  | 
181  |  | 
  | 
182  | 0  |   return new;  | 
183  | 0  | }  | 
184  |  |  | 
185  |  | #define OSPF_ASE_CALC_INTERVAL 1  | 
186  |  |  | 
187  |  | int ospf_ase_calculate_route(struct ospf *ospf, struct ospf_lsa *lsa)  | 
188  | 0  | { | 
189  | 0  |   uint32_t metric;  | 
190  | 0  |   struct as_external_lsa *al;  | 
191  | 0  |   struct ospf_route *asbr_route;  | 
192  | 0  |   struct prefix_ipv4 asbr, p;  | 
193  | 0  |   struct route_node *rn;  | 
194  | 0  |   struct ospf_route *new, * or ;  | 
195  | 0  |   int ret;  | 
196  |  | 
  | 
197  | 0  |   assert(lsa);  | 
198  | 0  |   al = (struct as_external_lsa *)lsa->data;  | 
199  |  | 
  | 
200  | 0  |   if (lsa->data->type == OSPF_AS_NSSA_LSA)  | 
201  | 0  |     if (IS_DEBUG_OSPF_NSSA)  | 
202  | 0  |       zlog_debug("%s: Processing Type-7", __func__); | 
203  |  |  | 
204  |  |   /* Stay away from any Local Translated Type-7 LSAs */  | 
205  | 0  |   if (CHECK_FLAG(lsa->flags, OSPF_LSA_LOCAL_XLT)) { | 
206  | 0  |     if (IS_DEBUG_OSPF_NSSA)  | 
207  | 0  |       zlog_debug("%s: Rejecting Local Xlt'd", __func__); | 
208  | 0  |     return 0;  | 
209  | 0  |   }  | 
210  |  |  | 
211  | 0  |   if (IS_DEBUG_OSPF(lsa, LSA)) { | 
212  | 0  |     zlog_debug(  | 
213  | 0  |       "Route[External]: Calculate AS-external-LSA to %pI4/%d adv_router %pI4",  | 
214  | 0  |       &al->header.id, ip_masklen(al->mask),  | 
215  | 0  |       &al->header.adv_router);  | 
216  | 0  |   }  | 
217  |  |  | 
218  |  |   /* (1) If the cost specified by the LSA is LSInfinity, or if the  | 
219  |  |          LSA's LS age is equal to MaxAge, then examine the next LSA. */  | 
220  | 0  |   if ((metric = GET_METRIC(al->e[0].metric)) >= OSPF_LS_INFINITY) { | 
221  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
222  | 0  |       zlog_debug(  | 
223  | 0  |         "Route[External]: Metric is OSPF_LS_INFINITY");  | 
224  | 0  |     return 0;  | 
225  | 0  |   }  | 
226  | 0  |   if (IS_LSA_MAXAGE(lsa)) { | 
227  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
228  | 0  |       zlog_debug(  | 
229  | 0  |         "Route[External]: AS-external-LSA is MAXAGE");  | 
230  | 0  |     return 0;  | 
231  | 0  |   }  | 
232  |  |  | 
233  |  |   /* (2) If the LSA was originated by the calculating router itself,  | 
234  |  |      examine the next LSA. */  | 
235  | 0  |   if (IS_LSA_SELF(lsa)) { | 
236  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
237  | 0  |       zlog_debug(  | 
238  | 0  |         "Route[External]: AS-external-LSA is self originated");  | 
239  | 0  |     return 0;  | 
240  | 0  |   }  | 
241  |  |  | 
242  |  |   /* (3) Call the destination described by the LSA N.  N's address is  | 
243  |  |          obtained by masking the LSA's Link State ID with the  | 
244  |  |          network/subnet mask contained in the body of the LSA.  Look  | 
245  |  |          up the routing table entries (potentially one per attached  | 
246  |  |          area) for the AS boundary router (ASBR) that originated the  | 
247  |  |          LSA. If no entries exist for router ASBR (i.e., ASBR is  | 
248  |  |          unreachable), do nothing with this LSA and consider the next  | 
249  |  |          in the list. */  | 
250  |  |  | 
251  | 0  |   asbr.family = AF_INET;  | 
252  | 0  |   asbr.prefix = al->header.adv_router;  | 
253  | 0  |   asbr.prefixlen = IPV4_MAX_BITLEN;  | 
254  | 0  |   apply_mask_ipv4(&asbr);  | 
255  |  | 
  | 
256  | 0  |   asbr_route = ospf_find_asbr_route(ospf, ospf->new_rtrs, &asbr);  | 
257  | 0  |   if (asbr_route == NULL) { | 
258  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
259  | 0  |       zlog_debug(  | 
260  | 0  |         "Route[External]: Can't find originating ASBR route");  | 
261  | 0  |     return 0;  | 
262  | 0  |   }  | 
263  | 0  |   if (!(asbr_route->u.std.flags & ROUTER_LSA_EXTERNAL)) { | 
264  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
265  | 0  |       zlog_debug(  | 
266  | 0  |         "Route[External]: Originating router is not an ASBR");  | 
267  | 0  |     return 0;  | 
268  | 0  |   }  | 
269  |  |  | 
270  |  |   /* Type-5 shouldn't be calculated if it is originated from NSSA ASBR.  | 
271  |  |    * As per RFC 3101, expectation is to receive type-7 lsas from  | 
272  |  |    * NSSA ASBR. Ignore calculation, if the current LSA is type-5 and  | 
273  |  |    * originated ASBR's area is NSSA.  | 
274  |  |    */  | 
275  | 0  |   if ((lsa->data->type == OSPF_AS_EXTERNAL_LSA)  | 
276  | 0  |       && (asbr_route->u.std.external_routing != OSPF_AREA_DEFAULT)) { | 
277  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
278  | 0  |       zlog_debug(  | 
279  | 0  |         "Route[External]: Ignore, If type-5 LSA from NSSA area.");  | 
280  | 0  |     return 0;  | 
281  | 0  |   }  | 
282  |  |  | 
283  |  |   /*     Else, this LSA describes an AS external path to destination  | 
284  |  |          N.  Examine the forwarding address specified in the AS-  | 
285  |  |          external-LSA.  This indicates the IP address to which  | 
286  |  |          packets for the destination should be forwarded. */  | 
287  |  |  | 
288  | 0  |   if (al->e[0].fwd_addr.s_addr == INADDR_ANY) { | 
289  |  |     /* If the forwarding address is set to 0.0.0.0, packets should  | 
290  |  |        be sent to the ASBR itself. Among the multiple routing table  | 
291  |  |        entries for the ASBR, select the preferred entry as follows.  | 
292  |  |        If RFC1583Compatibility is set to "disabled", prune the set  | 
293  |  |        of routing table entries for the ASBR as described in  | 
294  |  |        Section 16.4.1. In any case, among the remaining routing  | 
295  |  |        table entries, select the routing table entry with the least  | 
296  |  |        cost; when there are multiple least cost routing table  | 
297  |  |        entries the entry whose associated area has the largest OSPF  | 
298  |  |        Area ID (when considered as an unsigned 32-bit integer) is  | 
299  |  |        chosen. */  | 
300  |  |  | 
301  |  |     /* asbr_route already contains the requested route */  | 
302  | 0  |   } else { | 
303  |  |     /* If the forwarding address is non-zero, look up the  | 
304  |  |        forwarding address in the routing table.[24] The matching  | 
305  |  |        routing table entry must specify an intra-area or inter-area  | 
306  |  |        path; if no such path exists, do nothing with the LSA and  | 
307  |  |        consider the next in the list. */  | 
308  | 0  |     if (!ospf_ase_forward_address_check(ospf, al->e[0].fwd_addr)) { | 
309  | 0  |       if (IS_DEBUG_OSPF(lsa, LSA))  | 
310  | 0  |         zlog_debug(  | 
311  | 0  |           "Route[External]: Forwarding address is our router address");  | 
312  | 0  |       return 0;  | 
313  | 0  |     }  | 
314  |  |  | 
315  | 0  |     asbr.family = AF_INET;  | 
316  | 0  |     asbr.prefix = al->e[0].fwd_addr;  | 
317  | 0  |     asbr.prefixlen = IPV4_MAX_BITLEN;  | 
318  |  | 
  | 
319  | 0  |     rn = route_node_match(ospf->new_table, (struct prefix *)&asbr);  | 
320  |  | 
  | 
321  | 0  |     if (rn == NULL || (asbr_route = rn->info) == NULL) { | 
322  | 0  |       if (IS_DEBUG_OSPF(lsa, LSA))  | 
323  | 0  |         zlog_debug(  | 
324  | 0  |           "Route[External]: Can't find route to forwarding address");  | 
325  | 0  |       if (rn)  | 
326  | 0  |         route_unlock_node(rn);  | 
327  | 0  |       return 0;  | 
328  | 0  |     }  | 
329  |  |  | 
330  | 0  |     route_unlock_node(rn);  | 
331  | 0  |   }  | 
332  |  |  | 
333  |  |   /* (4) Let X be the cost specified by the preferred routing table  | 
334  |  |          entry for the ASBR/forwarding address, and Y the cost  | 
335  |  |          specified in the LSA.  X is in terms of the link state  | 
336  |  |          metric, and Y is a type 1 or 2 external metric. */  | 
337  |  |  | 
338  |  |  | 
339  |  |   /* (5) Look up the routing table entry for the destination N.  If  | 
340  |  |          no entry exists for N, install the AS external path to N,  | 
341  |  |          with next hop equal to the list of next hops to the  | 
342  |  |          forwarding address, and advertising router equal to ASBR.  | 
343  |  |          If the external metric type is 1, then the path-type is set  | 
344  |  |          to type 1 external and the cost is equal to X+Y.  If the  | 
345  |  |          external metric type is 2, the path-type is set to type 2  | 
346  |  |          external, the link state component of the route's cost is X,  | 
347  |  |          and the type 2 cost is Y. */  | 
348  | 0  |   new = ospf_ase_calculate_new_route(lsa, asbr_route, metric);  | 
349  |  |  | 
350  |  |   /* (6) Compare the AS external path described by the LSA with the  | 
351  |  |          existing paths in N's routing table entry, as follows. If  | 
352  |  |          the new path is preferred, it replaces the present paths in  | 
353  |  |          N's routing table entry.  If the new path is of equal  | 
354  |  |          preference, it is added to N's routing table entry's list of  | 
355  |  |          paths. */  | 
356  |  |  | 
357  |  |   /* Set prefix. */  | 
358  | 0  |   p.family = AF_INET;  | 
359  | 0  |   p.prefix = al->header.id;  | 
360  | 0  |   p.prefixlen = ip_masklen(al->mask);  | 
361  |  |  | 
362  |  |   /* if there is a Intra/Inter area route to the N  | 
363  |  |      do not install external route */  | 
364  | 0  |   if ((rn = route_node_lookup(ospf->new_table, (struct prefix *)&p))) { | 
365  | 0  |     route_unlock_node(rn);  | 
366  | 0  |     if (rn->info == NULL)  | 
367  | 0  |       zlog_info("Route[External]: rn->info NULL"); | 
368  | 0  |     if (new)  | 
369  | 0  |       ospf_route_free(new);  | 
370  | 0  |     return 0;  | 
371  | 0  |   }  | 
372  |  |   /* Find a route to the same dest */  | 
373  |  |   /* If there is no route, create new one. */  | 
374  | 0  |   if ((rn = route_node_lookup(ospf->new_external_route,  | 
375  | 0  |             (struct prefix *)&p)))  | 
376  | 0  |     route_unlock_node(rn);  | 
377  |  | 
  | 
378  | 0  |   if (!rn || (or = rn->info) == NULL) { | 
379  | 0  |     if (IS_DEBUG_OSPF(lsa, LSA))  | 
380  | 0  |       zlog_debug("Route[External]: Adding a new route %pFX with paths %u", | 
381  | 0  |            &p, listcount(asbr_route->paths));  | 
382  |  | 
  | 
383  | 0  |     ospf_route_add(ospf->new_external_route, &p, new, asbr_route);  | 
384  |  | 
  | 
385  | 0  |     if (al->e[0].fwd_addr.s_addr != INADDR_ANY)  | 
386  | 0  |       ospf_ase_complete_direct_routes(new, al->e[0].fwd_addr);  | 
387  | 0  |     return 0;  | 
388  | 0  |   } else { | 
389  |  |     /* (a) Intra-area and inter-area paths are always preferred  | 
390  |  |            over AS external paths.  | 
391  |  |  | 
392  |  |        (b) Type 1 external paths are always preferred over type 2  | 
393  |  |            external paths. When all paths are type 2 external  | 
394  |  |            paths, the paths with the smallest advertised type 2  | 
395  |  |            metric are always preferred. */  | 
396  | 0  |     ret = ospf_route_cmp(ospf, new, or);  | 
397  |  |  | 
398  |  |     /*     (c) If the new AS external path is still  | 
399  |  |        indistinguishable  | 
400  |  |          from the current paths in the N's routing table  | 
401  |  |        entry,  | 
402  |  |          and RFC1583Compatibility is set to "disabled", select  | 
403  |  |          the preferred paths based on the intra-AS paths to  | 
404  |  |        the  | 
405  |  |          ASBR/forwarding addresses, as specified in Section  | 
406  |  |          16.4.1.  | 
407  |  |  | 
408  |  |            (d) If the new AS external path is still  | 
409  |  |        indistinguishable  | 
410  |  |          from the current paths in the N's routing table  | 
411  |  |        entry,  | 
412  |  |          select the preferred path based on a least cost  | 
413  |  |          comparison.  Type 1 external paths are compared by  | 
414  |  |          looking at the sum of the distance to the forwarding  | 
415  |  |          address and the advertised type 1 metric (X+Y).  Type  | 
416  |  |        2  | 
417  |  |          external paths advertising equal type 2 metrics are  | 
418  |  |          compared by looking at the distance to the forwarding  | 
419  |  |          addresses.  | 
420  |  |     */  | 
421  |  |     /* New route is better */  | 
422  | 0  |     if (ret < 0) { | 
423  | 0  |       if (IS_DEBUG_OSPF(lsa, LSA))  | 
424  | 0  |         zlog_debug(  | 
425  | 0  |           "Route[External]: New route is better");  | 
426  | 0  |       ospf_route_subst(rn, new, asbr_route);  | 
427  | 0  |       if (al->e[0].fwd_addr.s_addr != INADDR_ANY)  | 
428  | 0  |         ospf_ase_complete_direct_routes(  | 
429  | 0  |           new, al->e[0].fwd_addr);  | 
430  | 0  |       or = new;  | 
431  | 0  |       new = NULL;  | 
432  | 0  |     }  | 
433  |  |     /* Old route is better */  | 
434  | 0  |     else if (ret > 0) { | 
435  | 0  |       if (IS_DEBUG_OSPF(lsa, LSA))  | 
436  | 0  |         zlog_debug(  | 
437  | 0  |           "Route[External]: Old route is better");  | 
438  |  |       /* do nothing */  | 
439  | 0  |     }  | 
440  |  |     /* Routes are equal */  | 
441  | 0  |     else { | 
442  | 0  |       if (IS_DEBUG_OSPF(lsa, LSA))  | 
443  | 0  |         zlog_debug("Route[External]: Routes are equal"); | 
444  | 0  |       ospf_route_copy_nexthops(or, asbr_route->paths);  | 
445  | 0  |       if (al->e[0].fwd_addr.s_addr != INADDR_ANY)  | 
446  | 0  |         ospf_ase_complete_direct_routes(  | 
447  | 0  |           or, al->e[0].fwd_addr);  | 
448  | 0  |     }  | 
449  | 0  |   }  | 
450  |  |   /* Make sure setting newly calculated ASBR route.*/  | 
451  | 0  |   or->u.ext.asbr = asbr_route;  | 
452  | 0  |   if (new)  | 
453  | 0  |     ospf_route_free(new);  | 
454  |  | 
  | 
455  | 0  |   lsa->route = or ;  | 
456  | 0  |   return 0;  | 
457  | 0  | }  | 
458  |  |  | 
459  |  | static int ospf_ase_route_match_same(struct route_table *rt,  | 
460  |  |              struct prefix *prefix,  | 
461  |  |              struct ospf_route *newor)  | 
462  | 0  | { | 
463  | 0  |   struct route_node *rn;  | 
464  | 0  |   struct ospf_route *or;  | 
465  | 0  |   struct ospf_path *op;  | 
466  | 0  |   struct ospf_path *newop;  | 
467  | 0  |   struct listnode *n1;  | 
468  | 0  |   struct listnode *n2;  | 
469  |  | 
  | 
470  | 0  |   if (!rt || !prefix)  | 
471  | 0  |     return 0;  | 
472  |  |  | 
473  | 0  |   rn = route_node_lookup(rt, prefix);  | 
474  | 0  |   if (!rn)  | 
475  | 0  |     return 0;  | 
476  |  |  | 
477  | 0  |   route_unlock_node(rn);  | 
478  |  | 
  | 
479  | 0  |   or = rn->info;  | 
480  |  | 
  | 
481  | 0  |   assert(or);  | 
482  |  | 
  | 
483  | 0  |   if (or->path_type != newor->path_type)  | 
484  | 0  |     return 0;  | 
485  |  |  | 
486  | 0  |   switch (or->path_type) { | 
487  | 0  |   case OSPF_PATH_TYPE1_EXTERNAL:  | 
488  | 0  |     if (or->cost != newor->cost)  | 
489  | 0  |       return 0;  | 
490  | 0  |     break;  | 
491  | 0  |   case OSPF_PATH_TYPE2_EXTERNAL:  | 
492  | 0  |     if ((or->cost != newor->cost)  | 
493  | 0  |         || (or->u.ext.type2_cost != newor->u.ext.type2_cost))  | 
494  | 0  |       return 0;  | 
495  | 0  |     break;  | 
496  | 0  |   default:  | 
497  | 0  |     assert(0);  | 
498  | 0  |     return 0;  | 
499  | 0  |   }  | 
500  |  |  | 
501  | 0  |   assert(or->paths);  | 
502  |  | 
  | 
503  | 0  |   if (or->paths->count != newor->paths->count)  | 
504  | 0  |     return 0;  | 
505  |  |  | 
506  |  |   /* Check each path. */  | 
507  | 0  |   for (n1 = listhead(or->paths), n2 = listhead(newor->paths); n1 && n2;  | 
508  | 0  |        n1 = listnextnode_unchecked(n1), n2 = listnextnode_unchecked(n2)) { | 
509  | 0  |     op = listgetdata(n1);  | 
510  | 0  |     newop = listgetdata(n2);  | 
511  |  | 
  | 
512  | 0  |     if (!IPV4_ADDR_SAME(&op->nexthop, &newop->nexthop))  | 
513  | 0  |       return 0;  | 
514  | 0  |     if (op->ifindex != newop->ifindex)  | 
515  | 0  |       return 0;  | 
516  | 0  |   }  | 
517  |  |  | 
518  | 0  |   if (or->u.ext.tag != newor->u.ext.tag)  | 
519  | 0  |     return 0;  | 
520  |  |  | 
521  | 0  |   return 1;  | 
522  | 0  | }  | 
523  |  |  | 
524  |  | static int ospf_ase_compare_tables(struct ospf *ospf,  | 
525  |  |            struct route_table *new_external_route,  | 
526  |  |            struct route_table *old_external_route)  | 
527  | 0  | { | 
528  | 0  |   struct route_node *rn, *new_rn;  | 
529  | 0  |   struct ospf_route * or ;  | 
530  |  |  | 
531  |  |   /* Remove deleted routes */  | 
532  | 0  |   for (rn = route_top(old_external_route); rn; rn = route_next(rn))  | 
533  | 0  |     if ((or = rn->info)) { | 
534  | 0  |       if (!(new_rn = route_node_lookup(new_external_route,  | 
535  | 0  |                &rn->p)))  | 
536  | 0  |         ospf_zebra_delete(  | 
537  | 0  |           ospf, (struct prefix_ipv4 *)&rn->p, or);  | 
538  | 0  |       else  | 
539  | 0  |         route_unlock_node(new_rn);  | 
540  | 0  |     }  | 
541  |  |  | 
542  |  |  | 
543  |  |   /* Install new routes */  | 
544  | 0  |   for (rn = route_top(new_external_route); rn; rn = route_next(rn))  | 
545  | 0  |     if ((or = rn->info) != NULL)  | 
546  | 0  |       if (!ospf_ase_route_match_same(old_external_route,  | 
547  | 0  |                    &rn->p, or))  | 
548  | 0  |         ospf_zebra_add(  | 
549  | 0  |           ospf, (struct prefix_ipv4 *)&rn->p, or);  | 
550  |  | 
  | 
551  | 0  |   return 0;  | 
552  | 0  | }  | 
553  |  |  | 
554  |  | static void ospf_ase_calculate_timer(struct event *t)  | 
555  | 0  | { | 
556  | 0  |   struct ospf *ospf;  | 
557  | 0  |   struct ospf_lsa *lsa;  | 
558  | 0  |   struct route_node *rn;  | 
559  | 0  |   struct listnode *node;  | 
560  | 0  |   struct ospf_area *area;  | 
561  | 0  |   struct timeval start_time, stop_time;  | 
562  | 0  | 
  | 
563  | 0  |   ospf = EVENT_ARG(t);  | 
564  | 0  |   ospf->t_ase_calc = NULL;  | 
565  | 0  | 
  | 
566  | 0  |   if (ospf->ase_calc) { | 
567  | 0  |     ospf->ase_calc = 0;  | 
568  | 0  | 
  | 
569  | 0  |     monotime(&start_time);  | 
570  | 0  | 
  | 
571  | 0  |     /* Calculate external route for each AS-external-LSA */  | 
572  | 0  |     LSDB_LOOP (EXTERNAL_LSDB(ospf), rn, lsa)  | 
573  | 0  |       ospf_ase_calculate_route(ospf, lsa);  | 
574  | 0  | 
  | 
575  | 0  |     /*  This version simple adds to the table all NSSA areas  */  | 
576  | 0  |     if (ospf->anyNSSA)  | 
577  | 0  |       for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { | 
578  | 0  |         if (IS_DEBUG_OSPF_NSSA)  | 
579  | 0  |           zlog_debug("%s: looking at area %pI4", | 
580  | 0  |                __func__, &area->area_id);  | 
581  | 0  | 
  | 
582  | 0  |         if (area->external_routing == OSPF_AREA_NSSA)  | 
583  | 0  |           LSDB_LOOP (NSSA_LSDB(area), rn, lsa)  | 
584  | 0  |             ospf_ase_calculate_route(ospf,  | 
585  | 0  |                    lsa);  | 
586  | 0  |       }  | 
587  | 0  |     /* kevinm: And add the NSSA routes in ospf_top */  | 
588  | 0  |     LSDB_LOOP (NSSA_LSDB(ospf), rn, lsa)  | 
589  | 0  |       ospf_ase_calculate_route(ospf, lsa);  | 
590  | 0  | 
  | 
591  | 0  |     /* Compare old and new external routing table and install the  | 
592  | 0  |        difference info zebra/kernel */  | 
593  | 0  |     ospf_ase_compare_tables(ospf, ospf->new_external_route,  | 
594  | 0  |           ospf->old_external_route);  | 
595  | 0  | 
  | 
596  | 0  |     /* Delete old external routing table */  | 
597  | 0  |     ospf_route_table_free(ospf->old_external_route);  | 
598  | 0  |     ospf->old_external_route = ospf->new_external_route;  | 
599  | 0  |     ospf->new_external_route = route_table_init();  | 
600  | 0  | 
  | 
601  | 0  |     monotime(&stop_time);  | 
602  | 0  | 
  | 
603  | 0  |     if (IS_DEBUG_OSPF_EVENT)  | 
604  | 0  |       zlog_info(  | 
605  | 0  |         "SPF Processing Time(usecs): External Routes: %lld",  | 
606  | 0  |         (stop_time.tv_sec - start_time.tv_sec)  | 
607  | 0  |             * 1000000LL  | 
608  | 0  |           + (stop_time.tv_usec  | 
609  | 0  |              - start_time.tv_usec));  | 
610  | 0  |   }  | 
611  | 0  | 
  | 
612  | 0  |   /*  | 
613  | 0  |    * Uninstall remnant routes that were installed before the restart, but  | 
614  | 0  |    * that are no longer valid.  | 
615  | 0  |    */  | 
616  | 0  |   if (ospf->gr_info.finishing_restart) { | 
617  | 0  |     ospf_zebra_gr_disable(ospf);  | 
618  | 0  |     ospf_zebra_gr_enable(ospf, ospf->gr_info.grace_period);  | 
619  | 0  |     ospf->gr_info.finishing_restart = false;  | 
620  | 0  |   }  | 
621  | 0  | }  | 
622  |  |  | 
623  |  | void ospf_ase_calculate_schedule(struct ospf *ospf)  | 
624  | 0  | { | 
625  | 0  |   if (ospf == NULL)  | 
626  | 0  |     return;  | 
627  |  |  | 
628  | 0  |   ospf->ase_calc = 1;  | 
629  | 0  | }  | 
630  |  |  | 
631  |  | void ospf_ase_calculate_timer_add(struct ospf *ospf)  | 
632  | 0  | { | 
633  | 0  |   if (ospf == NULL)  | 
634  | 0  |     return;  | 
635  |  |  | 
636  | 0  |   event_add_timer(master, ospf_ase_calculate_timer, ospf,  | 
637  | 0  |       OSPF_ASE_CALC_INTERVAL, &ospf->t_ase_calc);  | 
638  | 0  | }  | 
639  |  |  | 
640  |  | void ospf_ase_register_external_lsa(struct ospf_lsa *lsa, struct ospf *top)  | 
641  | 164  | { | 
642  | 164  |   struct route_node *rn;  | 
643  | 164  |   struct prefix_ipv4 p;  | 
644  | 164  |   struct list *lst;  | 
645  | 164  |   struct as_external_lsa *al;  | 
646  |  |  | 
647  | 164  |   al = (struct as_external_lsa *)lsa->data;  | 
648  | 164  |   p.family = AF_INET;  | 
649  | 164  |   p.prefix = lsa->data->id;  | 
650  | 164  |   p.prefixlen = ip_masklen(al->mask);  | 
651  | 164  |   apply_mask_ipv4(&p);  | 
652  |  |  | 
653  | 164  |   rn = route_node_get(top->external_lsas, (struct prefix *)&p);  | 
654  | 164  |   if ((lst = rn->info) == NULL)  | 
655  | 37  |     rn->info = lst = list_new();  | 
656  | 127  |   else  | 
657  | 127  |     route_unlock_node(rn);  | 
658  |  |  | 
659  |  |   /* We assume that if LSA is deleted from DB  | 
660  |  |      is is also deleted from this RT */  | 
661  | 164  |   listnode_add(lst, ospf_lsa_lock(lsa)); /* external_lsas lst */  | 
662  | 164  | }  | 
663  |  |  | 
664  |  | void ospf_ase_unregister_external_lsa(struct ospf_lsa *lsa, struct ospf *top)  | 
665  | 0  | { | 
666  | 0  |   struct route_node *rn;  | 
667  | 0  |   struct prefix_ipv4 p;  | 
668  | 0  |   struct list *lst;  | 
669  | 0  |   struct as_external_lsa *al;  | 
670  |  | 
  | 
671  | 0  |   al = (struct as_external_lsa *)lsa->data;  | 
672  | 0  |   p.family = AF_INET;  | 
673  | 0  |   p.prefix = lsa->data->id;  | 
674  | 0  |   p.prefixlen = ip_masklen(al->mask);  | 
675  | 0  |   apply_mask_ipv4(&p);  | 
676  |  | 
  | 
677  | 0  |   rn = route_node_lookup(top->external_lsas, (struct prefix *)&p);  | 
678  |  | 
  | 
679  | 0  |   if (rn) { | 
680  | 0  |     lst = rn->info;  | 
681  | 0  |     struct listnode *node = listnode_lookup(lst, lsa);  | 
682  |  |     /* Unlock lsa only if node is present in the list */  | 
683  | 0  |     if (node) { | 
684  | 0  |       listnode_delete(lst, lsa);  | 
685  | 0  |       ospf_lsa_unlock(&lsa); /* external_lsas list */  | 
686  | 0  |     }  | 
687  |  | 
  | 
688  | 0  |     route_unlock_node(rn);  | 
689  | 0  |   }  | 
690  | 0  | }  | 
691  |  |  | 
692  |  | void ospf_ase_external_lsas_finish(struct route_table *rt)  | 
693  | 0  | { | 
694  | 0  |   struct route_node *rn;  | 
695  | 0  |   struct ospf_lsa *lsa;  | 
696  | 0  |   struct list *lst;  | 
697  | 0  |   struct listnode *node, *nnode;  | 
698  |  | 
  | 
699  | 0  |   for (rn = route_top(rt); rn; rn = route_next(rn))  | 
700  | 0  |     if ((lst = rn->info) != NULL) { | 
701  | 0  |       for (ALL_LIST_ELEMENTS(lst, node, nnode, lsa))  | 
702  | 0  |         ospf_lsa_unlock(&lsa); /* external_lsas lst */  | 
703  | 0  |       list_delete(&lst);  | 
704  | 0  |     }  | 
705  |  | 
  | 
706  | 0  |   route_table_finish(rt);  | 
707  | 0  | }  | 
708  |  |  | 
709  |  | void ospf_ase_incremental_update(struct ospf *ospf, struct ospf_lsa *lsa)  | 
710  | 61  | { | 
711  | 61  |   struct list *lsas;  | 
712  | 61  |   struct listnode *node;  | 
713  | 61  |   struct route_node *rn, *rn2;  | 
714  | 61  |   struct prefix_ipv4 p;  | 
715  | 61  |   struct route_table *tmp_old;  | 
716  | 61  |   struct as_external_lsa *al;  | 
717  |  |  | 
718  | 61  |   al = (struct as_external_lsa *)lsa->data;  | 
719  | 61  |   p.family = AF_INET;  | 
720  | 61  |   p.prefix = lsa->data->id;  | 
721  | 61  |   p.prefixlen = ip_masklen(al->mask);  | 
722  | 61  |   apply_mask_ipv4(&p);  | 
723  |  |  | 
724  |  |   /* if new_table is NULL, there was no spf calculation, thus  | 
725  |  |      incremental update is unneeded */  | 
726  | 61  |   if (!ospf->new_table)  | 
727  | 61  |     return;  | 
728  |  |  | 
729  |  |   /* If there is already an intra-area or inter-area route  | 
730  |  |      to the destination, no recalculation is necessary  | 
731  |  |      (internal routes take precedence). */  | 
732  |  |  | 
733  | 0  |   rn = route_node_lookup(ospf->new_table, (struct prefix *)&p);  | 
734  | 0  |   if (rn) { | 
735  | 0  |     route_unlock_node(rn);  | 
736  | 0  |     if (rn->info)  | 
737  | 0  |       return;  | 
738  | 0  |   }  | 
739  |  |  | 
740  | 0  |   rn = route_node_lookup(ospf->external_lsas, (struct prefix *)&p);  | 
741  | 0  |   assert(rn);  | 
742  | 0  |   assert(rn->info);  | 
743  | 0  |   lsas = rn->info;  | 
744  | 0  |   route_unlock_node(rn);  | 
745  |  | 
  | 
746  | 0  |   for (ALL_LIST_ELEMENTS_RO(lsas, node, lsa))  | 
747  | 0  |     ospf_ase_calculate_route(ospf, lsa);  | 
748  |  |  | 
749  |  |   /* prepare temporary old routing table for compare */  | 
750  | 0  |   tmp_old = route_table_init();  | 
751  | 0  |   rn = route_node_lookup(ospf->old_external_route, (struct prefix *)&p);  | 
752  | 0  |   if (rn && rn->info) { | 
753  | 0  |     rn2 = route_node_get(tmp_old, (struct prefix *)&p);  | 
754  | 0  |     rn2->info = rn->info;  | 
755  | 0  |     route_unlock_node(rn);  | 
756  | 0  |   }  | 
757  |  |  | 
758  |  |   /* install changes to zebra */  | 
759  | 0  |   ospf_ase_compare_tables(ospf, ospf->new_external_route, tmp_old);  | 
760  |  |  | 
761  |  |   /* update ospf->old_external_route table */  | 
762  | 0  |   if (rn && rn->info)  | 
763  | 0  |     ospf_route_free((struct ospf_route *)rn->info);  | 
764  |  | 
  | 
765  | 0  |   rn2 = route_node_lookup(ospf->new_external_route, (struct prefix *)&p);  | 
766  |  |   /* if new route exists, install it to ospf->old_external_route */  | 
767  | 0  |   if (rn2 && rn2->info) { | 
768  | 0  |     if (!rn)  | 
769  | 0  |       rn = route_node_get(ospf->old_external_route,  | 
770  | 0  |               (struct prefix *)&p);  | 
771  | 0  |     rn->info = rn2->info;  | 
772  | 0  |   } else { | 
773  |  |     /* remove route node from ospf->old_external_route */  | 
774  | 0  |     if (rn) { | 
775  | 0  |       rn->info = NULL;  | 
776  | 0  |       route_unlock_node(rn);  | 
777  | 0  |     }  | 
778  | 0  |   }  | 
779  |  | 
  | 
780  | 0  |   if (rn2) { | 
781  |  |     /* rn2->info is stored in route node of ospf->old_external_route  | 
782  |  |      */  | 
783  | 0  |     rn2->info = NULL;  | 
784  | 0  |     route_unlock_node(rn2);  | 
785  | 0  |     route_unlock_node(rn2);  | 
786  | 0  |   }  | 
787  |  | 
  | 
788  | 0  |   route_table_finish(tmp_old);  | 
789  | 0  | }  |