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