dhcp: merge branch 'th/dhcp-nettools-lease-parse'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/750
This commit is contained in:
Thomas Haller 2021-02-11 14:43:45 +01:00
commit a21fdd4237
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
15 changed files with 1047 additions and 819 deletions

View file

@ -3623,7 +3623,8 @@ nm_utils_file_search_in_paths(const char * progname,
if (!path[0])
continue;
nm_str_buf_reset(&strbuf, path);
nm_str_buf_reset(&strbuf);
nm_str_buf_append(&strbuf, path);
nm_str_buf_ensure_trailing_c(&strbuf, '/');
s = nm_str_buf_append0(&strbuf, progname);

View file

@ -786,27 +786,27 @@ nm_utils_ip6_address_same_prefix_cmp(const struct in6_addr *addr_a,
return 0;
}
/**
* _nm_utils_ip4_get_default_prefix:
* @ip: an IPv4 address (in network byte order)
*
* When the Internet was originally set up, various ranges of IP addresses were
* segmented into three network classes: A, B, and C. This function will return
* a prefix that is associated with the IP address specified defining where it
* falls in the predefined classes.
*
* Returns: the default class prefix for the given IP
**/
/* The function is originally from ipcalc.c of Red Hat's initscripts. */
guint32
_nm_utils_ip4_get_default_prefix(guint32 ip)
{
if (((ntohl(ip) & 0xFF000000) >> 24) <= 127)
return 8; /* Class A - 255.0.0.0 */
else if (((ntohl(ip) & 0xFF000000) >> 24) <= 191)
return 16; /* Class B - 255.255.0.0 */
/*****************************************************************************/
return 24; /* Class C - 255.255.255.0 */
guint32
_nm_utils_ip4_get_default_prefix0(in_addr_t ip)
{
/* The function is originally from ipcalc.c of Red Hat's initscripts. */
switch (ntohl(ip) >> 24) {
case 0 ... 127:
return 8; /* Class A */
case 128 ... 191:
return 16; /* Class B */
case 192 ... 223:
return 24; /* Class C */
}
return 0;
}
guint32
_nm_utils_ip4_get_default_prefix(in_addr_t ip)
{
return _nm_utils_ip4_get_default_prefix0(ip) ?: 24;
}
gboolean

View file

@ -699,7 +699,8 @@ nm_utils_escaped_tokens_options_escape_val(const char *val, char **out_to_free)
/*****************************************************************************/
guint32 _nm_utils_ip4_prefix_to_netmask(guint32 prefix);
guint32 _nm_utils_ip4_get_default_prefix(guint32 ip);
guint32 _nm_utils_ip4_get_default_prefix0(in_addr_t ip);
guint32 _nm_utils_ip4_get_default_prefix(in_addr_t ip);
gconstpointer
nm_utils_ipx_address_clear_host_address(int family, gpointer dst, gconstpointer src, guint8 plen);

View file

@ -292,8 +292,10 @@ nm_str_buf_append_c_len(NMStrBuf *strbuf, char ch, gsize len)
}
}
static inline void
nm_str_buf_reset(NMStrBuf *strbuf, const char *str)
/*****************************************************************************/
static inline NMStrBuf *
nm_str_buf_reset(NMStrBuf *strbuf)
{
_nm_str_buf_assert(strbuf);
@ -305,8 +307,7 @@ nm_str_buf_reset(NMStrBuf *strbuf, const char *str)
strbuf->_priv_len = 0;
}
if (str)
nm_str_buf_append(strbuf, str);
return strbuf;
}
/*****************************************************************************/

View file

@ -93,6 +93,24 @@ nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot)
: (ValidHostnameFlags) 0);
}
char *
nm_sd_dns_name_normalize(const char *s)
{
nm_auto_free char *n = NULL;
int r;
r = dns_name_normalize(s, 0, &n);
if (r < 0)
return NULL;
nm_assert(n);
/* usually we try not to mix malloc/g_malloc and free/g_free. In practice,
* they are the same. So here we return a buffer allocated with malloc(),
* and the caller should free it with g_free(). */
return g_steal_pointer(&n);
}
/*****************************************************************************/
static gboolean

View file

