On 12/10/20 1:31 AM, Frederic Lecaille wrote:
>
> It would be preferable to send all your patches, so that others than me 
> may review your work (no diff between different versions of patches) and 
> continue to split your work in several patches.
> 

Ok, here is what I have so far as two patches (I combined feedback into the 
original commit):


>From cf965f47e04776ca20d2ee6ed22028741493824c Mon Sep 17 00:00:00 2001
From: Thayne McCombs <tha...@lucidchart.com>
Date: Fri, 20 Nov 2020 01:28:26 -0700
Subject: [PATCH 1/2] Add srvkey option to stick-table

This allows using the address of the server rather than the name of the
server for keeping track of servers in a backend for stickiness.

Fixes #814
---
 doc/configuration.txt           | 12 ++++++++-
 include/haproxy/dict.h          |  1 +
 include/haproxy/proxy-t.h       |  1 +
 include/haproxy/server-t.h      |  1 +
 include/haproxy/server.h        |  2 +-
 include/haproxy/stick_table-t.h | 11 ++++++--
 include/haproxy/tools.h         | 13 +++++++++
 src/cfgparse-listen.c           |  1 +
 src/cfgparse.c                  |  4 +--
 src/dict.c                      | 24 ++++++++++++++++-
 src/peers.c                     |  9 +++++--
 src/server.c                    | 40 ++++++++++++++++++++++++++--
 src/stick_table.c               | 31 +++++++++++++++++++++-
 src/stream.c                    | 47 +++++++++++++++++++++++----------
 src/tools.c                     | 45 +++++++++++++++++++++++++++++++
 15 files changed, 216 insertions(+), 26 deletions(-)

diff --git a/doc/configuration.txt b/doc/configuration.txt
index e60e3428d..e17061518 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -10649,7 +10649,7 @@ stick store-request <pattern> [table <table>] [{if | 
unless} <condition>]
 
 
 stick-table type {ip | integer | string [len <length>] | binary [len <length>]}
-            size <size> [expire <expire>] [nopurge] [peers <peersect>]
+            size <size> [expire <expire>] [nopurge] [peers <peersect>] [srvkey 
<srvkey>]
             [store <data_type>]*
   Configure the stickiness table for the current section
   May be used in sections :   defaults | frontend | listen | backend
@@ -10726,6 +10726,16 @@ stick-table type {ip | integer | string [len <length>] 
| binary [len <length>]}
                be removed once full. Be sure not to use the "nopurge" parameter
                if not expiration delay is specified.
 
+    <srvkey>   specifies how each server is identified for the purposes of the
+               stick table. The valid values are "name" and "addr". If "name" 
is
+               given, then <name> argument for the server (may be generated by
+               a template). If "addr" is given, then the server is identified
+               by its current network address, including the port. "addr" is
+               especially useful if you are using service discovery to generate
+               the addresses for servers with peered stick-tables and want
+               to consistently use the same host across peers for a stickiness
+               token.
+
    <data_type> is used to store additional information in the stick-table. This
                may be used by ACLs in order to control various criteria related
                to the activity of the client matching the stick-table. For each
diff --git a/include/haproxy/dict.h b/include/haproxy/dict.h
index 59e81352c..c55834ca5 100644
--- a/include/haproxy/dict.h
+++ b/include/haproxy/dict.h
@@ -31,5 +31,6 @@
 
 struct dict *new_dict(const char *name);
 struct dict_entry *dict_insert(struct dict *d, char *str);
+void dict_entry_unref(struct dict *d, struct dict_entry *de);
 
 #endif  /* _HAPROXY_DICT_H */
diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h
index 998e210f6..e62b79765 100644
--- a/include/haproxy/proxy-t.h
+++ b/include/haproxy/proxy-t.h
@@ -424,6 +424,7 @@ struct proxy {
                char *lfsd_file;                /* file name where the 
structured-data logformat string for RFC5424 appears (strdup) */
                int  lfsd_line;                 /* file name where the 
structured-data logformat string for RFC5424 appears */
        } conf;                                 /* config information */
