/* * VRF functions. * Copyright (C) 2014 6WIND S.A. * * This file is part of GNU Zebra. * * GNU Zebra is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 2, or (at your * option) any later version. * * GNU Zebra is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GNU Zebra; see the file COPYING. If not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "if.h" #include "vrf.h" #include "prefix.h" #include "table.h" #include "log.h" #include "memory.h" struct vrf { /* Identifier, same as the vector index */ vrf_id_t vrf_id; /* Name */ char *name; /* Master list of interfaces belonging to this VRF */ struct list *iflist; /* User data */ void *info; }; /* Holding VRF hooks */ struct vrf_master { int (*vrf_new_hook) (vrf_id_t, void **); int (*vrf_delete_hook) (vrf_id_t, void **); } vrf_master = {0,}; /* VRF table */ struct route_table *vrf_table = NULL; /* Build the table key */ static void vrf_build_key (vrf_id_t vrf_id, struct prefix *p) { p->family = AF_INET; p->prefixlen = IPV4_MAX_BITLEN; p->u.prefix4.s_addr = vrf_id; } /* Get a VRF. If not found, create one. */ static struct vrf * vrf_get (vrf_id_t vrf_id) { struct prefix p; struct route_node *rn; struct vrf *vrf; vrf_build_key (vrf_id, &p); rn = route_node_get (vrf_table, &p); if (rn->info) { vrf = (struct vrf *)rn->info; route_unlock_node (rn); /* get */ return vrf; } vrf = XCALLOC (MTYPE_VRF, sizeof (struct vrf)); vrf->vrf_id = vrf_id; rn->info = vrf; /* Initialize interfaces. */ if_init (vrf_id, &vrf->iflist); zlog_info ("VRF %u is created.", vrf_id); if (vrf_master.vrf_new_hook) (*vrf_master.vrf_new_hook) (vrf_id, &vrf->info); return vrf; } /* Delete a VRF. This is called in vrf_terminate(). */ static void vrf_delete (struct vrf *vrf) { zlog_info ("VRF %u is to be deleted.", vrf->vrf_id); if (vrf_master.vrf_delete_hook) (*vrf_master.vrf_delete_hook) (vrf->vrf_id, &vrf->info); if_terminate (vrf->vrf_id, &vrf->iflist); if (vrf->name) XFREE (MTYPE_VRF_NAME, vrf->name); XFREE (MTYPE_VRF, vrf); } /* Look up a VRF by identifier. */ static struct vrf * vrf_lookup (vrf_id_t vrf_id) { struct prefix p; struct route_node *rn; struct vrf *vrf = NULL; vrf_build_key (vrf_id, &p); rn = route_node_lookup (vrf_table, &p); if (rn) { vrf = (struct vrf *)rn->info; route_unlock_node (rn); /* lookup */ } return vrf; } /* Add a VRF hook. Please add hooks before calling vrf_init(). */ void vrf_add_hook (int type, int (*func)(vrf_id_t, void **)) { switch (type) { case VRF_NEW_HOOK: vrf_master.vrf_new_hook = func; break; case VRF_DELETE_HOOK: vrf_master.vrf_delete_hook = func; break; default: break; } } /* Return the iterator of the first VRF. */ vrf_iter_t vrf_first (void) { struct route_node *rn; for (rn = route_top (vrf_table); rn; rn = route_next (rn)) if (rn->info) { route_unlock_node (rn); /* top/next */ return (vrf_iter_t)rn; } return VRF_ITER_INVALID; } /* Return the next VRF iterator to the given iterator. */ vrf_iter_t vrf_next (vrf_iter_t iter) { struct route_node *rn = NULL; /* Lock it first because route_next() will unlock it. */ if (iter != VRF_ITER_INVALID) rn = route_next (route_lock_node ((struct route_node *)iter)); for (; rn; rn = route_next (rn)) if (rn->info) { route_unlock_node (rn); /* next */ return (vrf_iter_t)rn; } return VRF_ITER_INVALID; } /* Return the VRF iterator of the given VRF ID. If it does not exist, * the iterator of the next existing VRF is returned. */ vrf_iter_t vrf_iterator (vrf_id_t vrf_id) { struct prefix p; struct route_node *rn; vrf_build_key (vrf_id, &p); rn = route_node_get (vrf_table, &p); if (rn->info) { /* OK, the VRF exists. */ route_unlock_node (rn); /* get */ return (vrf_iter_t)rn; } /* Find the next VRF. */ for (rn = route_next (rn); rn; rn = route_next (rn)) if (rn->info) { route_unlock_node (rn); /* next */ return (vrf_iter_t)rn; } return VRF_ITER_INVALID; } /* Obtain the VRF ID from the given VRF iterator. */ vrf_id_t vrf_iter2id (vrf_iter_t iter) { struct route_node *rn = (struct route_node *) iter; return (rn && rn->info) ? ((struct vrf *)rn->info)->vrf_id : VRF_DEFAULT; } /* Obtain the data pointer from the given VRF iterator. */ void * vrf_iter2info (vrf_iter_t iter) { struct route_node *rn = (struct route_node *) iter; return (rn && rn->info) ? ((struct vrf *)rn->info)->info : NULL; } /* Obtain the interface list from the given VRF iterator. */ struct list * vrf_iter2iflist (vrf_iter_t iter) { struct route_node *rn = (struct route_node *) iter; return (rn && rn->info) ? ((struct vrf *)rn->info)->iflist : NULL; } /* Get the data pointer of the specified VRF. If not found, create one. */ void * vrf_info_get (vrf_id_t vrf_id) { struct vrf *vrf = vrf_get (vrf_id); return vrf->info; } /* Look up the data pointer of the specified VRF. */ void * vrf_info_lookup (vrf_id_t vrf_id) { struct vrf *vrf = vrf_lookup (vrf_id); return vrf ? vrf->info : NULL; } /* Look up the interface list in a VRF. */ struct list * vrf_iflist (vrf_id_t vrf_id) { struct vrf * vrf = vrf_lookup (vrf_id); return vrf ? vrf->iflist : NULL; } /* Get the interface list of the specified VRF. Create one if not find. */ struct list * vrf_iflist_get (vrf_id_t vrf_id) { struct vrf * vrf = vrf_get (vrf_id); return vrf->iflist; } /* Initialize VRF module. */ void vrf_init (void) { struct vrf *default_vrf; /* Allocate VRF table. */ vrf_table = route_table_init (); /* The default VRF always exists. */ default_vrf = vrf_get (VRF_DEFAULT); if (!default_vrf) { zlog_err ("vrf_init: failed to create the default VRF!"); exit (1); } /* Set the default VRF name. */ default_vrf->name = XSTRDUP (MTYPE_VRF_NAME, "Default-IP-Routing-Table"); } /* Terminate VRF module. */ void vrf_terminate (void) { struct route_node *rn; struct vrf *vrf; for (rn = route_top (vrf_table); rn; rn = route_next (rn)) if ((vrf = rn->info) != NULL) vrf_delete (vrf); route_table_finish (vrf_table); vrf_table = NULL; }