DHCPv6 destination address check
authorHans Dedecker <dedeckeh@gmail.com>
Tue, 1 Jul 2014 19:26:34 +0000 (21:26 +0200)
committerHans Dedecker <dedeckeh@gmail.com>
Tue, 1 Jul 2014 19:46:32 +0000 (21:46 +0200)
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.

src/dhcpv4.c
src/dhcpv6.c
src/dhcpv6.h
src/ndp.c
src/odhcpd.c
src/odhcpd.h
src/router.c

index b8d64ceaf7358c60bba1a6e3feaef6df90ecbfb3..9746aa2f3f9c6a8e88bc32683eed50d6821b5dcf 100644 (file)
@@ -34,7 +34,7 @@
 
 
 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);
@@ -225,7 +225,7 @@ static void dhcpv4_put(struct dhcpv4_message *msg, uint8_t **cookie,
 
 // 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;
index c2bb1c1d04367975aaae3f3a3344a48c3cebe116..f0a4938788a96ca7163c2c640dba6413990086aa 100644 (file)
@@ -28,9 +28,9 @@ static void relay_client_request(struct sockaddr_in6 *source,
 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,
-               struct interface *iface);
+               struct interface *iface, void *dest_addr);
 
 
 
@@ -100,6 +100,8 @@ int setup_dhcpv6_interface(struct interface *iface, bool enable)
 enum {
        IOV_NESTED = 0,
        IOV_DEST,
+       IOV_MAXRT,
+#define IOV_STAT IOV_MAXRT
        IOV_DNS,
        IOV_DNS_ADDR,
        IOV_SEARCH,
@@ -167,12 +169,12 @@ static void update_nested_message(uint8_t *data, size_t len, ssize_t pdiff)
        }
 }
 
-
 // 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;
+
        if (len < sizeof(*hdr))
                return;
 
@@ -187,9 +189,6 @@ static void handle_client_request(void *addr, void *data, size_t len,
                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];
@@ -199,20 +198,24 @@ static void handle_client_request(void *addr, void *data, size_t len,
                .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}
        };
        odhcpd_get_mac(iface, dest.mac);
 
+       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),
-                       htons(DHCPV6_STATUS_NOADDRSAVAIL)};
+                       htons(DHCPV6_STATUS_USEMULTICAST)};
 
        struct __attribute__((packed)) {
                uint16_t type;
@@ -269,6 +272,7 @@ static void handle_client_request(void *addr, void *data, size_t len,
        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},
@@ -288,6 +292,11 @@ static void handle_client_request(void *addr, void *data, size_t len,
        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) {
@@ -332,6 +341,19 @@ static void handle_client_request(void *addr, void *data, size_t len,
                }
        }
 
+       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;
@@ -340,10 +362,11 @@ static void handle_client_request(void *addr, void *data, size_t len,
        }
 
        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);
 }
@@ -351,10 +374,10 @@ static void handle_client_request(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) {
-               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);
index dbde88a040b0b8fc3bb468728f43c0d372e528d3..e8928845a532814673adc9248df54bc23fab8566 100644 (file)
@@ -69,6 +69,7 @@
 #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...
index 17ec8b48efa3657c57dba3de677b200446923906..50529b536f6fa4949d377ea819820e5b48266feb 100644 (file)
--- a/src/ndp.c
+++ b/src/ndp.c
@@ -32,9 +32,9 @@
 
 
 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,
-               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);
@@ -223,7 +223,7 @@ static ssize_t ping6(struct in6_addr *addr,
 
 // 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];
@@ -435,7 +435,7 @@ static void modify_neighbor(struct in6_addr *addr,
 // 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)) {
index bf9f16d8932845b454569572be627df78ccac6d7..794bbfc00648b6b08ad0bffe1944e03fd862ad38 100644 (file)
@@ -319,6 +319,7 @@ static void odhcpd_receive_packets(struct uloop_fd *u, _unused unsigned int even
                // Extract destination interface
                int destiface = 0;
                int *hlim = NULL;
+               void *dest = NULL;
                struct in6_pktinfo *pktinfo;
                struct in_pktinfo *pkt4info;
                for (struct cmsghdr *ch = CMSG_FIRSTHDR(&msg); ch != NULL; ch = CMSG_NXTHDR(&msg, ch)) {
@@ -326,10 +327,12 @@ static void odhcpd_receive_packets(struct uloop_fd *u, _unused unsigned int even
                                        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;
+                               dest = &pkt4info->ipi_addr;
                        } else if (ch->cmsg_level == IPPROTO_IPV6 &&
                                        ch->cmsg_type == IPV6_HOPLIMIT) {
                                hlim = (int*)CMSG_DATA(ch);
@@ -363,7 +366,7 @@ static void odhcpd_receive_packets(struct uloop_fd *u, _unused unsigned int even
                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);
        }
 }
 
index dfaec9b2bc99edcbc2dc9cca205f61f1f6ac431a..b2b38dc8d47387dbd693bc22f4f87e6671db9796 100644 (file)
@@ -61,7 +61,7 @@ extern struct list_head leases;
 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);
 };
 
 
index 772d77017b4112859f6824dc55e8057bff142912..f8ba5d7aac0332daa0c8776e12183c4c06e48edd 100644 (file)
@@ -30,7 +30,7 @@ static void forward_router_solicitation(const struct interface *iface);
 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);
 
@@ -170,7 +170,7 @@ static bool router_icmpv6_valid(struct sockaddr_in6 *source, uint8_t *data, size
 
 // 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;