1 #include "slurm/slurm_loader.h"
2
3 #include <stdlib.h>
4 #include <stdbool.h>
5 #include <sys/types.h> /* AF_INET, AF_INET6 (needed in OpenBSD) */
6 #include <sys/socket.h> /* AF_INET, AF_INET6 (needed in OpenBSD) */
7
8 #include "log.h"
9 #include "config.h"
10 #include "common.h"
11 #include "crypto/hash.h"
12 #include "slurm/slurm_parser.h"
13
14 #define SLURM_FILE_EXTENSION ".slurm"
15
16 struct slurm_parser_params {
17 struct db_table *db_table;
18 struct db_slurm *db_slurm;
19 };
20
21 /*
22 * Load the SLURM file(s) from the configured path.
23 *
24 * If this returns zero but @result is NULL, it's fine. There are no SLURM
25 * rules.
26 */
27 static int
load_slurm_files(struct slurm_csum_list * csums,struct db_slurm ** result)28 load_slurm_files(struct slurm_csum_list *csums, struct db_slurm **result)
29 {
30 struct db_slurm *db;
31 int error;
32
33 error = db_slurm_create(csums, &db);
34 if (error)
35 return error;
36
37 error = process_file_or_dir(config_get_slurm(), SLURM_FILE_EXTENSION,
38 false, slurm_parse, db);
39 if (error)
40 goto cancel;
41
42 /* Empty SLURM dir, or empty SLURM file(s) */
43 if (!db_slurm_has_data(db)) {
44 *result = NULL;
45 goto cancel; /* Success. */
46 }
47
48 *result = db;
49 return 0;
50
51 cancel:
52 db_slurm_destroy(db);
53 return error;
54 }
55
56 static int
slurm_pfx_filters_apply(struct vrp const * vrp,void * arg)57 slurm_pfx_filters_apply(struct vrp const *vrp, void *arg)
58 {
59 struct slurm_parser_params *params = arg;
60
61 if (db_slurm_vrp_is_filtered(params->db_slurm, vrp))
62 db_table_remove_roa(params->db_table, vrp);
63
64 return 0;
65 }
66
67 static int
slurm_pfx_assertions_add(struct slurm_prefix * prefix,void * arg)68 slurm_pfx_assertions_add(struct slurm_prefix *prefix, void *arg)
69 {
70 struct db_table *table = arg;
71 struct ipv4_prefix prefix4;
72 struct ipv6_prefix prefix6;
73 struct vrp vrp;
74
75 vrp = prefix->vrp;
76 if ((prefix->data_flag & SLURM_PFX_FLAG_MAX_LENGTH) == 0)
77 vrp.max_prefix_length = vrp.prefix_length;
78
79 if (vrp.addr_fam == AF_INET) {
80 prefix4.addr = vrp.prefix.v4;
81 prefix4.len = vrp.prefix_length;
82 return rtrhandler_handle_roa_v4(table, vrp.asn, &prefix4,
83 vrp.max_prefix_length);
84 }
85 if (vrp.addr_fam == AF_INET6) {
86 prefix6.addr = vrp.prefix.v6;
87 prefix6.len = vrp.prefix_length;
88 return rtrhandler_handle_roa_v6(table, vrp.asn, &prefix6,
89 vrp.max_prefix_length);
90 }
91
92 pr_crit("Unknown addr family type: %u", vrp.addr_fam);
93 }
94
95 static int
slurm_pfx_assertions_apply(struct slurm_parser_params * params)96 slurm_pfx_assertions_apply(struct slurm_parser_params *params)
97 {
98 return db_slurm_foreach_assertion_prefix(params->db_slurm,
99 slurm_pfx_assertions_add, params->db_table);
100 }
101
102 static int
slurm_bgpsec_filters_apply(struct router_key const * key,void * arg)103 slurm_bgpsec_filters_apply(struct router_key const *key, void *arg)
104 {
105 struct slurm_parser_params *params = arg;
106
107 if (db_slurm_bgpsec_is_filtered(params->db_slurm, key))
108 db_table_remove_router_key(params->db_table, key);
109
110 return 0;
111 }
112
113 static int
slurm_bgpsec_assertions_add(struct slurm_bgpsec * bgpsec,void * arg)114 slurm_bgpsec_assertions_add(struct slurm_bgpsec *bgpsec, void *arg)
115 {
116 struct db_table *table = arg;
117
118 return rtrhandler_handle_router_key(table, bgpsec->ski, bgpsec->asn,
119 bgpsec->router_public_key);
120 }
121
122 static int
slurm_bgpsec_assertions_apply(struct slurm_parser_params * params)123 slurm_bgpsec_assertions_apply(struct slurm_parser_params *params)
124 {
125 return db_slurm_foreach_assertion_bgpsec(params->db_slurm,
126 slurm_bgpsec_assertions_add, params->db_table);
127 }
128
129 static int
__slurm_load_checksums(char const * location,void * arg)130 __slurm_load_checksums(char const *location, void *arg)
131 {
132 struct slurm_csum_list *list;
133 struct slurm_file_csum *csum;
134 int error;
135
136 csum = malloc(sizeof(struct slurm_file_csum));
137 if (csum == NULL)
138 return pr_enomem();
139
140 error = hash_local_file("sha256", location, csum->csum,
141 &csum->csum_len);
142 if (error) {
143 free(csum);
144 return pr_op_err("Calculating slurm hash");
145 }
146
147 list = arg;
148 SLIST_INSERT_HEAD(list, csum, next);
149 list->list_size++;
150
151 return 0;
152 }
153
154 static void
destroy_local_csum_list(struct slurm_csum_list * list)155 destroy_local_csum_list(struct slurm_csum_list *list)
156 {
157 struct slurm_file_csum *tmp;
158
159 while (!SLIST_EMPTY(list)) {
160 tmp = SLIST_FIRST(list);
161 SLIST_REMOVE_HEAD(list, next);
162 free(tmp);
163 }
164 }
165
166 static int
slurm_load_checksums(struct slurm_csum_list * csums)167 slurm_load_checksums(struct slurm_csum_list *csums)
168 {
169 int error;
170
171 SLIST_INIT(csums);
172 csums->list_size = 0;
173
174 error = process_file_or_dir(config_get_slurm(), SLURM_FILE_EXTENSION,
175 false, __slurm_load_checksums, csums);
176 if (error)
177 destroy_local_csum_list(csums);
178
179 return error;
180 }
181
182 static bool
are_csum_lists_equals(struct slurm_csum_list * new_list,struct slurm_csum_list * old_list)183 are_csum_lists_equals(struct slurm_csum_list *new_list,
184 struct slurm_csum_list *old_list)
185 {
186 struct slurm_file_csum *newcsum, *old;
187 bool found = false;
188
189 if (new_list->list_size != old_list->list_size) {
190 return false;
191 }
192
193 SLIST_FOREACH(newcsum, new_list, next) {
194 SLIST_FOREACH(old, old_list, next) {
195
196 if (newcsum->csum_len != old->csum_len)
197 continue;
198
199 if (memcmp(newcsum->csum, old->csum,
200 newcsum->csum_len) == 0) {
201 found = true;
202 break;
203 }
204 }
205
206 if (!found)
207 return false;
208
209 found = false;
210 }
211
212 return true;
213 }
214
215 /* Load SLURM file(s) that have updates */
216 static int
update_slurm(struct db_slurm ** slurm)217 update_slurm(struct db_slurm **slurm)
218 {
219 struct slurm_csum_list new_csums;
220 struct slurm_csum_list old_csums;
221 struct db_slurm *new_slurm = NULL;
222 int error;
223
224 pr_op_info("Checking if there are new or modified SLURM files");
225
226 error = slurm_load_checksums(&new_csums);
227 if (error)
228 return error;
229
230 /* Empty DIR or FILE SLURM not found */
231 if (new_csums.list_size == 0)
232 goto success;
233
234 if (*slurm != NULL) {
235 db_slurm_get_csum_list(*slurm, &old_csums);
236 if (are_csum_lists_equals(&new_csums, &old_csums)) {
237 pr_op_info("Applying same old SLURM, no changes found.");
238 destroy_local_csum_list(&new_csums);
239 return 0;
240 }
241 }
242
243 pr_op_info("Applying configured SLURM");
244
245 error = load_slurm_files(&new_csums, &new_slurm);
246
247 /*
248 * Checksums were transferred to new_slurm on success, but they're
249 * still here on failure.
250 * Either way, new_csums is ready for cleanup.
251 */
252 destroy_local_csum_list(&new_csums);
253
254 if (error) {
255 /* Fall back to previous iteration's SLURM */
256 pr_op_info("Error %d loading SLURM. The validation will continue regardless.",
257 error);
258 if (*slurm != NULL) {
259 pr_op_info("A previous valid version of the SLURM exists and will be applied.");
260 db_slurm_log(*slurm);
261 }
262
263 return 0;
264 }
265
266 success:
267 /* Use new SLURM as last valid slurm */
268 if (*slurm != NULL)
269 db_slurm_destroy(*slurm);
270
271 *slurm = new_slurm;
272 return 0;
273 }
274
275 int
slurm_apply(struct db_table * base,struct db_slurm ** slurm)276 slurm_apply(struct db_table *base, struct db_slurm **slurm)
277 {
278 struct slurm_parser_params params;
279 int error;
280
281 if (config_get_slurm() == NULL)
282 return 0;
283
284 error = update_slurm(slurm);
285 if (error)
286 return error;
287
288 if (*slurm == NULL)
289 return 0;
290
291 /* Ok, apply SLURM */
292
293 params.db_table = base;
294 params.db_slurm = *slurm;
295
296 /* TODO invert this. SLURM rules are few, and base is massive. */
297 error = db_table_foreach_roa(base, slurm_pfx_filters_apply, ¶ms);
298 if (error)
299 return error;
300
301 error = db_table_foreach_router_key(base, slurm_bgpsec_filters_apply,
302 ¶ms);
303 if (error)
304 return error;
305
306 error = slurm_pfx_assertions_apply(¶ms);
307 if (error)
308 return error;
309
310 return slurm_bgpsec_assertions_apply(¶ms);
311 }
312