Squashed 'shared/n-dhcp4/' changes from fb1d43449ba8..6876b73ec113

6876b73ec113 all: rework logging to append logging messages to the event queue
65ca0e4bea32 client: check length of client id for n_dhcp4_client_config_set_client_id()
6a9ca10b7ad6 client: close timer file descriptor explicitly when EPOLL_CTL_ADD fails
e5f3b27f5301 connection: don't fail dispatch in case of receive errors
68790d5ec950 connection: keep trying after a failure in send()
788b03891cd0 n-dhcp4: fix logging macro
6cd0fd5e8b3f probe: request previous address after expiration
ef11c7079ad7 connection: avoid double free of NDhcp4Outgoing
922a9a2326e7 n-dhcp4: fix initialization of the 'secs' DHCP header field
7fff54117436 outgoing: fix uninitialized variable in n_dhcp4_outgoing_append()
27547faaf258 probe: move back to INIT after lease expires
d07cb4e32884 incoming: accept options that are longer than requested
34d365e2267f client: handle invalid return codes gracefully during n_dhcp4_client_dispatch()
2e8c6017f85b n-dhcp4: use packet socket in rebinding state
9a7c82143c06 n-dhcp4: support init-reboot state
e615e3f5b063 n-dhcp4/socket: use SO_REUSEADDR on UDP socket
8dbfa1ad2549 merge branch 'bengal:log'
6e50189fba86 n-dhcp4: log outgoing packets
aea0315d3862 n-dhcp4: log incoming packets
1ca64a82393d n-dhcp4: add logging API
72c16a0028ac client-probe: fix state transitions on timer dispatch
c023ae2ad829 merge branch 'leasetime'
780f94c333ca lease: add n_dhcp4_client_lease_get_basetime()
a94378274cf4 incoming: don't handle 0xFFFFFFFF timestamps special in n_dhcp4_incoming_query_u32()
6bb277aefe0b probe: unconditionally pass ownership of message in n_dhcp4_client_probe_dispatch_io()
1cf87c90c6a1 probe: fix leaking message during client probe
55239cef2d63 lease: remove unreachable code in n_dhcp4_incoming_get_timeouts()
fa5ee7903061 lease: expose the server IP address
0cfce4a4fe07 all: merge branch 'th/build-centos76'
cae4868e5f7e all: avoid {net,linux}/if.h clashes on old distros
05859a3e4d32 all: avoid c_min() macro to work with old GCC
0be7033dd940 probe: arm timers in bound state
8735cdb0f95f ci: switch to c-util automation
a0bb7c69a11a client: fallback to CLOCK_MONOTONIC for timerfd
308cb242caf7 ci: disable valgrind run
468c93195ad3 build: update submodules
9d0f690f9ee8 ci: switch to github actions
9ba143a037bb probe: allocate memory of right size in n_dhcp4_client_probe_option_new()
2546aa2c809d util/link: suppress gcc warning
e4a01f5870a6 ci: drop broken armv7hl
2e2fbfa18f2c build: update submodules
6277dfd20eca Merge pull request #2 from NetworkManager/th/declaration-after-statement
1b660ae2d12a outgoing: avoid "-Werror=declaration-after-statement" warning with static_assert
f34a54cd9719 client/probe: fix memory leak
21e68f3bba80 client/connection: fix memory leak

git-subtree-dir: shared/n-dhcp4
git-subtree-split: 6876b73ec113328712a5cdc2ffb9497fd774c0e2
This commit is contained in:
Thomas Haller
2020-06-03 22:21:50 +02:00
parent e23b3c9c3a
commit cc3e23d2e5
20 changed files with 633 additions and 130 deletions

View File

@@ -1,12 +0,0 @@
#!/bin/bash
set -e
rm -Rf "./ci-build"
mkdir "./ci-build"
cd "./ci-build"
${CHERRY_LIB_MESONSETUP} . "${CHERRY_LIB_SRCDIR}"
${CHERRY_LIB_NINJABUILD}
${CHERRY_LIB_MESONTEST}
# no valgrind tests, since setns(2) is not supported by it

50
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Continuous Integration
on:
push:
pull_request:
schedule:
- cron: '0 0 * * *'
jobs:
ci:
name: CI with Default Configuration
runs-on: ubuntu-latest
steps:
#
# Prepare CI
#
# We cannot use the github-action of the `ci-c-util` project, because we
# need privileges in the container. Therefore, fetch the CI sources and
# build the container manually.
#
- name: Fetch CI
uses: actions/checkout@v2
with:
repository: c-util/automation
ref: v1
path: automation
- name: Build CI
working-directory: automation/src/ci-c-util
run: docker build --tag ci-c-util:v1 .
#
# Run CI
#
# Take the CI image we built and run the CI with the default project
# configuration. We do not use valgrind, since it falls-over with bpf(2)
# syscalls.
#
- name: Fetch Sources
uses: actions/checkout@v2
with:
path: source
- name: Run through C-Util CI
run: |
docker run \
--privileged \
-v "$(pwd)/source:/github/workspace" \
"ci-c-util:v1" \
"--m32=1" \
"--source=/github/workspace"

View File

@@ -1,21 +0,0 @@
os: linux
dist: trusty
language: c
services:
- docker
before_install:
- curl -O -L "https://raw.githubusercontent.com/cherry-pick/cherry-images/v1/scripts/vmrun"
- curl -O -L "https://raw.githubusercontent.com/cherry-pick/cherry-ci/v1/scripts/cherryci"
- chmod +x "./vmrun" "./cherryci"
jobs:
include:
- stage: test
script:
- ./vmrun -- ../src/cherryci -d ../src/.cherryci -s c-util -m
- script:
- ./vmrun -T armv7hl -- ../src/cherryci -d ../src/.cherryci -s c-util
- script:
- ./vmrun -T i686 -- ../src/cherryci -d ../src/.cherryci -s c-util

View File

@@ -34,6 +34,7 @@ global:
n_dhcp4_client_lease_ref; n_dhcp4_client_lease_ref;
n_dhcp4_client_lease_unref; n_dhcp4_client_lease_unref;
n_dhcp4_client_lease_get_yiaddr; n_dhcp4_client_lease_get_yiaddr;
n_dhcp4_client_lease_get_siaddr;
n_dhcp4_client_lease_get_lifetime; n_dhcp4_client_lease_get_lifetime;
n_dhcp4_client_lease_query; n_dhcp4_client_lease_query;
n_dhcp4_client_lease_select; n_dhcp4_client_lease_select;

View File

