[Dnsmasq-discuss] [PATCH v3] Support Cisco Umbrella/OpenDNS Device ID & Remote IP

Simon Kelley simon at thekelleys.org.uk
Tue Apr 13 23:34:40 UTC 2021


Patch applied.


Cheers,

Simon.



On 09/04/2021 20:46, Brian Hartvigsen wrote:
> This is based on the information at
> https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic and
> https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic2 .
> Using --umbrella by itself will enable Remote IP reporting. This can not
> be used for any policy filtering in Cisco Umbrella/OpenDNS. Additional
> information can be supplied using specific option specifications,
> multiple can be separated by a comma:
> 
> --umbrella=orgid:1234,deviceid=0123456789abcdef
> 
> Specifies that you want to report organization 1234 using device
> 0123456789abcdef. For Cisco Umbrella Enterprise, see "Register (Create)
> a device" (https://docs.umbrella.com/umbrella-api/docs/create-a-device)
> for how to get a Device ID and "Organization ID endpoint"
> (https://docs.umbrella.com/umbrella-api/docs/organization-endpoint) to
> get organizations ID. For OpenDNS Home Users, there is no organization,
> see Registration API endpoint
> (https://docs.umbrella.com/umbrella-api/docs/registration-api-endpoint2)
> for how to get a Device ID. Asset ID should be ignored unless
> specifically instructed to use by support.
> 
> Signed-off-by: Brian Hartvigsen <brian.andrew at brianandjenny.com>
> ---
>  man/dnsmasq.8      |  8 +++++-
>  src/dns-protocol.h |  1 +
>  src/dnsmasq.h      |  7 +++++-
>  src/edns0.c        | 63 ++++++++++++++++++++++++++++++++++++++++++++++
>  src/option.c       | 59 ++++++++++++++++++++++++++++++++++++++++++-
>  5 files changed, 135 insertions(+), 3 deletions(-)
> 
> diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
> index fce580f..eec77af 100644
> --- a/man/dnsmasq.8
> +++ b/man/dnsmasq.8
> @@ -711,7 +711,13 @@ will add the /24 and /96 subnets of the requestor for IPv4 and IPv6 requestors,
>  will add 1.2.3.0/24 for IPv4 requestors and ::/0 for IPv6 requestors.
>  .B --add-subnet=1.2.3.4/24,1.2.3.4/24
>  will add 1.2.3.0/24 for both IPv4 and IPv6 requestors.
> -
> +.TP
> +.B --umbrella[=deviceid:<deviceid>[,orgid:<orgid>]]
> +Embeds the requestor's IP address in DNS queries forwarded upstream.
> +If device id or organization id are specified, the information is
> +included in the forwarded queries and may be able to be used in
> +filtering policies and reporting. The order of the deviceid and orgid
> +attributes is irrelevant, but must be separated by a comma.
>  .TP
>  .B \-c, --cache-size=<cachesize>
>  Set the size of dnsmasq's cache. The default is 150 names. Setting the cache size to zero disables caching. Note: huge cache size impacts performance.
> diff --git a/src/dns-protocol.h b/src/dns-protocol.h
> index 941bea6..8ad1964 100644
> --- a/src/dns-protocol.h
> +++ b/src/dns-protocol.h
> @@ -82,6 +82,7 @@
>  #define EDNS0_OPTION_CLIENT_SUBNET  8     /* IANA */
>  #define EDNS0_OPTION_NOMDEVICEID    65073 /* Nominum temporary assignment */
>  #define EDNS0_OPTION_NOMCPEID       65074 /* Nominum temporary assignment */
> +#define EDNS0_OPTION_UMBRELLA       20292 /* Cisco Umbrella temporary assignment */
>  
>  struct dns_header {
>    u16 id;
> diff --git a/src/dnsmasq.h b/src/dnsmasq.h
> index 1e21005..95dc8ae 100644
> --- a/src/dnsmasq.h
> +++ b/src/dnsmasq.h
> @@ -270,7 +270,9 @@ struct event_desc {
>  #define OPT_SINGLE_PORT    60
>  #define OPT_LEASE_RENEW    61
>  #define OPT_LOG_DEBUG      62
> -#define OPT_LAST           63
> +#define OPT_UMBRELLA       63
> +#define OPT_UMBRELLA_DEVID 64
> +#define OPT_LAST           65
>  
>  #define OPTION_BITS (sizeof(unsigned int)*8)
>  #define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
> @@ -1061,6 +1063,9 @@ extern struct daemon {
>    int port, query_port, min_port, max_port;
>    unsigned long local_ttl, neg_ttl, max_ttl, min_cache_ttl, max_cache_ttl, auth_ttl, dhcp_ttl, use_dhcp_ttl;
>    char *dns_client_id;
> +  u32 umbrella_org;
> +  u32 umbrella_asset;
> +  u8 umbrella_device[8];
>    struct hostsfile *addn_hosts;
>    struct dhcp_context *dhcp, *dhcp6;
>    struct ra_interface *ra_interfaces;
> diff --git a/src/edns0.c b/src/edns0.c
> index c85b318..7bd26b8 100644
> --- a/src/edns0.c
> +++ b/src/edns0.c
> @@ -427,6 +427,66 @@ int check_source(struct dns_header *header, size_t plen, unsigned char *pseudohe
>     return 1;
>  }
>  
> +/* See https://docs.umbrella.com/umbrella-api/docs/identifying-dns-traffic for
> + * detailed information on packet formating.
> + */
> +#define UMBRELLA_VERSION    1
> +#define UMBRELLA_TYPESZ     2
> +
> +#define UMBRELLA_ASSET      0x0004
> +#define UMBRELLA_ASSETSZ    sizeof(daemon->umbrella_asset)
> +#define UMBRELLA_ORG        0x0008
> +#define UMBRELLA_ORGSZ      sizeof(daemon->umbrella_org)
> +#define UMBRELLA_IPV4       0x0010
> +#define UMBRELLA_IPV6       0x0020
> +#define UMBRELLA_DEVICE     0x0040
> +#define UMBRELLA_DEVICESZ   sizeof(daemon->umbrella_device)
> +
> +struct umbrella_opt {
> +  u8 magic[4];
> +  u8 version;
> +  u8 flags;
> +  /* We have 4 possible fields since we'll never send both IPv4 and
> +   * IPv6, so using the larger of the two to calculate max buffer size.
> +   * Each field also has a type header.  So the following accounts for
> +   * the type headers and each field size to get a max buffer size.
> +   */
> +  u8 fields[4 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ + UMBRELLA_ASSETSZ];
> +};
> +
> +static size_t add_umbrella_opt(struct dns_header *header, size_t plen, unsigned char *limit, union mysockaddr *source, int *cacheable)
> +{
> +  *cacheable = 0;
> +
> +  struct umbrella_opt opt = {{"ODNS"}, UMBRELLA_VERSION, 0, {}};
> +  u8 *u = &opt.fields[0];
> +
> +  if (daemon->umbrella_org) {
> +    PUTSHORT(UMBRELLA_ORG, u);
> +    PUTLONG(daemon->umbrella_org, u);
> +  }
> +
> +  int family = source->sa.sa_family;
> +  PUTSHORT(family == AF_INET ? UMBRELLA_IPV4 : UMBRELLA_IPV6, u);
> +  int size = family == AF_INET ? INADDRSZ : IN6ADDRSZ;
> +  memcpy(u, get_addrp(source, family), size);
> +  u += size;
> +
> +  if (option_bool(OPT_UMBRELLA_DEVID)) {
> +    PUTSHORT(UMBRELLA_DEVICE, u);
> +    memcpy(u, (char *)&daemon->umbrella_device, UMBRELLA_DEVICESZ);
> +    u += UMBRELLA_DEVICESZ;
> +  }
> +
> +  if (daemon->umbrella_asset) {
> +    PUTSHORT(UMBRELLA_ASSET, u);
> +    PUTLONG(daemon->umbrella_asset, u);
> +  }
> +
> +  int len = u - &opt.magic[0];
> +  return add_pseudoheader(header, plen, (unsigned char *)limit, PACKETSZ, EDNS0_OPTION_UMBRELLA, (unsigned char *)&opt, len, 0, 1);
> +}
> +
>  /* Set *check_subnet if we add a client subnet option, which needs to checked 
>     in the reply. Set *cacheable to zero if we add an option which the answer
>     may depend on. */
> @@ -445,6 +505,9 @@ size_t add_edns0_config(struct dns_header *header, size_t plen, unsigned char *l
>    if (daemon->dns_client_id)
>      plen = add_pseudoheader(header, plen, limit, PACKETSZ, EDNS0_OPTION_NOMCPEID, 
>  			    (unsigned char *)daemon->dns_client_id, strlen(daemon->dns_client_id), 0, 1);
> +
> +  if (option_bool(OPT_UMBRELLA))
> +    plen = add_umbrella_opt(header, plen, limit, source, cacheable);
>    
>    if (option_bool(OPT_CLIENT_SUBNET))
>      {
> diff --git a/src/option.c b/src/option.c
> index 6de5914..e8926a4 100644
> --- a/src/option.c
> +++ b/src/option.c
> @@ -170,6 +170,7 @@ struct myoption {
>  #define LOPT_PXE_VENDOR    361
>  #define LOPT_DYNHOST       362
>  #define LOPT_LOG_DEBUG     363
> +#define LOPT_UMBRELLA	   364
>   
>  #ifdef HAVE_GETOPT_LONG
>  static const struct option opts[] =  
> @@ -345,6 +346,7 @@ static const struct myoption opts[] =
>      { "dhcp-ignore-clid", 0, 0,  LOPT_IGNORE_CLID },
>      { "dynamic-host", 1, 0, LOPT_DYNHOST },
>      { "log-debug", 0, 0, LOPT_LOG_DEBUG },
> +	{ "umbrella", 2, 0, LOPT_UMBRELLA },
>      { NULL, 0, 0, 0 }
>    };
>  
> @@ -527,6 +529,7 @@ static struct {
>    { LOPT_DUMPFILE, ARG_ONE, "<path>", gettext_noop("Path to debug packet dump file"), NULL },
>    { LOPT_DUMPMASK, ARG_ONE, "<hex>", gettext_noop("Mask which packets to dump"), NULL },
>    { LOPT_SCRIPT_TIME, OPT_LEASE_RENEW, NULL, gettext_noop("Call dhcp-script when lease expiry changes."), NULL },
> +  { LOPT_UMBRELLA, ARG_ONE, "[=<optspec>]", gettext_noop("Send Cisco Umbrella identifiers including remote IP."), NULL },
>    { 0, 0, NULL, NULL, NULL }
>  }; 
>  
> @@ -653,7 +656,7 @@ static char *canonicalise_opt(char *s)
>    return ret;
>  }
>  
> -static int atoi_check(char *a, int *res)
> +static int numeric_check(char *a)
>  {
>    char *p;
>  
> @@ -666,10 +669,29 @@ static int atoi_check(char *a, int *res)
>       if (*p < '0' || *p > '9')
>         return 0;
>  
> +  return 1;
> +}
> +
> +static int atoi_check(char *a, int *res)
> +{
> +  if (!numeric_check(a))
> +    return 0;
>    *res = atoi(a);
>    return 1;
>  }
>  
> +static int strtoul_check(char *a, u32 *res)
> +{
> +  if (!numeric_check(a))
> +    return 0;
> +  *res = strtoul(a, NULL, 10);
> +  if (errno == ERANGE) {
> +    errno = 0;
> +    return 0;
> +  }
> +  return 1;
> +}
> +
>  static int atoi_check16(char *a, int *res)
>  {
>    if (!(atoi_check(a, res)) ||
> @@ -2409,6 +2431,41 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
>  	daemon->dns_client_id = opt_string_alloc(arg);
>        break;
>  
> +    case LOPT_UMBRELLA: /* --umbrella */
> +      set_option_bool(OPT_UMBRELLA);
> +      while (arg) {
> +        comma = split(arg);
> +        if (strstr(arg, "deviceid:")) {
> +          arg += 9;
> +          if (strlen(arg) != 16)
> +              ret_err(gen_err);
> +          for (char *p = arg; *p; p++) {
> +            if (!isxdigit((int)*p))
> +              ret_err(gen_err);
> +          }
> +          set_option_bool(OPT_UMBRELLA_DEVID);
> +
> +          u8 *u = daemon->umbrella_device;
> +          char word[3];
> +          for (u8 i = 0; i < sizeof(daemon->umbrella_device); i++, arg+=2) {
> +            memcpy(word, &(arg[0]), 2);
> +            *u++ = strtoul(word, NULL, 16);
> +          }
> +        }
> +        else if (strstr(arg, "orgid:")) {
> +          if (!strtoul_check(arg+6, &daemon->umbrella_org)) {
> +            ret_err(gen_err);
> +          }
> +        }
> +        else if (strstr(arg, "assetid:")) {
> +          if (!strtoul_check(arg+8, &daemon->umbrella_asset)) {
> +            ret_err(gen_err);
> +          }
> +        }
> +        arg = comma;
> +      }
> +      break;
> +
>      case LOPT_ADD_MAC: /* --add-mac */
>        if (!arg)
>  	set_option_bool(OPT_ADD_MAC);
> 




More information about the Dnsmasq-discuss mailing list