libnftnl  1.0.8
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 execute_test(const char *dir_name)
148 {
149  DIR *d;
150  struct dirent *dent;
151  char path[PATH_MAX];
152  int ret = 0, exit_code = 0;
153  struct nftnl_parse_err *err;
154 
155  d = opendir(dir_name);
156  if (d == NULL) {
157  perror("opendir");
158  exit(EXIT_FAILURE);
159  }
160 
161  err = nftnl_parse_err_alloc();
162  if (err == NULL) {
163  perror("error");
164  exit(EXIT_FAILURE);
165  }
166 
167  while ((dent = readdir(d)) != NULL) {
168  int len = strlen(dent->d_name);
169 
170  if (strcmp(dent->d_name, ".") == 0 ||
171  strcmp(dent->d_name, "..") == 0)
172  continue;
173 
174  snprintf(path, sizeof(path), "%s/%s", dir_name, dent->d_name);
175 
176  if (strcmp(&dent->d_name[len-5], ".json") == 0) {
177  if ((ret = test_json(path, err)) == 0) {
178  if (!update) {
179  printf("parsing and validating %s: ",
180  path);
181  printf("\033[32mOK\e[0m\n");
182  }
183  }
184  exit_code += ret;
185  }
186  }
187 
188  closedir(d);
189  nftnl_parse_err_free(err);
190 
191  if (exit_code != 0)
192  exit(EXIT_FAILURE);
193 
194  return 0;
195 }
196 
197 static int execute_test_file(const char *filename)
198 {
199  char path[PATH_MAX];
200  struct nftnl_parse_err *err;
201  int ret = 0, len;
202 
203  err = nftnl_parse_err_alloc();
204  if (err == NULL) {
205  perror("error");
206  exit(EXIT_FAILURE);
207  }
208 
209  snprintf(path, sizeof(path), "%s", filename);
210 
211  len = strlen(filename);
212  if (strcmp(&filename[len-5], ".json") == 0) {
213  if ((ret = test_json(path, err)) == 0) {
214  if (!update) {
215  printf("parsing and validating %s: ",
216  path);
217  printf("\033[32mOK\e[0m\n");
218  }
219  }
220  nftnl_parse_err_free(err);
221  exit(EXIT_FAILURE);
222  }
223 
224  nftnl_parse_err_free(err);
225 
226  return 0;
227 }
228 
229 static void show_help(const char *name)
230 {
231  printf(
232 "Usage: %s [option]\n"
233 "\n"
234 "Options:\n"
235 " -d/--dir <directory> Check test files from <directory>.\n"
236 " -u/--update <directory> Update test files from <directory>.\n"
237 " -f/--file <file> Check test file <file>\n"
238 "\n",
239  name);
240 }
241 
242 int main(int argc, char *argv[])
243 {
244  int val;
245  int ret = 0;
246  int option_index = 0;
247  static struct option long_options[] = {
248  { "dir", required_argument, 0, 'd' },
249  { "update", required_argument, 0, 'u' },
250  { "file", required_argument, 0, 'f' },
251  { 0 }
252  };
253 
254  if (argc != 3) {
255  show_help(argv[0]);
256  exit(EXIT_FAILURE);
257  }
258 
259  while (1) {
260  val = getopt_long(argc, argv, "d:u:f:", long_options,
261  &option_index);
262 
263  if (val == -1)
264  break;
265 
266  switch (val) {
267  case 'd':
268  ret = execute_test(optarg);
269  break;
270  case 'u':
271  update = true;
272  ret = execute_test(optarg);
273  break;
274  case 'f':
275  ret = execute_test_file(optarg);
276  break;
277  default:
278  show_help(argv[0]);
279  break;
280  }
281  }
282  return ret;
283 }