+       struct eb_root used_server_addr;        /* list of server addresses in 
use */
        void *parent;                           /* parent of the proxy when 
applicable */
        struct comp *comp;                      /* http compression */
 
diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h
index 0e66be693..13f5a5dab 100644
--- a/include/haproxy/server-t.h
+++ b/include/haproxy/server-t.h
@@ -337,6 +337,7 @@ struct server {
                struct ebpt_node name;          /* place in the tree of used 
names */
                int line;                       /* line where the section 
appears */
        } conf;                                 /* config information */
+       struct ebpt_node addr_node;             /* Node for string 
representation of address for the server (including port number) */
        /* Template information used only for server objects which
         * serve as template filled at parsing time and used during
         * server allocations from server templates.
diff --git a/include/haproxy/server.h b/include/haproxy/server.h
index d63eb01bc..00036b1a1 100644
--- a/include/haproxy/server.h
+++ b/include/haproxy/server.h
@@ -38,7 +38,7 @@
 __decl_thread(extern HA_SPINLOCK_T idle_conn_srv_lock);
 extern struct eb_root idle_conn_srv;
 extern struct task *idle_conn_task;
-extern struct dict server_name_dict;
+extern struct dict server_key_dict;
 
 int srv_downtime(const struct server *s);
 int srv_lastsession(const struct server *s);
diff --git a/include/haproxy/stick_table-t.h b/include/haproxy/stick_table-t.h
index 59aadea22..2cb4a1b8f 100644
--- a/include/haproxy/stick_table-t.h
+++ b/include/haproxy/stick_table-t.h
@@ -56,7 +56,7 @@ enum {
        STKTABLE_DT_BYTES_OUT_RATE,/* bytes rate from servers to client */
        STKTABLE_DT_GPC1,         /* General Purpose Counter 1 (unsigned 32-bit 
integer) */
        STKTABLE_DT_GPC1_RATE,    /* General Purpose Counter 1's event rate */
-       STKTABLE_DT_SERVER_NAME,  /* The server name */
+       STKTABLE_DT_SERVER_KEY,   /* The server key */
        STKTABLE_STATIC_DATA_TYPES,/* number of types above */
        /* up to STKTABLE_EXTRA_DATA_TYPES types may be registered here, always
         * followed by the number of data types, must always be last.
@@ -80,6 +80,12 @@ enum {
        ARG_T_DELAY,              /* a delay which supports time units */
 };
 
+/* They types of keys that servers can be identified by */
+enum {
+       STKTABLE_SRV_NAME = 0,
+       STKTABLE_SRV_ADDR,
+};
+
 /* stick table key type flags */
 #define STK_F_CUSTOM_KEYSIZE      0x00000001   /* this table's key size is 
configurable */
 
