| /* 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 <zebra.h> |
| |
| #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 <network>/<length>, 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 <network>/<length>, 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 */ |
| } |