]> err.no Git - varnish/commitdiff
ACL code rewrite:
authorphk <phk@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Wed, 23 Jul 2008 15:01:54 +0000 (15:01 +0000)
committerphk <phk@d4fa192b-c00b-0410-8231-f00ffab90ce4>
Wed, 23 Jul 2008 15:01:54 +0000 (15:01 +0000)
Add IPv6 support (untested!)

Implement evil-acls

IPv6 support
------------

I have implemented IPv6 filtering support, but I have done so blindly
as I have no IPv6 networks to test with.

Please double check before relying on this to work, and please report
your findings back to us.

Syntax
------

The ACL rules still have the same syntax, but the sematics have
expanded to handle IPv6 also:

    acl foo {

        "foohost";              // Match, if the address is one of the
                                // ipv4 or ipv6 addresses of "foohost"

        ! "foohost";            // Fail, if...

        "192.168.1.7" / 24;     // Use mask for comparison:  The '7' is
                                // ignored

// Implicit masks:
!"172.16"; // Fail 172.16.0.0 to 172.16.255.255
"10.0.0"; // Match 10.0.0.0 to 10.0.0.255

        "www.freebsd.org" / 24; // This will give compile error, because
                                // the "www.freebsd.org" has both ipv4
                                // and ipv6 addresses, and using the same
                                // mask for both kinds do not make sense.

        ( ... );                // Ignore this rule if DNS lookup fails.

        ( ! "idiot.net" );      // If we can resolve "idiot.net", then
                                // return Failure to match, if we see them.
    }

Please notice that DNS lookup happens *only* on VCL compilation, if a
DNS record changes you need to recompile (ie: vcl.load or vcl.inline)
your VCL code again, it is not enough to just switch vcl (vcl.use).

(This is the same as with backend DNS lookups)

Evil-acls
---------

Most firewall or ip-filtering facilities, compile the lists of
networks and masks to a table, and matches proceed sequentially
through that table until the table is exhausted or a match is found.

Since we compile our ACLs into C-code, we might as well implement the
"evil-acl" concept, and compile the rules directly into C-code instead.

An ACL like this:

acl foo {
"172.16";
!"172.16.17";
"172.16.17.18";
"172.16"/18;
}

Compiles to:

if (fam == 2) {
 if (a[0] == 172) {
          if (a[1] == 16) {
           if (a[2] == 17) {
            if (a[3] == 18) {
             VRT_acl_log(sp, "MATCH bar " "172.16.17.18");
             return (1);
            }
            VRT_acl_log(sp, "NEG_MATCH bar " "172.16.17");
            return (0);
           }
           else if ((a[3] & 0xc0) == 0) {
            VRT_acl_log(sp, "MATCH bar " "172.16" "/18" );
            return (1);
           }
           VRT_acl_log(sp, "MATCH bar " "172.16");
           return (1);
          }
         }
        }
VRT_acl_log(sp, "NO_MATCH bar");
return (0);

As can be seen, for example the comparison with "172" is now shared
for all four rules in the ACL, instead of being carried out once for
each of the four rules.

In addition to this optimization, the C-compiler will of course use
its usual (and unusual) tricks to speed things up, quite likely inlining
the ACL code in the VCL functions where they are referenced.

It will also be noticed, that the compiler sorts the rules in "most
specific order".

This means that:
"172.16.17.18";
gets tested before
!"172.16.17";
even though it is listed later in the ACL.

Previously we tested the rules in the order given.

git-svn-id: svn+ssh://projects.linpro.no/svn/varnish/trunk@2995 d4fa192b-c00b-0410-8231-f00ffab90ce4

varnish-cache/bin/varnishd/cache_vrt_acl.c
varnish-cache/bin/varnishd/flint.lnt
varnish-cache/bin/varnishd/flint.sh
varnish-cache/bin/varnishtest/tests/c00005.vtc
varnish-cache/include/vrt.h
varnish-cache/lib/libvcl/flint.lnt
varnish-cache/lib/libvcl/vcc_acl.c
varnish-cache/lib/libvcl/vcc_compile.h
varnish-cache/lib/libvcl/vcc_fixed_token.c

