/* * Copyright (C) 2016 CumulusNetworks * Donald Sharp * * This file is part of Quagga * * Quagga 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. * * Quagga 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 "log.h" #include "linklist.h" #include "zebra/debug.h" #include "zebra/zserv.h" #include "zebra/rib.h" #include "zebra/zebra_vrf.h" #include "zebra/router-id.h" extern struct zebra_t zebrad; /* VRF information update. */ static void zebra_vrf_add_update (struct zebra_vrf *zvrf) { struct listnode *node, *nnode; struct zserv *client; if (IS_ZEBRA_DEBUG_EVENT) zlog_debug ("MESSAGE: ZEBRA_VRF_ADD %s", zvrf->name); for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) zsend_vrf_add (client, zvrf); } static void zebra_vrf_delete_update (struct zebra_vrf *zvrf) { struct listnode *node, *nnode; struct zserv *client; if (IS_ZEBRA_DEBUG_EVENT) zlog_debug ("MESSAGE: ZEBRA_VRF_DELETE %s", zvrf->name); for (ALL_LIST_ELEMENTS (zebrad.client_list, node, nnode, client)) zsend_vrf_delete (client, zvrf); } void zebra_vrf_update_all (struct zserv *client) { struct vrf *vrf; vrf_iter_t iter; for (iter = vrf_first (); iter != VRF_ITER_INVALID; iter = vrf_next (iter)) { if ((vrf = vrf_iter2vrf (iter)) && vrf->vrf_id) zsend_vrf_add (client, vrf_info_lookup (vrf->vrf_id)); } } /* Callback upon creating a new VRF. */ static int zebra_vrf_new (vrf_id_t vrf_id, const char *name, void **info) { struct zebra_vrf *zvrf = *info; zlog_info ("ZVRF %s with id %u", name, vrf_id); if (! zvrf) { zvrf = zebra_vrf_alloc (vrf_id, name); zvrf->zns = zebra_ns_lookup (NS_DEFAULT); /* Point to the global (single) NS */ *info = (void *)zvrf; router_id_init (zvrf); } return 0; } /* Callback upon enabling a VRF. */ static int zebra_vrf_enable (vrf_id_t vrf_id, const char *name, void **info) { struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info); assert (zvrf); zebra_vrf_add_update (zvrf); return 0; } /* Callback upon disabling a VRF. */ static int zebra_vrf_disable (vrf_id_t vrf_id, const char *name, void **info) { struct zebra_vrf *zvrf = (struct zebra_vrf *)(*info); if (IS_ZEBRA_DEBUG_KERNEL) zlog_debug ("VRF %s id %u is now disabled.", zvrf->name, zvrf->vrf_id); return 0; } static int zebra_vrf_delete (vrf_id_t vrf_id, const char *name, void **info) { struct zebra_vrf *zvrf = (struct zebra_vrf *) (*info); assert (zvrf); zebra_vrf_delete_update (zvrf); rib_close_table (zvrf->table[AFI_IP][SAFI_UNICAST]); rib_close_table (zvrf->table[AFI_IP6][SAFI_UNICAST]); list_delete_all_node (zvrf->rid_all_sorted_list); list_delete_all_node (zvrf->rid_lo_sorted_list); XFREE (MTYPE_ZEBRA_VRF, zvrf); return 0; } /* Lookup the routing table in a VRF based on both VRF-Id and table-id. * NOTE: Table-id is relevant only in the Default VRF. */ struct route_table * zebra_vrf_table_with_table_id (afi_t afi, safi_t safi, vrf_id_t vrf_id, u_int32_t table_id) { struct route_table *table = NULL; if (afi >= AFI_MAX || safi >= SAFI_MAX) return NULL; if (vrf_id == VRF_DEFAULT) { if (table_id == RT_TABLE_MAIN || table_id == zebrad.rtm_table_default) table = zebra_vrf_table (afi, safi, vrf_id); else table = zebra_vrf_other_route_table (afi, table_id, vrf_id); } else table = zebra_vrf_table (afi, safi, vrf_id); return table; } /* * Create a routing table for the specific AFI/SAFI in the given VRF. */ static void zebra_vrf_table_create (struct zebra_vrf *zvrf, afi_t afi, safi_t safi) { rib_table_info_t *info; struct route_table *table; assert (!zvrf->table[afi][safi]); table = route_table_init (); zvrf->table[afi][safi] = table; info = XCALLOC (MTYPE_RIB_TABLE_INFO, sizeof (*info)); info->zvrf = zvrf; info->afi = afi; info->safi = safi; table->info = info; } /* Allocate new zebra VRF. */ struct zebra_vrf * zebra_vrf_alloc (vrf_id_t vrf_id, const char *name) { struct zebra_vrf *zvrf; zvrf = XCALLOC (MTYPE_ZEBRA_VRF, sizeof (struct zebra_vrf)); /* Allocate routing table and static table. */ zebra_vrf_table_create (zvrf, AFI_IP, SAFI_UNICAST); zebra_vrf_table_create (zvrf, AFI_IP6, SAFI_UNICAST); zvrf->stable[AFI_IP][SAFI_UNICAST] = route_table_init (); zvrf->stable[AFI_IP6][SAFI_UNICAST] = route_table_init (); zebra_vrf_table_create (zvrf, AFI_IP, SAFI_MULTICAST); zebra_vrf_table_create (zvrf, AFI_IP6, SAFI_MULTICAST); zvrf->stable[AFI_IP][SAFI_MULTICAST] = route_table_init (); zvrf->stable[AFI_IP6][SAFI_MULTICAST] = route_table_init (); zvrf->rnh_table[AFI_IP] = route_table_init(); zvrf->rnh_table[AFI_IP6] = route_table_init(); zvrf->import_check_table[AFI_IP] = route_table_init(); zvrf->import_check_table[AFI_IP6] = route_table_init(); /* Set VRF ID */ zvrf->vrf_id = vrf_id; if (name) { strncpy (zvrf->name, name, strlen(name)); zvrf->name[strlen(name)] = '\0'; } return zvrf; } /* Lookup VRF by identifier. */ struct zebra_vrf * zebra_vrf_lookup (vrf_id_t vrf_id) { return vrf_info_lookup (vrf_id); } /* Lookup the routing table in an enabled VRF. */ struct route_table * zebra_vrf_table (afi_t afi, safi_t safi, vrf_id_t vrf_id) { struct zebra_vrf *zvrf = vrf_info_lookup (vrf_id); if (!zvrf) return NULL; if (afi >= AFI_MAX || safi >= SAFI_MAX) return NULL; return zvrf->table[afi][safi]; } /* Lookup the static routing table in a VRF. */ struct route_table * zebra_vrf_static_table (afi_t afi, safi_t safi, vrf_id_t vrf_id) { struct zebra_vrf *zvrf = vrf_info_lookup (vrf_id); if (!zvrf) return NULL; if (afi >= AFI_MAX || safi >= SAFI_MAX) return NULL; return zvrf->stable[afi][safi]; } struct route_table * zebra_vrf_other_route_table (afi_t afi, u_int32_t table_id, vrf_id_t vrf_id) { struct zebra_vrf *zvrf; rib_table_info_t *info; struct route_table *table; zvrf = vrf_info_lookup (vrf_id); if (! zvrf) return NULL; if(afi >= AFI_MAX) return NULL; if (table_id >= ZEBRA_KERNEL_TABLE_MAX) return NULL; if ((vrf_id == VRF_DEFAULT) && (table_id != RT_TABLE_MAIN) && (table_id != zebrad.rtm_table_default)) { if (zvrf->other_table[afi][table_id] == NULL) { table = route_table_init(); info = XCALLOC (MTYPE_RIB_TABLE_INFO, sizeof (*info)); info->zvrf = zvrf; info->afi = afi; info->safi = SAFI_UNICAST; table->info = info; zvrf->other_table[afi][table_id] = table; } return (zvrf->other_table[afi][table_id]); } return zvrf->table[afi][SAFI_UNICAST]; } /* Zebra VRF initialization. */ void zebra_vrf_init (void) { vrf_add_hook (VRF_NEW_HOOK, zebra_vrf_new); vrf_add_hook (VRF_ENABLE_HOOK, zebra_vrf_enable); vrf_add_hook (VRF_DISABLE_HOOK, zebra_vrf_disable); vrf_add_hook (VRF_DELETE_HOOK, zebra_vrf_delete); vrf_init (); }