diff options
Diffstat (limited to 'shared/n-dhcp4')
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-connection.c | 67 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-c-probe.c | 6 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-client.c | 150 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4-private.h | 77 | ||||
| -rw-r--r-- | shared/n-dhcp4/src/n-dhcp4.h | 15 |
5 files changed, 244 insertions, 71 deletions
diff --git a/shared/n-dhcp4/src/n-dhcp4-c-connection.c b/shared/n-dhcp4/src/n-dhcp4-c-connection.c index 30514e28..8c32a984 100644 --- a/shared/n-dhcp4/src/n-dhcp4-c-connection.c +++ b/shared/n-dhcp4/src/n-dhcp4-c-connection.c @@ -23,6 +23,7 @@ * @connection: connection to operate on * @client_config: client configuration to use * @probe_config: client probe configuration to use + * @log_queue: the log queue for logging events * @fd_epoll: epoll context to attach to, or -1 * * This initializes a new client connection using the configuration given in @@ -47,11 +48,13 @@ int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, NDhcp4ClientConfig *client_config, NDhcp4ClientProbeConfig *probe_config, + NDhcp4LogQueue *log_queue, int fd_epoll) { *connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection); connection->client_config = client_config; connection->probe_config = probe_config; connection->fd_epoll = fd_epoll; + connection->log_queue = log_queue; /* * We explicitly allow initializing connections with an invalid @@ -1066,25 +1069,27 @@ static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection, } if (request->userdata.client_addr == INADDR_ANY) { - n_dhcp4_c_log(connection->client_config, LOG_INFO, - "send %s to %s%s", - message_type_to_str(request->userdata.message_type), - broadcast ? - "255.255.255.255" : - inet_ntop(AF_INET, &connection->server_ip, - server_addr, sizeof(server_addr)), - error_msg); + n_dhcp4_log(connection->log_queue, + LOG_INFO, + "send %s to %s%s", + message_type_to_str(request->userdata.message_type), + broadcast ? + "255.255.255.255" : + inet_ntop(AF_INET, &connection->server_ip, + server_addr, sizeof(server_addr)), + error_msg); } else { - n_dhcp4_c_log(connection->client_config, LOG_INFO, - "send %s of %s to %s%s", - message_type_to_str(request->userdata.message_type), - inet_ntop(AF_INET, &request->userdata.client_addr, - client_addr, sizeof(client_addr)), - broadcast ? - "255.255.255.255" : - inet_ntop(AF_INET, &connection->server_ip, - server_addr, sizeof(server_addr)), - error_msg); + n_dhcp4_log(connection->log_queue, + LOG_INFO, + "send %s of %s to %s%s", + message_type_to_str(request->userdata.message_type), + inet_ntop(AF_INET, &request->userdata.client_addr, + client_addr, sizeof(client_addr)), + broadcast ? + "255.255.255.255" : + inet_ntop(AF_INET, &connection->server_ip, + server_addr, sizeof(server_addr)), + error_msg); } ++request->userdata.n_send; @@ -1207,19 +1212,21 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, return N_DHCP4_E_AGAIN; if (type == N_DHCP4_MESSAGE_OFFER || type == N_DHCP4_MESSAGE_ACK) { - n_dhcp4_c_log(connection->client_config, LOG_INFO, - "received %s of %s from %s", - message_type_to_str(type), - inet_ntop(AF_INET, &message->message.header.yiaddr, - client_addr, sizeof(client_addr)), - inet_ntop(AF_INET, &message->message.header.siaddr, - serv_addr, sizeof(serv_addr))); + n_dhcp4_log(connection->log_queue, + LOG_INFO, + "received %s of %s from %s", + message_type_to_str(type), + inet_ntop(AF_INET, &message->message.header.yiaddr, + client_addr, sizeof(client_addr)), + inet_ntop(AF_INET, &message->message.header.siaddr, + serv_addr, sizeof(serv_addr))); } else { - n_dhcp4_c_log(connection->client_config, LOG_INFO, - "received %s from %s", - message_type_to_str(type), - inet_ntop(AF_INET, &message->message.header.siaddr, - serv_addr, sizeof(serv_addr))); + n_dhcp4_log(connection->log_queue, + LOG_INFO, + "received %s from %s", + message_type_to_str(type), + inet_ntop(AF_INET, &message->message.header.siaddr, + serv_addr, sizeof(serv_addr))); } switch (type) { diff --git a/shared/n-dhcp4/src/n-dhcp4-c-probe.c b/shared/n-dhcp4/src/n-dhcp4-c-probe.c index 5e971298..f3d4f265 100644 --- a/shared/n-dhcp4/src/n-dhcp4-c-probe.c +++ b/shared/n-dhcp4/src/n-dhcp4-c-probe.c @@ -429,9 +429,13 @@ int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep, */ n_dhcp4_client_probe_config_initialize_random_seed(probe->config); + /* The new probe keeps a reference on @client. So we are sure that &client->log_queue + * stays alive as long as we need it. */ + r = n_dhcp4_c_connection_init(&probe->connection, client->config, probe->config, + &client->log_queue, active ? client->fd_epoll : -1); if (r) return r; @@ -1091,7 +1095,7 @@ int n_dhcp4_client_probe_transition_accept(NDhcp4ClientProbe *probe, NDhcp4Incom probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND; - n_dhcp4_client_arm_timer (probe->client); + n_dhcp4_client_arm_timer(probe->client); break; diff --git a/shared/n-dhcp4/src/n-dhcp4-client.c b/shared/n-dhcp4/src/n-dhcp4-client.c index 6b015e81..403a6932 100644 --- a/shared/n-dhcp4/src/n-dhcp4-client.c +++ b/shared/n-dhcp4/src/n-dhcp4-client.c @@ -94,9 +94,6 @@ int n_dhcp4_client_config_dup(NDhcp4ClientConfig *config, NDhcp4ClientConfig **d dup->n_mac = config->n_mac; memcpy(dup->broadcast_mac, config->broadcast_mac, sizeof(dup->broadcast_mac)); dup->n_broadcast_mac = config->n_broadcast_mac; - dup->log.level = config->log.level; - dup->log.func = config->log.func; - dup->log.data = config->log.data; r = n_dhcp4_client_config_set_client_id(dup, config->client_id, @@ -227,37 +224,64 @@ _c_public_ void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *conf * n_dhcp4_client_config_set_client_id() - set client-id property * @config: client configuration to operate on * @id: client id - * @n_id: length of the client id in bytes + * @n_id: length of the client id in bytes. The length + * must be from 2 up to 255 bytes. Set it to 0 + * to unset the client-id. * * This sets the client-id property of @config. It copies the entire client-id * buffer into the configuration. + * See RFC 2132 (section 9.14) for the format of the Client Identifier. * * Return: 0 on success, negative error code on failure. */ _c_public_ int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id) { uint8_t *t; + if (n_id == 0) { + config->client_id = c_free(config->client_id); + config->n_client_id = 0; + return 0; + } + + if (n_id < 2 || n_id > 255) + return -EINVAL; + t = malloc(n_id + 1); if (!t) return -ENOMEM; + memcpy(t, id, n_id); + t[n_id] = 0; /* safety 0 for debugging */ + free(config->client_id); config->client_id = t; config->n_client_id = n_id; - - memcpy(config->client_id, id, n_id); - config->client_id[n_id] = 0; /* safety 0 for debugging */ - return 0; } -_c_public_ void n_dhcp4_client_config_set_log_level(NDhcp4ClientConfig *config, int level) { - config->log.level = level; -} - -_c_public_ void n_dhcp4_client_config_set_log_func(NDhcp4ClientConfig *config, NDhcp4LogFunc func, void *data) { - config->log.func = func; - config->log.data = data; +/** + * n_dhcp4_client_set_log_level() - set the logging level of the client + * @client: the client to operate on + * @level: the minimum syslog logging level that is + * still logged. For example, set to LOG_NOTICE + * to receive logging events with level LOG_NOTICE + * and higher. Set to -1 to disable generating + * logging events (which is also the default). + * + * By enabling logging, you can get N_DHCP4_CLIENT_EVENT_LOG events. + * + * From the logging event you may steal the message if (and only if) "allow_steal_message" + * is true. In that case, clear the message field and free the message yourself. + * + * If a logging event cannot be logged due to out of memory, one message + * gets logged that messages are missing. Until the event with that message + * gets dropped, no further logging events will be queued. + * + * You may change the logging level at any time, but it does not affect + * logging events that are already queued. + */ +_c_public_ void n_dhcp4_client_set_log_level(NDhcp4Client *client, int level) { + client->log_queue.log_level = level; } /** @@ -313,6 +337,16 @@ NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node) { case N_DHCP4_CLIENT_EVENT_EXTENDED: node->event.extended.lease = n_dhcp4_client_lease_unref(node->event.extended.lease); break; + case N_DHCP4_CLIENT_EVENT_LOG: + if (_c_unlikely_(!node->event.log.allow_steal_message)) { + /* @node is the static node "nomem_node". It must not be + * freed. */ + c_list_unlink(&node->client_link); + node->is_public = false; + return NULL; + } + node->event.log.message = c_free((char *)node->event.log.message); + break; default: break; } @@ -395,8 +429,11 @@ _c_public_ int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *co ev.data.u32 = N_DHCP4_CLIENT_EPOLL_TIMER; r = epoll_ctl(client->fd_epoll, EPOLL_CTL_ADD, client->fd_timer, &ev); - if (r < 0) + if (r < 0) { + close(client->fd_timer); + client->fd_timer = -1; return -errno; + } *clientp = client; client = NULL; @@ -487,6 +524,78 @@ int n_dhcp4_client_raise(NDhcp4Client *client, NDhcp4CEventNode **nodep, unsigne } /** + * n_dhcp4_log_queue_fmt() - add a logging event. + * @client: the NDhcp4LogQueue to operate on + * @level: the syslog logging level + * @fmt: the format string for the message + * @... printf arguments for logging + * + * Appends a logging event to the event queue if logging is + * enabled and the logging level sufficiently high. + * + * Queuing a logging event might fail with out of memory. + * In that case, a static event will be queued that informs + * about lost messages. + */ +void n_dhcp4_log_queue_fmt(NDhcp4LogQueue *log_queue, + int level, + const char *fmt, + ...) { + NDhcp4CEventNode *node; + char *message; + va_list ap; + int r; + + if (level > log_queue->log_level) + return; + + /* Currently the logging queue is only implemented for + * the client. Nobody would enable logging except a + * client instance. */ + c_assert(log_queue->is_client); + + if (!c_list_is_empty (&log_queue->nomem_node.client_link)) { + /* we have the nomem_node queued after a recent out + * of memory. This disables all logging messages until + * the event gets popped. + * + * The reason is that we can only queue the nomem_node once, + * so if we now try to append another event and succeed, the + * user wouldn't know which messages got dropped. Instead, + * just drop them all!! */ + return; + } + + r = n_dhcp4_c_event_node_new(&node); + if (r < 0) + goto handle_nomem; + + va_start(ap, fmt); + r = vasprintf(&message, fmt, ap); + va_end(ap); + + if (r < 0) { + n_dhcp4_c_event_node_free(node); + goto handle_nomem; + } + + node->event = (NDhcp4ClientEvent) { + .event = N_DHCP4_CLIENT_EVENT_LOG, + .log = { + .level = level, + .message = message, + .allow_steal_message = true, + }, + }; + + c_list_link_tail(log_queue->event_list, &node->client_link); + return; + +handle_nomem: + c_list_link_tail(log_queue->event_list, &log_queue->nomem_node.client_link); +} + +/** * n_dhcp4_client_arm_timer() - update timer * @client: client to operate on * @@ -682,9 +791,10 @@ _c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) { /* continue normally */ } else if (r) { if (r >= _N_DHCP4_E_INTERNAL) { - n_dhcp4_c_log(client->config, LOG_ERR, - "invalid internal error code %d after dispatch", - r); + n_dhcp4_log(&client->log_queue, + LOG_ERR, + "invalid internal error code %d after dispatch", + r); return N_DHCP4_E_INTERNAL; } return r; @@ -753,6 +863,8 @@ _c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) { * the client attempted several incompatible * probes in parallel, then the most recent * ones will be cancelled asynchronously. + * * N_DHCP4_CLIENT_EVENT_LOG: A logging event if n_dhcp4_client_set_log_level() + * is enabled. * * Return: 0 on success, negative error code on failure. */ diff --git a/shared/n-dhcp4/src/n-dhcp4-private.h b/shared/n-dhcp4/src/n-dhcp4-private.h index e285d95d..90f8f0c3 100644 --- a/shared/n-dhcp4/src/n-dhcp4-private.h +++ b/shared/n-dhcp4/src/n-dhcp4-private.h @@ -24,6 +24,7 @@ typedef struct NDhcp4Outgoing NDhcp4Outgoing; typedef struct NDhcp4SConnection NDhcp4SConnection; typedef struct NDhcp4SConnectionIp NDhcp4SConnectionIp; typedef struct NDhcp4SEventNode NDhcp4SEventNode; +typedef struct NDhcp4LogQueue NDhcp4LogQueue; /* specs */ @@ -242,11 +243,6 @@ struct NDhcp4ClientConfig { size_t n_broadcast_mac; uint8_t *client_id; size_t n_client_id; - struct { - int level; - NDhcp4LogFunc func; - void *data; - } log; }; #define N_DHCP4_CLIENT_CONFIG_NULL(_x) { \ @@ -290,9 +286,42 @@ struct NDhcp4CEventNode { .probe_link = C_LIST_INIT((_x).probe_link), \ } +struct NDhcp4LogQueue { + CList *event_list; + NDhcp4CEventNode nomem_node; + int log_level; + bool is_client : 1; +}; + +#define N_DHCP4_LOG_QUEUE_NULL_DEFUNCT() { \ + .log_level = -1, \ + .is_client = false, \ + } + +#define N_DHCP4_LOG_QUEUE_NULL_CLIENT(client) { \ + .event_list = &((client).event_list), \ + .log_level = -1, \ + .is_client = true, \ + .nomem_node = { \ + .client_link = C_LIST_INIT((client).log_queue.nomem_node.client_link), \ + .probe_link = C_LIST_INIT((client).log_queue.nomem_node.probe_link), \ + .event = { \ + .event = N_DHCP4_CLIENT_EVENT_LOG, \ + .log = { \ + .level = LOG_CRIT, \ + .message = "one or more logging messages dropped due to out of memory", \ + .allow_steal_message = false, \ + }, \ + }, \ + .is_public = false, \ + }, \ + } + struct NDhcp4CConnection { NDhcp4ClientConfig *client_config; NDhcp4ClientProbeConfig *probe_config; + NDhcp4LogQueue *log_queue; + int fd_epoll; unsigned int state; /* current connection state */ @@ -324,6 +353,9 @@ struct NDhcp4Client { unsigned long n_refs; NDhcp4ClientConfig *config; CList event_list; + + NDhcp4LogQueue log_queue; + int fd_epoll; int fd_timer; @@ -339,6 +371,7 @@ struct NDhcp4Client { .event_list = C_LIST_INIT((_x).event_list), \ .fd_epoll = -1, \ .fd_timer = -1, \ + .log_queue = N_DHCP4_LOG_QUEUE_NULL_CLIENT(_x), \ } struct NDhcp4ClientProbe { @@ -570,6 +603,7 @@ NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node); int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, NDhcp4ClientConfig *client_config, NDhcp4ClientProbeConfig *probe_config, + NDhcp4LogQueue *log_queue, int fd_epoll); void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection); @@ -698,19 +732,28 @@ static inline uint64_t n_dhcp4_gettime(clockid_t clock) { return ts.tv_sec * 1000ULL * 1000ULL * 1000ULL + ts.tv_nsec; } -#define n_dhcp4_c_log(_config, _level, ...) \ +void n_dhcp4_log_queue_fmt(NDhcp4LogQueue *log_queue, + int level, + const char *fmt, + ...) _c_printf_(3, 4); + +/** + * n_dhcp4_log() - append a logging event + * @x_log_queue: the logging event queue + * @x_level: the syslog logging level for the message. + * @...: the format string and arguments. + * + * Warning: this macro only evaluates the format arguments if the logging + * level is enabled. + */ +#define n_dhcp4_log(x_log_queue, x_level, ...) \ do { \ - const NDhcp4ClientConfig *__config = _config; \ - int __level = _level; \ + NDhcp4LogQueue *const _log_queue = (x_log_queue); \ + const int _level = (x_level); \ \ - if (__level <= __config->log.level && __config->log.func) { \ - if (1) { \ - _config->log.func(__level, \ - __config->log.data, \ - __VA_ARGS__); \ - } else { \ - /* To have the compiler check arguments */ \ - printf(__VA_ARGS__); \ - } \ + if (_level <= _log_queue->log_level) { \ + n_dhcp4_log_queue_fmt(_log_queue, \ + _level, \ + __VA_ARGS__); \ } \ } while (0) diff --git a/shared/n-dhcp4/src/n-dhcp4.h b/shared/n-dhcp4/src/n-dhcp4.h index d3747662..81452848 100644 --- a/shared/n-dhcp4/src/n-dhcp4.h +++ b/shared/n-dhcp4/src/n-dhcp4.h @@ -29,8 +29,6 @@ typedef struct NDhcp4ServerEvent NDhcp4ServerEvent; typedef struct NDhcp4ServerIp NDhcp4ServerIp; typedef struct NDhcp4ServerLease NDhcp4ServerLease; -typedef void (*NDhcp4LogFunc)(int level, void *data, const char *fmt, ...); - #define N_DHCP4_CLIENT_START_DELAY_RFC2131 (UINT64_C(9000)) enum { @@ -63,6 +61,7 @@ enum { N_DHCP4_CLIENT_EVENT_EXTENDED, N_DHCP4_CLIENT_EVENT_EXPIRED, N_DHCP4_CLIENT_EVENT_CANCELLED, + N_DHCP4_CLIENT_EVENT_LOG, _N_DHCP4_CLIENT_EVENT_N, }; @@ -88,6 +87,14 @@ struct NDhcp4ClientEvent { struct { NDhcp4ClientProbe *probe; } retracted, expired, cancelled; + struct { + /* If allow_steal_message is true, then the user may steal the message when handling + * the event. In that case, set the message field to %NULL and free it yourself + * with free(). */ + const char *message; + int level; + bool allow_steal_message; + } log; }; }; @@ -113,8 +120,6 @@ void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *config, boo void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac); void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac); int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id); -void n_dhcp4_client_config_set_log_level(NDhcp4ClientConfig *config, int level); -void n_dhcp4_client_config_set_log_func(NDhcp4ClientConfig *config, NDhcp4LogFunc func, void *data); /* client-probe configs */ @@ -141,6 +146,8 @@ void n_dhcp4_client_get_fd(NDhcp4Client *client, int *fdp); int n_dhcp4_client_dispatch(NDhcp4Client *client); int n_dhcp4_client_pop_event(NDhcp4Client *client, NDhcp4ClientEvent **eventp); +void n_dhcp4_client_set_log_level(NDhcp4Client *client, int level); + int n_dhcp4_client_update_mtu(NDhcp4Client *client, uint16_t mtu); int n_dhcp4_client_probe(NDhcp4Client *client, |