mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
Implement RFC-4388 DHCPv4 leasequery.
This commit is contained in:
@@ -51,6 +51,9 @@ version 2.92
|
|||||||
server configured, queries are never sent upstream so they are never
|
server configured, queries are never sent upstream so they are never
|
||||||
validated and the new behaviour is moot.
|
validated and the new behaviour is moot.
|
||||||
|
|
||||||
|
Add support for leasequery to the dnsmasq DHCPv4 server.
|
||||||
|
This has to be specifically enabled with the --leasequery option.
|
||||||
|
|
||||||
|
|
||||||
version 2.91
|
version 2.91
|
||||||
Fix spurious "resource limit exceeded messages". Thanks to
|
Fix spurious "resource limit exceeded messages". Thanks to
|
||||||
|
|||||||
692
contrib/leasequery/leasequery.c
Normal file
692
contrib/leasequery/leasequery.c
Normal file
@@ -0,0 +1,692 @@
|
|||||||
|
/* leasequery is Copyright (c) 2025 Simon Kelley
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; version 2 dated June, 1991, or
|
||||||
|
(at your option) version 3 dated 29 June, 2007.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Author's email: simon@thekelleys.org.uk */
|
||||||
|
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <grp.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
typedef unsigned char u8;
|
||||||
|
typedef unsigned short u16;
|
||||||
|
typedef unsigned int u32;
|
||||||
|
typedef unsigned long long u64;
|
||||||
|
|
||||||
|
#define INADDRSZ 4
|
||||||
|
#define ADDRSTRLEN 46
|
||||||
|
|
||||||
|
#define option_len(opt) ((int)(((unsigned char *)(opt))[1]))
|
||||||
|
#define option_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2u+(unsigned int)(i)]))
|
||||||
|
|
||||||
|
#define DHCP_CHADDR_MAX 16
|
||||||
|
#define DHCP_SERVER_PORT 67
|
||||||
|
#define DHCP_CLIENT_PORT 68
|
||||||
|
#define BOOTREQUEST 1
|
||||||
|
#define BOOTREPLY 2
|
||||||
|
|
||||||
|
#define DHCP_COOKIE 0x63825363
|
||||||
|
|
||||||
|
#define DHCPLEASEQUERY 10
|
||||||
|
#define DHCPLEASEUNASSIGNED 11
|
||||||
|
#define DHCPLEASEUNKNOWN 12
|
||||||
|
#define DHCPLEASEACTIVE 13
|
||||||
|
|
||||||
|
#define OPTION_END 255
|
||||||
|
#define OPTION_ROUTER 3
|
||||||
|
#define OPTION_VENDOR_CLASS_OPT 43
|
||||||
|
#define OPTION_LEASE_TIME 51
|
||||||
|
#define OPTION_MESSAGE_TYPE 53
|
||||||
|
#define OPTION_SERVER_IDENTIFIER 54
|
||||||
|
#define OPTION_REQUESTED_OPTIONS 55
|
||||||
|
#define OPTION_VENDOR_ID 60
|
||||||
|
#define OPTION_CLIENT_ID 61
|
||||||
|
|
||||||
|
/* flags in top of length field for DHCP-option tables */
|
||||||
|
#define OT_ADDR_LIST 0x8000
|
||||||
|
#define OT_RFC1035_NAME 0x4000
|
||||||
|
#define OT_INTERNAL 0x2000
|
||||||
|
#define OT_NAME 0x1000
|
||||||
|
#define OT_CSTRING 0x0800
|
||||||
|
#define OT_DEC 0x0400
|
||||||
|
#define OT_TIME 0x0200
|
||||||
|
|
||||||
|
#define GETSHORT(s, cp) do { \
|
||||||
|
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||||
|
(s) = ((u16)t_cp[0] << 8) \
|
||||||
|
| ((u16)t_cp[1]) \
|
||||||
|
; \
|
||||||
|
(cp) += 2; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define GETLONG(l, cp) do { \
|
||||||
|
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||||
|
(l) = ((u32)t_cp[0] << 24) \
|
||||||
|
| ((u32)t_cp[1] << 16) \
|
||||||
|
| ((u32)t_cp[2] << 8) \
|
||||||
|
| ((u32)t_cp[3]) \
|
||||||
|
; \
|
||||||
|
(cp) += 4; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define PUTSHORT(s, cp) do { \
|
||||||
|
u16 t_s = (u16)(s); \
|
||||||
|
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||||
|
*t_cp++ = t_s >> 8; \
|
||||||
|
*t_cp = t_s; \
|
||||||
|
(cp) += 2; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define PUTLONG(l, cp) do { \
|
||||||
|
u32 t_l = (u32)(l); \
|
||||||
|
unsigned char *t_cp = (unsigned char *)(cp); \
|
||||||
|
*t_cp++ = t_l >> 24; \
|
||||||
|
*t_cp++ = t_l >> 16; \
|
||||||
|
*t_cp++ = t_l >> 8; \
|
||||||
|
*t_cp = t_l; \
|
||||||
|
(cp) += 4; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
union all_addr {
|
||||||
|
struct in_addr addr4;
|
||||||
|
struct in6_addr addr6;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dhcp_packet_with_opts{
|
||||||
|
struct dhcp_packet {
|
||||||
|
unsigned char op, htype, hlen, hops;
|
||||||
|
unsigned int xid;
|
||||||
|
unsigned short secs, flags;
|
||||||
|
struct in_addr ciaddr, yiaddr, siaddr, giaddr;
|
||||||
|
unsigned char chaddr[DHCP_CHADDR_MAX], sname[64], file[128];
|
||||||
|
} header;
|
||||||
|
unsigned char options[312];
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct opttab_t {
|
||||||
|
char *name;
|
||||||
|
u16 val, size;
|
||||||
|
} opttab[] = {
|
||||||
|
{ "netmask", 1, OT_ADDR_LIST },
|
||||||
|
{ "time-offset", 2, 4 },
|
||||||
|
{ "router", 3, OT_ADDR_LIST },
|
||||||
|
{ "dns-server", 6, OT_ADDR_LIST },
|
||||||
|
{ "log-server", 7, OT_ADDR_LIST },
|
||||||
|
{ "lpr-server", 9, OT_ADDR_LIST },
|
||||||
|
{ "hostname", 12, OT_INTERNAL | OT_NAME },
|
||||||
|
{ "boot-file-size", 13, 2 | OT_DEC },
|
||||||
|
{ "domain-name", 15, OT_NAME },
|
||||||
|
{ "swap-server", 16, OT_ADDR_LIST },
|
||||||
|
{ "root-path", 17, OT_NAME },
|
||||||
|
{ "extension-path", 18, OT_NAME },
|
||||||
|
{ "ip-forward-enable", 19, 1 },
|
||||||
|
{ "non-local-source-routing", 20, 1 },
|
||||||
|
{ "policy-filter", 21, OT_ADDR_LIST },
|
||||||
|
{ "max-datagram-reassembly", 22, 2 | OT_DEC },
|
||||||
|
{ "default-ttl", 23, 1 | OT_DEC },
|
||||||
|
{ "mtu", 26, 2 | OT_DEC },
|
||||||
|
{ "all-subnets-local", 27, 1 },
|
||||||
|
{ "broadcast", 28, OT_INTERNAL | OT_ADDR_LIST },
|
||||||
|
{ "router-discovery", 31, 1 },
|
||||||
|
{ "router-solicitation", 32, OT_ADDR_LIST },
|
||||||
|
{ "static-route", 33, OT_ADDR_LIST },
|
||||||
|
{ "trailer-encapsulation", 34, 1 },
|
||||||
|
{ "arp-timeout", 35, 4 | OT_DEC },
|
||||||
|
{ "ethernet-encap", 36, 1 },
|
||||||
|
{ "tcp-ttl", 37, 1 },
|
||||||
|
{ "tcp-keepalive", 38, 4 | OT_DEC },
|
||||||
|
{ "nis-domain", 40, OT_NAME },
|
||||||
|
{ "nis-server", 41, OT_ADDR_LIST },
|
||||||
|
{ "ntp-server", 42, OT_ADDR_LIST },
|
||||||
|
{ "vendor-encap", 43, OT_INTERNAL },
|
||||||
|
{ "netbios-ns", 44, OT_ADDR_LIST },
|
||||||
|
{ "netbios-dd", 45, OT_ADDR_LIST },
|
||||||
|
{ "netbios-nodetype", 46, 1 },
|
||||||
|
{ "netbios-scope", 47, 0 },
|
||||||
|
{ "x-windows-fs", 48, OT_ADDR_LIST },
|
||||||
|
{ "x-windows-dm", 49, OT_ADDR_LIST },
|
||||||
|
{ "requested-address", 50, OT_INTERNAL | OT_ADDR_LIST },
|
||||||
|
{ "lease-time", 51, OT_INTERNAL | OT_TIME },
|
||||||
|
{ "option-overload", 52, OT_INTERNAL },
|
||||||
|
{ "message-type", 53, OT_INTERNAL | OT_DEC },
|
||||||
|
{ "server-identifier", 54, OT_INTERNAL | OT_ADDR_LIST },
|
||||||
|
{ "parameter-request", 55, OT_INTERNAL },
|
||||||
|
{ "message", 56, OT_INTERNAL },
|
||||||
|
{ "max-message-size", 57, OT_INTERNAL },
|
||||||
|
{ "T1", 58, OT_TIME},
|
||||||
|
{ "T2", 59, OT_TIME},
|
||||||
|
{ "vendor-class", 60, OT_NAME },
|
||||||
|
{ "client-id", 61, OT_INTERNAL },
|
||||||
|
{ "nis+-domain", 64, OT_NAME },
|
||||||
|
{ "nis+-server", 65, OT_ADDR_LIST },
|
||||||
|
{ "tftp-server", 66, OT_NAME },
|
||||||
|
{ "bootfile-name", 67, OT_NAME },
|
||||||
|
{ "mobile-ip-home", 68, OT_ADDR_LIST },
|
||||||
|
{ "smtp-server", 69, OT_ADDR_LIST },
|
||||||
|
{ "pop3-server", 70, OT_ADDR_LIST },
|
||||||
|
{ "nntp-server", 71, OT_ADDR_LIST },
|
||||||
|
{ "irc-server", 74, OT_ADDR_LIST },
|
||||||
|
{ "user-class", 77, 0 },
|
||||||
|
{ "rapid-commit", 80, 0 },
|
||||||
|
{ "FQDN", 81, OT_INTERNAL },
|
||||||
|
{ "agent-info", 82, OT_INTERNAL },
|
||||||
|
{ "last-transaction", 91, 4 | OT_TIME },
|
||||||
|
{ "associated-ip", 92, OT_ADDR_LIST },
|
||||||
|
{ "client-arch", 93, 2 | OT_DEC },
|
||||||
|
{ "client-interface-id", 94, 0 },
|
||||||
|
{ "client-machine-id", 97, 0 },
|
||||||
|
{ "posix-timezone", 100, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||||
|
{ "tzdb-timezone", 101, OT_NAME }, /* RFC 4833, Sec. 2 */
|
||||||
|
{ "ipv6-only", 108, 4 | OT_DEC }, /* RFC 8925 */
|
||||||
|
{ "subnet-select", 118, OT_INTERNAL },
|
||||||
|
{ "domain-search", 119, OT_RFC1035_NAME },
|
||||||
|
{ "sip-server", 120, 0 },
|
||||||
|
{ "classless-static-route", 121, 0 },
|
||||||
|
{ "vendor-id-encap", 125, 0 },
|
||||||
|
{ "tftp-server-address", 150, OT_ADDR_LIST },
|
||||||
|
{ "server-ip-address", 255, OT_ADDR_LIST }, /* special, internal only, sets siaddr */
|
||||||
|
{ NULL, 0, 0 }
|
||||||
|
};
|
||||||
|
|
||||||
|
static void prettyprint_time(char *buf, unsigned int t);
|
||||||
|
static char *print_mac(char *buff, unsigned char *mac, int len);
|
||||||
|
|
||||||
|
int lookup_dhcp_opt(char *name)
|
||||||
|
{
|
||||||
|
const struct opttab_t *t = opttab;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; t[i].name; i++)
|
||||||
|
if (strcasecmp(t[i].name, name) == 0)
|
||||||
|
return t[i].val;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *option_string(unsigned int opt, unsigned char *val, int opt_len, char *buf, int buf_len)
|
||||||
|
{
|
||||||
|
int o, i, j, nodecode = 0;
|
||||||
|
const struct opttab_t *ot = opttab;
|
||||||
|
char addrbuff[ADDRSTRLEN];
|
||||||
|
|
||||||
|
for (o = 0; ot[o].name; o++)
|
||||||
|
if (ot[o].val == opt)
|
||||||
|
{
|
||||||
|
if (buf)
|
||||||
|
{
|
||||||
|
memset(buf, 0, buf_len);
|
||||||
|
|
||||||
|
if (ot[o].size & OT_ADDR_LIST)
|
||||||
|
{
|
||||||
|
union all_addr addr;
|
||||||
|
int addr_len = INADDRSZ;
|
||||||
|
|
||||||
|
for (buf[0]= 0, i = 0; i <= opt_len - addr_len; i += addr_len)
|
||||||
|
{
|
||||||
|
if (i != 0)
|
||||||
|
strncat(buf, ", ", buf_len - strlen(buf));
|
||||||
|
/* align */
|
||||||
|
memcpy(&addr, &val[i], addr_len);
|
||||||
|
inet_ntop(AF_INET, &val[i], addrbuff, ADDRSTRLEN);
|
||||||
|
strncat(buf, addrbuff, buf_len - strlen(buf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (ot[o].size & OT_NAME)
|
||||||
|
for (i = 0, j = 0; i < opt_len && j < buf_len ; i++)
|
||||||
|
{
|
||||||
|
char c = val[i];
|
||||||
|
if (isprint((unsigned char)c))
|
||||||
|
buf[j++] = c;
|
||||||
|
}
|
||||||
|
else if ((ot[o].size & (OT_DEC | OT_TIME)) && opt_len != 0)
|
||||||
|
{
|
||||||
|
unsigned int dec = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < opt_len; i++)
|
||||||
|
dec = (dec << 8) | val[i];
|
||||||
|
|
||||||
|
if (ot[o].size & OT_TIME)
|
||||||
|
prettyprint_time(buf, dec);
|
||||||
|
else
|
||||||
|
sprintf(buf, "%u", dec);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nodecode = 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opt_len != 0 && buf && (!ot[o].name || nodecode))
|
||||||
|
{
|
||||||
|
int trunc = 0;
|
||||||
|
if (opt_len > 14)
|
||||||
|
{
|
||||||
|
trunc = 1;
|
||||||
|
opt_len = 14;
|
||||||
|
}
|
||||||
|
print_mac(buf, val, opt_len);
|
||||||
|
if (trunc)
|
||||||
|
strncat(buf, "...", buf_len - strlen(buf));
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return ot[o].name ? ot[o].name : "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void prettyprint_time(char *buf, unsigned int t)
|
||||||
|
{
|
||||||
|
if (t == 0xffffffff)
|
||||||
|
sprintf(buf, "infinite");
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int x, p = 0;
|
||||||
|
if ((x = t/86400))
|
||||||
|
p += sprintf(&buf[p], "%ud", x);
|
||||||
|
if ((x = (t/3600)%24))
|
||||||
|
p += sprintf(&buf[p], "%uh", x);
|
||||||
|
if ((x = (t/60)%60))
|
||||||
|
p += sprintf(&buf[p], "%um", x);
|
||||||
|
if ((x = t%60))
|
||||||
|
sprintf(&buf[p], "%us", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *print_mac(char *buff, unsigned char *mac, int len)
|
||||||
|
{
|
||||||
|
char *p = buff;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
sprintf(p, "<null>");
|
||||||
|
else
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
p += sprintf(p, "%.2x%s", mac[i], (i == len - 1) ? "" : ":");
|
||||||
|
|
||||||
|
return buff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *dhcp_skip_opts(struct dhcp_packet_with_opts *pktp)
|
||||||
|
{
|
||||||
|
unsigned char *start = (unsigned char *)(((unsigned int *)&pktp->options[0]) + 1);
|
||||||
|
while (*start != 0)
|
||||||
|
start += start[1] + 2;
|
||||||
|
return start;
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned char *dhcp_find_opt(struct dhcp_packet_with_opts *pktp, int optno)
|
||||||
|
{
|
||||||
|
unsigned char *start = (unsigned char *)(((unsigned int *)&pktp->options[0]) + 1);
|
||||||
|
while (*start != OPTION_END)
|
||||||
|
{
|
||||||
|
if (*start == optno)
|
||||||
|
return start;
|
||||||
|
start += start[1] + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void option_put(struct dhcp_packet_with_opts *pktp, int opt, int len, unsigned int val)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
unsigned char *p = dhcp_skip_opts(pktp);
|
||||||
|
|
||||||
|
if (p)
|
||||||
|
{
|
||||||
|
*p++ = opt;
|
||||||
|
*p++ = len;
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
*(p++) = val >> (8 * (len - (i + 1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void option_put_string(struct dhcp_packet_with_opts *pktp, int opt,
|
||||||
|
const char *string, int null_term)
|
||||||
|
{
|
||||||
|
unsigned char *p;
|
||||||
|
size_t len = strlen(string);
|
||||||
|
|
||||||
|
if (null_term && len != 255)
|
||||||
|
len++;
|
||||||
|
|
||||||
|
if ((p = dhcp_skip_opts(pktp)))
|
||||||
|
{
|
||||||
|
*p++ = opt;
|
||||||
|
*p++ = len;
|
||||||
|
memcpy(p, string, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* in may equal out, when maxlen may be -1 (No max len).
|
||||||
|
Return -1 for extraneous no-hex chars found. */
|
||||||
|
int parse_hex(char *in, unsigned char *out, int maxlen,
|
||||||
|
unsigned int *wildcard_mask, int *mac_type)
|
||||||
|
{
|
||||||
|
int done = 0, mask = 0, i = 0;
|
||||||
|
char *r;
|
||||||
|
|
||||||
|
if (mac_type)
|
||||||
|
*mac_type = 0;
|
||||||
|
|
||||||
|
while (!done && (maxlen == -1 || i < maxlen))
|
||||||
|
{
|
||||||
|
for (r = in; *r != 0 && *r != ':' && *r != '-' && *r != ' '; r++)
|
||||||
|
if (*r != '*' && !isxdigit((unsigned char)*r))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (*r == 0)
|
||||||
|
done = 1;
|
||||||
|
|
||||||
|
if (r != in )
|
||||||
|
{
|
||||||
|
if (*r == '-' && i == 0 && mac_type)
|
||||||
|
{
|
||||||
|
*r = 0;
|
||||||
|
*mac_type = strtol(in, NULL, 16);
|
||||||
|
mac_type = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
*r = 0;
|
||||||
|
if (strcmp(in, "*") == 0)
|
||||||
|
{
|
||||||
|
mask = (mask << 1) | 1;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
int j, bytes = (1 + (r - in))/2;
|
||||||
|
for (j = 0; j < bytes; j++)
|
||||||
|
{
|
||||||
|
char sav;
|
||||||
|
if (j < bytes - 1)
|
||||||
|
{
|
||||||
|
sav = in[(j+1)*2];
|
||||||
|
in[(j+1)*2] = 0;
|
||||||
|
}
|
||||||
|
/* checks above allow mix of hexdigit and *, which
|
||||||
|
is illegal. */
|
||||||
|
if (strchr(&in[j*2], '*'))
|
||||||
|
return -1;
|
||||||
|
out[i] = strtol(&in[j*2], NULL, 16);
|
||||||
|
mask = mask << 1;
|
||||||
|
if (++i == maxlen)
|
||||||
|
break;
|
||||||
|
if (j < bytes - 1)
|
||||||
|
in[(j+1)*2] = sav;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in = r+1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wildcard_mask)
|
||||||
|
*wildcard_mask = mask;
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *split_chr(char *s, char c)
|
||||||
|
{
|
||||||
|
char *comma, *p;
|
||||||
|
|
||||||
|
if (!s || !(comma = strchr(s, c)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
p = comma;
|
||||||
|
*comma = ' ';
|
||||||
|
|
||||||
|
for (; *comma == ' '; comma++);
|
||||||
|
|
||||||
|
for (; (p >= s) && *p == ' '; p--)
|
||||||
|
*p = 0;
|
||||||
|
|
||||||
|
return comma;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *split(char *s)
|
||||||
|
{
|
||||||
|
return split_chr(s, ',');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
struct ifreq ifr;
|
||||||
|
struct in_addr iface_addr, server_addr, lease_addr;
|
||||||
|
struct dhcp_packet_with_opts pkt;
|
||||||
|
struct sockaddr_in saddr;
|
||||||
|
unsigned char *p;
|
||||||
|
ssize_t sz;
|
||||||
|
unsigned char mac[DHCP_CHADDR_MAX], clid[256], req_options[256];
|
||||||
|
char buff[500];
|
||||||
|
int clid_len = 0, mac_len = 0, mac_type = 0, opts_len = 0;
|
||||||
|
unsigned short port = DHCP_SERVER_PORT;
|
||||||
|
unsigned int xid;
|
||||||
|
struct timeval tv;
|
||||||
|
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
srand(tv.tv_usec);
|
||||||
|
xid = rand();
|
||||||
|
server_addr.s_addr = lease_addr.s_addr = iface_addr.s_addr = 0;
|
||||||
|
|
||||||
|
if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
|
||||||
|
{
|
||||||
|
perror("leasequery: cannot create socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
int option = getopt(argc, argv, "i:s:l:m:c:p:r:");
|
||||||
|
|
||||||
|
if (option == -1)
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (option)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
fprintf(stderr,
|
||||||
|
"-p <port> port on DHCP server.\n"
|
||||||
|
"-i <interface> exit interface to DHCP server.\n"
|
||||||
|
"-s <inet-addr> DHCP server address.\n"
|
||||||
|
"-l <inet-addr> Query lease by IP address.\n"
|
||||||
|
"-m <MAC-addr> Query lease by MAC address.\n"
|
||||||
|
"-c <hex> Query lease by client-id.\n"
|
||||||
|
"-r <name>|<int>[,...] List of options to return.\n");
|
||||||
|
exit(1);
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
port = atoi(optarg);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'i':
|
||||||
|
strncpy(ifr.ifr_name, optarg, IF_NAMESIZE);
|
||||||
|
ifr.ifr_addr.sa_family = AF_INET;
|
||||||
|
if (ioctl(fd, SIOCGIFADDR, &ifr) == -1)
|
||||||
|
{
|
||||||
|
perror("leasequery: cannot get interface address");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
iface_addr = ((struct sockaddr_in *) &ifr.ifr_addr)->sin_addr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 's':
|
||||||
|
if (inet_pton(AF_INET, optarg, &server_addr) <= 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "leasequery: bad server address\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
if (inet_pton(AF_INET, optarg, &lease_addr) <= 0)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "leasequery: bad lease address\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'm':
|
||||||
|
mac_type = 1; /* default ethernet */
|
||||||
|
mac_len = parse_hex(optarg, mac, DHCP_CHADDR_MAX, NULL, &mac_type);
|
||||||
|
if (!mac_type)
|
||||||
|
mac_type = 1; /* default ethernet */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'c':
|
||||||
|
clid_len = parse_hex(optarg, clid, 256, NULL, NULL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r':
|
||||||
|
{
|
||||||
|
char *comma;
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
while (optarg)
|
||||||
|
{
|
||||||
|
comma = split(optarg);
|
||||||
|
|
||||||
|
if ((opt = lookup_dhcp_opt(optarg)) != -1 || (opt = atoi(optarg)) != 0)
|
||||||
|
req_options[opts_len++] = opt;
|
||||||
|
|
||||||
|
optarg = comma;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!server_addr.s_addr)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "leasequery: no server address\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&pkt, 0, sizeof pkt);
|
||||||
|
pkt.header.op = BOOTREQUEST;
|
||||||
|
pkt.header.xid = xid;
|
||||||
|
pkt.header.ciaddr = lease_addr;
|
||||||
|
pkt.header.hlen = mac_len;
|
||||||
|
pkt.header.htype = mac_type;
|
||||||
|
memcpy(pkt.header.chaddr, mac, mac_len);
|
||||||
|
*((unsigned int *)&pkt.options[0]) = htonl(DHCP_COOKIE);
|
||||||
|
|
||||||
|
/* Dnsmasq extension. */
|
||||||
|
pkt.header.giaddr.s_addr = iface_addr.s_addr ? iface_addr.s_addr : INADDR_BROADCAST;
|
||||||
|
|
||||||
|
option_put(&pkt, OPTION_MESSAGE_TYPE, 1, DHCPLEASEQUERY);
|
||||||
|
|
||||||
|
if (clid_len != 0)
|
||||||
|
{
|
||||||
|
p = dhcp_skip_opts(&pkt);
|
||||||
|
*p++ = OPTION_CLIENT_ID;
|
||||||
|
*p++ = clid_len;
|
||||||
|
memcpy(p, clid, clid_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts_len != 0)
|
||||||
|
{
|
||||||
|
p = dhcp_skip_opts(&pkt);
|
||||||
|
*p++ = OPTION_REQUESTED_OPTIONS;
|
||||||
|
*p++ = opts_len;
|
||||||
|
memcpy(p, req_options, opts_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iface_addr.s_addr)
|
||||||
|
{
|
||||||
|
saddr.sin_family = AF_INET;
|
||||||
|
saddr.sin_port = htons(port);
|
||||||
|
saddr.sin_addr.s_addr = iface_addr.s_addr;
|
||||||
|
|
||||||
|
if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)))
|
||||||
|
{
|
||||||
|
perror("leasequery: cannot bind DHCP server socket");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saddr.sin_family = AF_INET;
|
||||||
|
saddr.sin_port = htons(port);
|
||||||
|
saddr.sin_addr = server_addr;
|
||||||
|
|
||||||
|
while((sz = sendto(fd, &pkt, dhcp_skip_opts(&pkt) - ((unsigned char *)&pkt), 0, (struct sockaddr *)&saddr, sizeof(saddr))) == -1 &&
|
||||||
|
errno == EINTR);
|
||||||
|
|
||||||
|
if (sz == -1)
|
||||||
|
{
|
||||||
|
perror("leasequery: sendto()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
sz = recv(fd, &pkt, sizeof(pkt), 0);
|
||||||
|
|
||||||
|
if (sz == -1)
|
||||||
|
{
|
||||||
|
perror("leasequery: recv()");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sz >= sizeof(pkt.header) &&
|
||||||
|
pkt.header.op == BOOTREPLY &&
|
||||||
|
(p = dhcp_find_opt(&pkt, OPTION_MESSAGE_TYPE)) &&
|
||||||
|
pkt.header.xid == xid)
|
||||||
|
{
|
||||||
|
if (p[2] == DHCPLEASEUNASSIGNED)
|
||||||
|
printf("UNASSIGNED\n");
|
||||||
|
else if (p[2] == DHCPLEASEUNKNOWN)
|
||||||
|
printf("UNKNOWN\n");
|
||||||
|
else if (p[2] == DHCPLEASEACTIVE)
|
||||||
|
{
|
||||||
|
print_mac(buff, pkt.header.chaddr, pkt.header.hlen);
|
||||||
|
printf("ACTIVE %s %s\n", inet_ntoa(pkt.header.ciaddr), buff);
|
||||||
|
|
||||||
|
p = (unsigned char *)(((unsigned int *)&pkt.options[0]) + 1);
|
||||||
|
|
||||||
|
while (*p != OPTION_END)
|
||||||
|
{
|
||||||
|
char *optname = option_string(p[0], option_ptr(p, 0), option_len(p), buff, 500);
|
||||||
|
|
||||||
|
printf("size:%3d option:%3d %s %s\n", option_len(p), p[0], optname, buff);
|
||||||
|
p += p[1] + 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1438,6 +1438,16 @@ DHCP options. This make extra space available in the DHCP packet for
|
|||||||
options but can, rarely, confuse old or broken clients. This flag
|
options but can, rarely, confuse old or broken clients. This flag
|
||||||
forces "simple and safe" behaviour to avoid problems in such a case.
|
forces "simple and safe" behaviour to avoid problems in such a case.
|
||||||
.TP
|
.TP
|
||||||
|
.B --leasequery
|
||||||
|
Enable RFC 4388 leasequery. The dnsmasq DHCP server will answer LEASEQUERY messages from DHCP relays
|
||||||
|
when this option is given. To correctly answer lease queries it is necessary to store extra data in
|
||||||
|
the lease database, and this is also enabled by the \fB--leasequery\fP option. The extra fields
|
||||||
|
(agent-info and vendorclass) are stored in the leases file in a somewhat backwards compatible manner.
|
||||||
|
Enabling and then disabling leasequery will not cause problems; the extra information will be aged
|
||||||
|
out of the database. However, enabling leasequery in release 2.92 or later, storing leases
|
||||||
|
which come via DHCP relays which add option-82 agent-info data, and then moving back to a pre-2.92 dnsmasq
|
||||||
|
binary may cause problems reading the leases file. As of release 2.92, leasequery is only supported for DHCPv4.
|
||||||
|
.TP
|
||||||
.B --dhcp-relay=<local address>[,<server address>[#<server port>]][,<interface]
|
.B --dhcp-relay=<local address>[,<server address>[#<server port>]][,<interface]
|
||||||
Configure dnsmasq to do DHCP relay. The local address is an address
|
Configure dnsmasq to do DHCP relay. The local address is an address
|
||||||
allocated to an interface on the host running dnsmasq. All DHCP
|
allocated to an interface on the host running dnsmasq. All DHCP
|
||||||
|
|||||||
@@ -701,7 +701,9 @@ static const struct opttab_t {
|
|||||||
{ "user-class", 77, 0 },
|
{ "user-class", 77, 0 },
|
||||||
{ "rapid-commit", 80, 0 },
|
{ "rapid-commit", 80, 0 },
|
||||||
{ "FQDN", 81, OT_INTERNAL },
|
{ "FQDN", 81, OT_INTERNAL },
|
||||||
{ "agent-id", 82, OT_INTERNAL },
|
{ "agent-info", 82, OT_INTERNAL },
|
||||||
|
{ "last-transaction", 91, 4 | OT_TIME },
|
||||||
|
{ "associated-ip", 92, OT_ADDR_LIST },
|
||||||
{ "client-arch", 93, 2 | OT_DEC },
|
{ "client-arch", 93, 2 | OT_DEC },
|
||||||
{ "client-interface-id", 94, 0 },
|
{ "client-interface-id", 94, 0 },
|
||||||
{ "client-machine-id", 97, 0 },
|
{ "client-machine-id", 97, 0 },
|
||||||
|
|||||||
@@ -57,6 +57,8 @@
|
|||||||
#define OPTION_RAPID_COMMIT 80
|
#define OPTION_RAPID_COMMIT 80
|
||||||
#define OPTION_CLIENT_FQDN 81
|
#define OPTION_CLIENT_FQDN 81
|
||||||
#define OPTION_AGENT_ID 82
|
#define OPTION_AGENT_ID 82
|
||||||
|
#define OPTION_LAST_TRANSACTION 91
|
||||||
|
#define OPTION_ASSOCIATED_IP 92
|
||||||
#define OPTION_ARCH 93
|
#define OPTION_ARCH 93
|
||||||
#define OPTION_PXE_UUID 97
|
#define OPTION_PXE_UUID 97
|
||||||
#define OPTION_SUBNET_SELECT 118
|
#define OPTION_SUBNET_SELECT 118
|
||||||
@@ -87,6 +89,11 @@
|
|||||||
#define DHCPNAK 6
|
#define DHCPNAK 6
|
||||||
#define DHCPRELEASE 7
|
#define DHCPRELEASE 7
|
||||||
#define DHCPINFORM 8
|
#define DHCPINFORM 8
|
||||||
|
#define DHCPFORCERENEW 9
|
||||||
|
#define DHCPLEASEQUERY 10
|
||||||
|
#define DHCPLEASEUNASSIGNED 11
|
||||||
|
#define DHCPLEASEUNKNOWN 12
|
||||||
|
#define DHCPLEASEACTIVE 13
|
||||||
|
|
||||||
#define BRDBAND_FORUM_IANA 3561 /* Broadband forum IANA enterprise */
|
#define BRDBAND_FORUM_IANA 3561 /* Broadband forum IANA enterprise */
|
||||||
|
|
||||||
|
|||||||
28
src/dhcp.c
28
src/dhcp.c
@@ -135,7 +135,7 @@ void dhcp_packet(time_t now, int pxe_fd)
|
|||||||
struct dhcp_packet *mess;
|
struct dhcp_packet *mess;
|
||||||
struct dhcp_context *context;
|
struct dhcp_context *context;
|
||||||
struct dhcp_relay *relay;
|
struct dhcp_relay *relay;
|
||||||
int is_relay_reply = 0;
|
int is_relay_reply = 0, is_relay_use_source = 0;
|
||||||
struct iname *tmp;
|
struct iname *tmp;
|
||||||
struct ifreq ifr;
|
struct ifreq ifr;
|
||||||
struct msghdr msg;
|
struct msghdr msg;
|
||||||
@@ -232,6 +232,17 @@ void dhcp_packet(time_t now, int pxe_fd)
|
|||||||
mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
|
mess = (struct dhcp_packet *)daemon->dhcp_packet.iov_base;
|
||||||
loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
|
loopback = !mess->giaddr.s_addr && (ifr.ifr_flags & IFF_LOOPBACK);
|
||||||
|
|
||||||
|
/* Non-standard extension:
|
||||||
|
If giaddr == 255.255.255.255 we reply to the source
|
||||||
|
address in the request packet header. This makes
|
||||||
|
stand-alone leasequery clients easier, as they
|
||||||
|
can leave source address determination to the kernel. */
|
||||||
|
if (mess->giaddr.s_addr == INADDR_BROADCAST)
|
||||||
|
{
|
||||||
|
mess->giaddr.s_addr = 0;
|
||||||
|
is_relay_use_source = 1;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_LINUX_NETWORK
|
#ifdef HAVE_LINUX_NETWORK
|
||||||
/* ARP fiddling uses original interface even if we pretend to use a different one. */
|
/* ARP fiddling uses original interface even if we pretend to use a different one. */
|
||||||
safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
|
safe_strncpy(arp_req.arp_dev, ifr.ifr_name, sizeof(arp_req.arp_dev));
|
||||||
@@ -337,8 +348,8 @@ void dhcp_packet(time_t now, int pxe_fd)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
lease_prune(NULL, now); /* lose any expired leases */
|
lease_prune(NULL, now); /* lose any expired leases */
|
||||||
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz,
|
iov.iov_len = dhcp_reply(parm.current, ifr.ifr_name, iface_index, (size_t)sz, now, unicast_dest,
|
||||||
now, unicast_dest, loopback, &is_inform, pxe_fd, iface_addr, recvtime);
|
loopback, &is_inform, pxe_fd, iface_addr, recvtime, is_relay_use_source);
|
||||||
lease_update_file(now);
|
lease_update_file(now);
|
||||||
lease_update_dns(0);
|
lease_update_dns(0);
|
||||||
|
|
||||||
@@ -365,11 +376,16 @@ void dhcp_packet(time_t now, int pxe_fd)
|
|||||||
if (mess->ciaddr.s_addr != 0)
|
if (mess->ciaddr.s_addr != 0)
|
||||||
dest.sin_addr = mess->ciaddr;
|
dest.sin_addr = mess->ciaddr;
|
||||||
}
|
}
|
||||||
else if (mess->giaddr.s_addr && !is_relay_reply)
|
if ((is_relay_use_source || mess->giaddr.s_addr) && !is_relay_reply)
|
||||||
|
{
|
||||||
|
/* Send to BOOTP relay. */
|
||||||
|
if (is_relay_use_source)
|
||||||
|
mess->giaddr.s_addr = INADDR_BROADCAST;
|
||||||
|
else
|
||||||
{
|
{
|
||||||
/* Send to BOOTP relay */
|
|
||||||
dest.sin_port = htons(daemon->dhcp_server_port);
|
|
||||||
dest.sin_addr = mess->giaddr;
|
dest.sin_addr = mess->giaddr;
|
||||||
|
dest.sin_port = htons(daemon->dhcp_server_port);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (mess->ciaddr.s_addr)
|
else if (mess->ciaddr.s_addr)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -282,7 +282,8 @@ struct event_desc {
|
|||||||
#define OPT_NO_0x20 74
|
#define OPT_NO_0x20 74
|
||||||
#define OPT_DO_0x20 75
|
#define OPT_DO_0x20 75
|
||||||
#define OPT_AUTH_LOG 76
|
#define OPT_AUTH_LOG 76
|
||||||
#define OPT_LAST 77
|
#define OPT_LEASEQUERY 77
|
||||||
|
#define OPT_LAST 78
|
||||||
|
|
||||||
#define OPTION_BITS (sizeof(unsigned int)*8)
|
#define OPTION_BITS (sizeof(unsigned int)*8)
|
||||||
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
#define OPTION_SIZE ( (OPT_LAST/OPTION_BITS)+((OPT_LAST%OPTION_BITS)!=0) )
|
||||||
@@ -853,6 +854,7 @@ struct dhcp_lease {
|
|||||||
char *old_hostname; /* hostname before it moved to another lease */
|
char *old_hostname; /* hostname before it moved to another lease */
|
||||||
int flags;
|
int flags;
|
||||||
time_t expires; /* lease expiry */
|
time_t expires; /* lease expiry */
|
||||||
|
time_t last_transaction;
|
||||||
#ifdef HAVE_BROKEN_RTC
|
#ifdef HAVE_BROKEN_RTC
|
||||||
unsigned int length;
|
unsigned int length;
|
||||||
#endif
|
#endif
|
||||||
@@ -864,6 +866,8 @@ struct dhcp_lease {
|
|||||||
int last_interface;
|
int last_interface;
|
||||||
int new_interface; /* save possible originated interface */
|
int new_interface; /* save possible originated interface */
|
||||||
int new_prefixlen; /* and its prefix length */
|
int new_prefixlen; /* and its prefix length */
|
||||||
|
unsigned char *agent_id, *vendorclass;
|
||||||
|
int agent_id_len, vendorclass_len;
|
||||||
#ifdef HAVE_DHCP6
|
#ifdef HAVE_DHCP6
|
||||||
struct in6_addr addr6;
|
struct in6_addr addr6;
|
||||||
unsigned int iaid;
|
unsigned int iaid;
|
||||||
@@ -1619,6 +1623,7 @@ void lease6_reset(void);
|
|||||||
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type,
|
struct dhcp_lease *lease6_find_by_client(struct dhcp_lease *first, int lease_type,
|
||||||
unsigned char *clid, int clid_len, unsigned int iaid);
|
unsigned char *clid, int clid_len, unsigned int iaid);
|
||||||
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
|
struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 addr);
|
||||||
|
struct dhcp_lease *lease6_find_by_plain_addr(struct in6_addr *addr);
|
||||||
u64 lease_find_max_addr6(struct dhcp_context *context);
|
u64 lease_find_max_addr6(struct dhcp_context *context);
|
||||||
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
|
void lease_ping_reply(struct in6_addr *sender, unsigned char *packet, char *interface);
|
||||||
void lease_update_slaac(time_t now);
|
void lease_update_slaac(time_t now);
|
||||||
@@ -1631,6 +1636,8 @@ void lease_set_hwaddr(struct dhcp_lease *lease, const unsigned char *hwaddr,
|
|||||||
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain);
|
void lease_set_hostname(struct dhcp_lease *lease, const char *name, int auth, char *domain, char *config_domain);
|
||||||
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
|
void lease_set_expires(struct dhcp_lease *lease, unsigned int len, time_t now);
|
||||||
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
|
void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now);
|
||||||
|
void lease_set_agent_id(struct dhcp_lease *lease, unsigned char *new, int len);
|
||||||
|
void lease_set_vendorclass(struct dhcp_lease *lease, unsigned char *new, int len);
|
||||||
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
|
struct dhcp_lease *lease_find_by_client(unsigned char *hwaddr, int hw_len, int hw_type,
|
||||||
unsigned char *clid, int clid_len);
|
unsigned char *clid, int clid_len);
|
||||||
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
|
struct dhcp_lease *lease_find_by_addr(struct in_addr addr);
|
||||||
@@ -1651,7 +1658,8 @@ void lease_add_extradata(struct dhcp_lease *lease, unsigned char *data,
|
|||||||
#ifdef HAVE_DHCP
|
#ifdef HAVE_DHCP
|
||||||
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||||
size_t sz, time_t now, int unicast_dest, int loopback,
|
size_t sz, time_t now, int unicast_dest, int loopback,
|
||||||
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime);
|
int *is_inform, int pxe, struct in_addr fallback,
|
||||||
|
time_t recvtime, int is_relay_use_source);
|
||||||
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
|
unsigned char *extended_hwaddr(int hwtype, int hwlen, unsigned char *hwaddr,
|
||||||
int clid_len, unsigned char *clid, int *len_out);
|
int clid_len, unsigned char *clid, int *len_out);
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
126
src/lease.c
126
src/lease.c
@@ -25,7 +25,7 @@ static int read_leases(time_t now, FILE *leasestream)
|
|||||||
unsigned long ei;
|
unsigned long ei;
|
||||||
union all_addr addr;
|
union all_addr addr;
|
||||||
struct dhcp_lease *lease;
|
struct dhcp_lease *lease;
|
||||||
int clid_len, hw_len, hw_type;
|
int opt_len, clid_len, hw_len, hw_type;
|
||||||
int items;
|
int items;
|
||||||
|
|
||||||
*daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0';
|
*daemon->dhcp_buff3 = *daemon->dhcp_buff2 = '\0';
|
||||||
@@ -56,6 +56,34 @@ static int read_leases(time_t now, FILE *leasestream)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Weird backwards compatible way of adding extra fields to leases */
|
||||||
|
if ((strcmp(daemon->dhcp_buff3, "vendorclass") == 0 || strcmp(daemon->dhcp_buff3, "agent-info") == 0))
|
||||||
|
{
|
||||||
|
if (fscanf(leasestream, " %764s", daemon->packet) == 1)
|
||||||
|
{
|
||||||
|
if (inet_pton(AF_INET, daemon->dhcp_buff2, &addr.addr4))
|
||||||
|
lease = lease_find_by_addr(addr.addr4);
|
||||||
|
#ifdef HAVE_DHCP6
|
||||||
|
else if (inet_pton(AF_INET6, daemon->dhcp_buff2, &addr.addr6))
|
||||||
|
lease = lease6_find_by_plain_addr(&addr.addr6);
|
||||||
|
#endif
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (lease)
|
||||||
|
{
|
||||||
|
opt_len = parse_hex(daemon->packet, (unsigned char *)daemon->packet, 255, NULL, NULL);
|
||||||
|
|
||||||
|
if (strcmp(daemon->dhcp_buff3, "vendorclass") == 0)
|
||||||
|
lease_set_vendorclass(lease, (unsigned char *)daemon->packet, opt_len);
|
||||||
|
else if (strcmp(daemon->dhcp_buff3, "agent-info") == 0)
|
||||||
|
lease_set_agent_id(lease, (unsigned char *)daemon->packet, opt_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (fscanf(leasestream, " %64s %255s %764s",
|
if (fscanf(leasestream, " %64s %255s %764s",
|
||||||
daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
|
daemon->namebuff, daemon->dhcp_buff, daemon->packet) != 3)
|
||||||
{
|
{
|
||||||
@@ -251,7 +279,7 @@ void lease_update_file(time_t now)
|
|||||||
{
|
{
|
||||||
struct dhcp_lease *lease;
|
struct dhcp_lease *lease;
|
||||||
time_t next_event;
|
time_t next_event;
|
||||||
int i, err = 0;
|
int i, err = 0, extras;
|
||||||
|
|
||||||
if (file_dirty != 0 && daemon->lease_stream)
|
if (file_dirty != 0 && daemon->lease_stream)
|
||||||
{
|
{
|
||||||
@@ -260,8 +288,10 @@ void lease_update_file(time_t now)
|
|||||||
if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
|
if (errno != 0 || ftruncate(fileno(daemon->lease_stream), 0) != 0)
|
||||||
err = errno;
|
err = errno;
|
||||||
|
|
||||||
for (lease = leases; lease; lease = lease->next)
|
for (extras = 0, lease = leases; lease; lease = lease->next)
|
||||||
{
|
{
|
||||||
|
if (lease->agent_id || lease->vendorclass)
|
||||||
|
extras = 1;
|
||||||
|
|
||||||
#ifdef HAVE_DHCP6
|
#ifdef HAVE_DHCP6
|
||||||
if (lease->flags & (LEASE_TA | LEASE_NA))
|
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||||
@@ -336,6 +366,36 @@ void lease_update_file(time_t now)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (extras)
|
||||||
|
{
|
||||||
|
/* Dump this at the end for least confusion with older parsing code. */
|
||||||
|
for (lease = leases; lease; lease = lease->next)
|
||||||
|
{
|
||||||
|
#ifdef HAVE_DHCP6
|
||||||
|
if (lease->flags & (LEASE_TA | LEASE_NA))
|
||||||
|
inet_ntop(AF_INET6, &lease->addr6, daemon->addrbuff, ADDRSTRLEN);
|
||||||
|
else
|
||||||
|
#endif
|
||||||
|
inet_ntop(AF_INET, &lease->addr, daemon->addrbuff, ADDRSTRLEN);
|
||||||
|
|
||||||
|
if (lease->agent_id)
|
||||||
|
{
|
||||||
|
ourprintf(&err, "agent-info %s ", daemon->addrbuff);
|
||||||
|
for (i = 0; i < lease->agent_id_len - 1; i++)
|
||||||
|
ourprintf(&err, "%.2x:", lease->agent_id[i]);
|
||||||
|
ourprintf(&err, "%.2x\n", lease->agent_id[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lease->vendorclass)
|
||||||
|
{
|
||||||
|
ourprintf(&err, "vendorclass %s ", daemon->addrbuff);
|
||||||
|
for (i = 0; i < lease->vendorclass_len - 1; i++)
|
||||||
|
ourprintf(&err, "%.2x:", lease->vendorclass[i]);
|
||||||
|
ourprintf(&err, "%.2x\n", lease->vendorclass[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (fflush(daemon->lease_stream) != 0 ||
|
if (fflush(daemon->lease_stream) != 0 ||
|
||||||
fsync(fileno(daemon->lease_stream)) < 0)
|
fsync(fileno(daemon->lease_stream)) < 0)
|
||||||
err = errno;
|
err = errno;
|
||||||
@@ -713,6 +773,22 @@ struct dhcp_lease *lease6_find_by_addr(struct in6_addr *net, int prefix, u64 add
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct dhcp_lease *lease6_find_by_plain_addr(struct in6_addr *addr)
|
||||||
|
{
|
||||||
|
struct dhcp_lease *lease;
|
||||||
|
|
||||||
|
for (lease = leases; lease; lease = lease->next)
|
||||||
|
{
|
||||||
|
if (!(lease->flags & (LEASE_TA | LEASE_NA)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IN6_ARE_ADDR_EQUAL(&lease->addr6, addr))
|
||||||
|
return lease;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* Find largest assigned address in context */
|
/* Find largest assigned address in context */
|
||||||
u64 lease_find_max_addr6(struct dhcp_context *context)
|
u64 lease_find_max_addr6(struct dhcp_context *context)
|
||||||
{
|
{
|
||||||
@@ -1084,6 +1160,46 @@ void lease_set_interface(struct dhcp_lease *lease, int interface, time_t now)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lease_set_agent_id(struct dhcp_lease *lease, unsigned char *new, int len)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (!lease->agent_id && !new)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lease->agent_id && new && lease->agent_id_len == len && memcmp(lease->agent_id, new, len) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
file_dirty = 1;
|
||||||
|
free(lease->agent_id);
|
||||||
|
lease->agent_id = NULL;
|
||||||
|
|
||||||
|
if (new && (lease->agent_id = whine_malloc(len)))
|
||||||
|
{
|
||||||
|
memcpy(lease->agent_id, new, len);
|
||||||
|
lease->agent_id_len = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lease_set_vendorclass(struct dhcp_lease *lease, unsigned char *new, int len)
|
||||||
|
{
|
||||||
|
if (!lease->vendorclass && !new)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (lease->vendorclass && new && lease->vendorclass_len == len && memcmp(lease->vendorclass, new, len) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
file_dirty = 1;
|
||||||
|
free(lease->vendorclass);
|
||||||
|
lease->vendorclass = NULL;
|
||||||
|
|
||||||
|
if (new && (lease->vendorclass = whine_malloc(len)))
|
||||||
|
{
|
||||||
|
memcpy(lease->vendorclass, new, len);
|
||||||
|
lease->vendorclass_len = len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void rerun_scripts(void)
|
void rerun_scripts(void)
|
||||||
{
|
{
|
||||||
struct dhcp_lease *lease;
|
struct dhcp_lease *lease;
|
||||||
@@ -1143,9 +1259,11 @@ int do_script_run(time_t now)
|
|||||||
#endif
|
#endif
|
||||||
old_leases = lease->next;
|
old_leases = lease->next;
|
||||||
|
|
||||||
free(lease->old_hostname);
|
free(lease->hostname);
|
||||||
free(lease->clid);
|
free(lease->clid);
|
||||||
free(lease->extradata);
|
free(lease->extradata);
|
||||||
|
free(lease->agent_id);
|
||||||
|
free(lease->vendorclass);
|
||||||
free(lease);
|
free(lease);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|||||||
@@ -43,6 +43,10 @@ const char * metric_names[] = {
|
|||||||
"leases_allocated_6",
|
"leases_allocated_6",
|
||||||
"leases_pruned_6",
|
"leases_pruned_6",
|
||||||
"tcp_connections",
|
"tcp_connections",
|
||||||
|
"dhcp_leasequery",
|
||||||
|
"dhcp_lease_unassigned",
|
||||||
|
"dhcp_lease_actve",
|
||||||
|
"dhcp_lease_unknown"
|
||||||
};
|
};
|
||||||
|
|
||||||
const char* get_metric_name(int i) {
|
const char* get_metric_name(int i) {
|
||||||
|
|||||||
@@ -42,6 +42,10 @@ enum {
|
|||||||
METRIC_LEASES_ALLOCATED_6,
|
METRIC_LEASES_ALLOCATED_6,
|
||||||
METRIC_LEASES_PRUNED_6,
|
METRIC_LEASES_PRUNED_6,
|
||||||
METRIC_TCP_CONNECTIONS,
|
METRIC_TCP_CONNECTIONS,
|
||||||
|
METRIC_DHCPLEASEQUERY,
|
||||||
|
METRIC_DHCPLEASEUNASSIGNED,
|
||||||
|
METRIC_DHCPLEASEACTIVE,
|
||||||
|
METRIC_DHCPLEASEUNKNOWN,
|
||||||
|
|
||||||
__METRIC_MAX,
|
__METRIC_MAX,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -195,6 +195,7 @@ struct myoption {
|
|||||||
#define LOPT_PXE_OPT 386
|
#define LOPT_PXE_OPT 386
|
||||||
#define LOPT_NO_ENCODE 387
|
#define LOPT_NO_ENCODE 387
|
||||||
#define LOPT_DO_ENCODE 388
|
#define LOPT_DO_ENCODE 388
|
||||||
|
#define LOPT_LEASEQUERY 389
|
||||||
|
|
||||||
#ifdef HAVE_GETOPT_LONG
|
#ifdef HAVE_GETOPT_LONG
|
||||||
static const struct option opts[] =
|
static const struct option opts[] =
|
||||||
@@ -394,6 +395,7 @@ static const struct myoption opts[] =
|
|||||||
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
|
{ "use-stale-cache", 2, 0 , LOPT_STALE_CACHE },
|
||||||
{ "no-ident", 0, 0, LOPT_NO_IDENT },
|
{ "no-ident", 0, 0, LOPT_NO_IDENT },
|
||||||
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
|
{ "max-tcp-connections", 1, 0, LOPT_MAX_PROCS },
|
||||||
|
{ "leasequery", 0, 0, LOPT_LEASEQUERY },
|
||||||
{ NULL, 0, 0, 0 }
|
{ NULL, 0, 0, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -499,6 +501,7 @@ static struct {
|
|||||||
{ '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
|
{ '4', ARG_DUP, "set:<tag>,<mac address>", gettext_noop("Map MAC address (with wildcards) to option set."), NULL },
|
||||||
{ LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
|
{ LOPT_BRIDGE, ARG_DUP, "<iface>,<alias>..", gettext_noop("Treat DHCP requests on aliases as arriving from interface."), NULL },
|
||||||
{ LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
|
{ LOPT_SHARED_NET, ARG_DUP, "<iface>|<addr>,<addr>", gettext_noop("Specify extra networks sharing a broadcast domain for DHCP"), NULL},
|
||||||
|
{ LOPT_LEASEQUERY, OPT_LEASEQUERY, NULL, gettext_noop("Enable RFC 4388 leasequery functions for DHCPv4"), NULL},
|
||||||
{ '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
|
{ '5', OPT_NO_PING, NULL, gettext_noop("Disable ICMP echo address checking in the DHCP server."), NULL },
|
||||||
{ '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
|
{ '6', ARG_ONE, "<path>", gettext_noop("Shell script to run on DHCP lease creation and destruction."), NULL },
|
||||||
{ LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
|
{ LOPT_LUASCRIPT, ARG_DUP, "path", gettext_noop("Lua script to run on DHCP lease creation and destruction."), NULL },
|
||||||
|
|||||||
217
src/rfc2131.c
217
src/rfc2131.c
@@ -56,7 +56,8 @@ static void do_options(struct dhcp_context *context,
|
|||||||
time_t now,
|
time_t now,
|
||||||
unsigned int lease_time,
|
unsigned int lease_time,
|
||||||
unsigned short fuzz,
|
unsigned short fuzz,
|
||||||
const char *pxevendor);
|
const char *pxevendor,
|
||||||
|
int leasequery);
|
||||||
|
|
||||||
|
|
||||||
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
|
static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);
|
||||||
@@ -73,7 +74,7 @@ static void handle_encap(struct dhcp_packet *mess, unsigned char *end, unsigned
|
|||||||
|
|
||||||
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
||||||
size_t sz, time_t now, int unicast_dest, int loopback,
|
size_t sz, time_t now, int unicast_dest, int loopback,
|
||||||
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
|
int *is_inform, int pxe, struct in_addr fallback, time_t recvtime, int is_relay_use_source)
|
||||||
{
|
{
|
||||||
unsigned char *opt, *clid = NULL;
|
unsigned char *opt, *clid = NULL;
|
||||||
struct dhcp_lease *ltmp, *lease = NULL;
|
struct dhcp_lease *ltmp, *lease = NULL;
|
||||||
@@ -185,7 +186,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
|
if (mess_type != DHCPLEASEQUERY && (opt = option_find(mess, sz, OPTION_AGENT_ID, 1)))
|
||||||
{
|
{
|
||||||
/* Any agent-id needs to be copied back out, verbatim, as the last option
|
/* Any agent-id needs to be copied back out, verbatim, as the last option
|
||||||
in the packet. Here, we shift it to the very end of the buffer, if it doesn't
|
in the packet. Here, we shift it to the very end of the buffer, if it doesn't
|
||||||
@@ -373,6 +374,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
context = context_new;
|
context = context_new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mess_type != DHCPLEASEQUERY)
|
||||||
|
{
|
||||||
if (!context)
|
if (!context)
|
||||||
{
|
{
|
||||||
const char *via;
|
const char *via;
|
||||||
@@ -414,6 +417,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
|
/* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
|
||||||
Otherwise assume the option is an array, and look for a matching element.
|
Otherwise assume the option is an array, and look for a matching element.
|
||||||
@@ -672,7 +676,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
|
|
||||||
clear_packet(mess, end);
|
clear_packet(mess, end);
|
||||||
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
|
do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),
|
||||||
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL);
|
netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -819,7 +823,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config)
|
if (mess_type != DHCPLEASEQUERY && config)
|
||||||
{
|
{
|
||||||
struct dhcp_netid_list *list;
|
struct dhcp_netid_list *list;
|
||||||
|
|
||||||
@@ -860,7 +864,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
clid = NULL;
|
clid = NULL;
|
||||||
|
|
||||||
/* Check if client is PXE client. */
|
/* Check if client is PXE client. */
|
||||||
if (daemon->enable_pxe &&
|
if (mess_type != DHCPLEASEQUERY &&
|
||||||
|
daemon->enable_pxe &&
|
||||||
is_pxe_client(mess, sz, &pxevendor))
|
is_pxe_client(mess, sz, &pxevendor))
|
||||||
{
|
{
|
||||||
if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
|
if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
|
||||||
@@ -1035,7 +1040,8 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* if we're just a proxy server, go no further */
|
/* if we're just a proxy server, go no further */
|
||||||
if ((context->flags & CONTEXT_PROXY) || pxe)
|
if (mess_type != DHCPLEASEQUERY &&
|
||||||
|
((context->flags & CONTEXT_PROXY) || pxe))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
|
if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS, 0)))
|
||||||
@@ -1047,6 +1053,158 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
|
|
||||||
switch (mess_type)
|
switch (mess_type)
|
||||||
{
|
{
|
||||||
|
case DHCPLEASEQUERY:
|
||||||
|
mess_type = DHCPLEASEUNKNOWN;
|
||||||
|
|
||||||
|
if (!option_bool(OPT_LEASEQUERY))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (mess->giaddr.s_addr == 0 && !is_relay_use_source)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
daemon->metrics[METRIC_DHCPLEASEQUERY]++;
|
||||||
|
log_packet("DHCPLEASEQUERY", mess->ciaddr.s_addr ? &mess->ciaddr : NULL, emac_len != 0 ? emac : NULL, emac_len, iface_name, NULL, NULL, mess->xid);
|
||||||
|
|
||||||
|
/* Put all the contexts on the ->current list for the next stages. */
|
||||||
|
for (context = daemon->dhcp; context; context = context->next)
|
||||||
|
context->current = context->next;
|
||||||
|
|
||||||
|
/* Have maybe already found the lease by MAC or clid. */
|
||||||
|
if (mess->ciaddr.s_addr != 0 &&
|
||||||
|
!(lease = lease_find_by_addr(mess->ciaddr)) &&
|
||||||
|
address_available(daemon->dhcp, mess->ciaddr, tagif_netid))
|
||||||
|
{
|
||||||
|
mess_type = DHCPLEASEUNASSIGNED;
|
||||||
|
daemon->metrics[METRIC_DHCPLEASEUNASSIGNED]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lease)
|
||||||
|
{
|
||||||
|
/* RFC4388 para 6.4.2 */
|
||||||
|
if (lease->agent_id)
|
||||||
|
{
|
||||||
|
unsigned char *sopt ;
|
||||||
|
|
||||||
|
for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
|
||||||
|
{
|
||||||
|
int search;
|
||||||
|
|
||||||
|
if (vendor->match_type == MATCH_CIRCUIT)
|
||||||
|
search = SUBOPT_CIRCUIT_ID;
|
||||||
|
else if (vendor->match_type == MATCH_REMOTE)
|
||||||
|
search = SUBOPT_REMOTE_ID;
|
||||||
|
else if (vendor->match_type == MATCH_SUBSCRIBER)
|
||||||
|
search = SUBOPT_SUBSCR_ID;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((sopt = option_find1(lease->agent_id, lease->agent_id + lease->agent_id_len, search, 1)) &&
|
||||||
|
vendor->len == option_len(sopt) &&
|
||||||
|
memcmp(option_ptr(sopt, 0), vendor->data, vendor->len) == 0)
|
||||||
|
{
|
||||||
|
vendor->netid.next = netid;
|
||||||
|
netid = &vendor->netid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tagif_netid = run_tag_if(netid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now find the context for this lease and config for this host. */
|
||||||
|
if ((context = narrow_context(daemon->dhcp, lease->addr, tagif_netid)))
|
||||||
|
{
|
||||||
|
if ((config = find_config(daemon->dhcp_conf, context, lease->clid, lease->clid_len,
|
||||||
|
lease->hwaddr, lease->hwaddr_len, lease->hwaddr_type, lease->hostname, tagif_netid)))
|
||||||
|
{
|
||||||
|
struct dhcp_netid_list *list;
|
||||||
|
|
||||||
|
for (list = config->netid; list; list = list->next)
|
||||||
|
{
|
||||||
|
list->list->next = netid;
|
||||||
|
netid = list->list;
|
||||||
|
}
|
||||||
|
|
||||||
|
tagif_netid = run_tag_if(netid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context->netid.net)
|
||||||
|
{
|
||||||
|
context->netid.next = netid;
|
||||||
|
tagif_netid = run_tag_if(&context->netid);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_tags(tagif_netid, ntohl(mess->xid));
|
||||||
|
emac = extended_hwaddr(lease->hwaddr_type, lease->hwaddr_len, lease->hwaddr, lease->clid_len, lease->clid, &emac_len);
|
||||||
|
mess_type = DHCPLEASEACTIVE;
|
||||||
|
daemon->metrics[METRIC_DHCPLEASEACTIVE]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log_packet(mess_type == DHCPLEASEACTIVE ? "DHCPLEASEACTIVE" : (mess_type == DHCPLEASEUNASSIGNED ? "DHCPLEASEUNASSIGNED" : "DHCPLEASEUNKNOWN"),
|
||||||
|
mess_type == DHCPLEASEACTIVE ? &lease->addr : (mess->ciaddr.s_addr != 0 ? &mess->ciaddr : NULL),
|
||||||
|
emac_len != 0 ? emac : NULL, emac_len,
|
||||||
|
iface_name, mess_type == DHCPLEASEACTIVE ? lease->hostname : NULL, NULL, mess->xid);
|
||||||
|
|
||||||
|
clear_packet(mess, end);
|
||||||
|
option_put(mess, end, OPTION_MESSAGE_TYPE, 1, mess_type);
|
||||||
|
|
||||||
|
if (mess_type == DHCPLEASEUNKNOWN)
|
||||||
|
{
|
||||||
|
daemon->metrics[METRIC_DHCPLEASEUNKNOWN]++;
|
||||||
|
mess->ciaddr.s_addr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mess_type == DHCPLEASEACTIVE)
|
||||||
|
{
|
||||||
|
unsigned char *p;
|
||||||
|
|
||||||
|
mess->ciaddr = lease->addr;
|
||||||
|
mess->hlen = lease->hwaddr_len;
|
||||||
|
mess->htype = lease->hwaddr_type;
|
||||||
|
memcpy(mess->chaddr, lease->hwaddr, lease->hwaddr_len);
|
||||||
|
|
||||||
|
if (lease->clid && in_list(req_options, OPTION_CLIENT_ID) &&
|
||||||
|
(p = free_space(mess, end, OPTION_CLIENT_ID, lease->clid_len)))
|
||||||
|
memcpy(p, lease->clid, lease->clid_len);
|
||||||
|
|
||||||
|
if (in_list(req_options, OPTION_LEASE_TIME))
|
||||||
|
{
|
||||||
|
if (lease->expires == 0) /* infinite lease */
|
||||||
|
option_put(mess, end, OPTION_LEASE_TIME, 4, 0xffffffff);
|
||||||
|
else
|
||||||
|
option_put(mess, end, OPTION_LEASE_TIME, 4, (unsigned int)(lease->expires - now));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lease->expires != 0)
|
||||||
|
{
|
||||||
|
time = calc_time(context, config, NULL);
|
||||||
|
|
||||||
|
if (in_list(req_options, OPTION_T1) && (lease->expires - now) > time/2)
|
||||||
|
option_put(mess, end, OPTION_T1, 4, ((unsigned int)(lease->expires - now)) - time/2);
|
||||||
|
if (in_list(req_options, OPTION_T2) && (lease->expires - now) > time/8)
|
||||||
|
option_put(mess, end, OPTION_T2, 4, ((unsigned int)(lease->expires - now)) - time/8);
|
||||||
|
if (in_list(req_options, OPTION_LAST_TRANSACTION) && (lease->expires - now) < time)
|
||||||
|
option_put(mess, end, OPTION_LAST_TRANSACTION, 4, time - ((unsigned int)(lease->expires - now)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lease->vendorclass)
|
||||||
|
{
|
||||||
|
memcpy(daemon->dhcp_buff3, lease->vendorclass, lease->vendorclass_len);
|
||||||
|
vendor_class_len = lease->vendorclass_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
subnet_addr.s_addr = 0;
|
||||||
|
do_options(context, mess, end, req_options, lease->hostname, get_domain(lease->addr), netid, subnet_addr,
|
||||||
|
0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0, NULL, 1);
|
||||||
|
|
||||||
|
/* Does this have to be last for leasequery replies also? RFC 4388 is silent on the subject. */
|
||||||
|
if (lease->agent_id && in_list(req_options, OPTION_AGENT_ID) &&
|
||||||
|
(p = free_space(mess, end, OPTION_AGENT_ID, lease->agent_id_len)))
|
||||||
|
memcpy(p, lease->agent_id, lease->agent_id_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dhcp_packet_size(mess, NULL, real_end);
|
||||||
|
|
||||||
case DHCPDECLINE:
|
case DHCPDECLINE:
|
||||||
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
|
if (!(opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER, INADDRSZ)) ||
|
||||||
option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
|
option_addr(opt).s_addr != server_id(context, override, fallback).s_addr)
|
||||||
@@ -1197,7 +1355,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
|
option_put(mess, end, OPTION_LEASE_TIME, 4, time);
|
||||||
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
|
/* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
|
||||||
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
|
do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),
|
||||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
|
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor, 0);
|
||||||
|
|
||||||
return dhcp_packet_size(mess, agent_id, real_end);
|
return dhcp_packet_size(mess, agent_id, real_end);
|
||||||
|
|
||||||
@@ -1529,6 +1687,20 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
lease_set_expires(lease, time, now);
|
lease_set_expires(lease, time, now);
|
||||||
lease_set_interface(lease, int_index, now);
|
lease_set_interface(lease, int_index, now);
|
||||||
|
|
||||||
|
if (option_bool(OPT_LEASEQUERY))
|
||||||
|
{
|
||||||
|
if (agent_id)
|
||||||
|
lease_set_agent_id(lease, option_ptr(agent_id, 0), option_len(agent_id));
|
||||||
|
if (vendor_class_len != 0)
|
||||||
|
lease_set_vendorclass(lease, (unsigned char *)daemon->dhcp_buff3, vendor_class_len);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* if leasequery no longer enabled, remove stuff that may have been stored when it was. */
|
||||||
|
lease_set_agent_id(lease, NULL, 0);
|
||||||
|
lease_set_vendorclass(lease, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (override.s_addr != 0)
|
if (override.s_addr != 0)
|
||||||
lease->override = override;
|
lease->override = override;
|
||||||
else
|
else
|
||||||
@@ -1544,7 +1716,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
if (rapid_commit)
|
if (rapid_commit)
|
||||||
option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
|
option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
|
||||||
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
|
do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),
|
||||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor);
|
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz, pxevendor, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
return dhcp_packet_size(mess, agent_id, real_end);
|
return dhcp_packet_size(mess, agent_id, real_end);
|
||||||
@@ -1611,7 +1783,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
|
|||||||
}
|
}
|
||||||
|
|
||||||
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
|
do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
|
||||||
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor);
|
netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0, pxevendor, 0);
|
||||||
|
|
||||||
*is_inform = 1; /* handle reply differently */
|
*is_inform = 1; /* handle reply differently */
|
||||||
return dhcp_packet_size(mess, agent_id, real_end);
|
return dhcp_packet_size(mess, agent_id, real_end);
|
||||||
@@ -1720,10 +1892,12 @@ static void log_packet(char *type, void *addr, unsigned char *ext_mac,
|
|||||||
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
daemon->addrbuff[0] = 0;
|
daemon->addrbuff[0] = daemon->namebuff[0] = 0;
|
||||||
|
|
||||||
if (addr)
|
if (addr)
|
||||||
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
inet_ntop(AF_INET, addr, daemon->addrbuff, ADDRSTRLEN);
|
||||||
|
|
||||||
|
if (ext_mac)
|
||||||
print_mac(daemon->namebuff, ext_mac, mac_len);
|
print_mac(daemon->namebuff, ext_mac, mac_len);
|
||||||
|
|
||||||
if (option_bool(OPT_LOG_OPTS))
|
if (option_bool(OPT_LOG_OPTS))
|
||||||
@@ -2412,7 +2586,8 @@ static void do_options(struct dhcp_context *context,
|
|||||||
time_t now,
|
time_t now,
|
||||||
unsigned int lease_time,
|
unsigned int lease_time,
|
||||||
unsigned short fuzz,
|
unsigned short fuzz,
|
||||||
const char *pxevendor)
|
const char *pxevendor,
|
||||||
|
int leasequery)
|
||||||
{
|
{
|
||||||
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
|
struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
|
||||||
struct dhcp_boot *boot;
|
struct dhcp_boot *boot;
|
||||||
@@ -2450,6 +2625,8 @@ static void do_options(struct dhcp_context *context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!leasequery)
|
||||||
|
{
|
||||||
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
|
for (id_list = daemon->force_broadcast; id_list; id_list = id_list->next)
|
||||||
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
|
if ((!id_list->list) || match_netid(id_list->list, netid, 0))
|
||||||
break;
|
break;
|
||||||
@@ -2515,6 +2692,7 @@ static void do_options(struct dhcp_context *context,
|
|||||||
if ((opt = option_find2(OPTION_END)))
|
if ((opt = option_find2(OPTION_END)))
|
||||||
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
|
mess->siaddr.s_addr = ((struct in_addr *)opt->val)->s_addr;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* We don't want to do option-overload for BOOTP, so make the file and sname
|
/* We don't want to do option-overload for BOOTP, so make the file and sname
|
||||||
fields look like they are in use, even when they aren't. This gets restored
|
fields look like they are in use, even when they aren't. This gets restored
|
||||||
@@ -2534,7 +2712,7 @@ static void do_options(struct dhcp_context *context,
|
|||||||
end -= 3;
|
end -= 3;
|
||||||
|
|
||||||
/* rfc3011 says this doesn't need to be in the requested options list. */
|
/* rfc3011 says this doesn't need to be in the requested options list. */
|
||||||
if (subnet_addr.s_addr)
|
if (!leasequery && subnet_addr.s_addr)
|
||||||
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
|
option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
|
||||||
|
|
||||||
if (lease_time != 0xffffffff)
|
if (lease_time != 0xffffffff)
|
||||||
@@ -2575,13 +2753,16 @@ static void do_options(struct dhcp_context *context,
|
|||||||
/* replies to DHCPINFORM may not have a valid context */
|
/* replies to DHCPINFORM may not have a valid context */
|
||||||
if (context)
|
if (context)
|
||||||
{
|
{
|
||||||
if (!option_find2(OPTION_NETMASK))
|
/* Netmask and broadcast always sent, except leasequery. */
|
||||||
|
if (!option_find2(OPTION_NETMASK) &&
|
||||||
|
(!leasequery || in_list(req_options, OPTION_NETMASK)))
|
||||||
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
|
option_put(mess, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr));
|
||||||
|
|
||||||
/* May not have a "guessed" broadcast address if we got no packets via a relay
|
/* May not have a "guessed" broadcast address if we got no packets via a relay
|
||||||
from this net yet (ie just unicast renewals after a restart */
|
from this net yet (ie just unicast renewals after a restart */
|
||||||
if (context->broadcast.s_addr &&
|
if (context->broadcast.s_addr &&
|
||||||
!option_find2(OPTION_BROADCAST))
|
!option_find2(OPTION_BROADCAST) &&
|
||||||
|
(!leasequery || in_list(req_options, OPTION_BROADCAST)))
|
||||||
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
|
option_put(mess, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr));
|
||||||
|
|
||||||
/* Same comments as broadcast apply, and also may not be able to get a sensible
|
/* Same comments as broadcast apply, and also may not be able to get a sensible
|
||||||
@@ -2663,7 +2844,7 @@ static void do_options(struct dhcp_context *context,
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* was it asked for, or are we sending it anyway? */
|
/* was it asked for, or are we sending it anyway? */
|
||||||
if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
|
if ((!(opt->flags & DHOPT_FORCE) || leasequery) && !in_list(req_options, optno))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
/* prohibit some used-internally options. T1 and T2 already handled. */
|
/* prohibit some used-internally options. T1 and T2 already handled. */
|
||||||
@@ -2729,8 +2910,8 @@ static void do_options(struct dhcp_context *context,
|
|||||||
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
|
config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&
|
if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT) || in_list(req_options, OPTION_VENDOR_ID)) &&
|
||||||
do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term) &&
|
(leasequery || do_encap_opts(config_opts, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, null_term)) &&
|
||||||
pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
|
pxe_arch == -1 && !done_vendor_class && vendor_class_len != 0 &&
|
||||||
(p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
|
(p = free_space(mess, end, OPTION_VENDOR_ID, vendor_class_len)))
|
||||||
/* If we send vendor encapsulated options, and haven't already sent option 60,
|
/* If we send vendor encapsulated options, and haven't already sent option 60,
|
||||||
|
|||||||
Reference in New Issue
Block a user