@@ -112,7 +118,7 @@ union stktable_data {
 
        /* types of each storable data */
        int server_id;
-       struct dict_entry *server_name;
+       struct dict_entry *server_key;
        unsigned int gpt0;
        unsigned int gpc0;
        struct freq_ctr_period gpc0_rate;
@@ -188,6 +194,7 @@ struct stktable {
        } peers;
 
        unsigned long type;       /* type of table (determines key format) */
+       unsigned int server_key_type; /* What type of key is used to identify 
servers */
        size_t key_size;          /* size of a key, maximum size in case of 
string */
        unsigned int size;        /* maximum number of sticky sessions in table 
*/
        unsigned int current;     /* number of sticky sessions currently in 
table */
diff --git a/include/haproxy/tools.h b/include/haproxy/tools.h
index 4080d7a5d..651c0701c 100644
--- a/include/haproxy/tools.h
+++ b/include/haproxy/tools.h
@@ -245,6 +245,19 @@ struct sockaddr_storage *str2sa_range(const char *str, int 
*port, int *low, int
                                       struct protocol **proto, char **err,
                                       const char *pfx, char **fqdn, unsigned 
int opts);
 
+
+/* converts <addr> and <port> into a string representation of the address and 
port. This is sort
+ * of an inverse of str2sa_range, with some restrictions. The supported 
families are AF_INET,
+ * AF_INET6, AF_UNIX, and AF_CUST_SOCKPAIR. If the family is unsopported NULL 
is returned.
+ * If map_ports is true, then the sign of the port is included in the output, 
to indicate it is
+ * relative to the incoming port. AF_INET and AF_INET6 will be in the form 
"<addr>:<port>".
+ * AF_UNIX will either be just the path (if using a pathname) or "abns@<path>" 
if it is abstract.
+ * AF_CUST_SOCKPAIR will be of the form "sockpair@<fd>".
+ *
+ * The returned char* is allocated, and it is the responsibility of the caller 
to free it.
+ */
+char *sa2str(const struct sockaddr_storage *addr, int port, int map_ports);
+
 /* converts <str> to a struct in_addr containing a network mask. It can be
  * passed in dotted form (255.255.255.0) or in CIDR form (24). It returns 1
  * if the conversion succeeds otherwise zero.
diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c
index 97a97e746..a493e741c 100644
--- a/src/cfgparse-listen.c
+++ b/src/cfgparse-listen.c
@@ -457,6 +457,7 @@ int cfg_parse_listen(const char *file, int linenum, char 
**args, int kwm)
                curproxy->grace  = defproxy.grace;
                curproxy->conf.used_listener_id = EB_ROOT;
                curproxy->conf.used_server_id = EB_ROOT;
+               curproxy->used_server_addr = EB_ROOT_UNIQUE;
 
                if (defproxy.check_path)
                        curproxy->check_path = strdup(defproxy.check_path);
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 957ae9e61..51e76c021 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -2666,7 +2666,7 @@ int check_config_validity()
                                free((void *)mrule->table.name);
                                mrule->table.t = target;
                                stktable_alloc_data_type(target, 
STKTABLE_DT_SERVER_ID, NULL);
-                               stktable_alloc_data_type(target, 
STKTABLE_DT_SERVER_NAME, NULL);
+                               stktable_alloc_data_type(target, 
STKTABLE_DT_SERVER_KEY, NULL);
                                if (!in_proxies_list(target->proxies_list, 
curproxy)) {
                                        curproxy->next_stkt_ref = 
target->proxies_list;
                                        target->proxies_list = curproxy;
@@ -2704,7 +2704,7 @@ int check_config_validity()
                                free((void *)mrule->table.name);
                                mrule->table.t = target;
                                stktable_alloc_data_type(target, 
STKTABLE_DT_SERVER_ID, NULL);
-                               stktable_alloc_data_type(target, 
STKTABLE_DT_SERVER_NAME, NULL);
+                               stktable_alloc_data_type(target, 
STKTABLE_DT_SERVER_KEY, NULL);
                                if (!in_proxies_list(target->proxies_list, 
curproxy)) {
                                        curproxy->next_stkt_ref = 
target->proxies_list;
                                        target->proxies_list = curproxy;
diff --git a/src/dict.c b/src/dict.c
index 903f07323..9b3536d96 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -87,8 +87,10 @@ struct dict_entry *dict_insert(struct dict *d, char *s)
        HA_RWLOCK_RDLOCK(DICT_LOCK, &d->rwlock);
        de = __dict_lookup(d, s);
        HA_RWLOCK_RDUNLOCK(DICT_LOCK, &d->rwlock);
-       if (de)
+       if (de) {
+               HA_ATOMIC_ADD(&de->refcount, 1);
                return de;
+       }
 
        de = new_dict_entry(s);
        if (!de)
@@ -105,3 +107,23 @@ struct dict_entry *dict_insert(struct dict *d, char *s)
        return de;
 }
 
+
+/*
+ * Unreference a dict entry previously acquired with <dict_insert>.
+ * If this is the last live reference to the entry, it is
+ * removed from the dictionary.
+ */
+void dict_entry_unref(struct dict *d, struct dict_entry *de)
+{
+       if (!de)
+               return;
+
+       if (HA_ATOMIC_SUB(&de->refcount, 1) != 0)
+               return;
+
+       HA_RWLOCK_WRLOCK(DICT_LOCK, &d->rwlock);
+       ebpt_delete(&de->value);
+       HA_RWLOCK_WRUNLOCK(DICT_LOCK, &d->rwlock);
+
+       free_dict_entry(de);
+}
diff --git a/src/peers.c b/src/peers.c
index b5c1d429f..3fa1a28b4 100644
--- a/src/peers.c
+++ b/src/peers.c
@@ -1633,13 +1633,16 @@ static int peer_treat_updatemsg(struct appctx *appctx, 
struct peer *p, int updt,
                                chunk->area[chunk->data] = '\0';
                                *msg_cur += value_len;
 
-                               de = dict_insert(&server_name_dict, 
chunk->area);
+                               de = dict_insert(&server_key_dict, chunk->area);
+                               dict_entry_unref(&server_key_dict, dc->rx[id - 
1].de);
                                dc->rx[id - 1].de = de;
                        }
                        if (de) {
                                data_ptr = stktable_data_ptr(st->table, ts, 
data_type);
-                               if (data_ptr)
+                               if (data_ptr) {
+                                       HA_ATOMIC_ADD(&de->refcount, 1);
                                        stktable_data_cast(data_ptr, 
std_t_dict) = de;
+                               }
                        }
                        break;
                }
