| %{ |
| /* |
| * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org> |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This software has been sponsored by Sophos Astaro <http://www.sophos.com> |
| */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <stdarg.h> |
| #include <libiptc/linux_list.h> |
| #include <libnftnl/table.h> |
| #include <libnftnl/chain.h> |
| |
| #include <netinet/in.h> |
| #include <linux/netfilter.h> |
| |
| extern char *yytext; |
| extern int yylineno; |
| |
| static LIST_HEAD(xtables_stack); |
| |
| struct stack_elem { |
| struct list_head head; |
| int token; |
| size_t size; |
| char data[]; |
| }; |
| |
| static void *stack_push(int token, size_t size) |
| { |
| struct stack_elem *e; |
| |
| e = calloc(1, sizeof(struct stack_elem) + size); |
| |
| e->token = token; |
| e->size = size; |
| |
| list_add(&e->head, &xtables_stack); |
| |
| return e->data; |
| } |
| |
| static struct stack_elem *stack_pop(void) |
| { |
| struct stack_elem *e; |
| |
| e = list_entry(xtables_stack.next, struct stack_elem, head); |
| |
| if (&e->head == &xtables_stack) |
| return NULL; |
| |
| list_del(&e->head); |
| return e; |
| } |
| |
| static inline void stack_put_i32(void *data, int value) |
| { |
| memcpy(data, &value, sizeof(int)); |
| } |
| |
| static inline void stack_put_str(void *data, const char *str) |
| { |
| memcpy(data, str, strlen(str)); |
| } |
| |
| static void stack_free(struct stack_elem *e) |
| { |
| free(e); |
| } |
| |
| %} |
| |
| %union { |
| int val; |
| char *string; |
| } |
| |
| %token T_FAMILY |
| %token T_TABLE |
| %token T_CHAIN |
| %token T_HOOK |
| %token T_PRIO |
| |
| %token <string> T_STRING |
| %token <val> T_INTEGER |
| |
| %% |
| |
| configfile : |
| | lines |
| ; |
| |
| lines : line |
| | lines line |
| ; |
| |
| line : family |
| ; |
| |
| family : T_FAMILY T_STRING '{' tables '}' |
| { |
| void *data = stack_push(T_FAMILY, strlen($2)+1); |
| stack_put_str(data, $2); |
| } |
| ; |
| |
| tables : table |
| | tables table |
| ; |
| |
| table : T_TABLE T_STRING '{' chains '}' |
| { |
| /* added in reverse order to pop it in order */ |
| void *data = stack_push(T_TABLE, strlen($2)+1); |
| stack_put_str(data, $2); |
| } |
| ; |
| |
| chains : chain |
| | chains chain |
| ; |
| |
| chain : T_CHAIN T_STRING T_HOOK T_STRING T_PRIO T_INTEGER |
| { |
| /* added in reverse order to pop it in order */ |
| void *data = stack_push(T_PRIO, sizeof(int32_t)); |
| stack_put_i32(data, $6); |
| data = stack_push(T_HOOK, strlen($4)+1); |
| stack_put_str(data, $4); |
| data = stack_push(T_CHAIN, strlen($2)+1); |
| stack_put_str(data, $2); |
| } |
| ; |
| |
| %% |
| |
| int __attribute__((noreturn)) |
| yyerror(char *msg) |
| { |
| fprintf(stderr, "parsing config file in line (%d), symbol '%s': %s\n", |
| yylineno, yytext, msg); |
| exit(EXIT_FAILURE); |
| } |
| |
| static int hooknametonum(const char *hookname) |
| { |
| if (strcmp(hookname, "NF_INET_LOCAL_IN") == 0) |
| return NF_INET_LOCAL_IN; |
| else if (strcmp(hookname, "NF_INET_FORWARD") == 0) |
| return NF_INET_FORWARD; |
| else if (strcmp(hookname, "NF_INET_LOCAL_OUT") == 0) |
| return NF_INET_LOCAL_OUT; |
| else if (strcmp(hookname, "NF_INET_PRE_ROUTING") == 0) |
| return NF_INET_PRE_ROUTING; |
| else if (strcmp(hookname, "NF_INET_POST_ROUTING") == 0) |
| return NF_INET_POST_ROUTING; |
| |
| return -1; |
| } |
| |
| static int32_t familytonumber(const char *family) |
| { |
| if (strcmp(family, "ipv4") == 0) |
| return AF_INET; |
| else if (strcmp(family, "ipv6") == 0) |
| return AF_INET6; |
| |
| return -1; |
| } |
| |
| int xtables_config_parse(char *filename, struct nftnl_table_list *table_list, |
| struct nftnl_chain_list *chain_list) |
| { |
| FILE *fp; |
| struct stack_elem *e; |
| struct nftnl_table *table = NULL; |
| struct nftnl_chain *chain = NULL; |
| int prio = 0; |
| int32_t family = 0; |
| |
| fp = fopen(filename, "r"); |
| if (!fp) |
| return -1; |
| |
| yyrestart(fp); |
| yyparse(); |
| fclose(fp); |
| |
| for (e = stack_pop(); e != NULL; e = stack_pop()) { |
| switch(e->token) { |
| case T_FAMILY: |
| family = familytonumber(e->data); |
| if (family == -1) |
| return -1; |
| break; |
| case T_TABLE: |
| table = nftnl_table_alloc(); |
| if (table == NULL) |
| return -1; |
| |
| nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family); |
| nftnl_table_set(table, NFTNL_TABLE_NAME, e->data); |
| /* This is intentionally prepending, instead of |
| * appending, since the elements in the stack are in |
| * the reverse order that chains appear in the |
| * configuration file. |
| */ |
| nftnl_table_list_add(table, table_list); |
| break; |
| case T_PRIO: |
| memcpy(&prio, e->data, sizeof(int32_t)); |
| break; |
| case T_CHAIN: |
| chain = nftnl_chain_alloc(); |
| if (chain == NULL) |
| return -1; |
| |
| nftnl_chain_set(chain, NFTNL_CHAIN_TABLE, |
| (char *)nftnl_table_get(table, NFTNL_TABLE_NAME)); |
| nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY, |
| nftnl_table_get_u32(table, NFTNL_TABLE_FAMILY)); |
| nftnl_chain_set_s32(chain, NFTNL_CHAIN_PRIO, prio); |
| nftnl_chain_set(chain, NFTNL_CHAIN_NAME, e->data); |
| /* Intentionally prepending, instead of appending */ |
| nftnl_chain_list_add(chain, chain_list); |
| break; |
| case T_HOOK: |
| nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM, |
| hooknametonum(e->data)); |
| break; |
| default: |
| printf("unknown token type %d\n", e->token); |
| break; |
| } |
| stack_free(e); |
| } |
| |
| return 0; |
| } |