Main Page | Modules | Class List | Directories | File List | Class Members | File Members | Related Pages

fw-pktfilter.c

Go to the documentation of this file.
00001 /*
00002  * fw-pktfilter.c
00003  *
00004  * Copyright (c) 2002 Dug Song <dugsong@monkey.org>
00005  * Copyright (c) 2001 Jean-Baptiste Marchand, Hervé Schauer Consultants.  
00006  *
00007  * $Id: fw-pktfilter.c,v 1.4 2005/02/15 06:37:06 dugsong Exp $
00008  */
00009 
00010 #include "config.h"
00011 
00012 #include <iphlpapi.h>
00013 
00014 #include <ctype.h>
00015 #include <errno.h>
00016 #include <stdio.h>
00017 #include <stdlib.h>
00018 #include <string.h>
00019 
00020 #include "dnet.h"
00021 
00022 #define PKTFILTER_PIPE "\\\\.\\pipe\\PktFltPipe"        
00023 #define MAX_RULE_LENGTH 256
00024 
00025 #define FILTER_FAILURE 0 /* filter had a syntax error */
00026 #define FILTER_SUCCESS 1 /* filter was correctly added */
00027 #define FILTER_MESSAGE 2 /* informative message returned to the client */
00028 
00029 char *icmp_types[] = {
00030         "echorep",      /* 0: echo reply */
00031         "",             /* 1: unused */
00032         "",             /* 2: unused */
00033         "unreach",      /* 3: destination unreachable */
00034         "squench",      /* 4: source quench */
00035         "redir",        /* 5: redirect */
00036         "",             /* 6: unused */
00037         "",             /* 7: unused */
00038         "echo",         /* 8: echo request */
00039         "router_adv",   /* 9: router advertisement */
00040         "router_sol",   /* 10: router solicitation */
00041         "timex",        /* 11: time exceeded */
00042         "paramprob",    /* 12: parameter problem */
00043         "timest",       /* 13: timestamp request */
00044         "timestrep",    /* 14: timestamp reply */
00045         "inforeq",      /* 15: information request */
00046         "inforep",      /* 16: information reply */
00047         "maskreq",      /* 17: address mask request */
00048         "maskrep",      /* 18: address mask reply */
00049         NULL
00050 };
00051 
00052 struct fw_handle {
00053         IP_ADAPTER_INFO *ifinfo;
00054         /* XXX - rules cache for delete lookup? */
00055 };
00056 
00057 static int
00058 parse_addr(char *p, struct addr *a)
00059 {
00060         if (strcmp(p, "any") == 0)
00061                 return (addr_aton("0.0.0.0/0", a));
00062         return (addr_aton(p, a));
00063 }
00064 
00065 static int
00066 parse_portspec(char *str, uint16_t *ports)
00067 {
00068         char *p = strsep(&str, " ");
00069         
00070         if (p[0] == '=') {
00071                 ports[0] = ports[1] = atoi(strsep(&str, " "));
00072         } else if (p[0] == '<') {
00073                 ports[1] = atoi(strsep(&str, " "));
00074                 if (p[1] != '=') ports[1]--;
00075         } else if (p[0] == '>') {
00076                 ports[1] = TCP_PORT_MAX;
00077                 ports[0] = atoi(strsep(&str, " "));
00078                 if (p[1] != '=') ports[0]++;
00079         } else if (p[0] != '\0') {
00080                 if (strcmp(strsep(&str, " "), "><") != 0)
00081                         return (-1);
00082                 ports[0] = atoi(p) + 1;
00083                 ports[1] = atoi(strsep(&str, " ")) - 1;
00084         }
00085         return (0);
00086 }
00087 
00088 static int
00089 parse_icmpspec(char *str, uint16_t *type, uint16_t *code)
00090 {
00091         char *p, *e;
00092         int i;
00093 
00094         p = strsep(&str, " ");
00095         for (i = 0; icmp_types[i] && strcmp(p, icmp_types[i]); i++)
00096                 ;
00097         if (icmp_types[i] == NULL) {
00098                 i = strtol(p, &e, 10);
00099                 if (*e != '\0')
00100                         return (-1);
00101         }
00102         type[0] = i;
00103         type[1] = 0xff;
00104         
00105         p = strsep(&str, " ");
00106         if (p != NULL && strcmp(p, "code")) {
00107                 p = strsep(&str, " ");
00108                 i = strtol(p, &e, 10);
00109                 if (*e != '\0')
00110                         return (-1);
00111                 code[0] = i;
00112                 code[1] = 0xff;
00113         }
00114         return (0);
00115 }
00116 
00117 /*
00118   <op> <dir> on <device> all
00119   <op> <dir> on <device> proto <proto> all
00120   <op> <dir> on <device> proto <proto> from <src> [ports] to <dst> [ports]
00121   <op> <dir> on <device> proto icmp all [icmp-type <type> [code <code>]]
00122   <op> <dir> on <device> proto icmp from <src> to <dst> [icmp-type <type> [code <code>]]
00123 */
00124 static int
00125 parse_rule(char *str, struct fw_rule *rule)
00126 {
00127         char *p, *q;
00128         
00129         memset(rule, 0, sizeof(*rule));
00130         
00131         /* action */
00132         p = strsep(&str, " ");
00133         if (strcmp(p, "block") == 0)
00134                 rule->fw_op = FW_OP_BLOCK;
00135         else if (strcmp(p, "pass") == 0)
00136                 rule->fw_op = FW_OP_ALLOW;
00137         else return (-1);
00138         
00139         /* direction */
00140         p = strsep(&str, " ");
00141         if (strcmp(p, "in") == 0)
00142                 rule->fw_dir = FW_DIR_IN;
00143         else if (strcmp(p, "out") == 0)
00144                 rule->fw_dir = FW_DIR_OUT;
00145         else return (-1);
00146 
00147         /* device */
00148         if (strcmp(strsep(&str, " "), "on") != 0)
00149                 return (-1);
00150         p = strsep(&str, " ");
00151         /* XXX - handle bug in pktfltsrv.c */
00152         if ((q = strstr(p, "proto")) != NULL)
00153                 *q = '\0';
00154         if (strcmp(p, "all") != 0)
00155                 strlcpy(rule->fw_device, p, sizeof(rule->fw_device));
00156         
00157         /* proto */
00158         p = strsep(&str, " ");
00159         /* XXX - handle bug in pktfltsrv.c */
00160         if (strcmp(p, "proto") == 0)
00161                 p = strsep(&str, " ");
00162         /* XXX - handle default rules */
00163         if (strcmp(p, "all") == 0)
00164                 return (0);
00165         if (strcmp(p, "icmp") == 0)
00166                 rule->fw_proto = IP_PROTO_ICMP;
00167         else if (strcmp(p, "tcp") == 0)
00168                 rule->fw_proto = IP_PROTO_TCP;
00169         else if (strcmp(p, "udp") == 0)
00170                 rule->fw_proto = IP_PROTO_UDP;
00171         else rule->fw_proto = atoi(p);
00172         
00173         /* source */
00174         p = strsep(&str, " ");
00175         if (strcmp(p, "all") == 0)
00176                 return (0);
00177         if (strcmp(p, "from") != 0)
00178                 goto icmp_type_code;
00179         p = strsep(&str, " ");
00180         if (parse_addr(p, &rule->fw_src) < 0)
00181                 return (-1);
00182         
00183         /* source port */
00184         p = strsep(&str, " ");
00185         if (strcmp(p, "port") == 0) {
00186                 if ((p = strstr(str, " to ")) == NULL)
00187                         return (-1);
00188                 *p++ = '\0';
00189                 if (parse_portspec(str, rule->fw_sport) < 0)
00190                         return (-1);
00191                 str = p + 3;
00192         } else if (strcmp(p, "to") != 0)
00193                 return (-1);
00194         
00195         /* destination */
00196         p = strsep(&str, " ");
00197         if (parse_addr(p, &rule->fw_dst) < 0)
00198                 return (-1);
00199 
00200         /* destination port */
00201         p = strsep(&str, " ");
00202         if (strcmp(p, "port") == 0)
00203                 return (parse_portspec(str, rule->fw_dport));
00204 
00205  icmp_type_code:
00206         /* icmp-type, code */
00207         if (strcmp(p, "icmp-type") == 0) {
00208                 if (parse_icmpspec(str, rule->fw_sport, rule->fw_dport) < 0)
00209                         return (-1);
00210         }
00211         return (0);
00212 }
00213 
00214 static int
00215 format_rule(const struct fw_rule *rule, char *buf, int len)
00216 {
00217         char tmp[128];
00218         
00219         strlcpy(buf, (rule->fw_op == FW_OP_ALLOW) ? "pass " : "block ", len);
00220         strlcat(buf, (rule->fw_dir == FW_DIR_IN) ? "in " : "out ", len);
00221         snprintf(tmp, sizeof(tmp), "on %s ", rule->fw_device);
00222         strlcat(buf, tmp, len);
00223         if (rule->fw_proto != 0) {
00224                 snprintf(tmp, sizeof(tmp), "proto %d ", rule->fw_proto);
00225                 strlcat(buf, tmp, len);
00226         }
00227         /* source */
00228         if (rule->fw_src.addr_type != ADDR_TYPE_NONE) {
00229                 snprintf(tmp, sizeof(tmp), "from %s ",
00230                     addr_ntoa(&rule->fw_src));
00231                 strlcat(buf, tmp, len);
00232         } else
00233                 strlcat(buf, "from any ", len);
00234         
00235         /* sport */
00236         if (rule->fw_proto == IP_PROTO_TCP || rule->fw_proto == IP_PROTO_UDP) {
00237                 if (rule->fw_sport[0] == rule->fw_sport[1])
00238                         snprintf(tmp, sizeof(tmp), "port = %d ",
00239                             rule->fw_sport[0]);
00240                 else
00241                         snprintf(tmp, sizeof(tmp), "port %d >< %d ",
00242                             rule->fw_sport[0] - 1, rule->fw_sport[1] + 1);
00243                 strlcat(buf, tmp, len);
00244         }
00245         /* destination */
00246         if (rule->fw_dst.addr_type != ADDR_TYPE_NONE) {
00247                 snprintf(tmp, sizeof(tmp), "to %s ",
00248                     addr_ntoa(&rule->fw_dst));
00249                 strlcat(buf, tmp, len);
00250         } else
00251                 strlcat(buf, "to any ", len);
00252         
00253         /* dport */
00254         if (rule->fw_proto == IP_PROTO_TCP || rule->fw_proto == IP_PROTO_UDP) {
00255                 if (rule->fw_dport[0] == rule->fw_dport[1])
00256                         snprintf(tmp, sizeof(tmp), "port = %d",
00257                             rule->fw_dport[0]);
00258                 else
00259                         snprintf(tmp, sizeof(tmp), "port %d >< %d",
00260                             rule->fw_dport[0] - 1, rule->fw_dport[1] + 1);
00261                 strlcat(buf, tmp, len);
00262         } else if (rule->fw_proto == IP_PROTO_ICMP) {
00263                 if (rule->fw_sport[1]) {
00264                         snprintf(tmp, sizeof(tmp), "icmp-type %d",
00265                             rule->fw_sport[0]);
00266                         strlcat(buf, tmp, len);
00267                         if (rule->fw_dport[1]) {
00268                                 snprintf(tmp, sizeof(tmp), " code %d",
00269                                     rule->fw_dport[0]);
00270                                 strlcat(buf, tmp, len);
00271                         }
00272                 }
00273         }
00274         return (strlen(buf));
00275 }
00276 
00277 static char *
00278 call_pipe(const char *msg, int len)
00279 {
00280         HANDLE *pipe;
00281         DWORD i;
00282         char *p, *reply, status;
00283         
00284         if (!WaitNamedPipe(PKTFILTER_PIPE, NMPWAIT_USE_DEFAULT_WAIT) ||
00285             (pipe = CreateFile(PKTFILTER_PIPE, GENERIC_READ | GENERIC_WRITE,
00286                 0, NULL, OPEN_EXISTING, 0, NULL)) == INVALID_HANDLE_VALUE) {
00287                 return (NULL);
00288         }
00289         reply = NULL;
00290         
00291         if (WriteFile(pipe, msg, len, &i, NULL)) {
00292                 if (ReadFile(pipe, &status, sizeof(status), &i, NULL)) {
00293                         if (status == FILTER_FAILURE) {
00294                                 ReadFile(pipe, &status, sizeof(status),
00295                                     &i, NULL);
00296                         } else if (status == FILTER_MESSAGE) {
00297                                 /* get msg length */
00298                                 if (ReadFile(pipe, &len, 4, &i, NULL)) {
00299                                         /* get msg */
00300                                         p = reply = calloc(1, len + 1);
00301                                         if (!ReadFile(pipe, reply, len,
00302                                                 &i, NULL)) {
00303                                                 free(reply);
00304                                                 reply = NULL;
00305                                         }
00306                                 }
00307                         } else if (status == FILTER_SUCCESS)
00308                                 reply = strdup("");     /* XXX */
00309                 }
00310         }
00311         CloseHandle(pipe);
00312         return (reply);
00313 }
00314 
00315 fw_t *
00316 fw_open(void)
00317 {
00318         fw_t *f;
00319         IP_ADAPTER_INFO *ifinfo;
00320         ULONG size;
00321         
00322         if ((f = calloc(1, sizeof(*f))) == NULL)
00323                 return (NULL);
00324         
00325         size = sizeof(*f->ifinfo);
00326         f->ifinfo = malloc(size);
00327         if (GetAdaptersInfo(f->ifinfo, &size) != ERROR_SUCCESS) {
00328                 free(f->ifinfo);
00329                 f->ifinfo = malloc(size);
00330                 GetAdaptersInfo(f->ifinfo, &size);
00331         }
00332         /* XXX - normalize interface names. */
00333         for (ifinfo = f->ifinfo; ifinfo != NULL; ifinfo = ifinfo->Next) {
00334                 char *fmt;
00335                 if (ifinfo->Type == MIB_IF_TYPE_ETHERNET)
00336                         fmt = "eth";
00337                 else if (ifinfo->Type == MIB_IF_TYPE_PPP)
00338                         fmt = "ppp";
00339                 else if (ifinfo->Type == MIB_IF_TYPE_SLIP)
00340                         fmt = "sl";
00341                 else if (ifinfo->Type == MIB_IF_TYPE_LOOPBACK)
00342                         fmt = "lo";
00343                 else if (ifinfo->Type == MIB_IF_TYPE_TOKENRING)
00344                         fmt = "tr";
00345                 else if (ifinfo->Type == MIB_IF_TYPE_FDDI)
00346                         fmt = "fd";
00347                 else 
00348                         fmt = "if";
00349                 sprintf(ifinfo->AdapterName, "%s%lu", fmt, ifinfo->ComboIndex);
00350         }
00351         return (f);
00352 }
00353 
00354 int
00355 fw_add(fw_t *f, const struct fw_rule *rule)
00356 {
00357         char *p, buf[MAX_RULE_LENGTH];
00358         int len;
00359         
00360         len = format_rule(rule, buf, sizeof(buf));
00361         
00362         if ((p = call_pipe(buf, len)) == NULL)
00363                 return (-1);
00364         free(p);
00365         return (0);
00366 }
00367 
00368 int
00369 fw_delete(fw_t *f, const struct fw_rule *rule)
00370 {
00371         struct fw_rule tmp;
00372         char *p, *line, *msg, cmd[128], buf[MAX_RULE_LENGTH];
00373         int n, ruleno, len;
00374         
00375         format_rule(rule, buf, sizeof(buf));
00376         
00377         len = snprintf(cmd, sizeof(cmd), "List on %s", rule->fw_device);
00378         if ((msg = call_pipe(cmd, len)) == NULL)
00379                 return (-1);
00380 
00381         for (ruleno = 0, p = msg; (line = strsep(&p, "\r\n")) != NULL; ) {
00382                 if (strncmp(line, "rule ", 5) == 0) {
00383                         line += 5;
00384                         n = atoi(strsep(&line, ":"));
00385                         if (parse_rule(line + 1, &tmp) == 0 &&
00386                             memcmp(&tmp, rule, sizeof(tmp)) == 0) {
00387                                 ruleno = n;
00388                                 break;
00389                         }
00390                 }
00391         }
00392         free(msg);
00393         if (ruleno == 0) {
00394                 errno = ENXIO;
00395                 SetLastError(ERROR_NO_DATA);
00396                 return (-1);
00397         }
00398         len = snprintf(cmd, sizeof(cmd), "delete %d on %s",
00399             ruleno, rule->fw_device);
00400         if ((p = call_pipe(cmd, len)) == NULL)
00401                 return (-1);
00402         free(p);
00403 
00404         return (0);
00405 }
00406 
00407 int
00408 fw_loop(fw_t *f, fw_handler callback, void *arg)
00409 {
00410         struct fw_rule rule;
00411         IP_ADAPTER_INFO *ifinfo;
00412         char *p, *line, *msg, buf[MAX_RULE_LENGTH];
00413         int len, ret;
00414 
00415         for (ret = 0, ifinfo = f->ifinfo; ret == 0 && ifinfo != NULL;
00416             ifinfo = ifinfo->Next) {
00417                 len = snprintf(buf, sizeof(buf), "list on %s",
00418                     ifinfo->AdapterName);
00419                 if ((msg = call_pipe(buf, len)) == NULL)
00420                         return (-1);
00421                 
00422                 /* parse msg */
00423                 for (p = msg; (line = strsep(&p, "\r\n")) != NULL; ) {
00424                         if (*line == '\0' || *line == '#' || isspace(*line))
00425                                 continue;
00426                         if (parse_rule(line, &rule) == 0) {
00427                                 if ((ret = callback(&rule, arg)) != 0)
00428                                         break;
00429                         }
00430                 }
00431                 free(msg);
00432         }
00433         return (ret);
00434 }
00435 
00436 fw_t *
00437 fw_close(fw_t *f)
00438 {
00439         if (f != NULL) {
00440                 free(f->ifinfo);
00441                 free(f);
00442         }
00443         return (NULL);
00444 }

Generated on Sun May 14 14:51:11 2006 by  doxygen 1.4.2