@@ -8,6 +8,8 @@
#include <c-stdaux.h> #include <c-stdaux.h>
#include <errno.h> #include <errno.h>
#include <limits.h> #include <limits.h>
#include <sys/socket.h> /* needed by linux/netdevice.h */
#include <linux/netdevice.h>
#include <net/if_arp.h> #include <net/if_arp.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
@@ -21,6 +23,7 @@
* @connection: connection to operate on * @connection: connection to operate on
* @client_config: client configuration to use * @client_config: client configuration to use
* @probe_config: client probe 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 * @fd_epoll: epoll context to attach to, or -1
* *
* This initializes a new client connection using the configuration given in * This initializes a new client connection using the configuration given in
@@ -45,11 +48,13 @@
int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, int n_dhcp4_c_connection_init(NDhcp4CConnection *connection,
NDhcp4ClientConfig *client_config, NDhcp4ClientConfig *client_config,
NDhcp4ClientProbeConfig *probe_config, NDhcp4ClientProbeConfig *probe_config,
NDhcp4LogQueue *log_queue,
int fd_epoll) { int fd_epoll) {
*connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection); *connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection);
connection->client_config = client_config; connection->client_config = client_config;
connection->probe_config = probe_config; connection->probe_config = probe_config;
connection->fd_epoll = fd_epoll; connection->fd_epoll = fd_epoll;
connection->log_queue = log_queue;
/* /*
* We explicitly allow initializing connections with an invalid * We explicitly allow initializing connections with an invalid
@@ -82,11 +87,12 @@ int n_dhcp4_c_connection_init(NDhcp4CConnection *connection,
*/ */
void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection) { void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection) {
n_dhcp4_c_connection_close(connection); n_dhcp4_c_connection_close(connection);
n_dhcp4_outgoing_free(connection->request);
*connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection); *connection = (NDhcp4CConnection)N_DHCP4_C_CONNECTION_NULL(*connection);
} }
static void n_dhcp4_c_connection_outgoing_set_secs(NDhcp4Outgoing *message) { static void n_dhcp4_c_connection_outgoing_set_secs(NDhcp4Outgoing *message) {
uint32_t secs; uint64_t secs;
/* /*
* This function sets the `secs` field for outgoing messages. It * This function sets the `secs` field for outgoing messages. It
@@ -122,12 +128,12 @@ static void n_dhcp4_c_connection_outgoing_set_secs(NDhcp4Outgoing *message) {
* *
* Note: Some DHCP relays reject a `secs` value of 0 (which might look * Note: Some DHCP relays reject a `secs` value of 0 (which might look
* like it is uninitialized). Hence, we always clamp the value to * like it is uninitialized). Hence, we always clamp the value to
* the range `[1, INF[`. * the range `[1, 65535]`.
*/ */
secs = message->userdata.base_time - message->userdata.start_time; secs = message->userdata.base_time - message->userdata.start_time;
secs /= 1000ULL * 1000ULL * 1000ULL; /* nsecs to secs */ secs /= 1000ULL * 1000ULL * 1000ULL; /* nsecs to secs */
secs = secs ?: 1; /* clamp to `[1, INF[` */ secs = C_CLAMP(secs, 1, UINT16_MAX);
n_dhcp4_outgoing_set_secs(message, secs); n_dhcp4_outgoing_set_secs(message, secs);
} }
@@ -136,7 +142,22 @@ int n_dhcp4_c_connection_listen(NDhcp4CConnection *connection) {
_c_cleanup_(c_closep) int fd_packet = -1; _c_cleanup_(c_closep) int fd_packet = -1;
int r; int r;
c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_INIT); if (connection->state == N_DHCP4_C_CONNECTION_STATE_PACKET)
return 0;
c_assert(connection->state == N_DHCP4_C_CONNECTION_STATE_INIT ||
connection->state == N_DHCP4_C_CONNECTION_STATE_DRAINING ||
connection->state == N_DHCP4_C_CONNECTION_STATE_UDP);
if (connection->fd_packet >= 0) {
epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_packet, NULL);
connection->fd_packet = c_close(connection->fd_packet);
}
if (connection->fd_udp >= 0) {
epoll_ctl(connection->fd_epoll, EPOLL_CTL_DEL, connection->fd_udp, NULL);
connection->fd_udp = c_close(connection->fd_udp);
}
r = n_dhcp4_c_socket_packet_new(&fd_packet, connection->client_config->ifindex); r = n_dhcp4_c_socket_packet_new(&fd_packet, connection->client_config->ifindex);
if (r) if (r)
@@ -316,7 +337,6 @@ void n_dhcp4_c_connection_get_timeout(NDhcp4CConnection *connection,
switch (connection->request->userdata.type) { switch (connection->request->userdata.type) {
case N_DHCP4_C_MESSAGE_DISCOVER: case N_DHCP4_C_MESSAGE_DISCOVER:
case N_DHCP4_C_MESSAGE_SELECT: case N_DHCP4_C_MESSAGE_SELECT:
case N_DHCP4_C_MESSAGE_REBOOT:
case N_DHCP4_C_MESSAGE_INFORM: case N_DHCP4_C_MESSAGE_INFORM:
/* /*
* Resend with an exponential backoff and a one second random * Resend with an exponential backoff and a one second random
@@ -335,6 +355,7 @@ void n_dhcp4_c_connection_get_timeout(NDhcp4CConnection *connection,
break; break;
case N_DHCP4_C_MESSAGE_REBIND: case N_DHCP4_C_MESSAGE_REBIND:
case N_DHCP4_C_MESSAGE_RENEW: case N_DHCP4_C_MESSAGE_RENEW:
case N_DHCP4_C_MESSAGE_REBOOT:
/* /*
* Resend every sixty seconds with a one second random slack. * Resend every sixty seconds with a one second random slack.
* *
@@ -510,6 +531,7 @@ static int n_dhcp4_c_connection_new_message(NDhcp4CConnection *connection,
n_dhcp4_c_connection_init_header(connection, header); n_dhcp4_c_connection_init_header(connection, header);
message->userdata.type = type; message->userdata.type = type;
message->userdata.message_type = message_type;
/* /*
* Note that some implementations expect the MESSAGE_TYPE option to be * Note that some implementations expect the MESSAGE_TYPE option to be
@@ -687,6 +709,7 @@ int n_dhcp4_c_connection_select_new(NDhcp4CConnection *connection,
*/ */
message->userdata.start_time = offer->userdata.start_time; message->userdata.start_time = offer->userdata.start_time;
message->userdata.base_time = offer->userdata.base_time; message->userdata.base_time = offer->userdata.base_time;
message->userdata.client_addr = client.s_addr;
n_dhcp4_incoming_get_xid(offer, &xid); n_dhcp4_incoming_get_xid(offer, &xid);
n_dhcp4_outgoing_set_xid(message, xid); n_dhcp4_outgoing_set_xid(message, xid);
@@ -760,6 +783,7 @@ int n_dhcp4_c_connection_renew_new(NDhcp4CConnection *connection,
if (r) if (r)
return r; return r;
message->userdata.client_addr = connection->client_ip;
*requestp = message; *requestp = message;
message = NULL; message = NULL;
return 0; return 0;
@@ -793,6 +817,7 @@ int n_dhcp4_c_connection_rebind_new(NDhcp4CConnection *connection,
if (r) if (r)
return r; return r;
message->userdata.client_addr = connection->client_ip;
*requestp = message; *requestp = message;
message = NULL; message = NULL;
return 0; return 0;
@@ -844,6 +869,7 @@ int n_dhcp4_c_connection_decline_new(NDhcp4CConnection *connection,
return r; return r;
} }
message->userdata.client_addr = client.s_addr;
*requestp = message; *requestp = message;
message = NULL; message = NULL;
return 0; return 0;
@@ -886,6 +912,7 @@ int n_dhcp4_c_connection_inform_new(NDhcp4CConnection *connection,
if (r) if (r)
return r; return r;
message->userdata.client_addr = connection->client_ip;
*requestp = message; *requestp = message;
message = NULL; message = NULL;
return 0; return 0;
@@ -952,10 +979,39 @@ int n_dhcp4_c_connection_release_new(NDhcp4CConnection *connection,
return 0; return 0;
} }
static const char *message_type_to_str(uint8_t type) {
switch (type) {
case N_DHCP4_MESSAGE_DISCOVER:
return "DISCOVER";
case N_DHCP4_MESSAGE_OFFER:
return "OFFER";
case N_DHCP4_MESSAGE_REQUEST:
return "REQUEST";
case N_DHCP4_MESSAGE_DECLINE:
return "DECLINE";
case N_DHCP4_MESSAGE_ACK:
return "ACK";
case N_DHCP4_MESSAGE_NAK:
return "NACK";
case N_DHCP4_MESSAGE_RELEASE:
return "RELEASE";
case N_DHCP4_MESSAGE_INFORM:
return "INFORM";
case N_DHCP4_MESSAGE_FORCERENEW:
return "FORCERENEW";
default:
return "UNKNOWN";
}
}
static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection, static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection,
NDhcp4Outgoing *request, NDhcp4Outgoing *request,
uint64_t timestamp) { uint64_t timestamp) {
char server_addr[INET_ADDRSTRLEN];
char client_addr[INET_ADDRSTRLEN];
char error_msg[128];
int r; int r;
bool broadcast = false;
/* /*
* Increment the base time and reset the xid field, * Increment the base time and reset the xid field,
@@ -990,28 +1046,52 @@ static int n_dhcp4_c_connection_send_request(NDhcp4CConnection *connection,
case N_DHCP4_C_MESSAGE_SELECT: case N_DHCP4_C_MESSAGE_SELECT:
case N_DHCP4_C_MESSAGE_REBOOT: case N_DHCP4_C_MESSAGE_REBOOT:
case N_DHCP4_C_MESSAGE_DECLINE: case N_DHCP4_C_MESSAGE_DECLINE:
case N_DHCP4_C_MESSAGE_REBIND:
broadcast = true;
r = n_dhcp4_c_connection_packet_broadcast(connection, request); r = n_dhcp4_c_connection_packet_broadcast(connection, request);
if (r)
return r;
break; break;
case N_DHCP4_C_MESSAGE_INFORM: case N_DHCP4_C_MESSAGE_INFORM:
case N_DHCP4_C_MESSAGE_REBIND: broadcast = true;
r = n_dhcp4_c_connection_udp_broadcast(connection, request); r = n_dhcp4_c_connection_udp_broadcast(connection, request);
if (r)
return r;
break; break;
case N_DHCP4_C_MESSAGE_RENEW: case N_DHCP4_C_MESSAGE_RENEW:
case N_DHCP4_C_MESSAGE_RELEASE: case N_DHCP4_C_MESSAGE_RELEASE:
r = n_dhcp4_c_connection_udp_send(connection, request); r = n_dhcp4_c_connection_udp_send(connection, request);
if (r)
return r;
break; break;
default: default:
c_assert(0); c_assert(0);
} }
if (r) {
snprintf(error_msg, sizeof(error_msg), ": error %d", r);
} else {
error_msg[0] = '\0';
}
if (request->userdata.client_addr == INADDR_ANY) {
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_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; ++request->userdata.n_send;
return 0; return 0;
} }
@@ -1030,13 +1110,14 @@ int n_dhcp4_c_connection_start_request(NDhcp4CConnection *connection,
if (request->userdata.start_time == 0) if (request->userdata.start_time == 0)
request->userdata.start_time = timestamp; request->userdata.start_time = timestamp;
n_dhcp4_outgoing_free(connection->request); connection->request = n_dhcp4_outgoing_free(connection->request);
connection->request = request;
r = n_dhcp4_c_connection_send_request(connection, request, timestamp); r = n_dhcp4_c_connection_send_request(connection, request, timestamp);
if (r) if (r)
return r; return r;
connection->request = request;
return 0; return 0;
} }
@@ -1060,9 +1141,18 @@ int n_dhcp4_c_connection_dispatch_timer(NDhcp4CConnection *connection,
return 0; return 0;
} }
/*
* Returns:
* 0 on success
* N_DHCP4_E_MALFORMED if a malformed packet was received
* N_DHCP4_E_UNEXPECTED if the packet received contains unexpected data
* N_DHCP4_E_AGAIN if there was another error (non fatal for the client)
*/
int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection, int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
NDhcp4Incoming **messagep) { NDhcp4Incoming **messagep) {
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL; _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = NULL;
char serv_addr[INET_ADDRSTRLEN];
char client_addr[INET_ADDRSTRLEN];
uint8_t type; uint8_t type;
int r; int r;
@@ -1072,10 +1162,11 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
connection->scratch_buffer, connection->scratch_buffer,
sizeof(connection->scratch_buffer), sizeof(connection->scratch_buffer),
&message); &message);
if (r) if (!r)
break;
else if (r == N_DHCP4_E_MALFORMED)
return r; return r;
return N_DHCP4_E_AGAIN;
break;
case N_DHCP4_C_CONNECTION_STATE_DRAINING: case N_DHCP4_C_CONNECTION_STATE_DRAINING:
r = n_dhcp4_c_socket_packet_recv(connection->fd_packet, r = n_dhcp4_c_socket_packet_recv(connection->fd_packet,
connection->scratch_buffer, connection->scratch_buffer,
@@ -1083,8 +1174,10 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
&message); &message);
if (!r) if (!r)
break; break;
else if (r != N_DHCP4_E_AGAIN) else if (r == N_DHCP4_E_MALFORMED)
return r; return r;
else if (r != N_DHCP4_E_AGAIN)
return N_DHCP4_E_AGAIN;
/* /*
* The UDP socket is open and the packet socket has been shut down * The UDP socket is open and the packet socket has been shut down
@@ -1102,18 +1195,39 @@ int n_dhcp4_c_connection_dispatch_io(NDhcp4CConnection *connection,
connection->scratch_buffer, connection->scratch_buffer,
sizeof(connection->scratch_buffer), sizeof(connection->scratch_buffer),
&message); &message);
if (r) if (!r)
break;
else if (r == N_DHCP4_E_MALFORMED)
return r; return r;
return N_DHCP4_E_AGAIN;
break;
default: default:
abort(); abort();
return -ENOTRECOVERABLE; return -ENOTRECOVERABLE;
} }
r = n_dhcp4_c_connection_verify_incoming(connection, message, &type); r = n_dhcp4_c_connection_verify_incoming(connection, message, &type);
if (r) if (r == N_DHCP4_E_MALFORMED || r == N_DHCP4_E_UNEXPECTED)
return r; return r;
else if (r != 0)
return N_DHCP4_E_AGAIN;
if (type == N_DHCP4_MESSAGE_OFFER || type == N_DHCP4_MESSAGE_ACK) {
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_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) { switch (type) {
case N_DHCP4_MESSAGE_OFFER: case N_DHCP4_MESSAGE_OFFER:

View File

@@ -44,10 +44,7 @@ static int n_dhcp4_incoming_get_timeouts(NDhcp4Incoming *message, uint64_t *t1p,
} else if (u32 == UINT32_MAX) { } else if (u32 == UINT32_MAX) {
lifetime = UINT64_MAX; lifetime = UINT64_MAX;
} else { } else {
if (u32 == UINT32_MAX) lifetime = u32 * (1000000000ULL);
lifetime = UINT64_MAX;
else
lifetime = u32 * (1000000000ULL);
} }
r = n_dhcp4_incoming_query_t2(message, &u32); r = n_dhcp4_incoming_query_t2(message, &u32);
@@ -206,6 +203,32 @@ _c_public_ void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct
yiaddr->s_addr = header->yiaddr; yiaddr->s_addr = header->yiaddr;
} }
/**
* n_dhcp4_client_lease_get_siaddr() - get the server IP address
* @lease: the lease to operate on
* @siaddr: return argument for the IP address
*
* Gets the server IP address cotained in the lease. Or INADDR_ANY if the
* lease does not contain an IP address.
*/
_c_public_ void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr) {
NDhcp4Header *header = n_dhcp4_incoming_get_header(lease->message);
siaddr->s_addr = header->siaddr;
}
/**
* n_dhcp4_client_lease_get_basetime() - get the timestamp when the lease was received.
* @lease: the lease to operate on
* @ns_basetimep: return argument for the base time in nano seconds
*
* Gets the timestamp when the lease was received in CLOCK_BOOTTIME. This
* is also the base timestamp for the expiration of the lifetime and t1/t2.
*/
_c_public_ void n_dhcp4_client_lease_get_basetime(NDhcp4ClientLease *lease, uint64_t *ns_basetimep) {
*ns_basetimep = lease->message->userdata.base_time;
}
/** /**
* n_dhcp4_client_lease_get_lifetime() - get the lifetime * n_dhcp4_client_lease_get_lifetime() - get the lifetime
* @lease: the lease to operate on * @lease: the lease to operate on

View File

@@ -26,7 +26,7 @@ static int n_dhcp4_client_probe_option_new(NDhcp4ClientProbeOption **optionp,
uint8_t n_data) { uint8_t n_data) {
NDhcp4ClientProbeOption *op; NDhcp4ClientProbeOption *op;
op = malloc(sizeof(op) + n_data); op = malloc(sizeof(*op) + n_data);
if (!op) if (!op)
return -ENOMEM; return -ENOMEM;
@@ -170,8 +170,6 @@ _c_public_ void n_dhcp4_client_probe_config_set_inform_only(NDhcp4ClientProbeCon
* INIT-REBOOT path, as described by the DHCP specification. In most cases, you * INIT-REBOOT path, as described by the DHCP specification. In most cases, you
* do not want this. * do not want this.
* *
* XXX: This is currently not implemented, and setting the property has no effect.
*
* Background: The INIT-REBOOT path allows a DHCP client to skip * Background: The INIT-REBOOT path allows a DHCP client to skip
* server-discovery when rebooting/resuming their machine. The DHCP * server-discovery when rebooting/resuming their machine. The DHCP
* client simply re-requests the lease it had acquired before. This * client simply re-requests the lease it had acquired before. This
@@ -431,18 +429,31 @@ int n_dhcp4_client_probe_new(NDhcp4ClientProbe **probep,
*/ */
n_dhcp4_client_probe_config_initialize_random_seed(probe->config); 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, r = n_dhcp4_c_connection_init(&probe->connection,
client->config, client->config,
probe->config, probe->config,
&client->log_queue,
active ? client->fd_epoll : -1); active ? client->fd_epoll : -1);
if (r) if (r)
return r; return r;
if (probe->config->requested_ip.s_addr != INADDR_ANY)
probe->last_address = probe->config->requested_ip;
if (probe->config->init_reboot && probe->last_address.s_addr != INADDR_ANY)
probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT;
else
probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
if (active) { if (active) {
/* /*
* Defer the sending of DISCOVER by a random amount (by default up to 9 seconds). * Defer the sending of DISCOVER by a random amount (by default up to 9 seconds).
*/ */
probe->ns_deferred = ns_now + (n_dhcp4_client_probe_config_get_random(probe->config) % (probe->config->ms_start_delay * 1000000ULL)); if (probe->state == N_DHCP4_CLIENT_PROBE_STATE_INIT)
probe->ns_deferred = ns_now + (n_dhcp4_client_probe_config_get_random(probe->config) % (probe->config->ms_start_delay * 1000000ULL));
probe->client->current_probe = probe; probe->client->current_probe = probe;
} else { } else {
r = n_dhcp4_client_probe_raise(probe, r = n_dhcp4_client_probe_raise(probe,
@@ -483,6 +494,7 @@ _c_public_ NDhcp4ClientProbe *n_dhcp4_client_probe_free(NDhcp4ClientProbe *probe
if (probe == probe->client->current_probe) if (probe == probe->client->current_probe)
probe->client->current_probe = NULL; probe->client->current_probe = NULL;
n_dhcp4_client_lease_unref(probe->current_lease);
n_dhcp4_c_connection_deinit(&probe->connection); n_dhcp4_c_connection_deinit(&probe->connection);
n_dhcp4_client_unref(probe->client); n_dhcp4_client_unref(probe->client);
n_dhcp4_client_probe_config_free(probe->config); n_dhcp4_client_probe_config_free(probe->config);
@@ -574,6 +586,14 @@ void n_dhcp4_client_probe_get_timeout(NDhcp4ClientProbe *probe, uint64_t *timeou
n_dhcp4_c_connection_get_timeout(&probe->connection, &timeout); n_dhcp4_c_connection_get_timeout(&probe->connection, &timeout);
switch (probe->state) { switch (probe->state) {
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
/* send DHCP request immediately */
timeout = 1;
break;
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
if (probe->ns_reinit && (!timeout || probe->ns_reinit < timeout))
timeout = probe->ns_reinit;
break;
case N_DHCP4_CLIENT_PROBE_STATE_INIT: case N_DHCP4_CLIENT_PROBE_STATE_INIT:
if (probe->ns_deferred && (!timeout || probe->ns_deferred < timeout)) if (probe->ns_deferred && (!timeout || probe->ns_deferred < timeout))
timeout = probe->ns_deferred; timeout = probe->ns_deferred;
@@ -625,6 +645,52 @@ static int n_dhcp4_client_probe_outgoing_append_options(NDhcp4ClientProbe *probe
return 0; return 0;
} }
static int n_dhcp4_client_probe_transition_reboot(NDhcp4ClientProbe *probe, uint64_t ns_now) {
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
int r;
switch (probe->state) {
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
r = n_dhcp4_c_connection_listen(&probe->connection);
if (r)
return r;
r = n_dhcp4_c_connection_reboot_new(&probe->connection, &request, &probe->last_address);
if (r)
return r;
r = n_dhcp4_client_probe_outgoing_append_options(probe, request);
if (r)
return r;
r = n_dhcp4_c_connection_start_request(&probe->connection, request, ns_now);
if (r)
return r;
else
request = NULL; /* consumed */
probe->state = N_DHCP4_CLIENT_PROBE_STATE_REBOOTING;
probe->ns_reinit = ns_now + 2000000000ULL;
break;
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
case N_DHCP4_CLIENT_PROBE_STATE_INIT:
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
default:
abort();
break;
}
return 0;
}
static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, uint64_t ns_now) { static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, uint64_t ns_now) {
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL; _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *request = NULL;
int r; int r;
@@ -634,13 +700,15 @@ static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, ui
r = n_dhcp4_c_connection_listen(&probe->connection); r = n_dhcp4_c_connection_listen(&probe->connection);
if (r) if (r)
return r; return r;
/* fall-through */
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
r = n_dhcp4_c_connection_discover_new(&probe->connection, &request); r = n_dhcp4_c_connection_discover_new(&probe->connection, &request);
if (r) if (r)
return r; return r;
if (probe->config->requested_ip.s_addr != INADDR_ANY) { if (probe->last_address.s_addr != INADDR_ANY) {
r = n_dhcp4_outgoing_append_requested_ip(request, probe->config->requested_ip); r = n_dhcp4_outgoing_append_requested_ip(request, probe->last_address);
if (r) if (r)
return r; return r;
} }
@@ -662,7 +730,6 @@ static int n_dhcp4_client_probe_transition_deferred(NDhcp4ClientProbe *probe, ui
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
case N_DHCP4_CLIENT_PROBE_STATE_BOUND: case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
@@ -725,6 +792,10 @@ static int n_dhcp4_client_probe_transition_t2(NDhcp4ClientProbe *probe, uint64_t
switch (probe->state) { switch (probe->state) {
case N_DHCP4_CLIENT_PROBE_STATE_BOUND: case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING: case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
r = n_dhcp4_c_connection_listen(&probe->connection);
if (r)
return r;
r = n_dhcp4_c_connection_rebind_new(&probe->connection, &request); r = n_dhcp4_c_connection_rebind_new(&probe->connection, &request);
if (r) if (r)
return r; return r;
@@ -777,11 +848,11 @@ static int n_dhcp4_client_probe_transition_lifetime(NDhcp4ClientProbe *probe) {
return r; return r;
c_assert(probe->client->current_probe == probe); c_assert(probe->client->current_probe == probe);
probe->client->current_probe = NULL;
n_dhcp4_c_connection_close(&probe->connection); probe->current_lease = n_dhcp4_client_lease_unref(probe->current_lease);
probe->state = N_DHCP4_CLIENT_PROBE_STATE_EXPIRED; probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
probe->ns_deferred = n_dhcp4_gettime(CLOCK_BOOTTIME) + UINT64_C(1);
break; break;
@@ -799,7 +870,8 @@ static int n_dhcp4_client_probe_transition_lifetime(NDhcp4ClientProbe *probe) {
return 0; return 0;
} }
static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message) { static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) {
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take;
_c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL;
NDhcp4CEventNode *node; NDhcp4CEventNode *node;
int r; int r;
@@ -817,7 +889,7 @@ static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp
if (r) if (r)
return r; return r;
/* message consumed, do not fail */ message = NULL; /* consumed */
n_dhcp4_client_lease_link(lease, probe); n_dhcp4_client_lease_link(lease, probe);
@@ -842,14 +914,26 @@ static int n_dhcp4_client_probe_transition_offer(NDhcp4ClientProbe *probe, NDhcp
return 0; return 0;
} }
static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message) { static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4Incoming *message_take) {
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *message = message_take;
_c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL; _c_cleanup_(n_dhcp4_client_lease_unrefp) NDhcp4ClientLease *lease = NULL;
NDhcp4CEventNode *node; NDhcp4CEventNode *node;
struct in_addr client = {};
struct in_addr server = {};
int r; int r;
switch (probe->state) { switch (probe->state) {
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
case N_DHCP4_CLIENT_PROBE_STATE_REBINDING: case N_DHCP4_CLIENT_PROBE_STATE_REBINDING:
n_dhcp4_incoming_get_yiaddr(message, &client);
r = n_dhcp4_incoming_query_server_identifier(message, &server);
if (r)
return r;
r = n_dhcp4_c_connection_connect(&probe->connection, &client, &server);
if (r)
return r;
/* fall-through */
case N_DHCP4_CLIENT_PROBE_STATE_RENEWING:
r = n_dhcp4_client_probe_raise(probe, r = n_dhcp4_client_probe_raise(probe,
&node, &node,
@@ -861,7 +945,7 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
if (r) if (r)
return r; return r;
/* message consumed, do not fail */ message = NULL; /* consumed */
n_dhcp4_client_lease_link(lease, probe); n_dhcp4_client_lease_link(lease, probe);
@@ -869,10 +953,12 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
n_dhcp4_client_lease_unref(probe->current_lease); n_dhcp4_client_lease_unref(probe->current_lease);
probe->current_lease = n_dhcp4_client_lease_ref(lease); probe->current_lease = n_dhcp4_client_lease_ref(lease);
probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND; probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND;
n_dhcp4_client_lease_get_yiaddr(lease, &probe->last_address);
probe->ns_nak_restart_delay = 0;
break; break;
case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING: case N_DHCP4_CLIENT_PROBE_STATE_REQUESTING:
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
r = n_dhcp4_client_probe_raise(probe, r = n_dhcp4_client_probe_raise(probe,
&node, &node,
@@ -884,20 +970,19 @@ static int n_dhcp4_client_probe_transition_ack(NDhcp4ClientProbe *probe, NDhcp4I
if (r) if (r)
return r; return r;
/* message consumed, don to fail */ message = NULL; /* consumed */
n_dhcp4_client_lease_link(lease, probe); n_dhcp4_client_lease_link(lease, probe);
node->event.granted.lease = n_dhcp4_client_lease_ref(lease); node->event.granted.lease = n_dhcp4_client_lease_ref(lease);
probe->current_lease = n_dhcp4_client_lease_ref(lease); probe->current_lease = n_dhcp4_client_lease_ref(lease);
probe->state = N_DHCP4_CLIENT_PROBE_STATE_GRANTED; probe->state = N_DHCP4_CLIENT_PROBE_STATE_GRANTED;
probe->ns_nak_restart_delay = 0;
break; break;
case N_DHCP4_CLIENT_PROBE_STATE_INIT: case N_DHCP4_CLIENT_PROBE_STATE_INIT:
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
case N_DHCP4_CLIENT_PROBE_STATE_BOUND: case N_DHCP4_CLIENT_PROBE_STATE_BOUND:
case N_DHCP4_CLIENT_PROBE_STATE_GRANTED: case N_DHCP4_CLIENT_PROBE_STATE_GRANTED:
case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED: case N_DHCP4_CLIENT_PROBE_STATE_EXPIRED:
@@ -927,9 +1012,11 @@ static int n_dhcp4_client_probe_transition_nak(NDhcp4ClientProbe *probe) {
return r; return r;
probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT; probe->state = N_DHCP4_CLIENT_PROBE_STATE_INIT;
probe->ns_deferred = n_dhcp4_gettime(CLOCK_BOOTTIME) + probe->ns_nak_restart_delay;
probe->ns_nak_restart_delay = C_CLAMP(probe->ns_nak_restart_delay * 2u,
UINT64_C(2) * UINT64_C(1000000000),
UINT64_C(300) * UINT64_C(1000000000));
break; break;
case N_DHCP4_CLIENT_PROBE_STATE_SELECTING: case N_DHCP4_CLIENT_PROBE_STATE_SELECTING:
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT: case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
case N_DHCP4_CLIENT_PROBE_STATE_INIT: case N_DHCP4_CLIENT_PROBE_STATE_INIT:
@@ -1008,7 +1095,7 @@ int n_dhcp4_client_probe_transition_accept(NDhcp4ClientProbe *probe, NDhcp4Incom
probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND; probe->state = N_DHCP4_CLIENT_PROBE_STATE_BOUND;
/* XXX: trigger timers */ n_dhcp4_client_arm_timer(probe->client);
break; break;
@@ -1076,6 +1163,19 @@ int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_no
int r; int r;
switch (probe->state) { switch (probe->state) {
case N_DHCP4_CLIENT_PROBE_STATE_INIT_REBOOT:
r = n_dhcp4_client_probe_transition_reboot(probe, ns_now);
if (r)
return r;
break;
case N_DHCP4_CLIENT_PROBE_STATE_REBOOTING:
if (ns_now >= probe->ns_reinit) {
r = n_dhcp4_client_probe_transition_deferred(probe, ns_now);
if (r)
return r;
}
break;
case N_DHCP4_CLIENT_PROBE_STATE_INIT: case N_DHCP4_CLIENT_PROBE_STATE_INIT:
if (ns_now >= probe->ns_deferred) { if (ns_now >= probe->ns_deferred) {
r = n_dhcp4_client_probe_transition_deferred(probe, ns_now); r = n_dhcp4_client_probe_transition_deferred(probe, ns_now);
@@ -1099,16 +1199,17 @@ int n_dhcp4_client_probe_dispatch_timer(NDhcp4ClientProbe *probe, uint64_t ns_no
r = n_dhcp4_client_probe_transition_lifetime(probe); r = n_dhcp4_client_probe_transition_lifetime(probe);
if (r) if (r)
return r; return r;
} else if (ns_now >= probe->current_lease->t2) { } else if (ns_now >= probe->current_lease->t2 &&
probe->state != N_DHCP4_CLIENT_PROBE_STATE_REBINDING) {
r = n_dhcp4_client_probe_transition_t2(probe, ns_now); r = n_dhcp4_client_probe_transition_t2(probe, ns_now);
if (r) if (r)
return r; return r;
} else if (ns_now >= probe->current_lease->t1) { } else if (ns_now >= probe->current_lease->t1 &&
probe->state == N_DHCP4_CLIENT_PROBE_STATE_BOUND) {
r = n_dhcp4_client_probe_transition_t1(probe, ns_now); r = n_dhcp4_client_probe_transition_t1(probe, ns_now);
if (r) if (r)
return r; return r;
} }
break; break;
default: default:
/* ignore */ /* ignore */
@@ -1145,6 +1246,7 @@ int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events)
return 0; return 0;
} }
abort();
return r; return r;
} }
@@ -1166,17 +1268,15 @@ int n_dhcp4_client_probe_dispatch_io(NDhcp4ClientProbe *probe, uint32_t events)
switch (type) { switch (type) {
case N_DHCP4_MESSAGE_OFFER: case N_DHCP4_MESSAGE_OFFER:
r = n_dhcp4_client_probe_transition_offer(probe, message); r = n_dhcp4_client_probe_transition_offer(probe, message);
message = NULL; /* consumed */
if (r) if (r)
return r; return r;
else
message = NULL; /* consumed */
break; break;
case N_DHCP4_MESSAGE_ACK: case N_DHCP4_MESSAGE_ACK:
r = n_dhcp4_client_probe_transition_ack(probe, message); r = n_dhcp4_client_probe_transition_ack(probe, message);
message = NULL; /* consumed */
if (r) if (r)
return r; return r;
else
message = NULL; /* consumed */
break; break;
case N_DHCP4_MESSAGE_NAK: case N_DHCP4_MESSAGE_NAK:
r = n_dhcp4_client_probe_transition_nak(probe); r = n_dhcp4_client_probe_transition_nak(probe);

View File

@@ -183,7 +183,11 @@ _c_public_ void n_dhcp4_client_config_set_request_broadcast(NDhcp4ClientConfig *
*/ */
_c_public_ void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac) { _c_public_ void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac) {
config->n_mac = n_mac; config->n_mac = n_mac;
memcpy(config->mac, mac, c_min(n_mac, sizeof(config->mac)));
if (n_mac > sizeof(config->mac))
n_mac = sizeof(config->mac);
memcpy(config->mac, mac, n_mac);
} }
/** /**
@@ -209,37 +213,77 @@ _c_public_ void n_dhcp4_client_config_set_mac(NDhcp4ClientConfig *config, const
*/ */
_c_public_ void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac) { _c_public_ void n_dhcp4_client_config_set_broadcast_mac(NDhcp4ClientConfig *config, const uint8_t *mac, size_t n_mac) {
config->n_broadcast_mac = n_mac; config->n_broadcast_mac = n_mac;
memcpy(config->broadcast_mac, mac, c_min(n_mac, sizeof(config->broadcast_mac)));
if (n_mac > sizeof(config->mac))
n_mac = sizeof(config->mac);
memcpy(config->broadcast_mac, mac, n_mac);
} }
/** /**
* n_dhcp4_client_config_set_client_id() - set client-id property * n_dhcp4_client_config_set_client_id() - set client-id property
* @config: client configuration to operate on * @config: client configuration to operate on
* @id: client id * @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 * This sets the client-id property of @config. It copies the entire client-id
* buffer into the configuration. * 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. * 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) { _c_public_ int n_dhcp4_client_config_set_client_id(NDhcp4ClientConfig *config, const uint8_t *id, size_t n_id) {
uint8_t *t; 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); t = malloc(n_id + 1);
if (!t) if (!t)
return -ENOMEM; return -ENOMEM;
memcpy(t, id, n_id);
t[n_id] = 0; /* safety 0 for debugging */
free(config->client_id); free(config->client_id);
config->client_id = t; config->client_id = t;
config->n_client_id = n_id; 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; return 0;
} }
/**
* 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;
}
/** /**
* n_dhcp4_c_event_node_new() - allocate new event * n_dhcp4_c_event_node_new() - allocate new event
* @nodep: output argument for new event * @nodep: output argument for new event
@@ -293,6 +337,16 @@ NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node) {
case N_DHCP4_CLIENT_EVENT_EXTENDED: case N_DHCP4_CLIENT_EVENT_EXTENDED:
node->event.extended.lease = n_dhcp4_client_lease_unref(node->event.extended.lease); node->event.extended.lease = n_dhcp4_client_lease_unref(node->event.extended.lease);
break; 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: default:
break; break;
} }
@@ -368,13 +422,18 @@ _c_public_ int n_dhcp4_client_new(NDhcp4Client **clientp, NDhcp4ClientConfig *co
return -errno; return -errno;
client->fd_timer = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK); client->fd_timer = timerfd_create(CLOCK_BOOTTIME, TFD_CLOEXEC | TFD_NONBLOCK);
if (client->fd_timer < 0 && errno == EINVAL)
client->fd_timer = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
if (client->fd_timer < 0) if (client->fd_timer < 0)
return -errno; return -errno;
ev.data.u32 = N_DHCP4_CLIENT_EPOLL_TIMER; ev.data.u32 = N_DHCP4_CLIENT_EPOLL_TIMER;
r = epoll_ctl(client->fd_epoll, EPOLL_CTL_ADD, client->fd_timer, &ev); 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; return -errno;
}
*clientp = client; *clientp = client;
client = NULL; client = NULL;
@@ -464,6 +523,78 @@ int n_dhcp4_client_raise(NDhcp4Client *client, NDhcp4CEventNode **nodep, unsigne
return 0; return 0;
} }
/**
* 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 * n_dhcp4_client_arm_timer() - update timer
* @client: client to operate on * @client: client to operate on
@@ -472,19 +603,39 @@ int n_dhcp4_client_raise(NDhcp4Client *client, NDhcp4CEventNode **nodep, unsigne
* must be called whenever a timeout on @client might have changed. * must be called whenever a timeout on @client might have changed.
*/ */
void n_dhcp4_client_arm_timer(NDhcp4Client *client) { void n_dhcp4_client_arm_timer(NDhcp4Client *client) {
uint64_t timeout = 0; uint64_t now, offset, timeout = 0;
int r; int r;
if (client->current_probe) if (client->current_probe)
n_dhcp4_client_probe_get_timeout(client->current_probe, &timeout); n_dhcp4_client_probe_get_timeout(client->current_probe, &timeout);
if (timeout != client->scheduled_timeout) { if (timeout != client->scheduled_timeout) {
/*
* Across our codebase, timeouts are specified as absolute
* timestamps on CLOCK_BOOTTIME. Unfortunately, there are
* systems with CLOCK_BOOTTIME support, but timerfd lacks it
* (in particular RHEL). Therefore, our timerfd might be on
* CLOCK_MONOTONIC.
* To account for this, we always schedule a relative timeout.
* We fetch the current time and then calculate the offset
* which we then schedule as relative timeout on the timerfd.
* This works regardless which clock the timerfd runs on.
* Once we no longer support CLOCK_MONOTONIC as fallback, we
* can simply switch to TFD_TIMER_ABSTIME here and specify
* `timeout` directly as value.
*/
now = n_dhcp4_gettime(CLOCK_BOOTTIME);
if (now >= timeout)
offset = 1; /* 0 would disarm the timerfd */
else
offset = timeout - now;
r = timerfd_settime(client->fd_timer, r = timerfd_settime(client->fd_timer,
TFD_TIMER_ABSTIME, 0,
&(struct itimerspec){ &(struct itimerspec){
.it_value = { .it_value = {
.tv_sec = timeout / UINT64_C(1000000000), .tv_sec = offset / UINT64_C(1000000000),
.tv_nsec = timeout % UINT64_C(1000000000), .tv_nsec = offset % UINT64_C(1000000000),
}, },
}, },
NULL); NULL);
@@ -639,7 +790,13 @@ _c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) {
/* continue normally */ /* continue normally */
} else if (r) { } else if (r) {
c_assert(r < _N_DHCP4_E_INTERNAL); if (r >= _N_DHCP4_E_INTERNAL) {
n_dhcp4_log(&client->log_queue,
LOG_ERR,
"invalid internal error code %d after dispatch",
r);
return N_DHCP4_E_INTERNAL;
}
return r; return r;
} }
} }
@@ -706,6 +863,8 @@ _c_public_ int n_dhcp4_client_dispatch(NDhcp4Client *client) {
* the client attempted several incompatible * the client attempted several incompatible
* probes in parallel, then the most recent * probes in parallel, then the most recent
* ones will be cancelled asynchronously. * 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. * Return: 0 on success, negative error code on failure.
*/ */

View File

@@ -326,7 +326,7 @@ static int n_dhcp4_incoming_query_u8(NDhcp4Incoming *message, uint8_t option, ui
r = n_dhcp4_incoming_query(message, option, &data, &n_data); r = n_dhcp4_incoming_query(message, option, &data, &n_data);
if (r) if (r)
return r; return r;
else if (n_data != sizeof(*data)) else if (n_data < sizeof(*data))
return N_DHCP4_E_MALFORMED; return N_DHCP4_E_MALFORMED;
*u8p = *data; *u8p = *data;
@@ -342,7 +342,7 @@ static int n_dhcp4_incoming_query_u16(NDhcp4Incoming *message, uint8_t option, u
r = n_dhcp4_incoming_query(message, option, &data, &n_data); r = n_dhcp4_incoming_query(message, option, &data, &n_data);
if (r) if (r)
return r; return r;
else if (n_data != sizeof(be16)) else if (n_data < sizeof(be16))
return N_DHCP4_E_MALFORMED; return N_DHCP4_E_MALFORMED;
memcpy(&be16, data, sizeof(be16)); memcpy(&be16, data, sizeof(be16));
@@ -360,15 +360,12 @@ static int n_dhcp4_incoming_query_u32(NDhcp4Incoming *message, uint8_t option, u
r = n_dhcp4_incoming_query(message, option, &data, &n_data); r = n_dhcp4_incoming_query(message, option, &data, &n_data);
if (r) if (r)
return r; return r;
else if (n_data != sizeof(be32)) else if (n_data < sizeof(be32))
return N_DHCP4_E_MALFORMED; return N_DHCP4_E_MALFORMED;
memcpy(&be32, data, sizeof(be32)); memcpy(&be32, data, sizeof(be32));
if (be32 == (uint32_t)-1) *u32p = ntohl(be32);
*u32p = 0;
else
*u32p = ntohl(be32);
return 0; return 0;
} }
@@ -381,7 +378,7 @@ static int n_dhcp4_incoming_query_in_addr(NDhcp4Incoming *message, uint8_t optio
r = n_dhcp4_incoming_query(message, option, &data, &n_data); r = n_dhcp4_incoming_query(message, option, &data, &n_data);
if (r) if (r)
return r; return r;
else if (n_data != sizeof(be32)) else if (n_data < sizeof(be32))
return N_DHCP4_E_MALFORMED; return N_DHCP4_E_MALFORMED;
memcpy(&be32, data, sizeof(be32)); memcpy(&be32, data, sizeof(be32));

View File

@@ -54,8 +54,6 @@
int n_dhcp4_outgoing_new(NDhcp4Outgoing **outgoingp, size_t max_size, uint8_t overload) { int n_dhcp4_outgoing_new(NDhcp4Outgoing **outgoingp, size_t max_size, uint8_t overload) {
_c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *outgoing = NULL; _c_cleanup_(n_dhcp4_outgoing_freep) NDhcp4Outgoing *outgoing = NULL;
c_assert(!(overload & ~(N_DHCP4_OVERLOAD_FILE | N_DHCP4_OVERLOAD_SNAME)));
/* /*
* Make sure the minimum limit is bigger than the maximum protocol * Make sure the minimum limit is bigger than the maximum protocol
* header plus the DHCP-message-header plus a single OPTION_END byte. * header plus the DHCP-message-header plus a single OPTION_END byte.
@@ -64,6 +62,8 @@ int n_dhcp4_outgoing_new(NDhcp4Outgoing **outgoingp, size_t max_size, uint8_t ov
sizeof(NDhcp4Message) + 1, sizeof(NDhcp4Message) + 1,
"Invalid minimum IP packet limit"); "Invalid minimum IP packet limit");
c_assert(!(overload & ~(N_DHCP4_OVERLOAD_FILE | N_DHCP4_OVERLOAD_SNAME)));
outgoing = calloc(1, sizeof(*outgoing)); outgoing = calloc(1, sizeof(*outgoing));
if (!outgoing) if (!outgoing)
return -ENOMEM; return -ENOMEM;
@@ -220,8 +220,9 @@ int n_dhcp4_outgoing_append(NDhcp4Outgoing *outgoing,
/* try fitting into allowed OPTIONs space */ /* try fitting into allowed OPTIONs space */
if (outgoing->max_size - outgoing->i_message >= n_data + 2U + 3U + 1U) { if (outgoing->max_size - outgoing->i_message >= n_data + 2U + 3U + 1U) {
/* try over-allocation to reduce allocation pressure */ /* try over-allocation to reduce allocation pressure */
n = c_min(outgoing->max_size, n = outgoing->n_message + n_data + 128;
outgoing->n_message + n_data + 128); if (n > outgoing->max_size)
n = outgoing->max_size;
m = realloc(outgoing->message, n); m = realloc(outgoing->message, n);
if (!m) if (!m)
return -ENOMEM; return -ENOMEM;
@@ -276,6 +277,7 @@ int n_dhcp4_outgoing_append(NDhcp4Outgoing *outgoing,
return 0; return 0;
} }
overload = outgoing->overload;
if (overload & N_DHCP4_OVERLOAD_SNAME) if (overload & N_DHCP4_OVERLOAD_SNAME)
outgoing->i_message = offsetof(NDhcp4Message, sname); outgoing->i_message = offsetof(NDhcp4Message, sname);
else else
@@ -340,7 +342,7 @@ int n_dhcp4_outgoing_append_requested_ip(NDhcp4Outgoing *message, struct in_addr
return n_dhcp4_outgoing_append_in_addr(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, addr); return n_dhcp4_outgoing_append_in_addr(message, N_DHCP4_OPTION_REQUESTED_IP_ADDRESS, addr);
} }
void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint32_t secs) { void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint16_t secs) {
NDhcp4Header *header = n_dhcp4_outgoing_get_header(message); NDhcp4Header *header = n_dhcp4_outgoing_get_header(message);
/* /*
@@ -349,7 +351,7 @@ void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint32_t secs) {
*/ */
c_assert(secs); c_assert(secs);
header->secs = htonl(secs); header->secs = htons(secs);
} }
void n_dhcp4_outgoing_set_xid(NDhcp4Outgoing *message, uint32_t xid) { void n_dhcp4_outgoing_set_xid(NDhcp4Outgoing *message, uint32_t xid) {

View File

@@ -7,11 +7,11 @@
#include <endian.h> #include <endian.h>
#include <inttypes.h> #include <inttypes.h>
#include <limits.h> #include <limits.h>
#include <linux/netdevice.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include <syslog.h>
#include "n-dhcp4.h" #include "n-dhcp4.h"
typedef struct NDhcp4CConnection NDhcp4CConnection; typedef struct NDhcp4CConnection NDhcp4CConnection;
@@ -24,6 +24,7 @@ typedef struct NDhcp4Outgoing NDhcp4Outgoing;
typedef struct NDhcp4SConnection NDhcp4SConnection; typedef struct NDhcp4SConnection NDhcp4SConnection;
typedef struct NDhcp4SConnectionIp NDhcp4SConnectionIp; typedef struct NDhcp4SConnectionIp NDhcp4SConnectionIp;
typedef struct NDhcp4SEventNode NDhcp4SEventNode; typedef struct NDhcp4SEventNode NDhcp4SEventNode;
typedef struct NDhcp4LogQueue NDhcp4LogQueue;
/* specs */ /* specs */
@@ -199,6 +200,8 @@ struct NDhcp4Outgoing {
struct { struct {
uint8_t type; uint8_t type;
uint8_t message_type;
uint32_t client_addr;
uint64_t start_time; uint64_t start_time;
uint64_t base_time; uint64_t base_time;
uint64_t send_time; uint64_t send_time;
@@ -234,9 +237,9 @@ struct NDhcp4ClientConfig {
int ifindex; int ifindex;
unsigned int transport; unsigned int transport;
bool request_broadcast; bool request_broadcast;
uint8_t mac[MAX_ADDR_LEN]; uint8_t mac[32]; /* MAX_ADDR_LEN */
size_t n_mac; size_t n_mac;
uint8_t broadcast_mac[MAX_ADDR_LEN]; uint8_t broadcast_mac[32]; /* MAX_ADDR_LEN */
size_t n_broadcast_mac; size_t n_broadcast_mac;
uint8_t *client_id; uint8_t *client_id;
size_t n_client_id; size_t n_client_id;
@@ -283,9 +286,42 @@ struct NDhcp4CEventNode {
.probe_link = C_LIST_INIT((_x).probe_link), \ .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 { struct NDhcp4CConnection {
NDhcp4ClientConfig *client_config; NDhcp4ClientConfig *client_config;
NDhcp4ClientProbeConfig *probe_config; NDhcp4ClientProbeConfig *probe_config;
NDhcp4LogQueue *log_queue;
int fd_epoll; int fd_epoll;
unsigned int state; /* current connection state */ unsigned int state; /* current connection state */
@@ -317,6 +353,9 @@ struct NDhcp4Client {
unsigned long n_refs; unsigned long n_refs;
NDhcp4ClientConfig *config; NDhcp4ClientConfig *config;
CList event_list; CList event_list;
NDhcp4LogQueue log_queue;
int fd_epoll; int fd_epoll;
int fd_timer; int fd_timer;
@@ -332,6 +371,7 @@ struct NDhcp4Client {
.event_list = C_LIST_INIT((_x).event_list), \ .event_list = C_LIST_INIT((_x).event_list), \
.fd_epoll = -1, \ .fd_epoll = -1, \
.fd_timer = -1, \ .fd_timer = -1, \
.log_queue = N_DHCP4_LOG_QUEUE_NULL_CLIENT(_x), \
} }
struct NDhcp4ClientProbe { struct NDhcp4ClientProbe {
@@ -342,7 +382,10 @@ struct NDhcp4ClientProbe {
void *userdata; void *userdata;
unsigned int state; /* current probe state */ unsigned int state; /* current probe state */
struct in_addr last_address; /* last address obtained */
uint64_t ns_deferred; /* timeout for deferred action */ uint64_t ns_deferred; /* timeout for deferred action */
uint64_t ns_reinit;
uint64_t ns_nak_restart_delay; /* restart delay after a nak */
NDhcp4ClientLease *current_lease; /* current lease */ NDhcp4ClientLease *current_lease; /* current lease */
NDhcp4CConnection connection; /* client connection wrapper */ NDhcp4CConnection connection; /* client connection wrapper */
@@ -467,7 +510,7 @@ int n_dhcp4_outgoing_append_lifetime(NDhcp4Outgoing *message, uint32_t lifetime)
int n_dhcp4_outgoing_append_server_identifier(NDhcp4Outgoing *message, struct in_addr addr); int n_dhcp4_outgoing_append_server_identifier(NDhcp4Outgoing *message, struct in_addr addr);
int n_dhcp4_outgoing_append_requested_ip(NDhcp4Outgoing *message, struct in_addr addr); int n_dhcp4_outgoing_append_requested_ip(NDhcp4Outgoing *message, struct in_addr addr);
void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint32_t secs); void n_dhcp4_outgoing_set_secs(NDhcp4Outgoing *message, uint16_t secs);
void n_dhcp4_outgoing_set_xid(NDhcp4Outgoing *message, uint32_t xid); void n_dhcp4_outgoing_set_xid(NDhcp4Outgoing *message, uint32_t xid);
void n_dhcp4_outgoing_set_yiaddr(NDhcp4Outgoing *message, struct in_addr yiaddr); void n_dhcp4_outgoing_set_yiaddr(NDhcp4Outgoing *message, struct in_addr yiaddr);
@@ -560,6 +603,7 @@ NDhcp4CEventNode *n_dhcp4_c_event_node_free(NDhcp4CEventNode *node);
int n_dhcp4_c_connection_init(NDhcp4CConnection *connection, int n_dhcp4_c_connection_init(NDhcp4CConnection *connection,
NDhcp4ClientConfig *client_config, NDhcp4ClientConfig *client_config,
NDhcp4ClientProbeConfig *probe_config, NDhcp4ClientProbeConfig *probe_config,
NDhcp4LogQueue *log_queue,
int fd_epoll); int fd_epoll);
void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection); void n_dhcp4_c_connection_deinit(NDhcp4CConnection *connection);
@@ -687,3 +731,29 @@ static inline uint64_t n_dhcp4_gettime(clockid_t clock) {
return ts.tv_sec * 1000ULL * 1000ULL * 1000ULL + ts.tv_nsec; return ts.tv_sec * 1000ULL * 1000ULL * 1000ULL + ts.tv_nsec;
} }
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 { \
NDhcp4LogQueue *const _log_queue = (x_log_queue); \
const int _level = (x_level); \
\
if (_level <= _log_queue->log_level) { \
n_dhcp4_log_queue_fmt(_log_queue, \
_level, \
__VA_ARGS__); \
} \
} while (0)

View File

@@ -5,16 +5,17 @@
#include <c-stdaux.h> #include <c-stdaux.h>
#include <errno.h> #include <errno.h>
#include <linux/filter.h> #include <linux/filter.h>
#include <sys/socket.h> /* needed by linux/if.h */
#include <linux/if.h>
#include <linux/if_packet.h> #include <linux/if_packet.h>
#include <linux/netdevice.h>
#include <linux/udp.h> #include <linux/udp.h>
#include <net/if.h>
#include <netinet/ip.h> #include <netinet/ip.h>
#include <stddef.h> #include <stddef.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h>
#include "n-dhcp4-private.h" #include "n-dhcp4-private.h"
#include "util/packet.h" #include "util/packet.h"
#include "util/socket.h" #include "util/socket.h"
@@ -194,6 +195,10 @@ int n_dhcp4_c_socket_udp_new(int *sockfdp,
if (sockfd < 0) if (sockfd < 0)
return -errno; return -errno;
r = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (r < 0)
return -errno;
r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); r = setsockopt(sockfd, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog));
if (r < 0) if (r < 0)
return -errno; return -errno;

View File

@@ -61,6 +61,7 @@ enum {
N_DHCP4_CLIENT_EVENT_EXTENDED, N_DHCP4_CLIENT_EVENT_EXTENDED,
N_DHCP4_CLIENT_EVENT_EXPIRED, N_DHCP4_CLIENT_EVENT_EXPIRED,
N_DHCP4_CLIENT_EVENT_CANCELLED, N_DHCP4_CLIENT_EVENT_CANCELLED,
N_DHCP4_CLIENT_EVENT_LOG,
_N_DHCP4_CLIENT_EVENT_N, _N_DHCP4_CLIENT_EVENT_N,
}; };
@@ -86,6 +87,14 @@ struct NDhcp4ClientEvent {
struct { struct {
NDhcp4ClientProbe *probe; NDhcp4ClientProbe *probe;
} retracted, expired, cancelled; } 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;
}; };
}; };
@@ -137,6 +146,8 @@ void n_dhcp4_client_get_fd(NDhcp4Client *client, int *fdp);
int n_dhcp4_client_dispatch(NDhcp4Client *client); int n_dhcp4_client_dispatch(NDhcp4Client *client);
int n_dhcp4_client_pop_event(NDhcp4Client *client, NDhcp4ClientEvent **eventp); 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_update_mtu(NDhcp4Client *client, uint16_t mtu);
int n_dhcp4_client_probe(NDhcp4Client *client, int n_dhcp4_client_probe(NDhcp4Client *client,
@@ -156,6 +167,8 @@ NDhcp4ClientLease *n_dhcp4_client_lease_ref(NDhcp4ClientLease *lease);
NDhcp4ClientLease *n_dhcp4_client_lease_unref(NDhcp4ClientLease *lease); NDhcp4ClientLease *n_dhcp4_client_lease_unref(NDhcp4ClientLease *lease);
void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct in_addr *yiaddr); void n_dhcp4_client_lease_get_yiaddr(NDhcp4ClientLease *lease, struct in_addr *yiaddr);
void n_dhcp4_client_lease_get_siaddr(NDhcp4ClientLease *lease, struct in_addr *siaddr);
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); 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_query(NDhcp4ClientLease *lease, uint8_t option, uint8_t **datap, size_t *n_datap);

View File

@@ -104,6 +104,7 @@ static void test_api_functions(void) {
(void *)n_dhcp4_client_lease_unrefp, (void *)n_dhcp4_client_lease_unrefp,
(void *)n_dhcp4_client_lease_unrefv, (void *)n_dhcp4_client_lease_unrefv,
(void *)n_dhcp4_client_lease_get_yiaddr, (void *)n_dhcp4_client_lease_get_yiaddr,
(void *)n_dhcp4_client_lease_get_siaddr,
(void *)n_dhcp4_client_lease_get_lifetime, (void *)n_dhcp4_client_lease_get_lifetime,
(void *)n_dhcp4_client_lease_query, (void *)n_dhcp4_client_lease_query,
(void *)n_dhcp4_client_lease_select, (void *)n_dhcp4_client_lease_select,

View File

@@ -322,6 +322,7 @@ static void test_connection(void) {
NDhcp4CConnection connection_client = N_DHCP4_C_CONNECTION_NULL(connection_client); NDhcp4CConnection connection_client = N_DHCP4_C_CONNECTION_NULL(connection_client);
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *offer = NULL; _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *offer = NULL;
_c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *ack = NULL; _c_cleanup_(n_dhcp4_incoming_freep) NDhcp4Incoming *ack = NULL;
NDhcp4LogQueue log_queue = N_DHCP4_LOG_QUEUE_NULL_DEFUNCT();
test_s_connection_init(ns_server, &connection_server, link_server.ifindex); test_s_connection_init(ns_server, &connection_server, link_server.ifindex);
n_dhcp4_s_connection_ip_init(&connection_server_ip, addr_server); n_dhcp4_s_connection_ip_init(&connection_server_ip, addr_server);
@@ -351,6 +352,7 @@ static void test_connection(void) {
r = n_dhcp4_c_connection_init(&connection_client, r = n_dhcp4_c_connection_init(&connection_client,
client_config, client_config,
probe_config, probe_config,
&log_queue,
efd_client); efd_client);
c_assert(!r); c_assert(!r);
test_c_connection_listen(ns_client, &connection_client); test_c_connection_listen(ns_client, &connection_client);
@@ -358,13 +360,13 @@ static void test_connection(void) {
test_discover(&connection_server, &connection_client, &addr_server, &addr_client, &offer); test_discover(&connection_server, &connection_client, &addr_server, &addr_client, &offer);
test_select(&connection_server, &connection_client, offer, &addr_server, &addr_client); test_select(&connection_server, &connection_client, offer, &addr_server, &addr_client);
test_reboot(&connection_server, &connection_client, &addr_server, &addr_client, &ack); test_reboot(&connection_server, &connection_client, &addr_server, &addr_client, &ack);
test_rebind(&connection_server, &connection_client, &addr_server, &addr_client);
test_decline(&connection_server, &connection_client, ack); test_decline(&connection_server, &connection_client, ack);
link_add_ip4(&link_client, &addr_client, 8); link_add_ip4(&link_client, &addr_client, 8);
test_c_connection_connect(ns_client, &connection_client, &addr_client, &addr_server); test_c_connection_connect(ns_client, &connection_client, &addr_client, &addr_server);
test_renew(&connection_server, &connection_client, &addr_server, &addr_client); test_renew(&connection_server, &connection_client, &addr_server, &addr_client);
test_rebind(&connection_server, &connection_client, &addr_server, &addr_client);
test_release(&connection_server, &connection_client, &addr_server, &addr_client); test_release(&connection_server, &connection_client, &addr_server, &addr_client);
n_dhcp4_c_connection_deinit(&connection_client); n_dhcp4_c_connection_deinit(&connection_client);

View File

@@ -59,7 +59,7 @@ static void link_query(int netns, const char *name, int *ifindexp, struct ether_
s = socket(AF_INET, SOCK_DGRAM, 0); s = socket(AF_INET, SOCK_DGRAM, 0);
c_assert(s >= 0); c_assert(s >= 0);
strncpy(ifr.ifr_name, name, n_name); memcpy(ifr.ifr_name, name, n_name);
r = ioctl(s, SIOCGIFHWADDR, &ifr); r = ioctl(s, SIOCGIFHWADDR, &ifr);
c_assert(r >= 0); c_assert(r >= 0);

View File

@@ -7,7 +7,6 @@
#include <c-stdaux.h> #include <c-stdaux.h>
#include <inttypes.h> #include <inttypes.h>
#include <linux/if_packet.h> #include <linux/if_packet.h>
#include <linux/netdevice.h>
#include <netinet/in.h> #include <netinet/in.h>
#include <stdlib.h> #include <stdlib.h>
#include <unistd.h> #include <unistd.h>
@@ -25,7 +24,7 @@ struct packet_sockaddr_ll {
unsigned short sll_hatype; unsigned short sll_hatype;
unsigned char sll_pkttype; unsigned char sll_pkttype;
unsigned char sll_halen; unsigned char sll_halen;
unsigned char sll_addr[MAX_ADDR_LEN]; unsigned char sll_addr[32]; /* MAX_ADDR_LEN */
}; };
uint16_t packet_internet_checksum(const uint8_t *data, size_t len); uint16_t packet_internet_checksum(const uint8_t *data, size_t len);