/* Routing Information Base. * Copyright (C) 1997, 98, 99, 2001 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 "prefix.h" #include "table.h" #include "memory.h" #include "vty.h" #include "str.h" #include "command.h" #include "linklist.h" #include "if.h" #include "log.h" #include "sockunion.h" #include "zebra/rib.h" #include "zebra/rt.h" #include "zebra/zserv.h" #include "zebra/redistribute.h" #include "zebra/debug.h" /* Routing information base and static table for IPv4. */ struct route_table *rib_table_ipv4; struct route_table *static_table_ipv4; /* Routing information base and static table for IPv6. */ #ifdef HAVE_IPV6 struct route_table *rib_table_ipv6; struct route_table *static_table_ipv6; #endif /* HAVE_IPV6 */ /* Default rtm_table for all clients */ extern int rtm_table_default; /* Each route type's string and default distance value. */ struct { int key; char c; char *str; int distance; } route_info[] = { {ZEBRA_ROUTE_SYSTEM, 'X', "system", 0}, {ZEBRA_ROUTE_KERNEL, 'K', "kernel", 0}, {ZEBRA_ROUTE_CONNECT, 'C', "connected", 0}, {ZEBRA_ROUTE_STATIC, 'S', "static", 1}, {ZEBRA_ROUTE_RIP, 'R', "rip", 120}, {ZEBRA_ROUTE_RIPNG, 'R', "ripng", 120}, {ZEBRA_ROUTE_OSPF, 'O', "ospf", 110}, {ZEBRA_ROUTE_OSPF6, 'O', "ospf6", 110}, {ZEBRA_ROUTE_ISIS, 'I', "isis", 115}, {ZEBRA_ROUTE_BGP, 'B', "bgp", 20 /* IBGP is 200. */} }; /* Add nexthop to the end of the list. */ void nexthop_add (struct rib *rib, struct nexthop *nexthop) { struct nexthop *last; for (last = rib->nexthop; last && last->next; last = last->next) ; if (last) last->next = nexthop; else rib->nexthop = nexthop; nexthop->prev = last; rib->nexthop_num++; } /* Delete specified nexthop from the list. */ void nexthop_delete (struct rib *rib, struct nexthop *nexthop) { if (nexthop->next) nexthop->next->prev = nexthop->prev; if (nexthop->prev) nexthop->prev->next = nexthop->next; else rib->nexthop = nexthop->next; rib->nexthop_num--; } /* Free nexthop. */ void nexthop_free (struct nexthop *nexthop) { if (nexthop->type == NEXTHOP_TYPE_IFNAME && nexthop->ifname) free (nexthop->ifname); XFREE (MTYPE_NEXTHOP, nexthop); } struct nexthop * nexthop_ifindex_add (struct rib *rib, unsigned int ifindex) { struct nexthop *nexthop; nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); memset (nexthop, 0, sizeof (struct nexthop)); nexthop->type = NEXTHOP_TYPE_IFINDEX; nexthop->ifindex = ifindex; nexthop_add (rib, nexthop); return nexthop; } struct nexthop * nexthop_ifname_add (struct rib *rib, char *ifname) { struct nexthop *nexthop; nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); memset (nexthop, 0, sizeof (struct nexthop)); nexthop->type = NEXTHOP_TYPE_IFNAME; nexthop->ifname = strdup (ifname); nexthop_add (rib, nexthop); return nexthop; } struct nexthop * nexthop_ipv4_add (struct rib *rib, struct in_addr *ipv4) { struct nexthop *nexthop; nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); memset (nexthop, 0, sizeof (struct nexthop)); nexthop->type = NEXTHOP_TYPE_IPV4; nexthop->gate.ipv4 = *ipv4; nexthop_add (rib, nexthop); return nexthop; } struct nexthop * nexthop_ipv4_ifindex_add (struct rib *rib, struct in_addr *ipv4, unsigned int ifindex) { struct nexthop *nexthop; nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); memset (nexthop, 0, sizeof (struct nexthop)); nexthop->type = NEXTHOP_TYPE_IPV4_IFINDEX; nexthop->gate.ipv4 = *ipv4; nexthop->ifindex = ifindex; nexthop_add (rib, nexthop); return nexthop; } #ifdef HAVE_IPV6 struct nexthop * nexthop_ipv6_add (struct rib *rib, struct in6_addr *ipv6) { struct nexthop *nexthop; nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); memset (nexthop, 0, sizeof (struct nexthop)); nexthop->type = NEXTHOP_TYPE_IPV6; nexthop->gate.ipv6 = *ipv6; nexthop_add (rib, nexthop); return nexthop; } struct nexthop * nexthop_ipv6_ifname_add (struct rib *rib, struct in6_addr *ipv6, char *ifname) { struct nexthop *nexthop; nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); memset (nexthop, 0, sizeof (struct nexthop)); nexthop->type = NEXTHOP_TYPE_IPV6_IFNAME; nexthop->gate.ipv6 = *ipv6; nexthop->ifname = XSTRDUP (0, ifname); nexthop_add (rib, nexthop); return nexthop; } struct nexthop * nexthop_ipv6_ifindex_add (struct rib *rib, struct in6_addr *ipv6, unsigned int ifindex) { struct nexthop *nexthop; nexthop = XMALLOC (MTYPE_NEXTHOP, sizeof (struct nexthop)); memset (nexthop, 0, sizeof (struct nexthop)); nexthop->type = NEXTHOP_TYPE_IPV6_IFINDEX; nexthop->gate.ipv6 = *ipv6; nexthop->ifindex = ifindex; nexthop_add (rib, nexthop); return nexthop; } #endif /* HAVE_IPV6 */ /* If force flag is not set, do not modify falgs at all for uninstall the route from FIB. */ int nexthop_active_ipv4 (struct rib *rib, struct nexthop *nexthop, int set, struct route_node *top) { struct prefix_ipv4 p; struct route_node *rn; struct rib *match; struct nexthop *newhop; if (nexthop->type == NEXTHOP_TYPE_IPV4) nexthop->ifindex = 0; if (set) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; p.prefix = nexthop->gate.ipv4; rn = route_node_match (rib_table_ipv4, (struct prefix *) &p); while (rn) { route_unlock_node (rn); /* If lookup self prefix return immidiately. */ if (rn == top) return 0; /* Pick up selected route. */ for (match = rn->info; match; match = match->next) if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; /* If there is no selected route or matched route is EGP, go up tree. */ if (! match || match->type == ZEBRA_ROUTE_BGP) { do { rn = rn->parent; } while (rn && rn->info == NULL); if (rn) route_lock_node (rn); } else { if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ newhop = match->nexthop; if (newhop && nexthop->type == NEXTHOP_TYPE_IPV4) nexthop->ifindex = newhop->ifindex; return 1; } else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) { for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE)) { if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); nexthop->rtype = newhop->type; if (newhop->type == NEXTHOP_TYPE_IPV4 || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) nexthop->rgate.ipv4 = newhop->gate.ipv4; if (newhop->type == NEXTHOP_TYPE_IFINDEX || newhop->type == NEXTHOP_TYPE_IFNAME || newhop->type == NEXTHOP_TYPE_IPV4_IFINDEX) nexthop->rifindex = newhop->ifindex; } return 1; } return 0; } else { return 0; } } } return 0; } #ifdef HAVE_IPV6 /* If force flag is not set, do not modify falgs at all for uninstall the route from FIB. */ int nexthop_active_ipv6 (struct rib *rib, struct nexthop *nexthop, int set, struct route_node *top) { struct prefix_ipv6 p; struct route_node *rn; struct rib *match; struct nexthop *newhop; if (nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = 0; if (set) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); /* Make lookup prefix. */ memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; p.prefixlen = IPV6_MAX_PREFIXLEN; p.prefix = nexthop->gate.ipv6; rn = route_node_match (rib_table_ipv6, (struct prefix *) &p); while (rn) { route_unlock_node (rn); /* If lookup self prefix return immidiately. */ if (rn == top) return 0; /* Pick up selected route. */ for (match = rn->info; match; match = match->next) if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; /* If there is no selected route or matched route is EGP, go up tree. */ if (! match || match->type == ZEBRA_ROUTE_BGP) { do { rn = rn->parent; } while (rn && rn->info == NULL); if (rn) route_lock_node (rn); } else { if (match->type == ZEBRA_ROUTE_CONNECT) { /* Directly point connected route. */ newhop = match->nexthop; if (newhop && nexthop->type == NEXTHOP_TYPE_IPV6) nexthop->ifindex = newhop->ifindex; return 1; } else if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_INTERNAL)) { for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB) && ! CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_RECURSIVE)) { if (set) { SET_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE); nexthop->rtype = newhop->type; if (newhop->type == NEXTHOP_TYPE_IPV6 || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) nexthop->rgate.ipv6 = newhop->gate.ipv6; if (newhop->type == NEXTHOP_TYPE_IFINDEX || newhop->type == NEXTHOP_TYPE_IFNAME || newhop->type == NEXTHOP_TYPE_IPV6_IFINDEX || newhop->type == NEXTHOP_TYPE_IPV6_IFNAME) nexthop->rifindex = newhop->ifindex; } return 1; } return 0; } else { return 0; } } } return 0; } #endif /* HAVE_IPV6 */ struct rib * rib_match_ipv4 (struct in_addr addr) { struct prefix_ipv4 p; struct route_node *rn; struct rib *match; struct nexthop *newhop; memset (&p, 0, sizeof (struct prefix_ipv4)); p.family = AF_INET; p.prefixlen = IPV4_MAX_PREFIXLEN; p.prefix = addr; rn = route_node_match (rib_table_ipv4, (struct prefix *) &p); while (rn) { route_unlock_node (rn); /* Pick up selected route. */ for (match = rn->info; match; match = match->next) if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; /* If there is no selected route or matched route is EGP, go up tree. */ if (! match || match->type == ZEBRA_ROUTE_BGP) { do { rn = rn->parent; } while (rn && rn->info == NULL); if (rn) route_lock_node (rn); } else { if (match->type == ZEBRA_ROUTE_CONNECT) /* Directly point connected route. */ return match; else { for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) return match; return NULL; } } } return NULL; } struct rib * rib_lookup_ipv4 (struct prefix_ipv4 *p) { struct route_node *rn; struct rib *match; struct nexthop *nexthop; rn = route_node_lookup (rib_table_ipv4, (struct prefix *) p); /* No route for this prefix. */ if (! rn) return NULL; /* Unlock node. */ route_unlock_node (rn); /* Pick up selected route. */ for (match = rn->info; match; match = match->next) if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; if (! match || match->type == ZEBRA_ROUTE_BGP) return NULL; if (match->type == ZEBRA_ROUTE_CONNECT) return match; for (nexthop = match->nexthop; nexthop; nexthop = nexthop->next) if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB)) return match; return NULL; } #ifdef HAVE_IPV6 struct rib * rib_match_ipv6 (struct in6_addr *addr) { struct prefix_ipv6 p; struct route_node *rn; struct rib *match; struct nexthop *newhop; memset (&p, 0, sizeof (struct prefix_ipv6)); p.family = AF_INET6; p.prefixlen = IPV6_MAX_PREFIXLEN; IPV6_ADDR_COPY (&p.prefix, addr); rn = route_node_match (rib_table_ipv6, (struct prefix *) &p); while (rn) { route_unlock_node (rn); /* Pick up selected route. */ for (match = rn->info; match; match = match->next) if (CHECK_FLAG (match->flags, ZEBRA_FLAG_SELECTED)) break; /* If there is no selected route or matched route is EGP, go up tree. */ if (! match || match->type == ZEBRA_ROUTE_BGP) { do { rn = rn->parent; } while (rn && rn->info == NULL); if (rn) route_lock_node (rn); } else { if (match->type == ZEBRA_ROUTE_CONNECT) /* Directly point connected route. */ return match; else { for (newhop = match->nexthop; newhop; newhop = newhop->next) if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)) return match; return NULL; } } } return NULL; } #endif /* HAVE_IPV6 */ int nexthop_active_check (struct route_node *rn, struct rib *rib, struct nexthop *nexthop, int set) { struct interface *ifp; switch (nexthop->type) { case NEXTHOP_TYPE_IFINDEX: ifp = if_lookup_by_index (nexthop->ifindex); if (ifp && if_is_up (ifp)) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; case NEXTHOP_TYPE_IFNAME: case NEXTHOP_TYPE_IPV6_IFNAME: ifp = if_lookup_by_name (nexthop->ifname); if (ifp && if_is_up (ifp)) { if (set) nexthop->ifindex = ifp->ifindex; SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); } else { if (set) nexthop->ifindex = 0; UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); } break; case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: if (nexthop_active_ipv4 (rib, nexthop, set, rn)) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; #ifdef HAVE_IPV6 case NEXTHOP_TYPE_IPV6: if (nexthop_active_ipv6 (rib, nexthop, set, rn)) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); break; case NEXTHOP_TYPE_IPV6_IFINDEX: if (IN6_IS_ADDR_LINKLOCAL (&nexthop->gate.ipv6)) { ifp = if_lookup_by_index (nexthop->ifindex); if (ifp && if_is_up (ifp)) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); } else { if (nexthop_active_ipv6 (rib, nexthop, set, rn)) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); else UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); } break; #endif /* HAVE_IPV6 */ default: break; } return CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); } int nexthop_active_update (struct route_node *rn, struct rib *rib, int set) { struct nexthop *nexthop; int active; rib->nexthop_active_num = 0; UNSET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { active = CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE); rib->nexthop_active_num += nexthop_active_check (rn, rib, nexthop, set); if (active != CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) SET_FLAG (rib->flags, ZEBRA_FLAG_CHANGED); } return rib->nexthop_active_num; } #define RIB_SYSTEM_ROUTE(R) \ ((R)->type == ZEBRA_ROUTE_KERNEL || (R)->type == ZEBRA_ROUTE_CONNECT) void newrib_free (struct rib *rib) { struct nexthop *nexthop; struct nexthop *next; for (nexthop = rib->nexthop; nexthop; nexthop = next) { next = nexthop->next; nexthop_free (nexthop); } XFREE (MTYPE_RIB, rib); } void rib_install_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; struct nexthop *nexthop; switch (PREFIX_FAMILY (&rn->p)) { case AF_INET: ret = kernel_add_ipv4 (&rn->p, rib); break; #ifdef HAVE_IPV6 case AF_INET6: ret = kernel_add_ipv6 (&rn->p, rib); break; #endif /* HAVE_IPV6 */ } if (ret < 0) { for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } } /* Uninstall the route from kernel. */ int rib_uninstall_kernel (struct route_node *rn, struct rib *rib) { int ret = 0; struct nexthop *nexthop; switch (PREFIX_FAMILY (&rn->p)) { case AF_INET: ret = kernel_delete_ipv4 (&rn->p, rib); break; #ifdef HAVE_IPV6 case AF_INET6: ret = kernel_delete_ipv6 (&rn->p, rib); break; #endif /* HAVE_IPV6 */ } for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); return ret; } /* Uninstall the route from kernel. */ void rib_uninstall (struct route_node *rn, struct rib *rib) { if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) { redistribute_delete (&rn->p, rib); if (! RIB_SYSTEM_ROUTE (rib)) rib_uninstall_kernel (rn, rib); UNSET_FLAG (rib->flags, ZEBRA_FLAG_SELECTED); } } /* Core function for processing routing information base. */ void rib_process (struct route_node *rn, struct rib *del) { struct rib *rib; struct rib *next; struct rib *fib = NULL; struct rib *select = NULL; for (rib = rn->info; rib; rib = next) { next = rib->next; /* Currently installed rib. */ if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) fib = rib; /* Skip unreachable nexthop. */ if (! nexthop_active_update (rn, rib, 0)) continue; /* Infinit distance. */ if (rib->distance == DISTANCE_INFINITY) continue; /* Newly selected rib. */ if (! select || rib->distance < select->distance || rib->type == ZEBRA_ROUTE_CONNECT) select = rib; } /* Deleted route check. */ if (del && CHECK_FLAG (del->flags, ZEBRA_FLAG_SELECTED)) fib = del; /* Same route is selected. */ if (select && select == fib) { if (CHECK_FLAG (select->flags, ZEBRA_FLAG_CHANGED)) { redistribute_delete (&rn->p, select); if (! RIB_SYSTEM_ROUTE (select)) rib_uninstall_kernel (rn, select); /* Set real nexthop. */ nexthop_active_update (rn, select, 1); if (! RIB_SYSTEM_ROUTE (select)) rib_install_kernel (rn, select); redistribute_add (&rn->p, select); } return; } /* Uninstall old rib from forwarding table. */ if (fib) { redistribute_delete (&rn->p, fib); if (! RIB_SYSTEM_ROUTE (fib)) rib_uninstall_kernel (rn, fib); UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); /* Set real nexthop. */ nexthop_active_update (rn, fib, 1); } /* Install new rib into forwarding table. */ if (select) { /* Set real nexthop. */ nexthop_active_update (rn, select, 1); if (! RIB_SYSTEM_ROUTE (select)) rib_install_kernel (rn, select); SET_FLAG (select->flags, ZEBRA_FLAG_SELECTED); redistribute_add (&rn->p, select); } } /* Add RIB to head of the route node. */ void rib_addnode (struct route_node *rn, struct rib *rib) { struct rib *head; head = rn->info; if (head) head->prev = rib; rib->next = head; rn->info = rib; } void rib_delnode (struct route_node *rn, struct rib *rib) { if (rib->next) rib->next->prev = rib->prev; if (rib->prev) rib->prev->next = rib->next; else rn->info = rib->next; } int rib_add_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, unsigned int ifindex, int table, u_int32_t metric, u_char distance) { struct rib *rib; struct rib *same = NULL; struct route_node *rn; struct nexthop *nexthop; /* Make it sure prefixlen is applied to the prefix. */ apply_mask_ipv4 (p); /* Set default distance by route type. */ if (distance == 0) { distance = route_info[type].distance; /* iBGP distance is 200. */ if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP)) distance = 200; } /* Lookup route node.*/ rn = route_node_get (rib_table_ipv4, (struct prefix *) p); /* If same type of route are installed, treat it as a implicit withdraw. */ for (rib = rn->info; rib; rib = rib->next) { if (rib->type == ZEBRA_ROUTE_CONNECT) { nexthop = rib->nexthop; /* Duplicate connected route comes in. */ if (rib->type == type && (! table || rib->table == table) && nexthop && nexthop->type == NEXTHOP_TYPE_IFINDEX && nexthop->ifindex == ifindex) { rib->refcnt++; return 0 ; } } else if (rib->type == type && (! table || rib->table == table)) { same = rib; rib_delnode (rn, same); route_unlock_node (rn); break; } } /* Allocate new rib structure. */ rib = XMALLOC (MTYPE_RIB, sizeof (struct rib)); memset (rib, 0, sizeof (struct rib)); rib->type = type; rib->distance = distance; rib->flags = flags; rib->metric = metric; rib->table = table; rib->nexthop_num = 0; rib->uptime = time (NULL); /* Nexthop settings. */ if (gate) { if (ifindex) nexthop_ipv4_ifindex_add (rib, gate, ifindex); else nexthop_ipv4_add (rib, gate); } else nexthop_ifindex_add (rib, ifindex); /* If this route is kernel route, set FIB flag to the route. */ if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT) for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); /* Link new rib to node.*/ rib_addnode (rn, rib); /* Process this route node. */ rib_process (rn, same); /* Free implicit route.*/ if (same) newrib_free (same); return 0; } int rib_add_ipv4_multipath (struct prefix_ipv4 *p, struct rib *rib) { struct route_node *rn; struct rib *same; struct nexthop *nexthop; /* Make it sure prefixlen is applied to the prefix. */ apply_mask_ipv4 (p); /* Set default distance by route type. */ if (rib->distance == 0) { rib->distance = route_info[rib->type].distance; /* iBGP distance is 200. */ if (rib->type == ZEBRA_ROUTE_BGP && CHECK_FLAG (rib->flags, ZEBRA_FLAG_IBGP)) rib->distance = 200; } /* Lookup route node.*/ rn = route_node_get (rib_table_ipv4, (struct prefix *) p); /* If same type of route are installed, treat it as a implicit withdraw. */ for (same = rn->info; same; same = same->next) { if (same->type == rib->type && same->table == rib->table && same->type != ZEBRA_ROUTE_CONNECT) { rib_delnode (rn, same); route_unlock_node (rn); break; } } /* If this route is kernel route, set FIB flag to the route. */ if (rib->type == ZEBRA_ROUTE_KERNEL || rib->type == ZEBRA_ROUTE_CONNECT) for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); /* Link new rib to node.*/ rib_addnode (rn, rib); /* Process this route node. */ rib_process (rn, same); /* Free implicit route.*/ if (same) newrib_free (same); return 0; } int rib_delete_ipv4 (int type, int flags, struct prefix_ipv4 *p, struct in_addr *gate, unsigned int ifindex, int table) { struct route_node *rn; struct rib *rib; struct rib *fib = NULL; struct rib *same = NULL; struct nexthop *nexthop; char buf1[BUFSIZ]; char buf2[BUFSIZ]; /* Apply mask. */ apply_mask_ipv4 (p); /* Lookup route node. */ rn = route_node_lookup (rib_table_ipv4, (struct prefix *) p); if (! rn) { if (IS_ZEBRA_DEBUG_KERNEL) { if (gate) zlog_info ("route %s/%d via %s ifindex %d doesn't exist in rib", inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ), p->prefixlen, inet_ntop (AF_INET, gate, buf2, BUFSIZ), ifindex); else zlog_info ("route %s/%d ifindex %d doesn't exist in rib", inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ), p->prefixlen, ifindex); } return ZEBRA_ERR_RTNOEXIST; } /* Lookup same type route. */ for (rib = rn->info; rib; rib = rib->next) { if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) fib = rib; if (rib->type == ZEBRA_ROUTE_CONNECT) { nexthop = rib->nexthop; if (rib->type == type && (! table || rib->table == table) && nexthop && nexthop->type == NEXTHOP_TYPE_IFINDEX && nexthop->ifindex == ifindex) { if (rib->refcnt) { rib->refcnt--; route_unlock_node (rn); route_unlock_node (rn); return 0; } same = rib; break; } } else { if (rib->type == type && (!table || rib->table == table)) { same = rib; break; } } } /* If same type of route can't be found and this message is from kernel. */ if (! same) { if (fib && type == ZEBRA_ROUTE_KERNEL) { /* Unset flags. */ for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); } else { if (IS_ZEBRA_DEBUG_KERNEL) { if (gate) zlog_info ("route %s/%d via %s ifindex %d type %d doesn't exist in rib", inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ), p->prefixlen, inet_ntop (AF_INET, gate, buf2, BUFSIZ), ifindex, type); else zlog_info ("route %s/%d ifindex %d type %d doesn't exist in rib", inet_ntop (AF_INET, &p->prefix, buf1, BUFSIZ), p->prefixlen, ifindex, type); } route_unlock_node (rn); return ZEBRA_ERR_RTNOEXIST; } } if (same) rib_delnode (rn, same); /* Process changes. */ rib_process (rn, same); if (same) { newrib_free (same); route_unlock_node (rn); } route_unlock_node (rn); return 0; } /* Delete all added route and close rib. */ void rib_close_ipv4 () { struct route_node *rn; struct rib *rib; for (rn = route_top (rib_table_ipv4); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) if (! RIB_SYSTEM_ROUTE (rib) && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) rib_uninstall_kernel (rn, rib); } /* Install static route into rib. */ void static_ipv4_install (struct prefix_ipv4 *p, struct static_ipv4 *si) { struct rib *rib; struct route_node *rn; /* Lookup existing route */ rn = route_node_get (rib_table_ipv4, (struct prefix *) p); for (rib = rn->info; rib; rib = rib->next) if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) break; if (rib) { /* Same distance static route is there. Update it with new nexthop. */ rib_uninstall (rn, rib); route_unlock_node (rn); switch (si->type) { case STATIC_IPV4_GATEWAY: nexthop_ipv4_add (rib, &si->gate.ipv4); break; case STATIC_IPV4_IFNAME: nexthop_ifname_add (rib, si->gate.ifname); break; } rib_process (rn, NULL); } else { /* This is new static route. */ rib = XMALLOC (MTYPE_RIB, sizeof (struct rib)); memset (rib, 0, sizeof (struct rib)); rib->type = ZEBRA_ROUTE_STATIC; rib->distance = si->distance; rib->metric = 0; rib->nexthop_num = 0; switch (si->type) { case STATIC_IPV4_GATEWAY: nexthop_ipv4_add (rib, &si->gate.ipv4); break; case STATIC_IPV4_IFNAME: nexthop_ifname_add (rib, si->gate.ifname); break; } /* Link this rib to the tree. */ rib_addnode (rn, rib); /* Process this prefix. */ rib_process (rn, NULL); } } int static_ipv4_nexthop_same (struct nexthop *nexthop, struct static_ipv4 *si) { if (nexthop->type == NEXTHOP_TYPE_IPV4 && si->type == STATIC_IPV4_GATEWAY && IPV4_ADDR_SAME (&nexthop->gate.ipv4, &si->gate.ipv4)) return 1; if (nexthop->type == NEXTHOP_TYPE_IFNAME && si->type == STATIC_IPV4_IFNAME && strcmp (nexthop->ifname, si->gate.ifname) == 0) return 1; return 0;; } /* Uninstall static route from RIB. */ void static_ipv4_uninstall (struct prefix_ipv4 *p, struct static_ipv4 *si) { struct route_node *rn; struct rib *rib; struct nexthop *nexthop; /* Lookup existing route with type and distance. */ rn = route_node_lookup (rib_table_ipv4, (struct prefix *) p); if (! rn) return; for (rib = rn->info; rib; rib = rib->next) if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) break; if (! rib) { route_unlock_node (rn); return; } /* Lookup nexthop. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) if (static_ipv4_nexthop_same (nexthop, si)) break; /* Can't find nexthop. */ if (! nexthop) { route_unlock_node (rn); return; } /* Check nexthop. */ if (rib->nexthop_num == 1) { rib_delnode (rn, rib); rib_process (rn, rib); newrib_free (rib); route_unlock_node (rn); } else { rib_uninstall (rn, rib); nexthop_delete (rib, nexthop); nexthop_free (nexthop); rib_process (rn, rib); } /* Unlock node. */ route_unlock_node (rn); } /* Add static route into static route configuration. */ int static_ipv4_add (struct prefix_ipv4 *p, struct in_addr *gate, char *ifname, u_char distance, int table) { u_char type = 0; struct route_node *rn; struct static_ipv4 *si; struct static_ipv4 *pp; struct static_ipv4 *cp; /* Lookup static route prefix. */ rn = route_node_get (static_table_ipv4, (struct prefix *) p); /* Make flags. */ if (gate) type = STATIC_IPV4_GATEWAY; if (ifname) type = STATIC_IPV4_IFNAME; /* Do nothing if there is a same static route. */ for (si = rn->info; si; si = si->next) { if (distance == si->distance && type == si->type && (! gate || IPV4_ADDR_SAME (gate, &si->gate.ipv4)) && (! ifname || strcmp (ifname, si->gate.ifname) == 0)) { route_unlock_node (rn); return 0; } } /* Make new static route structure. */ si = XMALLOC (MTYPE_STATIC_IPV4, sizeof (struct static_ipv4)); memset (si, 0, sizeof (struct static_ipv4)); si->type = type; si->distance = distance; if (gate) si->gate.ipv4 = *gate; if (ifname) si->gate.ifname = XSTRDUP (0, ifname); /* Add new static route information to the tree with sort by distance value and gateway address. */ for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next) { if (si->distance < cp->distance) break; if (si->distance > cp->distance) continue; if (si->type == STATIC_IPV4_GATEWAY && cp->type == STATIC_IPV4_GATEWAY) { if (ntohl (si->gate.ipv4.s_addr) < ntohl (cp->gate.ipv4.s_addr)) break; if (ntohl (si->gate.ipv4.s_addr) > ntohl (cp->gate.ipv4.s_addr)) continue; } } /* Make linked list. */ if (pp) pp->next = si; else rn->info = si; if (cp) cp->prev = si; si->prev = pp; si->next = cp; /* Install into rib. */ static_ipv4_install (p, si); return 1; } /* Delete static route from static route configuration. */ int static_ipv4_delete (struct prefix_ipv4 *p, struct in_addr *gate, char *ifname, u_char distance, int table) { u_char type = 0; struct route_node *rn; struct static_ipv4 *si; /* Lookup static route prefix. */ rn = route_node_lookup (static_table_ipv4, (struct prefix *) p); if (! rn) return 0; /* Make flags. */ if (gate) type = STATIC_IPV4_GATEWAY; if (ifname) type = STATIC_IPV4_IFNAME; /* Find same static route is the tree */ for (si = rn->info; si; si = si->next) if (distance == si->distance && type == si->type && (! gate || IPV4_ADDR_SAME (gate, &si->gate.ipv4)) && (! ifname || strcmp (ifname, si->gate.ifname) == 0)) break; /* Can't find static route. */ if (! si) { route_unlock_node (rn); return 0; } /* Install into rib. */ static_ipv4_uninstall (p, si); /* Unlink static route from linked list. */ if (si->prev) si->prev->next = si->next; else rn->info = si->next; if (si->next) si->next->prev = si->prev; /* Free static route configuration. */ XFREE (MTYPE_STATIC_IPV4, si); return 1; } /* Write IPv4 static route configuration. */ int static_ipv4_write (struct vty *vty) { struct route_node *rn; struct static_ipv4 *si; int write; write = 0; for (rn = route_top (static_table_ipv4); rn; rn = route_next (rn)) for (si = rn->info; si; si = si->next) { vty_out (vty, "ip route %s/%d", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen); switch (si->type) { case STATIC_IPV4_GATEWAY: vty_out (vty, " %s", inet_ntoa (si->gate.ipv4)); break; case STATIC_IPV4_IFNAME: vty_out (vty, " %s", si->gate.ifname); break; } if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) vty_out (vty, " %d", si->distance); vty_out (vty, "%s", VTY_NEWLINE); write = 1; } return write; } /* General fucntion for static route. */ int static_ipv4_func (struct vty *vty, int add_cmd, char *dest_str, char *mask_str, char *gate_str, char *distance_str) { int ret; u_char distance; struct prefix_ipv4 p; struct in_addr gate; struct in_addr mask; char *ifname; int table = rtm_table_default; ret = str2prefix_ipv4 (dest_str, &p); if (ret <= 0) { vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); return CMD_WARNING; } /* Cisco like mask notation. */ if (mask_str) { ret = inet_aton (mask_str, &mask); if (ret == 0) { vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); return CMD_WARNING; } p.prefixlen = ip_masklen (mask); } /* Apply mask for given prefix. */ apply_mask_ipv4 (&p); /* Administrative distance. */ if (distance_str) distance = atoi (distance_str); else distance = ZEBRA_STATIC_DISTANCE_DEFAULT; /* When gateway is A.B.C.D format, gate is treated as nexthop address other case gate is treated as interface name. */ ret = inet_aton (gate_str, &gate); if (ret) ifname = NULL; else ifname = gate_str; if (add_cmd) static_ipv4_add (&p, ifname ? NULL : &gate, ifname, distance, table); else static_ipv4_delete (&p, ifname ? NULL : &gate, ifname, distance, table); return CMD_SUCCESS; } /* Static route configuration. */ DEFUN (ip_route, ip_route_cmd, "ip route A.B.C.D/M (A.B.C.D|INTERFACE)", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n") { return static_ipv4_func (vty, 1, argv[0], NULL, argv[1], NULL); } DEFUN (ip_route_mask, ip_route_mask_cmd, "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE)", IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n") { return static_ipv4_func (vty, 1, argv[0], argv[1], argv[2], NULL); } DEFUN (ip_route_pref, ip_route_pref_cmd, "ip route A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>", IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" "Distance value for this route\n") { return static_ipv4_func (vty, 1, argv[0], NULL, argv[1], argv[2]); } DEFUN (ip_route_mask_pref, ip_route_mask_pref_cmd, "ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) <1-255>", IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" "Distance value for this route\n") { return static_ipv4_func (vty, 1, argv[0], argv[1], argv[2], argv[3]); } DEFUN (no_ip_route, no_ip_route_cmd, "no ip route A.B.C.D/M (A.B.C.D|INTERFACE)", NO_STR IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n") { return static_ipv4_func (vty, 0, argv[0], NULL, argv[1], NULL); } DEFUN (no_ip_route_mask, no_ip_route_mask_cmd, "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE)", NO_STR IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n") { return static_ipv4_func (vty, 0, argv[0], argv[1], argv[2], NULL); } DEFUN (no_ip_route_pref, no_ip_route_pref_cmd, "no ip route A.B.C.D/M (A.B.C.D|INTERFACE) <1-255>", NO_STR IP_STR "Establish static routes\n" "IP destination prefix (e.g. 10.0.0.0/8)\n" "IP gateway address\n" "IP gateway interface name\n" "Distance value for this route\n") { return static_ipv4_func (vty, 0, argv[0], NULL, argv[1], argv[2]); } DEFUN (no_ip_route_mask_pref, no_ip_route_mask_pref_cmd, "no ip route A.B.C.D A.B.C.D (A.B.C.D|INTERFACE) <1-255>", NO_STR IP_STR "Establish static routes\n" "IP destination prefix\n" "IP destination prefix mask\n" "IP gateway address\n" "IP gateway interface name\n" "Distance value for this route\n") { return static_ipv4_func (vty, 0, argv[0], argv[1], argv[2], argv[3]); } /* New RIB. Detailed information for IPv4 route. */ void vty_show_ip_route_detail (struct vty *vty, struct route_node *rn) { struct rib *rib; struct nexthop *nexthop; for (rib = rn->info; rib; rib = rib->next) { vty_out (vty, "Routing entry for %s/%d%s", inet_ntoa (rn->p.u.prefix4), rn->p.prefixlen, VTY_NEWLINE); vty_out (vty, " Known via \"%s\"", route_info[rib->type].str); vty_out (vty, ", distance %d, metric %d", rib->distance, rib->metric); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) vty_out (vty, ", best"); if (rib->refcnt) vty_out (vty, ", refcnt %ld", rib->refcnt); vty_out (vty, "%s", VTY_NEWLINE); #define ONE_DAY_SECOND 60*60*24 #define ONE_WEEK_SECOND 60*60*24*7 if (rib->type == ZEBRA_ROUTE_RIP || rib->type == ZEBRA_ROUTE_OSPF || rib->type == ZEBRA_ROUTE_ISIS || rib->type == ZEBRA_ROUTE_BGP) { time_t uptime; struct tm *tm; uptime = time (NULL); uptime -= rib->uptime; tm = gmtime (&uptime); vty_out (vty, " Last update "); if (uptime < ONE_DAY_SECOND) vty_out (vty, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); else if (uptime < ONE_WEEK_SECOND) vty_out (vty, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, tm->tm_min); else vty_out (vty, "%02dw%dd%02dh", tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); vty_out (vty, " ago%s", VTY_NEWLINE); } for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { vty_out (vty, " %c", CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' '); switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: vty_out (vty, " %s", inet_ntoa (nexthop->gate.ipv4)); if (nexthop->ifindex) vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex)); break; case NEXTHOP_TYPE_IFINDEX: vty_out (vty, " directly connected, %s", ifindex2ifname (nexthop->ifindex)); break; case NEXTHOP_TYPE_IFNAME: vty_out (vty, " directly connected, %s", nexthop->ifname); break; default: break; } if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { vty_out (vty, " (recursive"); switch (nexthop->rtype) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: vty_out (vty, " via %s)", inet_ntoa (nexthop->rgate.ipv4)); break; case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: vty_out (vty, " is directly connected, %s)", ifindex2ifname (nexthop->rifindex)); break; default: break; } } vty_out (vty, "%s", VTY_NEWLINE); } vty_out (vty, "%s", VTY_NEWLINE); } } void vty_show_ip_route (struct vty *vty, struct route_node *rn, struct rib *rib) { struct nexthop *nexthop; int len = 0; char buf[BUFSIZ]; /* Nexthop information. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { if (nexthop == rib->nexthop) { /* Prefix information. */ len = vty_out (vty, "%c%c%c %s/%d", route_info[rib->type].c, CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ', CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', inet_ntop (AF_INET, &rn->p.u.prefix, buf, BUFSIZ), rn->p.prefixlen); /* Distance and metric display. */ if (rib->type != ZEBRA_ROUTE_CONNECT && rib->type != ZEBRA_ROUTE_KERNEL) len += vty_out (vty, " [%d/%d]", rib->distance, rib->metric); } else vty_out (vty, " %c%*c", CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', len - 3, ' '); switch (nexthop->type) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: vty_out (vty, " via %s", inet_ntoa (nexthop->gate.ipv4)); if (nexthop->ifindex) vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex)); break; case NEXTHOP_TYPE_IFINDEX: vty_out (vty, " is directly connected, %s", ifindex2ifname (nexthop->ifindex)); break; case NEXTHOP_TYPE_IFNAME: vty_out (vty, " is directly connected, %s", nexthop->ifname); break; default: break; } if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { vty_out (vty, " (recursive"); switch (nexthop->rtype) { case NEXTHOP_TYPE_IPV4: case NEXTHOP_TYPE_IPV4_IFINDEX: vty_out (vty, " via %s)", inet_ntoa (nexthop->rgate.ipv4)); break; case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: vty_out (vty, " is directly connected, %s)", ifindex2ifname (nexthop->rifindex)); break; default: break; } } if (rib->type == ZEBRA_ROUTE_RIP || rib->type == ZEBRA_ROUTE_OSPF || rib->type == ZEBRA_ROUTE_ISIS || rib->type == ZEBRA_ROUTE_BGP) { time_t uptime; struct tm *tm; uptime = time (NULL); uptime -= rib->uptime; tm = gmtime (&uptime); #define ONE_DAY_SECOND 60*60*24 #define ONE_WEEK_SECOND 60*60*24*7 if (uptime < ONE_DAY_SECOND) vty_out (vty, ", %02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); else if (uptime < ONE_WEEK_SECOND) vty_out (vty, ", %dd%02dh%02dm", tm->tm_yday, tm->tm_hour, tm->tm_min); else vty_out (vty, ", %02dw%dd%02dh", tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); } vty_out (vty, "%s", VTY_NEWLINE); } } #define SHOW_ROUTE_V4_HEADER "Codes: K - kernel route, C - connected, S - static, R - RIP, O - OSPF,%s I - IS-IS, %s B - BGP, > - selected route, * - FIB route%s%s" DEFUN (show_ip_route, show_ip_route_cmd, "show ip route", SHOW_STR IP_STR "IP routing table\n") { struct route_node *rn; struct rib *rib; int first = 1; /* Show all IPv4 routes. */ for (rn = route_top (rib_table_ipv4); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) { if (first) { vty_out (vty, SHOW_ROUTE_V4_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); first = 0; } vty_show_ip_route (vty, rn, rib); } return CMD_SUCCESS; } DEFUN (show_ip_route_prefix_longer, show_ip_route_prefix_longer_cmd, "show ip route A.B.C.D/M longer-prefixes", SHOW_STR IP_STR "IP routing table\n" "IP prefix /, e.g., 35.0.0.0/8\n" "Show route matching the specified Network/Mask pair only\n") { struct route_node *rn; struct rib *rib; struct prefix p; int ret; int first = 1; ret = str2prefix (argv[0], &p); if (! ret) { vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE); return CMD_WARNING; } /* Show matched type IPv4 routes. */ for (rn = route_top (rib_table_ipv4); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) if (prefix_match (&p, &rn->p)) { if (first) { vty_out (vty, SHOW_ROUTE_V4_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); first = 0; } vty_show_ip_route (vty, rn, rib); } return CMD_SUCCESS; } DEFUN (show_ip_route_supernets, show_ip_route_supernets_cmd, "show ip route supernets-only", SHOW_STR IP_STR "IP routing table\n" "Show supernet entries only\n") { struct route_node *rn; struct rib *rib; u_int32_t addr; int first = 1; /* Show matched type IPv4 routes. */ for (rn = route_top (rib_table_ipv4); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) { addr = ntohl (rn->p.u.prefix4.s_addr); if ((IN_CLASSC (addr) && rn->p.prefixlen < 24) || (IN_CLASSB (addr) && rn->p.prefixlen < 16) || (IN_CLASSA (addr) && rn->p.prefixlen < 8)) { if (first) { vty_out (vty, SHOW_ROUTE_V4_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); first = 0; } vty_show_ip_route (vty, rn, rib); } } return CMD_SUCCESS; } DEFUN (show_ip_route_protocol, show_ip_route_protocol_cmd, "show ip route (bgp|connected|kernel|ospf|isis|rip|static)", SHOW_STR IP_STR "IP routing table\n" "Border Gateway Protocol (BGP)\n" "Connected\n" "Kernel\n" "Open Shortest Path First (OSPF)\n" "ISO IS-IS (ISIS)\n" "Routing Information Protocol (RIP)\n" "Static routes\n") { int type; struct route_node *rn; struct rib *rib; int first = 1; if (strncmp (argv[0], "b", 1) == 0) type = ZEBRA_ROUTE_BGP; else if (strncmp (argv[0], "c", 1) == 0) type = ZEBRA_ROUTE_CONNECT; else if (strncmp (argv[0], "k", 1) ==0) type = ZEBRA_ROUTE_KERNEL; else if (strncmp (argv[0], "o", 1) == 0) type = ZEBRA_ROUTE_OSPF; else if (strncmp (argv[0], "i", 1) == 0) type = ZEBRA_ROUTE_ISIS; else if (strncmp (argv[0], "r", 1) == 0) type = ZEBRA_ROUTE_RIP; else if (strncmp (argv[0], "s", 1) == 0) type = ZEBRA_ROUTE_STATIC; else { vty_out (vty, "Unknown route type%s", VTY_NEWLINE); return CMD_WARNING; } /* Show matched type IPv4 routes. */ for (rn = route_top (rib_table_ipv4); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) if (rib->type == type) { if (first) { vty_out (vty, SHOW_ROUTE_V4_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); first = 0; } vty_show_ip_route (vty, rn, rib); } return CMD_SUCCESS; } DEFUN (show_ip_route_addr, show_ip_route_addr_cmd, "show ip route A.B.C.D", SHOW_STR IP_STR "IP routing table\n" "Network in the IP routing table to display\n") { int ret; struct prefix_ipv4 p; struct route_node *rn; ret = str2prefix_ipv4 (argv[0], &p); if (ret <= 0) { vty_out (vty, "Malformed IPv4 address%s", VTY_NEWLINE); return CMD_WARNING; } rn = route_node_match (rib_table_ipv4, (struct prefix *) &p); if (! rn) { vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); return CMD_WARNING; } vty_show_ip_route_detail (vty, rn); route_unlock_node (rn); return CMD_SUCCESS; } DEFUN (show_ip_route_prefix, show_ip_route_prefix_cmd, "show ip route A.B.C.D/M", SHOW_STR IP_STR "IP routing table\n" "IP prefix /, e.g., 35.0.0.0/8\n") { int ret; struct prefix_ipv4 p; struct route_node *rn; ret = str2prefix_ipv4 (argv[0], &p); if (ret <= 0) { vty_out (vty, "Malformed IPv4 address%s", VTY_NEWLINE); return CMD_WARNING; } rn = route_node_match (rib_table_ipv4, (struct prefix *) &p); if (! rn || rn->p.prefixlen != p.prefixlen) { vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); return CMD_WARNING; } vty_show_ip_route_detail (vty, rn); route_unlock_node (rn); return CMD_SUCCESS; } #ifdef HAVE_IPV6 int rib_bogus_ipv6 (int type, struct prefix_ipv6 *p, struct in6_addr *gate, unsigned int ifindex, int table) { if (type == ZEBRA_ROUTE_CONNECT && IN6_IS_ADDR_UNSPECIFIED (&p->prefix)) return 1; if (type == ZEBRA_ROUTE_KERNEL && IN6_IS_ADDR_UNSPECIFIED (&p->prefix) && p->prefixlen == 96 && gate && IN6_IS_ADDR_UNSPECIFIED (gate)) { kernel_delete_ipv6_old (p, gate, ifindex, 0, table); return 1; } return 0; } int rib_add_ipv6 (int type, int flags, struct prefix_ipv6 *p, struct in6_addr *gate, unsigned int ifindex, int table) { struct rib *rib; struct rib *same = NULL; struct route_node *rn; struct nexthop *nexthop; int distance; u_int32_t metric = 0; /* Make sure mask is applied. */ apply_mask_ipv6 (p); /* Set default distance by route type. */ distance = route_info[type].distance; if (type == ZEBRA_ROUTE_BGP && CHECK_FLAG (flags, ZEBRA_FLAG_IBGP)) distance = 200; /* Make new rib. */ if (!table) table = RT_TABLE_MAIN; /* Filter bogus route. */ if (rib_bogus_ipv6 (type, p, gate, ifindex, table)) return 0; /* Lookup route node.*/ rn = route_node_get (rib_table_ipv6, (struct prefix *) p); /* If same type of route are installed, treat it as a implicit withdraw. */ for (rib = rn->info; rib; rib = rib->next) { if (rib->type == ZEBRA_ROUTE_CONNECT) { nexthop = rib->nexthop; if (rib->type == type && (! table || rib->table == table) && nexthop && nexthop->type == NEXTHOP_TYPE_IFINDEX && nexthop->ifindex == ifindex) { rib->refcnt++; return 0; } } else if (rib->type == type && (! table || (rib->table == table))) { same = rib; rib_delnode (rn, same); route_unlock_node (rn); break; } } /* Allocate new rib structure. */ rib = XMALLOC (MTYPE_RIB, sizeof (struct rib)); memset (rib, 0, sizeof (struct rib)); rib->type = type; rib->distance = distance; rib->flags = flags; rib->metric = metric; rib->table = table; rib->nexthop_num = 0; rib->uptime = time (NULL); /* Nexthop settings. */ if (gate) { if (ifindex) nexthop_ipv6_ifindex_add (rib, gate, ifindex); else nexthop_ipv6_add (rib, gate); } else nexthop_ifindex_add (rib, ifindex); /* If this route is kernel route, set FIB flag to the route. */ if (type == ZEBRA_ROUTE_KERNEL || type == ZEBRA_ROUTE_CONNECT) for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); /* Link new rib to node.*/ rib_addnode (rn, rib); /* Process this route node. */ rib_process (rn, same); /* Free implicit route.*/ if (same) newrib_free (same); return 0; } int rib_delete_ipv6 (int type, int flags, struct prefix_ipv6 *p, struct in6_addr *gate, unsigned int ifindex, int table) { struct route_node *rn; struct rib *rib; struct rib *fib = NULL; struct rib *same = NULL; struct nexthop *nexthop; char buf1[BUFSIZ]; char buf2[BUFSIZ]; /* Apply mask. */ apply_mask_ipv6 (p); /* Lookup route node. */ rn = route_node_lookup (rib_table_ipv6, (struct prefix *) p); if (! rn) { if (IS_ZEBRA_DEBUG_KERNEL) { if (gate) zlog_info ("route %s/%d via %s ifindex %d doesn't exist in rib", inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ), p->prefixlen, inet_ntop (AF_INET6, gate, buf2, BUFSIZ), ifindex); else zlog_info ("route %s/%d ifindex %d doesn't exist in rib", inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ), p->prefixlen, ifindex); } return ZEBRA_ERR_RTNOEXIST; } /* Lookup same type route. */ for (rib = rn->info; rib; rib = rib->next) { if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) fib = rib; if (rib->type == ZEBRA_ROUTE_CONNECT) { nexthop = rib->nexthop; if (rib->type == type && (! table || rib->table == table) && nexthop && nexthop->type == NEXTHOP_TYPE_IFINDEX && nexthop->ifindex == ifindex) { if (rib->refcnt) { rib->refcnt--; route_unlock_node (rn); route_unlock_node (rn); return 0; } same = rib; break; } } else { if (rib->type == type && (! table || rib->table == table)) { same = rib; break; } } } /* If same type of route can't be found and this message is from kernel. */ if (! same) { if (fib && type == ZEBRA_ROUTE_KERNEL) { /* Unset flags. */ for (nexthop = fib->nexthop; nexthop; nexthop = nexthop->next) UNSET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); UNSET_FLAG (fib->flags, ZEBRA_FLAG_SELECTED); } else { if (IS_ZEBRA_DEBUG_KERNEL) { if (gate) zlog_info ("route %s/%d via %s ifindex %d type %d doesn't exist in rib", inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ), p->prefixlen, inet_ntop (AF_INET6, gate, buf2, BUFSIZ), ifindex, type); else zlog_info ("route %s/%d ifindex %d type %d doesn't exist in rib", inet_ntop (AF_INET6, &p->prefix, buf1, BUFSIZ), p->prefixlen, ifindex, type); } route_unlock_node (rn); return ZEBRA_ERR_RTNOEXIST; } } if (same) rib_delnode (rn, same); /* Process changes. */ rib_process (rn, same); if (same) { newrib_free (same); route_unlock_node (rn); } route_unlock_node (rn); return 0; } /* Delete non system routes. */ void rib_close_ipv6 () { struct route_node *rn; struct rib *rib; for (rn = route_top (rib_table_ipv6); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) if (! RIB_SYSTEM_ROUTE (rib) && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) rib_uninstall_kernel (rn, rib); } /* Install static route into rib. */ void static_ipv6_install (struct prefix_ipv6 *p, struct static_ipv6 *si) { struct rib *rib; struct route_node *rn; /* Lookup existing route */ rn = route_node_get (rib_table_ipv6, (struct prefix *) p); for (rib = rn->info; rib; rib = rib->next) if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) break; if (rib) { /* Same distance static route is there. Update it with new nexthop. */ rib_uninstall (rn, rib); route_unlock_node (rn); switch (si->type) { case STATIC_IPV6_GATEWAY: nexthop_ipv6_add (rib, &si->ipv6); break; case STATIC_IPV6_IFNAME: nexthop_ifname_add (rib, si->ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: nexthop_ipv6_ifname_add (rib, &si->ipv6, si->ifname); break; } rib_process (rn, NULL); } else { /* This is new static route. */ rib = XMALLOC (MTYPE_RIB, sizeof (struct rib)); memset (rib, 0, sizeof (struct rib)); rib->type = ZEBRA_ROUTE_STATIC; rib->distance = si->distance; rib->metric = 0; rib->nexthop_num = 0; switch (si->type) { case STATIC_IPV6_GATEWAY: nexthop_ipv6_add (rib, &si->ipv6); break; case STATIC_IPV6_IFNAME: nexthop_ifname_add (rib, si->ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: nexthop_ipv6_ifname_add (rib, &si->ipv6, si->ifname); break; } /* Link this rib to the tree. */ rib_addnode (rn, rib); /* Process this prefix. */ rib_process (rn, NULL); } } int static_ipv6_nexthop_same (struct nexthop *nexthop, struct static_ipv6 *si) { if (nexthop->type == NEXTHOP_TYPE_IPV6 && si->type == STATIC_IPV6_GATEWAY && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->ipv6)) return 1; if (nexthop->type == NEXTHOP_TYPE_IFNAME && si->type == STATIC_IPV6_IFNAME && strcmp (nexthop->ifname, si->ifname) == 0) return 1; if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME && si->type == STATIC_IPV6_GATEWAY_IFNAME && IPV6_ADDR_SAME (&nexthop->gate.ipv6, &si->ipv6) && strcmp (nexthop->ifname, si->ifname) == 0) return 1; return 0;; } void static_ipv6_uninstall (struct prefix_ipv6 *p, struct static_ipv6 *si) { struct route_node *rn; struct rib *rib; struct nexthop *nexthop; /* Lookup existing route with type and distance. */ rn = route_node_lookup (rib_table_ipv6, (struct prefix *) p); if (! rn) return; for (rib = rn->info; rib; rib = rib->next) if (rib->type == ZEBRA_ROUTE_STATIC && rib->distance == si->distance) break; if (! rib) { route_unlock_node (rn); return; } /* Lookup nexthop. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) if (static_ipv6_nexthop_same (nexthop, si)) break; /* Can't find nexthop. */ if (! nexthop) { route_unlock_node (rn); return; } /* Check nexthop. */ if (rib->nexthop_num == 1) { rib_delnode (rn, rib); rib_process (rn, rib); newrib_free (rib); route_unlock_node (rn); } else { rib_uninstall (rn, rib); nexthop_delete (rib, nexthop); nexthop_free (nexthop); rib_process (rn, rib); } /* Unlock node. */ route_unlock_node (rn); } /* Add static route into static route configuration. */ int static_ipv6_add (struct prefix_ipv6 *p, u_char type, struct in6_addr *gate, char *ifname, u_char distance, int table) { struct route_node *rn; struct static_ipv6 *si; struct static_ipv6 *pp; struct static_ipv6 *cp; /* Lookup static route prefix. */ rn = route_node_get (static_table_ipv6, (struct prefix *) p); /* Do nothing if there is a same static route. */ for (si = rn->info; si; si = si->next) { if (distance == si->distance && type == si->type && (! gate || IPV6_ADDR_SAME (gate, &si->ipv6)) && (! ifname || strcmp (ifname, si->ifname) == 0)) { route_unlock_node (rn); return 0; } } /* Make new static route structure. */ si = XMALLOC (MTYPE_STATIC_IPV6, sizeof (struct static_ipv6)); memset (si, 0, sizeof (struct static_ipv6)); si->type = type; si->distance = distance; switch (type) { case STATIC_IPV6_GATEWAY: si->ipv6 = *gate; break; case STATIC_IPV6_IFNAME: si->ifname = XSTRDUP (0, ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: si->ipv6 = *gate; si->ifname = XSTRDUP (0, ifname); break; } /* Add new static route information to the tree with sort by distance value and gateway address. */ for (pp = NULL, cp = rn->info; cp; pp = cp, cp = cp->next) { if (si->distance < cp->distance) break; if (si->distance > cp->distance) continue; } /* Make linked list. */ if (pp) pp->next = si; else rn->info = si; if (cp) cp->prev = si; si->prev = pp; si->next = cp; /* Install into rib. */ static_ipv6_install (p, si); return 1; } /* Delete static route from static route configuration. */ int static_ipv6_delete (struct prefix_ipv6 *p, u_char type, struct in6_addr *gate, char *ifname, u_char distance, int table) { struct route_node *rn; struct static_ipv6 *si; /* Lookup static route prefix. */ rn = route_node_lookup (static_table_ipv6, (struct prefix *) p); if (! rn) return 0; /* Find same static route is the tree */ for (si = rn->info; si; si = si->next) if (distance == si->distance && type == si->type && (! gate || IPV6_ADDR_SAME (gate, &si->ipv6)) && (! ifname || strcmp (ifname, si->ifname) == 0)) break; /* Can't find static route. */ if (! si) { route_unlock_node (rn); return 0; } /* Install into rib. */ static_ipv6_uninstall (p, si); /* Unlink static route from linked list. */ if (si->prev) si->prev->next = si->next; else rn->info = si->next; if (si->next) si->next->prev = si->prev; /* Free static route configuration. */ XFREE (MTYPE_STATIC_IPV6, si); return 1; } /* General fucntion for IPv6 static route. */ int static_ipv6_func (struct vty *vty, int add_cmd, char *dest_str, char *gate_str, char *ifname, char *distance_str) { int ret; u_char distance; struct prefix_ipv6 p; struct in6_addr *gate = NULL; struct in6_addr gate_addr; u_char type = 0; int table = rtm_table_default; ret = str2prefix_ipv6 (dest_str, &p); if (ret <= 0) { vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); return CMD_WARNING; } /* Apply mask for given prefix. */ apply_mask_ipv6 (&p); /* Administrative distance. */ if (distance_str) distance = atoi (distance_str); else distance = ZEBRA_STATIC_DISTANCE_DEFAULT; /* When gateway is valid IPv6 addrees, then gate is treated as nexthop address other case gate is treated as interface name. */ ret = inet_pton (AF_INET6, gate_str, &gate_addr); if (ifname) { /* When ifname is specified. It must be come with gateway address. */ if (ret != 1) { vty_out (vty, "%% Malformed address%s", VTY_NEWLINE); return CMD_WARNING; } type = STATIC_IPV6_GATEWAY_IFNAME; gate = &gate_addr; } else { if (ret == 1) { type = STATIC_IPV6_GATEWAY; gate = &gate_addr; } else { type = STATIC_IPV6_IFNAME; ifname = gate_str; } } if (add_cmd) static_ipv6_add (&p, type, gate, ifname, distance, table); else static_ipv6_delete (&p, type, gate, ifname, distance, table); return CMD_SUCCESS; } /* Write IPv6 static route configuration. */ int static_ipv6_write (struct vty *vty) { struct route_node *rn; struct static_ipv6 *si; int write; char buf[BUFSIZ]; write = 0; for (rn = route_top (static_table_ipv6); rn; rn = route_next (rn)) for (si = rn->info; si; si = si->next) { vty_out (vty, "ipv6 route %s/%d", inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ), rn->p.prefixlen); switch (si->type) { case STATIC_IPV6_GATEWAY: vty_out (vty, " %s", inet_ntop (AF_INET6, &si->ipv6, buf, BUFSIZ)); break; case STATIC_IPV6_IFNAME: vty_out (vty, " %s", si->ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: vty_out (vty, " %s %s", inet_ntop (AF_INET6, &si->ipv6, buf, BUFSIZ), si->ifname); break; } if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) vty_out (vty, " %d", si->distance); vty_out (vty, "%s", VTY_NEWLINE); write = 1; } return write; } DEFUN (ipv6_route, ipv6_route_cmd, "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n") { return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, NULL); } DEFUN (ipv6_route_ifname, ipv6_route_ifname_cmd, "ipv6 route X:X::X:X/M X:X::X:X INTERFACE", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n") { return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], NULL); } DEFUN (ipv6_route_pref, ipv6_route_pref_cmd, "ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Distance value for this prefix\n") { return static_ipv6_func (vty, 1, argv[0], argv[1], NULL, argv[2]); } DEFUN (ipv6_route_ifname_pref, ipv6_route_ifname_pref_cmd, "ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Distance value for this prefix\n") { return static_ipv6_func (vty, 1, argv[0], argv[1], argv[2], argv[3]); } DEFUN (no_ipv6_route, no_ipv6_route_cmd, "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE)", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n") { return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, NULL); } DEFUN (no_ipv6_route_ifname, no_ipv6_route_ifname_cmd, "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n") { return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], NULL); } DEFUN (no_ipv6_route_pref, no_ipv6_route_pref_cmd, "no ipv6 route X:X::X:X/M (X:X::X:X|INTERFACE) <1-255>", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Distance value for this prefix\n") { return static_ipv6_func (vty, 0, argv[0], argv[1], NULL, argv[2]); } DEFUN (no_ipv6_route_ifname_pref, no_ipv6_route_ifname_pref_cmd, "no ipv6 route X:X::X:X/M X:X::X:X INTERFACE <1-255>", NO_STR IP_STR "Establish static routes\n" "IPv6 destination prefix (e.g. 3ffe:506::/32)\n" "IPv6 gateway address\n" "IPv6 gateway interface name\n" "Distance value for this prefix\n") { return static_ipv6_func (vty, 0, argv[0], argv[1], argv[2], argv[3]); } /* New RIB. Detailed information for IPv4 route. */ void vty_show_ipv6_route_detail (struct vty *vty, struct route_node *rn) { struct rib *rib; struct nexthop *nexthop; char buf[BUFSIZ]; for (rib = rn->info; rib; rib = rib->next) { vty_out (vty, "Routing entry for %s/%d%s", inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ), rn->p.prefixlen, VTY_NEWLINE); vty_out (vty, " Known via \"%s\"", route_info[rib->type].str); vty_out (vty, ", distance %d, metric %d", rib->distance, rib->metric); if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED)) vty_out (vty, ", best"); if (rib->refcnt) vty_out (vty, ", refcnt %ld", rib->refcnt); vty_out (vty, "%s", VTY_NEWLINE); #define ONE_DAY_SECOND 60*60*24 #define ONE_WEEK_SECOND 60*60*24*7 if (rib->type == ZEBRA_ROUTE_RIPNG || rib->type == ZEBRA_ROUTE_OSPF6 || rib->type == ZEBRA_ROUTE_ISIS || rib->type == ZEBRA_ROUTE_BGP) { time_t uptime; struct tm *tm; uptime = time (NULL); uptime -= rib->uptime; tm = gmtime (&uptime); vty_out (vty, " Last update "); if (uptime < ONE_DAY_SECOND) vty_out (vty, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); else if (uptime < ONE_WEEK_SECOND) vty_out (vty, "%dd%02dh%02dm", tm->tm_yday, tm->tm_hour, tm->tm_min); else vty_out (vty, "%02dw%dd%02dh", tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); vty_out (vty, " ago%s", VTY_NEWLINE); } for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { vty_out (vty, " %c", CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' '); switch (nexthop->type) { case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6_IFNAME: vty_out (vty, " %s", inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) vty_out (vty, ", %s", nexthop->ifname); else if (nexthop->ifindex) vty_out (vty, ", via %s", ifindex2ifname (nexthop->ifindex)); break; case NEXTHOP_TYPE_IFINDEX: vty_out (vty, " directly connected, %s", ifindex2ifname (nexthop->ifindex)); break; case NEXTHOP_TYPE_IFNAME: vty_out (vty, " directly connected, %s", nexthop->ifname); break; default: break; } if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { vty_out (vty, " (recursive"); switch (nexthop->rtype) { case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6_IFNAME: vty_out (vty, " via %s)", inet_ntop (AF_INET6, &nexthop->rgate.ipv6, buf, BUFSIZ)); if (nexthop->rifindex) vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); break; case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: vty_out (vty, " is directly connected, %s)", ifindex2ifname (nexthop->rifindex)); break; default: break; } } vty_out (vty, "%s", VTY_NEWLINE); } vty_out (vty, "%s", VTY_NEWLINE); } } void vty_show_ipv6_route (struct vty *vty, struct route_node *rn, struct rib *rib) { struct nexthop *nexthop; int len = 0; char buf[BUFSIZ]; /* Nexthop information. */ for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { if (nexthop == rib->nexthop) { /* Prefix information. */ len = vty_out (vty, "%c%c%c %s/%d", route_info[rib->type].c, CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELECTED) ? '>' : ' ', CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', inet_ntop (AF_INET6, &rn->p.u.prefix6, buf, BUFSIZ), rn->p.prefixlen); /* Distance and metric display. */ if (rib->type != ZEBRA_ROUTE_CONNECT && rib->type != ZEBRA_ROUTE_KERNEL) len += vty_out (vty, " [%d/%d]", rib->distance, rib->metric); } else vty_out (vty, " %c%*c", CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB) ? '*' : ' ', len - 3, ' '); switch (nexthop->type) { case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6_IFNAME: vty_out (vty, " via %s", inet_ntop (AF_INET6, &nexthop->gate.ipv6, buf, BUFSIZ)); if (nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) vty_out (vty, ", %s", nexthop->ifname); else if (nexthop->ifindex) vty_out (vty, ", %s", ifindex2ifname (nexthop->ifindex)); break; case NEXTHOP_TYPE_IFINDEX: vty_out (vty, " is directly connected, %s", ifindex2ifname (nexthop->ifindex)); break; case NEXTHOP_TYPE_IFNAME: vty_out (vty, " is directly connected, %s", nexthop->ifname); break; default: break; } if (! CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) vty_out (vty, " inactive"); if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { vty_out (vty, " (recursive"); switch (nexthop->rtype) { case NEXTHOP_TYPE_IPV6: case NEXTHOP_TYPE_IPV6_IFINDEX: case NEXTHOP_TYPE_IPV6_IFNAME: vty_out (vty, " via %s)", inet_ntop (AF_INET6, &nexthop->rgate.ipv6, buf, BUFSIZ)); if (nexthop->rifindex) vty_out (vty, ", %s", ifindex2ifname (nexthop->rifindex)); break; case NEXTHOP_TYPE_IFINDEX: case NEXTHOP_TYPE_IFNAME: vty_out (vty, " is directly connected, %s)", ifindex2ifname (nexthop->rifindex)); break; default: break; } } if (rib->type == ZEBRA_ROUTE_RIPNG || rib->type == ZEBRA_ROUTE_OSPF6 || rib->type == ZEBRA_ROUTE_ISIS || rib->type == ZEBRA_ROUTE_BGP) { time_t uptime; struct tm *tm; uptime = time (NULL); uptime -= rib->uptime; tm = gmtime (&uptime); #define ONE_DAY_SECOND 60*60*24 #define ONE_WEEK_SECOND 60*60*24*7 if (uptime < ONE_DAY_SECOND) vty_out (vty, ", %02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); else if (uptime < ONE_WEEK_SECOND) vty_out (vty, ", %dd%02dh%02dm", tm->tm_yday, tm->tm_hour, tm->tm_min); else vty_out (vty, ", %02dw%dd%02dh", tm->tm_yday/7, tm->tm_yday - ((tm->tm_yday/7) * 7), tm->tm_hour); } vty_out (vty, "%s", VTY_NEWLINE); } } #define SHOW_ROUTE_V6_HEADER "Codes: K - kernel route, C - connected, S - static, R - RIPng, O - OSPFv3, I - IS-IS,%s B - BGP, * - FIB route.%s%s" DEFUN (show_ipv6_route, show_ipv6_route_cmd, "show ipv6 route", SHOW_STR IP_STR "IPv6 routing table\n") { struct route_node *rn; struct rib *rib; int first = 1; /* Show all IPv6 route. */ for (rn = route_top (rib_table_ipv6); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) { if (first) { vty_out (vty, SHOW_ROUTE_V6_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); first = 0; } vty_show_ipv6_route (vty, rn, rib); } return CMD_SUCCESS; } DEFUN (show_ipv6_route_prefix_longer, show_ipv6_route_prefix_longer_cmd, "show ipv6 route X:X::X:X/M longer-prefixes", SHOW_STR IP_STR "IPv6 routing table\n" "IPv6 prefix\n" "Show route matching the specified Network/Mask pair only\n") { struct route_node *rn; struct rib *rib; struct prefix p; int ret; int first = 1; ret = str2prefix (argv[0], &p); if (! ret) { vty_out (vty, "%% Malformed Prefix%s", VTY_NEWLINE); return CMD_WARNING; } /* Show matched type IPv6 routes. */ for (rn = route_top (rib_table_ipv6); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) if (prefix_match (&p, &rn->p)) { if (first) { vty_out (vty, SHOW_ROUTE_V6_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); first = 0; } vty_show_ipv6_route (vty, rn, rib); } return CMD_SUCCESS; } DEFUN (show_ipv6_route_protocol, show_ipv6_route_protocol_cmd, "show ipv6 route (bgp|connected|kernel|ospf6|isis|ripng|static)", SHOW_STR IP_STR "IP routing table\n" "Border Gateway Protocol (BGP)\n" "Connected\n" "Kernel\n" "Open Shortest Path First (OSPFv3)\n" "ISO IS-IS (ISIS)\n" "Routing Information Protocol (RIPng)\n" "Static routes\n") { int type; struct route_node *rn; struct rib *rib; int first = 1; if (strncmp (argv[0], "b", 1) == 0) type = ZEBRA_ROUTE_BGP; else if (strncmp (argv[0], "c", 1) == 0) type = ZEBRA_ROUTE_CONNECT; else if (strncmp (argv[0], "k", 1) ==0) type = ZEBRA_ROUTE_KERNEL; else if (strncmp (argv[0], "o", 1) == 0) type = ZEBRA_ROUTE_OSPF6; else if (strncmp (argv[0], "i", 1) == 0) type = ZEBRA_ROUTE_ISIS; else if (strncmp (argv[0], "r", 1) == 0) type = ZEBRA_ROUTE_RIPNG; else if (strncmp (argv[0], "s", 1) == 0) type = ZEBRA_ROUTE_STATIC; else { vty_out (vty, "Unknown route type%s", VTY_NEWLINE); return CMD_WARNING; } /* Show matched type IPv6 routes. */ for (rn = route_top (rib_table_ipv6); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = rib->next) if (rib->type == type) { if (first) { vty_out (vty, SHOW_ROUTE_V6_HEADER, VTY_NEWLINE, VTY_NEWLINE, VTY_NEWLINE); first = 0; } vty_show_ipv6_route (vty, rn, rib); } return CMD_SUCCESS; } DEFUN (show_ipv6_route_addr, show_ipv6_route_addr_cmd, "show ipv6 route X:X::X:X", SHOW_STR IP_STR "IPv6 routing table\n" "IPv6 Address\n") { int ret; struct prefix_ipv6 p; struct route_node *rn; ret = str2prefix_ipv6 (argv[0], &p); if (ret <= 0) { vty_out (vty, "Malformed IPv6 address%s", VTY_NEWLINE); return CMD_WARNING; } rn = route_node_match (rib_table_ipv6, (struct prefix *) &p); if (! rn) { vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); return CMD_WARNING; } vty_show_ipv6_route_detail (vty, rn); route_unlock_node (rn); return CMD_SUCCESS; } DEFUN (show_ipv6_route_prefix, show_ipv6_route_prefix_cmd, "show ipv6 route X:X::X:X/M", SHOW_STR IP_STR "IPv6 routing table\n" "IPv6 prefix\n") { int ret; struct prefix_ipv6 p; struct route_node *rn; ret = str2prefix_ipv6 (argv[0], &p); if (ret <= 0) { vty_out (vty, "Malformed IPv6 prefix%s", VTY_NEWLINE); return CMD_WARNING; } rn = route_node_match (rib_table_ipv6, (struct prefix *) &p); if (! rn || rn->p.prefixlen != p.prefixlen) { vty_out (vty, "%% Network not in table%s", VTY_NEWLINE); return CMD_WARNING; } vty_show_ipv6_route_detail (vty, rn); route_unlock_node (rn); return CMD_SUCCESS; } #endif /* HAVE_IPV6 */ /* RIB update function. */ void rib_update () { struct route_node *rn; for (rn = route_top (rib_table_ipv4); rn; rn = route_next (rn)) /* Update reachability. */ rib_process (rn, NULL); #ifdef HAVE_IPV6 for (rn = route_top (rib_table_ipv6); rn; rn = route_next (rn)) rib_process (rn, NULL); #endif /* HAVE_IPV6 */ } /* Interface goes up. */ void rib_if_up (struct interface *ifp) { rib_update (); } /* Interface goes down. */ void rib_if_down (struct interface *ifp) { rib_update (); } /* Clean up routines. */ void rib_weed_table (struct route_table *rib_table) { struct route_node *rn; struct rib *rib; struct rib *next; for (rn = route_top (rib_table); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = next) { next = rib->next; if (rib->table != rtm_table_default && rib->table != RT_TABLE_MAIN) { rib_delnode (rn, rib); newrib_free (rib); route_unlock_node (rn); } } } /* Delete all routes from unmanaged tables. */ void rib_weed_tables () { rib_weed_table (rib_table_ipv4); #ifdef HAVE_IPV6 rib_weed_table (rib_table_ipv6); #endif /* HAVE_IPV6 */ } void rib_sweep_table (struct route_table *rib_table) { struct route_node *rn; struct rib *rib; struct rib *next; int ret = 0; for (rn = route_top (rib_table); rn; rn = route_next (rn)) for (rib = rn->info; rib; rib = next) { next = rib->next; if ((rib->type == ZEBRA_ROUTE_KERNEL) && CHECK_FLAG (rib->flags, ZEBRA_FLAG_SELFROUTE)) { ret = rib_uninstall_kernel (rn, rib); if (! ret) { rib_delnode (rn, rib); newrib_free (rib); route_unlock_node (rn); } } } } void rib_sweep_route () { rib_sweep_table (rib_table_ipv4); #ifdef HAVE_IPV6 rib_sweep_table (rib_table_ipv6); #endif /* HAVE_IPV6 */ } /* Close rib when zebra terminates. */ void rib_close () { rib_close_ipv4 (); #ifdef HAVE_IPV6 rib_close_ipv6 (); #endif /* HAVE_IPV6 */ } /* Static ip route configuration write function. */ int config_write_ip (struct vty *vty) { int write = 0; write += static_ipv4_write (vty); #ifdef HAVE_IPV6 write += static_ipv6_write (vty); #endif /* HAVE_IPV6 */ return write; } /* IP node for static routes. */ struct cmd_node ip_node = { IP_NODE, "", /* This node has no interface. */ 1 }; /* Routing information base initialize. */ void rib_init () { install_node (&ip_node, config_write_ip); rib_table_ipv4 = route_table_init (); static_table_ipv4 = route_table_init (); install_element (VIEW_NODE, &show_ip_route_cmd); install_element (VIEW_NODE, &show_ip_route_addr_cmd); install_element (VIEW_NODE, &show_ip_route_prefix_cmd); install_element (VIEW_NODE, &show_ip_route_prefix_longer_cmd); install_element (VIEW_NODE, &show_ip_route_protocol_cmd); install_element (VIEW_NODE, &show_ip_route_supernets_cmd); install_element (ENABLE_NODE, &show_ip_route_cmd); install_element (ENABLE_NODE, &show_ip_route_addr_cmd); install_element (ENABLE_NODE, &show_ip_route_prefix_cmd); install_element (ENABLE_NODE, &show_ip_route_prefix_longer_cmd); install_element (ENABLE_NODE, &show_ip_route_protocol_cmd); install_element (ENABLE_NODE, &show_ip_route_supernets_cmd); install_element (CONFIG_NODE, &ip_route_cmd); install_element (CONFIG_NODE, &ip_route_mask_cmd); install_element (CONFIG_NODE, &no_ip_route_cmd); install_element (CONFIG_NODE, &no_ip_route_mask_cmd); install_element (CONFIG_NODE, &ip_route_pref_cmd); install_element (CONFIG_NODE, &ip_route_mask_pref_cmd); install_element (CONFIG_NODE, &no_ip_route_pref_cmd); install_element (CONFIG_NODE, &no_ip_route_mask_pref_cmd); #ifdef HAVE_IPV6 rib_table_ipv6 = route_table_init (); static_table_ipv6 = route_table_init (); install_element (CONFIG_NODE, &ipv6_route_cmd); install_element (CONFIG_NODE, &ipv6_route_ifname_cmd); install_element (CONFIG_NODE, &no_ipv6_route_cmd); install_element (CONFIG_NODE, &no_ipv6_route_ifname_cmd); install_element (CONFIG_NODE, &ipv6_route_pref_cmd); install_element (CONFIG_NODE, &ipv6_route_ifname_pref_cmd); install_element (CONFIG_NODE, &no_ipv6_route_pref_cmd); install_element (CONFIG_NODE, &no_ipv6_route_ifname_pref_cmd); install_element (VIEW_NODE, &show_ipv6_route_cmd); install_element (VIEW_NODE, &show_ipv6_route_protocol_cmd); install_element (VIEW_NODE, &show_ipv6_route_addr_cmd); install_element (VIEW_NODE, &show_ipv6_route_prefix_cmd); install_element (VIEW_NODE, &show_ipv6_route_prefix_longer_cmd); install_element (ENABLE_NODE, &show_ipv6_route_cmd); install_element (ENABLE_NODE, &show_ipv6_route_protocol_cmd); install_element (ENABLE_NODE, &show_ipv6_route_addr_cmd); install_element (ENABLE_NODE, &show_ipv6_route_prefix_cmd); install_element (ENABLE_NODE, &show_ipv6_route_prefix_longer_cmd); #endif /* HAVE_IPV6 */ }