]>
Commit | Line | Data |
---|---|---|
9b0a0ba9 OZ |
1 | /* |
2 | * BIRD Test -- Utils for testing parsing configuration file | |
3 | * | |
4 | * (c) 2015 CZ.NIC z.s.p.o. | |
5 | * | |
6 | * Can be freely distributed and used under the terms of the GNU GPL. | |
7 | */ | |
8 | ||
9 | #include <stdlib.h> | |
10 | #include <fcntl.h> | |
11 | #include <unistd.h> | |
12 | ||
13 | #include "test/birdtest.h" | |
14 | #include "test/bt-utils.h" | |
15 | ||
16 | #include "nest/bird.h" | |
17 | #include "nest/route.h" | |
18 | #include "nest/protocol.h" | |
e55696a4 | 19 | #include "nest/mpls.h" |
9b0a0ba9 OZ |
20 | |
21 | #include "sysdep/unix/unix.h" | |
22 | #include "sysdep/unix/krt.h" | |
23 | ||
24 | #include "nest/iface.h" | |
25 | #include "nest/locks.h" | |
26 | ||
27 | #include "filter/filter.h" | |
28 | ||
29 | #define BETWEEN(a, b, c) (((a) >= (b)) && ((a) <= (c))) | |
30 | ||
31 | static const byte *bt_config_parse_pos; | |
32 | static uint bt_config_parse_remain_len; | |
33 | ||
34 | /* This is cf_read_hook for hard-coded text configuration */ | |
35 | static int | |
36 | cf_static_read(byte *dest, uint max_len, int fd UNUSED) | |
37 | { | |
38 | if (max_len > bt_config_parse_remain_len) | |
39 | max_len = bt_config_parse_remain_len; | |
40 | memcpy(dest, bt_config_parse_pos, max_len); | |
41 | bt_config_parse_pos += max_len; | |
42 | bt_config_parse_remain_len -= max_len; | |
43 | return max_len; | |
44 | } | |
45 | ||
46 | /* This is cf_read_hook for reading configuration files, | |
47 | * function is copied from main.c, cf_read() */ | |
48 | static int | |
49 | cf_file_read(byte *dest, uint max_len, int fd) | |
50 | { | |
51 | int l = read(fd, dest, max_len); | |
52 | if (l < 0) | |
53 | cf_error("Read error"); | |
54 | return l; | |
55 | } | |
56 | ||
57 | void | |
58 | bt_bird_init(void) | |
59 | { | |
60 | if(bt_verbose) | |
61 | log_init_debug(""); | |
62 | log_switch(bt_verbose != 0, NULL, NULL); | |
63 | ||
9b0a0ba9 | 64 | olock_init(); |
212eda07 | 65 | timer_init(); |
9b0a0ba9 OZ |
66 | io_init(); |
67 | rt_init(); | |
68 | if_init(); | |
333ddd4f | 69 | mpls_init(); |
9b0a0ba9 OZ |
70 | config_init(); |
71 | ||
72 | protos_build(); | |
9b0a0ba9 OZ |
73 | } |
74 | ||
75 | void bt_bird_cleanup(void) | |
76 | { | |
ee7e2ffd JMM |
77 | for (int i = 0; i < PROTOCOL__MAX; i++) |
78 | class_to_protocol[i] = NULL; | |
9b0a0ba9 OZ |
79 | |
80 | config = new_config = NULL; | |
81 | } | |
82 | ||
83 | static char * | |
84 | bt_load_file(const char *filename, int quiet) | |
85 | { | |
86 | FILE *f = fopen(filename, "rb"); | |
87 | if (!quiet) | |
88 | bt_assert_msg(f != NULL, "Open %s", filename); | |
89 | ||
90 | if (f == NULL) | |
91 | return NULL; | |
92 | ||
93 | fseek(f, 0, SEEK_END); | |
94 | long file_size_ = ftell(f); | |
95 | fseek(f, 0, SEEK_SET); | |
96 | ||
97 | if (file_size_ < 0) | |
98 | return NULL; | |
99 | ||
100 | size_t file_size = file_size_; | |
101 | size_t read_size = 0; | |
102 | ||
103 | char *file_body = mb_allocz(&root_pool, file_size+1); | |
104 | ||
105 | /* XXX: copied from cf-lex.c */ | |
106 | errno=0; | |
107 | while ((read_size += fread(file_body+read_size, 1, file_size-read_size, f)) != file_size && ferror(f)) | |
108 | { | |
109 | bt_debug("iteration \n"); | |
110 | if(errno != EINTR) | |
111 | { | |
112 | bt_abort_msg("errno: %d", errno); | |
113 | break; | |
114 | } | |
115 | errno=0; | |
116 | clearerr(f); | |
117 | } | |
118 | fclose(f); | |
119 | ||
120 | if (!quiet) | |
121 | bt_assert_msg(read_size == file_size, "Read %s", filename); | |
122 | ||
123 | return file_body; | |
124 | } | |
125 | ||
126 | static void | |
127 | bt_show_cfg_error(const struct config *cfg) | |
128 | { | |
129 | int lino = 0; | |
130 | int lino_delta = 5; | |
131 | int lino_err = cfg->err_lino; | |
132 | ||
133 | const char *str = bt_load_file(cfg->err_file_name, 1); | |
134 | ||
135 | while (str && *str) | |
136 | { | |
137 | lino++; | |
138 | if (BETWEEN(lino, lino_err - lino_delta, lino_err + lino_delta)) | |
139 | bt_debug("%4u%s", lino, (lino_err == lino ? " >> " : " ")); | |
140 | do | |
141 | { | |
142 | if (BETWEEN(lino, lino_err - lino_delta, lino_err + lino_delta)) | |
143 | bt_debug("%c", *str); | |
144 | } while (*str && *(str++) != '\n'); | |
145 | } | |
146 | bt_debug("\n"); | |
147 | } | |
148 | ||
149 | static struct config * | |
150 | bt_config_parse__(struct config *cfg) | |
151 | { | |
152 | bt_assert_msg(config_parse(cfg) == 1, "Parse %s", cfg->file_name); | |
153 | ||
154 | if (cfg->err_msg) | |
155 | { | |
3dabf7b8 | 156 | bt_log("Parse error %s, line %d: %s", cfg->err_file_name, cfg->err_lino, cfg->err_msg); |
9b0a0ba9 OZ |
157 | bt_show_cfg_error(cfg); |
158 | return NULL; | |
159 | } | |
160 | ||
161 | config_commit(cfg, RECONFIG_HARD, 0); | |
162 | new_config = cfg; | |
163 | ||
164 | return cfg; | |
165 | } | |
166 | ||
167 | struct config * | |
168 | bt_config_parse(const char *cfg_str) | |
169 | { | |
170 | struct config *cfg = config_alloc("configuration"); | |
171 | ||
172 | bt_config_parse_pos = cfg_str; | |
173 | bt_config_parse_remain_len = strlen(cfg_str); | |
174 | cf_read_hook = cf_static_read; | |
175 | ||
176 | return bt_config_parse__(cfg); | |
177 | } | |
178 | ||
179 | struct config * | |
180 | bt_config_file_parse(const char *filepath) | |
181 | { | |
182 | struct config *cfg = config_alloc(filepath); | |
183 | ||
184 | cfg->file_fd = open(filepath, O_RDONLY); | |
185 | bt_assert_msg(cfg->file_fd > 0, "Open %s", filepath); | |
186 | if (cfg->file_fd < 0) | |
187 | return NULL; | |
188 | ||
189 | cf_read_hook = cf_file_read; | |
190 | ||
191 | return bt_config_parse__(cfg); | |
192 | } | |
193 | ||
194 | /* | |
195 | * Returns @base raised to the power of @power. | |
196 | */ | |
197 | uint | |
198 | bt_naive_pow(uint base, uint power) | |
199 | { | |
200 | uint result = 1; | |
201 | uint i; | |
202 | for (i = 0; i < power; i++) | |
203 | result *= base; | |
204 | return result; | |
205 | } | |
206 | ||
207 | /** | |
208 | * bytes_to_hex - transform data into hexadecimal representation | |
209 | * @buf: preallocated string buffer | |
210 | * @in_data: data for transformation | |
211 | * @size: the length of @in_data | |
212 | * | |
213 | * This function transforms @in_data of length @size into hexadecimal | |
214 | * representation and writes it into @buf. | |
215 | */ | |
216 | void | |
217 | bt_bytes_to_hex(char *buf, const byte *in_data, size_t size) | |
218 | { | |
219 | size_t i; | |
220 | for(i = 0; i < size; i++) | |
221 | sprintf(buf + i*2, "%02x", in_data[i]); | |
222 | } | |
223 | ||
3cf91fb9 OZ |
224 | void |
225 | bt_random_net(net_addr *net, int type) | |
226 | { | |
227 | ip4_addr ip4; | |
228 | ip6_addr ip6; | |
229 | uint pxlen; | |
230 | ||
231 | switch (type) | |
232 | { | |
233 | case NET_IP4: | |
234 | pxlen = bt_random_n(24)+8; | |
235 | ip4 = ip4_from_u32((u32) bt_random()); | |
236 | net_fill_ip4(net, ip4_and(ip4, ip4_mkmask(pxlen)), pxlen); | |
237 | break; | |
238 | ||
239 | case NET_IP6: | |
240 | pxlen = bt_random_n(120)+8; | |
241 | ip6 = ip6_build(bt_random(), bt_random(), bt_random(), bt_random()); | |
242 | net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen); | |
243 | break; | |
244 | ||
245 | default: | |
246 | die("Net type %d not implemented", type); | |
247 | } | |
248 | } | |
249 | ||
250 | net_addr * | |
251 | bt_random_nets(int type, uint n) | |
252 | { | |
253 | net_addr *nets = tmp_alloc(n * sizeof(net_addr)); | |
254 | ||
255 | for (uint i = 0; i < n; i++) | |
256 | bt_random_net(&nets[i], type); | |
257 | ||
258 | return nets; | |
259 | } | |
260 | ||
261 | net_addr * | |
262 | bt_random_net_subset(net_addr *src, uint sn, uint dn) | |
263 | { | |
264 | net_addr *nets = tmp_alloc(dn * sizeof(net_addr)); | |
265 | ||
266 | for (uint i = 0; i < dn; i++) | |
267 | net_copy(&nets[i], &src[bt_random_n(sn)]); | |
268 | ||
269 | return nets; | |
270 | } | |
271 | ||
272 | void | |
273 | bt_read_net(const char *str, net_addr *net, int type) | |
274 | { | |
275 | ip4_addr ip4; | |
276 | ip6_addr ip6; | |
277 | uint pxlen; | |
278 | char addr[64]; | |
279 | ||
280 | switch (type) | |
281 | { | |
282 | case NET_IP4: | |
283 | if (sscanf(str, "%[0-9.]/%u", addr, &pxlen) != 2) | |
284 | goto err; | |
285 | ||
286 | if (!ip4_pton(addr, &ip4)) | |
287 | goto err; | |
288 | ||
289 | if (!net_validate_px4(ip4, pxlen)) | |
290 | goto err; | |
291 | ||
292 | net_fill_ip4(net, ip4, pxlen); | |
293 | break; | |
294 | ||
295 | case NET_IP6: | |
296 | if (sscanf(str, "%[0-9a-fA-F:.]/%u", addr, &pxlen) != 2) | |
297 | goto err; | |
298 | ||
299 | if (!ip6_pton(addr, &ip6)) | |
300 | goto err; | |
301 | ||
302 | if (!net_validate_px6(ip6, pxlen)) | |
303 | goto err; | |
304 | ||
305 | net_fill_ip6(net, ip6, pxlen); | |
306 | break; | |
307 | ||
308 | default: | |
309 | die("Net type %d not implemented", type); | |
310 | } | |
311 | return; | |
312 | ||
313 | err: | |
314 | bt_abort_msg("Invalid network '%s'", str); | |
315 | } | |
316 | ||
317 | net_addr * | |
318 | bt_read_nets(FILE *f, int type, uint *n) | |
319 | { | |
320 | char str[80]; | |
321 | ||
322 | net_addr *nets = tmp_alloc(*n * sizeof(net_addr)); | |
323 | uint i = 0; | |
324 | ||
325 | errno = 0; | |
326 | while (fgets(str, sizeof(str), f)) | |
327 | { | |
328 | if (str[0] == '\n') | |
329 | break; | |
330 | ||
331 | if (i >= *n) | |
332 | bt_abort_msg("Too many networks"); | |
333 | ||
334 | bt_read_net(str, &nets[i], type); | |
335 | bt_debug("ADD %s\n", str); | |
336 | i++; | |
337 | } | |
338 | bt_syscall(errno, "fgets()"); | |
339 | ||
340 | bt_debug("DONE reading %u nets\n", i); | |
341 | ||
342 | *n = i; | |
343 | return nets; | |
344 | } | |
345 | ||
346 | net_addr * | |
347 | bt_read_net_file(const char *filename, int type, uint *n) | |
348 | { | |
349 | FILE *f = fopen(filename, "r"); | |
350 | bt_syscall(!f, "fopen(%s)", filename); | |
351 | net_addr *nets = bt_read_nets(f, type, n); | |
352 | fclose(f); | |
353 | ||
354 | return nets; | |
355 | } |