%{ /* * (C) 2012 by Pablo Neira Ayuso * * 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 */ #include #include #include #include #include #include #include #include #include #include #include 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 T_STRING %token 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 nft_table_list *table_list, struct nft_chain_list *chain_list) { FILE *fp; struct stack_elem *e; struct nft_table *table = NULL; struct nft_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 = nft_table_alloc(); if (table == NULL) { perror("nft_table_alloc"); return -1; } nft_table_attr_set_u32(table, NFT_TABLE_ATTR_FAMILY, family); nft_table_attr_set(table, NFT_TABLE_ATTR_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. */ nft_table_list_add(table, table_list); break; case T_PRIO: memcpy(&prio, e->data, sizeof(int32_t)); break; case T_CHAIN: chain = nft_chain_alloc(); if (chain == NULL) { perror("nft_chain_alloc"); return -1; } nft_chain_attr_set(chain, NFT_CHAIN_ATTR_TABLE, (char *)nft_table_attr_get(table, NFT_TABLE_ATTR_NAME)); nft_chain_attr_set_u32(chain, NFT_CHAIN_ATTR_FAMILY, nft_table_attr_get_u32(table, NFT_TABLE_ATTR_FAMILY)); nft_chain_attr_set_s32(chain, NFT_CHAIN_ATTR_PRIO, prio); nft_chain_attr_set(chain, NFT_CHAIN_ATTR_NAME, e->data); /* Intentionally prepending, instead of appending */ nft_chain_list_add(chain, chain_list); break; case T_HOOK: nft_chain_attr_set_u32(chain, NFT_CHAIN_ATTR_HOOKNUM, hooknametonum(e->data)); break; default: printf("unknown token type %d\n", e->token); break; } stack_free(e); } return 0; }