[systemd-devel] [PATCH 16/24] sd-dhcp6-client: Receive and parse Advertise messages

Zbigniew Jędrzejewski-Szmek zbyszek at in.waw.pl
Wed Jun 18 07:11:59 PDT 2014


On Fri, Jun 13, 2014 at 04:45:06PM +0300, Patrik Flykt wrote:
> When receiving DHCPv6 messages, discard the ones that are not meant
> for DHCPv6 clients and verify the transaction id. Once that is done,
> process the Advertise message and select the Advertise with the
> highest preference.
> 
> Create a separate function for lease information parsing so that it
> can be reused in other parts of the protocol. Verify both DUID and
> IAID in the received message and store other necessary information
> with the lease structure.
> ---
>  src/libsystemd-network/dhcp6-internal.h  |   2 +
>  src/libsystemd-network/dhcp6-protocol.h  |   6 +
>  src/libsystemd-network/sd-dhcp6-client.c | 219 ++++++++++++++++++++++++++++++-
>  3 files changed, 225 insertions(+), 2 deletions(-)
> 
> diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h
> index ec1d82a..94e3a5d 100644
> --- a/src/libsystemd-network/dhcp6-internal.h
> +++ b/src/libsystemd-network/dhcp6-internal.h
> @@ -75,3 +75,5 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *address,
>  
>  const char *dhcp6_message_type_to_string(int s) _const_;
>  int dhcp6_message_type_from_string(const char *s) _pure_;
> +const char *dhcp6_message_status_to_string(int s) _const_;
> +int dhcp6_message_status_from_string(const char *s) _pure_;
> diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h
> index de100d7..e8df509 100644
> --- a/src/libsystemd-network/dhcp6-protocol.h
> +++ b/src/libsystemd-network/dhcp6-protocol.h
> @@ -21,6 +21,9 @@
>    along with systemd; If not, see <http://www.gnu.org/licenses/>.
>  ***/
>  
> +#include <netinet/ip6.h>
> +#include <netinet/udp.h>
> +
>  #include "macro.h"
>  #include "sparse-endian.h"
>  
> @@ -36,6 +39,9 @@ struct DHCP6Message {
>  
>  typedef struct DHCP6Message DHCP6Message;
>  
> +#define DHCP6_MIN_OPTIONS_SIZE \
> +        1280 - sizeof(struct ip6_hdr) - sizeof(struct udphdr)
> +
>  #define IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT \
>          { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
>                0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02 } } }
> diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c
> index bdd9177..a18698c 100644
> --- a/src/libsystemd-network/sd-dhcp6-client.c
> +++ b/src/libsystemd-network/sd-dhcp6-client.c
> @@ -21,6 +21,7 @@
>  
>  #include <errno.h>
>  #include <string.h>
> +#include <sys/ioctl.h>
>  
>  #include "udev.h"
>  #include "udev-util.h"
> @@ -34,6 +35,7 @@
>  #include "dhcp6-protocol.h"
>  #include "dhcp6-internal.h"
>  #include "icmp6-nd.h"
> +#include "dhcp6-lease-internal.h"
>  
>  #define SYSTEMD_PEN 43793
>  #define HASH_KEY SD_ID128_MAKE(80,11,8c,c2,fe,4a,03,ee,3e,d6,0c,6f,36,39,14,09)
> @@ -49,6 +51,7 @@ struct sd_dhcp6_client {
>          icmp6_nd *ra;
>          DHCP6IA ia_na;
>          be32_t transaction_id;
> +        struct sd_dhcp6_lease *lease;
>          int fd;
>          sd_event_source *receive_message;
>          usec_t retransmit_time;
> @@ -83,6 +86,17 @@ const char * dhcp6_message_type_table[_DHCP6_MESSAGE_MAX] = {
>  
>  DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_type, int);
>  
> +const char * dhcp6_message_status_table[_DHCP6_STATUS_MAX] = {
> +        [DHCP6_STATUS_SUCCESS] = "Success",
> +        [DHCP6_STATUS_UNSPEC_FAIL] = "Unspecified failure",
> +        [DHCP6_STATUS_NO_ADDRS_AVAIL] = "No addresses available",
> +        [DHCP6_STATUS_NO_BINDING] = "Binding unavailable",
> +        [DHCP6_STATUS_NOT_ON_LINK] = "Not on link",
> +        [DHCP6_STATUS_USE_MULTICAST] = "Use multicast",
> +};
> +
> +DEFINE_STRING_TABLE_LOOKUP(dhcp6_message_status, int);
> +
>  int sd_dhcp6_client_set_callback(sd_dhcp6_client *client,
>                                   sd_dhcp6_client_cb_t cb, void *userdata)
>  {
> @@ -381,9 +395,210 @@ static int client_ensure_iaid(sd_dhcp6_client *client) {
>          return 0;
>  }
>  
> +static int client_parse_message(sd_dhcp6_client *client,
> +                                DHCP6Message *message, size_t len,
> +                                sd_dhcp6_lease *lease) {
> +        int r;
> +        uint8_t *optval, *option = (uint8_t *)(message + 1), *id = NULL;
> +        uint16_t optcode, status;
> +        size_t optlen, id_len;
> +        bool clientid = false;
> +        be32_t iaid_lease;
> +
> +        while ((r = dhcp6_option_parse(&option, &len, &optcode, &optlen,
> +                                       &optval)) >= 0) {
> +                switch (optcode) {
> +                case DHCP6_OPTION_CLIENTID:
> +                        if (clientid) {
> +                                log_dhcp6_client(client, "%s contains multiple clientids",
> +                                                 dhcp6_message_type_to_string(message->type));
> +                                return -EINVAL;
> +                        }
> +
> +                        if (optlen != sizeof(client->duid) ||
> +                            memcmp(&client->duid, optval, optlen) != 0) {
> +                                log_dhcp6_client(client, "%s DUID does not match",
> +                                                 dhcp6_message_type_to_string(message->type));
> +
> +                                return -EINVAL;
> +                        }
> +                        clientid = true;
> +
> +                        break;
> +
> +                case DHCP6_OPTION_SERVERID:
> +                        r = dhcp6_lease_get_serverid(lease, &id, &id_len);
> +                        if (r >= 0 && id) {
> +                                log_dhcp6_client(client, "%s contains multiple serverids",
> +                                                 dhcp6_message_type_to_string(message->type));
> +                                return -EINVAL;
> +                        }
> +
> +                        r = dhcp6_lease_set_serverid(lease, optval, optlen);
> +                        if (r < 0)
> +                                return r;
> +
> +                        break;
> +
> +                case DHCP6_OPTION_PREFERENCE:
> +                        if (optlen != 1)
> +                                return -EINVAL;
> +
> +                        r = dhcp6_lease_set_preference(lease, *optval);
> +                        if (r < 0)
> +                                return r;
> +
> +                        break;
> +
> +                case DHCP6_OPTION_STATUS_CODE:
> +                        if (optlen < 2)
> +                                return -EINVAL;
> +
> +                        status = optval[0] << 8 | optval[1];
> +                        if (status) {
> +                                log_dhcp6_client(client, "%s Status %s",
> +                                                 dhcp6_message_type_to_string(message->type),
> +                                                 dhcp6_message_status_to_string(status));
> +                                return -EINVAL;
> +                        }
> +
> +                        break;
> +
> +                case DHCP6_OPTION_IA_NA:
> +                        r = dhcp6_option_parse_ia(&optval, &optlen, optcode,
> +                                                  &lease->ia);
> +                        if (r < 0 && r != -ENOMSG)
> +                                return r;
> +
> +                        r = dhcp6_lease_get_iaid(lease, &iaid_lease);
> +                        if (r < 0)
> +                                return r;
> +
> +                        if (client->ia_na.id != iaid_lease) {
> +                                log_dhcp6_client(client, "%s has wrong IAID",
> +                                                 dhcp6_message_type_to_string(message->type));
> +                                return -EINVAL;
> +                        }
> +
> +                        break;
> +                }
> +        }
> +
> +        if ((r < 0 && r != -ENOMSG) || !clientid) {
> +                log_dhcp6_client(client, "%s has incomplete options",
> +                                 dhcp6_message_type_to_string(message->type));
> +                return -EINVAL;
> +        }
> +
> +        r = dhcp6_lease_get_serverid(lease, &id, &id_len);
> +        if (r < 0)
> +                log_dhcp6_client(client, "%s has no server id",
> +                                 dhcp6_message_type_to_string(message->type));
> +
> +return r;
Indentation.

> +}
> +
> +static int client_receive_advertise(sd_dhcp6_client *client,
> +                                    DHCP6Message *advertise, size_t len) {
> +        int r;

Zbyszek


More information about the systemd-devel mailing list