/* * BIRD Test -- Utils for testing parsing configuration file * * (c) 2015 CZ.NIC z.s.p.o. * * Can be freely distributed and used under the terms of the GNU GPL. */ #include #include #include #include "test/birdtest.h" #include "test/bt-utils.h" #include "nest/bird.h" #include "nest/route.h" #include "nest/protocol.h" #include "nest/mpls.h" #include "sysdep/unix/unix.h" #include "sysdep/unix/krt.h" #include "nest/iface.h" #include "nest/locks.h" #include "filter/filter.h" #define BETWEEN(a, b, c) (((a) >= (b)) && ((a) <= (c))) static const byte *bt_config_parse_pos; static uint bt_config_parse_remain_len; /* This is cf_read_hook for hard-coded text configuration */ static int cf_static_read(byte *dest, uint max_len, int fd UNUSED) { if (max_len > bt_config_parse_remain_len) max_len = bt_config_parse_remain_len; memcpy(dest, bt_config_parse_pos, max_len); bt_config_parse_pos += max_len; bt_config_parse_remain_len -= max_len; return max_len; } /* This is cf_read_hook for reading configuration files, * function is copied from main.c, cf_read() */ static int cf_file_read(byte *dest, uint max_len, int fd) { int l = read(fd, dest, max_len); if (l < 0) cf_error("Read error"); return l; } void bt_bird_init(void) { if(bt_verbose) log_init_debug(""); log_switch(bt_verbose != 0, NULL, NULL); olock_init(); timer_init(); io_init(); rt_init(); if_init(); config_init(); protos_build(); } void bt_bird_cleanup(void) { for (int i = 0; i < PROTOCOL__MAX; i++) class_to_protocol[i] = NULL; config = new_config = NULL; } static char * bt_load_file(const char *filename, int quiet) { FILE *f = fopen(filename, "rb"); if (!quiet) bt_assert_msg(f != NULL, "Open %s", filename); if (f == NULL) return NULL; fseek(f, 0, SEEK_END); long file_size_ = ftell(f); fseek(f, 0, SEEK_SET); if (file_size_ < 0) return NULL; size_t file_size = file_size_; size_t read_size = 0; char *file_body = mb_allocz(&root_pool, file_size+1); /* XXX: copied from cf-lex.c */ errno=0; while ((read_size += fread(file_body+read_size, 1, file_size-read_size, f)) != file_size && ferror(f)) { bt_debug("iteration \n"); if(errno != EINTR) { bt_abort_msg("errno: %d", errno); break; } errno=0; clearerr(f); } fclose(f); if (!quiet) bt_assert_msg(read_size == file_size, "Read %s", filename); return file_body; } static void bt_show_cfg_error(const struct config *cfg) { int lino = 0; int lino_delta = 5; int lino_err = cfg->err_lino; const char *str = bt_load_file(cfg->err_file_name, 1); while (str && *str) { lino++; if (BETWEEN(lino, lino_err - lino_delta, lino_err + lino_delta)) bt_debug("%4u%s", lino, (lino_err == lino ? " >> " : " ")); do { if (BETWEEN(lino, lino_err - lino_delta, lino_err + lino_delta)) bt_debug("%c", *str); } while (*str && *(str++) != '\n'); } bt_debug("\n"); } static struct config * bt_config_parse__(struct config *cfg) { bt_assert_msg(config_parse(cfg) == 1, "Parse %s", cfg->file_name); if (cfg->err_msg) { bt_log("Parse error %s, line %d: %s", cfg->err_file_name, cfg->err_lino, cfg->err_msg); bt_show_cfg_error(cfg); return NULL; } config_commit(cfg, RECONFIG_HARD, 0); new_config = cfg; return cfg; } struct config * bt_config_parse(const char *cfg_str) { struct config *cfg = config_alloc("configuration"); bt_config_parse_pos = cfg_str; bt_config_parse_remain_len = strlen(cfg_str); cf_read_hook = cf_static_read; return bt_config_parse__(cfg); } struct config * bt_config_file_parse(const char *filepath) { struct config *cfg = config_alloc(filepath); cfg->file_fd = open(filepath, O_RDONLY); bt_assert_msg(cfg->file_fd > 0, "Open %s", filepath); if (cfg->file_fd < 0) return NULL; cf_read_hook = cf_file_read; return bt_config_parse__(cfg); } /* * Returns @base raised to the power of @power. */ uint bt_naive_pow(uint base, uint power) { uint result = 1; uint i; for (i = 0; i < power; i++) result *= base; return result; } /** * bytes_to_hex - transform data into hexadecimal representation * @buf: preallocated string buffer * @in_data: data for transformation * @size: the length of @in_data * * This function transforms @in_data of length @size into hexadecimal * representation and writes it into @buf. */ void bt_bytes_to_hex(char *buf, const byte *in_data, size_t size) { size_t i; for(i = 0; i < size; i++) sprintf(buf + i*2, "%02x", in_data[i]); } void bt_random_net(net_addr *net, int type) { ip4_addr ip4; ip6_addr ip6; uint pxlen; switch (type) { case NET_IP4: pxlen = bt_random_n(24)+8; ip4 = ip4_from_u32((u32) bt_random()); net_fill_ip4(net, ip4_and(ip4, ip4_mkmask(pxlen)), pxlen); break; case NET_IP6: pxlen = bt_random_n(120)+8; ip6 = ip6_build(bt_random(), bt_random(), bt_random(), bt_random()); net_fill_ip6(net, ip6_and(ip6, ip6_mkmask(pxlen)), pxlen); break; default: die("Net type %d not implemented", type); } } net_addr * bt_random_nets(int type, uint n) { net_addr *nets = tmp_alloc(n * sizeof(net_addr)); for (uint i = 0; i < n; i++) bt_random_net(&nets[i], type); return nets; } net_addr * bt_random_net_subset(net_addr *src, uint sn, uint dn) { net_addr *nets = tmp_alloc(dn * sizeof(net_addr)); for (uint i = 0; i < dn; i++) net_copy(&nets[i], &src[bt_random_n(sn)]); return nets; } void bt_read_net(const char *str, net_addr *net, int type) { ip4_addr ip4; ip6_addr ip6; uint pxlen; char addr[64]; switch (type) { case NET_IP4: if (sscanf(str, "%[0-9.]/%u", addr, &pxlen) != 2) goto err; if (!ip4_pton(addr, &ip4)) goto err; if (!net_validate_px4(ip4, pxlen)) goto err; net_fill_ip4(net, ip4, pxlen); break; case NET_IP6: if (sscanf(str, "%[0-9a-fA-F:.]/%u", addr, &pxlen) != 2) goto err; if (!ip6_pton(addr, &ip6)) goto err; if (!net_validate_px6(ip6, pxlen)) goto err; net_fill_ip6(net, ip6, pxlen); break; default: die("Net type %d not implemented", type); } return; err: bt_abort_msg("Invalid network '%s'", str); } net_addr * bt_read_nets(FILE *f, int type, uint *n) { char str[80]; net_addr *nets = tmp_alloc(*n * sizeof(net_addr)); uint i = 0; errno = 0; while (fgets(str, sizeof(str), f)) { if (str[0] == '\n') break; if (i >= *n) bt_abort_msg("Too many networks"); bt_read_net(str, &nets[i], type); bt_debug("ADD %s\n", str); i++; } bt_syscall(errno, "fgets()"); bt_debug("DONE reading %u nets\n", i); *n = i; return nets; } net_addr * bt_read_net_file(const char *filename, int type, uint *n) { FILE *f = fopen(filename, "r"); bt_syscall(!f, "fopen(%s)", filename); net_addr *nets = bt_read_nets(f, type, n); fclose(f); return nets; }