]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
718e3744 | 2 | /* |
3 | * Interface related function for RIPng. | |
4 | * Copyright (C) 1998 Kunihiro Ishiguro | |
718e3744 | 5 | */ |
6 | ||
7 | #include <zebra.h> | |
8 | ||
9 | #include "linklist.h" | |
10 | #include "if.h" | |
11 | #include "prefix.h" | |
12 | #include "memory.h" | |
13 | #include "network.h" | |
14 | #include "filter.h" | |
15 | #include "log.h" | |
16 | #include "stream.h" | |
17 | #include "zclient.h" | |
18 | #include "command.h" | |
fe08ba7e | 19 | #include "agg_table.h" |
24a58196 | 20 | #include "frrevent.h" |
4d4653af | 21 | #include "privs.h" |
6a69b354 | 22 | #include "vrf.h" |
7f9a4fd7 | 23 | #include "lib_errors.h" |
d406db4c | 24 | #include "northbound_cli.h" |
718e3744 | 25 | |
26 | #include "ripngd/ripngd.h" | |
27 | #include "ripngd/ripng_debug.h" | |
6b0655a2 | 28 | |
718e3744 | 29 | /* If RFC2133 definition is used. */ |
30 | #ifndef IPV6_JOIN_GROUP | |
c258527b | 31 | #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP |
718e3744 | 32 | #endif |
33 | #ifndef IPV6_LEAVE_GROUP | |
c258527b | 34 | #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP |
718e3744 | 35 | #endif |
36 | ||
bf8d3d6a | 37 | DEFINE_MTYPE_STATIC(RIPNGD, RIPNG_IF, "ripng interface"); |
eaf58ba9 | 38 | |
718e3744 | 39 | /* Static utility function. */ |
d62a17ae | 40 | static void ripng_enable_apply(struct interface *); |
41 | static void ripng_passive_interface_apply(struct interface *); | |
5c84b9a5 | 42 | static int ripng_enable_if_lookup(struct ripng *ripng, const char *ifname); |
d62a17ae | 43 | static int ripng_enable_network_lookup2(struct connected *); |
5c84b9a5 | 44 | static void ripng_enable_apply_all(struct ripng *ripng); |
718e3744 | 45 | |
46 | /* Join to the all rip routers multicast group. */ | |
5c84b9a5 | 47 | static int ripng_multicast_join(struct interface *ifp, int sock) |
718e3744 | 48 | { |
d62a17ae | 49 | int ret; |
50 | struct ipv6_mreq mreq; | |
51 | int save_errno; | |
52 | ||
53 | if (if_is_multicast(ifp)) { | |
54 | memset(&mreq, 0, sizeof(mreq)); | |
55 | inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr); | |
56 | mreq.ipv6mr_interface = ifp->ifindex; | |
57 | ||
58 | /* | |
59 | * NetBSD 1.6.2 requires root to join groups on gif(4). | |
60 | * While this is bogus, privs are available and easy to use | |
61 | * for this call as a workaround. | |
62 | */ | |
0cf6db21 | 63 | frr_with_privs(&ripngd_privs) { |
d62a17ae | 64 | |
5c84b9a5 | 65 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, |
01b9e3fd DL |
66 | (char *)&mreq, sizeof(mreq)); |
67 | save_errno = errno; | |
d62a17ae | 68 | |
01b9e3fd | 69 | } |
d62a17ae | 70 | |
71 | if (ret < 0 && save_errno == EADDRINUSE) { | |
72 | /* | |
73 | * Group is already joined. This occurs due to sloppy | |
74 | * group | |
75 | * management, in particular declining to leave the | |
76 | * group on | |
77 | * an interface that has just gone down. | |
78 | */ | |
9165c5f5 | 79 | zlog_warn("ripng join on %s EADDRINUSE (ignoring)", |
d62a17ae | 80 | ifp->name); |
81 | return 0; /* not an error */ | |
82 | } | |
83 | ||
84 | if (ret < 0) | |
85 | zlog_warn("can't setsockopt IPV6_JOIN_GROUP: %s", | |
86 | safe_strerror(save_errno)); | |
87 | ||
88 | if (IS_RIPNG_DEBUG_EVENT) | |
89 | zlog_debug( | |
90 | "RIPng %s join to all-rip-routers multicast group", | |
91 | ifp->name); | |
92 | ||
93 | if (ret < 0) | |
94 | return -1; | |
95 | } | |
96 | return 0; | |
718e3744 | 97 | } |
98 | ||
99 | /* Leave from the all rip routers multicast group. */ | |
5c84b9a5 | 100 | static int ripng_multicast_leave(struct interface *ifp, int sock) |
718e3744 | 101 | { |
d62a17ae | 102 | int ret; |
103 | struct ipv6_mreq mreq; | |
104 | ||
105 | if (if_is_multicast(ifp)) { | |
106 | memset(&mreq, 0, sizeof(mreq)); | |
107 | inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr); | |
108 | mreq.ipv6mr_interface = ifp->ifindex; | |
109 | ||
5c84b9a5 | 110 | ret = setsockopt(sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, |
d62a17ae | 111 | (char *)&mreq, sizeof(mreq)); |
112 | if (ret < 0) | |
9165c5f5 | 113 | zlog_warn("can't setsockopt IPV6_LEAVE_GROUP: %s", |
d62a17ae | 114 | safe_strerror(errno)); |
115 | ||
116 | if (IS_RIPNG_DEBUG_EVENT) | |
117 | zlog_debug( | |
118 | "RIPng %s leave from all-rip-routers multicast group", | |
119 | ifp->name); | |
120 | ||
121 | if (ret < 0) | |
122 | return -1; | |
123 | } | |
a94434b6 | 124 | |
d62a17ae | 125 | return 0; |
a94434b6 | 126 | } |
127 | ||
128 | /* How many link local IPv6 address could be used on the interface ? */ | |
d62a17ae | 129 | static int ripng_if_ipv6_lladdress_check(struct interface *ifp) |
a94434b6 | 130 | { |
d62a17ae | 131 | struct listnode *nn; |
132 | struct connected *connected; | |
133 | int count = 0; | |
a94434b6 | 134 | |
d62a17ae | 135 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, nn, connected)) { |
136 | struct prefix *p; | |
137 | p = connected->address; | |
718e3744 | 138 | |
d62a17ae | 139 | if ((p->family == AF_INET6) |
140 | && IN6_IS_ADDR_LINKLOCAL(&p->u.prefix6)) | |
141 | count++; | |
142 | } | |
718e3744 | 143 | |
d62a17ae | 144 | return count; |
718e3744 | 145 | } |
146 | ||
d62a17ae | 147 | static int ripng_if_down(struct interface *ifp) |
718e3744 | 148 | { |
fe08ba7e | 149 | struct agg_node *rp; |
d62a17ae | 150 | struct ripng_info *rinfo; |
151 | struct ripng_interface *ri; | |
5c84b9a5 | 152 | struct ripng *ripng; |
d62a17ae | 153 | struct list *list = NULL; |
154 | struct listnode *listnode = NULL, *nextnode = NULL; | |
155 | ||
5c84b9a5 | 156 | ri = ifp->info; |
29ceb848 | 157 | |
e16d030c | 158 | EVENT_OFF(ri->t_wakeup); |
29ceb848 | 159 | |
5c84b9a5 RW |
160 | ripng = ri->ripng; |
161 | ||
d62a17ae | 162 | if (ripng) |
fe08ba7e DS |
163 | for (rp = agg_route_top(ripng->table); rp; |
164 | rp = agg_route_next(rp)) | |
d62a17ae | 165 | if ((list = rp->info) != NULL) |
166 | for (ALL_LIST_ELEMENTS(list, listnode, nextnode, | |
167 | rinfo)) | |
168 | if (rinfo->ifindex == ifp->ifindex) | |
5c84b9a5 | 169 | ripng_ecmp_delete(ripng, rinfo); |
d62a17ae | 170 | |
d62a17ae | 171 | |
172 | if (ri->running) { | |
173 | if (IS_RIPNG_DEBUG_EVENT) | |
174 | zlog_debug("turn off %s", ifp->name); | |
175 | ||
176 | /* Leave from multicast group. */ | |
fa3bf3a2 MS |
177 | if (ripng) |
178 | ripng_multicast_leave(ifp, ripng->sock); | |
d62a17ae | 179 | |
180 | ri->running = 0; | |
181 | } | |
182 | ||
183 | return 0; | |
718e3744 | 184 | } |
185 | ||
61879cab | 186 | /* Interface link up message processing. */ |
ddbf3e60 | 187 | static int ripng_ifp_up(struct interface *ifp) |
718e3744 | 188 | { |
096f7609 | 189 | if (IS_RIPNG_DEBUG_ZEBRA) |
d62a17ae | 190 | zlog_debug( |
24c4ee4a | 191 | "interface up %s vrf %s(%u) index %d flags %llx metric %d mtu %d", |
096f7609 IR |
192 | ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, |
193 | ifp->ifindex, (unsigned long long)ifp->flags, | |
194 | ifp->metric, ifp->mtu6); | |
dde7b15b RW |
195 | |
196 | ripng_interface_sync(ifp); | |
718e3744 | 197 | |
d62a17ae | 198 | /* Check if this interface is RIPng enabled or not. */ |
199 | ripng_enable_apply(ifp); | |
718e3744 | 200 | |
d62a17ae | 201 | /* Check for a passive interface. */ |
202 | ripng_passive_interface_apply(ifp); | |
718e3744 | 203 | |
d62a17ae | 204 | /* Apply distribute list to the all interface. */ |
205 | ripng_distribute_update_interface(ifp); | |
718e3744 | 206 | |
d62a17ae | 207 | return 0; |
718e3744 | 208 | } |
209 | ||
61879cab | 210 | /* Interface link down message processing. */ |
b0b69e59 | 211 | static int ripng_ifp_down(struct interface *ifp) |
718e3744 | 212 | { |
dde7b15b | 213 | ripng_interface_sync(ifp); |
d62a17ae | 214 | ripng_if_down(ifp); |
718e3744 | 215 | |
096f7609 | 216 | if (IS_RIPNG_DEBUG_ZEBRA) |
d62a17ae | 217 | zlog_debug( |
24c4ee4a | 218 | "interface down %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", |
096f7609 IR |
219 | ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, |
220 | ifp->ifindex, (unsigned long long)ifp->flags, | |
221 | ifp->metric, ifp->mtu6); | |
718e3744 | 222 | |
d62a17ae | 223 | return 0; |
718e3744 | 224 | } |
225 | ||
61879cab | 226 | /* Interface addition message from zebra. */ |
ef7bd2a3 | 227 | static int ripng_ifp_create(struct interface *ifp) |
718e3744 | 228 | { |
dde7b15b | 229 | ripng_interface_sync(ifp); |
718e3744 | 230 | |
096f7609 | 231 | if (IS_RIPNG_DEBUG_ZEBRA) |
d62a17ae | 232 | zlog_debug( |
24c4ee4a | 233 | "RIPng interface add %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", |
096f7609 IR |
234 | ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, |
235 | ifp->ifindex, (unsigned long long)ifp->flags, | |
236 | ifp->metric, ifp->mtu6); | |
718e3744 | 237 | |
d62a17ae | 238 | /* Check is this interface is RIP enabled or not.*/ |
239 | ripng_enable_apply(ifp); | |
718e3744 | 240 | |
d62a17ae | 241 | /* Apply distribute list to the interface. */ |
242 | ripng_distribute_update_interface(ifp); | |
718e3744 | 243 | |
d62a17ae | 244 | /* Check interface routemap. */ |
245 | ripng_if_rmap_update_interface(ifp); | |
718e3744 | 246 | |
d62a17ae | 247 | return 0; |
718e3744 | 248 | } |
249 | ||
3c3c3252 | 250 | static int ripng_ifp_destroy(struct interface *ifp) |
718e3744 | 251 | { |
dde7b15b | 252 | ripng_interface_sync(ifp); |
d62a17ae | 253 | if (if_is_up(ifp)) { |
254 | ripng_if_down(ifp); | |
255 | } | |
a94434b6 | 256 | |
13c5f95e DS |
257 | if (IS_RIPNG_DEBUG_ZEBRA) |
258 | zlog_debug( | |
259 | "interface delete %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", | |
096f7609 IR |
260 | ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, |
261 | ifp->ifindex, (unsigned long long)ifp->flags, | |
262 | ifp->metric, ifp->mtu6); | |
a94434b6 | 263 | |
d62a17ae | 264 | return 0; |
718e3744 | 265 | } |
266 | ||
dde7b15b | 267 | /* VRF update for an interface. */ |
121f9dee | 268 | int ripng_interface_vrf_update(ZAPI_CALLBACK_ARGS) |
dde7b15b RW |
269 | { |
270 | struct interface *ifp; | |
271 | vrf_id_t new_vrf_id; | |
272 | ||
273 | ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, | |
274 | &new_vrf_id); | |
275 | if (!ifp) | |
276 | return 0; | |
277 | ||
24c4ee4a | 278 | if (IS_RIPNG_DEBUG_ZEBRA) { |
24c4ee4a DS |
279 | struct vrf *nvrf = vrf_lookup_by_id(new_vrf_id); |
280 | ||
281 | zlog_debug("interface %s VRF change vrf %s(%u) new vrf %s(%u)", | |
096f7609 IR |
282 | ifp->name, ifp->vrf->name, vrf_id, VRF_LOGNAME(nvrf), |
283 | new_vrf_id); | |
24c4ee4a | 284 | } |
dde7b15b | 285 | |
a36898e7 | 286 | if_update_to_new_vrf(ifp, new_vrf_id); |
dde7b15b RW |
287 | ripng_interface_sync(ifp); |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
5c84b9a5 | 292 | void ripng_interface_clean(struct ripng *ripng) |
a94434b6 | 293 | { |
d62a17ae | 294 | struct interface *ifp; |
295 | struct ripng_interface *ri; | |
a94434b6 | 296 | |
dde7b15b | 297 | FOR_ALL_INTERFACES (ripng->vrf, ifp) { |
d62a17ae | 298 | ri = ifp->info; |
a94434b6 | 299 | |
d62a17ae | 300 | ri->enable_network = 0; |
301 | ri->enable_interface = 0; | |
302 | ri->running = 0; | |
a94434b6 | 303 | |
e16d030c | 304 | EVENT_OFF(ri->t_wakeup); |
d62a17ae | 305 | } |
306 | } | |
a94434b6 | 307 | |
d62a17ae | 308 | static void ripng_apply_address_add(struct connected *ifc) |
309 | { | |
5c84b9a5 RW |
310 | struct ripng_interface *ri = ifc->ifp->info; |
311 | struct ripng *ripng = ri->ripng; | |
d62a17ae | 312 | struct prefix_ipv6 address; |
313 | struct prefix *p; | |
314 | ||
315 | if (!ripng) | |
316 | return; | |
317 | ||
318 | if (!if_is_up(ifc->ifp)) | |
319 | return; | |
320 | ||
321 | p = ifc->address; | |
322 | ||
323 | memset(&address, 0, sizeof(address)); | |
324 | address.family = p->family; | |
325 | address.prefix = p->u.prefix6; | |
326 | address.prefixlen = p->prefixlen; | |
327 | apply_mask_ipv6(&address); | |
328 | ||
329 | /* Check if this interface is RIP enabled or not | |
330 | or Check if this address's prefix is RIP enabled */ | |
5c84b9a5 | 331 | if ((ripng_enable_if_lookup(ripng, ifc->ifp->name) >= 0) |
d62a17ae | 332 | || (ripng_enable_network_lookup2(ifc) >= 0)) |
5c84b9a5 | 333 | ripng_redistribute_add(ripng, ZEBRA_ROUTE_CONNECT, |
d62a17ae | 334 | RIPNG_ROUTE_INTERFACE, &address, |
335 | ifc->ifp->ifindex, NULL, 0); | |
a94434b6 | 336 | } |
337 | ||
121f9dee | 338 | int ripng_interface_address_add(ZAPI_CALLBACK_ARGS) |
718e3744 | 339 | { |
d62a17ae | 340 | struct connected *c; |
341 | struct prefix *p; | |
718e3744 | 342 | |
d62a17ae | 343 | c = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, |
344 | zclient->ibuf, vrf_id); | |
718e3744 | 345 | |
d62a17ae | 346 | if (c == NULL) |
347 | return 0; | |
718e3744 | 348 | |
d62a17ae | 349 | p = c->address; |
718e3744 | 350 | |
d62a17ae | 351 | if (p->family == AF_INET6) { |
352 | struct ripng_interface *ri = c->ifp->info; | |
a94434b6 | 353 | |
d62a17ae | 354 | if (IS_RIPNG_DEBUG_ZEBRA) |
e0259674 | 355 | zlog_debug("RIPng connected address %pFX add", p); |
a94434b6 | 356 | |
d62a17ae | 357 | /* Check is this prefix needs to be redistributed. */ |
358 | ripng_apply_address_add(c); | |
a94434b6 | 359 | |
d62a17ae | 360 | /* Let's try once again whether the interface could be activated |
361 | */ | |
362 | if (!ri->running) { | |
363 | /* Check if this interface is RIP enabled or not.*/ | |
364 | ripng_enable_apply(c->ifp); | |
a94434b6 | 365 | |
d62a17ae | 366 | /* Apply distribute list to the interface. */ |
367 | ripng_distribute_update_interface(c->ifp); | |
718e3744 | 368 | |
d62a17ae | 369 | /* Check interface routemap. */ |
370 | ripng_if_rmap_update_interface(c->ifp); | |
371 | } | |
372 | } | |
373 | ||
374 | return 0; | |
718e3744 | 375 | } |
376 | ||
d62a17ae | 377 | static void ripng_apply_address_del(struct connected *ifc) |
378 | { | |
5c84b9a5 RW |
379 | struct ripng_interface *ri = ifc->ifp->info; |
380 | struct ripng *ripng = ri->ripng; | |
d62a17ae | 381 | struct prefix_ipv6 address; |
382 | struct prefix *p; | |
a94434b6 | 383 | |
d62a17ae | 384 | if (!ripng) |
385 | return; | |
a94434b6 | 386 | |
d62a17ae | 387 | if (!if_is_up(ifc->ifp)) |
388 | return; | |
a94434b6 | 389 | |
d62a17ae | 390 | p = ifc->address; |
a94434b6 | 391 | |
d62a17ae | 392 | memset(&address, 0, sizeof(address)); |
393 | address.family = p->family; | |
394 | address.prefix = p->u.prefix6; | |
395 | address.prefixlen = p->prefixlen; | |
396 | apply_mask_ipv6(&address); | |
a94434b6 | 397 | |
5c84b9a5 RW |
398 | ripng_redistribute_delete(ripng, ZEBRA_ROUTE_CONNECT, |
399 | RIPNG_ROUTE_INTERFACE, &address, | |
400 | ifc->ifp->ifindex); | |
a94434b6 | 401 | } |
402 | ||
121f9dee | 403 | int ripng_interface_address_delete(ZAPI_CALLBACK_ARGS) |
718e3744 | 404 | { |
d62a17ae | 405 | struct connected *ifc; |
406 | struct prefix *p; | |
d62a17ae | 407 | |
408 | ifc = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, | |
409 | zclient->ibuf, vrf_id); | |
410 | ||
411 | if (ifc) { | |
412 | p = ifc->address; | |
413 | ||
414 | if (p->family == AF_INET6) { | |
415 | if (IS_RIPNG_DEBUG_ZEBRA) | |
416 | zlog_debug( | |
e0259674 DS |
417 | "RIPng connected address %pFX delete", |
418 | p); | |
d62a17ae | 419 | |
f89c0aed | 420 | /* Check whether this prefix needs to be removed. */ |
d62a17ae | 421 | ripng_apply_address_del(ifc); |
422 | } | |
721c0857 | 423 | connected_free(&ifc); |
718e3744 | 424 | } |
718e3744 | 425 | |
d62a17ae | 426 | return 0; |
718e3744 | 427 | } |
6b0655a2 | 428 | |
718e3744 | 429 | /* Lookup RIPng enable network. */ |
f89c0aed | 430 | /* Check whether the interface has at least a connected prefix that |
29b94d58 | 431 | * is within the ripng->enable_network table. */ |
d62a17ae | 432 | static int ripng_enable_network_lookup_if(struct interface *ifp) |
718e3744 | 433 | { |
5c84b9a5 RW |
434 | struct ripng_interface *ri = ifp->info; |
435 | struct ripng *ripng = ri->ripng; | |
d62a17ae | 436 | struct listnode *node; |
437 | struct connected *connected; | |
438 | struct prefix_ipv6 address; | |
439 | ||
29b94d58 RW |
440 | if (!ripng) |
441 | return -1; | |
442 | ||
d62a17ae | 443 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { |
444 | struct prefix *p; | |
fe08ba7e | 445 | struct agg_node *n; |
d62a17ae | 446 | |
447 | p = connected->address; | |
448 | ||
449 | if (p->family == AF_INET6) { | |
450 | address.family = AF_INET6; | |
451 | address.prefix = p->u.prefix6; | |
452 | address.prefixlen = IPV6_MAX_BITLEN; | |
453 | ||
29b94d58 | 454 | n = agg_node_match(ripng->enable_network, |
fe08ba7e | 455 | (struct prefix *)&address); |
ee6f7757 | 456 | if (n) { |
fe08ba7e | 457 | agg_unlock_node(n); |
d62a17ae | 458 | return 1; |
459 | } | |
460 | } | |
461 | } | |
462 | return -1; | |
718e3744 | 463 | } |
464 | ||
f89c0aed | 465 | /* Check whether connected is within the ripng->enable_network table. */ |
d62a17ae | 466 | static int ripng_enable_network_lookup2(struct connected *connected) |
a94434b6 | 467 | { |
5c84b9a5 RW |
468 | struct ripng_interface *ri = connected->ifp->info; |
469 | struct ripng *ripng = ri->ripng; | |
d62a17ae | 470 | struct prefix_ipv6 address; |
471 | struct prefix *p; | |
a94434b6 | 472 | |
29b94d58 RW |
473 | if (!ripng) |
474 | return -1; | |
475 | ||
d62a17ae | 476 | p = connected->address; |
a94434b6 | 477 | |
d62a17ae | 478 | if (p->family == AF_INET6) { |
fe08ba7e | 479 | struct agg_node *node; |
a94434b6 | 480 | |
d62a17ae | 481 | address.family = p->family; |
482 | address.prefix = p->u.prefix6; | |
483 | address.prefixlen = IPV6_MAX_BITLEN; | |
a94434b6 | 484 | |
d62a17ae | 485 | /* LPM on p->family, p->u.prefix6/IPV6_MAX_BITLEN within |
29b94d58 RW |
486 | * ripng->enable_network */ |
487 | node = agg_node_match(ripng->enable_network, | |
fe08ba7e | 488 | (struct prefix *)&address); |
a94434b6 | 489 | |
d62a17ae | 490 | if (node) { |
fe08ba7e | 491 | agg_unlock_node(node); |
d62a17ae | 492 | return 1; |
493 | } | |
494 | } | |
a94434b6 | 495 | |
d62a17ae | 496 | return -1; |
a94434b6 | 497 | } |
498 | ||
718e3744 | 499 | /* Add RIPng enable network. */ |
5c84b9a5 | 500 | int ripng_enable_network_add(struct ripng *ripng, struct prefix *p) |
718e3744 | 501 | { |
fe08ba7e | 502 | struct agg_node *node; |
718e3744 | 503 | |
29b94d58 | 504 | node = agg_node_get(ripng->enable_network, p); |
718e3744 | 505 | |
d62a17ae | 506 | if (node->info) { |
fe08ba7e | 507 | agg_unlock_node(node); |
cc48702b | 508 | return NB_ERR_INCONSISTENCY; |
d62a17ae | 509 | } else |
510 | node->info = (void *)1; | |
718e3744 | 511 | |
d62a17ae | 512 | /* XXX: One should find a better solution than a generic one */ |
5c84b9a5 | 513 | ripng_enable_apply_all(ripng); |
a94434b6 | 514 | |
cc48702b | 515 | return NB_OK; |
718e3744 | 516 | } |
517 | ||
518 | /* Delete RIPng enable network. */ | |
5c84b9a5 | 519 | int ripng_enable_network_delete(struct ripng *ripng, struct prefix *p) |
718e3744 | 520 | { |
fe08ba7e | 521 | struct agg_node *node; |
718e3744 | 522 | |
29b94d58 | 523 | node = agg_node_lookup(ripng->enable_network, p); |
d62a17ae | 524 | if (node) { |
525 | node->info = NULL; | |
718e3744 | 526 | |
d62a17ae | 527 | /* Unlock info lock. */ |
fe08ba7e | 528 | agg_unlock_node(node); |
718e3744 | 529 | |
d62a17ae | 530 | /* Unlock lookup lock. */ |
fe08ba7e | 531 | agg_unlock_node(node); |
718e3744 | 532 | |
cc48702b | 533 | return NB_OK; |
d62a17ae | 534 | } |
cc48702b RW |
535 | |
536 | return NB_ERR_INCONSISTENCY; | |
718e3744 | 537 | } |
538 | ||
539 | /* Lookup function. */ | |
5c84b9a5 | 540 | static int ripng_enable_if_lookup(struct ripng *ripng, const char *ifname) |
718e3744 | 541 | { |
d62a17ae | 542 | unsigned int i; |
543 | char *str; | |
544 | ||
b0ba762f RW |
545 | if (!ripng) |
546 | return -1; | |
547 | ||
548 | for (i = 0; i < vector_active(ripng->enable_if); i++) | |
549 | if ((str = vector_slot(ripng->enable_if, i)) != NULL) | |
d62a17ae | 550 | if (strcmp(str, ifname) == 0) |
551 | return i; | |
552 | return -1; | |
718e3744 | 553 | } |
554 | ||
5c84b9a5 | 555 | int ripng_enable_if_add(struct ripng *ripng, const char *ifname) |
718e3744 | 556 | { |
d62a17ae | 557 | int ret; |
718e3744 | 558 | |
5c84b9a5 | 559 | ret = ripng_enable_if_lookup(ripng, ifname); |
d62a17ae | 560 | if (ret >= 0) |
cc48702b | 561 | return NB_ERR_INCONSISTENCY; |
718e3744 | 562 | |
b0ba762f | 563 | vector_set(ripng->enable_if, strdup(ifname)); |
718e3744 | 564 | |
5c84b9a5 | 565 | ripng_enable_apply_all(ripng); |
a94434b6 | 566 | |
cc48702b | 567 | return NB_OK; |
718e3744 | 568 | } |
569 | ||
5c84b9a5 | 570 | int ripng_enable_if_delete(struct ripng *ripng, const char *ifname) |
718e3744 | 571 | { |
d62a17ae | 572 | int index; |
573 | char *str; | |
718e3744 | 574 | |
5c84b9a5 | 575 | index = ripng_enable_if_lookup(ripng, ifname); |
d62a17ae | 576 | if (index < 0) |
cc48702b | 577 | return NB_ERR_INCONSISTENCY; |
718e3744 | 578 | |
b0ba762f | 579 | str = vector_slot(ripng->enable_if, index); |
d62a17ae | 580 | free(str); |
b0ba762f | 581 | vector_unset(ripng->enable_if, index); |
718e3744 | 582 | |
5c84b9a5 | 583 | ripng_enable_apply_all(ripng); |
a94434b6 | 584 | |
cc48702b | 585 | return NB_OK; |
718e3744 | 586 | } |
587 | ||
588 | /* Wake up interface. */ | |
e6685141 | 589 | static void ripng_interface_wakeup(struct event *t) |
718e3744 | 590 | { |
d62a17ae | 591 | struct interface *ifp; |
592 | struct ripng_interface *ri; | |
718e3744 | 593 | |
d62a17ae | 594 | /* Get interface. */ |
e16d030c | 595 | ifp = EVENT_ARG(t); |
718e3744 | 596 | |
d62a17ae | 597 | ri = ifp->info; |
718e3744 | 598 | |
d62a17ae | 599 | /* Join to multicast group. */ |
5c84b9a5 | 600 | if (ripng_multicast_join(ifp, ri->ripng->sock) < 0) { |
450971aa | 601 | flog_err_sys(EC_LIB_SOCKET, |
09c866e3 QY |
602 | "multicast join failed, interface %s not running", |
603 | ifp->name); | |
cc9f21da | 604 | return; |
d62a17ae | 605 | } |
606 | ||
607 | /* Set running flag. */ | |
608 | ri->running = 1; | |
718e3744 | 609 | |
d62a17ae | 610 | /* Send RIP request to the interface. */ |
611 | ripng_request(ifp); | |
718e3744 | 612 | } |
613 | ||
d62a17ae | 614 | static void ripng_connect_set(struct interface *ifp, int set) |
a94434b6 | 615 | { |
5c84b9a5 RW |
616 | struct ripng_interface *ri = ifp->info; |
617 | struct ripng *ripng = ri->ripng; | |
d62a17ae | 618 | struct listnode *node, *nnode; |
619 | struct connected *connected; | |
620 | struct prefix_ipv6 address; | |
621 | ||
622 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) { | |
623 | struct prefix *p; | |
624 | p = connected->address; | |
625 | ||
626 | if (p->family != AF_INET6) | |
627 | continue; | |
628 | ||
629 | address.family = AF_INET6; | |
630 | address.prefix = p->u.prefix6; | |
631 | address.prefixlen = p->prefixlen; | |
632 | apply_mask_ipv6(&address); | |
633 | ||
634 | if (set) { | |
f89c0aed | 635 | /* Check once more whether this prefix is within a |
d62a17ae | 636 | * "network IF_OR_PREF" one */ |
5c84b9a5 RW |
637 | if ((ripng_enable_if_lookup(ripng, connected->ifp->name) |
638 | >= 0) | |
d62a17ae | 639 | || (ripng_enable_network_lookup2(connected) >= 0)) |
640 | ripng_redistribute_add( | |
5c84b9a5 | 641 | ripng, ZEBRA_ROUTE_CONNECT, |
d62a17ae | 642 | RIPNG_ROUTE_INTERFACE, &address, |
643 | connected->ifp->ifindex, NULL, 0); | |
644 | } else { | |
5c84b9a5 RW |
645 | ripng_redistribute_delete(ripng, ZEBRA_ROUTE_CONNECT, |
646 | RIPNG_ROUTE_INTERFACE, | |
647 | &address, | |
648 | connected->ifp->ifindex); | |
649 | if (ripng_redistribute_check(ripng, | |
650 | ZEBRA_ROUTE_CONNECT)) | |
d62a17ae | 651 | ripng_redistribute_add( |
5c84b9a5 | 652 | ripng, ZEBRA_ROUTE_CONNECT, |
d62a17ae | 653 | RIPNG_ROUTE_REDISTRIBUTE, &address, |
654 | connected->ifp->ifindex, NULL, 0); | |
655 | } | |
656 | } | |
a94434b6 | 657 | } |
658 | ||
718e3744 | 659 | /* Check RIPng is enabed on this interface. */ |
d62a17ae | 660 | void ripng_enable_apply(struct interface *ifp) |
718e3744 | 661 | { |
d62a17ae | 662 | int ret; |
663 | struct ripng_interface *ri = NULL; | |
664 | ||
665 | /* Check interface. */ | |
666 | if (!if_is_up(ifp)) | |
667 | return; | |
668 | ||
669 | ri = ifp->info; | |
670 | ||
671 | /* Is this interface a candidate for RIPng ? */ | |
672 | ret = ripng_enable_network_lookup_if(ifp); | |
673 | ||
674 | /* If the interface is matched. */ | |
675 | if (ret > 0) | |
676 | ri->enable_network = 1; | |
677 | else | |
678 | ri->enable_network = 0; | |
679 | ||
680 | /* Check interface name configuration. */ | |
5c84b9a5 | 681 | ret = ripng_enable_if_lookup(ri->ripng, ifp->name); |
d62a17ae | 682 | if (ret >= 0) |
683 | ri->enable_interface = 1; | |
684 | else | |
685 | ri->enable_interface = 0; | |
686 | ||
687 | /* any candidate interface MUST have a link-local IPv6 address */ | |
688 | if ((!ripng_if_ipv6_lladdress_check(ifp)) | |
689 | && (ri->enable_network || ri->enable_interface)) { | |
690 | ri->enable_network = 0; | |
691 | ri->enable_interface = 0; | |
692 | zlog_warn("Interface %s does not have any link-local address", | |
693 | ifp->name); | |
694 | } | |
695 | ||
696 | /* Update running status of the interface. */ | |
697 | if (ri->enable_network || ri->enable_interface) { | |
698 | zlog_info("RIPng INTERFACE ON %s", ifp->name); | |
699 | ||
700 | /* Add interface wake up thread. */ | |
907a2395 DS |
701 | event_add_timer(master, ripng_interface_wakeup, ifp, 1, |
702 | &ri->t_wakeup); | |
d62a17ae | 703 | |
704 | ripng_connect_set(ifp, 1); | |
705 | } else { | |
706 | if (ri->running) { | |
707 | /* Might as well clean up the route table as well | |
708 | * ripng_if_down sets to 0 ri->running, and displays | |
709 | *"turn off %s" | |
710 | **/ | |
711 | ripng_if_down(ifp); | |
712 | ||
713 | ripng_connect_set(ifp, 0); | |
714 | } | |
718e3744 | 715 | } |
718e3744 | 716 | } |
717 | ||
718 | /* Set distribute list to all interfaces. */ | |
5c84b9a5 | 719 | static void ripng_enable_apply_all(struct ripng *ripng) |
718e3744 | 720 | { |
d62a17ae | 721 | struct interface *ifp; |
718e3744 | 722 | |
dde7b15b | 723 | FOR_ALL_INTERFACES (ripng->vrf, ifp) |
d62a17ae | 724 | ripng_enable_apply(ifp); |
718e3744 | 725 | } |
6b0655a2 | 726 | |
a94434b6 | 727 | /* Clear all network and neighbor configuration */ |
5c84b9a5 | 728 | void ripng_clean_network(struct ripng *ripng) |
a94434b6 | 729 | { |
d62a17ae | 730 | unsigned int i; |
731 | char *str; | |
fe08ba7e | 732 | struct agg_node *rn; |
d62a17ae | 733 | |
29b94d58 RW |
734 | /* ripng->enable_network */ |
735 | for (rn = agg_route_top(ripng->enable_network); rn; | |
fe08ba7e | 736 | rn = agg_route_next(rn)) |
d62a17ae | 737 | if (rn->info) { |
738 | rn->info = NULL; | |
fe08ba7e | 739 | agg_unlock_node(rn); |
d62a17ae | 740 | } |
741 | ||
b0ba762f RW |
742 | /* ripng->enable_if */ |
743 | for (i = 0; i < vector_active(ripng->enable_if); i++) | |
744 | if ((str = vector_slot(ripng->enable_if, i)) != NULL) { | |
d62a17ae | 745 | free(str); |
b0ba762f | 746 | vector_slot(ripng->enable_if, i) = NULL; |
d62a17ae | 747 | } |
a94434b6 | 748 | } |
6b0655a2 | 749 | |
718e3744 | 750 | /* Utility function for looking up passive interface settings. */ |
5c84b9a5 RW |
751 | static int ripng_passive_interface_lookup(struct ripng *ripng, |
752 | const char *ifname) | |
718e3744 | 753 | { |
d62a17ae | 754 | unsigned int i; |
755 | char *str; | |
756 | ||
0c32404f RW |
757 | for (i = 0; i < vector_active(ripng->passive_interface); i++) |
758 | if ((str = vector_slot(ripng->passive_interface, i)) != NULL) | |
d62a17ae | 759 | if (strcmp(str, ifname) == 0) |
760 | return i; | |
761 | return -1; | |
718e3744 | 762 | } |
763 | ||
d62a17ae | 764 | void ripng_passive_interface_apply(struct interface *ifp) |
718e3744 | 765 | { |
d62a17ae | 766 | int ret; |
767 | struct ripng_interface *ri; | |
5c84b9a5 | 768 | struct ripng *ripng; |
718e3744 | 769 | |
d62a17ae | 770 | ri = ifp->info; |
5c84b9a5 RW |
771 | ripng = ri->ripng; |
772 | if (!ripng) | |
773 | return; | |
718e3744 | 774 | |
5c84b9a5 | 775 | ret = ripng_passive_interface_lookup(ripng, ifp->name); |
d62a17ae | 776 | if (ret < 0) |
777 | ri->passive = 0; | |
778 | else | |
779 | ri->passive = 1; | |
718e3744 | 780 | } |
781 | ||
5c84b9a5 | 782 | static void ripng_passive_interface_apply_all(struct ripng *ripng) |
718e3744 | 783 | { |
d62a17ae | 784 | struct interface *ifp; |
718e3744 | 785 | |
dde7b15b | 786 | FOR_ALL_INTERFACES (ripng->vrf, ifp) |
d62a17ae | 787 | ripng_passive_interface_apply(ifp); |
718e3744 | 788 | } |
789 | ||
790 | /* Passive interface. */ | |
5c84b9a5 | 791 | int ripng_passive_interface_set(struct ripng *ripng, const char *ifname) |
718e3744 | 792 | { |
5c84b9a5 | 793 | if (ripng_passive_interface_lookup(ripng, ifname) >= 0) |
22e8c7ae | 794 | return NB_ERR_INCONSISTENCY; |
718e3744 | 795 | |
0c32404f | 796 | vector_set(ripng->passive_interface, strdup(ifname)); |
718e3744 | 797 | |
5c84b9a5 | 798 | ripng_passive_interface_apply_all(ripng); |
718e3744 | 799 | |
22e8c7ae | 800 | return NB_OK; |
718e3744 | 801 | } |
802 | ||
5c84b9a5 | 803 | int ripng_passive_interface_unset(struct ripng *ripng, const char *ifname) |
718e3744 | 804 | { |
d62a17ae | 805 | int i; |
806 | char *str; | |
718e3744 | 807 | |
5c84b9a5 | 808 | i = ripng_passive_interface_lookup(ripng, ifname); |
d62a17ae | 809 | if (i < 0) |
22e8c7ae | 810 | return NB_ERR_INCONSISTENCY; |
718e3744 | 811 | |
0c32404f | 812 | str = vector_slot(ripng->passive_interface, i); |
d62a17ae | 813 | free(str); |
0c32404f | 814 | vector_unset(ripng->passive_interface, i); |
718e3744 | 815 | |
5c84b9a5 | 816 | ripng_passive_interface_apply_all(ripng); |
718e3744 | 817 | |
22e8c7ae | 818 | return NB_OK; |
718e3744 | 819 | } |
820 | ||
821 | /* Free all configured RIP passive-interface settings. */ | |
5c84b9a5 | 822 | void ripng_passive_interface_clean(struct ripng *ripng) |
718e3744 | 823 | { |
d62a17ae | 824 | unsigned int i; |
825 | char *str; | |
826 | ||
0c32404f RW |
827 | for (i = 0; i < vector_active(ripng->passive_interface); i++) |
828 | if ((str = vector_slot(ripng->passive_interface, i)) != NULL) { | |
d62a17ae | 829 | free(str); |
0c32404f | 830 | vector_slot(ripng->passive_interface, i) = NULL; |
d62a17ae | 831 | } |
5c84b9a5 | 832 | ripng_passive_interface_apply_all(ripng); |
718e3744 | 833 | } |
834 | ||
835 | /* Write RIPng enable network and interface to the vty. */ | |
5c84b9a5 | 836 | int ripng_network_write(struct vty *vty, struct ripng *ripng) |
718e3744 | 837 | { |
d62a17ae | 838 | unsigned int i; |
839 | const char *ifname; | |
fe08ba7e | 840 | struct agg_node *node; |
d62a17ae | 841 | |
842 | /* Write enable network. */ | |
29b94d58 | 843 | for (node = agg_route_top(ripng->enable_network); node; |
fe08ba7e | 844 | node = agg_route_next(node)) |
26a3ffd6 DS |
845 | if (node->info) |
846 | vty_out(vty, " %pRN\n", node); | |
d62a17ae | 847 | |
848 | /* Write enable interface. */ | |
b0ba762f RW |
849 | for (i = 0; i < vector_active(ripng->enable_if); i++) |
850 | if ((ifname = vector_slot(ripng->enable_if, i)) != NULL) | |
22e8c7ae | 851 | vty_out(vty, " %s\n", ifname); |
d62a17ae | 852 | |
853 | return 0; | |
718e3744 | 854 | } |
855 | ||
d62a17ae | 856 | static struct ripng_interface *ri_new(void) |
718e3744 | 857 | { |
d62a17ae | 858 | struct ripng_interface *ri; |
5c84b9a5 | 859 | |
eaf58ba9 | 860 | ri = XCALLOC(MTYPE_RIPNG_IF, sizeof(struct ripng_interface)); |
a94434b6 | 861 | |
d62a17ae | 862 | /* Set default split-horizon behavior. If the interface is Frame |
863 | Relay or SMDS is enabled, the default value for split-horizon is | |
864 | off. But currently Zebra does detect Frame Relay or SMDS | |
865 | interface. So all interface is set to split horizon. */ | |
d406db4c RW |
866 | ri->split_horizon = |
867 | yang_get_default_enum("%s/split-horizon", RIPNG_IFACE); | |
a94434b6 | 868 | |
d62a17ae | 869 | return ri; |
718e3744 | 870 | } |
871 | ||
dde7b15b RW |
872 | void ripng_interface_sync(struct interface *ifp) |
873 | { | |
096f7609 | 874 | struct ripng_interface *ri; |
dde7b15b | 875 | |
096f7609 IR |
876 | ri = ifp->info; |
877 | if (ri) | |
878 | ri->ripng = ifp->vrf->info; | |
dde7b15b RW |
879 | } |
880 | ||
d62a17ae | 881 | static int ripng_if_new_hook(struct interface *ifp) |
718e3744 | 882 | { |
d62a17ae | 883 | ifp->info = ri_new(); |
dde7b15b RW |
884 | ripng_interface_sync(ifp); |
885 | ||
d62a17ae | 886 | return 0; |
718e3744 | 887 | } |
888 | ||
a94434b6 | 889 | /* Called when interface structure deleted. */ |
d62a17ae | 890 | static int ripng_if_delete_hook(struct interface *ifp) |
a94434b6 | 891 | { |
eaf58ba9 | 892 | XFREE(MTYPE_RIPNG_IF, ifp->info); |
d62a17ae | 893 | return 0; |
a94434b6 | 894 | } |
895 | ||
718e3744 | 896 | /* Initialization of interface. */ |
4d762f26 | 897 | void ripng_if_init(void) |
718e3744 | 898 | { |
d62a17ae | 899 | /* Interface initialize. */ |
ce19a04a DL |
900 | hook_register_prio(if_add, 0, ripng_if_new_hook); |
901 | hook_register_prio(if_del, 0, ripng_if_delete_hook); | |
718e3744 | 902 | |
d62a17ae | 903 | /* Install interface node. */ |
104fd767 | 904 | if_cmd_init_default(); |
138c5a74 DS |
905 | if_zapi_callbacks(ripng_ifp_create, ripng_ifp_up, |
906 | ripng_ifp_down, ripng_ifp_destroy); | |
718e3744 | 907 | } |