MINOR: tcp: add dst_is_local and src_is_local
It is sometimes needed in application server environments to easily tell if a source is local to the machine or a remote one, without necessarily knowing all the local addresses (dhcp, vrrp, etc). Similarly in transparent proxy configurations it is sometimes desired to tell the difference between local and remote destination addresses. This patch adds two new sample fetch functions for this : dst_is_local : boolean Returns true if the destination address of the incoming connection is local to the system, or false if the address doesn't exist on the system, meaning that it was intercepted in transparent mode. It can be useful to apply certain rules by default to forwarded traffic and other rules to the traffic targetting the real address of the machine. For example the stats page could be delivered only on this address, or SSH access could be locally redirected. Please note that the check involves a few system calls, so it's better to do it only once per connection. src_is_local : boolean Returns true if the source address of the incoming connection is local to the system, or false if the address doesn't exist on the system, meaning that it comes from a remote machine. Note that UNIX addresses are considered local. It can be useful to apply certain access restrictions based on where the client comes from (eg: require auth or https for remote machines). Please note that the check involves a few system calls, so it's better to do it only once per connection.
This commit is contained in:
parent
f0645dce4f
commit
16e015635c
|
@ -12733,6 +12733,16 @@ dst_conn : integer
|
|||
different limits to different listening ports or addresses. See also the
|
||||
"fe_conn" and "be_conn" fetches.
|
||||
|
||||
dst_is_local : boolean
|
||||
Returns true if the destination address of the incoming connection is local
|
||||
to the system, or false if the address doesn't exist on the system, meaning
|
||||
that it was intercepted in transparent mode. It can be useful to apply
|
||||
certain rules by default to forwarded traffic and other rules to the traffic
|
||||
targetting the real address of the machine. For example the stats page could
|
||||
be delivered only on this address, or SSH access could be locally redirected.
|
||||
Please note that the check involves a few system calls, so it's better to do
|
||||
it only once per connection.
|
||||
|
||||
dst_port : integer
|
||||
Returns an integer value corresponding to the destination TCP port of the
|
||||
connection on the client side, which is the port the client connected to.
|
||||
|
@ -13076,6 +13086,15 @@ src_inc_gpc0([<table>]) : integer
|
|||
acl kill src_inc_gpc0 gt 0
|
||||
tcp-request connection reject if abuse kill
|
||||
|
||||
src_is_local : boolean
|
||||
Returns true if the source address of the incoming connection is local to the
|
||||
system, or false if the address doesn't exist on the system, meaning that it
|
||||
comes from a remote machine. Note that UNIX addresses are considered local.
|
||||
It can be useful to apply certain access restrictions based on where the
|
||||
client comes from (eg: require auth or https for remote machines). Please
|
||||
note that the check involves a few system calls, so it's better to do it only
|
||||
once per connection.
|
||||
|
||||
src_kbytes_in([<table>]) : integer
|
||||
Returns the total amount of data received from the incoming connection's
|
||||
source address in the current proxy's stick-table or in the designated
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <arpa/inet.h>
|
||||
#include <common/chunk.h>
|
||||
#include <common/config.h>
|
||||
#include <common/namespace.h>
|
||||
#include <eb32tree.h>
|
||||
|
||||
#ifndef LLONG_MAX
|
||||
|
@ -358,6 +359,17 @@ int addr_to_str(struct sockaddr_storage *addr, char *str, int size);
|
|||
*/
|
||||
int port_to_str(struct sockaddr_storage *addr, char *str, int size);
|
||||
|
||||
/* check if the given address is local to the system or not. It will return
|
||||
* -1 when it's not possible to know, 0 when the address is not local, 1 when
|
||||
* it is. We don't want to iterate over all interfaces for this (and it is not
|
||||
* portable). So instead we try to bind in UDP to this address on a free non
|
||||
* privileged port and to connect to the same address, port 0 (connect doesn't
|
||||
* care). If it succeeds, we own the address. Note that non-inet addresses are
|
||||
* considered local since they're most likely AF_UNIX.
|
||||
*/
|
||||
int addr_is_local(const struct netns_entry *ns,
|
||||
const struct sockaddr_storage *orig);
|
||||
|
||||
/* will try to encode the string <string> replacing all characters tagged in
|
||||
* <map> with the hexadecimal representation of their ASCII-code (2 digits)
|
||||
* prefixed by <escape>, and will store the result between <start> (included)
|
||||
|
|
|
@ -2299,6 +2299,48 @@ smp_fetch_dst(const struct arg *args, struct sample *smp, const char *kw, void *
|
|||
return 1;
|
||||
}
|
||||
|
||||
/* check if the destination address of the front connection is local to the
|
||||
* system or if it was intercepted.
|
||||
*/
|
||||
int smp_fetch_dst_is_local(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
||||
{
|
||||
struct connection *conn = objt_conn(smp->sess->origin);
|
||||
struct listener *li = smp->sess->listener;
|
||||
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
conn_get_to_addr(conn);
|
||||
if (!(conn->flags & CO_FL_ADDR_TO_SET))
|
||||
return 0;
|
||||
|
||||
smp->data.type = SMP_T_BOOL;
|
||||
smp->flags = 0;
|
||||
smp->data.u.sint = addr_is_local(li->netns, &conn->addr.to);
|
||||
return smp->data.u.sint >= 0;
|
||||
}
|
||||
|
||||
/* check if the source address of the front connection is local to the system
|
||||
* or not.
|
||||
*/
|
||||
int smp_fetch_src_is_local(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
||||
{
|
||||
struct connection *conn = objt_conn(smp->sess->origin);
|
||||
struct listener *li = smp->sess->listener;
|
||||
|
||||
if (!conn)
|
||||
return 0;
|
||||
|
||||
conn_get_from_addr(conn);
|
||||
if (!(conn->flags & CO_FL_ADDR_FROM_SET))
|
||||
return 0;
|
||||
|
||||
smp->data.type = SMP_T_BOOL;
|
||||
smp->flags = 0;
|
||||
smp->data.u.sint = addr_is_local(li->netns, &conn->addr.from);
|
||||
return smp->data.u.sint >= 0;
|
||||
}
|
||||
|
||||
/* set temp integer to the frontend connexion's destination port */
|
||||
static int
|
||||
smp_fetch_dport(const struct arg *args, struct sample *smp, const char *kw, void *private)
|
||||
|
@ -2620,8 +2662,10 @@ static struct acl_kw_list acl_kws = {ILH, {
|
|||
*/
|
||||
static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
|
||||
{ "dst", smp_fetch_dst, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
|
||||
{ "dst_is_local", smp_fetch_dst_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
|
||||
{ "dst_port", smp_fetch_dport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
|
||||
{ "src", smp_fetch_src, 0, NULL, SMP_T_IPV4, SMP_USE_L4CLI },
|
||||
{ "src_is_local", smp_fetch_src_is_local, 0, NULL, SMP_T_BOOL, SMP_USE_L4CLI },
|
||||
{ "src_port", smp_fetch_sport, 0, NULL, SMP_T_SINT, SMP_USE_L4CLI },
|
||||
#ifdef TCP_INFO
|
||||
{ "fc_rtt", smp_fetch_fc_rtt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_L4CLI },
|
||||
|
|
|
@ -11,12 +11,14 @@
|
|||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
|
@ -1406,6 +1408,47 @@ int port_to_str(struct sockaddr_storage *addr, char *str, int size)
|
|||
return addr->ss_family;
|
||||
}
|
||||
|
||||
/* check if the given address is local to the system or not. It will return
|
||||
* -1 when it's not possible to know, 0 when the address is not local, 1 when
|
||||
* it is. We don't want to iterate over all interfaces for this (and it is not
|
||||
* portable). So instead we try to bind in UDP to this address on a free non
|
||||
* privileged port and to connect to the same address, port 0 (connect doesn't
|
||||
* care). If it succeeds, we own the address. Note that non-inet addresses are
|
||||
* considered local since they're most likely AF_UNIX.
|
||||
*/
|
||||
int addr_is_local(const struct netns_entry *ns,
|
||||
const struct sockaddr_storage *orig)
|
||||
{
|
||||
struct sockaddr_storage addr;
|
||||
int result;
|
||||
int fd;
|
||||
|
||||
if (!is_inet_addr(orig))
|
||||
return 1;
|
||||
|
||||
memcpy(&addr, orig, sizeof(addr));
|
||||
set_host_port(&addr, 0);
|
||||
|
||||
fd = my_socketat(ns, addr.ss_family, SOCK_DGRAM, IPPROTO_UDP);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
result = -1;
|
||||
if (bind(fd, (struct sockaddr *)&addr, get_addr_len(&addr)) == 0) {
|
||||
if (connect(fd, (struct sockaddr *)&addr, get_addr_len(&addr)) == -1)
|
||||
result = 0; // fail, non-local address
|
||||
else
|
||||
result = 1; // success, local address
|
||||
}
|
||||
else {
|
||||
if (errno == EADDRNOTAVAIL)
|
||||
result = 0; // definitely not local :-)
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* will try to encode the string <string> replacing all characters tagged in
|
||||
* <map> with the hexadecimal representation of their ASCII-code (2 digits)
|
||||
* prefixed by <escape>, and will store the result between <start> (included)
|
||||
|
|
Loading…
Reference in New Issue