As described in RFC3315 §15 any solicit, confirm, rebind or information request message is discarded if the destination address is unicast
Likewise any request (§18.2.1), renew (§18.2.3), release (§18.2.6) or decline (§18.2.7) message is discarded and the server replies with the status code use multicast.
static void handle_dhcpv4(void *addr, void *data, size_t len,
static void handle_dhcpv4(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest_addr);
static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
enum dhcpv4_msg msg, const uint8_t *mac, struct in_addr reqaddr,
const char *hostname);
static struct dhcpv4_assignment* dhcpv4_lease(struct interface *iface,
enum dhcpv4_msg msg, const uint8_t *mac, struct in_addr reqaddr,
const char *hostname);
// Simple DHCPv6-server for information requests
static void handle_dhcpv4(void *addr, void *data, size_t len,
// Simple DHCPv6-server for information requests
static void handle_dhcpv4(void *addr, void *data, size_t len,
- struct interface *iface)
+ struct interface *iface, _unused void *dest_addr)
{
if (!iface->dhcpv4)
return;
{
if (!iface->dhcpv4)
return;
static void relay_server_response(uint8_t *data, size_t len);
static void handle_dhcpv6(void *addr, void *data, size_t len,
static void relay_server_response(uint8_t *data, size_t len);
static void handle_dhcpv6(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest);
static void handle_client_request(void *addr, void *data, size_t len,
static void handle_client_request(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest_addr);
enum {
IOV_NESTED = 0,
IOV_DEST,
enum {
IOV_NESTED = 0,
IOV_DEST,
+ IOV_MAXRT,
+#define IOV_STAT IOV_MAXRT
IOV_DNS,
IOV_DNS_ADDR,
IOV_SEARCH,
IOV_DNS,
IOV_DNS_ADDR,
IOV_SEARCH,
// Simple DHCPv6-server for information requests
static void handle_client_request(void *addr, void *data, size_t len,
// Simple DHCPv6-server for information requests
static void handle_client_request(void *addr, void *data, size_t len,
- struct interface *iface)
+ struct interface *iface, void *dest_addr)
{
struct dhcpv6_client_header *hdr = data;
{
struct dhcpv6_client_header *hdr = data;
if (len < sizeof(*hdr))
return;
if (len < sizeof(*hdr))
return;
uint16_t duid_type;
uint16_t hardware_type;
uint8_t mac[6];
uint16_t duid_type;
uint16_t hardware_type;
uint8_t mac[6];
- uint16_t solmaxrt_type;
- uint16_t solmaxrt_length;
- uint32_t solmaxrt_value;
uint16_t clientid_type;
uint16_t clientid_length;
uint8_t clientid_buf[130];
uint16_t clientid_type;
uint16_t clientid_length;
uint8_t clientid_buf[130];
.serverid_length = htons(10),
.duid_type = htons(3),
.hardware_type = htons(1),
.serverid_length = htons(10),
.duid_type = htons(3),
.hardware_type = htons(1),
- .solmaxrt_type = htons(DHCPV6_OPT_SOL_MAX_RT),
- .solmaxrt_length = htons(4),
- .solmaxrt_value = htonl(60),
.clientid_type = htons(DHCPV6_OPT_CLIENTID),
.clientid_buf = {0}
};
.clientid_type = htons(DHCPV6_OPT_CLIENTID),
.clientid_buf = {0}
};
struct __attribute__((packed)) {
uint16_t type;
uint16_t len;
struct __attribute__((packed)) {
uint16_t type;
uint16_t len;
+ uint32_t value;
+ } maxrt = {htons(DHCPV6_OPT_SOL_MAX_RT), htons(sizeof(maxrt) - 4),
+ htonl(60)};
+
+ struct __attribute__((packed)) {
+ uint16_t type;
+ uint16_t len;
uint16_t value;
} stat = {htons(DHCPV6_OPT_STATUS), htons(sizeof(stat) - 4),
uint16_t value;
} stat = {htons(DHCPV6_OPT_STATUS), htons(sizeof(stat) - 4),
- htons(DHCPV6_STATUS_NOADDRSAVAIL)};
+ htons(DHCPV6_STATUS_USEMULTICAST)};
struct __attribute__((packed)) {
uint16_t type;
struct __attribute__((packed)) {
uint16_t type;
struct iovec iov[IOV_TOTAL] = {
[IOV_NESTED] = {NULL, 0},
[IOV_DEST] = {&dest, (uint8_t*)&dest.clientid_type - (uint8_t*)&dest},
struct iovec iov[IOV_TOTAL] = {
[IOV_NESTED] = {NULL, 0},
[IOV_DEST] = {&dest, (uint8_t*)&dest.clientid_type - (uint8_t*)&dest},
+ [IOV_MAXRT] = {&maxrt, sizeof(maxrt)},
[IOV_DNS] = {&dns, (dns_cnt) ? sizeof(dns) : 0},
[IOV_DNS_ADDR] = {dns_addr, dns_cnt * sizeof(*dns_addr)},
[IOV_SEARCH] = {&search, (search_len) ? sizeof(search) : 0},
[IOV_DNS] = {&dns, (dns_cnt) ? sizeof(dns) : 0},
[IOV_DNS_ADDR] = {dns_addr, dns_cnt * sizeof(*dns_addr)},
[IOV_SEARCH] = {&search, (search_len) ? sizeof(search) : 0},
if (opts[-4] == DHCPV6_MSG_ADVERTISE || opts[-4] == DHCPV6_MSG_REPLY || opts[-4] == DHCPV6_MSG_RELAY_REPL)
return;
if (opts[-4] == DHCPV6_MSG_ADVERTISE || opts[-4] == DHCPV6_MSG_REPLY || opts[-4] == DHCPV6_MSG_RELAY_REPL)
return;
+ if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 &&
+ (opts[-4] == DHCPV6_MSG_SOLICIT || opts[-4] == DHCPV6_MSG_CONFIRM ||
+ opts[-4] == DHCPV6_MSG_REBIND || opts[-4] == DHCPV6_MSG_INFORMATION_REQUEST))
+ return;
+
if (opts[-4] == DHCPV6_MSG_SOLICIT) {
dest.msg_type = DHCPV6_MSG_ADVERTISE;
} else if (opts[-4] == DHCPV6_MSG_INFORMATION_REQUEST) {
if (opts[-4] == DHCPV6_MSG_SOLICIT) {
dest.msg_type = DHCPV6_MSG_ADVERTISE;
} else if (opts[-4] == DHCPV6_MSG_INFORMATION_REQUEST) {
+ if (!IN6_IS_ADDR_MULTICAST((struct in6_addr *)dest_addr) && iov[IOV_NESTED].iov_len == 0 &&
+ (opts[-4] == DHCPV6_MSG_REQUEST || opts[-4] == DHCPV6_MSG_RENEW ||
+ opts[-4] == DHCPV6_MSG_RELEASE || opts[-4] == DHCPV6_MSG_DECLINE)) {
+ iov[IOV_STAT].iov_base = &stat;
+ iov[IOV_STAT].iov_len = sizeof(stat);
+
+ for (ssize_t i = IOV_STAT + 1; i < IOV_TOTAL; ++i)
+ iov[i].iov_len = 0;
+
+ odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
+ return;
+ }
+
if (opts[-4] != DHCPV6_MSG_INFORMATION_REQUEST) {
ssize_t ialen = dhcpv6_handle_ia(pdbuf, sizeof(pdbuf), iface, addr, &opts[-4], opts_end);
iov[IOV_PDBUF].iov_len = ialen;
if (opts[-4] != DHCPV6_MSG_INFORMATION_REQUEST) {
ssize_t ialen = dhcpv6_handle_ia(pdbuf, sizeof(pdbuf), iface, addr, &opts[-4], opts_end);
iov[IOV_PDBUF].iov_len = ialen;
}
if (iov[IOV_NESTED].iov_len > 0) // Update length
}
if (iov[IOV_NESTED].iov_len > 0) // Update length
- update_nested_message(data, len, iov[IOV_DEST].iov_len + iov[IOV_DNS].iov_len +
- iov[IOV_DNS_ADDR].iov_len + iov[IOV_SEARCH].iov_len +
- iov[IOV_SEARCH_DOMAIN].iov_len + iov[IOV_PDBUF].iov_len +
- iov[IOV_CERID].iov_len + iov[IOV_DHCPV6_RAW].iov_len - (4 + opts_end - opts));
+ update_nested_message(data, len, iov[IOV_DEST].iov_len + iov[IOV_MAXRT].iov_len +
+ iov[IOV_DNS].iov_len + iov[IOV_DNS_ADDR].iov_len +
+ iov[IOV_SEARCH].iov_len + iov[IOV_SEARCH_DOMAIN].iov_len +
+ iov[IOV_PDBUF].iov_len + iov[IOV_CERID].iov_len +
+ iov[IOV_DHCPV6_RAW].iov_len - (4 + opts_end - opts));
odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
}
odhcpd_send(iface->dhcpv6_event.uloop.fd, addr, iov, ARRAY_SIZE(iov), iface);
}
// Central DHCPv6-relay handler
static void handle_dhcpv6(void *addr, void *data, size_t len,
// Central DHCPv6-relay handler
static void handle_dhcpv6(void *addr, void *data, size_t len,
- struct interface *iface)
+ struct interface *iface, void *dest_addr)
{
if (iface->dhcpv6 == RELAYD_SERVER) {
{
if (iface->dhcpv6 == RELAYD_SERVER) {
- handle_client_request(addr, data, len, iface);
+ handle_client_request(addr, data, len, iface, dest_addr);
} else if (iface->dhcpv6 == RELAYD_RELAY) {
if (iface->master)
relay_server_response(data, len);
} else if (iface->dhcpv6 == RELAYD_RELAY) {
if (iface->master)
relay_server_response(data, len);
#define DHCPV6_STATUS_NOADDRSAVAIL 2
#define DHCPV6_STATUS_NOBINDING 3
#define DHCPV6_STATUS_NOTONLINK 4
#define DHCPV6_STATUS_NOADDRSAVAIL 2
#define DHCPV6_STATUS_NOBINDING 3
#define DHCPV6_STATUS_NOTONLINK 4
+#define DHCPV6_STATUS_USEMULTICAST 5
#define DHCPV6_STATUS_NOPREFIXAVAIL 6
// I just remembered I have an old one lying around...
#define DHCPV6_STATUS_NOPREFIXAVAIL 6
// I just remembered I have an old one lying around...
static void handle_solicit(void *addr, void *data, size_t len,
static void handle_solicit(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest);
static void handle_rtnetlink(void *addr, void *data, size_t len,
static void handle_rtnetlink(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest);
static struct ndp_neighbor* find_neighbor(struct in6_addr *addr, bool strict);
static void modify_neighbor(struct in6_addr *addr, struct interface *iface,
bool add);
static struct ndp_neighbor* find_neighbor(struct in6_addr *addr, bool strict);
static void modify_neighbor(struct in6_addr *addr, struct interface *iface,
bool add);
// Handle solicitations
static void handle_solicit(void *addr, void *data, size_t len,
// Handle solicitations
static void handle_solicit(void *addr, void *data, size_t len,
- struct interface *iface)
+ struct interface *iface, _unused void *dest)
{
struct ip6_hdr *ip6 = data;
struct nd_neighbor_solicit *req = (struct nd_neighbor_solicit*)&ip6[1];
{
struct ip6_hdr *ip6 = data;
struct nd_neighbor_solicit *req = (struct nd_neighbor_solicit*)&ip6[1];
// Handler for neighbor cache entries from the kernel. This is our source
// to learn and unlearn hosts on interfaces.
static void handle_rtnetlink(_unused void *addr, void *data, size_t len,
// Handler for neighbor cache entries from the kernel. This is our source
// to learn and unlearn hosts on interfaces.
static void handle_rtnetlink(_unused void *addr, void *data, size_t len,
- _unused struct interface *iface)
+ _unused struct interface *iface, _unused void *dest)
{
for (struct nlmsghdr *nh = data; NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
{
for (struct nlmsghdr *nh = data; NLMSG_OK(nh, len);
nh = NLMSG_NEXT(nh, len)) {
// Extract destination interface
int destiface = 0;
int *hlim = NULL;
// Extract destination interface
int destiface = 0;
int *hlim = NULL;
struct in6_pktinfo *pktinfo;
struct in_pktinfo *pkt4info;
for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL; ch = CMSG_NXTHDR(&msg, ch)) {
struct in6_pktinfo *pktinfo;
struct in_pktinfo *pkt4info;
for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL; ch = CMSG_NXTHDR(&msg, ch)) {
ch->cmsg_type == IPV6_PKTINFO) {
pktinfo = (struct in6_pktinfo*)CMSG_DATA(ch);
destiface = pktinfo->ipi6_ifindex;
ch->cmsg_type == IPV6_PKTINFO) {
pktinfo = (struct in6_pktinfo*)CMSG_DATA(ch);
destiface = pktinfo->ipi6_ifindex;
+ dest = &pktinfo->ipi6_addr;
} else if (ch->cmsg_level == IPPROTO_IP &&
ch->cmsg_type == IP_PKTINFO) {
pkt4info = (struct in_pktinfo*)CMSG_DATA(ch);
destiface = pkt4info->ipi_ifindex;
} else if (ch->cmsg_level == IPPROTO_IP &&
ch->cmsg_type == IP_PKTINFO) {
pkt4info = (struct in_pktinfo*)CMSG_DATA(ch);
destiface = pkt4info->ipi_ifindex;
+ dest = &pkt4info->ipi_addr;
} else if (ch->cmsg_level == IPPROTO_IPV6 &&
ch->cmsg_type == IPV6_HOPLIMIT) {
hlim = (int*)CMSG_DATA(ch);
} else if (ch->cmsg_level == IPPROTO_IPV6 &&
ch->cmsg_type == IPV6_HOPLIMIT) {
hlim = (int*)CMSG_DATA(ch);
syslog(LOG_DEBUG, "Received %li Bytes from %s%%%s", (long)len,
ipbuf, (iface) ? iface->ifname : "netlink");
syslog(LOG_DEBUG, "Received %li Bytes from %s%%%s", (long)len,
ipbuf, (iface) ? iface->ifname : "netlink");
- e->handle_dgram(&addr, data_buf, len, iface);
+ e->handle_dgram(&addr, data_buf, len, iface, dest);
struct odhcpd_event {
struct uloop_fd uloop;
void (*handle_dgram)(void *addr, void *data, size_t len,
struct odhcpd_event {
struct uloop_fd uloop;
void (*handle_dgram)(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest_addr);
static void forward_router_advertisement(uint8_t *data, size_t len);
static void handle_icmpv6(void *addr, void *data, size_t len,
static void forward_router_advertisement(uint8_t *data, size_t len);
static void handle_icmpv6(void *addr, void *data, size_t len,
- struct interface *iface);
+ struct interface *iface, void *dest);
static void send_router_advert(struct uloop_timeout *event);
static void sigusr1_refresh(int signal);
static void send_router_advert(struct uloop_timeout *event);
static void sigusr1_refresh(int signal);
// Event handler for incoming ICMPv6 packets
static void handle_icmpv6(void *addr, void *data, size_t len,
// Event handler for incoming ICMPv6 packets
static void handle_icmpv6(void *addr, void *data, size_t len,
- struct interface *iface)
+ struct interface *iface, _unused void *dest)
{
struct icmp6_hdr *hdr = data;
{
struct icmp6_hdr *hdr = data;