From 8eb9058dc1f99a5eb9b8726a978fcc0720837a10 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 26 Aug 2015 19:19:32 +0200 Subject: dhcp,network: implement RFC 4833 (DHCP Timezone option) This one is simply to add: encode the tzdata timezone in the DHCP options and optionally make use of it. --- src/libsystemd-network/dhcp-lease-internal.h | 1 + src/libsystemd-network/dhcp-protocol.h | 2 ++ src/libsystemd-network/dhcp-server-internal.h | 2 ++ src/libsystemd-network/sd-dhcp-lease.c | 32 +++++++++++++++++++++ src/libsystemd-network/sd-dhcp-server.c | 27 ++++++++++++++++++ src/libsystemd/sd-network/sd-network.c | 22 +++++++++++++++ src/network/networkctl.c | 9 ++++-- src/network/networkd-dhcp4.c | 5 ++++ src/network/networkd-link.c | 29 +++++++++++++++++++ src/network/networkd-network-gperf.gperf | 3 ++ src/network/networkd-network.c | 40 ++++++++++++++++++++++++++- src/network/networkd.h | 5 ++++ src/systemd/sd-dhcp-lease.h | 1 + src/systemd/sd-dhcp-server.h | 2 ++ src/systemd/sd-network.h | 3 ++ 15 files changed, 180 insertions(+), 3 deletions(-) diff --git a/src/libsystemd-network/dhcp-lease-internal.h b/src/libsystemd-network/dhcp-lease-internal.h index 5a3fcddb1b..6e247e9865 100644 --- a/src/libsystemd-network/dhcp-lease-internal.h +++ b/src/libsystemd-network/dhcp-lease-internal.h @@ -83,6 +83,7 @@ struct sd_dhcp_lease { size_t client_id_len; uint8_t *vendor_specific; size_t vendor_specific_len; + char *timezone; LIST_HEAD(struct sd_dhcp_raw_option, private_options); }; diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index 4308723f91..88a81d2866 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -137,6 +137,8 @@ enum { DHCP_OPTION_REBINDING_T2_TIME = 59, DHCP_OPTION_VENDOR_CLASS_IDENTIFIER = 60, DHCP_OPTION_CLIENT_IDENTIFIER = 61, + DHCP_OPTION_NEW_POSIX_TIMEZONE = 100, + DHCP_OPTION_NEW_TZDB_TIMEZONE = 101, DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121, DHCP_OPTION_PRIVATE_BASE = 224, DHCP_OPTION_PRIVATE_LAST = 254, diff --git a/src/libsystemd-network/dhcp-server-internal.h b/src/libsystemd-network/dhcp-server-internal.h index 58750c4418..24f99f801d 100644 --- a/src/libsystemd-network/dhcp-server-internal.h +++ b/src/libsystemd-network/dhcp-server-internal.h @@ -62,6 +62,8 @@ struct sd_dhcp_server { size_t pool_size; size_t next_offer; + char *timezone; + Hashmap *leases_by_client_id; DHCPLease **bound_leases; }; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 57369a353d..c312c7cad0 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -608,6 +608,22 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const uint8_t *option, break; + case DHCP_OPTION_NEW_TZDB_TIMEZONE: { + _cleanup_free_ char *tz = NULL; + + r = lease_parse_string(option, len, &tz); + if (r < 0) + return r; + + if (!timezone_is_valid(tz)) + return -EINVAL; + + free(lease->timezone); + lease->timezone = tz; + tz = NULL; + break; + } + case DHCP_OPTION_VENDOR_SPECIFIC: if (len >= 1) { free(lease->vendor_specific); @@ -757,6 +773,10 @@ int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file) { if (r >= 0) serialize_dhcp_routes(f, "ROUTES", routes, r); + r = sd_dhcp_lease_get_timezone(lease, &string); + if (r >= 0) + fprintf(f, "TIMEZONE=%s\n", string); + r = sd_dhcp_lease_get_client_id(lease, &client_id, &client_id_len); if (r >= 0) { _cleanup_free_ char *client_id_hex; @@ -840,6 +860,7 @@ int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file) { "ROOT_PATH", &lease->root_path, "ROUTES", &routes, "CLIENTID", &client_id_hex, + "TIMEZONE", &lease->timezone, "VENDOR_SPECIFIC", &vendor_specific_hex, "OPTION_224", &options[0], "OPTION_225", &options[1], @@ -1026,3 +1047,14 @@ int dhcp_lease_set_client_id(sd_dhcp_lease *lease, const uint8_t *client_id, return 0; } + +int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone) { + assert_return(lease, -EINVAL); + assert_return(timezone, -EINVAL); + + if (!lease->timezone) + return -ENXIO; + + *timezone = lease->timezone; + return 0; +} diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index faeab0fd30..730d95e2c0 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -136,6 +136,8 @@ sd_dhcp_server *sd_dhcp_server_unref(sd_dhcp_server *server) { sd_event_unref(server->event); + free(server->timezone); + while ((lease = hashmap_steal_first(server->leases_by_client_id))) dhcp_lease_free(lease); hashmap_free(server->leases_by_client_id); @@ -474,6 +476,15 @@ static int server_send_ack(sd_dhcp_server *server, DHCPRequest *req, if (r < 0) return r; + if (server->timezone) { + r = dhcp_option_append( + &packet->dhcp, req->max_optlen, &offset, 0, + DHCP_OPTION_NEW_TZDB_TIMEZONE, + strlen(server->timezone), server->timezone); + if (r < 0) + return r; + } + r = dhcp_server_send_packet(server, req, packet, DHCP_ACK, offset); if (r < 0) return r; @@ -993,3 +1004,19 @@ int sd_dhcp_server_forcerenew(sd_dhcp_server *server) { return r; } + +int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone) { + int r; + + assert_return(server, -EINVAL); + assert_return(timezone_is_valid(timezone), -EINVAL); + + if (streq_ptr(timezone, server->timezone)) + return 0; + + r = free_and_strdup(&server->timezone, timezone); + if (r < 0) + return r; + + return 1; +} diff --git a/src/libsystemd/sd-network/sd-network.c b/src/libsystemd/sd-network/sd-network.c index b63fdf8fcb..87d87359b8 100644 --- a/src/libsystemd/sd-network/sd-network.c +++ b/src/libsystemd/sd-network/sd-network.c @@ -214,6 +214,28 @@ _public_ int sd_network_link_get_lldp(int ifindex, char **lldp) { return 0; } +int sd_network_link_get_timezone(int ifindex, char **ret) { + _cleanup_free_ char *s = NULL, *p = NULL; + int r; + + assert_return(ifindex > 0, -EINVAL); + assert_return(ret, -EINVAL); + + if (asprintf(&p, "/run/systemd/netif/links/%d", ifindex) < 0) + return -ENOMEM; + + r = parse_env_file(p, NEWLINE, "TIMEZONE", &s, NULL); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + if (isempty(s)) + return -ENODATA; + + *ret = s; + s = NULL; + return 0; +} static int network_get_link_strv(const char *key, int ifindex, char ***ret) { _cleanup_free_ char *p = NULL, *s = NULL; diff --git a/src/network/networkctl.c b/src/network/networkctl.c index c9b53dc3f6..2281d4b718 100644 --- a/src/network/networkctl.c +++ b/src/network/networkctl.c @@ -497,7 +497,7 @@ static int link_status_one( sd_hwdb *hwdb, const char *name) { _cleanup_strv_free_ char **dns = NULL, **ntp = NULL, **domains = NULL; - _cleanup_free_ char *setup_state = NULL, *operational_state = NULL; + _cleanup_free_ char *setup_state = NULL, *operational_state = NULL, *timezone = NULL; _cleanup_netlink_message_unref_ sd_netlink_message *req = NULL, *reply = NULL; _cleanup_device_unref_ sd_device *d = NULL; char devid[2 + DECIMAL_STR_MAX(int)]; @@ -570,7 +570,6 @@ static int link_status_one( setup_state_to_color(setup_state, &on_color_setup, &off_color_setup); sd_network_link_get_dns(ifindex, &dns); - sd_network_link_get_ntp(ifindex, &ntp); sd_network_link_get_domains(ifindex, &domains); r = sd_network_link_get_wildcard_domain(ifindex); if (r > 0) { @@ -652,6 +651,8 @@ static int link_status_one( dump_list(" DNS: ", dns); if (!strv_isempty(domains)) dump_list(" Domain: ", domains); + + (void) sd_network_link_get_ntp(ifindex, &ntp); if (!strv_isempty(ntp)) dump_list(" NTP: ", ntp); @@ -661,6 +662,10 @@ static int link_status_one( if (!strv_isempty(carrier_bound_by)) dump_list("Carrier Bound By: ", carrier_bound_by); + (void) sd_network_link_get_timezone(ifindex, &timezone); + if (timezone) + printf(" Time Zone: %s", timezone); + return 0; } diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 1b2ff7c769..7fce389fa2 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -624,6 +624,11 @@ int dhcp4_configure(Link *link) { return r; } + /* Always acquire the timezone */ + r = sd_dhcp_client_set_request_option(link->dhcp_client, DHCP_OPTION_NEW_TZDB_TIMEZONE); + if (r < 0) + return r; + if (link->network->dhcp_sendhost) { _cleanup_free_ char *hostname = NULL; const char *hn = NULL; diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c index cc9dc393c6..c179aafebb 100644 --- a/src/network/networkd-link.c +++ b/src/network/networkd-link.c @@ -672,6 +672,27 @@ static int link_enter_set_addresses(Link *link) { return r; */ + if (link->network->dhcp_server_emit_timezone) { + _cleanup_free_ char *buffer = NULL; + const char *tz; + + if (link->network->dhcp_server_timezone) + tz = link->network->dhcp_server_timezone; + else { + r = get_timezone(&buffer); + if (r < 0) + log_warning_errno(r, "Failed to determine timezone: %m"); + else + tz = buffer; + } + + if (tz) { + r = sd_dhcp_server_set_timezone(link->dhcp_server, tz); + if (r < 0) + return r; + } + } + r = sd_dhcp_server_start(link->dhcp_server); if (r < 0) { log_link_warning_errno(link, r, "Could not start DHCPv4 server instance: %m"); @@ -2413,6 +2434,14 @@ int link_save(Link *link) { fputs("\n", f); } + if (link->dhcp_lease) { + const char *tz = NULL; + + r = sd_dhcp_lease_get_timezone(link->dhcp_lease, &tz); + if (r >= 0) + fprintf(f, "TIMEZONE=%s\n", tz); + } + if (link->dhcp_lease) { assert(link->network); diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 7ac7ef1ea3..c8c612d4eb 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -73,6 +73,9 @@ DHCP.RequestBroadcast, config_parse_bool, 0 DHCP.CriticalConnection, config_parse_bool, 0, offsetof(Network, dhcp_critical) DHCP.VendorClassIdentifier, config_parse_string, 0, offsetof(Network, dhcp_vendor_class_identifier) DHCP.RouteMetric, config_parse_unsigned, 0, offsetof(Network, dhcp_route_metric) +DHCP.UseTimezone, config_parse_bool, 0, offsetof(Network, dhcp_timezone) +DHCPServer.EmitTimezone, config_parse_bool, 0, offsetof(Network, dhcp_server_emit_timezone) +DHCPServer.Timezone, config_parse_timezone, 0, offsetof(Network, dhcp_server_timezone) Bridge.Cost, config_parse_unsigned, 0, offsetof(Network, cost) Bridge.UseBPDU, config_parse_bool, 0, offsetof(Network, use_bpdu) Bridge.HairPin, config_parse_bool, 0, offsetof(Network, hairpin) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 8f773e738d..3619e3160c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -124,7 +124,8 @@ static int network_load_one(Manager *manager, const char *filename) { "Address\0" "Route\0" "DHCP\0" - "DHCPv4\0" + "DHCPv4\0" /* compat */ + "DHCPServer\0" "Bridge\0" "BridgeFDB\0", config_item_perf_lookup, network_network_gperf_lookup, @@ -258,6 +259,8 @@ void network_free(Network *network) { condition_free_list(network->match_kernel); condition_free_list(network->match_arch); + free(network->dhcp_server_timezone); + free(network); } @@ -849,3 +852,38 @@ int config_parse_hostname( *hostname = hostname_cleanup(hn); return 0; } + +int config_parse_timezone( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + char **timezone = data, *tz = NULL; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + + r = config_parse_string(unit, filename, line, section, section_line, lvalue, ltype, rvalue, &tz, userdata); + if (r < 0) + return r; + + if (!timezone_is_valid(tz)) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Timezone is not valid, ignoring assignment: %s", rvalue); + free(tz); + return 0; + } + + free(*timezone); + *timezone = tz; + + return 0; +} diff --git a/src/network/networkd.h b/src/network/networkd.h index 5340922bf1..a337ea7bf0 100644 --- a/src/network/networkd.h +++ b/src/network/networkd.h @@ -143,12 +143,15 @@ struct Network { bool dhcp_broadcast; bool dhcp_critical; bool dhcp_routes; + bool dhcp_timezone; unsigned dhcp_route_metric; AddressFamilyBoolean link_local; bool ipv4ll_route; union in_addr_union ipv6_token; bool dhcp_server; + char *dhcp_server_timezone; + bool dhcp_server_emit_timezone; bool use_bpdu; bool hairpin; @@ -461,3 +464,5 @@ int config_parse_ipv6_privacy_extensions(const char *unit, const char *filename, /* Hostname */ int config_parse_hostname(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); + +int config_parse_timezone(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/systemd/sd-dhcp-lease.h b/src/systemd/sd-dhcp-lease.h index 5afa50a9d0..6e109e03fd 100644 --- a/src/systemd/sd-dhcp-lease.h +++ b/src/systemd/sd-dhcp-lease.h @@ -49,6 +49,7 @@ int sd_dhcp_lease_get_vendor_specific(sd_dhcp_lease *lease, const uint8_t **data size_t *data_len); int sd_dhcp_lease_get_client_id(sd_dhcp_lease *lease, const uint8_t **client_id, size_t *client_id_len); +int sd_dhcp_lease_get_timezone(sd_dhcp_lease *lease, const char **timezone); int sd_dhcp_lease_save(sd_dhcp_lease *lease, const char *lease_file); int sd_dhcp_lease_load(sd_dhcp_lease **ret, const char *lease_file); diff --git a/src/systemd/sd-dhcp-server.h b/src/systemd/sd-dhcp-server.h index 9af3b6532d..a2e1995cf9 100644 --- a/src/systemd/sd-dhcp-server.h +++ b/src/systemd/sd-dhcp-server.h @@ -47,5 +47,7 @@ int sd_dhcp_server_stop(sd_dhcp_server *server); int sd_dhcp_server_set_address(sd_dhcp_server *server, struct in_addr *address, unsigned char prefixlen); int sd_dhcp_server_set_lease_pool(sd_dhcp_server *server, struct in_addr *start, size_t size); +int sd_dhcp_server_set_timezone(sd_dhcp_server *server, const char *timezone); + int sd_dhcp_server_forcerenew(sd_dhcp_server *server); #endif diff --git a/src/systemd/sd-network.h b/src/systemd/sd-network.h index 4d96c867d9..4179015fbf 100644 --- a/src/systemd/sd-network.h +++ b/src/systemd/sd-network.h @@ -122,6 +122,9 @@ int sd_network_link_get_carrier_bound_to(int ifindex, char ***carriers); /* Get the CARRIERS that are bound to current link. */ int sd_network_link_get_carrier_bound_by(int ifindex, char ***carriers); +/* Get the timezone that was learnt on a specific link. */ +int sd_network_link_get_timezone(int ifindex, char **timezone); + /* Returns whether or not domains that don't match any link should be resolved * on this link. 1 for yes, 0 for no and negative value for error */ int sd_network_link_get_wildcard_domain(int ifindex); -- cgit v1.2.3-54-g00ecf