/* * Interface related function for RIPng. * Copyright (C) 1998 Kunihiro Ishiguro * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include "linklist.h" #include "if.h" #include "prefix.h" #include "memory.h" #include "network.h" #include "filter.h" #include "log.h" #include "stream.h" #include "zclient.h" #include "command.h" #include "table.h" #include "thread.h" #include "ripngd/ripngd.h" #include "ripngd/ripng_debug.h" /* If RFC2133 definition is used. */ #ifndef IPV6_JOIN_GROUP #define IPV6_JOIN_GROUP IPV6_ADD_MEMBERSHIP #endif #ifndef IPV6_LEAVE_GROUP #define IPV6_LEAVE_GROUP IPV6_DROP_MEMBERSHIP #endif /* Static utility function. */ static void ripng_enable_apply (struct interface *); static void ripng_passive_interface_apply (struct interface *); /* Join to the all rip routers multicast group. */ int ripng_multicast_join (struct interface *ifp) { int ret; struct ipv6_mreq mreq; memset (&mreq, 0, sizeof (mreq)); inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr); mreq.ipv6mr_interface = ifp->ifindex; ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreq, sizeof (mreq)); if (ret < 0) zlog_warn ("can't setsockopt IPV6_JOIN_GROUP: %s", strerror (errno)); if (IS_RIPNG_DEBUG_EVENT) zlog_info ("RIPng %s join to all-rip-routers multicast group", ifp->name); return ret; } /* Leave from the all rip routers multicast group. */ int ripng_multicast_leave (struct interface *ifp) { int ret; struct ipv6_mreq mreq; memset (&mreq, 0, sizeof (mreq)); inet_pton(AF_INET6, RIPNG_GROUP, &mreq.ipv6mr_multiaddr); mreq.ipv6mr_interface = ifp->ifindex; ret = setsockopt (ripng->sock, IPPROTO_IPV6, IPV6_LEAVE_GROUP, (char *) &mreq, sizeof (mreq)); if (ret < 0) zlog_warn ("can't setsockopt IPV6_LEAVE_GROUP: %s\n", strerror (errno)); if (IS_RIPNG_DEBUG_EVENT) zlog_info ("RIPng %s leave from all-rip-routers multicast group", ifp->name); return ret; } /* Check max mtu size. */ int ripng_check_max_mtu () { listnode node; struct interface *ifp; int mtu; mtu = 0; for (node = listhead (iflist); node; nextnode (node)) { ifp = getdata (node); if (mtu < ifp->mtu) mtu = ifp->mtu; } return mtu; } int ripng_if_down (struct interface *ifp) { struct route_node *rp; struct ripng_info *rinfo; struct ripng_interface *ri; if (ripng->table) { for (rp = route_top (ripng->table); rp; rp = route_next (rp)) if ((rinfo = rp->info) != NULL) { /* Routes got through this interface. */ if (rinfo->ifindex == ifp->ifindex && rinfo->type == ZEBRA_ROUTE_RIPNG && rinfo->sub_type == RIPNG_ROUTE_RTE) { ripng_zebra_ipv6_delete ((struct prefix_ipv6 *) &rp->p, &rinfo->nexthop, rinfo->ifindex); RIPNG_TIMER_OFF (rinfo->t_timeout); RIPNG_TIMER_OFF (rinfo->t_garbage_collect); rp->info = NULL; route_unlock_node (rp); ripng_info_free (rinfo); } else { /* All redistributed routes got through this interface. */ if (rinfo->ifindex == ifp->ifindex) ripng_redistribute_delete (rinfo->type, rinfo->sub_type, (struct prefix_ipv6 *) &rp->p, rinfo->ifindex); } } } ri = ifp->info; if (ripng && ri->running) { if (IS_RIPNG_DEBUG_EVENT) zlog_info ("turn off %s", ifp->name); /* Leave from multicast group. */ ripng_multicast_leave (ifp); ri->running = 0; } return 0; } /* Inteface link up message processing. */ int ripng_interface_up (int command, struct zclient *zclient, zebra_size_t length) { struct stream *s; struct interface *ifp; /* zebra_interface_state_read() updates interface structure in iflist. */ s = zclient->ibuf; ifp = zebra_interface_state_read (s); if (ifp == NULL) return 0; if (IS_RIPNG_DEBUG_ZEBRA) zlog_info ("interface up %s index %d flags %ld metric %d mtu %d", ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu); /* Check if this interface is RIPng enabled or not. */ ripng_enable_apply (ifp); /* Check for a passive interface. */ ripng_passive_interface_apply (ifp); /* Apply distribute list to the all interface. */ ripng_distribute_update_interface (ifp); return 0; } /* Inteface link down message processing. */ int ripng_interface_down (int command, struct zclient *zclient, zebra_size_t length) { struct stream *s; struct interface *ifp; /* zebra_interface_state_read() updates interface structure in iflist. */ s = zclient->ibuf; ifp = zebra_interface_state_read (s); if (ifp == NULL) return 0; ripng_if_down (ifp); if (IS_RIPNG_DEBUG_ZEBRA) zlog_info ("interface down %s index %d flags %ld metric %d mtu %d", ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu); return 0; } /* Inteface addition message from zebra. */ int ripng_interface_add (int command, struct zclient *zclient, zebra_size_t length) { struct interface *ifp; ifp = zebra_interface_add_read (zclient->ibuf); if (IS_RIPNG_DEBUG_ZEBRA) zlog_info ("RIPng interface add %s index %d flags %ld metric %d mtu %d", ifp->name, ifp->ifindex, ifp->flags, ifp->metric, ifp->mtu); /* Check is this interface is RIP enabled or not.*/ ripng_enable_apply (ifp); /* Apply distribute list to the interface. */ ripng_distribute_update_interface (ifp); /* Check interface routemap. */ ripng_if_rmap_update_interface (ifp); return 0; } int ripng_interface_delete (int command, struct zclient *zclient, zebra_size_t length) { return 0; } int ripng_interface_address_add (int command, struct zclient *zclient, zebra_size_t length) { struct connected *c; struct prefix *p; char buf[INET6_ADDRSTRLEN]; c = zebra_interface_address_add_read (zclient->ibuf); if (c == NULL) return 0; p = c->address; if (p->family == AF_INET6) { if (IS_RIPNG_DEBUG_ZEBRA) zlog_info ("RIPng connected address %s/%d add", inet_ntop (AF_INET6, &p->u.prefix6, buf, INET6_ADDRSTRLEN), p->prefixlen); /* Check is this interface is RIP enabled or not.*/ ripng_enable_apply (c->ifp); } return 0; } int ripng_interface_address_delete (int command, struct zclient *zclient, zebra_size_t length) { struct connected *ifc; struct prefix *p; char buf[INET6_ADDRSTRLEN]; ifc = zebra_interface_address_delete_read (zclient->ibuf); if (ifc) { p = ifc->address; if (p->family == AF_INET6) { if (IS_RIPNG_DEBUG_ZEBRA) zlog_info ("RIPng connected address %s/%d delete", inet_ntop (AF_INET6, &p->u.prefix6, buf, INET6_ADDRSTRLEN), p->prefixlen); /* Check is this interface is RIP enabled or not.*/ ripng_enable_apply (ifc->ifp); } connected_free (ifc); } return 0; } /* RIPng enable interface vector. */ vector ripng_enable_if; /* RIPng enable network table. */ struct route_table *ripng_enable_network; /* Lookup RIPng enable network. */ int ripng_enable_network_lookup (struct interface *ifp) { listnode listnode; struct connected *connected; for (listnode = listhead (ifp->connected); listnode; nextnode (listnode)) if ((connected = getdata (listnode)) != NULL) { struct prefix *p; struct route_node *node; p = connected->address; if (p->family == AF_INET6) { node = route_node_match (ripng_enable_network, p); if (node) { route_unlock_node (node); return 1; } } } return -1; } /* Add RIPng enable network. */ int ripng_enable_network_add (struct prefix *p) { struct route_node *node; node = route_node_get (ripng_enable_network, p); if (node->info) { route_unlock_node (node); return -1; } else node->info = "enabled"; return 1; } /* Delete RIPng enable network. */ int ripng_enable_network_delete (struct prefix *p) { struct route_node *node; node = route_node_lookup (ripng_enable_network, p); if (node) { node->info = NULL; /* Unlock info lock. */ route_unlock_node (node); /* Unlock lookup lock. */ route_unlock_node (node); return 1; } return -1; } /* Lookup function. */ int ripng_enable_if_lookup (char *ifname) { int i; char *str; for (i = 0; i < vector_max (ripng_enable_if); i++) if ((str = vector_slot (ripng_enable_if, i)) != NULL) if (strcmp (str, ifname) == 0) return i; return -1; } /* Add interface to ripng_enable_if. */ int ripng_enable_if_add (char *ifname) { int ret; ret = ripng_enable_if_lookup (ifname); if (ret >= 0) return -1; vector_set (ripng_enable_if, strdup (ifname)); return 1; } /* Delete interface from ripng_enable_if. */ int ripng_enable_if_delete (char *ifname) { int index; char *str; index = ripng_enable_if_lookup (ifname); if (index < 0) return -1; str = vector_slot (ripng_enable_if, index); free (str); vector_unset (ripng_enable_if, index); return 1; } /* Wake up interface. */ int ripng_interface_wakeup (struct thread *t) { struct interface *ifp; struct ripng_interface *ri; /* Get interface. */ ifp = THREAD_ARG (t); ri = ifp->info; ri->t_wakeup = NULL; /* Join to multicast group. */ ripng_multicast_join (ifp); /* Send RIP request to the interface. */ ripng_request (ifp); return 0; } /* Check RIPng is enabed on this interface. */ void ripng_enable_apply (struct interface *ifp) { int ret; struct ripng_interface *ri = NULL; /* Check interface. */ if (if_is_loopback (ifp)) return; if (! if_is_up (ifp)) return; ri = ifp->info; /* Check network configuration. */ ret = ripng_enable_network_lookup (ifp); /* If the interface is matched. */ if (ret > 0) ri->enable_network = 1; else ri->enable_network = 0; /* Check interface name configuration. */ ret = ripng_enable_if_lookup (ifp->name); if (ret >= 0) ri->enable_interface = 1; else ri->enable_interface = 0; /* Update running status of the interface. */ if (ri->enable_network || ri->enable_interface) { if (! ri->running) { if (IS_RIPNG_DEBUG_EVENT) zlog_info ("RIPng turn on %s", ifp->name); /* Add interface wake up thread. */ if (! ri->t_wakeup) ri->t_wakeup = thread_add_timer (master, ripng_interface_wakeup, ifp, 1); #if 0 /* Join to multicast group. */ ripng_multicast_join (ifp); /* Send RIP request to the interface. */ ripng_request (ifp); #endif /* 0 */ ri->running = 1; } } else { if (ri->running) { if (IS_RIPNG_DEBUG_EVENT) zlog_info ("RIPng turn off %s", ifp->name); /* Leave from multicast group. */ ripng_multicast_leave (ifp); ri->running = 0; } } } /* Set distribute list to all interfaces. */ static void ripng_enable_apply_all () { struct interface *ifp; listnode node; for (node = listhead (iflist); node; nextnode (node)) { ifp = getdata (node); ripng_enable_apply (ifp); } } /* Vector to store passive-interface name. */ vector Vripng_passive_interface; /* Utility function for looking up passive interface settings. */ int ripng_passive_interface_lookup (char *ifname) { int i; char *str; for (i = 0; i < vector_max (Vripng_passive_interface); i++) if ((str = vector_slot (Vripng_passive_interface, i)) != NULL) if (strcmp (str, ifname) == 0) return i; return -1; } void ripng_passive_interface_apply (struct interface *ifp) { int ret; struct ripng_interface *ri; ri = ifp->info; ret = ripng_passive_interface_lookup (ifp->name); if (ret < 0) ri->passive = 0; else ri->passive = 1; } void ripng_passive_interface_apply_all (void) { struct interface *ifp; listnode node; for (node = listhead (iflist); node; nextnode (node)) { ifp = getdata (node); ripng_passive_interface_apply (ifp); } } /* Passive interface. */ int ripng_passive_interface_set (struct vty *vty, char *ifname) { if (ripng_passive_interface_lookup (ifname) >= 0) return CMD_WARNING; vector_set (Vripng_passive_interface, strdup (ifname)); ripng_passive_interface_apply_all (); return CMD_SUCCESS; } int ripng_passive_interface_unset (struct vty *vty, char *ifname) { int i; char *str; i = ripng_passive_interface_lookup (ifname); if (i < 0) return CMD_WARNING; str = vector_slot (Vripng_passive_interface, i); free (str); vector_unset (Vripng_passive_interface, i); ripng_passive_interface_apply_all (); return CMD_SUCCESS; } /* Free all configured RIP passive-interface settings. */ void ripng_passive_interface_clean (void) { int i; char *str; for (i = 0; i < vector_max (Vripng_passive_interface); i++) if ((str = vector_slot (Vripng_passive_interface, i)) != NULL) { free (str); vector_slot (Vripng_passive_interface, i) = NULL; } ripng_passive_interface_apply_all (); } /* Write RIPng enable network and interface to the vty. */ int ripng_network_write (struct vty *vty) { int i; char *str; char *ifname; struct route_node *node; char buf[BUFSIZ]; /* Write enable network. */ for (node = route_top (ripng_enable_network); node; node = route_next (node)) if (node->info) { struct prefix *p = &node->p; vty_out (vty, " network %s/%d%s", inet_ntop (p->family, &p->u.prefix, buf, BUFSIZ), p->prefixlen, VTY_NEWLINE); } /* Write enable interface. */ for (i = 0; i < vector_max (ripng_enable_if); i++) if ((str = vector_slot (ripng_enable_if, i)) != NULL) vty_out (vty, " network %s%s", str, VTY_NEWLINE); /* Write passive interface. */ for (i = 0; i < vector_max (Vripng_passive_interface); i++) if ((ifname = vector_slot (Vripng_passive_interface, i)) != NULL) vty_out (vty, " passive-interface %s%s", ifname, VTY_NEWLINE); return 0; } /* RIPng enable on specified interface or matched network. */ DEFUN (ripng_network, ripng_network_cmd, "network IF_OR_ADDR", "RIPng enable on specified interface or network.\n" "Interface or address") { int ret; struct prefix p; ret = str2prefix (argv[0], &p); /* Given string is IPv6 network or interface name. */ if (ret) ret = ripng_enable_network_add (&p); else ret = ripng_enable_if_add (argv[0]); if (ret < 0) { vty_out (vty, "There is same network configuration %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } ripng_enable_apply_all (); return CMD_SUCCESS; } /* RIPng enable on specified interface or matched network. */ DEFUN (no_ripng_network, no_ripng_network_cmd, "no network IF_OR_ADDR", NO_STR "RIPng enable on specified interface or network.\n" "Interface or address") { int ret; struct prefix p; ret = str2prefix (argv[0], &p); /* Given string is interface name. */ if (ret) ret = ripng_enable_network_delete (&p); else ret = ripng_enable_if_delete (argv[0]); if (ret < 0) { vty_out (vty, "can't find network %s%s", argv[0], VTY_NEWLINE); return CMD_WARNING; } ripng_enable_apply_all (); return CMD_SUCCESS; } DEFUN (ripng_passive_interface, ripng_passive_interface_cmd, "passive-interface IFNAME", "Suppress routing updates on an interface\n" "Interface name\n") { return ripng_passive_interface_set (vty, argv[0]); } DEFUN (no_ripng_passive_interface, no_ripng_passive_interface_cmd, "no passive-interface IFNAME", NO_STR "Suppress routing updates on an interface\n" "Interface name\n") { return ripng_passive_interface_unset (vty, argv[0]); } struct ripng_interface * ri_new () { struct ripng_interface *ri; ri = XCALLOC (MTYPE_IF, sizeof (struct ripng_interface)); return ri; } int ripng_if_new_hook (struct interface *ifp) { ifp->info = ri_new (); return 0; } /* Configuration write function for ripngd. */ int interface_config_write (struct vty *vty) { listnode node; struct interface *ifp; struct ripng_interface *ri; int write = 0; for (node = listhead (iflist); node; nextnode (node)) { ifp = getdata (node); ri = ifp->info; vty_out (vty, "interface %s%s", ifp->name, VTY_NEWLINE); if (ifp->desc) vty_out (vty, " description %s%s", ifp->desc, VTY_NEWLINE); vty_out (vty, "!%s", VTY_NEWLINE); write++; } return write; } /* ripngd's interface node. */ struct cmd_node interface_node = { INTERFACE_NODE, "%s(config-if)# ", }; /* Initialization of interface. */ void ripng_if_init () { /* Interface initialize. */ iflist = list_new (); if_add_hook (IF_NEW_HOOK, ripng_if_new_hook); /* RIPng enable network init. */ ripng_enable_network = route_table_init (); /* RIPng enable interface init. */ ripng_enable_if = vector_init (1); /* RIPng passive interface. */ Vripng_passive_interface = vector_init (1); /* Install interface node. */ install_node (&interface_node, interface_config_write); install_element (CONFIG_NODE, &interface_cmd); install_element (INTERFACE_NODE, &config_end_cmd); install_element (INTERFACE_NODE, &config_exit_cmd); install_element (INTERFACE_NODE, &config_help_cmd); install_element (INTERFACE_NODE, &interface_desc_cmd); install_element (INTERFACE_NODE, &no_interface_desc_cmd); install_element (RIPNG_NODE, &ripng_network_cmd); install_element (RIPNG_NODE, &no_ripng_network_cmd); install_element (RIPNG_NODE, &ripng_passive_interface_cmd); install_element (RIPNG_NODE, &no_ripng_passive_interface_cmd); }