Add IPv6 support to GeoIP module

Hello all,

geoip module currently only supports a single country and a single city
MaxMind database that must cover IPv4 address range.

Below patch allows setting up to two city and country databases, one
for IPv4 and one for IPv6. It then looks-up the requesting IP address in
the appropriate database.

Please review and consider applying,

Bruno

diff -NurpP nginx-1.0.4.orig/src/http/modules/ngx_http_geoip_module.c
nginx-1.0.4/src/http/modules/ngx_http_geoip_module.c
— nginx-1.0.4.orig/src/http/modules/ngx_http_geoip_module.c
2011-05-16 15:50:58.000000000 +0200
+++ nginx-1.0.4/src/http/modules/ngx_http_geoip_module.c 2011-09-30
10:30:18.785083263 +0200
@@ -16,6 +16,9 @@ typedef struct {
GeoIP *country;
GeoIP *org;
GeoIP *city;

  • GeoIP *country_v6;
  • GeoIP *org_v6;
  • GeoIP *city_v6;
    } ngx_http_geoip_conf_t;

@@ -26,8 +29,13 @@ typedef struct {

typedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *,
u_long addr);
+typedef const char *(*ngx_http_geoip_variable_handler6_pt)(GeoIP *,
struct in6_addr addr);

-static ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,
+static ngx_int_t
ngx_http_geoip_country_code_variable(ngx_http_request_t *r,

  • ngx_http_variable_value_t *v, uintptr_t data);
    +static ngx_int_t
    ngx_http_geoip_country_code3_variable(ngx_http_request_t *r,

  • ngx_http_variable_value_t *v, uintptr_t data);
    +static ngx_int_t
    ngx_http_geoip_country_name_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data);
    static ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data);
    @@ -113,16 +121,16 @@ ngx_module_t ngx_http_geoip_module = {
    static ngx_http_variable_t ngx_http_geoip_vars[] = {

    { ngx_string(“geoip_country_code”), NULL,

  •  ngx_http_geoip_country_variable,
    
  •  (uintptr_t) GeoIP_country_code_by_ipnum, 0, 0 },
    
  •  ngx_http_geoip_country_code_variable,
    
  •  0, 0, 0 },
    

    { ngx_string(“geoip_country_code3”), NULL,

  •  ngx_http_geoip_country_variable,
    
  •  (uintptr_t) GeoIP_country_code3_by_ipnum, 0, 0 },
    
  •  ngx_http_geoip_country_code3_variable,
    
  •  0, 0, 0 },
    

    { ngx_string(“geoip_country_name”), NULL,

  •  ngx_http_geoip_country_variable,
    
  •  (uintptr_t) GeoIP_country_name_by_ipnum, 0, 0 },
    
  •  ngx_http_geoip_country_name_variable,
    
  •  0, 0, 0 },
    

    { ngx_string(“geoip_org”), NULL,
    ngx_http_geoip_org_variable,
    @@ -180,117 +188,146 @@ static ngx_http_variable_t ngx_http_geo
    };

-static u_long
-ngx_http_geoip_addr(ngx_http_request_t *r)
+static ngx_int_t
+ngx_http_geoip_get_char(ngx_http_request_t *r,
ngx_http_variable_value_t *v,

  • GeoIP *src, ngx_http_geoip_variable_handler_pt handler,
  • GeoIP *src_v6, ngx_http_geoip_variable_handler6_pt handler_v6)
    {
  • struct sockaddr_in *sin;
    -#if (NGX_HAVE_INET6)
  • u_char *p;
  • u_long addr;
  • struct sockaddr_in6 *sin6;
    -#endif
  • const char *ret = NULL;
    switch (r->connection->sockaddr->sa_family) {
  • case AF_INET:
  •    sin = (struct sockaddr_in *) r->connection->sockaddr;
    
  •    return ntohl(sin->sin_addr.s_addr);
    
  •    if (src) {
    
  •        struct sockaddr_in *sin = (struct sockaddr_in *) 
    

r->connection->sockaddr;

  •        ret = handler(src, ntohl(sin->sin_addr.s_addr));
    
  •    }
    
  •    break;
    

#if (NGX_HAVE_INET6)

 case AF_INET6:
  •    sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;
    
  •    if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
    
  •        p = sin6->sin6_addr.s6_addr;
    
  •        addr = p[12] << 24;
    
  •        addr += p[13] << 16;
    
  •        addr += p[14] << 8;
    
  •        addr += p[15];
    
  •        return addr;
    
  •    if (src_v6) {
    
  •        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) 
    

r->connection->sockaddr;

  •        if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
    
  •            u_char *p = sin6->sin6_addr.s6_addr;
    
  •            ret = src ? handler(src, (p[12] << 24) + (p[13] << 16) 
    
  • (p[14] << 8) + p[15]) : NULL;
  •        } else
    
  •            ret = handler_v6(src_v6, sin6->sin6_addr);
       }
    
  •    break;
    

