Mark, 2nd Oct 2010
The Varnish VCL match operator (~) will currently only match an ACL (access control list) to an IP address data type. Since there's no way to cast a string to an IP in VCL (although it is planned), you can't match a forwarded-for header against an ACL without using inline C. You can only match client.ip, which can be a problem depending on the cluster configuration.
The following nasty hack works by using VCL's inline C functionality to do the cast. Varnish compiles the ACL into a C function that you can call directly: match_acl_named_paybarrier_bypass(). If there is a match, it sets a header for the backend.
It's useful to play around with the command varnishd -C which prints out the compiled VCL code.
<?php
C{
#include <netinet/in.h>
#include <string.h>
}C
// We use inline C to do the equivalent of this string to IP cast:
// if (IP(req.http.X-Forwarded-For) ~ paybarrier_bypass)
// This syntax should be supported in Varnish 3.0.
C{
struct sockaddr xff_ip;
// Make a complete copy of client.ip.
xff_ip = *(VRT_r_client_ip(sp));
// Need to cast a pointer to sockaddr_in which is the struct required by
// inet_pton.
struct sockaddr_in *xff_ip_in = (struct sockaddr_in *) &xff_ip;
// X-Real-Forwarded-For may contain two IP addresses, so grab the first.
char *rxff_ip_str = strtok(VRT_GetHdr(sp, HDR_REQ, "\025X-Real-Forwarded-For:"), ",");
if (rxff_ip_str == NULL)
rxff_ip_str = VRT_GetHdr(sp, HDR_REQ, "\025X-Real-Forwarded-For:");
// Copy the ip address into the struct's sin_addr.
inet_pton(AF_INET, rxff_ip_str, &((*xff_ip_in).sin_addr));
// Run the match.
if (match_acl_named_paybarrier_bypass(sp, &(xff_ip))) {
VRT_SetHdr(sp, HDR_REQ, "\025X-Our-Header:", "X", vrt_magic_string_end);
VRT_done(sp, VCL_RET_PASS);
}
}C
?>











I would love to run this
I would love to run this piece of code, but I cannot seem to make it work.
Do you have any tips on how to run it?
Thanks!
This is what I get when
This is what I get when trying to compile:
Compiled VCL program failed to load:
./vcl.1P9zoqAU.so: undefined symbol: match_acl_named_paybarrier_bypass
I even made an ACL named paybarrier_bypass, but then I get this:
Unused acl paybarrier_bypass, defined:
If I "use" that ACL in the VCL, it compiles, but then Varnish does not run or work properly at all. It's very weird.
I've tried many different variations of this code, but cannot find anything that works. If you have used this code, and know how to make it work, please provide an example. I will be most grateful!
Thanks!
Not sure
Yeah you do have to "use" it, I remember that. What error are you getting when you do use it but Varnish won't start? Check the logs or stdout.
./vcl.8Dgf84IR.c: In function
./vcl.8Dgf84IR.c: In function ‘VGC_function_vcl_recv’:
./vcl.8Dgf84IR.c:2510: error: incompatible types in assignment
./vcl.8Dgf84IR.c:2522: warning: implicit declaration of function ‘inet_pton’
Line 2510 is xff_ip = *(VRT_r_client_ip(sp));
Maybe this doesn't work in Varnish 3? To get this far, I put the two includes above all subs, and then I put the rest of the code into vcl_recv. I changed the name of the acl to match what's in the C code. When I compile I get the error above. I'm not extremely good at C, but my hunch is that the type of VRT_r_client_ip() has changed from 2.x to 3.x. I wish I could find a working example of this. :)
Something that works.
C{
#include
#include
#include
#include
}C
acl foo {
"127.0.0.0"/8;
}
sub vcl_recv {
C{
//
// This is a hack from Igor Gariev (gariev hotmail com):
// Copy IP address from "X-Forwarded-For" header
// into Varnish's client_ip structure.
// This works with Varnish 3.0.1; test with other versions
//
// Trusted "X-Forwarded-For" header is a must!
// No commas are allowed. If your load balancer something other
// than a single IP, then use a regsub() to fix it.
//
struct sockaddr_storage *client_ip_ss = VRT_r_client_ip(sp);
struct sockaddr_in *client_ip_si = (struct sockaddr_in *) client_ip_ss;
struct in_addr *client_ip_ia = &(client_ip_si->sin_addr);
char *xff_ip = VRT_GetHdr(sp, HDR_REQ, "\020X-Forwarded-For:");
if (xff_ip != NULL) {
// Copy the ip address into the struct's sin_addr.
inet_pton(AF_INET, xff_ip, client_ip_ia);
}
}C
if (client.ip ~ foo) {
set req.http.Foo-Match = "yes";
} else {
set req.http.Foo-Match = "no";
}
}
woops
The includes that got stripped out are
C{
#include netinet/in.h
#include string.h
#include sys/socket.h
#include arpa/inet.h
}C