index 55dab36ac044cf2a124c72b8929bcd5501032eeb..8cbabdf3d9385aad3c83444c979a3b4b9175cb00 100644 (file)
  *
  * Runtime support for compiled VCL programs, ACLs
  *
- * XXX: getaddrinfo() does not return a TTL.  We might want to add
- * XXX: a refresh facility.
  */
 
 #include "config.h"
 
-#include <sys/types.h>
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
 #include "shmlog.h"
 #include "vrt.h"
-#include "vcl.h"
 #include "cache.h"
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <netdb.h>
-#include <netinet/in.h>
-
-
-static uint32_t ipv4mask[] = {
-       [0]     =       0xffffffff,
-#define M(n)   [n] = (uint32_t)((uint64_t)0xffffffff << (32 - n))
-       M( 1), M( 2), M( 3), M( 4), M( 5), M( 6), M( 7), M( 8), M( 9), M(10),
-       M(11), M(12), M(13), M(14), M(15), M(16), M(17), M(18), M(19), M(20),
-       M(21), M(22), M(23), M(24), M(25), M(26), M(27), M(28), M(29), M(30),
-       M(31), M(32)
-};
-
-static int
-vrt_acl_vsl(const struct sess *sp, const char *acln, const struct vrt_acl *ap, int r)
-{
-
-       AN(ap);
-       if (acln != NULL) {
-               if (ap->name == NULL) {
-                       assert(r == 0);
-                       WSP(sp, SLT_VCL_acl, "NO_MATCH %s", acln);
-                       return (r);
-               }
-               if (ap->priv == NULL) {
-                       assert(r == 0);
-                       WSP(sp, SLT_VCL_acl, "FAIL %s %s", acln, ap->desc);
-                       return (r);
-               }
-
-               WSP(sp, SLT_VCL_acl, "%s %s %s",
-                   r ? "MATCH" : "NEG_MATCH", acln, ap->desc);
-       }
-       return (r);
-}
-
-int
-VRT_acl_match(const struct sess *sp, struct sockaddr *sa, const char *acln, const struct vrt_acl *ap)
-{
-       struct addrinfo *a1;
-       struct sockaddr_in *sin1, *sin2;
-
-       if (sa->sa_family == AF_INET)
-               sin1 = (void*)sa;
-       else
-               sin1 = NULL;
-
-       for ( ; ap->name != NULL; ap++) {
-               if (ap->priv == NULL && ap->paren)
-                       continue;
-               if (ap->priv == NULL && ap->not) {
-                       return (vrt_acl_vsl(sp, acln, ap, 0));
-               }
-               if (ap->priv == NULL)
-                       continue;
-               for (a1 = ap->priv; a1 != NULL; a1 = a1->ai_next) {
-
-                       /* only match the right family */
-                       if (a1->ai_family != sp->sockaddr->sa_family)
-                               continue;
-
-                       if (a1->ai_family == AF_INET) {
-                               assert(sin1 != NULL);
-                               assert(a1->ai_addrlen >= sizeof (*sin2));
-                               sin2 = (void*)a1->ai_addr;
-                               if (0 == ((
-                                   htonl(sin1->sin_addr.s_addr) ^
-                                   htonl(sin2->sin_addr.s_addr)) &
-                                   ipv4mask[ap->mask > 32 ? 32 : ap->mask]))
-                                       return (
-                                           vrt_acl_vsl(sp, acln, ap, !ap->not));
-                               continue;
-                       }
-
-                       /* Not rules for unknown protos match */
-                       if (ap->not)
-                               return (vrt_acl_vsl(sp, acln, ap, 0));
-               }
-       }
-       return (vrt_acl_vsl(sp, acln, ap, 0));
-}
-
-void
-VRT_acl_init(struct vrt_acl *ap)
-{
-       struct addrinfo a0, *a1;
-       int i;
-
-       memset(&a0, 0, sizeof a0);
-       a0.ai_socktype = SOCK_STREAM;
-
-       for ( ; ap->name != NULL; ap++) {
-               a1 = NULL;
-               i = getaddrinfo(ap->name, NULL, &a0, &a1);
-               if (i != 0) {
-                       fprintf(stderr, "getaddrinfo(%s) = %s\n",
-                           ap->name, gai_strerror(i));
-                       if (a1 != NULL)
-                               freeaddrinfo(a1);
-                       a1 = NULL;
-               }
-               ap->priv = a1;
-       }
-}
 
 void
