]> source.dussan.org Git - rspamd.git/commitdiff
Rework IP addresses in upstreams.
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 7 Nov 2014 13:33:45 +0000 (13:33 +0000)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 7 Nov 2014 13:33:45 +0000 (13:33 +0000)
- Select ipv4/unix addresses if they exist and use ipv6 for ipv6 only
  upstreams (since the support of ipv6 is poor in many OSes and
  environments).
- Free IP list on upstream destruction.
- Add test cases for addresses selection.
- Allow adding of free form IP addresses to upstreams.

src/libutil/upstream.c
src/libutil/upstream.h
test/rspamd_upstream_test.c

index 7bec3729a0354e0a782651a80dea6a7a2dca673e..b5f1ed35e71082876dc10a49dfb7c2e7b672574c 100644 (file)
@@ -105,6 +105,42 @@ rspamd_upstreams_library_init (struct rdns_resolver *resolver,
        ev_base = base;
 }
 
+static gint
+rspamd_upstream_af_to_weight (const rspamd_inet_addr_t *addr)
+{
+       int ret;
+
+       switch (addr->af) {
+       case AF_UNIX:
+               ret = 2;
+               break;
+       case AF_INET:
+               ret = 1;
+               break;
+       default:
+               ret = 0;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Select IPv4 addresses before IPv6
+ */
+static gint
+rspamd_upstream_addr_sort_func (gconstpointer a, gconstpointer b)
+{
+       const rspamd_inet_addr_t *ip1 = (const rspamd_inet_addr_t *)a,
+                       *ip2 = (const rspamd_inet_addr_t *)b;
+       gint w1, w2;
+
+       w1 = rspamd_upstream_af_to_weight (ip1);
+       w2 = rspamd_upstream_af_to_weight (ip2);
+
+       return w2 - w1;
+}
+
 static void
 rspamd_upstream_set_active (struct upstream_list *ls, struct upstream *up)
 {
@@ -186,6 +222,8 @@ rspamd_upstream_update_addrs (struct upstream *up)
                up->addrs.cur = 0;
                up->addrs.count = addr_cnt;
                up->addrs.addr = new_addrs;
+               qsort (up->addrs.addr, up->addrs.count, sizeof (up->addrs.addr[0]),
+                                       rspamd_upstream_addr_sort_func);
                g_free (old);
        }
 
@@ -340,13 +378,36 @@ rspamd_upstream_dtor (struct upstream *up)
 
        rspamd_mutex_free (up->lock);
        g_free (up->name);
+       g_free (up->addrs.addr);
        g_slice_free1 (sizeof (*up), up);
 }
 
 rspamd_inet_addr_t*
 rspamd_upstream_addr (struct upstream *up)
 {
-       return &up->addrs.addr[up->addrs.cur++ % up->addrs.count];
+       gint idx, next_idx, w1, w2;
+       /*
+        * We know that addresses are sorted in the way that ipv4 addresses come
+        * first. Therefore, we select only ipv4 addresses if they exist, since
+        * many systems now has poorly supported ipv6
+        */
+       idx = up->addrs.cur;
+       next_idx = (idx + 1) % up->addrs.count;
+       w1 = rspamd_upstream_af_to_weight (&up->addrs.addr[idx]);
+       w2 = rspamd_upstream_af_to_weight (&up->addrs.addr[next_idx]);
+
+       /*
+        * We don't care about the exact priorities, but we prefer ipv4/unix
+        * addresses before any ipv6 addresses
+        */
+       if (!w1 || w2) {
+               up->addrs.cur = next_idx;
+       }
+       else {
+               up->addrs.cur = 0;
+       }
+
+       return &up->addrs.addr[up->addrs.cur];
 }
 
 const gchar*
@@ -377,12 +438,32 @@ rspamd_upstreams_add_upstream (struct upstream_list *ups,
        up->ls = ups;
        REF_INIT_RETAIN (up, rspamd_upstream_dtor);
        up->lock = rspamd_mutex_new ();
+       qsort (up->addrs.addr, up->addrs.count, sizeof (up->addrs.addr[0]),
+                       rspamd_upstream_addr_sort_func);
 
        rspamd_upstream_set_active (ups, up);
 
        return TRUE;
 }
 
+gboolean
+rspamd_upstream_add_addr (struct upstream *up, const rspamd_inet_addr_t *addr)
+{
+       gint nsz;
+
+       /*
+        * XXX: slow and inefficient
+        */
+       nsz = ++up->addrs.count;
+       up->addrs.addr = g_realloc (up->addrs.addr,
+                       nsz * sizeof (up->addrs.addr[0]));
+       memcpy (&up->addrs.addr[nsz - 1], addr, sizeof (*addr));
+       qsort (up->addrs.addr, up->addrs.count, sizeof (up->addrs.addr[0]),
+                               rspamd_upstream_addr_sort_func);
+
+       return TRUE;
+}
+
 gboolean
 rspamd_upstreams_parse_line (struct upstream_list *ups,
                const gchar *str, guint16 def_port, void *data)
index 812cc4bd8c0d41a706732e2c73e101d09f2e0df5..cff6acfb1eeba04fd4676380522ebd1effe20b03 100644 (file)
@@ -116,6 +116,14 @@ gboolean rspamd_upstreams_from_ucl (struct upstream_list *ups,
  */
 rspamd_inet_addr_t* rspamd_upstream_addr (struct upstream *up);
 
+/**
+ * Add custom address for an upstream
+ * @param up
+ * @return
+ */
+gboolean rspamd_upstream_add_addr (struct upstream *up,
+               const rspamd_inet_addr_t *addr);
+
 /**
  * Returns the symbolic name of the upstream
  * @param up
index 80d963963a9a021cf9b5e33a4e508c2dad241e8f..23a450ec49e76082200dbacb4c27d2141e8aa633 100644 (file)
@@ -72,6 +72,7 @@ rspamd_upstream_test_func (void)
        gdouble p;
        struct event ev;
        struct timeval tv;
+       rspamd_inet_addr_t *addr, *next_addr, paddr;
 
        cfg = (struct rspamd_config *)g_malloc (sizeof (struct rspamd_config));
        bzero (cfg, sizeof (struct rspamd_config));
@@ -129,6 +130,29 @@ rspamd_upstream_test_func (void)
        msg_debug ("p value for hash consistency: %.6f", p);
        g_assert (p > 0.9);
 
+       rspamd_upstreams_destroy (nls);
+
+       /*
+        * Test v4/v6 priorities
+        */
+       nls = rspamd_upstreams_create ();
+       g_assert (rspamd_upstreams_add_upstream (nls, "127.0.0.1", 0, NULL));
+       up = rspamd_upstream_get (nls, RSPAMD_UPSTREAM_RANDOM);
+       addr = g_malloc (sizeof (*addr));
+       rspamd_parse_inet_address(&paddr, "127.0.0.2");
+       g_assert (rspamd_upstream_add_addr (up, &paddr));
+       rspamd_parse_inet_address(&paddr, "::1");
+       g_assert (rspamd_upstream_add_addr (up, &paddr));
+       addr = rspamd_upstream_addr (up);
+       for (i = 0; i < 256; i ++) {
+               next_addr = rspamd_upstream_addr (up);
+               g_assert (addr->af == AF_INET);
+               g_assert (next_addr->af == AF_INET);
+               g_assert (addr != next_addr);
+               addr = next_addr;
+       }
+       rspamd_upstreams_destroy (nls);
+
        /* Upstream fail test */
        evtimer_set (&ev, rspamd_upstream_timeout_handler, resolver);
        event_base_set (ev_base, &ev);
@@ -143,4 +167,7 @@ rspamd_upstream_test_func (void)
 
        event_base_loop (ev_base, 0);
        g_assert (rspamd_upstreams_alive (ls) == 3);
+
+       g_free (cfg);
+       rspamd_upstreams_destroy (ls);
 }