@@ -3059,6 +3062,8 @@ static inline void flush_dcache(struct peer *peer)
        for (i = 0; i < dc->max_entries; i++) {
                ebpt_delete(&dc->tx->entries[i]);
                dc->tx->entries[i].key = NULL;
+               dict_entry_unref(&server_key_dict, dc->rx[i].de);
+               dc->rx[i].de = NULL;
        }
        dc->tx->prev_lookup = NULL;
        dc->tx->lru_key = 0;
diff --git a/src/server.c b/src/server.c
index 3a3ccc672..ef536d718 100644
--- a/src/server.c
+++ b/src/server.c
@@ -65,8 +65,8 @@ struct eb_root idle_conn_srv = EB_ROOT;
 struct task *idle_conn_task = NULL;
 
 /* The server names dictionary */
-struct dict server_name_dict = {
-       .name = "server names",
+struct dict server_key_dict = {
+       .name = "server keys",
        .values = EB_ROOT_UNIQUE,
 };
 
@@ -193,6 +193,36 @@ void srv_set_dyncookie(struct server *s)
        HA_RWLOCK_RDUNLOCK(PROXY_LOCK, &p->lock);
 }
 
+/*
+ * Must be called with the server lock held, and will write-lock the proxy.
+ */
+static void srv_set_addr_desc(struct server *s)
+{
+       struct proxy *p = s->proxy;
+       char *key;
+
+       key = sa2str(&s->addr, s->svc_port, s->flags & SRV_F_MAPPORTS);
+
+       if (s->addr_node.key) {
+               if (strcmp(key, s->addr_node.key) == 0) {
+                       free(key);
+                       return;
+               }
+
+               HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock);
+               ebpt_delete(&s->addr_node);
+               HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock);
+
+               free(s->addr_node.key);
+       }
+
+       s->addr_node.key = key;
+
+       HA_RWLOCK_WRLOCK(PROXY_LOCK, &p->lock);
+       ebis_insert(&p->used_server_addr, &s->addr_node);
+       HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &p->lock);
+}
+
 /*
  * Registers the server keyword list <kwl> as a list of valid keywords for next
  * parsing sessions.
@@ -2052,6 +2082,9 @@ int parse_server(const char *file, int linenum, char 
**args, struct proxy *curpr
 
                        newsrv->addr = *sk;
                        newsrv->svc_port = port;
+                       // we don't need to lock the server here, because
+                       // we are in the process of initializing
+                       srv_set_addr_desc(newsrv);
 
                        if (!newsrv->srvrq && !newsrv->hostname && 
!protocol_by_family(newsrv->addr.ss_family)) {
                                ha_alert("parsing [%s:%d] : Unknown protocol 
family %d '%s'\n",
@@ -3519,6 +3552,7 @@ int update_server_addr(struct server *s, void *ip, int 
ip_sin_family, const char
                break;
        };
        srv_set_dyncookie(s);
+       srv_set_addr_desc(s);
 
        return 0;
 }
@@ -3691,6 +3725,7 @@ out:
                /* force connection cleanup on the given server */
                srv_cleanup_connections(s);
                srv_set_dyncookie(s);
