1 /* zebra table Manager for routing table identifier management
2  * Copyright (C) 2018 6WIND
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the Free
6  * Software Foundation; either version 2 of the License, or (at your option)
7  * any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; see the file COPYING; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include "zebra.h"
20 
21 #include <stdio.h>
22 #include <string.h>
23 #include <sys/types.h>
24 
25 #include "lib/log.h"
26 #include "lib/memory.h"
27 #include "lib/table.h"
28 #include "lib/network.h"
29 #include "lib/stream.h"
30 #include "lib/zclient.h"
31 #include "lib/libfrr.h"
32 #include "lib/vrf.h"
33 
34 #include "zebra/zserv.h"
35 #include "zebra/zebra_vrf.h"
36 #include "zebra/label_manager.h" /* for NO_PROTO */
37 #include "zebra/table_manager.h"
38 #include "zebra/zebra_errors.h"
39 
40 /* routing table identifiers
41  *
42  */
43 #ifdef SUNOS_5
44 /* SunOS
45  */
46 #else
47 #if !defined(GNU_LINUX) && !defined(SUNOS_5)
48 /* BSD systems
49  */
50 #else
51 /* Linux Systems
52  */
53 #define RT_TABLE_ID_LOCAL                  255
54 #define RT_TABLE_ID_MAIN                   254
55 #define RT_TABLE_ID_DEFAULT                253
56 #define RT_TABLE_ID_COMPAT                 252
57 #define RT_TABLE_ID_UNSPEC                 0
58 #endif /* !def(GNU_LINUX) && !defined(SUNOS_5) */
59 #endif /* SUNOS_5 */
60 #define RT_TABLE_ID_UNRESERVED_MIN         1
61 #define RT_TABLE_ID_UNRESERVED_MAX         0xffffffff
62 
63 struct table_manager tbl_mgr;
64 
65 DEFINE_MGROUP(TABLE_MGR, "Table Manager");
66 DEFINE_MTYPE_STATIC(TABLE_MGR, TM_CHUNK, "Table Manager Chunk");
67 
delete_table_chunk(void * val)68 static void delete_table_chunk(void *val)
69 {
70 	XFREE(MTYPE_TM_CHUNK, val);
71 }
72 
73 /**
74  * Init table manager
75  */
table_manager_enable(ns_id_t ns_id)76 void table_manager_enable(ns_id_t ns_id)
77 {
78 	if (ns_id != NS_DEFAULT)
79 		return;
80 	tbl_mgr.lc_list = list_new();
81 	tbl_mgr.lc_list->del = delete_table_chunk;
82 	hook_register(zserv_client_close, release_daemon_table_chunks);
83 }
84 
85 /**
86  * Core function, assigns table chunks
87  *
88  * It first searches through the list to check if there's one available
89  * (previously released). Otherwise it creates and assigns a new one
90  *
91  * @param proto Daemon protocol of client, to identify the owner
92  * @param instance Instance, to identify the owner
93  * @para size Size of the table chunk
94  * @return Pointer to the assigned table chunk
95  */
assign_table_chunk(uint8_t proto,uint16_t instance,uint32_t size)96 struct table_manager_chunk *assign_table_chunk(uint8_t proto, uint16_t instance,
97 					       uint32_t size)
98 {
99 	struct table_manager_chunk *tmc;
100 	struct listnode *node;
101 	uint32_t start;
102 
103 	/* first check if there's one available */
104 	for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) {
105 		if (tmc->proto == NO_PROTO
106 		    && tmc->end - tmc->start + 1 == size) {
107 			tmc->proto = proto;
108 			tmc->instance = instance;
109 			return tmc;
110 		}
111 	}
112 	/* otherwise create a new one */
113 	tmc = XCALLOC(MTYPE_TM_CHUNK, sizeof(struct table_manager_chunk));
114 	if (!tmc)
115 		return NULL;
116 
117 	/* table RT IDs range are [1;252] and [256;0xffffffff]
118 	 * - check if the requested range can be within the first range,
119 	 * otherwise elect second one
120 	 * - TODO : vrf-lites have their own table identifier.
121 	 * In that case, table_id should be removed from the table range.
122 	 */
123 	if (list_isempty(tbl_mgr.lc_list))
124 		start = RT_TABLE_ID_UNRESERVED_MIN;
125 	else
126 		start = ((struct table_manager_chunk *)listgetdata(
127 			   listtail(tbl_mgr.lc_list)))->end + 1;
128 
129 #ifdef SUNOS_5
130 /* SunOS
131  */
132 #else
133 #if !defined(GNU_LINUX) && !defined(SUNOS_5)
134 /* BSD systems
135  */
136 #else
137 /* Linux Systems
138  */
139 	/* if not enough room space between MIN and COMPAT,
140 	 * then begin after LOCAL
141 	 */
142 	if (start < RT_TABLE_ID_COMPAT && (size >
143 				RT_TABLE_ID_COMPAT
144 				- RT_TABLE_ID_UNRESERVED_MIN))
145 		start = RT_TABLE_ID_LOCAL + 1;
146 #endif /* !def(GNU_LINUX) && !defined(SUNOS_5) */
147 #endif /* SUNOS_5 */
148 	tmc->start = start;
149 	if (RT_TABLE_ID_UNRESERVED_MAX - size  + 1 < start) {
150 		flog_err(EC_ZEBRA_TM_EXHAUSTED_IDS,
151 			 "Reached max table id. Start/Size %u/%u", start, size);
152 		XFREE(MTYPE_TM_CHUNK, tmc);
153 		return NULL;
154 	}
155 	tmc->end = tmc->start + size - 1;
156 	tmc->proto = proto;
157 	tmc->instance = instance;
158 	listnode_add(tbl_mgr.lc_list, tmc);
159 
160 	return tmc;
161 }
162 
163 /**
164  * Core function, release no longer used table chunks
165  *
166  * @param proto Daemon protocol of client, to identify the owner
167  * @param instance Instance, to identify the owner
168  * @param start First table RT ID of the chunk
169  * @param end Last table RT ID of the chunk
170  * @return 0 on success, -1 otherwise
171  */
release_table_chunk(uint8_t proto,uint16_t instance,uint32_t start,uint32_t end)172 int release_table_chunk(uint8_t proto, uint16_t instance, uint32_t start,
173 			uint32_t end)
174 {
175 	struct listnode *node;
176 	struct table_manager_chunk *tmc;
177 	int ret = -1;
178 
179 	/* check that size matches */
180 	zlog_debug("Releasing table chunk: %u - %u", start, end);
181 	/* find chunk and disown */
182 	for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) {
183 		if (tmc->start != start)
184 			continue;
185 		if (tmc->end != end)
186 			continue;
187 		if (tmc->proto != proto || tmc->instance != instance) {
188 			flog_err(EC_ZEBRA_TM_DAEMON_MISMATCH,
189 				 "%s: Daemon mismatch!!", __func__);
190 			continue;
191 		}
192 		tmc->proto = NO_PROTO;
193 		tmc->instance = 0;
194 		ret = 0;
195 		break;
196 	}
197 	if (ret != 0)
198 		flog_err(EC_ZEBRA_TM_UNRELEASED_CHUNK,
199 			 "%s: Table chunk not released!!", __func__);
200 
201 	return ret;
202 }
203 
204 /**
205  * Release table chunks from a client.
206  *
207  * Called on client disconnection or reconnection. It only releases chunks
208  * with empty keep value.
209  *
210  * @param client the client to release chunks from
211  * @return Number of chunks released
212  */
release_daemon_table_chunks(struct zserv * client)213 int release_daemon_table_chunks(struct zserv *client)
214 {
215 	uint8_t proto = client->proto;
216 	uint16_t instance = client->instance;
217 	struct listnode *node;
218 	struct table_manager_chunk *tmc;
219 	int count = 0;
220 	int ret;
221 
222 	for (ALL_LIST_ELEMENTS_RO(tbl_mgr.lc_list, node, tmc)) {
223 		if (tmc->proto == proto && tmc->instance == instance) {
224 			ret = release_table_chunk(tmc->proto, tmc->instance,
225 						  tmc->start, tmc->end);
226 			if (ret == 0)
227 				count++;
228 		}
229 	}
230 
231 	zlog_debug("%s: Released %d table chunks", __func__, count);
232 
233 	return count;
234 }
235 
table_manager_disable(ns_id_t ns_id)236 void table_manager_disable(ns_id_t ns_id)
237 {
238 	if (ns_id != NS_DEFAULT)
239 		return;
240 	list_delete(&tbl_mgr.lc_list);
241 }
242