Commit 97178f9b authored by Poul-Henning Kamp's avatar Poul-Henning Kamp

ACL code rewrite:

	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: http://www.varnish-cache.org/svn/trunk/varnish-cache@2995 d4fa192b-c00b-0410-8231-f00ffab90ce4
parent dde15a6d
......@@ -30,139 +30,16 @@
*
* 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);
}
......@@ -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
......
......@@ -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
......
......@@ -33,7 +33,7 @@ client c1 -run
varnish v1 -vcl+backend {
acl acl1 {
"!127.0.0.1";
! "127.0.0.1";
}
sub vcl_recv {
......
......@@ -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);
......
......@@ -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
......
This diff is collapsed.
......@@ -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;
......
......@@ -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");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment