libnftnl  1.0.5
nft-parsing-test.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <dirent.h>
5 #include <limits.h>
6 #include <errno.h>
7 #include <getopt.h>
8 
9 #include <libmnl/libmnl.h> /*nlmsghdr*/
10 #include <libnftnl/ruleset.h>
11 #include <libnftnl/table.h>
12 #include <libnftnl/chain.h>
13 #include <libnftnl/rule.h>
14 #include <libnftnl/set.h>
15 
16 enum {
17  TEST_XML_RULESET,
18  TEST_JSON_RULESET,
19 };
20 
21 static bool update = false;
22 
23 static void print_detail_error(char *a, char *b)
24 {
25  int i;
26  int from = -1;
27 
28  for (i = 0; i < strlen(b); i++) {
29  if (from == -1 && a[i] != b[i]) {
30  from = i;
31  break;
32 
33  }
34  }
35 
36  if (from != -1) {
37  int k = from - 10;
38 
39  if (k < 0)
40  k = 0;
41 
42  fprintf(stderr, "from file: ");
43  for (i = k; i < from + 10; i++)
44  fprintf(stderr, "%c", a[i]);
45 
46  fprintf(stderr, "\nfrom snprintf: ");
47  for (i = k; i < from + 10; i++)
48  fprintf(stderr, "%c", b[i]);
49 
50  /* Don't look twice below this comment ;-) */
51  fprintf(stderr, "\n ");
52  for (i = k; i < from + 10; i++) {
53  if (i == from)
54  fprintf(stderr, "^");
55  else
56  fprintf(stderr, " ");
57  }
58  fprintf(stderr, "\n");
59  }
60 }
61 
62 static int compare_test(uint32_t type, struct nftnl_ruleset *rs,
63  const char *filename, FILE *fp)
64 {
65  char orig[4096];
66  char out[4096];
67 
68  switch (type) {
69  case TEST_XML_RULESET:
70  nftnl_ruleset_snprintf(out, sizeof(out), rs,
71  NFTNL_OUTPUT_XML, NFTNL_OF_EVENT_NEW);
72  break;
73  case TEST_JSON_RULESET:
74  nftnl_ruleset_snprintf(out, sizeof(out), rs,
75  NFTNL_OUTPUT_JSON, NFTNL_OF_EVENT_NEW);
76  break;
77  default:
78  errno = EINVAL;
79  return -1;
80  }
81 
82  rewind(fp);
83  fgets(orig, sizeof(orig), fp);
84 
85  if (strncmp(orig, out, strlen(out)) == 0) {
86  if (update)
87  printf("%s: No changes to update\n", filename);
88  return 0;
89  }
90  if (update) {
91  FILE *fout;
92  printf("%s: Updating test file\n", filename);
93  fout = fopen(filename, "w");
94  if (fout == NULL) {
95  printf("unable to open file %s: %s\n", filename,
96  strerror(errno));
97  return -1;
98  }
99  fprintf(fout, "%s\n", out);
100  fclose(fout);
101  return 0;
102  }
103 
104  printf("validating %s: ", filename);
105  printf("\033[31mFAILED\e[0m\n");
106  print_detail_error(orig, out);
107  return -1;
108 }
109 
110 static int test_json(const char *filename, struct nftnl_parse_err *err)
111 {
112  int ret = -1;
113  struct nftnl_ruleset *rs;
114  FILE *fp;
115 
116  fp = fopen(filename, "r");
117  if (fp == NULL) {
118  printf("unable to open file %s: %s\n", filename,
119  strerror(errno));
120  return -1;
121  }
122 
123  rs = nftnl_ruleset_alloc();
124  if (rs == NULL) {
125  perror("nftnl_ruleset_alloc");
126  return -1;
127  }
128 
129  if (nftnl_ruleset_parse_file(rs, NFTNL_PARSE_JSON, fp, err) == 0)
130  ret = compare_test(TEST_JSON_RULESET, rs, filename, fp);
131  else
132  goto failparsing;
133 
134  nftnl_ruleset_free(rs);
135  fclose(fp);
136 
137  return ret;
138 
139 failparsing:
140  fclose(fp);
141  printf("parsing %s: ", filename);
142  printf("\033[31mFAILED\e[0m (%s)\n", strerror(errno));
143  nftnl_parse_perror("Reason", err);
144  return -1;
145 }
146 
147 static int test_xml(const char *filename, struct nftnl_parse_err *err)
148 {
149  int ret = -1;
150  struct nftnl_ruleset *rs;
151  FILE *fp;
152 
153  fp = fopen(filename, "r");
154  if (fp == NULL) {
155  printf("unable to open file %s: %s\n", filename,
156  strerror(errno));
157  return -1;
158  }
159 
160  rs = nftnl_ruleset_alloc();
161  if (rs == NULL) {
162  perror("nftnl_ruleset_alloc");
163  return -1;
164  }
165 
166  if (nftnl_ruleset_parse_file(rs, NFTNL_PARSE_XML, fp, err) == 0)
167  ret = compare_test(TEST_XML_RULESET, rs, filename, fp);
168  else
169  goto failparsing;
170 
171  nftnl_ruleset_free(rs);
172  fclose(fp);
173 
174  return ret;
175 
176 failparsing:
177  fclose(fp);
178  printf("parsing %s: ", filename);
179  printf("\033[31mFAILED\e[0m (%s)\n", strerror(errno));
180  nftnl_parse_perror("Reason", err);
181  return -1;
182 }
183 
184 static int execute_test(const char *dir_name)
185 {
186  DIR *d;
187  struct dirent *dent;
188  char path[PATH_MAX];
189  int ret = 0, exit_code = 0;
190  struct nftnl_parse_err *err;
191 
192  d = opendir(dir_name);
193  if (d == NULL) {
194  perror("opendir");
195  exit(EXIT_FAILURE);
196  }
197 
198  err = nftnl_parse_err_alloc();
199  if (err == NULL) {
200  perror("error");
201  exit(EXIT_FAILURE);
202  }
203 
204  while ((dent = readdir(d)) != NULL) {
205  int len = strlen(dent->d_name);
206 
207  if (strcmp(dent->d_name, ".") == 0 ||
208  strcmp(dent->d_name, "..") == 0)
209  continue;
210 
211  snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name);
212 
213  if (strcmp(&dent->d_name[len-4], ".xml") == 0) {
214  if ((ret = test_xml(path, err)) == 0) {
215  if (!update) {
216  printf("parsing and validating %s: ",
217  path);
218  printf("\033[32mOK\e[0m\n");
219  }
220  }
221  exit_code += ret;
222  }
223  if (strcmp(&dent->d_name[len-5], ".json") == 0) {
224  if ((ret = test_json(path, err)) == 0) {
225  if (!update) {
226  printf("parsing and validating %s: ",
227  path);
228  printf("\033[32mOK\e[0m\n");
229  }
230  }
231  exit_code += ret;
232  }
233  }
234 
235  closedir(d);
236  nftnl_parse_err_free(err);
237 
238  if (exit_code != 0)
239  exit(EXIT_FAILURE);
240 
241  return 0;
242 }
243 
244 static int execute_test_file(const char *filename)
245 {
246  char path[PATH_MAX];
247  int ret = 0;
248  struct nftnl_parse_err *err;
249 
250  err = nftnl_parse_err_alloc();
251  if (err == NULL) {
252  perror("error");
253  exit(EXIT_FAILURE);
254  }
255 
256  snprintf(path, sizeof(path), "%s", filename);
257 
258  int len = strlen(filename);
259  if (strcmp(&filename[len-4], ".xml") == 0) {
260  if ((ret = test_xml(path, err)) == 0) {
261  if (!update) {
262  printf("parsing and validating %s: ",
263  path);
264  printf("\033[32mOK\e[0m\n");
265  }
266  }
267  nftnl_parse_err_free(err);
268  exit(EXIT_FAILURE);
269  }
270  if (strcmp(&filename[len-5], ".json") == 0) {
271  if ((ret = test_json(path, err)) == 0) {
272  if (!update) {
273  printf("parsing and validating %s: ",
274  path);
275  printf("\033[32mOK\e[0m\n");
276  }
277  }
278  nftnl_parse_err_free(err);
279  exit(EXIT_FAILURE);
280  }
281 
282  nftnl_parse_err_free(err);
283 
284  return 0;
285 }
286 
287 static void show_help(const char *name)
288 {
289  printf(
290 "Usage: %s [option]\n"
291 "\n"
292 "Options:\n"
293 " -d/--dir <directory> Check test files from <directory>.\n"
294 " -u/--update <directory> Update test files from <directory>.\n"
295 " -f/--file <file> Check test file <file>\n"
296 "\n",
297  name);
298 }
299 
300 int main(int argc, char *argv[])
301 {
302  int val;
303  int ret = 0;
304  int option_index = 0;
305  static struct option long_options[] = {
306  { "dir", required_argument, 0, 'd' },
307  { "update", required_argument, 0, 'u' },
308  { "file", required_argument, 0, 'f' },
309  { 0 }
310  };
311 
312  if (argc != 3) {
313  show_help(argv[0]);
314  exit(EXIT_FAILURE);
315  }
316 
317  while (1) {
318  val = getopt_long(argc, argv, "d:u:f:", long_options,
319  &option_index);
320 
321  if (val == -1)
322  break;
323 
324  switch (val) {
325  case 'd':
326  ret = execute_test(optarg);
327  break;
328  case 'u':
329  update = true;
330  ret = execute_test(optarg);
331  break;
332  case 'f':
333  ret = execute_test_file(optarg);
334  break;
335  default:
336  show_help(argv[0]);
337  break;
338  }
339  }
340  return ret;
341 }