1 %{
2 /*
3 * (C) 2012 by Pablo Neira Ayuso <pablo@netfilter.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software has been sponsored by Sophos Astaro <http://www.sophos.com>
11 */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <stdint.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <stdarg.h>
19 #include <libiptc/linux_list.h>
20 #include <libnftnl/table.h>
21 #include <libnftnl/chain.h>
22
23 #include <netinet/in.h>
24 #include <linux/netfilter.h>
25
26 extern char *yytext;
27 extern int yylineno;
28
29 static LIST_HEAD(xtables_stack);
30
31 struct stack_elem {
32 struct list_head head;
33 int token;
34 size_t size;
35 char data[];
36 };
37
stack_push(int token,size_t size)38 static void *stack_push(int token, size_t size)
39 {
40 struct stack_elem *e;
41
42 e = calloc(1, sizeof(struct stack_elem) + size);
43
44 e->token = token;
45 e->size = size;
46
47 list_add(&e->head, &xtables_stack);
48
49 return e->data;
50 }
51
stack_pop(void)52 static struct stack_elem *stack_pop(void)
53 {
54 struct stack_elem *e;
55
56 e = list_entry(xtables_stack.next, struct stack_elem, head);
57
58 if (&e->head == &xtables_stack)
59 return NULL;
60
61 list_del(&e->head);
62 return e;
63 }
64
stack_put_i32(void * data,int value)65 static inline void stack_put_i32(void *data, int value)
66 {
67 memcpy(data, &value, sizeof(int));
68 }
69
stack_put_str(void * data,const char * str)70 static inline void stack_put_str(void *data, const char *str)
71 {
72 memcpy(data, str, strlen(str));
73 }
74
stack_free(struct stack_elem * e)75 static void stack_free(struct stack_elem *e)
76 {
77 free(e);
78 }
79
80 %}
81
82 %union {
83 int val;
84 char *string;
85 }
86
87 %token T_FAMILY
88 %token T_TABLE
89 %token T_CHAIN
90 %token T_HOOK
91 %token T_PRIO
92
93 %token <string> T_STRING
94 %token <val> T_INTEGER
95
96 %%
97
98 configfile :
99 | lines
100 ;
101
102 lines : line
103 | lines line
104 ;
105
106 line : family
107 ;
108
109 family : T_FAMILY T_STRING '{' tables '}'
110 {
111 void *data = stack_push(T_FAMILY, strlen($2)+1);
112 stack_put_str(data, $2);
113 }
114 ;
115
116 tables : table
117 | tables table
118 ;
119
120 table : T_TABLE T_STRING '{' chains '}'
121 {
122 /* added in reverse order to pop it in order */
123 void *data = stack_push(T_TABLE, strlen($2)+1);
124 stack_put_str(data, $2);
125 }
126 ;
127
128 chains : chain
129 | chains chain
130 ;
131
132 chain : T_CHAIN T_STRING T_HOOK T_STRING T_PRIO T_INTEGER
133 {
134 /* added in reverse order to pop it in order */
135 void *data = stack_push(T_PRIO, sizeof(int32_t));
136 stack_put_i32(data, $6);
137 data = stack_push(T_HOOK, strlen($4)+1);
138 stack_put_str(data, $4);
139 data = stack_push(T_CHAIN, strlen($2)+1);
140 stack_put_str(data, $2);
141 }
142 ;
143
144 %%
145
146 int __attribute__((noreturn))
147 yyerror(char *msg)
148 {
149 fprintf(stderr, "parsing config file in line (%d), symbol '%s': %s\n",
150 yylineno, yytext, msg);
151 exit(EXIT_FAILURE);
152 }
153
hooknametonum(const char * hookname)154 static int hooknametonum(const char *hookname)
155 {
156 if (strcmp(hookname, "NF_INET_LOCAL_IN") == 0)
157 return NF_INET_LOCAL_IN;
158 else if (strcmp(hookname, "NF_INET_FORWARD") == 0)
159 return NF_INET_FORWARD;
160 else if (strcmp(hookname, "NF_INET_LOCAL_OUT") == 0)
161 return NF_INET_LOCAL_OUT;
162 else if (strcmp(hookname, "NF_INET_PRE_ROUTING") == 0)
163 return NF_INET_PRE_ROUTING;
164 else if (strcmp(hookname, "NF_INET_POST_ROUTING") == 0)
165 return NF_INET_POST_ROUTING;
166
167 return -1;
168 }
169
familytonumber(const char * family)170 static int32_t familytonumber(const char *family)
171 {
172 if (strcmp(family, "ipv4") == 0)
173 return AF_INET;
174 else if (strcmp(family, "ipv6") == 0)
175 return AF_INET6;
176
177 return -1;
178 }
179
xtables_config_parse(char * filename,struct nftnl_table_list * table_list,struct nftnl_chain_list * chain_list)180 int xtables_config_parse(char *filename, struct nftnl_table_list *table_list,
181 struct nftnl_chain_list *chain_list)
182 {
183 FILE *fp;
184 struct stack_elem *e;
185 struct nftnl_table *table = NULL;
186 struct nftnl_chain *chain = NULL;
187 int prio = 0;
188 int32_t family = 0;
189
190 fp = fopen(filename, "r");
191 if (!fp)
192 return -1;
193
194 yyrestart(fp);
195 yyparse();
196 fclose(fp);
197
198 for (e = stack_pop(); e != NULL; e = stack_pop()) {
199 switch(e->token) {
200 case T_FAMILY:
201 family = familytonumber(e->data);
202 if (family == -1)
203 return -1;
204 break;
205 case T_TABLE:
206 table = nftnl_table_alloc();
207 if (table == NULL)
208 return -1;
209
210 nftnl_table_set_u32(table, NFTNL_TABLE_FAMILY, family);
211 nftnl_table_set(table, NFTNL_TABLE_NAME, e->data);
212 /* This is intentionally prepending, instead of
213 * appending, since the elements in the stack are in
214 * the reverse order that chains appear in the
215 * configuration file.
216 */
217 nftnl_table_list_add(table, table_list);
218 break;
219 case T_PRIO:
220 memcpy(&prio, e->data, sizeof(int32_t));
221 break;
222 case T_CHAIN:
223 chain = nftnl_chain_alloc();
224 if (chain == NULL)
225 return -1;
226
227 nftnl_chain_set(chain, NFTNL_CHAIN_TABLE,
228 (char *)nftnl_table_get(table, NFTNL_TABLE_NAME));
229 nftnl_chain_set_u32(chain, NFTNL_CHAIN_FAMILY,
230 nftnl_table_get_u32(table, NFTNL_TABLE_FAMILY));
231 nftnl_chain_set_s32(chain, NFTNL_CHAIN_PRIO, prio);
232 nftnl_chain_set(chain, NFTNL_CHAIN_NAME, e->data);
233 /* Intentionally prepending, instead of appending */
234 nftnl_chain_list_add(chain, chain_list);
235 break;
236 case T_HOOK:
237 nftnl_chain_set_u32(chain, NFTNL_CHAIN_HOOKNUM,
238 hooknametonum(e->data));
239 break;
240 default:
241 printf("unknown token type %d\n", e->token);
242 break;
243 }
244 stack_free(e);
245 }
246
247 return 0;
248 }
249