1 /*
2 * Copyright (C) 1993-2005 by Darren Reed.
3 * See the IPFILTER.LICENCE file for details on licencing.
4 *
5 * Copyright 2019 Joyent, Inc.
6 */
7
8 %{
9 #include "ipf.h"
10 #include <syslog.h>
11 #include <uuid/uuid.h>
12 #undef OPT_NAT
13 #undef OPT_VERBOSE
14 #include "ipmon_l.h"
15 #include "ipmon.h"
16
17 #define YYDEBUG 1
18
19 extern void yyerror __P((char *));
20 extern int yyparse __P((void));
21 extern int yylex __P((void));
22 extern int yydebug;
23 extern FILE *yyin;
24 extern int yylineNum;
25
26 typedef struct opt {
27 struct opt *o_next;
28 int o_line;
29 int o_type;
30 int o_num;
31 char *o_str;
32 struct in_addr o_ip;
33 } opt_t;
34
35 static void build_action __P((struct opt *));
36 static opt_t *new_opt __P((int));
37 static void free_action __P((ipmon_action_t *));
38
39 static ipmon_action_t *alist = NULL;
40 %}
41
42 %union {
43 char *str;
44 u_32_t num;
45 struct in_addr addr;
46 struct opt *opt;
47 union i6addr ip6;
48 uuid_t uuid;
49 }
50
51 %token <num> YY_NUMBER YY_HEX
52 %token <str> YY_STR
53 %token <ip6> YY_IPV6
54 %token <uuid> YY_UUID
55 %token YY_COMMENT
56 %token YY_CMP_EQ YY_CMP_NE YY_CMP_LE YY_CMP_GE YY_CMP_LT YY_CMP_GT
57 %token YY_RANGE_OUT YY_RANGE_IN
58
59 %token IPM_MATCH IPM_BODY IPM_COMMENT IPM_DIRECTION IPM_DSTIP IPM_DSTPORT
60 %token IPM_EVERY IPM_EXECUTE IPM_GROUP IPM_INTERFACE IPM_IN IPM_NO IPM_OUT
61 %token IPM_PACKET IPM_PACKETS IPM_POOL IPM_PROTOCOL IPM_RESULT IPM_RULE
62 %token IPM_SECOND IPM_SECONDS IPM_SRCIP IPM_SRCPORT IPM_LOGTAG IPM_WITH
63 %token IPM_DO IPM_SAVE IPM_SYSLOG IPM_NOTHING IPM_RAW IPM_TYPE IPM_NAT
64 %token IPM_STATE IPM_NATTAG IPM_IPF
65 %type <addr> ipv4
66 %type <opt> direction dstip dstport every execute group interface
67 %type <opt> protocol result rule srcip srcport logtag matching
68 %type <opt> matchopt nattag type doopt doing save syslog nothing
69 %type <num> saveopts saveopt typeopt
70
71 %%
72 file: line
73 | assign
74 | file line
75 | file assign
76 ;
77
78 line: IPM_MATCH '{' matching '}' IPM_DO '{' doing '}' ';'
79 { build_action($3); resetlexer(); }
80 | IPM_COMMENT
81 | YY_COMMENT
82 ;
83
84 assign: YY_STR assigning YY_STR ';' { set_variable($1, $3);
85 resetlexer();
86 free($1);
87 free($3);
88 }
89 ;
90
91 assigning:
92 '=' { yyvarnext = 1; }
93 ;
94
95 matching:
96 matchopt { $$ = $1; }
97 | matchopt ',' matching { $1->o_next = $3; $$ = $1; }
98 ;
99
100 matchopt:
101 direction { $$ = $1; }
102 | dstip { $$ = $1; }
103 | dstport { $$ = $1; }
104 | every { $$ = $1; }
105 | group { $$ = $1; }
106 | interface { $$ = $1; }
107 | protocol { $$ = $1; }
108 | result { $$ = $1; }
109 | rule { $$ = $1; }
110 | srcip { $$ = $1; }
111 | srcport { $$ = $1; }
112 | logtag { $$ = $1; }
113 | nattag { $$ = $1; }
114 | type { $$ = $1; }
115 ;
116
117 doing:
118 doopt { $$ = $1; }
119 | doopt ',' doing { $1->o_next = $3; $$ = $1; }
120 ;
121
122 doopt:
123 execute { $$ = $1; }
124 | save { $$ = $1; }
125 | syslog { $$ = $1; }
126 | nothing { $$ = $1; }
127 ;
128
129 direction:
130 IPM_DIRECTION '=' IPM_IN { $$ = new_opt(IPM_DIRECTION);
131 $$->o_num = IPM_IN; }
132 | IPM_DIRECTION '=' IPM_OUT { $$ = new_opt(IPM_DIRECTION);
133 $$->o_num = IPM_OUT; }
134 ;
135
136 dstip: IPM_DSTIP '=' ipv4 '/' YY_NUMBER { $$ = new_opt(IPM_DSTIP);
137 $$->o_ip = $3;
138 $$->o_num = $5; }
139 ;
140
141 dstport:
142 IPM_DSTPORT '=' YY_NUMBER { $$ = new_opt(IPM_DSTPORT);
143 $$->o_num = $3; }
144 | IPM_DSTPORT '=' YY_STR { $$ = new_opt(IPM_DSTPORT);
145 $$->o_str = $3; }
146 ;
147
148 every: IPM_EVERY IPM_SECOND { $$ = new_opt(IPM_SECOND);
149 $$->o_num = 1; }
150 | IPM_EVERY YY_NUMBER IPM_SECONDS { $$ = new_opt(IPM_SECOND);
151 $$->o_num = $2; }
152 | IPM_EVERY IPM_PACKET { $$ = new_opt(IPM_PACKET);
153 $$->o_num = 1; }
154 | IPM_EVERY YY_NUMBER IPM_PACKETS { $$ = new_opt(IPM_PACKET);
155 $$->o_num = $2; }
156 ;
157
158 group: IPM_GROUP '=' YY_NUMBER { $$ = new_opt(IPM_GROUP);
159 $$->o_num = $3; }
160 | IPM_GROUP '=' YY_STR { $$ = new_opt(IPM_GROUP);
161 $$->o_str = $3; }
162 ;
163
164 interface:
165 IPM_INTERFACE '=' YY_STR { $$ = new_opt(IPM_INTERFACE);
166 $$->o_str = $3; }
167 ;
168
169 logtag: IPM_LOGTAG '=' YY_NUMBER { $$ = new_opt(IPM_LOGTAG);
170 $$->o_num = $3; }
171 ;
172
173 nattag: IPM_NATTAG '=' YY_STR { $$ = new_opt(IPM_NATTAG);
174 $$->o_str = $3; }
175 ;
176
177 protocol:
178 IPM_PROTOCOL '=' YY_NUMBER { $$ = new_opt(IPM_PROTOCOL);
179 $$->o_num = $3; }
180 | IPM_PROTOCOL '=' YY_STR { $$ = new_opt(IPM_PROTOCOL);
181 $$->o_num = getproto($3);
182 free($3);
183 }
184 ;
185
186 result: IPM_RESULT '=' YY_STR { $$ = new_opt(IPM_RESULT);
187 $$->o_str = $3; }
188 ;
189
190 rule: IPM_RULE '=' YY_NUMBER { $$ = new_opt(IPM_RULE);
191 $$->o_num = YY_NUMBER; }
192 ;
193
194 srcip: IPM_SRCIP '=' ipv4 '/' YY_NUMBER { $$ = new_opt(IPM_SRCIP);
195 $$->o_ip = $3;
196 $$->o_num = $5; }
197 ;
198
199 srcport:
200 IPM_SRCPORT '=' YY_NUMBER { $$ = new_opt(IPM_SRCPORT);
201 $$->o_num = $3; }
202 | IPM_SRCPORT '=' YY_STR { $$ = new_opt(IPM_SRCPORT);
203 $$->o_str = $3; }
204 ;
205
206 type: IPM_TYPE '=' typeopt { $$ = new_opt(IPM_TYPE);
207 $$->o_num = $3; }
208 ;
209
210 typeopt:
211 IPM_IPF { $$ = IPL_MAGIC; }
212 | IPM_NAT { $$ = IPL_MAGIC_NAT; }
213 | IPM_STATE { $$ = IPL_MAGIC_STATE; }
214 ;
215
216 execute:
217 IPM_EXECUTE YY_STR { $$ = new_opt(IPM_EXECUTE);
218 $$->o_str = $2; }
219 ;
220
221 save: IPM_SAVE saveopts YY_STR { $$ = new_opt(IPM_SAVE);
222 $$->o_num = $2;
223 $$->o_str = $3; }
224 ;
225
226 saveopts: { $$ = 0; }
227 | saveopt { $$ = $1; }
228 | saveopt ',' saveopts { $$ = $1 | $3; }
229 ;
230
231 saveopt:
232 IPM_RAW { $$ = IPMDO_SAVERAW; }
233 ;
234
235 syslog: IPM_SYSLOG { $$ = new_opt(IPM_SYSLOG); }
236 ;
237
238 nothing:
239 IPM_NOTHING { $$ = 0; }
240 ;
241
242 ipv4: YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER '.' YY_NUMBER
243 { if ($1 > 255 || $3 > 255 || $5 > 255 || $7 > 255) {
244 yyerror("Invalid octet string for IP address");
245 return 0;
246 }
247 $$.s_addr = ($1 << 24) | ($3 << 16) | ($5 << 8) | $7;
248 $$.s_addr = htonl($$.s_addr);
249 }
250 %%
251 static struct wordtab yywords[] = {
252 { "body", IPM_BODY },
253 { "direction", IPM_DIRECTION },
254 { "do", IPM_DO },
255 { "dstip", IPM_DSTIP },
256 { "dstport", IPM_DSTPORT },
257 { "every", IPM_EVERY },
258 { "execute", IPM_EXECUTE },
259 { "group", IPM_GROUP },
260 { "in", IPM_IN },
261 { "interface", IPM_INTERFACE },
262 { "ipf", IPM_IPF },
263 { "logtag", IPM_LOGTAG },
264 { "match", IPM_MATCH },
265 { "nat", IPM_NAT },
266 { "nattag", IPM_NATTAG },
267 { "no", IPM_NO },
268 { "nothing", IPM_NOTHING },
269 { "out", IPM_OUT },
270 { "packet", IPM_PACKET },
271 { "packets", IPM_PACKETS },
272 { "protocol", IPM_PROTOCOL },
273 { "result", IPM_RESULT },
274 { "rule", IPM_RULE },
275 { "save", IPM_SAVE },
276 { "raw", IPM_RAW },
277 { "second", IPM_SECOND },
278 { "seconds", IPM_SECONDS },
279 { "srcip", IPM_SRCIP },
280 { "srcport", IPM_SRCPORT },
281 { "state", IPM_STATE },
282 { "syslog", IPM_SYSLOG },
283 { "with", IPM_WITH },
284 { NULL, 0 }
285 };
286
287 static int macflags[17][2] = {
288 { IPM_DIRECTION, IPMAC_DIRECTION },
289 { IPM_DSTIP, IPMAC_DSTIP },
290 { IPM_DSTPORT, IPMAC_DSTPORT },
291 { IPM_GROUP, IPMAC_GROUP },
292 { IPM_INTERFACE, IPMAC_INTERFACE },
293 { IPM_LOGTAG, IPMAC_LOGTAG },
294 { IPM_NATTAG, IPMAC_NATTAG },
295 { IPM_PACKET, IPMAC_EVERY },
296 { IPM_PROTOCOL, IPMAC_PROTOCOL },
297 { IPM_RESULT, IPMAC_RESULT },
298 { IPM_RULE, IPMAC_RULE },
299 { IPM_SECOND, IPMAC_EVERY },
300 { IPM_SRCIP, IPMAC_SRCIP },
301 { IPM_SRCPORT, IPMAC_SRCPORT },
302 { IPM_TYPE, IPMAC_TYPE },
303 { IPM_WITH, IPMAC_WITH },
304 { 0, 0 }
305 };
306
307 static opt_t *new_opt(type)
308 int type;
309 {
310 opt_t *o;
311
312 o = (opt_t *)malloc(sizeof(*o));
313 if (o == NULL)
314 yyerror("sorry, out of memory");
315 o->o_type = type;
316 o->o_line = yylineNum;
317 o->o_num = 0;
318 o->o_str = (char *)0;
319 o->o_next = NULL;
320 return o;
321 }
322
323 static void build_action(olist)
324 opt_t *olist;
325 {
326 ipmon_action_t *a;
327 opt_t *o;
328 char c;
329 int i;
330
331 a = (ipmon_action_t *)calloc(1, sizeof(*a));
332 if (a == NULL)
333 return;
334 while ((o = olist) != NULL) {
335 /*
336 * Check to see if the same comparator is being used more than
337 * once per matching statement.
338 */
339 for (i = 0; macflags[i][0]; i++)
340 if (macflags[i][0] == o->o_type)
341 break;
342 if (macflags[i][1] & a->ac_mflag) {
343 fprintf(stderr, "%s redfined on line %d\n",
344 yykeytostr(o->o_type), yylineNum);
345 if (o->o_str != NULL)
346 free(o->o_str);
347 olist = o->o_next;
348 free(o);
349 continue;
350 }
351
352 a->ac_mflag |= macflags[i][1];
353
354 switch (o->o_type)
355 {
356 case IPM_DIRECTION :
357 a->ac_direction = o->o_num;
358 break;
359 case IPM_DSTIP :
360 a->ac_dip = o->o_ip.s_addr;
361 a->ac_dmsk = htonl(0xffffffff << (32 - o->o_num));
362 break;
363 case IPM_DSTPORT :
364 a->ac_dport = htons(o->o_num);
365 break;
366 case IPM_EXECUTE :
367 a->ac_exec = o->o_str;
368 c = *o->o_str;
369 if (c== '"'|| c == '\'') {
370 if (o->o_str[strlen(o->o_str) - 1] == c) {
371 a->ac_run = strdup(o->o_str + 1);
372 a->ac_run[strlen(a->ac_run) - 1] ='\0';
373 } else
374 a->ac_run = o->o_str;
375 } else
376 a->ac_run = o->o_str;
377 o->o_str = NULL;
378 break;
379 case IPM_INTERFACE :
380 a->ac_iface = o->o_str;
381 o->o_str = NULL;
382 break;
383 case IPM_GROUP :
384 if (o->o_str != NULL)
385 strncpy(a->ac_group, o->o_str, FR_GROUPLEN);
386 else
387 sprintf(a->ac_group, "%d", o->o_num);
388 break;
389 case IPM_LOGTAG :
390 a->ac_logtag = o->o_num;
391 break;
392 case IPM_NATTAG :
393 strncpy(a->ac_nattag, o->o_str, sizeof(a->ac_nattag));
394 break;
395 case IPM_PACKET :
396 a->ac_packet = o->o_num;
397 break;
398 case IPM_PROTOCOL :
399 a->ac_proto = o->o_num;
400 break;
401 case IPM_RULE :
402 a->ac_rule = o->o_num;
403 break;
404 case IPM_RESULT :
405 if (!strcasecmp(o->o_str, "pass"))
406 a->ac_result = IPMR_PASS;
407 else if (!strcasecmp(o->o_str, "block"))
408 a->ac_result = IPMR_BLOCK;
409 else if (!strcasecmp(o->o_str, "nomatch"))
410 a->ac_result = IPMR_NOMATCH;
411 else if (!strcasecmp(o->o_str, "log"))
412 a->ac_result = IPMR_LOG;
413 break;
414 case IPM_SECOND :
415 a->ac_second = o->o_num;
416 break;
417 case IPM_SRCIP :
418 a->ac_sip = o->o_ip.s_addr;
419 a->ac_smsk = htonl(0xffffffff << (32 - o->o_num));
420 break;
421 case IPM_SRCPORT :
422 a->ac_sport = htons(o->o_num);
423 break;
424 case IPM_SAVE :
425 if (a->ac_savefile != NULL) {
426 fprintf(stderr, "%s redfined on line %d\n",
427 yykeytostr(o->o_type), yylineNum);
428 break;
429 }
430 a->ac_savefile = strdup(o->o_str);
431 a->ac_savefp = fopen(o->o_str, "a");
432 a->ac_dflag |= o->o_num & IPMDO_SAVERAW;
433 break;
434 case IPM_SYSLOG :
435 if (a->ac_syslog != 0) {
436 fprintf(stderr, "%s redfined on line %d\n",
437 yykeytostr(o->o_type), yylineNum);
438 break;
439 }
440 a->ac_syslog = 1;
441 break;
442 case IPM_TYPE :
443 a->ac_type = o->o_num;
444 break;
445 case IPM_WITH :
446 break;
447 default :
448 break;
449 }
450
451 olist = o->o_next;
452 if (o->o_str != NULL)
453 free(o->o_str);
454 free(o);
455 }
456 a->ac_next = alist;
457 alist = a;
458 }
459
460
461 int check_action(buf, log, opts, lvl)
462 char *buf, *log;
463 int opts, lvl;
464 {
465 ipmon_action_t *a;
466 struct timeval tv;
467 ipflog_t *ipf;
468 tcphdr_t *tcp;
469 iplog_t *ipl;
470 int matched;
471 u_long t1;
472 ip_t *ip;
473
474 matched = 0;
475 ipl = (iplog_t *)buf;
476 ipf = (ipflog_t *)(ipl +1);
477 ip = (ip_t *)(ipf + 1);
478 tcp = (tcphdr_t *)((char *)ip + (IP_HL(ip) << 2));
479
480 for (a = alist; a != NULL; a = a->ac_next) {
481 if ((a->ac_mflag & IPMAC_DIRECTION) != 0) {
482 if (a->ac_direction == IPM_IN) {
483 if ((ipf->fl_flags & FR_INQUE) == 0)
484 continue;
485 } else if (a->ac_direction == IPM_OUT) {
486 if ((ipf->fl_flags & FR_OUTQUE) == 0)
487 continue;
488 }
489 }
490
491 if ((a->ac_type != 0) && (a->ac_type != ipl->ipl_magic))
492 continue;
493
494 if ((a->ac_mflag & IPMAC_EVERY) != 0) {
495 gettimeofday(&tv, NULL);
496 t1 = tv.tv_sec - a->ac_lastsec;
497 if (tv.tv_usec <= a->ac_lastusec)
498 t1--;
499 if (a->ac_second != 0) {
500 if (t1 < a->ac_second)
501 continue;
502 a->ac_lastsec = tv.tv_sec;
503 a->ac_lastusec = tv.tv_usec;
504 }
505
506 if (a->ac_packet != 0) {
507 if (a->ac_pktcnt == 0)
508 a->ac_pktcnt++;
509 else if (a->ac_pktcnt == a->ac_packet) {
510 a->ac_pktcnt = 0;
511 continue;
512 } else {
513 a->ac_pktcnt++;
514 continue;
515 }
516 }
517 }
518
519 if ((a->ac_mflag & IPMAC_DSTIP) != 0) {
520 if ((ip->ip_dst.s_addr & a->ac_dmsk) != a->ac_dip)
521 continue;
522 }
523
524 if ((a->ac_mflag & IPMAC_DSTPORT) != 0) {
525 if (ip->ip_p != IPPROTO_UDP && ip->ip_p != IPPROTO_TCP)
526 continue;
527 if (tcp->th_dport != a->ac_dport)
528 continue;
529 }
530
531 if ((a->ac_mflag & IPMAC_GROUP) != 0) {
532 if (strncmp(a->ac_group, ipf->fl_group,
533 FR_GROUPLEN) != 0)
534 continue;
535 }
536
537 if ((a->ac_mflag & IPMAC_INTERFACE) != 0) {
538 if (strcmp(a->ac_iface, ipf->fl_ifname))
539 continue;
540 }
541
542 if ((a->ac_mflag & IPMAC_PROTOCOL) != 0) {
543 if (a->ac_proto != ip->ip_p)
544 continue;
545 }
546
547 if ((a->ac_mflag & IPMAC_RESULT) != 0) {
548 if ((ipf->fl_flags & FF_LOGNOMATCH) != 0) {
549 if (a->ac_result != IPMR_NOMATCH)
550 continue;
551 } else if (FR_ISPASS(ipf->fl_flags)) {
552 if (a->ac_result != IPMR_PASS)
553 continue;
554 } else if (FR_ISBLOCK(ipf->fl_flags)) {
555 if (a->ac_result != IPMR_BLOCK)
556 continue;
557 } else { /* Log only */
558 if (a->ac_result != IPMR_LOG)
559 continue;
560 }
561 }
562
563 if ((a->ac_mflag & IPMAC_RULE) != 0) {
564 if (a->ac_rule != ipf->fl_rule)
565 continue;
566 }
567
568 if ((a->ac_mflag & IPMAC_SRCIP) != 0) {
569 if ((ip->ip_src.s_addr & a->ac_smsk) != a->ac_sip)
570 continue;
571 }
572
573 if ((a->ac_mflag & IPMAC_SRCPORT) != 0) {
574 if (ip->ip_p != IPPROTO_UDP && ip->ip_p != IPPROTO_TCP)
575 continue;
576 if (tcp->th_sport != a->ac_sport)
577 continue;
578 }
579
580 if ((a->ac_mflag & IPMAC_LOGTAG) != 0) {
581 if (a->ac_logtag != ipf->fl_logtag)
582 continue;
583 }
584
585 if ((a->ac_mflag & IPMAC_NATTAG) != 0) {
586 if (strncmp(a->ac_nattag, ipf->fl_nattag.ipt_tag,
587 IPFTAG_LEN) != 0)
588 continue;
589 }
590
591 matched = 1;
592
593 /*
594 * It matched so now execute the command
595 */
596 if (a->ac_syslog != 0) {
597 syslog(lvl, "%s", log);
598 }
599
600 if (a->ac_savefp != NULL) {
601 if (a->ac_dflag & IPMDO_SAVERAW)
602 fwrite(ipl, 1, ipl->ipl_dsize, a->ac_savefp);
603 else
604 fputs(log, a->ac_savefp);
605 }
606
607 if (a->ac_exec != NULL) {
608 switch (fork())
609 {
610 case 0 :
611 {
612 FILE *pi;
613
614 pi = popen(a->ac_run, "w");
615 if (pi != NULL) {
616 fprintf(pi, "%s\n", log);
617 if ((opts & OPT_HEXHDR) != 0) {
618 dumphex(pi, 0, buf,
619 sizeof(*ipl) +
620 sizeof(*ipf));
621 }
622 if ((opts & OPT_HEXBODY) != 0) {
623 dumphex(pi, 0, (char *)ip,
624 ipf->fl_hlen +
625 ipf->fl_plen);
626 }
627 pclose(pi);
628 }
629 exit(1);
630 }
631 case -1 :
632 break;
633 default :
634 break;
635 }
636 }
637 }
638
639 return matched;
640 }
641
642
643 static void free_action(a)
644 ipmon_action_t *a;
645 {
646 if (a->ac_savefile != NULL) {
647 free(a->ac_savefile);
648 a->ac_savefile = NULL;
649 }
650 if (a->ac_savefp != NULL) {
651 fclose(a->ac_savefp);
652 a->ac_savefp = NULL;
653 }
654 if (a->ac_exec != NULL) {
655 free(a->ac_exec);
656 if (a->ac_run == a->ac_exec)
657 a->ac_run = NULL;
658 a->ac_exec = NULL;
659 }
660 if (a->ac_run != NULL) {
661 free(a->ac_run);
662 a->ac_run = NULL;
663 }
664 if (a->ac_iface != NULL) {
665 free(a->ac_iface);
666 a->ac_iface = NULL;
667 }
668 a->ac_next = NULL;
669 free(a);
670 }
671
672
673 int load_config(file)
674 char *file;
675 {
676 ipmon_action_t *a;
677 FILE *fp;
678 char *s;
679
680 s = getenv("YYDEBUG");
681 if (s != NULL)
682 yydebug = atoi(s);
683 else
684 yydebug = 0;
685
686 while ((a = alist) != NULL) {
687 alist = a->ac_next;
688 free_action(a);
689 }
690
691 yylineNum = 1;
692
693 (void) yysettab(yywords);
694
695 fp = fopen(file, "r");
696 if (!fp) {
697 perror("load_config:fopen:");
698 return -1;
699 }
700 yyin = fp;
701 while (!feof(fp))
702 yyparse();
703 fclose(fp);
704 return 0;
705 }