+               srv_set_addr_desc(s);
        }
        if (updater)
                chunk_appendf(msg, " by '%s'", updater);
@@ -4171,6 +4206,7 @@ static int srv_iterate_initaddr(struct server *srv)
        return return_code;
 out:
        srv_set_dyncookie(srv);
+       srv_set_addr_desc(srv);
        return return_code;
 }
 
diff --git a/src/stick_table.c b/src/stick_table.c
index 505916421..825005c2e 100644
--- a/src/stick_table.c
+++ b/src/stick_table.c
@@ -22,6 +22,7 @@
 #include <haproxy/arg.h>
 #include <haproxy/cfgparse.h>
 #include <haproxy/cli.h>
+#include <haproxy/dict.h>
 #include <haproxy/errors.h>
 #include <haproxy/global.h>
 #include <haproxy/http_rules.h>
@@ -94,6 +95,12 @@ void __stksess_free(struct stktable *t, struct stksess *ts)
  */
 void stksess_free(struct stktable *t, struct stksess *ts)
 {
+       void *data;
+       data = stktable_data_ptr(t, ts, STKTABLE_DT_SERVER_KEY);
+       if (data) {
+               dict_entry_unref(&server_key_dict, stktable_data_cast(data, 
server_key));
+               stktable_data_cast(data, server_key) = NULL;
+       }
        HA_SPIN_LOCK(STK_TABLE_LOCK, &t->lock);
        __stksess_free(t, ts);
        HA_SPIN_UNLOCK(STK_TABLE_LOCK, &t->lock);
@@ -877,6 +884,25 @@ int parse_stick_table(const char *file, int linenum, char 
**args,
                        }
                        idx++;
                }
+               else if (strcmp(args[idx], "srvkey") == 0) {
+                       char *keytype;
+                       idx++;
+                       keytype = args[idx];
+                       if (strcmp(keytype, "name") == 0) {
+                               t->server_key_type = STKTABLE_SRV_NAME;
+                       }
+                       else if (strcmp(keytype, "addr") == 0) {
+                               t->server_key_type = STKTABLE_SRV_ADDR;
+                       }
+                       else {
+                               ha_alert("parsing [%s:%d] : %s : unknown server 
key type '%s'.\n",
+                                               file, linenum, args[0], 
keytype);
+                               err_code |= ERR_ALERT | ERR_FATAL;
+                               goto out;
+
+                       }
+                       idx++;
+               }
                else {
                        ha_alert("parsing [%s:%d] : %s: unknown argument 
'%s'.\n",
                                 file, linenum, args[0], args[idx]);
@@ -1048,7 +1074,7 @@ struct stktable_data_type 
stktable_data_types[STKTABLE_DATA_TYPES] = {
        [STKTABLE_DT_BYTES_OUT_RATE]= { .name = "bytes_out_rate", .std_type = 
STD_T_FRQP, .arg_type = ARG_T_DELAY },
        [STKTABLE_DT_GPC1]          = { .name = "gpc1",           .std_type = 
STD_T_UINT  },
        [STKTABLE_DT_GPC1_RATE]     = { .name = "gpc1_rate",      .std_type = 
STD_T_FRQP, .arg_type = ARG_T_DELAY  },
-       [STKTABLE_DT_SERVER_NAME]   = { .name = "server_name",    .std_type = 
STD_T_DICT  },
+       [STKTABLE_DT_SERVER_KEY]    = { .name = "server_key",     .std_type = 
STD_T_DICT  },
 };
 
 /* Registers stick-table extra data type with index <idx>, name <name>, type
@@ -1095,6 +1121,9 @@ int stktable_get_data_type(char *name)
                if (strcmp(name, stktable_data_types[type].name) == 0)
                        return type;
        }
+       /* For backwards compatibility */
+       if (strcmp(name, "server_name") == 0)
+               return STKTABLE_DT_SERVER_KEY;
        return -1;
 }
 