-VRT_acl_fini(struct vrt_acl *ap)
+VRT_acl_log(const struct sess *sp, const char *msg)
 {
-       struct addrinfo *a1;
-
-       for ( ; ap->name != NULL; ap++) {
-               if (ap->priv == NULL)
-                       continue;
-               a1 = ap->priv;
-               ap->priv = NULL;
-               freeaddrinfo(a1);
-       }
+       WSL(sp->wrk, SLT_VCL_acl, sp->fd, msg);
 }
index d6dd4e444518f147e6a07fd6b96bbe32807c503b..3b9d95e54a40336ce5b67d891ebc4f096da1aa4a 100644 (file)
@@ -8,6 +8,7 @@
 -elib(123)  // size is both a variable and a macro with args
 -emacro(736, isnan)  // isnanf
 -efile(766, ../../config.h)
+-emacro(413, offsetof) // likely null pointer
 
 
 // -header(../../config.h)
@@ -46,6 +47,7 @@
 -esym(534, memmove)    // Ignoring return value of function
 -esym(534, strcpy)     // Ignoring return value of function
 -esym(534, vsb_printf) // Ignoring return value of function
+-esym(534, vsb_vprintf)        // Ignoring return value of function
 -esym(534, vsb_cat)    // Ignoring return value of function
 -esym(534, vsb_bcat)   // Ignoring return value of function
 -esym(534, vsb_putc)   // Ignoring return value of function
@@ -63,6 +65,8 @@
 -emacro(774, HTTPH)    // always false
 -emacro(527, ARGV_ERR) // unreachable
 
+-e788                  // enum value not used in defaulted switch
+
 // cache.h
 -emacro(506, INCOMPL) // Constant value Boolean
 
index 25c35176250d98f1aa58f7f8d4bd0c1341c93787..359d308a3bb561e11dc3e7c28bc2155164409071 100755 (executable)
@@ -8,7 +8,10 @@ flexelint \
        -I../.. \
        -DVARNISH_STATE_DIR=\"foo\" \
        flint.lnt \
-       *.c ../../lib/libvarnish/*.c > $T 2>&1
+       *.c \
+       ../../lib/libvarnish/*.c \
+       ../../lib/libvcl/*.c \
+       > $T 2>&1
 
 for t in Error Warning Info Note
 do
index 3edd216b541a9611a503addce7594063547ef3ca..14b827523921d54ab62f58ec7bfd495f6a7c0ce0 100644 (file)
@@ -33,7 +33,7 @@ client c1 -run
 
 varnish v1 -vcl+backend {
        acl acl1 {
-               "!127.0.0.1";
+               ! "127.0.0.1";
        }
 
        sub vcl_recv {
index d5d4cb1f1c4fa9c3ca3a2719a311d69bb6d54447..756c73bb0ccabe9029822e58e8d221f1a8969ecf 100644 (file)
@@ -120,19 +120,10 @@ struct vrt_ref {
        const char      *token;
 };
 
-struct vrt_acl {
-       unsigned char   not;
-       unsigned char   mask;
-       unsigned char   paren;
-       const char      *name;
-       const char      *desc;
-       void            *priv;
-};
-
 /* ACL related */
-int VRT_acl_match(const struct sess *, struct sockaddr *, const char *, const struct vrt_acl *);
-void VRT_acl_init(struct vrt_acl *);
-void VRT_acl_fini(struct vrt_acl *);
+#define VRT_ACL_MAXADDR                16      /* max(IPv4, IPv6) */
+
+void VRT_acl_log(const struct sess *, const char *msg);
 
 /* Regexp related */
 void VRT_re_init(void **, const char *, int sub);