@ -28,6 +28,8 @@ nm_sd_dns_name_to_wire_format(const char *domain, guint8 *buffer, size_t len, gb
int nm_sd_dns_name_is_valid(const char *s);
gboolean nm_sd_hostname_is_valid(const char *s, bool allow_trailing_dot);
char *nm_sd_dns_name_normalize(const char *s);
/*****************************************************************************/
gboolean nm_sd_http_url_is_valid_https(const char *url);

View file

@ -20,6 +20,7 @@
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
#include "nm-dhcp-utils.h"
#include "nm-dhcp-options.h"
#include "platform/nm-platform.h"
#include "nm-dhcp-client-logging.h"
@ -437,8 +438,7 @@ nm_dhcp_client_set_state(NMDhcpClient *self,
NMIPConfig * ip_config,
GHashTable * options)
{
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
gs_free char * event_id = NULL;
NMDhcpClientPrivate *priv = NM_DHCP_CLIENT_GET_PRIVATE(self);
if (NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED)) {
g_return_if_fail(NM_IS_IP_CONFIG_ADDR_FAMILY(ip_config, priv->addr_family));
@ -462,23 +462,36 @@ nm_dhcp_client_set_state(NMDhcpClient *self,
&& !NM_IN_SET(new_state, NM_DHCP_STATE_BOUND, NM_DHCP_STATE_EXTENDED))
return;
if (_LOGI_ENABLED()) {
if (_LOGD_ENABLED()) {
gs_free const char **keys = NULL;
guint i, nkeys;
keys = nm_utils_strdict_get_keys(options, TRUE, &nkeys);
for (i = 0; i < nkeys; i++) {
_LOGI("option %-20s => '%s'", keys[i], (char *) g_hash_table_lookup(options, keys[i]));
_LOGD("option %-20s => '%s'", keys[i], (char *) g_hash_table_lookup(options, keys[i]));
}
}
if (priv->addr_family == AF_INET6)
event_id = nm_dhcp_utils_get_dhcp6_event_id(options);
if (_LOGT_ENABLED() && priv->addr_family == AF_INET6) {
gs_free char *event_id = NULL;
_LOGI("state changed %s -> %s%s%s%s",
state_to_string(priv->state),
state_to_string(new_state),
NM_PRINT_FMT_QUOTED(event_id, ", event ID=\"", event_id, "\"", ""));
event_id = nm_dhcp_utils_get_dhcp6_event_id(options);
if (event_id)
_LOGT("event-id: \"%s\"", event_id);
}
if (_LOGI_ENABLED()) {
const char *req_str =
NM_IS_IPv4(priv->addr_family)
? nm_dhcp_option_request_string(AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS)
: nm_dhcp_option_request_string(AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS);
const char *addr = nm_g_hash_table_lookup(options, req_str);
_LOGI("state changed %s -> %s%s%s%s",
state_to_string(priv->state),
state_to_string(new_state),
NM_PRINT_FMT_QUOTED(addr, ", address=", addr, "", ""));
}
priv->state = new_state;
g_signal_emit(G_OBJECT(self), signals[SIGNAL_STATE_CHANGED], 0, new_state, ip_config, options);

File diff suppressed because it is too large Load diff

View file

@ -7,11 +7,13 @@
#include "nm-dhcp-options.h"
#define REQPREFIX "requested_"
#include "nm-glib-aux/nm-str-buf.h"
#define REQ(_num, _name, _include) \
{ \
.name = REQPREFIX ""_name, .option_num = _num, .include = _include, \
/*****************************************************************************/
#define REQ(_num, _name, _include) \
{ \
.name = NM_DHCP_OPTION_REQPREFIX ""_name, .option_num = _num, .include = _include, \
}
const NMDhcpOption _nm_dhcp_option_dhcp4_options[] = {
@ -167,8 +169,24 @@ 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),
};
{0}};
static const NMDhcpOption *const _sorted_options_4[G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options)] = {
#define A(idx) (&_nm_dhcp_option_dhcp4_options[(idx)])
A(0), A(1), A(8), A(18), A(19), A(2), A(20), A(21), A(22), A(23), A(24), A(3),
A(25), A(26), A(4), A(27), A(17), A(28), A(29), A(30), A(31), A(32), A(33), A(34),
A(35), A(5), A(36), A(6), A(37), A(38), A(39), A(40), A(9), A(41), A(42), A(43),
A(44), A(45), A(46), A(10), A(11), A(12), A(47), A(48), A(49), A(50), A(51), A(52),
A(13), A(53), A(54), A(55), A(57), A(58), A(59), A(60), A(61), A(62), A(63), A(64),
A(65), A(66), A(67), A(68), A(69), A(70), A(71), A(72), A(73), A(74), A(75), A(76),
A(77), A(78), A(79), A(80), A(81), A(82), A(83), A(84), A(85), A(86), A(87), A(56),
A(88), A(89), A(90), A(91), A(92), A(93), A(14), A(7), A(94), A(95), A(96), A(97),
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),
#undef A
};
const NMDhcpOption _nm_dhcp_option_dhcp6_options[] = {
REQ(NM_DHCP_OPTION_DHCP6_CLIENTID, "dhcp6_client_id", FALSE),
@ -194,73 +212,235 @@ const NMDhcpOption _nm_dhcp_option_dhcp6_options[] = {
REQ(NM_DHCP_OPTION_DHCP6_NM_RENEW, "renew", FALSE),
REQ(NM_DHCP_OPTION_DHCP6_NM_REBIND, "rebind", FALSE),
REQ(NM_DHCP_OPTION_DHCP6_NM_IAID, "iaid", FALSE),
};
{0}};
#undef REQ
const char *
nm_dhcp_option_request_string(const NMDhcpOption *requests, guint option)
static const NMDhcpOption *const _sorted_options_6[G_N_ELEMENTS(_nm_dhcp_option_dhcp6_options)] = {
#define A(idx) (&_nm_dhcp_option_dhcp6_options[(idx)])
A(0),
A(1),
A(2),
A(3),
A(4),
A(5),
A(6),
A(7),
A(8),
A(9),
A(10),
A(11),
A(12),
A(13),
A(14),
A(15),
#undef A
};
/*****************************************************************************/
static int
_sorted_options_generate_sort(gconstpointer pa, gconstpointer pb, gpointer user_data)
{
guint i = 0;
const NMDhcpOption *const *a = pa;
const NMDhcpOption *const *b = pb;
while (requests[i].name) {
if (requests[i].option_num == option)
return requests[i].name + NM_STRLEN(REQPREFIX);
i++;
NM_CMP_DIRECT((*a)->option_num, (*b)->option_num);
return nm_assert_unreachable_val(0);
}
static char *
_sorted_options_generate(const NMDhcpOption *base, const NMDhcpOption *const *sorted, guint n)
{
gs_free const NMDhcpOption **sort2 = NULL;
NMStrBuf sbuf = NM_STR_BUF_INIT(0, FALSE);
guint i;
sort2 = nm_memdup(sorted, n * sizeof(sorted[0]));
g_qsort_with_data(sort2, n, sizeof(sort2[0]), _sorted_options_generate_sort, NULL);
for (i = 0; i < n; i++) {
if (i > 0)
nm_str_buf_append(&sbuf, ", ");
nm_str_buf_append_printf(&sbuf, "A(%d)", (int) (sort2[i] - base));
}
return nm_str_buf_finalize(&sbuf, NULL);
}
_nm_unused static void
_ASSERT_sorted(int IS_IPv4, const NMDhcpOption *const *const sorted, int n)
{
const NMDhcpOption *const options =
IS_IPv4 ? _nm_dhcp_option_dhcp4_options : _nm_dhcp_option_dhcp6_options;
int i;
int j;
gs_free char *sorted_msg = NULL;
for (i = 0; i < n; i++) {
const NMDhcpOption *opt = sorted[i];
g_assert(opt);
g_assert(opt >= options);
g_assert(opt < &options[n]);
for (j = 0; j < i; j++) {
const NMDhcpOption *opt2 = sorted[j];
if (opt == opt2) {
g_error("%s:%d: the _sorted_options_%c at [%d] (opt=%u, %s) is duplicated at "
"[%d] (SORT: %s)",
__FILE__,
__LINE__,
IS_IPv4 ? '4' : '6',
i,
opt->option_num,
opt->name,
j,
(sorted_msg = _sorted_options_generate(options, sorted, n)));
}
}
if (i > 0) {
const NMDhcpOption *opt2 = sorted[i - 1];
if (opt2->option_num >= opt->option_num) {
g_error("%s:%d: the _sorted_options_%c at [%d] (opt=%u, %s) should come before "
"[%d] (opt=%u, %s) (SORT: %s)",
__FILE__,
__LINE__,
IS_IPv4 ? '4' : '6',
i,
opt->option_num,
opt->name,
i - 1,
opt2->option_num,
opt2->name,
(sorted_msg = _sorted_options_generate(options, sorted, n)));
}
}
}
}
/*****************************************************************************/
const NMDhcpOption *
nm_dhcp_option_find(int addr_family, guint option)
{
const int IS_IPv4 = NM_IS_IPv4(addr_family);
const NMDhcpOption *const *const sorted = IS_IPv4 ? _sorted_options_4 : _sorted_options_6;
const int n = IS_IPv4 ? G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options)
: G_N_ELEMENTS(_nm_dhcp_option_dhcp6_options);
int imin = 0;
int imax = n - 1;
int imid = (n - 1) / 2;
#if NM_MORE_ASSERTS > 10
nm_assert(n < G_MAXINT / 2);
if (IS_IPv4 && !NM_MORE_ASSERT_ONCE(10)) {
/* already checked */
} else if (!IS_IPv4 && !NM_MORE_ASSERT_ONCE(10)) {
/* already checked */
} else
_ASSERT_sorted(IS_IPv4, sorted, n);
#endif
for (;;) {
const guint o = sorted[imid]->option_num;
if (G_UNLIKELY(o == option))
return sorted[imid];
if (o < option)
imin = imid + 1;
else
imax = imid - 1;
if (G_UNLIKELY(imin > imax))
break;
imid = (imin + imax) / 2;
}
/* Option should always be found */
nm_assert_not_reached();
return NULL;
return nm_assert_unreachable_val(NULL);
}
/*****************************************************************************/
void
nm_dhcp_option_take_option(GHashTable * options,
const NMDhcpOption *requests,
guint option,
char * value)
nm_dhcp_option_take_option(GHashTable *options, int addr_family, guint option, char *value)
{
nm_assert(options);
nm_assert(requests);
nm_assert_addr_family(addr_family);
nm_assert(value);
nm_assert(g_utf8_validate(value, -1, NULL));
g_hash_table_insert(options, (gpointer) nm_dhcp_option_request_string(requests, option), value);
}
void
nm_dhcp_option_add_option(GHashTable * options,
const NMDhcpOption *requests,
guint option,
const char * value)
{
if (options)
nm_dhcp_option_take_option(options, requests, option, g_strdup(value));
}
void
nm_dhcp_option_add_option_u64(GHashTable * options,
const NMDhcpOption *requests,
guint option,
guint64 value)
{
if (options)
nm_dhcp_option_take_option(options,
requests,
option,
g_strdup_printf("%" G_GUINT64_FORMAT, value));
}
void
nm_dhcp_option_add_requests_to_options(GHashTable *options, const NMDhcpOption *requests)
{
guint i;
if (!options)
if (!options) {
nm_assert_not_reached();
g_free(value);
return;
}
for (i = 0; requests[i].name; i++) {
if (requests[i].include)
g_hash_table_insert(options, (gpointer) requests[i].name, g_strdup("1"));
g_hash_table_insert(options,
(gpointer) nm_dhcp_option_request_string(addr_family, option),
value);
}
void
nm_dhcp_option_add_option(GHashTable *options, int addr_family, guint option, const char *value)
{
nm_dhcp_option_take_option(options, addr_family, option, g_strdup(value));
}
void
nm_dhcp_option_add_option_utf8safe_escape(GHashTable * options,
int addr_family,
guint option,
const guint8 *data,
gsize n_data)
{
gs_free char *to_free = NULL;
const char * escaped;
escaped = nm_utils_buf_utf8safe_escape((char *) data, n_data, 0, &to_free);
nm_dhcp_option_add_option(options, addr_family, option, escaped ?: "");
}
void
nm_dhcp_option_add_option_u64(GHashTable *options, int addr_family, guint option, guint64 value)
{
nm_dhcp_option_take_option(options,
addr_family,
option,
g_strdup_printf("%" G_GUINT64_FORMAT, value));
}
void
nm_dhcp_option_add_option_in_addr(GHashTable *options,
int addr_family,
guint option,
in_addr_t value)
{
char sbuf[NM_UTILS_INET_ADDRSTRLEN];
nm_dhcp_option_add_option(options, addr_family, option, _nm_utils_inet4_ntop(value, sbuf));
}
void
nm_dhcp_option_add_requests_to_options(GHashTable *options, int addr_family)
{
const int IS_IPv4 = NM_IS_IPv4(addr_family);
const NMDhcpOption *const all_options =
IS_IPv4 ? _nm_dhcp_option_dhcp4_options : _nm_dhcp_option_dhcp6_options;
int n_options = IS_IPv4 ? G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options)
: G_N_ELEMENTS(_nm_dhcp_option_dhcp6_options);
int i;
for (i = 0; i < n_options; i++) {
if (all_options[i].include)
g_hash_table_insert(options, (gpointer) all_options[i].name, g_strdup("1"));
}
}

