[Dnsmasq-discuss] [PATCH v2] Support Cisco Umbrella/OpenDNS Device ID & Remote IP
Brian Hartvigsen
brian.andrew at brianandjenny.com
Thu Apr 8 02:16:10 UTC 2021
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 | 6 ++++-
src/edns0.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++
src/option.c | 32 ++++++++++++++++++++++++
5 files changed, 107 insertions(+), 2 deletions(-)
diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
index fce580f..53118c1 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..5459cef 100644
--- a/src/dnsmasq.h
+++ b/src/dnsmasq.h
@@ -270,7 +270,8 @@ 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_LAST 64
#define OPTION_BITS (sizeof(unsigned int)*8)
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
@@ -1061,6 +1062,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;
+ int umbrella_org;
+ int umbrella_asset;
+ u64 umbrella_device;
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..c5892f8 100644
--- a/src/edns0.c
+++ b/src/edns0.c
@@ -427,6 +427,65 @@ 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_ORG 0x0008
+#define UMBRELLA_ORGSZ sizeof(u32)
+#define UMBRELLA_IPV4 0x0010
+#define UMBRELLA_IPV6 0x0020
+#define UMBRELLA_DEVICE 0x0040
+#define UMBRELLA_DEVICESZ sizeof(u64)
+
+struct umbrella_opt {
+ u8 magic[4];
+ u8 version;
+ u8 flags;
+ /* We have 3 possible fields since we'll never send both IPv4 and
+ * IPv6, so using the larger of the two to calculate max buffer size.
+ * Add the size of each possible field and the type field to give max
+ * buffer size.
+ */
+ u8 fields[3 * UMBRELLA_TYPESZ + UMBRELLA_ORGSZ + IN6ADDRSZ + UMBRELLA_DEVICESZ];
+};
+
+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 (daemon->umbrella_device) {
+ PUTSHORT(UMBRELLA_DEVICE, u);
+ PUTLONG(daemon->umbrella_device >> 32, u)
+ PUTLONG(daemon->umbrella_device & 0xffffffff, u)
+ }
+
+ 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 +504,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..03668a2 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 }
};
@@ -2409,6 +2412,35 @@ 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);
+ }
+ daemon->umbrella_device = strtoull(arg, NULL, 16);
+ if (errno == ERANGE || daemon->umbrella_device == 0) {
+ ret_err(gen_err);
+ }
+ }
+ else if (strstr(arg, "orgid:")) {
+ if (!atoi_check(arg+6, &daemon->umbrella_org))
+ ret_err(gen_err);
+ }
+ else if (strstr(arg, "assetid:")) {
+ if (!atoi_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);
--
2.29.2
More information about the Dnsmasq-discuss
mailing list