diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index fe71f587a4..4bb6c83399 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -694,12 +694,40 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, /* https://tools.ietf.org/html/rfc2132#section-8.1 */ - v_str = nm_utils_buf_utf8safe_escape((char *) l_data, l_data_len, 0, &to_free); + v_str = nm_utils_buf_utf8safe_escape((char *) l_data, + l_data_len, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NIS_DOMAIN, v_str ?: ""); nm_ip4_config_set_nis_domain(ip4_config, v_str ?: ""); } + r = n_dhcp4_client_lease_get_file(lease, &v_str); + if (r == 0) { + gs_free char *to_free = NULL; + + v_str = nm_utils_buf_utf8safe_escape(v_str, + -1, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); + nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_FILENAME, v_str ?: ""); + } + + r = _client_lease_query(lease, NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, &l_data, &l_data_len); + if (r == 0 && nm_dhcp_lease_data_parse_cstr(l_data, l_data_len, &l_data_len)) { + gs_free char *to_free = NULL; + + v_str = nm_utils_buf_utf8safe_escape((char *) l_data, + l_data_len, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); + nm_dhcp_option_add_option(options, + AF_INET, + NM_DHCP_OPTION_DHCP4_BOOTFILE_NAME, + v_str ?: ""); + } + lease_parse_address_list(lease, ip4_config, NM_DHCP_OPTION_DHCP4_NIS_SERVERS, options, &sbuf); lease_parse_address_list(lease, diff --git a/src/core/dhcp/nm-dhcp-options.c b/src/core/dhcp/nm-dhcp-options.c index 8f0d7408a9..011f23e4e2 100644 --- a/src/core/dhcp/nm-dhcp-options.c +++ b/src/core/dhcp/nm-dhcp-options.c @@ -169,6 +169,7 @@ const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = { REQ(NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, "ip_address", FALSE), REQ(NM_DHCP_OPTION_DHCP4_NM_EXPIRY, "expiry", FALSE), REQ(NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER, "next_server", FALSE), + REQ(NM_DHCP_OPTION_DHCP4_NM_FILENAME, "filename", FALSE), }; static const NMDhcpOption *const _sorted_options_4[G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options)] = { @@ -184,7 +185,7 @@ static const NMDhcpOption *const _sorted_options_4[G_N_ELEMENTS(_nm_dhcp_option_ A(98), A(99), A(100), A(101), A(102), A(103), A(104), A(105), A(106), A(107), A(108), A(109), A(110), A(111), A(112), A(113), A(114), A(115), A(116), A(117), A(118), A(119), A(120), A(121), A(122), A(123), A(124), A(125), A(126), A(127), A(128), A(129), A(130), A(131), A(132), A(133), - A(134), A(15), A(135), A(136), A(16), A(137), A(138), A(139), A(140), A(141), + A(134), A(15), A(135), A(136), A(16), A(137), A(138), A(139), A(140), A(141), A(142), #undef A }; @@ -404,7 +405,10 @@ nm_dhcp_option_add_option_utf8safe_escape(GHashTable * options, gs_free char *to_free = NULL; const char * escaped; - escaped = nm_utils_buf_utf8safe_escape((char *) data, n_data, 0, &to_free); + escaped = nm_utils_buf_utf8safe_escape((char *) data, + n_data, + NM_UTILS_STR_UTF8_SAFE_FLAG_ESCAPE_CTRL, + &to_free); nm_dhcp_option_add_option(options, addr_family, option, escaped ?: ""); } diff --git a/src/core/dhcp/nm-dhcp-options.h b/src/core/dhcp/nm-dhcp-options.h index 585f118795..8ed2039d40 100644 --- a/src/core/dhcp/nm-dhcp-options.h +++ b/src/core/dhcp/nm-dhcp-options.h @@ -153,6 +153,7 @@ typedef enum { NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS = 1024, NM_DHCP_OPTION_DHCP4_NM_EXPIRY = 1025, NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER = 1026, + NM_DHCP_OPTION_DHCP4_NM_FILENAME = 1027, /* 'file' DHCP header */ } NMDhcpOptionDhcp4Options; typedef enum { @@ -185,7 +186,7 @@ typedef struct { bool include; } NMDhcpOption; -extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[142]; +extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[143]; extern const NMDhcpOption _nm_dhcp_option_dhcp6_options[16]; static inline const char * diff --git a/src/core/nm-config.c b/src/core/nm-config.c index fdc450a0e6..94ec7f381b 100644 --- a/src/core/nm-config.c +++ b/src/core/nm-config.c @@ -2343,8 +2343,9 @@ _nm_config_state_set(NMConfig *self, gboolean allow_persist, gboolean force_pers "route-metric-default-aspired" #define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROUTE_METRIC_DEFAULT_EFFECTIVE \ "route-metric-default-effective" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" -#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH "root-path" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_NEXT_SERVER "next-server" +#define DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_DHCP_BOOTFILE "dhcp-bootfile" static NM_UTILS_LOOKUP_STR_DEFINE( _device_state_managed_type_to_str, @@ -2565,7 +2566,8 @@ nm_config_device_state_write(int ifindex, guint32 route_metric_default_aspired, guint32 route_metric_default_effective, const char * next_server, - const char * root_path) + const char * root_path, + const char * dhcp_bootfile) { char path[NM_STRLEN(NM_CONFIG_DEVICE_STATE_DIR "/") + DEVICE_STATE_FILENAME_LEN_MAX + 1]; GError *local = NULL; @@ -2632,6 +2634,12 @@ nm_config_device_state_write(int ifindex, DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_ROOT_PATH, root_path); } + if (dhcp_bootfile) { + g_key_file_set_string(kf, + DEVICE_RUN_STATE_KEYFILE_GROUP_DEVICE, + DEVICE_RUN_STATE_KEYFILE_KEY_DEVICE_DHCP_BOOTFILE, + dhcp_bootfile); + } if (!g_key_file_save_to_file(kf, path, &local)) { _LOGW("device-state: write #%d (%s) failed: %s", ifindex, path, local->message); @@ -2639,7 +2647,9 @@ nm_config_device_state_write(int ifindex, return FALSE; } _LOGT("device-state: write #%d (%s); managed=%s%s%s%s%s%s%s, " - "route-metric-default=%" G_GUINT32_FORMAT "-%" G_GUINT32_FORMAT "%s%s%s%s%s%s", + "route-metric-default=%" G_GUINT32_FORMAT "-%" G_GUINT32_FORMAT "%s%s%s" + "%s%s%s" + "%s%s%s", ifindex, path, _device_state_managed_type_to_str(managed), @@ -2648,7 +2658,8 @@ nm_config_device_state_write(int ifindex, route_metric_default_aspired, route_metric_default_effective, NM_PRINT_FMT_QUOTED(next_server, ", next-server=", next_server, "", ""), - NM_PRINT_FMT_QUOTED(root_path, ", root-path=", root_path, "", "")); + NM_PRINT_FMT_QUOTED(root_path, ", root-path=", root_path, "", ""), + NM_PRINT_FMT_QUOTED(dhcp_bootfile, ", dhcp-bootfile=", dhcp_bootfile, "", "")); return TRUE; } diff --git a/src/core/nm-config.h b/src/core/nm-config.h index 7f1c0c4c57..8279f4d264 100644 --- a/src/core/nm-config.h +++ b/src/core/nm-config.h @@ -185,7 +185,8 @@ gboolean nm_config_device_state_write(int guint32 route_metric_default_aspired, guint32 route_metric_default_effective, const char * next_server, - const char * root_path); + const char * root_path, + const char * dhcp_bootfile); void nm_config_device_state_prune_stale(GHashTable *preserve_ifindexes, NMPlatform *preserve_in_platform); diff --git a/src/core/nm-manager.c b/src/core/nm-manager.c index b66f7fbb37..c1c8034a77 100644 --- a/src/core/nm-manager.c +++ b/src/core/nm-manager.c @@ -6803,8 +6803,9 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde guint32 route_metric_default_effective; NMTernary nm_owned; NMDhcpConfig * dhcp_config; - const char * next_server = NULL; - const char * root_path = NULL; + const char * next_server = NULL; + const char * root_path = NULL; + const char * dhcp_bootfile = NULL; NM_SET_OUT(out_ifindex, 0); @@ -6848,8 +6849,11 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde dhcp_config = nm_device_get_dhcp_config(device, AF_INET); if (dhcp_config) { - root_path = nm_dhcp_config_get_option(dhcp_config, "root_path"); - next_server = nm_dhcp_config_get_option(dhcp_config, "next_server"); + root_path = nm_dhcp_config_get_option(dhcp_config, "root_path"); + next_server = nm_dhcp_config_get_option(dhcp_config, "next_server"); + dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "filename"); + if (!dhcp_bootfile) + dhcp_bootfile = nm_dhcp_config_get_option(dhcp_config, "bootfile_name"); } if (!nm_config_device_state_write(ifindex, @@ -6860,7 +6864,8 @@ nm_manager_write_device_state(NMManager *self, NMDevice *device, int *out_ifinde route_metric_default_aspired, route_metric_default_effective, next_server, - root_path)) + root_path, + dhcp_bootfile)) return FALSE; NM_SET_OUT(out_ifindex, ifindex); diff --git a/src/n-dhcp4/src/libndhcp4.sym b/src/n-dhcp4/src/libndhcp4.sym index 6c06e2fd28..4d11734e4d 100644 --- a/src/n-dhcp4/src/libndhcp4.sym +++ b/src/n-dhcp4/src/libndhcp4.sym @@ -37,6 +37,7 @@ global: n_dhcp4_client_lease_get_siaddr; n_dhcp4_client_lease_get_lifetime; n_dhcp4_client_lease_get_server_identifier; + n_dhcp4_client_lease_get_file; n_dhcp4_client_lease_query; n_dhcp4_client_lease_select; n_dhcp4_client_lease_accept; diff --git a/src/n-dhcp4/src/n-dhcp4-c-lease.c b/src/n-dhcp4/src/n-dhcp4-c-lease.c index 515814d4e6..bae2f674cd 100644 --- a/src/n-dhcp4/src/n-dhcp4-c-lease.c +++ b/src/n-dhcp4/src/n-dhcp4-c-lease.c @@ -249,9 +249,11 @@ _c_public_ void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint * Gets the address contained in the server-identifier DHCP option, in network * byte order. * - * Return: 0 on success, negative error code on failure. + * Return: 0 on success, + * N_DHCP4_E_UNSET if the lease doesn't contain a server-identifier, or + * N_DCHP4_E_INTERNAL if the server-identifier is not valid. */ -_c_public_ int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr) { +_c_public_ int n_dhcp4_client_lease_get_server_identifier(NDhcp4ClientLease *lease, struct in_addr *addr) { uint8_t *data; size_t n_data; int r; @@ -260,13 +262,49 @@ _c_public_ int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *le if (r) return r; if (n_data < sizeof(struct in_addr)) - return N_DHCP4_E_MALFORMED; + return N_DHCP4_E_INTERNAL; memcpy(addr, data, sizeof(struct in_addr)); return 0; } +/** + * n_dhcp4_client_lease_get_file() - query the lease for the boot file name + * @lease: the lease to operate on + * @file: return argument for the file name + * + * Query the lease for the boot file name from the DHCP header. The file name + * is returned as a NULL-terminated string. + * + * Return: 0 on success, + * N_DHCP4_E_UNSET if the lease does not contain a file name, or + * N_DCHP4_E_INTERNAL if the file name is invalid. + */ +_c_public_ int n_dhcp4_client_lease_get_file(NDhcp4ClientLease *lease, const char **file) { + NDhcp4Message *message; + + if (lease->message->options[N_DHCP4_OPTION_OVERLOAD].size > 0 + && ((*lease->message->options[N_DHCP4_OPTION_OVERLOAD].value) & N_DHCP4_OVERLOAD_FILE)) { + /* The field is overloaded to contain other options */ + return N_DHCP4_E_UNSET; + } + + message = &lease->message->message; + + if (message->file[0] == '\0') + return N_DHCP4_E_UNSET; + + if (!memchr(message->file, '\0', sizeof(message->file))) { + /* The field is NULL-terminated (RFC 2131 section 2) */ + return N_DHCP4_E_INTERNAL; + } + + *file = (const char *) message->file; + + return 0; +} + /** * n_dhcp4_client_lease_query() - query the lease for an option * @lease: the lease to operate on diff --git a/src/n-dhcp4/src/n-dhcp4.h b/src/n-dhcp4/src/n-dhcp4.h index 435c5600d8..8493a487dd 100644 --- a/src/n-dhcp4/src/n-dhcp4.h +++ b/src/n-dhcp4/src/n-dhcp4.h @@ -171,7 +171,8 @@ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *s void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep); void n_dhcp4_client_lease_get_lifetime(NDhcp4ClientLease *lease, uint64_t *ns_lifetimep); int n_dhcp4_client_lease_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap); -int n_dhcp4_client_lease_get_server_identifier (NDhcp4ClientLease *lease, struct in_addr *addr); +int n_dhcp4_client_lease_get_server_identifier(NDhcp4ClientLease *lease, struct in_addr *addr); +int n_dhcp4_client_lease_get_file(NDhcp4ClientLease *lease, const char **file); int n_dhcp4_client_lease_select(NDhcp4ClientLease *lease); int n_dhcp4_client_lease_accept(NDhcp4ClientLease *lease);