View file

@ -177,29 +177,50 @@ typedef enum {
} NMDhcpOptionDhcp6Options;
#define NM_DHCP_OPTION_REQPREFIX "requested_"
typedef struct {
const char *name;
uint16_t option_num;
bool include;
} NMDhcpOption;
extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[];
extern const NMDhcpOption _nm_dhcp_option_dhcp6_options[];
extern const NMDhcpOption _nm_dhcp_option_dhcp4_options[142];
extern const NMDhcpOption _nm_dhcp_option_dhcp6_options[16];
const char *nm_dhcp_option_request_string(const NMDhcpOption *requests, guint option);
void nm_dhcp_option_take_option(GHashTable * options,
const NMDhcpOption *requests,
guint option,
char * value);
void nm_dhcp_option_add_option(GHashTable * options,
const NMDhcpOption *requests,
guint option,
const char * value);
void nm_dhcp_option_add_option_u64(GHashTable * options,
const NMDhcpOption *requests,
guint option,
guint64 value);
void nm_dhcp_option_add_requests_to_options(GHashTable *options, const NMDhcpOption *requests);
static inline const char *
nm_dhcp_option_get_name(const NMDhcpOption *option)
{
nm_assert(option);
nm_assert(option->name);
nm_assert(NM_STR_HAS_PREFIX(option->name, NM_DHCP_OPTION_REQPREFIX));
return &option->name[NM_STRLEN(NM_DHCP_OPTION_REQPREFIX)];
}
const NMDhcpOption *nm_dhcp_option_find(int addr_family, guint option);
static inline const char *
nm_dhcp_option_request_string(int addr_family, guint option)
{
return nm_dhcp_option_get_name(nm_dhcp_option_find(addr_family, option));
}
void nm_dhcp_option_take_option(GHashTable *options, int addr_family, guint option, char *value);
void
nm_dhcp_option_add_option(GHashTable *options, int addr_family, guint option, const char *value);
void nm_dhcp_option_add_option_utf8safe_escape(GHashTable * options,
int addr_family,
guint option,
const guint8 *data,
gsize n_data);
void nm_dhcp_option_add_option_in_addr(GHashTable *options,
int addr_family,
guint option,
in_addr_t value);
void
nm_dhcp_option_add_option_u64(GHashTable *options, int addr_family, guint option, guint64 value);
void nm_dhcp_option_add_requests_to_options(GHashTable *options, int addr_family);
GHashTable *nm_dhcp_option_create_options_dict(void);
#endif /* __NM_DHCP_OPTIONS_H__ */

