| /* Handling for loading a re-write file/database */ |
| /* |
| * (C) 2013 by On-Waves |
| * (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org> |
| * All Rights Reserved |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU Affero General Public License as published by |
| * the Free Software Foundation; either version 3 of the License, or |
| * (at your option) any later version. |
| * |
| * This program 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 Affero General Public License for more details. |
| * |
| * You should have received a copy of the GNU Affero General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| * |
| */ |
| |
| #include <openbsc/nat_rewrite_trie.h> |
| #include <openbsc/debug.h> |
| #include <openbsc/vty.h> |
| |
| #include <osmocom/core/talloc.h> |
| #include <osmocom/core/utils.h> |
| |
| #include <assert.h> |
| #include <stdio.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| #define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \ |
| if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \ |
| LOGP(DNAT, LOGL_ERROR, \ |
| "Prefix(%s) contains non ascii text at(%d=%c)\n", \ |
| prefix, pos, prefix[pos]); \ |
| goto fail; \ |
| } |
| #define TO_INT(c) \ |
| ((c) == '+' ? 10 : ((c - '0') % 10)) |
| |
| static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root) |
| { |
| struct nat_rewrite_rule *new = &root->rule; |
| |
| const int len = strlen(rule->prefix); |
| int i; |
| |
| if (len <= 0) { |
| LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n"); |
| goto fail; |
| } |
| |
| for (i = 0; i < len - 1; ++i) { |
| int pos; |
| |
| /* check if the input is valid */ |
| CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i); |
| |
| /* check if the next node is already valid */ |
| pos = TO_INT(rule->prefix[i]); |
| if (!new->rules[pos]) { |
| new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule); |
| if (!new->rules[pos]) { |
| LOGP(DNAT, LOGL_ERROR, |
| "Failed to allocate memory.\n"); |
| goto fail; |
| } |
| |
| new->rules[pos]->empty = 1; |
| } |
| |
| /* we continue here */ |
| new = new->rules[pos]; |
| } |
| |
| /* new now points to the place where we want to add it */ |
| int pos; |
| |
| /* check if the input is valid */ |
| CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1)); |
| |
| /* check if the next node is already valid */ |
| pos = TO_INT(rule->prefix[len - 1]); |
| if (!new->rules[pos]) |
| new->rules[pos] = rule; |
| else if (new->rules[pos]->empty) { |
| /* copy over entries */ |
| new->rules[pos]->empty = 0; |
| memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix)); |
| memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite)); |
| talloc_free(rule); |
| } else { |
| LOGP(DNAT, LOGL_ERROR, |
| "Prefix(%s) is already installed\n", rule->prefix); |
| goto fail; |
| } |
| |
| root->prefixes += 1; |
| return; |
| |
| fail: |
| talloc_free(rule); |
| return; |
| } |
| |
| static void handle_line(struct nat_rewrite *rewrite, char *line) |
| { |
| char *split; |
| struct nat_rewrite_rule *rule; |
| size_t size_prefix, size_end, len; |
| |
| |
| /* Find the ',' in the line */ |
| len = strlen(line); |
| split = strstr(line, ","); |
| if (!split) { |
| LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n"); |
| return; |
| } |
| |
| /* Check if there is space for the rewrite rule */ |
| size_prefix = split - line; |
| if (len - size_prefix <= 2) { |
| LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n"); |
| return; |
| } |
| |
| /* Continue after the ',' to the end */ |
| split = &line[size_prefix + 1]; |
| size_end = strlen(split) - 1; |
| |
| /* Check if both strings can fit into the static array */ |
| if (size_prefix > sizeof(rule->prefix) - 1) { |
| LOGP(DNAT, LOGL_ERROR, |
| "Prefix is too long with %zu\n", size_prefix); |
| return; |
| } |
| |
| if (size_end > sizeof(rule->rewrite) - 1) { |
| LOGP(DNAT, LOGL_ERROR, |
| "Rewrite is too long with %zu on %s\n", |
| size_end, &line[size_prefix + 1]); |
| return; |
| } |
| |
| /* Now create the entry and insert it into the trie */ |
| rule = talloc_zero(rewrite, struct nat_rewrite_rule); |
| if (!rule) { |
| LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n"); |
| return; |
| } |
| |
| memcpy(rule->prefix, line, size_prefix); |
| assert(size_prefix < sizeof(rule->prefix)); |
| rule->prefix[size_prefix] = '\0'; |
| |
| memcpy(rule->rewrite, split, size_end); |
| assert(size_end < sizeof(rule->rewrite)); |
| rule->rewrite[size_end] = '\0'; |
| |
| /* now insert and balance the tree */ |
| insert_rewrite_node(rule, rewrite); |
| } |
| |
| struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename) |
| { |
| FILE *file; |
| char *line = NULL; |
| size_t n = 2342; |
| struct nat_rewrite *res; |
| |
| file = fopen(filename, "r"); |
| if (!file) |
| return NULL; |
| |
| res = talloc_zero(ctx, struct nat_rewrite); |
| if (!res) { |
| fclose(file); |
| return NULL; |
| } |
| |
| /* mark the root as empty */ |
| res->rule.empty = 1; |
| |
| while (getline(&line, &n, file) != -1) { |
| handle_line(res, line); |
| } |
| |
| free(line); |
| fclose(file); |
| return res; |
| } |
| |
| /** |
| * Simple find that tries to do a longest match... |
| */ |
| struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite, |
| const char *prefix) |
| { |
| struct nat_rewrite_rule *rule = &rewrite->rule; |
| struct nat_rewrite_rule *last = NULL; |
| const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1)); |
| int i; |
| |
| for (i = 0; rule && i < len; ++i) { |
| int pos; |
| |
| CHECK_IS_DIGIT_OR_FAIL(prefix, i); |
| pos = TO_INT(prefix[i]); |
| |
| rule = rule->rules[pos]; |
| if (rule && !rule->empty) |
| last = rule; |
| } |
| |
| return last; |
| |
| fail: |
| return NULL; |
| } |
| |
| static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule) |
| { |
| int i; |
| if (!rule->empty) |
| printf("%s,%s\n", rule->prefix, rule->rewrite); |
| |
| for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { |
| if (!rule->rules[i]) |
| continue; |
| nat_rewrite_dump_rec(rule->rules[i]); |
| } |
| } |
| |
| void nat_rewrite_dump(struct nat_rewrite *rewrite) |
| { |
| nat_rewrite_dump_rec(&rewrite->rule); |
| } |
| |
| static void nat_rewrite_dump_rec_vty(struct vty *vty, struct nat_rewrite_rule *rule) |
| { |
| int i; |
| if (!rule->empty) |
| vty_out(vty, "%s,%s%s", rule->prefix, rule->rewrite, VTY_NEWLINE); |
| |
| for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) { |
| if (!rule->rules[i]) |
| continue; |
| nat_rewrite_dump_rec_vty(vty, rule->rules[i]); |
| } |
| } |
| |
| void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewrite) |
| { |
| nat_rewrite_dump_rec_vty(vty, &rewrite->rule); |
| } |