#endif
}

  • return INADDR_NONE;
  • if (ret == NULL) {
  •    goto not_found;
    
  • }
  • v->len = ngx_strlen(ret);
  • v->valid = 1;
  • v->no_cacheable = 0;
  • v->not_found = 0;
  • v->data = (u_char *) ret;
  • return NGX_OK;

+not_found:
+

  • v->not_found = 1;
  • return NGX_OK;
    }

static ngx_int_t
-ngx_http_geoip_country_variable(ngx_http_request_t *r,
+ngx_http_geoip_country_code_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{

  • ngx_http_geoip_variable_handler_pt handler =

  •    (ngx_http_geoip_variable_handler_pt) data;
    
  • const char *val;
    ngx_http_geoip_conf_t *gcf;

    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  • if (gcf->country == NULL) {

  •    goto not_found;
    
  • if (gcf->country == NULL && gcf->country_v6 == NULL) {
  •    v->not_found = 1;
    
  •    return NGX_OK;
    
    }
  • val = handler(gcf->country, ngx_http_geoip_addr®);
  • return ngx_http_geoip_get_char(r, v, gcf->country,
    &GeoIP_country_code_by_ipnum,
    +#if (NGX_HAVE_INET6)
  •                 gcf->country_v6, &GeoIP_country_code_by_ipnum_v6
    

+#else

  •                 NULL, NULL
    

+#endif

  •                 );
    

+}

  • if (val == NULL) {

  •    goto not_found;
    
  • }

  • v->len = ngx_strlen(val);

  • v->valid = 1;

  • v->no_cacheable = 0;

  • v->not_found = 0;

  • v->data = (u_char *) val;
    +static ngx_int_t
    +ngx_http_geoip_country_code3_variable(ngx_http_request_t *r,

  • ngx_http_variable_value_t *v, uintptr_t data)
    +{
  • ngx_http_geoip_conf_t *gcf;
  • return NGX_OK;
  • gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

-not_found:

  • if (gcf->country == NULL && gcf->country_v6 == NULL) {
  •    v->not_found = 1;
    
  • v->not_found = 1;
  •    return NGX_OK;
    
  • }
  • return NGX_OK;
  • return ngx_http_geoip_get_char(r, v, gcf->country,
    &GeoIP_country_code3_by_ipnum,
    +#if (NGX_HAVE_INET6)
  •                 gcf->country_v6, &GeoIP_country_code3_by_ipnum_v6
    

+#else

  •                 NULL, NULL
    

+#endif

  •                 );
    

}

static ngx_int_t
-ngx_http_geoip_org_variable(ngx_http_request_t *r,
+ngx_http_geoip_country_name_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{

  • ngx_http_geoip_variable_handler_pt handler =

  •    (ngx_http_geoip_variable_handler_pt) data;
    
  • const char *val;
    ngx_http_geoip_conf_t *gcf;

    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

  • if (gcf->org == NULL) {

  •    goto not_found;
    
  • if (gcf->country == NULL && gcf->country_v6 == NULL) {
  •    v->not_found = 1;
    
  •    return NGX_OK;
    
    }
  • val = handler(gcf->org, ngx_http_geoip_addr®);
  • return ngx_http_geoip_get_char(r, v, gcf->country,
    &GeoIP_country_name_by_ipnum,
    +#if (NGX_HAVE_INET6)
  •                 gcf->country_v6, &GeoIP_country_name_by_ipnum_v6
    

+#else

  •                 NULL, NULL
    

+#endif

  •                 );
    

+}

  • if (val == NULL) {

  •    goto not_found;
    
  • }

  • v->len = ngx_strlen(val);

  • v->valid = 1;

  • v->no_cacheable = 0;

  • v->not_found = 0;

  • v->data = (u_char *) val;
    +static ngx_int_t
    +ngx_http_geoip_org_variable(ngx_http_request_t *r,

  • ngx_http_variable_value_t *v, uintptr_t data)
    +{
  • ngx_http_geoip_conf_t *gcf;
  • return NGX_OK;
  • gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);

-not_found:

  • if (gcf->org == NULL && gcf->org_v6 == NULL) {
  •    v->not_found = 1;
    
  • v->not_found = 1;
  •    return NGX_OK;
    
  • }
  • return NGX_OK;
  • return ngx_http_geoip_get_char(r, v, gcf->org,
    (ngx_http_geoip_variable_handler_pt)&GeoIP_name_by_ipnum,
    +#if (NGX_HAVE_INET6)
  •                 gcf->org_v6, 
    

(ngx_http_geoip_variable_handler6_pt)&GeoIP_name_by_ipnum_v6
+#else

  •                 NULL, NULL
    

+#endif

  •                 );
    

}

@@ -451,8 +488,26 @@ ngx_http_geoip_get_city_record(ngx_http_

 gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);
  • if (gcf->city) {
  •    return GeoIP_record_by_ipnum(gcf->city, 
    

ngx_http_geoip_addr®);

  • switch (r->connection->sockaddr->sa_family) {
  • case AF_INET:
  •    if (gcf->city) {
    
  •        struct sockaddr_in *sin = (struct sockaddr_in *) 
    

r->connection->sockaddr;

  •        return GeoIP_record_by_ipnum(gcf->city, 
    

ntohl(sin->sin_addr.s_addr));

  •    }
    
  •    return NULL;
    

+#if (NGX_HAVE_INET6)

  • case AF_INET6:
  •    if (gcf->city_v6) {
    
  •        struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) 
    

r->connection->sockaddr;

  •        if (IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {
    
  •            u_char *p = sin6->sin6_addr.s6_addr;
    
  •            return gcf->city ? GeoIP_record_by_ipnum(gcf->city,
    
  •                         (p[12] << 24) + (p[13] << 16) + (p[14] << 
    
    • p[15]) : NULL;
  •        } else
    
  •            return GeoIP_record_by_ipnum_v6(gcf->city_v6, 
    

sin6->sin6_addr);

  •    }
    
  •    return NULL;
    

+#endif
}

 return NULL;

@@ -505,18 +560,19 @@ static char *
ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_geoip_conf_t *gcf = conf;

  • GeoIP *tmp;

    ngx_str_t *value;

  • if (gcf->country) {
  • if (gcf->country && gcf->country_v6) {
    return “is duplicate”;
    }

    value = cf->args->elts;

  • gcf->country = GeoIP_open((char *) value[1].data,
    GEOIP_MEMORY_CACHE);
  • tmp = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
  • if (gcf->country == NULL) {
  • if (tmp == NULL) {
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    “GeoIP_open(”%V") failed", &value[1]);

@@ -525,7 +581,7 @@ ngx_http_geoip_country(ngx_conf_t *cf, n

 if (cf->args->nelts == 3) {
     if (ngx_strcmp(value[2].data, "utf8") == 0) {
  •        GeoIP_set_charset (gcf->country, GEOIP_CHARSET_UTF8);
    
  •        GeoIP_set_charset (tmp, GEOIP_CHARSET_UTF8);
    
       } else {
           ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    

@@ -534,18 +590,33 @@ ngx_http_geoip_country(ngx_conf_t *cf, n
}
}

  • switch (gcf->country->databaseType) {
  • switch (tmp->databaseType) {

    case GEOIP_COUNTRY_EDITION:
    case GEOIP_PROXY_EDITION:
    case GEOIP_NETSPEED_EDITION:

  •    if (gcf->country) {
    
  •        GeoIP_delete(tmp);
    
  •        return "is duplicate";
    
  •    } else
    
  •        gcf->country = tmp;
    
  •    return NGX_CONF_OK;
    

+#if (NGX_HAVE_INET6)

  • case GEOIP_COUNTRY_EDITION_V6:
  •    if (gcf->country_v6) {
    
  •        GeoIP_delete(tmp);
    
  •        return "is duplicate";
    
  •    } else
    
  •        gcf->country_v6 = tmp;
       return NGX_CONF_OK;
    

+#endif

 default:
     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                        "invalid GeoIP database \"%V\" type:%d",
  •                       &value[1], gcf->country->databaseType);
    
  •                       &value[1], tmp->databaseType);
    
  •    GeoIP_delete(tmp);
       return NGX_CONF_ERROR;
    

    }
    }
    @@ -555,18 +626,19 @@ static char *
    ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
    ngx_http_geoip_conf_t *gcf = conf;

  • GeoIP *tmp;

    ngx_str_t *value;

  • if (gcf->org) {
  • if (gcf->org && gcf->org_v6) {
    return “is duplicate”;
    }

    value = cf->args->elts;

  • gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
  • tmp = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
  • if (gcf->org == NULL) {
  • if (tmp == NULL) {
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    “GeoIP_open(”%V") failed", &value[1]);

@@ -575,7 +647,7 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c

 if (cf->args->nelts == 3) {
     if (ngx_strcmp(value[2].data, "utf8") == 0) {
  •        GeoIP_set_charset (gcf->org, GEOIP_CHARSET_UTF8);
    
  •        GeoIP_set_charset (tmp, GEOIP_CHARSET_UTF8);
    
       } else {
           ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    

@@ -584,19 +656,37 @@ ngx_http_geoip_org(ngx_conf_t *cf, ngx_c
}
}

  • switch (gcf->org->databaseType) {
  • switch (tmp->databaseType) {

    case GEOIP_ISP_EDITION:
    case GEOIP_ORG_EDITION:
    case GEOIP_DOMAIN_EDITION:
    case GEOIP_ASNUM_EDITION:

  •    if (gcf->org) {
    
  •        GeoIP_delete(tmp);
    
  •        return "is duplicate";
    
  •    } else
    
  •        gcf->org = tmp;
    
  •    return NGX_CONF_OK;
    

+#if (NGX_HAVE_INET6)

  • case GEOIP_ISP_EDITION_V6:
  • case GEOIP_ORG_EDITION_V6:
  • case GEOIP_DOMAIN_EDITION_V6:
  • case GEOIP_ASNUM_EDITION_V6:
  •    if (gcf->org_v6) {
    
  •        GeoIP_delete(tmp);
    
  •        return "is duplicate";
    
  •    } else
    
  •        gcf->org_v6 = tmp;
       return NGX_CONF_OK;
    

+#endif

 default:
     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                        "invalid GeoIP database \"%V\" type:%d",
  •                       &value[1], gcf->org->databaseType);
    
  •                       &value[1], tmp->databaseType);
    
  •    GeoIP_delete(tmp);
       return NGX_CONF_ERROR;
    

    }
    }
    @@ -606,18 +696,19 @@ static char *
    ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
    {
    ngx_http_geoip_conf_t *gcf = conf;

  • GeoIP *tmp;

    ngx_str_t *value;

  • if (gcf->city) {
  • if (gcf->city && gcf->city_v6) {
    return “is duplicate”;
    }

    value = cf->args->elts;

  • gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
  • tmp = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);
  • if (gcf->city == NULL) {
  • if (tmp == NULL) {
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    “GeoIP_open(”%V") failed", &value[1]);

@@ -626,7 +717,7 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_

 if (cf->args->nelts == 3) {
     if (ngx_strcmp(value[2].data, "utf8") == 0) {
  •        GeoIP_set_charset (gcf->city, GEOIP_CHARSET_UTF8);
    
  •        GeoIP_set_charset (tmp, GEOIP_CHARSET_UTF8);
    
       } else {
           ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
    

@@ -635,17 +726,32 @@ ngx_http_geoip_city(ngx_conf_t *cf, ngx_
}
}

  • switch (gcf->city->databaseType) {
  • switch (tmp->databaseType) {

    case GEOIP_CITY_EDITION_REV0:
    case GEOIP_CITY_EDITION_REV1:

  •    if (gcf->city) {
    
  •        GeoIP_delete(tmp);
    
  •        return "is duplicate";
    
  •    } else
    
  •        gcf->city = tmp;
    
  •    return NGX_CONF_OK;
    

+#if (NGX_HAVE_INET6)

  • case GEOIP_CITY_EDITION_REV0_V6:
  • case GEOIP_CITY_EDITION_REV1_V6:
  •    if (gcf->city_v6) {
    
  •        GeoIP_delete(tmp);
    
  •        return "is duplicate";
    
  •    } else
    
  •        gcf->city_v6 = tmp;
       return NGX_CONF_OK;
    

+#endif

 default:
     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                        "invalid GeoIP City database \"%V\" 

type:%d",

  •                       &value[1], gcf->city->databaseType);
    
  •                       &value[1], tmp->databaseType);
       return NGX_CONF_ERROR;
    

    }
    }
    @@ -659,12 +765,21 @@ ngx_http_geoip_cleanup(void *data)
    if (gcf->country) {
    GeoIP_delete(gcf->country);
    }

  • if (gcf->country_v6) {

  •    GeoIP_delete(gcf->country_v6);
    
  • }

    if (gcf->org) {
    GeoIP_delete(gcf->org);
    }

  • if (gcf->org_v6) {

  •    GeoIP_delete(gcf->org_v6);
    
  • }

    if (gcf->city) {
    GeoIP_delete(gcf->city);
    }

  • if (gcf->city_v6) {

  •    GeoIP_delete(gcf->city_v6);
    
  • }
    }

Hello!

On Tue, Oct 04, 2011 at 09:22:31AM +0200, Bruno Prémont wrote:

Please review and consider applying,
Please take a look at this patch instead (this one is sitting in
my TODO queue, waiting for more thoughtfull review and likely
commit):

http://mailman.nginx.org/pipermail/nginx-devel/2011-June/000971.html

Do you have any problems with Gregor Kališnik’s patch? How your
patch is different?

Maxim D.

Most IPv6 stuff was introduced with newer versions of the GeoIP library
(IIRC something around 1.4.7), so you might either want to autodetect
GeoIP’s capabilities or embed the relevant stuff into

#if LIBGEOIP_VERSION >= 1004007

#endif

directives. Otherwise you might break builds against older versions of
the library.

Posted at Nginx Forum:

Hi.

On Wednesday 05 of October 2011 13:26:47 csg wrote:

Most IPv6 stuff was introduced with newer versions of the GeoIP library
(IIRC something around 1.4.7), so you might either want to autodetect
GeoIP’s capabilities or embed the relevant stuff into

#if LIBGEOIP_VERSION >= 1004007

#endif

directives. Otherwise you might break builds against older versions of
the library.

My patch does config checking if some GeoIPv6 specific constant exists.
And I’ve
tested agains older and newer versions of GeoIP. And it build without
any
problems.

Hallo,

On Wed, 5 Oct 2011 15:05:28 Maxim D. [email protected] wrote:

Please take a look at this patch instead (this one is sitting in
my TODO queue, waiting for more thoughtfull review and likely
commit):

GeoIPv6 patch

Do you have any problems with Gregor Kališnik’s patch?

I didn’t stumble on Gregor’s patch before writing mine and your
reference made me aware of it.

How your patch is different?

The difference between both patches is that mine accepts to load two
databases, one for IPv4 and one for IPv6.

From a short test with geoiplookup6 it seems that MaxMind’s IPv6 country
database includes the IPv4 database and thus can answer for mapped IPv4
addresses (which I didn’t know).

So as long as that remains true Gregor’s patch looks more simple (while
covering feature-test of geoip lib which mine doesn’t) and provides the
same features.

If IPv6 database once stops including knowledge about IPv4 addresses
lookup would start failing (but that hopefully would only happen when
IPv4 gets irrelevant).

Thanks,
Bruno

Hello!

On Wed, Oct 05, 2011 at 01:56:35PM +0200, Bruno Prémont wrote:

I didn’t stumble on Gregor’s patch before writing mine and your

So as long as that remains true Gregor’s patch looks more simple (while
covering feature-test of geoip lib which mine doesn’t) and provides the
same features.

If IPv6 database once stops including knowledge about IPv4 addresses
lookup would start failing (but that hopefully would only happen when
IPv4 gets irrelevant).

IPv6 database can’t really drop ipv4 mapped addresses, as it’s just
part of ipv6 address space and may appear even if you aren’t using
ipv4 at all. (This isn’t really good for security reasons, but
that’s completely different story.)

So you are happy with Gregor’s patch, right? Let’s focus on it
then.

Maxim D.

Hello Maxim,

On Wed, 5 Oct 2011 16:52:10 Maxim D. wrote:

IPv6 database can’t really drop ipv4 mapped addresses, as it’s just
part of ipv6 address space and may appear even if you aren’t using
ipv4 at all. (This isn’t really good for security reasons, but
that’s completely different story.)

So you are happy with Gregor’s patch, right? Let’s focus on it
then.

What’s the state of/progress with Gregor’s patch?

I’ve been using it on top of 1.0.x since October without any issues.
Is there anything preventing it from being applied?

Thanks,
Bruno

Hi.

On Friday 13 of April 2012 14:47:24 Maxim D. wrote:

We’ll work on various ipv6-related things after 1.2.0 release (it
was expected to happen yesterday, but was postponed to avoid
interference with security updates; next expected date is about 23
april). It will likely be committed then.

Nice to hear that.

Best regards,
Gregor Kališnik

Hello!

On Fri, Apr 13, 2012 at 10:11:53AM +0200, Bruno Prémont wrote:

What’s the state of/progress with Gregor’s patch?

I’ve been using it on top of 1.0.x since October without any issues.
Is there anything preventing it from being applied?

We’ll work on various ipv6-related things after 1.2.0 release (it
was expected to happen yesterday, but was postponed to avoid
interference with security updates; next expected date is about 23
april). It will likely be committed then.

Maxim D.