View file

@ -138,32 +138,26 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
options = out_options ? nm_dhcp_option_create_options_dict() : NULL;
_nm_utils_inet4_ntop(a_address.s_addr, addr_str);
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS,
addr_str);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_IP_ADDRESS, addr_str);
a_plen = nm_utils_ip4_netmask_to_prefix(a_netmask.s_addr);
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_SUBNET_MASK,
_nm_utils_inet4_ntop(a_netmask.s_addr, addr_str));
nm_dhcp_option_add_option_u64(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_IP_ADDRESS_LEASE_TIME,
a_lifetime);
nm_dhcp_option_add_option_u64(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_NM_EXPIRY,
(guint64)(ts_time + a_lifetime));
if (sd_dhcp_lease_get_next_server(lease, &a_next_server) == 0) {
_nm_utils_inet4_ntop(a_next_server.s_addr, addr_str);
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER,
addr_str);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NM_NEXT_SERVER, addr_str);
}
nm_ip4_config_add_address(ip4_config,
@ -179,18 +173,12 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
if (sd_dhcp_lease_get_server_identifier(lease, &server_id) >= 0) {
_nm_utils_inet4_ntop(server_id.s_addr, addr_str);
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_SERVER_ID,
addr_str);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_SERVER_ID, addr_str);
}
if (sd_dhcp_lease_get_broadcast(lease, &broadcast) >= 0) {
_nm_utils_inet4_ntop(broadcast.s_addr, addr_str);
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_BROADCAST,
addr_str);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_BROADCAST, addr_str);
}
num = sd_dhcp_lease_get_dns(lease, &addr_list);
@ -208,7 +196,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
nm_ip4_config_add_nameserver(ip4_config, addr_list[i].s_addr);
}
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_DOMAIN_NAME_SERVER,
str->str);
}
@ -221,7 +209,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
nm_ip4_config_add_search(ip4_config, search_domains[i]);
}
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_DOMAIN_SEARCH_LIST,
str->str);
}
@ -230,10 +218,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
gs_strfreev char **domains = NULL;
char ** d;
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_DOMAIN_NAME,
s);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_DOMAIN_NAME, s);
/* Multiple domains sometimes stuffed into option 15 "Domain Name".
* As systemd escapes such characters, split them at \\032. */
@ -243,10 +228,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
}
if (sd_dhcp_lease_get_hostname(lease, &s) >= 0) {
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_HOST_NAME,
s);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_HOST_NAME, s);
}
num = sd_dhcp_lease_get_routes(lease, &routes);
@ -348,12 +330,12 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
if (str_classless && str_classless->len > 0)
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_CLASSLESS_STATIC_ROUTE,
str_classless->str);
if (str_static && str_static->len > 0)
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_STATIC_ROUTE,
str_static->str);
}
@ -400,17 +382,11 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
}),
NULL);
}
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_ROUTER,
str->str);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_ROUTER, str->str);
}
if (sd_dhcp_lease_get_mtu(lease, &mtu) >= 0 && mtu) {
nm_dhcp_option_add_option_u64(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_INTERFACE_MTU,
mtu);
nm_dhcp_option_add_option_u64(options, AF_INET, NM_DHCP_OPTION_DHCP4_INTERFACE_MTU, mtu);
nm_ip4_config_set_mtu(ip4_config, mtu, NM_IP_CONFIG_SOURCE_DHCP);
}
@ -421,38 +397,29 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
_nm_utils_inet4_ntop(addr_list[i].s_addr, addr_str);
g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
}
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_NTP_SERVER,
str->str);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NTP_SERVER, str->str);
}
if (sd_dhcp_lease_get_root_path(lease, &s) >= 0) {
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_ROOT_PATH,
s);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_ROOT_PATH, s);
}
if (sd_dhcp_lease_get_t1(lease, &renewal) >= 0) {
nm_dhcp_option_add_option_u64(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_RENEWAL_T1_TIME,
renewal);
}
if (sd_dhcp_lease_get_t2(lease, &rebinding) >= 0) {
nm_dhcp_option_add_option_u64(options,
_nm_dhcp_option_dhcp4_options,
AF_INET,
NM_DHCP_OPTION_DHCP4_REBINDING_T2_TIME,
rebinding);
}
if (sd_dhcp_lease_get_timezone(lease, &s) >= 0) {
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp4_options,
NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE,
s);
nm_dhcp_option_add_option(options, AF_INET, NM_DHCP_OPTION_DHCP4_NEW_TZDB_TIMEZONE, s);
}
if (sd_dhcp_lease_get_vendor_specific(lease, &data, &data_len) >= 0)
@ -473,10 +440,7 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx,
g_free(option_string);
continue;
}
nm_dhcp_option_take_option(options,
_nm_dhcp_option_dhcp4_options,
private_options[i].code,
option_string);
nm_dhcp_option_take_option(options, AF_INET, private_options[i].code, option_string);
}
}
NM_SET_OUT(out_options, g_steal_pointer(&options));
@ -518,7 +482,7 @@ bound4_handle(NMDhcpSystemd *self, gboolean extended)
return;
}
nm_dhcp_option_add_requests_to_options(options, _nm_dhcp_option_dhcp4_options);
nm_dhcp_option_add_requests_to_options(options, AF_INET);
dhcp_lease_save(lease, priv->lease_file);
nm_dhcp_client_set_state(NM_DHCP_CLIENT(self),
@ -702,7 +666,7 @@ ip4_start(NMDhcpClient *client,
}
/* Add requested options */
for (i = 0; _nm_dhcp_option_dhcp4_options[i].name; i++) {
for (i = 0; i < (int) G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options); i++) {
if (_nm_dhcp_option_dhcp4_options[i].include) {
nm_assert(_nm_dhcp_option_dhcp4_options[i].option_num <= 255);
r = sd_dhcp_client_set_request_option(sd_client,
@ -821,10 +785,7 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx,
g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
};
if (str->len)
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp6_options,
NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS,
str->str);
nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_NM_IP_ADDRESS, str->str);
if (!info_only && nm_ip6_config_get_num_addresses(ip6_config) == 0) {
g_set_error_literal(error,
@ -842,10 +803,7 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx,
g_string_append(nm_gstring_add_space_delimiter(str), addr_str);
nm_ip6_config_add_nameserver(ip6_config, &dns[i]);
}
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp6_options,
NM_DHCP_OPTION_DHCP6_DNS_SERVERS,
str->str);
nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_DNS_SERVERS, str->str);
}
num = sd_dhcp6_lease_get_domains(lease, &domains);
@ -855,17 +813,11 @@ lease_to_ip6_config(NMDedupMultiIndex *multi_idx,
g_string_append(nm_gstring_add_space_delimiter(str), domains[i]);
nm_ip6_config_add_search(ip6_config, domains[i]);
}
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp6_options,
NM_DHCP_OPTION_DHCP6_DOMAIN_LIST,
str->str);
nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_DOMAIN_LIST, str->str);
}
if (sd_dhcp6_lease_get_fqdn(lease, &s) >= 0) {
nm_dhcp_option_add_option(options,
_nm_dhcp_option_dhcp6_options,
NM_DHCP_OPTION_DHCP6_FQDN,
s);
nm_dhcp_option_add_option(options, AF_INET6, NM_DHCP_OPTION_DHCP6_FQDN, s);
}
NM_SET_OUT(out_options, g_steal_pointer(&options));
@ -1020,7 +972,7 @@ ip6_start(NMDhcpClient * client,
}
/* Add requested options */
for (i = 0; _nm_dhcp_option_dhcp6_options[i].name; i++) {
for (i = 0; i < (int) G_N_ELEMENTS(_nm_dhcp_option_dhcp6_options); i++) {
if (_nm_dhcp_option_dhcp6_options[i].include) {
r = sd_dhcp6_client_set_request_option(sd_client,
_nm_dhcp_option_dhcp6_options[i].option_num);

View file

@ -8,7 +8,10 @@
#include <unistd.h>
#include <arpa/inet.h>
#include "nm-std-aux/unaligned.h"
#include "nm-glib-aux/nm-dedup-multi.h"
#include "nm-glib-aux/nm-str-buf.h"
#include "systemd/nm-sd-utils-shared.h"
#include "nm-dhcp-utils.h"
#include "nm-utils.h"
@ -834,3 +837,285 @@ nm_dhcp_utils_get_dhcp6_event_id(GHashTable *lease)
return g_strdup_printf("%s|%s", iaid, start);
}
/*****************************************************************************/
gboolean
nm_dhcp_lease_data_parse_u16(const guint8 *data, gsize n_data, uint16_t *out_val)
{
if (n_data != 2)
return FALSE;
*out_val = unaligned_read_be16(data);
return TRUE;
}
gboolean
nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, uint16_t *out_val)
{
uint16_t mtu;
if (!nm_dhcp_lease_data_parse_u16(data, n_data, &mtu))
return FALSE;
if (mtu < 68) {
/* https://tools.ietf.org/html/rfc2132#section-5.1:
*
* The minimum legal value for the MTU is 68. */
return FALSE;
}
*out_val = mtu;
return TRUE;
}
gboolean
nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_len)
{
/* WARNING: this function only validates that the string does not contain
* NUL characters (and ignores trailing NULs). It does not check character
* encoding! */
while (n_data > 0 && data[n_data - 1] == '\0')
n_data--;
if (n_data > 0) {
if (memchr(data, n_data, '\0')) {
/* we accept trailing NUL, but none in between.
*
* https://tools.ietf.org/html/rfc2132#section-2
* https://github.com/systemd/systemd/issues/1337 */
return FALSE;
}
}
NM_SET_OUT(out_new_len, n_data);
return TRUE;
}
char *
nm_dhcp_lease_data_parse_domain_validate(const char *str)
{
gs_free char *s = NULL;
s = nm_sd_dns_name_normalize(str);
if (!s)
return NULL;
if (nm_str_is_empty(s) || (s[0] == '.' && s[1] == '\0')) {
/* root domains are not allowed. */
return NULL;
}
if (nm_utils_is_localhost(s))
return NULL;
if (!g_utf8_validate(s, -1, NULL)) {
/* the result must be valid UTF-8. */
return NULL;
}
return g_steal_pointer(&s);
}
gboolean
nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val)
{
gs_free char *str1_free = NULL;
const char * str1;
gs_free char *s = NULL;
/* this is mostly the same as systemd's lease_parse_domain(). */
if (!nm_dhcp_lease_data_parse_cstr(data, n_data, &n_data))
return FALSE;
if (n_data == 0) {
/* empty domains are rejected. See
* https://tools.ietf.org/html/rfc2132#section-3.14
* https://tools.ietf.org/html/rfc2132#section-3.17
*
* Its minimum length is 1.
*
* Note that this is *after* we potentially stripped trailing NULs.
*/
return FALSE;
}
str1 = nm_strndup_a(300, (char *) data, n_data, &str1_free);
s = nm_dhcp_lease_data_parse_domain_validate(str1);
if (!s)
return FALSE;
*out_val = g_steal_pointer(&s);
return TRUE;
}
gboolean
nm_dhcp_lease_data_parse_in_addr(const guint8 *data, gsize n_data, in_addr_t *out_val)
{
/* - option 1, https://tools.ietf.org/html/rfc2132#section-3.3
* - option 28, https://tools.ietf.org/html/rfc2132#section-5.3
*/
if (n_data != 4)
return FALSE;
*out_val = unaligned_read_ne32(data);
return TRUE;
}
/*****************************************************************************/
static gboolean
lease_option_print_label(NMStrBuf *sbuf, size_t n_label, const uint8_t **datap, size_t *n_datap)
{
gsize i;
for (i = 0; i < n_label; ++i) {
uint8_t c = 0;
if (!nm_dhcp_lease_data_consume(datap, n_datap, &c, sizeof(c)))
return FALSE;
switch (c) {
case 'a' ... 'z':
case 'A' ... 'Z':
case '0' ... '9':
case '-':
case '_':
nm_str_buf_append_c(sbuf, c);
break;
case '.':
case '\\':
nm_str_buf_append_c2(sbuf, '\\', c);
break;
default:
nm_str_buf_append_printf(sbuf, "\\%3d", c);
}
}
return TRUE;
}
static char *
lease_option_print_domain_name(const uint8_t * cache,
size_t * n_cachep,
const uint8_t **datap,
size_t * n_datap)
{
nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, FALSE);
const uint8_t * domain;
size_t n_domain;
size_t n_cache = *n_cachep;
const uint8_t ** domainp = datap;
size_t * n_domainp = n_datap;
gboolean first = TRUE;
uint8_t c;
/*
* We are given two adjacent memory regions. The @cache contains alreday parsed
* domain names, and the @datap contains the remaining data to parse.
*
* A domain name is formed from a sequence of labels. Each label start with
* a length byte, where the two most significant bits are unset. A zero-length
* label indicates the end of the domain name.
*
* Alternatively, a label can be followed by an offset (indicated by the two
* most significant bits being set in the next byte that is read). The offset
* is an offset into the cache, where the next label of the domain name can
* be found.
*
* Note, that each time a jump to an offset is performed, the size of the
* cache shrinks, so this is guaranteed to terminate.
*/
if (cache + n_cache != *datap)
return NULL;
for (;;) {
if (!nm_dhcp_lease_data_consume(domainp, n_domainp, &c, sizeof(c)))
return NULL;
switch (c & 0xC0) {
case 0x00: /* label length */
{
size_t n_label = c;
if (n_label == 0) {
/*
* We reached the final label of the domain name. Adjust
* the cache to include the consumed data, and return.
*/
*n_cachep = *datap - cache;
return nm_str_buf_finalize(&sbuf, NULL);
}
if (!first)
nm_str_buf_append_c(&sbuf, '.');
else
first = FALSE;
if (!lease_option_print_label(&sbuf, n_label, domainp, n_domainp))
return NULL;
break;
}
case 0xC0: /* back pointer */
{
size_t offset = (c & 0x3F) << 16;
/*
* The offset is given as two bytes (in big endian), where the
* two high bits are masked out.
*/
if (!nm_dhcp_lease_data_consume(domainp, n_domainp, &c, sizeof(c)))
return NULL;
offset += c;
if (offset >= n_cache)
return NULL;
domain = cache + offset;
n_domain = n_cache - offset;
n_cache = offset;
domainp = &domain;
n_domainp = &n_domain;
break;
}
default:
return NULL;
}
}
}
char **
nm_dhcp_lease_data_parse_search_list(const guint8 *data, gsize n_data)
{
GPtrArray * array = NULL;
const guint8 *cache = data;
gsize n_cache = 0;
for (;;) {
gs_free char *s = NULL;
s = lease_option_print_domain_name(cache, &n_cache, &data, &n_data);
if (!s)
break;
if (!array)
array = g_ptr_array_new();
g_ptr_array_add(array, g_steal_pointer(&s));
}
if (!array)
return NULL;
g_ptr_array_add(array, NULL);
return (char **) g_ptr_array_free(array, FALSE);
}

