Queries on "ip_range" type (missing documentation)

Hi,

Unfortunately, I couldn't find any pointers in the documentation. I'd like to have some IP ranges saved and be able to:

  1. Perform a query for an IP address.
  2. Perform a query for a CIDR range.

I got #1 working with query string query (e.g., {"query":{"query_string":{"query":"iprangefield:172.16.0.0"}}}) but it doesn't look elegant and I'm wondering if there's a better way to do this. Something like the term query?

The other issue I have is that I don't know how to "match" a document. I need a way to see if there's already a document with the same field. Also, a way to check if a subnet is part of an existing document (larger prefix).

Thank you!

Bump

An ip_range field can be treated in the same way as any other range type field. See the docs here. The field value will be a network.

One thing to remember with ip_range fields is that if a document contains the range 0.0.0.0/0, it will match all queries.

You can find documents where the ip_range field contains a certain IP address using the match query. For example:

{
  "query": {
    "iprangefield": {
      "addr": "192.168.1.17"
    }
  }
}

You can also use a range query to see if document contains another network. For example:

{
  "query": {
    "range": {
      "iprangefield": {
        "from": "192.168.1.10",
        "to": "192.168.1.15"
      }
    }
  }
}
1 Like

@whatgeorgemade
Thanks! I got the match part working:

{
  "query": {
    "match": {
       "iprangefield": "172.16.0.0"
    }
  }
}

I got that one messed up somehow before...

Though, I still don't know how to match an exact range to avoid duplication. Do you have any idea? I want to avoid translating CIDR to two IP addresses get the result set, then post-process it to see if I get a match.

Matching the exact range is a bit more involved as range fields aren't meant for this sort of query.

One strategy would be to change your mapping and add a keyword multi-field that holds the original network address and mask. You can then do an exact match against that using a match query.

For example:

PUT testiprangefield
{
  "settings": {
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "iprangefield": {
        "type": "ip_range",
        "fields": {
          "raw": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

You can add documents in the usual way:

PUT testiprangefield/_doc/1
{
  "iprangefield": "192.168.1.1/28"
}

Then search against the raw multi-field.

GET testiprangefield/_search
{
  "query": {
    "match": {
      "iprangefield.raw": "192.168.1.1/28"
    }
  }
}
1 Like

I see, I was trying to avoid that (adding an extra field) if possible. So no CIDR for search.

Thank you again @whatgeorgemade! In this case I use the workarounds for CIDR and do pre+post-processing. I think I'll still skip adding an extra field, since I won't be able to have everything in ES. I'll query the range and process the result set to see if there's an exact match.

If you only have a single IP in your data, then you do not need an iprange as long as your ranges can be expressed using CIDR. You can use the ip datatype

DELETE my-index

PUT my-index
{
  "mappings": {
    "properties": {
      "ip_addr": {
        "type": "ip"
      }
    }
  }
}

PUT my-index/_bulk?refresh
{"index":{}}
{"ip_addr":"192.168.1.1"}
{"index":{}}
{"ip_addr":"1.1.1.1"}
{"index":{}}
{"ip_addr":"10.5.6.7"}


GET my-index/_search
{
  "query": {
    "terms": {
      "ip_addr": [
        "192.168.0.0/16",
        "127.16.0.0/16",
        "10.0.0.0/8"
      ]
    }
  }
}
1 Like

Thanks, but I'll need both and want to avoid adding too many fields (or documents) and use different queries. In the long run, I'll see if using ranges was the right move.