blob: 9291c89e643e06ae7f955952445f92f7cce65b7c [file] [log] [blame]
Holger Hans Peter Freyther85d3b342013-06-14 19:10:28 +02001/* Handling for loading a re-write file/database */
2/*
3 * (C) 2013 by On-Waves
4 * (C) 2013 by Holger Hans Peter Freyther <zecke@selfish.org>
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22#include <openbsc/nat_rewrite_trie.h>
23#include <openbsc/debug.h>
Holger Hans Peter Freytherddf191e2013-06-25 11:44:01 +020024#include <openbsc/vty.h>
Holger Hans Peter Freyther85d3b342013-06-14 19:10:28 +020025
26#include <osmocom/core/talloc.h>
27#include <osmocom/core/utils.h>
28
29#include <assert.h>
30#include <stdio.h>
31#include <string.h>
32#include <ctype.h>
33
34#define CHECK_IS_DIGIT_OR_FAIL(prefix, pos) \
35 if (!isdigit(prefix[pos]) && prefix[pos] != '+') { \
36 LOGP(DNAT, LOGL_ERROR, \
37 "Prefix(%s) contains non ascii text at(%d=%c)\n", \
38 prefix, pos, prefix[pos]); \
39 goto fail; \
40 }
41#define TO_INT(c) \
42 ((c) == '+' ? 10 : ((c - '0') % 10))
43
44static void insert_rewrite_node(struct nat_rewrite_rule *rule, struct nat_rewrite *root)
45{
46 struct nat_rewrite_rule *new = &root->rule;
47
Holger Hans Peter Freytherdaaea0c2015-08-03 09:28:41 +020048 const int len = strlen(rule->prefix);
Holger Hans Peter Freyther85d3b342013-06-14 19:10:28 +020049 int i;
50
Holger Hans Peter Freytherdaaea0c2015-08-03 09:28:41 +020051 if (len <= 0) {
Holger Hans Peter Freyther85d3b342013-06-14 19:10:28 +020052 LOGP(DNAT, LOGL_ERROR, "An empty prefix does not make sense.\n");
53 goto fail;
54 }
55
56 for (i = 0; i < len - 1; ++i) {
57 int pos;
58
59 /* check if the input is valid */
60 CHECK_IS_DIGIT_OR_FAIL(rule->prefix, i);
61
62 /* check if the next node is already valid */
63 pos = TO_INT(rule->prefix[i]);
64 if (!new->rules[pos]) {
65 new->rules[pos] = talloc_zero(root, struct nat_rewrite_rule);
66 if (!new->rules[pos]) {
67 LOGP(DNAT, LOGL_ERROR,
68 "Failed to allocate memory.\n");
69 goto fail;
70 }
71
72 new->rules[pos]->empty = 1;
73 }
74
75 /* we continue here */
76 new = new->rules[pos];
77 }
78
79 /* new now points to the place where we want to add it */
80 int pos;
81
82 /* check if the input is valid */
83 CHECK_IS_DIGIT_OR_FAIL(rule->prefix, (len - 1));
84
85 /* check if the next node is already valid */
86 pos = TO_INT(rule->prefix[len - 1]);
87 if (!new->rules[pos])
88 new->rules[pos] = rule;
89 else if (new->rules[pos]->empty) {
90 /* copy over entries */
91 new->rules[pos]->empty = 0;
92 memcpy(new->rules[pos]->prefix, rule->prefix, sizeof(rule->prefix));
93 memcpy(new->rules[pos]->rewrite, rule->rewrite, sizeof(rule->rewrite));
94 talloc_free(rule);
95 } else {
96 LOGP(DNAT, LOGL_ERROR,
97 "Prefix(%s) is already installed\n", rule->prefix);
98 goto fail;
99 }
100
101 root->prefixes += 1;
102 return;
103
104fail:
105 talloc_free(rule);
106 return;
107}
108
109static void handle_line(struct nat_rewrite *rewrite, char *line)
110{
111 char *split;
112 struct nat_rewrite_rule *rule;
113 size_t size_prefix, size_end, len;
114
115
116 /* Find the ',' in the line */
117 len = strlen(line);
118 split = strstr(line, ",");
119 if (!split) {
120 LOGP(DNAT, LOGL_ERROR, "Line doesn't contain ','\n");
121 return;
122 }
123
124 /* Check if there is space for the rewrite rule */
125 size_prefix = split - line;
126 if (len - size_prefix <= 2) {
127 LOGP(DNAT, LOGL_ERROR, "No rewrite available.\n");
128 return;
129 }
130
131 /* Continue after the ',' to the end */
132 split = &line[size_prefix + 1];
133 size_end = strlen(split) - 1;
134
135 /* Check if both strings can fit into the static array */
136 if (size_prefix > sizeof(rule->prefix) - 1) {
137 LOGP(DNAT, LOGL_ERROR,
138 "Prefix is too long with %zu\n", size_prefix);
139 return;
140 }
141
142 if (size_end > sizeof(rule->rewrite) - 1) {
143 LOGP(DNAT, LOGL_ERROR,
144 "Rewrite is too long with %zu on %s\n",
145 size_end, &line[size_prefix + 1]);
146 return;
147 }
148
149 /* Now create the entry and insert it into the trie */
150 rule = talloc_zero(rewrite, struct nat_rewrite_rule);
151 if (!rule) {
152 LOGP(DNAT, LOGL_ERROR, "Can not allocate memory\n");
153 return;
154 }
155
156 memcpy(rule->prefix, line, size_prefix);
157 assert(size_prefix < sizeof(rule->prefix));
158 rule->prefix[size_prefix] = '\0';
159
160 memcpy(rule->rewrite, split, size_end);
161 assert(size_end < sizeof(rule->rewrite));
162 rule->rewrite[size_end] = '\0';
163
164 /* now insert and balance the tree */
165 insert_rewrite_node(rule, rewrite);
166}
167
168struct nat_rewrite *nat_rewrite_parse(void *ctx, const char *filename)
169{
170 FILE *file;
171 char *line = NULL;
172 size_t n = 2342;
173 struct nat_rewrite *res;
174
175 file = fopen(filename, "r");
176 if (!file)
177 return NULL;
178
179 res = talloc_zero(ctx, struct nat_rewrite);
180 if (!res) {
181 fclose(file);
182 return NULL;
183 }
184
185 /* mark the root as empty */
186 res->rule.empty = 1;
187
188 while (getline(&line, &n, file) != -1) {
189 handle_line(res, line);
190 }
191
192 free(line);
193 fclose(file);
194 return res;
195}
196
197/**
198 * Simple find that tries to do a longest match...
199 */
200struct nat_rewrite_rule *nat_rewrite_lookup(struct nat_rewrite *rewrite,
201 const char *prefix)
202{
203 struct nat_rewrite_rule *rule = &rewrite->rule;
204 struct nat_rewrite_rule *last = NULL;
205 const int len = OSMO_MIN(strlen(prefix), (sizeof(rule->prefix) - 1));
206 int i;
207
208 for (i = 0; rule && i < len; ++i) {
209 int pos;
210
211 CHECK_IS_DIGIT_OR_FAIL(prefix, i);
212 pos = TO_INT(prefix[i]);
213
214 rule = rule->rules[pos];
215 if (rule && !rule->empty)
216 last = rule;
217 }
218
219 return last;
220
221fail:
222 return NULL;
223}
224
225static void nat_rewrite_dump_rec(struct nat_rewrite_rule *rule)
226{
227 int i;
228 if (!rule->empty)
229 printf("%s,%s\n", rule->prefix, rule->rewrite);
230
231 for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) {
232 if (!rule->rules[i])
233 continue;
234 nat_rewrite_dump_rec(rule->rules[i]);
235 }
236}
237
238void nat_rewrite_dump(struct nat_rewrite *rewrite)
239{
240 nat_rewrite_dump_rec(&rewrite->rule);
241}
Holger Hans Peter Freytherddf191e2013-06-25 11:44:01 +0200242
243static void nat_rewrite_dump_rec_vty(struct vty *vty, struct nat_rewrite_rule *rule)
244{
245 int i;
246 if (!rule->empty)
247 vty_out(vty, "%s,%s%s", rule->prefix, rule->rewrite, VTY_NEWLINE);
248
249 for (i = 0; i < ARRAY_SIZE(rule->rules); ++i) {
250 if (!rule->rules[i])
251 continue;
252 nat_rewrite_dump_rec_vty(vty, rule->rules[i]);
253 }
254}
255
256void nat_rewrite_dump_vty(struct vty *vty, struct nat_rewrite *rewrite)
257{
258 nat_rewrite_dump_rec_vty(vty, &rewrite->rule);
259}