]>
Commit | Line | Data |
---|---|---|
718e3744 | 1 | /* |
2 | * Zebra connect library for OSPFd | |
3 | * Copyright (C) 1997, 98, 99, 2000 Kunihiro Ishiguro, Toshiaki Takada | |
4 | * | |
5 | * This file is part of GNU Zebra. | |
6 | * | |
7 | * GNU Zebra is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation; either version 2, or (at your option) any | |
10 | * later version. | |
11 | * | |
12 | * GNU Zebra is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
896014f4 DL |
17 | * You should have received a copy of the GNU General Public License along |
18 | * with this program; see the file COPYING; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
718e3744 | 20 | */ |
21 | ||
22 | #include <zebra.h> | |
23 | ||
24 | #include "thread.h" | |
25 | #include "command.h" | |
26 | #include "network.h" | |
27 | #include "prefix.h" | |
28 | #include "routemap.h" | |
29 | #include "table.h" | |
30 | #include "stream.h" | |
31 | #include "memory.h" | |
32 | #include "zclient.h" | |
33 | #include "filter.h" | |
dd669bb0 | 34 | #include "plist.h" |
718e3744 | 35 | #include "log.h" |
2376c3f2 | 36 | #include "lib/bfd.h" |
5b30316e | 37 | #include "nexthop.h" |
718e3744 | 38 | |
39 | #include "ospfd/ospfd.h" | |
40 | #include "ospfd/ospf_interface.h" | |
41 | #include "ospfd/ospf_ism.h" | |
42 | #include "ospfd/ospf_asbr.h" | |
43 | #include "ospfd/ospf_asbr.h" | |
44 | #include "ospfd/ospf_abr.h" | |
45 | #include "ospfd/ospf_lsa.h" | |
46 | #include "ospfd/ospf_dump.h" | |
47 | #include "ospfd/ospf_route.h" | |
d5a5c8f0 DS |
48 | #include "ospfd/ospf_lsdb.h" |
49 | #include "ospfd/ospf_neighbor.h" | |
50 | #include "ospfd/ospf_nsm.h" | |
718e3744 | 51 | #include "ospfd/ospf_zebra.h" |
16f1b9ee | 52 | #include "ospfd/ospf_te.h" |
718e3744 | 53 | |
dfac5d39 CS |
54 | DEFINE_MTYPE_STATIC(OSPFD, OSPF_EXTERNAL, "OSPF External route table") |
55 | DEFINE_MTYPE_STATIC(OSPFD, OSPF_REDISTRIBUTE, "OSPF Redistriute") | |
b5a8894d | 56 | DEFINE_MTYPE_STATIC(OSPFD, OSPF_DIST_ARGS, "OSPF Distribute arguments") |
dfac5d39 | 57 | |
d62a17ae | 58 | DEFINE_HOOK(ospf_if_delete, (struct interface * ifp), (ifp)) |
3012671f | 59 | |
718e3744 | 60 | /* Zebra structure to hold current status. */ |
61 | struct zclient *zclient = NULL; | |
62 | ||
63 | /* For registering threads. */ | |
64 | extern struct thread_master *master; | |
18a6dce6 | 65 | |
66 | /* Router-id update message from zebra. */ | |
121f9dee | 67 | static int ospf_router_id_update_zebra(ZAPI_CALLBACK_ARGS) |
18a6dce6 | 68 | { |
b5a8894d | 69 | struct ospf *ospf = NULL; |
d62a17ae | 70 | struct prefix router_id; |
71 | zebra_router_id_update_read(zclient->ibuf, &router_id); | |
72 | ||
73 | if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) { | |
74 | char buf[PREFIX2STR_BUFFER]; | |
75 | prefix2str(&router_id, buf, sizeof(buf)); | |
996c9314 LB |
76 | zlog_debug("Zebra rcvd: router id update %s vrf %s id %u", buf, |
77 | ospf_vrf_id_to_name(vrf_id), vrf_id); | |
d62a17ae | 78 | } |
79 | ||
b5a8894d | 80 | ospf = ospf_lookup_by_vrf_id(vrf_id); |
d62a17ae | 81 | |
6021c6c0 CS |
82 | if (ospf != NULL) { |
83 | ospf->router_id_zebra = router_id.u.prefix4; | |
d62a17ae | 84 | ospf_router_id_update(ospf); |
6021c6c0 | 85 | } else { |
b5a8894d CS |
86 | if (IS_DEBUG_OSPF_EVENT) { |
87 | char buf[PREFIX2STR_BUFFER]; | |
88 | ||
89 | prefix2str(&router_id, buf, sizeof(buf)); | |
996c9314 LB |
90 | zlog_debug( |
91 | "%s: ospf instance not found for vrf %s id %u router_id %s", | |
92 | __PRETTY_FUNCTION__, | |
93 | ospf_vrf_id_to_name(vrf_id), vrf_id, buf); | |
b5a8894d CS |
94 | } |
95 | } | |
d62a17ae | 96 | return 0; |
18a6dce6 | 97 | } |
718e3744 | 98 | |
121f9dee | 99 | static int ospf_interface_delete(ZAPI_CALLBACK_ARGS) |
718e3744 | 100 | { |
d62a17ae | 101 | struct interface *ifp; |
102 | struct stream *s; | |
103 | struct route_node *rn; | |
718e3744 | 104 | |
d62a17ae | 105 | s = zclient->ibuf; |
106 | /* zebra_interface_state_read() updates interface structure in iflist */ | |
107 | ifp = zebra_interface_state_read(s, vrf_id); | |
718e3744 | 108 | |
d62a17ae | 109 | if (ifp == NULL) |
110 | return 0; | |
718e3744 | 111 | |
d62a17ae | 112 | if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) |
113 | zlog_debug( | |
b5a8894d | 114 | "Zebra: interface delete %s vrf %s[%u] index %d flags %llx metric %d mtu %d", |
a36898e7 DS |
115 | ifp->name, ospf_vrf_id_to_name(ifp->vrf_id), |
116 | ifp->vrf_id, ifp->ifindex, | |
d62a17ae | 117 | (unsigned long long)ifp->flags, ifp->metric, ifp->mtu); |
718e3744 | 118 | |
d62a17ae | 119 | hook_call(ospf_if_delete, ifp); |
718e3744 | 120 | |
d62a17ae | 121 | for (rn = route_top(IF_OIFS(ifp)); rn; rn = route_next(rn)) |
122 | if (rn->info) | |
123 | ospf_if_free((struct ospf_interface *)rn->info); | |
718e3744 | 124 | |
ff880b78 | 125 | if_set_index(ifp, IFINDEX_INTERNAL); |
d62a17ae | 126 | return 0; |
718e3744 | 127 | } |
128 | ||
121f9dee | 129 | static int ospf_interface_address_add(ZAPI_CALLBACK_ARGS) |
718e3744 | 130 | { |
d62a17ae | 131 | struct connected *c; |
b5a8894d CS |
132 | struct ospf *ospf = NULL; |
133 | ||
718e3744 | 134 | |
121f9dee | 135 | c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); |
718e3744 | 136 | |
d62a17ae | 137 | if (c == NULL) |
138 | return 0; | |
718e3744 | 139 | |
d62a17ae | 140 | if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) { |
141 | char buf[PREFIX2STR_BUFFER]; | |
142 | prefix2str(c->address, buf, sizeof(buf)); | |
b5a8894d CS |
143 | zlog_debug("Zebra: interface %s address add %s vrf %s id %u", |
144 | c->ifp->name, buf, ospf_vrf_id_to_name(vrf_id), | |
145 | vrf_id); | |
d62a17ae | 146 | } |
7f643ebf | 147 | |
b5a8894d | 148 | ospf = ospf_lookup_by_vrf_id(vrf_id); |
43b8d1d8 CS |
149 | if (!ospf) |
150 | return 0; | |
b5a8894d CS |
151 | |
152 | ospf_if_update(ospf, c->ifp); | |
718e3744 | 153 | |
ef7bd2a3 | 154 | ospf_if_interface(c->ifp); |
718e3744 | 155 | |
d62a17ae | 156 | return 0; |
718e3744 | 157 | } |
158 | ||
121f9dee | 159 | static int ospf_interface_address_delete(ZAPI_CALLBACK_ARGS) |
718e3744 | 160 | { |
d62a17ae | 161 | struct connected *c; |
162 | struct interface *ifp; | |
163 | struct ospf_interface *oi; | |
164 | struct route_node *rn; | |
165 | struct prefix p; | |
166 | ||
121f9dee | 167 | c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); |
d62a17ae | 168 | |
169 | if (c == NULL) | |
170 | return 0; | |
171 | ||
172 | if (IS_DEBUG_OSPF(zebra, ZEBRA_INTERFACE)) { | |
173 | char buf[PREFIX2STR_BUFFER]; | |
174 | prefix2str(c->address, buf, sizeof(buf)); | |
175 | zlog_debug("Zebra: interface %s address delete %s", | |
176 | c->ifp->name, buf); | |
177 | } | |
7f643ebf | 178 | |
d62a17ae | 179 | ifp = c->ifp; |
180 | p = *c->address; | |
181 | p.prefixlen = IPV4_MAX_PREFIXLEN; | |
718e3744 | 182 | |
d62a17ae | 183 | rn = route_node_lookup(IF_OIFS(ifp), &p); |
184 | if (!rn) { | |
185 | connected_free(c); | |
186 | return 0; | |
187 | } | |
718e3744 | 188 | |
d62a17ae | 189 | assert(rn->info); |
190 | oi = rn->info; | |
191 | route_unlock_node(rn); | |
cf795c5d | 192 | |
d62a17ae | 193 | /* Call interface hook functions to clean up */ |
194 | ospf_if_free(oi); | |
cf795c5d | 195 | |
ef7bd2a3 | 196 | ospf_if_interface(c->ifp); |
718e3744 | 197 | |
d62a17ae | 198 | connected_free(c); |
718e3744 | 199 | |
d62a17ae | 200 | return 0; |
718e3744 | 201 | } |
72357f2b | 202 | |
121f9dee | 203 | static int ospf_interface_link_params(ZAPI_CALLBACK_ARGS) |
16f1b9ee | 204 | { |
d62a17ae | 205 | struct interface *ifp; |
16f1b9ee | 206 | |
edc12762 | 207 | ifp = zebra_interface_link_params_read(zclient->ibuf, vrf_id); |
16f1b9ee | 208 | |
d62a17ae | 209 | if (ifp == NULL) |
210 | return 0; | |
16f1b9ee | 211 | |
d62a17ae | 212 | /* Update TE TLV */ |
213 | ospf_mpls_te_update_if(ifp); | |
16f1b9ee | 214 | |
d62a17ae | 215 | return 0; |
16f1b9ee OD |
216 | } |
217 | ||
b5a8894d | 218 | /* VRF update for an interface. */ |
121f9dee | 219 | static int ospf_interface_vrf_update(ZAPI_CALLBACK_ARGS) |
b5a8894d CS |
220 | { |
221 | struct interface *ifp = NULL; | |
222 | vrf_id_t new_vrf_id; | |
16f1b9ee | 223 | |
b5a8894d | 224 | ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, |
996c9314 | 225 | &new_vrf_id); |
b5a8894d CS |
226 | if (!ifp) |
227 | return 0; | |
228 | ||
229 | if (IS_DEBUG_OSPF_EVENT) | |
996c9314 LB |
230 | zlog_debug( |
231 | "%s: Rx Interface %s VRF change vrf_id %u New vrf %s id %u", | |
232 | __PRETTY_FUNCTION__, ifp->name, vrf_id, | |
233 | ospf_vrf_id_to_name(new_vrf_id), new_vrf_id); | |
b5a8894d CS |
234 | |
235 | /*if_update(ifp, ifp->name, strlen(ifp->name), new_vrf_id);*/ | |
a36898e7 | 236 | if_update_to_new_vrf(ifp, new_vrf_id); |
b5a8894d | 237 | |
996c9314 | 238 | return 0; |
b5a8894d CS |
239 | } |
240 | ||
241 | void ospf_zebra_add(struct ospf *ospf, struct prefix_ipv4 *p, | |
996c9314 | 242 | struct ospf_route * or) |
718e3744 | 243 | { |
5fef910e RW |
244 | struct zapi_route api; |
245 | struct zapi_nexthop *api_nh; | |
d7c0a89a | 246 | uint8_t distance; |
d62a17ae | 247 | struct ospf_path *path; |
248 | struct listnode *node; | |
5fef910e | 249 | int count = 0; |
d62a17ae | 250 | |
5fef910e | 251 | memset(&api, 0, sizeof(api)); |
b5a8894d | 252 | api.vrf_id = ospf->vrf_id; |
5fef910e RW |
253 | api.type = ZEBRA_ROUTE_OSPF; |
254 | api.instance = ospf->instance; | |
255 | api.safi = SAFI_UNICAST; | |
d00061ea | 256 | |
5fef910e RW |
257 | memcpy(&api.prefix, p, sizeof(*p)); |
258 | SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); | |
d00061ea | 259 | |
5fef910e RW |
260 | /* Metric value. */ |
261 | SET_FLAG(api.message, ZAPI_MESSAGE_METRIC); | |
262 | if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL) | |
263 | api.metric = or->cost + or->u.ext.type2_cost; | |
264 | else if (or->path_type == OSPF_PATH_TYPE2_EXTERNAL) | |
265 | api.metric = or->u.ext.type2_cost; | |
266 | else | |
267 | api.metric = or->cost; | |
d00061ea RW |
268 | |
269 | /* Check if path type is ASE */ | |
270 | if (((or->path_type == OSPF_PATH_TYPE1_EXTERNAL) | |
271 | || (or->path_type == OSPF_PATH_TYPE2_EXTERNAL)) | |
5fef910e RW |
272 | && (or->u.ext.tag > 0) && (or->u.ext.tag <= ROUTE_TAG_MAX)) { |
273 | SET_FLAG(api.message, ZAPI_MESSAGE_TAG); | |
274 | api.tag = or->u.ext.tag; | |
275 | } | |
d00061ea | 276 | |
5fef910e | 277 | /* Distance value. */ |
b5a8894d | 278 | distance = ospf_distance_apply(ospf, p, or); |
5fef910e RW |
279 | if (distance) { |
280 | SET_FLAG(api.message, ZAPI_MESSAGE_DISTANCE); | |
281 | api.distance = distance; | |
282 | } | |
d00061ea RW |
283 | |
284 | /* Nexthop, ifindex, distance and metric information. */ | |
285 | for (ALL_LIST_ELEMENTS_RO(or->paths, node, path)) { | |
a74e593b RW |
286 | if (count >= MULTIPATH_NUM) |
287 | break; | |
5fef910e | 288 | api_nh = &api.nexthops[count]; |
525c1839 | 289 | #ifdef HAVE_NETLINK |
d00061ea RW |
290 | if (path->unnumbered || (path->nexthop.s_addr != INADDR_ANY |
291 | && path->ifindex != 0)) { | |
525c1839 | 292 | #else /* HAVE_NETLINK */ |
d00061ea | 293 | if (path->nexthop.s_addr != INADDR_ANY && path->ifindex != 0) { |
5fef910e RW |
294 | #endif /* HAVE_NETLINK */ |
295 | api_nh->gate.ipv4 = path->nexthop; | |
296 | api_nh->ifindex = path->ifindex; | |
297 | api_nh->type = NEXTHOP_TYPE_IPV4_IFINDEX; | |
d00061ea | 298 | } else if (path->nexthop.s_addr != INADDR_ANY) { |
5fef910e RW |
299 | api_nh->gate.ipv4 = path->nexthop; |
300 | api_nh->type = NEXTHOP_TYPE_IPV4; | |
d00061ea | 301 | } else { |
5fef910e RW |
302 | api_nh->ifindex = path->ifindex; |
303 | api_nh->type = NEXTHOP_TYPE_IFINDEX; | |
d00061ea | 304 | } |
4a7371e9 | 305 | api_nh->vrf_id = ospf->vrf_id; |
5fef910e | 306 | count++; |
72357f2b | 307 | |
d00061ea | 308 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { |
96065dc3 CS |
309 | char buf[2][INET_ADDRSTRLEN]; |
310 | struct interface *ifp; | |
311 | ||
312 | ifp = if_lookup_by_index(path->ifindex, ospf->vrf_id); | |
4259ea81 | 313 | |
d00061ea | 314 | zlog_debug( |
96065dc3 | 315 | "Zebra: Route add %s nexthop %s, ifindex=%d %s", |
4259ea81 MS |
316 | prefix2str(p, buf[0], sizeof(buf[0])), |
317 | inet_ntop(AF_INET, &path->nexthop, | |
318 | buf[1], sizeof(buf[1])), | |
96065dc3 | 319 | path->ifindex, ifp ? ifp->name : " "); |
d62a17ae | 320 | } |
d00061ea | 321 | } |
a74e593b | 322 | api.nexthop_num = count; |
d62a17ae | 323 | |
5fef910e | 324 | zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); |
718e3744 | 325 | } |
326 | ||
b5a8894d | 327 | void ospf_zebra_delete(struct ospf *ospf, struct prefix_ipv4 *p, |
996c9314 | 328 | struct ospf_route * or) |
718e3744 | 329 | { |
5fef910e | 330 | struct zapi_route api; |
d62a17ae | 331 | |
5fef910e | 332 | memset(&api, 0, sizeof(api)); |
b5a8894d | 333 | api.vrf_id = ospf->vrf_id; |
5fef910e RW |
334 | api.type = ZEBRA_ROUTE_OSPF; |
335 | api.instance = ospf->instance; | |
336 | api.safi = SAFI_UNICAST; | |
337 | memcpy(&api.prefix, p, sizeof(*p)); | |
d62a17ae | 338 | |
5fef910e | 339 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { |
4259ea81 MS |
340 | char buf[PREFIX2STR_BUFFER]; |
341 | zlog_debug("Zebra: Route delete %s", | |
342 | prefix2str(p, buf, sizeof(buf))); | |
ba281d3d | 343 | } |
d00061ea | 344 | |
5fef910e | 345 | zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); |
718e3744 | 346 | } |
347 | ||
b5a8894d | 348 | void ospf_zebra_add_discard(struct ospf *ospf, struct prefix_ipv4 *p) |
718e3744 | 349 | { |
5fef910e | 350 | struct zapi_route api; |
d62a17ae | 351 | |
5fef910e | 352 | memset(&api, 0, sizeof(api)); |
b5a8894d | 353 | api.vrf_id = ospf->vrf_id; |
d00061ea RW |
354 | api.type = ZEBRA_ROUTE_OSPF; |
355 | api.instance = ospf->instance; | |
d00061ea | 356 | api.safi = SAFI_UNICAST; |
5fef910e | 357 | memcpy(&api.prefix, p, sizeof(*p)); |
09a484dd | 358 | zapi_route_set_blackhole(&api, BLACKHOLE_NULL); |
d00061ea | 359 | |
5fef910e | 360 | zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api); |
d00061ea | 361 | |
4259ea81 MS |
362 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { |
363 | char buf[PREFIX2STR_BUFFER]; | |
364 | zlog_debug("Zebra: Route add discard %s", | |
365 | prefix2str(p, buf, sizeof(buf))); | |
366 | } | |
718e3744 | 367 | } |
368 | ||
b5a8894d | 369 | void ospf_zebra_delete_discard(struct ospf *ospf, struct prefix_ipv4 *p) |
718e3744 | 370 | { |
5fef910e | 371 | struct zapi_route api; |
d62a17ae | 372 | |
5fef910e | 373 | memset(&api, 0, sizeof(api)); |
b5a8894d | 374 | api.vrf_id = ospf->vrf_id; |
d00061ea RW |
375 | api.type = ZEBRA_ROUTE_OSPF; |
376 | api.instance = ospf->instance; | |
d00061ea | 377 | api.safi = SAFI_UNICAST; |
5fef910e | 378 | memcpy(&api.prefix, p, sizeof(*p)); |
09a484dd | 379 | zapi_route_set_blackhole(&api, BLACKHOLE_NULL); |
d00061ea | 380 | |
5fef910e | 381 | zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api); |
d00061ea | 382 | |
4259ea81 MS |
383 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { |
384 | char buf[PREFIX2STR_BUFFER]; | |
385 | zlog_debug("Zebra: Route delete discard %s", | |
386 | prefix2str(p, buf, sizeof(buf))); | |
387 | } | |
718e3744 | 388 | } |
389 | ||
d7c0a89a QY |
390 | struct ospf_external *ospf_external_lookup(struct ospf *ospf, uint8_t type, |
391 | unsigned short instance) | |
7c8ff89e | 392 | { |
d62a17ae | 393 | struct list *ext_list; |
394 | struct listnode *node; | |
395 | struct ospf_external *ext; | |
7c8ff89e | 396 | |
de1ac5fd | 397 | ext_list = ospf->external[type]; |
d62a17ae | 398 | if (!ext_list) |
399 | return (NULL); | |
7c8ff89e | 400 | |
d62a17ae | 401 | for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) |
402 | if (ext->instance == instance) | |
403 | return ext; | |
7c8ff89e | 404 | |
d62a17ae | 405 | return NULL; |
7c8ff89e DS |
406 | } |
407 | ||
d7c0a89a QY |
408 | struct ospf_external *ospf_external_add(struct ospf *ospf, uint8_t type, |
409 | unsigned short instance) | |
7c8ff89e | 410 | { |
d62a17ae | 411 | struct list *ext_list; |
412 | struct ospf_external *ext; | |
7c8ff89e | 413 | |
de1ac5fd | 414 | ext = ospf_external_lookup(ospf, type, instance); |
d62a17ae | 415 | if (ext) |
416 | return ext; | |
7c8ff89e | 417 | |
de1ac5fd CS |
418 | if (!ospf->external[type]) |
419 | ospf->external[type] = list_new(); | |
7c8ff89e | 420 | |
de1ac5fd | 421 | ext_list = ospf->external[type]; |
9f5dc319 | 422 | ext = XCALLOC(MTYPE_OSPF_EXTERNAL, sizeof(struct ospf_external)); |
d62a17ae | 423 | ext->instance = instance; |
424 | EXTERNAL_INFO(ext) = route_table_init(); | |
7c8ff89e | 425 | |
d62a17ae | 426 | listnode_add(ext_list, ext); |
7c8ff89e | 427 | |
d62a17ae | 428 | return ext; |
7c8ff89e DS |
429 | } |
430 | ||
d7c0a89a | 431 | void ospf_external_del(struct ospf *ospf, uint8_t type, unsigned short instance) |
7c8ff89e | 432 | { |
d62a17ae | 433 | struct ospf_external *ext; |
434 | ||
de1ac5fd | 435 | ext = ospf_external_lookup(ospf, type, instance); |
d62a17ae | 436 | |
437 | if (ext) { | |
438 | if (EXTERNAL_INFO(ext)) | |
439 | route_table_finish(EXTERNAL_INFO(ext)); | |
440 | ||
de1ac5fd CS |
441 | listnode_delete(ospf->external[type], ext); |
442 | ||
443 | if (!ospf->external[type]->count) | |
6a154c88 | 444 | list_delete(&ospf->external[type]); |
de1ac5fd | 445 | |
7f586094 | 446 | XFREE(MTYPE_OSPF_EXTERNAL, ext); |
d62a17ae | 447 | } |
7c8ff89e DS |
448 | } |
449 | ||
d7c0a89a QY |
450 | struct ospf_redist *ospf_redist_lookup(struct ospf *ospf, uint8_t type, |
451 | unsigned short instance) | |
7c8ff89e | 452 | { |
d62a17ae | 453 | struct list *red_list; |
454 | struct listnode *node; | |
455 | struct ospf_redist *red; | |
7c8ff89e | 456 | |
d62a17ae | 457 | red_list = ospf->redist[type]; |
458 | if (!red_list) | |
459 | return (NULL); | |
7c8ff89e | 460 | |
d62a17ae | 461 | for (ALL_LIST_ELEMENTS_RO(red_list, node, red)) |
462 | if (red->instance == instance) | |
463 | return red; | |
7c8ff89e | 464 | |
d62a17ae | 465 | return NULL; |
7c8ff89e DS |
466 | } |
467 | ||
d7c0a89a QY |
468 | struct ospf_redist *ospf_redist_add(struct ospf *ospf, uint8_t type, |
469 | unsigned short instance) | |
7c8ff89e | 470 | { |
d62a17ae | 471 | struct list *red_list; |
472 | struct ospf_redist *red; | |
7c8ff89e | 473 | |
d62a17ae | 474 | red = ospf_redist_lookup(ospf, type, instance); |
475 | if (red) | |
476 | return red; | |
7c8ff89e | 477 | |
d62a17ae | 478 | if (!ospf->redist[type]) |
479 | ospf->redist[type] = list_new(); | |
7c8ff89e | 480 | |
d62a17ae | 481 | red_list = ospf->redist[type]; |
9f5dc319 | 482 | red = XCALLOC(MTYPE_OSPF_REDISTRIBUTE, sizeof(struct ospf_redist)); |
d62a17ae | 483 | red->instance = instance; |
484 | red->dmetric.type = -1; | |
485 | red->dmetric.value = -1; | |
1fb93326 | 486 | ROUTEMAP_NAME(red) = NULL; |
487 | ROUTEMAP(red) = NULL; | |
7c8ff89e | 488 | |
d62a17ae | 489 | listnode_add(red_list, red); |
7c8ff89e | 490 | |
d62a17ae | 491 | return red; |
7c8ff89e DS |
492 | } |
493 | ||
d7c0a89a | 494 | void ospf_redist_del(struct ospf *ospf, uint8_t type, unsigned short instance) |
7c8ff89e | 495 | { |
d62a17ae | 496 | struct ospf_redist *red; |
497 | ||
498 | red = ospf_redist_lookup(ospf, type, instance); | |
499 | ||
500 | if (red) { | |
501 | listnode_delete(ospf->redist[type], red); | |
502 | if (!ospf->redist[type]->count) { | |
6a154c88 | 503 | list_delete(&ospf->redist[type]); |
d62a17ae | 504 | } |
97a69b31 | 505 | ospf_routemap_unset(red); |
7f586094 | 506 | XFREE(MTYPE_OSPF_REDISTRIBUTE, red); |
d62a17ae | 507 | } |
7c8ff89e DS |
508 | } |
509 | ||
510 | ||
d7c0a89a QY |
511 | int ospf_is_type_redistributed(struct ospf *ospf, int type, |
512 | unsigned short instance) | |
718e3744 | 513 | { |
d62a17ae | 514 | return (DEFAULT_ROUTE_TYPE(type) |
49db7a7b | 515 | ? vrf_bitmap_check(zclient->default_information[AFI_IP], |
b5a8894d | 516 | ospf->vrf_id) |
d62a17ae | 517 | : ((instance |
518 | && redist_check_instance( | |
519 | &zclient->mi_redist[AFI_IP][type], | |
520 | instance)) | |
521 | || (!instance | |
522 | && vrf_bitmap_check( | |
523 | zclient->redist[AFI_IP][type], | |
b5a8894d | 524 | ospf->vrf_id)))); |
718e3744 | 525 | } |
526 | ||
d7c0a89a | 527 | int ospf_redistribute_set(struct ospf *ospf, int type, unsigned short instance, |
d62a17ae | 528 | int mtype, int mvalue) |
718e3744 | 529 | { |
d62a17ae | 530 | int force = 0; |
531 | struct ospf_redist *red; | |
532 | ||
533 | red = ospf_redist_lookup(ospf, type, instance); | |
162dbe41 | 534 | |
535 | if (red == NULL) { | |
536 | zlog_err( | |
537 | "Redistribute[%s][%d]: Lookup failed Type[%d] , Metric[%d]", | |
538 | ospf_redist_string(type), instance, | |
539 | metric_type(ospf, type, instance), | |
540 | metric_value(ospf, type, instance)); | |
541 | return CMD_WARNING_CONFIG_FAILED; | |
542 | } | |
543 | ||
b5a8894d | 544 | if (ospf_is_type_redistributed(ospf, type, instance)) { |
d62a17ae | 545 | if (mtype != red->dmetric.type) { |
546 | red->dmetric.type = mtype; | |
547 | force = LSA_REFRESH_FORCE; | |
548 | } | |
549 | if (mvalue != red->dmetric.value) { | |
550 | red->dmetric.value = mvalue; | |
551 | force = LSA_REFRESH_FORCE; | |
552 | } | |
553 | ||
554 | ospf_external_lsa_refresh_type(ospf, type, instance, force); | |
555 | ||
556 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) | |
557 | zlog_debug( | |
558 | "Redistribute[%s][%d]: Refresh Type[%d], Metric[%d]", | |
559 | ospf_redist_string(type), instance, | |
560 | metric_type(ospf, type, instance), | |
561 | metric_value(ospf, type, instance)); | |
562 | ||
563 | return CMD_SUCCESS; | |
564 | } | |
565 | ||
566 | red->dmetric.type = mtype; | |
567 | red->dmetric.value = mvalue; | |
568 | ||
de1ac5fd | 569 | ospf_external_add(ospf, type, instance); |
d62a17ae | 570 | |
571 | zclient_redistribute(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, | |
b5a8894d | 572 | instance, ospf->vrf_id); |
d62a17ae | 573 | |
574 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) | |
996c9314 LB |
575 | zlog_debug( |
576 | "Redistribute[%s][%d] vrf id %u: Start Type[%d], Metric[%d]", | |
577 | ospf_redist_string(type), instance, ospf->vrf_id, | |
578 | metric_type(ospf, type, instance), | |
579 | metric_value(ospf, type, instance)); | |
d62a17ae | 580 | |
581 | ospf_asbr_status_update(ospf, ++ospf->redistribute); | |
582 | ||
583 | return CMD_SUCCESS; | |
718e3744 | 584 | } |
585 | ||
d7c0a89a QY |
586 | int ospf_redistribute_unset(struct ospf *ospf, int type, |
587 | unsigned short instance) | |
718e3744 | 588 | { |
d62a17ae | 589 | if (type == zclient->redist_default && instance == zclient->instance) |
590 | return CMD_SUCCESS; | |
718e3744 | 591 | |
b5a8894d | 592 | if (!ospf_is_type_redistributed(ospf, type, instance)) |
d62a17ae | 593 | return CMD_SUCCESS; |
718e3744 | 594 | |
d62a17ae | 595 | zclient_redistribute(ZEBRA_REDISTRIBUTE_DELETE, zclient, AFI_IP, type, |
b5a8894d | 596 | instance, ospf->vrf_id); |
cf795c5d | 597 | |
d62a17ae | 598 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) |
b5a8894d CS |
599 | zlog_debug("Redistribute[%s][%d] vrf id %u: Stop", |
600 | ospf_redist_string(type), instance, ospf->vrf_id); | |
718e3744 | 601 | |
d62a17ae | 602 | /* Remove the routes from OSPF table. */ |
603 | ospf_redistribute_withdraw(ospf, type, instance); | |
7c8ff89e | 604 | |
de1ac5fd | 605 | ospf_external_del(ospf, type, instance); |
718e3744 | 606 | |
d62a17ae | 607 | ospf_asbr_status_update(ospf, --ospf->redistribute); |
718e3744 | 608 | |
d62a17ae | 609 | return CMD_SUCCESS; |
718e3744 | 610 | } |
611 | ||
d62a17ae | 612 | int ospf_redistribute_default_set(struct ospf *ospf, int originate, int mtype, |
613 | int mvalue) | |
718e3744 | 614 | { |
1fb93326 | 615 | struct prefix_ipv4 p; |
616 | struct in_addr nexthop; | |
617 | int cur_originate = ospf->default_originate; | |
d5eac1e0 | 618 | const char *type_str = NULL; |
1fb93326 | 619 | |
620 | nexthop.s_addr = 0; | |
621 | p.family = AF_INET; | |
622 | p.prefix.s_addr = 0; | |
623 | p.prefixlen = 0; | |
624 | ||
d62a17ae | 625 | ospf->default_originate = originate; |
020709f9 | 626 | |
d5eac1e0 | 627 | if (cur_originate == originate) { |
1fb93326 | 628 | /* Refresh the lsa since metric might different */ |
d62a17ae | 629 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) |
630 | zlog_debug( | |
631 | "Redistribute[%s]: Refresh Type[%d], Metric[%d]", | |
632 | ospf_redist_string(DEFAULT_ROUTE), | |
633 | metric_type(ospf, DEFAULT_ROUTE, 0), | |
634 | metric_value(ospf, DEFAULT_ROUTE, 0)); | |
718e3744 | 635 | |
1fb93326 | 636 | ospf_external_lsa_refresh_default(ospf); |
d5eac1e0 | 637 | return CMD_SUCCESS; |
1fb93326 | 638 | } |
718e3744 | 639 | |
d5eac1e0 DL |
640 | switch (cur_originate) { |
641 | case DEFAULT_ORIGINATE_NONE: | |
642 | break; | |
643 | case DEFAULT_ORIGINATE_ZEBRA: | |
1fb93326 | 644 | zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_DELETE, |
49db7a7b | 645 | zclient, AFI_IP, ospf->vrf_id); |
d5eac1e0 DL |
646 | ospf->redistribute--; |
647 | break; | |
648 | case DEFAULT_ORIGINATE_ALWAYS: | |
649 | ospf_external_info_delete(ospf, DEFAULT_ROUTE, 0, p); | |
650 | ospf_external_del(ospf, DEFAULT_ROUTE, 0); | |
651 | ospf->redistribute--; | |
652 | break; | |
1fb93326 | 653 | } |
718e3744 | 654 | |
d5eac1e0 DL |
655 | switch (originate) { |
656 | case DEFAULT_ORIGINATE_NONE: | |
657 | type_str = "none"; | |
658 | break; | |
659 | case DEFAULT_ORIGINATE_ZEBRA: | |
660 | type_str = "normal"; | |
661 | ospf->redistribute++; | |
662 | zclient_redistribute_default(ZEBRA_REDISTRIBUTE_DEFAULT_ADD, | |
663 | zclient, AFI_IP, ospf->vrf_id); | |
664 | break; | |
665 | case DEFAULT_ORIGINATE_ALWAYS: | |
666 | type_str = "always"; | |
667 | ospf->redistribute++; | |
668 | ospf_external_add(ospf, DEFAULT_ROUTE, 0); | |
669 | ospf_external_info_add(ospf, DEFAULT_ROUTE, 0, p, 0, nexthop, | |
670 | 0); | |
671 | break; | |
672 | } | |
718e3744 | 673 | |
d62a17ae | 674 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) |
d5eac1e0 DL |
675 | zlog_debug("Redistribute[DEFAULT]: %s Type[%d], Metric[%d]", |
676 | type_str, | |
677 | metric_type(ospf, DEFAULT_ROUTE, 0), | |
678 | metric_value(ospf, DEFAULT_ROUTE, 0)); | |
718e3744 | 679 | |
d5eac1e0 DL |
680 | ospf_external_lsa_refresh_default(ospf); |
681 | ospf_asbr_status_update(ospf, ospf->redistribute); | |
d62a17ae | 682 | return CMD_SUCCESS; |
718e3744 | 683 | } |
684 | ||
d62a17ae | 685 | static int ospf_external_lsa_originate_check(struct ospf *ospf, |
686 | struct external_info *ei) | |
718e3744 | 687 | { |
d62a17ae | 688 | /* If prefix is multicast, then do not originate LSA. */ |
689 | if (IN_MULTICAST(htonl(ei->p.prefix.s_addr))) { | |
690 | zlog_info( | |
691 | "LSA[Type5:%s]: Not originate AS-external-LSA, " | |
692 | "Prefix belongs multicast", | |
693 | inet_ntoa(ei->p.prefix)); | |
694 | return 0; | |
695 | } | |
696 | ||
697 | /* Take care of default-originate. */ | |
698 | if (is_prefix_default(&ei->p)) | |
699 | if (ospf->default_originate == DEFAULT_ORIGINATE_NONE) { | |
700 | zlog_info( | |
701 | "LSA[Type5:0.0.0.0]: Not originate AS-external-LSA " | |
702 | "for default"); | |
703 | return 0; | |
704 | } | |
705 | ||
706 | return 1; | |
718e3744 | 707 | } |
708 | ||
709 | /* If connected prefix is OSPF enable interface, then do not announce. */ | |
d62a17ae | 710 | int ospf_distribute_check_connected(struct ospf *ospf, struct external_info *ei) |
718e3744 | 711 | { |
d62a17ae | 712 | struct listnode *node; |
713 | struct ospf_interface *oi; | |
718e3744 | 714 | |
718e3744 | 715 | |
d62a17ae | 716 | for (ALL_LIST_ELEMENTS_RO(ospf->oiflist, node, oi)) |
717 | if (prefix_match(oi->address, (struct prefix *)&ei->p)) | |
718 | return 0; | |
719 | return 1; | |
718e3744 | 720 | } |
721 | ||
722 | /* return 1 if external LSA must be originated, 0 otherwise */ | |
d62a17ae | 723 | int ospf_redistribute_check(struct ospf *ospf, struct external_info *ei, |
724 | int *changed) | |
718e3744 | 725 | { |
d62a17ae | 726 | struct route_map_set_values save_values; |
727 | struct prefix_ipv4 *p = &ei->p; | |
728 | struct ospf_redist *red; | |
d7c0a89a QY |
729 | uint8_t type = is_prefix_default(&ei->p) ? DEFAULT_ROUTE : ei->type; |
730 | unsigned short instance = is_prefix_default(&ei->p) ? 0 : ei->instance; | |
d62a17ae | 731 | |
732 | if (changed) | |
733 | *changed = 0; | |
734 | ||
735 | if (!ospf_external_lsa_originate_check(ospf, ei)) | |
736 | return 0; | |
737 | ||
738 | /* Take care connected route. */ | |
739 | if (type == ZEBRA_ROUTE_CONNECT | |
740 | && !ospf_distribute_check_connected(ospf, ei)) | |
741 | return 0; | |
742 | ||
743 | if (!DEFAULT_ROUTE_TYPE(type) && DISTRIBUTE_NAME(ospf, type)) | |
744 | /* distirbute-list exists, but access-list may not? */ | |
745 | if (DISTRIBUTE_LIST(ospf, type)) | |
746 | if (access_list_apply(DISTRIBUTE_LIST(ospf, type), p) | |
747 | == FILTER_DENY) { | |
4259ea81 MS |
748 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { |
749 | char buf[PREFIX2STR_BUFFER]; | |
d62a17ae | 750 | zlog_debug( |
4259ea81 | 751 | "Redistribute[%s]: %s filtered by distribute-list.", |
d62a17ae | 752 | ospf_redist_string(type), |
4259ea81 MS |
753 | prefix2str(p, buf, sizeof(buf))); |
754 | } | |
d62a17ae | 755 | return 0; |
756 | } | |
757 | ||
758 | save_values = ei->route_map_set; | |
759 | ospf_reset_route_map_set_values(&ei->route_map_set); | |
760 | ||
761 | /* apply route-map if needed */ | |
762 | red = ospf_redist_lookup(ospf, type, instance); | |
763 | if (red && ROUTEMAP_NAME(red)) { | |
b68885f9 | 764 | route_map_result_t ret; |
d62a17ae | 765 | |
766 | ret = route_map_apply(ROUTEMAP(red), (struct prefix *)p, | |
767 | RMAP_OSPF, ei); | |
768 | ||
769 | if (ret == RMAP_DENYMATCH) { | |
770 | ei->route_map_set = save_values; | |
4259ea81 MS |
771 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { |
772 | char buf[PREFIX2STR_BUFFER]; | |
d62a17ae | 773 | zlog_debug( |
4259ea81 | 774 | "Redistribute[%s]: %s filtered by route-map.", |
d62a17ae | 775 | ospf_redist_string(type), |
4259ea81 MS |
776 | prefix2str(p, buf, sizeof(buf))); |
777 | } | |
d62a17ae | 778 | return 0; |
779 | } | |
780 | ||
781 | /* check if 'route-map set' changed something */ | |
782 | if (changed) | |
783 | *changed = !ospf_route_map_set_compare( | |
784 | &ei->route_map_set, &save_values); | |
785 | } | |
786 | ||
787 | return 1; | |
718e3744 | 788 | } |
789 | ||
790 | /* OSPF route-map set for redistribution */ | |
d62a17ae | 791 | void ospf_routemap_set(struct ospf_redist *red, const char *name) |
718e3744 | 792 | { |
93d836e6 | 793 | if (ROUTEMAP_NAME(red)) { |
794 | route_map_counter_decrement(ROUTEMAP(red)); | |
d62a17ae | 795 | free(ROUTEMAP_NAME(red)); |
93d836e6 | 796 | } |
718e3744 | 797 | |
d62a17ae | 798 | ROUTEMAP_NAME(red) = strdup(name); |
799 | ROUTEMAP(red) = route_map_lookup_by_name(name); | |
93d836e6 | 800 | route_map_counter_increment(ROUTEMAP(red)); |
718e3744 | 801 | } |
802 | ||
d62a17ae | 803 | void ospf_routemap_unset(struct ospf_redist *red) |
718e3744 | 804 | { |
93d836e6 | 805 | if (ROUTEMAP_NAME(red)) { |
806 | route_map_counter_decrement(ROUTEMAP(red)); | |
d62a17ae | 807 | free(ROUTEMAP_NAME(red)); |
93d836e6 | 808 | } |
718e3744 | 809 | |
d62a17ae | 810 | ROUTEMAP_NAME(red) = NULL; |
811 | ROUTEMAP(red) = NULL; | |
718e3744 | 812 | } |
813 | ||
814 | /* Zebra route add and delete treatment. */ | |
121f9dee | 815 | static int ospf_zebra_read_route(ZAPI_CALLBACK_ARGS) |
718e3744 | 816 | { |
74489921 RW |
817 | struct zapi_route api; |
818 | struct prefix_ipv4 p; | |
d62a17ae | 819 | unsigned long ifindex; |
820 | struct in_addr nexthop; | |
d62a17ae | 821 | struct external_info *ei; |
822 | struct ospf *ospf; | |
823 | int i; | |
1fb93326 | 824 | uint8_t rt_type; |
d62a17ae | 825 | |
b5a8894d | 826 | ospf = ospf_lookup_by_vrf_id(vrf_id); |
74489921 RW |
827 | if (ospf == NULL) |
828 | return 0; | |
d62a17ae | 829 | |
74489921 RW |
830 | if (zapi_route_decode(zclient->ibuf, &api) < 0) |
831 | return -1; | |
d62a17ae | 832 | |
74489921 RW |
833 | ifindex = api.nexthops[0].ifindex; |
834 | nexthop = api.nexthops[0].gate.ipv4; | |
1fb93326 | 835 | rt_type = api.type; |
d62a17ae | 836 | |
74489921 | 837 | memcpy(&p, &api.prefix, sizeof(p)); |
d62a17ae | 838 | if (IPV4_NET127(ntohl(p.prefix.s_addr))) |
839 | return 0; | |
840 | ||
1fb93326 | 841 | /* Re-destributed route is default route. |
842 | * Here, route type is used as 'ZEBRA_ROUTE_KERNEL' for | |
843 | * updating ex-info. But in resetting (no default-info | |
844 | * originate)ZEBRA_ROUTE_MAX is used to delete the ex-info. | |
845 | * Resolved this inconsistency by maintaining same route type. | |
846 | */ | |
847 | if (is_prefix_default(&p)) | |
848 | rt_type = DEFAULT_ROUTE; | |
849 | ||
5c780bc9 PZ |
850 | if (IS_DEBUG_OSPF(zebra, ZEBRA_REDISTRIBUTE)) { |
851 | char buf_prefix[PREFIX_STRLEN]; | |
852 | prefix2str(&api.prefix, buf_prefix, sizeof(buf_prefix)); | |
853 | ||
ae4080c5 MS |
854 | zlog_debug("%s: cmd %s from client %s: vrf_id %d, p %s", |
855 | __func__, zserv_command_string(cmd), | |
5c780bc9 PZ |
856 | zebra_route_string(api.type), vrf_id, buf_prefix); |
857 | } | |
858 | ||
121f9dee | 859 | if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_ADD) { |
d62a17ae | 860 | /* XXX|HACK|TODO|FIXME: |
09a484dd DL |
861 | * Maybe we should ignore reject/blackhole routes? Testing |
862 | * shows that there is no problems though and this is only way | |
863 | * to "summarize" routes in ASBR at the moment. Maybe we need | |
864 | * just a better generalised solution for these types? | |
d62a17ae | 865 | */ |
866 | ||
74489921 | 867 | /* Protocol tag overwrites all other tag value sent by zebra */ |
1fb93326 | 868 | if (ospf->dtag[rt_type] > 0) |
869 | api.tag = ospf->dtag[rt_type]; | |
d62a17ae | 870 | |
871 | /* | |
872 | * Given zebra sends update for a prefix via ADD message, it | |
873 | * should | |
874 | * be considered as an implicit DEL for that prefix with other | |
875 | * source | |
876 | * types. | |
877 | */ | |
1fb93326 | 878 | for (i = 0; i <= ZEBRA_ROUTE_MAX; i++) |
879 | if (i != rt_type) | |
996c9314 LB |
880 | ospf_external_info_delete(ospf, i, api.instance, |
881 | p); | |
d62a17ae | 882 | |
1fb93326 | 883 | ei = ospf_external_info_add(ospf, rt_type, api.instance, p, |
de1ac5fd | 884 | ifindex, nexthop, api.tag); |
d62a17ae | 885 | if (ei == NULL) { |
886 | /* Nothing has changed, so nothing to do; return */ | |
887 | return 0; | |
888 | } | |
fa3c7c7e | 889 | if (ospf->router_id.s_addr != 0) { |
d62a17ae | 890 | if (ei) { |
891 | if (is_prefix_default(&p)) | |
892 | ospf_external_lsa_refresh_default(ospf); | |
893 | else { | |
894 | struct ospf_lsa *current; | |
895 | ||
896 | current = ospf_external_info_find_lsa( | |
897 | ospf, &ei->p); | |
898 | if (!current) | |
899 | ospf_external_lsa_originate( | |
900 | ospf, ei); | |
901 | else { | |
902 | if (IS_DEBUG_OSPF( | |
903 | zebra, | |
904 | ZEBRA_REDISTRIBUTE)) | |
905 | zlog_debug( | |
74489921 | 906 | "ospf_zebra_read_route() : %s refreshing LSA", |
d62a17ae | 907 | inet_ntoa( |
908 | p.prefix)); | |
909 | ospf_external_lsa_refresh( | |
910 | ospf, current, ei, | |
911 | LSA_REFRESH_FORCE); | |
912 | } | |
913 | } | |
914 | } | |
915 | } | |
121f9dee | 916 | } else /* if (cmd == ZEBRA_REDISTRIBUTE_ROUTE_DEL) */ |
5048fe14 | 917 | { |
1fb93326 | 918 | ospf_external_info_delete(ospf, rt_type, api.instance, p); |
d62a17ae | 919 | if (is_prefix_default(&p)) |
920 | ospf_external_lsa_refresh_default(ospf); | |
921 | else | |
1fb93326 | 922 | ospf_external_lsa_flush(ospf, rt_type, &p, |
d62a17ae | 923 | ifindex /*, nexthop */); |
5048fe14 | 924 | } |
d62a17ae | 925 | |
926 | return 0; | |
718e3744 | 927 | } |
6b0655a2 | 928 | |
cf795c5d | 929 | |
d62a17ae | 930 | int ospf_distribute_list_out_set(struct ospf *ospf, int type, const char *name) |
718e3744 | 931 | { |
d62a17ae | 932 | /* Lookup access-list for distribute-list. */ |
933 | DISTRIBUTE_LIST(ospf, type) = access_list_lookup(AFI_IP, name); | |
718e3744 | 934 | |
d62a17ae | 935 | /* Clear previous distribute-name. */ |
936 | if (DISTRIBUTE_NAME(ospf, type)) | |
937 | free(DISTRIBUTE_NAME(ospf, type)); | |
718e3744 | 938 | |
d62a17ae | 939 | /* Set distribute-name. */ |
940 | DISTRIBUTE_NAME(ospf, type) = strdup(name); | |
718e3744 | 941 | |
d62a17ae | 942 | /* If access-list have been set, schedule update timer. */ |
943 | if (DISTRIBUTE_LIST(ospf, type)) | |
944 | ospf_distribute_list_update(ospf, type, 0); | |
718e3744 | 945 | |
d62a17ae | 946 | return CMD_SUCCESS; |
718e3744 | 947 | } |
948 | ||
d62a17ae | 949 | int ospf_distribute_list_out_unset(struct ospf *ospf, int type, |
950 | const char *name) | |
718e3744 | 951 | { |
d62a17ae | 952 | /* Schedule update timer. */ |
953 | if (DISTRIBUTE_LIST(ospf, type)) | |
954 | ospf_distribute_list_update(ospf, type, 0); | |
718e3744 | 955 | |
d62a17ae | 956 | /* Unset distribute-list. */ |
957 | DISTRIBUTE_LIST(ospf, type) = NULL; | |
718e3744 | 958 | |
d62a17ae | 959 | /* Clear distribute-name. */ |
960 | if (DISTRIBUTE_NAME(ospf, type)) | |
961 | free(DISTRIBUTE_NAME(ospf, type)); | |
cf795c5d | 962 | |
d62a17ae | 963 | DISTRIBUTE_NAME(ospf, type) = NULL; |
718e3744 | 964 | |
d62a17ae | 965 | return CMD_SUCCESS; |
718e3744 | 966 | } |
967 | ||
968 | /* distribute-list update timer. */ | |
d62a17ae | 969 | static int ospf_distribute_list_update_timer(struct thread *thread) |
718e3744 | 970 | { |
d62a17ae | 971 | struct route_node *rn; |
972 | struct external_info *ei; | |
973 | struct route_table *rt; | |
974 | struct ospf_lsa *lsa; | |
b5a8894d CS |
975 | int type, default_refresh = 0, arg_type; |
976 | struct ospf *ospf = NULL; | |
996c9314 | 977 | void **arg = THREAD_ARG(thread); |
b5a8894d CS |
978 | |
979 | ospf = (struct ospf *)arg[0]; | |
980 | arg_type = (int)(intptr_t)arg[1]; | |
d62a17ae | 981 | |
d62a17ae | 982 | if (ospf == NULL) |
983 | return 0; | |
984 | ||
985 | ospf->t_distribute_update = NULL; | |
986 | ||
987 | zlog_info("Zebra[Redistribute]: distribute-list update timer fired!"); | |
988 | ||
b5a8894d | 989 | if (IS_DEBUG_OSPF_EVENT) { |
996c9314 LB |
990 | zlog_debug( |
991 | "%s: ospf distribute-list update arg_type %d vrf %s id %d", | |
992 | __PRETTY_FUNCTION__, arg_type, | |
993 | ospf_vrf_id_to_name(ospf->vrf_id), ospf->vrf_id); | |
b5a8894d CS |
994 | } |
995 | ||
d62a17ae | 996 | /* foreach all external info. */ |
997 | for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { | |
998 | struct list *ext_list; | |
999 | struct listnode *node; | |
1000 | struct ospf_external *ext; | |
1001 | ||
de1ac5fd | 1002 | ext_list = ospf->external[type]; |
d62a17ae | 1003 | if (!ext_list) |
1004 | continue; | |
1005 | ||
1006 | for (ALL_LIST_ELEMENTS_RO(ext_list, node, ext)) { | |
1007 | rt = ext->external_info; | |
1008 | if (!rt) | |
1009 | continue; | |
1010 | for (rn = route_top(rt); rn; rn = route_next(rn)) | |
1011 | if ((ei = rn->info) != NULL) { | |
1012 | if (is_prefix_default(&ei->p)) | |
1013 | default_refresh = 1; | |
1014 | else if ( | |
1015 | (lsa = ospf_external_info_find_lsa( | |
1016 | ospf, &ei->p))) | |
1017 | ospf_external_lsa_refresh( | |
1018 | ospf, lsa, ei, | |
1019 | LSA_REFRESH_IF_CHANGED); | |
1020 | else | |
1021 | ospf_external_lsa_originate( | |
1022 | ospf, ei); | |
1023 | } | |
1024 | } | |
1025 | } | |
1026 | if (default_refresh) | |
1027 | ospf_external_lsa_refresh_default(ospf); | |
b5a8894d CS |
1028 | |
1029 | XFREE(MTYPE_OSPF_DIST_ARGS, arg); | |
d62a17ae | 1030 | return 0; |
718e3744 | 1031 | } |
1032 | ||
718e3744 | 1033 | /* Update distribute-list and set timer to apply access-list. */ |
d7c0a89a QY |
1034 | void ospf_distribute_list_update(struct ospf *ospf, int type, |
1035 | unsigned short instance) | |
718e3744 | 1036 | { |
d62a17ae | 1037 | struct route_table *rt; |
1038 | struct ospf_external *ext; | |
996c9314 | 1039 | void **args = XCALLOC(MTYPE_OSPF_DIST_ARGS, sizeof(void *) * 2); |
d62a17ae | 1040 | |
b5a8894d | 1041 | args[0] = ospf; |
996c9314 | 1042 | args[1] = (void *)((ptrdiff_t)type); |
b5a8894d | 1043 | |
d62a17ae | 1044 | /* External info does not exist. */ |
de1ac5fd | 1045 | ext = ospf_external_lookup(ospf, type, instance); |
a68730c6 DS |
1046 | if (!ext || !(rt = EXTERNAL_INFO(ext))) { |
1047 | XFREE(MTYPE_OSPF_DIST_ARGS, args); | |
d62a17ae | 1048 | return; |
a68730c6 | 1049 | } |
d62a17ae | 1050 | |
1051 | /* If exists previously invoked thread, then let it continue. */ | |
a68730c6 DS |
1052 | if (ospf->t_distribute_update) { |
1053 | XFREE(MTYPE_OSPF_DIST_ARGS, args); | |
d62a17ae | 1054 | return; |
a68730c6 | 1055 | } |
d62a17ae | 1056 | |
1057 | /* Set timer. */ | |
1058 | ospf->t_distribute_update = NULL; | |
1059 | thread_add_timer_msec(master, ospf_distribute_list_update_timer, | |
b5a8894d | 1060 | (void **)args, ospf->min_ls_interval, |
d62a17ae | 1061 | &ospf->t_distribute_update); |
718e3744 | 1062 | } |
1063 | ||
1064 | /* If access-list is updated, apply some check. */ | |
d62a17ae | 1065 | static void ospf_filter_update(struct access_list *access) |
718e3744 | 1066 | { |
d62a17ae | 1067 | struct ospf *ospf; |
1068 | int type; | |
1069 | int abr_inv = 0; | |
1070 | struct ospf_area *area; | |
b5a8894d | 1071 | struct listnode *node, *n1; |
d62a17ae | 1072 | |
1073 | /* If OSPF instance does not exist, return right now. */ | |
b5a8894d | 1074 | if (listcount(om->ospf) == 0) |
d62a17ae | 1075 | return; |
1076 | ||
b5a8894d | 1077 | /* Iterate all ospf [VRF] instances */ |
43b8d1d8 | 1078 | for (ALL_LIST_ELEMENTS_RO(om->ospf, n1, ospf)) { |
b5a8894d CS |
1079 | /* Update distribute-list, and apply filter. */ |
1080 | for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { | |
1081 | struct list *red_list; | |
b5a8894d CS |
1082 | struct ospf_redist *red; |
1083 | ||
1084 | red_list = ospf->redist[type]; | |
1085 | if (red_list) | |
996c9314 LB |
1086 | for (ALL_LIST_ELEMENTS_RO(red_list, node, |
1087 | red)) { | |
b5a8894d | 1088 | if (ROUTEMAP(red)) { |
996c9314 LB |
1089 | /* if route-map is not NULL it |
1090 | * may be | |
b5a8894d CS |
1091 | * using this access list */ |
1092 | ospf_distribute_list_update( | |
996c9314 LB |
1093 | ospf, type, |
1094 | red->instance); | |
b5a8894d | 1095 | } |
d62a17ae | 1096 | } |
d62a17ae | 1097 | |
b5a8894d CS |
1098 | /* There is place for route-map for default-information |
1099 | * (ZEBRA_ROUTE_MAX), | |
1100 | * but no distribute list. */ | |
1101 | if (type == ZEBRA_ROUTE_MAX) | |
1102 | break; | |
1103 | ||
1104 | if (DISTRIBUTE_NAME(ospf, type)) { | |
1105 | /* Keep old access-list for distribute-list. */ | |
996c9314 LB |
1106 | struct access_list *old = |
1107 | DISTRIBUTE_LIST(ospf, type); | |
b5a8894d CS |
1108 | |
1109 | /* Update access-list for distribute-list. */ | |
996c9314 LB |
1110 | DISTRIBUTE_LIST(ospf, type) = |
1111 | access_list_lookup( | |
1112 | AFI_IP, | |
1113 | DISTRIBUTE_NAME(ospf, type)); | |
b5a8894d CS |
1114 | |
1115 | /* No update for this distribute type. */ | |
996c9314 LB |
1116 | if (old == NULL |
1117 | && DISTRIBUTE_LIST(ospf, type) == NULL) | |
b5a8894d CS |
1118 | continue; |
1119 | ||
1120 | /* Schedule distribute-list update timer. */ | |
1121 | if (DISTRIBUTE_LIST(ospf, type) == NULL | |
996c9314 LB |
1122 | || strcmp(DISTRIBUTE_NAME(ospf, type), |
1123 | access->name) | |
1124 | == 0) | |
1125 | ospf_distribute_list_update(ospf, type, | |
1126 | 0); | |
b5a8894d | 1127 | } |
d62a17ae | 1128 | } |
d62a17ae | 1129 | |
b5a8894d CS |
1130 | /* Update Area access-list. */ |
1131 | for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { | |
1132 | if (EXPORT_NAME(area)) { | |
1133 | EXPORT_LIST(area) = NULL; | |
1134 | abr_inv++; | |
1135 | } | |
d62a17ae | 1136 | |
b5a8894d CS |
1137 | if (IMPORT_NAME(area)) { |
1138 | IMPORT_LIST(area) = NULL; | |
1139 | abr_inv++; | |
1140 | } | |
d62a17ae | 1141 | } |
d62a17ae | 1142 | |
b5a8894d CS |
1143 | /* Schedule ABR tasks -- this will be changed -- takada. */ |
1144 | if (IS_OSPF_ABR(ospf) && abr_inv) | |
1145 | ospf_schedule_abr_task(ospf); | |
1146 | } | |
718e3744 | 1147 | } |
dd669bb0 | 1148 | |
1149 | /* If prefix-list is updated, do some updates. */ | |
d62a17ae | 1150 | void ospf_prefix_list_update(struct prefix_list *plist) |
dd669bb0 | 1151 | { |
b5a8894d | 1152 | struct ospf *ospf = NULL; |
d62a17ae | 1153 | int type; |
1154 | int abr_inv = 0; | |
1155 | struct ospf_area *area; | |
b5a8894d | 1156 | struct listnode *node, *n1; |
d62a17ae | 1157 | |
1158 | /* If OSPF instatnce does not exist, return right now. */ | |
b5a8894d | 1159 | if (listcount(om->ospf) == 0) |
d62a17ae | 1160 | return; |
1161 | ||
b5a8894d CS |
1162 | /* Iterate all ospf [VRF] instances */ |
1163 | for (ALL_LIST_ELEMENTS_RO(om->ospf, n1, ospf)) { | |
1164 | ||
1165 | /* Update all route-maps which are used | |
1166 | * as redistribution filters. | |
1167 | * They might use prefix-list. | |
1168 | */ | |
1169 | for (type = 0; type <= ZEBRA_ROUTE_MAX; type++) { | |
1170 | struct list *red_list; | |
b5a8894d CS |
1171 | struct ospf_redist *red; |
1172 | ||
1173 | red_list = ospf->redist[type]; | |
1174 | if (red_list) { | |
996c9314 LB |
1175 | for (ALL_LIST_ELEMENTS_RO(red_list, node, |
1176 | red)) { | |
b5a8894d CS |
1177 | if (ROUTEMAP(red)) { |
1178 | /* if route-map is not NULL | |
1179 | * it may be using | |
1180 | * this prefix list */ | |
1181 | ospf_distribute_list_update( | |
1182 | ospf, type, | |
1183 | red->instance); | |
1184 | } | |
d62a17ae | 1185 | } |
1186 | } | |
b5a8894d | 1187 | } |
d62a17ae | 1188 | |
b5a8894d CS |
1189 | /* Update area filter-lists. */ |
1190 | for (ALL_LIST_ELEMENTS_RO(ospf->areas, node, area)) { | |
1191 | /* Update filter-list in. */ | |
1192 | if (PREFIX_NAME_IN(area)) | |
1193 | if (strcmp(PREFIX_NAME_IN(area), | |
996c9314 LB |
1194 | prefix_list_name(plist)) |
1195 | == 0) { | |
b5a8894d CS |
1196 | PREFIX_LIST_IN(area) = |
1197 | prefix_list_lookup( | |
996c9314 LB |
1198 | AFI_IP, |
1199 | PREFIX_NAME_IN(area)); | |
b5a8894d CS |
1200 | abr_inv++; |
1201 | } | |
d62a17ae | 1202 | |
b5a8894d CS |
1203 | /* Update filter-list out. */ |
1204 | if (PREFIX_NAME_OUT(area)) | |
1205 | if (strcmp(PREFIX_NAME_OUT(area), | |
996c9314 LB |
1206 | prefix_list_name(plist)) |
1207 | == 0) { | |
b5a8894d CS |
1208 | PREFIX_LIST_IN(area) = |
1209 | prefix_list_lookup( | |
996c9314 LB |
1210 | AFI_IP, |
1211 | PREFIX_NAME_OUT(area)); | |
b5a8894d CS |
1212 | abr_inv++; |
1213 | } | |
1214 | } | |
d62a17ae | 1215 | |
b5a8894d CS |
1216 | /* Schedule ABR task. */ |
1217 | if (IS_OSPF_ABR(ospf) && abr_inv) | |
1218 | ospf_schedule_abr_task(ospf); | |
1219 | } | |
dd669bb0 | 1220 | } |
cf795c5d | 1221 | |
d62a17ae | 1222 | static struct ospf_distance *ospf_distance_new(void) |
718e3744 | 1223 | { |
d62a17ae | 1224 | return XCALLOC(MTYPE_OSPF_DISTANCE, sizeof(struct ospf_distance)); |
718e3744 | 1225 | } |
1226 | ||
d62a17ae | 1227 | static void ospf_distance_free(struct ospf_distance *odistance) |
718e3744 | 1228 | { |
d62a17ae | 1229 | XFREE(MTYPE_OSPF_DISTANCE, odistance); |
718e3744 | 1230 | } |
1231 | ||
d62a17ae | 1232 | int ospf_distance_set(struct vty *vty, struct ospf *ospf, |
1233 | const char *distance_str, const char *ip_str, | |
1234 | const char *access_list_str) | |
718e3744 | 1235 | { |
d62a17ae | 1236 | int ret; |
1237 | struct prefix_ipv4 p; | |
d7c0a89a | 1238 | uint8_t distance; |
d62a17ae | 1239 | struct route_node *rn; |
1240 | struct ospf_distance *odistance; | |
1241 | ||
1242 | ret = str2prefix_ipv4(ip_str, &p); | |
1243 | if (ret == 0) { | |
1244 | vty_out(vty, "Malformed prefix\n"); | |
1245 | return CMD_WARNING_CONFIG_FAILED; | |
1246 | } | |
1247 | ||
1248 | distance = atoi(distance_str); | |
1249 | ||
1250 | /* Get OSPF distance node. */ | |
1251 | rn = route_node_get(ospf->distance_table, (struct prefix *)&p); | |
1252 | if (rn->info) { | |
1253 | odistance = rn->info; | |
1254 | route_unlock_node(rn); | |
1255 | } else { | |
1256 | odistance = ospf_distance_new(); | |
1257 | rn->info = odistance; | |
1258 | } | |
1259 | ||
1260 | /* Set distance value. */ | |
1261 | odistance->distance = distance; | |
1262 | ||
1263 | /* Reset access-list configuration. */ | |
1264 | if (odistance->access_list) { | |
1265 | free(odistance->access_list); | |
1266 | odistance->access_list = NULL; | |
1267 | } | |
1268 | if (access_list_str) | |
1269 | odistance->access_list = strdup(access_list_str); | |
1270 | ||
1271 | return CMD_SUCCESS; | |
718e3744 | 1272 | } |
1273 | ||
d62a17ae | 1274 | int ospf_distance_unset(struct vty *vty, struct ospf *ospf, |
1275 | const char *distance_str, const char *ip_str, | |
1276 | char const *access_list_str) | |
718e3744 | 1277 | { |
d62a17ae | 1278 | int ret; |
1279 | struct prefix_ipv4 p; | |
1280 | struct route_node *rn; | |
1281 | struct ospf_distance *odistance; | |
1282 | ||
1283 | ret = str2prefix_ipv4(ip_str, &p); | |
1284 | if (ret == 0) { | |
1285 | vty_out(vty, "Malformed prefix\n"); | |
1286 | return CMD_WARNING_CONFIG_FAILED; | |
1287 | } | |
1288 | ||
1289 | rn = route_node_lookup(ospf->distance_table, (struct prefix *)&p); | |
1290 | if (!rn) { | |
1291 | vty_out(vty, "Can't find specified prefix\n"); | |
1292 | return CMD_WARNING_CONFIG_FAILED; | |
1293 | } | |
1294 | ||
1295 | odistance = rn->info; | |
1296 | ||
1297 | if (odistance->access_list) | |
1298 | free(odistance->access_list); | |
1299 | ospf_distance_free(odistance); | |
1300 | ||
1301 | rn->info = NULL; | |
1302 | route_unlock_node(rn); | |
1303 | route_unlock_node(rn); | |
1304 | ||
1305 | return CMD_SUCCESS; | |
718e3744 | 1306 | } |
1307 | ||
d62a17ae | 1308 | void ospf_distance_reset(struct ospf *ospf) |
718e3744 | 1309 | { |
d62a17ae | 1310 | struct route_node *rn; |
1311 | struct ospf_distance *odistance; | |
1312 | ||
1313 | for (rn = route_top(ospf->distance_table); rn; rn = route_next(rn)) | |
1314 | if ((odistance = rn->info) != NULL) { | |
1315 | if (odistance->access_list) | |
1316 | free(odistance->access_list); | |
1317 | ospf_distance_free(odistance); | |
1318 | rn->info = NULL; | |
1319 | route_unlock_node(rn); | |
1320 | } | |
718e3744 | 1321 | } |
1322 | ||
d7c0a89a QY |
1323 | uint8_t ospf_distance_apply(struct ospf *ospf, struct prefix_ipv4 *p, |
1324 | struct ospf_route * or) | |
718e3744 | 1325 | { |
718e3744 | 1326 | |
d62a17ae | 1327 | if (ospf == NULL) |
1328 | return 0; | |
718e3744 | 1329 | |
d62a17ae | 1330 | if (ospf->distance_intra) |
1331 | if (or->path_type == OSPF_PATH_INTRA_AREA) | |
1332 | return ospf->distance_intra; | |
718e3744 | 1333 | |
d62a17ae | 1334 | if (ospf->distance_inter) |
1335 | if (or->path_type == OSPF_PATH_INTER_AREA) | |
1336 | return ospf->distance_inter; | |
718e3744 | 1337 | |
d62a17ae | 1338 | if (ospf->distance_external) |
1339 | if (or->path_type == OSPF_PATH_TYPE1_EXTERNAL || | |
1340 | or->path_type == OSPF_PATH_TYPE2_EXTERNAL) | |
1341 | return ospf->distance_external; | |
cf795c5d | 1342 | |
d62a17ae | 1343 | if (ospf->distance_all) |
1344 | return ospf->distance_all; | |
718e3744 | 1345 | |
d62a17ae | 1346 | return 0; |
718e3744 | 1347 | } |
1348 | ||
b5a8894d CS |
1349 | void ospf_zebra_vrf_register(struct ospf *ospf) |
1350 | { | |
1351 | if (!zclient || zclient->sock < 0 || !ospf) | |
1352 | return; | |
1353 | ||
6021c6c0 | 1354 | if (ospf->vrf_id != VRF_UNKNOWN) { |
b5a8894d CS |
1355 | if (IS_DEBUG_OSPF_EVENT) |
1356 | zlog_debug("%s: Register VRF %s id %u", | |
1357 | __PRETTY_FUNCTION__, | |
1358 | ospf_vrf_id_to_name(ospf->vrf_id), | |
1359 | ospf->vrf_id); | |
1360 | zclient_send_reg_requests(zclient, ospf->vrf_id); | |
1361 | } | |
1362 | } | |
1363 | ||
1364 | void ospf_zebra_vrf_deregister(struct ospf *ospf) | |
1365 | { | |
1366 | if (!zclient || zclient->sock < 0 || !ospf) | |
1367 | return; | |
1368 | ||
1369 | if (ospf->vrf_id != VRF_DEFAULT && ospf->vrf_id != VRF_UNKNOWN) { | |
1370 | if (IS_DEBUG_OSPF_EVENT) | |
6021c6c0 | 1371 | zlog_debug("%s: De-Register VRF %s id %u to Zebra.", |
b5a8894d CS |
1372 | __PRETTY_FUNCTION__, |
1373 | ospf_vrf_id_to_name(ospf->vrf_id), | |
1374 | ospf->vrf_id); | |
1375 | /* Deregister for router-id, interfaces, | |
1376 | * redistributed routes. */ | |
1377 | zclient_send_dereg_requests(zclient, ospf->vrf_id); | |
1378 | } | |
1379 | } | |
d62a17ae | 1380 | static void ospf_zebra_connected(struct zclient *zclient) |
7076bb2f | 1381 | { |
d62a17ae | 1382 | /* Send the client registration */ |
0945d5ed | 1383 | bfd_client_sendmsg(zclient, ZEBRA_BFD_CLIENT_REGISTER, VRF_DEFAULT); |
2376c3f2 | 1384 | |
d62a17ae | 1385 | zclient_send_reg_requests(zclient, VRF_DEFAULT); |
7076bb2f FL |
1386 | } |
1387 | ||
d7c0a89a | 1388 | void ospf_zebra_init(struct thread_master *master, unsigned short instance) |
718e3744 | 1389 | { |
d62a17ae | 1390 | /* Allocate zebra structure. */ |
26f63a1e | 1391 | zclient = zclient_new(master, &zclient_options_default); |
342213ea | 1392 | zclient_init(zclient, ZEBRA_ROUTE_OSPF, instance, &ospfd_privs); |
d62a17ae | 1393 | zclient->zebra_connected = ospf_zebra_connected; |
1394 | zclient->router_id_update = ospf_router_id_update_zebra; | |
d62a17ae | 1395 | zclient->interface_delete = ospf_interface_delete; |
d62a17ae | 1396 | zclient->interface_address_add = ospf_interface_address_add; |
1397 | zclient->interface_address_delete = ospf_interface_address_delete; | |
1398 | zclient->interface_link_params = ospf_interface_link_params; | |
b5a8894d | 1399 | zclient->interface_vrf_update = ospf_interface_vrf_update; |
d62a17ae | 1400 | |
74489921 RW |
1401 | zclient->redistribute_route_add = ospf_zebra_read_route; |
1402 | zclient->redistribute_route_del = ospf_zebra_read_route; | |
d62a17ae | 1403 | |
1404 | access_list_add_hook(ospf_filter_update); | |
1405 | access_list_delete_hook(ospf_filter_update); | |
1406 | prefix_list_add_hook(ospf_prefix_list_update); | |
1407 | prefix_list_delete_hook(ospf_prefix_list_update); | |
718e3744 | 1408 | } |