diff --git a/src/stream.c b/src/stream.c
index 6ca1538e8..e24ea49fc 100644
--- a/src/stream.c
+++ b/src/stream.c
@@ -1202,17 +1202,27 @@ static inline void sticking_rule_find_target(struct 
stream *s,
 
        /* Look for the server name previously stored in <t> stick-table */
        HA_RWLOCK_RDLOCK(STK_SESS_LOCK, &ts->lock);
-       ptr = __stktable_data_ptr(t, ts, STKTABLE_DT_SERVER_NAME);
-       de = stktable_data_cast(ptr, server_name);
+       ptr = __stktable_data_ptr(t, ts, STKTABLE_DT_SERVER_KEY);
+       de = stktable_data_cast(ptr, server_key);
        HA_RWLOCK_RDUNLOCK(STK_SESS_LOCK, &ts->lock);
 
        if (de) {
-               struct ebpt_node *name;
+               struct ebpt_node *node;
 
-               name = ebis_lookup(&px->conf.used_server_name, de->value.key);
-               if (name) {
-                       srv = container_of(name, struct server, conf.name);
-                       goto found;
+               if (t->server_key_type == STKTABLE_SRV_NAME) {
+                       node = ebis_lookup(&px->conf.used_server_name, 
de->value.key);
+                       if (node) {
+                               srv = container_of(node, struct server, 
conf.name);
+                               goto found;
+                       }
+               } else if (t->server_key_type == STKTABLE_SRV_ADDR) {
+                       HA_RWLOCK_RDLOCK(PROXY_LOCK, &px->lock);
+                       node = ebis_lookup(&px->used_server_addr, 
de->value.key);
+                       HA_RWLOCK_RDUNLOCK(PROXY_LOCK, &px->lock);
+                       if (node) {
+                               srv = container_of(node, struct server, 
addr_node);
+                               goto found;
+                       }
                }
        }
 
@@ -1378,7 +1388,9 @@ static int process_store_rules(struct stream *s, struct 
channel *rep, int an_bit
        for (i = 0; i < s->store_count; i++) {
                struct stksess *ts;
                void *ptr;
+               char *key;
                struct dict_entry *de;
+               struct stktable *t = s->store[i].table;
 
                if (objt_server(s->target) && objt_server(s->target)->flags & 
SRV_F_NON_STICK) {
                        stksess_free(s->store[i].table, s->store[i].ts);
@@ -1386,27 +1398,34 @@ static int process_store_rules(struct stream *s, struct 
channel *rep, int an_bit
                        continue;
                }
 
-               ts = stktable_set_entry(s->store[i].table, s->store[i].ts);
+               ts = stktable_set_entry(t, s->store[i].ts);
                if (ts != s->store[i].ts) {
                        /* the entry already existed, we can free ours */
-                       stksess_free(s->store[i].table, s->store[i].ts);
+                       stksess_free(t, s->store[i].ts);
                }
                s->store[i].ts = NULL;
 
                HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
-               ptr = __stktable_data_ptr(s->store[i].table, ts, 
STKTABLE_DT_SERVER_ID);
+               ptr = __stktable_data_ptr(t, ts, STKTABLE_DT_SERVER_ID);
                stktable_data_cast(ptr, server_id) = 
__objt_server(s->target)->puid;
                HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
 
+               if (t->server_key_type == STKTABLE_SRV_NAME)
+                       key = __objt_server(s->target)->id;
+               else if (t->server_key_type == STKTABLE_SRV_ADDR)
+                       key = __objt_server(s->target)->addr_node.key;
+               else
+                       continue;
+
                HA_RWLOCK_WRLOCK(STK_SESS_LOCK, &ts->lock);
-               de = dict_insert(&server_name_dict, 
__objt_server(s->target)->id);
+               de = dict_insert(&server_key_dict, key);
                if (de) {
-                       ptr = __stktable_data_ptr(s->store[i].table, ts, 
STKTABLE_DT_SERVER_NAME);
-                       stktable_data_cast(ptr, server_name) = de;
+                       ptr = __stktable_data_ptr(t, ts, 
STKTABLE_DT_SERVER_KEY);
+                       stktable_data_cast(ptr, server_key) = de;
                }
                HA_RWLOCK_WRUNLOCK(STK_SESS_LOCK, &ts->lock);
 
-               stktable_touch_local(s->store[i].table, ts, 1);
+               stktable_touch_local(t, ts, 1);
        }
        s->store_count = 0; /* everything is stored */
 
diff --git a/src/tools.c b/src/tools.c
index fdc91909a..1cc051148 100644
--- a/src/tools.c
+++ b/src/tools.c
@@ -1202,6 +1202,51 @@ struct sockaddr_storage *str2sa_range(const char *str, 
int *port, int *low, int
        return ret;
 }
 
+/* converts <addr> and <port> into a string representation of the address and 
port. This is sort
+ * of an inverse of str2sa_range, with some restrictions. The supported 
families are AF_INET,
+ * AF_INET6, AF_UNIX, and AF_CUST_SOCKPAIR. If the family is unsopported NULL 
is returned.
+ * If map_ports is true, then the sign of the port is included in the output, 
to indicate it is
+ * relative to the incoming port. AF_INET and AF_INET6 will be in the form 
"<addr>:<port>".
+ * AF_UNIX will either be just the path (if using a pathname) or "abns@<path>" 
if it is abstract.
+ * AF_CUST_SOCKPAIR will be of the form "sockpair@<fd>".
+ *
+ * The returned char* is allocated, and it is the responsibility of the caller 
to free it.
+ */
+char * sa2str(const struct sockaddr_storage *addr, int port, int map_ports)
+{
+       char buffer[INET6_ADDRSTRLEN];
+       char *out = NULL;
+       const void *ptr;
+       const char *path;
+
+       switch (addr->ss_family) {
+       case AF_INET:
+               ptr = &((struct sockaddr_in *)addr)->sin_addr;
+               break;
+       case AF_INET6:
+               ptr = &((struct sockaddr_in6 *)addr)->sin6_addr;
+               break;
+       case AF_UNIX:
+               path = ((struct sockaddr_un *)addr)->sun_path;
+               if (path[0] == '\0') {
+                       const int max_length = sizeof(struct sockaddr_un) - 
offsetof(struct sockaddr_un, sun_path) - 1;
+                       return memprintf(&out, "abns@%.*s", max_length, path+1);
+               } else {
+                       return strdup(path);
+               }
+       case AF_CUST_SOCKPAIR:
+               return memprintf(&out, "sockpair@%d", ((struct sockaddr_in 
*)addr)->sin_addr.s_addr);
+       default:
+               return NULL;
+       }
+       inet_ntop(addr->ss_family, ptr, buffer, get_addr_len(addr));
+       if (map_ports)
+               return memprintf(&out, "%s:%+d", buffer, port);
+       else
+               return memprintf(&out, "%s:%d", buffer, port);
+}
+
+
 /* converts <str> to a struct in_addr containing a network mask. It can be
  * passed in dotted form (255.255.255.0) or in CIDR form (24). It returns 1
  * if the conversion succeeds otherwise zero.
-- 
2.29.2


>From 8157a34247104413f7e0c4bb533e3ab342277097 Mon Sep 17 00:00:00 2001
From: Thayne McCombs <tha...@lucidchart.com>
Date: Wed, 9 Dec 2020 00:36:45 -0700
Subject: [PATCH 2/2] Add test for srvkey addr

---
 reg-tests/peers/srvkey_addr.vtc | 118 ++++++++++++++++++++++++++++++++
 1 file changed, 118 insertions(+)
 create mode 100644 reg-tests/peers/srvkey_addr.vtc

diff --git a/reg-tests/peers/srvkey_addr.vtc b/reg-tests/peers/srvkey_addr.vtc
new file mode 100644
index 000000000..52e43a6cb
--- /dev/null
+++ b/reg-tests/peers/srvkey_addr.vtc
@@ -0,0 +1,118 @@
+vtest "Test for peering by server address"
+feature ignore_unknown_macro
+
+#REQUIRE_VERSION=2.0
+#REGTEST_TYPE=slow
+
+server s1 {
+  rxreq
+  txresp -body "A"
+} -repeat 3 -start
+
+server s2 {
+  rxreq
+  txresp -body "B"
+} -repeat 3 -start
+
+server s3 {
+  rxreq
+  txresp -body "C"
+} -repeat 3 -start
+
+haproxy h1 -arg "-L A" -conf {
+  defaults
+    mode http
+    timeout client  1s
+    timeout connect 1s
+    timeout server  1s
+
+  peers peers
+    bind "fd@${A}"
+    server A
+    server B ${h2_B_addr}:${h2_B_port}
+
+  listen stkt
+    bind "fd@${fe}"
+    balance roundrobin
+    stick-table type string size 10m store gpc0 srvkey addr peers peers
+    stick on path
+    http-request track-sc0 path
+    http-request sc-inc-gpc0(0)
+    server s1_A ${s1_addr}:${s1_port}
+    server s2_A ${s2_addr}:${s2_port}
+    server s3_A ${s3_addr}:${s3_port}
+}
+
+haproxy h2 -arg "-L B" -conf {
+  defaults
+    mode http
+    timeout client  1s
+    timeout connect 1s
+    timeout server  1s
+
+  peers peers
+    bind "fd@${B}"
+    server A ${h1_A_addr}:${h1_A_port}
+    server B
+    server C ${h2_C_addr}:${h2_C_port}
+
+  listen stkt
+    bind "fd@${fe}"
+    balance random
+    stick-table type string size 10m store gpc0 srvkey addr peers peers
+    stick on path
+    http-request track-sc0 path
+    http-request sc-inc-gpc0(0)
+    server s2_B ${s2_addr}:${s2_port}
+    server s3_B ${s3_addr}:${s3_port}
+    server s1_B ${s1_addr}:${s1_port}
+}
+
+haproxy h3 -arg "-L C" -conf {
+  defaults
+    mode http
+    timeout client  1s
+    timeout connect 1s
+    timeout server  1s
+
+  peers peers
+    bind "fd@${C}"
+    server A ${h1_A_addr}:${h1_A_port}
+    server B ${h2_B_addr}:${h2_B_port}
+    server C
+
+  listen stkt
+    bind "fd@${fe}"
+    balance random
+    stick-table type string size 10m store gpc0 srvkey addr peers peers
+    stick on path
+    server s3_C ${s3_addr}:${s3_port}
+    server s1_C ${s1_addr}:${s1_port}
+    server s2_C ${s2_addr}:${s2_port}
+}
+
+haproxy h1 -start
+delay 0.2
+haproxy h2 -start
+delay 0.2
+haproxy h3 -start
+delay 0.2
+
+shell {
+  set -e
+  c1=`curl -sS ${h1_fe_addr}:${h1_fe_port}/c1`
+  c2=`curl -sS ${h1_fe_addr}:${h1_fe_port}/c2`
+  c3=`curl -sS ${h1_fe_addr}:${h1_fe_port}/c3`
+
+  echo $c1 $c2 $c3
+
+  sleep 1
+
+  [ "`curl -sS ${h2_fe_addr}:${h2_fe_port}/c1`" = "$c1" ] || exit 1
+  [ "`curl -sS ${h2_fe_addr}:${h2_fe_port}/c2`" = "$c2" ] || exit 1
+  [ "`curl -sS ${h2_fe_addr}:${h2_fe_port}/c3`" = "$c3" ] || exit 1
+
+  [ "`curl -sS ${h3_fe_addr}:${h3_fe_port}/c1`" = "$c1" ] || exit 1
+  [ "`curl -sS ${h3_fe_addr}:${h3_fe_port}/c2`" = "$c2" ] || exit 1
+  [ "`curl -sS ${h3_fe_addr}:${h3_fe_port}/c3`" = "$c3" ] || exit 1
+}
-- 
2.29.2


Reply via email to