View file

@ -36,8 +36,35 @@ gboolean nm_dhcp_utils_get_leasefile_path(int addr_family,
const char *uuid,
char ** out_leasefile_path);
char **nm_dhcp_parse_search_list(guint8 *data, size_t n_data);
char *nm_dhcp_utils_get_dhcp6_event_id(GHashTable *lease);
/*****************************************************************************/
static inline gboolean
nm_dhcp_lease_data_consume(const uint8_t **datap, size_t *n_datap, void *out, size_t n_out)
{
if (*n_datap < n_out)
return FALSE;
memcpy(out, *datap, n_out);
*datap += n_out;
*n_datap -= n_out;
return TRUE;
}
static inline gboolean
nm_dhcp_lease_data_consume_in_addr(const uint8_t **datap, size_t *n_datap, in_addr_t *addrp)
{
return nm_dhcp_lease_data_consume(datap, n_datap, addrp, sizeof(struct in_addr));
}
char *nm_dhcp_lease_data_parse_domain_validate(const char *str);
gboolean nm_dhcp_lease_data_parse_u16(const guint8 *data, gsize n_data, guint16 *out_val);
gboolean nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, guint16 *out_val);
gboolean nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_len);
gboolean nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val);
gboolean nm_dhcp_lease_data_parse_in_addr(const guint8 *data, gsize n_data, in_addr_t *out_val);
char ** nm_dhcp_lease_data_parse_search_list(const guint8 *data, gsize n_data);
#endif /* __NETWORKMANAGER_DHCP_UTILS_H__ */

