]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0-or-later | |
2 | /* Interface related function for RIP. | |
3 | * Copyright (C) 1997, 98 Kunihiro Ishiguro <kunihiro@zebra.org> | |
4 | */ | |
5 | ||
6 | #include <zebra.h> | |
7 | ||
8 | #include "command.h" | |
9 | #include "if.h" | |
10 | #include "sockunion.h" | |
11 | #include "prefix.h" | |
12 | #include "memory.h" | |
13 | #include "network.h" | |
14 | #include "table.h" | |
15 | #include "log.h" | |
16 | #include "stream.h" | |
17 | #include "frrevent.h" | |
18 | #include "zclient.h" | |
19 | #include "filter.h" | |
20 | #include "sockopt.h" | |
21 | #include "privs.h" | |
22 | #include "lib_errors.h" | |
23 | #include "northbound_cli.h" | |
24 | ||
25 | #include "zebra/connected.h" | |
26 | ||
27 | #include "ripd/ripd.h" | |
28 | #include "ripd/rip_debug.h" | |
29 | #include "ripd/rip_interface.h" | |
30 | ||
31 | DEFINE_MTYPE_STATIC(RIPD, RIP_INTERFACE, "RIP interface"); | |
32 | DEFINE_MTYPE(RIPD, RIP_INTERFACE_STRING, "RIP Interface String"); | |
33 | DEFINE_HOOK(rip_ifaddr_add, (struct connected * ifc), (ifc)); | |
34 | DEFINE_HOOK(rip_ifaddr_del, (struct connected * ifc), (ifc)); | |
35 | ||
36 | /* static prototypes */ | |
37 | static void rip_enable_apply(struct interface *); | |
38 | static void rip_passive_interface_apply(struct interface *); | |
39 | static int rip_if_down(struct interface *ifp); | |
40 | static int rip_enable_if_lookup(struct rip *rip, const char *ifname); | |
41 | static int rip_enable_network_lookup2(struct connected *connected); | |
42 | static void rip_enable_apply_all(struct rip *rip); | |
43 | ||
44 | const struct message ri_version_msg[] = {{RI_RIP_VERSION_1, "1"}, | |
45 | {RI_RIP_VERSION_2, "2"}, | |
46 | {RI_RIP_VERSION_1_AND_2, "1 2"}, | |
47 | {RI_RIP_VERSION_NONE, "none"}, | |
48 | {0}}; | |
49 | ||
50 | /* Join to the RIP version 2 multicast group. */ | |
51 | static int ipv4_multicast_join(int sock, struct in_addr group, | |
52 | struct in_addr ifa, ifindex_t ifindex) | |
53 | { | |
54 | int ret; | |
55 | ||
56 | ret = setsockopt_ipv4_multicast(sock, IP_ADD_MEMBERSHIP, ifa, | |
57 | group.s_addr, ifindex); | |
58 | ||
59 | if (ret < 0) | |
60 | zlog_info("can't setsockopt IP_ADD_MEMBERSHIP %s", | |
61 | safe_strerror(errno)); | |
62 | ||
63 | return ret; | |
64 | } | |
65 | ||
66 | /* Leave from the RIP version 2 multicast group. */ | |
67 | static int ipv4_multicast_leave(int sock, struct in_addr group, | |
68 | struct in_addr ifa, ifindex_t ifindex) | |
69 | { | |
70 | int ret; | |
71 | ||
72 | ret = setsockopt_ipv4_multicast(sock, IP_DROP_MEMBERSHIP, ifa, | |
73 | group.s_addr, ifindex); | |
74 | ||
75 | if (ret < 0) | |
76 | zlog_info("can't setsockopt IP_DROP_MEMBERSHIP"); | |
77 | ||
78 | return ret; | |
79 | } | |
80 | ||
81 | static void rip_interface_reset(struct rip_interface *); | |
82 | ||
83 | /* Allocate new RIP's interface configuration. */ | |
84 | static struct rip_interface *rip_interface_new(void) | |
85 | { | |
86 | struct rip_interface *ri; | |
87 | ||
88 | ri = XCALLOC(MTYPE_RIP_INTERFACE, sizeof(struct rip_interface)); | |
89 | ||
90 | rip_interface_reset(ri); | |
91 | ||
92 | return ri; | |
93 | } | |
94 | ||
95 | void rip_interface_multicast_set(int sock, struct connected *connected) | |
96 | { | |
97 | struct in_addr addr; | |
98 | ||
99 | assert(connected != NULL); | |
100 | ||
101 | addr = CONNECTED_ID(connected)->u.prefix4; | |
102 | ||
103 | if (setsockopt_ipv4_multicast_if(sock, addr, connected->ifp->ifindex) | |
104 | < 0) { | |
105 | zlog_warn( | |
106 | "Can't setsockopt IP_MULTICAST_IF on fd %d to ifindex %d for interface %s", | |
107 | sock, connected->ifp->ifindex, connected->ifp->name); | |
108 | } | |
109 | ||
110 | return; | |
111 | } | |
112 | ||
113 | /* Send RIP request packet to specified interface. */ | |
114 | static void rip_request_interface_send(struct interface *ifp, uint8_t version) | |
115 | { | |
116 | struct sockaddr_in to; | |
117 | ||
118 | /* RIPv2 support multicast. */ | |
119 | if (version == RIPv2 && if_is_multicast(ifp)) { | |
120 | ||
121 | if (IS_RIP_DEBUG_EVENT) | |
122 | zlog_debug("multicast request on %s", ifp->name); | |
123 | ||
124 | rip_request_send(NULL, ifp, version, NULL); | |
125 | return; | |
126 | } | |
127 | ||
128 | /* RIPv1 and non multicast interface. */ | |
129 | if (if_is_pointopoint(ifp) || if_is_broadcast(ifp)) { | |
130 | struct listnode *cnode, *cnnode; | |
131 | struct connected *connected; | |
132 | ||
133 | if (IS_RIP_DEBUG_EVENT) | |
134 | zlog_debug("broadcast request to %s", ifp->name); | |
135 | ||
136 | for (ALL_LIST_ELEMENTS(ifp->connected, cnode, cnnode, | |
137 | connected)) { | |
138 | if (connected->address->family != AF_INET) | |
139 | continue; | |
140 | ||
141 | memset(&to, 0, sizeof(struct sockaddr_in)); | |
142 | to.sin_port = htons(RIP_PORT_DEFAULT); | |
143 | if (connected->destination) | |
144 | /* use specified broadcast or peer | |
145 | * destination addr */ | |
146 | to.sin_addr = connected->destination->u.prefix4; | |
147 | else if (connected->address->prefixlen | |
148 | < IPV4_MAX_BITLEN) | |
149 | /* calculate the appropriate broadcast | |
150 | * address */ | |
151 | to.sin_addr.s_addr = ipv4_broadcast_addr( | |
152 | connected->address->u.prefix4.s_addr, | |
153 | connected->address->prefixlen); | |
154 | else | |
155 | /* do not know where to send the packet | |
156 | */ | |
157 | continue; | |
158 | ||
159 | if (IS_RIP_DEBUG_EVENT) | |
160 | zlog_debug("SEND request to %pI4", | |
161 | &to.sin_addr); | |
162 | ||
163 | rip_request_send(&to, ifp, version, connected); | |
164 | } | |
165 | } | |
166 | } | |
167 | ||
168 | /* This will be executed when interface goes up. */ | |
169 | static void rip_request_interface(struct interface *ifp) | |
170 | { | |
171 | struct rip_interface *ri; | |
172 | int vsend; | |
173 | ||
174 | /* In default ripd doesn't send RIP_REQUEST to the loopback interface. | |
175 | */ | |
176 | if (if_is_loopback(ifp)) | |
177 | return; | |
178 | ||
179 | /* If interface is down, don't send RIP packet. */ | |
180 | if (!if_is_operative(ifp)) | |
181 | return; | |
182 | ||
183 | /* Fetch RIP interface information. */ | |
184 | ri = ifp->info; | |
185 | ||
186 | /* If there is no version configuration in the interface, | |
187 | use rip's version setting. */ | |
188 | vsend = ((ri->ri_send == RI_RIP_UNSPEC) ? ri->rip->version_send | |
189 | : ri->ri_send); | |
190 | if (vsend & RIPv1) | |
191 | rip_request_interface_send(ifp, RIPv1); | |
192 | if (vsend & RIPv2) | |
193 | rip_request_interface_send(ifp, RIPv2); | |
194 | } | |
195 | ||
196 | /* Multicast packet receive socket. */ | |
197 | static int rip_multicast_join(struct interface *ifp, int sock) | |
198 | { | |
199 | struct listnode *cnode; | |
200 | struct connected *ifc; | |
201 | ||
202 | if (if_is_operative(ifp) && if_is_multicast(ifp)) { | |
203 | if (IS_RIP_DEBUG_EVENT) | |
204 | zlog_debug("multicast join at %s", ifp->name); | |
205 | ||
206 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, ifc)) { | |
207 | struct prefix_ipv4 *p; | |
208 | struct in_addr group; | |
209 | ||
210 | p = (struct prefix_ipv4 *)ifc->address; | |
211 | ||
212 | if (p->family != AF_INET) | |
213 | continue; | |
214 | ||
215 | group.s_addr = htonl(INADDR_RIP_GROUP); | |
216 | if (ipv4_multicast_join(sock, group, p->prefix, | |
217 | ifp->ifindex) | |
218 | < 0) | |
219 | return -1; | |
220 | else | |
221 | return 0; | |
222 | } | |
223 | } | |
224 | return 0; | |
225 | } | |
226 | ||
227 | /* Leave from multicast group. */ | |
228 | static void rip_multicast_leave(struct interface *ifp, int sock) | |
229 | { | |
230 | struct listnode *cnode; | |
231 | struct connected *connected; | |
232 | ||
233 | if (if_is_up(ifp) && if_is_multicast(ifp)) { | |
234 | if (IS_RIP_DEBUG_EVENT) | |
235 | zlog_debug("multicast leave from %s", ifp->name); | |
236 | ||
237 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { | |
238 | struct prefix_ipv4 *p; | |
239 | struct in_addr group; | |
240 | ||
241 | p = (struct prefix_ipv4 *)connected->address; | |
242 | ||
243 | if (p->family != AF_INET) | |
244 | continue; | |
245 | ||
246 | group.s_addr = htonl(INADDR_RIP_GROUP); | |
247 | if (ipv4_multicast_leave(sock, group, p->prefix, | |
248 | ifp->ifindex) | |
249 | == 0) | |
250 | return; | |
251 | } | |
252 | } | |
253 | } | |
254 | ||
255 | /* Is there and address on interface that I could use ? */ | |
256 | static int rip_if_ipv4_address_check(struct interface *ifp) | |
257 | { | |
258 | struct listnode *nn; | |
259 | struct connected *connected; | |
260 | int count = 0; | |
261 | ||
262 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, nn, connected)) { | |
263 | struct prefix *p; | |
264 | ||
265 | p = connected->address; | |
266 | ||
267 | if (p->family == AF_INET) | |
268 | count++; | |
269 | } | |
270 | ||
271 | return count; | |
272 | } | |
273 | ||
274 | ||
275 | /* Does this address belongs to me ? */ | |
276 | int if_check_address(struct rip *rip, struct in_addr addr) | |
277 | { | |
278 | struct interface *ifp; | |
279 | ||
280 | FOR_ALL_INTERFACES (rip->vrf, ifp) { | |
281 | struct listnode *cnode; | |
282 | struct connected *connected; | |
283 | ||
284 | for (ALL_LIST_ELEMENTS_RO(ifp->connected, cnode, connected)) { | |
285 | struct prefix_ipv4 *p; | |
286 | ||
287 | p = (struct prefix_ipv4 *)connected->address; | |
288 | ||
289 | if (p->family != AF_INET) | |
290 | continue; | |
291 | ||
292 | if (IPV4_ADDR_CMP(&p->prefix, &addr) == 0) | |
293 | return 1; | |
294 | } | |
295 | } | |
296 | return 0; | |
297 | } | |
298 | ||
299 | /* Interface link down message processing. */ | |
300 | static int rip_ifp_down(struct interface *ifp) | |
301 | { | |
302 | rip_interface_sync(ifp); | |
303 | rip_if_down(ifp); | |
304 | ||
305 | if (IS_RIP_DEBUG_ZEBRA) | |
306 | zlog_debug( | |
307 | "interface %s vrf %s(%u) index %d flags %llx metric %d mtu %d is down", | |
308 | ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, | |
309 | ifp->ifindex, (unsigned long long)ifp->flags, | |
310 | ifp->metric, ifp->mtu); | |
311 | ||
312 | return 0; | |
313 | } | |
314 | ||
315 | /* Interface link up message processing */ | |
316 | static int rip_ifp_up(struct interface *ifp) | |
317 | { | |
318 | if (IS_RIP_DEBUG_ZEBRA) | |
319 | zlog_debug( | |
320 | "interface %s vrf %s(%u) index %d flags %#llx metric %d mtu %d is up", | |
321 | ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, | |
322 | ifp->ifindex, (unsigned long long)ifp->flags, | |
323 | ifp->metric, ifp->mtu); | |
324 | ||
325 | rip_interface_sync(ifp); | |
326 | ||
327 | /* Check if this interface is RIP enabled or not.*/ | |
328 | rip_enable_apply(ifp); | |
329 | ||
330 | /* Check for a passive interface */ | |
331 | rip_passive_interface_apply(ifp); | |
332 | ||
333 | /* Apply distribute list to the all interface. */ | |
334 | rip_distribute_update_interface(ifp); | |
335 | ||
336 | return 0; | |
337 | } | |
338 | ||
339 | /* Interface addition message from zebra. */ | |
340 | static int rip_ifp_create(struct interface *ifp) | |
341 | { | |
342 | rip_interface_sync(ifp); | |
343 | ||
344 | if (IS_RIP_DEBUG_ZEBRA) | |
345 | zlog_debug( | |
346 | "interface add %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", | |
347 | ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, | |
348 | ifp->ifindex, (unsigned long long)ifp->flags, | |
349 | ifp->metric, ifp->mtu); | |
350 | ||
351 | /* Check if this interface is RIP enabled or not.*/ | |
352 | rip_enable_apply(ifp); | |
353 | ||
354 | /* Check for a passive interface */ | |
355 | rip_passive_interface_apply(ifp); | |
356 | ||
357 | /* Apply distribute list to the all interface. */ | |
358 | rip_distribute_update_interface(ifp); | |
359 | ||
360 | /* rip_request_neighbor_all (); */ | |
361 | ||
362 | /* Check interface routemap. */ | |
363 | rip_if_rmap_update_interface(ifp); | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | static int rip_ifp_destroy(struct interface *ifp) | |
369 | { | |
370 | rip_interface_sync(ifp); | |
371 | if (if_is_up(ifp)) { | |
372 | rip_if_down(ifp); | |
373 | } | |
374 | ||
375 | if (IS_RIP_DEBUG_ZEBRA) | |
376 | zlog_debug( | |
377 | "interface delete %s vrf %s(%u) index %d flags %#llx metric %d mtu %d", | |
378 | ifp->name, ifp->vrf->name, ifp->vrf->vrf_id, | |
379 | ifp->ifindex, (unsigned long long)ifp->flags, | |
380 | ifp->metric, ifp->mtu); | |
381 | ||
382 | return 0; | |
383 | } | |
384 | ||
385 | /* VRF update for an interface. */ | |
386 | int rip_interface_vrf_update(ZAPI_CALLBACK_ARGS) | |
387 | { | |
388 | struct interface *ifp; | |
389 | vrf_id_t new_vrf_id; | |
390 | ||
391 | ifp = zebra_interface_vrf_update_read(zclient->ibuf, vrf_id, | |
392 | &new_vrf_id); | |
393 | if (!ifp) | |
394 | return 0; | |
395 | ||
396 | if (IS_RIP_DEBUG_ZEBRA) { | |
397 | struct vrf *nvrf = vrf_lookup_by_id(new_vrf_id); | |
398 | ||
399 | zlog_debug("interface %s VRF change vrf %s(%u) new vrf %s(%u)", | |
400 | ifp->name, ifp->vrf->name, vrf_id, VRF_LOGNAME(nvrf), | |
401 | new_vrf_id); | |
402 | } | |
403 | ||
404 | if_update_to_new_vrf(ifp, new_vrf_id); | |
405 | rip_interface_sync(ifp); | |
406 | ||
407 | return 0; | |
408 | } | |
409 | ||
410 | static void rip_interface_clean(struct rip_interface *ri) | |
411 | { | |
412 | ri->enable_network = 0; | |
413 | ri->enable_interface = 0; | |
414 | ri->running = 0; | |
415 | ||
416 | EVENT_OFF(ri->t_wakeup); | |
417 | } | |
418 | ||
419 | void rip_interfaces_clean(struct rip *rip) | |
420 | { | |
421 | struct interface *ifp; | |
422 | ||
423 | FOR_ALL_INTERFACES (rip->vrf, ifp) | |
424 | rip_interface_clean(ifp->info); | |
425 | } | |
426 | ||
427 | static void rip_interface_reset(struct rip_interface *ri) | |
428 | { | |
429 | ri->auth_type = yang_get_default_enum("%s/authentication-scheme/mode", | |
430 | RIP_IFACE); | |
431 | ri->md5_auth_len = yang_get_default_enum( | |
432 | "%s/authentication-scheme/md5-auth-length", RIP_IFACE); | |
433 | ||
434 | /* Set default split-horizon behavior. If the interface is Frame | |
435 | Relay or SMDS is enabled, the default value for split-horizon is | |
436 | off. But currently Zebra does detect Frame Relay or SMDS | |
437 | interface. So all interface is set to split horizon. */ | |
438 | ri->split_horizon = | |
439 | yang_get_default_enum("%s/split-horizon", RIP_IFACE); | |
440 | ||
441 | ri->ri_send = yang_get_default_enum("%s/version-send", RIP_IFACE); | |
442 | ri->ri_receive = yang_get_default_enum("%s/version-receive", RIP_IFACE); | |
443 | ri->v2_broadcast = yang_get_default_bool("%s/v2-broadcast", RIP_IFACE); | |
444 | ||
445 | XFREE(MTYPE_RIP_INTERFACE_STRING, ri->auth_str); | |
446 | ||
447 | XFREE(MTYPE_RIP_INTERFACE_STRING, ri->key_chain); | |
448 | ||
449 | ri->list[RIP_FILTER_IN] = NULL; | |
450 | ri->list[RIP_FILTER_OUT] = NULL; | |
451 | ||
452 | ri->prefix[RIP_FILTER_IN] = NULL; | |
453 | ri->prefix[RIP_FILTER_OUT] = NULL; | |
454 | ||
455 | ri->recv_badpackets = 0; | |
456 | ri->recv_badroutes = 0; | |
457 | ri->sent_updates = 0; | |
458 | ||
459 | ri->passive = 0; | |
460 | ||
461 | rip_interface_clean(ri); | |
462 | } | |
463 | ||
464 | int rip_if_down(struct interface *ifp) | |
465 | { | |
466 | struct rip *rip; | |
467 | struct route_node *rp; | |
468 | struct rip_info *rinfo; | |
469 | struct rip_interface *ri = NULL; | |
470 | struct list *list = NULL; | |
471 | struct listnode *listnode = NULL, *nextnode = NULL; | |
472 | ||
473 | ri = ifp->info; | |
474 | ||
475 | EVENT_OFF(ri->t_wakeup); | |
476 | ||
477 | rip = ri->rip; | |
478 | if (rip) { | |
479 | for (rp = route_top(rip->table); rp; rp = route_next(rp)) | |
480 | if ((list = rp->info) != NULL) | |
481 | for (ALL_LIST_ELEMENTS(list, listnode, nextnode, | |
482 | rinfo)) | |
483 | if (rinfo->nh.ifindex == ifp->ifindex) | |
484 | rip_ecmp_delete(rip, rinfo); | |
485 | ||
486 | if (ri->running) { | |
487 | if (IS_RIP_DEBUG_EVENT) | |
488 | zlog_debug("turn off %s", ifp->name); | |
489 | ||
490 | /* Leave from multicast group. */ | |
491 | rip_multicast_leave(ifp, rip->sock); | |
492 | ||
493 | ri->running = 0; | |
494 | } | |
495 | } | |
496 | ||
497 | return 0; | |
498 | } | |
499 | ||
500 | static void rip_apply_address_add(struct connected *ifc) | |
501 | { | |
502 | struct rip_interface *ri = ifc->ifp->info; | |
503 | struct rip *rip = ri->rip; | |
504 | struct prefix_ipv4 address; | |
505 | struct nexthop nh; | |
506 | struct prefix *p; | |
507 | ||
508 | if (!rip) | |
509 | return; | |
510 | ||
511 | if (!if_is_up(ifc->ifp)) | |
512 | return; | |
513 | ||
514 | p = ifc->address; | |
515 | ||
516 | memset(&address, 0, sizeof(address)); | |
517 | memset(&nh, 0, sizeof(nh)); | |
518 | ||
519 | address.family = p->family; | |
520 | address.prefix = p->u.prefix4; | |
521 | address.prefixlen = p->prefixlen; | |
522 | apply_mask_ipv4(&address); | |
523 | ||
524 | nh.ifindex = ifc->ifp->ifindex; | |
525 | nh.type = NEXTHOP_TYPE_IFINDEX; | |
526 | ||
527 | /* Check if this interface is RIP enabled or not | |
528 | or Check if this address's prefix is RIP enabled */ | |
529 | if ((rip_enable_if_lookup(rip, ifc->ifp->name) >= 0) | |
530 | || (rip_enable_network_lookup2(ifc) >= 0)) | |
531 | rip_redistribute_add(rip, ZEBRA_ROUTE_CONNECT, | |
532 | RIP_ROUTE_INTERFACE, &address, &nh, 0, 0, | |
533 | 0); | |
534 | } | |
535 | ||
536 | int rip_interface_address_add(ZAPI_CALLBACK_ARGS) | |
537 | { | |
538 | struct connected *ifc; | |
539 | struct prefix *p; | |
540 | ||
541 | ifc = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_ADD, | |
542 | zclient->ibuf, vrf_id); | |
543 | ||
544 | if (ifc == NULL) | |
545 | return 0; | |
546 | ||
547 | p = ifc->address; | |
548 | ||
549 | if (p->family == AF_INET) { | |
550 | if (IS_RIP_DEBUG_ZEBRA) | |
551 | zlog_debug("connected address %pFX is added", p); | |
552 | ||
553 | rip_enable_apply(ifc->ifp); | |
554 | /* Check if this prefix needs to be redistributed */ | |
555 | rip_apply_address_add(ifc); | |
556 | ||
557 | hook_call(rip_ifaddr_add, ifc); | |
558 | } | |
559 | ||
560 | return 0; | |
561 | } | |
562 | ||
563 | static void rip_apply_address_del(struct connected *ifc) | |
564 | { | |
565 | struct rip_interface *ri = ifc->ifp->info; | |
566 | struct rip *rip = ri->rip; | |
567 | struct prefix_ipv4 address; | |
568 | struct prefix *p; | |
569 | ||
570 | if (!rip) | |
571 | return; | |
572 | ||
573 | if (!if_is_up(ifc->ifp)) | |
574 | return; | |
575 | ||
576 | p = ifc->address; | |
577 | ||
578 | memset(&address, 0, sizeof(address)); | |
579 | address.family = p->family; | |
580 | address.prefix = p->u.prefix4; | |
581 | address.prefixlen = p->prefixlen; | |
582 | apply_mask_ipv4(&address); | |
583 | ||
584 | rip_redistribute_delete(rip, ZEBRA_ROUTE_CONNECT, RIP_ROUTE_INTERFACE, | |
585 | &address, ifc->ifp->ifindex); | |
586 | } | |
587 | ||
588 | int rip_interface_address_delete(ZAPI_CALLBACK_ARGS) | |
589 | { | |
590 | struct connected *ifc; | |
591 | struct prefix *p; | |
592 | ||
593 | ifc = zebra_interface_address_read(ZEBRA_INTERFACE_ADDRESS_DELETE, | |
594 | zclient->ibuf, vrf_id); | |
595 | ||
596 | if (ifc) { | |
597 | p = ifc->address; | |
598 | if (p->family == AF_INET) { | |
599 | if (IS_RIP_DEBUG_ZEBRA) | |
600 | zlog_debug("connected address %pFX is deleted", | |
601 | p); | |
602 | ||
603 | hook_call(rip_ifaddr_del, ifc); | |
604 | ||
605 | /* Chech whether this prefix needs to be removed */ | |
606 | rip_apply_address_del(ifc); | |
607 | } | |
608 | ||
609 | connected_free(&ifc); | |
610 | } | |
611 | ||
612 | return 0; | |
613 | } | |
614 | ||
615 | /* Check interface is enabled by network statement. */ | |
616 | /* Check whether the interface has at least a connected prefix that | |
617 | * is within the ripng_enable_network table. */ | |
618 | static int rip_enable_network_lookup_if(struct interface *ifp) | |
619 | { | |
620 | struct rip_interface *ri = ifp->info; | |
621 | struct rip *rip = ri->rip; | |
622 | struct listnode *node, *nnode; | |
623 | struct connected *connected; | |
624 | struct prefix_ipv4 address; | |
625 | ||
626 | if (!rip) | |
627 | return -1; | |
628 | ||
629 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) { | |
630 | struct prefix *p; | |
631 | struct route_node *n; | |
632 | ||
633 | p = connected->address; | |
634 | ||
635 | if (p->family == AF_INET) { | |
636 | address.family = AF_INET; | |
637 | address.prefix = p->u.prefix4; | |
638 | address.prefixlen = IPV4_MAX_BITLEN; | |
639 | ||
640 | n = route_node_match(rip->enable_network, | |
641 | (struct prefix *)&address); | |
642 | if (n) { | |
643 | route_unlock_node(n); | |
644 | return 1; | |
645 | } | |
646 | } | |
647 | } | |
648 | return -1; | |
649 | } | |
650 | ||
651 | /* Check whether connected is within the ripng_enable_network table. */ | |
652 | static int rip_enable_network_lookup2(struct connected *connected) | |
653 | { | |
654 | struct rip_interface *ri = connected->ifp->info; | |
655 | struct rip *rip = ri->rip; | |
656 | struct prefix_ipv4 address; | |
657 | struct prefix *p; | |
658 | ||
659 | p = connected->address; | |
660 | ||
661 | if (p->family == AF_INET) { | |
662 | struct route_node *node; | |
663 | ||
664 | address.family = p->family; | |
665 | address.prefix = p->u.prefix4; | |
666 | address.prefixlen = IPV4_MAX_BITLEN; | |
667 | ||
668 | /* LPM on p->family, p->u.prefix4/IPV4_MAX_BITLEN within | |
669 | * rip->enable_network */ | |
670 | node = route_node_match(rip->enable_network, | |
671 | (struct prefix *)&address); | |
672 | ||
673 | if (node) { | |
674 | route_unlock_node(node); | |
675 | return 1; | |
676 | } | |
677 | } | |
678 | ||
679 | return -1; | |
680 | } | |
681 | /* Add RIP enable network. */ | |
682 | int rip_enable_network_add(struct rip *rip, struct prefix *p) | |
683 | { | |
684 | struct route_node *node; | |
685 | ||
686 | node = route_node_get(rip->enable_network, p); | |
687 | ||
688 | if (node->info) { | |
689 | route_unlock_node(node); | |
690 | return NB_ERR_INCONSISTENCY; | |
691 | } else | |
692 | node->info = (void *)1; | |
693 | ||
694 | /* XXX: One should find a better solution than a generic one */ | |
695 | rip_enable_apply_all(rip); | |
696 | ||
697 | return NB_OK; | |
698 | } | |
699 | ||
700 | /* Delete RIP enable network. */ | |
701 | int rip_enable_network_delete(struct rip *rip, struct prefix *p) | |
702 | { | |
703 | struct route_node *node; | |
704 | ||
705 | node = route_node_lookup(rip->enable_network, p); | |
706 | if (node) { | |
707 | node->info = NULL; | |
708 | ||
709 | /* Unlock info lock. */ | |
710 | route_unlock_node(node); | |
711 | ||
712 | /* Unlock lookup lock. */ | |
713 | route_unlock_node(node); | |
714 | ||
715 | /* XXX: One should find a better solution than a generic one */ | |
716 | rip_enable_apply_all(rip); | |
717 | ||
718 | return NB_OK; | |
719 | } | |
720 | ||
721 | return NB_ERR_INCONSISTENCY; | |
722 | } | |
723 | ||
724 | /* Check interface is enabled by ifname statement. */ | |
725 | static int rip_enable_if_lookup(struct rip *rip, const char *ifname) | |
726 | { | |
727 | unsigned int i; | |
728 | char *str; | |
729 | ||
730 | if (!rip) | |
731 | return -1; | |
732 | ||
733 | for (i = 0; i < vector_active(rip->enable_interface); i++) | |
734 | if ((str = vector_slot(rip->enable_interface, i)) != NULL) | |
735 | if (strcmp(str, ifname) == 0) | |
736 | return i; | |
737 | return -1; | |
738 | } | |
739 | ||
740 | /* Add interface to rip_enable_if. */ | |
741 | int rip_enable_if_add(struct rip *rip, const char *ifname) | |
742 | { | |
743 | int ret; | |
744 | ||
745 | ret = rip_enable_if_lookup(rip, ifname); | |
746 | if (ret >= 0) | |
747 | return NB_ERR_INCONSISTENCY; | |
748 | ||
749 | vector_set(rip->enable_interface, | |
750 | XSTRDUP(MTYPE_RIP_INTERFACE_STRING, ifname)); | |
751 | ||
752 | rip_enable_apply_all(rip); /* TODOVJ */ | |
753 | ||
754 | return NB_OK; | |
755 | } | |
756 | ||
757 | /* Delete interface from rip_enable_if. */ | |
758 | int rip_enable_if_delete(struct rip *rip, const char *ifname) | |
759 | { | |
760 | int index; | |
761 | char *str; | |
762 | ||
763 | index = rip_enable_if_lookup(rip, ifname); | |
764 | if (index < 0) | |
765 | return NB_ERR_INCONSISTENCY; | |
766 | ||
767 | str = vector_slot(rip->enable_interface, index); | |
768 | XFREE(MTYPE_RIP_INTERFACE_STRING, str); | |
769 | vector_unset(rip->enable_interface, index); | |
770 | ||
771 | rip_enable_apply_all(rip); /* TODOVJ */ | |
772 | ||
773 | return NB_OK; | |
774 | } | |
775 | ||
776 | /* Join to multicast group and send request to the interface. */ | |
777 | static void rip_interface_wakeup(struct event *t) | |
778 | { | |
779 | struct interface *ifp; | |
780 | struct rip_interface *ri; | |
781 | ||
782 | /* Get interface. */ | |
783 | ifp = EVENT_ARG(t); | |
784 | ||
785 | ri = ifp->info; | |
786 | ||
787 | /* Join to multicast group. */ | |
788 | if (rip_multicast_join(ifp, ri->rip->sock) < 0) { | |
789 | flog_err_sys(EC_LIB_SOCKET, | |
790 | "multicast join failed, interface %s not running", | |
791 | ifp->name); | |
792 | return; | |
793 | } | |
794 | ||
795 | /* Set running flag. */ | |
796 | ri->running = 1; | |
797 | ||
798 | /* Send RIP request to the interface. */ | |
799 | rip_request_interface(ifp); | |
800 | } | |
801 | ||
802 | static void rip_connect_set(struct interface *ifp, int set) | |
803 | { | |
804 | struct rip_interface *ri = ifp->info; | |
805 | struct rip *rip = ri->rip; | |
806 | struct listnode *node, *nnode; | |
807 | struct connected *connected; | |
808 | struct prefix_ipv4 address; | |
809 | struct nexthop nh; | |
810 | ||
811 | memset(&nh, 0, sizeof(nh)); | |
812 | ||
813 | for (ALL_LIST_ELEMENTS(ifp->connected, node, nnode, connected)) { | |
814 | struct prefix *p; | |
815 | p = connected->address; | |
816 | ||
817 | if (p->family != AF_INET) | |
818 | continue; | |
819 | ||
820 | address.family = AF_INET; | |
821 | address.prefix = p->u.prefix4; | |
822 | address.prefixlen = p->prefixlen; | |
823 | apply_mask_ipv4(&address); | |
824 | ||
825 | nh.ifindex = connected->ifp->ifindex; | |
826 | nh.type = NEXTHOP_TYPE_IFINDEX; | |
827 | if (set) { | |
828 | /* Check once more whether this prefix is within a | |
829 | * "network IF_OR_PREF" one */ | |
830 | if ((rip_enable_if_lookup(rip, connected->ifp->name) | |
831 | >= 0) | |
832 | || (rip_enable_network_lookup2(connected) >= 0)) | |
833 | rip_redistribute_add(rip, ZEBRA_ROUTE_CONNECT, | |
834 | RIP_ROUTE_INTERFACE, | |
835 | &address, &nh, 0, 0, 0); | |
836 | } else { | |
837 | rip_redistribute_delete(rip, ZEBRA_ROUTE_CONNECT, | |
838 | RIP_ROUTE_INTERFACE, &address, | |
839 | connected->ifp->ifindex); | |
840 | if (rip_redistribute_check(rip, ZEBRA_ROUTE_CONNECT)) | |
841 | rip_redistribute_add(rip, ZEBRA_ROUTE_CONNECT, | |
842 | RIP_ROUTE_REDISTRIBUTE, | |
843 | &address, &nh, 0, 0, 0); | |
844 | } | |
845 | } | |
846 | } | |
847 | ||
848 | /* Update interface status. */ | |
849 | void rip_enable_apply(struct interface *ifp) | |
850 | { | |
851 | int ret; | |
852 | struct rip_interface *ri = NULL; | |
853 | ||
854 | /* Check interface. */ | |
855 | if (!if_is_operative(ifp)) | |
856 | return; | |
857 | ||
858 | ri = ifp->info; | |
859 | ||
860 | /* Check network configuration. */ | |
861 | ret = rip_enable_network_lookup_if(ifp); | |
862 | ||
863 | /* If the interface is matched. */ | |
864 | if (ret > 0) | |
865 | ri->enable_network = 1; | |
866 | else | |
867 | ri->enable_network = 0; | |
868 | ||
869 | /* Check interface name configuration. */ | |
870 | ret = rip_enable_if_lookup(ri->rip, ifp->name); | |
871 | if (ret >= 0) | |
872 | ri->enable_interface = 1; | |
873 | else | |
874 | ri->enable_interface = 0; | |
875 | ||
876 | /* any interface MUST have an IPv4 address */ | |
877 | if (!rip_if_ipv4_address_check(ifp)) { | |
878 | ri->enable_network = 0; | |
879 | ri->enable_interface = 0; | |
880 | } | |
881 | ||
882 | /* Update running status of the interface. */ | |
883 | if (ri->enable_network || ri->enable_interface) { | |
884 | if (IS_RIP_DEBUG_EVENT) | |
885 | zlog_debug("turn on %s", ifp->name); | |
886 | ||
887 | /* Add interface wake up thread. */ | |
888 | event_add_timer(master, rip_interface_wakeup, ifp, 1, | |
889 | &ri->t_wakeup); | |
890 | rip_connect_set(ifp, 1); | |
891 | } else if (ri->running) { | |
892 | /* Might as well clean up the route table as well | |
893 | * rip_if_down sets to 0 ri->running, and displays "turn | |
894 | *off %s" | |
895 | **/ | |
896 | rip_if_down(ifp); | |
897 | ||
898 | rip_connect_set(ifp, 0); | |
899 | } | |
900 | } | |
901 | ||
902 | /* Apply network configuration to all interface. */ | |
903 | static void rip_enable_apply_all(struct rip *rip) | |
904 | { | |
905 | struct interface *ifp; | |
906 | ||
907 | /* Check each interface. */ | |
908 | FOR_ALL_INTERFACES (rip->vrf, ifp) | |
909 | rip_enable_apply(ifp); | |
910 | } | |
911 | ||
912 | int rip_neighbor_lookup(struct rip *rip, struct sockaddr_in *from) | |
913 | { | |
914 | struct prefix_ipv4 p; | |
915 | struct route_node *node; | |
916 | ||
917 | memset(&p, 0, sizeof(p)); | |
918 | p.family = AF_INET; | |
919 | p.prefix = from->sin_addr; | |
920 | p.prefixlen = IPV4_MAX_BITLEN; | |
921 | ||
922 | node = route_node_lookup(rip->neighbor, (struct prefix *)&p); | |
923 | if (node) { | |
924 | route_unlock_node(node); | |
925 | return 1; | |
926 | } | |
927 | return 0; | |
928 | } | |
929 | ||
930 | /* Add new RIP neighbor to the neighbor tree. */ | |
931 | int rip_neighbor_add(struct rip *rip, struct prefix_ipv4 *p) | |
932 | { | |
933 | struct route_node *node; | |
934 | ||
935 | node = route_node_get(rip->neighbor, (struct prefix *)p); | |
936 | ||
937 | if (node->info) | |
938 | return NB_ERR_INCONSISTENCY; | |
939 | ||
940 | node->info = rip->neighbor; | |
941 | ||
942 | return NB_OK; | |
943 | } | |
944 | ||
945 | /* Delete RIP neighbor from the neighbor tree. */ | |
946 | int rip_neighbor_delete(struct rip *rip, struct prefix_ipv4 *p) | |
947 | { | |
948 | struct route_node *node; | |
949 | ||
950 | /* Lock for look up. */ | |
951 | node = route_node_lookup(rip->neighbor, (struct prefix *)p); | |
952 | if (!node) | |
953 | return NB_ERR_INCONSISTENCY; | |
954 | ||
955 | node->info = NULL; | |
956 | ||
957 | /* Unlock lookup lock. */ | |
958 | route_unlock_node(node); | |
959 | ||
960 | /* Unlock real neighbor information lock. */ | |
961 | route_unlock_node(node); | |
962 | ||
963 | return NB_OK; | |
964 | } | |
965 | ||
966 | /* Clear all network and neighbor configuration. */ | |
967 | void rip_clean_network(struct rip *rip) | |
968 | { | |
969 | unsigned int i; | |
970 | char *str; | |
971 | struct route_node *rn; | |
972 | ||
973 | /* rip->enable_network. */ | |
974 | for (rn = route_top(rip->enable_network); rn; rn = route_next(rn)) | |
975 | if (rn->info) { | |
976 | rn->info = NULL; | |
977 | route_unlock_node(rn); | |
978 | } | |
979 | ||
980 | /* rip->enable_interface. */ | |
981 | for (i = 0; i < vector_active(rip->enable_interface); i++) | |
982 | if ((str = vector_slot(rip->enable_interface, i)) != NULL) { | |
983 | XFREE(MTYPE_RIP_INTERFACE_STRING, str); | |
984 | vector_slot(rip->enable_interface, i) = NULL; | |
985 | } | |
986 | } | |
987 | ||
988 | /* Utility function for looking up passive interface settings. */ | |
989 | static int rip_passive_nondefault_lookup(struct rip *rip, const char *ifname) | |
990 | { | |
991 | unsigned int i; | |
992 | char *str; | |
993 | ||
994 | for (i = 0; i < vector_active(rip->passive_nondefault); i++) | |
995 | if ((str = vector_slot(rip->passive_nondefault, i)) != NULL) | |
996 | if (strcmp(str, ifname) == 0) | |
997 | return i; | |
998 | return -1; | |
999 | } | |
1000 | ||
1001 | static void rip_passive_interface_apply(struct interface *ifp) | |
1002 | { | |
1003 | struct rip *rip; | |
1004 | struct rip_interface *ri; | |
1005 | ||
1006 | ri = ifp->info; | |
1007 | rip = ri->rip; | |
1008 | if (rip == NULL) | |
1009 | return; | |
1010 | ||
1011 | ri->passive = ((rip_passive_nondefault_lookup(rip, ifp->name) < 0) | |
1012 | ? rip->passive_default | |
1013 | : !rip->passive_default); | |
1014 | ||
1015 | if (IS_RIP_DEBUG_ZEBRA) | |
1016 | zlog_debug("interface %s: passive = %d", ifp->name, | |
1017 | ri->passive); | |
1018 | } | |
1019 | ||
1020 | static void rip_passive_interface_apply_all(struct rip *rip) | |
1021 | { | |
1022 | struct interface *ifp; | |
1023 | ||
1024 | FOR_ALL_INTERFACES (rip->vrf, ifp) | |
1025 | rip_passive_interface_apply(ifp); | |
1026 | } | |
1027 | ||
1028 | /* Passive interface. */ | |
1029 | int rip_passive_nondefault_set(struct rip *rip, const char *ifname) | |
1030 | { | |
1031 | if (rip_passive_nondefault_lookup(rip, ifname) >= 0) | |
1032 | /* | |
1033 | * Don't return an error, this can happen after changing | |
1034 | * 'passive-default'. | |
1035 | */ | |
1036 | return NB_OK; | |
1037 | ||
1038 | vector_set(rip->passive_nondefault, | |
1039 | XSTRDUP(MTYPE_RIP_INTERFACE_STRING, ifname)); | |
1040 | ||
1041 | rip_passive_interface_apply_all(rip); | |
1042 | ||
1043 | return NB_OK; | |
1044 | } | |
1045 | ||
1046 | int rip_passive_nondefault_unset(struct rip *rip, const char *ifname) | |
1047 | { | |
1048 | int i; | |
1049 | char *str; | |
1050 | ||
1051 | i = rip_passive_nondefault_lookup(rip, ifname); | |
1052 | if (i < 0) | |
1053 | /* | |
1054 | * Don't return an error, this can happen after changing | |
1055 | * 'passive-default'. | |
1056 | */ | |
1057 | return NB_OK; | |
1058 | ||
1059 | str = vector_slot(rip->passive_nondefault, i); | |
1060 | XFREE(MTYPE_RIP_INTERFACE_STRING, str); | |
1061 | vector_unset(rip->passive_nondefault, i); | |
1062 | ||
1063 | rip_passive_interface_apply_all(rip); | |
1064 | ||
1065 | return NB_OK; | |
1066 | } | |
1067 | ||
1068 | /* Free all configured RIP passive-interface settings. */ | |
1069 | void rip_passive_nondefault_clean(struct rip *rip) | |
1070 | { | |
1071 | unsigned int i; | |
1072 | char *str; | |
1073 | ||
1074 | for (i = 0; i < vector_active(rip->passive_nondefault); i++) | |
1075 | if ((str = vector_slot(rip->passive_nondefault, i)) != NULL) { | |
1076 | XFREE(MTYPE_RIP_INTERFACE_STRING, str); | |
1077 | vector_slot(rip->passive_nondefault, i) = NULL; | |
1078 | } | |
1079 | rip_passive_interface_apply_all(rip); | |
1080 | } | |
1081 | ||
1082 | int rip_show_network_config(struct vty *vty, struct rip *rip) | |
1083 | { | |
1084 | unsigned int i; | |
1085 | char *ifname; | |
1086 | struct route_node *node; | |
1087 | ||
1088 | /* Network type RIP enable interface statement. */ | |
1089 | for (node = route_top(rip->enable_network); node; | |
1090 | node = route_next(node)) | |
1091 | if (node->info) | |
1092 | vty_out(vty, " %pFX\n", &node->p); | |
1093 | ||
1094 | /* Interface name RIP enable statement. */ | |
1095 | for (i = 0; i < vector_active(rip->enable_interface); i++) | |
1096 | if ((ifname = vector_slot(rip->enable_interface, i)) != NULL) | |
1097 | vty_out(vty, " %s\n", ifname); | |
1098 | ||
1099 | /* RIP neighbors listing. */ | |
1100 | for (node = route_top(rip->neighbor); node; node = route_next(node)) | |
1101 | if (node->info) | |
1102 | vty_out(vty, " %pI4\n", &node->p.u.prefix4); | |
1103 | ||
1104 | return 0; | |
1105 | } | |
1106 | ||
1107 | void rip_interface_sync(struct interface *ifp) | |
1108 | { | |
1109 | struct rip_interface *ri; | |
1110 | ||
1111 | ri = ifp->info; | |
1112 | if (ri) | |
1113 | ri->rip = ifp->vrf->info; | |
1114 | } | |
1115 | ||
1116 | /* Called when interface structure allocated. */ | |
1117 | static int rip_interface_new_hook(struct interface *ifp) | |
1118 | { | |
1119 | ifp->info = rip_interface_new(); | |
1120 | rip_interface_sync(ifp); | |
1121 | ||
1122 | return 0; | |
1123 | } | |
1124 | ||
1125 | /* Called when interface structure deleted. */ | |
1126 | static int rip_interface_delete_hook(struct interface *ifp) | |
1127 | { | |
1128 | rip_interface_reset(ifp->info); | |
1129 | XFREE(MTYPE_RIP_INTERFACE, ifp->info); | |
1130 | return 0; | |
1131 | } | |
1132 | ||
1133 | /* Allocate and initialize interface vector. */ | |
1134 | void rip_if_init(void) | |
1135 | { | |
1136 | /* Default initial size of interface vector. */ | |
1137 | hook_register_prio(if_add, 0, rip_interface_new_hook); | |
1138 | hook_register_prio(if_del, 0, rip_interface_delete_hook); | |
1139 | ||
1140 | /* Install interface node. */ | |
1141 | if_cmd_init_default(); | |
1142 | if_zapi_callbacks(rip_ifp_create, rip_ifp_up, | |
1143 | rip_ifp_down, rip_ifp_destroy); | |
1144 | } |