index 24c9e6485375110d07a35b6399373cb140cc66a7..60b385040df0354146989b718feafa246946eb21 100644 (file)
@@ -16,6 +16,8 @@
                        // 796 and 797 (out-of-bounds errors).
 
 
+-emacro(413, offsetof) // likely null pointer
+
 // -ffc    // No automatic custody
 
 -esym(534, vsb_printf) // Ignoring return value of function
index a51b334bbb1f9f22c4d78e5b9f139a8d10d36ff4..1f5a401068aebfe90a3f8693ef75127012834f17 100644 (file)
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <stddef.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #include "vsb.h"
+#include "vrt.h"
 
 #include "vcc_priv.h"
 #include "vcc_compile.h"
 #include "libvarnish.h"
 
+struct acl_e {
+       VTAILQ_ENTRY(acl_e)     list;
+       unsigned char           data[VRT_ACL_MAXADDR + 1];
+       unsigned                mask;
+       unsigned                not;
+       unsigned                para;
+       struct token            *t_addr;
+       struct token            *t_mask;
+};
+
+/* Compare two acl rules for ordering */
+
+static int
+vcl_acl_cmp(struct tokenlist *tl, struct acl_e *ae1, struct acl_e *ae2)
+{
+       unsigned char *p1, *p2;
+       unsigned m;
+
+       (void)tl;
+       p1 = ae1->data;
+       p2 = ae2->data;
+       m = ae1->mask;
+       if (ae2->mask < m)
+               m = ae2->mask;
+       for (; m >= 8; m -= 8) {
+               if (*p1 < *p2)  
+                       return (-1);
+               if (*p1 > *p2)  
+                       return (1);
+               p1++;
+               p2++;
+       }
+       if (m) {
+               m = 0xff00 >> m;
+               m &= 0xff;
+               if ((*p1 & m) < (*p2 & m))
+                       return (-1);
+               if ((*p1 & m) > (*p2 & m))
+                       return (1);
+       }
+       if (ae1->mask > ae2->mask)
+               return (-1);
+       if (ae1->mask < ae2->mask)
+               return (1);
+
+       return (0);
+}
+
+
+static void
+vcl_acl_add_entry(struct tokenlist *tl, struct acl_e *ae)
+{
+       struct acl_e *ae2;
+       int i;
+
+       VTAILQ_FOREACH(ae2, &tl->acl, list) {
+               i = vcl_acl_cmp(tl, ae, ae2);
+               if (i == 0) {
+                       /* If the two rules agree, silently ignore it */
+                       if (ae->not == ae2->not)
+                               return;
+                       vsb_printf(tl->sb, "Conflicting ACL entries:\n");
+                       vcc_ErrWhere(tl, ae2->t_addr);
+                       vsb_printf(tl->sb, "vs:\n");
+                       vcc_ErrWhere(tl, ae->t_addr);
+                       return;
+               }
+               /*
+                * We could eliminate pointless rules here, for instance in:
+                *      "10.1.0.1";
+                *      "10.1";
+                * The first rule is clearly pointless, as the second one
+                * covers it.
+                *
+                * We do not do this however, because the shmlog may
+                * be used to gather statistics.
+                */
+               if (i < 0) {
+                       VTAILQ_INSERT_BEFORE(ae2, ae, list);
+                       return;
+               }
+       }
+       VTAILQ_INSERT_TAIL(&tl->acl, ae, list);
+}
+
 static void
-vcc_acl_top(struct tokenlist *tl, const char *acln)
+vcc_acl_emit_entry(struct tokenlist *tl, const struct acl_e *ae, int l, const unsigned char *u, int fam)
 {
+       struct acl_e *ae2;
+
+       if (fam == PF_INET && ae->mask > 32) {
+               vsb_printf(tl->sb,
+                   "Too wide mask (%u) for IPv4 address", ae->mask);
+               vcc_ErrWhere(tl, ae->t_mask);
+               return;
+       }
+       if (fam == PF_INET6 && ae->mask > 128) {
+               vsb_printf(tl->sb,
+                   "Too wide mask (%u) for IPv6 address", ae->mask);
+               vcc_ErrWhere(tl, ae->t_mask);
+               return;
+       }
 
-       Fh(tl, 1, "\nstatic struct vrt_acl acl_%s[] = {\n", acln);
-       tl->hindent += INDENT;
+       ae2 = TlAlloc(tl, sizeof *ae2);
+       AN(ae2);
+       *ae2 = *ae;
+
+       ae2->data[0] = fam;
+       ae2->mask += 8; /* family matching */
+
+       memcpy(ae2->data + 1, u, l);
+
+       vcl_acl_add_entry(tl, ae2);
 
 }
 