View file

@ -13,10 +13,13 @@
#include "nm-utils.h"
#include "dhcp/nm-dhcp-utils.h"
#include "dhcp/nm-dhcp-options.h"
#include "platform/nm-platform.h"
#include "nm-test-utils-core.h"
/*****************************************************************************/
static NMIP4Config *
_ip4_config_from_options(int ifindex, const char *iface, GHashTable *options, guint32 route_metric)
{
@ -202,7 +205,7 @@ test_parse_search_list(void)
char ** domains;
data = (guint8[]){0x05, 'l', 'o', 'c', 'a', 'l', 0x00};
domains = nm_dhcp_parse_search_list(data, 7);
domains = nm_dhcp_lease_data_parse_search_list(data, 7);
g_assert(domains);
g_assert_cmpint(g_strv_length(domains), ==, 1);
g_assert_cmpstr(domains[0], ==, "local");
@ -211,7 +214,7 @@ test_parse_search_list(void)
data = (guint8[]){0x04, 't', 'e', 's', 't', 0x07, 'e', 'x', 'a', 'm', 'p', 'l',
'e', 0x03, 'c', 'o', 'm', 0x00, 0xc0, 0x05, 0x03, 'a', 'b', 'c',
0xc0, 0x0d, 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x00};
domains = nm_dhcp_parse_search_list(data, 34);
domains = nm_dhcp_lease_data_parse_search_list(data, 34);
g_assert(domains);
g_assert_cmpint(g_strv_length(domains), ==, 4);
g_assert_cmpstr(domains[0], ==, "test.example.com");
@ -226,7 +229,7 @@ test_parse_search_list(void)
'a',
'd',
};
domains = nm_dhcp_parse_search_list(data, 4);
domains = nm_dhcp_lease_data_parse_search_list(data, 4);
g_assert(!domains);
data = (guint8[]){
@ -241,7 +244,7 @@ test_parse_search_list(void)
'a',
'd',
};
domains = nm_dhcp_parse_search_list(data, 10);
domains = nm_dhcp_lease_data_parse_search_list(data, 10);
g_assert(domains);
g_assert_cmpint(g_strv_length(domains), ==, 1);
g_assert_cmpstr(domains[0], ==, "okay");
@ -740,6 +743,46 @@ test_client_id_from_string(void)
COMPARE_ID(endcolon, TRUE, endcolon, strlen(endcolon));
}
/*****************************************************************************/
static void
test_dhcp_opt_list(gconstpointer test_data)
{
const gboolean IS_IPv4 = (GPOINTER_TO_INT(test_data) == 0);
const int addr_family = IS_IPv4 ? AF_INET : AF_INET6;
const NMDhcpOption *const options =
IS_IPv4 ? _nm_dhcp_option_dhcp4_options : _nm_dhcp_option_dhcp6_options;
const guint n = (IS_IPv4 ? G_N_ELEMENTS(_nm_dhcp_option_dhcp4_options)
: G_N_ELEMENTS(_nm_dhcp_option_dhcp6_options));
guint i;
guint j;
g_assert(options);
g_assert(n > 0);
for (i = 0; i < n; i++) {
const NMDhcpOption *const opt = &options[i];
g_assert_cmpstr(opt->name, !=, NULL);
g_assert(NM_STR_HAS_PREFIX(opt->name, NM_DHCP_OPTION_REQPREFIX));
for (j = 0; j < i; j++) {
const NMDhcpOption *const opt2 = &options[j];
g_assert_cmpstr(opt->name, !=, opt2->name);
g_assert_cmpint(opt->option_num, !=, opt2->option_num);
}
}
for (i = 0; i < n; i++) {
const NMDhcpOption *const opt = &options[i];
g_assert(opt == nm_dhcp_option_find(addr_family, opt->option_num));
}
}
/*****************************************************************************/
NMTST_DEFINE();
int
@ -776,6 +819,8 @@ main(int argc, char **argv)
g_test_add_func("/dhcp/client-id-from-string", test_client_id_from_string);
g_test_add_func("/dhcp/vendor-option-metered", test_vendor_option_metered);
g_test_add_func("/dhcp/parse-search-list", test_parse_search_list);
g_test_add_data_func("/dhcp/test_dhcp_opt_list/IPv4", GINT_TO_POINTER(0), test_dhcp_opt_list);
g_test_add_data_func("/dhcp/test_dhcp_opt_list/IPv6", GINT_TO_POINTER(1), test_dhcp_opt_list);
return g_test_run();
}

View file

@ -474,7 +474,7 @@ dns_servers_done:
gsize padding;
gsize len;
nm_str_buf_reset(&sbuf, NULL);
nm_str_buf_reset(&sbuf);
for (i = 0; i < rdata->dns_domains->len; i++) {
const NMNDiscDNSDomain *dns_domain =