mirror of
https://passt.top/passt
synced 2024-12-22 13:45:32 +00:00
dhcp, ndp, dhcpv6: Support for multiple DNS servers, search list
Add support for a variable amount of DNS servers, including zero, from /etc/resolv.conf, in DHCP, NDP and DHCPv6 implementations. Introduce support for domain search list for DHCP (RFC 3397), NDP (RFC 8106), and DHCPv6 (RFC 3646), also sourced from /etc/resolv.conf. Signed-off-by: Stefano Brivio <sbrivio@redhat.com>
This commit is contained in:
parent
0231ac1c86
commit
9010054ea4
97
dhcp.c
97
dhcp.c
@ -45,7 +45,6 @@ struct opt {
|
||||
static struct opt opts[255] = {
|
||||
[1] = { 0, 4, { 0 }, 0, { 0 }, }, /* Mask */
|
||||
[3] = { 0, 4, { 0 }, 0, { 0 }, }, /* Router */
|
||||
[6] = { 0, 4, { 0 }, 0, { 0 }, }, /* DNS */
|
||||
[51] = { 0, 4, { 0xff, 0xff, 0xff, 0xff }, 0, { 0 }, }, /* Lease time */
|
||||
[53] = { 0, 1, { 0 }, 0, { 0 }, }, /* Type */
|
||||
#define DHCPDISCOVER 1
|
||||
@ -154,6 +153,92 @@ static int fill(struct msg *m)
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* opt_dns_search_dup_ptr() - Look for possible domain name compression pointer
|
||||
* @buf: Current option buffer with existing labels
|
||||
* @cmp: Portion of domain name being added
|
||||
* @len: Length of current option buffer
|
||||
*
|
||||
* Return: offset to corresponding compression pointer if any, -1 if not found
|
||||
*/
|
||||
static int opt_dns_search_dup_ptr(unsigned char *buf, char *cmp, size_t len)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (buf[i] == 0 &&
|
||||
len - i - 1 >= strlen(cmp) &&
|
||||
!memcmp(buf + i + 1, cmp, strlen(cmp)))
|
||||
return i;
|
||||
|
||||
if ((buf[i] & 0xc0) == 0xc0 &&
|
||||
len - i - 2 >= strlen(cmp) &&
|
||||
!memcmp(buf + i + 2, cmp, strlen(cmp)))
|
||||
return i + 1;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* opt_set_dns_search() - Fill data and set length for Domain Search option
|
||||
* @c: Execution context
|
||||
* @max_len: Maximum total length of option buffer
|
||||
*/
|
||||
static void opt_set_dns_search(struct ctx *c, size_t max_len)
|
||||
{
|
||||
char buf[NS_MAXDNAME];
|
||||
int i;
|
||||
|
||||
opts[119].slen = 0;
|
||||
|
||||
for (i = 0; i < 255; i++)
|
||||
max_len -= opts[i].slen;
|
||||
|
||||
for (i = 0; *c->dns_search[i].n; i++) {
|
||||
unsigned int n;
|
||||
int dup = -1;
|
||||
char *p;
|
||||
|
||||
buf[0] = 0;
|
||||
for (p = c->dns_search[i].n, n = 1; *p; p++) {
|
||||
if (*p == '.') {
|
||||
/* RFC 1035 4.1.4 Message compression */
|
||||
dup = opt_dns_search_dup_ptr(opts[119].s, p + 1,
|
||||
opts[119].slen);
|
||||
|
||||
if (dup >= 0) {
|
||||
buf[n++] = '\xc0';
|
||||
buf[n++] = dup;
|
||||
break;
|
||||
} else {
|
||||
buf[n++] = '.';
|
||||
}
|
||||
} else {
|
||||
buf[n++] = *p;
|
||||
}
|
||||
}
|
||||
|
||||
/* The compression pointer is also an end of label */
|
||||
if (dup < 0)
|
||||
buf[n++] = 0;
|
||||
|
||||
if (n >= max_len)
|
||||
break;
|
||||
|
||||
memcpy(opts[119].s + opts[119].slen, buf, n);
|
||||
opts[119].slen += n;
|
||||
max_len -= n;
|
||||
}
|
||||
|
||||
for (i = 0; i < opts[119].slen; i++) {
|
||||
if (!opts[119].s[i] || opts[119].s[i] == '.') {
|
||||
opts[119].s[i] = strcspn((char *)opts[119].s + i + 1,
|
||||
".\xc0");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* dhcp() - Check if this is a DHCP message, reply as needed
|
||||
* @c: Execution context
|
||||
@ -213,10 +298,16 @@ int dhcp(struct ctx *c, struct ethhdr *eh, size_t len)
|
||||
m->yiaddr = c->addr4;
|
||||
*(unsigned long *)opts[1].s = c->mask4;
|
||||
*(unsigned long *)opts[3].s = c->gw4;
|
||||
*(unsigned long *)opts[6].s = c->dns4;
|
||||
*(unsigned long *)opts[54].s = c->gw4;
|
||||
|
||||
uh->len = htons(len = offsetof(struct msg, o) + fill(m));
|
||||
for (i = 0, opts[6].slen = 0; c->dns4[i]; i++) {
|
||||
((uint32_t *)opts[6].s)[i] = c->dns4[i];
|
||||
opts[6].slen += sizeof(uint32_t);
|
||||
}
|
||||
|
||||
opt_set_dns_search(c, sizeof(m->o));
|
||||
|
||||
uh->len = htons(len = offsetof(struct msg, o) + fill(m) + sizeof(*uh));
|
||||
uh->check = 0;
|
||||
uh->source = htons(67);
|
||||
uh->dest = htons(68);
|
||||
|
105
dhcpv6.c
105
dhcpv6.c
@ -15,13 +15,13 @@
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "passt.h"
|
||||
#include "tap.h"
|
||||
@ -43,6 +43,7 @@ struct opt_hdr {
|
||||
# define OPT_STATUS_CODE 13
|
||||
# define STATUS_NOTONLINK 4
|
||||
# define OPT_DNS_SERVERS 23
|
||||
# define OPT_DNS_SEARCH 24
|
||||
#else
|
||||
# define OPT_CLIENTID __bswap_constant_16(1)
|
||||
# define OPT_SERVERID __bswap_constant_16(2)
|
||||
@ -52,6 +53,7 @@ struct opt_hdr {
|
||||
# define OPT_STATUS_CODE __bswap_constant_16(13)
|
||||
# define STATUS_NOTONLINK __bswap_constant_16(4)
|
||||
# define OPT_DNS_SERVERS __bswap_constant_16(23)
|
||||
# define OPT_DNS_SEARCH __bswap_constant_16(24)
|
||||
#endif
|
||||
#define STR_NOTONLINK "Prefix not appropriate for link."
|
||||
|
||||
@ -157,11 +159,21 @@ struct opt_status_code {
|
||||
/**
|
||||
* struct opt_dns_servers - DNS Recursive Name Server option (RFC 3646)
|
||||
* @hdr: Option header
|
||||
* @addr: IPv6 DNS address
|
||||
* @addr: IPv6 DNS addresses
|
||||
*/
|
||||
struct opt_dns_servers {
|
||||
struct opt_hdr hdr;
|
||||
struct in6_addr addr;
|
||||
struct in6_addr addr[MAXNS];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct opt_dns_servers - Domain Search List option (RFC 3646)
|
||||
* @hdr: Option header
|
||||
* @list: NULL-separated list of domain names
|
||||
*/
|
||||
struct opt_dns_search {
|
||||
struct opt_hdr hdr;
|
||||
char list[MAXDNSRCH * NS_MAXDNAME];
|
||||
};
|
||||
|
||||
/**
|
||||
@ -200,8 +212,9 @@ static const struct udphdr uh_resp = {
|
||||
* @server_id: Server Identifier option
|
||||
* @ia_na: Non-temporary Address option
|
||||
* @ia_addr: Address for IA_NA
|
||||
* @dns_servers: DNS Recursive Name Server option
|
||||
* @client_id: Client Identifier, variable length, must be at the end
|
||||
* @client_id: Client Identifier, variable length
|
||||
* @dns_servers: DNS Recursive Name Server, here just for storage size
|
||||
* @dns_search: Domain Search List, here just for storage size
|
||||
*/
|
||||
static struct resp_t {
|
||||
struct udphdr uh;
|
||||
@ -211,6 +224,8 @@ static struct resp_t {
|
||||
struct opt_ia_na ia_na;
|
||||
struct opt_ia_addr ia_addr;
|
||||
struct opt_client_id client_id;
|
||||
struct opt_dns_servers dns_servers;
|
||||
struct opt_dns_search dns_search;
|
||||
} __attribute__((__packed__)) resp = {
|
||||
uh_resp,
|
||||
{ 0 },
|
||||
@ -226,10 +241,17 @@ static struct resp_t {
|
||||
IN6ADDR_ANY_INIT, (uint32_t)~0U, (uint32_t)~0U
|
||||
},
|
||||
|
||||
|
||||
{ { OPT_CLIENTID, 0, },
|
||||
{ 0 }
|
||||
},
|
||||
|
||||
{ { OPT_DNS_SERVERS, 0, },
|
||||
{ IN6ADDR_ANY_INIT }
|
||||
},
|
||||
|
||||
{ { OPT_DNS_SEARCH, 0, },
|
||||
{ 0 },
|
||||
},
|
||||
};
|
||||
|
||||
static const struct opt_status_code sc_not_on_link = {
|
||||
@ -352,6 +374,67 @@ ia_ta:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* dhcpv6_dns_fill() - Fill in DNS Servers and Domain Search list options
|
||||
* @c: Execution context
|
||||
* @buf: Response message buffer where options will be appended
|
||||
* @offset: Offset in message buffer for new options
|
||||
*
|
||||
* Return: updated length of response message buffer.
|
||||
*/
|
||||
static size_t dhcpv6_dns_fill(struct ctx *c, char *buf, int offset)
|
||||
{
|
||||
struct opt_dns_servers *srv = NULL;
|
||||
struct opt_dns_search *srch = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->dns6[i]); i++) {
|
||||
if (!i) {
|
||||
srv = (struct opt_dns_servers *)(buf + offset);
|
||||
offset += sizeof(struct opt_hdr);
|
||||
srv->hdr.t = OPT_DNS_SERVERS;
|
||||
srv->hdr.l = 0;
|
||||
}
|
||||
|
||||
memcpy(&srv->addr[i], &c->dns6[i], sizeof(srv->addr[i]));
|
||||
srv->hdr.l += sizeof(srv->addr[i]);
|
||||
offset += sizeof(srv->addr[i]);
|
||||
}
|
||||
|
||||
if (srv)
|
||||
srv->hdr.l = htons(srv->hdr.l);
|
||||
|
||||
for (i = 0; *c->dns_search[i].n; i++) {
|
||||
char *p;
|
||||
|
||||
if (!i) {
|
||||
srch = (struct opt_dns_search *)(buf + offset);
|
||||
offset += sizeof(struct opt_hdr);
|
||||
srch->hdr.t = OPT_DNS_SEARCH;
|
||||
srch->hdr.l = 0;
|
||||
p = srch->list;
|
||||
*p = 0;
|
||||
}
|
||||
|
||||
p = stpcpy(p + 1, c->dns_search[i].n);
|
||||
*(p++) = 0;
|
||||
srch->hdr.l += strlen(c->dns_search[i].n) + 2;
|
||||
offset += strlen(c->dns_search[i].n) + 2;
|
||||
}
|
||||
|
||||
if (srch) {
|
||||
for (i = 0; i < srch->hdr.l; i++) {
|
||||
if (srch->list[i] == '.' || !srch->list[i]) {
|
||||
srch->list[i] = strcspn(srch->list + i + 1,
|
||||
".");
|
||||
}
|
||||
}
|
||||
srch->hdr.l = htons(srch->hdr.l);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* dhcpv6() - Check if this is a DHCPv6 message, reply as needed
|
||||
* @c: Execution context
|
||||
@ -478,10 +561,14 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len)
|
||||
|
||||
memcpy(&resp.client_id, client_id,
|
||||
ntohs(client_id->l) + sizeof(struct opt_hdr));
|
||||
resp.uh.len = htons(n = offsetof(struct resp_t, client_id) +
|
||||
sizeof(struct opt_hdr) + ntohs(client_id->l));
|
||||
|
||||
n = offsetof(struct resp_t, client_id) +
|
||||
sizeof(struct opt_hdr) + ntohs(client_id->l);
|
||||
n = dhcpv6_dns_fill(c, (char *)&resp, n);
|
||||
resp.uh.len = htons(n);
|
||||
|
||||
resp.hdr.xid = mh->xid;
|
||||
|
||||
tap_ip_send(c, &c->gw6, IPPROTO_UDP, (char *)&resp, n);
|
||||
c->addr6_seen = c->addr6;
|
||||
|
||||
@ -489,7 +576,7 @@ int dhcpv6(struct ctx *c, struct ethhdr *eh, size_t len)
|
||||
}
|
||||
|
||||
/**
|
||||
* dhcpv6() - Initialise DUID and addresses for DHCPv6 server
|
||||
* dhcpv6_init() - Initialise DUID and addresses for DHCPv6 server
|
||||
* @c: Execution context
|
||||
*/
|
||||
void dhcpv6_init(struct ctx *c)
|
||||
|
54
ndp.c
54
ndp.c
@ -14,6 +14,7 @@
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
@ -21,7 +22,6 @@
|
||||
#include <linux/udp.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "passt.h"
|
||||
#include "util.h"
|
||||
@ -76,6 +76,9 @@ int ndp(struct ctx *c, struct ethhdr *eh, size_t len)
|
||||
memcpy(p, c->mac, ETH_ALEN);
|
||||
p += 6;
|
||||
} else if (ih->icmp6_type == RS) {
|
||||
size_t len = 0;
|
||||
int i, n;
|
||||
|
||||
info("NDP: received RS, sending RA");
|
||||
ihr->icmp6_type = RA;
|
||||
ihr->icmp6_code = 0;
|
||||
@ -95,13 +98,48 @@ int ndp(struct ctx *c, struct ethhdr *eh, size_t len)
|
||||
memcpy(p, &c->addr6, 8); /* prefix */
|
||||
p += 16;
|
||||
|
||||
*p++ = 25; /* RDNS */
|
||||
*p++ = 3; /* length */
|
||||
p += 2;
|
||||
*(uint32_t *)p = htonl(60); /* lifetime */
|
||||
p += 4;
|
||||
memcpy(p, &c->dns6, 16); /* address */
|
||||
p += 16;
|
||||
for (n = 0; !IN6_IS_ADDR_UNSPECIFIED(&c->dns6[n]); n++);
|
||||
if (n) {
|
||||
*p++ = 25; /* RDNSS */
|
||||
*p++ = 1 + 2 * n; /* length */
|
||||
p += 2; /* reserved */
|
||||
*(uint32_t *)p = htonl(60); /* lifetime */
|
||||
p += 4;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
memcpy(p, &c->dns6[i], 16); /* address */
|
||||
p += 16;
|
||||
}
|
||||
}
|
||||
|
||||
for (n = 0; *c->dns_search[n].n; n++)
|
||||
len += strlen(c->dns_search[n].n) + 2;
|
||||
if (len) {
|
||||
*p++ = 31; /* DNSSL */
|
||||
*p++ = 2 + (len + 8 - 1) / 8; /* length */
|
||||
p += 2; /* reserved */
|
||||
*(uint32_t *)p = htonl(60); /* lifetime */
|
||||
p += 4;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
char *dot;
|
||||
|
||||
*(p++) = '.';
|
||||
|
||||
strncpy((char *)p, c->dns_search[i].n,
|
||||
sizeof(buf) -
|
||||
((intptr_t)p - (intptr_t)buf));
|
||||
for (dot = (char *)p - 1; *dot; dot++) {
|
||||
if (*dot == '.')
|
||||
*dot = strcspn(dot + 1, ".");
|
||||
}
|
||||
p += strlen(c->dns_search[i].n);
|
||||
*(p++) = 0;
|
||||
}
|
||||
|
||||
memset(p, 0, len % 8); /* padding */
|
||||
p += len % 8;
|
||||
}
|
||||
|
||||
*p++ = 1; /* source ll */
|
||||
*p++ = 1; /* length */
|
||||
|
77
passt.c
77
passt.c
@ -299,30 +299,49 @@ out:
|
||||
*/
|
||||
static void get_dns(struct ctx *c)
|
||||
{
|
||||
struct in6_addr *dns6 = &c->dns6[0];
|
||||
struct fqdn *s = c->dns_search;
|
||||
uint32_t *dns4 = &c->dns4[0];
|
||||
char buf[BUFSIZ], *p, *end;
|
||||
int dns4 = 0, dns6 = 0;
|
||||
FILE *r;
|
||||
|
||||
r = fopen("/etc/resolv.conf", "r");
|
||||
while (fgets(buf, BUFSIZ, r) && !(dns4 && dns6)) {
|
||||
if (!strstr(buf, "nameserver "))
|
||||
continue;
|
||||
p = strrchr(buf, ' ');
|
||||
end = strpbrk(buf, "%\n");
|
||||
if (end)
|
||||
*end = 0;
|
||||
if (p && inet_pton(AF_INET, p + 1, &c->dns4))
|
||||
dns4 = 1;
|
||||
if (p && inet_pton(AF_INET6, p + 1, &c->dns6))
|
||||
dns6 = 1;
|
||||
while (fgets(buf, BUFSIZ, r)) {
|
||||
if (strstr(buf, "nameserver ") == buf) {
|
||||
p = strrchr(buf, ' ');
|
||||
if (!p)
|
||||
continue;
|
||||
|
||||
end = strpbrk(buf, "%\n");
|
||||
if (end)
|
||||
*end = 0;
|
||||
|
||||
if (dns4 - &c->dns4[0] < ARRAY_SIZE(c->dns4) &&
|
||||
inet_pton(AF_INET, p + 1, dns4))
|
||||
dns4++;
|
||||
|
||||
if (dns6 - &c->dns6[0] < ARRAY_SIZE(c->dns6) &&
|
||||
inet_pton(AF_INET6, p + 1, dns6))
|
||||
dns6++;
|
||||
} else if (strstr(buf, "search ") == buf &&
|
||||
s == c->dns_search) {
|
||||
end = strpbrk(buf, "\n");
|
||||
if (end)
|
||||
*end = 0;
|
||||
|
||||
p = strtok(buf, " \t");
|
||||
while ((p = strtok(NULL, " \t")) &&
|
||||
s - c->dns_search < ARRAY_SIZE(c->dns_search)) {
|
||||
strncpy(s->n, p, sizeof(c->dns_search[0]));
|
||||
s++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(r);
|
||||
if (dns4 || dns6)
|
||||
return;
|
||||
|
||||
err("Couldn't get any nameserver address");
|
||||
exit(EXIT_FAILURE);
|
||||
if (dns4 == c->dns4 && dns6 == c->dns6)
|
||||
warn("Couldn't get any nameserver address");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -785,8 +804,17 @@ int main(int argc, char **argv)
|
||||
inet_ntop(AF_INET, &c.mask4, buf4, sizeof(buf4)));
|
||||
info(" router: %s",
|
||||
inet_ntop(AF_INET, &c.gw4, buf4, sizeof(buf4)));
|
||||
info(" DNS: %s",
|
||||
inet_ntop(AF_INET, &c.dns4, buf4, sizeof(buf4)));
|
||||
for (i = 0; c.dns4[i]; i++) {
|
||||
if (!i)
|
||||
info(" DNS:");
|
||||
inet_ntop(AF_INET, &c.dns4[i], buf4, sizeof(buf4));
|
||||
info(" %s", buf4);
|
||||
}
|
||||
for (i = 0; *c.dns_search[i].n; i++) {
|
||||
if (!i)
|
||||
info(" search:");
|
||||
info(" %s", c.dns_search[i].n);
|
||||
}
|
||||
}
|
||||
if (c.v6) {
|
||||
info("NDP/DHCPv6:");
|
||||
@ -794,8 +822,17 @@ int main(int argc, char **argv)
|
||||
inet_ntop(AF_INET6, &c.addr6, buf6, sizeof(buf6)));
|
||||
info(" router: %s",
|
||||
inet_ntop(AF_INET6, &c.gw6, buf6, sizeof(buf6)));
|
||||
info(" DNS: %s",
|
||||
inet_ntop(AF_INET6, &c.dns6, buf6, sizeof(buf6)));
|
||||
for (i = 0; !IN6_IS_ADDR_UNSPECIFIED(&c.dns6[i]); i++) {
|
||||
if (!i)
|
||||
info(" DNS:");
|
||||
inet_ntop(AF_INET6, &c.dns6[i], buf6, sizeof(buf6));
|
||||
info(" %s", buf6);
|
||||
}
|
||||
for (i = 0; *c.dns_search[i].n; i++) {
|
||||
if (!i)
|
||||
info(" search:");
|
||||
info(" %s", c.dns_search[i].n);
|
||||
}
|
||||
}
|
||||
|
||||
listen:
|
||||
|
17
passt.h
17
passt.h
@ -20,6 +20,12 @@ struct tap_msg {
|
||||
#include "tcp.h"
|
||||
#include "udp.h"
|
||||
|
||||
#include <resolv.h> /* For MAXNS below */
|
||||
|
||||
struct fqdn {
|
||||
char n[NS_MAXDNAME];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ctx - Execution context
|
||||
* @epollfd: file descriptor for epoll instance
|
||||
@ -31,13 +37,14 @@ struct tap_msg {
|
||||
* @addr4_seen: Latest IPv4 address seen as source from tap
|
||||
* @mask4: IPv4 netmask, network order
|
||||
* @gw4: Default IPv4 gateway, network order
|
||||
* @dns4: IPv4 DNS address, network order
|
||||
* @dns4: IPv4 DNS addresses, zero-terminated, network order
|
||||
* @dns_search: DNS search list
|
||||
* @v6: Enable IPv6 transport
|
||||
* @addr6: IPv6 address for external, routable interface
|
||||
* @addr6_seen: Latest IPv6 global/site address seen as source from tap
|
||||
* @addr6_ll_seen: Latest IPv6 link-local address seen as source from tap
|
||||
* @gw6: Default IPv6 gateway
|
||||
* @dns4: IPv6 DNS address
|
||||
* @dns4: IPv4 DNS addresses, zero-terminated
|
||||
* @ifn: Name of routable interface
|
||||
*/
|
||||
struct ctx {
|
||||
@ -51,14 +58,16 @@ struct ctx {
|
||||
uint32_t addr4_seen;
|
||||
uint32_t mask4;
|
||||
uint32_t gw4;
|
||||
uint32_t dns4;
|
||||
uint32_t dns4[MAXNS + 1];
|
||||
|
||||
struct fqdn dns_search[MAXDNSRCH];
|
||||
|
||||
int v6;
|
||||
struct in6_addr addr6;
|
||||
struct in6_addr addr6_seen;
|
||||
struct in6_addr addr6_ll_seen;
|
||||
struct in6_addr gw6;
|
||||
struct in6_addr dns6;
|
||||
struct in6_addr dns6[MAXNS + 1];
|
||||
|
||||
char ifn[IF_NAMESIZE];
|
||||
|
||||
|
7
util.h
7
util.h
@ -22,11 +22,16 @@ void debug(const char *format, ...);
|
||||
CHECK_SET_MIN_MAX(c->proto_ctx.fd_, (fd)); \
|
||||
} while (0)
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
|
||||
#endif
|
||||
#ifndef MAX
|
||||
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
||||
#endif
|
||||
|
||||
#define ARRAY_SIZE(a) ((int)(sizeof(a) / sizeof((a)[0])))
|
||||
|
||||
#define IN_INTERVAL(a, b, x) ((x) >= (a) && (x) <= (b))
|
||||
|
||||
#define FD_PROTO(x, proto) \
|
||||
(IN_INTERVAL(c->proto.fd_min, c->proto.fd_max, (x)))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user