summaryrefslogtreecommitdiff
path: root/src/libsystemd-network
diff options
context:
space:
mode:
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r--src/libsystemd-network/dhcp-protocol.h10
-rw-r--r--src/libsystemd-network/sd-dhcp-client.c53
-rw-r--r--src/libsystemd-network/sd-ndisc.c18
3 files changed, 70 insertions, 11 deletions
diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h
index 69b37fc352..05bb5ae493 100644
--- a/src/libsystemd-network/dhcp-protocol.h
+++ b/src/libsystemd-network/dhcp-protocol.h
@@ -137,6 +137,7 @@ enum {
DHCP_OPTION_REBINDING_T2_TIME = 59,
DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60,
DHCP_OPTION_CLIENT_IDENTIFIER = 61,
+ DHCP_OPTION_FQDN = 81,
DHCP_OPTION_NEW_POSIX_TIMEZONE = 100,
DHCP_OPTION_NEW_TZDB_TIMEZONE = 101,
DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
@@ -144,3 +145,12 @@ enum {
DHCP_OPTION_PRIVATE_LAST = 254,
DHCP_OPTION_END = 255,
};
+
+#define DHCP_MAX_FQDN_LENGTH 255
+
+enum {
+ DHCP_FQDN_FLAG_S = (1 << 0),
+ DHCP_FQDN_FLAG_O = (1 << 1),
+ DHCP_FQDN_FLAG_E = (1 << 2),
+ DHCP_FQDN_FLAG_N = (1 << 3),
+};
diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c
index 137537253a..5ec0e661f7 100644
--- a/src/libsystemd-network/sd-dhcp-client.c
+++ b/src/libsystemd-network/sd-dhcp-client.c
@@ -34,6 +34,8 @@
#include "dhcp-internal.h"
#include "dhcp-lease-internal.h"
#include "dhcp-protocol.h"
+#include "dns-domain.h"
+#include "hostname-util.h"
#include "random-util.h"
#include "string-util.h"
#include "util.h"
@@ -298,6 +300,9 @@ int sd_dhcp_client_set_hostname(sd_dhcp_client *client,
assert_return(client, -EINVAL);
+ if (!hostname_is_valid(hostname, false) && !dns_name_is_valid(hostname))
+ return -EINVAL;
+
if (streq_ptr(client->hostname, hostname))
return 0;
@@ -539,6 +544,24 @@ static int client_message_init(sd_dhcp_client *client, DHCPPacket **ret,
return 0;
}
+static int client_append_fqdn_option(DHCPMessage *message, size_t optlen, size_t *optoffset,
+ const char *fqdn) {
+ uint8_t buffer[3 + DHCP_MAX_FQDN_LENGTH];
+ int r;
+
+ buffer[0] = DHCP_FQDN_FLAG_S | /* Request server to perform A RR DNS updates */
+ DHCP_FQDN_FLAG_E; /* Canonical wire format */
+ buffer[1] = 0; /* RCODE1 (deprecated) */
+ buffer[2] = 0; /* RCODE2 (deprecated) */
+
+ r = dns_name_to_wire_format(fqdn, buffer + 3, sizeof(buffer) - 3);
+ if (r > 0)
+ r = dhcp_option_append(message, optlen, optoffset, 0,
+ DHCP_OPTION_FQDN, 3 + r, buffer);
+
+ return r;
+}
+
static int dhcp_client_send_raw(sd_dhcp_client *client, DHCPPacket *packet,
size_t len) {
dhcp_packet_append_ip_headers(packet, INADDR_ANY, DHCP_PORT_CLIENT,
@@ -576,13 +599,21 @@ static int client_send_discover(sd_dhcp_client *client) {
return r;
}
- /* it is unclear from RFC 2131 if client should send hostname in
- DHCPDISCOVER but dhclient does and so we do as well
- */
if (client->hostname) {
- r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_HOST_NAME,
- strlen(client->hostname), client->hostname);
+ /* According to RFC 4702 "clients that send the Client FQDN option in
+ their messages MUST NOT also send the Host Name option". Just send
+ one of the two depending on the hostname type.
+ */
+ if (dns_name_single_label(client->hostname)) {
+ /* it is unclear from RFC 2131 if client should send hostname in
+ DHCPDISCOVER but dhclient does and so we do as well
+ */
+ r = dhcp_option_append(&discover->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_HOST_NAME,
+ strlen(client->hostname), client->hostname);
+ } else
+ r = client_append_fqdn_option(&discover->dhcp, optlen, &optoffset,
+ client->hostname);
if (r < 0)
return r;
}
@@ -688,9 +719,13 @@ static int client_send_request(sd_dhcp_client *client) {
}
if (client->hostname) {
- r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
- DHCP_OPTION_HOST_NAME,
- strlen(client->hostname), client->hostname);
+ if (dns_name_single_label(client->hostname))
+ r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0,
+ DHCP_OPTION_HOST_NAME,
+ strlen(client->hostname), client->hostname);
+ else
+ r = client_append_fqdn_option(&request->dhcp, optlen, &optoffset,
+ client->hostname);
if (r < 0)
return r;
}
diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c
index 6703d87bc4..713438f212 100644
--- a/src/libsystemd-network/sd-ndisc.c
+++ b/src/libsystemd-network/sd-ndisc.c
@@ -32,6 +32,7 @@
#include "in-addr-util.h"
#include "list.h"
#include "socket-util.h"
+#include "string-util.h"
#define NDISC_ROUTER_SOLICITATION_INTERVAL 4 * USEC_PER_SEC
#define NDISC_MAX_ROUTER_SOLICITATIONS 3
@@ -484,6 +485,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
int r, buflen = 0, pref, stateful;
union sockaddr_union router = {};
socklen_t router_len = sizeof(router);
+ struct in6_addr *gw;
unsigned lifetime;
ssize_t len;
@@ -503,9 +505,21 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
if (len < 0) {
log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m");
return 0;
- } else if (router_len != sizeof(router.in6) && router_len != 0) {
+ } else if (router_len == 0)
+ gw = NULL; /* only happens when running the test-suite over a socketpair */
+ else if (router_len != sizeof(router.in6)) {
log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len);
return 0;
+ } else
+ gw = &router.in6.sin6_addr;
+
+ if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) {
+ _cleanup_free_ char *addr = NULL;
+
+ (void)in_addr_to_string(AF_INET6, (const union in_addr_union*) gw, &addr);
+
+ log_ndisc(nd, "Received RA from non-link-local address %s. Ignoring.", strna(addr));
+ return 0;
}
if (ra->nd_ra_type != ND_ROUTER_ADVERT)
@@ -544,7 +558,7 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r
}
if (nd->router_callback)
- nd->router_callback(nd, stateful, router_len != 0 ? &router.in6.sin6_addr : NULL, lifetime, pref, nd->userdata);
+ nd->router_callback(nd, stateful, gw, lifetime, pref, nd->userdata);
return 0;
}