+static void
+vcc_acl_try_getaddrinfo(struct tokenlist *tl, struct acl_e *ae)
+{
+       struct addrinfo *res0, *res, hint;
+       struct sockaddr_in *sin4;
+       struct sockaddr_in6 *sin6;
+       unsigned char *u, i4, i6;
+       int error, l;
+
+       memset(&hint, 0, sizeof hint);
+       hint.ai_family = PF_UNSPEC;
+       hint.ai_socktype = SOCK_STREAM;
+       error = getaddrinfo(ae->t_addr->dec, "0", &hint, &res0);
+       if (error) {
+               if (ae->para) {
+                       vsb_printf(tl->sb,
+                           "Warning: %s ignored\n  -- %s\n",
+                           ae->t_addr->dec, gai_strerror(error));
+                       Fh(tl, 1, "/* Ignored ACL entry: %s%s",
+                           ae->para ? "\"(\" " : "", ae->not ? "\"!\" " : "");
+                       EncToken(tl->fh, ae->t_addr);
+                       if (ae->t_mask) 
+                               Fh(tl, 0, "/%u", ae->mask);
+                       Fh(tl, 0, "%s\n", ae->para ? " \")\"" : "");
+                       Fh(tl, 1, " * getaddrinfo:  %s */\n",
+                            gai_strerror(error));
+               } else {
+                       vsb_printf(tl->sb,
+                           "DNS lookup(%s): %s\n",
+                           ae->t_addr->dec, gai_strerror(error));
+                       vcc_ErrWhere(tl, ae->t_addr);
+               }
+               return;
+       }
+
+       i4 = i6 = 0;
+       for(res = res0; res != NULL; res = res->ai_next) {
+               switch(res->ai_family) {
+               case PF_INET:
+                       sin4 = (void*)res->ai_addr;
+                       u = (void*)&sin4->sin_addr;
+                       l = 4;
+                       if (ae->t_mask == NULL)
+                               ae->mask = 32;
+                       i4++;
+                       vcc_acl_emit_entry(tl, ae, l, u, res->ai_family);
+                       break;
+               case PF_INET6:
+                       sin6 = (void*)res->ai_addr;
+                       u = (void*)&sin6->sin6_addr;
+                       l = 16;
+                       if (ae->t_mask == NULL)
+                               ae->mask = 128;
+                       i6++;
+                       vcc_acl_emit_entry(tl, ae, l, u, res->ai_family);
+                       break;
+               default:
+                       vsb_printf(tl->sb,
+                           "Ignoring unknown protocol family (%d) for %.*s\n",
+                               res->ai_family, PF(ae->t_addr));
+                       continue;
+               }
+               ERRCHK(tl);
+       }
+       freeaddrinfo(res0);
+
+       if (ae->t_mask != NULL && i4 > 0 && i6 > 0) {
+               vsb_printf(tl->sb,
+                   "Mask (%u) specified, but string resolves to"
+                   " both IPv4 and IPv6 addresses.\n", ae->mask);
+               vcc_ErrWhere(tl, ae->t_mask);
+               return;
+       }
+}
+
+/*--------------------------------------------------------------------
+ * Ancient stupidity on the part of X/Open and other standards orgs
+ * dictate that "192.168" be translated to 192.0.0.168.  Ever since
+ * CIDR happened, "192.168/16" notation has been used, but appearantly
+ * no API supports parsing this, so roll our own.
+ */
+
+static int
+vcc_acl_try_netnotation(struct tokenlist *tl, struct acl_e *ae)
+{
+       unsigned char b[4];
+       int i, j, k;
+       const char *p;
+
+       memset(b, 0, sizeof b);
+       p = ae->t_addr->dec;
+       for (i = 0; i < 4; i++) {
+               j = sscanf(p, "%hhu%n", &b[i], &k);
+               if (j != 1)
+                       return (0);
+               if (p[k] == '\0')
+                       break;
+               if (p[k] != '.')
+                       return (0);
+               p += k + 1;
+       }
+       if (ae->t_mask == NULL)
+               ae->mask = 8 + 8 * i;
+       vcc_acl_emit_entry(tl, ae, 4, b, AF_INET);
+       return (1);
+}
+
 static void
 vcc_acl_entry(struct tokenlist *tl)
 {
-       unsigned mask, para, not;
-       struct token *t;
+       struct acl_e *ae;
 
-       not = para = mask = 0;
+       ae = TlAlloc(tl, sizeof *ae);
+       AN(ae);
+
+       if (tl->t->tok == '!') {
+               ae->not = 1;
+               vcc_NextToken(tl);
+       }
 
        if (tl->t->tok == '(') {
-               para = 1;
+               ae->para = 1;
                vcc_NextToken(tl);
        }
 
-       if (tl->t->tok == '!') {
-               not = 1;
+       if (!ae->not && tl->t->tok == '!') {
+               ae->not = 1;
                vcc_NextToken(tl);
        }
 
        ExpectErr(tl, CSTR);
-       /* XXX: try to look it up, warn if failure */
-       t = tl->t;
+       ae->t_addr = tl->t;
        vcc_NextToken(tl);
+
        if (tl->t->tok == '/') {
                vcc_NextToken(tl);
+               ae->t_mask = tl->t;
                ExpectErr(tl, CNUM);
-               mask = vcc_UintVal(tl);
+               ae->mask = vcc_UintVal(tl);
                vcc_NextToken(tl);
        }
-       Fh(tl, 1, "{ %u, %u, %u, ", not, mask, para);
-       EncToken(tl->fh, t);
-       Fh(tl, 0, ", \"");
-       if (para)
-               Fh(tl, 0, "(");
-       if (not)
-               Fh(tl, 0, "!");
-       Fh(tl, 0, "\\\"\" ");
-       EncToken(tl->fh, t);
-       Fh(tl, 0, " \"\\\"");
-       if (mask)
-               Fh(tl, 0, "/%u", mask);
-       if (para)
-               Fh(tl, 0, ")");
-       Fh(tl, 0, "\" },\n");
-
-       if (para) {
+
+       if (ae->para) {
                ExpectErr(tl, ')');
                vcc_NextToken(tl);
        }
+
+       if (!vcc_acl_try_netnotation(tl, ae)) {
+               ERRCHK(tl);
+               vcc_acl_try_getaddrinfo(tl, ae);
+       }
+       ERRCHK(tl);
 }
 
 static void
-vcc_acl_bot(struct tokenlist *tl, const char *acln)
+vcc_acl_bot(const struct tokenlist *tl, const char *acln, int silent)
 {
+       struct acl_e *ae;
+       int depth, l, m, i;
+       unsigned at[VRT_ACL_MAXADDR + 1];
+       const char *oc;
+
+       Fh(tl, 0, "\nstatic int\n");
+       Fh(tl, 0, "match_acl_%s(const struct sess *sp, const void *p)\n", acln);
+       Fh(tl, 0, "{\n");
+       Fh(tl, 0, "\tunsigned fam;\n");
+       Fh(tl, 0, "\tconst unsigned char *a;\n");
+       Fh(tl, 0, "\n");
+       Fh(tl, 0, "\ta = p;\n");
+       Fh(tl, 0, "\tfam = a[%d];\n", offsetof(struct sockaddr, sa_family));
+       Fh(tl, 0, "\tif (fam == %d)\n", PF_INET);
+       Fh(tl, 0, "\t\ta += %d;\n", offsetof(struct sockaddr_in, sin_addr));
+       Fh(tl, 0, "\telse if (fam == %d)\n", PF_INET6);
+       Fh(tl, 0, "\t\ta += %d;\n", offsetof(struct sockaddr_in6, sin6_addr));
+       Fh(tl, 0, "\telse\n");
+       Fh(tl, 0, "\t\treturn(0);\n");
+       Fh(tl, 0, "\n");
+       depth = -1;
+       oc = 0;
+       at[0] = 256;
+       VTAILQ_FOREACH(ae, &tl->acl, list) {
+
+               /* Find how much common prefix we have */
+               for (l = 0; l <= depth && l * 8 < ae->mask; l++) {
+                       assert(l >= 0);
+                       if (ae->data[l] != at[l])
+                               break;
+               }
 
-       Fh(tl, 1, "{ 0, 0, 0, (void*)0, ""}\n", 0, 0);
-       tl->hindent -= INDENT;
-       Fh(tl, 1, "};\n");
-       Fi(tl, 1, "\tVRT_acl_init(acl_%s);\n", acln);
-       Ff(tl, 1, "\tVRT_acl_fini(acl_%s);\n", acln);
+               /* Back down, if necessary */
+               oc = "";
+               while (l <= depth) {
+                       Fh(tl, 0, "\t%*s}\n",
+                           -depth, "");
+                       depth--;
+                       oc = "else ";
+               }
+               m = ae->mask;
+               m -= l * 8;
+               for (i = l; m >= 8; m -= 8, i++) {
+                       if (i == 0) {
+                               Fh(tl, 0, "\t%*s%sif (fam == %d) {\n",
+                                   -i, "", oc, ae->data[i]);
+                       } else {
+                               Fh(tl, 0, "\t%*s%sif (a[%d] == %d) {\n",
+                                   -i, "", oc, i - 1, ae->data[i]);
+                       }
+                       at[i] = ae->data[i];
+                       depth = i;
+                       oc = "";
+               }
+               if (m > 0) {
+                       Fh(tl, 0, "\t%*s%sif ((a[%d] & 0x%x) == %d) {\n",
+                           -i, "",
+                           oc,
+                           i, (0xff00 >> m) & 0xff,
+                           ae->data[i] & ((0xff00 >> m) & 0xff));
+                       at[i] = 256;
+                       depth = i;
+                       oc = "";
+               }
+
+               i = (ae->mask + 7) / 8;
+
+               if (!silent) {
+                       Fh(tl, 0, "\t%*sVRT_acl_log(sp, \"%sMATCH %s \" ",
+                           -i, "",
+                           ae->not ? "NEG_" : "",
+                           acln,
+                           PF(ae->t_addr));
+                       EncToken(tl->fh, ae->t_addr);
+                       if (ae->t_mask != NULL)
+                               Fh(tl, 0, " \"/%.*s\" ", PF(ae->t_mask));
+                       Fh(tl, 0, ");\n");
+               }
+
+               Fh(tl, 0, "\t%*sreturn (%d);\n", -i, "", ae->not ? 0 : 1);
+       }
+
+       for (; 0 <= depth; depth--) 
+               Fh(tl, 0, "\t%*.*s}\n", depth, depth, "");
+       if (!silent)
+               Fh(tl, 0, "\tVRT_acl_log(sp, \"NO_MATCH %s\");\n", acln);
+       Fh(tl, 0, "\treturn (0);\n}\n");
 }
 
 void
@@ -121,21 +421,21 @@ vcc_Cond_Ip(const struct var *vp, struct tokenlist *tl)
                vcc_NextToken(tl);
                ExpectErr(tl, ID);
                vcc_AddRef(tl, tl->t, R_ACL);
-               Fb(tl, 1, "VRT_acl_match(sp, %s, \"%.*s\", acl_%.*s)\n",
-                   vp->rname, PF(tl->t), PF(tl->t));
+               Fb(tl, 1, "match_acl_%.*s(sp, %s)\n", PF(tl->t), vp->rname);
                vcc_NextToken(tl);
                break;
        case T_EQ:
        case T_NEQ:
+
+               VTAILQ_INIT(&tl->acl);
                tcond = tl->t->tok;
                vcc_NextToken(tl);
                asprintf(&acln, "acl_%u", tl->cnt);
                assert(acln != NULL);
-               vcc_acl_top(tl, acln);
                vcc_acl_entry(tl);
-               vcc_acl_bot(tl, acln);
-               Fb(tl, 1, "%sVRT_acl_match(sp, %s, 0, acl_%s)\n",
-                   (tcond == T_NEQ ? "!" : ""), vp->rname, acln);
+               vcc_acl_bot(tl, acln, 1);
+               Fb(tl, 1, "%smatch_acl_%s(sp, %s)\n",
+                   (tcond == T_NEQ ? "!" : ""), acln, vp->rname);
                free(acln);
                break;
        default:
@@ -155,6 +455,7 @@ vcc_Acl(struct tokenlist *tl)
        char *acln;
 
        vcc_NextToken(tl);
+       VTAILQ_INIT(&tl->acl);
 
        ExpectErr(tl, ID);
        an = tl->t;
@@ -164,8 +465,6 @@ vcc_Acl(struct tokenlist *tl)
        asprintf(&acln, "%.*s", PF(an));
        assert(acln != NULL);
 
-       vcc_acl_top(tl, acln);
-
        ExpectErr(tl, '{');
        vcc_NextToken(tl);
 
@@ -178,7 +477,7 @@ vcc_Acl(struct tokenlist *tl)
        ExpectErr(tl, '}');
        vcc_NextToken(tl);
 
-       vcc_acl_bot(tl, acln);
+       vcc_acl_bot(tl, acln, 0);
 
        free(acln);
 }
index 05a83c47fa9c02d29dc1b16d9aab9b7bce183da3..696bea062b6c0bbc492fff0bd3b8379440b6bf58 100644 (file)
@@ -35,6 +35,8 @@
 
 #define INDENT         2
 
+struct acl_e;
+
 struct membit {
        VTAILQ_ENTRY(membit)    list;
        void                    *ptr;
@@ -85,6 +87,8 @@ struct tokenlist {
        struct proc             *curproc;
        struct proc             *mprocs[N_METHODS];
 
+       VTAILQ_HEAD(, acl_e)    acl;
+
        unsigned                recnt;
        unsigned                nhashcount;
        unsigned                nsockaddr;
index 14634afc3db9d318d3caf0ad5969fadfaf03be28..52c289fdd3004e6c3015cc09863beb50807ef3b3 100644 (file)
@@ -401,19 +401,10 @@ vcl_output_lang_h(struct vsb *sb)
        vsb_cat(sb, "   const char      *token;\n");
        vsb_cat(sb, "};\n");
        vsb_cat(sb, "\n");
-       vsb_cat(sb, "struct vrt_acl {\n");
-       vsb_cat(sb, "   unsigned char   not;\n");
-       vsb_cat(sb, "   unsigned char   mask;\n");
-       vsb_cat(sb, "   unsigned char   paren;\n");
-       vsb_cat(sb, "   const char      *name;\n");
-       vsb_cat(sb, "   const char      *desc;\n");
-       vsb_cat(sb, "   void            *priv;\n");
-       vsb_cat(sb, "};\n");
-       vsb_cat(sb, "\n");
        vsb_cat(sb, "/* ACL related */\n");
-       vsb_cat(sb, "int VRT_acl_match(const struct sess *, struct sockaddr *, const char *, const struct vrt_acl *);\n");
-       vsb_cat(sb, "void VRT_acl_init(struct vrt_acl *);\n");
-       vsb_cat(sb, "void VRT_acl_fini(struct vrt_acl *);\n");
+       vsb_cat(sb, "#define VRT_ACL_MAXADDR            16      /* max(IPv4, IPv6) */\n");
+       vsb_cat(sb, "\n");
+       vsb_cat(sb, "void VRT_acl_log(const struct sess *, const char *msg);\n");
        vsb_cat(sb, "\n");
        vsb_cat(sb, "/* Regexp related */\n");
        vsb_cat(sb, "void VRT_re_init(void **, const char *, int sub);\n");