]>
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 | /* RIPng enable interface vector. */ |
450 | vector ripng_enable_if; | |
451 | ||
452 | /* RIPng enable network table. */ | |
fe08ba7e | 453 | struct agg_table *ripng_enable_network; |
718e3744 | 454 | |
455 | /* Lookup RIPng enable network. */ | |
a94434b6 | 456 | /* Check wether the interface has at least a connected prefix that |
457 | * is within the ripng_enable_network table. */ | |
d62a17ae | 458 | static int ripng_enable_network_lookup_if(struct interface *ifp) |
718e3744 | 459 | { |
d62a17ae | 460 | struct listnode *node; |
461 | struct connected *connected; | |
462 | struct prefix_ipv6 address; | |
463 | ||
464 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, node, connected)) { | |
465 | struct prefix *p; | |
fe08ba7e | 466 | struct agg_node *n; |
d62a17ae | 467 | |
468 | p = connected->address; | |
469 | ||
470 | if (p->family == AF_INET6) { | |
471 | address.family = AF_INET6; | |
472 | address.prefix = p->u.prefix6; | |
473 | address.prefixlen = IPV6_MAX_BITLEN; | |
474 | ||
fe08ba7e DS |
475 | n = agg_node_match(ripng_enable_network, |
476 | (struct prefix *)&address); | |
ee6f7757 | 477 | if (n) { |
fe08ba7e | 478 | agg_unlock_node(n); |
d62a17ae | 479 | return 1; |
480 | } | |
481 | } | |
482 | } | |
483 | return -1; | |
718e3744 | 484 | } |
485 | ||
a94434b6 | 486 | /* Check wether connected is within the ripng_enable_network table. */ |
d62a17ae | 487 | static int ripng_enable_network_lookup2(struct connected *connected) |
a94434b6 | 488 | { |
d62a17ae | 489 | struct prefix_ipv6 address; |
490 | struct prefix *p; | |
a94434b6 | 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 |
502 | * ripng_enable_network */ | |
fe08ba7e DS |
503 | node = agg_node_match(ripng_enable_network, |
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 | |
fe08ba7e | 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 | |
fe08ba7e | 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 | ||
561 | for (i = 0; i < vector_active(ripng_enable_if); i++) | |
562 | if ((str = vector_slot(ripng_enable_if, i)) != NULL) | |
563 | if (strcmp(str, ifname) == 0) | |
564 | return i; | |
565 | return -1; | |
718e3744 | 566 | } |
567 | ||
568 | /* Add interface to ripng_enable_if. */ | |
cc48702b | 569 | int ripng_enable_if_add(const char *ifname) |
718e3744 | 570 | { |
d62a17ae | 571 | int ret; |
718e3744 | 572 | |
d62a17ae | 573 | ret = ripng_enable_if_lookup(ifname); |
574 | if (ret >= 0) | |
cc48702b | 575 | return NB_ERR_INCONSISTENCY; |
718e3744 | 576 | |
d62a17ae | 577 | vector_set(ripng_enable_if, strdup(ifname)); |
718e3744 | 578 | |
d62a17ae | 579 | ripng_enable_apply_all(); |
a94434b6 | 580 | |
cc48702b | 581 | return NB_OK; |
718e3744 | 582 | } |
583 | ||
584 | /* Delete interface from ripng_enable_if. */ | |
cc48702b | 585 | int ripng_enable_if_delete(const char *ifname) |
718e3744 | 586 | { |
d62a17ae | 587 | int index; |
588 | char *str; | |
718e3744 | 589 | |
d62a17ae | 590 | index = ripng_enable_if_lookup(ifname); |
591 | if (index < 0) | |
cc48702b | 592 | return NB_ERR_INCONSISTENCY; |
718e3744 | 593 | |
d62a17ae | 594 | str = vector_slot(ripng_enable_if, index); |
595 | free(str); | |
596 | vector_unset(ripng_enable_if, index); | |
718e3744 | 597 | |
d62a17ae | 598 | ripng_enable_apply_all(); |
a94434b6 | 599 | |
cc48702b | 600 | return NB_OK; |
718e3744 | 601 | } |
602 | ||
603 | /* Wake up interface. */ | |
d62a17ae | 604 | static int ripng_interface_wakeup(struct thread *t) |
718e3744 | 605 | { |
d62a17ae | 606 | struct interface *ifp; |
607 | struct ripng_interface *ri; | |
718e3744 | 608 | |
d62a17ae | 609 | /* Get interface. */ |
610 | ifp = THREAD_ARG(t); | |
718e3744 | 611 | |
d62a17ae | 612 | ri = ifp->info; |
613 | ri->t_wakeup = NULL; | |
718e3744 | 614 | |
d62a17ae | 615 | /* Join to multicast group. */ |
616 | if (ripng_multicast_join(ifp) < 0) { | |
450971aa | 617 | flog_err_sys(EC_LIB_SOCKET, |
09c866e3 QY |
618 | "multicast join failed, interface %s not running", |
619 | ifp->name); | |
d62a17ae | 620 | return 0; |
621 | } | |
622 | ||
623 | /* Set running flag. */ | |
624 | ri->running = 1; | |
718e3744 | 625 | |
d62a17ae | 626 | /* Send RIP request to the interface. */ |
627 | ripng_request(ifp); | |
718e3744 | 628 | |
d62a17ae | 629 | return 0; |
718e3744 | 630 | } |
631 | ||
d62a17ae | 632 | static void ripng_connect_set(struct interface *ifp, int set) |
a94434b6 | 633 | { |
d62a17ae | 634 | struct listnode *node, *nnode; |
635 | struct connected *connected; | |
636 | struct prefix_ipv6 address; | |
637 | ||
638 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) { | |
639 | struct prefix *p; | |
640 | p = connected->address; | |
641 | ||
642 | if (p->family != AF_INET6) | |
643 | continue; | |
644 | ||
645 | address.family = AF_INET6; | |
646 | address.prefix = p->u.prefix6; | |
647 | address.prefixlen = p->prefixlen; | |
648 | apply_mask_ipv6(&address); | |
649 | ||
650 | if (set) { | |
651 | /* Check once more wether this prefix is within a | |
652 | * "network IF_OR_PREF" one */ | |
653 | if ((ripng_enable_if_lookup(connected->ifp->name) >= 0) | |
654 | || (ripng_enable_network_lookup2(connected) >= 0)) | |
655 | ripng_redistribute_add( | |
656 | ZEBRA_ROUTE_CONNECT, | |
657 | RIPNG_ROUTE_INTERFACE, &address, | |
658 | connected->ifp->ifindex, NULL, 0); | |
659 | } else { | |
660 | ripng_redistribute_delete( | |
661 | ZEBRA_ROUTE_CONNECT, RIPNG_ROUTE_INTERFACE, | |
662 | &address, connected->ifp->ifindex); | |
663 | if (ripng_redistribute_check(ZEBRA_ROUTE_CONNECT)) | |
664 | ripng_redistribute_add( | |
665 | ZEBRA_ROUTE_CONNECT, | |
666 | RIPNG_ROUTE_REDISTRIBUTE, &address, | |
667 | connected->ifp->ifindex, NULL, 0); | |
668 | } | |
669 | } | |
a94434b6 | 670 | } |
671 | ||
718e3744 | 672 | /* Check RIPng is enabed on this interface. */ |
d62a17ae | 673 | void ripng_enable_apply(struct interface *ifp) |
718e3744 | 674 | { |
d62a17ae | 675 | int ret; |
676 | struct ripng_interface *ri = NULL; | |
677 | ||
678 | /* Check interface. */ | |
679 | if (!if_is_up(ifp)) | |
680 | return; | |
681 | ||
682 | ri = ifp->info; | |
683 | ||
684 | /* Is this interface a candidate for RIPng ? */ | |
685 | ret = ripng_enable_network_lookup_if(ifp); | |
686 | ||
687 | /* If the interface is matched. */ | |
688 | if (ret > 0) | |
689 | ri->enable_network = 1; | |
690 | else | |
691 | ri->enable_network = 0; | |
692 | ||
693 | /* Check interface name configuration. */ | |
694 | ret = ripng_enable_if_lookup(ifp->name); | |
695 | if (ret >= 0) | |
696 | ri->enable_interface = 1; | |
697 | else | |
698 | ri->enable_interface = 0; | |
699 | ||
700 | /* any candidate interface MUST have a link-local IPv6 address */ | |
701 | if ((!ripng_if_ipv6_lladdress_check(ifp)) | |
702 | && (ri->enable_network || ri->enable_interface)) { | |
703 | ri->enable_network = 0; | |
704 | ri->enable_interface = 0; | |
705 | zlog_warn("Interface %s does not have any link-local address", | |
706 | ifp->name); | |
707 | } | |
708 | ||
709 | /* Update running status of the interface. */ | |
710 | if (ri->enable_network || ri->enable_interface) { | |
711 | zlog_info("RIPng INTERFACE ON %s", ifp->name); | |
712 | ||
713 | /* Add interface wake up thread. */ | |
714 | thread_add_timer(master, ripng_interface_wakeup, ifp, 1, | |
715 | &ri->t_wakeup); | |
716 | ||
717 | ripng_connect_set(ifp, 1); | |
718 | } else { | |
719 | if (ri->running) { | |
720 | /* Might as well clean up the route table as well | |
721 | * ripng_if_down sets to 0 ri->running, and displays | |
722 | *"turn off %s" | |
723 | **/ | |
724 | ripng_if_down(ifp); | |
725 | ||
726 | ripng_connect_set(ifp, 0); | |
727 | } | |
718e3744 | 728 | } |
718e3744 | 729 | } |
730 | ||
731 | /* Set distribute list to all interfaces. */ | |
d62a17ae | 732 | static void ripng_enable_apply_all(void) |
718e3744 | 733 | { |
f4e14fdb | 734 | struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); |
d62a17ae | 735 | struct interface *ifp; |
718e3744 | 736 | |
451fda4f | 737 | FOR_ALL_INTERFACES (vrf, ifp) |
d62a17ae | 738 | ripng_enable_apply(ifp); |
718e3744 | 739 | } |
6b0655a2 | 740 | |
a94434b6 | 741 | /* Clear all network and neighbor configuration */ |
d62a17ae | 742 | void ripng_clean_network() |
a94434b6 | 743 | { |
d62a17ae | 744 | unsigned int i; |
745 | char *str; | |
fe08ba7e | 746 | struct agg_node *rn; |
d62a17ae | 747 | |
748 | /* ripng_enable_network */ | |
fe08ba7e DS |
749 | for (rn = agg_route_top(ripng_enable_network); rn; |
750 | rn = agg_route_next(rn)) | |
d62a17ae | 751 | if (rn->info) { |
752 | rn->info = NULL; | |
fe08ba7e | 753 | agg_unlock_node(rn); |
d62a17ae | 754 | } |
755 | ||
756 | /* ripng_enable_if */ | |
757 | for (i = 0; i < vector_active(ripng_enable_if); i++) | |
758 | if ((str = vector_slot(ripng_enable_if, i)) != NULL) { | |
759 | free(str); | |
760 | vector_slot(ripng_enable_if, i) = NULL; | |
761 | } | |
a94434b6 | 762 | } |
6b0655a2 | 763 | |
718e3744 | 764 | /* Vector to store passive-interface name. */ |
765 | vector Vripng_passive_interface; | |
766 | ||
767 | /* Utility function for looking up passive interface settings. */ | |
d62a17ae | 768 | static int ripng_passive_interface_lookup(const char *ifname) |
718e3744 | 769 | { |
d62a17ae | 770 | unsigned int i; |
771 | char *str; | |
772 | ||
773 | for (i = 0; i < vector_active(Vripng_passive_interface); i++) | |
774 | if ((str = vector_slot(Vripng_passive_interface, i)) != NULL) | |
775 | if (strcmp(str, ifname) == 0) | |
776 | return i; | |
777 | return -1; | |
718e3744 | 778 | } |
779 | ||
d62a17ae | 780 | void ripng_passive_interface_apply(struct interface *ifp) |
718e3744 | 781 | { |
d62a17ae | 782 | int ret; |
783 | struct ripng_interface *ri; | |
718e3744 | 784 | |
d62a17ae | 785 | ri = ifp->info; |
718e3744 | 786 | |
d62a17ae | 787 | ret = ripng_passive_interface_lookup(ifp->name); |
788 | if (ret < 0) | |
789 | ri->passive = 0; | |
790 | else | |
791 | ri->passive = 1; | |
718e3744 | 792 | } |
793 | ||
d62a17ae | 794 | static void ripng_passive_interface_apply_all(void) |
718e3744 | 795 | { |
f4e14fdb | 796 | struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); |
d62a17ae | 797 | struct interface *ifp; |
718e3744 | 798 | |
451fda4f | 799 | FOR_ALL_INTERFACES (vrf, ifp) |
d62a17ae | 800 | ripng_passive_interface_apply(ifp); |
718e3744 | 801 | } |
802 | ||
803 | /* Passive interface. */ | |
22e8c7ae | 804 | int ripng_passive_interface_set(const char *ifname) |
718e3744 | 805 | { |
d62a17ae | 806 | if (ripng_passive_interface_lookup(ifname) >= 0) |
22e8c7ae | 807 | return NB_ERR_INCONSISTENCY; |
718e3744 | 808 | |
d62a17ae | 809 | vector_set(Vripng_passive_interface, strdup(ifname)); |
718e3744 | 810 | |
d62a17ae | 811 | ripng_passive_interface_apply_all(); |
718e3744 | 812 | |
22e8c7ae | 813 | return NB_OK; |
718e3744 | 814 | } |
815 | ||
22e8c7ae | 816 | int ripng_passive_interface_unset(const char *ifname) |
718e3744 | 817 | { |
d62a17ae | 818 | int i; |
819 | char *str; | |
718e3744 | 820 | |
d62a17ae | 821 | i = ripng_passive_interface_lookup(ifname); |
822 | if (i < 0) | |
22e8c7ae | 823 | return NB_ERR_INCONSISTENCY; |
718e3744 | 824 | |
d62a17ae | 825 | str = vector_slot(Vripng_passive_interface, i); |
826 | free(str); | |
827 | vector_unset(Vripng_passive_interface, i); | |
718e3744 | 828 | |
d62a17ae | 829 | ripng_passive_interface_apply_all(); |
718e3744 | 830 | |
22e8c7ae | 831 | return NB_OK; |
718e3744 | 832 | } |
833 | ||
834 | /* Free all configured RIP passive-interface settings. */ | |
d62a17ae | 835 | void ripng_passive_interface_clean(void) |
718e3744 | 836 | { |
d62a17ae | 837 | unsigned int i; |
838 | char *str; | |
839 | ||
840 | for (i = 0; i < vector_active(Vripng_passive_interface); i++) | |
841 | if ((str = vector_slot(Vripng_passive_interface, i)) != NULL) { | |
842 | free(str); | |
843 | vector_slot(Vripng_passive_interface, i) = NULL; | |
844 | } | |
845 | ripng_passive_interface_apply_all(); | |
718e3744 | 846 | } |
847 | ||
848 | /* Write RIPng enable network and interface to the vty. */ | |
22e8c7ae | 849 | int ripng_network_write(struct vty *vty) |
718e3744 | 850 | { |
d62a17ae | 851 | unsigned int i; |
852 | const char *ifname; | |
fe08ba7e | 853 | struct agg_node *node; |
d62a17ae | 854 | char buf[BUFSIZ]; |
855 | ||
856 | /* Write enable network. */ | |
fe08ba7e DS |
857 | for (node = agg_route_top(ripng_enable_network); node; |
858 | node = agg_route_next(node)) | |
d62a17ae | 859 | if (node->info) { |
860 | struct prefix *p = &node->p; | |
22e8c7ae | 861 | vty_out(vty, " %s/%d\n", |
d62a17ae | 862 | inet_ntop(p->family, &p->u.prefix, buf, BUFSIZ), |
863 | p->prefixlen); | |
864 | } | |
865 | ||
866 | /* Write enable interface. */ | |
867 | for (i = 0; i < vector_active(ripng_enable_if); i++) | |
868 | if ((ifname = vector_slot(ripng_enable_if, i)) != NULL) | |
22e8c7ae | 869 | vty_out(vty, " %s\n", ifname); |
d62a17ae | 870 | |
871 | return 0; | |
718e3744 | 872 | } |
873 | ||
d62a17ae | 874 | static struct ripng_interface *ri_new(void) |
718e3744 | 875 | { |
d62a17ae | 876 | struct ripng_interface *ri; |
877 | ri = XCALLOC(MTYPE_IF, sizeof(struct ripng_interface)); | |
a94434b6 | 878 | |
d62a17ae | 879 | /* Set default split-horizon behavior. If the interface is Frame |
880 | Relay or SMDS is enabled, the default value for split-horizon is | |
881 | off. But currently Zebra does detect Frame Relay or SMDS | |
882 | interface. So all interface is set to split horizon. */ | |
d406db4c RW |
883 | ri->split_horizon = |
884 | yang_get_default_enum("%s/split-horizon", RIPNG_IFACE); | |
a94434b6 | 885 | |
d62a17ae | 886 | return ri; |
718e3744 | 887 | } |
888 | ||
d62a17ae | 889 | static int ripng_if_new_hook(struct interface *ifp) |
718e3744 | 890 | { |
d62a17ae | 891 | ifp->info = ri_new(); |
892 | return 0; | |
718e3744 | 893 | } |
894 | ||
a94434b6 | 895 | /* Called when interface structure deleted. */ |
d62a17ae | 896 | static int ripng_if_delete_hook(struct interface *ifp) |
a94434b6 | 897 | { |
d62a17ae | 898 | XFREE(MTYPE_IF, ifp->info); |
899 | ifp->info = NULL; | |
900 | return 0; | |
a94434b6 | 901 | } |
902 | ||
718e3744 | 903 | /* Configuration write function for ripngd. */ |
d62a17ae | 904 | static int interface_config_write(struct vty *vty) |
718e3744 | 905 | { |
f4e14fdb | 906 | struct vrf *vrf = vrf_lookup_by_id(VRF_DEFAULT); |
d62a17ae | 907 | struct interface *ifp; |
d62a17ae | 908 | int write = 0; |
909 | ||
451fda4f | 910 | FOR_ALL_INTERFACES (vrf, ifp) { |
d406db4c | 911 | struct lyd_node *dnode; |
d62a17ae | 912 | |
d406db4c RW |
913 | dnode = yang_dnode_get( |
914 | running_config->dnode, | |
915 | "/frr-interface:lib/interface[name='%s'][vrf='%s']", | |
916 | ifp->name, vrf->name); | |
917 | if (dnode == NULL) | |
d62a17ae | 918 | continue; |
919 | ||
d406db4c RW |
920 | write = 1; |
921 | nb_cli_show_dnode_cmds(vty, dnode, false); | |
a94434b6 | 922 | } |
d406db4c | 923 | |
d62a17ae | 924 | return write; |
718e3744 | 925 | } |
926 | ||
927 | /* ripngd's interface node. */ | |
d62a17ae | 928 | static struct cmd_node interface_node = { |
929 | INTERFACE_NODE, "%s(config-if)# ", 1 /* VTYSH */ | |
718e3744 | 930 | }; |
931 | ||
932 | /* Initialization of interface. */ | |
d62a17ae | 933 | void ripng_if_init() |
718e3744 | 934 | { |
d62a17ae | 935 | /* Interface initialize. */ |
ce19a04a DL |
936 | hook_register_prio(if_add, 0, ripng_if_new_hook); |
937 | hook_register_prio(if_del, 0, ripng_if_delete_hook); | |
718e3744 | 938 | |
d62a17ae | 939 | /* RIPng enable network init. */ |
fe08ba7e | 940 | ripng_enable_network = agg_table_init(); |
718e3744 | 941 | |
d62a17ae | 942 | /* RIPng enable interface init. */ |
943 | ripng_enable_if = vector_init(1); | |
718e3744 | 944 | |
d62a17ae | 945 | /* RIPng passive interface. */ |
946 | Vripng_passive_interface = vector_init(1); | |
718e3744 | 947 | |
d62a17ae | 948 | /* Install interface node. */ |
949 | install_node(&interface_node, interface_config_write); | |
950 | if_cmd_init(); | |
718e3744 | 951 | } |