remove now unsed fw3_free_list() helper
[project/firewall3.git] / utils.c
1 /*
2 * firewall3 - 3rd OpenWrt UCI firewall implementation
3 *
4 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include "utils.h"
20 #include "options.h"
21
22 #include "zones.h"
23 #include "ipsets.h"
24
25
26 static int lock_fd = -1;
27 static pid_t pipe_pid = -1;
28 static FILE *pipe_fd = NULL;
29
30 static void
31 warn_elem_section_name(struct uci_section *s, bool find_name)
32 {
33 int i = 0;
34 struct uci_option *o;
35 struct uci_element *tmp;
36
37 if (s->anonymous)
38 {
39 uci_foreach_element(&s->package->sections, tmp)
40 {
41 if (strcmp(uci_to_section(tmp)->type, s->type))
42 continue;
43
44 if (&s->e == tmp)
45 break;
46
47 i++;
48 }
49
50 fprintf(stderr, "@%s[%d]", s->type, i);
51
52 if (find_name)
53 {
54 uci_foreach_element(&s->options, tmp)
55 {
56 o = uci_to_option(tmp);
57
58 if (!strcmp(tmp->name, "name") && (o->type == UCI_TYPE_STRING))
59 {
60 fprintf(stderr, " (%s)", o->v.string);
61 break;
62 }
63 }
64 }
65 }
66 else
67 {
68 fprintf(stderr, "'%s'", s->e.name);
69 }
70
71 if (find_name)
72 fprintf(stderr, " ");
73 }
74
75 void
76 warn_elem(struct uci_element *e, const char *format, ...)
77 {
78 if (e->type == UCI_TYPE_SECTION)
79 {
80 fprintf(stderr, "Warning: Section ");
81 warn_elem_section_name(uci_to_section(e), true);
82 }
83 else if (e->type == UCI_TYPE_OPTION)
84 {
85 fprintf(stderr, "Warning: Option ");
86 warn_elem_section_name(uci_to_option(e)->section, false);
87 fprintf(stderr, ".%s ", e->name);
88 }
89
90 va_list argptr;
91 va_start(argptr, format);
92 vfprintf(stderr, format, argptr);
93 va_end(argptr);
94
95 fprintf(stderr, "\n");
96 }
97
98 void
99 warn(const char* format, ...)
100 {
101 fprintf(stderr, "Warning: ");
102 va_list argptr;
103 va_start(argptr, format);
104 vfprintf(stderr, format, argptr);
105 va_end(argptr);
106 fprintf(stderr, "\n");
107 }
108
109 void
110 error(const char* format, ...)
111 {
112 fprintf(stderr, "Error: ");
113 va_list argptr;
114 va_start(argptr, format);
115 vfprintf(stderr, format, argptr);
116 va_end(argptr);
117 fprintf(stderr, "\n");
118
119 exit(1);
120 }
121
122 void
123 info(const char* format, ...)
124 {
125 va_list argptr;
126 va_start(argptr, format);
127 vfprintf(stderr, format, argptr);
128 va_end(argptr);
129 fprintf(stderr, "\n");
130 }
131
132 const char *
133 fw3_find_command(const char *cmd)
134 {
135 struct stat s;
136 int plen = 0, clen = strlen(cmd) + 1;
137 char *search, *p;
138 static char path[PATH_MAX];
139
140 if (!stat(cmd, &s) && S_ISREG(s.st_mode))
141 return cmd;
142
143 search = getenv("PATH");
144
145 if (!search)
146 search = "/bin:/usr/bin:/sbin:/usr/sbin";
147
148 p = search;
149
150 do
151 {
152 if (*p != ':' && *p != '\0')
153 continue;
154
155 plen = p - search;
156
157 if ((plen + clen) >= sizeof(path))
158 continue;
159
160 strncpy(path, search, plen);
161 sprintf(path + plen, "/%s", cmd);
162
163 if (!stat(path, &s) && S_ISREG(s.st_mode))
164 return path;
165
166 search = p + 1;
167 }
168 while (*p++);
169
170 return NULL;
171 }
172
173 bool
174 fw3_stdout_pipe(void)
175 {
176 pipe_fd = stdout;
177 return true;
178 }
179
180 bool
181 __fw3_command_pipe(bool silent, const char *command, ...)
182 {
183 pid_t pid;
184 va_list argp;
185 int pfds[2];
186 int argn;
187 char *arg, **args, **tmp;
188
189 command = fw3_find_command(command);
190
191 if (!command)
192 return false;
193
194 if (pipe(pfds))
195 return false;
196
197 argn = 2;
198 args = malloc(argn * sizeof(arg));
199
200 if (!args)
201 return false;
202
203 args[0] = (char *)command;
204 args[1] = NULL;
205
206 va_start(argp, command);
207
208 while ((arg = va_arg(argp, char *)) != NULL)
209 {
210 tmp = realloc(args, ++argn * sizeof(arg));
211
212 if (!tmp)
213 break;
214
215 args = tmp;
216 args[argn-2] = arg;
217 args[argn-1] = NULL;
218 }
219
220 va_end(argp);
221
222 switch ((pid = fork()))
223 {
224 case -1:
225 return false;
226
227 case 0:
228 dup2(pfds[0], 0);
229
230 close(pfds[0]);
231 close(pfds[1]);
232
233 close(1);
234
235 if (silent)
236 close(2);
237
238 execv(command, args);
239
240 default:
241 signal(SIGPIPE, SIG_IGN);
242 pipe_pid = pid;
243 close(pfds[0]);
244 }
245
246 pipe_fd = fdopen(pfds[1], "w");
247 return true;
248 }
249
250 void
251 fw3_pr(const char *fmt, ...)
252 {
253 va_list args;
254 va_start(args, fmt);
255 vfprintf(pipe_fd, fmt, args);
256 va_end(args);
257 }
258
259 void
260 fw3_command_close(void)
261 {
262 if (pipe_fd && pipe_fd != stdout)
263 fclose(pipe_fd);
264
265 if (pipe_pid > -1)
266 waitpid(pipe_pid, NULL, 0);
267
268 signal(SIGPIPE, SIG_DFL);
269
270 pipe_fd = NULL;
271 pipe_pid = -1;
272 }
273
274 bool
275 fw3_has_table(bool ipv6, const char *table)
276 {
277 FILE *f;
278
279 char line[12];
280 bool seen = false;
281
282 const char *path = ipv6
283 ? "/proc/net/ip6_tables_names" : "/proc/net/ip_tables_names";
284
285 if (!(f = fopen(path, "r")))
286 return false;
287
288 while (fgets(line, sizeof(line), f))
289 {
290 if (!strncmp(line, table, strlen(table)))
291 {
292 seen = true;
293 break;
294 }
295 }
296
297 fclose(f);
298
299 return seen;
300 }
301
302
303 bool
304 fw3_lock(void)
305 {
306 lock_fd = open(FW3_LOCKFILE, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR);
307
308 if (lock_fd < 0)
309 {
310 warn("Cannot create lock file %s: %s", FW3_LOCKFILE, strerror(errno));
311 return false;
312 }
313
314 if (flock(lock_fd, LOCK_EX))
315 {
316 warn("Cannot acquire exclusive lock: %s", strerror(errno));
317 return false;
318 }
319
320 return true;
321 }
322
323 void
324 fw3_unlock(void)
325 {
326 if (lock_fd < 0)
327 return;
328
329 if (flock(lock_fd, LOCK_UN))
330 warn("Cannot release exclusive lock: %s", strerror(errno));
331
332 close(lock_fd);
333 unlink(FW3_LOCKFILE);
334
335 lock_fd = -1;
336 }
337
338
339 bool
340 fw3_read_statefile(void *state)
341 {
342 FILE *sf;
343
344 int n, type;
345 char line[128];
346 const char *p, *name;
347
348 uint16_t flags[2];
349
350 struct fw3_state *s = state;
351 struct fw3_zone *zone;
352 struct fw3_ipset *ipset;
353
354 sf = fopen(FW3_STATEFILE, "r");
355
356 if (!sf)
357 return false;
358
359 while (fgets(line, sizeof(line), sf))
360 {
361 p = strtok(line, " \t\n");
362
363 if (!p)
364 continue;
365
366 type = strtoul(p, NULL, 10);
367 name = strtok(NULL, " \t\n");
368
369 if (!name)
370 continue;
371
372 for (n = 0, p = strtok(NULL, " \t\n");
373 n < ARRAY_SIZE(flags) && p != NULL;
374 n++, p = strtok(NULL, " \t\n"))
375 {
376 flags[n] = strtoul(p, NULL, 10);
377 }
378
379 switch (type)
380 {
381 case FW3_TYPE_DEFAULTS:
382 s->running_defaults.flags = flags[0];
383 break;
384
385 case FW3_TYPE_ZONE:
386 if (!(zone = fw3_lookup_zone(state, name, false)))
387 {
388 zone = fw3_alloc_zone();
389
390 if (!zone)
391 continue;
392
393 zone->name = strdup(name);
394 list_add_tail(&zone->list, &s->zones);
395 }
396
397 zone->src_flags = flags[0];
398 zone->dst_flags = flags[1];
399 list_add_tail(&zone->running_list, &s->running_zones);
400 break;
401
402 case FW3_TYPE_IPSET:
403 if (!(ipset = fw3_lookup_ipset(state, name, false)))
404 {
405 ipset = fw3_alloc_ipset();
406
407 if (!ipset)
408 continue;
409
410 ipset->name = strdup(name);
411 list_add_tail(&ipset->list, &s->ipsets);
412 }
413
414 ipset->flags = flags[0];
415 list_add_tail(&ipset->running_list, &s->running_ipsets);
416 break;
417 }
418 }
419
420 fclose(sf);
421
422 return true;
423 }
424
425 void
426 fw3_write_statefile(void *state)
427 {
428 FILE *sf;
429 struct fw3_state *s = state;
430 struct fw3_defaults *d = &s->defaults;
431 struct fw3_zone *z;
432 struct fw3_ipset *i;
433
434 int mask = (1 << FW3_FAMILY_V4) | (1 << FW3_FAMILY_V6);
435
436 if (!(d->flags & mask))
437 {
438 if (unlink(FW3_STATEFILE))
439 warn("Unable to remove state %s: %s",
440 FW3_STATEFILE, strerror(errno));
441
442 return;
443 }
444
445 sf = fopen(FW3_STATEFILE, "w");
446
447 if (!sf)
448 {
449 warn("Cannot create state %s: %s", FW3_STATEFILE, strerror(errno));
450 return;
451 }
452
453 fprintf(sf, "%u - %u\n", FW3_TYPE_DEFAULTS, d->flags);
454
455 list_for_each_entry(z, &s->running_zones, running_list)
456 {
457 fprintf(sf, "%u %s %u %u\n", FW3_TYPE_ZONE,
458 z->name, z->src_flags, z->dst_flags);
459 }
460
461 list_for_each_entry(i, &s->running_ipsets, running_list)
462 {
463 fprintf(sf, "%u %s %u\n", FW3_TYPE_IPSET, i->name, i->flags);
464 }
465
466 fclose(sf);
467 }
468
469
470 struct object_list_heads
471 {
472 struct list_head list;
473 struct list_head running_list;
474 };
475
476 void
477 fw3_set_running(void *object, struct list_head *dest)
478 {
479 struct object_list_heads *o = object;
480
481 if (dest && !o->running_list.next)
482 list_add_tail(&o->running_list, dest);
483 else if (!dest && o->running_list.next)
484 list_del(&o->running_list);
485 }
486
487 void
488 fw3_free_object(void *obj, const void *opts)
489 {
490 const struct fw3_option *ol;
491 struct list_head *list, *cur, *tmp;
492
493 for (ol = opts; ol->name; ol++)
494 {
495 if (!ol->elem_size)
496 continue;
497
498 list = (struct list_head *)((char *)obj + ol->offset);
499 list_for_each_safe(cur, tmp, list)
500 {
501 list_del(cur);
502 free(cur);
503 }
504 }
505
506 free(obj);
507 }