1 /*
2 * Copyright (C) 1993-2001 by Darren Reed.
3 *
4 * See the IPFILTER.LICENCE file for details on licencing.
5 *
6 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
7 * Use is subject to license terms.
8 */
9
10 #include "ipf.h"
11
12
13 typedef struct {
14 int c;
15 int e;
16 int n;
17 int p;
18 int s;
19 } mc_t;
20
21
22 static char *portcmp[] = { "*", "==", "!=", "<", ">", "<=", ">=", "**", "***" };
23 static int count = 0;
24
25 int intcmp __P((const void *, const void *));
26 static void indent __P((FILE *, int));
27 static void printeq __P((FILE *, char *, int, int, int));
28 static void printipeq __P((FILE *, char *, int, int, int));
29 static void addrule __P((FILE *, frentry_t *));
30 static void printhooks __P((FILE *, int, int, frgroup_t *));
31 static void emitheader __P((frgroup_t *, u_int, u_int));
32 static void emitGroup __P((int, int, void *, frentry_t *, char *,
33 u_int, u_int));
34 static void emittail __P((void));
35 static void printCgroup __P((int, frentry_t *, mc_t *, char *));
36
37 #define FRC_IFN 0
38 #define FRC_V 1
39 #define FRC_P 2
40 #define FRC_FL 3
41 #define FRC_TOS 4
42 #define FRC_TTL 5
43 #define FRC_SRC 6
44 #define FRC_DST 7
45 #define FRC_TCP 8
46 #define FRC_SP 9
47 #define FRC_DP 10
48 #define FRC_OPT 11
49 #define FRC_SEC 12
50 #define FRC_ATH 13
51 #define FRC_ICT 14
52 #define FRC_ICC 15
53 #define FRC_MAX 16
54
55
56 static FILE *cfile = NULL;
57
58 /*
59 * This is called once per filter rule being loaded to emit data structures
60 * required.
61 */
printc(fr)62 void printc(fr)
63 frentry_t *fr;
64 {
65 fripf_t *ipf;
66 u_long *ulp;
67 char *and;
68 FILE *fp;
69 int i;
70
71 if (fr->fr_v != 4)
72 return;
73 if ((fr->fr_type != FR_T_IPF) && (fr->fr_type != FR_T_NONE))
74 return;
75 if ((fr->fr_type == FR_T_IPF) &&
76 ((fr->fr_datype != FRI_NORMAL) || (fr->fr_satype != FRI_NORMAL)))
77 return;
78 ipf = fr->fr_ipf;
79
80 if (cfile == NULL)
81 cfile = fopen("ip_rules.c", "w");
82 if (cfile == NULL)
83 return;
84 fp = cfile;
85 if (count == 0) {
86 fprintf(fp, "/*\n");
87 fprintf(fp, "* Copyright (C) 1993-2000 by Darren Reed.\n");
88 fprintf(fp, "*\n");
89 fprintf(fp, "* Redistribution and use in source and binary forms are permitted\n");
90 fprintf(fp, "* provided that this notice is preserved and due credit is given\n");
91 fprintf(fp, "* to the original author and the contributors.\n");
92 fprintf(fp, "*/\n\n");
93
94 fprintf(fp, "#include <sys/types.h>\n");
95 fprintf(fp, "#include <sys/time.h>\n");
96 fprintf(fp, "#include <sys/socket.h>\n");
97 fprintf(fp, "#if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__sgi)\n");
98 fprintf(fp, "# include <sys/systm.h>\n");
99 fprintf(fp, "#endif\n");
100 fprintf(fp, "#include <sys/errno.h>\n");
101 fprintf(fp, "#include <sys/param.h>\n");
102 fprintf(fp,
103 "#if !defined(__SVR4) && !defined(__svr4__) && !defined(__hpux)\n");
104 fprintf(fp, "# include <sys/mbuf.h>\n");
105 fprintf(fp, "#endif\n");
106 fprintf(fp,
107 "#if defined(__FreeBSD__) && (__FreeBSD_version > 220000)\n");
108 fprintf(fp, "# include <sys/sockio.h>\n");
109 fprintf(fp, "#else\n");
110 fprintf(fp, "# include <sys/ioctl.h>\n");
111 fprintf(fp, "#endif /* FreeBSD */\n");
112 fprintf(fp, "#include <net/if.h>\n");
113 fprintf(fp, "#include <netinet/in.h>\n");
114 fprintf(fp, "#include <netinet/in_systm.h>\n");
115 fprintf(fp, "#include <netinet/ip.h>\n");
116 fprintf(fp, "#include <netinet/tcp.h>\n");
117 fprintf(fp, "#include \"netinet/ip_compat.h\"\n");
118 fprintf(fp, "#include \"netinet/ip_fil.h\"\n\n");
119 fprintf(fp, "#include \"netinet/ip_rules.h\"\n\n");
120 fprintf(fp, "#ifndef _KERNEL\n");
121 fprintf(fp, "# include <string.h>\n");
122 fprintf(fp, "#endif /* _KERNEL */\n");
123 fprintf(fp, "\n");
124 fprintf(fp, "#ifdef IPFILTER_COMPILED\n");
125 }
126
127 addrule(fp, fr);
128 fr->fr_type |= FR_T_BUILTIN;
129 and = "";
130 fr->fr_ref = 1;
131 i = sizeof(*fr);
132 if (i & -(1 - sizeof(*ulp)))
133 i += sizeof(u_long);
134 for (i /= sizeof(u_long), ulp = (u_long *)fr; i > 0; i--) {
135 fprintf(fp, "%s%#lx", and, *ulp++);
136 and = ", ";
137 }
138 fprintf(fp, "\n};\n");
139 fr->fr_type &= ~FR_T_BUILTIN;
140
141 count++;
142
143 fflush(fp);
144 }
145
146
147 static frgroup_t *groups = NULL;
148
149
addrule(fp,fr)150 static void addrule(fp, fr)
151 FILE *fp;
152 frentry_t *fr;
153 {
154 frentry_t *f, **fpp;
155 frgroup_t *g;
156 u_long *ulp;
157 char *and;
158 int i;
159
160 f = (frentry_t *)malloc(sizeof(*f));
161 if (f == NULL) {
162 fprintf(stderr, "out of memory\n");
163 exit(1);
164 }
165 bcopy((char *)fr, (char *)f, sizeof(*fr));
166 if (fr->fr_ipf) {
167 f->fr_ipf = (fripf_t *)malloc(sizeof(*f->fr_ipf));
168 if (f->fr_ipf == NULL) {
169 fprintf(stderr, "out of memory\n");
170 exit(1);
171 }
172 bcopy((char *)fr->fr_ipf, (char *)f->fr_ipf,
173 sizeof(*fr->fr_ipf));
174 }
175
176 f->fr_next = NULL;
177 for (g = groups; g != NULL; g = g->fg_next)
178 if ((strncmp(g->fg_name, f->fr_group, FR_GROUPLEN) == 0) &&
179 (g->fg_flags == (f->fr_flags & FR_INOUT)))
180 break;
181
182 if (g == NULL) {
183 g = (frgroup_t *)calloc(1, sizeof(*g));
184 if (g == NULL) {
185 fprintf(stderr, "out of memory\n");
186 exit(1);
187 }
188 g->fg_next = groups;
189 groups = g;
190 g->fg_head = f;
191 bcopy(f->fr_group, g->fg_name, FR_GROUPLEN);
192 g->fg_ref = 0;
193 g->fg_flags = f->fr_flags & FR_INOUT;
194 }
195
196 for (fpp = &g->fg_start; *fpp != NULL; )
197 fpp = &((*fpp)->fr_next);
198 *fpp = f;
199
200 if (fr->fr_dsize > 0) {
201 fprintf(fp, "\
202 static u_long ipf%s_rule_data_%s_%u[] = {\n",
203 f->fr_flags & FR_INQUE ? "in" : "out",
204 g->fg_name, g->fg_ref);
205 and = "";
206 i = fr->fr_dsize;
207 ulp = fr->fr_data;
208 for (i /= sizeof(u_long); i > 0; i--) {
209 fprintf(fp, "%s%#lx", and, *ulp++);
210 and = ", ";
211 }
212 fprintf(fp, "\n};\n");
213 }
214
215 fprintf(fp, "\nstatic u_long %s_rule_%s_%d[] = {\n",
216 f->fr_flags & FR_INQUE ? "in" : "out", g->fg_name, g->fg_ref);
217
218 g->fg_ref++;
219
220 if (f->fr_grhead != 0) {
221 for (g = groups; g != NULL; g = g->fg_next)
222 if ((strncmp(g->fg_name, f->fr_grhead,
223 FR_GROUPLEN) == 0) &&
224 g->fg_flags == (f->fr_flags & FR_INOUT))
225 break;
226 if (g == NULL) {
227 g = (frgroup_t *)calloc(1, sizeof(*g));
228 if (g == NULL) {
229 fprintf(stderr, "out of memory\n");
230 exit(1);
231 }
232 g->fg_next = groups;
233 groups = g;
234 g->fg_head = f;
235 bcopy(f->fr_grhead, g->fg_name, FR_GROUPLEN);
236 g->fg_ref = 0;
237 g->fg_flags = f->fr_flags & FR_INOUT;
238 }
239 }
240 }
241
242
intcmp(c1,c2)243 int intcmp(c1, c2)
244 const void *c1, *c2;
245 {
246 const mc_t *i1 = (const mc_t *)c1, *i2 = (const mc_t *)c2;
247
248 if (i1->n == i2->n) {
249 return i1->c - i2->c;
250 }
251 return i2->n - i1->n;
252 }
253
254
indent(fp,in)255 static void indent(fp, in)
256 FILE *fp;
257 int in;
258 {
259 for (; in; in--)
260 fputc('\t', fp);
261 }
262
printeq(fp,var,m,max,v)263 static void printeq(fp, var, m, max, v)
264 FILE *fp;
265 char *var;
266 int m, max, v;
267 {
268 if (m == max)
269 fprintf(fp, "%s == %#x) {\n", var, v);
270 else
271 fprintf(fp, "(%s & %#x) == %#x) {\n", var, m, v);
272 }
273
274 /*
275 * Parameters: var - IP# being compared
276 * fl - 0 for positive match, 1 for negative match
277 * m - netmask
278 * v - required address
279 */
printipeq(fp,var,fl,m,v)280 static void printipeq(fp, var, fl, m, v)
281 FILE *fp;
282 char *var;
283 int fl, m, v;
284 {
285 if (m == 0xffffffff)
286 fprintf(fp, "%s ", var);
287 else
288 fprintf(fp, "(%s & %#x) ", var, m);
289 fprintf(fp, "%c", fl ? '!' : '=');
290 fprintf(fp, "= %#x) {\n", v);
291 }
292
293
emit(num,dir,v,fr)294 void emit(num, dir, v, fr)
295 int num, dir;
296 void *v;
297 frentry_t *fr;
298 {
299 u_int incnt, outcnt;
300 frgroup_t *g;
301 frentry_t *f;
302
303 for (g = groups; g != NULL; g = g->fg_next) {
304 if (dir == 0 || dir == -1) {
305 if ((g->fg_flags & FR_INQUE) == 0)
306 continue;
307 for (incnt = 0, f = g->fg_start; f != NULL;
308 f = f->fr_next)
309 incnt++;
310 emitGroup(num, dir, v, fr, g->fg_name, incnt, 0);
311 }
312 if (dir == 1 || dir == -1) {
313 if ((g->fg_flags & FR_OUTQUE) == 0)
314 continue;
315 for (outcnt = 0, f = g->fg_start; f != NULL;
316 f = f->fr_next)
317 outcnt++;
318 emitGroup(num, dir, v, fr, g->fg_name, 0, outcnt);
319 }
320 }
321
322 if (num == -1 && dir == -1) {
323 for (g = groups; g != NULL; g = g->fg_next) {
324 if ((g->fg_flags & FR_INQUE) != 0) {
325 for (incnt = 0, f = g->fg_start; f != NULL;
326 f = f->fr_next)
327 incnt++;
328 if (incnt > 0)
329 emitheader(g, incnt, 0);
330 }
331 if ((g->fg_flags & FR_OUTQUE) != 0) {
332 for (outcnt = 0, f = g->fg_start; f != NULL;
333 f = f->fr_next)
334 outcnt++;
335 if (outcnt > 0)
336 emitheader(g, 0, outcnt);
337 }
338 }
339 emittail();
340 fprintf(cfile, "#endif /* IPFILTER_COMPILED */\n");
341 }
342
343 }
344
345
emitheader(grp,incount,outcount)346 static void emitheader(grp, incount, outcount)
347 frgroup_t *grp;
348 u_int incount, outcount;
349 {
350 static FILE *fph = NULL;
351 frgroup_t *g;
352
353 if (fph == NULL) {
354 fph = fopen("ip_rules.h", "w");
355 if (fph == NULL)
356 return;
357
358 fprintf(fph, "extern int ipfrule_add __P((void));\n");
359 fprintf(fph, "extern int ipfrule_remove __P((void));\n");
360 }
361
362 printhooks(cfile, incount, outcount, grp);
363
364 if (incount) {
365 fprintf(fph, "\n\
366 extern frentry_t *ipfrule_match_in_%s __P((fr_info_t *, u_32_t *));\n\
367 extern frentry_t *ipf_rules_in_%s[%d];\n",
368 grp->fg_name, grp->fg_name, incount);
369
370 for (g = groups; g != grp; g = g->fg_next)
371 if ((strncmp(g->fg_name, grp->fg_name,
372 FR_GROUPLEN) == 0) &&
373 g->fg_flags == grp->fg_flags)
374 break;
375 if (g == grp) {
376 fprintf(fph, "\n\
377 extern int ipfrule_add_in_%s __P((void));\n\
378 extern int ipfrule_remove_in_%s __P((void));\n", grp->fg_name, grp->fg_name);
379 }
380 }
381 if (outcount) {
382 fprintf(fph, "\n\
383 extern frentry_t *ipfrule_match_out_%s __P((fr_info_t *, u_32_t *));\n\
384 extern frentry_t *ipf_rules_out_%s[%d];\n",
385 grp->fg_name, grp->fg_name, outcount);
386
387 for (g = groups; g != grp; g = g->fg_next)
388 if ((strncmp(g->fg_name, grp->fg_name,
389 FR_GROUPLEN) == 0) &&
390 g->fg_flags == grp->fg_flags)
391 break;
392 if (g == grp) {
393 fprintf(fph, "\n\
394 extern int ipfrule_add_out_%s __P((void));\n\
395 extern int ipfrule_remove_out_%s __P((void));\n",
396 grp->fg_name, grp->fg_name);
397 }
398 }
399 }
400
emittail()401 static void emittail()
402 {
403 frgroup_t *g;
404
405 fprintf(cfile, "\n\
406 int ipfrule_add()\n\
407 {\n\
408 int err;\n\
409 \n");
410 for (g = groups; g != NULL; g = g->fg_next)
411 fprintf(cfile, "\
412 err = ipfrule_add_%s_%s();\n\
413 if (err != 0)\n\
414 return err;\n",
415 (g->fg_flags & FR_INQUE) ? "in" : "out", g->fg_name);
416 fprintf(cfile, "\
417 return 0;\n");
418 fprintf(cfile, "}\n\
419 \n");
420
421 fprintf(cfile, "\n\
422 int ipfrule_remove()\n\
423 {\n\
424 int err;\n\
425 \n");
426 for (g = groups; g != NULL; g = g->fg_next)
427 fprintf(cfile, "\
428 err = ipfrule_remove_%s_%s();\n\
429 if (err != 0)\n\
430 return err;\n",
431 (g->fg_flags & FR_INQUE) ? "in" : "out", g->fg_name);
432 fprintf(cfile, "\
433 return 0;\n");
434 fprintf(cfile, "}\n");
435 }
436
437
emitGroup(num,dir,v,fr,group,incount,outcount)438 static void emitGroup(num, dir, v, fr, group, incount, outcount)
439 int num, dir;
440 void *v;
441 frentry_t *fr;
442 char *group;
443 u_int incount, outcount;
444 {
445 static FILE *fp = NULL;
446 static int header[2] = { 0, 0 };
447 static char egroup[FR_GROUPLEN] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
448 static int openfunc = 0;
449 static mc_t *n = NULL;
450 static int sin = 0;
451 frentry_t *f;
452 frgroup_t *g;
453 fripf_t *ipf;
454 int i, in, j;
455 mc_t *m = v;
456
457 if (fp == NULL)
458 fp = cfile;
459 if (fp == NULL)
460 return;
461 if (strncmp(egroup, group, FR_GROUPLEN)) {
462 for (sin--; sin > 0; sin--) {
463 indent(fp, sin);
464 fprintf(fp, "}\n");
465 }
466 if (openfunc == 1) {
467 fprintf(fp, "\treturn fr;\n}\n");
468 openfunc = 0;
469 if (n != NULL) {
470 free(n);
471 n = NULL;
472 }
473 }
474 sin = 0;
475 header[0] = 0;
476 header[1] = 0;
477 strncpy(egroup, group, FR_GROUPLEN);
478 } else if (openfunc == 1 && num < 0) {
479 if (n != NULL) {
480 free(n);
481 n = NULL;
482 }
483 for (sin--; sin > 0; sin--) {
484 indent(fp, sin);
485 fprintf(fp, "}\n");
486 }
487 if (openfunc == 1) {
488 fprintf(fp, "\treturn fr;\n}\n");
489 openfunc = 0;
490 }
491 }
492
493 if (dir == -1)
494 return;
495
496 for (g = groups; g != NULL; g = g->fg_next) {
497 if (dir == 0 && (g->fg_flags & FR_INQUE) == 0)
498 continue;
499 else if (dir == 1 && (g->fg_flags & FR_OUTQUE) == 0)
500 continue;
501 if (strncmp(g->fg_name, group, FR_GROUPLEN) != 0)
502 continue;
503 break;
504 }
505
506 /*
507 * Output the array of pointers to rules for this group.
508 */
509 if (num == -2 && dir == 0 && header[0] == 0 && incount != 0) {
510 fprintf(fp, "\nfrentry_t *ipf_rules_in_%s[%d] = {",
511 group, incount);
512 for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) {
513 if ((f->fr_flags & FR_INQUE) == 0)
514 continue;
515 if ((i & 1) == 0) {
516 fprintf(fp, "\n\t");
517 }
518 fprintf(fp,
519 "(frentry_t *)&in_rule_%s_%d",
520 f->fr_group, i);
521 if (i + 1 < incount)
522 fprintf(fp, ", ");
523 i++;
524 }
525 fprintf(fp, "\n};\n");
526 }
527
528 if (num == -2 && dir == 1 && header[1] == 0 && outcount != 0) {
529 fprintf(fp, "\nfrentry_t *ipf_rules_out_%s[%d] = {",
530 group, outcount);
531 for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) {
532 if ((f->fr_flags & FR_OUTQUE) == 0)
533 continue;
534 if ((i & 1) == 0) {
535 fprintf(fp, "\n\t");
536 }
537 fprintf(fp,
538 "(frentry_t *)&out_rule_%s_%d",
539 f->fr_group, i);
540 if (i + 1 < outcount)
541 fprintf(fp, ", ");
542 i++;
543 }
544 fprintf(fp, "\n};\n");
545 fp = NULL;
546 }
547
548 if (num < 0)
549 return;
550
551 in = 0;
552 ipf = fr->fr_ipf;
553
554 /*
555 * If the function header has not been printed then print it now.
556 */
557 if (header[dir] == 0) {
558 int pdst = 0, psrc = 0;
559
560 openfunc = 1;
561 fprintf(fp, "\nfrentry_t *ipfrule_match_%s_%s(fin, passp)\n",
562 (dir == 0) ? "in" : "out", group);
563 fprintf(fp, "fr_info_t *fin;\n");
564 fprintf(fp, "u_32_t *passp;\n");
565 fprintf(fp, "{\n");
566 fprintf(fp, "\tfrentry_t *fr = NULL;\n");
567
568 /*
569 * Print out any variables that need to be declared.
570 */
571 for (f = g->fg_start, i = 0; f != NULL; f = f->fr_next) {
572 if (incount + outcount > m[FRC_SRC].e + 1)
573 psrc = 1;
574 if (incount + outcount > m[FRC_DST].e + 1)
575 pdst = 1;
576 }
577 if (psrc == 1)
578 fprintf(fp, "\tu_32_t src = ntohl(%s);\n",
579 "fin->fin_fi.fi_saddr");
580 if (pdst == 1)
581 fprintf(fp, "\tu_32_t dst = ntohl(%s);\n",
582 "fin->fin_fi.fi_daddr");
583 }
584
585 for (i = 0; i < FRC_MAX; i++) {
586 switch(m[i].c)
587 {
588 case FRC_IFN :
589 if (*fr->fr_ifname)
590 m[i].s = 1;
591 break;
592 case FRC_V :
593 if (ipf != NULL && ipf->fri_mip.fi_v != 0)
594 m[i].s = 1;
595 break;
596 case FRC_FL :
597 if (ipf != NULL && ipf->fri_mip.fi_flx != 0)
598 m[i].s = 1;
599 break;
600 case FRC_P :
601 if (ipf != NULL && ipf->fri_mip.fi_p != 0)
602 m[i].s = 1;
603 break;
604 case FRC_TTL :
605 if (ipf != NULL && ipf->fri_mip.fi_ttl != 0)
606 m[i].s = 1;
607 break;
608 case FRC_TOS :
609 if (ipf != NULL && ipf->fri_mip.fi_tos != 0)
610 m[i].s = 1;
611 break;
612 case FRC_TCP :
613 if (ipf == NULL)
614 break;
615 if ((ipf->fri_ip.fi_p == IPPROTO_TCP) &&
616 fr->fr_tcpfm != 0)
617 m[i].s = 1;
618 break;
619 case FRC_SP :
620 if (ipf == NULL)
621 break;
622 if (fr->fr_scmp == FR_INRANGE)
623 m[i].s = 1;
624 else if (fr->fr_scmp == FR_OUTRANGE)
625 m[i].s = 1;
626 else if (fr->fr_scmp != 0)
627 m[i].s = 1;
628 break;
629 case FRC_DP :
630 if (ipf == NULL)
631 break;
632 if (fr->fr_dcmp == FR_INRANGE)
633 m[i].s = 1;
634 else if (fr->fr_dcmp == FR_OUTRANGE)
635 m[i].s = 1;
636 else if (fr->fr_dcmp != 0)
637 m[i].s = 1;
638 break;
639 case FRC_SRC :
640 if (ipf == NULL)
641 break;
642 if (fr->fr_satype == FRI_LOOKUP) {
643 ;
644 } else if ((fr->fr_smask != 0) ||
645 (fr->fr_flags & FR_NOTSRCIP) != 0)
646 m[i].s = 1;
647 break;
648 case FRC_DST :
649 if (ipf == NULL)
650 break;
651 if (fr->fr_datype == FRI_LOOKUP) {
652 ;
653 } else if ((fr->fr_dmask != 0) ||
654 (fr->fr_flags & FR_NOTDSTIP) != 0)
655 m[i].s = 1;
656 break;
657 case FRC_OPT :
658 if (ipf == NULL)
659 break;
660 if (fr->fr_optmask != 0)
661 m[i].s = 1;
662 break;
663 case FRC_SEC :
664 if (ipf == NULL)
665 break;
666 if (fr->fr_secmask != 0)
667 m[i].s = 1;
668 break;
669 case FRC_ATH :
670 if (ipf == NULL)
671 break;
672 if (fr->fr_authmask != 0)
673 m[i].s = 1;
674 break;
675 case FRC_ICT :
676 if (ipf == NULL)
677 break;
678 if ((fr->fr_icmpm & 0xff00) != 0)
679 m[i].s = 1;
680 break;
681 case FRC_ICC :
682 if (ipf == NULL)
683 break;
684 if ((fr->fr_icmpm & 0xff) != 0)
685 m[i].s = 1;
686 break;
687 }
688 }
689
690 if (!header[dir]) {
691 fprintf(fp, "\n");
692 header[dir] = 1;
693 sin = 0;
694 }
695
696 qsort(m, FRC_MAX, sizeof(mc_t), intcmp);
697
698 if (n) {
699 /*
700 * Calculate the indentation interval upto the last common
701 * common comparison being made.
702 */
703 for (i = 0, in = 1; i < FRC_MAX; i++) {
704 if (n[i].c != m[i].c)
705 break;
706 if (n[i].s != m[i].s)
707 break;
708 if (n[i].s) {
709 if (n[i].n && (n[i].n > n[i].e)) {
710 m[i].p++;
711 in += m[i].p;
712 break;
713 }
714 if (n[i].e > 0) {
715 in++;
716 } else
717 break;
718 }
719 }
720 if (sin != in) {
721 for (j = sin - 1; j >= in; j--) {
722 indent(fp, j);
723 fprintf(fp, "}\n");
724 }
725 }
726 } else {
727 in = 1;
728 i = 0;
729 }
730
731 /*
732 * print out C code that implements a filter rule.
733 */
734 for (; i < FRC_MAX; i++) {
735 switch(m[i].c)
736 {
737 case FRC_IFN :
738 if (m[i].s) {
739 indent(fp, in);
740 fprintf(fp, "if (fin->fin_ifp == ");
741 fprintf(fp, "ipf_rules_%s_%s[%d]->fr_ifa) {\n",
742 dir ? "out" : "in", group, num);
743 in++;
744 }
745 break;
746 case FRC_V :
747 if (m[i].s) {
748 indent(fp, in);
749 fprintf(fp, "if (fin->fin_v == %d) {\n",
750 ipf->fri_ip.fi_v);
751 in++;
752 }
753 break;
754 case FRC_FL :
755 if (m[i].s) {
756 indent(fp, in);
757 fprintf(fp, "if (");
758 printeq(fp, "fin->fin_flx",
759 ipf->fri_mip.fi_flx, 0xf,
760 ipf->fri_ip.fi_flx);
761 in++;
762 }
763 break;
764 case FRC_P :
765 if (m[i].s) {
766 indent(fp, in);
767 fprintf(fp, "if (fin->fin_p == %d) {\n",
768 ipf->fri_ip.fi_p);
769 in++;
770 }
771 break;
772 case FRC_TTL :
773 if (m[i].s) {
774 indent(fp, in);
775 fprintf(fp, "if (");
776 printeq(fp, "fin->fin_ttl",
777 ipf->fri_mip.fi_ttl, 0xff,
778 ipf->fri_ip.fi_ttl);
779 in++;
780 }
781 break;
782 case FRC_TOS :
783 if (m[i].s) {
784 indent(fp, in);
785 fprintf(fp, "if (fin->fin_tos");
786 printeq(fp, "fin->fin_tos",
787 ipf->fri_mip.fi_tos, 0xff,
788 ipf->fri_ip.fi_tos);
789 in++;
790 }
791 break;
792 case FRC_TCP :
793 if (m[i].s) {
794 indent(fp, in);
795 fprintf(fp, "if (");
796 printeq(fp, "fin->fin_tcpf", fr->fr_tcpfm,
797 0xff, fr->fr_tcpf);
798 in++;
799 }
800 break;
801 case FRC_SP :
802 if (!m[i].s)
803 break;
804 if (fr->fr_scmp == FR_INRANGE) {
805 indent(fp, in);
806 fprintf(fp, "if ((fin->fin_data[0] > %d) && ",
807 fr->fr_sport);
808 fprintf(fp, "(fin->fin_data[0] < %d)",
809 fr->fr_stop);
810 fprintf(fp, ") {\n");
811 in++;
812 } else if (fr->fr_scmp == FR_OUTRANGE) {
813 indent(fp, in);
814 fprintf(fp, "if ((fin->fin_data[0] < %d) || ",
815 fr->fr_sport);
816 fprintf(fp, "(fin->fin_data[0] > %d)",
817 fr->fr_stop);
818 fprintf(fp, ") {\n");
819 in++;
820 } else if (fr->fr_scmp) {
821 indent(fp, in);
822 fprintf(fp, "if (fin->fin_data[0] %s %d)",
823 portcmp[fr->fr_scmp], fr->fr_sport);
824 fprintf(fp, " {\n");
825 in++;
826 }
827 break;
828 case FRC_DP :
829 if (!m[i].s)
830 break;
831 if (fr->fr_dcmp == FR_INRANGE) {
832 indent(fp, in);
833 fprintf(fp, "if ((fin->fin_data[1] > %d) && ",
834 fr->fr_dport);
835 fprintf(fp, "(fin->fin_data[1] < %d)",
836 fr->fr_dtop);
837 fprintf(fp, ") {\n");
838 in++;
839 } else if (fr->fr_dcmp == FR_OUTRANGE) {
840 indent(fp, in);
841 fprintf(fp, "if ((fin->fin_data[1] < %d) || ",
842 fr->fr_dport);
843 fprintf(fp, "(fin->fin_data[1] > %d)",
844 fr->fr_dtop);
845 fprintf(fp, ") {\n");
846 in++;
847 } else if (fr->fr_dcmp) {
848 indent(fp, in);
849 fprintf(fp, "if (fin->fin_data[1] %s %d)",
850 portcmp[fr->fr_dcmp], fr->fr_dport);
851 fprintf(fp, " {\n");
852 in++;
853 }
854 break;
855 case FRC_SRC :
856 if (!m[i].s)
857 break;
858 if (fr->fr_satype == FRI_LOOKUP) {
859 ;
860 } else if ((fr->fr_smask != 0) ||
861 (fr->fr_flags & FR_NOTSRCIP) != 0) {
862 indent(fp, in);
863 fprintf(fp, "if (");
864 printipeq(fp, "src",
865 fr->fr_flags & FR_NOTSRCIP,
866 fr->fr_smask, fr->fr_saddr);
867 in++;
868 }
869 break;
870 case FRC_DST :
871 if (!m[i].s)
872 break;
873 if (fr->fr_datype == FRI_LOOKUP) {
874 ;
875 } else if ((fr->fr_dmask != 0) ||
876 (fr->fr_flags & FR_NOTDSTIP) != 0) {
877 indent(fp, in);
878 fprintf(fp, "if (");
879 printipeq(fp, "dst",
880 fr->fr_flags & FR_NOTDSTIP,
881 fr->fr_dmask, fr->fr_daddr);
882 in++;
883 }
884 break;
885 case FRC_OPT :
886 if (m[i].s) {
887 indent(fp, in);
888 fprintf(fp, "if (");
889 printeq(fp, "fin->fin_fi.fi_optmsk",
890 fr->fr_optmask, 0xffffffff,
891 fr->fr_optbits);
892 in++;
893 }
894 break;
895 case FRC_SEC :
896 if (m[i].s) {
897 indent(fp, in);
898 fprintf(fp, "if (");
899 printeq(fp, "fin->fin_fi.fi_secmsk",
900 fr->fr_secmask, 0xffff,
901 fr->fr_secbits);
902 in++;
903 }
904 break;
905 case FRC_ATH :
906 if (m[i].s) {
907 indent(fp, in);
908 fprintf(fp, "if (");
909 printeq(fp, "fin->fin_fi.fi_authmsk",
910 fr->fr_authmask, 0xffff,
911 fr->fr_authbits);
912 in++;
913 }
914 break;
915 case FRC_ICT :
916 if (m[i].s) {
917 indent(fp, in);
918 fprintf(fp, "if (");
919 printeq(fp, "fin->fin_data[0]",
920 fr->fr_icmpm & 0xff00, 0xffff,
921 fr->fr_icmp & 0xff00);
922 in++;
923 }
924 break;
925 case FRC_ICC :
926 if (m[i].s) {
927 indent(fp, in);
928 fprintf(fp, "if (");
929 printeq(fp, "fin->fin_data[0]",
930 fr->fr_icmpm & 0xff, 0xffff,
931 fr->fr_icmp & 0xff);
932 in++;
933 }
934 break;
935 }
936
937 }
938
939 indent(fp, in);
940 if (fr->fr_flags & FR_QUICK) {
941 fprintf(fp, "return (frentry_t *)&%s_rule_%s_%d;\n",
942 fr->fr_flags & FR_INQUE ? "in" : "out",
943 fr->fr_group, num);
944 } else {
945 fprintf(fp, "fr = (frentry_t *)&%s_rule_%s_%d;\n",
946 fr->fr_flags & FR_INQUE ? "in" : "out",
947 fr->fr_group, num);
948 }
949 if (n == NULL) {
950 n = (mc_t *)malloc(sizeof(*n) * FRC_MAX);
951 if (n == NULL) {
952 fprintf(stderr, "out of memory\n");
953 exit(1);
954 }
955 }
956 bcopy((char *)m, (char *)n, sizeof(*n) * FRC_MAX);
957 sin = in;
958 }
959
960
printC(dir)961 void printC(dir)
962 int dir;
963 {
964 static mc_t *m = NULL;
965 frgroup_t *g;
966
967 if (m == NULL) {
968 m = (mc_t *)calloc(1, sizeof(*m) * FRC_MAX);
969 if (m == NULL) {
970 fprintf(stderr, "out of memory\n");
971 exit(1);
972 }
973 }
974
975 for (g = groups; g != NULL; g = g->fg_next) {
976 if ((dir == 0) && ((g->fg_flags & FR_INQUE) != 0))
977 printCgroup(dir, g->fg_start, m, g->fg_name);
978 if ((dir == 1) && ((g->fg_flags & FR_OUTQUE) != 0))
979 printCgroup(dir, g->fg_start, m, g->fg_name);
980 }
981
982 emit(-1, dir, m, NULL);
983 }
984
985
986 /*
987 * Now print out code to implement all of the rules.
988 */
printCgroup(dir,top,m,group)989 static void printCgroup(dir, top, m, group)
990 int dir;
991 frentry_t *top;
992 mc_t *m;
993 char *group;
994 {
995 frentry_t *fr, *fr1;
996 int i, n, rn;
997 u_int count;
998
999 for (count = 0, fr1 = top; fr1 != NULL; fr1 = fr1->fr_next) {
1000 if ((dir == 0) && ((fr1->fr_flags & FR_INQUE) != 0))
1001 count++;
1002 else if ((dir == 1) && ((fr1->fr_flags & FR_OUTQUE) != 0))
1003 count++;
1004 }
1005
1006 if (dir == 0)
1007 emitGroup(-2, dir, m, fr1, group, count, 0);
1008 else if (dir == 1)
1009 emitGroup(-2, dir, m, fr1, group, 0, count);
1010
1011 /*
1012 * Before printing each rule, check to see how many of its fields are
1013 * matched by subsequent rules.
1014 */
1015 for (fr1 = top, rn = 0; fr1 != NULL; fr1 = fr1->fr_next, rn++) {
1016 if (!dir && !(fr1->fr_flags & FR_INQUE))
1017 continue;
1018 if (dir && !(fr1->fr_flags & FR_OUTQUE))
1019 continue;
1020 n = 0xfffffff;
1021
1022 for (i = 0; i < FRC_MAX; i++)
1023 m[i].e = 0;
1024 qsort(m, FRC_MAX, sizeof(mc_t), intcmp);
1025
1026 for (i = 0; i < FRC_MAX; i++) {
1027 m[i].c = i;
1028 m[i].e = 0;
1029 m[i].n = 0;
1030 m[i].s = 0;
1031 }
1032
1033 for (fr = fr1->fr_next; fr; fr = fr->fr_next) {
1034 if (!dir && !(fr->fr_flags & FR_INQUE))
1035 continue;
1036 if (dir && !(fr->fr_flags & FR_OUTQUE))
1037 continue;
1038
1039 if ((n & 0x0001) &&
1040 !strcmp(fr1->fr_ifname, fr->fr_ifname)) {
1041 m[FRC_IFN].e++;
1042 m[FRC_IFN].n++;
1043 } else
1044 n &= ~0x0001;
1045
1046 if ((n & 0x0002) && (fr1->fr_v == fr->fr_v)) {
1047 m[FRC_V].e++;
1048 m[FRC_V].n++;
1049 } else
1050 n &= ~0x0002;
1051
1052 if ((n & 0x0004) &&
1053 (fr->fr_type == fr1->fr_type) &&
1054 (fr->fr_type == FR_T_IPF) &&
1055 (fr1->fr_mip.fi_flx == fr->fr_mip.fi_flx) &&
1056 (fr1->fr_ip.fi_flx == fr->fr_ip.fi_flx)) {
1057 m[FRC_FL].e++;
1058 m[FRC_FL].n++;
1059 } else
1060 n &= ~0x0004;
1061
1062 if ((n & 0x0008) &&
1063 (fr->fr_type == fr1->fr_type) &&
1064 (fr->fr_type == FR_T_IPF) &&
1065 (fr1->fr_proto == fr->fr_proto)) {
1066 m[FRC_P].e++;
1067 m[FRC_P].n++;
1068 } else
1069 n &= ~0x0008;
1070
1071 if ((n & 0x0010) &&
1072 (fr->fr_type == fr1->fr_type) &&
1073 (fr->fr_type == FR_T_IPF) &&
1074 (fr1->fr_ttl == fr->fr_ttl)) {
1075 m[FRC_TTL].e++;
1076 m[FRC_TTL].n++;
1077 } else
1078 n &= ~0x0010;
1079
1080 if ((n & 0x0020) &&
1081 (fr->fr_type == fr1->fr_type) &&
1082 (fr->fr_type == FR_T_IPF) &&
1083 (fr1->fr_tos == fr->fr_tos)) {
1084 m[FRC_TOS].e++;
1085 m[FRC_TOS].n++;
1086 } else
1087 n &= ~0x0020;
1088
1089 if ((n & 0x0040) &&
1090 (fr->fr_type == fr1->fr_type) &&
1091 (fr->fr_type == FR_T_IPF) &&
1092 ((fr1->fr_tcpfm == fr->fr_tcpfm) &&
1093 (fr1->fr_tcpf == fr->fr_tcpf))) {
1094 m[FRC_TCP].e++;
1095 m[FRC_TCP].n++;
1096 } else
1097 n &= ~0x0040;
1098
1099 if ((n & 0x0080) &&
1100 (fr->fr_type == fr1->fr_type) &&
1101 (fr->fr_type == FR_T_IPF) &&
1102 ((fr1->fr_scmp == fr->fr_scmp) &&
1103 (fr1->fr_stop == fr->fr_stop) &&
1104 (fr1->fr_sport == fr->fr_sport))) {
1105 m[FRC_SP].e++;
1106 m[FRC_SP].n++;
1107 } else
1108 n &= ~0x0080;
1109
1110 if ((n & 0x0100) &&
1111 (fr->fr_type == fr1->fr_type) &&
1112 (fr->fr_type == FR_T_IPF) &&
1113 ((fr1->fr_dcmp == fr->fr_dcmp) &&
1114 (fr1->fr_dtop == fr->fr_dtop) &&
1115 (fr1->fr_dport == fr->fr_dport))) {
1116 m[FRC_DP].e++;
1117 m[FRC_DP].n++;
1118 } else
1119 n &= ~0x0100;
1120
1121 if ((n & 0x0200) &&
1122 (fr->fr_type == fr1->fr_type) &&
1123 (fr->fr_type == FR_T_IPF) &&
1124 ((fr1->fr_satype == FRI_LOOKUP) &&
1125 (fr->fr_satype == FRI_LOOKUP) &&
1126 (fr1->fr_srcnum == fr->fr_srcnum))) {
1127 m[FRC_SRC].e++;
1128 m[FRC_SRC].n++;
1129 } else if ((n & 0x0200) &&
1130 (fr->fr_type == fr1->fr_type) &&
1131 (fr->fr_type == FR_T_IPF) &&
1132 (((fr1->fr_flags & FR_NOTSRCIP) ==
1133 (fr->fr_flags & FR_NOTSRCIP)))) {
1134 if ((fr1->fr_smask == fr->fr_smask) &&
1135 (fr1->fr_saddr == fr->fr_saddr))
1136 m[FRC_SRC].e++;
1137 else
1138 n &= ~0x0200;
1139 if (fr1->fr_smask &&
1140 (fr1->fr_saddr & fr1->fr_smask) ==
1141 (fr->fr_saddr & fr1->fr_smask)) {
1142 m[FRC_SRC].n++;
1143 n |= 0x0200;
1144 }
1145 } else {
1146 n &= ~0x0200;
1147 }
1148
1149 if ((n & 0x0400) &&
1150 (fr->fr_type == fr1->fr_type) &&
1151 (fr->fr_type == FR_T_IPF) &&
1152 ((fr1->fr_datype == FRI_LOOKUP) &&
1153 (fr->fr_datype == FRI_LOOKUP) &&
1154 (fr1->fr_dstnum == fr->fr_dstnum))) {
1155 m[FRC_DST].e++;
1156 m[FRC_DST].n++;
1157 } else if ((n & 0x0400) &&
1158 (fr->fr_type == fr1->fr_type) &&
1159 (fr->fr_type == FR_T_IPF) &&
1160 (((fr1->fr_flags & FR_NOTDSTIP) ==
1161 (fr->fr_flags & FR_NOTDSTIP)))) {
1162 if ((fr1->fr_dmask == fr->fr_dmask) &&
1163 (fr1->fr_daddr == fr->fr_daddr))
1164 m[FRC_DST].e++;
1165 else
1166 n &= ~0x0400;
1167 if (fr1->fr_dmask &&
1168 (fr1->fr_daddr & fr1->fr_dmask) ==
1169 (fr->fr_daddr & fr1->fr_dmask)) {
1170 m[FRC_DST].n++;
1171 n |= 0x0400;
1172 }
1173 } else {
1174 n &= ~0x0400;
1175 }
1176
1177 if ((n & 0x0800) &&
1178 (fr->fr_type == fr1->fr_type) &&
1179 (fr->fr_type == FR_T_IPF) &&
1180 (fr1->fr_optmask == fr->fr_optmask) &&
1181 (fr1->fr_optbits == fr->fr_optbits)) {
1182 m[FRC_OPT].e++;
1183 m[FRC_OPT].n++;
1184 } else
1185 n &= ~0x0800;
1186
1187 if ((n & 0x1000) &&
1188 (fr->fr_type == fr1->fr_type) &&
1189 (fr->fr_type == FR_T_IPF) &&
1190 (fr1->fr_secmask == fr->fr_secmask) &&
1191 (fr1->fr_secbits == fr->fr_secbits)) {
1192 m[FRC_SEC].e++;
1193 m[FRC_SEC].n++;
1194 } else
1195 n &= ~0x1000;
1196
1197 if ((n & 0x10000) &&
1198 (fr->fr_type == fr1->fr_type) &&
1199 (fr->fr_type == FR_T_IPF) &&
1200 (fr1->fr_authmask == fr->fr_authmask) &&
1201 (fr1->fr_authbits == fr->fr_authbits)) {
1202 m[FRC_ATH].e++;
1203 m[FRC_ATH].n++;
1204 } else
1205 n &= ~0x10000;
1206
1207 if ((n & 0x20000) &&
1208 (fr->fr_type == fr1->fr_type) &&
1209 (fr->fr_type == FR_T_IPF) &&
1210 ((fr1->fr_icmpm & 0xff00) ==
1211 (fr->fr_icmpm & 0xff00)) &&
1212 ((fr1->fr_icmp & 0xff00) ==
1213 (fr->fr_icmp & 0xff00))) {
1214 m[FRC_ICT].e++;
1215 m[FRC_ICT].n++;
1216 } else
1217 n &= ~0x20000;
1218
1219 if ((n & 0x40000) &&
1220 (fr->fr_type == fr1->fr_type) &&
1221 (fr->fr_type == FR_T_IPF) &&
1222 ((fr1->fr_icmpm & 0xff) == (fr->fr_icmpm & 0xff)) &&
1223 ((fr1->fr_icmp & 0xff) == (fr->fr_icmp & 0xff))) {
1224 m[FRC_ICC].e++;
1225 m[FRC_ICC].n++;
1226 } else
1227 n &= ~0x40000;
1228 }
1229 /*msort(m);*/
1230
1231 if (dir == 0)
1232 emitGroup(rn, dir, m, fr1, group, count, 0);
1233 else if (dir == 1)
1234 emitGroup(rn, dir, m, fr1, group, 0, count);
1235 }
1236 }
1237
printhooks(fp,in,out,grp)1238 static void printhooks(fp, in, out, grp)
1239 FILE *fp;
1240 int in;
1241 int out;
1242 frgroup_t *grp;
1243 {
1244 frentry_t *fr;
1245 char *group;
1246 int dogrp, i;
1247 char *instr;
1248
1249 group = grp->fg_name;
1250 dogrp = 0;
1251
1252 if (in && out) {
1253 fprintf(stderr,
1254 "printhooks called with both in and out set\n");
1255 exit(1);
1256 }
1257
1258 if (in) {
1259 instr = "in";
1260 } else if (out) {
1261 instr = "out";
1262 } else {
1263 instr = "???";
1264 }
1265 fprintf(fp, "static frentry_t ipfrule_%s_%s;\n", instr, group);
1266
1267 fprintf(fp, "\
1268 \n\
1269 int ipfrule_add_%s_%s()\n", instr, group);
1270 fprintf(fp, "\
1271 {\n\
1272 int i, j, err = 0, max;\n\
1273 frentry_t *fp;\n");
1274
1275 if (dogrp)
1276 fprintf(fp, "\
1277 frgroup_t *fg;\n");
1278
1279 fprintf(fp, "\n");
1280
1281 for (i = 0, fr = grp->fg_start; fr != NULL; i++, fr = fr->fr_next)
1282 if (fr->fr_dsize > 0) {
1283 fprintf(fp, "\
1284 ipf_rules_%s_%s[%d]->fr_data = &ipf%s_rule_data_%s_%u;\n",
1285 instr, grp->fg_name, i,
1286 instr, grp->fg_name, i);
1287 }
1288 fprintf(fp, "\
1289 max = sizeof(ipf_rules_%s_%s)/sizeof(frentry_t *);\n\
1290 for (i = 0; i < max; i++) {\n\
1291 fp = ipf_rules_%s_%s[i];\n\
1292 fp->fr_next = NULL;\n", instr, group, instr, group);
1293
1294 fprintf(fp, "\
1295 for (j = i + 1; j < max; j++)\n\
1296 if (strncmp(fp->fr_group,\n\
1297 ipf_rules_%s_%s[j]->fr_group,\n\
1298 FR_GROUPLEN) == 0) {\n\
1299 fp->fr_next = ipf_rules_%s_%s[j];\n\
1300 break;\n\
1301 }\n", instr, group, instr, group);
1302 if (dogrp)
1303 fprintf(fp, "\
1304 \n\
1305 if (fp->fr_grhead != 0) {\n\
1306 fg = fr_addgroup(fp->fr_grhead, fp, FR_INQUE,\n\
1307 IPL_LOGIPF, 0);\n\
1308 if (fg != NULL)\n\
1309 fp->fr_grp = &fg->fg_start;\n\
1310 }\n");
1311 fprintf(fp, "\
1312 }\n\
1313 \n\
1314 fp = &ipfrule_%s_%s;\n", instr, group);
1315 fprintf(fp, "\
1316 bzero((char *)fp, sizeof(*fp));\n\
1317 fp->fr_type = FR_T_CALLFUNC|FR_T_BUILTIN;\n\
1318 fp->fr_flags = FR_%sQUE|FR_NOMATCH;\n\
1319 fp->fr_data = (void *)ipf_rules_%s_%s[0];\n",
1320 (in != 0) ? "IN" : "OUT", instr, group);
1321 fprintf(fp, "\
1322 fp->fr_dsize = sizeof(ipf_rules_%s_%s[0]);\n",
1323 instr, group);
1324
1325 fprintf(fp, "\
1326 fp->fr_v = 4;\n\
1327 fp->fr_func = (ipfunc_t)ipfrule_match_%s_%s;\n\
1328 err = frrequest(IPL_LOGIPF, SIOCADDFR, (caddr_t)fp, fr_active, 0);\n",
1329 instr, group);
1330 fprintf(fp, "\treturn err;\n}\n");
1331
1332 fprintf(fp, "\n\n\
1333 int ipfrule_remove_%s_%s()\n", instr, group);
1334 fprintf(fp, "\
1335 {\n\
1336 int err = 0, i;\n\
1337 frentry_t *fp;\n\
1338 \n\
1339 /*\n\
1340 * Try to remove the %sbound rule.\n", instr);
1341
1342 fprintf(fp, "\
1343 */\n\
1344 if (ipfrule_%s_%s.fr_ref > 0) {\n", instr, group);
1345
1346 fprintf(fp, "\
1347 err = EBUSY;\n\
1348 } else {\n");
1349
1350 fprintf(fp, "\
1351 i = sizeof(ipf_rules_%s_%s)/sizeof(frentry_t *) - 1;\n\
1352 for (; i >= 0; i--) {\n\
1353 fp = ipf_rules_%s_%s[i];\n\
1354 if (fp->fr_ref > 1) {\n\
1355 err = EBUSY;\n\
1356 break;\n\
1357 }\n\
1358 }\n\
1359 }\n\
1360 if (err == 0)\n\
1361 err = frrequest(IPL_LOGIPF, SIOCDELFR,\n\
1362 (caddr_t)&ipfrule_%s_%s, fr_active, 0);\n",
1363 instr, group, instr, group, instr, group);
1364 fprintf(fp, "\
1365 if (err)\n\
1366 return err;\n\
1367 \n\n");
1368
1369 fprintf(fp, "\treturn err;\n}\n");
1370 }
1371