From 34e8c5a23cd1c53ef3c1169388dabe1f6dfd7319 Mon Sep 17 00:00:00 2001 From: Patrik Flykt Date: Thu, 19 Jun 2014 15:39:23 +0300 Subject: [PATCH] sd-dhcp6-client: Add functions to bind to DHCPv6 UDP socket Add a function that creates a UDP socket bound to the given interface and optionally to an IPv6 address. Add another function that will send the DHCPv6 UDP packet to its destination. Using IPV6_PKTINFO in setsockopt to bind the IPv6 socket to an interface is documented in section 4. of RFC 3542, "Advanced Sockets Application Program Interface (API) for IPv6" Add a define for DHCPv6 Relay Agents and Servers multicast address as its not available elsewhere. --- src/libsystemd-network/dhcp6-internal.h | 4 ++ src/libsystemd-network/dhcp6-network.c | 63 +++++++++++++++++++++++++ src/libsystemd-network/dhcp6-protocol.h | 4 ++ 3 files changed, 71 insertions(+) diff --git a/src/libsystemd-network/dhcp6-internal.h b/src/libsystemd-network/dhcp6-internal.h index 30b624d54..7a491fb2e 100644 --- a/src/libsystemd-network/dhcp6-internal.h +++ b/src/libsystemd-network/dhcp6-internal.h @@ -65,3 +65,7 @@ int dhcp6_option_append(uint8_t **buf, size_t *buflen, uint16_t code, int dhcp6_option_append_ia(uint8_t **buf, size_t *buflen, DHCP6IA *ia); int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode, size_t *optlen, uint8_t **optvalue); + +int dhcp6_network_bind_udp_socket(int index, struct in6_addr *address); +int dhcp6_network_send_udp_socket(int s, struct in6_addr *address, + const void *packet, size_t len); diff --git a/src/libsystemd-network/dhcp6-network.c b/src/libsystemd-network/dhcp6-network.c index 53ce23d16..fe56c1027 100644 --- a/src/libsystemd-network/dhcp6-network.c +++ b/src/libsystemd-network/dhcp6-network.c @@ -31,6 +31,7 @@ #include "socket-util.h" #include "dhcp6-internal.h" +#include "dhcp6-protocol.h" #define IN6ADDR_ALL_ROUTERS_MULTICAST_INIT \ { { { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ @@ -129,3 +130,65 @@ int dhcp_network_icmp6_send_router_solicitation(int s, const struct ether_addr * return 0; } + +int dhcp6_network_bind_udp_socket(int index, struct in6_addr *local_address) { + struct in6_pktinfo pktinfo = { + .ipi6_ifindex = index, + }; + union sockaddr_union src = { + .in6.sin6_family = AF_INET6, + .in6.sin6_port = htobe16(DHCP6_PORT_CLIENT), + .in6.sin6_addr = IN6ADDR_ANY_INIT, + }; + _cleanup_close_ int s = -1; + int r, off = 0, on = 1; + + if (local_address) + memcpy(&src.in6.sin6_addr, local_address, + sizeof(src.in6.sin6_addr)); + + s = socket(AF_INET6, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, + IPPROTO_UDP); + if (s < 0) + return -errno; + + r = setsockopt(s, IPPROTO_IPV6, IPV6_PKTINFO, &pktinfo, + sizeof(pktinfo)); + if (r < 0) + return -errno; + + r = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (r < 0) + return -errno; + + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &off, sizeof(off)); + if (r < 0) + return -errno; + + r = bind(s, &src.sa, sizeof(src.in6)); + if (r < 0) + return -errno; + + r = s; + s = -1; + return r; +} + +int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address, + const void *packet, size_t len) { + union sockaddr_union dest = { + .in6.sin6_family = AF_INET6, + .in6.sin6_port = htobe16(DHCP6_PORT_SERVER), + }; + int r; + + assert(server_address); + + memcpy(&dest.in6.sin6_addr, server_address, sizeof(dest.in6.sin6_addr)); + + r = sendto(s, packet, len, 0, &dest.sa, sizeof(dest.in6)); + if (r < 0) + return -errno; + + return 0; +} diff --git a/src/libsystemd-network/dhcp6-protocol.h b/src/libsystemd-network/dhcp6-protocol.h index 6ca72ec15..c58a07b17 100644 --- a/src/libsystemd-network/dhcp6-protocol.h +++ b/src/libsystemd-network/dhcp6-protocol.h @@ -36,6 +36,10 @@ struct DHCP6Message { typedef struct DHCP6Message DHCP6Message; +#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 } } } + enum { DHCP6_PORT_SERVER = 547, DHCP6_PORT_CLIENT = 546, -- 2.30.2